mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 00:51:49 +00:00 
			
		
		
		
	Compare commits
	
		
			22 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					f3ec4b514d | ||
| 
						 | 
					fc5798fa71 | ||
| 
						 | 
					95d7ad543f | ||
| 
						 | 
					d9b2903d78 | ||
| 
						 | 
					32a664eedc | ||
| 
						 | 
					e7477890cf | ||
| 
						 | 
					9bf72ff05f | ||
| 
						 | 
					46b4c970d1 | ||
| 
						 | 
					c83ecf764d | ||
| 
						 | 
					a2485a18cb | ||
| 
						 | 
					8ef2ad17b5 | ||
| 
						 | 
					4579f78bf9 | ||
| 
						 | 
					1853407645 | ||
| 
						 | 
					cb5efc1c42 | ||
| 
						 | 
					d26c2b1a44 | ||
| 
						 | 
					8bda8e5393 | ||
| 
						 | 
					954b8a0cff | ||
| 
						 | 
					7c17e72db4 | ||
| 
						 | 
					d180aee57f | ||
| 
						 | 
					e3ffecefc0 | ||
| 
						 | 
					4c61cf153c | ||
| 
						 | 
					c78fb90e2f | 
@@ -121,7 +121,7 @@ async def to_code(config):
 | 
			
		||||
        decoded = base64.b64decode(conf[CONF_KEY])
 | 
			
		||||
        cg.add(var.set_noise_psk(list(decoded)))
 | 
			
		||||
        cg.add_define("USE_API_NOISE")
 | 
			
		||||
        cg.add_library("esphome/noise-c", "0.1.1")
 | 
			
		||||
        cg.add_library("esphome/noise-c", "0.1.3")
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add_define("USE_API_PLAINTEXT")
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -422,7 +422,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
 | 
			
		||||
  msg.accuracy_decimals = sensor->get_accuracy_decimals();
 | 
			
		||||
  msg.force_update = sensor->get_force_update();
 | 
			
		||||
  msg.device_class = sensor->get_device_class();
 | 
			
		||||
  msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
 | 
			
		||||
  msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
 | 
			
		||||
  msg.disabled_by_default = sensor->is_disabled_by_default();
 | 
			
		||||
 | 
			
		||||
  return this->send_list_entities_sensor_response(msg);
 | 
			
		||||
@@ -702,15 +702,7 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
 | 
			
		||||
  // string message = 3;
 | 
			
		||||
  buffer.encode_string(3, line, strlen(line));
 | 
			
		||||
  // SubscribeLogsResponse - 29
 | 
			
		||||
  bool success = this->send_buffer(buffer, 29);
 | 
			
		||||
  if (!success) {
 | 
			
		||||
    buffer = this->create_buffer();
 | 
			
		||||
    // bool send_failed = 4;
 | 
			
		||||
    buffer.encode_bool(4, true);
 | 
			
		||||
    return this->send_buffer(buffer, 29);
 | 
			
		||||
  } else {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  return this->send_buffer(buffer, 29);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HelloResponse APIConnection::hello(const HelloRequest &msg) {
 | 
			
		||||
@@ -783,8 +775,23 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
 | 
			
		||||
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
 | 
			
		||||
  if (this->remove_)
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!this->helper_->can_write_without_blocking())
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!this->helper_->can_write_without_blocking()) {
 | 
			
		||||
    delay(0);
 | 
			
		||||
    APIError err = helper_->loop();
 | 
			
		||||
    if (err != APIError::OK) {
 | 
			
		||||
      on_fatal_error();
 | 
			
		||||
      ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    if (!this->helper_->can_write_without_blocking()) {
 | 
			
		||||
      // SubscribeLogsResponse
 | 
			
		||||
      if (message_type != 29) {
 | 
			
		||||
        ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
 | 
			
		||||
      }
 | 
			
		||||
      delay(0);
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  APIError err = this->helper_->write_packet(message_type, buffer.get_buffer()->data(), buffer.get_buffer()->size());
 | 
			
		||||
  if (err == APIError::WOULD_BLOCK)
 | 
			
		||||
 
 | 
			
		||||
@@ -125,6 +125,7 @@ APIError APINoiseFrameHelper::init() {
 | 
			
		||||
    HELPER_LOG("Setting nonblocking failed with errno %d", errno);
 | 
			
		||||
    return APIError::TCP_NONBLOCKING_FAILED;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int enable = 1;
 | 
			
		||||
  err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
@@ -494,12 +495,13 @@ APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload
 | 
			
		||||
  size_t total_len = 3 + mbuf.size;
 | 
			
		||||
  tmpbuf[1] = (uint8_t)(mbuf.size >> 8);
 | 
			
		||||
  tmpbuf[2] = (uint8_t) mbuf.size;
 | 
			
		||||
 | 
			
		||||
  struct iovec iov;
 | 
			
		||||
  iov.iov_base = &tmpbuf[0];
 | 
			
		||||
  iov.iov_len = total_len;
 | 
			
		||||
 | 
			
		||||
  // write raw to not have two packets sent if NAGLE disabled
 | 
			
		||||
  aerr = write_raw_(&tmpbuf[0], total_len);
 | 
			
		||||
  if (aerr != APIError::OK) {
 | 
			
		||||
    return aerr;
 | 
			
		||||
  }
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
  return write_raw_(&iov, 1);
 | 
			
		||||
}
 | 
			
		||||
APIError APINoiseFrameHelper::try_send_tx_buf_() {
 | 
			
		||||
  // try send from tx_buf
 | 
			
		||||
@@ -526,16 +528,19 @@ APIError APINoiseFrameHelper::try_send_tx_buf_() {
 | 
			
		||||
 * @param data The data to write
 | 
			
		||||
 * @param len The length of data
 | 
			
		||||
 */
 | 
			
		||||
APIError APINoiseFrameHelper::write_raw_(const uint8_t *data, size_t len) {
 | 
			
		||||
  if (len == 0)
 | 
			
		||||
APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
 | 
			
		||||
  if (iovcnt == 0)
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  int err;
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
 | 
			
		||||
  // uncomment for even more debugging
 | 
			
		||||
  size_t total_write_len = 0;
 | 
			
		||||
  for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
#ifdef HELPER_LOG_PACKETS
 | 
			
		||||
  ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str());
 | 
			
		||||
    ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
 | 
			
		||||
#endif
 | 
			
		||||
    total_write_len += iov[i].iov_len;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!tx_buf_.empty()) {
 | 
			
		||||
    // try to empty tx_buf_ first
 | 
			
		||||
@@ -546,41 +551,56 @@ APIError APINoiseFrameHelper::write_raw_(const uint8_t *data, size_t len) {
 | 
			
		||||
 | 
			
		||||
  if (!tx_buf_.empty()) {
 | 
			
		||||
    // tx buf not empty, can't write now because then stream would be inconsistent
 | 
			
		||||
    tx_buf_.insert(tx_buf_.end(), data, data + len);
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
 | 
			
		||||
                     reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
 | 
			
		||||
    }
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ssize_t sent = socket_->write(data, len);
 | 
			
		||||
  ssize_t sent = socket_->writev(iov, iovcnt);
 | 
			
		||||
  if (is_would_block(sent)) {
 | 
			
		||||
    // operation would block, add buffer to tx_buf
 | 
			
		||||
    tx_buf_.insert(tx_buf_.end(), data, data + len);
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
 | 
			
		||||
                     reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
 | 
			
		||||
    }
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  } else if (sent == -1) {
 | 
			
		||||
    // an error occured
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    HELPER_LOG("Socket write failed with errno %d", errno);
 | 
			
		||||
    return APIError::SOCKET_WRITE_FAILED;
 | 
			
		||||
  } else if (sent != len) {
 | 
			
		||||
  } else if (sent != total_write_len) {
 | 
			
		||||
    // partially sent, add end to tx_buf
 | 
			
		||||
    tx_buf_.insert(tx_buf_.end(), data + sent, data + len);
 | 
			
		||||
    size_t to_consume = sent;
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      if (to_consume >= iov[i].iov_len) {
 | 
			
		||||
        to_consume -= iov[i].iov_len;
 | 
			
		||||
      } else {
 | 
			
		||||
        tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
 | 
			
		||||
                       reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
 | 
			
		||||
        to_consume = 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  }
 | 
			
		||||
  // fully sent
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
}
 | 
			
		||||
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
 | 
			
		||||
  uint8_t header[3];
 | 
			
		||||
  header[0] = 0x01;  // indicator
 | 
			
		||||
  header[1] = (uint8_t)(len >> 8);
 | 
			
		||||
  header[2] = (uint8_t) len;
 | 
			
		||||
 | 
			
		||||
  aerr = write_raw_(header, 3);
 | 
			
		||||
  if (aerr != APIError::OK)
 | 
			
		||||
    return aerr;
 | 
			
		||||
  aerr = write_raw_(data, len);
 | 
			
		||||
  return aerr;
 | 
			
		||||
  struct iovec iov[2];
 | 
			
		||||
  iov[0].iov_base = header;
 | 
			
		||||
  iov[0].iov_len = 3;
 | 
			
		||||
  iov[1].iov_base = const_cast<uint8_t *>(data);
 | 
			
		||||
  iov[1].iov_len = len;
 | 
			
		||||
 | 
			
		||||
  return write_raw_(iov, 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Initiate the data structures for the handshake.
 | 
			
		||||
@@ -863,15 +883,13 @@ APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *pay
 | 
			
		||||
  ProtoVarInt(payload_len).encode(header);
 | 
			
		||||
  ProtoVarInt(type).encode(header);
 | 
			
		||||
 | 
			
		||||
  aerr = write_raw_(&header[0], header.size());
 | 
			
		||||
  if (aerr != APIError::OK) {
 | 
			
		||||
    return aerr;
 | 
			
		||||
  }
 | 
			
		||||
  aerr = write_raw_(payload, payload_len);
 | 
			
		||||
  if (aerr != APIError::OK) {
 | 
			
		||||
    return aerr;
 | 
			
		||||
  }
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
  struct iovec iov[2];
 | 
			
		||||
  iov[0].iov_base = &header[0];
 | 
			
		||||
  iov[0].iov_len = header.size();
 | 
			
		||||
  iov[1].iov_base = const_cast<uint8_t *>(payload);
 | 
			
		||||
  iov[1].iov_len = payload_len;
 | 
			
		||||
 | 
			
		||||
  return write_raw_(iov, 2);
 | 
			
		||||
}
 | 
			
		||||
APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
 | 
			
		||||
  // try send from tx_buf
 | 
			
		||||
@@ -896,16 +914,19 @@ APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
 | 
			
		||||
 * @param data The data to write
 | 
			
		||||
 * @param len The length of data
 | 
			
		||||
 */
 | 
			
		||||
APIError APIPlaintextFrameHelper::write_raw_(const uint8_t *data, size_t len) {
 | 
			
		||||
  if (len == 0)
 | 
			
		||||
APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
 | 
			
		||||
  if (iovcnt == 0)
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  int err;
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
 | 
			
		||||
  // uncomment for even more debugging
 | 
			
		||||
  size_t total_write_len = 0;
 | 
			
		||||
  for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
#ifdef HELPER_LOG_PACKETS
 | 
			
		||||
  ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str());
 | 
			
		||||
    ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
 | 
			
		||||
#endif
 | 
			
		||||
    total_write_len += iov[i].iov_len;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!tx_buf_.empty()) {
 | 
			
		||||
    // try to empty tx_buf_ first
 | 
			
		||||
@@ -916,23 +937,38 @@ APIError APIPlaintextFrameHelper::write_raw_(const uint8_t *data, size_t len) {
 | 
			
		||||
 | 
			
		||||
  if (!tx_buf_.empty()) {
 | 
			
		||||
    // tx buf not empty, can't write now because then stream would be inconsistent
 | 
			
		||||
    tx_buf_.insert(tx_buf_.end(), data, data + len);
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
 | 
			
		||||
                     reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
 | 
			
		||||
    }
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ssize_t sent = socket_->write(data, len);
 | 
			
		||||
  ssize_t sent = socket_->writev(iov, iovcnt);
 | 
			
		||||
  if (is_would_block(sent)) {
 | 
			
		||||
    // operation would block, add buffer to tx_buf
 | 
			
		||||
    tx_buf_.insert(tx_buf_.end(), data, data + len);
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
 | 
			
		||||
                     reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
 | 
			
		||||
    }
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  } else if (sent == -1) {
 | 
			
		||||
    // an error occured
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    HELPER_LOG("Socket write failed with errno %d", errno);
 | 
			
		||||
    return APIError::SOCKET_WRITE_FAILED;
 | 
			
		||||
  } else if (sent != len) {
 | 
			
		||||
  } else if (sent != total_write_len) {
 | 
			
		||||
    // partially sent, add end to tx_buf
 | 
			
		||||
    tx_buf_.insert(tx_buf_.end(), data + sent, data + len);
 | 
			
		||||
    size_t to_consume = sent;
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      if (to_consume >= iov[i].iov_len) {
 | 
			
		||||
        to_consume -= iov[i].iov_len;
 | 
			
		||||
      } else {
 | 
			
		||||
        tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
 | 
			
		||||
                       reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
 | 
			
		||||
        to_consume = 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  }
 | 
			
		||||
  // fully sent
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,7 @@ const char *api_error_to_str(APIError err);
 | 
			
		||||
 | 
			
		||||
class APIFrameHelper {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual ~APIFrameHelper() = default;
 | 
			
		||||
  virtual APIError init() = 0;
 | 
			
		||||
  virtual APIError loop() = 0;
 | 
			
		||||
  virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
 | 
			
		||||
@@ -96,7 +97,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
 | 
			
		||||
  APIError try_read_frame_(ParsedFrame *frame);
 | 
			
		||||
  APIError try_send_tx_buf_();
 | 
			
		||||
  APIError write_frame_(const uint8_t *data, size_t len);
 | 
			
		||||
  APIError write_raw_(const uint8_t *data, size_t len);
 | 
			
		||||
  APIError write_raw_(const struct iovec *iov, int iovcnt);
 | 
			
		||||
  APIError init_handshake_();
 | 
			
		||||
  APIError check_handshake_finished_();
 | 
			
		||||
  void send_explicit_handshake_reject_(const std::string &reason);
 | 
			
		||||
@@ -154,7 +155,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
 | 
			
		||||
 | 
			
		||||
  APIError try_read_frame_(ParsedFrame *frame);
 | 
			
		||||
  APIError try_send_tx_buf_();
 | 
			
		||||
  APIError write_raw_(const uint8_t *data, size_t len);
 | 
			
		||||
  APIError write_raw_(const struct iovec *iov, int iovcnt);
 | 
			
		||||
 | 
			
		||||
  std::unique_ptr<socket::Socket> socket_;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -134,6 +134,11 @@ void APIServer::loop() {
 | 
			
		||||
void APIServer::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "API Server:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Address: %s:%u", network_get_address().c_str(), this->port_);
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Using noise encryption: YES");
 | 
			
		||||
#else
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Using noise encryption: NO");
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
bool APIServer::uses_password() const { return !this->password_.empty(); }
 | 
			
		||||
bool APIServer::check_password(const std::string &password) const {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ template<typename... Ts> class OpenAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit OpenAction(Cover *cover) : cover_(cover) {}
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { this->cover_->open(); }
 | 
			
		||||
  void play(Ts... x) override { this->cover_->make_call().set_command_open().perform(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  Cover *cover_;
 | 
			
		||||
@@ -21,7 +21,7 @@ template<typename... Ts> class CloseAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit CloseAction(Cover *cover) : cover_(cover) {}
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { this->cover_->close(); }
 | 
			
		||||
  void play(Ts... x) override { this->cover_->make_call().set_command_close().perform(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  Cover *cover_;
 | 
			
		||||
@@ -31,7 +31,7 @@ template<typename... Ts> class StopAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit StopAction(Cover *cover) : cover_(cover) {}
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { this->cover_->stop(); }
 | 
			
		||||
  void play(Ts... x) override { this->cover_->make_call().set_command_stop().perform(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  Cover *cover_;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ class DemoSensor : public sensor::Sensor, public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
  void update() override {
 | 
			
		||||
    float val = random_float();
 | 
			
		||||
    bool increasing = this->state_class == sensor::STATE_CLASS_TOTAL_INCREASING;
 | 
			
		||||
    bool increasing = this->get_state_class() == sensor::STATE_CLASS_TOTAL_INCREASING;
 | 
			
		||||
    if (increasing) {
 | 
			
		||||
      float base = isnan(this->state) ? 0.0f : this->state;
 | 
			
		||||
      this->publish_state(base + val * 10);
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ std::unique_ptr<LightTransformer> AddressableLight::create_default_transition()
 | 
			
		||||
  return make_unique<AddressableLightTransformer>(*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Color esp_color_from_light_color_values(LightColorValues val) {
 | 
			
		||||
Color color_from_light_color_values(LightColorValues val) {
 | 
			
		||||
  auto r = to_uint8_scale(val.get_color_brightness() * val.get_red());
 | 
			
		||||
  auto g = to_uint8_scale(val.get_color_brightness() * val.get_green());
 | 
			
		||||
  auto b = to_uint8_scale(val.get_color_brightness() * val.get_blue());
 | 
			
		||||
@@ -44,7 +44,7 @@ void AddressableLight::update_state(LightState *state) {
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  // don't use LightState helper, gamma correction+brightness is handled by ESPColorView
 | 
			
		||||
  this->all() = esp_color_from_light_color_values(val);
 | 
			
		||||
  this->all() = color_from_light_color_values(val);
 | 
			
		||||
  this->schedule_show();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -54,7 +54,7 @@ void AddressableLightTransformer::start() {
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto end_values = this->target_values_;
 | 
			
		||||
  this->target_color_ = esp_color_from_light_color_values(end_values);
 | 
			
		||||
  this->target_color_ = color_from_light_color_values(end_values);
 | 
			
		||||
 | 
			
		||||
  // our transition will handle brightness, disable brightness in correction.
 | 
			
		||||
  this->light_.correction_.set_local_brightness(255);
 | 
			
		||||
@@ -62,10 +62,13 @@ void AddressableLightTransformer::start() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
optional<LightColorValues> AddressableLightTransformer::apply() {
 | 
			
		||||
  // Don't try to transition over running effects, instead immediately use the target values. write_state() and the
 | 
			
		||||
  // effects pick up the change from current_values.
 | 
			
		||||
  float smoothed_progress = LightTransitionTransformer::smoothed_progress(this->get_progress_());
 | 
			
		||||
 | 
			
		||||
  // When running an output-buffer modifying effect, don't try to transition individual LEDs, but instead just fade the
 | 
			
		||||
  // LightColorValues. write_state() then picks up the change in brightness, and the color change is picked up by the
 | 
			
		||||
  // effects which respect it.
 | 
			
		||||
  if (this->light_.is_effect_active())
 | 
			
		||||
    return this->target_values_;
 | 
			
		||||
    return LightColorValues::lerp(this->get_start_values(), this->get_target_values(), smoothed_progress);
 | 
			
		||||
 | 
			
		||||
  // Use a specialized transition for addressable lights: instead of using a unified transition for
 | 
			
		||||
  // all LEDs, we use the current state of each LED as the start.
 | 
			
		||||
@@ -75,8 +78,6 @@ optional<LightColorValues> AddressableLightTransformer::apply() {
 | 
			
		||||
  // Instead, we "fake" the look of the LERP by using an exponential average over time and using
 | 
			
		||||
  // dynamically-calculated alpha values to match the look.
 | 
			
		||||
 | 
			
		||||
  float smoothed_progress = LightTransitionTransformer::smoothed_progress(this->get_progress_());
 | 
			
		||||
 | 
			
		||||
  float denom = (1.0f - smoothed_progress);
 | 
			
		||||
  float alpha = denom == 0.0f ? 0.0f : (smoothed_progress - this->last_transition_progress_) / denom;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,9 @@ namespace light {
 | 
			
		||||
 | 
			
		||||
using ESPColor ESPDEPRECATED("esphome::light::ESPColor is deprecated, use esphome::Color instead.", "v1.21") = Color;
 | 
			
		||||
 | 
			
		||||
/// Convert the color information from a `LightColorValues` object to a `Color` object (does not apply brightness).
 | 
			
		||||
Color color_from_light_color_values(LightColorValues val);
 | 
			
		||||
 | 
			
		||||
class AddressableLight : public LightOutput, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual int32_t size() const = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -38,11 +38,8 @@ class AddressableLightEffect : public LightEffect {
 | 
			
		||||
  void stop() override { this->get_addressable_()->set_effect_active(false); }
 | 
			
		||||
  virtual void apply(AddressableLight &it, const Color ¤t_color) = 0;
 | 
			
		||||
  void apply() override {
 | 
			
		||||
    LightColorValues color = this->state_->remote_values;
 | 
			
		||||
    // not using any color correction etc. that will be handled by the addressable layer
 | 
			
		||||
    Color current_color =
 | 
			
		||||
        Color(static_cast<uint8_t>(color.get_red() * 255), static_cast<uint8_t>(color.get_green() * 255),
 | 
			
		||||
              static_cast<uint8_t>(color.get_blue() * 255), static_cast<uint8_t>(color.get_white() * 255));
 | 
			
		||||
    // not using any color correction etc. that will be handled by the addressable layer through ESPColorCorrection
 | 
			
		||||
    Color current_color = color_from_light_color_values(this->state_->remote_values);
 | 
			
		||||
    this->apply(*this->get_addressable_(), current_color);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -121,6 +121,9 @@ void LightState::loop() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this->transformer_->is_finished()) {
 | 
			
		||||
      // if the transition has written directly to the output, current_values is outdated, so update it
 | 
			
		||||
      this->current_values = this->transformer_->get_target_values();
 | 
			
		||||
 | 
			
		||||
      this->transformer_->stop();
 | 
			
		||||
      this->transformer_ = nullptr;
 | 
			
		||||
      this->target_state_reached_callback_.call();
 | 
			
		||||
 
 | 
			
		||||
@@ -12,12 +12,18 @@ namespace light {
 | 
			
		||||
class LightTransitionTransformer : public LightTransformer {
 | 
			
		||||
 public:
 | 
			
		||||
  void start() override {
 | 
			
		||||
    // When turning light on from off state, use colors from target state.
 | 
			
		||||
    // When turning light on from off state, use target state and only increase brightness from zero.
 | 
			
		||||
    if (!this->start_values_.is_on() && this->target_values_.is_on()) {
 | 
			
		||||
      this->start_values_ = LightColorValues(this->target_values_);
 | 
			
		||||
      this->start_values_.set_brightness(0.0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // When turning light off from on state, use source state and only decrease brightness to zero.
 | 
			
		||||
    if (this->start_values_.is_on() && !this->target_values_.is_on()) {
 | 
			
		||||
      this->target_values_ = LightColorValues(this->start_values_);
 | 
			
		||||
      this->target_values_.set_brightness(0.0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // When changing color mode, go through off state, as color modes are orthogonal and there can't be two active.
 | 
			
		||||
    if (this->start_values_.get_color_mode() != this->target_values_.get_color_mode()) {
 | 
			
		||||
      this->changing_color_mode_ = true;
 | 
			
		||||
 
 | 
			
		||||
@@ -281,4 +281,4 @@ async def to_code(config):
 | 
			
		||||
    if CONF_HUMIDITY_SETPOINT in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_HUMIDITY_SETPOINT])
 | 
			
		||||
        cg.add(var.set_humidity_setpoint_sensor(sens))
 | 
			
		||||
    cg.add_library("dudanov/MideaUART", "1.1.5")
 | 
			
		||||
    cg.add_library("dudanov/MideaUART", "1.1.8")
 | 
			
		||||
 
 | 
			
		||||
@@ -31,16 +31,9 @@ void MQTTSensorComponent::dump_config() {
 | 
			
		||||
std::string MQTTSensorComponent::component_type() const { return "sensor"; }
 | 
			
		||||
 | 
			
		||||
uint32_t MQTTSensorComponent::get_expire_after() const {
 | 
			
		||||
  if (this->expire_after_.has_value()) {
 | 
			
		||||
  if (this->expire_after_.has_value())
 | 
			
		||||
    return *this->expire_after_;
 | 
			
		||||
  } else {
 | 
			
		||||
#ifdef USE_DEEP_SLEEP
 | 
			
		||||
    if (deep_sleep::global_has_deep_sleep) {
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return this->sensor_->calculate_expected_filter_update_interval() * 5;
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire_after_ = expire_after; }
 | 
			
		||||
void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; }
 | 
			
		||||
@@ -61,8 +54,8 @@ void MQTTSensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryCo
 | 
			
		||||
  if (this->sensor_->get_force_update())
 | 
			
		||||
    root["force_update"] = true;
 | 
			
		||||
 | 
			
		||||
  if (this->sensor_->state_class != STATE_CLASS_NONE)
 | 
			
		||||
    root["state_class"] = state_class_to_string(this->sensor_->state_class);
 | 
			
		||||
  if (this->sensor_->get_state_class() != STATE_CLASS_NONE)
 | 
			
		||||
    root["state_class"] = state_class_to_string(this->sensor_->get_state_class());
 | 
			
		||||
 | 
			
		||||
  config.command_topic = false;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ namespace sensor {
 | 
			
		||||
static const char *const TAG = "sensor.filter";
 | 
			
		||||
 | 
			
		||||
// Filter
 | 
			
		||||
uint32_t Filter::expected_interval(uint32_t input) { return input; }
 | 
			
		||||
void Filter::input(float value) {
 | 
			
		||||
  ESP_LOGVV(TAG, "Filter(%p)::input(%f)", this, value);
 | 
			
		||||
  optional<float> out = this->new_value(value);
 | 
			
		||||
@@ -29,15 +28,6 @@ void Filter::initialize(Sensor *parent, Filter *next) {
 | 
			
		||||
  this->parent_ = parent;
 | 
			
		||||
  this->next_ = next;
 | 
			
		||||
}
 | 
			
		||||
uint32_t Filter::calculate_remaining_interval(uint32_t input) {
 | 
			
		||||
  uint32_t this_interval = this->expected_interval(input);
 | 
			
		||||
  ESP_LOGVV(TAG, "Filter(%p)::calculate_remaining_interval(%u) -> %u", this, input, this_interval);
 | 
			
		||||
  if (this->next_ == nullptr) {
 | 
			
		||||
    return this_interval;
 | 
			
		||||
  } else {
 | 
			
		||||
    return this->next_->calculate_remaining_interval(this_interval);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MedianFilter
 | 
			
		||||
MedianFilter::MedianFilter(size_t window_size, size_t send_every, size_t send_first_at)
 | 
			
		||||
@@ -75,8 +65,6 @@ optional<float> MedianFilter::new_value(float value) {
 | 
			
		||||
  return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t MedianFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
 | 
			
		||||
 | 
			
		||||
// MinFilter
 | 
			
		||||
MinFilter::MinFilter(size_t window_size, size_t send_every, size_t send_first_at)
 | 
			
		||||
    : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {}
 | 
			
		||||
@@ -106,8 +94,6 @@ optional<float> MinFilter::new_value(float value) {
 | 
			
		||||
  return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t MinFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
 | 
			
		||||
 | 
			
		||||
// MaxFilter
 | 
			
		||||
MaxFilter::MaxFilter(size_t window_size, size_t send_every, size_t send_first_at)
 | 
			
		||||
    : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {}
 | 
			
		||||
@@ -137,8 +123,6 @@ optional<float> MaxFilter::new_value(float value) {
 | 
			
		||||
  return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t MaxFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
 | 
			
		||||
 | 
			
		||||
// SlidingWindowMovingAverageFilter
 | 
			
		||||
SlidingWindowMovingAverageFilter::SlidingWindowMovingAverageFilter(size_t window_size, size_t send_every,
 | 
			
		||||
                                                                   size_t send_first_at)
 | 
			
		||||
@@ -177,8 +161,6 @@ optional<float> SlidingWindowMovingAverageFilter::new_value(float value) {
 | 
			
		||||
  return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t SlidingWindowMovingAverageFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
 | 
			
		||||
 | 
			
		||||
// ExponentialMovingAverageFilter
 | 
			
		||||
ExponentialMovingAverageFilter::ExponentialMovingAverageFilter(float alpha, size_t send_every)
 | 
			
		||||
    : send_every_(send_every), send_at_(send_every - 1), alpha_(alpha) {}
 | 
			
		||||
@@ -203,7 +185,6 @@ optional<float> ExponentialMovingAverageFilter::new_value(float value) {
 | 
			
		||||
}
 | 
			
		||||
void ExponentialMovingAverageFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; }
 | 
			
		||||
void ExponentialMovingAverageFilter::set_alpha(float alpha) { this->alpha_ = alpha; }
 | 
			
		||||
uint32_t ExponentialMovingAverageFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
 | 
			
		||||
 | 
			
		||||
// LambdaFilter
 | 
			
		||||
LambdaFilter::LambdaFilter(lambda_filter_t lambda_filter) : lambda_filter_(std::move(lambda_filter)) {}
 | 
			
		||||
@@ -296,14 +277,6 @@ void OrFilter::initialize(Sensor *parent, Filter *next) {
 | 
			
		||||
  this->phi_.initialize(parent, nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t OrFilter::expected_interval(uint32_t input) {
 | 
			
		||||
  uint32_t min_interval = UINT32_MAX;
 | 
			
		||||
  for (Filter *filter : this->filters_) {
 | 
			
		||||
    min_interval = std::min(min_interval, filter->calculate_remaining_interval(input));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return min_interval;
 | 
			
		||||
}
 | 
			
		||||
// DebounceFilter
 | 
			
		||||
optional<float> DebounceFilter::new_value(float value) {
 | 
			
		||||
  this->set_timeout("debounce", this->time_period_, [this, value]() { this->output(value); });
 | 
			
		||||
@@ -324,7 +297,6 @@ optional<float> HeartbeatFilter::new_value(float value) {
 | 
			
		||||
 | 
			
		||||
  return {};
 | 
			
		||||
}
 | 
			
		||||
uint32_t HeartbeatFilter::expected_interval(uint32_t input) { return this->time_period_; }
 | 
			
		||||
void HeartbeatFilter::setup() {
 | 
			
		||||
  this->set_interval("heartbeat", this->time_period_, [this]() {
 | 
			
		||||
    ESP_LOGVV(TAG, "HeartbeatFilter(%p)::interval(has_value=%s, last_input=%f)", this, YESNO(this->has_value_),
 | 
			
		||||
 
 | 
			
		||||
@@ -33,11 +33,6 @@ class Filter {
 | 
			
		||||
 | 
			
		||||
  void input(float value);
 | 
			
		||||
 | 
			
		||||
  /// Return the amount of time that this filter is expected to take based on the input time interval.
 | 
			
		||||
  virtual uint32_t expected_interval(uint32_t input);
 | 
			
		||||
 | 
			
		||||
  uint32_t calculate_remaining_interval(uint32_t input);
 | 
			
		||||
 | 
			
		||||
  void output(float value);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
@@ -68,8 +63,6 @@ class MedianFilter : public Filter {
 | 
			
		||||
  void set_send_every(size_t send_every);
 | 
			
		||||
  void set_window_size(size_t window_size);
 | 
			
		||||
 | 
			
		||||
  uint32_t expected_interval(uint32_t input) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::deque<float> queue_;
 | 
			
		||||
  size_t send_every_;
 | 
			
		||||
@@ -98,8 +91,6 @@ class MinFilter : public Filter {
 | 
			
		||||
  void set_send_every(size_t send_every);
 | 
			
		||||
  void set_window_size(size_t window_size);
 | 
			
		||||
 | 
			
		||||
  uint32_t expected_interval(uint32_t input) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::deque<float> queue_;
 | 
			
		||||
  size_t send_every_;
 | 
			
		||||
@@ -128,8 +119,6 @@ class MaxFilter : public Filter {
 | 
			
		||||
  void set_send_every(size_t send_every);
 | 
			
		||||
  void set_window_size(size_t window_size);
 | 
			
		||||
 | 
			
		||||
  uint32_t expected_interval(uint32_t input) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::deque<float> queue_;
 | 
			
		||||
  size_t send_every_;
 | 
			
		||||
@@ -159,8 +148,6 @@ class SlidingWindowMovingAverageFilter : public Filter {
 | 
			
		||||
  void set_send_every(size_t send_every);
 | 
			
		||||
  void set_window_size(size_t window_size);
 | 
			
		||||
 | 
			
		||||
  uint32_t expected_interval(uint32_t input) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  float sum_{0.0};
 | 
			
		||||
  std::deque<float> queue_;
 | 
			
		||||
@@ -183,8 +170,6 @@ class ExponentialMovingAverageFilter : public Filter {
 | 
			
		||||
  void set_send_every(size_t send_every);
 | 
			
		||||
  void set_alpha(float alpha);
 | 
			
		||||
 | 
			
		||||
  uint32_t expected_interval(uint32_t input) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool first_value_{true};
 | 
			
		||||
  float accumulator_{0.0f};
 | 
			
		||||
@@ -279,8 +264,6 @@ class HeartbeatFilter : public Filter, public Component {
 | 
			
		||||
 | 
			
		||||
  optional<float> new_value(float value) override;
 | 
			
		||||
 | 
			
		||||
  uint32_t expected_interval(uint32_t input) override;
 | 
			
		||||
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
@@ -306,8 +289,6 @@ class OrFilter : public Filter {
 | 
			
		||||
 | 
			
		||||
  void initialize(Sensor *parent, Filter *next) override;
 | 
			
		||||
 | 
			
		||||
  uint32_t expected_interval(uint32_t input) override;
 | 
			
		||||
 | 
			
		||||
  optional<float> new_value(float value) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace sensor {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "sensor";
 | 
			
		||||
 | 
			
		||||
const char *state_class_to_string(StateClass state_class) {
 | 
			
		||||
std::string state_class_to_string(StateClass state_class) {
 | 
			
		||||
  switch (state_class) {
 | 
			
		||||
    case STATE_CLASS_MEASUREMENT:
 | 
			
		||||
      return "measurement";
 | 
			
		||||
@@ -18,6 +18,51 @@ const char *state_class_to_string(StateClass state_class) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Sensor::Sensor(const std::string &name) : Nameable(name), state(NAN), raw_state(NAN) {}
 | 
			
		||||
Sensor::Sensor() : Sensor("") {}
 | 
			
		||||
 | 
			
		||||
std::string Sensor::get_unit_of_measurement() {
 | 
			
		||||
  if (this->unit_of_measurement_.has_value())
 | 
			
		||||
    return *this->unit_of_measurement_;
 | 
			
		||||
  return this->unit_of_measurement();
 | 
			
		||||
}
 | 
			
		||||
void Sensor::set_unit_of_measurement(const std::string &unit_of_measurement) {
 | 
			
		||||
  this->unit_of_measurement_ = unit_of_measurement;
 | 
			
		||||
}
 | 
			
		||||
std::string Sensor::unit_of_measurement() { return ""; }
 | 
			
		||||
 | 
			
		||||
std::string Sensor::get_icon() {
 | 
			
		||||
  if (this->icon_.has_value())
 | 
			
		||||
    return *this->icon_;
 | 
			
		||||
  return this->icon();
 | 
			
		||||
}
 | 
			
		||||
void Sensor::set_icon(const std::string &icon) { this->icon_ = icon; }
 | 
			
		||||
std::string Sensor::icon() { return ""; }
 | 
			
		||||
 | 
			
		||||
int8_t Sensor::get_accuracy_decimals() {
 | 
			
		||||
  if (this->accuracy_decimals_.has_value())
 | 
			
		||||
    return *this->accuracy_decimals_;
 | 
			
		||||
  return this->accuracy_decimals();
 | 
			
		||||
}
 | 
			
		||||
void Sensor::set_accuracy_decimals(int8_t accuracy_decimals) { this->accuracy_decimals_ = accuracy_decimals; }
 | 
			
		||||
int8_t Sensor::accuracy_decimals() { return 0; }
 | 
			
		||||
 | 
			
		||||
std::string Sensor::get_device_class() {
 | 
			
		||||
  if (this->device_class_.has_value())
 | 
			
		||||
    return *this->device_class_;
 | 
			
		||||
  return this->device_class();
 | 
			
		||||
}
 | 
			
		||||
void Sensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
 | 
			
		||||
std::string Sensor::device_class() { return ""; }
 | 
			
		||||
 | 
			
		||||
void Sensor::set_state_class(StateClass state_class) { this->state_class_ = state_class; }
 | 
			
		||||
StateClass Sensor::get_state_class() {
 | 
			
		||||
  if (this->state_class_.has_value())
 | 
			
		||||
    return *this->state_class_;
 | 
			
		||||
  return this->state_class();
 | 
			
		||||
}
 | 
			
		||||
StateClass Sensor::state_class() { return StateClass::STATE_CLASS_NONE; }
 | 
			
		||||
 | 
			
		||||
void Sensor::publish_state(float state) {
 | 
			
		||||
  this->raw_state = state;
 | 
			
		||||
  this->raw_callback_.call(state);
 | 
			
		||||
@@ -30,54 +75,12 @@ void Sensor::publish_state(float state) {
 | 
			
		||||
    this->filter_list_->input(state);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
std::string Sensor::unit_of_measurement() { return ""; }
 | 
			
		||||
std::string Sensor::icon() { return ""; }
 | 
			
		||||
uint32_t Sensor::update_interval() { return 0; }
 | 
			
		||||
int8_t Sensor::accuracy_decimals() { return 0; }
 | 
			
		||||
Sensor::Sensor(const std::string &name) : Nameable(name), state(NAN), raw_state(NAN) {}
 | 
			
		||||
Sensor::Sensor() : Sensor("") {}
 | 
			
		||||
 | 
			
		||||
void Sensor::set_unit_of_measurement(const std::string &unit_of_measurement) {
 | 
			
		||||
  this->unit_of_measurement_ = unit_of_measurement;
 | 
			
		||||
}
 | 
			
		||||
void Sensor::set_icon(const std::string &icon) { this->icon_ = icon; }
 | 
			
		||||
void Sensor::set_accuracy_decimals(int8_t accuracy_decimals) { this->accuracy_decimals_ = accuracy_decimals; }
 | 
			
		||||
void Sensor::add_on_state_callback(std::function<void(float)> &&callback) { this->callback_.add(std::move(callback)); }
 | 
			
		||||
void Sensor::add_on_raw_state_callback(std::function<void(float)> &&callback) {
 | 
			
		||||
  this->raw_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
std::string Sensor::get_icon() {
 | 
			
		||||
  if (this->icon_.has_value())
 | 
			
		||||
    return *this->icon_;
 | 
			
		||||
  return this->icon();
 | 
			
		||||
}
 | 
			
		||||
void Sensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
 | 
			
		||||
std::string Sensor::get_device_class() {
 | 
			
		||||
  if (this->device_class_.has_value())
 | 
			
		||||
    return *this->device_class_;
 | 
			
		||||
  return this->device_class();
 | 
			
		||||
}
 | 
			
		||||
std::string Sensor::device_class() { return ""; }
 | 
			
		||||
void Sensor::set_state_class(StateClass state_class) { this->state_class = state_class; }
 | 
			
		||||
void Sensor::set_state_class(const std::string &state_class) {
 | 
			
		||||
  if (str_equals_case_insensitive(state_class, "measurement")) {
 | 
			
		||||
    this->state_class = STATE_CLASS_MEASUREMENT;
 | 
			
		||||
  } else if (str_equals_case_insensitive(state_class, "total_increasing")) {
 | 
			
		||||
    this->state_class = STATE_CLASS_TOTAL_INCREASING;
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGW(TAG, "'%s' - Unrecognized state class %s", this->get_name().c_str(), state_class.c_str());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
std::string Sensor::get_unit_of_measurement() {
 | 
			
		||||
  if (this->unit_of_measurement_.has_value())
 | 
			
		||||
    return *this->unit_of_measurement_;
 | 
			
		||||
  return this->unit_of_measurement();
 | 
			
		||||
}
 | 
			
		||||
int8_t Sensor::get_accuracy_decimals() {
 | 
			
		||||
  if (this->accuracy_decimals_.has_value())
 | 
			
		||||
    return *this->accuracy_decimals_;
 | 
			
		||||
  return this->accuracy_decimals();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Sensor::add_filter(Filter *filter) {
 | 
			
		||||
  // inefficient, but only happens once on every sensor setup and nobody's going to have massive amounts of
 | 
			
		||||
  // filters
 | 
			
		||||
@@ -119,24 +122,7 @@ void Sensor::internal_send_state_to_frontend(float state) {
 | 
			
		||||
  this->callback_.call(state);
 | 
			
		||||
}
 | 
			
		||||
bool Sensor::has_state() const { return this->has_state_; }
 | 
			
		||||
uint32_t Sensor::calculate_expected_filter_update_interval() {
 | 
			
		||||
  uint32_t interval = this->update_interval();
 | 
			
		||||
  if (interval == 4294967295UL)
 | 
			
		||||
    // update_interval: never
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  if (this->filter_list_ == nullptr) {
 | 
			
		||||
    return interval;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return this->filter_list_->calculate_remaining_interval(interval);
 | 
			
		||||
}
 | 
			
		||||
uint32_t Sensor::hash_base() { return 2455723294UL; }
 | 
			
		||||
 | 
			
		||||
PollingSensorComponent::PollingSensorComponent(const std::string &name, uint32_t update_interval)
 | 
			
		||||
    : PollingComponent(update_interval), Sensor(name) {}
 | 
			
		||||
 | 
			
		||||
uint32_t PollingSensorComponent::update_interval() { return this->get_update_interval(); }
 | 
			
		||||
 | 
			
		||||
}  // namespace sensor
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/sensor/filter.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/components/sensor/filter.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace sensor {
 | 
			
		||||
@@ -13,7 +13,7 @@ namespace sensor {
 | 
			
		||||
    if (!(obj)->get_device_class().empty()) { \
 | 
			
		||||
      ESP_LOGCONFIG(TAG, "%s  Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
 | 
			
		||||
    } \
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "%s  State Class: '%s'", prefix, state_class_to_string((obj)->state_class)); \
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "%s  State Class: '%s'", prefix, state_class_to_string((obj)->get_state_class()).c_str()); \
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "%s  Unit of Measurement: '%s'", prefix, (obj)->get_unit_of_measurement().c_str()); \
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "%s  Accuracy Decimals: %d", prefix, (obj)->get_accuracy_decimals()); \
 | 
			
		||||
    if (!(obj)->get_icon().empty()) { \
 | 
			
		||||
@@ -36,7 +36,7 @@ enum StateClass : uint8_t {
 | 
			
		||||
  STATE_CLASS_TOTAL_INCREASING = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char *state_class_to_string(StateClass state_class);
 | 
			
		||||
std::string state_class_to_string(StateClass state_class);
 | 
			
		||||
 | 
			
		||||
/** Base-class for all sensors.
 | 
			
		||||
 *
 | 
			
		||||
@@ -47,26 +47,42 @@ class Sensor : public Nameable {
 | 
			
		||||
  explicit Sensor();
 | 
			
		||||
  explicit Sensor(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /** Manually set the unit of measurement of this sensor. By default the sensor's default defined by
 | 
			
		||||
   * unit_of_measurement() is used.
 | 
			
		||||
   *
 | 
			
		||||
   * @param unit_of_measurement The unit of measurement, "" to disable.
 | 
			
		||||
   */
 | 
			
		||||
  /// Get the unit of measurement, using the manual override if set.
 | 
			
		||||
  std::string get_unit_of_measurement();
 | 
			
		||||
  /// Manually set the unit of measurement.
 | 
			
		||||
  void set_unit_of_measurement(const std::string &unit_of_measurement);
 | 
			
		||||
 | 
			
		||||
  /** Manually set the icon of this sensor. By default the sensor's default defined by icon() is used.
 | 
			
		||||
   *
 | 
			
		||||
   * @param icon The icon, for example "mdi:flash". "" to disable.
 | 
			
		||||
   */
 | 
			
		||||
  /// Get the icon. Uses the manual override if specified or the default value instead.
 | 
			
		||||
  std::string get_icon();
 | 
			
		||||
  /// Manually set the icon, for example "mdi:flash".
 | 
			
		||||
  void set_icon(const std::string &icon);
 | 
			
		||||
 | 
			
		||||
  /** Manually set the accuracy in decimals for this sensor. By default, the sensor's default defined by
 | 
			
		||||
   * accuracy_decimals() is used.
 | 
			
		||||
   *
 | 
			
		||||
   * @param accuracy_decimals The accuracy decimal that should be used.
 | 
			
		||||
   */
 | 
			
		||||
  /// Get the accuracy in decimals, using the manual override if set.
 | 
			
		||||
  int8_t get_accuracy_decimals();
 | 
			
		||||
  /// Manually set the accuracy in decimals.
 | 
			
		||||
  void set_accuracy_decimals(int8_t accuracy_decimals);
 | 
			
		||||
 | 
			
		||||
  /// Get the device class, using the manual override if set.
 | 
			
		||||
  std::string get_device_class();
 | 
			
		||||
  /// Manually set the device class.
 | 
			
		||||
  void set_device_class(const std::string &device_class);
 | 
			
		||||
 | 
			
		||||
  /// Get the state class, using the manual override if set.
 | 
			
		||||
  StateClass get_state_class();
 | 
			
		||||
  /// Manually set the state class.
 | 
			
		||||
  void set_state_class(StateClass state_class);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get whether force update mode is enabled.
 | 
			
		||||
   *
 | 
			
		||||
   * If the sensor is in force_update mode, the frontend is required to save all
 | 
			
		||||
   * state changes to the database when they are published, even if the state is the
 | 
			
		||||
   * same as before.
 | 
			
		||||
   */
 | 
			
		||||
  bool get_force_update() const { return force_update_; }
 | 
			
		||||
  /// Set force update mode.
 | 
			
		||||
  void set_force_update(bool force_update) { force_update_ = force_update; }
 | 
			
		||||
 | 
			
		||||
  /// Add a filter to the filter chain. Will be appended to the back.
 | 
			
		||||
  void add_filter(Filter *filter);
 | 
			
		||||
 | 
			
		||||
@@ -93,15 +109,6 @@ class Sensor : public Nameable {
 | 
			
		||||
  /// Getter-syntax for .raw_state
 | 
			
		||||
  float get_raw_state() const;
 | 
			
		||||
 | 
			
		||||
  /// Get the accuracy in decimals. Uses the manual override if specified or the default value instead.
 | 
			
		||||
  int8_t get_accuracy_decimals();
 | 
			
		||||
 | 
			
		||||
  /// Get the unit of measurement. Uses the manual override if specified or the default value instead.
 | 
			
		||||
  std::string get_unit_of_measurement();
 | 
			
		||||
 | 
			
		||||
  /// Get the Home Assistant Icon. Uses the manual override if specified or the default value instead.
 | 
			
		||||
  std::string get_icon();
 | 
			
		||||
 | 
			
		||||
  /** Publish a new state to the front-end.
 | 
			
		||||
   *
 | 
			
		||||
   * First, the new state will be assigned to the raw_value. Then it's passed through all filters
 | 
			
		||||
@@ -127,35 +134,15 @@ class Sensor : public Nameable {
 | 
			
		||||
   */
 | 
			
		||||
  float state;
 | 
			
		||||
 | 
			
		||||
  /// Manually set the Home Assistant device class (see sensor::device_class)
 | 
			
		||||
  void set_device_class(const std::string &device_class);
 | 
			
		||||
 | 
			
		||||
  /// Get the device class for this sensor, using the manual override if specified.
 | 
			
		||||
  std::string get_device_class();
 | 
			
		||||
 | 
			
		||||
  /** This member variable stores the current raw state of the sensor. Unlike .state,
 | 
			
		||||
   * this will be updated immediately when publish_state is called.
 | 
			
		||||
  /** This member variable stores the current raw state of the sensor, without any filters applied.
 | 
			
		||||
   *
 | 
			
		||||
   * Unlike .state,this will be updated immediately when publish_state is called.
 | 
			
		||||
   */
 | 
			
		||||
  float raw_state;
 | 
			
		||||
 | 
			
		||||
  /// Return whether this sensor has gotten a full state (that passed through all filters) yet.
 | 
			
		||||
  bool has_state() const;
 | 
			
		||||
 | 
			
		||||
  // The state class of this sensor state
 | 
			
		||||
  StateClass state_class{STATE_CLASS_NONE};
 | 
			
		||||
 | 
			
		||||
  /// Manually set the Home Assistant state class (see sensor::state_class)
 | 
			
		||||
  void set_state_class(StateClass state_class);
 | 
			
		||||
  void set_state_class(const std::string &state_class);
 | 
			
		||||
 | 
			
		||||
  /** Override this to set the Home Assistant device class for this sensor.
 | 
			
		||||
   *
 | 
			
		||||
   * Return "" to disable this feature.
 | 
			
		||||
   *
 | 
			
		||||
   * @return The device class of this sensor, for example "temperature".
 | 
			
		||||
   */
 | 
			
		||||
  virtual std::string device_class();
 | 
			
		||||
 | 
			
		||||
  /** A unique ID for this sensor, empty for no unique id. See unique ID requirements:
 | 
			
		||||
   * https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements
 | 
			
		||||
   *
 | 
			
		||||
@@ -163,65 +150,38 @@ class Sensor : public Nameable {
 | 
			
		||||
   */
 | 
			
		||||
  virtual std::string unique_id();
 | 
			
		||||
 | 
			
		||||
  /// Return with which interval the sensor is polled. Return 0 for non-polling mode.
 | 
			
		||||
  virtual uint32_t update_interval();
 | 
			
		||||
 | 
			
		||||
  /// Calculate the expected update interval for values that pass through all filters.
 | 
			
		||||
  uint32_t calculate_expected_filter_update_interval();
 | 
			
		||||
 | 
			
		||||
  void internal_send_state_to_frontend(float state);
 | 
			
		||||
 | 
			
		||||
  bool get_force_update() const { return force_update_; }
 | 
			
		||||
  /** Set this sensor's force_update mode.
 | 
			
		||||
   *
 | 
			
		||||
   * If the sensor is in force_update mode, the frontend is required to save all
 | 
			
		||||
   * state changes to the database when they are published, even if the state is the
 | 
			
		||||
   * same as before.
 | 
			
		||||
   */
 | 
			
		||||
  void set_force_update(bool force_update) { force_update_ = force_update; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  /** Override this to set the Home Assistant unit of measurement for this sensor.
 | 
			
		||||
   *
 | 
			
		||||
   * Return "" to disable this feature.
 | 
			
		||||
   *
 | 
			
		||||
   * @return The icon of this sensor, for example "°C".
 | 
			
		||||
   */
 | 
			
		||||
  /// Override this to set the default unit of measurement.
 | 
			
		||||
  virtual std::string unit_of_measurement();  // NOLINT
 | 
			
		||||
 | 
			
		||||
  /** Override this to set the Home Assistant icon for this sensor.
 | 
			
		||||
   *
 | 
			
		||||
   * Return "" to disable this feature.
 | 
			
		||||
   *
 | 
			
		||||
   * @return The icon of this sensor, for example "mdi:battery".
 | 
			
		||||
   */
 | 
			
		||||
  /// Override this to set the default icon.
 | 
			
		||||
  virtual std::string icon();  // NOLINT
 | 
			
		||||
 | 
			
		||||
  /// Return the accuracy in decimals for this sensor.
 | 
			
		||||
  /// Override this to set the default accuracy in decimals.
 | 
			
		||||
  virtual int8_t accuracy_decimals();  // NOLINT
 | 
			
		||||
 | 
			
		||||
  optional<std::string> device_class_{};  ///< Stores the override of the device class
 | 
			
		||||
  /// Override this to set the default device class.
 | 
			
		||||
  virtual std::string device_class();  // NOLINT
 | 
			
		||||
 | 
			
		||||
  /// Override this to set the default state class.
 | 
			
		||||
  virtual StateClass state_class();  // NOLINT
 | 
			
		||||
 | 
			
		||||
  uint32_t hash_base() override;
 | 
			
		||||
 | 
			
		||||
  CallbackManager<void(float)> raw_callback_;  ///< Storage for raw state callbacks.
 | 
			
		||||
  CallbackManager<void(float)> callback_;      ///< Storage for filtered state callbacks.
 | 
			
		||||
  /// Override the unit of measurement
 | 
			
		||||
  optional<std::string> unit_of_measurement_;
 | 
			
		||||
  /// Override the icon advertised to Home Assistant, otherwise sensor's icon will be used.
 | 
			
		||||
  optional<std::string> icon_;
 | 
			
		||||
  /// Override the accuracy in decimals, otherwise the sensor's values will be used.
 | 
			
		||||
  optional<int8_t> accuracy_decimals_;
 | 
			
		||||
  Filter *filter_list_{nullptr};  ///< Store all active filters.
 | 
			
		||||
 | 
			
		||||
  bool has_state_{false};
 | 
			
		||||
  bool force_update_{false};
 | 
			
		||||
};
 | 
			
		||||
  Filter *filter_list_{nullptr};  ///< Store all active filters.
 | 
			
		||||
 | 
			
		||||
class PollingSensorComponent : public PollingComponent, public Sensor {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit PollingSensorComponent(const std::string &name, uint32_t update_interval);
 | 
			
		||||
 | 
			
		||||
  uint32_t update_interval() override;
 | 
			
		||||
  optional<std::string> unit_of_measurement_;           ///< Unit of measurement override
 | 
			
		||||
  optional<std::string> icon_;                          ///< Icon override
 | 
			
		||||
  optional<int8_t> accuracy_decimals_;                  ///< Accuracy in decimals override
 | 
			
		||||
  optional<std::string> device_class_;                  ///< Device class override
 | 
			
		||||
  optional<StateClass> state_class_{STATE_CLASS_NONE};  ///< State class override
 | 
			
		||||
  bool force_update_{false};                            ///< Force update mode
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace sensor
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,10 @@
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
#include <esp_idf_version.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace socket {
 | 
			
		||||
 | 
			
		||||
@@ -75,7 +79,57 @@ class BSDSocketImpl : public Socket {
 | 
			
		||||
  }
 | 
			
		||||
  int listen(int backlog) override { return ::listen(fd_, backlog); }
 | 
			
		||||
  ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); }
 | 
			
		||||
  ssize_t readv(const struct iovec *iov, int iovcnt) override {
 | 
			
		||||
#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4
 | 
			
		||||
    // esp-idf v3 doesn't have readv, emulate it
 | 
			
		||||
    ssize_t ret = 0;
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      ssize_t err = this->read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
 | 
			
		||||
      if (err == -1) {
 | 
			
		||||
        if (ret != 0)
 | 
			
		||||
          // if we already read some don't return an error
 | 
			
		||||
          break;
 | 
			
		||||
        return err;
 | 
			
		||||
      }
 | 
			
		||||
      ret += err;
 | 
			
		||||
      if (err != iov[i].iov_len)
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
#elif defined(ARDUINO_ARCH_ESP32)
 | 
			
		||||
    // ESP-IDF v4 only has symbol lwip_readv
 | 
			
		||||
    return ::lwip_readv(fd_, iov, iovcnt);
 | 
			
		||||
#else
 | 
			
		||||
    return ::readv(fd_, iov, iovcnt);
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  ssize_t write(const void *buf, size_t len) override { return ::write(fd_, buf, len); }
 | 
			
		||||
  ssize_t send(void *buf, size_t len, int flags) { return ::send(fd_, buf, len, flags); }
 | 
			
		||||
  ssize_t writev(const struct iovec *iov, int iovcnt) override {
 | 
			
		||||
#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4
 | 
			
		||||
    // esp-idf v3 doesn't have writev, emulate it
 | 
			
		||||
    ssize_t ret = 0;
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      ssize_t err =
 | 
			
		||||
          this->send(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len, i == iovcnt - 1 ? 0 : MSG_MORE);
 | 
			
		||||
      if (err == -1) {
 | 
			
		||||
        if (ret != 0)
 | 
			
		||||
          // if we already wrote some don't return an error
 | 
			
		||||
          break;
 | 
			
		||||
        return err;
 | 
			
		||||
      }
 | 
			
		||||
      ret += err;
 | 
			
		||||
      if (err != iov[i].iov_len)
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
#elif defined(ARDUINO_ARCH_ESP32)
 | 
			
		||||
    // ESP-IDF v4 only has symbol lwip_writev
 | 
			
		||||
    return ::lwip_writev(fd_, iov, iovcnt);
 | 
			
		||||
#else
 | 
			
		||||
    return ::writev(fd_, iov, iovcnt);
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  int setblocking(bool blocking) override {
 | 
			
		||||
    int fl = ::fcntl(fd_, F_GETFL, 0);
 | 
			
		||||
    if (blocking) {
 | 
			
		||||
 
 | 
			
		||||
@@ -81,6 +81,11 @@ struct sockaddr_storage {
 | 
			
		||||
};
 | 
			
		||||
typedef uint32_t socklen_t;
 | 
			
		||||
 | 
			
		||||
struct iovec {
 | 
			
		||||
  void *iov_base;
 | 
			
		||||
  size_t iov_len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP8266
 | 
			
		||||
// arduino-esp8266 declares a global vars called INADDR_NONE/ANY which are invalid with the define
 | 
			
		||||
#ifdef INADDR_ANY
 | 
			
		||||
@@ -104,6 +109,7 @@ typedef uint32_t socklen_t;
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/uio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 
 | 
			
		||||
@@ -256,7 +256,7 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
        errno = EINVAL;
 | 
			
		||||
        return -1;
 | 
			
		||||
      }
 | 
			
		||||
      *reinterpret_cast<int *>(optval) = tcp_nagle_disabled(pcb_);
 | 
			
		||||
      *reinterpret_cast<int *>(optval) = nodelay_;
 | 
			
		||||
      *optlen = 4;
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
@@ -285,11 +285,7 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
        return -1;
 | 
			
		||||
      }
 | 
			
		||||
      int val = *reinterpret_cast<const int *>(optval);
 | 
			
		||||
      if (val != 0) {
 | 
			
		||||
        tcp_nagle_disable(pcb_);
 | 
			
		||||
      } else {
 | 
			
		||||
        tcp_nagle_enable(pcb_);
 | 
			
		||||
      }
 | 
			
		||||
      nodelay_ = val;
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -371,7 +367,23 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
 | 
			
		||||
    return read;
 | 
			
		||||
  }
 | 
			
		||||
  ssize_t write(const void *buf, size_t len) override {
 | 
			
		||||
  ssize_t readv(const struct iovec *iov, int iovcnt) override {
 | 
			
		||||
    ssize_t ret = 0;
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      ssize_t err = read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
 | 
			
		||||
      if (err == -1) {
 | 
			
		||||
        if (ret != 0)
 | 
			
		||||
          // if we already read some don't return an error
 | 
			
		||||
          break;
 | 
			
		||||
        return err;
 | 
			
		||||
      }
 | 
			
		||||
      ret += err;
 | 
			
		||||
      if (err != iov[i].iov_len)
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
  }
 | 
			
		||||
  ssize_t internal_write(const void *buf, size_t len) {
 | 
			
		||||
    if (pcb_ == nullptr) {
 | 
			
		||||
      errno = ECONNRESET;
 | 
			
		||||
      return -1;
 | 
			
		||||
@@ -400,25 +412,64 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
      errno = ECONNRESET;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (tcp_nagle_disabled(pcb_)) {
 | 
			
		||||
      LWIP_LOG("tcp_output(%p)", pcb_);
 | 
			
		||||
      err = tcp_output(pcb_);
 | 
			
		||||
      if (err == ERR_ABRT) {
 | 
			
		||||
        LWIP_LOG("  -> err ERR_ABRT");
 | 
			
		||||
        // sometimes lwip returns ERR_ABRT for no apparent reason
 | 
			
		||||
        // the connection works fine afterwards, and back with ESPAsyncTCP we
 | 
			
		||||
        // indirectly also ignored this error
 | 
			
		||||
        // FIXME: figure out where this is returned and what it means in this context
 | 
			
		||||
        return to_send;
 | 
			
		||||
      }
 | 
			
		||||
      if (err != ERR_OK) {
 | 
			
		||||
        LWIP_LOG("  -> err %d", err);
 | 
			
		||||
        errno = ECONNRESET;
 | 
			
		||||
        return -1;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return to_send;
 | 
			
		||||
  }
 | 
			
		||||
  int internal_output() {
 | 
			
		||||
    LWIP_LOG("tcp_output(%p)", pcb_);
 | 
			
		||||
    err_t err = tcp_output(pcb_);
 | 
			
		||||
    if (err == ERR_ABRT) {
 | 
			
		||||
      LWIP_LOG("  -> err ERR_ABRT");
 | 
			
		||||
      // sometimes lwip returns ERR_ABRT for no apparent reason
 | 
			
		||||
      // the connection works fine afterwards, and back with ESPAsyncTCP we
 | 
			
		||||
      // indirectly also ignored this error
 | 
			
		||||
      // FIXME: figure out where this is returned and what it means in this context
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (err != ERR_OK) {
 | 
			
		||||
      LWIP_LOG("  -> err %d", err);
 | 
			
		||||
      errno = ECONNRESET;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  ssize_t write(const void *buf, size_t len) override {
 | 
			
		||||
    ssize_t written = internal_write(buf, len);
 | 
			
		||||
    if (written == -1)
 | 
			
		||||
      return -1;
 | 
			
		||||
    if (written == 0)
 | 
			
		||||
      // no need to output if nothing written
 | 
			
		||||
      return 0;
 | 
			
		||||
    if (nodelay_) {
 | 
			
		||||
      int err = internal_output();
 | 
			
		||||
      if (err == -1)
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return written;
 | 
			
		||||
  }
 | 
			
		||||
  ssize_t writev(const struct iovec *iov, int iovcnt) override {
 | 
			
		||||
    ssize_t written = 0;
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      ssize_t err = internal_write(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
 | 
			
		||||
      if (err == -1) {
 | 
			
		||||
        if (written != 0)
 | 
			
		||||
          // if we already read some don't return an error
 | 
			
		||||
          break;
 | 
			
		||||
        return err;
 | 
			
		||||
      }
 | 
			
		||||
      written += err;
 | 
			
		||||
      if (err != iov[i].iov_len)
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (written == 0)
 | 
			
		||||
      // no need to output if nothing written
 | 
			
		||||
      return 0;
 | 
			
		||||
    if (nodelay_) {
 | 
			
		||||
      int err = internal_output();
 | 
			
		||||
      if (err == -1)
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return written;
 | 
			
		||||
  }
 | 
			
		||||
  int setblocking(bool blocking) override {
 | 
			
		||||
    if (pcb_ == nullptr) {
 | 
			
		||||
      errno = ECONNRESET;
 | 
			
		||||
@@ -498,6 +549,9 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
  bool rx_closed_ = false;
 | 
			
		||||
  pbuf *rx_buf_ = nullptr;
 | 
			
		||||
  size_t rx_buf_offset_ = 0;
 | 
			
		||||
  // don't use lwip nodelay flag, it sometimes causes reconnect
 | 
			
		||||
  // instead use it for determining whether to call lwip_output
 | 
			
		||||
  bool nodelay_ = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Socket> socket(int domain, int type, int protocol) {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,9 @@ class Socket {
 | 
			
		||||
  virtual int setsockopt(int level, int optname, const void *optval, socklen_t optlen) = 0;
 | 
			
		||||
  virtual int listen(int backlog) = 0;
 | 
			
		||||
  virtual ssize_t read(void *buf, size_t len) = 0;
 | 
			
		||||
  virtual ssize_t readv(const struct iovec *iov, int iovcnt) = 0;
 | 
			
		||||
  virtual ssize_t write(const void *buf, size_t len) = 0;
 | 
			
		||||
  virtual ssize_t writev(const struct iovec *iov, int iovcnt) = 0;
 | 
			
		||||
  virtual int setblocking(bool blocking) = 0;
 | 
			
		||||
  virtual int loop() { return 0; };
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -34,8 +34,8 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
        cv.Optional(CONF_JS_INCLUDE): cv.file_,
 | 
			
		||||
        cv.Optional(CONF_AUTH): cv.Schema(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Required(CONF_USERNAME): cv.string_strict,
 | 
			
		||||
                cv.Required(CONF_PASSWORD): cv.string_strict,
 | 
			
		||||
                cv.Required(CONF_USERNAME): cv.All(cv.string_strict, cv.Length(min=1)),
 | 
			
		||||
                cv.Required(CONF_PASSWORD): cv.All(cv.string_strict, cv.Length(min=1)),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
 | 
			
		||||
@@ -57,8 +57,8 @@ async def to_code(config):
 | 
			
		||||
    cg.add(var.set_css_url(config[CONF_CSS_URL]))
 | 
			
		||||
    cg.add(var.set_js_url(config[CONF_JS_URL]))
 | 
			
		||||
    if CONF_AUTH in config:
 | 
			
		||||
        cg.add(var.set_username(config[CONF_AUTH][CONF_USERNAME]))
 | 
			
		||||
        cg.add(var.set_password(config[CONF_AUTH][CONF_PASSWORD]))
 | 
			
		||||
        cg.add(paren.set_auth_username(config[CONF_AUTH][CONF_USERNAME]))
 | 
			
		||||
        cg.add(paren.set_auth_password(config[CONF_AUTH][CONF_PASSWORD]))
 | 
			
		||||
    if CONF_CSS_INCLUDE in config:
 | 
			
		||||
        cg.add_define("WEBSERVER_CSS_INCLUDE")
 | 
			
		||||
        path = CORE.relative_config_path(config[CONF_CSS_INCLUDE])
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#include "web_server.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/util.h"
 | 
			
		||||
#include "esphome/components/json/json_util.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/util.h"
 | 
			
		||||
 | 
			
		||||
#include "StreamString.h"
 | 
			
		||||
 | 
			
		||||
@@ -151,9 +151,6 @@ void WebServer::setup() {
 | 
			
		||||
void WebServer::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Web Server:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Address: %s:%u", network_get_address().c_str(), this->base_->get_port());
 | 
			
		||||
  if (this->using_auth()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Basic authentication enabled");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f; }
 | 
			
		||||
 | 
			
		||||
@@ -728,10 +725,6 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
void WebServer::handleRequest(AsyncWebServerRequest *request) {
 | 
			
		||||
  if (this->using_auth() && !request->authenticate(this->username_, this->password_)) {
 | 
			
		||||
    return request->requestAuthentication();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (request->url() == "/") {
 | 
			
		||||
    this->handle_index_request(request);
 | 
			
		||||
    return;
 | 
			
		||||
 
 | 
			
		||||
@@ -30,10 +30,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
 | 
			
		||||
 public:
 | 
			
		||||
  WebServer(web_server_base::WebServerBase *base) : base_(base) {}
 | 
			
		||||
 | 
			
		||||
  void set_username(const char *username) { username_ = username; }
 | 
			
		||||
 | 
			
		||||
  void set_password(const char *password) { password_ = password; }
 | 
			
		||||
 | 
			
		||||
  /** Set the URL to the CSS <link> that's sent to each client. Defaults to
 | 
			
		||||
   * https://esphome.io/_static/webserver-v1.min.css
 | 
			
		||||
   *
 | 
			
		||||
@@ -83,8 +79,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
 | 
			
		||||
  void handle_js_request(AsyncWebServerRequest *request);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  bool using_auth() { return username_ != nullptr && password_ != nullptr; }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  void on_sensor_update(sensor::Sensor *obj, float state) override;
 | 
			
		||||
  /// Handle a sensor request under '/sensor/<id>'.
 | 
			
		||||
@@ -182,8 +176,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
 | 
			
		||||
 protected:
 | 
			
		||||
  web_server_base::WebServerBase *base_;
 | 
			
		||||
  AsyncEventSource events_{"/events"};
 | 
			
		||||
  const char *username_{nullptr};
 | 
			
		||||
  const char *password_{nullptr};
 | 
			
		||||
  const char *css_url_{nullptr};
 | 
			
		||||
  const char *css_include_{nullptr};
 | 
			
		||||
  const char *js_url_{nullptr};
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,17 @@ namespace web_server_base {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "web_server_base";
 | 
			
		||||
 | 
			
		||||
void WebServerBase::add_handler(AsyncWebHandler *handler) {
 | 
			
		||||
  // remove all handlers
 | 
			
		||||
 | 
			
		||||
  if (!credentials_.username.empty()) {
 | 
			
		||||
    handler = new internal::AuthMiddlewareHandler(handler, &credentials_);
 | 
			
		||||
  }
 | 
			
		||||
  this->handlers_.push_back(handler);
 | 
			
		||||
  if (this->server_ != nullptr)
 | 
			
		||||
    this->server_->addHandler(handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void report_ota_error() {
 | 
			
		||||
  StreamString ss;
 | 
			
		||||
  Update.printError(ss);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
@@ -7,6 +9,68 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace web_server_base {
 | 
			
		||||
 | 
			
		||||
namespace internal {
 | 
			
		||||
 | 
			
		||||
class MiddlewareHandler : public AsyncWebHandler {
 | 
			
		||||
 public:
 | 
			
		||||
  MiddlewareHandler(AsyncWebHandler *next) : next_(next) {}
 | 
			
		||||
 | 
			
		||||
  bool canHandle(AsyncWebServerRequest *request) override { return next_->canHandle(request); }
 | 
			
		||||
  void handleRequest(AsyncWebServerRequest *request) override { next_->handleRequest(request); }
 | 
			
		||||
  void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len,
 | 
			
		||||
                    bool final) override {
 | 
			
		||||
    next_->handleUpload(request, filename, index, data, len, final);
 | 
			
		||||
  }
 | 
			
		||||
  void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override {
 | 
			
		||||
    next_->handleBody(request, data, len, index, total);
 | 
			
		||||
  }
 | 
			
		||||
  bool isRequestHandlerTrivial() override { return next_->isRequestHandlerTrivial(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  AsyncWebHandler *next_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Credentials {
 | 
			
		||||
  std::string username;
 | 
			
		||||
  std::string password;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AuthMiddlewareHandler : public MiddlewareHandler {
 | 
			
		||||
 public:
 | 
			
		||||
  AuthMiddlewareHandler(AsyncWebHandler *next, Credentials *credentials)
 | 
			
		||||
      : MiddlewareHandler(next), credentials_(credentials) {}
 | 
			
		||||
 | 
			
		||||
  bool check_auth(AsyncWebServerRequest *request) {
 | 
			
		||||
    bool success = request->authenticate(credentials_->username.c_str(), credentials_->password.c_str());
 | 
			
		||||
    if (!success) {
 | 
			
		||||
      request->requestAuthentication();
 | 
			
		||||
    }
 | 
			
		||||
    return success;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void handleRequest(AsyncWebServerRequest *request) override {
 | 
			
		||||
    if (!check_auth(request))
 | 
			
		||||
      return;
 | 
			
		||||
    MiddlewareHandler::handleRequest(request);
 | 
			
		||||
  }
 | 
			
		||||
  void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len,
 | 
			
		||||
                    bool final) override {
 | 
			
		||||
    if (!check_auth(request))
 | 
			
		||||
      return;
 | 
			
		||||
    MiddlewareHandler::handleUpload(request, filename, index, data, len, final);
 | 
			
		||||
  }
 | 
			
		||||
  void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override {
 | 
			
		||||
    if (!check_auth(request))
 | 
			
		||||
      return;
 | 
			
		||||
    MiddlewareHandler::handleBody(request, data, len, index, total);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  Credentials *credentials_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace internal
 | 
			
		||||
 | 
			
		||||
class WebServerBase : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void init() {
 | 
			
		||||
@@ -32,13 +96,10 @@ class WebServerBase : public Component {
 | 
			
		||||
  AsyncWebServer *get_server() const { return server_; }
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
  void add_handler(AsyncWebHandler *handler) {
 | 
			
		||||
    // remove all handlers
 | 
			
		||||
  void set_auth_username(std::string auth_username) { credentials_.username = std::move(auth_username); }
 | 
			
		||||
  void set_auth_password(std::string auth_password) { credentials_.password = std::move(auth_password); }
 | 
			
		||||
 | 
			
		||||
    this->handlers_.push_back(handler);
 | 
			
		||||
    if (this->server_ != nullptr)
 | 
			
		||||
      this->server_->addHandler(handler);
 | 
			
		||||
  }
 | 
			
		||||
  void add_handler(AsyncWebHandler *handler);
 | 
			
		||||
 | 
			
		||||
  void add_ota_handler();
 | 
			
		||||
 | 
			
		||||
@@ -52,6 +113,7 @@ class WebServerBase : public Component {
 | 
			
		||||
  uint16_t port_{80};
 | 
			
		||||
  AsyncWebServer *server_{nullptr};
 | 
			
		||||
  std::vector<AsyncWebHandler *> handlers_;
 | 
			
		||||
  internal::Credentials credentials_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class OTARequestHandler : public AsyncWebHandler {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
"""Constants used by esphome."""
 | 
			
		||||
 | 
			
		||||
__version__ = "2021.9.0"
 | 
			
		||||
__version__ = "2021.9.3"
 | 
			
		||||
 | 
			
		||||
ESP_PLATFORM_ESP32 = "ESP32"
 | 
			
		||||
ESP_PLATFORM_ESP8266 = "ESP8266"
 | 
			
		||||
 
 | 
			
		||||
@@ -36,8 +36,8 @@ lib_deps =
 | 
			
		||||
    6306@1.0.3  ; HM3301
 | 
			
		||||
    glmnet/Dsmr@0.3        ; used by dsmr
 | 
			
		||||
    rweather/Crypto@0.2.0  ; used by dsmr
 | 
			
		||||
    esphome/noise-c@0.1.1  ; used by api
 | 
			
		||||
    dudanov/MideaUART@1.1.0  ; used by midea
 | 
			
		||||
    esphome/noise-c@0.1.3  ; used by api
 | 
			
		||||
    dudanov/MideaUART@1.1.8  ; used by midea
 | 
			
		||||
 | 
			
		||||
build_flags =
 | 
			
		||||
    -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE
 | 
			
		||||
 
 | 
			
		||||
@@ -10,4 +10,4 @@ platformio==5.2.0
 | 
			
		||||
esptool==3.1
 | 
			
		||||
click==7.1.2
 | 
			
		||||
esphome-dashboard==20210908.0
 | 
			
		||||
aioesphomeapi==9.0.0
 | 
			
		||||
aioesphomeapi==9.1.4
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user