mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/dev' into component_source_logstring
This commit is contained in:
		@@ -64,7 +64,7 @@ void AbsoluteHumidityComponent::loop() {
 | 
			
		||||
      ESP_LOGW(TAG, "No valid state from humidity sensor!");
 | 
			
		||||
    }
 | 
			
		||||
    this->publish_state(NAN);
 | 
			
		||||
    this->status_set_warning("Unable to calculate absolute humidity.");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Unable to calculate absolute humidity."));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,7 @@ void AHT10Component::read_data_() {
 | 
			
		||||
    ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
 | 
			
		||||
  }
 | 
			
		||||
  if (this->read(data, 6) != i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning("Read failed, will retry");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Read failed, will retry"));
 | 
			
		||||
    this->restart_read_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -113,7 +113,7 @@ void AHT10Component::read_data_() {
 | 
			
		||||
    } else {
 | 
			
		||||
      ESP_LOGD(TAG, "Invalid humidity, retrying");
 | 
			
		||||
      if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
 | 
			
		||||
        this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
        this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
 | 
			
		||||
      }
 | 
			
		||||
      this->restart_read_();
 | 
			
		||||
      return;
 | 
			
		||||
@@ -144,7 +144,7 @@ void AHT10Component::update() {
 | 
			
		||||
    return;
 | 
			
		||||
  this->start_time_ = millis();
 | 
			
		||||
  if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
    this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->restart_read_();
 | 
			
		||||
 
 | 
			
		||||
@@ -112,7 +112,7 @@ void APIConnection::start() {
 | 
			
		||||
  APIError err = this->helper_->init();
 | 
			
		||||
  if (err != APIError::OK) {
 | 
			
		||||
    on_fatal_error();
 | 
			
		||||
    this->log_warning_("Helper init failed", err);
 | 
			
		||||
    this->log_warning_(LOG_STR("Helper init failed"), err);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->client_info_.peername = helper_->getpeername();
 | 
			
		||||
@@ -159,7 +159,7 @@ void APIConnection::loop() {
 | 
			
		||||
        break;
 | 
			
		||||
      } else if (err != APIError::OK) {
 | 
			
		||||
        on_fatal_error();
 | 
			
		||||
        this->log_warning_("Reading failed", err);
 | 
			
		||||
        this->log_warning_(LOG_STR("Reading failed"), err);
 | 
			
		||||
        return;
 | 
			
		||||
      } else {
 | 
			
		||||
        this->last_traffic_ = now;
 | 
			
		||||
@@ -1565,7 +1565,7 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
 | 
			
		||||
    return false;
 | 
			
		||||
  if (err != APIError::OK) {
 | 
			
		||||
    on_fatal_error();
 | 
			
		||||
    this->log_warning_("Packet write failed", err);
 | 
			
		||||
    this->log_warning_(LOG_STR("Packet write failed"), err);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  // Do not set last_traffic_ on send
 | 
			
		||||
@@ -1752,7 +1752,7 @@ void APIConnection::process_batch_() {
 | 
			
		||||
                                                       std::span<const PacketInfo>(packet_info, packet_count));
 | 
			
		||||
  if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
 | 
			
		||||
    on_fatal_error();
 | 
			
		||||
    this->log_warning_("Batch write failed", err);
 | 
			
		||||
    this->log_warning_(LOG_STR("Batch write failed"), err);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
@@ -1830,11 +1830,14 @@ void APIConnection::process_state_subscriptions_() {
 | 
			
		||||
}
 | 
			
		||||
#endif  // USE_API_HOMEASSISTANT_STATES
 | 
			
		||||
 | 
			
		||||
void APIConnection::log_warning_(const char *message, APIError err) {
 | 
			
		||||
  ESP_LOGW(TAG, "%s: %s %s errno=%d", this->get_client_combined_info().c_str(), message, api_error_to_str(err), errno);
 | 
			
		||||
void APIConnection::log_warning_(const LogString *message, APIError err) {
 | 
			
		||||
  ESP_LOGW(TAG, "%s: %s %s errno=%d", this->get_client_combined_info().c_str(), LOG_STR_ARG(message),
 | 
			
		||||
           LOG_STR_ARG(api_error_to_logstr(err)), errno);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIConnection::log_socket_operation_failed_(APIError err) { this->log_warning_("Socket operation failed", err); }
 | 
			
		||||
void APIConnection::log_socket_operation_failed_(APIError err) {
 | 
			
		||||
  this->log_warning_(LOG_STR("Socket operation failed"), err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace esphome::api
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -732,7 +732,7 @@ class APIConnection final : public APIServerConnection {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Helper function to log API errors with errno
 | 
			
		||||
  void log_warning_(const char *message, APIError err);
 | 
			
		||||
  void log_warning_(const LogString *message, APIError err);
 | 
			
		||||
  // Specific helper for duplicated error message
 | 
			
		||||
  void log_socket_operation_failed_(APIError err);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -23,59 +23,59 @@ static const char *const TAG = "api.frame_helper";
 | 
			
		||||
#define LOG_PACKET_SENDING(data, len) ((void) 0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
const char *api_error_to_str(APIError err) {
 | 
			
		||||
const LogString *api_error_to_logstr(APIError err) {
 | 
			
		||||
  // not using switch to ensure compiler doesn't try to build a big table out of it
 | 
			
		||||
  if (err == APIError::OK) {
 | 
			
		||||
    return "OK";
 | 
			
		||||
    return LOG_STR("OK");
 | 
			
		||||
  } else if (err == APIError::WOULD_BLOCK) {
 | 
			
		||||
    return "WOULD_BLOCK";
 | 
			
		||||
    return LOG_STR("WOULD_BLOCK");
 | 
			
		||||
  } else if (err == APIError::BAD_INDICATOR) {
 | 
			
		||||
    return "BAD_INDICATOR";
 | 
			
		||||
    return LOG_STR("BAD_INDICATOR");
 | 
			
		||||
  } else if (err == APIError::BAD_DATA_PACKET) {
 | 
			
		||||
    return "BAD_DATA_PACKET";
 | 
			
		||||
    return LOG_STR("BAD_DATA_PACKET");
 | 
			
		||||
  } else if (err == APIError::TCP_NODELAY_FAILED) {
 | 
			
		||||
    return "TCP_NODELAY_FAILED";
 | 
			
		||||
    return LOG_STR("TCP_NODELAY_FAILED");
 | 
			
		||||
  } else if (err == APIError::TCP_NONBLOCKING_FAILED) {
 | 
			
		||||
    return "TCP_NONBLOCKING_FAILED";
 | 
			
		||||
    return LOG_STR("TCP_NONBLOCKING_FAILED");
 | 
			
		||||
  } else if (err == APIError::CLOSE_FAILED) {
 | 
			
		||||
    return "CLOSE_FAILED";
 | 
			
		||||
    return LOG_STR("CLOSE_FAILED");
 | 
			
		||||
  } else if (err == APIError::SHUTDOWN_FAILED) {
 | 
			
		||||
    return "SHUTDOWN_FAILED";
 | 
			
		||||
    return LOG_STR("SHUTDOWN_FAILED");
 | 
			
		||||
  } else if (err == APIError::BAD_STATE) {
 | 
			
		||||
    return "BAD_STATE";
 | 
			
		||||
    return LOG_STR("BAD_STATE");
 | 
			
		||||
  } else if (err == APIError::BAD_ARG) {
 | 
			
		||||
    return "BAD_ARG";
 | 
			
		||||
    return LOG_STR("BAD_ARG");
 | 
			
		||||
  } else if (err == APIError::SOCKET_READ_FAILED) {
 | 
			
		||||
    return "SOCKET_READ_FAILED";
 | 
			
		||||
    return LOG_STR("SOCKET_READ_FAILED");
 | 
			
		||||
  } else if (err == APIError::SOCKET_WRITE_FAILED) {
 | 
			
		||||
    return "SOCKET_WRITE_FAILED";
 | 
			
		||||
    return LOG_STR("SOCKET_WRITE_FAILED");
 | 
			
		||||
  } else if (err == APIError::OUT_OF_MEMORY) {
 | 
			
		||||
    return "OUT_OF_MEMORY";
 | 
			
		||||
    return LOG_STR("OUT_OF_MEMORY");
 | 
			
		||||
  } else if (err == APIError::CONNECTION_CLOSED) {
 | 
			
		||||
    return "CONNECTION_CLOSED";
 | 
			
		||||
    return LOG_STR("CONNECTION_CLOSED");
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
  else if (err == APIError::BAD_HANDSHAKE_PACKET_LEN) {
 | 
			
		||||
    return "BAD_HANDSHAKE_PACKET_LEN";
 | 
			
		||||
    return LOG_STR("BAD_HANDSHAKE_PACKET_LEN");
 | 
			
		||||
  } else if (err == APIError::HANDSHAKESTATE_READ_FAILED) {
 | 
			
		||||
    return "HANDSHAKESTATE_READ_FAILED";
 | 
			
		||||
    return LOG_STR("HANDSHAKESTATE_READ_FAILED");
 | 
			
		||||
  } else if (err == APIError::HANDSHAKESTATE_WRITE_FAILED) {
 | 
			
		||||
    return "HANDSHAKESTATE_WRITE_FAILED";
 | 
			
		||||
    return LOG_STR("HANDSHAKESTATE_WRITE_FAILED");
 | 
			
		||||
  } else if (err == APIError::HANDSHAKESTATE_BAD_STATE) {
 | 
			
		||||
    return "HANDSHAKESTATE_BAD_STATE";
 | 
			
		||||
    return LOG_STR("HANDSHAKESTATE_BAD_STATE");
 | 
			
		||||
  } else if (err == APIError::CIPHERSTATE_DECRYPT_FAILED) {
 | 
			
		||||
    return "CIPHERSTATE_DECRYPT_FAILED";
 | 
			
		||||
    return LOG_STR("CIPHERSTATE_DECRYPT_FAILED");
 | 
			
		||||
  } else if (err == APIError::CIPHERSTATE_ENCRYPT_FAILED) {
 | 
			
		||||
    return "CIPHERSTATE_ENCRYPT_FAILED";
 | 
			
		||||
    return LOG_STR("CIPHERSTATE_ENCRYPT_FAILED");
 | 
			
		||||
  } else if (err == APIError::HANDSHAKESTATE_SETUP_FAILED) {
 | 
			
		||||
    return "HANDSHAKESTATE_SETUP_FAILED";
 | 
			
		||||
    return LOG_STR("HANDSHAKESTATE_SETUP_FAILED");
 | 
			
		||||
  } else if (err == APIError::HANDSHAKESTATE_SPLIT_FAILED) {
 | 
			
		||||
    return "HANDSHAKESTATE_SPLIT_FAILED";
 | 
			
		||||
    return LOG_STR("HANDSHAKESTATE_SPLIT_FAILED");
 | 
			
		||||
  } else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) {
 | 
			
		||||
    return "BAD_HANDSHAKE_ERROR_BYTE";
 | 
			
		||||
    return LOG_STR("BAD_HANDSHAKE_ERROR_BYTE");
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  return "UNKNOWN";
 | 
			
		||||
  return LOG_STR("UNKNOWN");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Default implementation for loop - handles sending buffered data
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,7 @@ enum class APIError : uint16_t {
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char *api_error_to_str(APIError err);
 | 
			
		||||
const LogString *api_error_to_logstr(APIError err);
 | 
			
		||||
 | 
			
		||||
class APIFrameHelper {
 | 
			
		||||
 public:
 | 
			
		||||
 
 | 
			
		||||
@@ -10,10 +10,18 @@
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
#include <pgmspace.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome::api {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "api.noise";
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
static const char PROLOGUE_INIT[] PROGMEM = "NoiseAPIInit";
 | 
			
		||||
#else
 | 
			
		||||
static const char *const PROLOGUE_INIT = "NoiseAPIInit";
 | 
			
		||||
#endif
 | 
			
		||||
static constexpr size_t PROLOGUE_INIT_LEN = 12;  // strlen("NoiseAPIInit")
 | 
			
		||||
 | 
			
		||||
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->client_info_->get_combined_info().c_str(), ##__VA_ARGS__)
 | 
			
		||||
@@ -27,42 +35,42 @@ static constexpr size_t PROLOGUE_INIT_LEN = 12;  // strlen("NoiseAPIInit")
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/// Convert a noise error code to a readable error
 | 
			
		||||
std::string noise_err_to_str(int err) {
 | 
			
		||||
const LogString *noise_err_to_logstr(int err) {
 | 
			
		||||
  if (err == NOISE_ERROR_NO_MEMORY)
 | 
			
		||||
    return "NO_MEMORY";
 | 
			
		||||
    return LOG_STR("NO_MEMORY");
 | 
			
		||||
  if (err == NOISE_ERROR_UNKNOWN_ID)
 | 
			
		||||
    return "UNKNOWN_ID";
 | 
			
		||||
    return LOG_STR("UNKNOWN_ID");
 | 
			
		||||
  if (err == NOISE_ERROR_UNKNOWN_NAME)
 | 
			
		||||
    return "UNKNOWN_NAME";
 | 
			
		||||
    return LOG_STR("UNKNOWN_NAME");
 | 
			
		||||
  if (err == NOISE_ERROR_MAC_FAILURE)
 | 
			
		||||
    return "MAC_FAILURE";
 | 
			
		||||
    return LOG_STR("MAC_FAILURE");
 | 
			
		||||
  if (err == NOISE_ERROR_NOT_APPLICABLE)
 | 
			
		||||
    return "NOT_APPLICABLE";
 | 
			
		||||
    return LOG_STR("NOT_APPLICABLE");
 | 
			
		||||
  if (err == NOISE_ERROR_SYSTEM)
 | 
			
		||||
    return "SYSTEM";
 | 
			
		||||
    return LOG_STR("SYSTEM");
 | 
			
		||||
  if (err == NOISE_ERROR_REMOTE_KEY_REQUIRED)
 | 
			
		||||
    return "REMOTE_KEY_REQUIRED";
 | 
			
		||||
    return LOG_STR("REMOTE_KEY_REQUIRED");
 | 
			
		||||
  if (err == NOISE_ERROR_LOCAL_KEY_REQUIRED)
 | 
			
		||||
    return "LOCAL_KEY_REQUIRED";
 | 
			
		||||
    return LOG_STR("LOCAL_KEY_REQUIRED");
 | 
			
		||||
  if (err == NOISE_ERROR_PSK_REQUIRED)
 | 
			
		||||
    return "PSK_REQUIRED";
 | 
			
		||||
    return LOG_STR("PSK_REQUIRED");
 | 
			
		||||
  if (err == NOISE_ERROR_INVALID_LENGTH)
 | 
			
		||||
    return "INVALID_LENGTH";
 | 
			
		||||
    return LOG_STR("INVALID_LENGTH");
 | 
			
		||||
  if (err == NOISE_ERROR_INVALID_PARAM)
 | 
			
		||||
    return "INVALID_PARAM";
 | 
			
		||||
    return LOG_STR("INVALID_PARAM");
 | 
			
		||||
  if (err == NOISE_ERROR_INVALID_STATE)
 | 
			
		||||
    return "INVALID_STATE";
 | 
			
		||||
    return LOG_STR("INVALID_STATE");
 | 
			
		||||
  if (err == NOISE_ERROR_INVALID_NONCE)
 | 
			
		||||
    return "INVALID_NONCE";
 | 
			
		||||
    return LOG_STR("INVALID_NONCE");
 | 
			
		||||
  if (err == NOISE_ERROR_INVALID_PRIVATE_KEY)
 | 
			
		||||
    return "INVALID_PRIVATE_KEY";
 | 
			
		||||
    return LOG_STR("INVALID_PRIVATE_KEY");
 | 
			
		||||
  if (err == NOISE_ERROR_INVALID_PUBLIC_KEY)
 | 
			
		||||
    return "INVALID_PUBLIC_KEY";
 | 
			
		||||
    return LOG_STR("INVALID_PUBLIC_KEY");
 | 
			
		||||
  if (err == NOISE_ERROR_INVALID_FORMAT)
 | 
			
		||||
    return "INVALID_FORMAT";
 | 
			
		||||
    return LOG_STR("INVALID_FORMAT");
 | 
			
		||||
  if (err == NOISE_ERROR_INVALID_SIGNATURE)
 | 
			
		||||
    return "INVALID_SIGNATURE";
 | 
			
		||||
  return to_string(err);
 | 
			
		||||
    return LOG_STR("INVALID_SIGNATURE");
 | 
			
		||||
  return LOG_STR("UNKNOWN");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Initialize the frame helper, returns OK if successful.
 | 
			
		||||
@@ -75,7 +83,11 @@ APIError APINoiseFrameHelper::init() {
 | 
			
		||||
  // init prologue
 | 
			
		||||
  size_t old_size = prologue_.size();
 | 
			
		||||
  prologue_.resize(old_size + PROLOGUE_INIT_LEN);
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
  memcpy_P(prologue_.data() + old_size, PROLOGUE_INIT, PROLOGUE_INIT_LEN);
 | 
			
		||||
#else
 | 
			
		||||
  std::memcpy(prologue_.data() + old_size, PROLOGUE_INIT, PROLOGUE_INIT_LEN);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  state_ = State::CLIENT_HELLO;
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
@@ -83,18 +95,18 @@ APIError APINoiseFrameHelper::init() {
 | 
			
		||||
// Helper for handling handshake frame errors
 | 
			
		||||
APIError APINoiseFrameHelper::handle_handshake_frame_error_(APIError aerr) {
 | 
			
		||||
  if (aerr == APIError::BAD_INDICATOR) {
 | 
			
		||||
    send_explicit_handshake_reject_("Bad indicator byte");
 | 
			
		||||
    send_explicit_handshake_reject_(LOG_STR("Bad indicator byte"));
 | 
			
		||||
  } else if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
 | 
			
		||||
    send_explicit_handshake_reject_("Bad handshake packet len");
 | 
			
		||||
    send_explicit_handshake_reject_(LOG_STR("Bad handshake packet len"));
 | 
			
		||||
  }
 | 
			
		||||
  return aerr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper for handling noise library errors
 | 
			
		||||
APIError APINoiseFrameHelper::handle_noise_error_(int err, const char *func_name, APIError api_err) {
 | 
			
		||||
APIError APINoiseFrameHelper::handle_noise_error_(int err, const LogString *func_name, APIError api_err) {
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    HELPER_LOG("%s failed: %s", func_name, noise_err_to_str(err).c_str());
 | 
			
		||||
    HELPER_LOG("%s failed: %s", LOG_STR_ARG(func_name), LOG_STR_ARG(noise_err_to_logstr(err)));
 | 
			
		||||
    return api_err;
 | 
			
		||||
  }
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
@@ -279,11 +291,11 @@ APIError APINoiseFrameHelper::state_action_() {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (frame.empty()) {
 | 
			
		||||
        send_explicit_handshake_reject_("Empty handshake message");
 | 
			
		||||
        send_explicit_handshake_reject_(LOG_STR("Empty handshake message"));
 | 
			
		||||
        return APIError::BAD_HANDSHAKE_ERROR_BYTE;
 | 
			
		||||
      } else if (frame[0] != 0x00) {
 | 
			
		||||
        HELPER_LOG("Bad handshake error byte: %u", frame[0]);
 | 
			
		||||
        send_explicit_handshake_reject_("Bad handshake error byte");
 | 
			
		||||
        send_explicit_handshake_reject_(LOG_STR("Bad handshake error byte"));
 | 
			
		||||
        return APIError::BAD_HANDSHAKE_ERROR_BYTE;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@@ -293,8 +305,10 @@ APIError APINoiseFrameHelper::state_action_() {
 | 
			
		||||
      err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
 | 
			
		||||
      if (err != 0) {
 | 
			
		||||
        // Special handling for MAC failure
 | 
			
		||||
        send_explicit_handshake_reject_(err == NOISE_ERROR_MAC_FAILURE ? "Handshake MAC failure" : "Handshake error");
 | 
			
		||||
        return handle_noise_error_(err, "noise_handshakestate_read_message", APIError::HANDSHAKESTATE_READ_FAILED);
 | 
			
		||||
        send_explicit_handshake_reject_(err == NOISE_ERROR_MAC_FAILURE ? LOG_STR("Handshake MAC failure")
 | 
			
		||||
                                                                       : LOG_STR("Handshake error"));
 | 
			
		||||
        return handle_noise_error_(err, LOG_STR("noise_handshakestate_read_message"),
 | 
			
		||||
                                   APIError::HANDSHAKESTATE_READ_FAILED);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      aerr = check_handshake_finished_();
 | 
			
		||||
@@ -307,8 +321,8 @@ APIError APINoiseFrameHelper::state_action_() {
 | 
			
		||||
      noise_buffer_set_output(mbuf, buffer + 1, sizeof(buffer) - 1);
 | 
			
		||||
 | 
			
		||||
      err = noise_handshakestate_write_message(handshake_, &mbuf, nullptr);
 | 
			
		||||
      APIError aerr_write =
 | 
			
		||||
          handle_noise_error_(err, "noise_handshakestate_write_message", APIError::HANDSHAKESTATE_WRITE_FAILED);
 | 
			
		||||
      APIError aerr_write = handle_noise_error_(err, LOG_STR("noise_handshakestate_write_message"),
 | 
			
		||||
                                                APIError::HANDSHAKESTATE_WRITE_FAILED);
 | 
			
		||||
      if (aerr_write != APIError::OK)
 | 
			
		||||
        return aerr_write;
 | 
			
		||||
      buffer[0] = 0x00;  // success
 | 
			
		||||
@@ -331,15 +345,31 @@ APIError APINoiseFrameHelper::state_action_() {
 | 
			
		||||
  }
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
}
 | 
			
		||||
void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &reason) {
 | 
			
		||||
void APINoiseFrameHelper::send_explicit_handshake_reject_(const LogString *reason) {
 | 
			
		||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
 | 
			
		||||
  // On ESP8266 with flash strings, we need to use PROGMEM-aware functions
 | 
			
		||||
  size_t reason_len = strlen_P(reinterpret_cast<PGM_P>(reason));
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
  data.resize(reason.length() + 1);
 | 
			
		||||
  data.resize(reason_len + 1);
 | 
			
		||||
  data[0] = 0x01;  // failure
 | 
			
		||||
 | 
			
		||||
  // Copy error message from PROGMEM
 | 
			
		||||
  if (reason_len > 0) {
 | 
			
		||||
    memcpy_P(data.data() + 1, reinterpret_cast<PGM_P>(reason), reason_len);
 | 
			
		||||
  }
 | 
			
		||||
#else
 | 
			
		||||
  // Normal memory access
 | 
			
		||||
  const char *reason_str = LOG_STR_ARG(reason);
 | 
			
		||||
  size_t reason_len = strlen(reason_str);
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
  data.resize(reason_len + 1);
 | 
			
		||||
  data[0] = 0x01;  // failure
 | 
			
		||||
 | 
			
		||||
  // Copy error message in bulk
 | 
			
		||||
  if (!reason.empty()) {
 | 
			
		||||
    std::memcpy(data.data() + 1, reason.c_str(), reason.length());
 | 
			
		||||
  if (reason_len > 0) {
 | 
			
		||||
    std::memcpy(data.data() + 1, reason_str, reason_len);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // temporarily remove failed state
 | 
			
		||||
  auto orig_state = state_;
 | 
			
		||||
@@ -368,7 +398,8 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
 | 
			
		||||
  noise_buffer_init(mbuf);
 | 
			
		||||
  noise_buffer_set_inout(mbuf, frame.data(), frame.size(), frame.size());
 | 
			
		||||
  err = noise_cipherstate_decrypt(recv_cipher_, &mbuf);
 | 
			
		||||
  APIError decrypt_err = handle_noise_error_(err, "noise_cipherstate_decrypt", APIError::CIPHERSTATE_DECRYPT_FAILED);
 | 
			
		||||
  APIError decrypt_err =
 | 
			
		||||
      handle_noise_error_(err, LOG_STR("noise_cipherstate_decrypt"), APIError::CIPHERSTATE_DECRYPT_FAILED);
 | 
			
		||||
  if (decrypt_err != APIError::OK)
 | 
			
		||||
    return decrypt_err;
 | 
			
		||||
 | 
			
		||||
@@ -450,7 +481,8 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, st
 | 
			
		||||
                           4 + packet.payload_size + frame_footer_size_);
 | 
			
		||||
 | 
			
		||||
    int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
 | 
			
		||||
    APIError aerr = handle_noise_error_(err, "noise_cipherstate_encrypt", APIError::CIPHERSTATE_ENCRYPT_FAILED);
 | 
			
		||||
    APIError aerr =
 | 
			
		||||
        handle_noise_error_(err, LOG_STR("noise_cipherstate_encrypt"), APIError::CIPHERSTATE_ENCRYPT_FAILED);
 | 
			
		||||
    if (aerr != APIError::OK)
 | 
			
		||||
      return aerr;
 | 
			
		||||
 | 
			
		||||
@@ -504,25 +536,27 @@ APIError APINoiseFrameHelper::init_handshake_() {
 | 
			
		||||
  nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
 | 
			
		||||
 | 
			
		||||
  err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
 | 
			
		||||
  APIError aerr = handle_noise_error_(err, "noise_handshakestate_new_by_id", APIError::HANDSHAKESTATE_SETUP_FAILED);
 | 
			
		||||
  APIError aerr =
 | 
			
		||||
      handle_noise_error_(err, LOG_STR("noise_handshakestate_new_by_id"), APIError::HANDSHAKESTATE_SETUP_FAILED);
 | 
			
		||||
  if (aerr != APIError::OK)
 | 
			
		||||
    return aerr;
 | 
			
		||||
 | 
			
		||||
  const auto &psk = ctx_->get_psk();
 | 
			
		||||
  err = noise_handshakestate_set_pre_shared_key(handshake_, psk.data(), psk.size());
 | 
			
		||||
  aerr = handle_noise_error_(err, "noise_handshakestate_set_pre_shared_key", APIError::HANDSHAKESTATE_SETUP_FAILED);
 | 
			
		||||
  aerr = handle_noise_error_(err, LOG_STR("noise_handshakestate_set_pre_shared_key"),
 | 
			
		||||
                             APIError::HANDSHAKESTATE_SETUP_FAILED);
 | 
			
		||||
  if (aerr != APIError::OK)
 | 
			
		||||
    return aerr;
 | 
			
		||||
 | 
			
		||||
  err = noise_handshakestate_set_prologue(handshake_, prologue_.data(), prologue_.size());
 | 
			
		||||
  aerr = handle_noise_error_(err, "noise_handshakestate_set_prologue", APIError::HANDSHAKESTATE_SETUP_FAILED);
 | 
			
		||||
  aerr = handle_noise_error_(err, LOG_STR("noise_handshakestate_set_prologue"), APIError::HANDSHAKESTATE_SETUP_FAILED);
 | 
			
		||||
  if (aerr != APIError::OK)
 | 
			
		||||
    return aerr;
 | 
			
		||||
  // set_prologue copies it into handshakestate, so we can get rid of it now
 | 
			
		||||
  prologue_ = {};
 | 
			
		||||
 | 
			
		||||
  err = noise_handshakestate_start(handshake_);
 | 
			
		||||
  aerr = handle_noise_error_(err, "noise_handshakestate_start", APIError::HANDSHAKESTATE_SETUP_FAILED);
 | 
			
		||||
  aerr = handle_noise_error_(err, LOG_STR("noise_handshakestate_start"), APIError::HANDSHAKESTATE_SETUP_FAILED);
 | 
			
		||||
  if (aerr != APIError::OK)
 | 
			
		||||
    return aerr;
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
@@ -540,7 +574,8 @@ APIError APINoiseFrameHelper::check_handshake_finished_() {
 | 
			
		||||
    return APIError::HANDSHAKESTATE_BAD_STATE;
 | 
			
		||||
  }
 | 
			
		||||
  int err = noise_handshakestate_split(handshake_, &send_cipher_, &recv_cipher_);
 | 
			
		||||
  APIError aerr = handle_noise_error_(err, "noise_handshakestate_split", APIError::HANDSHAKESTATE_SPLIT_FAILED);
 | 
			
		||||
  APIError aerr =
 | 
			
		||||
      handle_noise_error_(err, LOG_STR("noise_handshakestate_split"), APIError::HANDSHAKESTATE_SPLIT_FAILED);
 | 
			
		||||
  if (aerr != APIError::OK)
 | 
			
		||||
    return aerr;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,9 +32,9 @@ class APINoiseFrameHelper final : public APIFrameHelper {
 | 
			
		||||
  APIError write_frame_(const uint8_t *data, uint16_t len);
 | 
			
		||||
  APIError init_handshake_();
 | 
			
		||||
  APIError check_handshake_finished_();
 | 
			
		||||
  void send_explicit_handshake_reject_(const std::string &reason);
 | 
			
		||||
  void send_explicit_handshake_reject_(const LogString *reason);
 | 
			
		||||
  APIError handle_handshake_frame_error_(APIError aerr);
 | 
			
		||||
  APIError handle_noise_error_(int err, const char *func_name, APIError api_err);
 | 
			
		||||
  APIError handle_noise_error_(int err, const LogString *func_name, APIError api_err);
 | 
			
		||||
 | 
			
		||||
  // Pointers first (4 bytes each)
 | 
			
		||||
  NoiseHandshakeState *handshake_{nullptr};
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ constexpr static const uint8_t AXS_READ_TOUCHPAD[11] = {0xb5, 0xab, 0xa5, 0x5a,
 | 
			
		||||
 | 
			
		||||
#define ERROR_CHECK(err) \
 | 
			
		||||
  if ((err) != i2c::ERROR_OK) { \
 | 
			
		||||
    this->status_set_warning("Failed to communicate"); \
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to communicate")); \
 | 
			
		||||
    return; \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -149,7 +149,7 @@ void BL0942::setup() {
 | 
			
		||||
  this->write_reg_(BL0942_REG_USR_WRPROT, 0);
 | 
			
		||||
 | 
			
		||||
  if (this->read_reg_(BL0942_REG_MODE) != mode)
 | 
			
		||||
    this->status_set_warning("BL0942 setup failed!");
 | 
			
		||||
    this->status_set_warning(LOG_STR("BL0942 setup failed!"));
 | 
			
		||||
 | 
			
		||||
  this->flush();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,17 +11,35 @@ namespace captive_portal {
 | 
			
		||||
static const char *const TAG = "captive_portal";
 | 
			
		||||
 | 
			
		||||
void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
 | 
			
		||||
  AsyncResponseStream *stream = request->beginResponseStream("application/json");
 | 
			
		||||
  stream->addHeader("cache-control", "public, max-age=0, must-revalidate");
 | 
			
		||||
  AsyncResponseStream *stream = request->beginResponseStream(F("application/json"));
 | 
			
		||||
  stream->addHeader(F("cache-control"), F("public, max-age=0, must-revalidate"));
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
  stream->print(F("{\"mac\":\""));
 | 
			
		||||
  stream->print(get_mac_address_pretty().c_str());
 | 
			
		||||
  stream->print(F("\",\"name\":\""));
 | 
			
		||||
  stream->print(App.get_name().c_str());
 | 
			
		||||
  stream->print(F("\",\"aps\":[{}"));
 | 
			
		||||
#else
 | 
			
		||||
  stream->printf(R"({"mac":"%s","name":"%s","aps":[{})", get_mac_address_pretty().c_str(), App.get_name().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  for (auto &scan : wifi::global_wifi_component->get_scan_result()) {
 | 
			
		||||
    if (scan.get_is_hidden())
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    // Assumes no " in ssid, possible unicode isses?
 | 
			
		||||
      // Assumes no " in ssid, possible unicode isses?
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
    stream->print(F(",{\"ssid\":\""));
 | 
			
		||||
    stream->print(scan.get_ssid().c_str());
 | 
			
		||||
    stream->print(F("\",\"rssi\":"));
 | 
			
		||||
    stream->print(scan.get_rssi());
 | 
			
		||||
    stream->print(F(",\"lock\":"));
 | 
			
		||||
    stream->print(scan.get_with_auth());
 | 
			
		||||
    stream->print(F("}"));
 | 
			
		||||
#else
 | 
			
		||||
    stream->printf(R"(,{"ssid":"%s","rssi":%d,"lock":%d})", scan.get_ssid().c_str(), scan.get_rssi(),
 | 
			
		||||
                   scan.get_with_auth());
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  stream->print(F("]}"));
 | 
			
		||||
  request->send(stream);
 | 
			
		||||
@@ -34,7 +52,7 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
 | 
			
		||||
  ESP_LOGI(TAG, "  Password=" LOG_SECRET("'%s'"), psk.c_str());
 | 
			
		||||
  wifi::global_wifi_component->save_wifi_sta(ssid, psk);
 | 
			
		||||
  wifi::global_wifi_component->start_scanning();
 | 
			
		||||
  request->redirect("/?save");
 | 
			
		||||
  request->redirect(F("/?save"));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CaptivePortal::setup() {
 | 
			
		||||
@@ -53,18 +71,23 @@ void CaptivePortal::start() {
 | 
			
		||||
  this->dns_server_ = make_unique<DNSServer>();
 | 
			
		||||
  this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
 | 
			
		||||
  network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
 | 
			
		||||
  this->dns_server_->start(53, "*", ip);
 | 
			
		||||
  this->dns_server_->start(53, F("*"), ip);
 | 
			
		||||
  // Re-enable loop() when DNS server is started
 | 
			
		||||
  this->enable_loop();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
 | 
			
		||||
    if (!this->active_ || req->host().c_str() == wifi::global_wifi_component->wifi_soft_ap_ip().str()) {
 | 
			
		||||
      req->send(404, "text/html", "File not found");
 | 
			
		||||
      req->send(404, F("text/html"), F("File not found"));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
    String url = F("http://");
 | 
			
		||||
    url += wifi::global_wifi_component->wifi_soft_ap_ip().str().c_str();
 | 
			
		||||
#else
 | 
			
		||||
    auto url = "http://" + wifi::global_wifi_component->wifi_soft_ap_ip().str();
 | 
			
		||||
#endif
 | 
			
		||||
    req->redirect(url.c_str());
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@@ -73,19 +96,19 @@ void CaptivePortal::start() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CaptivePortal::handleRequest(AsyncWebServerRequest *req) {
 | 
			
		||||
  if (req->url() == "/") {
 | 
			
		||||
  if (req->url() == F("/")) {
 | 
			
		||||
#ifndef USE_ESP8266
 | 
			
		||||
    auto *response = req->beginResponse(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
 | 
			
		||||
    auto *response = req->beginResponse(200, F("text/html"), INDEX_GZ, sizeof(INDEX_GZ));
 | 
			
		||||
#else
 | 
			
		||||
    auto *response = req->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
 | 
			
		||||
    auto *response = req->beginResponse_P(200, F("text/html"), INDEX_GZ, sizeof(INDEX_GZ));
 | 
			
		||||
#endif
 | 
			
		||||
    response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
    response->addHeader(F("Content-Encoding"), F("gzip"));
 | 
			
		||||
    req->send(response);
 | 
			
		||||
    return;
 | 
			
		||||
  } else if (req->url() == "/config.json") {
 | 
			
		||||
  } else if (req->url() == F("/config.json")) {
 | 
			
		||||
    this->handle_config(req);
 | 
			
		||||
    return;
 | 
			
		||||
  } else if (req->url() == "/wifisave") {
 | 
			
		||||
  } else if (req->url() == F("/wifisave")) {
 | 
			
		||||
    this->handle_wifisave(req);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -45,11 +45,11 @@ class CaptivePortal : public AsyncWebHandler, public Component {
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if (request->method() == HTTP_GET) {
 | 
			
		||||
      if (request->url() == "/")
 | 
			
		||||
      if (request->url() == F("/"))
 | 
			
		||||
        return true;
 | 
			
		||||
      if (request->url() == "/config.json")
 | 
			
		||||
      if (request->url() == F("/config.json"))
 | 
			
		||||
        return true;
 | 
			
		||||
      if (request->url() == "/wifisave")
 | 
			
		||||
      if (request->url() == F("/wifisave"))
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ bool DallasTemperatureSensor::read_scratch_pad_() {
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGW(TAG, "'%s' - reading scratch pad failed bus reset", this->get_name().c_str());
 | 
			
		||||
    this->status_set_warning("bus reset failed");
 | 
			
		||||
    this->status_set_warning(LOG_STR("bus reset failed"));
 | 
			
		||||
  }
 | 
			
		||||
  return success;
 | 
			
		||||
}
 | 
			
		||||
@@ -124,7 +124,7 @@ bool DallasTemperatureSensor::check_scratch_pad_() {
 | 
			
		||||
            crc8(this->scratch_pad_, 8));
 | 
			
		||||
#endif
 | 
			
		||||
  if (!chksum_validity) {
 | 
			
		||||
    this->status_set_warning("scratch pad checksum invalid");
 | 
			
		||||
    this->status_set_warning(LOG_STR("scratch pad checksum invalid"));
 | 
			
		||||
    ESP_LOGD(TAG, "Scratch pad: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X (%02X)", this->scratch_pad_[0],
 | 
			
		||||
             this->scratch_pad_[1], this->scratch_pad_[2], this->scratch_pad_[3], this->scratch_pad_[4],
 | 
			
		||||
             this->scratch_pad_[5], this->scratch_pad_[6], this->scratch_pad_[7], this->scratch_pad_[8],
 | 
			
		||||
 
 | 
			
		||||
@@ -30,19 +30,19 @@ void ESPHomeOTAComponent::setup() {
 | 
			
		||||
 | 
			
		||||
  this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0);  // monitored for incoming connections
 | 
			
		||||
  if (this->server_ == nullptr) {
 | 
			
		||||
    this->log_socket_error_("creation");
 | 
			
		||||
    this->log_socket_error_(LOG_STR("creation"));
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  int enable = 1;
 | 
			
		||||
  int err = this->server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    this->log_socket_error_("reuseaddr");
 | 
			
		||||
    this->log_socket_error_(LOG_STR("reuseaddr"));
 | 
			
		||||
    // we can still continue
 | 
			
		||||
  }
 | 
			
		||||
  err = this->server_->setblocking(false);
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    this->log_socket_error_("non-blocking");
 | 
			
		||||
    this->log_socket_error_(LOG_STR("non-blocking"));
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -51,21 +51,21 @@ void ESPHomeOTAComponent::setup() {
 | 
			
		||||
 | 
			
		||||
  socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
 | 
			
		||||
  if (sl == 0) {
 | 
			
		||||
    this->log_socket_error_("set sockaddr");
 | 
			
		||||
    this->log_socket_error_(LOG_STR("set sockaddr"));
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  err = this->server_->bind((struct sockaddr *) &server, sizeof(server));
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    this->log_socket_error_("bind");
 | 
			
		||||
    this->log_socket_error_(LOG_STR("bind"));
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  err = this->server_->listen(4);
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    this->log_socket_error_("listen");
 | 
			
		||||
    this->log_socket_error_(LOG_STR("listen"));
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -114,17 +114,17 @@ void ESPHomeOTAComponent::handle_handshake_() {
 | 
			
		||||
      return;
 | 
			
		||||
    int err = this->client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
 | 
			
		||||
    if (err != 0) {
 | 
			
		||||
      this->log_socket_error_("nodelay");
 | 
			
		||||
      this->log_socket_error_(LOG_STR("nodelay"));
 | 
			
		||||
      this->cleanup_connection_();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    err = this->client_->setblocking(false);
 | 
			
		||||
    if (err != 0) {
 | 
			
		||||
      this->log_socket_error_("non-blocking");
 | 
			
		||||
      this->log_socket_error_(LOG_STR("non-blocking"));
 | 
			
		||||
      this->cleanup_connection_();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this->log_start_("handshake");
 | 
			
		||||
    this->log_start_(LOG_STR("handshake"));
 | 
			
		||||
    this->client_connect_time_ = App.get_loop_component_start_time();
 | 
			
		||||
    this->magic_buf_pos_ = 0;  // Reset magic buffer position
 | 
			
		||||
  }
 | 
			
		||||
@@ -150,7 +150,7 @@ void ESPHomeOTAComponent::handle_handshake_() {
 | 
			
		||||
    if (read <= 0) {
 | 
			
		||||
      // Error or connection closed
 | 
			
		||||
      if (read == -1) {
 | 
			
		||||
        this->log_socket_error_("reading magic bytes");
 | 
			
		||||
        this->log_socket_error_(LOG_STR("reading magic bytes"));
 | 
			
		||||
      } else {
 | 
			
		||||
        ESP_LOGW(TAG, "Remote closed during handshake");
 | 
			
		||||
      }
 | 
			
		||||
@@ -209,7 +209,7 @@ void ESPHomeOTAComponent::handle_data_() {
 | 
			
		||||
 | 
			
		||||
  // Read features - 1 byte
 | 
			
		||||
  if (!this->readall_(buf, 1)) {
 | 
			
		||||
    this->log_read_error_("features");
 | 
			
		||||
    this->log_read_error_(LOG_STR("features"));
 | 
			
		||||
    goto error;  // NOLINT(cppcoreguidelines-avoid-goto)
 | 
			
		||||
  }
 | 
			
		||||
  ota_features = buf[0];  // NOLINT
 | 
			
		||||
@@ -288,7 +288,7 @@ void ESPHomeOTAComponent::handle_data_() {
 | 
			
		||||
 | 
			
		||||
  // Read size, 4 bytes MSB first
 | 
			
		||||
  if (!this->readall_(buf, 4)) {
 | 
			
		||||
    this->log_read_error_("size");
 | 
			
		||||
    this->log_read_error_(LOG_STR("size"));
 | 
			
		||||
    goto error;  // NOLINT(cppcoreguidelines-avoid-goto)
 | 
			
		||||
  }
 | 
			
		||||
  ota_size = 0;
 | 
			
		||||
@@ -302,7 +302,7 @@ void ESPHomeOTAComponent::handle_data_() {
 | 
			
		||||
  // starting the update, set the warning status and notify
 | 
			
		||||
  // listeners. This ensures that port scanners do not
 | 
			
		||||
  // accidentally trigger the update process.
 | 
			
		||||
  this->log_start_("update");
 | 
			
		||||
  this->log_start_(LOG_STR("update"));
 | 
			
		||||
  this->status_set_warning();
 | 
			
		||||
#ifdef USE_OTA_STATE_CALLBACK
 | 
			
		||||
  this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0);
 | 
			
		||||
@@ -320,7 +320,7 @@ void ESPHomeOTAComponent::handle_data_() {
 | 
			
		||||
 | 
			
		||||
  // Read binary MD5, 32 bytes
 | 
			
		||||
  if (!this->readall_(buf, 32)) {
 | 
			
		||||
    this->log_read_error_("MD5 checksum");
 | 
			
		||||
    this->log_read_error_(LOG_STR("MD5 checksum"));
 | 
			
		||||
    goto error;  // NOLINT(cppcoreguidelines-avoid-goto)
 | 
			
		||||
  }
 | 
			
		||||
  sbuf[32] = '\0';
 | 
			
		||||
@@ -393,7 +393,7 @@ void ESPHomeOTAComponent::handle_data_() {
 | 
			
		||||
 | 
			
		||||
  // Read ACK
 | 
			
		||||
  if (!this->readall_(buf, 1) || buf[0] != ota::OTA_RESPONSE_OK) {
 | 
			
		||||
    this->log_read_error_("ack");
 | 
			
		||||
    this->log_read_error_(LOG_STR("ack"));
 | 
			
		||||
    // do not go to error, this is not fatal
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -477,12 +477,14 @@ float ESPHomeOTAComponent::get_setup_priority() const { return setup_priority::A
 | 
			
		||||
uint16_t ESPHomeOTAComponent::get_port() const { return this->port_; }
 | 
			
		||||
void ESPHomeOTAComponent::set_port(uint16_t port) { this->port_ = port; }
 | 
			
		||||
 | 
			
		||||
void ESPHomeOTAComponent::log_socket_error_(const char *msg) { ESP_LOGW(TAG, "Socket %s: errno %d", msg, errno); }
 | 
			
		||||
void ESPHomeOTAComponent::log_socket_error_(const LogString *msg) {
 | 
			
		||||
  ESP_LOGW(TAG, "Socket %s: errno %d", LOG_STR_ARG(msg), errno);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESPHomeOTAComponent::log_read_error_(const char *what) { ESP_LOGW(TAG, "Read %s failed", what); }
 | 
			
		||||
void ESPHomeOTAComponent::log_read_error_(const LogString *what) { ESP_LOGW(TAG, "Read %s failed", LOG_STR_ARG(what)); }
 | 
			
		||||
 | 
			
		||||
void ESPHomeOTAComponent::log_start_(const char *phase) {
 | 
			
		||||
  ESP_LOGD(TAG, "Starting %s from %s", phase, this->client_->getpeername().c_str());
 | 
			
		||||
void ESPHomeOTAComponent::log_start_(const LogString *phase) {
 | 
			
		||||
  ESP_LOGD(TAG, "Starting %s from %s", LOG_STR_ARG(phase), this->client_->getpeername().c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESPHomeOTAComponent::cleanup_connection_() {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_OTA
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/preferences.h"
 | 
			
		||||
#include "esphome/components/ota/ota_backend.h"
 | 
			
		||||
#include "esphome/components/socket/socket.h"
 | 
			
		||||
@@ -31,9 +32,9 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
 | 
			
		||||
  void handle_data_();
 | 
			
		||||
  bool readall_(uint8_t *buf, size_t len);
 | 
			
		||||
  bool writeall_(const uint8_t *buf, size_t len);
 | 
			
		||||
  void log_socket_error_(const char *msg);
 | 
			
		||||
  void log_read_error_(const char *what);
 | 
			
		||||
  void log_start_(const char *phase);
 | 
			
		||||
  void log_socket_error_(const LogString *msg);
 | 
			
		||||
  void log_read_error_(const LogString *what);
 | 
			
		||||
  void log_start_(const LogString *phase);
 | 
			
		||||
  void cleanup_connection_();
 | 
			
		||||
  void yield_and_feed_watchdog_();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -492,7 +492,7 @@ void EthernetComponent::start_connect_() {
 | 
			
		||||
  global_eth_component->ipv6_count_ = 0;
 | 
			
		||||
#endif /* USE_NETWORK_IPV6 */
 | 
			
		||||
  this->connect_begin_ = millis();
 | 
			
		||||
  this->status_set_warning("waiting for IP configuration");
 | 
			
		||||
  this->status_set_warning(LOG_STR("waiting for IP configuration"));
 | 
			
		||||
 | 
			
		||||
  esp_err_t err;
 | 
			
		||||
  err = esp_netif_set_hostname(this->eth_netif_, App.get_name().c_str());
 | 
			
		||||
 
 | 
			
		||||
@@ -11,22 +11,22 @@ static const uint8_t NUMBER_OF_READ_RETRIES = 5;
 | 
			
		||||
void GDK101Component::update() {
 | 
			
		||||
  uint8_t data[2];
 | 
			
		||||
  if (!this->read_dose_1m_(data)) {
 | 
			
		||||
    this->status_set_warning("Failed to read dose 1m");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to read dose 1m"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->read_dose_10m_(data)) {
 | 
			
		||||
    this->status_set_warning("Failed to read dose 10m");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to read dose 10m"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->read_status_(data)) {
 | 
			
		||||
    this->status_set_warning("Failed to read status");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to read status"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->read_measurement_duration_(data)) {
 | 
			
		||||
    this->status_set_warning("Failed to read measurement duration");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to read measurement duration"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,23 @@ namespace gpio {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "gpio.binary_sensor";
 | 
			
		||||
 | 
			
		||||
static const LogString *interrupt_type_to_string(gpio::InterruptType type) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case gpio::INTERRUPT_RISING_EDGE:
 | 
			
		||||
      return LOG_STR("RISING_EDGE");
 | 
			
		||||
    case gpio::INTERRUPT_FALLING_EDGE:
 | 
			
		||||
      return LOG_STR("FALLING_EDGE");
 | 
			
		||||
    case gpio::INTERRUPT_ANY_EDGE:
 | 
			
		||||
      return LOG_STR("ANY_EDGE");
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("UNKNOWN");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const LogString *gpio_mode_to_string(bool use_interrupt) {
 | 
			
		||||
  return use_interrupt ? LOG_STR("interrupt") : LOG_STR("polling");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IRAM_ATTR GPIOBinarySensorStore::gpio_intr(GPIOBinarySensorStore *arg) {
 | 
			
		||||
  bool new_state = arg->isr_pin_.digital_read();
 | 
			
		||||
  if (new_state != arg->last_state_) {
 | 
			
		||||
@@ -51,25 +68,9 @@ void GPIOBinarySensor::setup() {
 | 
			
		||||
void GPIOBinarySensor::dump_config() {
 | 
			
		||||
  LOG_BINARY_SENSOR("", "GPIO Binary Sensor", this);
 | 
			
		||||
  LOG_PIN("  Pin: ", this->pin_);
 | 
			
		||||
  const char *mode = this->use_interrupt_ ? "interrupt" : "polling";
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Mode: %s", mode);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Mode: %s", LOG_STR_ARG(gpio_mode_to_string(this->use_interrupt_)));
 | 
			
		||||
  if (this->use_interrupt_) {
 | 
			
		||||
    const char *interrupt_type;
 | 
			
		||||
    switch (this->interrupt_type_) {
 | 
			
		||||
      case gpio::INTERRUPT_RISING_EDGE:
 | 
			
		||||
        interrupt_type = "RISING_EDGE";
 | 
			
		||||
        break;
 | 
			
		||||
      case gpio::INTERRUPT_FALLING_EDGE:
 | 
			
		||||
        interrupt_type = "FALLING_EDGE";
 | 
			
		||||
        break;
 | 
			
		||||
      case gpio::INTERRUPT_ANY_EDGE:
 | 
			
		||||
        interrupt_type = "ANY_EDGE";
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        interrupt_type = "UNKNOWN";
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Interrupt Type: %s", interrupt_type);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Interrupt Type: %s", LOG_STR_ARG(interrupt_type_to_string(this->interrupt_type_)));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ static const size_t MAX_BUTTONS = 4;  // max number of buttons scanned
 | 
			
		||||
 | 
			
		||||
#define ERROR_CHECK(err) \
 | 
			
		||||
  if ((err) != i2c::ERROR_OK) { \
 | 
			
		||||
    this->status_set_warning(ESP_LOG_MSG_COMM_FAIL); \
 | 
			
		||||
    this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); \
 | 
			
		||||
    return; \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ static const char *const TAG = "honeywellabp2";
 | 
			
		||||
void HONEYWELLABP2Sensor::read_sensor_data() {
 | 
			
		||||
  if (this->read(raw_data_, 7) != i2c::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
    this->status_set_warning("couldn't read sensor data");
 | 
			
		||||
    this->status_set_warning(LOG_STR("couldn't read sensor data"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  float press_counts = encode_uint24(raw_data_[1], raw_data_[2], raw_data_[3]);  // calculate digital pressure counts
 | 
			
		||||
@@ -31,7 +31,7 @@ void HONEYWELLABP2Sensor::read_sensor_data() {
 | 
			
		||||
void HONEYWELLABP2Sensor::start_measurement() {
 | 
			
		||||
  if (this->write(i2c_cmd_, 3) != i2c::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
    this->status_set_warning("couldn't start measurement");
 | 
			
		||||
    this->status_set_warning(LOG_STR("couldn't start measurement"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->measurement_running_ = true;
 | 
			
		||||
@@ -40,7 +40,7 @@ void HONEYWELLABP2Sensor::start_measurement() {
 | 
			
		||||
bool HONEYWELLABP2Sensor::is_measurement_ready() {
 | 
			
		||||
  if (this->read(raw_data_, 1) != i2c::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
    this->status_set_warning("couldn't check measurement");
 | 
			
		||||
    this->status_set_warning(LOG_STR("couldn't check measurement"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if ((raw_data_[0] & (0x1 << STATUS_BIT_BUSY)) > 0) {
 | 
			
		||||
@@ -53,7 +53,7 @@ bool HONEYWELLABP2Sensor::is_measurement_ready() {
 | 
			
		||||
void HONEYWELLABP2Sensor::measurement_timeout() {
 | 
			
		||||
  ESP_LOGE(TAG, "Timeout!");
 | 
			
		||||
  this->measurement_running_ = false;
 | 
			
		||||
  this->status_set_warning("measurement timed out");
 | 
			
		||||
  this->status_set_warning(LOG_STR("measurement timed out"));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float HONEYWELLABP2Sensor::get_pressure() { return this->last_pressure_; }
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ void KMeterISOComponent::setup() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t read_buf[4] = {1};
 | 
			
		||||
  if (!this->read_register(KMETER_ERROR_STATUS_REG, read_buf, 1)) {
 | 
			
		||||
  if (!this->read_bytes(KMETER_ERROR_STATUS_REG, read_buf, 1)) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "Could not read from the device.");
 | 
			
		||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 
 | 
			
		||||
@@ -11,19 +11,21 @@ static const char *const TAG = "light";
 | 
			
		||||
 | 
			
		||||
// Helper functions to reduce code size for logging
 | 
			
		||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_WARN
 | 
			
		||||
static void log_validation_warning(const char *name, const char *param_name, float val, float min, float max) {
 | 
			
		||||
  ESP_LOGW(TAG, "'%s': %s value %.2f is out of range [%.1f - %.1f]", name, param_name, val, min, max);
 | 
			
		||||
static void log_validation_warning(const char *name, const LogString *param_name, float val, float min, float max) {
 | 
			
		||||
  ESP_LOGW(TAG, "'%s': %s value %.2f is out of range [%.1f - %.1f]", name, LOG_STR_ARG(param_name), val, min, max);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void log_feature_not_supported(const char *name, const char *feature) {
 | 
			
		||||
  ESP_LOGW(TAG, "'%s': %s not supported", name, feature);
 | 
			
		||||
static void log_feature_not_supported(const char *name, const LogString *feature) {
 | 
			
		||||
  ESP_LOGW(TAG, "'%s': %s not supported", name, LOG_STR_ARG(feature));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void log_color_mode_not_supported(const char *name, const char *feature) {
 | 
			
		||||
  ESP_LOGW(TAG, "'%s': color mode does not support setting %s", name, feature);
 | 
			
		||||
static void log_color_mode_not_supported(const char *name, const LogString *feature) {
 | 
			
		||||
  ESP_LOGW(TAG, "'%s': color mode does not support setting %s", name, LOG_STR_ARG(feature));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void log_invalid_parameter(const char *name, const char *message) { ESP_LOGW(TAG, "'%s': %s", name, message); }
 | 
			
		||||
static void log_invalid_parameter(const char *name, const LogString *message) {
 | 
			
		||||
  ESP_LOGW(TAG, "'%s': %s", name, LOG_STR_ARG(message));
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
#define log_validation_warning(name, param_name, val, min, max)
 | 
			
		||||
#define log_feature_not_supported(name, feature)
 | 
			
		||||
@@ -201,19 +203,19 @@ LightColorValues LightCall::validate_() {
 | 
			
		||||
 | 
			
		||||
  // Brightness exists check
 | 
			
		||||
  if (this->has_brightness() && this->brightness_ > 0.0f && !(color_mode & ColorCapability::BRIGHTNESS)) {
 | 
			
		||||
    log_feature_not_supported(name, "brightness");
 | 
			
		||||
    log_feature_not_supported(name, LOG_STR("brightness"));
 | 
			
		||||
    this->set_flag_(FLAG_HAS_BRIGHTNESS, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Transition length possible check
 | 
			
		||||
  if (this->has_transition_() && this->transition_length_ != 0 && !(color_mode & ColorCapability::BRIGHTNESS)) {
 | 
			
		||||
    log_feature_not_supported(name, "transitions");
 | 
			
		||||
    log_feature_not_supported(name, LOG_STR("transitions"));
 | 
			
		||||
    this->set_flag_(FLAG_HAS_TRANSITION, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Color brightness exists check
 | 
			
		||||
  if (this->has_color_brightness() && this->color_brightness_ > 0.0f && !(color_mode & ColorCapability::RGB)) {
 | 
			
		||||
    log_color_mode_not_supported(name, "RGB brightness");
 | 
			
		||||
    log_color_mode_not_supported(name, LOG_STR("RGB brightness"));
 | 
			
		||||
    this->set_flag_(FLAG_HAS_COLOR_BRIGHTNESS, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -221,7 +223,7 @@ LightColorValues LightCall::validate_() {
 | 
			
		||||
  if ((this->has_red() && this->red_ > 0.0f) || (this->has_green() && this->green_ > 0.0f) ||
 | 
			
		||||
      (this->has_blue() && this->blue_ > 0.0f)) {
 | 
			
		||||
    if (!(color_mode & ColorCapability::RGB)) {
 | 
			
		||||
      log_color_mode_not_supported(name, "RGB color");
 | 
			
		||||
      log_color_mode_not_supported(name, LOG_STR("RGB color"));
 | 
			
		||||
      this->set_flag_(FLAG_HAS_RED, false);
 | 
			
		||||
      this->set_flag_(FLAG_HAS_GREEN, false);
 | 
			
		||||
      this->set_flag_(FLAG_HAS_BLUE, false);
 | 
			
		||||
@@ -231,21 +233,21 @@ LightColorValues LightCall::validate_() {
 | 
			
		||||
  // White value exists check
 | 
			
		||||
  if (this->has_white() && this->white_ > 0.0f &&
 | 
			
		||||
      !(color_mode & ColorCapability::WHITE || color_mode & ColorCapability::COLD_WARM_WHITE)) {
 | 
			
		||||
    log_color_mode_not_supported(name, "white value");
 | 
			
		||||
    log_color_mode_not_supported(name, LOG_STR("white value"));
 | 
			
		||||
    this->set_flag_(FLAG_HAS_WHITE, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Color temperature exists check
 | 
			
		||||
  if (this->has_color_temperature() &&
 | 
			
		||||
      !(color_mode & ColorCapability::COLOR_TEMPERATURE || color_mode & ColorCapability::COLD_WARM_WHITE)) {
 | 
			
		||||
    log_color_mode_not_supported(name, "color temperature");
 | 
			
		||||
    log_color_mode_not_supported(name, LOG_STR("color temperature"));
 | 
			
		||||
    this->set_flag_(FLAG_HAS_COLOR_TEMPERATURE, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Cold/warm white value exists check
 | 
			
		||||
  if ((this->has_cold_white() && this->cold_white_ > 0.0f) || (this->has_warm_white() && this->warm_white_ > 0.0f)) {
 | 
			
		||||
    if (!(color_mode & ColorCapability::COLD_WARM_WHITE)) {
 | 
			
		||||
      log_color_mode_not_supported(name, "cold/warm white value");
 | 
			
		||||
      log_color_mode_not_supported(name, LOG_STR("cold/warm white value"));
 | 
			
		||||
      this->set_flag_(FLAG_HAS_COLD_WHITE, false);
 | 
			
		||||
      this->set_flag_(FLAG_HAS_WARM_WHITE, false);
 | 
			
		||||
    }
 | 
			
		||||
@@ -255,7 +257,7 @@ LightColorValues LightCall::validate_() {
 | 
			
		||||
  if (this->has_##name_()) { \
 | 
			
		||||
    auto val = this->name_##_; \
 | 
			
		||||
    if (val < (min) || val > (max)) { \
 | 
			
		||||
      log_validation_warning(name, LOG_STR_LITERAL(upper_name), val, (min), (max)); \
 | 
			
		||||
      log_validation_warning(name, LOG_STR(upper_name), val, (min), (max)); \
 | 
			
		||||
      this->name_##_ = clamp(val, (min), (max)); \
 | 
			
		||||
    } \
 | 
			
		||||
  }
 | 
			
		||||
@@ -319,7 +321,7 @@ LightColorValues LightCall::validate_() {
 | 
			
		||||
 | 
			
		||||
  // Flash length check
 | 
			
		||||
  if (this->has_flash_() && this->flash_length_ == 0) {
 | 
			
		||||
    log_invalid_parameter(name, "flash length must be greater than zero");
 | 
			
		||||
    log_invalid_parameter(name, LOG_STR("flash length must be greater than zero"));
 | 
			
		||||
    this->set_flag_(FLAG_HAS_FLASH, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -338,13 +340,13 @@ LightColorValues LightCall::validate_() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->has_effect_() && (this->has_transition_() || this->has_flash_())) {
 | 
			
		||||
    log_invalid_parameter(name, "effect cannot be used with transition/flash");
 | 
			
		||||
    log_invalid_parameter(name, LOG_STR("effect cannot be used with transition/flash"));
 | 
			
		||||
    this->set_flag_(FLAG_HAS_TRANSITION, false);
 | 
			
		||||
    this->set_flag_(FLAG_HAS_FLASH, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->has_flash_() && this->has_transition_()) {
 | 
			
		||||
    log_invalid_parameter(name, "flash cannot be used with transition");
 | 
			
		||||
    log_invalid_parameter(name, LOG_STR("flash cannot be used with transition"));
 | 
			
		||||
    this->set_flag_(FLAG_HAS_TRANSITION, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -361,7 +363,7 @@ LightColorValues LightCall::validate_() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->has_transition_() && !supports_transition) {
 | 
			
		||||
    log_feature_not_supported(name, "transitions");
 | 
			
		||||
    log_feature_not_supported(name, LOG_STR("transitions"));
 | 
			
		||||
    this->set_flag_(FLAG_HAS_TRANSITION, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -371,7 +373,7 @@ LightColorValues LightCall::validate_() {
 | 
			
		||||
  bool target_state = this->has_state() ? this->state_ : v.is_on();
 | 
			
		||||
  if (!this->has_flash_() && !target_state) {
 | 
			
		||||
    if (this->has_effect_()) {
 | 
			
		||||
      log_invalid_parameter(name, "cannot start effect when turning off");
 | 
			
		||||
      log_invalid_parameter(name, LOG_STR("cannot start effect when turning off"));
 | 
			
		||||
      this->set_flag_(FLAG_HAS_EFFECT, false);
 | 
			
		||||
    } else if (this->parent_->active_effect_index_ != 0 && explicit_turn_off_request) {
 | 
			
		||||
      // Auto turn off effect
 | 
			
		||||
 
 | 
			
		||||
@@ -258,7 +258,7 @@ void Logger::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Log Baud Rate: %" PRIu32 "\n"
 | 
			
		||||
                "  Hardware UART: %s",
 | 
			
		||||
                this->baud_rate_, get_uart_selection_());
 | 
			
		||||
                this->baud_rate_, LOG_STR_ARG(get_uart_selection_()));
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
 | 
			
		||||
  if (this->log_buffer_) {
 | 
			
		||||
 
 | 
			
		||||
@@ -226,7 +226,7 @@ class Logger : public Component {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#ifndef USE_HOST
 | 
			
		||||
  const char *get_uart_selection_();
 | 
			
		||||
  const LogString *get_uart_selection_();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // Group 4-byte aligned members first
 | 
			
		||||
 
 | 
			
		||||
@@ -190,20 +190,28 @@ void HOT Logger::write_msg_(const char *msg) {
 | 
			
		||||
void HOT Logger::write_msg_(const char *msg) { this->hw_serial_->println(msg); }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
const char *const UART_SELECTIONS[] = {
 | 
			
		||||
    "UART0",           "UART1",
 | 
			
		||||
const LogString *Logger::get_uart_selection_() {
 | 
			
		||||
  switch (this->uart_) {
 | 
			
		||||
    case UART_SELECTION_UART0:
 | 
			
		||||
      return LOG_STR("UART0");
 | 
			
		||||
    case UART_SELECTION_UART1:
 | 
			
		||||
      return LOG_STR("UART1");
 | 
			
		||||
#ifdef USE_ESP32_VARIANT_ESP32
 | 
			
		||||
    "UART2",
 | 
			
		||||
    case UART_SELECTION_UART2:
 | 
			
		||||
      return LOG_STR("UART2");
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOGGER_USB_CDC
 | 
			
		||||
    "USB_CDC",
 | 
			
		||||
    case UART_SELECTION_USB_CDC:
 | 
			
		||||
      return LOG_STR("USB_CDC");
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOGGER_USB_SERIAL_JTAG
 | 
			
		||||
    "USB_SERIAL_JTAG",
 | 
			
		||||
    case UART_SELECTION_USB_SERIAL_JTAG:
 | 
			
		||||
      return LOG_STR("USB_SERIAL_JTAG");
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char *Logger::get_uart_selection_() { return UART_SELECTIONS[this->uart_]; }
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("UNKNOWN");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace esphome::logger
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -35,9 +35,17 @@ void Logger::pre_setup() {
 | 
			
		||||
 | 
			
		||||
void HOT Logger::write_msg_(const char *msg) { this->hw_serial_->println(msg); }
 | 
			
		||||
 | 
			
		||||
const char *const UART_SELECTIONS[] = {"UART0", "UART1", "UART0_SWAP"};
 | 
			
		||||
 | 
			
		||||
const char *Logger::get_uart_selection_() { return UART_SELECTIONS[this->uart_]; }
 | 
			
		||||
const LogString *Logger::get_uart_selection_() {
 | 
			
		||||
  switch (this->uart_) {
 | 
			
		||||
    case UART_SELECTION_UART0:
 | 
			
		||||
      return LOG_STR("UART0");
 | 
			
		||||
    case UART_SELECTION_UART1:
 | 
			
		||||
      return LOG_STR("UART1");
 | 
			
		||||
    case UART_SELECTION_UART0_SWAP:
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("UART0_SWAP");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace esphome::logger
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -51,9 +51,19 @@ void Logger::pre_setup() {
 | 
			
		||||
 | 
			
		||||
void HOT Logger::write_msg_(const char *msg) { this->hw_serial_->println(msg); }
 | 
			
		||||
 | 
			
		||||
const char *const UART_SELECTIONS[] = {"DEFAULT", "UART0", "UART1", "UART2"};
 | 
			
		||||
 | 
			
		||||
const char *Logger::get_uart_selection_() { return UART_SELECTIONS[this->uart_]; }
 | 
			
		||||
const LogString *Logger::get_uart_selection_() {
 | 
			
		||||
  switch (this->uart_) {
 | 
			
		||||
    case UART_SELECTION_DEFAULT:
 | 
			
		||||
      return LOG_STR("DEFAULT");
 | 
			
		||||
    case UART_SELECTION_UART0:
 | 
			
		||||
      return LOG_STR("UART0");
 | 
			
		||||
    case UART_SELECTION_UART1:
 | 
			
		||||
      return LOG_STR("UART1");
 | 
			
		||||
    case UART_SELECTION_UART2:
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("UART2");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace esphome::logger
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,9 +29,20 @@ void Logger::pre_setup() {
 | 
			
		||||
 | 
			
		||||
void HOT Logger::write_msg_(const char *msg) { this->hw_serial_->println(msg); }
 | 
			
		||||
 | 
			
		||||
const char *const UART_SELECTIONS[] = {"UART0", "UART1", "USB_CDC"};
 | 
			
		||||
 | 
			
		||||
const char *Logger::get_uart_selection_() { return UART_SELECTIONS[this->uart_]; }
 | 
			
		||||
const LogString *Logger::get_uart_selection_() {
 | 
			
		||||
  switch (this->uart_) {
 | 
			
		||||
    case UART_SELECTION_UART0:
 | 
			
		||||
      return LOG_STR("UART0");
 | 
			
		||||
    case UART_SELECTION_UART1:
 | 
			
		||||
      return LOG_STR("UART1");
 | 
			
		||||
#ifdef USE_LOGGER_USB_CDC
 | 
			
		||||
    case UART_SELECTION_USB_CDC:
 | 
			
		||||
      return LOG_STR("USB_CDC");
 | 
			
		||||
#endif
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("UNKNOWN");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace esphome::logger
 | 
			
		||||
#endif  // USE_RP2040
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ void Logger::pre_setup() {
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    if (!device_is_ready(uart_dev)) {
 | 
			
		||||
      ESP_LOGE(TAG, "%s is not ready.", get_uart_selection_());
 | 
			
		||||
      ESP_LOGE(TAG, "%s is not ready.", LOG_STR_ARG(get_uart_selection_()));
 | 
			
		||||
    } else {
 | 
			
		||||
      this->uart_dev_ = uart_dev;
 | 
			
		||||
    }
 | 
			
		||||
@@ -77,9 +77,20 @@ void HOT Logger::write_msg_(const char *msg) {
 | 
			
		||||
  uart_poll_out(this->uart_dev_, '\n');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *const UART_SELECTIONS[] = {"UART0", "UART1", "USB_CDC"};
 | 
			
		||||
 | 
			
		||||
const char *Logger::get_uart_selection_() { return UART_SELECTIONS[this->uart_]; }
 | 
			
		||||
const LogString *Logger::get_uart_selection_() {
 | 
			
		||||
  switch (this->uart_) {
 | 
			
		||||
    case UART_SELECTION_UART0:
 | 
			
		||||
      return LOG_STR("UART0");
 | 
			
		||||
    case UART_SELECTION_UART1:
 | 
			
		||||
      return LOG_STR("UART1");
 | 
			
		||||
#ifdef USE_LOGGER_USB_CDC
 | 
			
		||||
    case UART_SELECTION_USB_CDC:
 | 
			
		||||
      return LOG_STR("USB_CDC");
 | 
			
		||||
#endif
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("UNKNOWN");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace esphome::logger
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace m5stack_8angle {
 | 
			
		||||
void M5Stack8AngleSwitchBinarySensor::update() {
 | 
			
		||||
  int8_t out = this->parent_->read_switch();
 | 
			
		||||
  if (out == -1) {
 | 
			
		||||
    this->status_set_warning("Could not read binary sensor state from M5Stack 8Angle.");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Could not read binary sensor state from M5Stack 8Angle."));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->publish_state(out != 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ void M5Stack8AngleKnobSensor::update() {
 | 
			
		||||
  if (this->parent_ != nullptr) {
 | 
			
		||||
    int32_t raw_pos = this->parent_->read_knob_pos_raw(this->channel_, this->bits_);
 | 
			
		||||
    if (raw_pos == -1) {
 | 
			
		||||
      this->status_set_warning("Could not read knob position from M5Stack 8Angle.");
 | 
			
		||||
      this->status_set_warning(LOG_STR("Could not read knob position from M5Stack 8Angle."));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (this->raw_) {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ void MAX17043Component::update() {
 | 
			
		||||
 | 
			
		||||
  if (this->voltage_sensor_ != nullptr) {
 | 
			
		||||
    if (!this->read_byte_16(MAX17043_VCELL, &raw_voltage)) {
 | 
			
		||||
      this->status_set_warning("Unable to read MAX17043_VCELL");
 | 
			
		||||
      this->status_set_warning(LOG_STR("Unable to read MAX17043_VCELL"));
 | 
			
		||||
    } else {
 | 
			
		||||
      float voltage = (1.25 * (float) (raw_voltage >> 4)) / 1000.0;
 | 
			
		||||
      this->voltage_sensor_->publish_state(voltage);
 | 
			
		||||
@@ -31,7 +31,7 @@ void MAX17043Component::update() {
 | 
			
		||||
  }
 | 
			
		||||
  if (this->battery_remaining_sensor_ != nullptr) {
 | 
			
		||||
    if (!this->read_byte_16(MAX17043_SOC, &raw_percent)) {
 | 
			
		||||
      this->status_set_warning("Unable to read MAX17043_SOC");
 | 
			
		||||
      this->status_set_warning(LOG_STR("Unable to read MAX17043_SOC"));
 | 
			
		||||
    } else {
 | 
			
		||||
      float percent = (float) ((raw_percent >> 8) + 0.003906f * (raw_percent & 0x00ff));
 | 
			
		||||
      this->battery_remaining_sensor_->publish_state(percent);
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ static const char *const TAG = "mcp23x08_base";
 | 
			
		||||
 | 
			
		||||
bool MCP23X08Base::digital_read_hw(uint8_t pin) {
 | 
			
		||||
  if (!this->read_reg(mcp23x08_base::MCP23X08_GPIO, &this->input_mask_)) {
 | 
			
		||||
    this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
    this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,13 +11,13 @@ bool MCP23X17Base::digital_read_hw(uint8_t pin) {
 | 
			
		||||
  uint8_t data;
 | 
			
		||||
  if (pin < 8) {
 | 
			
		||||
    if (!this->read_reg(mcp23x17_base::MCP23X17_GPIOA, &data)) {
 | 
			
		||||
      this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
      this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    this->input_mask_ = encode_uint16(this->input_mask_ >> 8, data);
 | 
			
		||||
  } else {
 | 
			
		||||
    if (!this->read_reg(mcp23x17_base::MCP23X17_GPIOB, &data)) {
 | 
			
		||||
      this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
      this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    this->input_mask_ = encode_uint16(data, this->input_mask_ & 0xFF);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,30 @@
 | 
			
		||||
#include "esphome/core/version.h"
 | 
			
		||||
#include "mdns_component.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
#include <pgmspace.h>
 | 
			
		||||
// Macro to define strings in PROGMEM on ESP8266, regular memory on other platforms
 | 
			
		||||
#define MDNS_STATIC_CONST_CHAR(name, value) static const char name[] PROGMEM = value
 | 
			
		||||
// Helper to get string from PROGMEM - returns a temporary std::string
 | 
			
		||||
// Only define this function if we have services that will use it
 | 
			
		||||
#if defined(USE_API) || defined(USE_PROMETHEUS) || defined(USE_WEBSERVER) || defined(USE_MDNS_EXTRA_SERVICES)
 | 
			
		||||
static std::string mdns_string_p(const char *src) {
 | 
			
		||||
  char buf[64];
 | 
			
		||||
  strncpy_P(buf, src, sizeof(buf) - 1);
 | 
			
		||||
  buf[sizeof(buf) - 1] = '\0';
 | 
			
		||||
  return std::string(buf);
 | 
			
		||||
}
 | 
			
		||||
#define MDNS_STR(name) mdns_string_p(name)
 | 
			
		||||
#else
 | 
			
		||||
// If no services are configured, we still need the fallback service but it uses string literals
 | 
			
		||||
#define MDNS_STR(name) std::string(name)
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
// On non-ESP8266 platforms, use regular const char*
 | 
			
		||||
#define MDNS_STATIC_CONST_CHAR(name, value) static constexpr const char *name = value
 | 
			
		||||
#define MDNS_STR(name) name
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "esphome/components/api/api_server.h"
 | 
			
		||||
#endif
 | 
			
		||||
@@ -21,6 +45,32 @@ static const char *const TAG = "mdns";
 | 
			
		||||
#define USE_WEBSERVER_PORT 80  // NOLINT
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Define all constant strings using the macro
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(SERVICE_ESPHOMELIB, "_esphomelib");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(SERVICE_TCP, "_tcp");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(SERVICE_PROMETHEUS, "_prometheus-http");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(SERVICE_HTTP, "_http");
 | 
			
		||||
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_FRIENDLY_NAME, "friendly_name");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_VERSION, "version");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_MAC, "mac");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_PLATFORM, "platform");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_BOARD, "board");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_NETWORK, "network");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_API_ENCRYPTION, "api_encryption");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_API_ENCRYPTION_SUPPORTED, "api_encryption_supported");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_PROJECT_NAME, "project_name");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_PROJECT_VERSION, "project_version");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(TXT_PACKAGE_IMPORT_URL, "package_import_url");
 | 
			
		||||
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(PLATFORM_ESP8266, "ESP8266");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(PLATFORM_ESP32, "ESP32");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(PLATFORM_RP2040, "RP2040");
 | 
			
		||||
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(NETWORK_WIFI, "wifi");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(NETWORK_ETHERNET, "ethernet");
 | 
			
		||||
MDNS_STATIC_CONST_CHAR(NETWORK_THREAD, "thread");
 | 
			
		||||
 | 
			
		||||
void MDNSComponent::compile_records_() {
 | 
			
		||||
  this->hostname_ = App.get_name();
 | 
			
		||||
 | 
			
		||||
@@ -50,8 +100,8 @@ void MDNSComponent::compile_records_() {
 | 
			
		||||
  if (api::global_api_server != nullptr) {
 | 
			
		||||
    this->services_.emplace_back();
 | 
			
		||||
    auto &service = this->services_.back();
 | 
			
		||||
    service.service_type = "_esphomelib";
 | 
			
		||||
    service.proto = "_tcp";
 | 
			
		||||
    service.service_type = MDNS_STR(SERVICE_ESPHOMELIB);
 | 
			
		||||
    service.proto = MDNS_STR(SERVICE_TCP);
 | 
			
		||||
    service.port = api::global_api_server->get_port();
 | 
			
		||||
 | 
			
		||||
    const std::string &friendly_name = App.get_friendly_name();
 | 
			
		||||
@@ -82,47 +132,47 @@ void MDNSComponent::compile_records_() {
 | 
			
		||||
    txt_records.reserve(txt_count);
 | 
			
		||||
 | 
			
		||||
    if (!friendly_name_empty) {
 | 
			
		||||
      txt_records.emplace_back(MDNSTXTRecord{"friendly_name", friendly_name});
 | 
			
		||||
      txt_records.push_back({MDNS_STR(TXT_FRIENDLY_NAME), friendly_name});
 | 
			
		||||
    }
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"version", ESPHOME_VERSION});
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"mac", get_mac_address()});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_VERSION), ESPHOME_VERSION});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_MAC), get_mac_address()});
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"platform", "ESP8266"});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_ESP8266)});
 | 
			
		||||
#elif defined(USE_ESP32)
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"platform", "ESP32"});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_ESP32)});
 | 
			
		||||
#elif defined(USE_RP2040)
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"platform", "RP2040"});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_RP2040)});
 | 
			
		||||
#elif defined(USE_LIBRETINY)
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"platform", lt_cpu_get_model_name()});
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"board", ESPHOME_BOARD});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_BOARD), ESPHOME_BOARD});
 | 
			
		||||
 | 
			
		||||
#if defined(USE_WIFI)
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"network", "wifi"});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_WIFI)});
 | 
			
		||||
#elif defined(USE_ETHERNET)
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"network", "ethernet"});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_ETHERNET)});
 | 
			
		||||
#elif defined(USE_OPENTHREAD)
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"network", "thread"});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_THREAD)});
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
    static constexpr const char *NOISE_ENCRYPTION = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
 | 
			
		||||
    MDNS_STATIC_CONST_CHAR(NOISE_ENCRYPTION, "Noise_NNpsk0_25519_ChaChaPoly_SHA256");
 | 
			
		||||
    if (api::global_api_server->get_noise_ctx()->has_psk()) {
 | 
			
		||||
      txt_records.emplace_back(MDNSTXTRecord{"api_encryption", NOISE_ENCRYPTION});
 | 
			
		||||
      txt_records.push_back({MDNS_STR(TXT_API_ENCRYPTION), MDNS_STR(NOISE_ENCRYPTION)});
 | 
			
		||||
    } else {
 | 
			
		||||
      txt_records.emplace_back(MDNSTXTRecord{"api_encryption_supported", NOISE_ENCRYPTION});
 | 
			
		||||
      txt_records.push_back({MDNS_STR(TXT_API_ENCRYPTION_SUPPORTED), MDNS_STR(NOISE_ENCRYPTION)});
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef ESPHOME_PROJECT_NAME
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"project_name", ESPHOME_PROJECT_NAME});
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"project_version", ESPHOME_PROJECT_VERSION});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_PROJECT_NAME), ESPHOME_PROJECT_NAME});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_PROJECT_VERSION), ESPHOME_PROJECT_VERSION});
 | 
			
		||||
#endif  // ESPHOME_PROJECT_NAME
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DASHBOARD_IMPORT
 | 
			
		||||
    txt_records.emplace_back(MDNSTXTRecord{"package_import_url", dashboard_import::get_package_import_url()});
 | 
			
		||||
    txt_records.push_back({MDNS_STR(TXT_PACKAGE_IMPORT_URL), dashboard_import::get_package_import_url()});
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
#endif  // USE_API
 | 
			
		||||
@@ -130,16 +180,16 @@ void MDNSComponent::compile_records_() {
 | 
			
		||||
#ifdef USE_PROMETHEUS
 | 
			
		||||
  this->services_.emplace_back();
 | 
			
		||||
  auto &prom_service = this->services_.back();
 | 
			
		||||
  prom_service.service_type = "_prometheus-http";
 | 
			
		||||
  prom_service.proto = "_tcp";
 | 
			
		||||
  prom_service.service_type = MDNS_STR(SERVICE_PROMETHEUS);
 | 
			
		||||
  prom_service.proto = MDNS_STR(SERVICE_TCP);
 | 
			
		||||
  prom_service.port = USE_WEBSERVER_PORT;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_WEBSERVER
 | 
			
		||||
  this->services_.emplace_back();
 | 
			
		||||
  auto &web_service = this->services_.back();
 | 
			
		||||
  web_service.service_type = "_http";
 | 
			
		||||
  web_service.proto = "_tcp";
 | 
			
		||||
  web_service.service_type = MDNS_STR(SERVICE_HTTP);
 | 
			
		||||
  web_service.proto = MDNS_STR(SERVICE_TCP);
 | 
			
		||||
  web_service.port = USE_WEBSERVER_PORT;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -58,8 +58,13 @@ void MQTTSensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon
 | 
			
		||||
  if (this->sensor_->get_force_update())
 | 
			
		||||
    root[MQTT_FORCE_UPDATE] = true;
 | 
			
		||||
 | 
			
		||||
  if (this->sensor_->get_state_class() != STATE_CLASS_NONE)
 | 
			
		||||
    root[MQTT_STATE_CLASS] = state_class_to_string(this->sensor_->get_state_class());
 | 
			
		||||
  if (this->sensor_->get_state_class() != STATE_CLASS_NONE) {
 | 
			
		||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
 | 
			
		||||
    root[MQTT_STATE_CLASS] = (const __FlashStringHelper *) state_class_to_string(this->sensor_->get_state_class());
 | 
			
		||||
#else
 | 
			
		||||
    root[MQTT_STATE_CLASS] = LOG_STR_ARG(state_class_to_string(this->sensor_->get_state_class()));
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  config.command_topic = false;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,7 @@ bool PI4IOE5V6408Component::read_gpio_outputs_() {
 | 
			
		||||
 | 
			
		||||
  uint8_t data;
 | 
			
		||||
  if (!this->read_byte(PI4IOE5V6408_REGISTER_OUT_SET, &data)) {
 | 
			
		||||
    this->status_set_warning("Failed to read output register");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to read output register"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  this->output_mask_ = data;
 | 
			
		||||
@@ -82,7 +82,7 @@ bool PI4IOE5V6408Component::read_gpio_modes_() {
 | 
			
		||||
 | 
			
		||||
  uint8_t data;
 | 
			
		||||
  if (!this->read_byte(PI4IOE5V6408_REGISTER_IO_DIR, &data)) {
 | 
			
		||||
    this->status_set_warning("Failed to read GPIO modes");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to read GPIO modes"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
 | 
			
		||||
@@ -99,7 +99,7 @@ bool PI4IOE5V6408Component::digital_read_hw(uint8_t pin) {
 | 
			
		||||
 | 
			
		||||
  uint8_t data;
 | 
			
		||||
  if (!this->read_byte(PI4IOE5V6408_REGISTER_IN_STATE, &data)) {
 | 
			
		||||
    this->status_set_warning("Failed to read GPIO state");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to read GPIO state"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  this->input_mask_ = data;
 | 
			
		||||
@@ -117,7 +117,7 @@ void PI4IOE5V6408Component::digital_write_hw(uint8_t pin, bool value) {
 | 
			
		||||
    this->output_mask_ &= ~(1 << pin);
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->write_byte(PI4IOE5V6408_REGISTER_OUT_SET, this->output_mask_)) {
 | 
			
		||||
    this->status_set_warning("Failed to write output register");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to write output register"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
 | 
			
		||||
@@ -131,15 +131,15 @@ bool PI4IOE5V6408Component::write_gpio_modes_() {
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (!this->write_byte(PI4IOE5V6408_REGISTER_IO_DIR, this->mode_mask_)) {
 | 
			
		||||
    this->status_set_warning("Failed to write GPIO modes");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to write GPIO modes"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->write_byte(PI4IOE5V6408_REGISTER_PULL_SELECT, this->pull_up_down_mask_)) {
 | 
			
		||||
    this->status_set_warning("Failed to write GPIO pullup/pulldown");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to write GPIO pullup/pulldown"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->write_byte(PI4IOE5V6408_REGISTER_PULL_ENABLE, this->pull_enable_mask_)) {
 | 
			
		||||
    this->status_set_warning("Failed to write GPIO pull enable");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to write GPIO pull enable"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,15 @@ namespace script {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "script";
 | 
			
		||||
 | 
			
		||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
 | 
			
		||||
void ScriptLogger::esp_log_(int level, int line, const __FlashStringHelper *format, const char *param) {
 | 
			
		||||
  esp_log_printf_(level, TAG, line, format, param);
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
void ScriptLogger::esp_log_(int level, int line, const char *format, const char *param) {
 | 
			
		||||
  esp_log_printf_(level, TAG, line, format, param);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace script
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,15 @@ namespace script {
 | 
			
		||||
 | 
			
		||||
class ScriptLogger {
 | 
			
		||||
 protected:
 | 
			
		||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
 | 
			
		||||
  void esp_logw_(int line, const __FlashStringHelper *format, const char *param) {
 | 
			
		||||
    esp_log_(ESPHOME_LOG_LEVEL_WARN, line, format, param);
 | 
			
		||||
  }
 | 
			
		||||
  void esp_logd_(int line, const __FlashStringHelper *format, const char *param) {
 | 
			
		||||
    esp_log_(ESPHOME_LOG_LEVEL_DEBUG, line, format, param);
 | 
			
		||||
  }
 | 
			
		||||
  void esp_log_(int level, int line, const __FlashStringHelper *format, const char *param);
 | 
			
		||||
#else
 | 
			
		||||
  void esp_logw_(int line, const char *format, const char *param) {
 | 
			
		||||
    esp_log_(ESPHOME_LOG_LEVEL_WARN, line, format, param);
 | 
			
		||||
  }
 | 
			
		||||
@@ -17,6 +26,7 @@ class ScriptLogger {
 | 
			
		||||
    esp_log_(ESPHOME_LOG_LEVEL_DEBUG, line, format, param);
 | 
			
		||||
  }
 | 
			
		||||
  void esp_log_(int level, int line, const char *format, const char *param);
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// The abstract base class for all script types.
 | 
			
		||||
@@ -57,7 +67,8 @@ template<typename... Ts> class SingleScript : public Script<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  void execute(Ts... x) override {
 | 
			
		||||
    if (this->is_action_running()) {
 | 
			
		||||
      this->esp_logw_(__LINE__, "Script '%s' is already running! (mode: single)", this->name_.c_str());
 | 
			
		||||
      this->esp_logw_(__LINE__, ESPHOME_LOG_FORMAT("Script '%s' is already running! (mode: single)"),
 | 
			
		||||
                      this->name_.c_str());
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -74,7 +85,7 @@ template<typename... Ts> class RestartScript : public Script<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  void execute(Ts... x) override {
 | 
			
		||||
    if (this->is_action_running()) {
 | 
			
		||||
      this->esp_logd_(__LINE__, "Script '%s' restarting (mode: restart)", this->name_.c_str());
 | 
			
		||||
      this->esp_logd_(__LINE__, ESPHOME_LOG_FORMAT("Script '%s' restarting (mode: restart)"), this->name_.c_str());
 | 
			
		||||
      this->stop_action();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -93,11 +104,13 @@ template<typename... Ts> class QueueingScript : public Script<Ts...>, public Com
 | 
			
		||||
      // num_runs_ is the number of *queued* instances, so total number of instances is
 | 
			
		||||
      // num_runs_ + 1
 | 
			
		||||
      if (this->max_runs_ != 0 && this->num_runs_ + 1 >= this->max_runs_) {
 | 
			
		||||
        this->esp_logw_(__LINE__, "Script '%s' maximum number of queued runs exceeded!", this->name_.c_str());
 | 
			
		||||
        this->esp_logw_(__LINE__, ESPHOME_LOG_FORMAT("Script '%s' maximum number of queued runs exceeded!"),
 | 
			
		||||
                        this->name_.c_str());
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this->esp_logd_(__LINE__, "Script '%s' queueing new instance (mode: queued)", this->name_.c_str());
 | 
			
		||||
      this->esp_logd_(__LINE__, ESPHOME_LOG_FORMAT("Script '%s' queueing new instance (mode: queued)"),
 | 
			
		||||
                      this->name_.c_str());
 | 
			
		||||
      this->num_runs_++;
 | 
			
		||||
      this->var_queue_.push(std::make_tuple(x...));
 | 
			
		||||
      return;
 | 
			
		||||
@@ -143,7 +156,8 @@ template<typename... Ts> class ParallelScript : public Script<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  void execute(Ts... x) override {
 | 
			
		||||
    if (this->max_runs_ != 0 && this->automation_parent_->num_running() >= this->max_runs_) {
 | 
			
		||||
      this->esp_logw_(__LINE__, "Script '%s' maximum number of parallel runs exceeded!", this->name_.c_str());
 | 
			
		||||
      this->esp_logw_(__LINE__, ESPHOME_LOG_FORMAT("Script '%s' maximum number of parallel runs exceeded!"),
 | 
			
		||||
                      this->name_.c_str());
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this->trigger(x...);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,19 @@ static const int8_t SEN5X_INDEX_SCALE_FACTOR = 10;                            //
 | 
			
		||||
static const int8_t SEN5X_MIN_INDEX_VALUE = 1 * SEN5X_INDEX_SCALE_FACTOR;     // must be adjusted by the scale factor
 | 
			
		||||
static const int16_t SEN5X_MAX_INDEX_VALUE = 500 * SEN5X_INDEX_SCALE_FACTOR;  // must be adjusted by the scale factor
 | 
			
		||||
 | 
			
		||||
static const LogString *rht_accel_mode_to_string(RhtAccelerationMode mode) {
 | 
			
		||||
  switch (mode) {
 | 
			
		||||
    case LOW_ACCELERATION:
 | 
			
		||||
      return LOG_STR("LOW");
 | 
			
		||||
    case MEDIUM_ACCELERATION:
 | 
			
		||||
      return LOG_STR("MEDIUM");
 | 
			
		||||
    case HIGH_ACCELERATION:
 | 
			
		||||
      return LOG_STR("HIGH");
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("UNKNOWN");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SEN5XComponent::setup() {
 | 
			
		||||
  // the sensor needs 1000 ms to enter the idle state
 | 
			
		||||
  this->set_timeout(1000, [this]() {
 | 
			
		||||
@@ -50,7 +63,7 @@ void SEN5XComponent::setup() {
 | 
			
		||||
    uint32_t stop_measurement_delay = 0;
 | 
			
		||||
    // In order to query the device periodic measurement must be ceased
 | 
			
		||||
    if (raw_read_status) {
 | 
			
		||||
      ESP_LOGD(TAG, "Sensor has data available, stopping periodic measurement");
 | 
			
		||||
      ESP_LOGD(TAG, "Data is available; stopping periodic measurement");
 | 
			
		||||
      if (!this->write_command(SEN5X_CMD_STOP_MEASUREMENTS)) {
 | 
			
		||||
        ESP_LOGE(TAG, "Failed to stop measurements");
 | 
			
		||||
        this->mark_failed();
 | 
			
		||||
@@ -71,7 +84,8 @@ void SEN5XComponent::setup() {
 | 
			
		||||
      this->serial_number_[0] = static_cast<bool>(uint16_t(raw_serial_number[0]) & 0xFF);
 | 
			
		||||
      this->serial_number_[1] = static_cast<uint16_t>(raw_serial_number[0] & 0xFF);
 | 
			
		||||
      this->serial_number_[2] = static_cast<uint16_t>(raw_serial_number[1] >> 8);
 | 
			
		||||
      ESP_LOGD(TAG, "Serial number %02d.%02d.%02d", serial_number_[0], serial_number_[1], serial_number_[2]);
 | 
			
		||||
      ESP_LOGV(TAG, "Serial number %02d.%02d.%02d", this->serial_number_[0], this->serial_number_[1],
 | 
			
		||||
               this->serial_number_[2]);
 | 
			
		||||
 | 
			
		||||
      uint16_t raw_product_name[16];
 | 
			
		||||
      if (!this->get_register(SEN5X_CMD_GET_PRODUCT_NAME, raw_product_name, 16, 20)) {
 | 
			
		||||
@@ -88,45 +102,43 @@ void SEN5XComponent::setup() {
 | 
			
		||||
        // first char
 | 
			
		||||
        current_char = *current_int >> 8;
 | 
			
		||||
        if (current_char) {
 | 
			
		||||
          product_name_.push_back(current_char);
 | 
			
		||||
          this->product_name_.push_back(current_char);
 | 
			
		||||
          // second char
 | 
			
		||||
          current_char = *current_int & 0xFF;
 | 
			
		||||
          if (current_char) {
 | 
			
		||||
            product_name_.push_back(current_char);
 | 
			
		||||
            this->product_name_.push_back(current_char);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        current_int++;
 | 
			
		||||
      } while (current_char && --max);
 | 
			
		||||
 | 
			
		||||
      Sen5xType sen5x_type = UNKNOWN;
 | 
			
		||||
      if (product_name_ == "SEN50") {
 | 
			
		||||
      if (this->product_name_ == "SEN50") {
 | 
			
		||||
        sen5x_type = SEN50;
 | 
			
		||||
      } else {
 | 
			
		||||
        if (product_name_ == "SEN54") {
 | 
			
		||||
        if (this->product_name_ == "SEN54") {
 | 
			
		||||
          sen5x_type = SEN54;
 | 
			
		||||
        } else {
 | 
			
		||||
          if (product_name_ == "SEN55") {
 | 
			
		||||
          if (this->product_name_ == "SEN55") {
 | 
			
		||||
            sen5x_type = SEN55;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        ESP_LOGD(TAG, "Productname %s", product_name_.c_str());
 | 
			
		||||
        ESP_LOGD(TAG, "Product name: %s", this->product_name_.c_str());
 | 
			
		||||
      }
 | 
			
		||||
      if (this->humidity_sensor_ && sen5x_type == SEN50) {
 | 
			
		||||
        ESP_LOGE(TAG, "For Relative humidity a SEN54 OR SEN55 is required. You are using a <%s> sensor",
 | 
			
		||||
                 this->product_name_.c_str());
 | 
			
		||||
        ESP_LOGE(TAG, "Relative humidity requires a SEN54 or SEN55");
 | 
			
		||||
        this->humidity_sensor_ = nullptr;  // mark as not used
 | 
			
		||||
      }
 | 
			
		||||
      if (this->temperature_sensor_ && sen5x_type == SEN50) {
 | 
			
		||||
        ESP_LOGE(TAG, "For Temperature a SEN54 OR SEN55 is required. You are using a <%s> sensor",
 | 
			
		||||
                 this->product_name_.c_str());
 | 
			
		||||
        ESP_LOGE(TAG, "Temperature requires a SEN54 or SEN55");
 | 
			
		||||
        this->temperature_sensor_ = nullptr;  // mark as not used
 | 
			
		||||
      }
 | 
			
		||||
      if (this->voc_sensor_ && sen5x_type == SEN50) {
 | 
			
		||||
        ESP_LOGE(TAG, "For VOC a SEN54 OR SEN55 is required. You are using a <%s> sensor", this->product_name_.c_str());
 | 
			
		||||
        ESP_LOGE(TAG, "VOC requires a SEN54 or SEN55");
 | 
			
		||||
        this->voc_sensor_ = nullptr;  // mark as not used
 | 
			
		||||
      }
 | 
			
		||||
      if (this->nox_sensor_ && sen5x_type != SEN55) {
 | 
			
		||||
        ESP_LOGE(TAG, "For NOx a SEN55 is required. You are using a <%s> sensor", this->product_name_.c_str());
 | 
			
		||||
        ESP_LOGE(TAG, "NOx requires a SEN55");
 | 
			
		||||
        this->nox_sensor_ = nullptr;  // mark as not used
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@@ -137,7 +149,7 @@ void SEN5XComponent::setup() {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      this->firmware_version_ >>= 8;
 | 
			
		||||
      ESP_LOGD(TAG, "Firmware version %d", this->firmware_version_);
 | 
			
		||||
      ESP_LOGV(TAG, "Firmware version %d", this->firmware_version_);
 | 
			
		||||
 | 
			
		||||
      if (this->voc_sensor_ && this->store_baseline_) {
 | 
			
		||||
        uint32_t combined_serial =
 | 
			
		||||
@@ -150,7 +162,7 @@ void SEN5XComponent::setup() {
 | 
			
		||||
 | 
			
		||||
        if (this->pref_.load(&this->voc_baselines_storage_)) {
 | 
			
		||||
          ESP_LOGI(TAG, "Loaded VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32,
 | 
			
		||||
                   this->voc_baselines_storage_.state0, voc_baselines_storage_.state1);
 | 
			
		||||
                   this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Initialize storage timestamp
 | 
			
		||||
@@ -158,13 +170,13 @@ void SEN5XComponent::setup() {
 | 
			
		||||
 | 
			
		||||
        if (this->voc_baselines_storage_.state0 > 0 && this->voc_baselines_storage_.state1 > 0) {
 | 
			
		||||
          ESP_LOGI(TAG, "Setting VOC baseline from save state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32,
 | 
			
		||||
                   this->voc_baselines_storage_.state0, voc_baselines_storage_.state1);
 | 
			
		||||
                   this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1);
 | 
			
		||||
          uint16_t states[4];
 | 
			
		||||
 | 
			
		||||
          states[0] = voc_baselines_storage_.state0 >> 16;
 | 
			
		||||
          states[1] = voc_baselines_storage_.state0 & 0xFFFF;
 | 
			
		||||
          states[2] = voc_baselines_storage_.state1 >> 16;
 | 
			
		||||
          states[3] = voc_baselines_storage_.state1 & 0xFFFF;
 | 
			
		||||
          states[0] = this->voc_baselines_storage_.state0 >> 16;
 | 
			
		||||
          states[1] = this->voc_baselines_storage_.state0 & 0xFFFF;
 | 
			
		||||
          states[2] = this->voc_baselines_storage_.state1 >> 16;
 | 
			
		||||
          states[3] = this->voc_baselines_storage_.state1 & 0xFFFF;
 | 
			
		||||
 | 
			
		||||
          if (!this->write_command(SEN5X_CMD_VOC_ALGORITHM_STATE, states, 4)) {
 | 
			
		||||
            ESP_LOGE(TAG, "Failed to set VOC baseline from saved state");
 | 
			
		||||
@@ -182,11 +194,11 @@ void SEN5XComponent::setup() {
 | 
			
		||||
        delay(20);
 | 
			
		||||
        uint16_t secs[2];
 | 
			
		||||
        if (this->read_data(secs, 2)) {
 | 
			
		||||
          auto_cleaning_interval_ = secs[0] << 16 | secs[1];
 | 
			
		||||
          this->auto_cleaning_interval_ = secs[0] << 16 | secs[1];
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (acceleration_mode_.has_value()) {
 | 
			
		||||
        result = this->write_command(SEN5X_CMD_RHT_ACCELERATION_MODE, acceleration_mode_.value());
 | 
			
		||||
      if (this->acceleration_mode_.has_value()) {
 | 
			
		||||
        result = this->write_command(SEN5X_CMD_RHT_ACCELERATION_MODE, this->acceleration_mode_.value());
 | 
			
		||||
      } else {
 | 
			
		||||
        result = this->write_command(SEN5X_CMD_RHT_ACCELERATION_MODE);
 | 
			
		||||
      }
 | 
			
		||||
@@ -197,7 +209,7 @@ void SEN5XComponent::setup() {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      delay(20);
 | 
			
		||||
      if (!acceleration_mode_.has_value()) {
 | 
			
		||||
      if (!this->acceleration_mode_.has_value()) {
 | 
			
		||||
        uint16_t mode;
 | 
			
		||||
        if (this->read_data(mode)) {
 | 
			
		||||
          this->acceleration_mode_ = RhtAccelerationMode(mode);
 | 
			
		||||
@@ -227,19 +239,18 @@ void SEN5XComponent::setup() {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!this->write_command(cmd)) {
 | 
			
		||||
        ESP_LOGE(TAG, "Error starting continuous measurements.");
 | 
			
		||||
        ESP_LOGE(TAG, "Error starting continuous measurements");
 | 
			
		||||
        this->error_code_ = MEASUREMENT_INIT_FAILED;
 | 
			
		||||
        this->mark_failed();
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      initialized_ = true;
 | 
			
		||||
      ESP_LOGD(TAG, "Sensor initialized");
 | 
			
		||||
      this->initialized_ = true;
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SEN5XComponent::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "sen5x:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "SEN5X:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    switch (this->error_code_) {
 | 
			
		||||
@@ -247,16 +258,16 @@ void SEN5XComponent::dump_config() {
 | 
			
		||||
        ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
        break;
 | 
			
		||||
      case MEASUREMENT_INIT_FAILED:
 | 
			
		||||
        ESP_LOGW(TAG, "Measurement Initialization failed");
 | 
			
		||||
        ESP_LOGW(TAG, "Measurement initialization failed");
 | 
			
		||||
        break;
 | 
			
		||||
      case SERIAL_NUMBER_IDENTIFICATION_FAILED:
 | 
			
		||||
        ESP_LOGW(TAG, "Unable to read sensor serial id");
 | 
			
		||||
        ESP_LOGW(TAG, "Unable to read serial ID");
 | 
			
		||||
        break;
 | 
			
		||||
      case PRODUCT_NAME_FAILED:
 | 
			
		||||
        ESP_LOGW(TAG, "Unable to read product name");
 | 
			
		||||
        break;
 | 
			
		||||
      case FIRMWARE_FAILED:
 | 
			
		||||
        ESP_LOGW(TAG, "Unable to read sensor firmware version");
 | 
			
		||||
        ESP_LOGW(TAG, "Unable to read firmware version");
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        ESP_LOGW(TAG, "Unknown setup error");
 | 
			
		||||
@@ -264,26 +275,17 @@ void SEN5XComponent::dump_config() {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Productname: %s\n"
 | 
			
		||||
                "  Product name: %s\n"
 | 
			
		||||
                "  Firmware version: %d\n"
 | 
			
		||||
                "  Serial number %02d.%02d.%02d",
 | 
			
		||||
                this->product_name_.c_str(), this->firmware_version_, serial_number_[0], serial_number_[1],
 | 
			
		||||
                serial_number_[2]);
 | 
			
		||||
                this->product_name_.c_str(), this->firmware_version_, this->serial_number_[0], this->serial_number_[1],
 | 
			
		||||
                this->serial_number_[2]);
 | 
			
		||||
  if (this->auto_cleaning_interval_.has_value()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Auto cleaning interval %" PRId32 " seconds", auto_cleaning_interval_.value());
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Auto cleaning interval: %" PRId32 "s", this->auto_cleaning_interval_.value());
 | 
			
		||||
  }
 | 
			
		||||
  if (this->acceleration_mode_.has_value()) {
 | 
			
		||||
    switch (this->acceleration_mode_.value()) {
 | 
			
		||||
      case LOW_ACCELERATION:
 | 
			
		||||
        ESP_LOGCONFIG(TAG, "  Low RH/T acceleration mode");
 | 
			
		||||
        break;
 | 
			
		||||
      case MEDIUM_ACCELERATION:
 | 
			
		||||
        ESP_LOGCONFIG(TAG, "  Medium RH/T acceleration mode");
 | 
			
		||||
        break;
 | 
			
		||||
      case HIGH_ACCELERATION:
 | 
			
		||||
        ESP_LOGCONFIG(TAG, "  High RH/T acceleration mode");
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  RH/T acceleration mode: %s",
 | 
			
		||||
                  LOG_STR_ARG(rht_accel_mode_to_string(this->acceleration_mode_.value())));
 | 
			
		||||
  }
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
  LOG_SENSOR("  ", "PM  1.0", this->pm_1_0_sensor_);
 | 
			
		||||
@@ -297,7 +299,7 @@ void SEN5XComponent::dump_config() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SEN5XComponent::update() {
 | 
			
		||||
  if (!initialized_) {
 | 
			
		||||
  if (!this->initialized_) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -320,8 +322,8 @@ void SEN5XComponent::update() {
 | 
			
		||||
            this->voc_baselines_storage_.state1 = state1;
 | 
			
		||||
 | 
			
		||||
            if (this->pref_.save(&this->voc_baselines_storage_)) {
 | 
			
		||||
              ESP_LOGI(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 " ,state1: 0x%04" PRIX32,
 | 
			
		||||
                       this->voc_baselines_storage_.state0, voc_baselines_storage_.state1);
 | 
			
		||||
              ESP_LOGI(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32,
 | 
			
		||||
                       this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1);
 | 
			
		||||
            } else {
 | 
			
		||||
              ESP_LOGW(TAG, "Could not store VOC baselines");
 | 
			
		||||
            }
 | 
			
		||||
@@ -333,7 +335,7 @@ void SEN5XComponent::update() {
 | 
			
		||||
 | 
			
		||||
  if (!this->write_command(SEN5X_CMD_READ_MEASUREMENT)) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    ESP_LOGD(TAG, "write error read measurement (%d)", this->last_error_);
 | 
			
		||||
    ESP_LOGD(TAG, "Write error: read measurement (%d)", this->last_error_);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->set_timeout(20, [this]() {
 | 
			
		||||
@@ -341,7 +343,7 @@ void SEN5XComponent::update() {
 | 
			
		||||
 | 
			
		||||
    if (!this->read_data(measurements, 8)) {
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
      ESP_LOGD(TAG, "read data error (%d)", this->last_error_);
 | 
			
		||||
      ESP_LOGD(TAG, "Read data error (%d)", this->last_error_);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -413,7 +415,7 @@ bool SEN5XComponent::write_tuning_parameters_(uint16_t i2c_command, const GasTun
 | 
			
		||||
  params[5] = tuning.gain_factor;
 | 
			
		||||
  auto result = write_command(i2c_command, params, 6);
 | 
			
		||||
  if (!result) {
 | 
			
		||||
    ESP_LOGE(TAG, "set tuning parameters failed. i2c command=%0xX, err=%d", i2c_command, this->last_error_);
 | 
			
		||||
    ESP_LOGE(TAG, "Set tuning parameters failed (command=%0xX, err=%d)", i2c_command, this->last_error_);
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
@@ -424,7 +426,7 @@ bool SEN5XComponent::write_temperature_compensation_(const TemperatureCompensati
 | 
			
		||||
  params[1] = compensation.normalized_offset_slope;
 | 
			
		||||
  params[2] = compensation.time_constant;
 | 
			
		||||
  if (!write_command(SEN5X_CMD_TEMPERATURE_COMPENSATION, params, 3)) {
 | 
			
		||||
    ESP_LOGE(TAG, "set temperature_compensation failed. Err=%d", this->last_error_);
 | 
			
		||||
    ESP_LOGE(TAG, "Set temperature_compensation failed (%d)", this->last_error_);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
@@ -433,7 +435,7 @@ bool SEN5XComponent::write_temperature_compensation_(const TemperatureCompensati
 | 
			
		||||
bool SEN5XComponent::start_fan_cleaning() {
 | 
			
		||||
  if (!write_command(SEN5X_CMD_START_CLEANING_FAN)) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    ESP_LOGE(TAG, "write error start fan (%d)", this->last_error_);
 | 
			
		||||
    ESP_LOGE(TAG, "Start fan cleaning failed (%d)", this->last_error_);
 | 
			
		||||
    return false;
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGD(TAG, "Fan auto clean started");
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace sen5x {
 | 
			
		||||
 | 
			
		||||
enum ERRORCODE {
 | 
			
		||||
enum ERRORCODE : uint8_t {
 | 
			
		||||
  COMMUNICATION_FAILED,
 | 
			
		||||
  SERIAL_NUMBER_IDENTIFICATION_FAILED,
 | 
			
		||||
  MEASUREMENT_INIT_FAILED,
 | 
			
		||||
@@ -18,19 +18,17 @@ enum ERRORCODE {
 | 
			
		||||
  UNKNOWN
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Shortest time interval of 3H for storing baseline values.
 | 
			
		||||
// Prevents wear of the flash because of too many write operations
 | 
			
		||||
const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 10800;
 | 
			
		||||
// Store anyway if the baseline difference exceeds the max storage diff value
 | 
			
		||||
const uint32_t MAXIMUM_STORAGE_DIFF = 50;
 | 
			
		||||
enum RhtAccelerationMode : uint16_t {
 | 
			
		||||
  LOW_ACCELERATION = 0,
 | 
			
		||||
  MEDIUM_ACCELERATION = 1,
 | 
			
		||||
  HIGH_ACCELERATION = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Sen5xBaselines {
 | 
			
		||||
  int32_t state0;
 | 
			
		||||
  int32_t state1;
 | 
			
		||||
} PACKED;  // NOLINT
 | 
			
		||||
 | 
			
		||||
enum RhtAccelerationMode : uint16_t { LOW_ACCELERATION = 0, MEDIUM_ACCELERATION = 1, HIGH_ACCELERATION = 2 };
 | 
			
		||||
 | 
			
		||||
struct GasTuning {
 | 
			
		||||
  uint16_t index_offset;
 | 
			
		||||
  uint16_t learning_time_offset_hours;
 | 
			
		||||
@@ -46,6 +44,12 @@ struct TemperatureCompensation {
 | 
			
		||||
  uint16_t time_constant;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Shortest time interval of 3H for storing baseline values.
 | 
			
		||||
// Prevents wear of the flash because of too many write operations
 | 
			
		||||
static const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 10800;
 | 
			
		||||
// Store anyway if the baseline difference exceeds the max storage diff value
 | 
			
		||||
static const uint32_t MAXIMUM_STORAGE_DIFF = 50;
 | 
			
		||||
 | 
			
		||||
class SEN5XComponent : public PollingComponent, public sensirion_common::SensirionI2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
@@ -102,8 +106,14 @@ class SEN5XComponent : public PollingComponent, public sensirion_common::Sensiri
 | 
			
		||||
 protected:
 | 
			
		||||
  bool write_tuning_parameters_(uint16_t i2c_command, const GasTuning &tuning);
 | 
			
		||||
  bool write_temperature_compensation_(const TemperatureCompensation &compensation);
 | 
			
		||||
 | 
			
		||||
  uint32_t seconds_since_last_store_;
 | 
			
		||||
  uint16_t firmware_version_;
 | 
			
		||||
  ERRORCODE error_code_;
 | 
			
		||||
  uint8_t serial_number_[4];
 | 
			
		||||
  bool initialized_{false};
 | 
			
		||||
  bool store_baseline_;
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *pm_1_0_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *pm_2_5_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *pm_4_0_sensor_{nullptr};
 | 
			
		||||
@@ -115,18 +125,14 @@ class SEN5XComponent : public PollingComponent, public sensirion_common::Sensiri
 | 
			
		||||
  // SEN55 only
 | 
			
		||||
  sensor::Sensor *nox_sensor_{nullptr};
 | 
			
		||||
 | 
			
		||||
  std::string product_name_;
 | 
			
		||||
  uint8_t serial_number_[4];
 | 
			
		||||
  uint16_t firmware_version_;
 | 
			
		||||
  Sen5xBaselines voc_baselines_storage_;
 | 
			
		||||
  bool store_baseline_;
 | 
			
		||||
  uint32_t seconds_since_last_store_;
 | 
			
		||||
  ESPPreferenceObject pref_;
 | 
			
		||||
  optional<RhtAccelerationMode> acceleration_mode_;
 | 
			
		||||
  optional<uint32_t> auto_cleaning_interval_;
 | 
			
		||||
  optional<GasTuning> voc_tuning_params_;
 | 
			
		||||
  optional<GasTuning> nox_tuning_params_;
 | 
			
		||||
  optional<TemperatureCompensation> temperature_compensation_;
 | 
			
		||||
  ESPPreferenceObject pref_;
 | 
			
		||||
  std::string product_name_;
 | 
			
		||||
  Sen5xBaselines voc_baselines_storage_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace sen5x
 | 
			
		||||
 
 | 
			
		||||
@@ -11,21 +11,22 @@ static const char *const TAG = "sensirion_i2c";
 | 
			
		||||
// To avoid memory allocations for small writes a stack buffer is used
 | 
			
		||||
static const size_t BUFFER_STACK_SIZE = 16;
 | 
			
		||||
 | 
			
		||||
bool SensirionI2CDevice::read_data(uint16_t *data, uint8_t len) {
 | 
			
		||||
bool SensirionI2CDevice::read_data(uint16_t *data, const uint8_t len) {
 | 
			
		||||
  const uint8_t num_bytes = len * 3;
 | 
			
		||||
  std::vector<uint8_t> buf(num_bytes);
 | 
			
		||||
  uint8_t buf[num_bytes];
 | 
			
		||||
 | 
			
		||||
  last_error_ = this->read(buf.data(), num_bytes);
 | 
			
		||||
  if (last_error_ != i2c::ERROR_OK) {
 | 
			
		||||
  this->last_error_ = this->read(buf, num_bytes);
 | 
			
		||||
  if (this->last_error_ != i2c::ERROR_OK) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (uint8_t i = 0; i < len; i++) {
 | 
			
		||||
    const uint8_t j = 3 * i;
 | 
			
		||||
    uint8_t crc = sht_crc_(buf[j], buf[j + 1]);
 | 
			
		||||
    // Use MSB first since Sensirion devices use CRC-8 with MSB first
 | 
			
		||||
    uint8_t crc = crc8(&buf[j], 2, 0xFF, CRC_POLYNOMIAL, true);
 | 
			
		||||
    if (crc != buf[j + 2]) {
 | 
			
		||||
      ESP_LOGE(TAG, "CRC8 Checksum invalid at pos %d! 0x%02X != 0x%02X", i, buf[j + 2], crc);
 | 
			
		||||
      last_error_ = i2c::ERROR_CRC;
 | 
			
		||||
      ESP_LOGE(TAG, "CRC invalid @ %d! 0x%02X != 0x%02X", i, buf[j + 2], crc);
 | 
			
		||||
      this->last_error_ = i2c::ERROR_CRC;
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    data[i] = encode_uint16(buf[j], buf[j + 1]);
 | 
			
		||||
@@ -34,10 +35,10 @@ bool SensirionI2CDevice::read_data(uint16_t *data, uint8_t len) {
 | 
			
		||||
}
 | 
			
		||||
/***
 | 
			
		||||
 * write command with parameters and insert crc
 | 
			
		||||
 * use stack array for less than 4 parameters. Most sensirion i2c commands have less parameters
 | 
			
		||||
 * use stack array for less than 4 parameters. Most Sensirion I2C commands have less parameters
 | 
			
		||||
 */
 | 
			
		||||
bool SensirionI2CDevice::write_command_(uint16_t command, CommandLen command_len, const uint16_t *data,
 | 
			
		||||
                                        uint8_t data_len) {
 | 
			
		||||
                                        const uint8_t data_len) {
 | 
			
		||||
  uint8_t temp_stack[BUFFER_STACK_SIZE];
 | 
			
		||||
  std::unique_ptr<uint8_t[]> temp_heap;
 | 
			
		||||
  uint8_t *temp;
 | 
			
		||||
@@ -74,56 +75,26 @@ bool SensirionI2CDevice::write_command_(uint16_t command, CommandLen command_len
 | 
			
		||||
    temp[raw_idx++] = data[i] & 0xFF;
 | 
			
		||||
    temp[raw_idx++] = data[i] >> 8;
 | 
			
		||||
#endif
 | 
			
		||||
    temp[raw_idx++] = sht_crc_(data[i]);
 | 
			
		||||
    // Use MSB first since Sensirion devices use CRC-8 with MSB first
 | 
			
		||||
    temp[raw_idx++] = crc8(&temp[raw_idx - 2], 2, 0xFF, CRC_POLYNOMIAL, true);
 | 
			
		||||
  }
 | 
			
		||||
  last_error_ = this->write(temp, raw_idx);
 | 
			
		||||
  return last_error_ == i2c::ERROR_OK;
 | 
			
		||||
  this->last_error_ = this->write(temp, raw_idx);
 | 
			
		||||
  return this->last_error_ == i2c::ERROR_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool SensirionI2CDevice::get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, uint8_t len,
 | 
			
		||||
                                       uint8_t delay_ms) {
 | 
			
		||||
bool SensirionI2CDevice::get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, const uint8_t len,
 | 
			
		||||
                                       const uint8_t delay_ms) {
 | 
			
		||||
  if (!this->write_command_(reg, command_len, nullptr, 0)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to write i2c register=0x%X (%d) err=%d,", reg, command_len, this->last_error_);
 | 
			
		||||
    ESP_LOGE(TAG, "Write failed: reg=0x%X (%d) err=%d,", reg, command_len, this->last_error_);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  delay(delay_ms);
 | 
			
		||||
  bool result = this->read_data(data, len);
 | 
			
		||||
  if (!result) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to read data from register=0x%X err=%d,", reg, this->last_error_);
 | 
			
		||||
    ESP_LOGE(TAG, "Read failed: reg=0x%X err=%d,", reg, this->last_error_);
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The 8-bit CRC checksum is transmitted after each data word
 | 
			
		||||
uint8_t SensirionI2CDevice::sht_crc_(uint16_t data) {
 | 
			
		||||
  uint8_t bit;
 | 
			
		||||
  uint8_t crc = 0xFF;
 | 
			
		||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 | 
			
		||||
  crc ^= data >> 8;
 | 
			
		||||
#else
 | 
			
		||||
  crc ^= data & 0xFF;
 | 
			
		||||
#endif
 | 
			
		||||
  for (bit = 8; bit > 0; --bit) {
 | 
			
		||||
    if (crc & 0x80) {
 | 
			
		||||
      crc = (crc << 1) ^ crc_polynomial_;
 | 
			
		||||
    } else {
 | 
			
		||||
      crc = (crc << 1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 | 
			
		||||
  crc ^= data & 0xFF;
 | 
			
		||||
#else
 | 
			
		||||
  crc ^= data >> 8;
 | 
			
		||||
#endif
 | 
			
		||||
  for (bit = 8; bit > 0; --bit) {
 | 
			
		||||
    if (crc & 0x80) {
 | 
			
		||||
      crc = (crc << 1) ^ crc_polynomial_;
 | 
			
		||||
    } else {
 | 
			
		||||
      crc = (crc << 1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return crc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace sensirion_common
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -8,10 +8,10 @@ namespace esphome {
 | 
			
		||||
namespace sensirion_common {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implementation of a i2c functions for Sensirion sensors
 | 
			
		||||
 * Sensirion data requires crc checking.
 | 
			
		||||
 * Implementation of I2C functions for Sensirion sensors
 | 
			
		||||
 * Sensirion data requires CRC checking.
 | 
			
		||||
 * Each 16 bit word is/must be followed 8 bit CRC code
 | 
			
		||||
 * (Applies to read and write - note the i2c command code doesn't need a CRC)
 | 
			
		||||
 * (Applies to read and write - note the I2C command code doesn't need a CRC)
 | 
			
		||||
 * Format:
 | 
			
		||||
 *   | 16 Bit Command Code | 16 bit Data word 1 | CRC of DW 1 | 16 bit Data word 1 | CRC of DW 2 | ..
 | 
			
		||||
 */
 | 
			
		||||
@@ -21,79 +21,79 @@ class SensirionI2CDevice : public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  enum CommandLen : uint8_t { ADDR_8_BIT = 1, ADDR_16_BIT = 2 };
 | 
			
		||||
 | 
			
		||||
  /** Read data words from i2c device.
 | 
			
		||||
   * handles crc check used by Sensirion sensors
 | 
			
		||||
  /** Read data words from I2C device.
 | 
			
		||||
   * handles CRC check used by Sensirion sensors
 | 
			
		||||
   * @param data pointer to raw result
 | 
			
		||||
   * @param len number of words to read
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  bool read_data(uint16_t *data, uint8_t len);
 | 
			
		||||
 | 
			
		||||
  /** Read 1 data word from i2c device.
 | 
			
		||||
  /** Read 1 data word from I2C device.
 | 
			
		||||
   * @param data reference to raw result
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  bool read_data(uint16_t &data) { return this->read_data(&data, 1); }
 | 
			
		||||
 | 
			
		||||
  /** get data words from i2c register.
 | 
			
		||||
   * handles crc check used by Sensirion sensors
 | 
			
		||||
   * @param  i2c register
 | 
			
		||||
  /** get data words from I2C register.
 | 
			
		||||
   * handles CRC check used by Sensirion sensors
 | 
			
		||||
   * @param  I2C register
 | 
			
		||||
   * @param data pointer to raw result
 | 
			
		||||
   * @param len number of words to read
 | 
			
		||||
   * @param delay milliseconds to to wait between sending the i2c command and reading the result
 | 
			
		||||
   * @param delay milliseconds to to wait between sending the I2C command and reading the result
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  bool get_register(uint16_t command, uint16_t *data, uint8_t len, uint8_t delay = 0) {
 | 
			
		||||
    return get_register_(command, ADDR_16_BIT, data, len, delay);
 | 
			
		||||
  }
 | 
			
		||||
  /** Read 1 data word from 16 bit i2c register.
 | 
			
		||||
   * @param  i2c register
 | 
			
		||||
  /** Read 1 data word from 16 bit I2C register.
 | 
			
		||||
   * @param  I2C register
 | 
			
		||||
   * @param data reference to raw result
 | 
			
		||||
   * @param delay milliseconds to to wait between sending the i2c command and reading the result
 | 
			
		||||
   * @param delay milliseconds to to wait between sending the I2C command and reading the result
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  bool get_register(uint16_t i2c_register, uint16_t &data, uint8_t delay = 0) {
 | 
			
		||||
    return this->get_register_(i2c_register, ADDR_16_BIT, &data, 1, delay);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** get data words from i2c register.
 | 
			
		||||
   * handles crc check used by Sensirion sensors
 | 
			
		||||
   * @param  i2c register
 | 
			
		||||
  /** get data words from I2C register.
 | 
			
		||||
   * handles CRC check used by Sensirion sensors
 | 
			
		||||
   * @param  I2C register
 | 
			
		||||
   * @param data pointer to raw result
 | 
			
		||||
   * @param len number of words to read
 | 
			
		||||
   * @param delay milliseconds to to wait between sending the i2c command and reading the result
 | 
			
		||||
   * @param delay milliseconds to to wait between sending the I2C command and reading the result
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  bool get_8bit_register(uint8_t i2c_register, uint16_t *data, uint8_t len, uint8_t delay = 0) {
 | 
			
		||||
    return get_register_(i2c_register, ADDR_8_BIT, data, len, delay);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Read 1 data word from 8 bit i2c register.
 | 
			
		||||
   * @param  i2c register
 | 
			
		||||
  /** Read 1 data word from 8 bit I2C register.
 | 
			
		||||
   * @param  I2C register
 | 
			
		||||
   * @param data reference to raw result
 | 
			
		||||
   * @param delay milliseconds to to wait between sending the i2c command and reading the result
 | 
			
		||||
   * @param delay milliseconds to to wait between sending the I2C command and reading the result
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  bool get_8bit_register(uint8_t i2c_register, uint16_t &data, uint8_t delay = 0) {
 | 
			
		||||
    return this->get_register_(i2c_register, ADDR_8_BIT, &data, 1, delay);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Write a command to the i2c device.
 | 
			
		||||
   * @param command i2c command to send
 | 
			
		||||
  /** Write a command to the I2C device.
 | 
			
		||||
   * @param command I2C command to send
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  template<class T> bool write_command(T i2c_register) { return write_command(i2c_register, nullptr, 0); }
 | 
			
		||||
 | 
			
		||||
  /** Write a command and one data word to the i2c device .
 | 
			
		||||
   * @param command i2c command to send
 | 
			
		||||
   * @param data argument for the i2c command
 | 
			
		||||
  /** Write a command and one data word to the I2C device .
 | 
			
		||||
   * @param command I2C command to send
 | 
			
		||||
   * @param data argument for the I2C command
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  template<class T> bool write_command(T i2c_register, uint16_t data) { return write_command(i2c_register, &data, 1); }
 | 
			
		||||
 | 
			
		||||
  /** Write a command with arguments as words
 | 
			
		||||
   * @param i2c_register i2c command to send - an be uint8_t or uint16_t
 | 
			
		||||
   * @param data vector<uint16> arguments for the i2c command
 | 
			
		||||
   * @param i2c_register I2C command to send - an be uint8_t or uint16_t
 | 
			
		||||
   * @param data vector<uint16> arguments for the I2C command
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  template<class T> bool write_command(T i2c_register, const std::vector<uint16_t> &data) {
 | 
			
		||||
@@ -101,57 +101,39 @@ class SensirionI2CDevice : public i2c::I2CDevice {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Write a command with arguments as words
 | 
			
		||||
   * @param i2c_register i2c command to send - an be uint8_t or uint16_t
 | 
			
		||||
   * @param data arguments for the i2c command
 | 
			
		||||
   * @param i2c_register I2C command to send - an be uint8_t or uint16_t
 | 
			
		||||
   * @param data arguments for the I2C command
 | 
			
		||||
   * @param len number of arguments (words)
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  template<class T> bool write_command(T i2c_register, const uint16_t *data, uint8_t len) {
 | 
			
		||||
    // limit to 8 or 16 bit only
 | 
			
		||||
    static_assert(sizeof(i2c_register) == 1 || sizeof(i2c_register) == 2,
 | 
			
		||||
                  "only 8 or 16 bit command types are supported.");
 | 
			
		||||
    static_assert(sizeof(i2c_register) == 1 || sizeof(i2c_register) == 2, "Only 8 or 16 bit command types supported");
 | 
			
		||||
    return write_command_(i2c_register, CommandLen(sizeof(T)), data, len);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint8_t crc_polynomial_{0x31u};  // default for sensirion
 | 
			
		||||
  /** Write a command with arguments as words
 | 
			
		||||
   * @param command i2c command to send can be uint8_t or uint16_t
 | 
			
		||||
   * @param command I2C command to send can be uint8_t or uint16_t
 | 
			
		||||
   * @param command_len either 1 for short 8 bit command or 2 for 16 bit command codes
 | 
			
		||||
   * @param data arguments for the i2c command
 | 
			
		||||
   * @param data arguments for the I2C command
 | 
			
		||||
   * @param data_len number of arguments (words)
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  bool write_command_(uint16_t command, CommandLen command_len, const uint16_t *data, uint8_t data_len);
 | 
			
		||||
 | 
			
		||||
  /** get data words from i2c register.
 | 
			
		||||
   * handles crc check used by Sensirion sensors
 | 
			
		||||
   * @param  i2c register
 | 
			
		||||
  /** get data words from I2C register.
 | 
			
		||||
   * handles CRC check used by Sensirion sensors
 | 
			
		||||
   * @param  I2C register
 | 
			
		||||
   * @param command_len either 1 for short 8 bit command or 2 for 16 bit command codes
 | 
			
		||||
   * @param data pointer to raw result
 | 
			
		||||
   * @param len number of words to read
 | 
			
		||||
   * @param delay milliseconds to to wait between sending the i2c command and reading the result
 | 
			
		||||
   * @param delay milliseconds to to wait between sending the I2C command and reading the result
 | 
			
		||||
   * @return true if reading succeeded
 | 
			
		||||
   */
 | 
			
		||||
  bool get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, uint8_t len, uint8_t delay);
 | 
			
		||||
 | 
			
		||||
  /** 8-bit CRC checksum that is transmitted after each data word for read and write operation
 | 
			
		||||
   * @param command i2c command to send
 | 
			
		||||
   * @param data data word for which the crc8 checksum is calculated
 | 
			
		||||
   * @param len number of arguments (words)
 | 
			
		||||
   * @return 8 Bit CRC
 | 
			
		||||
   */
 | 
			
		||||
  uint8_t sht_crc_(uint16_t data);
 | 
			
		||||
 | 
			
		||||
  /** 8-bit CRC checksum that is transmitted after each data word for read and write operation
 | 
			
		||||
   * @param command i2c command to send
 | 
			
		||||
   * @param data1 high byte of data word
 | 
			
		||||
   * @param data2 low byte of data word
 | 
			
		||||
   * @return 8 Bit CRC
 | 
			
		||||
   */
 | 
			
		||||
  uint8_t sht_crc_(uint8_t data1, uint8_t data2) { return sht_crc_(encode_uint16(data1, data2)); }
 | 
			
		||||
 | 
			
		||||
  /** last error code from i2c operation
 | 
			
		||||
  /** last error code from I2C operation
 | 
			
		||||
   */
 | 
			
		||||
  i2c::ErrorCode last_error_;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,8 @@ void log_sensor(const char *tag, const char *prefix, const char *type, Sensor *o
 | 
			
		||||
                "%s  State Class: '%s'\n"
 | 
			
		||||
                "%s  Unit of Measurement: '%s'\n"
 | 
			
		||||
                "%s  Accuracy Decimals: %d",
 | 
			
		||||
                prefix, type, obj->get_name().c_str(), prefix, state_class_to_string(obj->get_state_class()), prefix,
 | 
			
		||||
                prefix, type, obj->get_name().c_str(), prefix,
 | 
			
		||||
                LOG_STR_ARG(state_class_to_string(obj->get_state_class())), prefix,
 | 
			
		||||
                obj->get_unit_of_measurement_ref().c_str(), prefix, obj->get_accuracy_decimals());
 | 
			
		||||
 | 
			
		||||
  if (!obj->get_device_class_ref().empty()) {
 | 
			
		||||
@@ -33,17 +34,17 @@ void log_sensor(const char *tag, const char *prefix, const char *type, Sensor *o
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *state_class_to_string(StateClass state_class) {
 | 
			
		||||
const LogString *state_class_to_string(StateClass state_class) {
 | 
			
		||||
  switch (state_class) {
 | 
			
		||||
    case STATE_CLASS_MEASUREMENT:
 | 
			
		||||
      return "measurement";
 | 
			
		||||
      return LOG_STR("measurement");
 | 
			
		||||
    case STATE_CLASS_TOTAL_INCREASING:
 | 
			
		||||
      return "total_increasing";
 | 
			
		||||
      return LOG_STR("total_increasing");
 | 
			
		||||
    case STATE_CLASS_TOTAL:
 | 
			
		||||
      return "total";
 | 
			
		||||
      return LOG_STR("total");
 | 
			
		||||
    case STATE_CLASS_NONE:
 | 
			
		||||
    default:
 | 
			
		||||
      return "";
 | 
			
		||||
      return LOG_STR("");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ enum StateClass : uint8_t {
 | 
			
		||||
  STATE_CLASS_TOTAL = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char *state_class_to_string(StateClass state_class);
 | 
			
		||||
const LogString *state_class_to_string(StateClass state_class);
 | 
			
		||||
 | 
			
		||||
/** Base-class for all sensors.
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
@@ -211,7 +211,7 @@ void SGP4xComponent::measure_raw_() {
 | 
			
		||||
 | 
			
		||||
  if (!this->write_command(command, data, 2)) {
 | 
			
		||||
    ESP_LOGD(TAG, "write error (%d)", this->last_error_);
 | 
			
		||||
    this->status_set_warning("measurement request failed");
 | 
			
		||||
    this->status_set_warning(LOG_STR("measurement request failed"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -220,7 +220,7 @@ void SGP4xComponent::measure_raw_() {
 | 
			
		||||
    raw_data[1] = 0;
 | 
			
		||||
    if (!this->read_data(raw_data, response_words)) {
 | 
			
		||||
      ESP_LOGD(TAG, "read error (%d)", this->last_error_);
 | 
			
		||||
      this->status_set_warning("measurement read failed");
 | 
			
		||||
      this->status_set_warning(LOG_STR("measurement read failed"));
 | 
			
		||||
      this->voc_index_ = this->nox_index_ = UINT16_MAX;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ void SHT4XComponent::update() {
 | 
			
		||||
  // Send command
 | 
			
		||||
  if (!this->write_command(MEASURECOMMANDS[this->precision_])) {
 | 
			
		||||
    // Warning will be printed only if warning status is not set yet
 | 
			
		||||
    this->status_set_warning("Failed to send measurement command");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to send measurement command"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ void SoundLevelComponent::loop() {
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    if (!this->status_has_warning()) {
 | 
			
		||||
      this->status_set_warning("Microphone isn't running, can't compute statistics");
 | 
			
		||||
      this->status_set_warning(LOG_STR("Microphone isn't running, can't compute statistics"));
 | 
			
		||||
 | 
			
		||||
      // Deallocate buffers, if necessary
 | 
			
		||||
      this->stop_();
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ bool TCA9555Component::read_gpio_outputs_() {
 | 
			
		||||
    return false;
 | 
			
		||||
  uint8_t data[2];
 | 
			
		||||
  if (!this->read_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) {
 | 
			
		||||
    this->status_set_warning("Failed to read output register");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to read output register"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  this->output_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
 | 
			
		||||
@@ -64,7 +64,7 @@ bool TCA9555Component::read_gpio_modes_() {
 | 
			
		||||
  uint8_t data[2];
 | 
			
		||||
  bool success = this->read_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2);
 | 
			
		||||
  if (!success) {
 | 
			
		||||
    this->status_set_warning("Failed to read mode register");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to read mode register"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  this->mode_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
 | 
			
		||||
@@ -79,7 +79,7 @@ bool TCA9555Component::digital_read_hw(uint8_t pin) {
 | 
			
		||||
  uint8_t bank_number = pin < 8 ? 0 : 1;
 | 
			
		||||
  uint8_t register_to_read = bank_number ? TCA9555_INPUT_PORT_REGISTER_1 : TCA9555_INPUT_PORT_REGISTER_0;
 | 
			
		||||
  if (!this->read_bytes(register_to_read, &data, 1)) {
 | 
			
		||||
    this->status_set_warning("Failed to read input register");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to read input register"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  uint8_t second_half = this->input_mask_ >> 8;
 | 
			
		||||
@@ -108,7 +108,7 @@ void TCA9555Component::digital_write_hw(uint8_t pin, bool value) {
 | 
			
		||||
  data[0] = this->output_mask_;
 | 
			
		||||
  data[1] = this->output_mask_ >> 8;
 | 
			
		||||
  if (!this->write_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) {
 | 
			
		||||
    this->status_set_warning("Failed to write output register");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to write output register"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -123,7 +123,7 @@ bool TCA9555Component::write_gpio_modes_() {
 | 
			
		||||
  data[0] = this->mode_mask_;
 | 
			
		||||
  data[1] = this->mode_mask_ >> 8;
 | 
			
		||||
  if (!this->write_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2)) {
 | 
			
		||||
    this->status_set_warning("Failed to write mode register");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Failed to write mode register"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ void TMP1075Sensor::update() {
 | 
			
		||||
  uint16_t regvalue;
 | 
			
		||||
  if (!read_byte_16(REG_TEMP, ®value)) {
 | 
			
		||||
    ESP_LOGW(TAG, "'%s' - unable to read temperature register", this->name_.c_str());
 | 
			
		||||
    this->status_set_warning("can't read");
 | 
			
		||||
    this->status_set_warning(LOG_STR("can't read"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
 
 | 
			
		||||
@@ -28,12 +28,12 @@ void UDPComponent::setup() {
 | 
			
		||||
    int enable = 1;
 | 
			
		||||
    auto err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
 | 
			
		||||
    if (err != 0) {
 | 
			
		||||
      this->status_set_warning("Socket unable to set reuseaddr");
 | 
			
		||||
      this->status_set_warning(LOG_STR("Socket unable to set reuseaddr"));
 | 
			
		||||
      // we can still continue
 | 
			
		||||
    }
 | 
			
		||||
    err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int));
 | 
			
		||||
    if (err != 0) {
 | 
			
		||||
      this->status_set_warning("Socket unable to set broadcast");
 | 
			
		||||
      this->status_set_warning(LOG_STR("Socket unable to set broadcast"));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // create listening socket if we either want to subscribe to providers, or need to listen
 | 
			
		||||
@@ -55,7 +55,7 @@ void UDPComponent::setup() {
 | 
			
		||||
    int enable = 1;
 | 
			
		||||
    err = this->listen_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
 | 
			
		||||
    if (err != 0) {
 | 
			
		||||
      this->status_set_warning("Socket unable to set reuseaddr");
 | 
			
		||||
      this->status_set_warning(LOG_STR("Socket unable to set reuseaddr"));
 | 
			
		||||
      // we can still continue
 | 
			
		||||
    }
 | 
			
		||||
    struct sockaddr_in server {};
 | 
			
		||||
 
 | 
			
		||||
@@ -266,7 +266,7 @@ void USBUartTypeCdcAcm::on_connected() {
 | 
			
		||||
  for (auto *channel : this->channels_) {
 | 
			
		||||
    if (i == cdc_devs.size()) {
 | 
			
		||||
      ESP_LOGE(TAG, "No configuration found for channel %d", channel->index_);
 | 
			
		||||
      this->status_set_warning("No configuration found for channel");
 | 
			
		||||
      this->status_set_warning(LOG_STR("No configuration found for channel"));
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    channel->cdc_dev_ = cdc_devs[i++];
 | 
			
		||||
 
 | 
			
		||||
@@ -74,12 +74,12 @@ void WakeOnLanButton::setup() {
 | 
			
		||||
  int enable = 1;
 | 
			
		||||
  auto err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    this->status_set_warning("Socket unable to set reuseaddr");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Socket unable to set reuseaddr"));
 | 
			
		||||
    // we can still continue
 | 
			
		||||
  }
 | 
			
		||||
  err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int));
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    this->status_set_warning("Socket unable to set broadcast");
 | 
			
		||||
    this->status_set_warning(LOG_STR("Socket unable to set broadcast"));
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -198,9 +198,20 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin
 | 
			
		||||
void OTARequestHandler::handleRequest(AsyncWebServerRequest *request) {
 | 
			
		||||
  AsyncWebServerResponse *response;
 | 
			
		||||
  // Use the ota_success_ flag to determine the actual result
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
  static const char UPDATE_SUCCESS[] PROGMEM = "Update Successful!";
 | 
			
		||||
  static const char UPDATE_FAILED[] PROGMEM = "Update Failed!";
 | 
			
		||||
  static const char TEXT_PLAIN[] PROGMEM = "text/plain";
 | 
			
		||||
  static const char CONNECTION_STR[] PROGMEM = "Connection";
 | 
			
		||||
  static const char CLOSE_STR[] PROGMEM = "close";
 | 
			
		||||
  const char *msg = this->ota_success_ ? UPDATE_SUCCESS : UPDATE_FAILED;
 | 
			
		||||
  response = request->beginResponse_P(200, TEXT_PLAIN, msg);
 | 
			
		||||
  response->addHeader(CONNECTION_STR, CLOSE_STR);
 | 
			
		||||
#else
 | 
			
		||||
  const char *msg = this->ota_success_ ? "Update Successful!" : "Update Failed!";
 | 
			
		||||
  response = request->beginResponse(200, "text/plain", msg);
 | 
			
		||||
  response->addHeader("Connection", "close");
 | 
			
		||||
#endif
 | 
			
		||||
  request->send(response);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -148,7 +148,7 @@ void WiFiComponent::loop() {
 | 
			
		||||
 | 
			
		||||
    switch (this->state_) {
 | 
			
		||||
      case WIFI_COMPONENT_STATE_COOLDOWN: {
 | 
			
		||||
        this->status_set_warning("waiting to reconnect");
 | 
			
		||||
        this->status_set_warning(LOG_STR("waiting to reconnect"));
 | 
			
		||||
        if (millis() - this->action_started_ > 5000) {
 | 
			
		||||
          if (this->fast_connect_ || this->retry_hidden_) {
 | 
			
		||||
            if (!this->selected_ap_.get_bssid().has_value())
 | 
			
		||||
@@ -161,13 +161,13 @@ void WiFiComponent::loop() {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case WIFI_COMPONENT_STATE_STA_SCANNING: {
 | 
			
		||||
        this->status_set_warning("scanning for networks");
 | 
			
		||||
        this->status_set_warning(LOG_STR("scanning for networks"));
 | 
			
		||||
        this->check_scanning_finished();
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case WIFI_COMPONENT_STATE_STA_CONNECTING:
 | 
			
		||||
      case WIFI_COMPONENT_STATE_STA_CONNECTING_2: {
 | 
			
		||||
        this->status_set_warning("associating to network");
 | 
			
		||||
        this->status_set_warning(LOG_STR("associating to network"));
 | 
			
		||||
        this->check_connecting_finished();
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,6 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "component";
 | 
			
		||||
static const char *const UNSPECIFIED_MESSAGE = "unspecified";
 | 
			
		||||
 | 
			
		||||
// Global vectors for component data that doesn't belong in every instance.
 | 
			
		||||
// Using vector instead of unordered_map for both because:
 | 
			
		||||
@@ -143,7 +142,7 @@ void Component::call_dump_config() {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGE(TAG, "  %s is marked FAILED: %s", LOG_STR_ARG(this->get_component_log_str()),
 | 
			
		||||
             error_msg ? error_msg : UNSPECIFIED_MESSAGE);
 | 
			
		||||
             error_msg ? error_msg : LOG_STR_LITERAL("unspecified"));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -278,6 +277,7 @@ bool Component::is_ready() const {
 | 
			
		||||
bool Component::can_proceed() { return true; }
 | 
			
		||||
bool Component::status_has_warning() const { return this->component_state_ & STATUS_LED_WARNING; }
 | 
			
		||||
bool Component::status_has_error() const { return this->component_state_ & STATUS_LED_ERROR; }
 | 
			
		||||
 | 
			
		||||
void Component::status_set_warning(const char *message) {
 | 
			
		||||
  // Don't spam the log. This risks missing different warning messages though.
 | 
			
		||||
  if ((this->component_state_ & STATUS_LED_WARNING) != 0)
 | 
			
		||||
@@ -285,7 +285,16 @@ void Component::status_set_warning(const char *message) {
 | 
			
		||||
  this->component_state_ |= STATUS_LED_WARNING;
 | 
			
		||||
  App.app_state_ |= STATUS_LED_WARNING;
 | 
			
		||||
  ESP_LOGW(TAG, "%s set Warning flag: %s", LOG_STR_ARG(this->get_component_log_str()),
 | 
			
		||||
           message ? message : UNSPECIFIED_MESSAGE);
 | 
			
		||||
           message ? message : LOG_STR_LITERAL("unspecified"));
 | 
			
		||||
}
 | 
			
		||||
void Component::status_set_warning(const LogString *message) {
 | 
			
		||||
  // Don't spam the log. This risks missing different warning messages though.
 | 
			
		||||
  if ((this->component_state_ & STATUS_LED_WARNING) != 0)
 | 
			
		||||
    return;
 | 
			
		||||
  this->component_state_ |= STATUS_LED_WARNING;
 | 
			
		||||
  App.app_state_ |= STATUS_LED_WARNING;
 | 
			
		||||
  ESP_LOGW(TAG, "%s set Warning flag: %s", LOG_STR_ARG(this->get_component_log_str()),
 | 
			
		||||
           message ? LOG_STR_ARG(message) : LOG_STR_LITERAL("unspecified"));
 | 
			
		||||
}
 | 
			
		||||
void Component::status_set_error(const char *message) {
 | 
			
		||||
  if ((this->component_state_ & STATUS_LED_ERROR) != 0)
 | 
			
		||||
@@ -293,7 +302,7 @@ void Component::status_set_error(const char *message) {
 | 
			
		||||
  this->component_state_ |= STATUS_LED_ERROR;
 | 
			
		||||
  App.app_state_ |= STATUS_LED_ERROR;
 | 
			
		||||
  ESP_LOGE(TAG, "%s set Error flag: %s", LOG_STR_ARG(this->get_component_log_str()),
 | 
			
		||||
           message ? message : UNSPECIFIED_MESSAGE);
 | 
			
		||||
           message ? message : LOG_STR_LITERAL("unspecified"));
 | 
			
		||||
  if (message != nullptr) {
 | 
			
		||||
    // Lazy allocate the error messages vector if needed
 | 
			
		||||
    if (!component_error_messages) {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,9 @@
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
 | 
			
		||||
// Forward declaration for LogString
 | 
			
		||||
struct LogString;
 | 
			
		||||
 | 
			
		||||
/** Default setup priorities for components of different types.
 | 
			
		||||
 *
 | 
			
		||||
 * Components should return one of these setup priorities in get_setup_priority.
 | 
			
		||||
@@ -204,6 +207,7 @@ class Component {
 | 
			
		||||
  bool status_has_error() const;
 | 
			
		||||
 | 
			
		||||
  void status_set_warning(const char *message = nullptr);
 | 
			
		||||
  void status_set_warning(const LogString *message);
 | 
			
		||||
 | 
			
		||||
  void status_set_error(const char *message = nullptr);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user