diff --git a/esphome/components/animation/__init__.py b/esphome/components/animation/__init__.py index 3f03e5c185..1780bdf72e 100644 --- a/esphome/components/animation/__init__.py +++ b/esphome/components/animation/__init__.py @@ -60,6 +60,10 @@ async def to_code(config): image.seek(frameIndex) frame = image.convert("L", dither=Image.NONE) pixels = list(frame.getdata()) + if len(pixels) != height * width: + raise core.EsphomeError( + f"Unexpected number of pixels in frame {frameIndex}: {len(pixels)} != {height*width}" + ) for pix in pixels: data[pos] = pix pos += 1 @@ -69,8 +73,14 @@ async def to_code(config): pos = 0 for frameIndex in range(frames): image.seek(frameIndex) + if CONF_RESIZE in config: + image.thumbnail(config[CONF_RESIZE]) frame = image.convert("RGB") pixels = list(frame.getdata()) + if len(pixels) != height * width: + raise core.EsphomeError( + f"Unexpected number of pixels in frame {frameIndex}: {len(pixels)} != {height*width}" + ) for pix in pixels: data[pos] = pix[0] pos += 1 diff --git a/esphome/components/anova/anova_base.cpp b/esphome/components/anova/anova_base.cpp index d55404089e..dcef75e483 100644 --- a/esphome/components/anova/anova_base.cpp +++ b/esphome/components/anova/anova_base.cpp @@ -73,51 +73,52 @@ AnovaPacket *AnovaCodec::get_stop_request() { } void AnovaCodec::decode(const uint8_t *data, uint16_t length) { - memset(this->buf_, 0, 32); - strncpy(this->buf_, (char *) data, length); + char buf[32]; + memset(buf, 0, sizeof(buf)); + strncpy(buf, (char *) data, std::min(length, sizeof(buf) - 1)); this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false; switch (this->current_query_) { case READ_DEVICE_STATUS: { - if (!strncmp(this->buf_, "stopped", 7)) { + if (!strncmp(buf, "stopped", 7)) { this->has_running_ = true; this->running_ = false; } - if (!strncmp(this->buf_, "running", 7)) { + if (!strncmp(buf, "running", 7)) { this->has_running_ = true; this->running_ = true; } break; } case START: { - if (!strncmp(this->buf_, "start", 5)) { + if (!strncmp(buf, "start", 5)) { this->has_running_ = true; this->running_ = true; } break; } case STOP: { - if (!strncmp(this->buf_, "stop", 4)) { + if (!strncmp(buf, "stop", 4)) { this->has_running_ = true; this->running_ = false; } break; } case READ_TARGET_TEMPERATURE: { - this->target_temp_ = parse_number(this->buf_, sizeof(this->buf_)).value_or(0.0f); + this->target_temp_ = parse_number(buf, sizeof(buf)).value_or(0.0f); if (this->fahrenheit_) this->target_temp_ = ftoc(this->target_temp_); this->has_target_temp_ = true; break; } case SET_TARGET_TEMPERATURE: { - this->target_temp_ = parse_number(this->buf_, sizeof(this->buf_)).value_or(0.0f); + this->target_temp_ = parse_number(buf, sizeof(buf)).value_or(0.0f); if (this->fahrenheit_) this->target_temp_ = ftoc(this->target_temp_); this->has_target_temp_ = true; break; } case READ_CURRENT_TEMPERATURE: { - this->current_temp_ = parse_number(this->buf_, sizeof(this->buf_)).value_or(0.0f); + this->current_temp_ = parse_number(buf, sizeof(buf)).value_or(0.0f); if (this->fahrenheit_) this->current_temp_ = ftoc(this->current_temp_); this->has_current_temp_ = true; @@ -125,8 +126,8 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) { } case SET_UNIT: case READ_UNIT: { - this->unit_ = this->buf_[0]; - this->fahrenheit_ = this->buf_[0] == 'f'; + this->unit_ = buf[0]; + this->fahrenheit_ = buf[0] == 'f'; this->has_unit_ = true; break; } diff --git a/esphome/components/anova/anova_base.h b/esphome/components/anova/anova_base.h index 7c1383512d..b831157849 100644 --- a/esphome/components/anova/anova_base.h +++ b/esphome/components/anova/anova_base.h @@ -70,7 +70,6 @@ class AnovaCodec { bool has_current_temp_; bool has_unit_; bool has_running_; - char buf_[32]; bool fahrenheit_; CurrentQuery current_query_; diff --git a/esphome/components/esp32_camera_web_server/camera_web_server.cpp b/esphome/components/esp32_camera_web_server/camera_web_server.cpp index c9a684c7e5..653a274bf4 100644 --- a/esphome/components/esp32_camera_web_server/camera_web_server.cpp +++ b/esphome/components/esp32_camera_web_server/camera_web_server.cpp @@ -21,12 +21,19 @@ static const char *const TAG = "esp32_camera_web_server"; #define CONTENT_TYPE "image/jpeg" #define CONTENT_LENGTH "Content-Length" -static const char *const STREAM_HEADER = - "HTTP/1.1 200\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: multipart/x-mixed-replace;boundary=" PART_BOUNDARY - "\r\n"; -static const char *const STREAM_500 = "HTTP/1.1 500\r\nContent-Type: text/plain\r\n\r\nNo frames send.\r\n"; -static const char *const STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; +static const char *const STREAM_HEADER = "HTTP/1.0 200 OK\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Connection: close\r\n" + "Content-Type: multipart/x-mixed-replace;boundary=" PART_BOUNDARY "\r\n" + "\r\n" + "--" PART_BOUNDARY "\r\n"; +static const char *const STREAM_ERROR = "Content-Type: text/plain\r\n" + "\r\n" + "No frames send.\r\n" + "--" PART_BOUNDARY "\r\n"; static const char *const STREAM_PART = "Content-Type: " CONTENT_TYPE "\r\n" CONTENT_LENGTH ": %u\r\n\r\n"; +static const char *const STREAM_BOUNDARY = "\r\n" + "--" PART_BOUNDARY "\r\n"; CameraWebServer::CameraWebServer() {} @@ -45,6 +52,7 @@ void CameraWebServer::setup() { config.ctrl_port = this->port_; config.max_open_sockets = 1; config.backlog_conn = 2; + config.lru_purge_enable = true; if (httpd_start(&this->httpd_, &config) != ESP_OK) { mark_failed(); @@ -172,9 +180,6 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { ESP_LOGW(TAG, "STREAM: failed to acquire frame"); res = ESP_FAIL; } - if (res == ESP_OK) { - res = httpd_send_all(req, STREAM_BOUNDARY, strlen(STREAM_BOUNDARY)); - } if (res == ESP_OK) { size_t hlen = snprintf(part_buf, 64, STREAM_PART, image->get_data_length()); res = httpd_send_all(req, part_buf, hlen); @@ -182,6 +187,9 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { if (res == ESP_OK) { res = httpd_send_all(req, (const char *) image->get_data_buffer(), image->get_data_length()); } + if (res == ESP_OK) { + res = httpd_send_all(req, STREAM_BOUNDARY, strlen(STREAM_BOUNDARY)); + } if (res == ESP_OK) { frames++; int64_t frame_time = millis() - last_frame; @@ -193,7 +201,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { } if (!frames) { - res = httpd_send_all(req, STREAM_500, strlen(STREAM_500)); + res = httpd_send_all(req, STREAM_ERROR, strlen(STREAM_ERROR)); } ESP_LOGI(TAG, "STREAM: closed. Frames: %u", frames); diff --git a/esphome/components/ezo/ezo.cpp b/esphome/components/ezo/ezo.cpp index 7f7a41fb41..426f2807c1 100644 --- a/esphome/components/ezo/ezo.cpp +++ b/esphome/components/ezo/ezo.cpp @@ -32,7 +32,7 @@ void EZOSensor::update() { } void EZOSensor::loop() { - uint8_t buf[20]; + uint8_t buf[21]; if (!(this->state_ & EZO_STATE_WAIT)) { if (this->state_ & EZO_STATE_SEND_TEMP) { int len = sprintf((char *) buf, "T,%0.3f", this->tempcomp_); @@ -74,7 +74,7 @@ void EZOSensor::loop() { if (buf[0] != 1) return; - float val = parse_number((char *) &buf[1], sizeof(buf) - 1).value_or(0); + float val = parse_number((char *) &buf[1], sizeof(buf) - 2).value_or(0); this->publish_state(val); } diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index 53209dfc7b..159b08d2d9 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -94,17 +94,21 @@ UART_DIRECTIONS = { "BOTH": UARTDirection.UART_DIRECTION_BOTH, } +AFTER_DEFAULTS = {CONF_BYTES: 256, CONF_TIMEOUT: "100ms"} + DEBUG_SCHEMA = cv.Schema( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UARTDebugger), cv.Optional(CONF_DIRECTION, default="BOTH"): cv.enum( UART_DIRECTIONS, upper=True ), - cv.Optional(CONF_AFTER): cv.Schema( + cv.Optional(CONF_AFTER, default=AFTER_DEFAULTS): cv.Schema( { - cv.Optional(CONF_BYTES, default=256): cv.validate_bytes, cv.Optional( - CONF_TIMEOUT, default="100ms" + CONF_BYTES, default=AFTER_DEFAULTS[CONF_BYTES] + ): cv.validate_bytes, + cv.Optional( + CONF_TIMEOUT, default=AFTER_DEFAULTS[CONF_TIMEOUT] ): cv.positive_time_period_milliseconds, cv.Optional(CONF_DELIMITER): cv.templatable(validate_raw_data), } diff --git a/esphome/components/zyaura/zyaura.cpp b/esphome/components/zyaura/zyaura.cpp index 11643a5c23..621439aa0c 100644 --- a/esphome/components/zyaura/zyaura.cpp +++ b/esphome/components/zyaura/zyaura.cpp @@ -57,38 +57,46 @@ void IRAM_ATTR ZaSensorStore::interrupt(ZaSensorStore *arg) { void IRAM_ATTR ZaSensorStore::set_data_(ZaMessage *message) { switch (message->type) { case HUMIDITY: - this->humidity = (message->value > 10000) ? NAN : (message->value / 100.0f); + this->humidity = message->value; break; - case TEMPERATURE: - this->temperature = (message->value > 5970) ? NAN : (message->value / 16.0f - 273.15f); + this->temperature = message->value; break; - case CO2: - this->co2 = (message->value > 10000) ? NAN : message->value; - break; - - default: + this->co2 = message->value; break; } } -bool ZyAuraSensor::publish_state_(sensor::Sensor *sensor, float *value) { - // Sensor doesn't added to configuration +bool ZyAuraSensor::publish_state_(ZaDataType data_type, sensor::Sensor *sensor, uint16_t *data_value) { + // Sensor wasn't added to configuration if (sensor == nullptr) { return true; } - sensor->publish_state(*value); + float value = NAN; + switch (data_type) { + case HUMIDITY: + value = (*data_value > 10000) ? NAN : (*data_value / 100.0f); + break; + case TEMPERATURE: + value = (*data_value > 5970) ? NAN : (*data_value / 16.0f - 273.15f); + break; + case CO2: + value = (*data_value > 10000) ? NAN : *data_value; + break; + } + + sensor->publish_state(value); // Sensor reported wrong value - if (std::isnan(*value)) { + if (std::isnan(value)) { ESP_LOGW(TAG, "Sensor reported invalid data. Is the update interval too small?"); this->status_set_warning(); return false; } - *value = NAN; + *data_value = -1; return true; } @@ -104,9 +112,9 @@ void ZyAuraSensor::dump_config() { } void ZyAuraSensor::update() { - bool co2_result = this->publish_state_(this->co2_sensor_, &this->store_.co2); - bool temperature_result = this->publish_state_(this->temperature_sensor_, &this->store_.temperature); - bool humidity_result = this->publish_state_(this->humidity_sensor_, &this->store_.humidity); + bool co2_result = this->publish_state_(CO2, this->co2_sensor_, &this->store_.co2); + bool temperature_result = this->publish_state_(TEMPERATURE, this->temperature_sensor_, &this->store_.temperature); + bool humidity_result = this->publish_state_(HUMIDITY, this->humidity_sensor_, &this->store_.humidity); if (co2_result && temperature_result && humidity_result) { this->status_clear_warning(); diff --git a/esphome/components/zyaura/zyaura.h b/esphome/components/zyaura/zyaura.h index 2b9e3fbb35..85c31ec75a 100644 --- a/esphome/components/zyaura/zyaura.h +++ b/esphome/components/zyaura/zyaura.h @@ -42,9 +42,9 @@ class ZaDataProcessor { class ZaSensorStore { public: - float co2 = NAN; - float temperature = NAN; - float humidity = NAN; + uint16_t co2 = -1; + uint16_t temperature = -1; + uint16_t humidity = -1; void setup(InternalGPIOPin *pin_clock, InternalGPIOPin *pin_data); static void interrupt(ZaSensorStore *arg); @@ -79,7 +79,7 @@ class ZyAuraSensor : public PollingComponent { sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *humidity_sensor_{nullptr}; - bool publish_state_(sensor::Sensor *sensor, float *value); + bool publish_state_(ZaDataType data_type, sensor::Sensor *sensor, uint16_t *data_value); }; } // namespace zyaura diff --git a/esphome/const.py b/esphome/const.py index 4bd02022d1..6af44197ff 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.1" +__version__ = "2021.11.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index c67ad8eea3..9cdbf7ca16 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -362,45 +362,47 @@ std::string str_sanitize(const std::string &str); /// @name Parsing & formatting ///@{ -/// Parse a unsigned decimal number. +/// Parse an unsigned decimal number (requires null-terminated string). template::value && std::is_unsigned::value), int> = 0> optional parse_number(const char *str, size_t len) { char *end = nullptr; unsigned long value = ::strtoul(str, &end, 10); // NOLINT(google-runtime-int) - if (end == nullptr || end != str + len || value > std::numeric_limits::max()) + if (end == str || *end != '\0' || value > std::numeric_limits::max()) return {}; return value; } +/// Parse an unsigned decimal number. template::value && std::is_unsigned::value), int> = 0> optional parse_number(const std::string &str) { - return parse_number(str.c_str(), str.length()); + return parse_number(str.c_str(), str.length() + 1); } -/// Parse a signed decimal number. +/// Parse a signed decimal number (requires null-terminated string). template::value && std::is_signed::value), int> = 0> optional parse_number(const char *str, size_t len) { char *end = nullptr; signed long value = ::strtol(str, &end, 10); // NOLINT(google-runtime-int) - if (end == nullptr || end != str + len || value < std::numeric_limits::min() || - value > std::numeric_limits::max()) + if (end == str || *end != '\0' || value < std::numeric_limits::min() || value > std::numeric_limits::max()) return {}; return value; } +/// Parse a signed decimal number. template::value && std::is_signed::value), int> = 0> optional parse_number(const std::string &str) { - return parse_number(str.c_str(), str.length()); + return parse_number(str.c_str(), str.length() + 1); } -/// Parse a decimal floating-point number. +/// Parse a decimal floating-point number (requires null-terminated string). template::value), int> = 0> optional parse_number(const char *str, size_t len) { char *end = nullptr; float value = ::strtof(str, &end); - if (end == nullptr || end != str + len || value == HUGE_VALF) + if (end == str || *end != '\0' || value == HUGE_VALF) return {}; return value; } +/// Parse a decimal floating-point number. template::value), int> = 0> optional parse_number(const std::string &str) { - return parse_number(str.c_str(), str.length()); + return parse_number(str.c_str(), str.length() + 1); } ///@} diff --git a/tests/test2.yaml b/tests/test2.yaml index f90e522b1e..3afef9501d 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -39,6 +39,12 @@ uart: tx_pin: GPIO22 rx_pin: GPIO23 baud_rate: 115200 + # Specifically added for testing debug with no after: definition. + debug: + dummy_receiver: false + direction: rx + sequence: + - lambda: UARTDebug::log_hex(direction, bytes, ':'); ota: safe_mode: True