From a779592414092163f5e29299567102f9072f6475 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:40:46 +1300 Subject: [PATCH 001/273] Bump version to 2021.10.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a9eec3e249..ce7d363e92 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0-dev" +__version__ = "2021.10.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From f57980b069759037c7217336ba1aca590b9699f6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 13 Oct 2021 22:30:29 +1300 Subject: [PATCH 002/273] Bump version to 2021.10.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index ce7d363e92..456b6d9209 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b1" +__version__ = "2021.10.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From b3b9ccd314d7117eee216037ca1ec6c075a3573b Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Wed, 13 Oct 2021 21:53:00 +0200 Subject: [PATCH 003/273] Fix light state remaining on after turn off with transition (#2509) --- esphome/components/light/transformers.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/esphome/components/light/transformers.h b/esphome/components/light/transformers.h index 90646f4e61..c22846ceb1 100644 --- a/esphome/components/light/transformers.h +++ b/esphome/components/light/transformers.h @@ -18,10 +18,13 @@ class LightTransitionTransformer : public LightTransformer { this->start_values_.set_brightness(0.0f); } - // When turning light off from on state, use source state and only decrease brightness to zero. + // When turning light off from on state, use source state and only decrease brightness to zero. Use a second + // variable for transition end state, as overwriting target_values breaks LightState logic. 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); + this->end_values_ = LightColorValues(this->start_values_); + this->end_values_.set_brightness(0.0f); + } else { + this->end_values_ = LightColorValues(this->target_values_); } // When changing color mode, go through off state, as color modes are orthogonal and there can't be two active. @@ -43,7 +46,7 @@ class LightTransitionTransformer : public LightTransformer { } LightColorValues &start = this->changing_color_mode_ && p > 0.5f ? this->intermediate_values_ : this->start_values_; - LightColorValues &end = this->changing_color_mode_ && p < 0.5f ? this->intermediate_values_ : this->target_values_; + LightColorValues &end = this->changing_color_mode_ && p < 0.5f ? this->intermediate_values_ : this->end_values_; if (this->changing_color_mode_) p = p < 0.5f ? p * 2 : (p - 0.5) * 2; @@ -57,6 +60,7 @@ class LightTransitionTransformer : public LightTransformer { static float smoothed_progress(float x) { return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); } bool changing_color_mode_{false}; + LightColorValues end_values_{}; LightColorValues intermediate_values_{}; }; From 48ff2ffc68479963dc76ea678fb2297a9e6bdc92 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Thu, 14 Oct 2021 08:59:52 +1300 Subject: [PATCH 004/273] Fix: Light flash not restoring previous LightState (#2383) * Update light state when transformer has finished * Revert writing direct to output * Correct handling of zero-length light transformers * Allow transformers to handle zero-length transitions, and check more boundary conditions when transitioning back to start state * Removed log.h * Fixed race condition between LightFlashTransformer.apply() and is_finished() * clang-format * Step progress from 0.0f to 1.0f at t=start_time for zero-length transforms to avoid divide-by-zero --- .../components/light/addressable_light.cpp | 2 +- esphome/components/light/light_transformer.h | 10 ++++- esphome/components/light/transformers.h | 37 ++++++++++--------- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/esphome/components/light/addressable_light.cpp b/esphome/components/light/addressable_light.cpp index f3e6c0ef1d..a8e0c7b762 100644 --- a/esphome/components/light/addressable_light.cpp +++ b/esphome/components/light/addressable_light.cpp @@ -79,7 +79,7 @@ optional AddressableLightTransformer::apply() { // dynamically-calculated alpha values to match the look. float denom = (1.0f - smoothed_progress); - float alpha = denom == 0.0f ? 0.0f : (smoothed_progress - this->last_transition_progress_) / denom; + float alpha = denom == 0.0f ? 1.0f : (smoothed_progress - this->last_transition_progress_) / denom; // We need to use a low-resolution alpha here which makes the transition set in only after ~half of the length // We solve this by accumulating the fractional part of the alpha over time. diff --git a/esphome/components/light/light_transformer.h b/esphome/components/light/light_transformer.h index dd904d0eed..35b045d5b4 100644 --- a/esphome/components/light/light_transformer.h +++ b/esphome/components/light/light_transformer.h @@ -39,7 +39,15 @@ class LightTransformer { protected: /// The progress of this transition, on a scale of 0 to 1. - float get_progress_() { return clamp((millis() - this->start_time_) / float(this->length_), 0.0f, 1.0f); } + float get_progress_() { + uint32_t now = esphome::millis(); + if (now < this->start_time_) + return 0.0f; + if (now >= this->start_time_ + this->length_) + return 1.0f; + + return clamp((now - this->start_time_) / float(this->length_), 0.0f, 1.0f); + } uint32_t start_time_; uint32_t length_; diff --git a/esphome/components/light/transformers.h b/esphome/components/light/transformers.h index c22846ceb1..a557bd39b1 100644 --- a/esphome/components/light/transformers.h +++ b/esphome/components/light/transformers.h @@ -73,9 +73,7 @@ class LightFlashTransformer : public LightTransformer { if (this->transition_length_ * 2 > this->length_) this->transition_length_ = this->length_ / 2; - // do not create transition if length is 0 - if (this->transition_length_ == 0) - return; + this->begun_lightstate_restore_ = false; // first transition to original target this->transformer_ = this->state_.get_output()->create_default_transition(); @@ -83,40 +81,45 @@ class LightFlashTransformer : public LightTransformer { } optional apply() override { - // transition transformer does not handle 0 length as progress returns nan - if (this->transition_length_ == 0) - return this->target_values_; + optional result = {}; + + if (this->transformer_ == nullptr && millis() > this->start_time_ + this->length_ - this->transition_length_) { + // second transition back to start value + this->transformer_ = this->state_.get_output()->create_default_transition(); + this->transformer_->setup(this->state_.current_values, this->get_start_values(), this->transition_length_); + this->begun_lightstate_restore_ = true; + } if (this->transformer_ != nullptr) { - if (!this->transformer_->is_finished()) { - return this->transformer_->apply(); - } else { + result = this->transformer_->apply(); + + if (this->transformer_->is_finished()) { this->transformer_->stop(); this->transformer_ = nullptr; } } - if (millis() > this->start_time_ + this->length_ - this->transition_length_) { - // second transition back to start value - this->transformer_ = this->state_.get_output()->create_default_transition(); - this->transformer_->setup(this->state_.current_values, this->get_start_values(), this->transition_length_); - } - - // once transition is complete, don't change states until next transition - return optional(); + return result; } // Restore the original values after the flash. void stop() override { + if (this->transformer_ != nullptr) { + this->transformer_->stop(); + this->transformer_ = nullptr; + } this->state_.current_values = this->get_start_values(); this->state_.remote_values = this->get_start_values(); this->state_.publish_state(); } + bool is_finished() override { return this->begun_lightstate_restore_ && LightTransformer::is_finished(); } + protected: LightState &state_; uint32_t transition_length_; std::unique_ptr transformer_{nullptr}; + bool begun_lightstate_restore_; }; } // namespace light From 5f7cef0b06b0f12db3d7131db2fbcf5b71848225 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Wed, 13 Oct 2021 22:21:43 +0200 Subject: [PATCH 005/273] Disallow using UART2 for logger on ESP-32 variants that lack it (#2510) --- esphome/components/esp32/__init__.py | 6 +++++- esphome/components/logger/__init__.py | 7 +++++++ esphome/components/logger/logger.cpp | 8 +++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 704f9bb3e8..09eabe1fa7 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -21,12 +21,16 @@ from esphome.core import CORE, HexInt import esphome.config_validation as cv import esphome.codegen as cg -from .const import ( +from .const import ( # noqa KEY_BOARD, KEY_ESP32, KEY_SDKCONFIG_OPTIONS, KEY_VARIANT, + VARIANT_ESP32, + VARIANT_ESP32S2, + VARIANT_ESP32S3, VARIANT_ESP32C3, + VARIANT_ESP32H2, VARIANTS, ) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index bc1bc6bb41..fe2a3ec8f8 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -19,6 +19,7 @@ from esphome.const import ( CONF_TX_BUFFER_SIZE, ) from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority +from esphome.components.esp32 import get_esp32_variant, VARIANT_ESP32S2, VARIANT_ESP32C3 CODEOWNERS = ["@esphome/core"] logger_ns = cg.esphome_ns.namespace("logger") @@ -52,6 +53,10 @@ LOG_LEVEL_SEVERITY = [ "VERY_VERBOSE", ] +ESP32_REDUCED_VARIANTS = [VARIANT_ESP32C3, VARIANT_ESP32S2] + +UART_SELECTION_ESP32_REDUCED = ["UART0", "UART1"] + UART_SELECTION_ESP32 = ["UART0", "UART1", "UART2"] UART_SELECTION_ESP8266 = ["UART0", "UART0_SWAP", "UART1"] @@ -75,6 +80,8 @@ is_log_level = cv.one_of(*LOG_LEVELS, upper=True) def uart_selection(value): if CORE.is_esp32: + if get_esp32_variant() in ESP32_REDUCED_VARIANTS: + return cv.one_of(*UART_SELECTION_ESP32_REDUCED, upper=True)(value) return cv.one_of(*UART_SELECTION_ESP32, upper=True)(value) if CORE.is_esp8266: return cv.one_of(*UART_SELECTION_ESP8266, upper=True)(value) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 2d85969bf3..b38c7f1a69 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -153,13 +153,9 @@ void Logger::pre_setup() { case UART_SELECTION_UART1: this->hw_serial_ = &Serial1; break; -#ifdef USE_ESP32 +#if defined(USE_ESP32) && !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32S2 case UART_SELECTION_UART2: -#if !CONFIG_IDF_TARGET_ESP32S2 && !CONFIG_IDF_TARGET_ESP32C3 - // FIXME: Validate in config that UART2 can't be set for ESP32-S2 (only has - // UART0-UART1) this->hw_serial_ = &Serial2; -#endif break; #endif } @@ -173,9 +169,11 @@ void Logger::pre_setup() { case UART_SELECTION_UART1: uart_num_ = UART_NUM_1; break; +#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32S2 case UART_SELECTION_UART2: uart_num_ = UART_NUM_2; break; +#endif } uart_config_t uart_config{}; uart_config.baud_rate = (int) baud_rate_; From d8a6dfe5ce9ca7b5ea4d25a3a3e99ce800ca6ee2 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Thu, 14 Oct 2021 20:58:35 +1300 Subject: [PATCH 006/273] Fix BME680_BSEC compilation issue with ESP32 (#2516) --- esphome/components/bme680_bsec/__init__.py | 8 +++++++- tests/test1.yaml | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/esphome/components/bme680_bsec/__init__.py b/esphome/components/bme680_bsec/__init__.py index d258819aa4..38da18d702 100644 --- a/esphome/components/bme680_bsec/__init__.py +++ b/esphome/components/bme680_bsec/__init__.py @@ -2,6 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c from esphome.const import CONF_ID +from esphome.core import CORE CODEOWNERS = ["@trvrnrth"] DEPENDENCIES = ["i2c"] @@ -44,7 +45,8 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional( CONF_STATE_SAVE_INTERVAL, default="6hours" ): cv.positive_time_period_minutes, - } + }, + cv.only_with_arduino, ).extend(i2c.i2c_device_schema(0x76)) @@ -60,5 +62,9 @@ async def to_code(config): var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds) ) + if CORE.is_esp32: + # Although this component does not use SPI, the BSEC library requires the SPI library + cg.add_library("SPI", None) + cg.add_define("USE_BSEC") cg.add_library("BSEC Software Library", "1.6.1480") diff --git a/tests/test1.yaml b/tests/test1.yaml index 157ccfc5d1..62fc781eca 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -265,6 +265,9 @@ wled: adalight: +bme680_bsec: + i2c_id: i2c_bus + esp32_ble_tracker: ble_client: @@ -478,6 +481,19 @@ sensor: duration: 150ms update_interval: 15s i2c_id: i2c_bus + - platform: bme680_bsec + temperature: + name: "BME680 Temperature" + pressure: + name: "BME680 Pressure" + humidity: + name: "BME680 Humidity" + iaq: + name: "BME680 IAQ" + co2_equivalent: + name: "BME680 CO2 Equivalent" + breath_voc_equivalent: + name: "BME680 Breath VOC Equivalent" - platform: bmp085 temperature: name: 'Outside Temperature' From c51d8c90214efa10ffb8c8a85da1ecb19c992b8a Mon Sep 17 00:00:00 2001 From: Dmitriy Lopatko Date: Thu, 14 Oct 2021 10:00:53 +0200 Subject: [PATCH 007/273] add missing include in sgp30 (#2517) --- esphome/components/sgp30/sgp30.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 1a64a12907..87cf0fa61a 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -1,4 +1,5 @@ #include "sgp30.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" #include "esphome/core/application.h" #include From 9b7fb829f9297e45b3ae4e641f310072bbfd9065 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Thu, 14 Oct 2021 21:04:50 +1300 Subject: [PATCH 008/273] Fix: Color modes not being correctly used in light partitions (#2513) --- .../light/addressable_light_wrapper.h | 89 +++++++++++++++++-- 1 file changed, 80 insertions(+), 9 deletions(-) diff --git a/esphome/components/light/addressable_light_wrapper.h b/esphome/components/light/addressable_light_wrapper.h index cd5bcabd47..d358502430 100644 --- a/esphome/components/light/addressable_light_wrapper.h +++ b/esphome/components/light/addressable_light_wrapper.h @@ -16,24 +16,94 @@ class AddressableLightWrapper : public light::AddressableLight { void clear_effect_data() override { this->wrapper_state_[4] = 0; } - light::LightTraits get_traits() override { return this->light_state_->get_traits(); } + light::LightTraits get_traits() override { + LightTraits traits; + + // Choose which color mode to use. + // This is ordered by how closely each color mode matches the underlying RGBW data structure used in LightPartition. + ColorMode color_mode_precedence[] = {ColorMode::RGB_WHITE, + ColorMode::RGB_COLD_WARM_WHITE, + ColorMode::RGB_COLOR_TEMPERATURE, + ColorMode::RGB, + ColorMode::WHITE, + ColorMode::COLD_WARM_WHITE, + ColorMode::COLOR_TEMPERATURE, + ColorMode::BRIGHTNESS, + ColorMode::ON_OFF, + ColorMode::UNKNOWN}; + + LightTraits parent_traits = this->light_state_->get_traits(); + for (auto cm : color_mode_precedence) { + if (parent_traits.supports_color_mode(cm)) { + this->color_mode_ = cm; + break; + } + } + + // Report a color mode that's compatible with both the partition and the underlying light + switch (this->color_mode_) { + case ColorMode::RGB_WHITE: + case ColorMode::RGB_COLD_WARM_WHITE: + case ColorMode::RGB_COLOR_TEMPERATURE: + traits.set_supported_color_modes({light::ColorMode::RGB_WHITE}); + break; + + case ColorMode::RGB: + traits.set_supported_color_modes({light::ColorMode::RGB}); + break; + + case ColorMode::WHITE: + case ColorMode::COLD_WARM_WHITE: + case ColorMode::COLOR_TEMPERATURE: + case ColorMode::BRIGHTNESS: + traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS}); + break; + + case ColorMode::ON_OFF: + traits.set_supported_color_modes({light::ColorMode::ON_OFF}); + break; + + default: + traits.set_supported_color_modes({light::ColorMode::UNKNOWN}); + } + + return traits; + } void write_state(light::LightState *state) override { + // Don't overwrite state if the underlying light is turned on + if (this->light_state_->remote_values.is_on()) { + this->mark_shown_(); + return; + } + float gamma = this->light_state_->get_gamma_correct(); float r = gamma_uncorrect(this->wrapper_state_[0] / 255.0f, gamma); float g = gamma_uncorrect(this->wrapper_state_[1] / 255.0f, gamma); float b = gamma_uncorrect(this->wrapper_state_[2] / 255.0f, gamma); float w = gamma_uncorrect(this->wrapper_state_[3] / 255.0f, gamma); - float brightness = fmaxf(r, fmaxf(g, b)); auto call = this->light_state_->make_call(); - call.set_state(true); - call.set_brightness_if_supported(1.0f); - call.set_color_brightness_if_supported(brightness); - call.set_red_if_supported(r); - call.set_green_if_supported(g); - call.set_blue_if_supported(b); - call.set_white_if_supported(w); + + float color_brightness = fmaxf(r, fmaxf(g, b)); + float brightness = fmaxf(color_brightness, w); + if (brightness == 0.0f) { + call.set_state(false); + } else { + color_brightness /= brightness; + w /= brightness; + + call.set_state(true); + call.set_color_mode_if_supported(this->color_mode_); + call.set_brightness_if_supported(brightness); + call.set_color_brightness_if_supported(color_brightness); + call.set_red_if_supported(r); + call.set_green_if_supported(g); + call.set_blue_if_supported(b); + call.set_white_if_supported(w); + call.set_warm_white_if_supported(w); + call.set_cold_white_if_supported(w); + } call.set_transition_length_if_supported(0); call.set_publish(false); call.set_save(false); @@ -50,6 +120,7 @@ class AddressableLightWrapper : public light::AddressableLight { light::LightState *light_state_; uint8_t *wrapper_state_; + ColorMode color_mode_{ColorMode::UNKNOWN}; }; } // namespace light From 73940bc1bd421df14d66ac3ae67fac40e28b9775 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Thu, 14 Oct 2021 11:25:10 +0200 Subject: [PATCH 009/273] Don't define UART_SELECTION_UART2 when UART2 is unavailable (#2512) --- esphome/components/logger/logger.cpp | 4 ++-- esphome/components/logger/logger.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index b38c7f1a69..97ad4c2cb9 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -153,7 +153,7 @@ void Logger::pre_setup() { case UART_SELECTION_UART1: this->hw_serial_ = &Serial1; break; -#if defined(USE_ESP32) && !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32S2 +#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) case UART_SELECTION_UART2: this->hw_serial_ = &Serial2; break; @@ -169,7 +169,7 @@ void Logger::pre_setup() { case UART_SELECTION_UART1: uart_num_ = UART_NUM_1; break; -#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32S2 +#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) case UART_SELECTION_UART2: uart_num_ = UART_NUM_2; break; diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index e6fa6e2058..8756bc2387 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -24,7 +24,7 @@ namespace logger { enum UARTSelection { UART_SELECTION_UART0 = 0, UART_SELECTION_UART1, -#ifdef USE_ESP32 +#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) UART_SELECTION_UART2, #endif #ifdef USE_ESP8266 From 10c6601b0abe4138a4292679208d917f290df9b4 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Thu, 14 Oct 2021 23:31:52 +1300 Subject: [PATCH 010/273] Revert "Added test for bme680_bsec" (#2518) This reverts commit 7f6a50d291b14935b17802b4dce52135fad1e49e due to BSEC library license restrictions. --- tests/test1.yaml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/test1.yaml b/tests/test1.yaml index 62fc781eca..157ccfc5d1 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -265,9 +265,6 @@ wled: adalight: -bme680_bsec: - i2c_id: i2c_bus - esp32_ble_tracker: ble_client: @@ -481,19 +478,6 @@ sensor: duration: 150ms update_interval: 15s i2c_id: i2c_bus - - platform: bme680_bsec - temperature: - name: "BME680 Temperature" - pressure: - name: "BME680 Pressure" - humidity: - name: "BME680 Humidity" - iaq: - name: "BME680 IAQ" - co2_equivalent: - name: "BME680 CO2 Equivalent" - breath_voc_equivalent: - name: "BME680 Breath VOC Equivalent" - platform: bmp085 temperature: name: 'Outside Temperature' From d4e65eb82ad972bafdf8a0017980a7b85e4c7e12 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 15 Oct 2021 09:42:44 +1300 Subject: [PATCH 011/273] Bump version to 2021.10.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 456b6d9209..eacbb19a7f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b2" +__version__ = "2021.10.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 15b5ea43a759a15d3dd0059aa58ff5c153a1ff80 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Thu, 14 Oct 2021 11:24:57 +0200 Subject: [PATCH 012/273] Add pressure compensation during runtime (#2493) Co-authored-by: Oxan van Leeuwen --- esphome/components/scd4x/scd4x.cpp | 133 ++++++++++++++++++----------- esphome/components/scd4x/scd4x.h | 9 +- esphome/components/scd4x/sensor.py | 9 +- 3 files changed, 97 insertions(+), 54 deletions(-) diff --git a/esphome/components/scd4x/scd4x.cpp b/esphome/components/scd4x/scd4x.cpp index c91fd5e882..eacb39edf1 100644 --- a/esphome/components/scd4x/scd4x.cpp +++ b/esphome/components/scd4x/scd4x.cpp @@ -1,4 +1,5 @@ #include "scd4x.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" namespace esphome { @@ -38,6 +39,7 @@ void SCD4XComponent::setup() { return; } + uint32_t stop_measurement_delay = 0; // In order to query the device periodic measurement must be ceased if (raw_read_status[0]) { ESP_LOGD(TAG, "Sensor has data available, stopping periodic measurement"); @@ -46,68 +48,72 @@ void SCD4XComponent::setup() { this->mark_failed(); return; } + // According to the SCD4x datasheet the sensor will only respond to other commands after waiting 500 ms after + // issuing the stop_periodic_measurement command + stop_measurement_delay = 500; } + this->set_timeout(stop_measurement_delay, [this]() { + if (!this->write_command_(SCD4X_CMD_GET_SERIAL_NUMBER)) { + ESP_LOGE(TAG, "Failed to write get serial command"); + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } - if (!this->write_command_(SCD4X_CMD_GET_SERIAL_NUMBER)) { - ESP_LOGE(TAG, "Failed to write get serial command"); - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); - return; - } + uint16_t raw_serial_number[3]; + if (!this->read_data_(raw_serial_number, 3)) { + ESP_LOGE(TAG, "Failed to read serial number"); + this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED; + this->mark_failed(); + return; + } + ESP_LOGD(TAG, "Serial number %02d.%02d.%02d", (uint16_t(raw_serial_number[0]) >> 8), + uint16_t(raw_serial_number[0] & 0xFF), (uint16_t(raw_serial_number[1]) >> 8)); - uint16_t raw_serial_number[3]; - if (!this->read_data_(raw_serial_number, 3)) { - ESP_LOGE(TAG, "Failed to read serial number"); - this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED; - this->mark_failed(); - return; - } - ESP_LOGD(TAG, "Serial number %02d.%02d.%02d", (uint16_t(raw_serial_number[0]) >> 8), - uint16_t(raw_serial_number[0] & 0xFF), (uint16_t(raw_serial_number[1]) >> 8)); - - if (!this->write_command_(SCD4X_CMD_TEMPERATURE_OFFSET, - (uint16_t)(temperature_offset_ * SCD4X_TEMPERATURE_OFFSET_MULTIPLIER))) { - ESP_LOGE(TAG, "Error setting temperature offset."); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); - return; - } - - // If pressure compensation available use it - // else use altitude - if (ambient_pressure_compensation_) { - if (!this->write_command_(SCD4X_CMD_AMBIENT_PRESSURE_COMPENSATION, ambient_pressure_compensation_)) { - ESP_LOGE(TAG, "Error setting ambient pressure compensation."); + if (!this->write_command_(SCD4X_CMD_TEMPERATURE_OFFSET, + (uint16_t)(temperature_offset_ * SCD4X_TEMPERATURE_OFFSET_MULTIPLIER))) { + ESP_LOGE(TAG, "Error setting temperature offset."); this->error_code_ = MEASUREMENT_INIT_FAILED; this->mark_failed(); return; } - } else { - if (!this->write_command_(SCD4X_CMD_ALTITUDE_COMPENSATION, altitude_compensation_)) { - ESP_LOGE(TAG, "Error setting altitude compensation."); + + // If pressure compensation available use it + // else use altitude + if (ambient_pressure_compensation_) { + if (!this->update_ambient_pressure_compensation_(ambient_pressure_)) { + ESP_LOGE(TAG, "Error setting ambient pressure compensation."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } + } else { + if (!this->write_command_(SCD4X_CMD_ALTITUDE_COMPENSATION, altitude_compensation_)) { + ESP_LOGE(TAG, "Error setting altitude compensation."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } + } + + if (!this->write_command_(SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION, enable_asc_ ? 1 : 0)) { + ESP_LOGE(TAG, "Error setting automatic self calibration."); this->error_code_ = MEASUREMENT_INIT_FAILED; this->mark_failed(); return; } - } - if (!this->write_command_(SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION, enable_asc_ ? 1 : 0)) { - ESP_LOGE(TAG, "Error setting automatic self calibration."); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); - return; - } + // Finally start sensor measurements + if (!this->write_command_(SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS)) { + ESP_LOGE(TAG, "Error starting continuous measurements."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } - // Finally start sensor measurements - if (!this->write_command_(SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS)) { - ESP_LOGE(TAG, "Error starting continuous measurements."); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); - return; - } - - initialized_ = true; - ESP_LOGD(TAG, "Sensor initialized"); + initialized_ = true; + ESP_LOGD(TAG, "Sensor initialized"); + }); }); } @@ -150,6 +156,13 @@ void SCD4XComponent::update() { return; } + if (this->ambient_pressure_source_ != nullptr) { + float pressure = this->ambient_pressure_source_->state / 1000.0f; + if (!std::isnan(pressure)) { + set_ambient_pressure_compensation(this->ambient_pressure_source_->state / 1000.0f); + } + } + // Check if data is ready if (!this->write_command_(SCD4X_CMD_GET_DATA_READY_STATUS)) { this->status_set_warning(); @@ -191,6 +204,28 @@ void SCD4XComponent::update() { this->status_clear_warning(); } +// Note pressure in bar here. Convert to hPa +void SCD4XComponent::set_ambient_pressure_compensation(float pressure_in_bar) { + ambient_pressure_compensation_ = true; + uint16_t new_ambient_pressure = (uint16_t)(pressure_in_bar * 1000); + // remove millibar from comparison to avoid frequent updates +/- 10 millibar doesn't matter + if (initialized_ && (new_ambient_pressure / 10 != ambient_pressure_ / 10)) { + update_ambient_pressure_compensation_(new_ambient_pressure); + ambient_pressure_ = new_ambient_pressure; + } else { + ESP_LOGD(TAG, "ambient pressure compensation skipped - no change required"); + } +} + +bool SCD4XComponent::update_ambient_pressure_compensation_(uint16_t pressure_in_hpa) { + if (this->write_command_(SCD4X_CMD_AMBIENT_PRESSURE_COMPENSATION, pressure_in_hpa)) { + ESP_LOGD(TAG, "setting ambient pressure compensation to %d hPa", pressure_in_hpa); + return true; + } else { + ESP_LOGE(TAG, "Error setting ambient pressure compensation."); + return false; + } +} uint8_t SCD4XComponent::sht_crc_(uint8_t data1, uint8_t data2) { uint8_t bit; diff --git a/esphome/components/scd4x/scd4x.h b/esphome/components/scd4x/scd4x.h index 3c428b8623..4fe2bf14cc 100644 --- a/esphome/components/scd4x/scd4x.h +++ b/esphome/components/scd4x/scd4x.h @@ -18,10 +18,8 @@ class SCD4XComponent : public PollingComponent, public i2c::I2CDevice { void set_automatic_self_calibration(bool asc) { enable_asc_ = asc; } void set_altitude_compensation(uint16_t altitude) { altitude_compensation_ = altitude; } - void set_ambient_pressure_compensation(float pressure) { - ambient_pressure_compensation_ = true; - ambient_pressure_ = (uint16_t)(pressure * 1000); - } + void set_ambient_pressure_compensation(float pressure_in_bar); + void set_ambient_pressure_source(sensor::Sensor *pressure) { ambient_pressure_source_ = pressure; } void set_temperature_offset(float offset) { temperature_offset_ = offset; }; void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; } @@ -33,6 +31,7 @@ class SCD4XComponent : public PollingComponent, public i2c::I2CDevice { bool read_data_(uint16_t *data, uint8_t len); bool write_command_(uint16_t command); bool write_command_(uint16_t command, uint16_t data); + bool update_ambient_pressure_compensation_(uint16_t pressure_in_hpa); ERRORCODE error_code_; @@ -47,6 +46,8 @@ class SCD4XComponent : public PollingComponent, public i2c::I2CDevice { sensor::Sensor *co2_sensor_{nullptr}; sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *humidity_sensor_{nullptr}; + // used for compensation + sensor::Sensor *ambient_pressure_source_{nullptr}; }; } // namespace scd4x diff --git a/esphome/components/scd4x/sensor.py b/esphome/components/scd4x/sensor.py index 0b1a960f6f..3e814ffe78 100644 --- a/esphome/components/scd4x/sensor.py +++ b/esphome/components/scd4x/sensor.py @@ -29,6 +29,7 @@ CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration" CONF_ALTITUDE_COMPENSATION = "altitude_compensation" CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation" CONF_TEMPERATURE_OFFSET = "temperature_offset" +CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE = "ambient_pressure_compensation_source" CONFIG_SCHEMA = ( cv.Schema( @@ -62,6 +63,9 @@ CONFIG_SCHEMA = ( ), cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION): cv.pressure, cv.Optional(CONF_TEMPERATURE_OFFSET, default="4°C"): cv.temperature, + cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE): cv.use_id( + sensor.Sensor + ), } ) .extend(cv.polling_component_schema("60s")) @@ -92,7 +96,10 @@ async def to_code(config): cg.add(getattr(var, funcName)(config[key])) for key, funcName in SENSOR_MAP.items(): - if key in config: sens = await sensor.new_sensor(config[key]) cg.add(getattr(var, funcName)(sens)) + + if CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE in config: + sens = await cg.get_variable(config[CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE]) + cg.add(var.set_ambient_pressure_source(sens)) From c3a8a044b93b9d53b1a5a2fb451ac18a5bb62196 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 15 Oct 2021 02:26:26 -0500 Subject: [PATCH 013/273] Fix Nextion HTTPClient error for ESP32 (#2524) --- esphome/components/nextion/display.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index f4b35fd56f..d95810bfbe 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -8,7 +8,7 @@ from esphome.const import ( CONF_BRIGHTNESS, CONF_TRIGGER_ID, ) - +from esphome.core import CORE from . import Nextion, nextion_ns, nextion_ref from .base_component import ( CONF_ON_SLEEP, @@ -76,6 +76,9 @@ async def to_code(config): if CONF_TFT_URL in config: cg.add_define("USE_NEXTION_TFT_UPLOAD") cg.add(var.set_tft_url(config[CONF_TFT_URL])) + if CORE.is_esp32: + cg.add_library("WiFiClientSecure", None) + cg.add_library("HTTPClient", None) if CONF_TOUCH_SLEEP_TIMEOUT in config: cg.add(var.set_touch_sleep_timeout_internal(config[CONF_TOUCH_SLEEP_TIMEOUT])) From 98755f36212d689bbc3fc7abc227c2587fc8e282 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Fri, 15 Oct 2021 09:27:56 +0200 Subject: [PATCH 014/273] Fix bug in register name definition (#2526) --- esphome/components/modbus_controller/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 7a69029dab..6b452ea25c 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -38,7 +38,7 @@ ModbusRegisterType_ns = modbus_controller_ns.namespace("ModbusRegisterType") ModbusRegisterType = ModbusRegisterType_ns.enum("ModbusRegisterType") MODBUS_REGISTER_TYPE = { "coil": ModbusRegisterType.COIL, - "discrete_input": ModbusRegisterType.DISCRETE, + "discrete_input": ModbusRegisterType.DISCRETE_INPUT, "holding": ModbusRegisterType.HOLDING, "read": ModbusRegisterType.READ, } From 4dd1bf920d824aa4bae6ca6836859264e05b567f Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Fri, 15 Oct 2021 22:07:05 +0200 Subject: [PATCH 015/273] Replace framework version_hint with source option (#2529) --- esphome/components/esp32/__init__.py | 134 +++++++++++-------------- esphome/components/esp8266/__init__.py | 75 ++++++-------- esphome/core/config.py | 11 +- 3 files changed, 97 insertions(+), 123 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 09eabe1fa7..8a13468c76 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -7,6 +7,7 @@ from esphome.helpers import write_file_if_changed from esphome.const import ( CONF_BOARD, CONF_FRAMEWORK, + CONF_SOURCE, CONF_TYPE, CONF_VARIANT, CONF_VERSION, @@ -53,7 +54,7 @@ def set_core_data(config): elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO: CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( - config[CONF_FRAMEWORK][CONF_VERSION_HINT] + config[CONF_FRAMEWORK][CONF_VERSION] ) CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD] CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT] @@ -94,6 +95,13 @@ def _format_framework_arduino_version(ver: cv.Version) -> str: return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" +def _format_framework_espidf_version(ver: cv.Version) -> str: + # format the given arduino (https://github.com/espressif/esp-idf/releases) version to + # a PIO platformio/framework-espidf value + # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf + return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + + # NOTE: Keep this in mind when updating the recommended version: # * New framework historically have had some regressions, especially for WiFi. # The new version needs to be thoroughly validated before changing the @@ -123,119 +131,97 @@ ESP_IDF_PLATFORM_VERSION = cv.Version(3, 3, 2) def _arduino_check_versions(value): value = value.copy() lookups = { - "dev": ("https://github.com/espressif/arduino-esp32.git", cv.Version(2, 0, 0)), - "latest": ("", cv.Version(1, 0, 3)), - "recommended": ( - _format_framework_arduino_version(RECOMMENDED_ARDUINO_FRAMEWORK_VERSION), - RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, - ), + "dev": (cv.Version(2, 0, 0), "https://github.com/espressif/arduino-esp32.git"), + "latest": (cv.Version(1, 0, 6), None), + "recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None), } - ver_value = value[CONF_VERSION] - default_ver_hint = None - if ver_value.lower() in lookups: - default_ver_hint = str(lookups[ver_value.lower()][1]) - ver_value = lookups[ver_value.lower()][0] + + if value[CONF_VERSION] in lookups: + if CONF_SOURCE in value: + raise cv.Invalid( + "Framework version needs to be explicitly specified when custom source is used." + ) + + version, source = lookups[value[CONF_VERSION]] else: - with cv.suppress_invalid(): - ver = cv.Version.parse(cv.version_number(value)) - if ver <= cv.Version(1, 0, 3): - ver_value = f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" - else: - ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" - default_ver_hint = str(ver) - value[CONF_VERSION] = ver_value + version = cv.Version.parse(cv.version_number(value[CONF_VERSION])) + source = value.get(CONF_SOURCE, None) - if CONF_VERSION_HINT not in value and default_ver_hint is None: - raise cv.Invalid("Needs a version hint to understand the framework version") + value[CONF_VERSION] = str(version) + value[CONF_SOURCE] = source or _format_framework_arduino_version(version) - ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint) - value[CONF_VERSION_HINT] = ver_hint_s - plat_ver = value.get(CONF_PLATFORM_VERSION, ARDUINO_PLATFORM_VERSION) - value[CONF_PLATFORM_VERSION] = str(plat_ver) + platform_version = value.get(CONF_PLATFORM_VERSION, ARDUINO_PLATFORM_VERSION) + value[CONF_PLATFORM_VERSION] = str(platform_version) - if cv.Version.parse(ver_hint_s) != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: + if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: _LOGGER.warning( - "The selected arduino framework version is not the recommended one" - ) - _LOGGER.warning( - "If there are connectivity or build issues please remove the manual version" + "The selected Arduino framework version is not the recommended one. " + "If there are connectivity or build issues please remove the manual version." ) return value -def _format_framework_espidf_version(ver: cv.Version) -> str: - # format the given arduino (https://github.com/espressif/esp-idf/releases) version to - # a PIO platformio/framework-espidf value - # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf - return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" - - def _esp_idf_check_versions(value): value = value.copy() lookups = { - "dev": ("https://github.com/espressif/esp-idf.git", cv.Version(4, 3, 1)), - "latest": ("", cv.Version(4, 3, 0)), - "recommended": ( - _format_framework_espidf_version(RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION), - RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, - ), + "dev": (cv.Version(4, 3, 1), "https://github.com/espressif/esp-idf.git"), + "latest": (cv.Version(4, 3, 0), None), + "recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None), } - ver_value = value[CONF_VERSION] - default_ver_hint = None - if ver_value.lower() in lookups: - default_ver_hint = str(lookups[ver_value.lower()][1]) - ver_value = lookups[ver_value.lower()][0] + + if value[CONF_VERSION] in lookups: + if CONF_SOURCE in value: + raise cv.Invalid( + "Framework version needs to be explicitly specified when custom source is used." + ) + + version, source = lookups[value[CONF_VERSION]] else: - with cv.suppress_invalid(): - ver = cv.Version.parse(cv.version_number(value)) - ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" - default_ver_hint = str(ver) - value[CONF_VERSION] = ver_value + version = cv.Version.parse(cv.version_number(value[CONF_VERSION])) + source = value.get(CONF_SOURCE, None) - if CONF_VERSION_HINT not in value and default_ver_hint is None: - raise cv.Invalid("Needs a version hint to understand the framework version") + if version < cv.Version(4, 0, 0): + raise cv.Invalid("Only ESP-IDF 4.0+ is supported.") - ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint) - value[CONF_VERSION_HINT] = ver_hint_s - if cv.Version.parse(ver_hint_s) < cv.Version(4, 0, 0): - raise cv.Invalid("Only ESP-IDF 4.0+ is supported") - if cv.Version.parse(ver_hint_s) != RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION: + value[CONF_VERSION] = str(version) + value[CONF_SOURCE] = source or _format_framework_espidf_version(version) + + platform_version = value.get(CONF_PLATFORM_VERSION, ESP_IDF_PLATFORM_VERSION) + value[CONF_PLATFORM_VERSION] = str(platform_version) + + if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: _LOGGER.warning( - "The selected esp-idf framework version is not the recommended one" + "The selected ESP-IDF framework version is not the recommended one. " + "If there are connectivity or build issues please remove the manual version." ) - _LOGGER.warning( - "If there are connectivity or build issues please remove the manual version" - ) - - plat_ver = value.get(CONF_PLATFORM_VERSION, ESP_IDF_PLATFORM_VERSION) - value[CONF_PLATFORM_VERSION] = str(plat_ver) return value -CONF_VERSION_HINT = "version_hint" CONF_PLATFORM_VERSION = "platform_version" + ARDUINO_FRAMEWORK_SCHEMA = cv.All( cv.Schema( { cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, - cv.Optional(CONF_VERSION_HINT): cv.version_number, + cv.Optional(CONF_SOURCE): cv.string_strict, cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, } ), _arduino_check_versions, ) + CONF_SDKCONFIG_OPTIONS = "sdkconfig_options" ESP_IDF_FRAMEWORK_SCHEMA = cv.All( cv.Schema( { cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, - cv.Optional(CONF_VERSION_HINT): cv.version_number, + cv.Optional(CONF_SOURCE): cv.string_strict, + cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): { cv.string_strict: cv.string_strict }, - cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, cv.Optional(CONF_ADVANCED, default={}): cv.Schema( { cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC, default=False): cv.boolean, @@ -293,7 +279,7 @@ async def to_code(config): cg.add_build_flag("-Wno-nonnull-compare") cg.add_platformio_option( "platform_packages", - [f"platformio/framework-espidf @ {conf[CONF_VERSION]}"], + [f"platformio/framework-espidf @ {conf[CONF_SOURCE]}"], ) add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False) add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True) @@ -323,7 +309,7 @@ async def to_code(config): cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO") cg.add_platformio_option( "platform_packages", - [f"platformio/framework-arduinoespressif32 @ {conf[CONF_VERSION]}"], + [f"platformio/framework-arduinoespressif32 @ {conf[CONF_SOURCE]}"], ) cg.add_platformio_option("board_build.partitions", "partitions.csv") diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 93a461ba1f..a5323db8bf 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -4,6 +4,7 @@ from esphome.const import ( CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_FRAMEWORK, + CONF_SOURCE, CONF_VERSION, KEY_CORE, KEY_FRAMEWORK_VERSION, @@ -31,7 +32,7 @@ def set_core_data(config): CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp8266" CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( - config[CONF_FRAMEWORK][CONF_VERSION_HINT] + config[CONF_FRAMEWORK][CONF_VERSION] ) CORE.data[KEY_ESP8266][KEY_BOARD] = config[CONF_BOARD] return config @@ -70,66 +71,50 @@ ARDUINO_3_PLATFORM_VERSION = cv.Version(3, 0, 2) def _arduino_check_versions(value): value = value.copy() lookups = { - "dev": ("https://github.com/esp8266/Arduino.git", cv.Version(3, 0, 2)), - "latest": ("", cv.Version(3, 0, 2)), - "recommended": ( - _format_framework_arduino_version(RECOMMENDED_ARDUINO_FRAMEWORK_VERSION), - RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, - ), + "dev": (cv.Version(3, 0, 2), "https://github.com/esp8266/Arduino.git"), + "latest": (cv.Version(3, 0, 2), None), + "recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None), } - ver_value = value[CONF_VERSION] - default_ver_hint = None - if ver_value.lower() in lookups: - default_ver_hint = str(lookups[ver_value.lower()][1]) - ver_value = lookups[ver_value.lower()][0] + + if value[CONF_VERSION] in lookups: + if CONF_SOURCE in value: + raise cv.Invalid( + "Framework version needs to be explicitly specified when custom source is used." + ) + + version, source = lookups[value[CONF_VERSION]] else: - with cv.suppress_invalid(): - ver = cv.Version.parse(cv.version_number(value)) - if ver <= cv.Version(2, 4, 1): - ver_value = f"~1.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" - elif ver <= cv.Version(2, 6, 2): - ver_value = f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" - else: - ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" - default_ver_hint = str(ver) + version = cv.Version.parse(cv.version_number(value[CONF_VERSION])) + source = value.get(CONF_SOURCE, None) - value[CONF_VERSION] = ver_value + value[CONF_VERSION] = str(version) + value[CONF_SOURCE] = source or _format_framework_arduino_version(version) - if CONF_VERSION_HINT not in value and default_ver_hint is None: - raise cv.Invalid("Needs a version hint to understand the framework version") - - ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint) - value[CONF_VERSION_HINT] = ver_hint_s - plat_ver = value.get(CONF_PLATFORM_VERSION) - - if plat_ver is None: - ver_hint = cv.Version.parse(ver_hint_s) - if ver_hint >= cv.Version(3, 0, 0): - plat_ver = ARDUINO_3_PLATFORM_VERSION - elif ver_hint >= cv.Version(2, 5, 0): - plat_ver = ARDUINO_2_PLATFORM_VERSION + platform_version = value.get(CONF_PLATFORM_VERSION) + if platform_version is None: + if version >= cv.Version(3, 0, 0): + platform_version = ARDUINO_3_PLATFORM_VERSION + elif version >= cv.Version(2, 5, 0): + platform_version = ARDUINO_2_PLATFORM_VERSION else: - plat_ver = cv.Version(1, 8, 0) - value[CONF_PLATFORM_VERSION] = str(plat_ver) + platform_version = cv.Version(1, 8, 0) + value[CONF_PLATFORM_VERSION] = str(platform_version) - if cv.Version.parse(ver_hint_s) != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: + if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: _LOGGER.warning( - "The selected arduino framework version is not the recommended one" - ) - _LOGGER.warning( - "If there are connectivity or build issues please remove the manual version" + "The selected Arduino framework version is not the recommended one. " + "If there are connectivity or build issues please remove the manual version." ) return value -CONF_VERSION_HINT = "version_hint" CONF_PLATFORM_VERSION = "platform_version" ARDUINO_FRAMEWORK_SCHEMA = cv.All( cv.Schema( { cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, - cv.Optional(CONF_VERSION_HINT): cv.version_number, + cv.Optional(CONF_SOURCE): cv.string_strict, cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, } ), @@ -167,7 +152,7 @@ async def to_code(config): cg.add_build_flag("-DUSE_ESP8266_FRAMEWORK_ARDUINO") cg.add_platformio_option( "platform_packages", - [f"platformio/framework-arduinoespressif8266 @ {conf[CONF_VERSION]}"], + [f"platformio/framework-arduinoespressif8266 @ {conf[CONF_SOURCE]}"], ) cg.add_platformio_option( "platform", f"platformio/espressif8266 @ {conf[CONF_PLATFORM_VERSION]}" diff --git a/esphome/core/config.py b/esphome/core/config.py index bbdfcf124c..c495fefddd 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -23,6 +23,7 @@ from esphome.const import ( CONF_PLATFORMIO_OPTIONS, CONF_PRIORITY, CONF_PROJECT, + CONF_SOURCE, CONF_TRIGGER_ID, CONF_TYPE, CONF_VERSION, @@ -181,10 +182,12 @@ def preload_core_config(config, result): if CONF_BOARD_FLASH_MODE in conf: plat_conf[CONF_BOARD_FLASH_MODE] = conf.pop(CONF_BOARD_FLASH_MODE) if CONF_ARDUINO_VERSION in conf: - plat_conf[CONF_FRAMEWORK] = { - CONF_TYPE: "arduino", - CONF_VERSION: conf.pop(CONF_ARDUINO_VERSION), - } + plat_conf[CONF_FRAMEWORK] = {CONF_TYPE: "arduino"} + try: + cv.Version.parse(conf[CONF_ARDUINO_VERSION]) + plat_conf[CONF_FRAMEWORK][CONF_VERSION] = conf.pop(CONF_ARDUINO_VERSION) + except ValueError: + plat_conf[CONF_FRAMEWORK][CONF_SOURCE] = conf.pop(CONF_ARDUINO_VERSION) if CONF_BOARD in conf: plat_conf[CONF_BOARD] = conf.pop(CONF_BOARD) # Insert generated target platform config to main config From f83950fd75e0c0156484c7d54ac556b762a7f636 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 17 Oct 2021 08:53:49 +0200 Subject: [PATCH 016/273] Fix bitshift on read in ADE7953 (#2537) --- esphome/components/ade7953/ade7953.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/ade7953/ade7953.h b/esphome/components/ade7953/ade7953.h index c6fb383ed8..bb160cd8eb 100644 --- a/esphome/components/ade7953/ade7953.h +++ b/esphome/components/ade7953/ade7953.h @@ -76,9 +76,9 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent { return err; *value = 0; *value |= ((uint32_t) recv[0]) << 24; - *value |= ((uint32_t) recv[1]) << 24; - *value |= ((uint32_t) recv[2]) << 24; - *value |= ((uint32_t) recv[3]) << 24; + *value |= ((uint32_t) recv[1]) << 16; + *value |= ((uint32_t) recv[2]) << 8; + *value |= ((uint32_t) recv[3]); return i2c::ERROR_OK; } From db3fa1ade72a253d4ed90d95c05f803b0d890ff3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sun, 17 Oct 2021 19:54:09 +1300 Subject: [PATCH 017/273] Allow downloading all bin files from backend in dashboard (#2514) Co-authored-by: Otto Winter --- esphome/__main__.py | 29 +++++++++++- esphome/dashboard/dashboard.py | 82 +++++++++++++++++++++++++++++----- 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index feb95e93c7..1b9c601091 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -180,7 +180,11 @@ def compile_program(args, config): from esphome import platformio_api _LOGGER.info("Compiling app...") - return platformio_api.run_compile(config, CORE.verbose) + rc = platformio_api.run_compile(config, CORE.verbose) + if rc != 0: + return rc + idedata = platformio_api.get_idedata(config) + return 0 if idedata is not None else 1 def upload_using_esptool(config, port): @@ -458,6 +462,21 @@ def command_update_all(args): return failed +def command_idedata(args, config): + from esphome import platformio_api + import json + + logging.disable(logging.INFO) + logging.disable(logging.WARNING) + + idedata = platformio_api.get_idedata(config) + if idedata is None: + return 1 + + print(json.dumps(idedata.raw, indent=2) + "\n") + return 0 + + PRE_CONFIG_ACTIONS = { "wizard": command_wizard, "version": command_version, @@ -475,6 +494,7 @@ POST_CONFIG_ACTIONS = { "clean-mqtt": command_clean_mqtt, "mqtt-fingerprint": command_mqtt_fingerprint, "clean": command_clean, + "idedata": command_idedata, } @@ -650,6 +670,11 @@ def parse_args(argv): "configuration", help="Your YAML configuration file directories.", nargs="+" ) + parser_idedata = subparsers.add_parser("idedata") + parser_idedata.add_argument( + "configuration", help="Your YAML configuration file(s).", nargs=1 + ) + # Keep backward compatibility with the old command line format of # esphome . # @@ -762,7 +787,7 @@ def run_esphome(argv): config = read_config(dict(args.substitution) if args.substitution else {}) if config is None: - return 1 + return 2 CORE.config = config if args.command not in POST_CONFIG_ACTIONS: diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index eb698a7de1..501666b100 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -9,6 +9,7 @@ import json import logging import multiprocessing import os +from pathlib import Path import secrets import shutil import subprocess @@ -26,7 +27,7 @@ import tornado.process import tornado.web import tornado.websocket -from esphome import const, util +from esphome import const, platformio_api, util from esphome.helpers import mkdir_p, get_bool_env, run_system_command from esphome.storage_json import ( EsphomeStorageJSON, @@ -398,17 +399,45 @@ class DownloadBinaryRequestHandler(BaseHandler): @authenticated @bind_config def get(self, configuration=None): - # pylint: disable=no-value-for-parameter - storage_path = ext_storage_path(settings.config_dir, configuration) - storage_json = StorageJSON.load(storage_path) - if storage_json is None: - self.send_error() + type = self.get_argument("type", "firmware.bin") + + if type == "firmware.bin": + storage_path = ext_storage_path(settings.config_dir, configuration) + storage_json = StorageJSON.load(storage_path) + if storage_json is None: + self.send_error(404) + return + filename = f"{storage_json.name}.bin" + path = storage_json.firmware_bin_path + + else: + args = ["esphome", "idedata", settings.rel_path(configuration)] + rc, stdout, _ = run_system_command(*args) + + if rc != 0: + self.send_error(404 if rc == 2 else 500) + return + + idedata = platformio_api.IDEData(json.loads(stdout)) + + found = False + for image in idedata.extra_flash_images: + if image.path.endswith(type): + path = image.path + filename = type + found = True + break + + if not found: + self.send_error(404) + return + + self.set_header("Content-Type", "application/octet-stream") + self.set_header("Content-Disposition", f'attachment; filename="{filename}"') + if not Path(path).is_file(): + self.send_error(404) return - path = storage_json.firmware_bin_path - self.set_header("Content-Type", "application/octet-stream") - filename = f"{storage_json.name}.bin" - self.set_header("Content-Disposition", f'attachment; filename="{filename}"') with open(path, "rb") as f: while True: data = f.read(16384) @@ -418,6 +447,38 @@ class DownloadBinaryRequestHandler(BaseHandler): self.finish() +class ManifestRequestHandler(BaseHandler): + @authenticated + @bind_config + def get(self, configuration=None): + args = ["esphome", "idedata", settings.rel_path(configuration)] + rc, stdout, _ = run_system_command(*args) + + if rc != 0: + self.send_error(404 if rc == 2 else 500) + return + + idedata = platformio_api.IDEData(json.loads(stdout)) + + firmware_offset = "0x10000" if idedata.extra_flash_images else "0x0" + flash_images = [ + { + "path": f"./download.bin?configuration={configuration}&type=firmware.bin", + "offset": firmware_offset, + } + ] + [ + { + "path": f"./download.bin?configuration={configuration}&type={os.path.basename(image.path)}", + "offset": image.offset, + } + for image in idedata.extra_flash_images + ] + + self.set_header("Content-Type", "application/json") + self.write(json.dumps(flash_images)) + self.finish() + + def _list_dashboard_entries(): files = settings.list_yaml_files() return [DashboardEntry(file) for file in files] @@ -862,6 +923,7 @@ def make_app(debug=get_bool_env(ENV_DEV)): (f"{rel}info", InfoRequestHandler), (f"{rel}edit", EditRequestHandler), (f"{rel}download.bin", DownloadBinaryRequestHandler), + (f"{rel}manifest.json", ManifestRequestHandler), (f"{rel}serial-ports", SerialPortRequestHandler), (f"{rel}ping", PingRequestHandler), (f"{rel}delete", DeleteRequestHandler), From f045382d2095faa3149c5272129da521fa68ebd4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 17 Oct 2021 00:26:59 -0700 Subject: [PATCH 018/273] Bump dashboard to 20211015.0 (#2525) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 23a00d3755..8028007a0a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.1 esptool==3.1 click==8.0.3 -esphome-dashboard==20211011.1 +esphome-dashboard==20211015.0 aioesphomeapi==9.1.5 # esp-idf requires this, but doesn't bundle it by default From 4b44280d5392d6b1f1e3722993a405bfe268d3b5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sun, 17 Oct 2021 20:34:07 +1300 Subject: [PATCH 019/273] Bump version to 2021.10.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index eacbb19a7f..b505087115 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b3" +__version__ = "2021.10.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 70b62f272eb2eeb7639f641240a20f02a1ffaefa Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sun, 17 Oct 2021 21:01:51 +0200 Subject: [PATCH 020/273] Only show timestamp for dashboard access logs (#2540) --- esphome/__main__.py | 7 ++++++- esphome/log.py | 14 ++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 1b9c601091..97059154fd 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -758,7 +758,12 @@ def run_esphome(argv): args = parse_args(argv) CORE.dashboard = args.dashboard - setup_log(args.verbose, args.quiet) + setup_log( + args.verbose, + args.quiet, + # Show timestamp for dashboard access logs + args.command == "dashboard", + ) if args.deprecated_argv_suggestion is not None and args.command != "vscode": _LOGGER.warning( "Calling ESPHome with the configuration before the command is deprecated " diff --git a/esphome/log.py b/esphome/log.py index abefcf6308..e7ba0fdd82 100644 --- a/esphome/log.py +++ b/esphome/log.py @@ -49,8 +49,10 @@ def color(col: str, msg: str, reset: bool = True) -> bool: class ESPHomeLogFormatter(logging.Formatter): - def __init__(self): - super().__init__(fmt="%(asctime)s %(levelname)s %(message)s", style="%") + def __init__(self, *, include_timestamp: bool): + fmt = "%(asctime)s " if include_timestamp else "" + fmt += "%(levelname)s %(message)s" + super().__init__(fmt=fmt, style="%") def format(self, record): formatted = super().format(record) @@ -64,7 +66,9 @@ class ESPHomeLogFormatter(logging.Formatter): return f"{prefix}{formatted}{Style.RESET_ALL}" -def setup_log(debug=False, quiet=False): +def setup_log( + debug: bool = False, quiet: bool = False, include_timestamp: bool = False +) -> None: import colorama if debug: @@ -79,4 +83,6 @@ def setup_log(debug=False, quiet=False): logging.getLogger("urllib3").setLevel(logging.WARNING) colorama.init() - logging.getLogger().handlers[0].setFormatter(ESPHomeLogFormatter()) + logging.getLogger().handlers[0].setFormatter( + ESPHomeLogFormatter(include_timestamp=include_timestamp) + ) From 0524f8c677c864c7ebfbbe3bff2fad30cc949e91 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Oct 2021 09:55:35 +1300 Subject: [PATCH 021/273] Fix const used for IDF recommended version (#2542) --- esphome/components/esp32/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 8a13468c76..44e24b21d2 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -190,7 +190,7 @@ def _esp_idf_check_versions(value): platform_version = value.get(CONF_PLATFORM_VERSION, ESP_IDF_PLATFORM_VERSION) value[CONF_PLATFORM_VERSION] = str(platform_version) - if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: + if version != RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION: _LOGGER.warning( "The selected ESP-IDF framework version is not the recommended one. " "If there are connectivity or build issues please remove the manual version." From 63a9acaa19c6292610d92e1764ae6995883cd331 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Oct 2021 09:56:31 +1300 Subject: [PATCH 022/273] Fix Bluetooth setup_priorities (#2458) Co-authored-by: Otto Winter --- esphome/components/ble_client/ble_client.cpp | 2 ++ esphome/components/ble_client/ble_client.h | 1 + esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp | 2 +- esphome/components/esp32_ble_server/ble_server.cpp | 2 +- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp | 2 ++ esphome/components/esp32_ble_tracker/esp32_ble_tracker.h | 1 + 6 files changed, 8 insertions(+), 2 deletions(-) diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index 8ff516d735..e6cdb0c23d 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -11,6 +11,8 @@ namespace ble_client { static const char *const TAG = "ble_client"; +float BLEClient::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } + void BLEClient::setup() { auto ret = esp_ble_gattc_app_register(this->app_id); if (ret) { diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index 4a17ccb79b..23123914e8 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -81,6 +81,7 @@ class BLEClient : public espbt::ESPBTClient, public Component { void setup() override; void dump_config() override; void loop() override; + float get_setup_priority() const override; void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override; diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp index f6bab8e6df..955bc8595f 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp @@ -57,7 +57,7 @@ void ESP32BLEBeacon::setup() { ); } -float ESP32BLEBeacon::get_setup_priority() const { return setup_priority::DATA; } +float ESP32BLEBeacon::get_setup_priority() const { return setup_priority::BLUETOOTH; } void ESP32BLEBeacon::ble_core_task(void *params) { ble_setup(); diff --git a/esphome/components/esp32_ble_server/ble_server.cpp b/esphome/components/esp32_ble_server/ble_server.cpp index 0b91c238c3..e0fb80f94b 100644 --- a/esphome/components/esp32_ble_server/ble_server.cpp +++ b/esphome/components/esp32_ble_server/ble_server.cpp @@ -154,7 +154,7 @@ void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t ga } } -float BLEServer::get_setup_priority() const { return setup_priority::BLUETOOTH - 10; } +float BLEServer::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } void BLEServer::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 BLE Server:"); } diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 65749f5124..303cb34aa7 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -40,6 +40,8 @@ uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) { return u; } +float ESP32BLETracker::get_setup_priority() const { return setup_priority::BLUETOOTH; } + void ESP32BLETracker::setup() { global_esp32_ble_tracker = this; this->scan_result_lock_ = xSemaphoreCreateMutex(); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 1308119df5..02e102f06c 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -171,6 +171,7 @@ class ESP32BLETracker : public Component { /// Setup the FreeRTOS task and the Bluetooth stack. void setup() override; void dump_config() override; + float get_setup_priority() const override; void loop() override; From 723fb7eaac75cbfcea472db58bc311f66ee6ee77 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 18 Oct 2021 02:36:18 +0200 Subject: [PATCH 023/273] Autodetect ESP32 variant (#2530) Co-authored-by: Otto winter --- esphome/components/esp32/__init__.py | 25 ++++-- esphome/components/esp32/boards.py | 124 ++++++++++++++++++++++++++ esphome/components/logger/__init__.py | 3 +- 3 files changed, 144 insertions(+), 8 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 44e24b21d2..e1128ff227 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -27,13 +27,10 @@ from .const import ( # noqa KEY_ESP32, KEY_SDKCONFIG_OPTIONS, KEY_VARIANT, - VARIANT_ESP32, - VARIANT_ESP32S2, - VARIANT_ESP32S3, VARIANT_ESP32C3, - VARIANT_ESP32H2, VARIANTS, ) +from .boards import BOARD_TO_VARIANT # force import gpio to register pin schema from .gpio import esp32_pin_to_code # noqa @@ -199,6 +196,21 @@ def _esp_idf_check_versions(value): return value +def _detect_variant(value): + if CONF_VARIANT not in value: + board = value[CONF_BOARD] + if board not in BOARD_TO_VARIANT: + raise cv.Invalid( + "This board is unknown, please set the variant manually", + path=[CONF_BOARD], + ) + + value = value.copy() + value[CONF_VARIANT] = BOARD_TO_VARIANT[board] + + return value + + CONF_PLATFORM_VERSION = "platform_version" ARDUINO_FRAMEWORK_SCHEMA = cv.All( @@ -250,12 +262,11 @@ CONFIG_SCHEMA = cv.All( cv.Schema( { cv.Required(CONF_BOARD): cv.string_strict, - cv.Optional(CONF_VARIANT, default="ESP32"): cv.one_of( - *VARIANTS, upper=True - ), + cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True), cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA, } ), + _detect_variant, set_core_data, ) diff --git a/esphome/components/esp32/boards.py b/esphome/components/esp32/boards.py index ddf4bf2026..7f7bb2259f 100644 --- a/esphome/components/esp32/boards.py +++ b/esphome/components/esp32/boards.py @@ -1,3 +1,5 @@ +from .const import VARIANT_ESP32, VARIANT_ESP32S2, VARIANT_ESP32C3 + ESP32_BASE_PINS = { "TX": 1, "RX": 3, @@ -925,3 +927,125 @@ ESP32_BOARD_PINS = { }, "xinabox_cw02": {"LED": 27}, } + +""" +BOARD_TO_VARIANT generated with: + +git clone https://github.com/platformio/platform-espressif32 +for x in platform-espressif32/boards/*.json; do + mcu=$(jq -r .build.mcu <"$x"); + fname=$(basename "$x") + board="${fname%.*}" + variant=$(echo "$mcu" | tr '[:lower:]' '[:upper:]') + echo " \"$board\": VARIANT_${variant}," +done | sort +""" + +BOARD_TO_VARIANT = { + "alksesp32": VARIANT_ESP32, + "az-delivery-devkit-v4": VARIANT_ESP32, + "bpi-bit": VARIANT_ESP32, + "briki_abc_esp32": VARIANT_ESP32, + "briki_mbc-wb_esp32": VARIANT_ESP32, + "d-duino-32": VARIANT_ESP32, + "esp320": VARIANT_ESP32, + "esp32-c3-devkitm-1": VARIANT_ESP32C3, + "esp32cam": VARIANT_ESP32, + "esp32-devkitlipo": VARIANT_ESP32, + "esp32dev": VARIANT_ESP32, + "esp32doit-devkit-v1": VARIANT_ESP32, + "esp32doit-espduino": VARIANT_ESP32, + "esp32-evb": VARIANT_ESP32, + "esp32-gateway": VARIANT_ESP32, + "esp32-poe-iso": VARIANT_ESP32, + "esp32-poe": VARIANT_ESP32, + "esp32-pro": VARIANT_ESP32, + "esp32-s2-kaluga-1": VARIANT_ESP32S2, + "esp32-s2-saola-1": VARIANT_ESP32S2, + "esp32thing_plus": VARIANT_ESP32, + "esp32thing": VARIANT_ESP32, + "esp32vn-iot-uno": VARIANT_ESP32, + "espea32": VARIANT_ESP32, + "espectro32": VARIANT_ESP32, + "espino32": VARIANT_ESP32, + "esp-wrover-kit": VARIANT_ESP32, + "etboard": VARIANT_ESP32, + "featheresp32-s2": VARIANT_ESP32S2, + "featheresp32": VARIANT_ESP32, + "firebeetle32": VARIANT_ESP32, + "fm-devkit": VARIANT_ESP32, + "frogboard": VARIANT_ESP32, + "healthypi4": VARIANT_ESP32, + "heltec_wifi_kit_32_v2": VARIANT_ESP32, + "heltec_wifi_kit_32": VARIANT_ESP32, + "heltec_wifi_lora_32_V2": VARIANT_ESP32, + "heltec_wifi_lora_32": VARIANT_ESP32, + "heltec_wireless_stick_lite": VARIANT_ESP32, + "heltec_wireless_stick": VARIANT_ESP32, + "honeylemon": VARIANT_ESP32, + "hornbill32dev": VARIANT_ESP32, + "hornbill32minima": VARIANT_ESP32, + "imbrios-logsens-v1p1": VARIANT_ESP32, + "inex_openkb": VARIANT_ESP32, + "intorobot": VARIANT_ESP32, + "iotaap_magnolia": VARIANT_ESP32, + "iotbusio": VARIANT_ESP32, + "iotbusproteus": VARIANT_ESP32, + "kits-edu": VARIANT_ESP32, + "labplus_mpython": VARIANT_ESP32, + "lolin32_lite": VARIANT_ESP32, + "lolin32": VARIANT_ESP32, + "lolin_d32_pro": VARIANT_ESP32, + "lolin_d32": VARIANT_ESP32, + "lopy4": VARIANT_ESP32, + "lopy": VARIANT_ESP32, + "m5stack-atom": VARIANT_ESP32, + "m5stack-core2": VARIANT_ESP32, + "m5stack-core-esp32": VARIANT_ESP32, + "m5stack-coreink": VARIANT_ESP32, + "m5stack-fire": VARIANT_ESP32, + "m5stack-grey": VARIANT_ESP32, + "m5stack-timer-cam": VARIANT_ESP32, + "m5stick-c": VARIANT_ESP32, + "magicbit": VARIANT_ESP32, + "mgbot-iotik32a": VARIANT_ESP32, + "mgbot-iotik32b": VARIANT_ESP32, + "mhetesp32devkit": VARIANT_ESP32, + "mhetesp32minikit": VARIANT_ESP32, + "microduino-core-esp32": VARIANT_ESP32, + "nano32": VARIANT_ESP32, + "nina_w10": VARIANT_ESP32, + "node32s": VARIANT_ESP32, + "nodemcu-32s": VARIANT_ESP32, + "nscreen-32": VARIANT_ESP32, + "odroid_esp32": VARIANT_ESP32, + "onehorse32dev": VARIANT_ESP32, + "oroca_edubot": VARIANT_ESP32, + "pico32": VARIANT_ESP32, + "piranha_esp32": VARIANT_ESP32, + "pocket_32": VARIANT_ESP32, + "pycom_gpy": VARIANT_ESP32, + "qchip": VARIANT_ESP32, + "quantum": VARIANT_ESP32, + "sensesiot_weizen": VARIANT_ESP32, + "sg-o_airMon": VARIANT_ESP32, + "s_odi_ultra": VARIANT_ESP32, + "sparkfun_lora_gateway_1-channel": VARIANT_ESP32, + "tinypico": VARIANT_ESP32, + "ttgo-lora32-v1": VARIANT_ESP32, + "ttgo-lora32-v21": VARIANT_ESP32, + "ttgo-lora32-v2": VARIANT_ESP32, + "ttgo-t1": VARIANT_ESP32, + "ttgo-t7-v13-mini32": VARIANT_ESP32, + "ttgo-t7-v14-mini32": VARIANT_ESP32, + "ttgo-t-beam": VARIANT_ESP32, + "ttgo-t-watch": VARIANT_ESP32, + "turta_iot_node": VARIANT_ESP32, + "vintlabs-devkit-v1": VARIANT_ESP32, + "wemosbat": VARIANT_ESP32, + "wemos_d1_mini32": VARIANT_ESP32, + "wesp32": VARIANT_ESP32, + "widora-air": VARIANT_ESP32, + "wifiduino32": VARIANT_ESP32, + "xinabox_cw02": VARIANT_ESP32, +} diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index fe2a3ec8f8..20a0b0f792 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -19,7 +19,8 @@ from esphome.const import ( CONF_TX_BUFFER_SIZE, ) from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority -from esphome.components.esp32 import get_esp32_variant, VARIANT_ESP32S2, VARIANT_ESP32C3 +from esphome.components.esp32 import get_esp32_variant +from esphome.components.esp32.const import VARIANT_ESP32S2, VARIANT_ESP32C3 CODEOWNERS = ["@esphome/core"] logger_ns = cg.esphome_ns.namespace("logger") From b5734c2b208e7b5285baa75d94307162944f0493 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Oct 2021 15:31:01 +1300 Subject: [PATCH 024/273] Bump version to 2021.10.0b5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index b505087115..39616ca062 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b4" +__version__ = "2021.10.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 4a1e50fed171e44eb8f80cbd6e3ab9db7b394e2a Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Fri, 15 Oct 2021 22:06:32 +0200 Subject: [PATCH 025/273] OTA firmware MD5 check + password support for esp-idf (#2507) Co-authored-by: Maurice Makaay --- CODEOWNERS | 1 + esphome/components/md5/__init__.py | 1 + esphome/components/md5/md5.cpp | 51 ++++++++++++++++ esphome/components/md5/md5.h | 58 +++++++++++++++++++ esphome/components/ota/__init__.py | 12 +--- .../ota/ota_backend_arduino_esp32.h | 1 + .../components/ota/ota_backend_esp_idf.cpp | 12 +++- esphome/components/ota/ota_backend_esp_idf.h | 3 + esphome/components/ota/ota_component.cpp | 27 ++++----- esphome/core/defines.h | 1 + 10 files changed, 139 insertions(+), 28 deletions(-) create mode 100644 esphome/components/md5/__init__.py create mode 100644 esphome/components/md5/md5.cpp create mode 100644 esphome/components/md5/md5.h diff --git a/CODEOWNERS b/CODEOWNERS index 4c3084d463..a7cf3a1b68 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -89,6 +89,7 @@ esphome/components/mcp23x17_base/* @jesserockz esphome/components/mcp23xxx_base/* @jesserockz esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp9808/* @k7hpn +esphome/components/md5/* @esphome/core esphome/components/mdns/* @esphome/core esphome/components/midea/* @dudanov esphome/components/mitsubishi/* @RubyBailey diff --git a/esphome/components/md5/__init__.py b/esphome/components/md5/__init__.py new file mode 100644 index 0000000000..f70ffa9520 --- /dev/null +++ b/esphome/components/md5/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/md5/md5.cpp b/esphome/components/md5/md5.cpp new file mode 100644 index 0000000000..c6ff783439 --- /dev/null +++ b/esphome/components/md5/md5.cpp @@ -0,0 +1,51 @@ +#include +#include +#include "md5.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace md5 { + +void MD5Digest::init() { + memset(this->digest_, 0, 16); + MD5Init(&this->ctx_); +} + +void MD5Digest::add(const uint8_t *data, size_t len) { MD5Update(&this->ctx_, data, len); } + +void MD5Digest::calculate() { MD5Final(this->digest_, &this->ctx_); } + +void MD5Digest::get_bytes(uint8_t *output) { memcpy(output, this->digest_, 16); } + +void MD5Digest::get_hex(char *output) { + for (size_t i = 0; i < 16; i++) { + sprintf(output + i * 2, "%02x", this->digest_[i]); + } +} + +bool MD5Digest::equals_bytes(const char *expected) { + for (size_t i = 0; i < 16; i++) { + if (expected[i] != this->digest_[i]) { + return false; + } + } + return true; +} + +bool MD5Digest::equals_hex(const char *expected) { + for (size_t i = 0; i < 16; i++) { + auto high = parse_hex(expected[i * 2]); + auto low = parse_hex(expected[i * 2 + 1]); + if (!high.has_value() || !low.has_value()) { + return false; + } + auto value = (*high << 4) | *low; + if (value != this->digest_[i]) { + return false; + } + } + return true; +} + +} // namespace md5 +} // namespace esphome diff --git a/esphome/components/md5/md5.h b/esphome/components/md5/md5.h new file mode 100644 index 0000000000..e40f419347 --- /dev/null +++ b/esphome/components/md5/md5.h @@ -0,0 +1,58 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_ESP_IDF +#include "esp32/rom/md5_hash.h" +#define MD5_CTX_TYPE MD5Context +#endif + +#if defined(USE_ARDUINO) && defined(USE_ESP32) +#include "rom/md5_hash.h" +#define MD5_CTX_TYPE MD5Context +#endif + +#if defined(USE_ARDUINO) && defined(USE_ESP8266) +#include +#define MD5_CTX_TYPE md5_context_t +#endif + +namespace esphome { +namespace md5 { + +class MD5Digest { + public: + MD5Digest() = default; + ~MD5Digest() = default; + + /// Initialize a new MD5 digest computation. + void init(); + + /// Add bytes of data for the digest. + void add(const uint8_t *data, size_t len); + void add(const char *data, size_t len) { this->add((const uint8_t *) data, len); } + + /// Compute the digest, based on the provided data. + void calculate(); + + /// Retrieve the MD5 digest as bytes. + /// The output must be able to hold 16 bytes or more. + void get_bytes(uint8_t *output); + + /// Retrieve the MD5 digest as hex characters. + /// The output must be able to hold 32 bytes or more. + void get_hex(char *output); + + /// Compare the digest against a provided byte-encoded digest (16 bytes). + bool equals_bytes(const char *expected); + + /// Compare the digest against a provided hex-encoded digest (32 bytes). + bool equals_hex(const char *expected); + + protected: + MD5_CTX_TYPE ctx_{}; + uint8_t digest_[16]; +}; + +} // namespace md5 +} // namespace esphome diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index bcfb28979d..53b282c43e 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -15,7 +15,7 @@ from esphome.core import CORE, coroutine_with_priority CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] -AUTO_LOAD = ["socket"] +AUTO_LOAD = ["socket", "md5"] CONF_ON_STATE_CHANGE = "on_state_change" CONF_ON_BEGIN = "on_begin" @@ -35,20 +35,12 @@ OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template()) OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template()) -def validate_password_support(value): - if CORE.using_arduino: - return value - if CORE.using_esp_idf: - raise cv.Invalid("Password support is not implemented yet for ESP-IDF") - raise NotImplementedError - - CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(OTAComponent), cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean, cv.SplitDefault(CONF_PORT, esp8266=8266, esp32=3232): cv.port, - cv.Optional(CONF_PASSWORD): cv.All(cv.string, validate_password_support), + cv.Optional(CONF_PASSWORD): cv.string, cv.Optional( CONF_REBOOT_TIMEOUT, default="5min" ): cv.positive_time_period_milliseconds, diff --git a/esphome/components/ota/ota_backend_arduino_esp32.h b/esphome/components/ota/ota_backend_arduino_esp32.h index 8343bdf94f..6b712502fb 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.h +++ b/esphome/components/ota/ota_backend_arduino_esp32.h @@ -9,6 +9,7 @@ namespace esphome { namespace ota { class ArduinoESP32OTABackend : public OTABackend { + public: OTAResponseTypes begin(size_t image_size) override; void set_update_md5(const char *md5) override; OTAResponseTypes write(uint8_t *data, size_t len) override; diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota/ota_backend_esp_idf.cpp index 4eb17d82f1..336b3798d9 100644 --- a/esphome/components/ota/ota_backend_esp_idf.cpp +++ b/esphome/components/ota/ota_backend_esp_idf.cpp @@ -4,6 +4,7 @@ #include "ota_backend_esp_idf.h" #include "ota_component.h" #include +#include "esphome/components/md5/md5.h" namespace esphome { namespace ota { @@ -24,15 +25,15 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size) { } return OTA_RESPONSE_ERROR_UNKNOWN; } + this->md5_.init(); return OTA_RESPONSE_OK; } -void IDFOTABackend::set_update_md5(const char *md5) { - // pass -} +void IDFOTABackend::set_update_md5(const char *expected_md5) { memcpy(this->expected_bin_md5_, expected_md5, 32); } OTAResponseTypes IDFOTABackend::write(uint8_t *data, size_t len) { esp_err_t err = esp_ota_write(this->update_handle_, data, len); + this->md5_.add(data, len); if (err != ESP_OK) { if (err == ESP_ERR_OTA_VALIDATE_FAILED) { return OTA_RESPONSE_ERROR_MAGIC; @@ -45,6 +46,11 @@ OTAResponseTypes IDFOTABackend::write(uint8_t *data, size_t len) { } OTAResponseTypes IDFOTABackend::end() { + this->md5_.calculate(); + if (!this->md5_.equals_hex(this->expected_bin_md5_)) { + this->abort(); + return OTA_RESPONSE_ERROR_UPDATE_END; + } esp_err_t err = esp_ota_end(this->update_handle_); this->update_handle_ = 0; if (err == ESP_OK) { diff --git a/esphome/components/ota/ota_backend_esp_idf.h b/esphome/components/ota/ota_backend_esp_idf.h index d6e2e2742a..49c6e124fa 100644 --- a/esphome/components/ota/ota_backend_esp_idf.h +++ b/esphome/components/ota/ota_backend_esp_idf.h @@ -5,6 +5,7 @@ #include "ota_component.h" #include "ota_backend.h" #include +#include "esphome/components/md5/md5.h" namespace esphome { namespace ota { @@ -20,6 +21,8 @@ class IDFOTABackend : public OTABackend { private: esp_ota_handle_t update_handle_{0}; const esp_partition_t *partition_; + md5::MD5Digest md5_{}; + char expected_bin_md5_[32]; }; } // namespace ota diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 9ad3814f5c..89bee17452 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -8,15 +8,12 @@ #include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/util.h" +#include "esphome/components/md5/md5.h" #include "esphome/components/network/util.h" #include #include -#ifdef USE_OTA_PASSWORD -#include -#endif - namespace esphome { namespace ota { @@ -173,12 +170,12 @@ void OTAComponent::handle_() { if (!this->password_.empty()) { buf[0] = OTA_RESPONSE_REQUEST_AUTH; this->writeall_(buf, 1); - MD5Builder md5_builder{}; - md5_builder.begin(); + md5::MD5Digest md5{}; + md5.init(); sprintf(sbuf, "%08X", random_uint32()); - md5_builder.add(sbuf); - md5_builder.calculate(); - md5_builder.getChars(sbuf); + md5.add(sbuf, 8); + md5.calculate(); + md5.get_hex(sbuf); ESP_LOGV(TAG, "Auth: Nonce is %s", sbuf); // Send nonce, 32 bytes hex MD5 @@ -188,10 +185,10 @@ void OTAComponent::handle_() { } // prepare challenge - md5_builder.begin(); - md5_builder.add(this->password_.c_str()); + md5.init(); + md5.add(this->password_.c_str(), this->password_.length()); // add nonce - md5_builder.add(sbuf); + md5.add(sbuf, 32); // Receive cnonce, 32 bytes hex MD5 if (!this->readall_(buf, 32)) { @@ -201,11 +198,11 @@ void OTAComponent::handle_() { sbuf[32] = '\0'; ESP_LOGV(TAG, "Auth: CNonce is %s", sbuf); // add cnonce - md5_builder.add(sbuf); + md5.add(sbuf, 32); // calculate result - md5_builder.calculate(); - md5_builder.getChars(sbuf); + md5.calculate(); + md5.get_hex(sbuf); ESP_LOGV(TAG, "Auth: Result is %s", sbuf); // Receive result, 32 bytes hex MD5 diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 7c2261920a..b44987a768 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -26,6 +26,7 @@ #define USE_LOGGER #define USE_MDNS #define USE_NUMBER +#define USE_OTA_PASSWORD #define USE_OTA_STATE_CALLBACK #define USE_POWER_SUPPLY #define USE_PROMETHEUS From ecd115851fd4ed9d82a1db6865130fcbeb73eec1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Oct 2021 21:26:36 +1300 Subject: [PATCH 026/273] Bump version to 2021.10.0b6 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 39616ca062..6bf9ef2a64 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b5" +__version__ = "2021.10.0b6" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 430598b7a1d07f9c2f702a9f1314bae7f671f5af Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 19 Oct 2021 15:16:39 +1300 Subject: [PATCH 027/273] Bump dashboard to 20211019.0 (#2549) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8028007a0a..665ba27ed6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.1 esptool==3.1 click==8.0.3 -esphome-dashboard==20211015.0 +esphome-dashboard==20211019.0 aioesphomeapi==9.1.5 # esp-idf requires this, but doesn't bundle it by default From 3e9c7f2e9ffdd356f254b054d3792646de7b66e0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 19 Oct 2021 15:47:31 +1300 Subject: [PATCH 028/273] Bump version to 2021.10.0b7 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 6bf9ef2a64..f83fc7a651 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b6" +__version__ = "2021.10.0b7" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 3a760fbb4460a91cff240b65f62151eec17b41d2 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Tue, 19 Oct 2021 12:56:49 +0200 Subject: [PATCH 029/273] Fix ADC pin validation on ESP32-C3 (#2551) --- esphome/components/adc/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index 9a0407d0f4..26ef504c1a 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -35,7 +35,7 @@ def validate_adc_pin(value): if is_esp32c3(): if not (0 <= value <= 4): # ADC1 raise cv.Invalid("ESP32-C3: Only pins 0 though 4 support ADC.") - if not (32 <= value <= 39): # ADC1 + elif not (32 <= value <= 39): # ADC1 raise cv.Invalid("ESP32: Only pins 32 though 39 support ADC.") elif CORE.is_esp8266: from esphome.components.esp8266.gpio import CONF_ANALOG From 1b0e60374bd997a69ea0ba63bc02d4e3a5c6bcf6 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Tue, 19 Oct 2021 23:10:24 +0200 Subject: [PATCH 030/273] ignore exception when not waiting for a response (#2552) --- esphome/components/modbus/modbus.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index 1f6d868baf..45c5bfb603 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -96,23 +96,27 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) { ESP_LOGW(TAG, "Modbus CRC Check failed! %02X!=%02X", computed_crc, remote_crc); return false; } - - waiting_for_response = 0; std::vector data(this->rx_buffer_.begin() + data_offset, this->rx_buffer_.begin() + data_offset + data_len); - bool found = false; for (auto *device : this->devices_) { if (device->address_ == address) { // Is it an error response? if ((function_code & 0x80) == 0x80) { - ESP_LOGW(TAG, "Modbus error function code: 0x%X exception: %d", function_code, raw[2]); - device->on_modbus_error(function_code & 0x7F, raw[2]); + ESP_LOGD(TAG, "Modbus error function code: 0x%X exception: %d", function_code, raw[2]); + if (waiting_for_response != 0) { + device->on_modbus_error(function_code & 0x7F, raw[2]); + } else { + // Ignore modbus exception not related to a pending command + ESP_LOGD(TAG, "Ignoring Modbus error - not expecting a response"); + } } else { device->on_modbus_data(data); } found = true; } } + waiting_for_response = 0; + if (!found) { ESP_LOGW(TAG, "Got Modbus frame from unknown address 0x%02X! ", address); } @@ -196,6 +200,7 @@ void Modbus::send_raw(const std::vector &payload) { if (this->flow_control_pin_ != nullptr) this->flow_control_pin_->digital_write(false); waiting_for_response = payload[0]; + ESP_LOGV(TAG, "Modbus write raw: %s", hexencode(payload).c_str()); last_send_ = millis(); } From e10ab1da78658ef75ec8ebc328fa9b3c21fd775b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Oct 2021 10:14:30 +1300 Subject: [PATCH 031/273] Bump version to 2021.10.0b8 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index f83fc7a651..ad95096b94 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b7" +__version__ = "2021.10.0b8" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From ea0977abb4451c35a39e076c5ba92e3f8666c8ed Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Oct 2021 10:53:49 +1300 Subject: [PATCH 032/273] Bump dashboard to 20211020.0 (#2556) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 665ba27ed6..120c71ff69 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.1 esptool==3.1 click==8.0.3 -esphome-dashboard==20211019.0 +esphome-dashboard==20211020.0 aioesphomeapi==9.1.5 # esp-idf requires this, but doesn't bundle it by default From 7feffa64f3788db14e48d724b0b8405ff3c5a92f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Oct 2021 11:00:02 +1300 Subject: [PATCH 033/273] Bump version to 2021.10.0b9 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index ad95096b94..4ade772cc1 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b8" +__version__ = "2021.10.0b9" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 9ac365feef5dad4412af5c271aa6f6c02be6e5b5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Oct 2021 13:25:00 +1300 Subject: [PATCH 034/273] Fix HA addon so it does not have logout button (#2558) --- esphome/dashboard/dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 501666b100..63378a38b5 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -597,7 +597,7 @@ class MainRequestHandler(BaseHandler): get_template_path("index"), begin=begin, **template_args(), - login_enabled=settings.using_auth, + login_enabled=settings.using_password, ) From b9f66373c1b5ffc78dc15b87dbbde884d830bbeb Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Oct 2021 16:17:00 +1300 Subject: [PATCH 035/273] Bump esphome-dashboard to 20211020.1 (#2559) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 120c71ff69..16f23f4b7d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.1 esptool==3.1 click==8.0.3 -esphome-dashboard==20211020.0 +esphome-dashboard==20211020.1 aioesphomeapi==9.1.5 # esp-idf requires this, but doesn't bundle it by default From 8456a8cecb3125c318accc407aec3b71e135fb75 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Oct 2021 16:39:24 +1300 Subject: [PATCH 036/273] Bump version to 2021.10.0b10 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 4ade772cc1..f0951cbdaa 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b9" +__version__ = "2021.10.0b10" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From dad244fb7a998d52e1900f5692ea97d5d65f252d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 21 Oct 2021 06:45:10 +1300 Subject: [PATCH 037/273] A few esp32_ble_server/improv fixes (#2562) --- .../components/esp32_ble_server/ble_server.cpp | 17 +++++++++-------- .../components/esp32_ble_server/ble_server.h | 14 ++++++++------ .../esp32_improv/esp32_improv_component.h | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/esphome/components/esp32_ble_server/ble_server.cpp b/esphome/components/esp32_ble_server/ble_server.cpp index e0fb80f94b..15bea07021 100644 --- a/esphome/components/esp32_ble_server/ble_server.cpp +++ b/esphome/components/esp32_ble_server/ble_server.cpp @@ -98,19 +98,20 @@ bool BLEServer::create_device_characteristics_() { return true; } -BLEService *BLEServer::create_service(const uint8_t *uuid, bool advertise) { +std::shared_ptr BLEServer::create_service(const uint8_t *uuid, bool advertise) { return this->create_service(ESPBTUUID::from_raw(uuid), advertise); } -BLEService *BLEServer::create_service(uint16_t uuid, bool advertise) { +std::shared_ptr BLEServer::create_service(uint16_t uuid, bool advertise) { return this->create_service(ESPBTUUID::from_uint16(uuid), advertise); } -BLEService *BLEServer::create_service(const std::string &uuid, bool advertise) { +std::shared_ptr BLEServer::create_service(const std::string &uuid, bool advertise) { return this->create_service(ESPBTUUID::from_raw(uuid), advertise); } -BLEService *BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles, uint8_t inst_id) { +std::shared_ptr BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles, + uint8_t inst_id) { ESP_LOGV(TAG, "Creating service - %s", uuid.to_string().c_str()); - BLEService *service = new BLEService(uuid, num_handles, inst_id); // NOLINT(cppcoreguidelines-owning-memory) - this->services_.push_back(service); + std::shared_ptr service = std::make_shared(uuid, num_handles, inst_id); + this->services_.emplace_back(service); if (advertise) { esp32_ble::global_ble->get_advertising()->add_service_uuid(uuid); } @@ -149,12 +150,12 @@ void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t ga break; } - for (auto *service : this->services_) { + for (const auto &service : this->services_) { service->gatts_event_handler(event, gatts_if, param); } } -float BLEServer::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } +float BLEServer::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH + 10; } void BLEServer::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 BLE Server:"); } diff --git a/esphome/components/esp32_ble_server/ble_server.h b/esphome/components/esp32_ble_server/ble_server.h index 9f7e8b8fc0..d275eeab01 100644 --- a/esphome/components/esp32_ble_server/ble_server.h +++ b/esphome/components/esp32_ble_server/ble_server.h @@ -11,6 +11,7 @@ #include "esphome/core/preferences.h" #include +#include #ifdef USE_ESP32 @@ -43,10 +44,11 @@ class BLEServer : public Component { void set_manufacturer(const std::string &manufacturer) { this->manufacturer_ = manufacturer; } void set_model(const std::string &model) { this->model_ = model; } - BLEService *create_service(const uint8_t *uuid, bool advertise = false); - BLEService *create_service(uint16_t uuid, bool advertise = false); - BLEService *create_service(const std::string &uuid, bool advertise = false); - BLEService *create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15, uint8_t inst_id = 0); + std::shared_ptr create_service(const uint8_t *uuid, bool advertise = false); + std::shared_ptr create_service(uint16_t uuid, bool advertise = false); + std::shared_ptr create_service(const std::string &uuid, bool advertise = false); + std::shared_ptr create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15, + uint8_t inst_id = 0); esp_gatt_if_t get_gatts_if() { return this->gatts_if_; } uint32_t get_connected_client_count() { return this->connected_clients_; } @@ -74,8 +76,8 @@ class BLEServer : public Component { uint32_t connected_clients_{0}; std::map clients_; - std::vector services_; - BLEService *device_information_service_; + std::vector> services_; + std::shared_ptr device_information_service_; std::vector service_components_; diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index 53cda5f399..3a5d150fbe 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -48,7 +48,7 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent { std::vector incoming_data_; wifi::WiFiAP connecting_sta_; - BLEService *service_; + std::shared_ptr service_; BLECharacteristic *status_; BLECharacteristic *error_; BLECharacteristic *rpc_; From 95593eeeabe091cc80ea485ef57d469b69765bd5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 21 Oct 2021 07:08:34 +1300 Subject: [PATCH 038/273] Bump esphome-dashboard to 20211021.0 (#2564) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 16f23f4b7d..63804e89e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.1 esptool==3.1 click==8.0.3 -esphome-dashboard==20211020.1 +esphome-dashboard==20211021.0 aioesphomeapi==9.1.5 # esp-idf requires this, but doesn't bundle it by default From 996ec59d286d0888c48313439feb404b6c9818fb Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 20 Oct 2021 20:15:09 +0200 Subject: [PATCH 039/273] Move running process log line to debug level (#2565) --- esphome/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/util.py b/esphome/util.py index 527e370ad8..0f168cade3 100644 --- a/esphome/util.py +++ b/esphome/util.py @@ -178,7 +178,7 @@ def run_external_command( orig_argv = sys.argv orig_exit = sys.exit # mock sys.exit full_cmd = " ".join(shlex_quote(x) for x in cmd) - _LOGGER.info("Running: %s", full_cmd) + _LOGGER.debug("Running: %s", full_cmd) orig_stdout = sys.stdout sys.stdout = RedirectText(sys.stdout, filter_lines=filter_lines) @@ -214,7 +214,7 @@ def run_external_command( def run_external_process(*cmd, **kwargs): full_cmd = " ".join(shlex_quote(x) for x in cmd) - _LOGGER.info("Running: %s", full_cmd) + _LOGGER.debug("Running: %s", full_cmd) filter_lines = kwargs.get("filter_lines") capture_stdout = kwargs.get("capture_stdout", False) From 3af297aa7660b2dd6f5f10bd8185619d24d511d1 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 20 Oct 2021 20:31:13 +0200 Subject: [PATCH 040/273] Revert nextion clang-tidy changes (#2566) --- .../binary_sensor/nextion_binarysensor.cpp | 4 +- .../binary_sensor/nextion_binarysensor.h | 3 +- esphome/components/nextion/nextion.cpp | 73 ++++++++++++------- esphome/components/nextion/nextion.h | 11 ++- esphome/components/nextion/nextion_base.h | 9 +-- .../nextion/nextion_component_base.h | 3 +- esphome/components/nextion/nextion_upload.cpp | 14 ++-- .../nextion/sensor/nextion_sensor.cpp | 8 +- .../nextion/sensor/nextion_sensor.h | 5 +- .../nextion/switch/nextion_switch.cpp | 4 +- .../nextion/switch/nextion_switch.h | 5 +- .../text_sensor/nextion_textsensor.cpp | 4 +- .../nextion/text_sensor/nextion_textsensor.h | 5 +- 13 files changed, 78 insertions(+), 70 deletions(-) diff --git a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp index c5bfa78efe..bf6e74cb38 100644 --- a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +++ b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp @@ -33,7 +33,7 @@ void NextionBinarySensor::update() { if (this->variable_name_.empty()) // This is a touch component return; - this->nextion_->add_to_get_queue(shared_from_this()); + this->nextion_->add_to_get_queue(this); } void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nextion) { @@ -48,7 +48,7 @@ void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nexti this->needs_to_send_update_ = true; } else { this->needs_to_send_update_ = false; - this->nextion_->add_no_result_to_queue_with_set(shared_from_this(), (int) state); + this->nextion_->add_no_result_to_queue_with_set(this, (int) state); } } diff --git a/esphome/components/nextion/binary_sensor/nextion_binarysensor.h b/esphome/components/nextion/binary_sensor/nextion_binarysensor.h index b86ee74013..b6b23ada85 100644 --- a/esphome/components/nextion/binary_sensor/nextion_binarysensor.h +++ b/esphome/components/nextion/binary_sensor/nextion_binarysensor.h @@ -10,8 +10,7 @@ class NextionBinarySensor; class NextionBinarySensor : public NextionComponent, public binary_sensor::BinarySensorInitiallyOff, - public PollingComponent, - public std::enable_shared_from_this { + public PollingComponent { public: NextionBinarySensor(NextionBase *nextion) { this->nextion_ = nextion; } diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index d56c370412..f23f55c9bb 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -196,7 +196,7 @@ void Nextion::print_queue_members_() { ESP_LOGN(TAG, "print_queue_members_ (top 10) size %zu", this->nextion_queue_.size()); ESP_LOGN(TAG, "*******************************************"); int count = 0; - for (auto &i : this->nextion_queue_) { + for (auto *i : this->nextion_queue_) { if (count++ == 10) break; @@ -257,9 +257,8 @@ bool Nextion::remove_from_q_(bool report_empty) { return false; } - auto nb = std::move(this->nextion_queue_.front()); - this->nextion_queue_.pop_front(); - auto &component = nb->component; + NextionQueue *nb = this->nextion_queue_.front(); + NextionComponentBase *component = nb->component; ESP_LOGN(TAG, "Removing %s from the queue", component->get_variable_name().c_str()); @@ -267,8 +266,10 @@ bool Nextion::remove_from_q_(bool report_empty) { if (component->get_variable_name() == "sleep_wake") { this->is_sleeping_ = false; } + delete component; // NOLINT(cppcoreguidelines-owning-memory) } - + delete nb; // NOLINT(cppcoreguidelines-owning-memory) + this->nextion_queue_.pop_front(); return true; } @@ -357,7 +358,7 @@ void Nextion::process_nextion_commands_() { int index = 0; int found = -1; for (auto &nb : this->nextion_queue_) { - auto &component = nb->component; + NextionComponentBase *component = nb->component; if (component->get_queue_type() == NextionQueueType::WAVEFORM_SENSOR) { ESP_LOGW(TAG, "Nextion reported invalid Waveform ID %d or Channel # %d was used!", @@ -368,6 +369,9 @@ void Nextion::process_nextion_commands_() { found = index; + delete component; // NOLINT(cppcoreguidelines-owning-memory) + delete nb; // NOLINT(cppcoreguidelines-owning-memory) + break; } ++index; @@ -464,9 +468,8 @@ void Nextion::process_nextion_commands_() { break; } - auto nb = std::move(this->nextion_queue_.front()); - this->nextion_queue_.pop_front(); - auto &component = nb->component; + NextionQueue *nb = this->nextion_queue_.front(); + NextionComponentBase *component = nb->component; if (component->get_queue_type() != NextionQueueType::TEXT_SENSOR) { ESP_LOGE(TAG, "ERROR: Received string return but next in queue \"%s\" is not a text sensor", @@ -477,6 +480,9 @@ void Nextion::process_nextion_commands_() { component->set_state_from_string(to_process, true, false); } + delete nb; // NOLINT(cppcoreguidelines-owning-memory) + this->nextion_queue_.pop_front(); + break; } // 0x71 0x01 0x02 0x03 0x04 0xFF 0xFF 0xFF @@ -505,9 +511,8 @@ void Nextion::process_nextion_commands_() { ++dataindex; } - auto nb = std::move(this->nextion_queue_.front()); - this->nextion_queue_.pop_front(); - auto &component = nb->component; + NextionQueue *nb = this->nextion_queue_.front(); + NextionComponentBase *component = nb->component; if (component->get_queue_type() != NextionQueueType::SENSOR && component->get_queue_type() != NextionQueueType::BINARY_SENSOR && @@ -521,6 +526,9 @@ void Nextion::process_nextion_commands_() { component->set_state_from_int(value, true, false); } + delete nb; // NOLINT(cppcoreguidelines-owning-memory) + this->nextion_queue_.pop_front(); + break; } @@ -682,7 +690,7 @@ void Nextion::process_nextion_commands_() { int index = 0; int found = -1; for (auto &nb : this->nextion_queue_) { - auto &component = nb->component; + auto component = nb->component; if (component->get_queue_type() == NextionQueueType::WAVEFORM_SENSOR) { size_t buffer_to_send = component->get_wave_buffer().size() < 255 ? component->get_wave_buffer().size() : 255; // ADDT command can only send 255 @@ -699,6 +707,8 @@ void Nextion::process_nextion_commands_() { component->get_wave_buffer().begin() + buffer_to_send); } found = index; + delete component; // NOLINT(cppcoreguidelines-owning-memory) + delete nb; // NOLINT(cppcoreguidelines-owning-memory) break; } ++index; @@ -727,7 +737,7 @@ void Nextion::process_nextion_commands_() { if (!this->nextion_queue_.empty() && this->nextion_queue_.front()->queue_time + this->max_q_age_ms_ < ms) { for (int i = 0; i < this->nextion_queue_.size(); i++) { - auto &component = this->nextion_queue_[i]->component; + NextionComponentBase *component = this->nextion_queue_[i]->component; if (this->nextion_queue_[i]->queue_time + this->max_q_age_ms_ < ms) { if (this->nextion_queue_[i]->queue_time == 0) ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\" queue_time 0", @@ -744,8 +754,11 @@ void Nextion::process_nextion_commands_() { if (component->get_variable_name() == "sleep_wake") { this->is_sleeping_ = false; } + delete component; // NOLINT(cppcoreguidelines-owning-memory) } + delete this->nextion_queue_[i]; // NOLINT(cppcoreguidelines-owning-memory) + this->nextion_queue_.erase(this->nextion_queue_.begin() + i); i--; @@ -899,16 +912,18 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool * @param variable_name Name for the queue */ void Nextion::add_no_result_to_queue_(const std::string &variable_name) { - auto nextion_queue = make_unique(); + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; - nextion_queue->component = make_unique(); + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + nextion_queue->component = new nextion::NextionComponentBase; nextion_queue->component->set_variable_name(variable_name); nextion_queue->queue_time = millis(); - ESP_LOGN(TAG, "Add to queue type: NORESULT component %s", nextion_queue->component->get_variable_name().c_str()); + this->nextion_queue_.push_back(nextion_queue); - this->nextion_queue_.push_back(std::move(nextion_queue)); + ESP_LOGN(TAG, "Add to queue type: NORESULT component %s", nextion_queue->component->get_variable_name().c_str()); } /** @@ -979,7 +994,7 @@ bool Nextion::add_no_result_to_queue_with_printf_(const std::string &variable_na * @param is_sleep_safe The command is safe to send when the Nextion is sleeping */ -void Nextion::add_no_result_to_queue_with_set(std::shared_ptr component, int state_value) { +void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) { this->add_no_result_to_queue_with_set(component->get_variable_name(), component->get_variable_name_to_send(), state_value); } @@ -1007,8 +1022,7 @@ void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &varia * @param state_value String value to set * @param is_sleep_safe The command is safe to send when the Nextion is sleeping */ -void Nextion::add_no_result_to_queue_with_set(std::shared_ptr component, - const std::string &state_value) { +void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) { this->add_no_result_to_queue_with_set(component->get_variable_name(), component->get_variable_name_to_send(), state_value); } @@ -1028,11 +1042,12 @@ void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &varia state_value.c_str()); } -void Nextion::add_to_get_queue(std::shared_ptr component) { +void Nextion::add_to_get_queue(NextionComponentBase *component) { if ((!this->is_setup() && !this->ignore_is_setup_)) return; - auto nextion_queue = make_unique(); + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; nextion_queue->component = component; nextion_queue->queue_time = millis(); @@ -1043,7 +1058,7 @@ void Nextion::add_to_get_queue(std::shared_ptr component) std::string command = "get " + component->get_variable_name_to_send(); if (this->send_command_(command)) { - this->nextion_queue_.push_back(std::move(nextion_queue)); + this->nextion_queue_.push_back(nextion_queue); } } @@ -1055,13 +1070,15 @@ void Nextion::add_to_get_queue(std::shared_ptr component) * @param buffer_to_send The buffer size * @param buffer_size The buffer data */ -void Nextion::add_addt_command_to_queue(std::shared_ptr component) { +void Nextion::add_addt_command_to_queue(NextionComponentBase *component) { if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) return; - auto nextion_queue = make_unique(); + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; - nextion_queue->component = std::make_shared(); + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + nextion_queue->component = new nextion::NextionComponentBase; nextion_queue->queue_time = millis(); size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size() @@ -1070,7 +1087,7 @@ void Nextion::add_addt_command_to_queue(std::shared_ptr co std::string command = "addt " + to_string(component->get_component_id()) + "," + to_string(component->get_wave_channel_id()) + "," + to_string(buffer_to_send); if (this->send_command_(command)) { - this->nextion_queue_.push_back(std::move(nextion_queue)); + this->nextion_queue_.push_back(nextion_queue); } } diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 1bee41f6cf..285b3ac9a3 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -707,18 +707,17 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void set_nextion_sensor_state(NextionQueueType queue_type, const std::string &name, float state); void set_nextion_text_state(const std::string &name, const std::string &state); - void add_no_result_to_queue_with_set(std::shared_ptr component, int state_value) override; + void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) override; void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, int state_value) override; - void add_no_result_to_queue_with_set(std::shared_ptr component, - const std::string &state_value) override; + void add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) override; void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, const std::string &state_value) override; - void add_to_get_queue(std::shared_ptr component) override; + void add_to_get_queue(NextionComponentBase *component) override; - void add_addt_command_to_queue(std::shared_ptr component) override; + void add_addt_command_to_queue(NextionComponentBase *component) override; void update_components_by_prefix(const std::string &prefix); @@ -729,7 +728,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void set_auto_wake_on_touch_internal(bool auto_wake_on_touch) { this->auto_wake_on_touch_ = auto_wake_on_touch; } protected: - std::deque> nextion_queue_; + std::deque nextion_queue_; uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag); void all_components_send_state_(bool force_update = false); uint64_t comok_sent_ = 0; diff --git a/esphome/components/nextion/nextion_base.h b/esphome/components/nextion/nextion_base.h index d91c70c960..a24fd74060 100644 --- a/esphome/components/nextion/nextion_base.h +++ b/esphome/components/nextion/nextion_base.h @@ -24,19 +24,18 @@ class NextionBase; class NextionBase { public: - virtual void add_no_result_to_queue_with_set(std::shared_ptr component, int state_value) = 0; + virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) = 0; virtual void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, int state_value) = 0; - virtual void add_no_result_to_queue_with_set(std::shared_ptr component, - const std::string &state_value) = 0; + virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) = 0; virtual void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, const std::string &state_value) = 0; - virtual void add_addt_command_to_queue(std::shared_ptr component) = 0; + virtual void add_addt_command_to_queue(NextionComponentBase *component) = 0; - virtual void add_to_get_queue(std::shared_ptr component) = 0; + virtual void add_to_get_queue(NextionComponentBase *component) = 0; virtual void set_component_background_color(const char *component, Color color) = 0; virtual void set_component_pressed_background_color(const char *component, Color color) = 0; diff --git a/esphome/components/nextion/nextion_component_base.h b/esphome/components/nextion/nextion_component_base.h index 2725d5a30c..71ad803bc4 100644 --- a/esphome/components/nextion/nextion_component_base.h +++ b/esphome/components/nextion/nextion_component_base.h @@ -1,6 +1,5 @@ #pragma once #include -#include #include "esphome/core/defines.h" namespace esphome { @@ -23,7 +22,7 @@ class NextionComponentBase; class NextionQueue { public: virtual ~NextionQueue() = default; - std::shared_ptr component; + NextionComponentBase *component; uint32_t queue_time = 0; }; diff --git a/esphome/components/nextion/nextion_upload.cpp b/esphome/components/nextion/nextion_upload.cpp index cebdbec31a..cd1c073320 100644 --- a/esphome/components/nextion/nextion_upload.cpp +++ b/esphome/components/nextion/nextion_upload.cpp @@ -281,12 +281,14 @@ void Nextion::upload_tft() { #endif // NOLINTNEXTLINE(readability-static-accessed-through-instance) ESP_LOGD(TAG, "Allocating buffer size %d, Heap size is %u", chunk_size, ESP.getFreeHeap()); - this->transfer_buffer_ = new (std::nothrow) uint8_t[chunk_size]; // NOLINT(cppcoreguidelines-owning-memory) - if (this->transfer_buffer_ == nullptr) { // Try a smaller size + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + this->transfer_buffer_ = new (std::nothrow) uint8_t[chunk_size]; + if (this->transfer_buffer_ == nullptr) { // Try a smaller size ESP_LOGD(TAG, "Could not allocate buffer size: %d trying 4096 instead", chunk_size); chunk_size = 4096; ESP_LOGD(TAG, "Allocating %d buffer", chunk_size); - this->transfer_buffer_ = new (std::nothrow) uint8_t[chunk_size]; // NOLINT(cppcoreguidelines-owning-memory) + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + this->transfer_buffer_ = new uint8_t[chunk_size]; if (!this->transfer_buffer_) this->upload_end_(); @@ -330,7 +332,8 @@ void Nextion::upload_end_() { WiFiClient *Nextion::get_wifi_client_() { if (this->tft_url_.compare(0, 6, "https:") == 0) { if (this->wifi_client_secure_ == nullptr) { - this->wifi_client_secure_ = new BearSSL::WiFiClientSecure(); // NOLINT(cppcoreguidelines-owning-memory) + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + this->wifi_client_secure_ = new BearSSL::WiFiClientSecure(); this->wifi_client_secure_->setInsecure(); this->wifi_client_secure_->setBufferSizes(512, 512); } @@ -338,7 +341,8 @@ WiFiClient *Nextion::get_wifi_client_() { } if (this->wifi_client_ == nullptr) { - this->wifi_client_ = new WiFiClient(); // NOLINT(cppcoreguidelines-owning-memory) + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + this->wifi_client_ = new WiFiClient(); } return this->wifi_client_; } diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp index e983ebcc6f..4b7532d32d 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.cpp +++ b/esphome/components/nextion/sensor/nextion_sensor.cpp @@ -34,7 +34,7 @@ void NextionSensor::update() { return; if (this->wave_chan_id_ == UINT8_MAX) { - this->nextion_->add_to_get_queue(shared_from_this()); + this->nextion_->add_to_get_queue(this); } else { if (this->send_last_value_) { this->add_to_wave_buffer(this->last_value_); @@ -62,9 +62,9 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { double to_multiply = pow(10, this->precision_); int state_value = (int) (state * to_multiply); - this->nextion_->add_no_result_to_queue_with_set(shared_from_this(), (int) state_value); + this->nextion_->add_no_result_to_queue_with_set(this, (int) state_value); } else { - this->nextion_->add_no_result_to_queue_with_set(shared_from_this(), (int) state); + this->nextion_->add_no_result_to_queue_with_set(this, (int) state); } } } @@ -103,7 +103,7 @@ void NextionSensor::wave_update_() { buffer_to_send, this->wave_buffer_.size(), this->component_id_, this->wave_chan_id_); #endif - this->nextion_->add_addt_command_to_queue(shared_from_this()); + this->nextion_->add_addt_command_to_queue(this); } } // namespace nextion diff --git a/esphome/components/nextion/sensor/nextion_sensor.h b/esphome/components/nextion/sensor/nextion_sensor.h index 068ff0451b..e4dde9a513 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.h +++ b/esphome/components/nextion/sensor/nextion_sensor.h @@ -8,10 +8,7 @@ namespace esphome { namespace nextion { class NextionSensor; -class NextionSensor : public NextionComponent, - public sensor::Sensor, - public PollingComponent, - public std::enable_shared_from_this { +class NextionSensor : public NextionComponent, public sensor::Sensor, public PollingComponent { public: NextionSensor(NextionBase *nextion) { this->nextion_ = nextion; } void send_state_to_nextion() override { this->set_state(this->state, false, true); }; diff --git a/esphome/components/nextion/switch/nextion_switch.cpp b/esphome/components/nextion/switch/nextion_switch.cpp index 0bd958e0d8..1f32ad3425 100644 --- a/esphome/components/nextion/switch/nextion_switch.cpp +++ b/esphome/components/nextion/switch/nextion_switch.cpp @@ -20,7 +20,7 @@ void NextionSwitch::process_bool(const std::string &variable_name, bool on) { void NextionSwitch::update() { if (!this->nextion_->is_setup()) return; - this->nextion_->add_to_get_queue(shared_from_this()); + this->nextion_->add_to_get_queue(this); } void NextionSwitch::set_state(bool state, bool publish, bool send_to_nextion) { @@ -32,7 +32,7 @@ void NextionSwitch::set_state(bool state, bool publish, bool send_to_nextion) { this->needs_to_send_update_ = true; } else { this->needs_to_send_update_ = false; - this->nextion_->add_no_result_to_queue_with_set(shared_from_this(), (int) state); + this->nextion_->add_no_result_to_queue_with_set(this, (int) state); } } if (publish) { diff --git a/esphome/components/nextion/switch/nextion_switch.h b/esphome/components/nextion/switch/nextion_switch.h index d7783e5c51..1548287473 100644 --- a/esphome/components/nextion/switch/nextion_switch.h +++ b/esphome/components/nextion/switch/nextion_switch.h @@ -8,10 +8,7 @@ namespace esphome { namespace nextion { class NextionSwitch; -class NextionSwitch : public NextionComponent, - public switch_::Switch, - public PollingComponent, - public std::enable_shared_from_this { +class NextionSwitch : public NextionComponent, public switch_::Switch, public PollingComponent { public: NextionSwitch(NextionBase *nextion) { this->nextion_ = nextion; } diff --git a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp index fa7cb35025..08f032df74 100644 --- a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp +++ b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp @@ -18,7 +18,7 @@ void NextionTextSensor::process_text(const std::string &variable_name, const std void NextionTextSensor::update() { if (!this->nextion_->is_setup()) return; - this->nextion_->add_to_get_queue(shared_from_this()); + this->nextion_->add_to_get_queue(this); } void NextionTextSensor::set_state(const std::string &state, bool publish, bool send_to_nextion) { @@ -29,7 +29,7 @@ void NextionTextSensor::set_state(const std::string &state, bool publish, bool s if (this->nextion_->is_sleeping() || !this->visible_) { this->needs_to_send_update_ = true; } else { - this->nextion_->add_no_result_to_queue_with_set(shared_from_this(), state); + this->nextion_->add_no_result_to_queue_with_set(this, state); } } diff --git a/esphome/components/nextion/text_sensor/nextion_textsensor.h b/esphome/components/nextion/text_sensor/nextion_textsensor.h index 762797727d..5716d0a008 100644 --- a/esphome/components/nextion/text_sensor/nextion_textsensor.h +++ b/esphome/components/nextion/text_sensor/nextion_textsensor.h @@ -8,10 +8,7 @@ namespace esphome { namespace nextion { class NextionTextSensor; -class NextionTextSensor : public NextionComponent, - public text_sensor::TextSensor, - public PollingComponent, - public std::enable_shared_from_this { +class NextionTextSensor : public NextionComponent, public text_sensor::TextSensor, public PollingComponent { public: NextionTextSensor(NextionBase *nextion) { this->nextion_ = nextion; } void update() override; From 56cc31e8e752572f3233c6e68ef4f832d6b3132d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 21 Oct 2021 07:32:27 +1300 Subject: [PATCH 041/273] Bump version to 2021.10.0b11 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index f0951cbdaa..2c1bd219d7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b10" +__version__ = "2021.10.0b11" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 625463d87168bcc563896355e531c4b9a18bef34 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 21 Oct 2021 07:57:10 +1300 Subject: [PATCH 042/273] Bump version to 2021.10.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 2c1bd219d7..59441e23c8 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b11" +__version__ = "2021.10.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 332c9e891b4abb7c72c324f02d0b8750e6199b1f Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Thu, 21 Oct 2021 12:23:21 +0200 Subject: [PATCH 043/273] Fix MDNS for ESP8266 devices (#2571) Co-authored-by: Maurice Makaay Co-authored-by: Otto winter Co-authored-by: Maurice Makaay --- esphome/components/mdns/mdns_component.cpp | 6 +++--- esphome/components/mdns/mdns_component.h | 4 ++++ esphome/components/mdns/mdns_esp8266.cpp | 16 ++++++++++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index 372d980eb0..631af9eba9 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -23,7 +23,7 @@ std::vector MDNSComponent::compile_services_() { #ifdef USE_API if (api::global_api_server != nullptr) { MDNSService service{}; - service.service_type = "esphomelib"; + service.service_type = "_esphomelib"; service.proto = "_tcp"; service.port = api::global_api_server->get_port(); service.txt_records.push_back({"version", ESPHOME_VERSION}); @@ -57,7 +57,7 @@ std::vector MDNSComponent::compile_services_() { #ifdef USE_PROMETHEUS { MDNSService service{}; - service.service_type = "prometheus-http"; + service.service_type = "_prometheus-http"; service.proto = "_tcp"; service.port = WEBSERVER_PORT; res.push_back(service); @@ -68,7 +68,7 @@ std::vector MDNSComponent::compile_services_() { // Publish "http" service if not using native API // This is just to have *some* mDNS service so that .local resolution works MDNSService service{}; - service.service_type = "http"; + service.service_type = "_http"; service.proto = "_tcp"; service.port = WEBSERVER_PORT; service.txt_records.push_back({"version", ESPHOME_VERSION}); diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index 985947d99c..679fb1a768 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -13,7 +13,11 @@ struct MDNSTXTRecord { }; struct MDNSService { + // service name _including_ underscore character prefix + // as defined in RFC6763 Section 7 std::string service_type; + // second label indicating protocol _including_ underscore character prefix + // as defined in RFC6763 Section 7, like "_tcp" or "_udp" std::string proto; uint16_t port; std::vector txt_records; diff --git a/esphome/components/mdns/mdns_esp8266.cpp b/esphome/components/mdns/mdns_esp8266.cpp index 48f31f1bbf..1a73e4b5b3 100644 --- a/esphome/components/mdns/mdns_esp8266.cpp +++ b/esphome/components/mdns/mdns_esp8266.cpp @@ -17,9 +17,21 @@ void MDNSComponent::setup() { auto services = compile_services_(); for (const auto &service : services) { - MDNS.addService(service.service_type.c_str(), service.proto.c_str(), service.port); + // Strip the leading underscore from the proto and service_type. While it is + // part of the wire protocol to have an underscore, and for example ESP-IDF + // expects the underscore to be there, the ESP8266 implementation always adds + // the underscore itself. + auto proto = service.proto.c_str(); + while (*proto == '_') { + proto++; + } + auto service_type = service.service_type.c_str(); + while (*service_type == '_') { + service_type++; + } + MDNS.addService(service_type, proto, service.port); for (const auto &record : service.txt_records) { - MDNS.addServiceTxt(service.service_type.c_str(), service.proto.c_str(), record.key.c_str(), record.value.c_str()); + MDNS.addServiceTxt(service_type, proto, record.key.c_str(), record.value.c_str()); } } } From f6935a4b4b61769d8b13320985753e97278d345f Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 21 Oct 2021 12:23:37 +0200 Subject: [PATCH 044/273] Fix ESP8266 GPIO0 Pullup Validation (#2572) --- esphome/components/esp8266/gpio.py | 4 ++-- tests/test3.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/esp8266/gpio.py b/esphome/components/esp8266/gpio.py index 0ebfbd6f69..fa5c94dff5 100644 --- a/esphome/components/esp8266/gpio.py +++ b/esphome/components/esp8266/gpio.py @@ -107,9 +107,9 @@ def validate_supports(value): raise cv.Invalid( "Open-drain only works with output mode", [CONF_MODE, CONF_OPEN_DRAIN] ) - if is_pullup and num == 0: + if is_pullup and num == 16: raise cv.Invalid( - "GPIO Pin 0 does not support pullup pin mode. " + "GPIO Pin 16 does not support pullup pin mode. " "Please choose another pin.", [CONF_MODE, CONF_PULLUP], ) diff --git a/tests/test3.yaml b/tests/test3.yaml index 73e314c94c..f7cba5e787 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -1150,7 +1150,7 @@ servo: ttp229_lsf: ttp229_bsf: - sdo_pin: D0 + sdo_pin: D2 scl_pin: D1 sim800l: From 2f32833a22e7d91b11221688f09c2a5c06aa8651 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 21 Oct 2021 12:24:01 +0200 Subject: [PATCH 045/273] Fix wifi ble coexistence check (#2573) --- esphome/components/wifi/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 19e4046711..faf3cca280 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -159,8 +159,15 @@ def final_validate_power_esp32_ble(value): "esp32_ble_server", "esp32_ble_tracker", ]: + if conflicting not in fv.full_config.get(): + continue + try: - cv.require_framework_version(esp32_arduino=cv.Version(1, 0, 5))(None) + # Only arduino 1.0.5+ and esp-idf impacted + cv.require_framework_version( + esp32_arduino=cv.Version(1, 0, 5), + esp_idf=cv.Version(4, 0, 0), + )(None) except cv.Invalid: pass else: From e7baa42e6309949db5b1f7b22813f04f96ec3060 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 21 Oct 2021 14:20:23 +0200 Subject: [PATCH 046/273] Arduino global delay/millis/... symbols workaround (#2575) --- esphome/core/config.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/esphome/core/config.py b/esphome/core/config.py index c495fefddd..3c53d81784 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -206,6 +206,31 @@ def include_file(path, basename): cg.add_global(cg.RawStatement(f'#include "{basename}"')) +ARDUINO_GLUE_CODE = """\ +#define yield() esphome::yield() +#define millis() esphome::millis() +#define delay(x) esphome::delay(x) +#define delayMicroseconds(x) esphome::delayMicroseconds(x) +""" + + +@coroutine_with_priority(-999.0) +async def add_arduino_global_workaround(): + # The Arduino framework defined these itself in the global + # namespace. For the esphome codebase that is not a problem, + # but when custom code + # 1. writes `millis()` for example AND + # 2. has `using namespace esphome;` like our guides suggest + # Then the compiler will complain that the call is ambiguous + # Define a hacky macro so that the call is never ambiguous + # and always uses the esphome namespace one. + # See also https://github.com/esphome/issues/issues/2510 + # Priority -999 so that it runs before adding includes, as those + # also might reference these symbols + for line in ARDUINO_GLUE_CODE.splitlines(): + cg.add_global(cg.RawStatement(line)) + + @coroutine_with_priority(-1000.0) async def add_includes(includes): # Add includes at the very end, so that the included files can access global variables @@ -287,6 +312,9 @@ async def to_code(config): cg.add_build_flag("-Wno-unused-but-set-variable") cg.add_build_flag("-Wno-sign-compare") + if CORE.using_arduino: + CORE.add_job(add_arduino_global_workaround) + if config[CONF_INCLUDES]: CORE.add_job(add_includes, config[CONF_INCLUDES]) From 7b8d826704715792f5fba2d8e8e9540b3cb86d46 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 21 Oct 2021 14:20:57 +0200 Subject: [PATCH 047/273] Fix ESP8266 OTA adds unnecessary Update library (#2579) --- esphome/components/ota/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 53b282c43e..b3d3b7ad23 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -90,9 +90,7 @@ async def to_code(config): ) cg.add(RawExpression(f"if ({condition}) return")) - if CORE.is_esp8266: - cg.add_library("Update", None) - elif CORE.is_esp32 and CORE.using_arduino: + if CORE.is_esp32 and CORE.using_arduino: cg.add_library("Update", None) use_state_callback = False From eae3d72a4da02f03929ddc2cc6d40e7b0c81c0d4 Mon Sep 17 00:00:00 2001 From: Otto winter Date: Thu, 21 Oct 2021 14:23:08 +0200 Subject: [PATCH 048/273] Bump version to 2021.10.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 59441e23c8..f07c83b32d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0" +__version__ = "2021.10.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From ab07ee57c6f96025c2dbaefc5a642a05f689b0b3 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 21 Oct 2021 14:39:36 +0200 Subject: [PATCH 049/273] Fix ESP8266 dallas GPIO16 INPUT_PULLUP (#2581) --- esphome/components/esp8266/gpio.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/esphome/components/esp8266/gpio.cpp b/esphome/components/esp8266/gpio.cpp index cb703c18e1..7805889b40 100644 --- a/esphome/components/esp8266/gpio.cpp +++ b/esphome/components/esp8266/gpio.cpp @@ -50,6 +50,13 @@ void ESP8266GPIOPin::pin_mode(gpio::Flags flags) { mode = OUTPUT; } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { mode = INPUT_PULLUP; + if (pin_ == 16) { + // GPIO16 doesn't have a pullup, so pinMode would fail. + // However, sometimes this method is called with pullup mode anyway + // for example from dallas one_wire. For those cases convert this + // to a INPUT mode. + mode = INPUT; + } } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) { mode = INPUT_PULLDOWN_16; } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { From f0aba6ceb282155f69c2928e9e896eea0e924ac8 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 21 Oct 2021 14:53:08 +0200 Subject: [PATCH 050/273] Fix platformio version in Dockerfile doesn't match requirements (#2582) --- docker/Dockerfile | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index e66c3e1d95..7928ff8901 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -43,7 +43,7 @@ RUN \ # Ubuntu python3-pip is missing wheel pip3 install --no-cache-dir \ wheel==0.36.2 \ - platformio==5.2.0 \ + platformio==5.2.1 \ # Change some platformio settings && platformio settings set enable_telemetry No \ && platformio settings set check_libraries_interval 1000000 \ diff --git a/requirements.txt b/requirements.txt index 63804e89e8..d0ee047f3a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ tornado==6.1 tzlocal==3.0 # from time tzdata>=2021.1 # from time pyserial==3.5 -platformio==5.2.1 +platformio==5.2.1 # When updating platformio, also update Dockerfile esptool==3.1 click==8.0.3 esphome-dashboard==20211021.0 From 7d9d9fcf3601e1058e4c47b43481d1bab8dc63ae Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 21 Oct 2021 15:29:32 +0200 Subject: [PATCH 051/273] Fix platformio_install_deps no longer installing all lib_deps (#2584) --- docker/platformio_install_deps.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docker/platformio_install_deps.py b/docker/platformio_install_deps.py index 5625bd4d01..c7b11cf321 100755 --- a/docker/platformio_install_deps.py +++ b/docker/platformio_install_deps.py @@ -8,6 +8,23 @@ import sys config = configparser.ConfigParser(inline_comment_prefixes=(';', )) config.read(sys.argv[1]) -libs = [x for x in config['common']['lib_deps'].splitlines() if len(x) != 0] + +libs = [] +# Extract from every lib_deps key in all sections +for section in config.sections(): + conf = config[section] + if "lib_deps" not in conf: + continue + for lib_dep in conf["lib_deps"].splitlines(): + if not lib_dep: + # Empty line or comment + continue + if lib_dep.startswith("${"): + # Extending from another section + continue + if "@" not in lib_dep: + # No version pinned, this is an internal lib + continue + libs.append(lib_dep) subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs]) From eed0c18d65e2f732128e48661218abc6a499b6dd Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 21 Oct 2021 18:57:03 +0200 Subject: [PATCH 052/273] Fix HeatpumpIR pin (#2585) --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 9cc7477d51..6a8b342314 100644 --- a/platformio.ini +++ b/platformio.ini @@ -49,7 +49,7 @@ lib_deps = glmnet/Dsmr@0.5 ; dsmr rweather/Crypto@0.2.0 ; dsmr dudanov/MideaUART@1.1.8 ; midea - tonia/HeatpumpIR@^1.0.15 ; heatpumpir + tonia/HeatpumpIR@1.0.15 ; heatpumpir build_flags = ${common.build_flags} -DUSE_ARDUINO From ab3440142112e956ab40676088c8e7fb2bc584c7 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Thu, 21 Oct 2021 18:59:49 +0200 Subject: [PATCH 053/273] Fix PlatformIO version for latest Arduino framework (#2590) --- esphome/components/esp8266/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index a5323db8bf..ddaeee6ab7 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -65,7 +65,7 @@ RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 7, 4) # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif8266 ARDUINO_2_PLATFORM_VERSION = cv.Version(2, 6, 2) # for arduino 3 framework versions -ARDUINO_3_PLATFORM_VERSION = cv.Version(3, 0, 2) +ARDUINO_3_PLATFORM_VERSION = cv.Version(3, 2, 0) def _arduino_check_versions(value): From ed0b34b2fe03d71cc28694c9ec39446fd888c9bc Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Thu, 21 Oct 2021 19:55:19 +0200 Subject: [PATCH 054/273] Fix pin/component switchup in SX1509 pin configuration (#2593) --- esphome/components/sx1509/__init__.py | 4 ++-- esphome/core/helpers.cpp | 1 + tests/test4.yaml | 10 ++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/esphome/components/sx1509/__init__.py b/esphome/components/sx1509/__init__.py index f1b7d5f424..879ced2fb3 100644 --- a/esphome/components/sx1509/__init__.py +++ b/esphome/components/sx1509/__init__.py @@ -80,8 +80,8 @@ def validate_mode(value): CONF_SX1509 = "sx1509" SX1509_PIN_SCHEMA = cv.All( { - cv.GenerateID(): cv.declare_id(SX1509Component), - cv.Required(CONF_SX1509): cv.use_id(SX1509GPIOPin), + cv.GenerateID(): cv.declare_id(SX1509GPIOPin), + cv.Required(CONF_SX1509): cv.use_id(SX1509Component), cv.Required(CONF_NUMBER): cv.int_range(min=0, max=15), cv.Optional(CONF_MODE, default={}): cv.All( { diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 780df3ca6d..bc97259a71 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -358,6 +358,7 @@ template T clamp(const T val, const T min, const T max) { return max; return val; } +template uint8_t clamp(uint8_t, uint8_t, uint8_t); template float clamp(float, float, float); template int clamp(int, int, int); diff --git a/tests/test4.yaml b/tests/test4.yaml index 4f2025ad74..bc249c5ecb 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -59,6 +59,10 @@ tuya: pipsolar: id: inverter0 +sx1509: + - id: sx1509_hub + address: 0x3E + sensor: - platform: homeassistant entity_id: sensor.hello_world @@ -209,6 +213,7 @@ sensor: - or: - throttle: "20min" - delta: 0.02 + # # platform sensor.apds9960 requires component apds9960 # @@ -308,6 +313,11 @@ binary_sensor: y_max: 212 on_state: - lambda: 'ESP_LOGI("main", "key0: %s", (x ? "touch" : "release"));' + - platform: gpio + name: GPIO SX1509 test + pin: + sx1509: sx1509_hub + number: 3 climate: - platform: tuya From 115bca98f16ef99c718d563ec25f395d0aef744b Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Thu, 21 Oct 2021 19:56:47 +0200 Subject: [PATCH 055/273] Fix old-style `arduino_version` on ESP8266 and with magic values (#2591) --- esphome/const.py | 5 ++++- esphome/core/config.py | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/esphome/const.py b/esphome/const.py index f07c83b32d..ce401b9f73 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,10 @@ __version__ = "2021.10.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" -TARGET_PLATFORMS = ["esp32", "esp8266"] +PLATFORM_ESP32 = "esp32" +PLATFORM_ESP8266 = "esp8266" + +TARGET_PLATFORMS = [PLATFORM_ESP32, PLATFORM_ESP8266] TARGET_FRAMEWORKS = ["arduino", "esp-idf"] # See also https://github.com/platformio/platform-espressif8266/releases diff --git a/esphome/core/config.py b/esphome/core/config.py index 3c53d81784..235e0eeb84 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -29,6 +29,7 @@ from esphome.const import ( CONF_VERSION, KEY_CORE, TARGET_PLATFORMS, + PLATFORM_ESP8266, ) from esphome.core import CORE, coroutine_with_priority from esphome.helpers import copy_file_if_changed, walk_files @@ -182,9 +183,13 @@ def preload_core_config(config, result): if CONF_BOARD_FLASH_MODE in conf: plat_conf[CONF_BOARD_FLASH_MODE] = conf.pop(CONF_BOARD_FLASH_MODE) if CONF_ARDUINO_VERSION in conf: - plat_conf[CONF_FRAMEWORK] = {CONF_TYPE: "arduino"} + plat_conf[CONF_FRAMEWORK] = {} + if plat != PLATFORM_ESP8266: + plat_conf[CONF_FRAMEWORK][CONF_TYPE] = "arduino" + try: - cv.Version.parse(conf[CONF_ARDUINO_VERSION]) + if conf[CONF_ARDUINO_VERSION] not in ("recommended", "latest", "dev"): + cv.Version.parse(conf[CONF_ARDUINO_VERSION]) plat_conf[CONF_FRAMEWORK][CONF_VERSION] = conf.pop(CONF_ARDUINO_VERSION) except ValueError: plat_conf[CONF_FRAMEWORK][CONF_SOURCE] = conf.pop(CONF_ARDUINO_VERSION) From 68cbe58d005903c88736725f44991c718b355109 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Oct 2021 20:01:03 +0200 Subject: [PATCH 056/273] Bump esphome-dashboard from 20211021.0 to 20211021.1 (#2594) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d0ee047f3a..51bbb445d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.1 # When updating platformio, also update Dockerfile esptool==3.1 click==8.0.3 -esphome-dashboard==20211021.0 +esphome-dashboard==20211021.1 aioesphomeapi==9.1.5 # esp-idf requires this, but doesn't bundle it by default From bbcd52396745f3a33da705c403da66f322228d52 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Thu, 21 Oct 2021 20:07:37 +0200 Subject: [PATCH 057/273] Fix validation of addressable light IDs (#2588) --- esphome/components/light/addressable_light.h | 6 ++++++ esphome/components/light/types.py | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index fea7508515..2b2c0ca7e4 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -22,6 +22,12 @@ using ESPColor ESPDEPRECATED("esphome::light::ESPColor is deprecated, use esphom /// Convert the color information from a `LightColorValues` object to a `Color` object (does not apply brightness). Color color_from_light_color_values(LightColorValues val); +/// Use a custom state class for addressable lights, to allow type system to discriminate between addressable and +/// non-addressable lights. +class AddressableLightState : public LightState { + using LightState::LightState; +}; + class AddressableLight : public LightOutput, public Component { public: virtual int32_t size() const = 0; diff --git a/esphome/components/light/types.py b/esphome/components/light/types.py index cf544e5435..bc20cd5555 100644 --- a/esphome/components/light/types.py +++ b/esphome/components/light/types.py @@ -4,10 +4,9 @@ from esphome import automation # Base light_ns = cg.esphome_ns.namespace("light") LightState = light_ns.class_("LightState", cg.EntityBase, cg.Component) -# Fake class for addressable lights -AddressableLightState = light_ns.class_("LightState", LightState) +AddressableLightState = light_ns.class_("AddressableLightState", LightState) LightOutput = light_ns.class_("LightOutput") -AddressableLight = light_ns.class_("AddressableLight", cg.Component) +AddressableLight = light_ns.class_("AddressableLight", LightOutput, cg.Component) AddressableLightRef = AddressableLight.operator("ref") Color = cg.esphome_ns.class_("Color") From f93e7d4e3a63f946ecaef103bf239d9295dbaae1 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 22 Oct 2021 10:46:44 +0200 Subject: [PATCH 058/273] Fix socket connection closed not detected (#2587) --- esphome/components/api/api_connection.cpp | 2 + esphome/components/api/api_frame_helper.cpp | 48 ++++++++++++++----- esphome/components/api/api_frame_helper.h | 1 + esphome/components/ota/ota_component.cpp | 9 ++++ .../components/socket/lwip_raw_tcp_impl.cpp | 8 +++- 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 47171ba50f..c87ccf4dc0 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -78,6 +78,8 @@ void APIConnection::loop() { on_fatal_error(); if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) { ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str()); + } else if (err == APIError::CONNECTION_CLOSED) { + ESP_LOGW(TAG, "%s: Connection closed", client_info_.c_str()); } else { ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); } diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 4971272f41..c0e37ec90d 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -10,7 +10,7 @@ namespace api { static const char *const TAG = "api.socket"; -/// Is the given return value (from read/write syscalls) a wouldblock error? +/// Is the given return value (from write syscalls) a wouldblock error? bool is_would_block(ssize_t ret) { if (ret == -1) { return errno == EWOULDBLOCK || errno == EAGAIN; @@ -64,6 +64,8 @@ const char *api_error_to_str(APIError err) { return "HANDSHAKESTATE_SPLIT_FAILED"; } else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) { return "BAD_HANDSHAKE_ERROR_BYTE"; + } else if (err == APIError::CONNECTION_CLOSED) { + return "CONNECTION_CLOSED"; } return "UNKNOWN"; } @@ -185,12 +187,17 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) { // no header information yet size_t to_read = 3 - rx_header_buf_len_; ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read); - if (is_would_block(received)) { - return APIError::WOULD_BLOCK; - } else if (received == -1) { + if (received == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + return APIError::WOULD_BLOCK; + } state_ = State::FAILED; HELPER_LOG("Socket read failed with errno %d", errno); return APIError::SOCKET_READ_FAILED; + } else if (received == 0) { + state_ = State::FAILED; + HELPER_LOG("Connection closed"); + return APIError::CONNECTION_CLOSED; } rx_header_buf_len_ += received; if (received != to_read) { @@ -227,12 +234,17 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) { // more data to read size_t to_read = msg_size - rx_buf_len_; ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read); - if (is_would_block(received)) { - return APIError::WOULD_BLOCK; - } else if (received == -1) { + if (received == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + return APIError::WOULD_BLOCK; + } state_ = State::FAILED; HELPER_LOG("Socket read failed with errno %d", errno); return APIError::SOCKET_READ_FAILED; + } else if (received == 0) { + state_ = State::FAILED; + HELPER_LOG("Connection closed"); + return APIError::CONNECTION_CLOSED; } rx_buf_len_ += received; if (received != to_read) { @@ -778,12 +790,17 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { while (!rx_header_parsed_) { uint8_t data; ssize_t received = socket_->read(&data, 1); - if (is_would_block(received)) { - return APIError::WOULD_BLOCK; - } else if (received == -1) { + if (received == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + return APIError::WOULD_BLOCK; + } state_ = State::FAILED; HELPER_LOG("Socket read failed with errno %d", errno); return APIError::SOCKET_READ_FAILED; + } else if (received == 0) { + state_ = State::FAILED; + HELPER_LOG("Connection closed"); + return APIError::CONNECTION_CLOSED; } rx_header_buf_.push_back(data); @@ -824,12 +841,17 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { // more data to read size_t to_read = rx_header_parsed_len_ - rx_buf_len_; ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read); - if (is_would_block(received)) { - return APIError::WOULD_BLOCK; - } else if (received == -1) { + if (received == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + return APIError::WOULD_BLOCK; + } state_ = State::FAILED; HELPER_LOG("Socket read failed with errno %d", errno); return APIError::SOCKET_READ_FAILED; + } else if (received == 0) { + state_ = State::FAILED; + HELPER_LOG("Connection closed"); + return APIError::CONNECTION_CLOSED; } rx_buf_len_ += received; if (received != to_read) { diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 7fdb26fd40..57e3c961d5 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -53,6 +53,7 @@ enum class APIError : int { HANDSHAKESTATE_SETUP_FAILED = 1019, HANDSHAKESTATE_SPLIT_FAILED = 1020, BAD_HANDSHAKE_ERROR_BYTE = 1021, + CONNECTION_CLOSED = 1022, }; const char *api_error_to_str(APIError err); diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 89bee17452..6d51087882 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -275,6 +275,12 @@ void OTAComponent::handle_() { } ESP_LOGW(TAG, "Error receiving data for update, errno: %d", errno); goto error; + } else if (read == 0) { + // $ man recv + // "When a stream socket peer has performed an orderly shutdown, the return value will + // be 0 (the traditional "end-of-file" return)." + ESP_LOGW(TAG, "Remote end closed connection"); + goto error; } error_code = backend->write(buf, read); @@ -362,6 +368,9 @@ bool OTAComponent::readall_(uint8_t *buf, size_t len) { } ESP_LOGW(TAG, "Failed to read %d bytes of data, errno: %d", len, errno); return false; + } else if (read == 0) { + ESP_LOGW(TAG, "Remote closed connection"); + return false; } else { at += read; } diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 54dfddac3f..922d895ff4 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -320,8 +320,7 @@ class LWIPRawImpl : public Socket { return -1; } if (rx_closed_ && rx_buf_ == nullptr) { - errno = ECONNRESET; - return -1; + return 0; } if (len == 0) { return 0; @@ -366,6 +365,11 @@ class LWIPRawImpl : public Socket { read += copysize; } + if (read == 0) { + errno = EWOULDBLOCK; + return -1; + } + return read; } ssize_t readv(const struct iovec *iov, int iovcnt) override { From 42873dd37ca231dc1dd33f8f8cc72ac4bc10f541 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 22 Oct 2021 12:12:07 +0200 Subject: [PATCH 059/273] Bump noise-c from 0.1.3 to 0.1.4 (#2602) --- esphome/components/api/__init__.py | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index b0608a69dd..6b2e7fd06b 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -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.3") + cg.add_library("esphome/noise-c", "0.1.4") else: cg.add_define("USE_API_PLAINTEXT") diff --git a/platformio.ini b/platformio.ini index 6a8b342314..ac5144fc37 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,7 +26,7 @@ build_flags = [common] lib_deps = - esphome/noise-c@0.1.3 ; api + esphome/noise-c@0.1.4 ; api makuna/NeoPixelBus@2.6.7 ; neopixelbus build_flags = -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE From 5be52f71f9c036d78fa1fe6799650899a6180794 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 22 Oct 2021 13:02:55 +0200 Subject: [PATCH 060/273] Add OTA upload compression for ESP8266 (#2601) --- esphome/components/ota/ota_backend.h | 1 + .../ota/ota_backend_arduino_esp32.h | 1 + .../ota/ota_backend_arduino_esp8266.h | 1 + esphome/components/ota/ota_backend_esp_idf.h | 1 + esphome/components/ota/ota_component.cpp | 9 +++- esphome/components/ota/ota_component.h | 1 + esphome/espota2.py | 44 ++++++++++++------- 7 files changed, 42 insertions(+), 16 deletions(-) diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h index c253e009c6..5c5b61a278 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota/ota_backend.h @@ -12,6 +12,7 @@ class OTABackend { virtual OTAResponseTypes write(uint8_t *data, size_t len) = 0; virtual OTAResponseTypes end() = 0; virtual void abort() = 0; + virtual bool supports_compression() = 0; }; } // namespace ota diff --git a/esphome/components/ota/ota_backend_arduino_esp32.h b/esphome/components/ota/ota_backend_arduino_esp32.h index 6b712502fb..f86a70d678 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.h +++ b/esphome/components/ota/ota_backend_arduino_esp32.h @@ -15,6 +15,7 @@ class ArduinoESP32OTABackend : public OTABackend { OTAResponseTypes write(uint8_t *data, size_t len) override; OTAResponseTypes end() override; void abort() override; + bool supports_compression() override { return false; } }; } // namespace ota diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.h b/esphome/components/ota/ota_backend_arduino_esp8266.h index d1195af911..cf29a90fc1 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.h +++ b/esphome/components/ota/ota_backend_arduino_esp8266.h @@ -16,6 +16,7 @@ class ArduinoESP8266OTABackend : public OTABackend { OTAResponseTypes write(uint8_t *data, size_t len) override; OTAResponseTypes end() override; void abort() override; + bool supports_compression() override { return true; } }; } // namespace ota diff --git a/esphome/components/ota/ota_backend_esp_idf.h b/esphome/components/ota/ota_backend_esp_idf.h index 49c6e124fa..af09d0d693 100644 --- a/esphome/components/ota/ota_backend_esp_idf.h +++ b/esphome/components/ota/ota_backend_esp_idf.h @@ -17,6 +17,7 @@ class IDFOTABackend : public OTABackend { OTAResponseTypes write(uint8_t *data, size_t len) override; OTAResponseTypes end() override; void abort() override; + bool supports_compression() override { return false; } private: esp_ota_handle_t update_handle_{0}; diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 6d51087882..e49c108320 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -104,6 +104,8 @@ void OTAComponent::loop() { } } +static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01; + void OTAComponent::handle_() { OTAResponseTypes error_code = OTA_RESPONSE_ERROR_UNKNOWN; bool update_started = false; @@ -154,6 +156,8 @@ void OTAComponent::handle_() { buf[1] = OTA_VERSION_1_0; this->writeall_(buf, 2); + backend = make_ota_backend(); + // Read features - 1 byte if (!this->readall_(buf, 1)) { ESP_LOGW(TAG, "Reading features failed!"); @@ -164,6 +168,10 @@ void OTAComponent::handle_() { // Acknowledge header - 1 byte buf[0] = OTA_RESPONSE_HEADER_OK; + if ((ota_features & FEATURE_SUPPORTS_COMPRESSION) != 0 && backend->supports_compression()) { + buf[0] = OTA_RESPONSE_SUPPORTS_COMPRESSION; + } + this->writeall_(buf, 1); #ifdef USE_OTA_PASSWORD @@ -241,7 +249,6 @@ void OTAComponent::handle_() { } ESP_LOGV(TAG, "OTA size is %u bytes", ota_size); - backend = make_ota_backend(); error_code = backend->begin(ota_size); if (error_code != OTA_RESPONSE_OK) goto error; diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index e08e187df6..5647d52eeb 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -19,6 +19,7 @@ enum OTAResponseTypes { OTA_RESPONSE_BIN_MD5_OK = 67, OTA_RESPONSE_RECEIVE_OK = 68, OTA_RESPONSE_UPDATE_END_OK = 69, + OTA_RESPONSE_SUPPORTS_COMPRESSION = 70, OTA_RESPONSE_ERROR_MAGIC = 128, OTA_RESPONSE_ERROR_UPDATE_PREPARE = 129, diff --git a/esphome/espota2.py b/esphome/espota2.py index f8a2fab94c..8f299395dd 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -4,6 +4,7 @@ import random import socket import sys import time +import gzip from esphome.core import EsphomeError from esphome.helpers import is_ip_address, resolve_ip_address @@ -17,6 +18,7 @@ RESPONSE_UPDATE_PREPARE_OK = 66 RESPONSE_BIN_MD5_OK = 67 RESPONSE_RECEIVE_OK = 68 RESPONSE_UPDATE_END_OK = 69 +RESPONSE_SUPPORTS_COMPRESSION = 70 RESPONSE_ERROR_MAGIC = 128 RESPONSE_ERROR_UPDATE_PREPARE = 129 @@ -34,6 +36,8 @@ OTA_VERSION_1_0 = 1 MAGIC_BYTES = [0x6C, 0x26, 0xF7, 0x5C, 0x45] +FEATURE_SUPPORTS_COMPRESSION = 0x01 + _LOGGER = logging.getLogger(__name__) @@ -170,11 +174,9 @@ def send_check(sock, data, msg): def perform_ota(sock, password, file_handle, filename): - file_md5 = hashlib.md5(file_handle.read()).hexdigest() - file_size = file_handle.tell() + file_contents = file_handle.read() + file_size = len(file_contents) _LOGGER.info("Uploading %s (%s bytes)", filename, file_size) - file_handle.seek(0) - _LOGGER.debug("MD5 of binary is %s", file_md5) # Enable nodelay, we need it for phase 1 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) @@ -185,8 +187,16 @@ def perform_ota(sock, password, file_handle, filename): raise OTAError(f"Unsupported OTA version {version}") # Features - send_check(sock, 0x00, "features") - receive_exactly(sock, 1, "features", RESPONSE_HEADER_OK) + send_check(sock, FEATURE_SUPPORTS_COMPRESSION, "features") + features = receive_exactly( + sock, 1, "features", [RESPONSE_HEADER_OK, RESPONSE_SUPPORTS_COMPRESSION] + )[0] + + if features == RESPONSE_SUPPORTS_COMPRESSION: + upload_contents = gzip.compress(file_contents, compresslevel=9) + _LOGGER.info("Compressed to %s bytes", len(upload_contents)) + else: + upload_contents = file_contents (auth,) = receive_exactly( sock, 1, "auth", [RESPONSE_REQUEST_AUTH, RESPONSE_AUTH_OK] @@ -213,16 +223,20 @@ def perform_ota(sock, password, file_handle, filename): send_check(sock, result, "auth result") receive_exactly(sock, 1, "auth result", RESPONSE_AUTH_OK) - file_size_encoded = [ - (file_size >> 24) & 0xFF, - (file_size >> 16) & 0xFF, - (file_size >> 8) & 0xFF, - (file_size >> 0) & 0xFF, + upload_size = len(upload_contents) + upload_size_encoded = [ + (upload_size >> 24) & 0xFF, + (upload_size >> 16) & 0xFF, + (upload_size >> 8) & 0xFF, + (upload_size >> 0) & 0xFF, ] - send_check(sock, file_size_encoded, "binary size") + send_check(sock, upload_size_encoded, "binary size") receive_exactly(sock, 1, "binary size", RESPONSE_UPDATE_PREPARE_OK) - send_check(sock, file_md5, "file checksum") + upload_md5 = hashlib.md5(upload_contents).hexdigest() + _LOGGER.debug("MD5 of upload is %s", upload_md5) + + send_check(sock, upload_md5, "file checksum") receive_exactly(sock, 1, "file checksum", RESPONSE_BIN_MD5_OK) # Disable nodelay for transfer @@ -236,7 +250,7 @@ def perform_ota(sock, password, file_handle, filename): offset = 0 progress = ProgressBar() while True: - chunk = file_handle.read(1024) + chunk = upload_contents[offset : offset + 1024] if not chunk: break offset += len(chunk) @@ -247,7 +261,7 @@ def perform_ota(sock, password, file_handle, filename): sys.stderr.write("\n") raise OTAError(f"Error sending data: {err}") from err - progress.update(offset / float(file_size)) + progress.update(offset / upload_size) progress.done() # Enable nodelay for last checks From ff2c316b1830571c50b69e6bfc91962a0b0850c5 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 22 Oct 2021 14:14:07 +0200 Subject: [PATCH 061/273] Re-raise keyboardinterrupt (#2603) --- esphome/util.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/util.py b/esphome/util.py index 0f168cade3..937635fa43 100644 --- a/esphome/util.py +++ b/esphome/util.py @@ -192,8 +192,8 @@ def run_external_command( sys.argv = list(cmd) sys.exit = mock_exit return func() or 0 - except KeyboardInterrupt: - return 1 + except KeyboardInterrupt: # pylint: disable=try-except-raise + raise except SystemExit as err: return err.args[0] except Exception as err: # pylint: disable=broad-except @@ -227,6 +227,8 @@ def run_external_process(*cmd, **kwargs): try: return subprocess.call(cmd, stdout=sub_stdout, stderr=sub_stderr) + except KeyboardInterrupt: # pylint: disable=try-except-raise + raise except Exception as err: # pylint: disable=broad-except _LOGGER.error("Running command failed: %s", err) _LOGGER.error("Please try running %s locally.", full_cmd) From dfb96e4b7fb23ed00707d5401facf8970b930168 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 22 Oct 2021 14:14:14 +0200 Subject: [PATCH 062/273] Add owner to all libraries used (#2604) --- esphome/components/bme680_bsec/__init__.py | 2 +- esphome/components/mqtt/__init__.py | 2 +- esphome/components/web_server_base/__init__.py | 2 +- platformio.ini | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/bme680_bsec/__init__.py b/esphome/components/bme680_bsec/__init__.py index 38da18d702..2f844fa666 100644 --- a/esphome/components/bme680_bsec/__init__.py +++ b/esphome/components/bme680_bsec/__init__.py @@ -67,4 +67,4 @@ async def to_code(config): cg.add_library("SPI", None) cg.add_define("USE_BSEC") - cg.add_library("BSEC Software Library", "1.6.1480") + cg.add_library("boschsensortec/BSEC Software Library", "1.6.1480") diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 8f02f8d437..3d52dab67f 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -215,7 +215,7 @@ async def to_code(config): await cg.register_component(var, config) # https://github.com/OttoWinter/async-mqtt-client/blob/master/library.json - cg.add_library("ottowinter/AsyncMqttClient-esphome", "0.8.4") + cg.add_library("ottowinter/AsyncMqttClient-esphome", "0.8.6") cg.add_define("USE_MQTT") cg.add_global(mqtt_ns.using) diff --git a/esphome/components/web_server_base/__init__.py b/esphome/components/web_server_base/__init__.py index 95d59a863e..019f31954f 100644 --- a/esphome/components/web_server_base/__init__.py +++ b/esphome/components/web_server_base/__init__.py @@ -28,4 +28,4 @@ async def to_code(config): cg.add_library("FS", None) cg.add_library("Update", None) # https://github.com/esphome/ESPAsyncWebServer/blob/master/library.json - cg.add_library("esphome/ESPAsyncWebServer-esphome", "1.3.0") + cg.add_library("esphome/ESPAsyncWebServer-esphome", "1.3.1") diff --git a/platformio.ini b/platformio.ini index ac5144fc37..3d720e24ac 100644 --- a/platformio.ini +++ b/platformio.ini @@ -39,9 +39,9 @@ src_filter = extends = common lib_deps = ${common.lib_deps} - ottowinter/AsyncMqttClient-esphome@0.8.4 ; mqtt + ottowinter/AsyncMqttClient-esphome@0.8.6 ; mqtt ottowinter/ArduinoJson-esphomelib@5.13.3 ; json - esphome/ESPAsyncWebServer-esphome@1.3.0 ; web_server_base + esphome/ESPAsyncWebServer-esphome@1.3.1 ; web_server_base fastled/FastLED@3.3.2 ; fastled_base mikalhart/TinyGPSPlus@1.0.2 ; gps freekode/TM1651@1.0.1 ; tm1651 From 6bdae55ee16f638cd80d43d8a1a79b466ca510bf Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 22 Oct 2021 16:52:43 +0200 Subject: [PATCH 063/273] Fix compiler warnings and update platformio line filter (#2607) --- esphome/components/climate/climate.cpp | 4 ++++ esphome/components/mqtt/mqtt_fan.cpp | 6 ++++++ esphome/components/web_server/web_server.cpp | 6 ++++++ esphome/components/web_server_base/__init__.py | 2 +- esphome/platformio_api.py | 17 ++++++++++++----- platformio.ini | 2 +- 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 34e6328d8a..ebea20ed1f 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -440,7 +440,11 @@ void Climate::set_visual_max_temperature_override(float visual_max_temperature_o void Climate::set_visual_temperature_step_override(float visual_temperature_step_override) { this->visual_temperature_step_override_ = visual_temperature_step_override; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" Climate::Climate(const std::string &name) : EntityBase(name) {} +#pragma GCC diagnostic pop + Climate::Climate() : Climate("") {} ClimateCall Climate::make_call() { return ClimateCall(this); } diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 898183cc58..1703343a77 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -88,9 +88,12 @@ void MQTTFanComponent::setup() { if (this->state_->get_traits().supports_speed()) { this->subscribe(this->get_speed_command_topic(), [this](const std::string &topic, const std::string &payload) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" this->state_->make_call() .set_speed(payload.c_str()) // NOLINT(clang-diagnostic-deprecated-declarations) .perform(); +#pragma GCC diagnostic pop }); } @@ -145,6 +148,8 @@ bool MQTTFanComponent::publish_state() { } if (traits.supports_speed()) { const char *payload; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations) switch (fan::speed_level_to_enum(this->state_->speed, traits.supported_speed_count())) { case FAN_SPEED_LOW: { // NOLINT(clang-diagnostic-deprecated-declarations) @@ -161,6 +166,7 @@ bool MQTTFanComponent::publish_state() { break; } } +#pragma GCC diagnostic pop bool success = this->publish(this->get_speed_state_topic(), payload); failed = failed || !success; } diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index e99431be36..44ace38990 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -414,6 +414,8 @@ std::string WebServer::fan_json(fan::FanState *obj) { const auto traits = obj->get_traits(); if (traits.supports_speed()) { root["speed_level"] = obj->speed; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations) switch (fan::speed_level_to_enum(obj->speed, traits.supported_speed_count())) { case fan::FAN_SPEED_LOW: // NOLINT(clang-diagnostic-deprecated-declarations) @@ -426,6 +428,7 @@ std::string WebServer::fan_json(fan::FanState *obj) { root["speed"] = "high"; break; } +#pragma GCC diagnostic pop } if (obj->get_traits().supports_oscillation()) root["oscillation"] = obj->oscillating; @@ -448,7 +451,10 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc auto call = obj->turn_on(); if (request->hasParam("speed")) { String speed = request->getParam("speed")->value(); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" call.set_speed(speed.c_str()); // NOLINT(clang-diagnostic-deprecated-declarations) +#pragma GCC diagnostic pop } if (request->hasParam("speed_level")) { String speed_level = request->getParam("speed_level")->value(); diff --git a/esphome/components/web_server_base/__init__.py b/esphome/components/web_server_base/__init__.py index 019f31954f..4da94d990a 100644 --- a/esphome/components/web_server_base/__init__.py +++ b/esphome/components/web_server_base/__init__.py @@ -28,4 +28,4 @@ async def to_code(config): cg.add_library("FS", None) cg.add_library("Update", None) # https://github.com/esphome/ESPAsyncWebServer/blob/master/library.json - cg.add_library("esphome/ESPAsyncWebServer-esphome", "1.3.1") + cg.add_library("esphome/ESPAsyncWebServer-esphome", "2.0.0") diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 054c0cb1b0..70e4430e71 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -46,24 +46,31 @@ IGNORE_LIB_WARNINGS = f"(?:{'|'.join(['Hash', 'Update'])})" FILTER_PLATFORMIO_LINES = [ r"Verbose mode can be enabled via `-v, --verbose` option.*", r"CONFIGURATION: https://docs.platformio.org/.*", - r"PLATFORM: .*", r"DEBUG: Current.*", - r"PACKAGES: .*", + r"LDF Modes:.*", r"LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf.*", - r"LDF Modes: Finder ~ chain, Compatibility ~ soft.*", f"Looking for {IGNORE_LIB_WARNINGS} library in registry", f"Warning! Library `.*'{IGNORE_LIB_WARNINGS}.*` has not been found in PlatformIO Registry.", f"You can ignore this message, if `.*{IGNORE_LIB_WARNINGS}.*` is a built-in library.*", r"Scanning dependencies...", r"Found \d+ compatible libraries", r"Memory Usage -> http://bit.ly/pio-memory-usage", - r"esptool.py v.*", r"Found: https://platformio.org/lib/show/.*", r"Using cache: .*", r"Installing dependencies", - r".* @ .* is already installed", + r"Library Manager: Already installed, built-in library", r"Building in .* mode", r"Advanced Memory Usage is available via .*", + r"Merged .* ELF section", + r"esptool.py v.*", + r"Checking size .*", + r"Retrieving maximum program size .*", + r"PLATFORM: .*", + r"PACKAGES:.*", + r" - framework-arduinoespressif.* \(.*\)", + r" - tool-esptool.* \(.*\)", + r" - toolchain-.* \(.*\)", + r"Creating BIN file .*", ] diff --git a/platformio.ini b/platformio.ini index 3d720e24ac..ee895ed882 100644 --- a/platformio.ini +++ b/platformio.ini @@ -41,7 +41,7 @@ lib_deps = ${common.lib_deps} ottowinter/AsyncMqttClient-esphome@0.8.6 ; mqtt ottowinter/ArduinoJson-esphomelib@5.13.3 ; json - esphome/ESPAsyncWebServer-esphome@1.3.1 ; web_server_base + esphome/ESPAsyncWebServer-esphome@2.0.0 ; web_server_base fastled/FastLED@3.3.2 ; fastled_base mikalhart/TinyGPSPlus@1.0.2 ; gps freekode/TM1651@1.0.1 ; tm1651 From 901ec918b1f0575bdfbced07aa7f275e9f75cef0 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 22 Oct 2021 17:23:31 +0200 Subject: [PATCH 064/273] Fix ESP8266 OTA compression only starting framework v2.7.0 (#2610) --- esphome/components/ota/ota_backend_arduino_esp8266.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.h b/esphome/components/ota/ota_backend_arduino_esp8266.h index cf29a90fc1..329f2cf0f2 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.h +++ b/esphome/components/ota/ota_backend_arduino_esp8266.h @@ -5,6 +5,7 @@ #include "ota_component.h" #include "ota_backend.h" +#include "esphome/core/macros.h" namespace esphome { namespace ota { @@ -16,7 +17,11 @@ class ArduinoESP8266OTABackend : public OTABackend { OTAResponseTypes write(uint8_t *data, size_t len) override; OTAResponseTypes end() override; void abort() override; +#if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) bool supports_compression() override { return true; } +#else + bool supports_compression() override { return false; } +#endif }; } // namespace ota From c7ef18fbc4ace4dbf4ba2d0f7bf84ddec77d5356 Mon Sep 17 00:00:00 2001 From: Andreas Hergert <36455093+andreashergert1984@users.noreply.github.com> Date: Fri, 22 Oct 2021 18:20:57 +0200 Subject: [PATCH 065/273] Bugfix tca9548a and idf refactor anh (#2612) Co-authored-by: Andreas Hergert --- esphome/components/tca9548a/tca9548a.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/tca9548a/tca9548a.cpp b/esphome/components/tca9548a/tca9548a.cpp index 5117ad8969..e902eb5ed4 100644 --- a/esphome/components/tca9548a/tca9548a.cpp +++ b/esphome/components/tca9548a/tca9548a.cpp @@ -22,7 +22,7 @@ i2c::ErrorCode TCA9548AChannel::writev(uint8_t address, i2c::WriteBuffer *buffer void TCA9548AComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up TCA9548A..."); uint8_t status = 0; - if (!this->read_register(0x00, &status, 1)) { + if (this->read_register(0x00, &status, 1) != i2c::ERROR_OK) { ESP_LOGI(TAG, "TCA9548A failed"); this->mark_failed(); return; From eda1c471ad247c1f94e7fd1b7d33e80ef430eeaf Mon Sep 17 00:00:00 2001 From: Otto winter Date: Fri, 22 Oct 2021 18:26:24 +0200 Subject: [PATCH 066/273] Bump version to 2021.10.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index ce401b9f73..37c325465b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.1" +__version__ = "2021.10.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From cbfbcf7f1b74969152cbcf69ab9446a3f7bf6dc9 Mon Sep 17 00:00:00 2001 From: Andreas Hergert <36455093+andreashergert1984@users.noreply.github.com> Date: Fri, 22 Oct 2021 18:52:47 +0200 Subject: [PATCH 067/273] fixed dependency for pca9685 component (#2614) Co-authored-by: Otto Winter Co-authored-by: Andreas --- esphome/components/pca9685/pca9685_output.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/pca9685/pca9685_output.cpp b/esphome/components/pca9685/pca9685_output.cpp index 1ad6f4a665..957f4062fc 100644 --- a/esphome/components/pca9685/pca9685_output.cpp +++ b/esphome/components/pca9685/pca9685_output.cpp @@ -1,6 +1,7 @@ #include "pca9685_output.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace pca9685 { From 9aaaf4dd4be4b88e295d6ee8ffbcacb8eab231fe Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 23 Oct 2021 12:37:50 +0200 Subject: [PATCH 068/273] Bump platform-espressif8266 from 2.6.2 to 2.6.3 (#2620) --- esphome/components/esp8266/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index ddaeee6ab7..b2706bd4fb 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -63,7 +63,7 @@ RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 7, 4) # The platformio/espressif8266 version to use for arduino 2 framework versions # - https://github.com/platformio/platform-espressif8266/releases # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif8266 -ARDUINO_2_PLATFORM_VERSION = cv.Version(2, 6, 2) +ARDUINO_2_PLATFORM_VERSION = cv.Version(2, 6, 3) # for arduino 3 framework versions ARDUINO_3_PLATFORM_VERSION = cv.Version(3, 2, 0) From 2abe09529a0a18fba35dfc05d4253a93f640552f Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sat, 23 Oct 2021 13:25:46 +0200 Subject: [PATCH 069/273] Autodetect flash size (#2615) --- esphome/__main__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/__main__.py b/esphome/__main__.py index 97059154fd..c2a6dd343f 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -226,6 +226,8 @@ def upload_using_esptool(config, port): mcu, "write_flash", "-z", + "--flash_size", + "detect", ] for img in flash_images: cmd += [img.offset, img.path] From b34eed125dc604417c313891e37420624b3218b6 Mon Sep 17 00:00:00 2001 From: 0hax <43876620+0hax@users.noreply.github.com> Date: Sat, 23 Oct 2021 19:01:23 +0200 Subject: [PATCH 070/273] Teleinfo ptec (#2599) * teleinfo: handle historical mode correctly. In historical mode, tags like PTEC leads to an issue where we detect a timestamp wheras this is not possible in historical mode. PTEC teleinfo tag looks like: PTEC HP.. Instead of the usual format IINST1 001 I This make our data parsing fails. While at here, make sure we continue parsing other tags even if parsing one of the tag fails. Signed-off-by: 0hax <0hax@protonmail.com> * teleinfo: fix compilation with loglevel set to debug. Signed-off-by: 0hax <0hax@protonmail.com> --- .../teleinfo/sensor/teleinfo_sensor.cpp | 2 +- esphome/components/teleinfo/teleinfo.cpp | 17 +++++++++-------- .../text_sensor/teleinfo_text_sensor.cpp | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp b/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp index 661c149c09..4e4cd9f9e6 100644 --- a/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp +++ b/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp @@ -9,6 +9,6 @@ void TeleInfoSensor::publish_val(const std::string &val) { auto newval = parse_float(val); publish_state(*newval); } -void TeleInfoSensor::dump_config() { LOG_SENSOR(" ", tag.c_str(), this); } +void TeleInfoSensor::dump_config() { LOG_SENSOR(" ", "Teleinfo Sensor", this); } } // namespace teleinfo } // namespace esphome diff --git a/esphome/components/teleinfo/teleinfo.cpp b/esphome/components/teleinfo/teleinfo.cpp index badd66ae83..5a1e44ac8b 100644 --- a/esphome/components/teleinfo/teleinfo.cpp +++ b/esphome/components/teleinfo/teleinfo.cpp @@ -141,21 +141,22 @@ void TeleInfo::loop() { field_len = get_field(tag_, buf_finger, grp_end, separator_, MAX_TAG_SIZE); if (!field_len || field_len >= MAX_TAG_SIZE) { ESP_LOGE(TAG, "Invalid tag."); - break; + continue; } /* Advance buf_finger to after the tag and the separator. */ buf_finger += field_len + 1; /* - * If there is two separators and the tag is not equal to "DATE", - * it means there is a timestamp to read first. + * If there is two separators and the tag is not equal to "DATE" or + * historical mode is not in use (separator_ != 0x20), it means there is a + * timestamp to read first. */ - if (std::count(buf_finger, grp_end, separator_) == 2 && strcmp(tag_, "DATE") != 0) { + if (std::count(buf_finger, grp_end, separator_) == 2 && strcmp(tag_, "DATE") != 0 && separator_ != 0x20) { field_len = get_field(timestamp_, buf_finger, grp_end, separator_, MAX_TIMESTAMP_SIZE); if (!field_len || field_len >= MAX_TIMESTAMP_SIZE) { - ESP_LOGE(TAG, "Invalid Timestamp"); - break; + ESP_LOGE(TAG, "Invalid timestamp for tag %s", timestamp_); + continue; } /* Advance buf_finger to after the first data and the separator. */ @@ -164,8 +165,8 @@ void TeleInfo::loop() { field_len = get_field(val_, buf_finger, grp_end, separator_, MAX_VAL_SIZE); if (!field_len || field_len >= MAX_VAL_SIZE) { - ESP_LOGE(TAG, "Invalid Value"); - break; + ESP_LOGE(TAG, "Invalid value for tag %s", tag_); + continue; } /* Advance buf_finger to end of group */ diff --git a/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.cpp b/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.cpp index 1adbd9ce13..87cf0dea17 100644 --- a/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.cpp +++ b/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.cpp @@ -6,6 +6,6 @@ namespace teleinfo { static const char *const TAG = "teleinfo_text_sensor"; TeleInfoTextSensor::TeleInfoTextSensor(const char *tag) { this->tag = std::string(tag); } void TeleInfoTextSensor::publish_val(const std::string &val) { publish_state(val); } -void TeleInfoTextSensor::dump_config() { LOG_TEXT_SENSOR(" ", tag.c_str(), this); } +void TeleInfoTextSensor::dump_config() { LOG_TEXT_SENSOR(" ", "Teleinfo Text Sensor", this); } } // namespace teleinfo } // namespace esphome From 91999a38ca1608a234d42d2079263a6d53064692 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 23 Oct 2021 19:25:53 +0200 Subject: [PATCH 071/273] Fix glue code missing micros() (#2623) --- esphome/core/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/core/config.py b/esphome/core/config.py index 235e0eeb84..262451df88 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -214,6 +214,7 @@ def include_file(path, basename): ARDUINO_GLUE_CODE = """\ #define yield() esphome::yield() #define millis() esphome::millis() +#define micros() esphome::micros() #define delay(x) esphome::delay(x) #define delayMicroseconds(x) esphome::delayMicroseconds(x) """ From c6adaaea9726fbdcbc8414144291048e556fb2ef Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 26 Oct 2021 21:55:20 +1300 Subject: [PATCH 072/273] Remove power and energy from sensors that are not true power (#2628) --- esphome/components/dsmr/sensor.py | 16 ++++++++-------- esphome/components/havells_solar/sensor.py | 4 +--- esphome/components/pipsolar/sensor/__init__.py | 4 ++-- esphome/components/sdm_meter/sensor.py | 4 ---- esphome/components/selec_meter/sensor.py | 8 -------- 5 files changed, 11 insertions(+), 25 deletions(-) diff --git a/esphome/components/dsmr/sensor.py b/esphome/components/dsmr/sensor.py index 761009c766..d809d0d105 100644 --- a/esphome/components/dsmr/sensor.py +++ b/esphome/components/dsmr/sensor.py @@ -75,14 +75,14 @@ CONFIG_SCHEMA = cv.Schema( UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, ICON_EMPTY, 3, - DEVICE_CLASS_ENERGY, + DEVICE_CLASS_EMPTY, STATE_CLASS_NONE, ), cv.Optional("total_exported_energy"): sensor.sensor_schema( UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, ICON_EMPTY, 3, - DEVICE_CLASS_ENERGY, + DEVICE_CLASS_EMPTY, STATE_CLASS_NONE, ), cv.Optional("power_delivered"): sensor.sensor_schema( @@ -166,42 +166,42 @@ CONFIG_SCHEMA = cv.Schema( UNIT_KILOVOLT_AMPS_REACTIVE, ICON_EMPTY, 3, - DEVICE_CLASS_POWER, + DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_delivered_l2"): sensor.sensor_schema( UNIT_KILOVOLT_AMPS_REACTIVE, ICON_EMPTY, 3, - DEVICE_CLASS_POWER, + DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_delivered_l3"): sensor.sensor_schema( UNIT_KILOVOLT_AMPS_REACTIVE, ICON_EMPTY, 3, - DEVICE_CLASS_POWER, + DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_returned_l1"): sensor.sensor_schema( UNIT_KILOVOLT_AMPS_REACTIVE, ICON_EMPTY, 3, - DEVICE_CLASS_POWER, + DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_returned_l2"): sensor.sensor_schema( UNIT_KILOVOLT_AMPS_REACTIVE, ICON_EMPTY, 3, - DEVICE_CLASS_POWER, + DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_returned_l3"): sensor.sensor_schema( UNIT_KILOVOLT_AMPS_REACTIVE, ICON_EMPTY, 3, - DEVICE_CLASS_POWER, + DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT, ), cv.Optional("voltage_l1"): sensor.sensor_schema( diff --git a/esphome/components/havells_solar/sensor.py b/esphome/components/havells_solar/sensor.py index 3ec12d5b83..d7c8d544f9 100644 --- a/esphome/components/havells_solar/sensor.py +++ b/esphome/components/havells_solar/sensor.py @@ -93,13 +93,12 @@ PV_SENSORS = { CONF_VOLTAGE_SAMPLED_BY_SECONDARY_CPU: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=0, - device_class=DEVICE_CLASS_POWER, + device_class=DEVICE_CLASS_VOLTAGE, state_class=STATE_CLASS_MEASUREMENT, ), CONF_INSULATION_OF_P_TO_GROUND: sensor.sensor_schema( unit_of_measurement=UNIT_KOHM, accuracy_decimals=0, - device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), } @@ -135,7 +134,6 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE, accuracy_decimals=2, - device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_ENERGY_PRODUCTION_DAY): sensor.sensor_schema( diff --git a/esphome/components/pipsolar/sensor/__init__.py b/esphome/components/pipsolar/sensor/__init__.py index 5e4dd6c40c..a206e41988 100644 --- a/esphome/components/pipsolar/sensor/__init__.py +++ b/esphome/components/pipsolar/sensor/__init__.py @@ -89,7 +89,7 @@ TYPES = { UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT ), CONF_AC_OUTPUT_RATING_APPARENT_POWER: sensor.sensor_schema( - UNIT_VOLT_AMPS, ICON_EMPTY, 1, DEVICE_CLASS_POWER + UNIT_VOLT_AMPS, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY ), CONF_AC_OUTPUT_RATING_ACTIVE_POWER: sensor.sensor_schema( UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER @@ -159,7 +159,7 @@ TYPES = { UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY ), CONF_AC_OUTPUT_APPARENT_POWER: sensor.sensor_schema( - UNIT_VOLT_AMPS, ICON_EMPTY, 1, DEVICE_CLASS_POWER + UNIT_VOLT_AMPS, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY ), CONF_AC_OUTPUT_ACTIVE_POWER: sensor.sensor_schema( UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER diff --git a/esphome/components/sdm_meter/sensor.py b/esphome/components/sdm_meter/sensor.py index 8a0d9674a7..87c99c9152 100644 --- a/esphome/components/sdm_meter/sensor.py +++ b/esphome/components/sdm_meter/sensor.py @@ -64,13 +64,11 @@ PHASE_SENSORS = { CONF_APPARENT_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS, accuracy_decimals=2, - device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), CONF_REACTIVE_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE, accuracy_decimals=2, - device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), CONF_POWER_FACTOR: sensor.sensor_schema( @@ -115,13 +113,11 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_IMPORT_REACTIVE_ENERGY): sensor.sensor_schema( unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, accuracy_decimals=2, - device_class=DEVICE_CLASS_ENERGY, state_class=STATE_CLASS_TOTAL_INCREASING, ), cv.Optional(CONF_EXPORT_REACTIVE_ENERGY): sensor.sensor_schema( unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, accuracy_decimals=2, - device_class=DEVICE_CLASS_ENERGY, state_class=STATE_CLASS_TOTAL_INCREASING, ), } diff --git a/esphome/components/selec_meter/sensor.py b/esphome/components/selec_meter/sensor.py index 168d3a3db2..e698255c25 100644 --- a/esphome/components/selec_meter/sensor.py +++ b/esphome/components/selec_meter/sensor.py @@ -71,25 +71,21 @@ SENSORS = { CONF_TOTAL_REACTIVE_ENERGY: sensor.sensor_schema( unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, accuracy_decimals=2, - device_class=DEVICE_CLASS_ENERGY, state_class=STATE_CLASS_TOTAL_INCREASING, ), CONF_IMPORT_REACTIVE_ENERGY: sensor.sensor_schema( unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, accuracy_decimals=2, - device_class=DEVICE_CLASS_ENERGY, state_class=STATE_CLASS_TOTAL_INCREASING, ), CONF_EXPORT_REACTIVE_ENERGY: sensor.sensor_schema( unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, accuracy_decimals=2, - device_class=DEVICE_CLASS_ENERGY, state_class=STATE_CLASS_TOTAL_INCREASING, ), CONF_APPARENT_ENERGY: sensor.sensor_schema( unit_of_measurement=UNIT_KILOVOLT_AMPS_HOURS, accuracy_decimals=2, - device_class=DEVICE_CLASS_ENERGY, state_class=STATE_CLASS_TOTAL_INCREASING, ), CONF_ACTIVE_POWER: sensor.sensor_schema( @@ -101,13 +97,11 @@ SENSORS = { CONF_REACTIVE_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE, accuracy_decimals=3, - device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), CONF_APPARENT_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS, accuracy_decimals=3, - device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), CONF_VOLTAGE: sensor.sensor_schema( @@ -142,13 +136,11 @@ SENSORS = { CONF_MAXIMUM_DEMAND_REACTIVE_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE, accuracy_decimals=3, - device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), CONF_MAXIMUM_DEMAND_APPARENT_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS, accuracy_decimals=3, - device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), } From 72108684ea20bc6b8527726a27c5c298919f3e24 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Tue, 26 Oct 2021 11:30:25 +0200 Subject: [PATCH 073/273] fix modbus output (#2630) --- .../components/modbus_controller/__init__.py | 15 +++++++++++++++ .../modbus_controller/number/__init__.py | 17 +---------------- .../modbus_controller/output/__init__.py | 13 ++++++++++++- .../modbus_controller/output/modbus_output.h | 3 ++- .../modbus_controller/sensor/__init__.py | 18 +----------------- 5 files changed, 31 insertions(+), 35 deletions(-) diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 6b452ea25c..8499cec561 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -61,6 +61,21 @@ SENSOR_VALUE_TYPE = { "FP32_R": SensorValueType.FP32_R, } +TYPE_REGISTER_MAP = { + "RAW": 1, + "U_WORD": 1, + "S_WORD": 1, + "U_DWORD": 2, + "U_DWORD_R": 2, + "S_DWORD": 2, + "S_DWORD_R": 2, + "U_QWORD": 4, + "U_QWORDU_R": 4, + "S_QWORD": 4, + "U_QWORD_R": 4, + "FP32": 2, + "FP32_R": 2, +} MULTI_CONF = True diff --git a/esphome/components/modbus_controller/number/__init__.py b/esphome/components/modbus_controller/number/__init__.py index c7919bb972..afb69f8798 100644 --- a/esphome/components/modbus_controller/number/__init__.py +++ b/esphome/components/modbus_controller/number/__init__.py @@ -17,6 +17,7 @@ from .. import ( ModbusController, SENSOR_VALUE_TYPE, SensorItem, + TYPE_REGISTER_MAP, ) @@ -39,22 +40,6 @@ ModbusNumber = modbus_controller_ns.class_( "ModbusNumber", cg.Component, number.Number, SensorItem ) -TYPE_REGISTER_MAP = { - "RAW": 1, - "U_WORD": 1, - "S_WORD": 1, - "U_DWORD": 2, - "U_DWORD_R": 2, - "S_DWORD": 2, - "S_DWORD_R": 2, - "U_QWORD": 4, - "U_QWORDU_R": 4, - "S_QWORD": 4, - "U_QWORD_R": 4, - "FP32": 2, - "FP32_R": 2, -} - def validate_min_max(config): if config[CONF_MAX_VALUE] <= config[CONF_MIN_VALUE]: diff --git a/esphome/components/modbus_controller/output/__init__.py b/esphome/components/modbus_controller/output/__init__.py index 9c41fc011c..4aca4db64f 100644 --- a/esphome/components/modbus_controller/output/__init__.py +++ b/esphome/components/modbus_controller/output/__init__.py @@ -13,11 +13,13 @@ from .. import ( SensorItem, modbus_controller_ns, ModbusController, + TYPE_REGISTER_MAP, ) from ..const import ( CONF_BYTE_OFFSET, CONF_MODBUS_CONTROLLER_ID, + CONF_REGISTER_COUNT, CONF_VALUE_TYPE, CONF_WRITE_LAMBDA, ) @@ -40,6 +42,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_OFFSET, default=0): cv.positive_int, cv.Optional(CONF_BYTE_OFFSET): cv.positive_int, cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(SENSOR_VALUE_TYPE), + cv.Optional(CONF_REGISTER_COUNT, default=0): cv.positive_int, cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda, cv.Optional(CONF_MULTIPLY, default=1.0): cv.float_, } @@ -54,8 +57,16 @@ async def to_code(config): # A CONF_BYTE_OFFSET setting overrides CONF_OFFSET if CONF_BYTE_OFFSET in config: byte_offset = config[CONF_BYTE_OFFSET] + value_type = config[CONF_VALUE_TYPE] + reg_count = config[CONF_REGISTER_COUNT] + if reg_count == 0: + reg_count = TYPE_REGISTER_MAP[value_type] var = cg.new_Pvariable( - config[CONF_ID], config[CONF_ADDRESS], byte_offset, config[CONF_VALUE_TYPE] + config[CONF_ID], + config[CONF_ADDRESS], + byte_offset, + value_type, + reg_count, ) await output.register_output(var, config) cg.add(var.set_write_multiply(config[CONF_MULTIPLY])) diff --git a/esphome/components/modbus_controller/output/modbus_output.h b/esphome/components/modbus_controller/output/modbus_output.h index 053186a321..6e8521854b 100644 --- a/esphome/components/modbus_controller/output/modbus_output.h +++ b/esphome/components/modbus_controller/output/modbus_output.h @@ -11,12 +11,13 @@ using value_to_data_t = std::function(float); class ModbusOutput : public output::FloatOutput, public Component, public SensorItem { public: - ModbusOutput(uint16_t start_address, uint8_t offset, SensorValueType value_type) + ModbusOutput(uint16_t start_address, uint8_t offset, SensorValueType value_type, int register_count) : output::FloatOutput(), Component() { this->register_type = ModbusRegisterType::HOLDING; this->start_address = start_address; this->offset = offset; this->bitmask = bitmask; + this->register_count = register_count; this->sensor_value_type = value_type; this->skip_updates = 0; this->start_address += offset; diff --git a/esphome/components/modbus_controller/sensor/__init__.py b/esphome/components/modbus_controller/sensor/__init__.py index 687f3d82fb..82acfe120b 100644 --- a/esphome/components/modbus_controller/sensor/__init__.py +++ b/esphome/components/modbus_controller/sensor/__init__.py @@ -9,6 +9,7 @@ from .. import ( ModbusController, MODBUS_REGISTER_TYPE, SENSOR_VALUE_TYPE, + TYPE_REGISTER_MAP, ) from ..const import ( CONF_BITMASK, @@ -29,23 +30,6 @@ ModbusSensor = modbus_controller_ns.class_( "ModbusSensor", cg.Component, sensor.Sensor, SensorItem ) -TYPE_REGISTER_MAP = { - "RAW": 1, - "U_WORD": 1, - "S_WORD": 1, - "U_DWORD": 2, - "U_DWORD_R": 2, - "S_DWORD": 2, - "S_DWORD_R": 2, - "U_QWORD": 4, - "U_QWORDU_R": 4, - "S_QWORD": 4, - "U_QWORD_R": 4, - "FP32": 2, - "FP32_R": 2, -} - - CONFIG_SCHEMA = cv.All( sensor.SENSOR_SCHEMA.extend( { From f1377b560e696330e1a6faa902c38ddf81032be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerm=C3=A1k?= Date: Tue, 26 Oct 2021 18:10:45 +0200 Subject: [PATCH 074/273] Fix pin number validation for sn74hc595 (#2621) --- esphome/components/sn74hc595/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sn74hc595/__init__.py b/esphome/components/sn74hc595/__init__.py index 0d1ff6ecba..630abc8bca 100644 --- a/esphome/components/sn74hc595/__init__.py +++ b/esphome/components/sn74hc595/__init__.py @@ -60,7 +60,7 @@ SN74HC595_PIN_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(SN74HC595GPIOPin), cv.Required(CONF_SN74HC595): cv.use_id(SN74HC595Component), - cv.Required(CONF_NUMBER): cv.int_range(min=0, max=7), + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=31), cv.Optional(CONF_MODE, default={}): cv.All( { cv.Optional(CONF_OUTPUT, default=True): cv.All( From 23560e608c5848b6029c76847f94144cb8fc9b04 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 27 Oct 2021 08:27:51 +1300 Subject: [PATCH 075/273] Fix select.set using lambda (#2633) --- esphome/components/select/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index c156a63a86..7e4047d3c8 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -90,6 +90,6 @@ async def to_code(config): async def select_set_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) - template_ = await cg.templatable(config[CONF_OPTION], args, str) + template_ = await cg.templatable(config[CONF_OPTION], args, cg.std_string) cg.add(var.set_option(template_)) return var From bd782fc82809a7f77734db05b146caab3d568d93 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 27 Oct 2021 10:49:11 +1300 Subject: [PATCH 076/273] Bump version to 2021.10.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 37c325465b..d90ff04e7f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.2" +__version__ = "2021.10.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From b226215593449487dba12f2d27b0f02efa650c36 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Nov 2021 10:10:05 +1300 Subject: [PATCH 077/273] Bump version to 2021.11.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index f0df8c562d..26fd514173 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0b11" +__version__ = "2021.11.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 8cbb3798986013d4af3b1fcf1cf499243444a50f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Nov 2021 10:35:18 +1300 Subject: [PATCH 078/273] Remove import (not sure how it got there) --- esphome/components/bme680_bsec/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/components/bme680_bsec/__init__.py b/esphome/components/bme680_bsec/__init__.py index b27d477ea0..83e519f8aa 100644 --- a/esphome/components/bme680_bsec/__init__.py +++ b/esphome/components/bme680_bsec/__init__.py @@ -2,7 +2,6 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c from esphome.const import CONF_ID -from esphome.core import CORE CODEOWNERS = ["@trvrnrth"] DEPENDENCIES = ["i2c"] From 4b7fe202ec2fc7501268da01c65e75544a9fb285 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Nov 2021 11:24:48 +1300 Subject: [PATCH 079/273] Fix template number initial value being NaN (#2692) --- esphome/components/template/number/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/template/number/__init__.py b/esphome/components/template/number/__init__.py index 887f6b15ad..3dec7066d3 100644 --- a/esphome/components/template/number/__init__.py +++ b/esphome/components/template/number/__init__.py @@ -35,6 +35,9 @@ def validate(config): raise cv.Invalid("initial_value cannot be used with lambda") if CONF_RESTORE_VALUE in config: raise cv.Invalid("restore_value cannot be used with lambda") + elif CONF_INITIAL_VALUE not in config: + config[CONF_INITIAL_VALUE] = config[CONF_MIN_VALUE] + if not config[CONF_OPTIMISTIC] and CONF_SET_ACTION not in config: raise cv.Invalid( "Either optimistic mode must be enabled, or set_action must be set, to handle the number being set." @@ -80,8 +83,7 @@ async def to_code(config): else: cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) - if CONF_INITIAL_VALUE in config: - cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE])) + cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE])) if CONF_RESTORE_VALUE in config: cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE])) From 21c896d8f83be31dc1a6ff8067f59a04d0326a54 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Saura Date: Wed, 10 Nov 2021 23:28:45 +0100 Subject: [PATCH 080/273] [remote_transmitter] accurate pulse timing for ESP8266 (#2476) --- .../remote_transmitter/remote_transmitter.h | 3 + .../remote_transmitter_esp8266.cpp | 78 ++++++++++--------- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index 733ac5e50d..a4235e875f 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -32,6 +32,9 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec); void space_(uint32_t usec); + + void await_target_time_(); + uint32_t target_time_; #endif #ifdef USE_ESP32 diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp index 74e62d4e3b..39752cac5b 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp @@ -33,56 +33,64 @@ void RemoteTransmitterComponent::calculate_on_off_time_(uint32_t carrier_frequen *off_time_period = period - *on_time_period; } +void RemoteTransmitterComponent::await_target_time_() { + const uint32_t current_time = micros(); + if (this->target_time_ == 0) + this->target_time_ = current_time; + else if (this->target_time_ > current_time) + delayMicroseconds(this->target_time_ - current_time); +} + void RemoteTransmitterComponent::mark_(uint32_t on_time, uint32_t off_time, uint32_t usec) { - if (this->carrier_duty_percent_ == 100 || (on_time == 0 && off_time == 0)) { - this->pin_->digital_write(true); - delayMicroseconds(usec); - this->pin_->digital_write(false); - return; - } - - const uint32_t start_time = micros(); - uint32_t current_time = start_time; - - while (current_time - start_time < usec) { - const uint32_t elapsed = current_time - start_time; - this->pin_->digital_write(true); - - delayMicroseconds(std::min(on_time, usec - elapsed)); - this->pin_->digital_write(false); - if (elapsed + on_time >= usec) - return; - - delayMicroseconds(std::min(usec - elapsed - on_time, off_time)); - - current_time = micros(); + this->await_target_time_(); + this->pin_->digital_write(true); + + const uint32_t target = this->target_time_ + usec; + if (this->carrier_duty_percent_ < 100 && (on_time > 0 || off_time > 0)) { + while (true) { // Modulate with carrier frequency + this->target_time_ += on_time; + if (this->target_time_ >= target) + break; + this->await_target_time_(); + this->pin_->digital_write(false); + + this->target_time_ += off_time; + if (this->target_time_ >= target) + break; + this->await_target_time_(); + this->pin_->digital_write(true); + } } + this->target_time_ = target; } + void RemoteTransmitterComponent::space_(uint32_t usec) { + this->await_target_time_(); this->pin_->digital_write(false); - delayMicroseconds(usec); + this->target_time_ += usec; } + void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { ESP_LOGD(TAG, "Sending remote code..."); uint32_t on_time, off_time; this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); + this->target_time_ = 0; for (uint32_t i = 0; i < send_times; i++) { - { - InterruptLock lock; - for (int32_t item : this->temp_.get_data()) { - if (item > 0) { - const auto length = uint32_t(item); - this->mark_(on_time, off_time, length); - } else { - const auto length = uint32_t(-item); - this->space_(length); - } - App.feed_wdt(); + for (int32_t item : this->temp_.get_data()) { + if (item > 0) { + const auto length = uint32_t(item); + this->mark_(on_time, off_time, length); + } else { + const auto length = uint32_t(-item); + this->space_(length); } + App.feed_wdt(); } + this->await_target_time_(); // wait for duration of last pulse + this->pin_->digital_write(false); if (i + 1 < send_times) - delayMicroseconds(send_wait); + this->target_time_ += send_wait; } } From 755289331146f9cd53dc002a69e693513cd4c323 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Wed, 10 Nov 2021 23:34:17 +0100 Subject: [PATCH 081/273] Uart debugging support (#2478) Co-authored-by: Maurice Makaay Co-authored-by: Maurice Makaay Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/uart/__init__.py | 69 +++++++ esphome/components/uart/uart.h | 6 +- esphome/components/uart/uart_component.h | 21 ++ .../uart/uart_component_esp32_arduino.cpp | 12 +- .../uart/uart_component_esp8266.cpp | 9 +- .../uart/uart_component_esp_idf.cpp | 12 +- esphome/components/uart/uart_debugger.cpp | 193 ++++++++++++++++++ esphome/components/uart/uart_debugger.h | 101 +++++++++ esphome/const.py | 7 + esphome/core/defines.h | 1 + tests/test1.yaml | 12 ++ 11 files changed, 430 insertions(+), 13 deletions(-) create mode 100644 esphome/components/uart/uart_debugger.cpp create mode 100644 esphome/components/uart/uart_debugger.h diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index 35af3eedf7..53209dfc7b 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -14,6 +14,16 @@ from esphome.const import ( CONF_DATA, CONF_RX_BUFFER_SIZE, CONF_INVERT, + CONF_TRIGGER_ID, + CONF_SEQUENCE, + CONF_TIMEOUT, + CONF_DEBUG, + CONF_DIRECTION, + CONF_AFTER, + CONF_BYTES, + CONF_DELIMITER, + CONF_DUMMY_RECEIVER, + CONF_DUMMY_RECEIVER_ID, ) from esphome.core import CORE @@ -31,6 +41,8 @@ ESP8266UartComponent = uart_ns.class_( UARTDevice = uart_ns.class_("UARTDevice") UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action) +UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action) +UARTDummyReceiver = uart_ns.class_("UARTDummyReceiver", cg.Component) MULTI_CONF = True @@ -75,6 +87,34 @@ CONF_STOP_BITS = "stop_bits" CONF_DATA_BITS = "data_bits" CONF_PARITY = "parity" +UARTDirection = uart_ns.enum("UARTDirection") +UART_DIRECTIONS = { + "RX": UARTDirection.UART_DIRECTION_RX, + "TX": UARTDirection.UART_DIRECTION_TX, + "BOTH": UARTDirection.UART_DIRECTION_BOTH, +} + +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_BYTES, default=256): cv.validate_bytes, + cv.Optional( + CONF_TIMEOUT, default="100ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_DELIMITER): cv.templatable(validate_raw_data), + } + ), + cv.Required(CONF_SEQUENCE): automation.validate_automation(), + cv.Optional(CONF_DUMMY_RECEIVER, default=False): cv.boolean, + cv.GenerateID(CONF_DUMMY_RECEIVER_ID): cv.declare_id(UARTDummyReceiver), + } +) + CONFIG_SCHEMA = cv.All( cv.Schema( { @@ -91,12 +131,38 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_INVERT): cv.invalid( "This option has been removed. Please instead use invert in the tx/rx pin schemas." ), + cv.Optional(CONF_DEBUG): DEBUG_SCHEMA, } ).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN), ) +async def debug_to_code(config, parent): + trigger = cg.new_Pvariable(config[CONF_TRIGGER_ID], parent) + await cg.register_component(trigger, config) + for action in config[CONF_SEQUENCE]: + await automation.build_automation( + trigger, + [(UARTDirection, "direction"), (cg.std_vector.template(cg.uint8), "bytes")], + action, + ) + cg.add(trigger.set_direction(config[CONF_DIRECTION])) + after = config[CONF_AFTER] + cg.add(trigger.set_after_bytes(after[CONF_BYTES])) + cg.add(trigger.set_after_timeout(after[CONF_TIMEOUT])) + if CONF_DELIMITER in after: + data = after[CONF_DELIMITER] + if isinstance(data, bytes): + data = list(data) + for byte in after[CONF_DELIMITER]: + cg.add(trigger.add_delimiter_byte(byte)) + if config[CONF_DUMMY_RECEIVER]: + dummy = cg.new_Pvariable(config[CONF_DUMMY_RECEIVER_ID], parent) + await cg.register_component(dummy, {}) + cg.add_define("USE_UART_DEBUGGER") + + async def to_code(config): cg.add_global(uart_ns.using) var = cg.new_Pvariable(config[CONF_ID]) @@ -115,6 +181,9 @@ async def to_code(config): cg.add(var.set_data_bits(config[CONF_DATA_BITS])) cg.add(var.set_parity(config[CONF_PARITY])) + if CONF_DEBUG in config: + await debug_to_code(config[CONF_DEBUG], var) + # A schema to use for all UART devices, all UART integrations must extend this! UART_DEVICE_SCHEMA = cv.Schema( diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index c368f9ed6b..d41dbe26e6 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -45,17 +45,17 @@ class UARTDevice { // Compat APIs int read() { uint8_t data; - if (!read_byte(&data)) + if (!this->read_byte(&data)) return -1; return data; } size_t write(uint8_t data) { - write_byte(data); + this->write_byte(data); return 1; } int peek() { uint8_t data; - if (!peek_byte(&data)) + if (!this->peek_byte(&data)) return -1; return data; } diff --git a/esphome/components/uart/uart_component.h b/esphome/components/uart/uart_component.h index de85cd2ca3..73694d3db7 100644 --- a/esphome/components/uart/uart_component.h +++ b/esphome/components/uart/uart_component.h @@ -2,9 +2,13 @@ #include #include +#include "esphome/core/defines.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" +#ifdef USE_UART_DEBUGGER +#include "esphome/core/automation.h" +#endif namespace esphome { namespace uart { @@ -15,6 +19,14 @@ enum UARTParityOptions { UART_CONFIG_PARITY_ODD, }; +#ifdef USE_UART_DEBUGGER +enum UARTDirection { + UART_DIRECTION_RX, + UART_DIRECTION_TX, + UART_DIRECTION_BOTH, +}; +#endif + const LogString *parity_to_str(UARTParityOptions parity); class UARTComponent { @@ -50,6 +62,12 @@ class UARTComponent { void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } uint32_t get_baud_rate() const { return baud_rate_; } +#ifdef USE_UART_DEBUGGER + void add_debug_callback(std::function &&callback) { + this->debug_callback_.add(std::move(callback)); + } +#endif + protected: virtual void check_logger_conflict() = 0; bool check_read_timeout_(size_t len = 1); @@ -61,6 +79,9 @@ class UARTComponent { uint8_t stop_bits_; uint8_t data_bits_; UARTParityOptions parity_; +#ifdef USE_UART_DEBUGGER + CallbackManager debug_callback_{}; +#endif }; } // namespace uart diff --git a/esphome/components/uart/uart_component_esp32_arduino.cpp b/esphome/components/uart/uart_component_esp32_arduino.cpp index 1b1ce382f2..95cdde4a43 100644 --- a/esphome/components/uart/uart_component_esp32_arduino.cpp +++ b/esphome/components/uart/uart_component_esp32_arduino.cpp @@ -117,26 +117,32 @@ void ESP32ArduinoUARTComponent::dump_config() { void ESP32ArduinoUARTComponent::write_array(const uint8_t *data, size_t len) { this->hw_serial_->write(data, len); +#ifdef USE_UART_DEBUGGER for (size_t i = 0; i < len; i++) { - ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); + this->debug_callback_.call(UART_DIRECTION_TX, data[i]); } +#endif } + bool ESP32ArduinoUARTComponent::peek_byte(uint8_t *data) { if (!this->check_read_timeout_()) return false; *data = this->hw_serial_->peek(); return true; } + bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) { if (!this->check_read_timeout_(len)) return false; this->hw_serial_->readBytes(data, len); +#ifdef USE_UART_DEBUGGER for (size_t i = 0; i < len; i++) { - ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); + this->debug_callback_.call(UART_DIRECTION_RX, data[i]); } - +#endif return true; } + int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); } void ESP32ArduinoUARTComponent::flush() { ESP_LOGVV(TAG, " Flushing..."); diff --git a/esphome/components/uart/uart_component_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp index 973306cde2..c367de05bb 100644 --- a/esphome/components/uart/uart_component_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -130,9 +130,11 @@ void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) { for (size_t i = 0; i < len; i++) this->sw_serial_->write_byte(data[i]); } +#ifdef USE_UART_DEBUGGER for (size_t i = 0; i < len; i++) { - ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); + this->debug_callback_.call(UART_DIRECTION_TX, data[i]); } +#endif } bool ESP8266UartComponent::peek_byte(uint8_t *data) { if (!this->check_read_timeout_()) @@ -153,10 +155,11 @@ bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) { for (size_t i = 0; i < len; i++) data[i] = this->sw_serial_->read_byte(); } +#ifdef USE_UART_DEBUGGER for (size_t i = 0; i < len; i++) { - ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); + this->debug_callback_.call(UART_DIRECTION_RX, data[i]); } - +#endif return true; } int ESP8266UartComponent::available() { diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 1cccd5821e..4d6a6af0fc 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -130,10 +130,13 @@ void IDFUARTComponent::write_array(const uint8_t *data, size_t len) { xSemaphoreTake(this->lock_, portMAX_DELAY); uart_write_bytes(this->uart_num_, data, len); xSemaphoreGive(this->lock_); +#ifdef USE_UART_DEBUGGER for (size_t i = 0; i < len; i++) { - ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); + this->debug_callback_.call(UART_DIRECTION_TX, data[i]); } +#endif } + bool IDFUARTComponent::peek_byte(uint8_t *data) { if (!this->check_read_timeout_()) return false; @@ -152,6 +155,7 @@ bool IDFUARTComponent::peek_byte(uint8_t *data) { xSemaphoreGive(this->lock_); return true; } + bool IDFUARTComponent::read_array(uint8_t *data, size_t len) { size_t length_to_read = len; if (!this->check_read_timeout_(len)) @@ -165,12 +169,12 @@ bool IDFUARTComponent::read_array(uint8_t *data, size_t len) { } if (length_to_read > 0) uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_RATE_MS); - xSemaphoreGive(this->lock_); +#ifdef USE_UART_DEBUGGER for (size_t i = 0; i < len; i++) { - ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); + this->debug_callback_.call(UART_DIRECTION_RX, data[i]); } - +#endif return true; } diff --git a/esphome/components/uart/uart_debugger.cpp b/esphome/components/uart/uart_debugger.cpp new file mode 100644 index 0000000000..9a535656a2 --- /dev/null +++ b/esphome/components/uart/uart_debugger.cpp @@ -0,0 +1,193 @@ +#include "esphome/core/defines.h" +#ifdef USE_UART_DEBUGGER + +#include +#include "uart_debugger.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace uart { + +static const char *const TAG = "uart_debug"; + +UARTDebugger::UARTDebugger(UARTComponent *parent) { + parent->add_debug_callback([this](UARTDirection direction, uint8_t byte) { + if (!this->is_my_direction_(direction) || this->is_recursive_()) { + return; + } + this->trigger_after_direction_change_(direction); + this->store_byte_(direction, byte); + this->trigger_after_delimiter_(byte); + this->trigger_after_bytes_(); + }); +} + +void UARTDebugger::loop() { this->trigger_after_timeout_(); } + +bool UARTDebugger::is_my_direction_(UARTDirection direction) { + return this->for_direction_ == UART_DIRECTION_BOTH || this->for_direction_ == direction; +} + +bool UARTDebugger::is_recursive_() { return this->is_triggering_; } + +void UARTDebugger::trigger_after_direction_change_(UARTDirection direction) { + if (this->has_buffered_bytes_() && this->for_direction_ == UART_DIRECTION_BOTH && + this->last_direction_ != direction) { + this->fire_trigger_(); + } +} + +void UARTDebugger::store_byte_(UARTDirection direction, uint8_t byte) { + this->bytes_.push_back(byte); + this->last_direction_ = direction; + this->last_time_ = millis(); +} + +void UARTDebugger::trigger_after_delimiter_(uint8_t byte) { + if (this->after_delimiter_.empty() || !this->has_buffered_bytes_()) { + return; + } + if (this->after_delimiter_[this->after_delimiter_pos_] != byte) { + this->after_delimiter_pos_ = 0; + return; + } + this->after_delimiter_pos_++; + if (this->after_delimiter_pos_ == this->after_delimiter_.size()) { + this->fire_trigger_(); + this->after_delimiter_pos_ = 0; + } +} + +void UARTDebugger::trigger_after_bytes_() { + if (this->has_buffered_bytes_() && this->after_bytes_ > 0 && this->bytes_.size() >= this->after_bytes_) { + this->fire_trigger_(); + } +} + +void UARTDebugger::trigger_after_timeout_() { + if (this->has_buffered_bytes_() && this->after_timeout_ > 0 && millis() - this->last_time_ >= this->after_timeout_) { + this->fire_trigger_(); + } +} + +bool UARTDebugger::has_buffered_bytes_() { return !this->bytes_.empty(); } + +void UARTDebugger::fire_trigger_() { + this->is_triggering_ = true; + trigger(this->last_direction_, this->bytes_); + this->bytes_.clear(); + this->is_triggering_ = false; +} + +void UARTDummyReceiver::loop() { + // Reading up to a limited number of bytes, to make sure that this loop() + // won't lock up the system on a continuous incoming stream of bytes. + uint8_t data; + int count = 50; + while (this->available() && count--) { + this->read_byte(&data); + } +} + +void UARTDebug::log_hex(UARTDirection direction, std::vector bytes, uint8_t separator) { + std::string res; + if (direction == UART_DIRECTION_RX) { + res += "<<< "; + } else { + res += ">>> "; + } + size_t len = bytes.size(); + char buf[5]; + for (size_t i = 0; i < len; i++) { + if (i > 0) { + res += separator; + } + sprintf(buf, "%02X", bytes[i]); + res += buf; + } + ESP_LOGD(TAG, "%s", res.c_str()); +} + +void UARTDebug::log_string(UARTDirection direction, std::vector bytes) { + std::string res; + if (direction == UART_DIRECTION_RX) { + res += "<<< \""; + } else { + res += ">>> \""; + } + size_t len = bytes.size(); + char buf[5]; + for (size_t i = 0; i < len; i++) { + if (bytes[i] == 7) { + res += "\\a"; + } else if (bytes[i] == 8) { + res += "\\b"; + } else if (bytes[i] == 9) { + res += "\\t"; + } else if (bytes[i] == 10) { + res += "\\n"; + } else if (bytes[i] == 11) { + res += "\\v"; + } else if (bytes[i] == 12) { + res += "\\f"; + } else if (bytes[i] == 13) { + res += "\\r"; + } else if (bytes[i] == 27) { + res += "\\e"; + } else if (bytes[i] == 34) { + res += "\\\""; + } else if (bytes[i] == 39) { + res += "\\'"; + } else if (bytes[i] == 92) { + res += "\\\\"; + } else if (bytes[i] < 32 || bytes[i] > 127) { + sprintf(buf, "\\x%02X", bytes[i]); + res += buf; + } else { + res += bytes[i]; + } + } + res += '"'; + ESP_LOGD(TAG, "%s", res.c_str()); +} + +void UARTDebug::log_int(UARTDirection direction, std::vector bytes, uint8_t separator) { + std::string res; + size_t len = bytes.size(); + if (direction == UART_DIRECTION_RX) { + res += "<<< "; + } else { + res += ">>> "; + } + for (size_t i = 0; i < len; i++) { + if (i > 0) { + res += separator; + } + res += to_string(bytes[i]); + } + ESP_LOGD(TAG, "%s", res.c_str()); +} + +void UARTDebug::log_binary(UARTDirection direction, std::vector bytes, uint8_t separator) { + std::string res; + size_t len = bytes.size(); + if (direction == UART_DIRECTION_RX) { + res += "<<< "; + } else { + res += ">>> "; + } + char buf[20]; + for (size_t i = 0; i < len; i++) { + if (i > 0) { + res += separator; + } + sprintf(buf, "0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(bytes[i]), bytes[i]); + res += buf; + } + ESP_LOGD(TAG, "%s", res.c_str()); +} + +} // namespace uart +} // namespace esphome +#endif diff --git a/esphome/components/uart/uart_debugger.h b/esphome/components/uart/uart_debugger.h new file mode 100644 index 0000000000..6e84bbe450 --- /dev/null +++ b/esphome/components/uart/uart_debugger.h @@ -0,0 +1,101 @@ +#pragma once +#include "esphome/core/defines.h" +#ifdef USE_UART_DEBUGGER + +#include +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "uart.h" +#include "uart_component.h" + +namespace esphome { +namespace uart { + +/// The UARTDebugger class adds debugging support to a UART bus. +/// +/// It accumulates bytes that travel over the UART bus and triggers one or +/// more actions that can log the data at an appropriate time. What +/// 'appropriate time' means exactly, is determined by a number of +/// configurable constraints. E.g. when a given number of bytes is gathered +/// and/or when no more data has been seen for a given time interval. +class UARTDebugger : public Component, public Trigger> { + public: + explicit UARTDebugger(UARTComponent *parent); + void loop() override; + + /// Set the direction in which to inspect the bytes: incoming, outgoing + /// or both. When debugging in both directions, logging will be triggered + /// when the direction of the data stream changes. + void set_direction(UARTDirection direction) { this->for_direction_ = direction; } + + /// Set the maximum number of bytes to accumulate. When the number of bytes + /// is reached, logging will be triggered. + void set_after_bytes(size_t size) { this->after_bytes_ = size; } + + /// Set a timeout for the data stream. When no new bytes are seen during + /// this timeout, logging will be triggered. + void set_after_timeout(uint32_t timeout) { this->after_timeout_ = timeout; } + + /// Add a delimiter byte. This can be called multiple times to setup a + /// multi-byte delimiter (a typical example would be '\r\n'). + /// When the constructued byte sequence is found in the data stream, + /// logging will be triggered. + void add_delimiter_byte(uint8_t byte) { this->after_delimiter_.push_back(byte); } + + protected: + UARTDirection for_direction_; + UARTDirection last_direction_{}; + std::vector bytes_{}; + size_t after_bytes_; + uint32_t after_timeout_; + uint32_t last_time_{}; + std::vector after_delimiter_{}; + size_t after_delimiter_pos_{}; + bool is_triggering_{false}; + + bool is_my_direction_(UARTDirection direction); + bool is_recursive_(); + void store_byte_(UARTDirection direction, uint8_t byte); + void trigger_after_direction_change_(UARTDirection direction); + void trigger_after_delimiter_(uint8_t byte); + void trigger_after_bytes_(); + void trigger_after_timeout_(); + bool has_buffered_bytes_(); + void fire_trigger_(); +}; + +/// This UARTDevice is used by the serial debugger to read data from a +/// serial interface when the 'dummy_receiver' option is enabled. +/// The data are not stored, nor processed. This is most useful when the +/// debugger is used to reverse engineer a serial protocol, for which no +/// specific UARTDevice implementation exists (yet), but for which the +/// incoming bytes must be read to drive the debugger. +class UARTDummyReceiver : public Component, public UARTDevice { + public: + UARTDummyReceiver(UARTComponent *parent) : UARTDevice(parent) {} + void loop() override; +}; + +/// This class contains some static methods, that can be used to easily +/// create a logging action for the debugger. +class UARTDebug { + public: + /// Log the bytes as hex values, separated by the provided separator + /// character. + static void log_hex(UARTDirection direction, std::vector bytes, uint8_t separator); + + /// Log the bytes as string values, escaping unprintable characters. + static void log_string(UARTDirection direction, std::vector bytes); + + /// Log the bytes as integer values, separated by the provided separator + /// character. + static void log_int(UARTDirection direction, std::vector bytes, uint8_t separator); + + /// Log the bytes as ' ()' values, separated by the provided + /// separator. + static void log_binary(UARTDirection direction, std::vector bytes, uint8_t separator); +}; + +} // namespace uart +} // namespace esphome +#endif diff --git a/esphome/const.py b/esphome/const.py index 26fd514173..af079ac69d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -34,6 +34,7 @@ ARDUINO_VERSION_ESP8266 = { SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"} HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"} + CONF_ABOVE = "above" CONF_ACCELERATION = "acceleration" CONF_ACCELERATION_X = "acceleration_x" @@ -47,6 +48,7 @@ CONF_ACTIVE_POWER = "active_power" CONF_ADDRESS = "address" CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id" CONF_ADVANCED = "advanced" +CONF_AFTER = "after" CONF_ALPHA = "alpha" CONF_ALTITUDE = "altitude" CONF_AND = "and" @@ -93,6 +95,7 @@ CONF_BUFFER_SIZE = "buffer_size" CONF_BUILD_PATH = "build_path" CONF_BUS_VOLTAGE = "bus_voltage" CONF_BUSY_PIN = "busy_pin" +CONF_BYTES = "bytes" CONF_CALCULATED_LUX = "calculated_lux" CONF_CALIBRATE_LINEAR = "calibrate_linear" CONF_CALIBRATION = "calibration" @@ -164,6 +167,7 @@ CONF_DAYS_OF_WEEK = "days_of_week" CONF_DC_PIN = "dc_pin" CONF_DEASSERT_RTS_DTR = "deassert_rts_dtr" CONF_DEBOUNCE = "debounce" +CONF_DEBUG = "debug" CONF_DECAY_MODE = "decay_mode" CONF_DECELERATION = "deceleration" CONF_DEFAULT_MODE = "default_mode" @@ -171,6 +175,7 @@ CONF_DEFAULT_TARGET_TEMPERATURE_HIGH = "default_target_temperature_high" CONF_DEFAULT_TARGET_TEMPERATURE_LOW = "default_target_temperature_low" CONF_DEFAULT_TRANSITION_LENGTH = "default_transition_length" CONF_DELAY = "delay" +CONF_DELIMITER = "delimiter" CONF_DELTA = "delta" CONF_DEVICE = "device" CONF_DEVICE_CLASS = "device_class" @@ -192,6 +197,8 @@ CONF_DNS2 = "dns2" CONF_DOMAIN = "domain" CONF_DRY_ACTION = "dry_action" CONF_DRY_MODE = "dry_mode" +CONF_DUMMY_RECEIVER = "dummy_receiver" +CONF_DUMMY_RECEIVER_ID = "dummy_receiver_id" CONF_DUMP = "dump" CONF_DURATION = "duration" CONF_EAP = "eap" diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 94fac73906..e679fe1cef 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -37,6 +37,7 @@ #define USE_SWITCH #define USE_TEXT_SENSOR #define USE_TIME +#define USE_UART_DEBUGGER #define USE_WEBSERVER #define USE_WIFI diff --git a/tests/test1.yaml b/tests/test1.yaml index dee7493bf2..04d928e1b8 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -193,6 +193,18 @@ uart: data_bits: 8 stop_bits: 1 rx_buffer_size: 512 + debug: + dummy_receiver: true + direction: both + after: + bytes: 50 + timeout: 500ms + delimiter: "\r\n" + sequence: + - lambda: UARTDebug::log_hex(direction, bytes, ':'); + - lambda: UARTDebug::log_string(direction, bytes); + - lambda: UARTDebug::log_int(direction, bytes, ','); + - lambda: UARTDebug::log_binary(direction, bytes, ';'); - id: adalight_uart tx_pin: GPIO25 From b4cd8d21a517c19dfdb7e432899db4fcaec10428 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Wed, 10 Nov 2021 23:53:25 +0100 Subject: [PATCH 082/273] Enable addressable light power supply based on raw values (#2690) --- esphome/components/light/addressable_light.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index 2b2c0ca7e4..8302239d6a 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -87,7 +87,7 @@ class AddressableLight : public LightOutput, public Component { void mark_shown_() { #ifdef USE_POWER_SUPPLY for (const auto &c : *this) { - if (c.get().is_on()) { + if (c.get_red_raw() > 0 || c.get_green_raw() > 0 || c.get_blue_raw() > 0 || c.get_white_raw() > 0) { this->power_.request(); return; } From 78026e766f9bb1b8a567ec7fdfdb3f44393fb515 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Nov 2021 12:25:41 +1300 Subject: [PATCH 083/273] Bump version to 2021.11.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index af079ac69d..065145761b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.0b1" +__version__ = "2021.11.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 38cb9888094ce71ccba8c29b97483146fcd432dd Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Nov 2021 15:15:37 +1300 Subject: [PATCH 084/273] Remove my.ha links from improv (#2695) --- .../components/esp32_improv/esp32_improv_component.cpp | 10 ++++++++-- .../improv_serial/improv_serial_component.cpp | 3 +-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index faa9ab7df6..22bebdfe98 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -11,6 +11,7 @@ namespace esphome { namespace esp32_improv { static const char *const TAG = "esp32_improv.component"; +static const char *const ESPHOME_MY_LINK = "https://my.home-assistant.io/redirect/config_flow_start?domain=esphome"; ESP32ImprovComponent::ESP32ImprovComponent() { global_improv_component = this; } @@ -124,8 +125,13 @@ void ESP32ImprovComponent::loop() { this->cancel_timeout("wifi-connect-timeout"); this->set_state_(improv::STATE_PROVISIONED); - std::string url = "https://my.home-assistant.io/redirect/config_flow_start?domain=esphome"; - std::vector data = improv::build_rpc_response(improv::WIFI_SETTINGS, {url}); + std::vector urls = {ESPHOME_MY_LINK}; +#ifdef USE_WEBSERVER + auto ip = wifi::global_wifi_component->wifi_sta_ip(); + std::string webserver_url = "http://" + ip.str() + ":" + to_string(WEBSERVER_PORT); + urls.push_back(webserver_url); +#endif + std::vector data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls); this->send_response_(data); this->set_timeout("end-service", 1000, [this] { this->service_->stop(); diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index a12f1bd83b..abbb76ab11 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -92,8 +92,7 @@ void ImprovSerialComponent::loop() { } std::vector ImprovSerialComponent::build_rpc_settings_response_(improv::Command command) { - std::string url = "https://my.home-assistant.io/redirect/config_flow_start?domain=esphome"; - std::vector urls = {url}; + std::vector urls; #ifdef USE_WEBSERVER auto ip = wifi::global_wifi_component->wifi_sta_ip(); std::string webserver_url = "http://" + ip.str() + ":" + to_string(WEBSERVER_PORT); From 62c3f301e7459c57db7195416cab93ba9f0a93a6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Nov 2021 22:56:35 +1300 Subject: [PATCH 085/273] Only allow prometheus when using arduino (#2697) --- esphome/components/prometheus/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/prometheus/__init__.py b/esphome/components/prometheus/__init__.py index f5f166d085..45345f06e8 100644 --- a/esphome/components/prometheus/__init__.py +++ b/esphome/components/prometheus/__init__.py @@ -15,7 +15,8 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id( web_server_base.WebServerBase ), - } + }, + cv.only_with_arduino, ).extend(cv.COMPONENT_SCHEMA) From b526155cce6611c908e1da8ff562c92c42458313 Mon Sep 17 00:00:00 2001 From: lcavalli Date: Fri, 12 Nov 2021 01:17:10 +0100 Subject: [PATCH 086/273] Update device classes for binary sensors (#2703) --- esphome/components/binary_sensor/__init__.py | 6 ++++-- esphome/const.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index faafcddd06..3f11e18e45 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -44,10 +44,11 @@ from esphome.const import ( DEVICE_CLASS_POWER, DEVICE_CLASS_PRESENCE, DEVICE_CLASS_PROBLEM, + DEVICE_CLASS_RUNNING, DEVICE_CLASS_SAFETY, DEVICE_CLASS_SMOKE, DEVICE_CLASS_SOUND, - DEVICE_CLASS_UPDATE, + DEVICE_CLASS_TAMPER, DEVICE_CLASS_VIBRATION, DEVICE_CLASS_WINDOW, ) @@ -76,10 +77,11 @@ DEVICE_CLASSES = [ DEVICE_CLASS_POWER, DEVICE_CLASS_PRESENCE, DEVICE_CLASS_PROBLEM, + DEVICE_CLASS_RUNNING, DEVICE_CLASS_SAFETY, DEVICE_CLASS_SMOKE, DEVICE_CLASS_SOUND, - DEVICE_CLASS_UPDATE, + DEVICE_CLASS_TAMPER, DEVICE_CLASS_VIBRATION, DEVICE_CLASS_WINDOW, ] diff --git a/esphome/const.py b/esphome/const.py index 065145761b..32773f06bd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -878,10 +878,11 @@ DEVICE_CLASS_OPENING = "opening" DEVICE_CLASS_PLUG = "plug" DEVICE_CLASS_PRESENCE = "presence" DEVICE_CLASS_PROBLEM = "problem" +DEVICE_CLASS_RUNNING = "running" DEVICE_CLASS_SAFETY = "safety" DEVICE_CLASS_SMOKE = "smoke" DEVICE_CLASS_SOUND = "sound" -DEVICE_CLASS_UPDATE = "update" +DEVICE_CLASS_TAMPER = "tamper" DEVICE_CLASS_VIBRATION = "vibration" DEVICE_CLASS_WINDOW = "window" # device classes of both binary_sensor and sensor component From f1c5e2ef8130fb2b737724bc0033247f3e7fac4f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 12 Nov 2021 16:12:31 +1300 Subject: [PATCH 087/273] Bump version to 2021.11.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 32773f06bd..f45c6022dd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.0b2" +__version__ = "2021.11.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 81a36146efb4b686d2eb49d2ae73d16be7e5b6a6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 13 Nov 2021 21:22:32 +1300 Subject: [PATCH 088/273] Bump ESPAsyncWebServer to 2.1.0 (#2686) --- esphome/components/web_server_base/__init__.py | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/web_server_base/__init__.py b/esphome/components/web_server_base/__init__.py index dc1a2bc2f0..14fb033a56 100644 --- a/esphome/components/web_server_base/__init__.py +++ b/esphome/components/web_server_base/__init__.py @@ -28,4 +28,4 @@ async def to_code(config): cg.add_library("FS", None) cg.add_library("Update", None) # https://github.com/esphome/ESPAsyncWebServer/blob/master/library.json - cg.add_library("esphome/ESPAsyncWebServer-esphome", "2.0.1") + cg.add_library("esphome/ESPAsyncWebServer-esphome", "2.1.0") diff --git a/platformio.ini b/platformio.ini index 0dd32268e0..5e89afe8e6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -41,7 +41,7 @@ lib_deps = ${common.lib_deps} ottowinter/AsyncMqttClient-esphome@0.8.6 ; mqtt ottowinter/ArduinoJson-esphomelib@5.13.3 ; json - esphome/ESPAsyncWebServer-esphome@2.0.1 ; web_server_base + esphome/ESPAsyncWebServer-esphome@2.1.0 ; web_server_base fastled/FastLED@3.3.2 ; fastled_base mikalhart/TinyGPSPlus@1.0.2 ; gps freekode/TM1651@1.0.1 ; tm1651 From 87e1cdeedb39a01353d683c31082fbd13e24d70a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Bia=C5=82ek?= Date: Sun, 14 Nov 2021 14:59:34 +0100 Subject: [PATCH 089/273] Allow setting custom command_topic for Select and Number components (#2714) --- esphome/components/number/__init__.py | 2 +- esphome/components/select/__init__.py | 2 +- tests/test1.yaml | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index bb3427e4bd..1da25caafe 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -41,7 +41,7 @@ NumberInRangeCondition = number_ns.class_( icon = cv.icon -NUMBER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( +NUMBER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent), cv.GenerateID(): cv.declare_id(Number), diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index 8ea159d657..c15036e9f9 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -30,7 +30,7 @@ SelectSetAction = select_ns.class_("SelectSetAction", automation.Action) icon = cv.icon -SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( +SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSelectComponent), cv.GenerateID(): cv.declare_id(Select), diff --git a/tests/test1.yaml b/tests/test1.yaml index 04d928e1b8..a7f2e24465 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2512,3 +2512,23 @@ teleinfo: uart_id: uart0 update_interval: 60s historical_mode: true + +number: + - platform: template + id: test_number + state_topic: livingroom/custom_state_topic + command_topic: livingroom/custom_command_topic + min_value: 0 + step: 1 + max_value: 10 + optimistic: true + +select: + - platform: template + id: test_select + state_topic: livingroom/custom_state_topic + command_topic: livingroom/custom_command_topic + options: + - one + - two + optimistic: true From ab506b09fe66c62123aa624e7e31791752a11b75 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 14 Nov 2021 20:05:11 +0100 Subject: [PATCH 090/273] Restore InterruptLock on wifi-less ESP8266 (#2712) --- esphome/core/helpers.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index daca3ffd32..27608a84c1 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -9,6 +9,7 @@ #ifdef USE_WIFI #include #endif +#include #include #elif defined(USE_ESP32_FRAMEWORK_ARDUINO) #include @@ -430,13 +431,8 @@ void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green } #ifdef USE_ESP8266 -#ifdef USE_WIFI IRAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); } IRAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); } -#else -IRAM_ATTR InterruptLock::InterruptLock() {} -IRAM_ATTR InterruptLock::~InterruptLock() {} -#endif #endif #ifdef USE_ESP32 IRAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); } From f4a140e126fcbd3649d5a9569131dda66687a4ce Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 14 Nov 2021 21:45:25 +0100 Subject: [PATCH 091/273] Feed WDT between doing ESP32 touchpad measurements (#2720) --- esphome/components/esp32_touch/esp32_touch.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/esp32_touch/esp32_touch.cpp b/esphome/components/esp32_touch/esp32_touch.cpp index cb72820900..85f4058eee 100644 --- a/esphome/components/esp32_touch/esp32_touch.cpp +++ b/esphome/components/esp32_touch/esp32_touch.cpp @@ -1,6 +1,7 @@ #ifdef USE_ESP32 #include "esp32_touch.h" +#include "esphome/core/application.h" #include "esphome/core/log.h" #include "esphome/core/hal.h" @@ -125,6 +126,8 @@ void ESP32TouchComponent::loop() { if (should_print) { ESP_LOGD(TAG, "Touch Pad '%s' (T%u): %u", child->get_name().c_str(), child->get_touch_pad(), value); } + + App.feed_wdt(); } if (should_print) { From c2f57baec2cef35e1fde9b4f1781e639dab37208 Mon Sep 17 00:00:00 2001 From: "Sergey V. DUDANOV" Date: Mon, 15 Nov 2021 01:40:35 +0400 Subject: [PATCH 092/273] RemoteTransmitter fix. Bug from version 2021.10. Some changes. (#2706) --- .../midea/{adapter.cpp => ac_adapter.cpp} | 4 +- .../midea/{adapter.h => ac_adapter.h} | 6 +- .../midea/{automations.h => ac_automations.h} | 2 + esphome/components/midea/air_conditioner.cpp | 13 +-- esphome/components/midea/air_conditioner.h | 21 +++- esphome/components/midea/appliance_base.h | 109 ++++++++++-------- esphome/components/midea/climate.py | 20 ++-- .../midea/{midea_ir.h => ir_transmitter.h} | 15 +++ 8 files changed, 122 insertions(+), 68 deletions(-) rename esphome/components/midea/{adapter.cpp => ac_adapter.cpp} (98%) rename esphome/components/midea/{adapter.h => ac_adapter.h} (95%) rename esphome/components/midea/{automations.h => ac_automations.h} (97%) rename esphome/components/midea/{midea_ir.h => ir_transmitter.h} (73%) diff --git a/esphome/components/midea/adapter.cpp b/esphome/components/midea/ac_adapter.cpp similarity index 98% rename from esphome/components/midea/adapter.cpp rename to esphome/components/midea/ac_adapter.cpp index a3f19dbda8..2837713c35 100644 --- a/esphome/components/midea/adapter.cpp +++ b/esphome/components/midea/ac_adapter.cpp @@ -1,10 +1,11 @@ #ifdef USE_ARDUINO #include "esphome/core/log.h" -#include "adapter.h" +#include "ac_adapter.h" namespace esphome { namespace midea { +namespace ac { const char *const Constants::TAG = "midea"; const std::string Constants::FREEZE_PROTECTION = "freeze protection"; @@ -171,6 +172,7 @@ void Converters::to_climate_traits(ClimateTraits &traits, const dudanov::midea:: traits.add_supported_custom_preset(Constants::FREEZE_PROTECTION); } +} // namespace ac } // namespace midea } // namespace esphome diff --git a/esphome/components/midea/adapter.h b/esphome/components/midea/ac_adapter.h similarity index 95% rename from esphome/components/midea/adapter.h rename to esphome/components/midea/ac_adapter.h index 2497cbbe5b..c17894ae31 100644 --- a/esphome/components/midea/adapter.h +++ b/esphome/components/midea/ac_adapter.h @@ -2,12 +2,15 @@ #ifdef USE_ARDUINO +// MideaUART #include + #include "esphome/components/climate/climate_traits.h" -#include "appliance_base.h" +#include "air_conditioner.h" namespace esphome { namespace midea { +namespace ac { using MideaMode = dudanov::midea::ac::Mode; using MideaSwingMode = dudanov::midea::ac::SwingMode; @@ -41,6 +44,7 @@ class Converters { static void to_climate_traits(ClimateTraits &traits, const dudanov::midea::ac::Capabilities &capabilities); }; +} // namespace ac } // namespace midea } // namespace esphome diff --git a/esphome/components/midea/automations.h b/esphome/components/midea/ac_automations.h similarity index 97% rename from esphome/components/midea/automations.h rename to esphome/components/midea/ac_automations.h index 5b638286ac..d4ed2e7168 100644 --- a/esphome/components/midea/automations.h +++ b/esphome/components/midea/ac_automations.h @@ -7,6 +7,7 @@ namespace esphome { namespace midea { +namespace ac { template class MideaActionBase : public Action { public: @@ -55,6 +56,7 @@ template class PowerOffAction : public MideaActionBase { void play(Ts... x) override { this->parent_->do_power_off(); } }; +} // namespace ac } // namespace midea } // namespace esphome diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index 103b852936..dd48f640a2 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -2,13 +2,11 @@ #include "esphome/core/log.h" #include "air_conditioner.h" -#include "adapter.h" -#ifdef USE_REMOTE_TRANSMITTER -#include "midea_ir.h" -#endif +#include "ac_adapter.h" namespace esphome { namespace midea { +namespace ac { static void set_sensor(Sensor *sensor, float value) { if (sensor != nullptr && (!sensor->has_state() || sensor->get_raw_state() != value)) @@ -122,7 +120,7 @@ void AirConditioner::dump_config() { void AirConditioner::do_follow_me(float temperature, bool beeper) { #ifdef USE_REMOTE_TRANSMITTER IrFollowMeData data(static_cast(lroundf(temperature)), beeper); - this->transmit_ir(data); + this->transmitter_.transmit(data); #else ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component"); #endif @@ -131,7 +129,7 @@ void AirConditioner::do_follow_me(float temperature, bool beeper) { void AirConditioner::do_swing_step() { #ifdef USE_REMOTE_TRANSMITTER IrSpecialData data(0x01); - this->transmit_ir(data); + this->transmitter_.transmit(data); #else ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component"); #endif @@ -143,13 +141,14 @@ void AirConditioner::do_display_toggle() { } else { #ifdef USE_REMOTE_TRANSMITTER IrSpecialData data(0x08); - this->transmit_ir(data); + this->transmitter_.transmit(data); #else ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component"); #endif } } +} // namespace ac } // namespace midea } // namespace esphome diff --git a/esphome/components/midea/air_conditioner.h b/esphome/components/midea/air_conditioner.h index 8dfb9dcb3d..a6023b78bb 100644 --- a/esphome/components/midea/air_conditioner.h +++ b/esphome/components/midea/air_conditioner.h @@ -2,17 +2,25 @@ #ifdef USE_ARDUINO +// MideaUART #include + #include "appliance_base.h" #include "esphome/components/sensor/sensor.h" namespace esphome { namespace midea { +namespace ac { using sensor::Sensor; using climate::ClimateCall; +using climate::ClimatePreset; +using climate::ClimateTraits; +using climate::ClimateMode; +using climate::ClimateSwingMode; +using climate::ClimateFanMode; -class AirConditioner : public ApplianceBase { +class AirConditioner : public ApplianceBase, public climate::Climate { public: void dump_config() override; void set_outdoor_temperature_sensor(Sensor *sensor) { this->outdoor_sensor_ = sensor; } @@ -31,15 +39,26 @@ class AirConditioner : public ApplianceBase void do_beeper_off() { this->set_beeper_feedback(false); } void do_power_on() { this->base_.setPowerState(true); } void do_power_off() { this->base_.setPowerState(false); } + void set_supported_modes(const std::set &modes) { this->supported_modes_ = modes; } + void set_supported_swing_modes(const std::set &modes) { this->supported_swing_modes_ = modes; } + void set_supported_presets(const std::set &presets) { this->supported_presets_ = presets; } + void set_custom_presets(const std::set &presets) { this->supported_custom_presets_ = presets; } + void set_custom_fan_modes(const std::set &modes) { this->supported_custom_fan_modes_ = modes; } protected: void control(const ClimateCall &call) override; ClimateTraits traits() override; + std::set supported_modes_{}; + std::set supported_swing_modes_{}; + std::set supported_presets_{}; + std::set supported_custom_presets_{}; + std::set supported_custom_fan_modes_{}; Sensor *outdoor_sensor_{nullptr}; Sensor *humidity_sensor_{nullptr}; Sensor *power_sensor_{nullptr}; }; +} // namespace ac } // namespace midea } // namespace esphome diff --git a/esphome/components/midea/appliance_base.h b/esphome/components/midea/appliance_base.h index 88a722e389..060cbd996b 100644 --- a/esphome/components/midea/appliance_base.h +++ b/esphome/components/midea/appliance_base.h @@ -2,84 +2,97 @@ #ifdef USE_ARDUINO +// MideaUART +#include +#include + +// Include global defines +#include "esphome/core/defines.h" + #include "esphome/core/component.h" #include "esphome/core/log.h" #include "esphome/components/uart/uart.h" #include "esphome/components/climate/climate.h" -#ifdef USE_REMOTE_TRANSMITTER -#include "esphome/components/remote_base/midea_protocol.h" -#include "esphome/components/remote_transmitter/remote_transmitter.h" -#endif -#include -#include +#include "ir_transmitter.h" namespace esphome { namespace midea { -using climate::ClimatePreset; -using climate::ClimateTraits; -using climate::ClimateMode; -using climate::ClimateSwingMode; -using climate::ClimateFanMode; +/* Stream from UART component */ +class UARTStream : public Stream { + public: + void set_uart(uart::UARTComponent *uart) { this->uart_ = uart; } -template -class ApplianceBase : public Component, public uart::UARTDevice, public climate::Climate, public Stream { + /* Stream interface implementation */ + + int available() override { return this->uart_->available(); } + int read() override { + uint8_t data; + this->uart_->read_byte(&data); + return data; + } + int peek() override { + uint8_t data; + this->uart_->peek_byte(&data); + return data; + } + size_t write(uint8_t data) override { + this->uart_->write_byte(data); + return 1; + } + size_t write(const uint8_t *data, size_t size) override { + this->uart_->write_array(data, size); + return size; + } + void flush() override { this->uart_->flush(); } + + protected: + uart::UARTComponent *uart_; +}; + +template class ApplianceBase : public Component { static_assert(std::is_base_of::value, "T must derive from dudanov::midea::ApplianceBase class"); public: ApplianceBase() { - this->base_.setStream(this); + this->base_.setStream(&this->stream_); this->base_.addOnStateCallback(std::bind(&ApplianceBase::on_status_change, this)); dudanov::midea::ApplianceBase::setLogger( [](int level, const char *tag, int line, const String &format, va_list args) { esp_log_vprintf_(level, tag, line, format.c_str(), args); }); } - bool can_proceed() override { - return this->base_.getAutoconfStatus() != dudanov::midea::AutoconfStatus::AUTOCONF_PROGRESS; - } - float get_setup_priority() const override { return setup_priority::BEFORE_CONNECTION; } - void setup() override { this->base_.setup(); } - void loop() override { this->base_.loop(); } + +#ifdef USE_REMOTE_TRANSMITTER + void set_transmitter(RemoteTransmitterBase *transmitter) { this->transmitter_.set_transmitter(transmitter); } +#endif + + /* UART communication */ + + void set_uart_parent(uart::UARTComponent *parent) { this->stream_.set_uart(parent); } void set_period(uint32_t ms) { this->base_.setPeriod(ms); } void set_response_timeout(uint32_t ms) { this->base_.setTimeout(ms); } void set_request_attempts(uint32_t attempts) { this->base_.setNumAttempts(attempts); } + + /* Component methods */ + + void setup() override { this->base_.setup(); } + void loop() override { this->base_.loop(); } + float get_setup_priority() const override { return setup_priority::BEFORE_CONNECTION; } + bool can_proceed() override { + return this->base_.getAutoconfStatus() != dudanov::midea::AutoconfStatus::AUTOCONF_PROGRESS; + } + void set_beeper_feedback(bool state) { this->base_.setBeeper(state); } void set_autoconf(bool value) { this->base_.setAutoconf(value); } - void set_supported_modes(const std::set &modes) { this->supported_modes_ = modes; } - void set_supported_swing_modes(const std::set &modes) { this->supported_swing_modes_ = modes; } - void set_supported_presets(const std::set &presets) { this->supported_presets_ = presets; } - void set_custom_presets(const std::set &presets) { this->supported_custom_presets_ = presets; } - void set_custom_fan_modes(const std::set &modes) { this->supported_custom_fan_modes_ = modes; } virtual void on_status_change() = 0; -#ifdef USE_REMOTE_TRANSMITTER - void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) { - this->transmitter_ = transmitter; - } - void transmit_ir(remote_base::MideaData &data) { - data.finalize(); - auto transmit = this->transmitter_->transmit(); - remote_base::MideaProtocol().encode(transmit.get_data(), data); - transmit.perform(); - } -#endif - - int available() override { return uart::UARTDevice::available(); } - int read() override { return uart::UARTDevice::read(); } - int peek() override { return uart::UARTDevice::peek(); } - void flush() override { uart::UARTDevice::flush(); } - size_t write(uint8_t data) override { return uart::UARTDevice::write(data); } protected: T base_; - std::set supported_modes_{}; - std::set supported_swing_modes_{}; - std::set supported_presets_{}; - std::set supported_custom_presets_{}; - std::set supported_custom_fan_modes_{}; + UARTStream stream_; #ifdef USE_REMOTE_TRANSMITTER - remote_transmitter::RemoteTransmitterComponent *transmitter_{nullptr}; + IrTransmitter transmitter_; #endif }; diff --git a/esphome/components/midea/climate.py b/esphome/components/midea/climate.py index 08e82025b6..46c0019efa 100644 --- a/esphome/components/midea/climate.py +++ b/esphome/components/midea/climate.py @@ -40,9 +40,9 @@ AUTO_LOAD = ["sensor"] CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" CONF_POWER_USAGE = "power_usage" CONF_HUMIDITY_SETPOINT = "humidity_setpoint" -midea_ns = cg.esphome_ns.namespace("midea") -AirConditioner = midea_ns.class_("AirConditioner", climate.Climate, cg.Component) -Capabilities = midea_ns.namespace("Constants") +midea_ac_ns = cg.esphome_ns.namespace("midea").namespace("ac") +AirConditioner = midea_ac_ns.class_("AirConditioner", climate.Climate, cg.Component) +Capabilities = midea_ac_ns.namespace("Constants") def templatize(value): @@ -156,13 +156,13 @@ CONFIG_SCHEMA = cv.All( ) # Actions -FollowMeAction = midea_ns.class_("FollowMeAction", automation.Action) -DisplayToggleAction = midea_ns.class_("DisplayToggleAction", automation.Action) -SwingStepAction = midea_ns.class_("SwingStepAction", automation.Action) -BeeperOnAction = midea_ns.class_("BeeperOnAction", automation.Action) -BeeperOffAction = midea_ns.class_("BeeperOffAction", automation.Action) -PowerOnAction = midea_ns.class_("PowerOnAction", automation.Action) -PowerOffAction = midea_ns.class_("PowerOffAction", automation.Action) +FollowMeAction = midea_ac_ns.class_("FollowMeAction", automation.Action) +DisplayToggleAction = midea_ac_ns.class_("DisplayToggleAction", automation.Action) +SwingStepAction = midea_ac_ns.class_("SwingStepAction", automation.Action) +BeeperOnAction = midea_ac_ns.class_("BeeperOnAction", automation.Action) +BeeperOffAction = midea_ac_ns.class_("BeeperOffAction", automation.Action) +PowerOnAction = midea_ac_ns.class_("PowerOnAction", automation.Action) +PowerOffAction = midea_ac_ns.class_("PowerOffAction", automation.Action) MIDEA_ACTION_BASE_SCHEMA = cv.Schema( { diff --git a/esphome/components/midea/midea_ir.h b/esphome/components/midea/ir_transmitter.h similarity index 73% rename from esphome/components/midea/midea_ir.h rename to esphome/components/midea/ir_transmitter.h index abd4324bcc..34a9f8498e 100644 --- a/esphome/components/midea/midea_ir.h +++ b/esphome/components/midea/ir_transmitter.h @@ -7,6 +7,7 @@ namespace esphome { namespace midea { +using remote_base::RemoteTransmitterBase; using IrData = remote_base::MideaData; class IrFollowMeData : public IrData { @@ -38,6 +39,20 @@ class IrSpecialData : public IrData { IrSpecialData(uint8_t code) : IrData({MIDEA_TYPE_SPECIAL, code, 0xFF, 0xFF, 0xFF}) {} }; +class IrTransmitter { + public: + void set_transmitter(RemoteTransmitterBase *transmitter) { this->transmitter_ = transmitter; } + void transmit(IrData &data) { + data.finalize(); + auto transmit = this->transmitter_->transmit(); + remote_base::MideaProtocol().encode(transmit.get_data(), data); + transmit.perform(); + } + + protected: + RemoteTransmitterBase *transmitter_{nullptr}; +}; + } // namespace midea } // namespace esphome From fea3c4809893a6b3a94ebac7a32e7dc7214d2996 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 15 Nov 2021 10:59:48 +1300 Subject: [PATCH 093/273] Fix indentation of write_lambda for modbus_controller number (#2722) --- .../modbus_controller/number/__init__.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/esphome/components/modbus_controller/number/__init__.py b/esphome/components/modbus_controller/number/__init__.py index afb69f8798..4de0ffbcea 100644 --- a/esphome/components/modbus_controller/number/__init__.py +++ b/esphome/components/modbus_controller/number/__init__.py @@ -129,14 +129,14 @@ async def to_code(config): return_type=cg.optional.template(float), ) cg.add(var.set_template(template_)) - if CONF_WRITE_LAMBDA in config: - template_ = await cg.process_lambda( - config[CONF_WRITE_LAMBDA], - [ - (ModbusNumber.operator("ptr"), "item"), - (cg.float_, "x"), - (cg.std_vector.template(cg.uint16).operator("ref"), "payload"), - ], - return_type=cg.optional.template(float), - ) - cg.add(var.set_write_template(template_)) + if CONF_WRITE_LAMBDA in config: + template_ = await cg.process_lambda( + config[CONF_WRITE_LAMBDA], + [ + (ModbusNumber.operator("ptr"), "item"), + (cg.float_, "x"), + (cg.std_vector.template(cg.uint16).operator("ref"), "payload"), + ], + return_type=cg.optional.template(float), + ) + cg.add(var.set_write_template(template_)) From 194f922312119556cef465d4b9b89ce7477a9a33 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 15 Nov 2021 11:03:40 +1300 Subject: [PATCH 094/273] Bump version to 2021.11.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index f45c6022dd..324356ee0b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.0b3" +__version__ = "2021.11.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 09e87823180bd37366391f5cd89338ae63d64f2a Mon Sep 17 00:00:00 2001 From: Alexandre-Jacques St-Jacques Date: Sun, 14 Nov 2021 17:58:22 -0500 Subject: [PATCH 095/273] Remove unnecessary duplicate touch_pad_filter_start (#2724) --- esphome/components/esp32_touch/esp32_touch.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/components/esp32_touch/esp32_touch.cpp b/esphome/components/esp32_touch/esp32_touch.cpp index 85f4058eee..b225ae1a8a 100644 --- a/esphome/components/esp32_touch/esp32_touch.cpp +++ b/esphome/components/esp32_touch/esp32_touch.cpp @@ -94,7 +94,6 @@ void ESP32TouchComponent::dump_config() { if (this->iir_filter_enabled_()) { ESP_LOGCONFIG(TAG, " IIR Filter: %ums", this->iir_filter_); - touch_pad_filter_start(this->iir_filter_); } else { ESP_LOGCONFIG(TAG, " IIR Filter DISABLED"); } From 687a7e9b2f3a8813e195f7b5e5cba3a27ad92241 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 15 Nov 2021 12:02:18 +1300 Subject: [PATCH 096/273] Bump version to 2021.11.0b5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 324356ee0b..a47673b085 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.0b4" +__version__ = "2021.11.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 0f2df599988c5421972bf33a6b297b15d7391b49 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Nov 2021 09:53:52 +1300 Subject: [PATCH 097/273] Add zeroconf as a direct dependency and lock the version (#2729) --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 6e1fe56057..c4b211283d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,7 @@ esptool==3.2 click==8.0.3 esphome-dashboard==20211021.1 aioesphomeapi==10.2.0 +zeroconf==0.36.13 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From 0a545a28b9d7e6844695d005cc21515fc8436d58 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Nov 2021 09:59:00 +1300 Subject: [PATCH 098/273] Bump version to 2021.11.0b6 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a47673b085..e2bfc81208 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.0b5" +__version__ = "2021.11.0b6" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From b0a0a153f3bf5182ce0b7d5f61e29021184d5e5f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Nov 2021 11:02:45 +1300 Subject: [PATCH 099/273] Improv serial/checksum changes (#2731) Co-authored-by: Paulus Schoutsen --- esphome/components/improv/improv.cpp | 54 ++++++++++--------- esphome/components/improv/improv.h | 9 ++-- .../improv_serial/improv_serial_component.cpp | 35 ++++++++---- 3 files changed, 61 insertions(+), 37 deletions(-) diff --git a/esphome/components/improv/improv.cpp b/esphome/components/improv/improv.cpp index 94068bc626..759962b51a 100644 --- a/esphome/components/improv/improv.cpp +++ b/esphome/components/improv/improv.cpp @@ -2,30 +2,32 @@ namespace improv { -ImprovCommand parse_improv_data(const std::vector &data) { - return parse_improv_data(data.data(), data.size()); +ImprovCommand parse_improv_data(const std::vector &data, bool check_checksum) { + return parse_improv_data(data.data(), data.size(), check_checksum); } -ImprovCommand parse_improv_data(const uint8_t *data, size_t length) { +ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_checksum) { ImprovCommand improv_command; Command command = (Command) data[0]; uint8_t data_length = data[1]; - if (data_length != length - 3) { + if (data_length != length - 2 - check_checksum) { improv_command.command = UNKNOWN; return improv_command; } - uint8_t checksum = data[length - 1]; + if (check_checksum) { + uint8_t checksum = data[length - 1]; - uint32_t calculated_checksum = 0; - for (uint8_t i = 0; i < length - 1; i++) { - calculated_checksum += data[i]; - } + uint32_t calculated_checksum = 0; + for (uint8_t i = 0; i < length - 1; i++) { + calculated_checksum += data[i]; + } - if ((uint8_t) calculated_checksum != checksum) { - improv_command.command = BAD_CHECKSUM; - return improv_command; + if ((uint8_t) calculated_checksum != checksum) { + improv_command.command = BAD_CHECKSUM; + return improv_command; + } } if (command == WIFI_SETTINGS) { @@ -46,7 +48,7 @@ ImprovCommand parse_improv_data(const uint8_t *data, size_t length) { return improv_command; } -std::vector build_rpc_response(Command command, const std::vector &datum) { +std::vector build_rpc_response(Command command, const std::vector &datum, bool add_checksum) { std::vector out; uint32_t length = 0; out.push_back(command); @@ -58,17 +60,19 @@ std::vector build_rpc_response(Command command, const std::vector build_rpc_response(Command command, const std::vector &datum) { +#ifdef ARDUINO +std::vector build_rpc_response(Command command, const std::vector &datum, bool add_checksum) { std::vector out; uint32_t length = 0; out.push_back(command); @@ -80,14 +84,16 @@ std::vector build_rpc_response(Command command, const std::vector &data); -ImprovCommand parse_improv_data(const uint8_t *data, size_t length); +ImprovCommand parse_improv_data(const std::vector &data, bool check_checksum = true); +ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_checksum = true); -std::vector build_rpc_response(Command command, const std::vector &datum); +std::vector build_rpc_response(Command command, const std::vector &datum, + bool add_checksum = true); #ifdef ARDUINO -std::vector build_rpc_response(Command command, const std::vector &datum); +std::vector build_rpc_response(Command command, const std::vector &datum, bool add_checksum = true); #endif // ARDUINO } // namespace improv diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index abbb76ab11..a9a7467125 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -98,13 +98,13 @@ std::vector ImprovSerialComponent::build_rpc_settings_response_(improv: std::string webserver_url = "http://" + ip.str() + ":" + to_string(WEBSERVER_PORT); urls.push_back(webserver_url); #endif - std::vector data = improv::build_rpc_response(command, urls); + std::vector data = improv::build_rpc_response(command, urls, false); return data; } std::vector ImprovSerialComponent::build_version_info_() { std::vector infos = {"ESPHome", ESPHOME_VERSION, ESPHOME_VARIANT, App.get_name()}; - std::vector data = improv::build_rpc_response(improv::GET_DEVICE_INFO, infos); + std::vector data = improv::build_rpc_response(improv::GET_DEVICE_INFO, infos, false); return data; }; @@ -140,22 +140,33 @@ bool ImprovSerialComponent::parse_improv_serial_byte_(uint8_t byte) { if (at < 8 + data_len) return true; - if (at == 8 + data_len) { + if (at == 8 + data_len) + return true; + + if (at == 8 + data_len + 1) { + uint8_t checksum = 0x00; + for (uint8_t i = 0; i < at; i++) + checksum += raw[i]; + + if (checksum != byte) { + ESP_LOGW(TAG, "Error decoding Improv payload"); + this->set_error_(improv::ERROR_INVALID_RPC); + return false; + } + if (type == TYPE_RPC) { this->set_error_(improv::ERROR_NONE); - auto command = improv::parse_improv_data(&raw[9], data_len); + auto command = improv::parse_improv_data(&raw[9], data_len, false); return this->parse_improv_payload_(command); } } - return true; + + // If we got here then the command coming is is improv, but not an RPC command + return false; } bool ImprovSerialComponent::parse_improv_payload_(improv::ImprovCommand &command) { switch (command.command) { - case improv::BAD_CHECKSUM: - ESP_LOGW(TAG, "Error decoding Improv payload"); - this->set_error_(improv::ERROR_INVALID_RPC); - return false; case improv::WIFI_SETTINGS: { wifi::WiFiAP sta{}; sta.set_ssid(command.ssid); @@ -232,6 +243,12 @@ void ImprovSerialComponent::send_response_(std::vector &response) { data[7] = TYPE_RPC_RESPONSE; data[8] = response.size(); data.insert(data.end(), response.begin(), response.end()); + + uint8_t checksum = 0x00; + for (uint8_t d : data) + checksum += d; + data.push_back(checksum); + this->write_data_(data); } From d7432f7c10b7be33a5b7d974cb91eb61a378515a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Nov 2021 11:05:51 +1300 Subject: [PATCH 100/273] Bump version to 2021.11.0b7 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e2bfc81208..48b1e8aa96 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.0b6" +__version__ = "2021.11.0b7" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From c75566b374bb7dbf38f05e331f6b3a3f2f09885c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Nov 2021 12:47:06 +1300 Subject: [PATCH 101/273] Fix zeroconf time comparisons (#2733) Co-authored-by: J. Nick Koston --- esphome/zeroconf.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/zeroconf.py b/esphome/zeroconf.py index a19fc143ec..1fbdf7e93f 100644 --- a/esphome/zeroconf.py +++ b/esphome/zeroconf.py @@ -13,8 +13,9 @@ from zeroconf import ( RecordUpdateListener, Zeroconf, ServiceBrowser, + ServiceStateChange, + current_time_millis, ) -from zeroconf._services import ServiceStateChange _CLASS_IN = 1 _FLAGS_QR_QUERY = 0x0000 # query @@ -88,7 +89,7 @@ class DashboardStatus(threading.Thread): entries = self.zc.cache.entries_with_name(key) if not entries: return False - now = time.time() * 1000 + now = current_time_millis() return any( (entry.created + DashboardStatus.OFFLINE_AFTER) >= now for entry in entries @@ -99,7 +100,7 @@ class DashboardStatus(threading.Thread): self.on_update( {key: self.host_status(host) for key, host in self.key_to_host.items()} ) - now = time.time() * 1000 + now = current_time_millis() for host in self.query_hosts: entries = self.zc.cache.entries_with_name(host) if not entries or all( From cbbafbcca2a340aa6701c63fe719e71c25a53cbc Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Nov 2021 12:53:56 +1300 Subject: [PATCH 102/273] Bump version to 2021.11.0b8 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 48b1e8aa96..38ef0e60ee 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.0b7" +__version__ = "2021.11.0b8" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 0d47d41c85fce14c36cb144a7446260496032b30 Mon Sep 17 00:00:00 2001 From: Ryan Hoffman Date: Tue, 16 Nov 2021 12:53:36 -0500 Subject: [PATCH 103/273] Use as_reversed_hex_array in ble_sensor to fix UUID parsing (#2737) #1627 renamed as_hex_array to as_reversed_hex_array but forgot to rename these users. --- esphome/components/ble_client/sensor/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/esphome/components/ble_client/sensor/__init__.py b/esphome/components/ble_client/sensor/__init__.py index efe4bf0e9a..4aa6a92ba5 100644 --- a/esphome/components/ble_client/sensor/__init__.py +++ b/esphome/components/ble_client/sensor/__init__.py @@ -67,7 +67,7 @@ async def to_code(config): var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])) ) elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): - uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID]) + uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID]) cg.add(var.set_service_uuid128(uuid128)) if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): @@ -87,7 +87,9 @@ async def to_code(config): elif len(config[CONF_CHARACTERISTIC_UUID]) == len( esp32_ble_tracker.bt_uuid128_format ): - uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_CHARACTERISTIC_UUID]) + uuid128 = esp32_ble_tracker.as_reversed_hex_array( + config[CONF_CHARACTERISTIC_UUID] + ) cg.add(var.set_char_uuid128(uuid128)) if CONF_DESCRIPTOR_UUID in config: @@ -108,7 +110,9 @@ async def to_code(config): elif len(config[CONF_DESCRIPTOR_UUID]) == len( esp32_ble_tracker.bt_uuid128_format ): - uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_DESCRIPTOR_UUID]) + uuid128 = esp32_ble_tracker.as_reversed_hex_array( + config[CONF_DESCRIPTOR_UUID] + ) cg.add(var.set_descr_uuid128(uuid128)) if CONF_LAMBDA in config: From c41547fd4a631eae1018acdd93dbd58020476553 Mon Sep 17 00:00:00 2001 From: rotarykite Date: Wed, 17 Nov 2021 02:57:03 +0800 Subject: [PATCH 104/273] Fix senseair component uart read timeout (#2658) Co-authored-by: DAVe3283 Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Chua Jun Chieh --- esphome/components/senseair/senseair.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/esphome/components/senseair/senseair.cpp b/esphome/components/senseair/senseair.cpp index 610892dd9e..50b9e01f17 100644 --- a/esphome/components/senseair/senseair.cpp +++ b/esphome/components/senseair/senseair.cpp @@ -141,12 +141,16 @@ void SenseAirComponent::abc_get_period() { } bool SenseAirComponent::senseair_write_command_(const uint8_t *command, uint8_t *response, uint8_t response_length) { + // Verify we have somewhere to store the response + if (response == nullptr) { + return false; + } + // Write wake up byte required by some S8 sensor models + this->write_byte(0); this->flush(); + delay(5); this->write_array(command, SENSEAIR_REQUEST_LENGTH); - if (response == nullptr) - return true; - bool ret = this->read_array(response, response_length); this->flush(); return ret; From 7e495a5e278345237135d3fb77a867ec7e655f39 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Nov 2021 08:00:26 +1300 Subject: [PATCH 105/273] Bump version to 2021.11.0b9 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 38ef0e60ee..5d25ba1688 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.0b8" +__version__ = "2021.11.0b9" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 58a0b28a39a3b78ef41e530bc796499541115277 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Nov 2021 21:58:30 +1300 Subject: [PATCH 106/273] Bump version to 2021.11.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5d25ba1688..128d12aad5 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.0b9" +__version__ = "2021.11.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 67558bec472237495410a610f9457d04971c5e66 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 17 Nov 2021 09:52:40 +0100 Subject: [PATCH 107/273] Fix HM3301 AQI index calculator (#2739) --- esphome/components/hm3301/aqi_calculator.h | 2 +- esphome/components/hm3301/caqi_calculator.h | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/esphome/components/hm3301/aqi_calculator.h b/esphome/components/hm3301/aqi_calculator.h index 1410eac72b..a3839b643c 100644 --- a/esphome/components/hm3301/aqi_calculator.h +++ b/esphome/components/hm3301/aqi_calculator.h @@ -33,7 +33,7 @@ class AQICalculator : public AbstractAQICalculator { int conc_lo = array[grid_index][0]; int conc_hi = array[grid_index][1]; - return ((aqi_hi - aqi_lo) / (conc_hi - conc_lo)) * (value - conc_lo) + aqi_lo; + return (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo; } int get_grid_index_(uint16_t value, int array[AMOUNT_OF_LEVELS][2]) { diff --git a/esphome/components/hm3301/caqi_calculator.h b/esphome/components/hm3301/caqi_calculator.h index 51158454d0..a7f5460e0a 100644 --- a/esphome/components/hm3301/caqi_calculator.h +++ b/esphome/components/hm3301/caqi_calculator.h @@ -37,9 +37,7 @@ class CAQICalculator : public AbstractAQICalculator { int conc_lo = array[grid_index][0]; int conc_hi = array[grid_index][1]; - int aqi = ((aqi_hi - aqi_lo) / (conc_hi - conc_lo)) * (value - conc_lo) + aqi_lo; - - return aqi; + return (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo; } int get_grid_index_(uint16_t value, int array[AMOUNT_OF_LEVELS][2]) { From 8294d10d5bb55c6590117e303ad92a9d071670a2 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 17 Nov 2021 11:28:31 +0100 Subject: [PATCH 108/273] Re-instate device class update for binary sensors (#2743) --- esphome/components/binary_sensor/__init__.py | 2 ++ esphome/const.py | 1 + 2 files changed, 3 insertions(+) diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 3f11e18e45..1eab76d54e 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -49,6 +49,7 @@ from esphome.const import ( DEVICE_CLASS_SMOKE, DEVICE_CLASS_SOUND, DEVICE_CLASS_TAMPER, + DEVICE_CLASS_UPDATE, DEVICE_CLASS_VIBRATION, DEVICE_CLASS_WINDOW, ) @@ -82,6 +83,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_SMOKE, DEVICE_CLASS_SOUND, DEVICE_CLASS_TAMPER, + DEVICE_CLASS_UPDATE, DEVICE_CLASS_VIBRATION, DEVICE_CLASS_WINDOW, ] diff --git a/esphome/const.py b/esphome/const.py index 128d12aad5..4c540ccb11 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -883,6 +883,7 @@ DEVICE_CLASS_SAFETY = "safety" DEVICE_CLASS_SMOKE = "smoke" DEVICE_CLASS_SOUND = "sound" DEVICE_CLASS_TAMPER = "tamper" +DEVICE_CLASS_UPDATE = "update" DEVICE_CLASS_VIBRATION = "vibration" DEVICE_CLASS_WINDOW = "window" # device classes of both binary_sensor and sensor component From 9c6a475a6e29d47f0f52f079bcb57d79cfa8d5e7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Nov 2021 23:31:38 +1300 Subject: [PATCH 109/273] Bump version to 2021.11.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 4c540ccb11..4bd02022d1 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.0" +__version__ = "2021.11.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From d30e2f2a4f0313bbe1bc1b4d30279be0cb70d86a Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Thu, 18 Nov 2021 22:41:26 +0100 Subject: [PATCH 110/273] Allow UART debug configuration with no after: definition (#2753) --- esphome/components/uart/__init__.py | 10 +++++++--- tests/test2.yaml | 6 ++++++ 2 files changed, 13 insertions(+), 3 deletions(-) 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/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 From 3178243811627b1068f31ac74adec062e79c2fd6 Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Thu, 18 Nov 2021 22:20:32 +0000 Subject: [PATCH 111/273] Fix frame scaling for animated gifs (#2750) --- esphome/components/animation/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 From 3a72dd5cb6f9595615c6dca4d778b6adac925b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Mon, 22 Nov 2021 00:09:11 +0100 Subject: [PATCH 112/273] esp32_camera_web_server: Improve support for MotionEye (#2777) --- .../camera_web_server.cpp | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) 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); From 980b7cda8f519ffbe6aeddafae32eb37e6e2c008 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 21 Nov 2021 15:11:36 -0800 Subject: [PATCH 113/273] Remove floating point ops from the ISR (#2751) Co-authored-by: Samuel Sieb --- esphome/components/zyaura/zyaura.cpp | 40 +++++++++++++++++----------- esphome/components/zyaura/zyaura.h | 8 +++--- 2 files changed, 28 insertions(+), 20 deletions(-) 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 From 8e1c9f50427890beb7d62da8319910ff9f56f409 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Thu, 25 Nov 2021 21:00:49 +0100 Subject: [PATCH 114/273] Fix parsing numbers from null-terminated buffers (#2755) --- esphome/components/anova/anova_base.cpp | 23 ++++++++++++----------- esphome/components/anova/anova_base.h | 1 - esphome/components/ezo/ezo.cpp | 4 ++-- esphome/core/helpers.h | 22 ++++++++++++---------- 4 files changed, 26 insertions(+), 24 deletions(-) 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/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/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); } ///@} From 7d03823afd9d24092c27b9b44ee99af56c7aa495 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 26 Nov 2021 09:02:54 +1300 Subject: [PATCH 115/273] Bump version to 2021.11.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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-_" From 57a029189ca5309d7ddc3fddfbd30a43683d07d5 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Fri, 26 Nov 2021 21:25:58 +0100 Subject: [PATCH 116/273] Add missing nvs_flash_init() to ESP32 preferences code (#2805) Co-authored-by: Maurice Makaay --- esphome/components/esp32/preferences.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index 96b7e7809e..8c2b67a942 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -76,6 +76,7 @@ class ESP32Preferences : public ESPPreferences { uint32_t current_offset = 0; void open() { + nvs_flash_init(); esp_err_t err = nvs_open("esphome", NVS_READWRITE, &nvs_handle); if (err == 0) return; From 5009b3029ff569bd7df35cce6fb265e51a515cad Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 27 Nov 2021 21:13:01 +1300 Subject: [PATCH 117/273] Bump version to 2021.11.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 6af44197ff..cb6ea8addd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.2" +__version__ = "2021.11.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From db2128a3447daaf19ad26f4cb792f07b4075627c Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 28 Nov 2021 20:00:29 +0100 Subject: [PATCH 118/273] Fix parsing numbers in Anova (#2816) --- esphome/components/anova/anova_base.cpp | 6 +++--- esphome/core/helpers.cpp | 5 +++++ esphome/core/helpers.h | 6 ++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/esphome/components/anova/anova_base.cpp b/esphome/components/anova/anova_base.cpp index dcef75e483..cb877bef35 100644 --- a/esphome/components/anova/anova_base.cpp +++ b/esphome/components/anova/anova_base.cpp @@ -104,21 +104,21 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) { break; } case READ_TARGET_TEMPERATURE: { - this->target_temp_ = parse_number(buf, sizeof(buf)).value_or(0.0f); + this->target_temp_ = parse_number(str_until(buf, '\r')).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(buf, sizeof(buf)).value_or(0.0f); + this->target_temp_ = parse_number(str_until(buf, '\r')).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(buf, sizeof(buf)).value_or(0.0f); + this->current_temp_ = parse_number(str_until(buf, '\r')).value_or(0.0f); if (this->fahrenheit_) this->current_temp_ = ftoc(this->current_temp_); this->has_current_temp_ = true; diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 27608a84c1..cfc1c74145 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -444,6 +444,11 @@ IRAM_ATTR InterruptLock::~InterruptLock() { portENABLE_INTERRUPTS(); } std::string str_truncate(const std::string &str, size_t length) { return str.length() > length ? str.substr(0, length) : str; } +std::string str_until(const char *str, char ch) { + char *pos = strchr(str, ch); + return pos == nullptr ? std::string(str) : std::string(str, pos - str); +} +std::string str_until(const std::string &str, char ch) { return str.substr(0, str.find(ch)); } std::string str_snake_case(const std::string &str) { std::string result; result.resize(str.length()); diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 9cdbf7ca16..7718c5f1b2 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -351,6 +351,12 @@ template::value, int> = 0> constexpr /// Truncate a string to a specific length. std::string str_truncate(const std::string &str, size_t length); +/// Extract the part of the string until either the first occurence of the specified character, or the end (requires str +/// to be null-terminated). +std::string str_until(const char *str, char ch); +/// Extract the part of the string until either the first occurence of the specified character, or the end. +std::string str_until(const std::string &str, char ch); + /// Convert the string to snake case (lowercase with underscores). std::string str_snake_case(const std::string &str); From 3d5e1d8d91a117d4a26e5fb0205c7eef280bb26c Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 28 Nov 2021 20:02:10 +0100 Subject: [PATCH 119/273] Fix parsing of multiple values in EZO sensor (#2814) Co-authored-by: Lydia Sevelt --- esphome/components/ezo/ezo.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esphome/components/ezo/ezo.cpp b/esphome/components/ezo/ezo.cpp index 426f2807c1..ca6f121dbb 100644 --- a/esphome/components/ezo/ezo.cpp +++ b/esphome/components/ezo/ezo.cpp @@ -74,6 +74,11 @@ void EZOSensor::loop() { if (buf[0] != 1) return; + // some sensors return multiple comma-separated values, terminate string after first one + for (int i = 1; i < sizeof(buf) - 1; i++) + if (buf[i] == ',') + buf[i] = '\0'; + float val = parse_number((char *) &buf[1], sizeof(buf) - 2).value_or(0); this->publish_state(val); } From 50ec1d0445d4a67d08b3ab0b903733b7aa0c9d9c Mon Sep 17 00:00:00 2001 From: Carlos Garcia Saura Date: Sun, 28 Nov 2021 20:06:53 +0100 Subject: [PATCH 120/273] Fix compilation error for WPA enterprise in ESP-IDF (#2815) --- esphome/components/wifi/wifi_component_esp_idf.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 7f71b7078c..1d346c0a8e 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -375,8 +375,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", err); } } - esp_wpa2_config_t wpa2_config = WPA2_CONFIG_INIT_DEFAULT(); - err = esp_wifi_sta_wpa2_ent_enable(&wpa2_config); + err = esp_wifi_sta_wpa2_ent_enable(); if (err != ESP_OK) { ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed! %d", err); } From e55506f9dbbb4cceec2345c8e37bf6b0bc95b15c Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Sun, 28 Nov 2021 19:12:40 +0000 Subject: [PATCH 121/273] Correct bitmask for third color (blue) scaling. (#2817) --- esphome/components/display/display_color_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/display/display_color_utils.h b/esphome/components/display/display_color_utils.h index 8fc3b0adb9..202de912de 100644 --- a/esphome/components/display/display_color_utils.h +++ b/esphome/components/display/display_color_utils.h @@ -42,7 +42,7 @@ class ColorUtil { ? esp_scale(((colorcode >> third_bits) & ((1 << second_bits) - 1)), ((1 << second_bits) - 1)) : esp_scale(((colorcode >> 8) & 0xFF), ((1 << second_bits) - 1)); - third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & 0xFF), ((1 << third_bits) - 1)) + third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & ((1 << third_bits) - 1)), ((1 << third_bits) - 1)) : esp_scale(((colorcode >> 0) & 0xFF), (1 << third_bits) - 1)); Color color_return; From a5fb0360118d163f31273732c7e1ae27ed3ed74b Mon Sep 17 00:00:00 2001 From: Conclusio Date: Sun, 28 Nov 2021 20:13:42 +0100 Subject: [PATCH 122/273] Add delay to improve stability (#2793) --- esphome/components/scd30/scd30.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/scd30/scd30.cpp b/esphome/components/scd30/scd30.cpp index e6d6ec1c1a..272ee75e30 100644 --- a/esphome/components/scd30/scd30.cpp +++ b/esphome/components/scd30/scd30.cpp @@ -200,6 +200,7 @@ bool SCD30Component::is_data_ready_() { if (!this->write_command_(SCD30_CMD_GET_DATA_READY_STATUS)) { return false; } + delay(4); uint16_t is_data_ready; if (!this->read_data_(&is_data_ready, 1)) { return false; From ea9e75039baa83636e498a87f019ee67bd3512ff Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Nov 2021 10:18:49 +1300 Subject: [PATCH 123/273] Bump version to 2021.11.4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index cb6ea8addd..c2df6ea922 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.11.3" +__version__ = "2021.11.4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From c6414138c723ff18492f1ce75a8ae7b2a2b3281a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 2 Dec 2021 19:38:49 +1300 Subject: [PATCH 124/273] Bump version to 2021.12.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index f3189a6918..27feacb296 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.12.0-dev" +__version__ = "2021.12.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 86c205fe43089b7a7f3d1e23eb31978385150f15 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 2 Dec 2021 21:08:11 +1300 Subject: [PATCH 125/273] Remove blank line --- esphome/const.py | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 27feacb296..925e198fd7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -14,7 +14,6 @@ HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"} SECRETS_FILES = {"secrets.yaml", "secrets.yml"} - CONF_ABOVE = "above" CONF_ACCELERATION = "acceleration" CONF_ACCELERATION_X = "acceleration_x" From 9dcd3d18a04d9d50805ec67743457bdcccb0e023 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Thu, 2 Dec 2021 19:52:56 +0100 Subject: [PATCH 126/273] Update ota_component.cpp (#2852) --- esphome/components/ota/ota_component.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 79edd91173..0cf5ea242c 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -277,6 +277,7 @@ void OTAComponent::handle_() { ssize_t read = this->client_->read(buf, requested); if (read == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { + App.feed_wdt(); delay(1); continue; } @@ -305,8 +306,9 @@ void OTAComponent::handle_() { #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(OTA_IN_PROGRESS, percentage, 0); #endif - // slow down OTA update to avoid getting killed by task watchdog (task_wdt) - delay(10); + // feed watchdog and give other tasks a chance to run + App.feed_wdt(); + yield(); } } From 329bf861d674c9b1d23c85afa5e98738a52ba050 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 3 Dec 2021 07:54:34 +1300 Subject: [PATCH 127/273] Bump version to 2021.12.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 925e198fd7..40ed60cd6f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.12.0b1" +__version__ = "2021.12.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 5ac88de985c23c9fb2d195ad3dbe4457ffca8db8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 6 Dec 2021 19:42:49 +1300 Subject: [PATCH 128/273] Bump esphome-dashboard to 20211206.0 (#2870) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6061476802..22cfeecd5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.2 # When updating platformio, also update Dockerfile esptool==3.2 click==8.0.3 -esphome-dashboard==20211201.0 +esphome-dashboard==20211206.0 aioesphomeapi==10.6.0 zeroconf==0.36.13 From f72abc6f3d3d7e09a2c95c8c3f631ea92b292c92 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Mon, 6 Dec 2021 07:54:46 +0100 Subject: [PATCH 129/273] tlc59208f : fix compilation error (#2867) --- esphome/components/tlc59208f/tlc59208f_output.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/tlc59208f/tlc59208f_output.cpp b/esphome/components/tlc59208f/tlc59208f_output.cpp index 59fb9f98ed..bd62f8de6d 100644 --- a/esphome/components/tlc59208f/tlc59208f_output.cpp +++ b/esphome/components/tlc59208f/tlc59208f_output.cpp @@ -1,6 +1,7 @@ #include "tlc59208f_output.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace tlc59208f { From 1bc757ad0677cac4eb3cb445dd8990b86fc091f9 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Saura Date: Mon, 6 Dec 2021 07:56:53 +0100 Subject: [PATCH 130/273] ADC: Turn verbose the debugging "got voltage" (#2863) --- esphome/components/adc/adc_sensor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index c8242ce008..0a439f8b8d 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -91,7 +91,7 @@ void ADCSensor::dump_config() { float ADCSensor::get_setup_priority() const { return setup_priority::DATA; } void ADCSensor::update() { float value_v = this->sample(); - ESP_LOGD(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v); + ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v); this->publish_state(value_v); } From 3ac720df476fef4316fcdefb4209329e2476921f Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Mon, 6 Dec 2021 07:58:26 +0100 Subject: [PATCH 131/273] SPS30 : fix i2c read size (#2866) --- esphome/components/sps30/sps30.cpp | 26 ++++++++++++++++---------- esphome/components/sps30/sps30.h | 1 + 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/esphome/components/sps30/sps30.cpp b/esphome/components/sps30/sps30.cpp index 472b7606ed..6160120564 100644 --- a/esphome/components/sps30/sps30.cpp +++ b/esphome/components/sps30/sps30.cpp @@ -32,14 +32,11 @@ void SPS30Component::setup() { return; } - uint16_t raw_firmware_version[4]; - if (!this->read_data_(raw_firmware_version, 4)) { + if (!this->read_data_(&raw_firmware_version_, 1)) { this->error_code_ = FIRMWARE_VERSION_READ_FAILED; this->mark_failed(); return; } - ESP_LOGD(TAG, " Firmware version v%0d.%02d", (raw_firmware_version[0] >> 8), - uint16_t(raw_firmware_version[0] & 0xFF)); /// Serial number identification if (!this->write_command_(SPS30_CMD_GET_SERIAL_NUMBER)) { this->error_code_ = SERIAL_NUMBER_REQUEST_FAILED; @@ -59,6 +56,8 @@ void SPS30Component::setup() { this->serial_number_[i * 2 + 1] = uint16_t(uint16_t(raw_serial_number[i] & 0xFF)); } ESP_LOGD(TAG, " Serial Number: '%s'", this->serial_number_); + this->status_clear_warning(); + this->skipped_data_read_cycles_ = 0; this->start_continuous_measurement_(); }); } @@ -93,10 +92,17 @@ void SPS30Component::dump_config() { } LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, " Serial Number: '%s'", this->serial_number_); - LOG_SENSOR(" ", "PM1.0", this->pm_1_0_sensor_); - LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_); - LOG_SENSOR(" ", "PM4", this->pm_4_0_sensor_); - LOG_SENSOR(" ", "PM10", this->pm_10_0_sensor_); + ESP_LOGCONFIG(TAG, " Firmware version v%0d.%0d", (raw_firmware_version_ >> 8), + uint16_t(raw_firmware_version_ & 0xFF)); + LOG_SENSOR(" ", "PM1.0 Weight Concentration", this->pm_1_0_sensor_); + LOG_SENSOR(" ", "PM2.5 Weight Concentration", this->pm_2_5_sensor_); + LOG_SENSOR(" ", "PM4 Weight Concentration", this->pm_4_0_sensor_); + LOG_SENSOR(" ", "PM10 Weight Concentration", this->pm_10_0_sensor_); + LOG_SENSOR(" ", "PM1.0 Number Concentration", this->pmc_1_0_sensor_); + LOG_SENSOR(" ", "PM2.5 Number Concentration", this->pmc_2_5_sensor_); + LOG_SENSOR(" ", "PM4 Number Concentration", this->pmc_4_0_sensor_); + LOG_SENSOR(" ", "PM10 Number Concentration", this->pmc_10_0_sensor_); + LOG_SENSOR(" ", "PM typical size", this->pm_size_sensor_); } void SPS30Component::update() { @@ -123,8 +129,8 @@ void SPS30Component::update() { return; } - uint16_t raw_read_status[1]; - if (!this->read_data_(raw_read_status, 1) || raw_read_status[0] == 0x00) { + uint16_t raw_read_status; + if (!this->read_data_(&raw_read_status, 1) || raw_read_status == 0x00) { ESP_LOGD(TAG, "Sensor measurement not ready yet."); this->skipped_data_read_cycles_++; /// The following logic is required to address the cases when a sensor is quickly replaced before it's marked diff --git a/esphome/components/sps30/sps30.h b/esphome/components/sps30/sps30.h index 2f977252a5..bae33a46e1 100644 --- a/esphome/components/sps30/sps30.h +++ b/esphome/components/sps30/sps30.h @@ -33,6 +33,7 @@ class SPS30Component : public PollingComponent, public i2c::I2CDevice { bool read_data_(uint16_t *data, uint8_t len); uint8_t sht_crc_(uint8_t data1, uint8_t data2); char serial_number_[17] = {0}; /// Terminating NULL character + uint16_t raw_firmware_version_; bool start_continuous_measurement_(); uint8_t skipped_data_read_cycles_ = 0; From 56870ed4a8cbe82db4c51acc8ec7d9fd9ed741a2 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 6 Dec 2021 07:59:50 +0100 Subject: [PATCH 132/273] Fix MCP23x17 not disabling pullup after config change (#2855) --- esphome/components/mcp23x17_base/mcp23x17_base.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/mcp23x17_base/mcp23x17_base.cpp b/esphome/components/mcp23x17_base/mcp23x17_base.cpp index e975670faa..744f2fbe9c 100644 --- a/esphome/components/mcp23x17_base/mcp23x17_base.cpp +++ b/esphome/components/mcp23x17_base/mcp23x17_base.cpp @@ -24,6 +24,7 @@ void MCP23X17Base::pin_mode(uint8_t pin, gpio::Flags flags) { uint8_t gppu = pin < 8 ? mcp23x17_base::MCP23X17_GPPUA : mcp23x17_base::MCP23X17_GPPUB; if (flags == gpio::FLAG_INPUT) { this->update_reg(pin, true, iodir); + this->update_reg(pin, false, gppu); } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { this->update_reg(pin, true, iodir); this->update_reg(pin, true, gppu); From a66e94a0b00110bc011b6eda718adebc60af65c8 Mon Sep 17 00:00:00 2001 From: Massimiliano Ravelli Date: Mon, 6 Dec 2021 08:01:50 +0100 Subject: [PATCH 133/273] Ignore already stopped dhcp for ethernet (#2862) Co-authored-by: Oxan van Leeuwen Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/ethernet/ethernet_component.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 00f68df2b4..384a31ed2f 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -184,7 +184,9 @@ void EthernetComponent::start_connect_() { } err = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_ETH); - ESPHL_ERROR_CHECK(err, "DHCPC stop error"); + if (err != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) { + ESPHL_ERROR_CHECK(err, "DHCPC stop error"); + } err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_ETH, &info); ESPHL_ERROR_CHECK(err, "DHCPC set IP info error"); From c128880033c05012b47db901366db85433219486 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 6 Dec 2021 20:15:34 +1300 Subject: [PATCH 134/273] Add endpoint to fetch secrets keys (#2873) --- esphome/dashboard/dashboard.py | 25 ++++++++++++++++++++++++- esphome/yaml_util.py | 7 ++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index c98047d9e5..5e5cc4ecd2 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -27,7 +27,7 @@ import tornado.process import tornado.web import tornado.websocket -from esphome import const, platformio_api, util +from esphome import const, platformio_api, util, yaml_util from esphome.helpers import mkdir_p, get_bool_env, run_system_command from esphome.storage_json import ( EsphomeStorageJSON, @@ -836,6 +836,28 @@ class LogoutHandler(BaseHandler): self.redirect("./login") +class SecretKeysRequestHandler(BaseHandler): + @authenticated + def get(self): + + filename = None + + for secret_filename in const.SECRETS_FILES: + relative_filename = settings.rel_path(secret_filename) + if os.path.isfile(relative_filename): + filename = relative_filename + break + + if filename is None: + self.send_error(404) + return + + secret_keys = list(yaml_util.load_yaml(filename, clear_secrets=False)) + + self.set_header("content-type", "application/json") + self.write(json.dumps(secret_keys)) + + def get_base_frontend_path(): if ENV_DEV not in os.environ: import esphome_dashboard @@ -939,6 +961,7 @@ def make_app(debug=get_bool_env(ENV_DEV)): (f"{rel}static/(.*)", StaticFileHandler, {"path": get_static_path()}), (f"{rel}devices", ListDevicesHandler), (f"{rel}import", ImportRequestHandler), + (f"{rel}secret_keys", SecretKeysRequestHandler), ], **app_settings, ) diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index bdadbbd43a..57009be57e 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -329,9 +329,10 @@ ESPHomeLoader.add_constructor("!lambda", ESPHomeLoader.construct_lambda) ESPHomeLoader.add_constructor("!force", ESPHomeLoader.construct_force) -def load_yaml(fname): - _SECRET_VALUES.clear() - _SECRET_CACHE.clear() +def load_yaml(fname, clear_secrets=True): + if clear_secrets: + _SECRET_VALUES.clear() + _SECRET_CACHE.clear() return _load_yaml_internal(fname) From 24874f4c3cb9e9efa3d70758ccd1d4b31e03a7c3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 6 Dec 2021 20:57:56 +1300 Subject: [PATCH 135/273] Adopt using wifi secrets that should exist at this point (#2874) --- esphome/components/dashboard_import/__init__.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/esphome/components/dashboard_import/__init__.py b/esphome/components/dashboard_import/__init__.py index d483c77c61..4c47c32ccc 100644 --- a/esphome/components/dashboard_import/__init__.py +++ b/esphome/components/dashboard_import/__init__.py @@ -29,12 +29,11 @@ CONFIG_SCHEMA = cv.Schema( } ) -WIFI_MESSAGE = """ +WIFI_CONFIG = """ -# Do not forget to add your own wifi configuration before installing this configuration -# wifi: -# ssid: !secret wifi_ssid -# password: !secret wifi_password +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password """ @@ -55,6 +54,6 @@ def import_config(path: str, name: str, project_name: str, import_url: str) -> N "esphome": {"name_add_mac_suffix": False}, } p.write_text( - dump(config) + WIFI_MESSAGE, + dump(config) + WIFI_CONFIG, encoding="utf8", ) From 7ee4bb621c37b58ef037a565d06d0642c1623e2c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 6 Dec 2021 20:58:51 +1300 Subject: [PATCH 136/273] Allow wizard to specify secrets (#2875) --- esphome/wizard.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/esphome/wizard.py b/esphome/wizard.py index 6c87b66453..5f4f347ba7 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -86,12 +86,11 @@ def wizard_file(**kwargs): config += "\n\nwifi:\n" if "ssid" in kwargs: - # pylint: disable=consider-using-f-string - config += """ ssid: "{ssid}" - password: "{psk}" -""".format( - **kwargs - ) + if kwargs["ssid"].startswith("!secret"): + template = " ssid: {ssid}\n password: {psk}\n" + else: + template = """ ssid: "{ssid}"\n password: "{psk}"\n""" + config += template.format(**kwargs) else: config += """ # ssid: "My SSID" # password: "mypassword" From df315a1f5182a4ab18fc4c147dc370209c9f3704 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 6 Dec 2021 19:24:20 +0100 Subject: [PATCH 137/273] Feed watchdog when no component loops (#2857) --- esphome/core/application.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 1bef99e868..a423397453 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -37,6 +37,7 @@ void Application::setup() { component->call(); this->scheduler.process_to_add(); + this->feed_wdt(); if (component->can_proceed()) continue; @@ -46,14 +47,15 @@ void Application::setup() { do { uint32_t new_app_state = STATUS_LED_WARNING; this->scheduler.call(); + this->feed_wdt(); for (uint32_t j = 0; j <= i; j++) { this->components_[j]->call(); new_app_state |= this->components_[j]->get_component_state(); this->app_state_ |= new_app_state; + this->feed_wdt(); } this->app_state_ = new_app_state; yield(); - this->feed_wdt(); } while (!component->can_proceed()); } @@ -65,6 +67,7 @@ void Application::loop() { uint32_t new_app_state = 0; this->scheduler.call(); + this->feed_wdt(); for (Component *component : this->looping_components_) { { WarnIfComponentBlockingGuard guard{component}; From 09b7c6f55092501be2283b451be00132908deced Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 7 Dec 2021 07:41:40 +1300 Subject: [PATCH 138/273] Bump esphome-dashboard to 20211207.0 (#2877) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 22cfeecd5d..e27ae0f625 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.2 # When updating platformio, also update Dockerfile esptool==3.2 click==8.0.3 -esphome-dashboard==20211206.0 +esphome-dashboard==20211207.0 aioesphomeapi==10.6.0 zeroconf==0.36.13 From ed5e2dd332643b201b1c9854556c6260bd91561e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 7 Dec 2021 07:47:48 +1300 Subject: [PATCH 139/273] Bump version to 2021.12.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 40ed60cd6f..a77629ad35 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.12.0b2" +__version__ = "2021.12.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From e763469af89fbd22d293424eed89dd393c78df9c Mon Sep 17 00:00:00 2001 From: Carlos Garcia Saura Date: Mon, 6 Dec 2021 23:26:06 +0100 Subject: [PATCH 140/273] Feed watchdog while setting up OTA (#2876) --- esphome/components/ota/ota_component.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 0cf5ea242c..92256eb1b6 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -372,6 +372,7 @@ bool OTAComponent::readall_(uint8_t *buf, size_t len) { ssize_t read = this->client_->read(buf + at, len - at); if (read == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { + App.feed_wdt(); delay(1); continue; } @@ -383,6 +384,7 @@ bool OTAComponent::readall_(uint8_t *buf, size_t len) { } else { at += read; } + App.feed_wdt(); delay(1); } @@ -401,6 +403,7 @@ bool OTAComponent::writeall_(const uint8_t *buf, size_t len) { ssize_t written = this->client_->write(buf + at, len - at); if (written == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { + App.feed_wdt(); delay(1); continue; } @@ -409,6 +412,7 @@ bool OTAComponent::writeall_(const uint8_t *buf, size_t len) { } else { at += written; } + App.feed_wdt(); delay(1); } return true; From fbc84861c7fba05e40603b513c65d73a37f27136 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 8 Dec 2021 09:22:03 +1300 Subject: [PATCH 141/273] Use new platform component config blocks for wizard (#2885) --- esphome/wizard.py | 24 ++++++++++++++++++++++-- tests/unit_tests/test_wizard.py | 9 +++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/esphome/wizard.py b/esphome/wizard.py index 5f4f347ba7..f2632caf71 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -45,9 +45,9 @@ OTA_BIG = r""" ____ _______ BASE_CONFIG = """esphome: name: {name} - platform: {platform} - board: {board} +""" +LOGGER_API_CONFIG = """ # Enable logging logger: @@ -55,6 +55,18 @@ logger: api: """ +ESP8266_CONFIG = """ +esp8266: + board: {board} +""" + +ESP32_CONFIG = """ +esp32: + board: {board} + framework: + type: arduino +""" + def sanitize_double_quotes(value): return value.replace("\\", "\\\\").replace('"', '\\"') @@ -71,6 +83,14 @@ def wizard_file(**kwargs): config = BASE_CONFIG.format(**kwargs) + config += ( + ESP8266_CONFIG.format(**kwargs) + if kwargs["platform"] == "ESP8266" + else ESP32_CONFIG.format(**kwargs) + ) + + config += LOGGER_API_CONFIG + # Configure API if "password" in kwargs: config += f" password: \"{kwargs['password']}\"\n" diff --git a/tests/unit_tests/test_wizard.py b/tests/unit_tests/test_wizard.py index 18e040b0a6..59fcfbff60 100644 --- a/tests/unit_tests/test_wizard.py +++ b/tests/unit_tests/test_wizard.py @@ -11,7 +11,7 @@ def default_config(): return { "name": "test-name", "platform": "test_platform", - "board": "test_board", + "board": "esp01_1m", "ssid": "test_ssid", "psk": "test_psk", "password": "", @@ -105,6 +105,7 @@ def test_wizard_write_sets_platform(default_config, tmp_path, monkeypatch): If the platform is not explicitly set, use "ESP8266" if the board is one of the ESP8266 boards """ # Given + del default_config["platform"] monkeypatch.setattr(wz, "write_file", MagicMock()) # When @@ -112,7 +113,7 @@ def test_wizard_write_sets_platform(default_config, tmp_path, monkeypatch): # Then generated_config = wz.write_file.call_args.args[1] - assert f"platform: {default_config['platform']}" in generated_config + assert "esp8266:" in generated_config def test_wizard_write_defaults_platform_from_board_esp8266( @@ -132,7 +133,7 @@ def test_wizard_write_defaults_platform_from_board_esp8266( # Then generated_config = wz.write_file.call_args.args[1] - assert "platform: ESP8266" in generated_config + assert "esp8266:" in generated_config def test_wizard_write_defaults_platform_from_board_esp32( @@ -152,7 +153,7 @@ def test_wizard_write_defaults_platform_from_board_esp32( # Then generated_config = wz.write_file.call_args.args[1] - assert "platform: ESP32" in generated_config + assert "esp32:" in generated_config def test_safe_print_step_prints_step_number_and_description(monkeypatch): From 090e10730cfcfd22df5a3252def5c09df89d1fb9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 8 Dec 2021 12:42:50 +1300 Subject: [PATCH 142/273] Bump esphome-dashboard to 20211208.0 (#2887) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e27ae0f625..f7b1a6a1fe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.2 # When updating platformio, also update Dockerfile esptool==3.2 click==8.0.3 -esphome-dashboard==20211207.0 +esphome-dashboard==20211208.0 aioesphomeapi==10.6.0 zeroconf==0.36.13 From f3d9d707b6dca605382f74bf855e2ec12139bc47 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 8 Dec 2021 12:58:14 +1300 Subject: [PATCH 143/273] Bump version to 2021.12.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a77629ad35..67bbcba853 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.12.0b3" +__version__ = "2021.12.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 649366ff446f1812681e76c3de38b2834fac1a69 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 10 Dec 2021 09:32:34 +1300 Subject: [PATCH 144/273] Fix published state for modbus number (#2894) --- .../modbus_controller/number/modbus_number.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/esphome/components/modbus_controller/number/modbus_number.cpp b/esphome/components/modbus_controller/number/modbus_number.cpp index ba2ffdd09f..70be9eaa1f 100644 --- a/esphome/components/modbus_controller/number/modbus_number.cpp +++ b/esphome/components/modbus_controller/number/modbus_number.cpp @@ -27,6 +27,7 @@ void ModbusNumber::parse_and_publish(const std::vector &data) { void ModbusNumber::control(float value) { std::vector data; + float write_value = value; // Is there are lambda configured? if (this->write_transform_func_.has_value()) { // data is passed by reference @@ -35,23 +36,23 @@ void ModbusNumber::control(float value) { auto val = (*this->write_transform_func_)(this, value, data); if (val.has_value()) { ESP_LOGV(TAG, "Value overwritten by lambda"); - value = val.value(); + write_value = val.value(); } else { ESP_LOGV(TAG, "Communication handled by lambda - exiting control"); return; } } else { - value = multiply_by_ * value; + write_value = multiply_by_ * write_value; } // lambda didn't set payload if (data.empty()) { - data = float_to_payload(value, this->sensor_value_type); + data = float_to_payload(write_value, this->sensor_value_type); } ESP_LOGD(TAG, "Updating register: connected Sensor=%s start address=0x%X register count=%d new value=%.02f (val=%.02f)", - this->get_name().c_str(), this->start_address, this->register_count, value, value); + this->get_name().c_str(), this->start_address, this->register_count, write_value, write_value); // Create and send the write command auto write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset, From 708b928c73e7a1a5eb9173a7b74c1decf6d6ed9d Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Thu, 9 Dec 2021 17:44:43 -0300 Subject: [PATCH 145/273] Modbus number/output use write single (#2896) Co-authored-by: Martin <25747549+martgras@users.noreply.github.com> --- esphome/components/modbus_controller/const.py | 1 + .../components/modbus_controller/number/__init__.py | 3 +++ .../modbus_controller/number/modbus_number.cpp | 10 +++++++--- .../modbus_controller/number/modbus_number.h | 2 ++ .../components/modbus_controller/output/__init__.py | 3 +++ .../modbus_controller/output/modbus_output.cpp | 10 ++++++++-- .../modbus_controller/output/modbus_output.h | 2 ++ .../components/modbus_controller/switch/__init__.py | 2 +- 8 files changed, 27 insertions(+), 6 deletions(-) diff --git a/esphome/components/modbus_controller/const.py b/esphome/components/modbus_controller/const.py index 8d1676dd38..baf72efb94 100644 --- a/esphome/components/modbus_controller/const.py +++ b/esphome/components/modbus_controller/const.py @@ -10,5 +10,6 @@ CONF_REGISTER_COUNT = "register_count" CONF_REGISTER_TYPE = "register_type" CONF_RESPONSE_SIZE = "response_size" CONF_SKIP_UPDATES = "skip_updates" +CONF_USE_WRITE_MULTIPLE = "use_write_multiple" CONF_VALUE_TYPE = "value_type" CONF_WRITE_LAMBDA = "write_lambda" diff --git a/esphome/components/modbus_controller/number/__init__.py b/esphome/components/modbus_controller/number/__init__.py index 3c5db9b9c8..4ad6601fee 100644 --- a/esphome/components/modbus_controller/number/__init__.py +++ b/esphome/components/modbus_controller/number/__init__.py @@ -25,6 +25,7 @@ from ..const import ( CONF_FORCE_NEW_RANGE, CONF_MODBUS_CONTROLLER_ID, CONF_SKIP_UPDATES, + CONF_USE_WRITE_MULTIPLE, CONF_VALUE_TYPE, CONF_WRITE_LAMBDA, ) @@ -69,6 +70,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_MIN_VALUE, default=-16777215.0): cv.float_, cv.Optional(CONF_STEP, default=1): cv.positive_float, cv.Optional(CONF_MULTIPLY, default=1.0): cv.float_, + cv.Optional(CONF_USE_WRITE_MULTIPLE, default=False): cv.boolean, } ) .extend(cv.polling_component_schema("60s")), @@ -105,6 +107,7 @@ async def to_code(config): cg.add(var.set_parent(parent)) cg.add(parent.add_sensor_item(var)) await add_modbus_base_properties(var, config, ModbusNumber) + cg.add(var.set_use_write_mutiple(config[CONF_USE_WRITE_MULTIPLE])) if CONF_WRITE_LAMBDA in config: template_ = await cg.process_lambda( config[CONF_WRITE_LAMBDA], diff --git a/esphome/components/modbus_controller/number/modbus_number.cpp b/esphome/components/modbus_controller/number/modbus_number.cpp index 70be9eaa1f..e5afd0c611 100644 --- a/esphome/components/modbus_controller/number/modbus_number.cpp +++ b/esphome/components/modbus_controller/number/modbus_number.cpp @@ -55,9 +55,13 @@ void ModbusNumber::control(float value) { this->get_name().c_str(), this->start_address, this->register_count, write_value, write_value); // Create and send the write command - auto write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset, - this->register_count, data); - + ModbusCommandItem write_cmd; + if (this->register_count == 1 && !this->use_write_multiple_) { + write_cmd = ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset, data[0]); + } else { + write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset, + this->register_count, data); + } // publish new value write_cmd.on_data_func = [this, write_cmd, value](ModbusRegisterType register_type, uint16_t start_address, const std::vector &data) { diff --git a/esphome/components/modbus_controller/number/modbus_number.h b/esphome/components/modbus_controller/number/modbus_number.h index 271bbfac50..c678cd00cc 100644 --- a/esphome/components/modbus_controller/number/modbus_number.h +++ b/esphome/components/modbus_controller/number/modbus_number.h @@ -35,6 +35,7 @@ class ModbusNumber : public number::Number, public Component, public SensorItem using write_transform_func_t = std::function(ModbusNumber *, float, std::vector &)>; void set_template(transform_func_t &&f) { this->transform_func_ = f; } void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; } protected: void control(float value) override; @@ -42,6 +43,7 @@ class ModbusNumber : public number::Number, public Component, public SensorItem optional write_transform_func_; ModbusController *parent_; float multiply_by_{1.0}; + bool use_write_multiple_{false}; }; } // namespace modbus_controller diff --git a/esphome/components/modbus_controller/output/__init__.py b/esphome/components/modbus_controller/output/__init__.py index eacd96579f..a26d05a18b 100644 --- a/esphome/components/modbus_controller/output/__init__.py +++ b/esphome/components/modbus_controller/output/__init__.py @@ -18,6 +18,7 @@ from .. import ( from ..const import ( CONF_MODBUS_CONTROLLER_ID, + CONF_USE_WRITE_MULTIPLE, CONF_VALUE_TYPE, CONF_WRITE_LAMBDA, ) @@ -36,6 +37,7 @@ CONFIG_SCHEMA = cv.All( cv.GenerateID(): cv.declare_id(ModbusOutput), cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda, cv.Optional(CONF_MULTIPLY, default=1.0): cv.float_, + cv.Optional(CONF_USE_WRITE_MULTIPLE, default=False): cv.boolean, } ), validate_modbus_register, @@ -54,6 +56,7 @@ async def to_code(config): await output.register_output(var, config) cg.add(var.set_write_multiply(config[CONF_MULTIPLY])) parent = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID]) + cg.add(var.set_use_write_mutiple(config[CONF_USE_WRITE_MULTIPLE])) cg.add(var.set_parent(parent)) if CONF_WRITE_LAMBDA in config: template_ = await cg.process_lambda( diff --git a/esphome/components/modbus_controller/output/modbus_output.cpp b/esphome/components/modbus_controller/output/modbus_output.cpp index d2b5d02bda..4c2e5775b9 100644 --- a/esphome/components/modbus_controller/output/modbus_output.cpp +++ b/esphome/components/modbus_controller/output/modbus_output.cpp @@ -40,8 +40,14 @@ void ModbusOutput::write_state(float value) { this->start_address, this->register_count, value, original_value); // Create and send the write command - auto write_cmd = - ModbusCommandItem::create_write_multiple_command(parent_, this->start_address, this->register_count, data); + // Create and send the write command + ModbusCommandItem write_cmd; + if (this->register_count == 1 && !this->use_write_multiple_) { + write_cmd = ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset, data[0]); + } else { + write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset, + this->register_count, data); + } parent_->queue_command(write_cmd); } diff --git a/esphome/components/modbus_controller/output/modbus_output.h b/esphome/components/modbus_controller/output/modbus_output.h index 6e8521854b..78d3474ad6 100644 --- a/esphome/components/modbus_controller/output/modbus_output.h +++ b/esphome/components/modbus_controller/output/modbus_output.h @@ -33,6 +33,7 @@ class ModbusOutput : public output::FloatOutput, public Component, public Sensor using write_transform_func_t = std::function(ModbusOutput *, float, std::vector &)>; void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; } protected: void write_state(float value) override; @@ -40,6 +41,7 @@ class ModbusOutput : public output::FloatOutput, public Component, public Sensor ModbusController *parent_; float multiply_by_{1.0}; + bool use_write_multiple_; }; } // namespace modbus_controller diff --git a/esphome/components/modbus_controller/switch/__init__.py b/esphome/components/modbus_controller/switch/__init__.py index df11b268ac..9858d45617 100644 --- a/esphome/components/modbus_controller/switch/__init__.py +++ b/esphome/components/modbus_controller/switch/__init__.py @@ -18,10 +18,10 @@ from ..const import ( CONF_FORCE_NEW_RANGE, CONF_MODBUS_CONTROLLER_ID, CONF_REGISTER_TYPE, + CONF_USE_WRITE_MULTIPLE, CONF_WRITE_LAMBDA, ) -CONF_USE_WRITE_MULTIPLE = "use_write_multiple" DEPENDENCIES = ["modbus_controller"] CODEOWNERS = ["@martgras"] From 3bf632003056dd93c0fbc22cbcbeb199340a9dbd Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 10 Dec 2021 09:55:48 +1300 Subject: [PATCH 146/273] Bump version to 2021.12.0b5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 67bbcba853..e284ee6414 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.12.0b4" +__version__ = "2021.12.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From d504daef9102b0eccfa1a3d2d9f5c7dd16c09160 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sat, 11 Dec 2021 01:03:22 -0600 Subject: [PATCH 147/273] Fix for two points setting when fan_only_cooling is disabled (#2903) Co-authored-by: Paulus Schoutsen Co-authored-by: Keith Burzinski --- esphome/components/thermostat/climate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/thermostat/climate.py b/esphome/components/thermostat/climate.py index 7b5ee7c624..20565e811c 100644 --- a/esphome/components/thermostat/climate.py +++ b/esphome/components/thermostat/climate.py @@ -431,7 +431,8 @@ async def to_code(config): heat_cool_mode_available = CONF_HEAT_ACTION in config and CONF_COOL_ACTION in config two_points_available = CONF_HEAT_ACTION in config and ( - CONF_COOL_ACTION in config or CONF_FAN_ONLY_ACTION in config + CONF_COOL_ACTION in config + or (config[CONF_FAN_ONLY_COOLING] and CONF_FAN_ONLY_ACTION in config) ) sens = await cg.get_variable(config[CONF_SENSOR]) From bfaa64883794d1167d80e4d12ba8c42862861d0e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 11 Dec 2021 21:03:41 +1300 Subject: [PATCH 148/273] Bump esphome-dashboard to 20211211.0 (#2904) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f7b1a6a1fe..c45797a71f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.2 # When updating platformio, also update Dockerfile esptool==3.2 click==8.0.3 -esphome-dashboard==20211208.0 +esphome-dashboard==20211211.0 aioesphomeapi==10.6.0 zeroconf==0.36.13 From 08057720b81b894e74e1afea9278afb9631755dd Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 11 Dec 2021 21:07:07 +1300 Subject: [PATCH 149/273] Bump version to 2021.12.0b6 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e284ee6414..a078ca56a6 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.12.0b5" +__version__ = "2021.12.0b6" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 8c9e0e552dab8ac7c04fc8b1142fe650671b8bfb Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sun, 12 Dec 2021 07:10:51 +1300 Subject: [PATCH 150/273] Bump version to 2021.12.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a078ca56a6..1508f9b78a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.12.0b6" +__version__ = "2021.12.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From e32a999cd0bb8a4dec5c8dc266407bfad93e7197 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Dec 2021 03:21:09 +0100 Subject: [PATCH 151/273] Set text sensor state property to filter output (#2893) --- esphome/components/text_sensor/text_sensor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp index 0bcab90843..5d47e7465a 100644 --- a/esphome/components/text_sensor/text_sensor.cpp +++ b/esphome/components/text_sensor/text_sensor.cpp @@ -62,7 +62,7 @@ void TextSensor::add_on_raw_state_callback(std::function call std::string TextSensor::get_state() const { return this->state; } std::string TextSensor::get_raw_state() const { return this->raw_state; } void TextSensor::internal_send_state_to_frontend(const std::string &state) { - this->state = this->raw_state; + this->state = state; this->has_state_ = true; ESP_LOGD(TAG, "'%s': Sending state '%s'", this->name_.c_str(), state.c_str()); this->callback_.call(state); From 386a5b63629775e74fc8ddd85598c2eed8fb1f57 Mon Sep 17 00:00:00 2001 From: wilberforce Date: Tue, 14 Dec 2021 15:08:01 +1300 Subject: [PATCH 152/273] Allow button POST on press from web server (#2913) --- esphome/components/web_server/web_server.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 29cb4827bd..1e7696edfb 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -390,8 +390,6 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM #ifdef USE_BUTTON void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (button::Button *obj : App.get_buttons()) { - if (obj->is_internal()) - continue; if (obj->get_object_id() != match.id) continue; From 4bb779d9a565caf562e16a0169cf35c099282b51 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 15 Dec 2021 14:57:32 +1300 Subject: [PATCH 153/273] Bump version to 2021.12.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 1508f9b78a..a47d90f401 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.12.0" +__version__ = "2021.12.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 6d39f64be714ca4b94b6e2a6219fdd7270339d93 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 6 Dec 2021 08:01:14 +0100 Subject: [PATCH 154/273] Don't disable idle task WDT when it's not enabled (#2856) --- esphome/components/esp32/core.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp index a9756b41cd..6123d83a34 100644 --- a/esphome/components/esp32/core.cpp +++ b/esphome/components/esp32/core.cpp @@ -42,11 +42,11 @@ void arch_init() { // Idle task watchdog is disabled on ESP-IDF #elif defined(USE_ARDUINO) enableLoopWDT(); - // Disable idle task watchdog on the core we're using (Arduino pins the process to a core) -#if CONFIG_ARDUINO_RUNNING_CORE == 0 + // Disable idle task watchdog on the core we're using (Arduino pins the task to a core) +#if defined(CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0) && CONFIG_ARDUINO_RUNNING_CORE == 0 disableCore0WDT(); #endif -#if CONFIG_ARDUINO_RUNNING_CORE == 1 +#if defined(CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1) && CONFIG_ARDUINO_RUNNING_CORE == 1 disableCore1WDT(); #endif #endif From 9471df0a1b7b4933610bed6c6b79838f38b9370b Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 20 Dec 2021 20:19:20 +0100 Subject: [PATCH 155/273] Fix MQTT button press action (#2917) --- esphome/components/mqtt/mqtt_button.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/mqtt/mqtt_button.cpp b/esphome/components/mqtt/mqtt_button.cpp index 25ff327cf9..5f3aaa1dd9 100644 --- a/esphome/components/mqtt/mqtt_button.cpp +++ b/esphome/components/mqtt/mqtt_button.cpp @@ -17,7 +17,7 @@ MQTTButtonComponent::MQTTButtonComponent(button::Button *button) : MQTTComponent void MQTTButtonComponent::setup() { this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) { - if (payload == "press") { + if (payload == "PRESS") { this->button_->press(); } else { ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str()); @@ -31,6 +31,7 @@ void MQTTButtonComponent::dump_config() { } void MQTTButtonComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { + config.state_topic = false; if (!this->button_->get_device_class().empty()) root[MQTT_DEVICE_CLASS] = this->button_->get_device_class(); } From 5516f65971da8a0a3c6e073bb31392b6f5d8766e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 21 Dec 2021 08:24:08 +1300 Subject: [PATCH 156/273] Bump version to 2021.12.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a47d90f401..614288ba51 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.12.1" +__version__ = "2021.12.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From ffd4280d6c7af4ab7e0ee8f5fe8373531419d413 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 22 Dec 2021 20:08:54 +1300 Subject: [PATCH 157/273] Require arduino in webserver for better validation (#2941) --- esphome/components/web_server/__init__.py | 57 +++++++++++++---------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index d9ff84d501..62d5ec6f14 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -22,31 +22,38 @@ AUTO_LOAD = ["json", "web_server_base"] web_server_ns = cg.esphome_ns.namespace("web_server") WebServer = web_server_ns.class_("WebServer", cg.Component, cg.Controller) -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(WebServer), - cv.Optional(CONF_PORT, default=80): cv.port, - cv.Optional( - CONF_CSS_URL, default="https://esphome.io/_static/webserver-v1.min.css" - ): cv.string, - cv.Optional(CONF_CSS_INCLUDE): cv.file_, - cv.Optional( - CONF_JS_URL, default="https://esphome.io/_static/webserver-v1.min.js" - ): cv.string, - cv.Optional(CONF_JS_INCLUDE): cv.file_, - cv.Optional(CONF_AUTH): cv.Schema( - { - 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( - web_server_base.WebServerBase - ), - cv.Optional(CONF_INCLUDE_INTERNAL, default=False): cv.boolean, - cv.Optional(CONF_OTA, default=True): cv.boolean, - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(WebServer), + cv.Optional(CONF_PORT, default=80): cv.port, + cv.Optional( + CONF_CSS_URL, default="https://esphome.io/_static/webserver-v1.min.css" + ): cv.string, + cv.Optional(CONF_CSS_INCLUDE): cv.file_, + cv.Optional( + CONF_JS_URL, default="https://esphome.io/_static/webserver-v1.min.js" + ): cv.string, + cv.Optional(CONF_JS_INCLUDE): cv.file_, + cv.Optional(CONF_AUTH): cv.Schema( + { + 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( + web_server_base.WebServerBase + ), + cv.Optional(CONF_INCLUDE_INTERNAL, default=False): cv.boolean, + cv.Optional(CONF_OTA, default=True): cv.boolean, + }, + ).extend(cv.COMPONENT_SCHEMA), + cv.only_with_arduino, +) @coroutine_with_priority(40.0) From fc0a6546a221984b6a7e124d497aa8a669955c94 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 23 Dec 2021 08:31:56 +1300 Subject: [PATCH 158/273] Only allow internal pins for dht sensor (#2940) --- esphome/components/dht/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/dht/sensor.py b/esphome/components/dht/sensor.py index 1334f0270c..cd1886728e 100644 --- a/esphome/components/dht/sensor.py +++ b/esphome/components/dht/sensor.py @@ -33,7 +33,7 @@ DHT = dht_ns.class_("DHT", cg.PollingComponent) CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(DHT), - cv.Required(CONF_PIN): pins.gpio_input_pin_schema, + cv.Required(CONF_PIN): pins.internal_gpio_input_pin_schema, cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, accuracy_decimals=1, From 41879e41e6227dc2273ca083831da80a4938241f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 23 Dec 2021 08:43:17 +1300 Subject: [PATCH 159/273] Workaround installing as editable package not working (#2936) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 62a64c851d..7df2fbf3d8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -64,7 +64,7 @@ RUN \ # Copy esphome and install COPY . /esphome -RUN pip3 install --no-cache-dir -e /esphome +RUN pip3 install --no-cache-dir /esphome # Settings for dashboard ENV USERNAME="" PASSWORD="" From 28f87dc8045e0f1af7794c121f20ca943f2f2ad0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 30 Dec 2021 10:42:22 +1300 Subject: [PATCH 160/273] Remove -e for hassio images (#2964) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 7df2fbf3d8..eece2108f7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -112,7 +112,7 @@ RUN \ # Copy esphome and install COPY . /esphome -RUN pip3 install --no-cache-dir -e /esphome +RUN pip3 install --no-cache-dir /esphome # Labels LABEL \ From b37739eec2fb39bb97602411a71a949da82902ee Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 30 Dec 2021 13:58:47 +1300 Subject: [PATCH 161/273] Bump version to 2021.12.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 614288ba51..9ed0975cd4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.12.2" +__version__ = "2021.12.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From d32633b3c7604d5a3415db6c4d14a52fba5c96e5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 22 Dec 2021 15:27:34 +1300 Subject: [PATCH 162/273] Update curl package version in docker (#2939) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index eece2108f7..25f2cf85d2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -27,7 +27,7 @@ RUN \ python3-cryptography=3.3.2-1 \ iputils-ping=3:20210202-1 \ git=1:2.30.2-1 \ - curl=7.74.0-1.3+b1 \ + curl=7.74.0-1.3+deb11u1 \ && rm -rf \ /tmp/* \ /var/{cache,log}/* \ From 961c27f1c27ff8e9496d669254f2fb522e3361f6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 13 Jan 2022 11:02:07 +1300 Subject: [PATCH 163/273] Bump version to 2022.1.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index fdc880caf3..a26e3f050f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.1.0-dev" +__version__ = "2022.1.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From d6009453df6d79c693ae8cb1c5cc554aa570ed28 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 12 Jan 2022 22:35:30 -0800 Subject: [PATCH 164/273] Add factory to download name (#3040) --- esphome/dashboard/dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index ca257d93b4..f9ae3a4fc8 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -416,7 +416,7 @@ class DownloadBinaryRequestHandler(BaseHandler): if storage_json is None: self.send_error(404) return - filename = f"{storage_json.name}.bin" + filename = f"{storage_json.name}-factory.bin" path = storage_json.firmware_bin_path.replace( "firmware.bin", "firmware-factory.bin" ) From 6dfe3039d03e585dc22c23c35c597580b60f62b3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 12 Jan 2022 23:22:19 -0800 Subject: [PATCH 165/273] Bump dashboard to 20220113.2 (#3041) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b21218f511..05636da805 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.4 # When updating platformio, also update Dockerfile esptool==3.2 click==8.0.3 -esphome-dashboard==20220113.1 +esphome-dashboard==20220113.2 aioesphomeapi==10.6.0 zeroconf==0.37.0 From cbe30924049be5caa16a44fbf959ef8e0ae72afe Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 13 Jan 2022 21:28:45 +1300 Subject: [PATCH 166/273] Bump version to 2022.1.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a26e3f050f..daa61ebd9b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.1.0b1" +__version__ = "2022.1.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From d274545e770c11784a20ec14ad697d03ec907583 Mon Sep 17 00:00:00 2001 From: Ohad Lutzky Date: Sun, 16 Jan 2022 22:14:45 +0000 Subject: [PATCH 167/273] Disable caching for binary download (#3054) --- esphome/dashboard/dashboard.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index f9ae3a4fc8..c68d037fe6 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -445,6 +445,7 @@ class DownloadBinaryRequestHandler(BaseHandler): self.set_header("Content-Type", "application/octet-stream") self.set_header("Content-Disposition", f'attachment; filename="{filename}"') + self.set_header("Cache-Control", "no-cache") if not Path(path).is_file(): self.send_error(404) return From 282313ab52f4cf6433767e95eddcfa486e1ed5c6 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 16 Jan 2022 23:15:11 +0100 Subject: [PATCH 168/273] Rename post_build scripts to fix codeowners script (#3057) --- esphome/components/esp32/__init__.py | 2 +- .../components/esp32/{post_build.py => post_build.py.script} | 0 esphome/components/esp8266/__init__.py | 2 +- .../components/esp8266/{post_build.py => post_build.py.script} | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename esphome/components/esp32/{post_build.py => post_build.py.script} (100%) rename esphome/components/esp8266/{post_build.py => post_build.py.script} (100%) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 161803eaf4..8214886f8c 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -417,7 +417,7 @@ def copy_files(): ) dir = os.path.dirname(__file__) - post_build_file = os.path.join(dir, "post_build.py") + post_build_file = os.path.join(dir, "post_build.py.script") copy_file_if_changed( post_build_file, CORE.relative_build_path("post_build.py"), diff --git a/esphome/components/esp32/post_build.py b/esphome/components/esp32/post_build.py.script similarity index 100% rename from esphome/components/esp32/post_build.py rename to esphome/components/esp32/post_build.py.script diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 34a4a2fadb..7182042770 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -220,7 +220,7 @@ async def to_code(config): def copy_files(): dir = os.path.dirname(__file__) - post_build_file = os.path.join(dir, "post_build.py") + post_build_file = os.path.join(dir, "post_build.py.script") copy_file_if_changed( post_build_file, CORE.relative_build_path("post_build.py"), diff --git a/esphome/components/esp8266/post_build.py b/esphome/components/esp8266/post_build.py.script similarity index 100% rename from esphome/components/esp8266/post_build.py rename to esphome/components/esp8266/post_build.py.script From c5eba0451771620ee75d6a0f5cc757b205867ff7 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 16 Jan 2022 23:40:15 +0100 Subject: [PATCH 169/273] Remove deprecated attribute from virtual entity methods (#3056) --- .../components/binary_sensor/binary_sensor.h | 6 +++-- esphome/components/cover/cover.h | 6 ++++- esphome/components/sensor/sensor.h | 24 ++++++++++++------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index ecf68de74c..591f444387 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -74,8 +74,10 @@ class BinarySensor : public EntityBase { // ========== OVERRIDE METHODS ========== // (You'll only need this when creating your own custom binary sensor) - /// Get the default device class for this sensor, or empty string for no default. - ESPDEPRECATED("device_class() is deprecated, set property during config validation instead.", "2022.01") + /** Override this to set the default device class. + * + * @deprecated This method is deprecated, set the property during config validation instead. (2022.1) + */ virtual std::string device_class(); protected: diff --git a/esphome/components/cover/cover.h b/esphome/components/cover/cover.h index 779e4a2a46..1b5d3a8fa1 100644 --- a/esphome/components/cover/cover.h +++ b/esphome/components/cover/cover.h @@ -169,7 +169,11 @@ class Cover : public EntityBase { friend CoverCall; virtual void control(const CoverCall &call) = 0; - ESPDEPRECATED("device_class() is deprecated, set property during config validation instead.", "2022.01") + + /** Override this to set the default device class. + * + * @deprecated This method is deprecated, set the property during config validation instead. (2022.1) + */ virtual std::string device_class(); optional restore_state_(); diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index 794aecca95..d31fe9d834 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -150,20 +150,28 @@ class Sensor : public EntityBase { void internal_send_state_to_frontend(float state); protected: - /// Override this to set the default unit of measurement. - ESPDEPRECATED("unit_of_measurement() is deprecated, set property during config validation instead.", "2022.01") + /** Override this to set the default unit of measurement. + * + * @deprecated This method is deprecated, set the property during config validation instead. (2022.1) + */ virtual std::string unit_of_measurement(); // NOLINT - /// Override this to set the default accuracy in decimals. - ESPDEPRECATED("accuracy_decimals() is deprecated, set property during config validation instead.", "2022.01") + /** Override this to set the default accuracy in decimals. + * + * @deprecated This method is deprecated, set the property during config validation instead. (2022.1) + */ virtual int8_t accuracy_decimals(); // NOLINT - /// Override this to set the default device class. - ESPDEPRECATED("device_class() is deprecated, set property during config validation instead.", "2022.01") + /** Override this to set the default device class. + * + * @deprecated This method is deprecated, set the property during config validation instead. (2022.1) + */ virtual std::string device_class(); // NOLINT - /// Override this to set the default state class. - ESPDEPRECATED("state_class() is deprecated, set property during config validation instead.", "2022.01") + /** Override this to set the default state class. + * + * @deprecated This method is deprecated, set the property during config validation instead. (2022.1) + */ virtual StateClass state_class(); // NOLINT uint32_t hash_base() override; From 01b62a16c3eb081edf34a99196e0c7f17de0525f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 17 Jan 2022 12:31:44 +1300 Subject: [PATCH 170/273] Add number setting to web_server/rest_api (#3055) --- esphome/components/web_server/web_server.cpp | 45 +++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 4cc77da256..7413af67c4 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -236,7 +236,18 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) { #ifdef USE_NUMBER for (auto *obj : App.get_numbers()) if (this->include_internal_ || !obj->is_internal()) - write_row(stream, obj, "number", ""); + write_row(stream, obj, "number", "", [](AsyncResponseStream &stream, EntityBase *obj) { + number::Number *number = (number::Number *) obj; + stream.print(R"(traits.get_min_value()); + stream.print(R"(" max=")"); + stream.print(number->traits.get_max_value()); + stream.print(R"(" step=")"); + stream.print(number->traits.get_step()); + stream.print(R"(" value=")"); + stream.print(number->state); + stream.print(R"("/>)"); + }); #endif #ifdef USE_SELECT @@ -652,8 +663,29 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM for (auto *obj : App.get_numbers()) { if (obj->get_object_id() != match.id) continue; - std::string data = this->number_json(obj, obj->state); - request->send(200, "text/json", data.c_str()); + + if (request->method() == HTTP_GET) { + std::string data = this->number_json(obj, obj->state); + request->send(200, "text/json", data.c_str()); + return; + } + + if (match.method != "set") { + request->send(404); + return; + } + + auto call = obj->make_call(); + + if (request->hasParam("value")) { + String value = request->getParam("value")->value(); + optional value_f = parse_number(value.c_str()); + if (value_f.has_value()) + call.set_value(*value_f); + } + + this->defer([call]() mutable { call.perform(); }); + request->send(200); return; } request->send(404); @@ -661,9 +693,8 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM std::string WebServer::number_json(number::Number *obj, float value) { return json::build_json([obj, value](JsonObject root) { root["id"] = "number-" + obj->get_object_id(); - char buffer[64]; - snprintf(buffer, sizeof(buffer), "%f", value); - root["state"] = buffer; + std::string state = str_sprintf("%f", value); + root["state"] = state; root["value"] = value; }); } @@ -769,7 +800,7 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { #endif #ifdef USE_NUMBER - if (request->method() == HTTP_GET && match.domain == "number") + if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "number") return true; #endif From afbf9897154776c77444a788679847060da908e6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 17 Jan 2022 12:40:07 +1300 Subject: [PATCH 171/273] Bump version to 2022.1.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index daa61ebd9b..9005cc927a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.1.0b2" +__version__ = "2022.1.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 348f880e150d76aca5ff0c2a0dfd68fee32010b4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 17 Jan 2022 11:44:18 -0800 Subject: [PATCH 172/273] bump dashboard to 20220116.0 (#3061) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 05636da805..9add417bdf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.4 # When updating platformio, also update Dockerfile esptool==3.2 click==8.0.3 -esphome-dashboard==20220113.2 +esphome-dashboard==20220116.0 aioesphomeapi==10.6.0 zeroconf==0.37.0 From 7b03e07908497c98069ca864e13eaf81403a1101 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Mon, 17 Jan 2022 21:05:13 +0100 Subject: [PATCH 173/273] [modbus_controller] add missing skip_updates (#3063) --- esphome/components/modbus_controller/switch/__init__.py | 2 ++ esphome/components/modbus_controller/switch/modbus_switch.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/modbus_controller/switch/__init__.py b/esphome/components/modbus_controller/switch/__init__.py index 9858d45617..0dfbd83cb8 100644 --- a/esphome/components/modbus_controller/switch/__init__.py +++ b/esphome/components/modbus_controller/switch/__init__.py @@ -18,6 +18,7 @@ from ..const import ( CONF_FORCE_NEW_RANGE, CONF_MODBUS_CONTROLLER_ID, CONF_REGISTER_TYPE, + CONF_SKIP_UPDATES, CONF_USE_WRITE_MULTIPLE, CONF_WRITE_LAMBDA, ) @@ -53,6 +54,7 @@ async def to_code(config): config[CONF_ADDRESS], byte_offset, config[CONF_BITMASK], + config[CONF_SKIP_UPDATES], config[CONF_FORCE_NEW_RANGE], ) await cg.register_component(var, config) diff --git a/esphome/components/modbus_controller/switch/modbus_switch.h b/esphome/components/modbus_controller/switch/modbus_switch.h index 5ac2af01a1..6732c01eef 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.h +++ b/esphome/components/modbus_controller/switch/modbus_switch.h @@ -10,14 +10,14 @@ namespace modbus_controller { class ModbusSwitch : public Component, public switch_::Switch, public SensorItem { public: ModbusSwitch(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, - bool force_new_range) + uint8_t skip_updates, bool force_new_range) : Component(), switch_::Switch() { this->register_type = register_type; this->start_address = start_address; this->offset = offset; this->bitmask = bitmask; this->sensor_value_type = SensorValueType::BIT; - this->skip_updates = 0; + this->skip_updates = skip_updates; this->register_count = 1; if (register_type == ModbusRegisterType::HOLDING || register_type == ModbusRegisterType::COIL) { this->start_address += offset; From 869743a7427f33e8e61fa9c4429b040ab0bf2561 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Tue, 18 Jan 2022 02:29:57 +0100 Subject: [PATCH 174/273] Fail hard if no random bytes available for encryption (#3067) --- esphome/components/api/api_frame_helper.cpp | 8 +++++++- esphome/core/helpers.cpp | 7 +++---- esphome/core/helpers.h | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 094dd67e33..d9eadb2aaa 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -1,6 +1,7 @@ #include "api_frame_helper.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "proto.h" #include @@ -721,7 +722,12 @@ APIError APINoiseFrameHelper::shutdown(int how) { } extern "C" { // declare how noise generates random bytes (here with a good HWRNG based on the RF system) -void noise_rand_bytes(void *output, size_t len) { esphome::random_bytes(reinterpret_cast(output), len); } +void noise_rand_bytes(void *output, size_t len) { + if (!esphome::random_bytes(reinterpret_cast(output), len)) { + ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!"); + arch_restart(); + } +} } #endif // USE_API_NOISE diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index e15e3a8ea3..5f29abe579 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -287,13 +287,12 @@ uint32_t random_uint32() { #endif } float random_float() { return static_cast(random_uint32()) / static_cast(UINT32_MAX); } -void random_bytes(uint8_t *data, size_t len) { +bool random_bytes(uint8_t *data, size_t len) { #ifdef USE_ESP32 esp_fill_random(data, len); + return true; #elif defined(USE_ESP8266) - if (os_get_random(data, len) != 0) { - ESP_LOGE(TAG, "Failed to generate random bytes!"); - } + return os_get_random(data, len) == 0; #else #error "No random source available for this configuration." #endif diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index f071b4a814..c9a27a2fab 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -311,7 +311,7 @@ uint32_t random_uint32(); /// Return a random float between 0 and 1. float random_float(); /// Generate \p len number of random bytes. -void random_bytes(uint8_t *data, size_t len); +bool random_bytes(uint8_t *data, size_t len); ///@} From 72d60f30f79a89c1b2506a32a22832d6a91c3e19 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 18 Jan 2022 15:49:31 +1300 Subject: [PATCH 175/273] Bump version to 2022.1.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 9005cc927a..a94902d946 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.1.0b3" +__version__ = "2022.1.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 9296a078a7fea5c837b8c4c6b0d75096b7ed0e63 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 19 Jan 2022 16:08:27 +1300 Subject: [PATCH 176/273] Bump version to 2022.1.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a94902d946..637cf43928 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.1.0b4" +__version__ = "2022.1.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From c19458696e78327516ae1559e2336f07aeb30114 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 20 Jan 2022 08:33:13 +1300 Subject: [PATCH 177/273] Add *.py.script files to distributions (#3074) --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 0fe80762b3..a3126404f2 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,4 +4,5 @@ include requirements.txt include esphome/dashboard/templates/*.html recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE recursive-include esphome *.cpp *.h *.tcc +recursive-include esphome *.py.script recursive-include esphome LICENSE.txt From cd6f4fb93fbad3df8dc2539611931da33d7d323a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 20 Jan 2022 08:34:18 +1300 Subject: [PATCH 178/273] Bump version to 2022.1.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 637cf43928..a9f4f0900a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.1.0" +__version__ = "2022.1.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 76a238912b2b474c928e5ce0bceba1e05b15ebdd Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Wed, 19 Jan 2022 21:19:24 +0100 Subject: [PATCH 179/273] [modbus_controller] fix incorrect start address for number write (#3073) --- .../components/modbus_controller/number/modbus_number.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/modbus_controller/number/modbus_number.cpp b/esphome/components/modbus_controller/number/modbus_number.cpp index 5e977f5df4..a0e990d272 100644 --- a/esphome/components/modbus_controller/number/modbus_number.cpp +++ b/esphome/components/modbus_controller/number/modbus_number.cpp @@ -57,9 +57,11 @@ void ModbusNumber::control(float value) { // Create and send the write command ModbusCommandItem write_cmd; if (this->register_count == 1 && !this->use_write_multiple_) { - write_cmd = ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset, data[0]); + // since offset is in bytes and a register is 16 bits we get the start by adding offset/2 + write_cmd = + ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset / 2, data[0]); } else { - write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset, + write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset / 2, this->register_count, data); } // publish new value From 46af4cad6ef8cceddd6787a7ddc41e1c39dc63a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pl=C3=A1cido=20Revilla?= Date: Sat, 22 Jan 2022 12:04:36 -0800 Subject: [PATCH 180/273] Set the wrapped single light in light partition to internal (#3092) --- esphome/components/partition/light.py | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/components/partition/light.py b/esphome/components/partition/light.py index 822b7ac306..73cda2c926 100644 --- a/esphome/components/partition/light.py +++ b/esphome/components/partition/light.py @@ -104,7 +104,6 @@ async def to_code(config): ) light_state = cg.new_Pvariable(conf[CONF_LIGHT_ID], "", wrapper) await cg.register_component(light_state, conf) - cg.add(cg.App.register_light(light_state)) segments.append(AddressableSegment(light_state, 0, 1, False)) else: From bdb9546ca3d9743cd382ce1031b9ddff4dee14a1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 25 Jan 2022 09:11:20 +1300 Subject: [PATCH 181/273] Bump version to 2022.1.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a9f4f0900a..e97e0ac41a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.1.1" +__version__ = "2022.1.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 7479e0aadaeb4bd62294f9d82af1cfb48b077139 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 31 Jan 2022 10:58:27 +1300 Subject: [PATCH 182/273] Fix backwards string case helpers (#3126) --- esphome/components/dallas/dallas_component.cpp | 2 +- esphome/core/helpers.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/dallas/dallas_component.cpp b/esphome/components/dallas/dallas_component.cpp index 3610e79447..17412b08c9 100644 --- a/esphome/components/dallas/dallas_component.cpp +++ b/esphome/components/dallas/dallas_component.cpp @@ -235,7 +235,7 @@ float DallasTemperatureSensor::get_temp_c() { return temp / 128.0f; } -std::string DallasTemperatureSensor::unique_id() { return "dallas-" + str_upper_case(format_hex(this->address_)); } +std::string DallasTemperatureSensor::unique_id() { return "dallas-" + str_lower_case(format_hex(this->address_)); } } // namespace dallas } // namespace esphome diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 5f29abe579..11a2b1acb0 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -316,8 +316,8 @@ template std::string str_ctype_transform(const std::string &str) std::transform(str.begin(), str.end(), result.begin(), [](unsigned char ch) { return fn(ch); }); return result; } -std::string str_lower_case(const std::string &str) { return str_ctype_transform(str); } -std::string str_upper_case(const std::string &str) { return str_ctype_transform(str); } +std::string str_lower_case(const std::string &str) { return str_ctype_transform(str); } +std::string str_upper_case(const std::string &str) { return str_ctype_transform(str); } std::string str_snake_case(const std::string &str) { std::string result; result.resize(str.length()); From c7f091ab10effc0e386163e2832777109b7b89d0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 2 Feb 2022 22:16:24 +1300 Subject: [PATCH 183/273] Bump version to 2022.1.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e97e0ac41a..243b3d84b1 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.1.2" +__version__ = "2022.1.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 9ad84150aa35673fbc63c0168d0601d4967476ff Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 8 Feb 2022 09:21:52 +0100 Subject: [PATCH 184/273] Enable mDNS during OTA safe mode (#3146) --- esphome/components/mdns/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index b95469d9da..b5be153d5a 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -1,7 +1,7 @@ from esphome.const import CONF_ID import esphome.codegen as cg import esphome.config_validation as cv -from esphome.core import CORE +from esphome.core import CORE, coroutine_with_priority CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] @@ -29,6 +29,7 @@ CONFIG_SCHEMA = cv.All( ) +@coroutine_with_priority(55.0) async def to_code(config): if CORE.using_arduino: if CORE.is_esp32: From 2b5dce52327094cda8d209e7b0cc0ec0ce799df3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 9 Feb 2022 11:42:00 +1300 Subject: [PATCH 185/273] Try fix canbus config validation (#3173) --- esphome/components/canbus/__init__.py | 32 +++++++++++++-------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/esphome/components/canbus/__init__.py b/esphome/components/canbus/__init__.py index 3a3cece579..808b31d1d2 100644 --- a/esphome/components/canbus/__init__.py +++ b/esphome/components/canbus/__init__.py @@ -14,10 +14,14 @@ CONF_BIT_RATE = "bit_rate" CONF_ON_FRAME = "on_frame" -def validate_id(id_value, id_ext): - if not id_ext: - if id_value > 0x7FF: - raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)") +def validate_id(config): + if CONF_CAN_ID in config: + id_value = config[CONF_CAN_ID] + id_ext = config[CONF_USE_EXTENDED_ID] + if not id_ext: + if id_value > 0x7FF: + raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)") + return config def validate_raw_data(value): @@ -67,23 +71,18 @@ CANBUS_SCHEMA = cv.Schema( cv.Optional(CONF_ON_FRAME): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger), - cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), + cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, - cv.Optional(CONF_ON_FRAME): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger), - cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), - cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, - } - ), - } + }, + validate_id, ), - } + }, ).extend(cv.COMPONENT_SCHEMA) +CANBUS_SCHEMA.add_extra(validate_id) + async def setup_canbus_core_(var, config): - validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID]) await cg.register_component(var, config) cg.add(var.set_can_id([config[CONF_CAN_ID]])) cg.add(var.set_use_extended_id([config[CONF_USE_EXTENDED_ID]])) @@ -92,7 +91,6 @@ async def setup_canbus_core_(var, config): for conf in config.get(CONF_ON_FRAME, []): can_id = conf[CONF_CAN_ID] ext_id = conf[CONF_USE_EXTENDED_ID] - validate_id(can_id, ext_id) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id) await cg.register_component(trigger, conf) await automation.build_automation( @@ -117,11 +115,11 @@ async def register_canbus(var, config): cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, cv.Required(CONF_DATA): cv.templatable(validate_raw_data), }, + validate_id, key=CONF_DATA, ), ) async def canbus_action_to_code(config, action_id, template_arg, args): - validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID]) var = cg.new_Pvariable(action_id, template_arg) await cg.register_parented(var, config[CONF_CANBUS_ID]) From 53d3718028c9f3257f12a95d8a154221bda3b183 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 9 Feb 2022 11:54:41 +1300 Subject: [PATCH 186/273] Bump version to 2022.1.4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 243b3d84b1..566f79a390 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.1.3" +__version__ = "2022.1.4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 71a438e2cb00e2f6b9fac735800c29b2012010b2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 9 Feb 2022 23:47:36 +1300 Subject: [PATCH 187/273] Bump version to 2022.2.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index dcd6fea31f..d3b78ca44a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.2.0-dev" +__version__ = "2022.2.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 8dcc9d6b66ac90ec9945dd083d5eab8d5ba63148 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Feb 2022 20:13:02 +0100 Subject: [PATCH 188/273] Bump aioesphomeapi from 10.8.1 to 10.8.2 (#3182) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a33461b79b..0c408f90b5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ platformio==5.2.4 # When updating platformio, also update Dockerfile esptool==3.2 click==8.0.3 esphome-dashboard==20220209.0 -aioesphomeapi==10.8.1 +aioesphomeapi==10.8.2 zeroconf==0.37.0 # esp-idf requires this, but doesn't bundle it by default From f376a39e55c724c5dbda7eb2cc0b8380249678cf Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 10 Feb 2022 11:12:05 +1300 Subject: [PATCH 189/273] Clamp rotary_encoder restored value to min and max (#3184) --- esphome/components/rotary_encoder/rotary_encoder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/rotary_encoder/rotary_encoder.cpp b/esphome/components/rotary_encoder/rotary_encoder.cpp index c5227b41a7..c5e9cec596 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.cpp +++ b/esphome/components/rotary_encoder/rotary_encoder.cpp @@ -138,6 +138,8 @@ void RotaryEncoderSensor::setup() { initial_value = 0; break; } + initial_value = clamp(initial_value, this->store_.min_value, this->store_.max_value); + this->store_.counter = initial_value; this->store_.last_read = initial_value; From dd554bcdf4fe0041ea144d44ce4e2313c229f6a2 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 11 Feb 2022 09:06:00 +0100 Subject: [PATCH 190/273] Make generating combined binary output verbose (#3127) --- esphome/components/esp32/post_build.py.script | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/esphome/components/esp32/post_build.py.script b/esphome/components/esp32/post_build.py.script index 7feaf9e8e5..2bb1a6c3d6 100644 --- a/esphome/components/esp32/post_build.py.script +++ b/esphome/components/esp32/post_build.py.script @@ -1,13 +1,16 @@ # Source https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005864664 import esptool +from SCons.Script import ARGUMENTS # pylint: disable=E0602 Import("env") # noqa def esp32_create_combined_bin(source, target, env): - print("Generating combined binary for serial flashing") + verbose = bool(int(ARGUMENTS.get("PIOVERBOSE", "0"))) + if verbose: + print("Generating combined binary for serial flashing") app_offset = 0x10000 new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") @@ -24,18 +27,21 @@ def esp32_create_combined_bin(source, target, env): "--flash_size", flash_size, ] - print(" Offset | File") + if verbose: + print(" Offset | File") for section in sections: sect_adr, sect_file = section.split(" ", 1) - print(f" - {sect_adr} | {sect_file}") + if verbose: + print(f" - {sect_adr} | {sect_file}") cmd += [sect_adr, sect_file] - print(f" - {hex(app_offset)} | {firmware_name}") cmd += [hex(app_offset), firmware_name] - print() - print(f"Using esptool.py arguments: {' '.join(cmd)}") - print() + if verbose: + print(f" - {hex(app_offset)} | {firmware_name}") + print() + print(f"Using esptool.py arguments: {' '.join(cmd)}") + print() esptool.main(cmd) From dcc80f9032c6d30bdf6b22dfbd76ee7b27b33d7f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 15 Feb 2022 11:57:47 +1300 Subject: [PATCH 191/273] Allow framework version validator to be maximum version (#3197) --- esphome/components/fastled_clockless/light.py | 7 +++- esphome/components/fastled_spi/light.py | 7 +++- esphome/config_validation.py | 35 ++++++++++++++----- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/esphome/components/fastled_clockless/light.py b/esphome/components/fastled_clockless/light.py index acf9488ae3..dc456d4959 100644 --- a/esphome/components/fastled_clockless/light.py +++ b/esphome/components/fastled_clockless/light.py @@ -49,7 +49,12 @@ CONFIG_SCHEMA = cv.All( } ), _validate, - cv.only_with_arduino, + cv.require_framework_version( + esp8266_arduino=cv.Version(2, 7, 4), + esp32_arduino=cv.Version(99, 0, 0), + max_version=True, + extra_message="Please see note on documentation for FastLED", + ), ) diff --git a/esphome/components/fastled_spi/light.py b/esphome/components/fastled_spi/light.py index a729fc015a..b3ce1722ee 100644 --- a/esphome/components/fastled_spi/light.py +++ b/esphome/components/fastled_spi/light.py @@ -33,7 +33,12 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_DATA_RATE): cv.frequency, } ), - cv.only_with_arduino, + cv.require_framework_version( + esp8266_arduino=cv.Version(2, 7, 4), + esp32_arduino=cv.Version(99, 0, 0), + max_version=True, + extra_message="Please see note on documentation for FastLED", + ), ) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 2e7a4e5677..8e1c63a54e 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1713,30 +1713,49 @@ def require_framework_version( esp_idf=None, esp32_arduino=None, esp8266_arduino=None, + max_version=False, + extra_message=None, ): def validator(value): core_data = CORE.data[KEY_CORE] framework = core_data[KEY_TARGET_FRAMEWORK] if framework == "esp-idf": if esp_idf is None: - raise Invalid("This feature is incompatible with esp-idf") + msg = "This feature is incompatible with esp-idf" + if extra_message: + msg += f". {extra_message}" + raise Invalid(msg) required = esp_idf elif CORE.is_esp32 and framework == "arduino": if esp32_arduino is None: - raise Invalid( - "This feature is incompatible with ESP32 using arduino framework" - ) + msg = "This feature is incompatible with ESP32 using arduino framework" + if extra_message: + msg += f". {extra_message}" + raise Invalid(msg) required = esp32_arduino elif CORE.is_esp8266 and framework == "arduino": if esp8266_arduino is None: - raise Invalid("This feature is incompatible with ESP8266") + msg = "This feature is incompatible with ESP8266" + if extra_message: + msg += f". {extra_message}" + raise Invalid(msg) required = esp8266_arduino else: raise NotImplementedError + + if max_version: + if core_data[KEY_FRAMEWORK_VERSION] > required: + msg = f"This feature requires framework version {required} or lower" + if extra_message: + msg += f". {extra_message}" + raise Invalid(msg) + return value + if core_data[KEY_FRAMEWORK_VERSION] < required: - raise Invalid( - f"This feature requires at least framework version {required}" - ) + msg = f"This feature requires at least framework version {required}" + if extra_message: + msg += f". {extra_message}" + raise Invalid(msg) return value return validator From bb6b77bd98a71e983166dcceae042873f5e51456 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 15 Feb 2022 12:00:12 +1300 Subject: [PATCH 192/273] Bump version to 2022.2.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index d3b78ca44a..580826c954 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.2.0b1" +__version__ = "2022.2.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 16dc7762f9935a4714925de39a3323c2dc880b82 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Tue, 15 Feb 2022 20:54:21 +0100 Subject: [PATCH 193/273] Fix strlcpy() uses to make long SSIDs and passwords work (#3199) Co-authored-by: Maurice Makaay --- esphome/components/wifi/wifi_component_esp32_arduino.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index e1332e3181..83381f3424 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -161,8 +161,8 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - strlcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid)); - strlcpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password)); + strncpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid)); + strncpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password)); // The weakest authmode to accept in the fast scan mode if (ap.get_password().empty()) { @@ -709,7 +709,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - strlcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid)); + strncpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid)); conf.ap.channel = ap.get_channel().value_or(1); conf.ap.ssid_hidden = ap.get_ssid().size(); conf.ap.max_connection = 5; @@ -720,7 +720,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.ap.password = 0; } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; - strlcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.ssid)); + strncpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.ssid)); } #if ESP_IDF_VERSION_MAJOR >= 4 From 6ddad6b299db67d937f315f1cfcf2b4d9c40f570 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 16 Feb 2022 09:11:46 +1300 Subject: [PATCH 194/273] Update HA addon token (#3200) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 02a55494e9..f5281c2fb7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -143,7 +143,7 @@ jobs: needs: [deploy-docker] steps: - env: - TOKEN: ${{ secrets.DEPLOY_HASSIO_TOKEN }} + TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }} run: | TAG="${GITHUB_REF#refs/tags/}" curl \ From ec7a79049a37af82b0c01f20e0cac0ec2c94f930 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 16 Feb 2022 09:45:05 +1300 Subject: [PATCH 195/273] Bump version to 2022.2.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 580826c954..8bb99752aa 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.2.0b2" +__version__ = "2022.2.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 69002fb1e6cdd3a6d79f2be389d0ec6fe78bfe55 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 16 Feb 2022 20:13:14 +1300 Subject: [PATCH 196/273] Bump version to 2022.2.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 8bb99752aa..70e27374ae 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.2.0b3" +__version__ = "2022.2.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 1fb214165bb8297b1661d693e5ac2841fd8f8eb5 Mon Sep 17 00:00:00 2001 From: Stewart Date: Wed, 16 Feb 2022 15:50:10 +0000 Subject: [PATCH 197/273] Fix missed ARDUINO_VERSION_CODE to USE_ARDUINO_VERSION_CODE changes (#3206) Co-authored-by: Stewart Morgan --- esphome/components/debug/debug_component.cpp | 6 +++--- esphome/components/debug/debug_component.h | 4 ++-- esphome/components/ota/ota_backend_arduino_esp8266.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index a2697084bd..97d5aeb8a8 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -53,9 +53,9 @@ void DebugComponent::dump_config() { #ifdef USE_SENSOR LOG_SENSOR(" ", "Free space on heap", this->free_sensor_); LOG_SENSOR(" ", "Largest free heap block", this->block_sensor_); -#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) +#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) LOG_SENSOR(" ", "Heap fragmentation", this->fragmentation_sensor_); -#endif // defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) +#endif // defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) #endif // USE_SENSOR ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION); @@ -316,7 +316,7 @@ void DebugComponent::update() { #endif } -#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) +#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) if (this->fragmentation_sensor_ != nullptr) { // NOLINTNEXTLINE(readability-static-accessed-through-instance) this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation()); diff --git a/esphome/components/debug/debug_component.h b/esphome/components/debug/debug_component.h index f966b4fafc..4dc1659616 100644 --- a/esphome/components/debug/debug_component.h +++ b/esphome/components/debug/debug_component.h @@ -28,7 +28,7 @@ class DebugComponent : public PollingComponent { #ifdef USE_SENSOR void set_free_sensor(sensor::Sensor *free_sensor) { free_sensor_ = free_sensor; } void set_block_sensor(sensor::Sensor *block_sensor) { block_sensor_ = block_sensor; } -#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) +#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) void set_fragmentation_sensor(sensor::Sensor *fragmentation_sensor) { fragmentation_sensor_ = fragmentation_sensor; } #endif void set_loop_time_sensor(sensor::Sensor *loop_time_sensor) { loop_time_sensor_ = loop_time_sensor; } @@ -42,7 +42,7 @@ class DebugComponent : public PollingComponent { sensor::Sensor *free_sensor_{nullptr}; sensor::Sensor *block_sensor_{nullptr}; -#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) +#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) sensor::Sensor *fragmentation_sensor_{nullptr}; #endif sensor::Sensor *loop_time_sensor_{nullptr}; diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.h b/esphome/components/ota/ota_backend_arduino_esp8266.h index 329f2cf0f2..7937c665b0 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.h +++ b/esphome/components/ota/ota_backend_arduino_esp8266.h @@ -17,7 +17,7 @@ class ArduinoESP8266OTABackend : public OTABackend { OTAResponseTypes write(uint8_t *data, size_t len) override; OTAResponseTypes end() override; void abort() override; -#if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) +#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) bool supports_compression() override { return true; } #else bool supports_compression() override { return false; } From dc54b177785f2b49f852e4cbd9a0b13ee128fcfc Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 17 Feb 2022 07:36:14 +1300 Subject: [PATCH 198/273] Bump version to 2022.2.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 70e27374ae..7d4fc234db 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.2.0" +__version__ = "2022.2.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From b9398897c1f11eb7ddef665a8cf841099b990399 Mon Sep 17 00:00:00 2001 From: Stewart Date: Thu, 17 Feb 2022 00:53:26 +0000 Subject: [PATCH 199/273] Set entity-category to diagnostic for debug component (#3209) Co-authored-by: Stewart Morgan Co-authored-by: root --- esphome/components/debug/sensor.py | 29 +++++++++++++++++++++---- esphome/components/debug/text_sensor.py | 6 +++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/esphome/components/debug/sensor.py b/esphome/components/debug/sensor.py index deea6fd5ed..f7ea07d138 100644 --- a/esphome/components/debug/sensor.py +++ b/esphome/components/debug/sensor.py @@ -6,6 +6,7 @@ from esphome.const import ( CONF_FRAGMENTATION, CONF_BLOCK, CONF_LOOP_TIME, + ENTITY_CATEGORY_DIAGNOSTIC, UNIT_MILLISECOND, UNIT_PERCENT, UNIT_BYTES, @@ -18,14 +19,34 @@ DEPENDENCIES = ["debug"] CONFIG_SCHEMA = { cv.GenerateID(CONF_DEBUG_ID): cv.use_id(DebugComponent), - cv.Optional(CONF_FREE): sensor.sensor_schema(UNIT_BYTES, ICON_COUNTER, 0), - cv.Optional(CONF_BLOCK): sensor.sensor_schema(UNIT_BYTES, ICON_COUNTER, 0), + cv.Optional(CONF_FREE): sensor.sensor_schema( + unit_of_measurement=UNIT_BYTES, + icon=ICON_COUNTER, + accuracy_decimals=0, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_BLOCK): sensor.sensor_schema( + unit_of_measurement=UNIT_BYTES, + icon=ICON_COUNTER, + accuracy_decimals=0, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), cv.Optional(CONF_FRAGMENTATION): cv.All( cv.only_on_esp8266, cv.require_framework_version(esp8266_arduino=cv.Version(2, 5, 2)), - sensor.sensor_schema(UNIT_PERCENT, ICON_COUNTER, 1), + sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_COUNTER, + accuracy_decimals=1, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + ), + cv.Optional(CONF_LOOP_TIME): sensor.sensor_schema( + unit_of_measurement=UNIT_MILLISECOND, + icon=ICON_TIMER, + accuracy_decimals=0, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, ), - cv.Optional(CONF_LOOP_TIME): sensor.sensor_schema(UNIT_MILLISECOND, ICON_TIMER, 0), } diff --git a/esphome/components/debug/text_sensor.py b/esphome/components/debug/text_sensor.py index f8d1016fbf..11e6354f57 100644 --- a/esphome/components/debug/text_sensor.py +++ b/esphome/components/debug/text_sensor.py @@ -1,7 +1,7 @@ from esphome.components import text_sensor import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_DEVICE +from esphome.const import CONF_DEVICE, ENTITY_CATEGORY_DIAGNOSTIC from . import CONF_DEBUG_ID, DebugComponent @@ -11,7 +11,9 @@ DEPENDENCIES = ["debug"] CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(CONF_DEBUG_ID): cv.use_id(DebugComponent), - cv.Optional(CONF_DEVICE): text_sensor.text_sensor_schema(), + cv.Optional(CONF_DEVICE): text_sensor.text_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ), } ) From 7dcc4d030bb4eb2997eef44453981e0d1515c45c Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 17 Feb 2022 11:56:14 +0100 Subject: [PATCH 200/273] Fix platformio docker version mismstch (#3215) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 5d9decbf1b..6735037c1e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -43,7 +43,7 @@ RUN \ # Ubuntu python3-pip is missing wheel pip3 install --no-cache-dir \ wheel==0.37.1 \ - platformio==5.2.4 \ + platformio==5.2.5 \ # Change some platformio settings && platformio settings set enable_telemetry No \ && platformio settings set check_libraries_interval 1000000 \ From 8886b7e1417823bd12e1f9e37d07ee0c6a28aa1a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 18 Feb 2022 10:11:22 +1300 Subject: [PATCH 201/273] Add LONG LONG flag for arduinojson (#3212) --- esphome/components/json/json_util.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/json/json_util.h b/esphome/components/json/json_util.h index 57fe6107d8..2299a4cfed 100644 --- a/esphome/components/json/json_util.h +++ b/esphome/components/json/json_util.h @@ -4,9 +4,10 @@ #include "esphome/core/helpers.h" -#undef ARDUINOJSON_ENABLE_STD_STRING #define ARDUINOJSON_ENABLE_STD_STRING 1 // NOLINT +#define ARDUINOJSON_USE_LONG_LONG 1 // NOLINT + #include namespace esphome { From 83b7181bcb22262dee690a591099aa036cd06b20 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 18 Feb 2022 10:16:11 +1300 Subject: [PATCH 202/273] Bump version to 2022.2.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 7d4fc234db..b501428f9e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.2.1" +__version__ = "2022.2.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From ccc2fbfd67aebf4179c56e8e0e4e84166aaa15d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Feb 2022 13:57:46 +0100 Subject: [PATCH 203/273] Bump platformio from 5.2.4 to 5.2.5 (#3188) * Bump platformio from 5.2.4 to 5.2.5 Bumps [platformio](https://github.com/platformio/platformio) from 5.2.4 to 5.2.5. - [Release notes](https://github.com/platformio/platformio/releases) - [Changelog](https://github.com/platformio/platformio-core/blob/develop/HISTORY.rst) - [Commits](https://github.com/platformio/platformio/commits) --- updated-dependencies: - dependency-name: platformio dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Update requirements.txt Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Otto Winter --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0c408f90b5..acbf1d9984 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,12 +6,12 @@ tornado==6.1 tzlocal==4.1 # from time tzdata>=2021.1 # from time pyserial==3.5 -platformio==5.2.4 # When updating platformio, also update Dockerfile +platformio==5.2.5 # When updating platformio, also update Dockerfile esptool==3.2 click==8.0.3 esphome-dashboard==20220209.0 aioesphomeapi==10.8.2 -zeroconf==0.37.0 +zeroconf==0.38.3 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From 5764c988afab26b6b9c6249017f0b6575c8672e4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 18 Feb 2022 11:51:56 +1300 Subject: [PATCH 204/273] Bump version to 2022.2.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index b501428f9e..0e8695adef 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.2.2" +__version__ = "2022.2.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 0c4de2bc97ebc75130aa04f391b202793ebba9bb Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 19 Feb 2022 14:11:01 +0100 Subject: [PATCH 205/273] Publish NAN when dallas conversion failed (#3227) --- esphome/components/dallas/dallas_component.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/dallas/dallas_component.cpp b/esphome/components/dallas/dallas_component.cpp index 1eed2ebf78..6240983798 100644 --- a/esphome/components/dallas/dallas_component.cpp +++ b/esphome/components/dallas/dallas_component.cpp @@ -110,6 +110,9 @@ void DallasComponent::update() { if (!result) { ESP_LOGE(TAG, "Requesting conversion failed"); this->status_set_warning(); + for (auto *sensor : this->sensors_) { + sensor->publish_state(NAN); + } return; } From 616c787e377f12c83bcaf7f252d329ba4715581a Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 19 Feb 2022 14:11:45 +0100 Subject: [PATCH 206/273] Fix ESP8266 climate memaccess warning (#3226) --- esphome/components/climate/climate.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index ebea20ed1f..65b5ef4eb4 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -1,4 +1,5 @@ #include "climate.h" +#include "esphome/core/macros.h" namespace esphome { namespace climate { @@ -326,14 +327,17 @@ optional Climate::restore_state_() { return recovered; } void Climate::save_state_() { -#if defined(USE_ESP_IDF) && !defined(CLANG_TIDY) +#if (defined(USE_ESP_IDF) || (defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0))) && \ + !defined(CLANG_TIDY) #pragma GCC diagnostic ignored "-Wclass-memaccess" +#define TEMP_IGNORE_MEMACCESS #endif ClimateDeviceRestoreState state{}; // initialize as zero to prevent random data on stack triggering erase memset(&state, 0, sizeof(ClimateDeviceRestoreState)); -#if USE_ESP_IDF && !defined(CLANG_TIDY) +#ifdef TEMP_IGNORE_MEMACCESS #pragma GCC diagnostic pop +#undef TEMP_IGNORE_MEMACCESS #endif state.mode = this->mode; From 1d0395d1c7cfc546f41b4b9116df4b2b7b482f1d Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 19 Feb 2022 15:09:17 +0100 Subject: [PATCH 207/273] Improve ESP8266 iram usage (#3223) --- esphome/core/scheduler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 7f0ed0b17c..56f823556b 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -116,7 +116,7 @@ optional HOT Scheduler::next_schedule_in() { return 0; return next_time - now; } -void IRAM_ATTR HOT Scheduler::call() { +void HOT Scheduler::call() { const uint32_t now = this->millis_(); this->process_to_add(); From b881bc071ecd0e1493441ba981b00e7101ed3c6d Mon Sep 17 00:00:00 2001 From: Tyler Bules Date: Sat, 19 Feb 2022 09:13:48 -0500 Subject: [PATCH 208/273] ESP32-C3 deep sleep fix (#3066) --- esphome/components/deep_sleep/__init__.py | 40 ++++++++++++++++++- .../deep_sleep/deep_sleep_component.cpp | 14 ++++++- .../deep_sleep/deep_sleep_component.h | 5 ++- tests/test1.yaml | 2 +- tests/test2.yaml | 2 +- 5 files changed, 58 insertions(+), 5 deletions(-) diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index ba4c2c0d7e..2a74d0c1bb 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -11,9 +11,39 @@ from esphome.const import ( CONF_WAKEUP_PIN, ) +from esphome.components.esp32 import get_esp32_variant +from esphome.components.esp32.const import ( + VARIANT_ESP32, + VARIANT_ESP32C3, +) + +WAKEUP_PINS = { + VARIANT_ESP32: [ + 0, + 2, + 4, + 12, + 13, + 14, + 15, + 25, + 26, + 27, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + ], + VARIANT_ESP32C3: [0, 1, 2, 3, 4, 5], +} + def validate_pin_number(value): - valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 37, 38, 39] + valid_pins = WAKEUP_PINS.get(get_esp32_variant(), WAKEUP_PINS[VARIANT_ESP32]) if value[CONF_NUMBER] not in valid_pins: raise cv.Invalid( f"Only pins {', '.join(str(x) for x in valid_pins)} support wakeup" @@ -21,6 +51,14 @@ def validate_pin_number(value): return value +def validate_config(config): + if get_esp32_variant() == VARIANT_ESP32C3 and CONF_ESP32_EXT1_WAKEUP in config: + raise cv.Invalid("ESP32-C3 does not support wakeup from touch.") + if get_esp32_variant() == VARIANT_ESP32C3 and CONF_TOUCH_WAKEUP in config: + raise cv.Invalid("ESP32-C3 does not support wakeup from ext1") + return config + + deep_sleep_ns = cg.esphome_ns.namespace("deep_sleep") DeepSleepComponent = deep_sleep_ns.class_("DeepSleepComponent", cg.Component) EnterDeepSleepAction = deep_sleep_ns.class_("EnterDeepSleepAction", automation.Action) diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index 7774014d3d..82751b538b 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -104,7 +104,7 @@ void DeepSleepComponent::begin_sleep(bool manual) { App.run_safe_shutdown_hooks(); -#ifdef USE_ESP32 +#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) if (this->sleep_duration_.has_value()) esp_sleep_enable_timer_wakeup(*this->sleep_duration_); if (this->wakeup_pin_ != nullptr) { @@ -126,6 +126,18 @@ void DeepSleepComponent::begin_sleep(bool manual) { esp_deep_sleep_start(); #endif +#ifdef USE_ESP32_VARIANT_ESP32C3 + if (this->sleep_duration_.has_value()) + esp_sleep_enable_timer_wakeup(*this->sleep_duration_); + if (this->wakeup_pin_ != nullptr) { + bool level = !this->wakeup_pin_->is_inverted(); + if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) { + level = !level; + } + esp_deep_sleep_enable_gpio_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level); + } +#endif + #ifdef USE_ESP8266 ESP.deepSleep(*this->sleep_duration_); // NOLINT(readability-static-accessed-through-instance) #endif diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index 59df199a9f..057d992427 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -57,13 +57,16 @@ class DeepSleepComponent : public Component { public: /// Set the duration in ms the component should sleep once it's in deep sleep mode. void set_sleep_duration(uint32_t time_ms); -#ifdef USE_ESP32 +#if defined(USE_ESP32) /** Set the pin to wake up to on the ESP32 once it's in deep sleep mode. * Use the inverted property to set the wakeup level. */ void set_wakeup_pin(InternalGPIOPin *pin) { this->wakeup_pin_ = pin; } void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode); +#endif + +#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) void set_ext1_wakeup(Ext1Wakeup ext1_wakeup); diff --git a/tests/test1.yaml b/tests/test1.yaml index d8fea223dc..b9799bdb36 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -262,7 +262,7 @@ power_supply: deep_sleep: run_duration: 20s sleep_duration: 50s - wakeup_pin: GPIO39 + wakeup_pin: GPIO2 wakeup_pin_mode: INVERT_WAKEUP ads1115: diff --git a/tests/test2.yaml b/tests/test2.yaml index 2a122b971f..76b9775c54 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -60,7 +60,7 @@ deep_sleep: gpio_wakeup_reason: 10s touch_wakeup_reason: 15s sleep_duration: 50s - wakeup_pin: GPIO39 + wakeup_pin: GPIO2 wakeup_pin_mode: INVERT_WAKEUP as3935_i2c: From e73d47918f9b3c070af75919a0418d598d77273e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 21 Feb 2022 09:58:53 +1300 Subject: [PATCH 209/273] Fix lilygo touchscreen rotation (#3221) --- .../touchscreen/lilygo_t5_47_touchscreen.cpp | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp index b5cf63980b..b92d7d6f10 100644 --- a/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +++ b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp @@ -113,8 +113,27 @@ void LilygoT547Touchscreen::loop() { if (tp.state == 0x06) tp.state = 0x07; - tp.y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F)); - tp.x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F)); + uint16_t y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F)); + uint16_t x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F)); + + switch (this->rotation_) { + case ROTATE_0_DEGREES: + tp.y = this->display_height_ - y; + tp.x = x; + break; + case ROTATE_90_DEGREES: + tp.x = this->display_height_ - y; + tp.y = this->display_width_ - x; + break; + case ROTATE_180_DEGREES: + tp.y = y; + tp.x = this->display_width_ - x; + break; + case ROTATE_270_DEGREES: + tp.x = y; + tp.y = x; + break; + } this->defer([this, tp]() { this->send_touch_(tp); }); } @@ -122,8 +141,28 @@ void LilygoT547Touchscreen::loop() { TouchPoint tp; tp.id = (buffer[0] >> 4) & 0x0F; tp.state = 0x06; - tp.y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F)); - tp.x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F)); + + uint16_t y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F)); + uint16_t x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F)); + + switch (this->rotation_) { + case ROTATE_0_DEGREES: + tp.y = this->display_height_ - y; + tp.x = x; + break; + case ROTATE_90_DEGREES: + tp.x = this->display_height_ - y; + tp.y = this->display_width_ - x; + break; + case ROTATE_180_DEGREES: + tp.y = y; + tp.x = this->display_width_ - x; + break; + case ROTATE_270_DEGREES: + tp.x = y; + tp.y = x; + break; + } this->defer([this, tp]() { this->send_touch_(tp); }); } From dbd4e927d8987c319cc580df913e1d1f1c5da44d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 21 Feb 2022 10:01:00 +1300 Subject: [PATCH 210/273] Fix fatal erroring in addon startup script (#3244) --- docker/ha-addon-rootfs/etc/cont-init.d/10-requirements.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/ha-addon-rootfs/etc/cont-init.d/10-requirements.sh b/docker/ha-addon-rootfs/etc/cont-init.d/10-requirements.sh index 9d49c2b4dd..544787d568 100755 --- a/docker/ha-addon-rootfs/etc/cont-init.d/10-requirements.sh +++ b/docker/ha-addon-rootfs/etc/cont-init.d/10-requirements.sh @@ -7,12 +7,12 @@ # Check SSL requirements, if enabled if bashio::config.true 'ssl'; then if ! bashio::config.has_value 'certfile'; then - bashio::fatal 'SSL is enabled, but no certfile was specified.' + bashio::log.fatal 'SSL is enabled, but no certfile was specified.' bashio::exit.nok fi if ! bashio::config.has_value 'keyfile'; then - bashio::fatal 'SSL is enabled, but no keyfile was specified' + bashio::log.fatal 'SSL is enabled, but no keyfile was specified' bashio::exit.nok fi From 2748e6ba2910d5653b45e95fbadcec64591fdbaf Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 21 Feb 2022 10:17:25 +1300 Subject: [PATCH 211/273] Bump version to 2022.2.4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 0e8695adef..015a598ded 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.2.3" +__version__ = "2022.2.4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 3d0899aa58859e91b37507be391c0d4845dac005 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Mon, 21 Feb 2022 02:05:13 +0100 Subject: [PATCH 212/273] Respect ESPHOME_USE_SUBPROCESS in esp32 post_build script (#3246) --- esphome/components/esp32/post_build.py.script | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32/post_build.py.script b/esphome/components/esp32/post_build.py.script index 2bb1a6c3d6..406516a102 100644 --- a/esphome/components/esp32/post_build.py.script +++ b/esphome/components/esp32/post_build.py.script @@ -1,6 +1,10 @@ # Source https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005864664 -import esptool +import os +if os.environ.get("ESPHOME_USE_SUBPROCESS") is None: + import esptool +else: + import subprocess from SCons.Script import ARGUMENTS # pylint: disable=E0602 @@ -42,8 +46,11 @@ def esp32_create_combined_bin(source, target, env): print() print(f"Using esptool.py arguments: {' '.join(cmd)}") print() - esptool.main(cmd) + if os.environ.get("ESPHOME_USE_SUBPROCESS") is None: + esptool.main(cmd) + else: + subprocess.run(["esptool.py", *cmd]) # pylint: disable=E0602 env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa From 95acf1906703d5658dd9cbae8ef61afe74ac737a Mon Sep 17 00:00:00 2001 From: Nicholas Peters Date: Mon, 21 Feb 2022 19:53:24 -0500 Subject: [PATCH 213/273] Fix regression caused by TSL2591 auto gain (#3249) --- esphome/components/tsl2591/tsl2591.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/esphome/components/tsl2591/tsl2591.cpp b/esphome/components/tsl2591/tsl2591.cpp index 8a540c5f13..f8c59a53c6 100644 --- a/esphome/components/tsl2591/tsl2591.cpp +++ b/esphome/components/tsl2591/tsl2591.cpp @@ -43,16 +43,34 @@ void TSL2591Component::disable_if_power_saving_() { } void TSL2591Component::setup() { - if (this->component_gain_ == TSL2591_CGAIN_AUTO) - this->gain_ = TSL2591_GAIN_MED; + switch (this->component_gain_) { + case TSL2591_CGAIN_LOW: + this->gain_ = TSL2591_GAIN_LOW; + break; + case TSL2591_CGAIN_MED: + this->gain_ = TSL2591_GAIN_MED; + break; + case TSL2591_CGAIN_HIGH: + this->gain_ = TSL2591_GAIN_HIGH; + break; + case TSL2591_CGAIN_MAX: + this->gain_ = TSL2591_GAIN_MAX; + break; + case TSL2591_CGAIN_AUTO: + this->gain_ = TSL2591_GAIN_MED; + break; + } + uint8_t address = this->address_; ESP_LOGI(TAG, "Setting up TSL2591 sensor at I2C address 0x%02X", address); + uint8_t id; if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_DEVICE_ID, &id)) { ESP_LOGE(TAG, "Failed I2C read during setup()"); this->mark_failed(); return; } + if (id != 0x50) { ESP_LOGE(TAG, "Could not find the TSL2591 sensor. The ID register of the device at address 0x%02X reported 0x%02X " @@ -61,6 +79,7 @@ void TSL2591Component::setup() { this->mark_failed(); return; } + this->set_integration_time_and_gain(this->integration_time_, this->gain_); this->disable_if_power_saving_(); } From 97aca8e54c62e514c7cd042c1f3c2f7c8f789a06 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 23 Feb 2022 11:20:48 +1300 Subject: [PATCH 214/273] Bump version to 2022.2.5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 015a598ded..7cd88a8101 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.2.4" +__version__ = "2022.2.5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 859cca49d19a95ce3329180710cbaaf650ef7ac0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 2 Mar 2022 16:44:35 +1300 Subject: [PATCH 215/273] Only get free memory size from internal (#3259) --- esphome/components/json/json_util.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 7e88fb6e59..9acba76597 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -22,7 +22,7 @@ std::string build_json(const json_build_t &f) { #ifdef USE_ESP8266 const size_t free_heap = ESP.getMaxFreeBlockSize() - 2048; // NOLINT(readability-static-accessed-through-instance) #elif defined(USE_ESP32) - const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT) - 2048; + const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL) - 2048; #endif DynamicJsonDocument json_document(free_heap); @@ -42,7 +42,7 @@ void parse_json(const std::string &data, const json_parse_t &f) { #ifdef USE_ESP8266 const size_t free_heap = ESP.getMaxFreeBlockSize() - 2048; // NOLINT(readability-static-accessed-through-instance) #elif defined(USE_ESP32) - const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT) - 2048; + const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL) - 2048; #endif DynamicJsonDocument json_document(free_heap); From 942b0de7fd7ad31d4e79bbe749ac8b928ba597cb Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 2 Mar 2022 17:07:08 +1300 Subject: [PATCH 216/273] Bump version to 2022.2.6 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 7cd88a8101..e49f468c30 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.2.5" +__version__ = "2022.2.6" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 023d26f5218c8bcafee022873e7737d5cf5838f2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 9 Mar 2022 20:07:50 +1300 Subject: [PATCH 217/273] Bump version to 2022.3.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 2ec00edf7b..26444baffd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.3.0-dev" +__version__ = "2022.3.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 06a35056989ad441b94c35d0240a5fbf97ad6f67 Mon Sep 17 00:00:00 2001 From: stegm Date: Wed, 9 Mar 2022 20:40:43 +0100 Subject: [PATCH 218/273] Add optimistic config flag to modbus select. (#3267) --- esphome/components/modbus_controller/select/__init__.py | 4 +++- esphome/components/modbus_controller/select/modbus_select.cpp | 3 +++ esphome/components/modbus_controller/select/modbus_select.h | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/components/modbus_controller/select/__init__.py b/esphome/components/modbus_controller/select/__init__.py index 7d03064fa5..6f194ef2a3 100644 --- a/esphome/components/modbus_controller/select/__init__.py +++ b/esphome/components/modbus_controller/select/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import select -from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA +from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA, CONF_OPTIMISTIC from esphome.jsonschema import jschema_composite from .. import ( @@ -79,6 +79,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_FORCE_NEW_RANGE, default=False): cv.boolean, cv.Required(CONF_OPTIONSMAP): ensure_option_map(), cv.Optional(CONF_USE_WRITE_MULTIPLE, default=False): cv.boolean, + cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, cv.Optional(CONF_LAMBDA): cv.returning_lambda, cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda, }, @@ -112,6 +113,7 @@ async def to_code(config): cg.add(parent.add_sensor_item(var)) cg.add(var.set_parent(parent)) cg.add(var.set_use_write_mutiple(config[CONF_USE_WRITE_MULTIPLE])) + cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) if CONF_LAMBDA in config: template_ = await cg.process_lambda( diff --git a/esphome/components/modbus_controller/select/modbus_select.cpp b/esphome/components/modbus_controller/select/modbus_select.cpp index 2c6b32f545..33cef39a18 100644 --- a/esphome/components/modbus_controller/select/modbus_select.cpp +++ b/esphome/components/modbus_controller/select/modbus_select.cpp @@ -80,6 +80,9 @@ void ModbusSelect::control(const std::string &value) { } parent_->queue_command(write_cmd); + + if (this->optimistic_) + this->publish_state(value); } } // namespace modbus_controller diff --git a/esphome/components/modbus_controller/select/modbus_select.h b/esphome/components/modbus_controller/select/modbus_select.h index 0875194768..2a31dfd7cc 100644 --- a/esphome/components/modbus_controller/select/modbus_select.h +++ b/esphome/components/modbus_controller/select/modbus_select.h @@ -32,6 +32,7 @@ class ModbusSelect : public Component, public select::Select, public SensorItem void set_parent(ModbusController *const parent) { this->parent_ = parent; } void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; } + void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } void set_template(transform_func_t &&f) { this->transform_func_ = f; } void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } @@ -43,6 +44,7 @@ class ModbusSelect : public Component, public select::Select, public SensorItem std::vector mapping_; ModbusController *parent_; bool use_write_multiple_{false}; + bool optimistic_{false}; optional transform_func_; optional write_transform_func_; }; From b7535693fa4628f8f831e2c87fdbccc3c050f05f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 16 Mar 2022 13:35:37 +1300 Subject: [PATCH 219/273] Add helper overloads for hex print 16-bit (#3297) --- esphome/core/helpers.cpp | 19 +++++++++++++++++++ esphome/core/helpers.h | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index a346cd7e0b..b03d890ad8 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -213,6 +213,25 @@ std::string format_hex_pretty(const uint8_t *data, size_t length) { } std::string format_hex_pretty(const std::vector &data) { return format_hex_pretty(data.data(), data.size()); } +std::string format_hex_pretty(const uint16_t *data, size_t length) { + if (length == 0) + return ""; + std::string ret; + ret.resize(5 * length - 1); + for (size_t i = 0; i < length; i++) { + ret[5 * i] = format_hex_pretty_char((data[i] & 0xF000) >> 12); + ret[5 * i + 1] = format_hex_pretty_char((data[i] & 0x0F00) >> 8); + ret[5 * i + 2] = format_hex_pretty_char((data[i] & 0x00F0) >> 4); + ret[5 * i + 3] = format_hex_pretty_char(data[i] & 0x000F); + if (i != length - 1) + ret[5 * i + 2] = '.'; + } + if (length > 4) + return ret + " (" + to_string(length) + ")"; + return ret; +} +std::string format_hex_pretty(const std::vector &data) { return format_hex_pretty(data.data(), data.size()); } + ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) { if (on == nullptr && strcasecmp(str, "on") == 0) return PARSE_ON; diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index e0763d2c71..074bea6fd1 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -386,8 +386,12 @@ template::value, int> = 0> std::stri /// Format the byte array \p data of length \p len in pretty-printed, human-readable hex. std::string format_hex_pretty(const uint8_t *data, size_t length); +/// Format the word array \p data of length \p len in pretty-printed, human-readable hex. +std::string format_hex_pretty(const uint16_t *data, size_t length); /// Format the vector \p data in pretty-printed, human-readable hex. std::string format_hex_pretty(const std::vector &data); +/// Format the vector \p data in pretty-printed, human-readable hex. +std::string format_hex_pretty(const std::vector &data); /// Format an unsigned integer in pretty-printed, human-readable hex, starting with the most significant byte. template::value, int> = 0> std::string format_hex_pretty(T val) { val = convert_big_endian(val); From 756f71c3822a2f457b42c5b55f8af1151c69c444 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 16 Mar 2022 13:43:05 +1300 Subject: [PATCH 220/273] Allow custom register type for modbus number (#3202) --- esphome/components/modbus_controller/number/__init__.py | 6 ++++++ esphome/components/modbus_controller/number/modbus_number.h | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/esphome/components/modbus_controller/number/__init__.py b/esphome/components/modbus_controller/number/__init__.py index 56ec734315..37a39ff334 100644 --- a/esphome/components/modbus_controller/number/__init__.py +++ b/esphome/components/modbus_controller/number/__init__.py @@ -11,6 +11,7 @@ from esphome.const import ( ) from .. import ( + MODBUS_WRITE_REGISTER_TYPE, add_modbus_base_properties, modbus_controller_ns, modbus_calc_properties, @@ -24,6 +25,7 @@ from ..const import ( CONF_CUSTOM_COMMAND, CONF_FORCE_NEW_RANGE, CONF_MODBUS_CONTROLLER_ID, + CONF_REGISTER_TYPE, CONF_SKIP_UPDATES, CONF_USE_WRITE_MULTIPLE, CONF_VALUE_TYPE, @@ -61,6 +63,9 @@ CONFIG_SCHEMA = cv.All( number.NUMBER_SCHEMA.extend(ModbusItemBaseSchema).extend( { cv.GenerateID(): cv.declare_id(ModbusNumber), + cv.Optional(CONF_REGISTER_TYPE, default="holding"): cv.enum( + MODBUS_WRITE_REGISTER_TYPE + ), cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(SENSOR_VALUE_TYPE), cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda, # 24 bits are the maximum value for fp32 before precison is lost @@ -81,6 +86,7 @@ async def to_code(config): byte_offset, reg_count = modbus_calc_properties(config) var = cg.new_Pvariable( config[CONF_ID], + config[CONF_REGISTER_TYPE], config[CONF_ADDRESS], byte_offset, config[CONF_BITMASK], diff --git a/esphome/components/modbus_controller/number/modbus_number.h b/esphome/components/modbus_controller/number/modbus_number.h index 0c525d9c89..aa5c8d1500 100644 --- a/esphome/components/modbus_controller/number/modbus_number.h +++ b/esphome/components/modbus_controller/number/modbus_number.h @@ -11,9 +11,9 @@ using value_to_data_t = std::function(float); class ModbusNumber : public number::Number, public Component, public SensorItem { public: - ModbusNumber(uint16_t start_address, uint8_t offset, uint32_t bitmask, SensorValueType value_type, int register_count, - uint8_t skip_updates, bool force_new_range) { - this->register_type = ModbusRegisterType::HOLDING; + ModbusNumber(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, + SensorValueType value_type, int register_count, uint8_t skip_updates, bool force_new_range) { + this->register_type = register_type; this->start_address = start_address; this->offset = offset; this->bitmask = bitmask; From e5c2dbc7ec1f7e06c043a095957a6945f4f7448d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 16 Mar 2022 14:06:00 +1300 Subject: [PATCH 221/273] Bump version to 2022.3.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 26444baffd..579cefd507 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.3.0b1" +__version__ = "2022.3.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 792a24f38d05f87a106096f90be7cd9cbe959b6f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 16 Mar 2022 22:32:24 +1300 Subject: [PATCH 222/273] Bump version to 2022.3.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 579cefd507..e9e3809b2c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.3.0b2" +__version__ = "2022.3.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 24b75b7ed64ee9e239e27511c91e33a66519e9a2 Mon Sep 17 00:00:00 2001 From: wysiwyng <4764286+wysiwyng@users.noreply.github.com> Date: Wed, 16 Mar 2022 20:33:05 +0100 Subject: [PATCH 223/273] Fix WDT reset during dallas search algorithm (#3293) --- esphome/components/dallas/esp_one_wire.cpp | 4 ---- esphome/components/dallas/esp_one_wire.h | 1 - 2 files changed, 5 deletions(-) diff --git a/esphome/components/dallas/esp_one_wire.cpp b/esphome/components/dallas/esp_one_wire.cpp index 885846e5e5..5bd0f42855 100644 --- a/esphome/components/dallas/esp_one_wire.cpp +++ b/esphome/components/dallas/esp_one_wire.cpp @@ -142,7 +142,6 @@ void IRAM_ATTR ESPOneWire::select(uint64_t address) { void IRAM_ATTR ESPOneWire::reset_search() { this->last_discrepancy_ = 0; this->last_device_flag_ = false; - this->last_family_discrepancy_ = 0; this->rom_number_ = 0; } uint64_t IRAM_ATTR ESPOneWire::search() { @@ -195,9 +194,6 @@ uint64_t IRAM_ATTR ESPOneWire::search() { if (!branch) { last_zero = id_bit_number; - if (last_zero < 9) { - this->last_discrepancy_ = last_zero; - } } } diff --git a/esphome/components/dallas/esp_one_wire.h b/esphome/components/dallas/esp_one_wire.h index ef6f079f02..7544a6fe98 100644 --- a/esphome/components/dallas/esp_one_wire.h +++ b/esphome/components/dallas/esp_one_wire.h @@ -60,7 +60,6 @@ class ESPOneWire { ISRInternalGPIOPin pin_; uint8_t last_discrepancy_{0}; - uint8_t last_family_discrepancy_{0}; bool last_device_flag_{false}; uint64_t rom_number_{0}; }; From 0729ed538e6b13b5ad5d4f6d3051e79012430cf5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 23 Mar 2022 09:45:05 +1300 Subject: [PATCH 224/273] Webserver utilize Component Iterator to not overload eventstream (#3310) --- esphome/components/api/api_connection.cpp | 2 +- esphome/components/api/api_server.h | 1 - esphome/components/api/list_entities.cpp | 3 +- esphome/components/api/list_entities.h | 6 +- esphome/components/api/proto.cpp | 1 - esphome/components/api/subscribe_state.cpp | 3 +- esphome/components/api/subscribe_state.h | 6 +- .../components/web_server/list_entities.cpp | 97 +++++++++++++++++++ esphome/components/web_server/list_entities.h | 60 ++++++++++++ esphome/components/web_server/web_server.cpp | 86 +--------------- esphome/components/web_server/web_server.h | 7 +- .../util.cpp => core/component_iterator.cpp} | 56 ++++++----- .../api/util.h => core/component_iterator.h} | 23 +++-- 13 files changed, 217 insertions(+), 134 deletions(-) create mode 100644 esphome/components/web_server/list_entities.cpp create mode 100644 esphome/components/web_server/list_entities.h rename esphome/{components/api/util.cpp => core/component_iterator.cpp} (80%) rename esphome/{components/api/util.h => core/component_iterator.h} (91%) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index b998ef5929..81f2465b74 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -23,7 +23,7 @@ static const char *const TAG = "api.connection"; static const int ESP32_CAMERA_STOP_STREAM = 5000; APIConnection::APIConnection(std::unique_ptr sock, APIServer *parent) - : parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) { + : parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) { this->proto_write_buffer_.reserve(64); #if defined(USE_API_PLAINTEXT) diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 3214da5b3d..fdc46922ad 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -7,7 +7,6 @@ #include "esphome/components/socket/socket.h" #include "api_pb2.h" #include "api_pb2_service.h" -#include "util.h" #include "list_entities.h" #include "subscribe_state.h" #include "user_services.h" diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index fb0dfa3d05..9f55fda617 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -40,8 +40,7 @@ bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->s #endif bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); } -ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client) - : ComponentIterator(server), client_(client) {} +ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {} bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) { auto resp = service->encode_list_service_response(); return this->client_->send_list_entities_services_response(resp); diff --git a/esphome/components/api/list_entities.h b/esphome/components/api/list_entities.h index bfceb39ebf..51c343eb03 100644 --- a/esphome/components/api/list_entities.h +++ b/esphome/components/api/list_entities.h @@ -1,8 +1,8 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/component_iterator.h" #include "esphome/core/defines.h" -#include "util.h" namespace esphome { namespace api { @@ -11,7 +11,7 @@ class APIConnection; class ListEntitiesIterator : public ComponentIterator { public: - ListEntitiesIterator(APIServer *server, APIConnection *client); + ListEntitiesIterator(APIConnection *client); #ifdef USE_BINARY_SENSOR bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override; #endif @@ -60,5 +60,3 @@ class ListEntitiesIterator : public ComponentIterator { } // namespace api } // namespace esphome - -#include "api_server.h" diff --git a/esphome/components/api/proto.cpp b/esphome/components/api/proto.cpp index 0ba277d90a..ca7a4c0887 100644 --- a/esphome/components/api/proto.cpp +++ b/esphome/components/api/proto.cpp @@ -1,5 +1,4 @@ #include "proto.h" -#include "util.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index 10416ecc5c..ba277502c8 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -50,8 +50,7 @@ bool InitialStateIterator::on_select(select::Select *select) { #ifdef USE_LOCK bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); } #endif -InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client) - : ComponentIterator(server), client_(client) {} +InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {} } // namespace api } // namespace esphome diff --git a/esphome/components/api/subscribe_state.h b/esphome/components/api/subscribe_state.h index caea013f84..515e1a2d07 100644 --- a/esphome/components/api/subscribe_state.h +++ b/esphome/components/api/subscribe_state.h @@ -1,9 +1,9 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/component_iterator.h" #include "esphome/core/controller.h" #include "esphome/core/defines.h" -#include "util.h" namespace esphome { namespace api { @@ -12,7 +12,7 @@ class APIConnection; class InitialStateIterator : public ComponentIterator { public: - InitialStateIterator(APIServer *server, APIConnection *client); + InitialStateIterator(APIConnection *client); #ifdef USE_BINARY_SENSOR bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override; #endif @@ -55,5 +55,3 @@ class InitialStateIterator : public ComponentIterator { } // namespace api } // namespace esphome - -#include "api_server.h" diff --git a/esphome/components/web_server/list_entities.cpp b/esphome/components/web_server/list_entities.cpp new file mode 100644 index 0000000000..6f833a5c83 --- /dev/null +++ b/esphome/components/web_server/list_entities.cpp @@ -0,0 +1,97 @@ +#ifdef USE_ARDUINO + +#include "list_entities.h" +#include "esphome/core/application.h" +#include "esphome/core/log.h" +#include "esphome/core/util.h" + +#include "web_server.h" + +namespace esphome { +namespace web_server { + +ListEntitiesIterator::ListEntitiesIterator(WebServer *web_server) : web_server_(web_server) {} + +#ifdef USE_BINARY_SENSOR +bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { + this->web_server_->events_.send( + this->web_server_->binary_sensor_json(binary_sensor, binary_sensor->state, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif +#ifdef USE_COVER +bool ListEntitiesIterator::on_cover(cover::Cover *cover) { + this->web_server_->events_.send(this->web_server_->cover_json(cover, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif +#ifdef USE_FAN +bool ListEntitiesIterator::on_fan(fan::Fan *fan) { + this->web_server_->events_.send(this->web_server_->fan_json(fan, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif +#ifdef USE_LIGHT +bool ListEntitiesIterator::on_light(light::LightState *light) { + this->web_server_->events_.send(this->web_server_->light_json(light, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif +#ifdef USE_SENSOR +bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { + this->web_server_->events_.send(this->web_server_->sensor_json(sensor, sensor->state, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif +#ifdef USE_SWITCH +bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { + this->web_server_->events_.send(this->web_server_->switch_json(a_switch, a_switch->state, DETAIL_ALL).c_str(), + "state"); + return true; +} +#endif +#ifdef USE_BUTTON +bool ListEntitiesIterator::on_button(button::Button *button) { + this->web_server_->events_.send(this->web_server_->button_json(button, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif +#ifdef USE_TEXT_SENSOR +bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) { + this->web_server_->events_.send( + this->web_server_->text_sensor_json(text_sensor, text_sensor->state, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif +#ifdef USE_LOCK +bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { + this->web_server_->events_.send(this->web_server_->lock_json(a_lock, a_lock->state, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif + +#ifdef USE_CLIMATE +bool ListEntitiesIterator::on_climate(climate::Climate *climate) { + this->web_server_->events_.send(this->web_server_->climate_json(climate, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif + +#ifdef USE_NUMBER +bool ListEntitiesIterator::on_number(number::Number *number) { + this->web_server_->events_.send(this->web_server_->number_json(number, number->state, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif + +#ifdef USE_SELECT +bool ListEntitiesIterator::on_select(select::Select *select) { + this->web_server_->events_.send(this->web_server_->select_json(select, select->state, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif + +} // namespace web_server +} // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/web_server/list_entities.h b/esphome/components/web_server/list_entities.h new file mode 100644 index 0000000000..85868caff8 --- /dev/null +++ b/esphome/components/web_server/list_entities.h @@ -0,0 +1,60 @@ +#pragma once + +#ifdef USE_ARDUINO + +#include "esphome/core/component.h" +#include "esphome/core/component_iterator.h" +#include "esphome/core/defines.h" +namespace esphome { +namespace web_server { + +class WebServer; + +class ListEntitiesIterator : public ComponentIterator { + public: + ListEntitiesIterator(WebServer *web_server); +#ifdef USE_BINARY_SENSOR + bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override; +#endif +#ifdef USE_COVER + bool on_cover(cover::Cover *cover) override; +#endif +#ifdef USE_FAN + bool on_fan(fan::Fan *fan) override; +#endif +#ifdef USE_LIGHT + bool on_light(light::LightState *light) override; +#endif +#ifdef USE_SENSOR + bool on_sensor(sensor::Sensor *sensor) override; +#endif +#ifdef USE_SWITCH + bool on_switch(switch_::Switch *a_switch) override; +#endif +#ifdef USE_BUTTON + bool on_button(button::Button *button) override; +#endif +#ifdef USE_TEXT_SENSOR + bool on_text_sensor(text_sensor::TextSensor *text_sensor) override; +#endif +#ifdef USE_CLIMATE + bool on_climate(climate::Climate *climate) override; +#endif +#ifdef USE_NUMBER + bool on_number(number::Number *number) override; +#endif +#ifdef USE_SELECT + bool on_select(select::Select *select) override; +#endif +#ifdef USE_LOCK + bool on_lock(lock::Lock *a_lock) override; +#endif + + protected: + WebServer *web_server_; +}; + +} // namespace web_server +} // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 278aeab937..0dfd608661 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1,6 +1,7 @@ #ifdef USE_ARDUINO #include "web_server.h" + #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/entity_base.h" @@ -17,7 +18,7 @@ #endif #ifdef USE_LOGGER -#include +#include "esphome/components/logger/logger.h" #endif #ifdef USE_FAN @@ -106,87 +107,7 @@ void WebServer::setup() { }).c_str(), "ping", millis(), 30000); -#ifdef USE_SENSOR - for (auto *obj : App.get_sensors()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->sensor_json(obj, obj->state, DETAIL_ALL).c_str(), "state"); - } -#endif - -#ifdef USE_SWITCH - for (auto *obj : App.get_switches()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->switch_json(obj, obj->state, DETAIL_ALL).c_str(), "state"); - } -#endif - -#ifdef USE_BUTTON - for (auto *obj : App.get_buttons()) - client->send(this->button_json(obj, DETAIL_ALL).c_str(), "state"); -#endif - -#ifdef USE_BINARY_SENSOR - for (auto *obj : App.get_binary_sensors()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->binary_sensor_json(obj, obj->state, DETAIL_ALL).c_str(), "state"); - } -#endif - -#ifdef USE_FAN - for (auto *obj : App.get_fans()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->fan_json(obj, DETAIL_ALL).c_str(), "state"); - } -#endif - -#ifdef USE_LIGHT - for (auto *obj : App.get_lights()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->light_json(obj, DETAIL_ALL).c_str(), "state"); - } -#endif - -#ifdef USE_TEXT_SENSOR - for (auto *obj : App.get_text_sensors()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->text_sensor_json(obj, obj->state, DETAIL_ALL).c_str(), "state"); - } -#endif - -#ifdef USE_COVER - for (auto *obj : App.get_covers()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->cover_json(obj, DETAIL_ALL).c_str(), "state"); - } -#endif - -#ifdef USE_NUMBER - for (auto *obj : App.get_numbers()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->number_json(obj, obj->state, DETAIL_ALL).c_str(), "state"); - } -#endif - -#ifdef USE_SELECT - for (auto *obj : App.get_selects()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->select_json(obj, obj->state, DETAIL_ALL).c_str(), "state"); - } -#endif - -#ifdef USE_CLIMATE - for (auto *obj : App.get_climates()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->climate_json(obj, DETAIL_ALL).c_str(), "state"); - } -#endif - -#ifdef USE_LOCK - for (auto *obj : App.get_locks()) { - if (this->include_internal_ || !obj->is_internal()) - client->send(this->lock_json(obj, obj->state, DETAIL_ALL).c_str(), "state"); - } -#endif + this->entities_iterator_.begin(this->include_internal_); }); #ifdef USE_LOGGER @@ -203,6 +124,7 @@ void WebServer::setup() { this->set_interval(10000, [this]() { this->events_.send("", "ping", millis(), 30000); }); } +void WebServer::loop() { this->entities_iterator_.advance(); } void WebServer::dump_config() { ESP_LOGCONFIG(TAG, "Web Server:"); ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->base_->get_port()); diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 2717997f60..73813ecfa1 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -2,6 +2,8 @@ #ifdef USE_ARDUINO +#include "list_entities.h" + #include "esphome/components/web_server_base/web_server_base.h" #include "esphome/core/component.h" #include "esphome/core/controller.h" @@ -32,7 +34,7 @@ enum JsonDetail { DETAIL_ALL, DETAIL_STATE }; */ class WebServer : public Controller, public Component, public AsyncWebHandler { public: - WebServer(web_server_base::WebServerBase *base) : base_(base) {} + WebServer(web_server_base::WebServerBase *base) : base_(base), entities_iterator_(ListEntitiesIterator(this)) {} /** Set the URL to the CSS that's sent to each client. Defaults to * https://esphome.io/_static/webserver-v1.min.css @@ -76,6 +78,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { // (In most use cases you won't need these) /// Setup the internal web server and register handlers. void setup() override; + void loop() override; void dump_config() override; @@ -217,8 +220,10 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { bool isRequestHandlerTrivial() override; protected: + friend ListEntitiesIterator; web_server_base::WebServerBase *base_; AsyncEventSource events_{"/events"}; + ListEntitiesIterator entities_iterator_; const char *css_url_{nullptr}; const char *css_include_{nullptr}; const char *js_url_{nullptr}; diff --git a/esphome/components/api/util.cpp b/esphome/core/component_iterator.cpp similarity index 80% rename from esphome/components/api/util.cpp rename to esphome/core/component_iterator.cpp index fd55f89f9b..4781607a2d 100644 --- a/esphome/components/api/util.cpp +++ b/esphome/core/component_iterator.cpp @@ -1,16 +1,18 @@ -#include "util.h" -#include "api_server.h" -#include "user_services.h" -#include "esphome/core/log.h" +#include "component_iterator.h" + #include "esphome/core/application.h" -namespace esphome { -namespace api { +#ifdef USE_API +#include "esphome/components/api/api_server.h" +#include "esphome/components/api/user_services.h" +#endif -ComponentIterator::ComponentIterator(APIServer *server) : server_(server) {} -void ComponentIterator::begin() { +namespace esphome { + +void ComponentIterator::begin(bool include_internal) { this->state_ = IteratorState::BEGIN; this->at_ = 0; + this->include_internal_ = include_internal; } void ComponentIterator::advance() { bool advance_platform = false; @@ -32,7 +34,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *binary_sensor = App.get_binary_sensors()[this->at_]; - if (binary_sensor->is_internal()) { + if (binary_sensor->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -47,7 +49,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *cover = App.get_covers()[this->at_]; - if (cover->is_internal()) { + if (cover->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -62,7 +64,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *fan = App.get_fans()[this->at_]; - if (fan->is_internal()) { + if (fan->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -77,7 +79,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *light = App.get_lights()[this->at_]; - if (light->is_internal()) { + if (light->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -92,7 +94,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *sensor = App.get_sensors()[this->at_]; - if (sensor->is_internal()) { + if (sensor->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -107,7 +109,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *a_switch = App.get_switches()[this->at_]; - if (a_switch->is_internal()) { + if (a_switch->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -122,7 +124,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *button = App.get_buttons()[this->at_]; - if (button->is_internal()) { + if (button->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -137,7 +139,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *text_sensor = App.get_text_sensors()[this->at_]; - if (text_sensor->is_internal()) { + if (text_sensor->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -146,20 +148,22 @@ void ComponentIterator::advance() { } break; #endif +#ifdef USE_API case IteratorState ::SERVICE: - if (this->at_ >= this->server_->get_user_services().size()) { + if (this->at_ >= api::global_api_server->get_user_services().size()) { advance_platform = true; } else { - auto *service = this->server_->get_user_services()[this->at_]; + auto *service = api::global_api_server->get_user_services()[this->at_]; success = this->on_service(service); } break; +#endif #ifdef USE_ESP32_CAMERA case IteratorState::CAMERA: if (esp32_camera::global_esp32_camera == nullptr) { advance_platform = true; } else { - if (esp32_camera::global_esp32_camera->is_internal()) { + if (esp32_camera::global_esp32_camera->is_internal() && !this->include_internal_) { advance_platform = success = true; break; } else { @@ -174,7 +178,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *climate = App.get_climates()[this->at_]; - if (climate->is_internal()) { + if (climate->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -189,7 +193,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *number = App.get_numbers()[this->at_]; - if (number->is_internal()) { + if (number->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -204,7 +208,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *select = App.get_selects()[this->at_]; - if (select->is_internal()) { + if (select->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -219,7 +223,7 @@ void ComponentIterator::advance() { advance_platform = true; } else { auto *a_lock = App.get_locks()[this->at_]; - if (a_lock->is_internal()) { + if (a_lock->is_internal() && !this->include_internal_) { success = true; break; } else { @@ -244,10 +248,10 @@ void ComponentIterator::advance() { } bool ComponentIterator::on_end() { return true; } bool ComponentIterator::on_begin() { return true; } -bool ComponentIterator::on_service(UserServiceDescriptor *service) { return true; } +#ifdef USE_API +bool ComponentIterator::on_service(api::UserServiceDescriptor *service) { return true; } +#endif #ifdef USE_ESP32_CAMERA bool ComponentIterator::on_camera(esp32_camera::ESP32Camera *camera) { return true; } #endif - -} // namespace api } // namespace esphome diff --git a/esphome/components/api/util.h b/esphome/core/component_iterator.h similarity index 91% rename from esphome/components/api/util.h rename to esphome/core/component_iterator.h index 9204b0829e..bd95fe95e1 100644 --- a/esphome/components/api/util.h +++ b/esphome/core/component_iterator.h @@ -1,23 +1,24 @@ #pragma once -#include "esphome/core/helpers.h" #include "esphome/core/component.h" #include "esphome/core/controller.h" +#include "esphome/core/helpers.h" + #ifdef USE_ESP32_CAMERA #include "esphome/components/esp32_camera/esp32_camera.h" #endif namespace esphome { -namespace api { -class APIServer; +#ifdef USE_API +namespace api { class UserServiceDescriptor; +} // namespace api +#endif class ComponentIterator { public: - ComponentIterator(APIServer *server); - - void begin(); + void begin(bool include_internal = false); void advance(); virtual bool on_begin(); #ifdef USE_BINARY_SENSOR @@ -44,7 +45,9 @@ class ComponentIterator { #ifdef USE_TEXT_SENSOR virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0; #endif - virtual bool on_service(UserServiceDescriptor *service); +#ifdef USE_API + virtual bool on_service(api::UserServiceDescriptor *service); +#endif #ifdef USE_ESP32_CAMERA virtual bool on_camera(esp32_camera::ESP32Camera *camera); #endif @@ -90,7 +93,9 @@ class ComponentIterator { #ifdef USE_TEXT_SENSOR TEXT_SENSOR, #endif +#ifdef USE_API SERVICE, +#endif #ifdef USE_ESP32_CAMERA CAMERA, #endif @@ -109,9 +114,7 @@ class ComponentIterator { MAX, } state_{IteratorState::NONE}; size_t at_{0}; - - APIServer *server_; + bool include_internal_{false}; }; -} // namespace api } // namespace esphome From 4e4a512107f714bac29fab43a2fd6ddb7dcea308 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 23 Mar 2022 09:46:25 +1300 Subject: [PATCH 225/273] Reserve less memory for json (#3289) --- esphome/components/json/json_util.cpp | 60 +++++++++++++++++++-------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 9acba76597..2070b312e8 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -16,16 +16,24 @@ static const char *const TAG = "json"; static std::vector global_json_build_buffer; // NOLINT std::string build_json(const json_build_t &f) { - // Here we are allocating as much heap memory as available minus 2kb to be safe + // Here we are allocating up to 5kb of memory, + // with the heap size minus 2kb to be safe if less than 5kb // as we can not have a true dynamic sized document. // The excess memory is freed below with `shrinkToFit()` #ifdef USE_ESP8266 - const size_t free_heap = ESP.getMaxFreeBlockSize() - 2048; // NOLINT(readability-static-accessed-through-instance) + const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance) #elif defined(USE_ESP32) - const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL) - 2048; + const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); #endif - DynamicJsonDocument json_document(free_heap); + const size_t request_size = std::min(free_heap - 2048, (size_t) 5120); + + DynamicJsonDocument json_document(request_size); + if (json_document.memoryPool().buffer() == nullptr) { + ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, largest free heap block: %u bytes", + request_size, free_heap); + return "{}"; + } JsonObject root = json_document.to(); f(root); json_document.shrinkToFit(); @@ -36,27 +44,45 @@ std::string build_json(const json_build_t &f) { } void parse_json(const std::string &data, const json_parse_t &f) { - // Here we are allocating as much heap memory as available minus 2kb to be safe + // Here we are allocating 1.5 times the data size, + // with the heap size minus 2kb to be safe if less than that // as we can not have a true dynamic sized document. // The excess memory is freed below with `shrinkToFit()` #ifdef USE_ESP8266 - const size_t free_heap = ESP.getMaxFreeBlockSize() - 2048; // NOLINT(readability-static-accessed-through-instance) + const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance) #elif defined(USE_ESP32) - const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL) - 2048; + const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); #endif + bool pass = false; + do { + const size_t request_size = std::min(free_heap - 2048, (size_t)(data.size() * 1.5)); - DynamicJsonDocument json_document(free_heap); - DeserializationError err = deserializeJson(json_document, data); - json_document.shrinkToFit(); + DynamicJsonDocument json_document(request_size); + if (json_document.memoryPool().buffer() == nullptr) { + ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, free heap: %u", request_size, + free_heap); + return; + } + DeserializationError err = deserializeJson(json_document, data); + json_document.shrinkToFit(); - JsonObject root = json_document.as(); + JsonObject root = json_document.as(); - if (err) { - ESP_LOGW(TAG, "Parsing JSON failed."); - return; - } - - f(root); + if (err == DeserializationError::Ok) { + pass = true; + f(root); + } else if (err == DeserializationError::NoMemory) { + if (request_size * 2 >= free_heap) { + ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller"); + return; + } + ESP_LOGW(TAG, "Increasing memory allocation."); + continue; + } else { + ESP_LOGE(TAG, "JSON parse error: %s", err.c_str()); + return; + } + } while (!pass); } } // namespace json From 9a9d5964eeda7fda76900ec50109f4465f092871 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 23 Mar 2022 11:12:22 +1300 Subject: [PATCH 226/273] Add small delay before setting up app in safe mode (#3323) --- esphome/components/ota/ota_component.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 37da3bdc44..3138c495da 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -473,6 +473,8 @@ bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_ App.reboot(); }); + // Delay here to allow power to stabilise before Wi-Fi/Ethernet is initialised. + delay(100); // NOLINT App.setup(); ESP_LOGI(TAG, "Waiting for OTA attempt."); From 24029cc918ea6f9bfbdcfb2f0f4a932dd5c460c8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 23 Mar 2022 12:26:02 +1300 Subject: [PATCH 227/273] Bump version to 2022.3.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e9e3809b2c..8bf303db09 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.3.0" +__version__ = "2022.3.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From bb5f7249a6575751b95edf91eafc0b572c215dea Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 28 Mar 2022 17:04:25 +1300 Subject: [PATCH 228/273] Actually increase request memory for json parsing (#3331) --- esphome/components/json/json_util.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 2070b312e8..10179c9954 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -54,9 +54,8 @@ void parse_json(const std::string &data, const json_parse_t &f) { const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); #endif bool pass = false; + size_t request_size = std::min(free_heap - 2048, (size_t)(data.size() * 1.5)); do { - const size_t request_size = std::min(free_heap - 2048, (size_t)(data.size() * 1.5)); - DynamicJsonDocument json_document(request_size); if (json_document.memoryPool().buffer() == nullptr) { ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, free heap: %u", request_size, @@ -76,7 +75,8 @@ void parse_json(const std::string &data, const json_parse_t &f) { ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller"); return; } - ESP_LOGW(TAG, "Increasing memory allocation."); + ESP_LOGV(TAG, "Increasing memory allocation."); + request_size *= 2; continue; } else { ESP_LOGE(TAG, "JSON parse error: %s", err.c_str()); From 8be2456c7e8facb0bab7eaaed6a0e8a4f8472350 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 30 Mar 2022 08:15:50 +1300 Subject: [PATCH 229/273] Bump version to 2022.3.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 8bf303db09..7eeed11271 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.3.1" +__version__ = "2022.3.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From ad57faa9a9808545da35a61a6c3b9d2a016d1c49 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 13 Apr 2022 13:42:28 +1200 Subject: [PATCH 230/273] Bump version to 2022.4.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 9096e66f4e..01d2d59c3d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.4.0-dev" +__version__ = "2022.4.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From c59adf612f8ccdc3f415e99ce538ba1c8fc6c928 Mon Sep 17 00:00:00 2001 From: matthias882 <30553262+matthias882@users.noreply.github.com> Date: Wed, 13 Apr 2022 23:36:16 +0200 Subject: [PATCH 231/273] Changes accuracy of single cell voltage (#3387) --- esphome/components/daly_bms/sensor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/daly_bms/sensor.py b/esphome/components/daly_bms/sensor.py index e2e8528317..2274a2153a 100644 --- a/esphome/components/daly_bms/sensor.py +++ b/esphome/components/daly_bms/sensor.py @@ -98,6 +98,8 @@ CELL_VOLTAGE_SCHEMA = sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, device_class=DEVICE_CLASS_VOLTAGE, state_class=STATE_CLASS_MEASUREMENT, + icon=ICON_FLASH, + accuracy_decimals=3, ) CONFIG_SCHEMA = cv.All( From d5134e88b16edaeb9e39339cdd1f3b7ddd645d52 Mon Sep 17 00:00:00 2001 From: rnauber <7414650+rnauber@users.noreply.github.com> Date: Thu, 14 Apr 2022 03:13:51 +0200 Subject: [PATCH 232/273] Add support for Shelly Dimmer 2 (#2954) Co-authored-by: Niclas Larsson Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Jernej Kos Co-authored-by: Richard Nauber --- CODEOWNERS | 1 + esphome/components/shelly_dimmer/LICENSE.txt | 2 + esphome/components/shelly_dimmer/__init__.py | 1 + esphome/components/shelly_dimmer/dev_table.h | 158 +++ esphome/components/shelly_dimmer/light.py | 219 ++++ .../shelly_dimmer/shelly_dimmer.cpp | 526 ++++++++ .../components/shelly_dimmer/shelly_dimmer.h | 117 ++ .../components/shelly_dimmer/stm32flash.cpp | 1061 +++++++++++++++++ esphome/components/shelly_dimmer/stm32flash.h | 129 ++ esphome/core/defines.h | 6 + tests/test1.yaml | 12 + 11 files changed, 2232 insertions(+) create mode 100644 esphome/components/shelly_dimmer/LICENSE.txt create mode 100644 esphome/components/shelly_dimmer/__init__.py create mode 100644 esphome/components/shelly_dimmer/dev_table.h create mode 100644 esphome/components/shelly_dimmer/light.py create mode 100644 esphome/components/shelly_dimmer/shelly_dimmer.cpp create mode 100644 esphome/components/shelly_dimmer/shelly_dimmer.h create mode 100644 esphome/components/shelly_dimmer/stm32flash.cpp create mode 100644 esphome/components/shelly_dimmer/stm32flash.h diff --git a/CODEOWNERS b/CODEOWNERS index 7595fc52e2..02945ec0a4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -173,6 +173,7 @@ esphome/components/select/* @esphome/core esphome/components/sensirion_common/* @martgras esphome/components/sensor/* @esphome/core esphome/components/sgp40/* @SenexCrenshaw +esphome/components/shelly_dimmer/* @edge90 @rnauber esphome/components/sht4x/* @sjtrny esphome/components/shutdown/* @esphome/core @jsuanet esphome/components/sim800l/* @glmnet diff --git a/esphome/components/shelly_dimmer/LICENSE.txt b/esphome/components/shelly_dimmer/LICENSE.txt new file mode 100644 index 0000000000..524fe0d514 --- /dev/null +++ b/esphome/components/shelly_dimmer/LICENSE.txt @@ -0,0 +1,2 @@ +The firmware files for the STM microcontroller (shelly-dimmer-stm32_*.bin) are taken from +https://github.com/jamesturton/shelly-dimmer-stm32 and GPLv3 licensed. diff --git a/esphome/components/shelly_dimmer/__init__.py b/esphome/components/shelly_dimmer/__init__.py new file mode 100644 index 0000000000..accefbbc34 --- /dev/null +++ b/esphome/components/shelly_dimmer/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@rnauber", "@edge90"] diff --git a/esphome/components/shelly_dimmer/dev_table.h b/esphome/components/shelly_dimmer/dev_table.h new file mode 100644 index 0000000000..f4bf7778f2 --- /dev/null +++ b/esphome/components/shelly_dimmer/dev_table.h @@ -0,0 +1,158 @@ +/* + stm32flash - Open Source ST STM32 flash program for Arduino + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2014-2015 Antonio Borneo + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#pragma once + +#include "esphome/core/defines.h" +#ifdef USE_SHD_FIRMWARE_DATA +#include "stm32flash.h" + +namespace esphome { +namespace shelly_dimmer { + +constexpr uint32_t SZ_128 = 0x00000080; +constexpr uint32_t SZ_256 = 0x00000100; +constexpr uint32_t SZ_1K = 0x00000400; +constexpr uint32_t SZ_2K = 0x00000800; +constexpr uint32_t SZ_16K = 0x00004000; +constexpr uint32_t SZ_32K = 0x00008000; +constexpr uint32_t SZ_64K = 0x00010000; +constexpr uint32_t SZ_128K = 0x00020000; +constexpr uint32_t SZ_256K = 0x00040000; + +/* + * Page-size for page-by-page flash erase. + * Arrays are zero terminated; last non-zero value is automatically repeated + */ + +/* fixed size pages */ +constexpr uint32_t p_128[] = {SZ_128, 0}; // NOLINT +constexpr uint32_t p_256[] = {SZ_256, 0}; // NOLINT +constexpr uint32_t p_1k[] = {SZ_1K, 0}; // NOLINT +constexpr uint32_t p_2k[] = {SZ_2K, 0}; // NOLINT +/* F2 and F4 page size */ +constexpr uint32_t f2f4[] = {SZ_16K, SZ_16K, SZ_16K, SZ_16K, SZ_64K, SZ_128K, 0}; // NOLINT +/* F4 dual bank page size */ +constexpr uint32_t f4db[] = {SZ_16K, SZ_16K, SZ_16K, SZ_16K, SZ_64K, SZ_128K, SZ_128K, // NOLINT + SZ_128K, SZ_16K, SZ_16K, SZ_16K, SZ_16K, SZ_64K, SZ_128K, 0}; +/* F7 page size */ +constexpr uint32_t f7[] = {SZ_32K, SZ_32K, SZ_32K, SZ_32K, SZ_128K, SZ_256K, 0}; // NOLINT + +/* + * Device table, corresponds to the "Bootloader device-dependant parameters" + * table in ST document AN2606. + * Note that the option bytes upper range is inclusive! + */ +constexpr stm32_dev_t DEVICES[] = { + /* ID "name" SRAM-address-range FLASH-address-range PPS PSize + Option-byte-addr-range System-mem-addr-range Flags */ + /* F0 */ + {0x440, "STM32F030x8/F05xxx", 0x20000800, 0x20002000, 0x08000000, 0x08010000, 4, p_1k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFEC00, 0x1FFFF800, 0}, + {0x442, "STM32F030xC/F09xxx", 0x20001800, 0x20008000, 0x08000000, 0x08040000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFC800, 0x1FFFF800, F_OBLL}, + {0x444, "STM32F03xx4/6", 0x20000800, 0x20001000, 0x08000000, 0x08008000, 4, p_1k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFEC00, 0x1FFFF800, 0}, + {0x445, "STM32F04xxx/F070x6", 0x20001800, 0x20001800, 0x08000000, 0x08008000, 4, p_1k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFC400, 0x1FFFF800, 0}, + {0x448, "STM32F070xB/F071xx/F72xx", 0x20001800, 0x20004000, 0x08000000, 0x08020000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFC800, 0x1FFFF800, 0}, + /* F1 */ + {0x412, "STM32F10xxx Low-density", 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, p_1k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFF000, 0x1FFFF800, 0}, + {0x410, "STM32F10xxx Medium-density", 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, p_1k, 0x1FFFF800, + 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0}, + {0x414, "STM32F10xxx High-density", 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFF000, 0x1FFFF800, 0}, + {0x420, "STM32F10xxx Medium-density VL", 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, p_1k, 0x1FFFF800, + 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0}, + {0x428, "STM32F10xxx High-density VL", 0x20000200, 0x20008000, 0x08000000, 0x08080000, 2, p_2k, 0x1FFFF800, + 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0}, + {0x418, "STM32F105xx/F107xx", 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFB000, 0x1FFFF800, 0}, + {0x430, "STM32F10xxx XL-density", 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFE000, 0x1FFFF800, 0}, + /* F2 */ + {0x411, "STM32F2xxxx", 0x20002000, 0x20020000, 0x08000000, 0x08100000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, + 0x1FFF7800, 0}, + /* F3 */ + {0x432, "STM32F373xx/F378xx", 0x20001400, 0x20008000, 0x08000000, 0x08040000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFD800, 0x1FFFF800, 0}, + {0x422, "STM32F302xB(C)/F303xB(C)/F358xx", 0x20001400, 0x2000A000, 0x08000000, 0x08040000, 2, p_2k, 0x1FFFF800, + 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0}, + {0x439, "STM32F301xx/F302x4(6/8)/F318xx", 0x20001800, 0x20004000, 0x08000000, 0x08010000, 2, p_2k, 0x1FFFF800, + 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0}, + {0x438, "STM32F303x4(6/8)/F334xx/F328xx", 0x20001800, 0x20003000, 0x08000000, 0x08010000, 2, p_2k, 0x1FFFF800, + 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0}, + {0x446, "STM32F302xD(E)/F303xD(E)/F398xx", 0x20001800, 0x20010000, 0x08000000, 0x08080000, 2, p_2k, 0x1FFFF800, + 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0}, + /* F4 */ + {0x413, "STM32F40xxx/41xxx", 0x20003000, 0x20020000, 0x08000000, 0x08100000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, + 0x1FFF0000, 0x1FFF7800, 0}, + {0x419, "STM32F42xxx/43xxx", 0x20003000, 0x20030000, 0x08000000, 0x08200000, 1, f4db, 0x1FFEC000, 0x1FFFC00F, + 0x1FFF0000, 0x1FFF7800, 0}, + {0x423, "STM32F401xB(C)", 0x20003000, 0x20010000, 0x08000000, 0x08040000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, + 0x1FFF0000, 0x1FFF7800, 0}, + {0x433, "STM32F401xD(E)", 0x20003000, 0x20018000, 0x08000000, 0x08080000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, + 0x1FFF0000, 0x1FFF7800, 0}, + {0x458, "STM32F410xx", 0x20003000, 0x20008000, 0x08000000, 0x08020000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, + 0x1FFF7800, 0}, + {0x431, "STM32F411xx", 0x20003000, 0x20020000, 0x08000000, 0x08080000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, + 0x1FFF7800, 0}, + {0x421, "STM32F446xx", 0x20003000, 0x20020000, 0x08000000, 0x08080000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, + 0x1FFF7800, 0}, + {0x434, "STM32F469xx", 0x20003000, 0x20060000, 0x08000000, 0x08200000, 1, f4db, 0x1FFEC000, 0x1FFFC00F, 0x1FFF0000, + 0x1FFF7800, 0}, + /* F7 */ + {0x449, "STM32F74xxx/75xxx", 0x20004000, 0x20050000, 0x08000000, 0x08100000, 1, f7, 0x1FFF0000, 0x1FFF001F, + 0x1FF00000, 0x1FF0EDC0, 0}, + /* L0 */ + {0x425, "STM32L031xx/041xx", 0x20001000, 0x20002000, 0x08000000, 0x08008000, 32, p_128, 0x1FF80000, 0x1FF8001F, + 0x1FF00000, 0x1FF01000, 0}, + {0x417, "STM32L05xxx/06xxx", 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, p_128, 0x1FF80000, 0x1FF8001F, + 0x1FF00000, 0x1FF01000, 0}, + {0x447, "STM32L07xxx/08xxx", 0x20002000, 0x20005000, 0x08000000, 0x08030000, 32, p_128, 0x1FF80000, 0x1FF8001F, + 0x1FF00000, 0x1FF02000, 0}, + /* L1 */ + {0x416, "STM32L1xxx6(8/B)", 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, p_256, 0x1FF80000, 0x1FF8001F, + 0x1FF00000, 0x1FF01000, F_NO_ME}, + {0x429, "STM32L1xxx6(8/B)A", 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, p_256, 0x1FF80000, 0x1FF8001F, + 0x1FF00000, 0x1FF01000, 0}, + {0x427, "STM32L1xxxC", 0x20001000, 0x20008000, 0x08000000, 0x08040000, 16, p_256, 0x1FF80000, 0x1FF8001F, + 0x1FF00000, 0x1FF02000, 0}, + {0x436, "STM32L1xxxD", 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, p_256, 0x1FF80000, 0x1FF8009F, + 0x1FF00000, 0x1FF02000, 0}, + {0x437, "STM32L1xxxE", 0x20001000, 0x20014000, 0x08000000, 0x08080000, 16, p_256, 0x1FF80000, 0x1FF8009F, + 0x1FF00000, 0x1FF02000, F_NO_ME}, + /* L4 */ + {0x415, "STM32L476xx/486xx", 0x20003100, 0x20018000, 0x08000000, 0x08100000, 1, p_2k, 0x1FFF7800, 0x1FFFF80F, + 0x1FFF0000, 0x1FFF7000, 0}, + /* These are not (yet) in AN2606: */ + {0x641, "Medium_Density PL", 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, p_1k, 0x1FFFF800, 0x1FFFF80F, + 0x1FFFF000, 0x1FFFF800, 0}, + {0x9a8, "STM32W-128K", 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, p_1k, 0x08040800, 0x0804080F, 0x08040000, + 0x08040800, 0}, + {0x9b0, "STM32W-256K", 0x20000200, 0x20004000, 0x08000000, 0x08040000, 4, p_2k, 0x08040800, 0x0804080F, 0x08040000, + 0x08040800, 0}, + {0x0, "", 0x0, 0x0, 0x0, 0x0, 0x0, nullptr, 0x0, 0x0, 0x0, 0x0, 0x0}, +}; + +} // namespace shelly_dimmer +} // namespace esphome +#endif diff --git a/esphome/components/shelly_dimmer/light.py b/esphome/components/shelly_dimmer/light.py new file mode 100644 index 0000000000..003498c090 --- /dev/null +++ b/esphome/components/shelly_dimmer/light.py @@ -0,0 +1,219 @@ +from pathlib import Path +import hashlib +import re +import requests + + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import light, sensor, uart +from esphome.const import ( + CONF_OUTPUT_ID, + CONF_GAMMA_CORRECT, + CONF_POWER, + CONF_VOLTAGE, + CONF_CURRENT, + CONF_VERSION, + CONF_URL, + CONF_UPDATE_INTERVAL, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, +) +from esphome.core import HexInt, CORE + +DOMAIN = "shelly_dimmer" +DEPENDENCIES = ["sensor", "uart"] + +shelly_dimmer_ns = cg.esphome_ns.namespace("shelly_dimmer") +ShellyDimmer = shelly_dimmer_ns.class_( + "ShellyDimmer", light.LightOutput, cg.PollingComponent, uart.UARTDevice +) + +CONF_FIRMWARE = "firmware" +CONF_SHA256 = "sha256" +CONF_UPDATE = "update" + +CONF_LEADING_EDGE = "leading_edge" +CONF_WARMUP_BRIGHTNESS = "warmup_brightness" +# CONF_WARMUP_TIME = "warmup_time" +CONF_MIN_BRIGHTNESS = "min_brightness" +CONF_MAX_BRIGHTNESS = "max_brightness" + +CONF_NRST_PIN = "nrst_pin" +CONF_BOOT0_PIN = "boot0_pin" + +KNOWN_FIRMWARE = { + "51.5": ( + "https://github.com/jamesturton/shelly-dimmer-stm32/releases/download/v51.5/shelly-dimmer-stm32_v51.5.bin", + "553fc1d78ed113227af7683eaa9c26189a961c4ea9a48000fb5aa8f8ac5d7b60", + ), + "51.6": ( + "https://github.com/jamesturton/shelly-dimmer-stm32/releases/download/v51.6/shelly-dimmer-stm32_v51.6.bin", + "eda483e111c914723a33f5088f1397d5c0b19333db4a88dc965636b976c16c36", + ), +} + + +def parse_firmware_version(value): + match = re.match(r"(\d+).(\d+)", value) + if match is None: + raise ValueError(f"Not a valid version number {value}") + major = int(match[1]) + minor = int(match[2]) + return major, minor + + +def get_firmware(value): + if not value[CONF_UPDATE]: + return None + + def dl(url): + try: + req = requests.get(url) + req.raise_for_status() + except requests.exceptions.RequestException as e: + raise cv.Invalid(f"Could not download firmware file ({url}): {e}") + + h = hashlib.new("sha256") + h.update(req.content) + return req.content, h.hexdigest() + + url = value[CONF_URL] + + if CONF_SHA256 in value: # we have a hash, enable caching + path = ( + Path(CORE.config_dir) + / ".esphome" + / DOMAIN + / (value[CONF_SHA256] + "_fw_stm.bin") + ) + + if not path.is_file(): + firmware_data, dl_hash = dl(url) + + if dl_hash != value[CONF_SHA256]: + raise cv.Invalid( + f"Hash mismatch for {url}: {dl_hash} != {value[CONF_SHA256]}" + ) + + path.parent.mkdir(exist_ok=True, parents=True) + path.write_bytes(firmware_data) + + else: + firmware_data = path.read_bytes() + else: # no caching, download every time + firmware_data, dl_hash = dl(url) + + return [HexInt(x) for x in firmware_data] + + +def validate_firmware(value): + config = value.copy() + if CONF_URL not in config: + try: + config[CONF_URL], config[CONF_SHA256] = KNOWN_FIRMWARE[config[CONF_VERSION]] + except KeyError as e: + raise cv.Invalid( + f"Firmware {config[CONF_VERSION]} is unknown, please specify an '{CONF_URL}' ..." + ) from e + get_firmware(config) + return config + + +def validate_sha256(value): + value = cv.string(value) + if not value.isalnum() or not len(value) == 64: + raise ValueError(f"Not a valid SHA256 hex string: {value}") + return value + + +def validate_version(value): + parse_firmware_version(value) + return value + + +CONFIG_SCHEMA = ( + light.BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(ShellyDimmer), + cv.Optional(CONF_FIRMWARE, default="51.6"): cv.maybe_simple_value( + { + cv.Optional(CONF_URL): cv.url, + cv.Optional(CONF_SHA256): validate_sha256, + cv.Required(CONF_VERSION): validate_version, + cv.Optional(CONF_UPDATE, default=False): cv.boolean, + }, + validate_firmware, # converts a simple version key to generate the full url + key=CONF_VERSION, + ), + cv.Optional(CONF_NRST_PIN, default="GPIO5"): pins.gpio_output_pin_schema, + cv.Optional(CONF_BOOT0_PIN, default="GPIO4"): pins.gpio_output_pin_schema, + cv.Optional(CONF_LEADING_EDGE, default=False): cv.boolean, + cv.Optional(CONF_WARMUP_BRIGHTNESS, default=100): cv.uint16_t, + # cv.Optional(CONF_WARMUP_TIME, default=20): cv.uint16_t, + cv.Optional(CONF_MIN_BRIGHTNESS, default=0): cv.uint16_t, + cv.Optional(CONF_MAX_BRIGHTNESS, default=1000): cv.uint16_t, + cv.Optional(CONF_POWER): sensor.sensor_schema( + unit_of_measurement=UNIT_WATT, + accuracy_decimals=1, + device_class=DEVICE_CLASS_POWER, + ), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + unit_of_measurement=UNIT_VOLT, + accuracy_decimals=1, + device_class=DEVICE_CLASS_VOLTAGE, + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + unit_of_measurement=UNIT_AMPERE, + device_class=DEVICE_CLASS_POWER, + accuracy_decimals=2, + ), + # Change the default gamma_correct setting. + cv.Optional(CONF_GAMMA_CORRECT, default=1.0): cv.positive_float, + } + ) + .extend(cv.polling_component_schema("10s")) + .extend(uart.UART_DEVICE_SCHEMA) +) + + +def to_code(config): + fw_hex = get_firmware(config[CONF_FIRMWARE]) + fw_major, fw_minor = parse_firmware_version(config[CONF_FIRMWARE][CONF_VERSION]) + + if fw_hex is not None: + cg.add_define("USE_SHD_FIRMWARE_DATA", fw_hex) + cg.add_define("USE_SHD_FIRMWARE_MAJOR_VERSION", fw_major) + cg.add_define("USE_SHD_FIRMWARE_MINOR_VERSION", fw_minor) + + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) + yield cg.register_component(var, config) + config.pop( + CONF_UPDATE_INTERVAL + ) # drop UPDATE_INTERVAL as it does not apply to the light component + + yield light.register_light(var, config) + yield uart.register_uart_device(var, config) + + nrst_pin = yield cg.gpio_pin_expression(config[CONF_NRST_PIN]) + cg.add(var.set_nrst_pin(nrst_pin)) + boot0_pin = yield cg.gpio_pin_expression(config[CONF_BOOT0_PIN]) + cg.add(var.set_boot0_pin(boot0_pin)) + + cg.add(var.set_leading_edge(config[CONF_LEADING_EDGE])) + cg.add(var.set_warmup_brightness(config[CONF_WARMUP_BRIGHTNESS])) + # cg.add(var.set_warmup_time(config[CONF_WARMUP_TIME])) + cg.add(var.set_min_brightness(config[CONF_MIN_BRIGHTNESS])) + cg.add(var.set_max_brightness(config[CONF_MAX_BRIGHTNESS])) + + for key in [CONF_POWER, CONF_VOLTAGE, CONF_CURRENT]: + if key not in config: + continue + + conf = config[key] + sens = yield sensor.new_sensor(conf) + cg.add(getattr(var, f"set_{key}_sensor")(sens)) diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.cpp b/esphome/components/shelly_dimmer/shelly_dimmer.cpp new file mode 100644 index 0000000000..3b79d0bf57 --- /dev/null +++ b/esphome/components/shelly_dimmer/shelly_dimmer.cpp @@ -0,0 +1,526 @@ +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" + +#include "shelly_dimmer.h" +#ifdef USE_SHD_FIRMWARE_DATA +#include "stm32flash.h" +#endif + +#ifndef USE_ESP_IDF +#include +#endif + +#include +#include +#include +#include + +namespace { + +constexpr char TAG[] = "shelly_dimmer"; + +constexpr uint8_t SHELLY_DIMMER_ACK_TIMEOUT = 200; // ms +constexpr uint8_t SHELLY_DIMMER_MAX_RETRIES = 3; +constexpr uint16_t SHELLY_DIMMER_MAX_BRIGHTNESS = 1000; // 100% + +// Protocol framing. +constexpr uint8_t SHELLY_DIMMER_PROTO_START_BYTE = 0x01; +constexpr uint8_t SHELLY_DIMMER_PROTO_END_BYTE = 0x04; + +// Supported commands. +constexpr uint8_t SHELLY_DIMMER_PROTO_CMD_SWITCH = 0x01; +constexpr uint8_t SHELLY_DIMMER_PROTO_CMD_POLL = 0x10; +constexpr uint8_t SHELLY_DIMMER_PROTO_CMD_VERSION = 0x11; +constexpr uint8_t SHELLY_DIMMER_PROTO_CMD_SETTINGS = 0x20; + +// Command payload sizes. +constexpr uint8_t SHELLY_DIMMER_PROTO_CMD_SWITCH_SIZE = 2; +constexpr uint8_t SHELLY_DIMMER_PROTO_CMD_SETTINGS_SIZE = 10; +constexpr uint8_t SHELLY_DIMMER_PROTO_MAX_FRAME_SIZE = 4 + 72 + 3; + +// STM Firmware +#ifdef USE_SHD_FIRMWARE_DATA +constexpr uint8_t STM_FIRMWARE[] PROGMEM = USE_SHD_FIRMWARE_DATA; +constexpr uint32_t STM_FIRMWARE_SIZE_IN_BYTES = sizeof(STM_FIRMWARE); +#endif + +// Scaling Constants +constexpr float POWER_SCALING_FACTOR = 880373; +constexpr float VOLTAGE_SCALING_FACTOR = 347800; +constexpr float CURRENT_SCALING_FACTOR = 1448; + +// Esentially std::size() for pre c++17 +template constexpr size_t size(const T (&/*unused*/)[N]) noexcept { return N; } + +} // Anonymous namespace + +namespace esphome { +namespace shelly_dimmer { + +/// Computes a crappy checksum as defined by the Shelly Dimmer protocol. +uint16_t shelly_dimmer_checksum(const uint8_t *buf, int len) { + return std::accumulate(buf, buf + len, 0); +} + +void ShellyDimmer::setup() { + this->pin_nrst_->setup(); + this->pin_boot0_->setup(); + + ESP_LOGI(TAG, "Initializing Shelly Dimmer..."); + + // Reset the STM32 and check the firmware version. + for (int i = 0; i < 2; i++) { + this->reset_normal_boot_(); + this->send_command_(SHELLY_DIMMER_PROTO_CMD_VERSION, nullptr, 0); + ESP_LOGI(TAG, "STM32 current firmware version: %d.%d, desired version: %d.%d", this->version_major_, + this->version_minor_, USE_SHD_FIRMWARE_MAJOR_VERSION, USE_SHD_FIRMWARE_MINOR_VERSION); + if (this->version_major_ != USE_SHD_FIRMWARE_MAJOR_VERSION || + this->version_minor_ != USE_SHD_FIRMWARE_MINOR_VERSION) { +#ifdef USE_SHD_FIRMWARE_DATA + // Update firmware if needed. + ESP_LOGW(TAG, "Unsupported STM32 firmware version, flashing"); + if (i > 0) { + // Upgrade was already performed but the reported version is still not right. + ESP_LOGE(TAG, "STM32 firmware upgrade already performed, but version is still incorrect"); + this->mark_failed(); + return; + } + + if (!this->upgrade_firmware_()) { + ESP_LOGW(TAG, "Failed to upgrade firmware"); + this->mark_failed(); + return; + } + + // Firmware upgrade completed, do the checks again. + continue; +#else + ESP_LOGW(TAG, "Firmware version mismatch, put 'update: true' in the yaml to flash an update."); + this->mark_failed(); + return; +#endif + } + break; + } + + this->send_settings_(); + // Do an immediate poll to refresh current state. + this->send_command_(SHELLY_DIMMER_PROTO_CMD_POLL, nullptr, 0); + + this->ready_ = true; +} + +void ShellyDimmer::update() { this->send_command_(SHELLY_DIMMER_PROTO_CMD_POLL, nullptr, 0); } + +void ShellyDimmer::dump_config() { + ESP_LOGCONFIG(TAG, "ShellyDimmer:"); + LOG_PIN(" NRST Pin: ", this->pin_nrst_); + LOG_PIN(" BOOT0 Pin: ", this->pin_boot0_); + + ESP_LOGCONFIG(TAG, " Leading Edge: %s", YESNO(this->leading_edge_)); + ESP_LOGCONFIG(TAG, " Warmup Brightness: %d", this->warmup_brightness_); + // ESP_LOGCONFIG(TAG, " Warmup Time: %d", this->warmup_time_); + // ESP_LOGCONFIG(TAG, " Fade Rate: %d", this->fade_rate_); + ESP_LOGCONFIG(TAG, " Minimum Brightness: %d", this->min_brightness_); + ESP_LOGCONFIG(TAG, " Maximum Brightness: %d", this->max_brightness_); + + LOG_UPDATE_INTERVAL(this); + + ESP_LOGCONFIG(TAG, " STM32 current firmware version: %d.%d ", this->version_major_, this->version_minor_); + ESP_LOGCONFIG(TAG, " STM32 required firmware version: %d.%d", USE_SHD_FIRMWARE_MAJOR_VERSION, + USE_SHD_FIRMWARE_MINOR_VERSION); + + if (this->version_major_ != USE_SHD_FIRMWARE_MAJOR_VERSION || + this->version_minor_ != USE_SHD_FIRMWARE_MINOR_VERSION) { + ESP_LOGE(TAG, " Firmware version mismatch, put 'update: true' in the yaml to flash an update."); + } +} + +void ShellyDimmer::write_state(light::LightState *state) { + if (!this->ready_) { + return; + } + + float brightness; + state->current_values_as_brightness(&brightness); + + const uint16_t brightness_int = this->convert_brightness_(brightness); + if (brightness_int == this->brightness_) { + ESP_LOGV(TAG, "Not sending unchanged value"); + return; + } + ESP_LOGD(TAG, "Brightness update: %d (raw: %f)", brightness_int, brightness); + + this->send_brightness_(brightness_int); +} +#ifdef USE_SHD_FIRMWARE_DATA +bool ShellyDimmer::upgrade_firmware_() { + ESP_LOGW(TAG, "Starting STM32 firmware upgrade"); + this->reset_dfu_boot_(); + + // Could be constexpr in c++17 + static const auto CLOSE = [](stm32_t *stm32) { stm32_close(stm32); }; + + // Cleanup with RAII + std::unique_ptr stm32{stm32_init(this, STREAM_SERIAL, 1), CLOSE}; + + if (!stm32) { + ESP_LOGW(TAG, "Failed to initialize STM32"); + return false; + } + + // Erase STM32 flash. + if (stm32_erase_memory(stm32.get(), 0, STM32_MASS_ERASE) != STM32_ERR_OK) { + ESP_LOGW(TAG, "Failed to erase STM32 flash memory"); + return false; + } + + static constexpr uint32_t BUFFER_SIZE = 256; + + // Copy the STM32 firmware over in 256-byte chunks. Note that the firmware is stored + // in flash memory so all accesses need to be 4-byte aligned. + uint8_t buffer[BUFFER_SIZE]; + const uint8_t *p = STM_FIRMWARE; + uint32_t offset = 0; + uint32_t addr = stm32->dev->fl_start; + const uint32_t end = addr + STM_FIRMWARE_SIZE_IN_BYTES; + + while (addr < end && offset < STM_FIRMWARE_SIZE_IN_BYTES) { + const uint32_t left_of_buffer = std::min(end - addr, BUFFER_SIZE); + const uint32_t len = std::min(left_of_buffer, STM_FIRMWARE_SIZE_IN_BYTES - offset); + + if (len == 0) { + break; + } + + std::memcpy(buffer, p, BUFFER_SIZE); + p += BUFFER_SIZE; + + if (stm32_write_memory(stm32.get(), addr, buffer, len) != STM32_ERR_OK) { + ESP_LOGW(TAG, "Failed to write to STM32 flash memory"); + return false; + } + + addr += len; + offset += len; + } + + ESP_LOGI(TAG, "STM32 firmware upgrade successful"); + + return true; +} +#endif + +uint16_t ShellyDimmer::convert_brightness_(float brightness) { + // Special case for zero as only zero means turn off completely. + if (brightness == 0.0) { + return 0; + } + + return remap(brightness, 0.0f, 1.0f, this->min_brightness_, this->max_brightness_); +} + +void ShellyDimmer::send_brightness_(uint16_t brightness) { + const uint8_t payload[] = { + // Brightness (%) * 10. + static_cast(brightness & 0xff), + static_cast(brightness >> 8), + }; + static_assert(size(payload) == SHELLY_DIMMER_PROTO_CMD_SWITCH_SIZE, "Invalid payload size"); + + this->send_command_(SHELLY_DIMMER_PROTO_CMD_SWITCH, payload, SHELLY_DIMMER_PROTO_CMD_SWITCH_SIZE); + + this->brightness_ = brightness; +} + +void ShellyDimmer::send_settings_() { + const uint16_t fade_rate = std::min(uint16_t{100}, this->fade_rate_); + + float brightness = 0.0; + if (this->state_ != nullptr) { + this->state_->current_values_as_brightness(&brightness); + } + const uint16_t brightness_int = this->convert_brightness_(brightness); + ESP_LOGD(TAG, "Brightness update: %d (raw: %f)", brightness_int, brightness); + + const uint8_t payload[] = { + // Brightness (%) * 10. + static_cast(brightness_int & 0xff), + static_cast(brightness_int >> 8), + // Leading / trailing edge [0x01 = leading, 0x02 = trailing]. + this->leading_edge_ ? uint8_t{0x01} : uint8_t{0x02}, + 0x00, + // Fade rate. + static_cast(fade_rate & 0xff), + static_cast(fade_rate >> 8), + // Warmup brightness. + static_cast(this->warmup_brightness_ & 0xff), + static_cast(this->warmup_brightness_ >> 8), + // Warmup time. + static_cast(this->warmup_time_ & 0xff), + static_cast(this->warmup_time_ >> 8), + }; + static_assert(size(payload) == SHELLY_DIMMER_PROTO_CMD_SETTINGS_SIZE, "Invalid payload size"); + + this->send_command_(SHELLY_DIMMER_PROTO_CMD_SETTINGS, payload, SHELLY_DIMMER_PROTO_CMD_SETTINGS_SIZE); + + // Also send brightness separately as it is ignored above. + this->send_brightness_(brightness_int); +} + +bool ShellyDimmer::send_command_(uint8_t cmd, const uint8_t *const payload, uint8_t len) { + ESP_LOGD(TAG, "Sending command: 0x%02x (%d bytes) payload 0x%s", cmd, len, format_hex(payload, len).c_str()); + + // Prepare a command frame. + uint8_t frame[SHELLY_DIMMER_PROTO_MAX_FRAME_SIZE]; + const size_t frame_len = this->frame_command_(frame, cmd, payload, len); + + // Write the frame and wait for acknowledgement. + int retries = SHELLY_DIMMER_MAX_RETRIES; + while (retries--) { + this->write_array(frame, frame_len); + this->flush(); + + ESP_LOGD(TAG, "Command sent, waiting for reply"); + const uint32_t tx_time = millis(); + while (millis() - tx_time < SHELLY_DIMMER_ACK_TIMEOUT) { + if (this->read_frame_()) { + return true; + } + delay(1); + } + ESP_LOGW(TAG, "Timeout while waiting for reply"); + } + ESP_LOGW(TAG, "Failed to send command"); + return false; +} + +size_t ShellyDimmer::frame_command_(uint8_t *data, uint8_t cmd, const uint8_t *const payload, size_t len) { + size_t pos = 0; + + // Generate a frame. + data[0] = SHELLY_DIMMER_PROTO_START_BYTE; + data[1] = ++this->seq_; + data[2] = cmd; + data[3] = len; + pos += 4; + + if (payload != nullptr) { + std::memcpy(data + 4, payload, len); + pos += len; + } + + // Calculate checksum for the payload. + const uint16_t csum = shelly_dimmer_checksum(data + 1, 3 + len); + data[pos++] = static_cast(csum >> 8); + data[pos++] = static_cast(csum & 0xff); + data[pos++] = SHELLY_DIMMER_PROTO_END_BYTE; + return pos; +} + +int ShellyDimmer::handle_byte_(uint8_t c) { + const uint8_t pos = this->buffer_pos_; + + if (pos == 0) { + // Must be start byte. + return c == SHELLY_DIMMER_PROTO_START_BYTE ? 1 : -1; + } else if (pos < 4) { + // Header. + return 1; + } + + // Decode payload length from header. + const uint8_t payload_len = this->buffer_[3]; + if ((4 + payload_len + 3) > SHELLY_DIMMER_BUFFER_SIZE) { + return -1; + } + + if (pos < 4 + payload_len + 1) { + // Payload. + return 1; + } + + if (pos == 4 + payload_len + 1) { + // Verify checksum. + const uint16_t csum = (this->buffer_[pos - 1] << 8 | c); + const uint16_t csum_verify = shelly_dimmer_checksum(&this->buffer_[1], 3 + payload_len); + if (csum != csum_verify) { + return -1; + } + return 1; + } + + if (pos == 4 + payload_len + 2) { + // Must be end byte. + return c == SHELLY_DIMMER_PROTO_END_BYTE ? 0 : -1; + } + return -1; +} + +bool ShellyDimmer::read_frame_() { + while (this->available()) { + const uint8_t c = this->read(); + this->buffer_[this->buffer_pos_] = c; + + ESP_LOGV(TAG, "Read byte: 0x%02x (pos %d)", c, this->buffer_pos_); + + switch (this->handle_byte_(c)) { + case 0: { + // Frame successfully received. + this->handle_frame_(); + this->buffer_pos_ = 0; + return true; + } + case -1: { + // Failure. + this->buffer_pos_ = 0; + break; + } + case 1: { + // Need more data. + this->buffer_pos_++; + break; + } + } + } + return false; +} + +bool ShellyDimmer::handle_frame_() { + const uint8_t seq = this->buffer_[1]; + const uint8_t cmd = this->buffer_[2]; + const uint8_t payload_len = this->buffer_[3]; + + ESP_LOGD(TAG, "Got frame: 0x%02x", cmd); + + // Compare with expected identifier as the frame is always a response to + // our previously sent command. + if (seq != this->seq_) { + return false; + } + + const uint8_t *payload = &this->buffer_[4]; + + // Handle response. + switch (cmd) { + case SHELLY_DIMMER_PROTO_CMD_POLL: { + if (payload_len < 16) { + return false; + } + + const uint8_t hw_version = payload[0]; + // payload[1] is unused. + const uint16_t brightness = encode_uint16(payload[3], payload[2]); + + const uint32_t power_raw = encode_uint32(payload[7], payload[6], payload[5], payload[4]); + + const uint32_t voltage_raw = encode_uint32(payload[11], payload[10], payload[9], payload[8]); + + const uint32_t current_raw = encode_uint32(payload[15], payload[14], payload[13], payload[12]); + + const uint16_t fade_rate = payload[16]; + + float power = 0; + if (power_raw > 0) { + power = POWER_SCALING_FACTOR / static_cast(power_raw); + } + + float voltage = 0; + if (voltage_raw > 0) { + voltage = VOLTAGE_SCALING_FACTOR / static_cast(voltage_raw); + } + + float current = 0; + if (current_raw > 0) { + current = CURRENT_SCALING_FACTOR / static_cast(current_raw); + } + + ESP_LOGI(TAG, "Got dimmer data:"); + ESP_LOGI(TAG, " HW version: %d", hw_version); + ESP_LOGI(TAG, " Brightness: %d", brightness); + ESP_LOGI(TAG, " Fade rate: %d", fade_rate); + ESP_LOGI(TAG, " Power: %f W", power); + ESP_LOGI(TAG, " Voltage: %f V", voltage); + ESP_LOGI(TAG, " Current: %f A", current); + + // Update sensors. + if (this->power_sensor_ != nullptr) { + this->power_sensor_->publish_state(power); + } + if (this->voltage_sensor_ != nullptr) { + this->voltage_sensor_->publish_state(voltage); + } + if (this->current_sensor_ != nullptr) { + this->current_sensor_->publish_state(current); + } + + return true; + } + case SHELLY_DIMMER_PROTO_CMD_VERSION: { + if (payload_len < 2) { + return false; + } + + this->version_minor_ = payload[0]; + this->version_major_ = payload[1]; + return true; + } + case SHELLY_DIMMER_PROTO_CMD_SWITCH: + case SHELLY_DIMMER_PROTO_CMD_SETTINGS: { + return !(payload_len < 1 || payload[0] != 0x01); + } + default: { + return false; + } + } +} + +void ShellyDimmer::reset_(bool boot0) { + ESP_LOGD(TAG, "Reset STM32, boot0=%d", boot0); + + this->pin_boot0_->digital_write(boot0); + this->pin_nrst_->digital_write(false); + + // Wait 50ms for the STM32 to reset. + delay(50); // NOLINT + + // Clear receive buffer. + while (this->available()) { + this->read(); + } + + this->pin_nrst_->digital_write(true); + // Wait 50ms for the STM32 to boot. + delay(50); // NOLINT + + ESP_LOGD(TAG, "Reset STM32 done"); +} + +void ShellyDimmer::reset_normal_boot_() { + // set NONE parity in normal mode + +#ifndef USE_ESP_IDF // workaround for reconfiguring the uart + Serial.end(); + Serial.begin(115200, SERIAL_8N1); + Serial.flush(); +#endif + + this->flush(); + this->reset_(false); +} + +void ShellyDimmer::reset_dfu_boot_() { + // set EVEN parity in bootloader mode + +#ifndef USE_ESP_IDF // workaround for reconfiguring the uart + Serial.end(); + Serial.begin(115200, SERIAL_8E1); + Serial.flush(); +#endif + + this->flush(); + this->reset_(true); +} + +} // namespace shelly_dimmer +} // namespace esphome diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.h b/esphome/components/shelly_dimmer/shelly_dimmer.h new file mode 100644 index 0000000000..b7d476279e --- /dev/null +++ b/esphome/components/shelly_dimmer/shelly_dimmer.h @@ -0,0 +1,117 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/log.h" +#include "esphome/components/light/light_output.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/uart/uart.h" + +#include + +namespace esphome { +namespace shelly_dimmer { + +class ShellyDimmer : public PollingComponent, public light::LightOutput, public uart::UARTDevice { + private: + static constexpr uint16_t SHELLY_DIMMER_BUFFER_SIZE = 256; + + public: + float get_setup_priority() const override { return setup_priority::LATE; } + + void setup() override; + void update() override; + void dump_config() override; + + light::LightTraits get_traits() override { + auto traits = light::LightTraits(); + traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS}); + return traits; + } + + void setup_state(light::LightState *state) override { this->state_ = state; } + void write_state(light::LightState *state) override; + + void set_nrst_pin(GPIOPin *nrst_pin) { this->pin_nrst_ = nrst_pin; } + void set_boot0_pin(GPIOPin *boot0_pin) { this->pin_boot0_ = boot0_pin; } + + void set_leading_edge(bool leading_edge) { this->leading_edge_ = leading_edge; } + void set_warmup_brightness(uint16_t warmup_brightness) { this->warmup_brightness_ = warmup_brightness; } + void set_warmup_time(uint16_t warmup_time) { this->warmup_time_ = warmup_time; } + void set_fade_rate(uint16_t fade_rate) { this->fade_rate_ = fade_rate; } + void set_min_brightness(uint16_t min_brightness) { this->min_brightness_ = min_brightness; } + void set_max_brightness(uint16_t max_brightness) { this->max_brightness_ = max_brightness; } + + void set_power_sensor(sensor::Sensor *power_sensor) { this->power_sensor_ = power_sensor; } + void set_voltage_sensor(sensor::Sensor *voltage_sensor) { this->voltage_sensor_ = voltage_sensor; } + void set_current_sensor(sensor::Sensor *current_sensor) { this->current_sensor_ = current_sensor; } + + protected: + GPIOPin *pin_nrst_; + GPIOPin *pin_boot0_; + + // Frame parser state. + uint8_t seq_{0}; + std::array buffer_; + uint8_t buffer_pos_{0}; + + // Firmware version. + uint8_t version_major_; + uint8_t version_minor_; + + // Configuration. + bool leading_edge_{false}; + uint16_t warmup_brightness_{100}; + uint16_t warmup_time_{20}; + uint16_t fade_rate_{0}; + uint16_t min_brightness_{0}; + uint16_t max_brightness_{1000}; + + light::LightState *state_{nullptr}; + sensor::Sensor *power_sensor_{nullptr}; + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_{nullptr}; + + bool ready_{false}; + uint16_t brightness_; + + /// Convert relative brightness into a dimmer brightness value. + uint16_t convert_brightness_(float brightness); + + /// Sends the given brightness value. + void send_brightness_(uint16_t brightness); + + /// Sends dimmer configuration. + void send_settings_(); + + /// Performs a firmware upgrade. + bool upgrade_firmware_(); + + /// Sends a command and waits for an acknowledgement. + bool send_command_(uint8_t cmd, const uint8_t *payload, uint8_t len); + + /// Frames a given command payload. + size_t frame_command_(uint8_t *data, uint8_t cmd, const uint8_t *payload, size_t len); + + /// Handles a single byte as part of a protocol frame. + /// + /// Returns -1 on failure, 0 when finished and 1 when more bytes needed. + int handle_byte_(uint8_t c); + + /// Reads a response frame. + bool read_frame_(); + + /// Handles a complete frame. + bool handle_frame_(); + + /// Reset STM32 with the BOOT0 pin set to the given value. + void reset_(bool boot0); + + /// Reset STM32 to boot the regular firmware. + void reset_normal_boot_(); + + /// Reset STM32 to boot into DFU mode to enable firmware upgrades. + void reset_dfu_boot_(); +}; + +} // namespace shelly_dimmer +} // namespace esphome diff --git a/esphome/components/shelly_dimmer/stm32flash.cpp b/esphome/components/shelly_dimmer/stm32flash.cpp new file mode 100644 index 0000000000..4c777776fb --- /dev/null +++ b/esphome/components/shelly_dimmer/stm32flash.cpp @@ -0,0 +1,1061 @@ +/* + stm32flash - Open Source ST STM32 flash program for Arduino + Copyright 2010 Geoffrey McRae + Copyright 2012-2014 Tormod Volden + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "esphome/core/defines.h" +#ifdef USE_SHD_FIRMWARE_DATA + +#include + +#include "stm32flash.h" +#include "debug.h" + +#include "dev_table.h" +#include "esphome/core/log.h" + +#include +#include + +namespace { + +constexpr uint8_t STM32_ACK = 0x79; +constexpr uint8_t STM32_NACK = 0x1F; +constexpr uint8_t STM32_BUSY = 0x76; + +constexpr uint8_t STM32_CMD_INIT = 0x7F; +constexpr uint8_t STM32_CMD_GET = 0x00; /* get the version and command supported */ +constexpr uint8_t STM32_CMD_GVR = 0x01; /* get version and read protection status */ +constexpr uint8_t STM32_CMD_GID = 0x02; /* get ID */ +constexpr uint8_t STM32_CMD_RM = 0x11; /* read memory */ +constexpr uint8_t STM32_CMD_GO = 0x21; /* go */ +constexpr uint8_t STM32_CMD_WM = 0x31; /* write memory */ +constexpr uint8_t STM32_CMD_WM_NS = 0x32; /* no-stretch write memory */ +constexpr uint8_t STM32_CMD_ER = 0x43; /* erase */ +constexpr uint8_t STM32_CMD_EE = 0x44; /* extended erase */ +constexpr uint8_t STM32_CMD_EE_NS = 0x45; /* extended erase no-stretch */ +constexpr uint8_t STM32_CMD_WP = 0x63; /* write protect */ +constexpr uint8_t STM32_CMD_WP_NS = 0x64; /* write protect no-stretch */ +constexpr uint8_t STM32_CMD_UW = 0x73; /* write unprotect */ +constexpr uint8_t STM32_CMD_UW_NS = 0x74; /* write unprotect no-stretch */ +constexpr uint8_t STM32_CMD_RP = 0x82; /* readout protect */ +constexpr uint8_t STM32_CMD_RP_NS = 0x83; /* readout protect no-stretch */ +constexpr uint8_t STM32_CMD_UR = 0x92; /* readout unprotect */ +constexpr uint8_t STM32_CMD_UR_NS = 0x93; /* readout unprotect no-stretch */ +constexpr uint8_t STM32_CMD_CRC = 0xA1; /* compute CRC */ +constexpr uint8_t STM32_CMD_ERR = 0xFF; /* not a valid command */ + +constexpr uint32_t STM32_RESYNC_TIMEOUT = 35 * 1000; /* milliseconds */ +constexpr uint32_t STM32_MASSERASE_TIMEOUT = 35 * 1000; /* milliseconds */ +constexpr uint32_t STM32_PAGEERASE_TIMEOUT = 5 * 1000; /* milliseconds */ +constexpr uint32_t STM32_BLKWRITE_TIMEOUT = 1 * 1000; /* milliseconds */ +constexpr uint32_t STM32_WUNPROT_TIMEOUT = 1 * 1000; /* milliseconds */ +constexpr uint32_t STM32_WPROT_TIMEOUT = 1 * 1000; /* milliseconds */ +constexpr uint32_t STM32_RPROT_TIMEOUT = 1 * 1000; /* milliseconds */ +constexpr uint32_t DEFAULT_TIMEOUT = 5 * 1000; /* milliseconds */ + +constexpr uint8_t STM32_CMD_GET_LENGTH = 17; /* bytes in the reply */ + +/* Reset code for ARMv7-M (Cortex-M3) and ARMv6-M (Cortex-M0) + * see ARMv7-M or ARMv6-M Architecture Reference Manual (table B3-8) + * or "The definitive guide to the ARM Cortex-M3", section 14.4. + */ +constexpr uint8_t STM_RESET_CODE[] = { + 0x01, 0x49, // ldr r1, [pc, #4] ; () + 0x02, 0x4A, // ldr r2, [pc, #8] ; () + 0x0A, 0x60, // str r2, [r1, #0] + 0xfe, 0xe7, // endless: b endless + 0x0c, 0xed, 0x00, 0xe0, // .word 0xe000ed0c = NVIC AIRCR register address + 0x04, 0x00, 0xfa, 0x05 // .word 0x05fa0004 = VECTKEY | SYSRESETREQ +}; + +constexpr uint32_t STM_RESET_CODE_SIZE = sizeof(STM_RESET_CODE); + +/* RM0360, Empty check + * On STM32F070x6 and STM32F030xC devices only, internal empty check flag is + * implemented to allow easy programming of the virgin devices by the boot loader. This flag is + * used when BOOT0 pin is defining Main Flash memory as the target boot space. When the + * flag is set, the device is considered as empty and System memory (boot loader) is selected + * instead of the Main Flash as a boot space to allow user to program the Flash memory. + * This flag is updated only during Option bytes loading: it is set when the content of the + * address 0x08000 0000 is read as 0xFFFF FFFF, otherwise it is cleared. It means a power + * on or setting of OBL_LAUNCH bit in FLASH_CR register is needed to clear this flag after + * programming of a virgin device to execute user code after System reset. + */ +constexpr uint8_t STM_OBL_LAUNCH_CODE[] = { + 0x01, 0x49, // ldr r1, [pc, #4] ; () + 0x02, 0x4A, // ldr r2, [pc, #8] ; () + 0x0A, 0x60, // str r2, [r1, #0] + 0xfe, 0xe7, // endless: b endless + 0x10, 0x20, 0x02, 0x40, // address: FLASH_CR = 40022010 + 0x00, 0x20, 0x00, 0x00 // value: OBL_LAUNCH = 00002000 +}; + +constexpr uint32_t STM_OBL_LAUNCH_CODE_SIZE = sizeof(STM_OBL_LAUNCH_CODE); + +constexpr char TAG[] = "stm32flash"; + +} // Anonymous namespace + +namespace esphome { +namespace shelly_dimmer { + +namespace { + +int flash_addr_to_page_ceil(const stm32_t *stm, uint32_t addr) { + if (!(addr >= stm->dev->fl_start && addr <= stm->dev->fl_end)) + return 0; + + int page = 0; + addr -= stm->dev->fl_start; + const auto *psize = stm->dev->fl_ps; + + while (addr >= psize[0]) { + addr -= psize[0]; + page++; + if (psize[1]) + psize++; + } + + return addr ? page + 1 : page; +} + +stm32_err_t stm32_get_ack_timeout(const stm32_t *stm, uint32_t timeout) { + auto *stream = stm->stream; + uint8_t rxbyte; + + if (!(stm->flags & STREAM_OPT_RETRY)) + timeout = 0; + + if (timeout == 0) + timeout = DEFAULT_TIMEOUT; + + const uint32_t start_time = millis(); + do { + yield(); + if (!stream->available()) { + if (millis() < start_time + timeout) + continue; + ESP_LOGD(TAG, "Failed to read ACK timeout=%i", timeout); + return STM32_ERR_UNKNOWN; + } + + stream->read_byte(&rxbyte); + + if (rxbyte == STM32_ACK) + return STM32_ERR_OK; + if (rxbyte == STM32_NACK) + return STM32_ERR_NACK; + if (rxbyte != STM32_BUSY) { + ESP_LOGD(TAG, "Got byte 0x%02x instead of ACK", rxbyte); + return STM32_ERR_UNKNOWN; + } + } while (true); +} + +stm32_err_t stm32_get_ack(const stm32_t *stm) { return stm32_get_ack_timeout(stm, 0); } + +stm32_err_t stm32_send_command_timeout(const stm32_t *stm, const uint8_t cmd, const uint32_t timeout) { + auto *const stream = stm->stream; + + static constexpr auto BUFFER_SIZE = 2; + const uint8_t buf[] = { + cmd, + static_cast(cmd ^ 0xFF), + }; + static_assert(sizeof(buf) == BUFFER_SIZE, "Buf expected to be 2 bytes"); + + stream->write_array(buf, BUFFER_SIZE); + stream->flush(); + + stm32_err_t s_err = stm32_get_ack_timeout(stm, timeout); + if (s_err == STM32_ERR_OK) + return STM32_ERR_OK; + if (s_err == STM32_ERR_NACK) { + ESP_LOGD(TAG, "Got NACK from device on command 0x%02x", cmd); + } else { + ESP_LOGD(TAG, "Unexpected reply from device on command 0x%02x", cmd); + } + return STM32_ERR_UNKNOWN; +} + +stm32_err_t stm32_send_command(const stm32_t *stm, const uint8_t cmd) { + return stm32_send_command_timeout(stm, cmd, 0); +} + +/* if we have lost sync, send a wrong command and expect a NACK */ +stm32_err_t stm32_resync(const stm32_t *stm) { + auto *const stream = stm->stream; + uint32_t t0 = millis(); + auto t1 = t0; + + static constexpr auto BUFFER_SIZE = 2; + const uint8_t buf[] = { + STM32_CMD_ERR, + static_cast(STM32_CMD_ERR ^ 0xFF), + }; + static_assert(sizeof(buf) == BUFFER_SIZE, "Buf expected to be 2 bytes"); + + uint8_t ack; + while (t1 < t0 + STM32_RESYNC_TIMEOUT) { + stream->write_array(buf, BUFFER_SIZE); + stream->flush(); + if (!stream->read_array(&ack, 1)) { + t1 = millis(); + continue; + } + if (ack == STM32_NACK) + return STM32_ERR_OK; + t1 = millis(); + } + return STM32_ERR_UNKNOWN; +} + +/* + * some command receive reply frame with variable length, and length is + * embedded in reply frame itself. + * We can guess the length, but if we guess wrong the protocol gets out + * of sync. + * Use resync for frame oriented interfaces (e.g. I2C) and byte-by-byte + * read for byte oriented interfaces (e.g. UART). + * + * to run safely, data buffer should be allocated for 256+1 bytes + * + * len is value of the first byte in the frame. + */ +stm32_err_t stm32_guess_len_cmd(const stm32_t *stm, const uint8_t cmd, uint8_t *const data, unsigned int len) { + auto *const stream = stm->stream; + + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm->flags & STREAM_OPT_BYTE) { + /* interface is UART-like */ + if (!stream->read_array(data, 1)) + return STM32_ERR_UNKNOWN; + len = data[0]; + if (!stream->read_array(data + 1, len + 1)) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; + } + + const auto ret = stream->read_array(data, len + 2); + if (ret && len == data[0]) + return STM32_ERR_OK; + if (!ret) { + /* restart with only one byte */ + if (stm32_resync(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (!stream->read_array(data, 1)) + return STM32_ERR_UNKNOWN; + } + + ESP_LOGD(TAG, "Re sync (len = %d)", data[0]); + if (stm32_resync(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + len = data[0]; + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (!stream->read_array(data, len + 2)) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; +} + +/* + * Some interface, e.g. UART, requires a specific init sequence to let STM32 + * autodetect the interface speed. + * The sequence is only required one time after reset. + * This function sends the init sequence and, in case of timeout, recovers + * the interface. + */ +stm32_err_t stm32_send_init_seq(const stm32_t *stm) { + auto *const stream = stm->stream; + + stream->write_array(&STM32_CMD_INIT, 1); + stream->flush(); + + uint8_t byte; + bool ret = stream->read_array(&byte, 1); + if (ret && byte == STM32_ACK) + return STM32_ERR_OK; + if (ret && byte == STM32_NACK) { + /* We could get error later, but let's continue, for now. */ + ESP_LOGD(TAG, "Warning: the interface was not closed properly."); + return STM32_ERR_OK; + } + if (!ret) { + ESP_LOGD(TAG, "Failed to init device."); + return STM32_ERR_UNKNOWN; + } + + /* + * Check if previous STM32_CMD_INIT was taken as first byte + * of a command. Send a new byte, we should get back a NACK. + */ + stream->write_array(&STM32_CMD_INIT, 1); + stream->flush(); + + ret = stream->read_array(&byte, 1); + if (ret && byte == STM32_NACK) + return STM32_ERR_OK; + ESP_LOGD(TAG, "Failed to init device."); + return STM32_ERR_UNKNOWN; +} + +stm32_err_t stm32_mass_erase(const stm32_t *stm) { + auto *const stream = stm->stream; + + if (stm32_send_command(stm, stm->cmd->er) != STM32_ERR_OK) { + ESP_LOGD(TAG, "Can't initiate chip mass erase!"); + return STM32_ERR_UNKNOWN; + } + + /* regular erase (0x43) */ + if (stm->cmd->er == STM32_CMD_ER) { + const auto s_err = stm32_send_command_timeout(stm, 0xFF, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; + } + + /* extended erase */ + static constexpr auto BUFFER_SIZE = 3; + const uint8_t buf[] = { + 0xFF, /* 0xFFFF the magic number for mass erase */ + 0xFF, 0x00, /* checksum */ + }; + static_assert(sizeof(buf) == BUFFER_SIZE, "Expected the buffer to be 3 bytes"); + stream->write_array(buf, 3); + stream->flush(); + + const auto s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + ESP_LOGD(TAG, "Mass erase failed. Try specifying the number of pages to be erased."); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +template std::unique_ptr malloc_array_raii(size_t size) { + // Could be constexpr in c++17 + static const auto DELETOR = [](T *memory) { + free(memory); // NOLINT + }; + return std::unique_ptr{static_cast(malloc(size)), // NOLINT + DELETOR}; +} + +stm32_err_t stm32_pages_erase(const stm32_t *stm, const uint32_t spage, const uint32_t pages) { + auto *const stream = stm->stream; + uint8_t cs = 0; + int i = 0; + + /* The erase command reported by the bootloader is either 0x43, 0x44 or 0x45 */ + /* 0x44 is Extended Erase, a 2 byte based protocol and needs to be handled differently. */ + /* 0x45 is clock no-stretching version of Extended Erase for I2C port. */ + if (stm32_send_command(stm, stm->cmd->er) != STM32_ERR_OK) { + ESP_LOGD(TAG, "Can't initiate chip mass erase!"); + return STM32_ERR_UNKNOWN; + } + + /* regular erase (0x43) */ + if (stm->cmd->er == STM32_CMD_ER) { + // Free memory with RAII + auto buf = malloc_array_raii(1 + pages + 1); + + if (!buf) + return STM32_ERR_UNKNOWN; + + buf[i++] = pages - 1; + cs ^= (pages - 1); + for (auto pg_num = spage; pg_num < (pages + spage); pg_num++) { + buf[i++] = pg_num; + cs ^= pg_num; + } + buf[i++] = cs; + stream->write_array(&buf[0], i); + stream->flush(); + + const auto s_err = stm32_get_ack_timeout(stm, pages * STM32_PAGEERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; + } + + /* extended erase */ + + // Free memory with RAII + auto buf = malloc_array_raii(2 + 2 * pages + 1); + + if (!buf) + return STM32_ERR_UNKNOWN; + + /* Number of pages to be erased - 1, two bytes, MSB first */ + uint8_t pg_byte = (pages - 1) >> 8; + buf[i++] = pg_byte; + cs ^= pg_byte; + pg_byte = (pages - 1) & 0xFF; + buf[i++] = pg_byte; + cs ^= pg_byte; + + for (auto pg_num = spage; pg_num < spage + pages; pg_num++) { + pg_byte = pg_num >> 8; + cs ^= pg_byte; + buf[i++] = pg_byte; + pg_byte = pg_num & 0xFF; + cs ^= pg_byte; + buf[i++] = pg_byte; + } + buf[i++] = cs; + stream->write_array(&buf[0], i); + stream->flush(); + + const auto s_err = stm32_get_ack_timeout(stm, pages * STM32_PAGEERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + ESP_LOGD(TAG, "Page-by-page erase failed. Check the maximum pages your device supports."); + return STM32_ERR_UNKNOWN; + } + + return STM32_ERR_OK; +} + +template stm32_err_t stm32_check_ack_timeout(const stm32_err_t s_err, const T &&log) { + switch (s_err) { + case STM32_ERR_OK: + return STM32_ERR_OK; + case STM32_ERR_NACK: + log(); + // TODO: c++17 [[fallthrough]] + /* fallthrough */ + default: + return STM32_ERR_UNKNOWN; + } +} + +/* detect CPU endian */ +bool cpu_le() { + static constexpr int N = 1; + + // returns true if little endian + return *reinterpret_cast(&N) == 1; +} + +uint32_t le_u32(const uint32_t v) { + if (!cpu_le()) + return ((v & 0xFF000000) >> 24) | ((v & 0x00FF0000) >> 8) | ((v & 0x0000FF00) << 8) | ((v & 0x000000FF) << 24); + return v; +} + +template void populate_buffer_with_address(uint8_t (&buffer)[N], uint32_t address) { + buffer[0] = static_cast(address >> 24); + buffer[1] = static_cast((address >> 16) & 0xFF); + buffer[2] = static_cast((address >> 8) & 0xFF); + buffer[3] = static_cast(address & 0xFF); + buffer[4] = static_cast(buffer[0] ^ buffer[1] ^ buffer[2] ^ buffer[3]); +} + +} // Anonymous namespace + +} // namespace shelly_dimmer +} // namespace esphome + +namespace esphome { +namespace shelly_dimmer { + +/* find newer command by higher code */ +#define newer(prev, a) (((prev) == STM32_CMD_ERR) ? (a) : (((prev) > (a)) ? (prev) : (a))) + +stm32_t *stm32_init(uart::UARTDevice *stream, const uint8_t flags, const char init) { + uint8_t buf[257]; + + // Could be constexpr in c++17 + static const auto CLOSE = [](stm32_t *stm32) { stm32_close(stm32); }; + + // Cleanup with RAII + std::unique_ptr stm{static_cast(calloc(sizeof(stm32_t), 1)), // NOLINT + CLOSE}; + + if (!stm) { + return nullptr; + } + stm->stream = stream; + stm->flags = flags; + + stm->cmd = static_cast(malloc(sizeof(stm32_cmd_t))); // NOLINT + if (!stm->cmd) { + return nullptr; + } + memset(stm->cmd, STM32_CMD_ERR, sizeof(stm32_cmd_t)); + + if ((stm->flags & STREAM_OPT_CMD_INIT) && init) { + if (stm32_send_init_seq(stm.get()) != STM32_ERR_OK) + return nullptr; // NOLINT + } + + /* get the version and read protection status */ + if (stm32_send_command(stm.get(), STM32_CMD_GVR) != STM32_ERR_OK) { + return nullptr; // NOLINT + } + + /* From AN, only UART bootloader returns 3 bytes */ + { + const auto len = (stm->flags & STREAM_OPT_GVR_ETX) ? 3 : 1; + if (!stream->read_array(buf, len)) + return nullptr; // NOLINT + stm->version = buf[0]; + stm->option1 = (stm->flags & STREAM_OPT_GVR_ETX) ? buf[1] : 0; + stm->option2 = (stm->flags & STREAM_OPT_GVR_ETX) ? buf[2] : 0; + if (stm32_get_ack(stm.get()) != STM32_ERR_OK) { + return nullptr; + } + } + + { + const auto len = ([&]() { + /* get the bootloader information */ + if (stm->cmd_get_reply) { + for (auto i = 0; stm->cmd_get_reply[i].length; ++i) { + if (stm->version == stm->cmd_get_reply[i].version) { + return stm->cmd_get_reply[i].length; + } + } + } + + return STM32_CMD_GET_LENGTH; + })(); + + if (stm32_guess_len_cmd(stm.get(), STM32_CMD_GET, buf, len) != STM32_ERR_OK) + return nullptr; + } + + const auto stop = buf[0] + 1; + stm->bl_version = buf[1]; + int new_cmds = 0; + for (auto i = 1; i < stop; ++i) { + const auto val = buf[i + 1]; + switch (val) { + case STM32_CMD_GET: + stm->cmd->get = val; + break; + case STM32_CMD_GVR: + stm->cmd->gvr = val; + break; + case STM32_CMD_GID: + stm->cmd->gid = val; + break; + case STM32_CMD_RM: + stm->cmd->rm = val; + break; + case STM32_CMD_GO: + stm->cmd->go = val; + break; + case STM32_CMD_WM: + case STM32_CMD_WM_NS: + stm->cmd->wm = newer(stm->cmd->wm, val); + break; + case STM32_CMD_ER: + case STM32_CMD_EE: + case STM32_CMD_EE_NS: + stm->cmd->er = newer(stm->cmd->er, val); + break; + case STM32_CMD_WP: + case STM32_CMD_WP_NS: + stm->cmd->wp = newer(stm->cmd->wp, val); + break; + case STM32_CMD_UW: + case STM32_CMD_UW_NS: + stm->cmd->uw = newer(stm->cmd->uw, val); + break; + case STM32_CMD_RP: + case STM32_CMD_RP_NS: + stm->cmd->rp = newer(stm->cmd->rp, val); + break; + case STM32_CMD_UR: + case STM32_CMD_UR_NS: + stm->cmd->ur = newer(stm->cmd->ur, val); + break; + case STM32_CMD_CRC: + stm->cmd->crc = newer(stm->cmd->crc, val); + break; + default: + if (new_cmds++ == 0) { + ESP_LOGD(TAG, "GET returns unknown commands (0x%2x", val); + } else { + ESP_LOGD(TAG, ", 0x%2x", val); + } + } + } + if (new_cmds) + ESP_LOGD(TAG, ")"); + if (stm32_get_ack(stm.get()) != STM32_ERR_OK) { + return nullptr; + } + + if (stm->cmd->get == STM32_CMD_ERR || stm->cmd->gvr == STM32_CMD_ERR || stm->cmd->gid == STM32_CMD_ERR) { + ESP_LOGD(TAG, "Error: bootloader did not returned correct information from GET command"); + return nullptr; + } + + /* get the device ID */ + if (stm32_guess_len_cmd(stm.get(), stm->cmd->gid, buf, 1) != STM32_ERR_OK) { + return nullptr; + } + const auto returned = buf[0] + 1; + if (returned < 2) { + ESP_LOGD(TAG, "Only %d bytes sent in the PID, unknown/unsupported device", returned); + return nullptr; + } + stm->pid = (buf[1] << 8) | buf[2]; + if (returned > 2) { + ESP_LOGD(TAG, "This bootloader returns %d extra bytes in PID:", returned); + for (auto i = 2; i <= returned; i++) + ESP_LOGD(TAG, " %02x", buf[i]); + } + if (stm32_get_ack(stm.get()) != STM32_ERR_OK) { + return nullptr; + } + + stm->dev = DEVICES; + while (stm->dev->id != 0x00 && stm->dev->id != stm->pid) + ++stm->dev; + + if (!stm->dev->id) { + ESP_LOGD(TAG, "Unknown/unsupported device (Device ID: 0x%03x)", stm->pid); + return nullptr; + } + + // TODO: Would be much better if the unique_ptr was returned from this function + // Release ownership of unique_ptr + return stm.release(); // NOLINT +} + +void stm32_close(stm32_t *stm) { + if (stm) + free(stm->cmd); // NOLINT + free(stm); // NOLINT +} + +stm32_err_t stm32_read_memory(const stm32_t *stm, const uint32_t address, uint8_t *data, const unsigned int len) { + auto *const stream = stm->stream; + + if (!len) + return STM32_ERR_OK; + + if (len > 256) { + ESP_LOGD(TAG, "Error: READ length limit at 256 bytes"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->rm == STM32_CMD_ERR) { + ESP_LOGD(TAG, "Error: READ command not implemented in bootloader."); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->rm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + static constexpr auto BUFFER_SIZE = 5; + uint8_t buf[BUFFER_SIZE]; + populate_buffer_with_address(buf, address); + + stream->write_array(buf, BUFFER_SIZE); + stream->flush(); + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_send_command(stm, len - 1) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (!stream->read_array(data, len)) + return STM32_ERR_UNKNOWN; + + return STM32_ERR_OK; +} + +stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, const uint8_t *data, const unsigned int len) { + auto *const stream = stm->stream; + + if (!len) + return STM32_ERR_OK; + + if (len > 256) { + ESP_LOGD(TAG, "Error: READ length limit at 256 bytes"); + return STM32_ERR_UNKNOWN; + } + + /* must be 32bit aligned */ + if (address & 0x3) { + ESP_LOGD(TAG, "Error: WRITE address must be 4 byte aligned"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->wm == STM32_CMD_ERR) { + ESP_LOGD(TAG, "Error: WRITE command not implemented in bootloader."); + return STM32_ERR_NO_CMD; + } + + /* send the address and checksum */ + if (stm32_send_command(stm, stm->cmd->wm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + static constexpr auto BUFFER_SIZE = 5; + uint8_t buf1[BUFFER_SIZE]; + populate_buffer_with_address(buf1, address); + + stream->write_array(buf1, BUFFER_SIZE); + stream->flush(); + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + const unsigned int aligned_len = (len + 3) & ~3; + uint8_t cs = aligned_len - 1; + uint8_t buf[256 + 2]; + + buf[0] = aligned_len - 1; + for (auto i = 0; i < len; i++) { + cs ^= data[i]; + buf[i + 1] = data[i]; + } + /* padding data */ + for (auto i = len; i < aligned_len; i++) { + cs ^= 0xFF; + buf[i + 1] = 0xFF; + } + buf[aligned_len + 1] = cs; + stream->write_array(buf, aligned_len + 2); + stream->flush(); + + const auto s_err = stm32_get_ack_timeout(stm, STM32_BLKWRITE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_wunprot_memory(const stm32_t *stm) { + if (stm->cmd->uw == STM32_CMD_ERR) { + ESP_LOGD(TAG, "Error: WRITE UNPROTECT command not implemented in bootloader."); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->uw) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + return stm32_check_ack_timeout(stm32_get_ack_timeout(stm, STM32_WUNPROT_TIMEOUT), + []() { ESP_LOGD(TAG, "Error: Failed to WRITE UNPROTECT"); }); +} + +stm32_err_t stm32_wprot_memory(const stm32_t *stm) { + if (stm->cmd->wp == STM32_CMD_ERR) { + ESP_LOGD(TAG, "Error: WRITE PROTECT command not implemented in bootloader."); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->wp) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + return stm32_check_ack_timeout(stm32_get_ack_timeout(stm, STM32_WPROT_TIMEOUT), + []() { ESP_LOGD(TAG, "Error: Failed to WRITE PROTECT"); }); +} + +stm32_err_t stm32_runprot_memory(const stm32_t *stm) { + if (stm->cmd->ur == STM32_CMD_ERR) { + ESP_LOGD(TAG, "Error: READOUT UNPROTECT command not implemented in bootloader."); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->ur) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + return stm32_check_ack_timeout(stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT), + []() { ESP_LOGD(TAG, "Error: Failed to READOUT UNPROTECT"); }); +} + +stm32_err_t stm32_readprot_memory(const stm32_t *stm) { + if (stm->cmd->rp == STM32_CMD_ERR) { + ESP_LOGD(TAG, "Error: READOUT PROTECT command not implemented in bootloader."); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->rp) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + return stm32_check_ack_timeout(stm32_get_ack_timeout(stm, STM32_RPROT_TIMEOUT), + []() { ESP_LOGD(TAG, "Error: Failed to READOUT PROTECT"); }); +} + +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint32_t spage, uint32_t pages) { + if (!pages || spage > STM32_MAX_PAGES || ((pages != STM32_MASS_ERASE) && ((spage + pages) > STM32_MAX_PAGES))) + return STM32_ERR_OK; + + if (stm->cmd->er == STM32_CMD_ERR) { + ESP_LOGD(TAG, "Error: ERASE command not implemented in bootloader."); + return STM32_ERR_NO_CMD; + } + + if (pages == STM32_MASS_ERASE) { + /* + * Not all chips support mass erase. + * Mass erase can be obtained executing a "readout protect" + * followed by "readout un-protect". This method is not + * suggested because can hang the target if a debug SWD/JTAG + * is connected. When the target enters in "readout + * protection" mode it will consider the debug connection as + * a tentative of intrusion and will hang. + * Erasing the flash page-by-page is the safer way to go. + */ + if (!(stm->dev->flags & F_NO_ME)) + return stm32_mass_erase(stm); + + pages = flash_addr_to_page_ceil(stm, stm->dev->fl_end); + } + + /* + * Some device, like STM32L152, cannot erase more than 512 pages in + * one command. Split the call. + */ + static constexpr uint32_t MAX_PAGE_SIZE = 512; + while (pages) { + const auto n = std::min(pages, MAX_PAGE_SIZE); + const auto s_err = stm32_pages_erase(stm, spage, n); + if (s_err != STM32_ERR_OK) + return s_err; + spage += n; + pages -= n; + } + return STM32_ERR_OK; +} + +static stm32_err_t stm32_run_raw_code(const stm32_t *stm, uint32_t target_address, const uint8_t *code, + uint32_t code_size) { + static constexpr uint32_t BUFFER_SIZE = 256; + + const auto stack_le = le_u32(0x20002000); + const auto code_address_le = le_u32(target_address + 8 + 1); // thumb mode address (!) + uint32_t length = code_size + 8; + + /* Must be 32-bit aligned */ + if (target_address & 0x3) { + ESP_LOGD(TAG, "Error: code address must be 4 byte aligned"); + return STM32_ERR_UNKNOWN; + } + + // Could be constexpr in c++17 + static const auto DELETOR = [](uint8_t *memory) { + free(memory); // NOLINT + }; + + // Free memory with RAII + std::unique_ptr mem{static_cast(malloc(length)), // NOLINT + DELETOR}; + + if (!mem) + return STM32_ERR_UNKNOWN; + + memcpy(mem.get(), &stack_le, sizeof(stack_le)); + memcpy(mem.get() + 4, &code_address_le, sizeof(code_address_le)); + memcpy(mem.get() + 8, code, code_size); + + auto *pos = mem.get(); + auto address = target_address; + while (length > 0) { + const auto w = std::min(length, BUFFER_SIZE); + if (stm32_write_memory(stm, address, pos, w) != STM32_ERR_OK) { + return STM32_ERR_UNKNOWN; + } + + address += w; + pos += w; + length -= w; + } + + return stm32_go(stm, target_address); +} + +stm32_err_t stm32_go(const stm32_t *stm, const uint32_t address) { + auto *const stream = stm->stream; + + if (stm->cmd->go == STM32_CMD_ERR) { + ESP_LOGD(TAG, "Error: GO command not implemented in bootloader."); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->go) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + static constexpr auto BUFFER_SIZE = 5; + uint8_t buf[BUFFER_SIZE]; + populate_buffer_with_address(buf, address); + + stream->write_array(buf, BUFFER_SIZE); + stream->flush(); + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; +} + +stm32_err_t stm32_reset_device(const stm32_t *stm) { + const auto target_address = stm->dev->ram_start; + + if (stm->dev->flags & F_OBLL) { + /* set the OBL_LAUNCH bit to reset device (see RM0360, 2.5) */ + return stm32_run_raw_code(stm, target_address, STM_OBL_LAUNCH_CODE, STM_OBL_LAUNCH_CODE_SIZE); + } else { + return stm32_run_raw_code(stm, target_address, STM_RESET_CODE, STM_RESET_CODE_SIZE); + } +} + +stm32_err_t stm32_crc_memory(const stm32_t *stm, const uint32_t address, const uint32_t length, uint32_t *const crc) { + static constexpr auto BUFFER_SIZE = 5; + auto *const stream = stm->stream; + + if (address & 0x3 || length & 0x3) { + ESP_LOGD(TAG, "Start and end addresses must be 4 byte aligned"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->crc == STM32_CMD_ERR) { + ESP_LOGD(TAG, "Error: CRC command not implemented in bootloader."); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->crc) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + { + static constexpr auto BUFFER_SIZE = 5; + uint8_t buf[BUFFER_SIZE]; + populate_buffer_with_address(buf, address); + + stream->write_array(buf, BUFFER_SIZE); + stream->flush(); + } + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + { + static constexpr auto BUFFER_SIZE = 5; + uint8_t buf[BUFFER_SIZE]; + populate_buffer_with_address(buf, address); + + stream->write_array(buf, BUFFER_SIZE); + stream->flush(); + } + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + { + uint8_t buf[BUFFER_SIZE]; + if (!stream->read_array(buf, BUFFER_SIZE)) + return STM32_ERR_UNKNOWN; + + if (buf[4] != (buf[0] ^ buf[1] ^ buf[2] ^ buf[3])) + return STM32_ERR_UNKNOWN; + + *crc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + } + + return STM32_ERR_OK; +} + +/* + * CRC computed by STM32 is similar to the standard crc32_be() + * implemented, for example, in Linux kernel in ./lib/crc32.c + * But STM32 computes it on units of 32 bits word and swaps the + * bytes of the word before the computation. + * Due to byte swap, I cannot use any CRC available in existing + * libraries, so here is a simple not optimized implementation. + */ +uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len) { + static constexpr uint32_t CRCPOLY_BE = 0x04c11db7; + static constexpr uint32_t CRC_MSBMASK = 0x80000000; + + if (len & 0x3) { + ESP_LOGD(TAG, "Buffer length must be multiple of 4 bytes"); + return 0; + } + + while (len) { + uint32_t data = *buf++; + data |= *buf++ << 8; + data |= *buf++ << 16; + data |= *buf++ << 24; + len -= 4; + + crc ^= data; + + for (size_t i = 0; i < 32; ++i) { + if (crc & CRC_MSBMASK) { + crc = (crc << 1) ^ CRCPOLY_BE; + } else { + crc = (crc << 1); + } + } + } + return crc; +} + +stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, uint32_t length, uint32_t *crc) { + static constexpr uint32_t CRC_INIT_VALUE = 0xFFFFFFFF; + static constexpr uint32_t BUFFER_SIZE = 256; + + uint8_t buf[BUFFER_SIZE]; + + if (address & 0x3 || length & 0x3) { + ESP_LOGD(TAG, "Start and end addresses must be 4 byte aligned"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->crc != STM32_CMD_ERR) + return stm32_crc_memory(stm, address, length, crc); + + const auto start = address; + const auto total_len = length; + uint32_t current_crc = CRC_INIT_VALUE; + while (length) { + const auto len = std::min(BUFFER_SIZE, length); + if (stm32_read_memory(stm, address, buf, len) != STM32_ERR_OK) { + ESP_LOGD(TAG, "Failed to read memory at address 0x%08x, target write-protected?", address); + return STM32_ERR_UNKNOWN; + } + current_crc = stm32_sw_crc(current_crc, buf, len); + length -= len; + address += len; + + ESP_LOGD(TAG, "\rCRC address 0x%08x (%.2f%%) ", address, (100.0f / (float) total_len) * (float) (address - start)); + } + ESP_LOGD(TAG, "Done."); + *crc = current_crc; + return STM32_ERR_OK; +} + +} // namespace shelly_dimmer +} // namespace esphome +#endif diff --git a/esphome/components/shelly_dimmer/stm32flash.h b/esphome/components/shelly_dimmer/stm32flash.h new file mode 100644 index 0000000000..c561375c38 --- /dev/null +++ b/esphome/components/shelly_dimmer/stm32flash.h @@ -0,0 +1,129 @@ +/* + stm32flash - Open Source ST STM32 flash program for Arduino + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "esphome/core/defines.h" +#ifdef USE_SHD_FIRMWARE_DATA + +#include +#include "esphome/components/uart/uart.h" + +namespace esphome { +namespace shelly_dimmer { + +/* flags */ +constexpr auto STREAM_OPT_BYTE = (1 << 0); /* byte (not frame) oriented */ +constexpr auto STREAM_OPT_GVR_ETX = (1 << 1); /* cmd GVR returns protection status */ +constexpr auto STREAM_OPT_CMD_INIT = (1 << 2); /* use INIT cmd to autodetect speed */ +constexpr auto STREAM_OPT_RETRY = (1 << 3); /* allowed read() retry after timeout */ +constexpr auto STREAM_OPT_I2C = (1 << 4); /* i2c */ +constexpr auto STREAM_OPT_STRETCH_W = (1 << 5); /* warning for no-stretching commands */ + +constexpr auto STREAM_SERIAL = (STREAM_OPT_BYTE | STREAM_OPT_GVR_ETX | STREAM_OPT_CMD_INIT | STREAM_OPT_RETRY); +constexpr auto STREAM_I2C = (STREAM_OPT_I2C | STREAM_OPT_STRETCH_W); + +constexpr auto STM32_MAX_RX_FRAME = 256; /* cmd read memory */ +constexpr auto STM32_MAX_TX_FRAME = (1 + 256 + 1); /* cmd write memory */ + +constexpr auto STM32_MAX_PAGES = 0x0000ffff; +constexpr auto STM32_MASS_ERASE = 0x00100000; /* > 2 x max_pages */ + +using stm32_err_t = enum Stm32Err { + STM32_ERR_OK = 0, + STM32_ERR_UNKNOWN, /* Generic error */ + STM32_ERR_NACK, + STM32_ERR_NO_CMD, /* Command not available in bootloader */ +}; + +using flags_t = enum Flags { + F_NO_ME = 1 << 0, /* Mass-Erase not supported */ + F_OBLL = 1 << 1, /* OBL_LAUNCH required */ +}; + +using stm32_cmd_t = struct Stm32Cmd { + uint8_t get; + uint8_t gvr; + uint8_t gid; + uint8_t rm; + uint8_t go; + uint8_t wm; + uint8_t er; /* this may be extended erase */ + uint8_t wp; + uint8_t uw; + uint8_t rp; + uint8_t ur; + uint8_t crc; +}; + +using stm32_dev_t = struct Stm32Dev { // NOLINT + const uint16_t id; + const char *name; + const uint32_t ram_start, ram_end; + const uint32_t fl_start, fl_end; + const uint16_t fl_pps; // pages per sector + const uint32_t *fl_ps; // page size + const uint32_t opt_start, opt_end; + const uint32_t mem_start, mem_end; + const uint32_t flags; +}; + +using stm32_t = struct Stm32 { + uart::UARTDevice *stream; + uint8_t flags; + struct VarlenCmd *cmd_get_reply; + uint8_t bl_version; + uint8_t version; + uint8_t option1, option2; + uint16_t pid; + stm32_cmd_t *cmd; + const stm32_dev_t *dev; +}; + +/* + * Specify the length of reply for command GET + * This is helpful for frame-oriented protocols, e.g. i2c, to avoid time + * consuming try-fail-timeout-retry operation. + * On byte-oriented protocols, i.e. UART, this information would be skipped + * after read the first byte, so not needed. + */ +struct VarlenCmd { + uint8_t version; + uint8_t length; +}; + +stm32_t *stm32_init(uart::UARTDevice *stream, uint8_t flags, char init); +void stm32_close(stm32_t *stm); +stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, uint8_t *data, unsigned int len); +stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, const uint8_t *data, unsigned int len); +stm32_err_t stm32_wunprot_memory(const stm32_t *stm); +stm32_err_t stm32_wprot_memory(const stm32_t *stm); +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint32_t spage, uint32_t pages); +stm32_err_t stm32_go(const stm32_t *stm, uint32_t address); +stm32_err_t stm32_reset_device(const stm32_t *stm); +stm32_err_t stm32_readprot_memory(const stm32_t *stm); +stm32_err_t stm32_runprot_memory(const stm32_t *stm); +stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, uint32_t length, uint32_t *crc); +stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, uint32_t length, uint32_t *crc); +uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len); + +} // namespace shelly_dimmer +} // namespace esphome + +#endif // USE_SHD_FIRMWARE_DATA diff --git a/esphome/core/defines.h b/esphome/core/defines.h index f304f847a5..c854e2b987 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -93,3 +93,9 @@ //#define USE_BSEC // Requires a library with proprietary license. #define USE_DASHBOARD_IMPORT + +// Dummy firmware payload for shelly_dimmer +#define USE_SHD_FIRMWARE_MAJOR_VERSION 56 +#define USE_SHD_FIRMWARE_MINOR_VERSION 5 +#define USE_SHD_FIRMWARE_DATA \ + {} diff --git a/tests/test1.yaml b/tests/test1.yaml index 77c4a76bda..98a3ffcf4b 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1751,6 +1751,18 @@ light: to: 25 - single_light_id: ${roomname}_lights + - platform: shelly_dimmer + name: "Shelly Dimmer Light" + power: + name: "Shelly Dimmer Power" + voltage: + name: "Shelly Dimmer Voltage" + current: + name: "Shelly Dimmer Current" + max_brightness: 500 + firmware: "51.6" + uart_id: uart0 + remote_transmitter: - pin: 32 carrier_duty_percent: 100% From 2243021b581ad1f855de5976e2152fcdc4e97f91 Mon Sep 17 00:00:00 2001 From: Janez Troha <239513+dz0ny@users.noreply.github.com> Date: Thu, 14 Apr 2022 03:42:43 +0200 Subject: [PATCH 233/273] Allocate smaller amount of buffer for JSON (#3384) --- esphome/components/json/json_util.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 10179c9954..2bd8112255 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -23,13 +23,13 @@ std::string build_json(const json_build_t &f) { #ifdef USE_ESP8266 const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance) #elif defined(USE_ESP32) - const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); + const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); #endif - const size_t request_size = std::min(free_heap - 2048, (size_t) 5120); + const size_t request_size = std::min(free_heap, (size_t) 512); DynamicJsonDocument json_document(request_size); - if (json_document.memoryPool().buffer() == nullptr) { + if (json_document.capacity() == 0) { ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, largest free heap block: %u bytes", request_size, free_heap); return "{}"; @@ -37,7 +37,7 @@ std::string build_json(const json_build_t &f) { JsonObject root = json_document.to(); f(root); json_document.shrinkToFit(); - + ESP_LOGV(TAG, "Size after shrink %u bytes", json_document.capacity()); std::string output; serializeJson(json_document, output); return output; @@ -51,13 +51,13 @@ void parse_json(const std::string &data, const json_parse_t &f) { #ifdef USE_ESP8266 const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance) #elif defined(USE_ESP32) - const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); + const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); #endif bool pass = false; - size_t request_size = std::min(free_heap - 2048, (size_t)(data.size() * 1.5)); + size_t request_size = std::min(free_heap, (size_t)(data.size() * 1.5)); do { DynamicJsonDocument json_document(request_size); - if (json_document.memoryPool().buffer() == nullptr) { + if (json_document.capacity() == 0) { ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, free heap: %u", request_size, free_heap); return; From dcb226b20221fd481d15e8f4925120b64b138fdf Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 14 Apr 2022 13:48:35 +1200 Subject: [PATCH 234/273] Bump version to 2022.4.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 01d2d59c3d..04fa5c2bf7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.4.0b1" +__version__ = "2022.4.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 6b393438e9177bd3e547e962cff9d6559a161407 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Mon, 18 Apr 2022 22:42:02 +0200 Subject: [PATCH 235/273] Fix power_delivered/produced_phase sensor deviceclass in DSMR (#3395) --- esphome/components/dsmr/sensor.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/dsmr/sensor.py b/esphome/components/dsmr/sensor.py index bb4722655c..0b0439baa4 100644 --- a/esphome/components/dsmr/sensor.py +++ b/esphome/components/dsmr/sensor.py @@ -143,37 +143,37 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional("power_delivered_l1"): sensor.sensor_schema( unit_of_measurement=UNIT_KILOWATT, accuracy_decimals=3, - device_class=DEVICE_CLASS_CURRENT, + device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional("power_delivered_l2"): sensor.sensor_schema( unit_of_measurement=UNIT_KILOWATT, accuracy_decimals=3, - device_class=DEVICE_CLASS_CURRENT, + device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional("power_delivered_l3"): sensor.sensor_schema( unit_of_measurement=UNIT_KILOWATT, accuracy_decimals=3, - device_class=DEVICE_CLASS_CURRENT, + device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional("power_returned_l1"): sensor.sensor_schema( unit_of_measurement=UNIT_KILOWATT, accuracy_decimals=3, - device_class=DEVICE_CLASS_CURRENT, + device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional("power_returned_l2"): sensor.sensor_schema( unit_of_measurement=UNIT_KILOWATT, accuracy_decimals=3, - device_class=DEVICE_CLASS_CURRENT, + device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional("power_returned_l3"): sensor.sensor_schema( unit_of_measurement=UNIT_KILOWATT, accuracy_decimals=3, - device_class=DEVICE_CLASS_CURRENT, + device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_delivered_l1"): sensor.sensor_schema( From 9283559c6b0649a4dc2f5b4795d9da6b49e3f771 Mon Sep 17 00:00:00 2001 From: rnauber <7414650+rnauber@users.noreply.github.com> Date: Mon, 18 Apr 2022 22:43:34 +0200 Subject: [PATCH 236/273] Shelly Dimmer: Delete obsolete LICENSE.txt (#3394) --- esphome/components/shelly_dimmer/LICENSE.txt | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 esphome/components/shelly_dimmer/LICENSE.txt diff --git a/esphome/components/shelly_dimmer/LICENSE.txt b/esphome/components/shelly_dimmer/LICENSE.txt deleted file mode 100644 index 524fe0d514..0000000000 --- a/esphome/components/shelly_dimmer/LICENSE.txt +++ /dev/null @@ -1,2 +0,0 @@ -The firmware files for the STM microcontroller (shelly-dimmer-stm32_*.bin) are taken from -https://github.com/jamesturton/shelly-dimmer-stm32 and GPLv3 licensed. From 712115b6ce682a03fce071f59a5d7e54b9c8905e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 19 Apr 2022 12:33:38 +1200 Subject: [PATCH 237/273] Bump version to 2022.4.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 04fa5c2bf7..bca64e8175 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.4.0b2" +__version__ = "2022.4.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From ad41c07a1f704d938cbc6a5ffb2542a5a438f76e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Apr 2022 06:56:09 +1200 Subject: [PATCH 238/273] Dont require {} for wifi ap with defaults (#3404) --- esphome/components/wifi/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 20f43cb450..b56902df2f 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -126,6 +126,13 @@ WIFI_NETWORK_AP = WIFI_NETWORK_BASE.extend( } ) + +def wifi_network_ap(value): + if value is None: + value = {} + return WIFI_NETWORK_AP(value) + + WIFI_NETWORK_STA = WIFI_NETWORK_BASE.extend( { cv.Optional(CONF_BSSID): cv.mac_address, @@ -252,7 +259,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_PASSWORD): validate_password, cv.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, cv.Optional(CONF_EAP): EAP_AUTH_SCHEMA, - cv.Optional(CONF_AP): WIFI_NETWORK_AP, + cv.Optional(CONF_AP): wifi_network_ap, cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, cv.Optional( CONF_REBOOT_TIMEOUT, default="15min" From e26e0d7c01d7cfd0844866a2ec97273e34125955 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Apr 2022 16:35:43 +1200 Subject: [PATCH 239/273] Bump version to 2022.4.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index bca64e8175..66f8b26d17 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.4.0b3" +__version__ = "2022.4.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From a8c1b63edb20ab08895656d96f149ec4e1371c5d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Apr 2022 17:06:08 +1200 Subject: [PATCH 240/273] Bump version to 2022.4.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 66f8b26d17..161b60f7fa 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.4.0b4" +__version__ = "2022.4.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From d6e039a1d14ab6473321d2fc6d1a47158accd672 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 11 May 2022 12:50:42 +1200 Subject: [PATCH 241/273] Bump version to 2022.5.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index fc928dc530..c254edf1ee 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.5.0-dev" +__version__ = "2022.5.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 63096ac2bcc03c48e7a076e8efaa8ad19411b828 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Wed, 11 May 2022 23:25:00 +0200 Subject: [PATCH 242/273] On epoch sync, restore local TZ (#3462) Co-authored-by: Maurice Makaay --- esphome/components/time/real_time_clock.cpp | 12 ++++++++++-- esphome/components/time/real_time_clock.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index 36c5f4161d..7b5f0aa49b 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -13,11 +13,11 @@ static const char *const TAG = "time"; RealTimeClock::RealTimeClock() = default; void RealTimeClock::call_setup() { - setenv("TZ", this->timezone_.c_str(), 1); - tzset(); + this->apply_timezone_(); PollingComponent::call_setup(); } void RealTimeClock::synchronize_epoch_(uint32_t epoch) { + // Update UTC epoch time. struct timeval timev { .tv_sec = static_cast(epoch), .tv_usec = 0, }; @@ -30,6 +30,9 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) { ret = settimeofday(&timev, nullptr); } + // Move timezone back to local timezone. + this->apply_timezone_(); + if (ret != 0) { ESP_LOGW(TAG, "setimeofday() failed with code %d", ret); } @@ -41,6 +44,11 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) { this->time_sync_callback_.call(); } +void RealTimeClock::apply_timezone_() { + setenv("TZ", this->timezone_.c_str(), 1); + tzset(); +} + size_t ESPTime::strftime(char *buffer, size_t buffer_len, const char *format) { struct tm c_tm = this->to_c_tm(); return ::strftime(buffer, buffer_len, format, &c_tm); diff --git a/esphome/components/time/real_time_clock.h b/esphome/components/time/real_time_clock.h index b22c6f04d7..7f4afee306 100644 --- a/esphome/components/time/real_time_clock.h +++ b/esphome/components/time/real_time_clock.h @@ -137,6 +137,7 @@ class RealTimeClock : public PollingComponent { void synchronize_epoch_(uint32_t epoch); std::string timezone_{}; + void apply_timezone_(); CallbackManager time_sync_callback_; }; From 40f622949e2b2d3b21ebb419324c70cf9605cc30 Mon Sep 17 00:00:00 2001 From: Niclas Larsson Date: Thu, 12 May 2022 00:26:51 +0200 Subject: [PATCH 243/273] Shelly dimmer: Use unique_ptr to handle the lifetime of stm32_t (#3400) Co-authored-by: Martin <25747549+martgras@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../shelly_dimmer/shelly_dimmer.cpp | 9 +- .../components/shelly_dimmer/stm32flash.cpp | 119 +++++++++--------- esphome/components/shelly_dimmer/stm32flash.h | 28 +++-- 3 files changed, 79 insertions(+), 77 deletions(-) diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.cpp b/esphome/components/shelly_dimmer/shelly_dimmer.cpp index 3b79d0bf57..32c556da5e 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.cpp +++ b/esphome/components/shelly_dimmer/shelly_dimmer.cpp @@ -158,11 +158,8 @@ bool ShellyDimmer::upgrade_firmware_() { ESP_LOGW(TAG, "Starting STM32 firmware upgrade"); this->reset_dfu_boot_(); - // Could be constexpr in c++17 - static const auto CLOSE = [](stm32_t *stm32) { stm32_close(stm32); }; - // Cleanup with RAII - std::unique_ptr stm32{stm32_init(this, STREAM_SERIAL, 1), CLOSE}; + auto stm32 = stm32_init(this, STREAM_SERIAL, 1); if (!stm32) { ESP_LOGW(TAG, "Failed to initialize STM32"); @@ -170,7 +167,7 @@ bool ShellyDimmer::upgrade_firmware_() { } // Erase STM32 flash. - if (stm32_erase_memory(stm32.get(), 0, STM32_MASS_ERASE) != STM32_ERR_OK) { + if (stm32_erase_memory(stm32, 0, STM32_MASS_ERASE) != STM32_ERR_OK) { ESP_LOGW(TAG, "Failed to erase STM32 flash memory"); return false; } @@ -196,7 +193,7 @@ bool ShellyDimmer::upgrade_firmware_() { std::memcpy(buffer, p, BUFFER_SIZE); p += BUFFER_SIZE; - if (stm32_write_memory(stm32.get(), addr, buffer, len) != STM32_ERR_OK) { + if (stm32_write_memory(stm32, addr, buffer, len) != STM32_ERR_OK) { ESP_LOGW(TAG, "Failed to write to STM32 flash memory"); return false; } diff --git a/esphome/components/shelly_dimmer/stm32flash.cpp b/esphome/components/shelly_dimmer/stm32flash.cpp index 4c777776fb..e688f2de36 100644 --- a/esphome/components/shelly_dimmer/stm32flash.cpp +++ b/esphome/components/shelly_dimmer/stm32flash.cpp @@ -117,7 +117,7 @@ namespace shelly_dimmer { namespace { -int flash_addr_to_page_ceil(const stm32_t *stm, uint32_t addr) { +int flash_addr_to_page_ceil(const stm32_unique_ptr &stm, uint32_t addr) { if (!(addr >= stm->dev->fl_start && addr <= stm->dev->fl_end)) return 0; @@ -135,7 +135,7 @@ int flash_addr_to_page_ceil(const stm32_t *stm, uint32_t addr) { return addr ? page + 1 : page; } -stm32_err_t stm32_get_ack_timeout(const stm32_t *stm, uint32_t timeout) { +stm32_err_t stm32_get_ack_timeout(const stm32_unique_ptr &stm, uint32_t timeout) { auto *stream = stm->stream; uint8_t rxbyte; @@ -168,9 +168,9 @@ stm32_err_t stm32_get_ack_timeout(const stm32_t *stm, uint32_t timeout) { } while (true); } -stm32_err_t stm32_get_ack(const stm32_t *stm) { return stm32_get_ack_timeout(stm, 0); } +stm32_err_t stm32_get_ack(const stm32_unique_ptr &stm) { return stm32_get_ack_timeout(stm, 0); } -stm32_err_t stm32_send_command_timeout(const stm32_t *stm, const uint8_t cmd, const uint32_t timeout) { +stm32_err_t stm32_send_command_timeout(const stm32_unique_ptr &stm, const uint8_t cmd, const uint32_t timeout) { auto *const stream = stm->stream; static constexpr auto BUFFER_SIZE = 2; @@ -194,12 +194,12 @@ stm32_err_t stm32_send_command_timeout(const stm32_t *stm, const uint8_t cmd, co return STM32_ERR_UNKNOWN; } -stm32_err_t stm32_send_command(const stm32_t *stm, const uint8_t cmd) { +stm32_err_t stm32_send_command(const stm32_unique_ptr &stm, const uint8_t cmd) { return stm32_send_command_timeout(stm, cmd, 0); } /* if we have lost sync, send a wrong command and expect a NACK */ -stm32_err_t stm32_resync(const stm32_t *stm) { +stm32_err_t stm32_resync(const stm32_unique_ptr &stm) { auto *const stream = stm->stream; uint32_t t0 = millis(); auto t1 = t0; @@ -238,7 +238,7 @@ stm32_err_t stm32_resync(const stm32_t *stm) { * * len is value of the first byte in the frame. */ -stm32_err_t stm32_guess_len_cmd(const stm32_t *stm, const uint8_t cmd, uint8_t *const data, unsigned int len) { +stm32_err_t stm32_guess_len_cmd(const stm32_unique_ptr &stm, const uint8_t cmd, uint8_t *const data, unsigned int len) { auto *const stream = stm->stream; if (stm32_send_command(stm, cmd) != STM32_ERR_OK) @@ -286,7 +286,7 @@ stm32_err_t stm32_guess_len_cmd(const stm32_t *stm, const uint8_t cmd, uint8_t * * This function sends the init sequence and, in case of timeout, recovers * the interface. */ -stm32_err_t stm32_send_init_seq(const stm32_t *stm) { +stm32_err_t stm32_send_init_seq(const stm32_unique_ptr &stm) { auto *const stream = stm->stream; stream->write_array(&STM32_CMD_INIT, 1); @@ -320,7 +320,7 @@ stm32_err_t stm32_send_init_seq(const stm32_t *stm) { return STM32_ERR_UNKNOWN; } -stm32_err_t stm32_mass_erase(const stm32_t *stm) { +stm32_err_t stm32_mass_erase(const stm32_unique_ptr &stm) { auto *const stream = stm->stream; if (stm32_send_command(stm, stm->cmd->er) != STM32_ERR_OK) { @@ -364,7 +364,7 @@ template std::unique_ptr malloc_array_raii DELETOR}; } -stm32_err_t stm32_pages_erase(const stm32_t *stm, const uint32_t spage, const uint32_t pages) { +stm32_err_t stm32_pages_erase(const stm32_unique_ptr &stm, const uint32_t spage, const uint32_t pages) { auto *const stream = stm->stream; uint8_t cs = 0; int i = 0; @@ -474,6 +474,18 @@ template void populate_buffer_with_address(uint8_t (&buffer)[N], uint3 buffer[4] = static_cast(buffer[0] ^ buffer[1] ^ buffer[2] ^ buffer[3]); } +template stm32_unique_ptr make_stm32_with_deletor(T ptr) { + static const auto CLOSE = [](stm32_t *stm32) { + if (stm32) { + free(stm32->cmd); // NOLINT + } + free(stm32); // NOLINT + }; + + // Cleanup with RAII + return std::unique_ptr{ptr, CLOSE}; +} + } // Anonymous namespace } // namespace shelly_dimmer @@ -485,48 +497,44 @@ namespace shelly_dimmer { /* find newer command by higher code */ #define newer(prev, a) (((prev) == STM32_CMD_ERR) ? (a) : (((prev) > (a)) ? (prev) : (a))) -stm32_t *stm32_init(uart::UARTDevice *stream, const uint8_t flags, const char init) { +stm32_unique_ptr stm32_init(uart::UARTDevice *stream, const uint8_t flags, const char init) { uint8_t buf[257]; - // Could be constexpr in c++17 - static const auto CLOSE = [](stm32_t *stm32) { stm32_close(stm32); }; - - // Cleanup with RAII - std::unique_ptr stm{static_cast(calloc(sizeof(stm32_t), 1)), // NOLINT - CLOSE}; + auto stm = make_stm32_with_deletor(static_cast(calloc(sizeof(stm32_t), 1))); // NOLINT if (!stm) { - return nullptr; + return make_stm32_with_deletor(nullptr); } stm->stream = stream; stm->flags = flags; stm->cmd = static_cast(malloc(sizeof(stm32_cmd_t))); // NOLINT if (!stm->cmd) { - return nullptr; + return make_stm32_with_deletor(nullptr); } memset(stm->cmd, STM32_CMD_ERR, sizeof(stm32_cmd_t)); if ((stm->flags & STREAM_OPT_CMD_INIT) && init) { - if (stm32_send_init_seq(stm.get()) != STM32_ERR_OK) - return nullptr; // NOLINT + if (stm32_send_init_seq(stm) != STM32_ERR_OK) + return make_stm32_with_deletor(nullptr); } /* get the version and read protection status */ - if (stm32_send_command(stm.get(), STM32_CMD_GVR) != STM32_ERR_OK) { - return nullptr; // NOLINT + if (stm32_send_command(stm, STM32_CMD_GVR) != STM32_ERR_OK) { + return make_stm32_with_deletor(nullptr); } /* From AN, only UART bootloader returns 3 bytes */ { const auto len = (stm->flags & STREAM_OPT_GVR_ETX) ? 3 : 1; if (!stream->read_array(buf, len)) - return nullptr; // NOLINT + return make_stm32_with_deletor(nullptr); + stm->version = buf[0]; stm->option1 = (stm->flags & STREAM_OPT_GVR_ETX) ? buf[1] : 0; stm->option2 = (stm->flags & STREAM_OPT_GVR_ETX) ? buf[2] : 0; - if (stm32_get_ack(stm.get()) != STM32_ERR_OK) { - return nullptr; + if (stm32_get_ack(stm) != STM32_ERR_OK) { + return make_stm32_with_deletor(nullptr); } } @@ -544,8 +552,8 @@ stm32_t *stm32_init(uart::UARTDevice *stream, const uint8_t flags, const char in return STM32_CMD_GET_LENGTH; })(); - if (stm32_guess_len_cmd(stm.get(), STM32_CMD_GET, buf, len) != STM32_ERR_OK) - return nullptr; + if (stm32_guess_len_cmd(stm, STM32_CMD_GET, buf, len) != STM32_ERR_OK) + return make_stm32_with_deletor(nullptr); } const auto stop = buf[0] + 1; @@ -607,23 +615,23 @@ stm32_t *stm32_init(uart::UARTDevice *stream, const uint8_t flags, const char in } if (new_cmds) ESP_LOGD(TAG, ")"); - if (stm32_get_ack(stm.get()) != STM32_ERR_OK) { - return nullptr; + if (stm32_get_ack(stm) != STM32_ERR_OK) { + return make_stm32_with_deletor(nullptr); } if (stm->cmd->get == STM32_CMD_ERR || stm->cmd->gvr == STM32_CMD_ERR || stm->cmd->gid == STM32_CMD_ERR) { ESP_LOGD(TAG, "Error: bootloader did not returned correct information from GET command"); - return nullptr; + return make_stm32_with_deletor(nullptr); } /* get the device ID */ - if (stm32_guess_len_cmd(stm.get(), stm->cmd->gid, buf, 1) != STM32_ERR_OK) { - return nullptr; + if (stm32_guess_len_cmd(stm, stm->cmd->gid, buf, 1) != STM32_ERR_OK) { + return make_stm32_with_deletor(nullptr); } const auto returned = buf[0] + 1; if (returned < 2) { ESP_LOGD(TAG, "Only %d bytes sent in the PID, unknown/unsupported device", returned); - return nullptr; + return make_stm32_with_deletor(nullptr); } stm->pid = (buf[1] << 8) | buf[2]; if (returned > 2) { @@ -631,8 +639,8 @@ stm32_t *stm32_init(uart::UARTDevice *stream, const uint8_t flags, const char in for (auto i = 2; i <= returned; i++) ESP_LOGD(TAG, " %02x", buf[i]); } - if (stm32_get_ack(stm.get()) != STM32_ERR_OK) { - return nullptr; + if (stm32_get_ack(stm) != STM32_ERR_OK) { + return make_stm32_with_deletor(nullptr); } stm->dev = DEVICES; @@ -641,21 +649,14 @@ stm32_t *stm32_init(uart::UARTDevice *stream, const uint8_t flags, const char in if (!stm->dev->id) { ESP_LOGD(TAG, "Unknown/unsupported device (Device ID: 0x%03x)", stm->pid); - return nullptr; + return make_stm32_with_deletor(nullptr); } - // TODO: Would be much better if the unique_ptr was returned from this function - // Release ownership of unique_ptr - return stm.release(); // NOLINT + return stm; } -void stm32_close(stm32_t *stm) { - if (stm) - free(stm->cmd); // NOLINT - free(stm); // NOLINT -} - -stm32_err_t stm32_read_memory(const stm32_t *stm, const uint32_t address, uint8_t *data, const unsigned int len) { +stm32_err_t stm32_read_memory(const stm32_unique_ptr &stm, const uint32_t address, uint8_t *data, + const unsigned int len) { auto *const stream = stm->stream; if (!len) @@ -693,7 +694,8 @@ stm32_err_t stm32_read_memory(const stm32_t *stm, const uint32_t address, uint8_ return STM32_ERR_OK; } -stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, const uint8_t *data, const unsigned int len) { +stm32_err_t stm32_write_memory(const stm32_unique_ptr &stm, uint32_t address, const uint8_t *data, + const unsigned int len) { auto *const stream = stm->stream; if (!len) @@ -753,7 +755,7 @@ stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, const uint8 return STM32_ERR_OK; } -stm32_err_t stm32_wunprot_memory(const stm32_t *stm) { +stm32_err_t stm32_wunprot_memory(const stm32_unique_ptr &stm) { if (stm->cmd->uw == STM32_CMD_ERR) { ESP_LOGD(TAG, "Error: WRITE UNPROTECT command not implemented in bootloader."); return STM32_ERR_NO_CMD; @@ -766,7 +768,7 @@ stm32_err_t stm32_wunprot_memory(const stm32_t *stm) { []() { ESP_LOGD(TAG, "Error: Failed to WRITE UNPROTECT"); }); } -stm32_err_t stm32_wprot_memory(const stm32_t *stm) { +stm32_err_t stm32_wprot_memory(const stm32_unique_ptr &stm) { if (stm->cmd->wp == STM32_CMD_ERR) { ESP_LOGD(TAG, "Error: WRITE PROTECT command not implemented in bootloader."); return STM32_ERR_NO_CMD; @@ -779,7 +781,7 @@ stm32_err_t stm32_wprot_memory(const stm32_t *stm) { []() { ESP_LOGD(TAG, "Error: Failed to WRITE PROTECT"); }); } -stm32_err_t stm32_runprot_memory(const stm32_t *stm) { +stm32_err_t stm32_runprot_memory(const stm32_unique_ptr &stm) { if (stm->cmd->ur == STM32_CMD_ERR) { ESP_LOGD(TAG, "Error: READOUT UNPROTECT command not implemented in bootloader."); return STM32_ERR_NO_CMD; @@ -792,7 +794,7 @@ stm32_err_t stm32_runprot_memory(const stm32_t *stm) { []() { ESP_LOGD(TAG, "Error: Failed to READOUT UNPROTECT"); }); } -stm32_err_t stm32_readprot_memory(const stm32_t *stm) { +stm32_err_t stm32_readprot_memory(const stm32_unique_ptr &stm) { if (stm->cmd->rp == STM32_CMD_ERR) { ESP_LOGD(TAG, "Error: READOUT PROTECT command not implemented in bootloader."); return STM32_ERR_NO_CMD; @@ -805,7 +807,7 @@ stm32_err_t stm32_readprot_memory(const stm32_t *stm) { []() { ESP_LOGD(TAG, "Error: Failed to READOUT PROTECT"); }); } -stm32_err_t stm32_erase_memory(const stm32_t *stm, uint32_t spage, uint32_t pages) { +stm32_err_t stm32_erase_memory(const stm32_unique_ptr &stm, uint32_t spage, uint32_t pages) { if (!pages || spage > STM32_MAX_PAGES || ((pages != STM32_MASS_ERASE) && ((spage + pages) > STM32_MAX_PAGES))) return STM32_ERR_OK; @@ -847,7 +849,7 @@ stm32_err_t stm32_erase_memory(const stm32_t *stm, uint32_t spage, uint32_t page return STM32_ERR_OK; } -static stm32_err_t stm32_run_raw_code(const stm32_t *stm, uint32_t target_address, const uint8_t *code, +static stm32_err_t stm32_run_raw_code(const stm32_unique_ptr &stm, uint32_t target_address, const uint8_t *code, uint32_t code_size) { static constexpr uint32_t BUFFER_SIZE = 256; @@ -893,7 +895,7 @@ static stm32_err_t stm32_run_raw_code(const stm32_t *stm, uint32_t target_addres return stm32_go(stm, target_address); } -stm32_err_t stm32_go(const stm32_t *stm, const uint32_t address) { +stm32_err_t stm32_go(const stm32_unique_ptr &stm, const uint32_t address) { auto *const stream = stm->stream; if (stm->cmd->go == STM32_CMD_ERR) { @@ -916,7 +918,7 @@ stm32_err_t stm32_go(const stm32_t *stm, const uint32_t address) { return STM32_ERR_OK; } -stm32_err_t stm32_reset_device(const stm32_t *stm) { +stm32_err_t stm32_reset_device(const stm32_unique_ptr &stm) { const auto target_address = stm->dev->ram_start; if (stm->dev->flags & F_OBLL) { @@ -927,7 +929,8 @@ stm32_err_t stm32_reset_device(const stm32_t *stm) { } } -stm32_err_t stm32_crc_memory(const stm32_t *stm, const uint32_t address, const uint32_t length, uint32_t *const crc) { +stm32_err_t stm32_crc_memory(const stm32_unique_ptr &stm, const uint32_t address, const uint32_t length, + uint32_t *const crc) { static constexpr auto BUFFER_SIZE = 5; auto *const stream = stm->stream; @@ -1022,7 +1025,7 @@ uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len) { return crc; } -stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, uint32_t length, uint32_t *crc) { +stm32_err_t stm32_crc_wrapper(const stm32_unique_ptr &stm, uint32_t address, uint32_t length, uint32_t *crc) { static constexpr uint32_t CRC_INIT_VALUE = 0xFFFFFFFF; static constexpr uint32_t BUFFER_SIZE = 256; diff --git a/esphome/components/shelly_dimmer/stm32flash.h b/esphome/components/shelly_dimmer/stm32flash.h index c561375c38..d973b35222 100644 --- a/esphome/components/shelly_dimmer/stm32flash.h +++ b/esphome/components/shelly_dimmer/stm32flash.h @@ -23,6 +23,7 @@ #ifdef USE_SHD_FIRMWARE_DATA #include +#include #include "esphome/components/uart/uart.h" namespace esphome { @@ -108,19 +109,20 @@ struct VarlenCmd { uint8_t length; }; -stm32_t *stm32_init(uart::UARTDevice *stream, uint8_t flags, char init); -void stm32_close(stm32_t *stm); -stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, uint8_t *data, unsigned int len); -stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, const uint8_t *data, unsigned int len); -stm32_err_t stm32_wunprot_memory(const stm32_t *stm); -stm32_err_t stm32_wprot_memory(const stm32_t *stm); -stm32_err_t stm32_erase_memory(const stm32_t *stm, uint32_t spage, uint32_t pages); -stm32_err_t stm32_go(const stm32_t *stm, uint32_t address); -stm32_err_t stm32_reset_device(const stm32_t *stm); -stm32_err_t stm32_readprot_memory(const stm32_t *stm); -stm32_err_t stm32_runprot_memory(const stm32_t *stm); -stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, uint32_t length, uint32_t *crc); -stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, uint32_t length, uint32_t *crc); +using stm32_unique_ptr = std::unique_ptr; + +stm32_unique_ptr stm32_init(uart::UARTDevice *stream, uint8_t flags, char init); +stm32_err_t stm32_read_memory(const stm32_unique_ptr &stm, uint32_t address, uint8_t *data, unsigned int len); +stm32_err_t stm32_write_memory(const stm32_unique_ptr &stm, uint32_t address, const uint8_t *data, unsigned int len); +stm32_err_t stm32_wunprot_memory(const stm32_unique_ptr &stm); +stm32_err_t stm32_wprot_memory(const stm32_unique_ptr &stm); +stm32_err_t stm32_erase_memory(const stm32_unique_ptr &stm, uint32_t spage, uint32_t pages); +stm32_err_t stm32_go(const stm32_unique_ptr &stm, uint32_t address); +stm32_err_t stm32_reset_device(const stm32_unique_ptr &stm); +stm32_err_t stm32_readprot_memory(const stm32_unique_ptr &stm); +stm32_err_t stm32_runprot_memory(const stm32_unique_ptr &stm); +stm32_err_t stm32_crc_memory(const stm32_unique_ptr &stm, uint32_t address, uint32_t length, uint32_t *crc); +stm32_err_t stm32_crc_wrapper(const stm32_unique_ptr &stm, uint32_t address, uint32_t length, uint32_t *crc); uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len); } // namespace shelly_dimmer From c1480029fb1cb94c2aa445d33247c4e49e18cf23 Mon Sep 17 00:00:00 2001 From: James Szalay Date: Wed, 11 May 2022 23:26:14 -0400 Subject: [PATCH 244/273] Use heat mode for heat. Move EXT HT to custom presets. (#3437) * Use heat mode for heat. Move EXT HT to custom presets. * Fix syntax error. --- esphome/components/bedjet/bedjet.cpp | 6 ++++-- esphome/components/bedjet/bedjet.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/bedjet/bedjet.cpp b/esphome/components/bedjet/bedjet.cpp index 1a932da0c5..38ed6206a8 100644 --- a/esphome/components/bedjet/bedjet.cpp +++ b/esphome/components/bedjet/bedjet.cpp @@ -117,7 +117,7 @@ void Bedjet::control(const ClimateCall &call) { pkt = this->codec_->get_button_request(BTN_OFF); break; case climate::CLIMATE_MODE_HEAT: - pkt = this->codec_->get_button_request(BTN_EXTHT); + pkt = this->codec_->get_button_request(BTN_HEAT); break; case climate::CLIMATE_MODE_FAN_ONLY: pkt = this->codec_->get_button_request(BTN_COOL); @@ -137,7 +137,7 @@ void Bedjet::control(const ClimateCall &call) { } else { this->force_refresh_ = true; this->mode = mode; - // We're using (custom) preset for Turbo & M1-3 presets, so changing climate mode will clear those + // We're using (custom) preset for Turbo, EXT HT, & M1-3 presets, so changing climate mode will clear those this->custom_preset.reset(); this->preset.reset(); } @@ -186,6 +186,8 @@ void Bedjet::control(const ClimateCall &call) { pkt = this->codec_->get_button_request(BTN_M2); } else if (preset == "M3") { pkt = this->codec_->get_button_request(BTN_M3); + } else if (preset == "EXT HT") { + pkt = this->codec_->get_button_request(BTN_EXTHT); } else { ESP_LOGW(TAG, "Unsupported preset: %s", preset.c_str()); return; diff --git a/esphome/components/bedjet/bedjet.h b/esphome/components/bedjet/bedjet.h index b061d2b5ec..0565be6045 100644 --- a/esphome/components/bedjet/bedjet.h +++ b/esphome/components/bedjet/bedjet.h @@ -67,6 +67,8 @@ class Bedjet : public climate::Climate, public esphome::ble_client::BLEClientNod // We could fetch biodata from bedjet and set these names that way. // But then we have to invert the lookup in order to send the right preset. // For now, we can leave them as M1-3 to match the remote buttons. + // EXT HT added to match remote button. + "EXT HT", "M1", "M2", "M3", From e914828add0d6e41ac22c8d671184a73294560a8 Mon Sep 17 00:00:00 2001 From: Michael Davidson Date: Thu, 12 May 2022 14:54:45 +1000 Subject: [PATCH 245/273] Make custom_fan and custom_preset templatable as per documentation (#3330) --- esphome/components/climate/__init__.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index 87b9a4b3e2..1de9aa3f3a 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -287,9 +287,11 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema( cv.Exclusive(CONF_FAN_MODE, "fan_mode"): cv.templatable( validate_climate_fan_mode ), - cv.Exclusive(CONF_CUSTOM_FAN_MODE, "fan_mode"): cv.string_strict, + cv.Exclusive(CONF_CUSTOM_FAN_MODE, "fan_mode"): cv.templatable( + cv.string_strict + ), cv.Exclusive(CONF_PRESET, "preset"): cv.templatable(validate_climate_preset), - cv.Exclusive(CONF_CUSTOM_PRESET, "preset"): cv.string_strict, + cv.Exclusive(CONF_CUSTOM_PRESET, "preset"): cv.templatable(cv.string_strict), cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode), } ) @@ -324,13 +326,17 @@ async def climate_control_to_code(config, action_id, template_arg, args): template_ = await cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode) cg.add(var.set_fan_mode(template_)) if CONF_CUSTOM_FAN_MODE in config: - template_ = await cg.templatable(config[CONF_CUSTOM_FAN_MODE], args, str) + template_ = await cg.templatable( + config[CONF_CUSTOM_FAN_MODE], args, cg.std_string + ) cg.add(var.set_custom_fan_mode(template_)) if CONF_PRESET in config: template_ = await cg.templatable(config[CONF_PRESET], args, ClimatePreset) cg.add(var.set_preset(template_)) if CONF_CUSTOM_PRESET in config: - template_ = await cg.templatable(config[CONF_CUSTOM_PRESET], args, str) + template_ = await cg.templatable( + config[CONF_CUSTOM_PRESET], args, cg.std_string + ) cg.add(var.set_custom_preset(template_)) if CONF_SWING_MODE in config: template_ = await cg.templatable( From 28883f711b6aa53db54ab16289de28baea890d15 Mon Sep 17 00:00:00 2001 From: Brian Kaufman Date: Wed, 11 May 2022 21:57:50 -0700 Subject: [PATCH 246/273] Update captive portal canHandle function (#3360) --- esphome/components/captive_portal/captive_portal.h | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/esphome/components/captive_portal/captive_portal.h b/esphome/components/captive_portal/captive_portal.h index 0e68bc9cef..c2aada171f 100644 --- a/esphome/components/captive_portal/captive_portal.h +++ b/esphome/components/captive_portal/captive_portal.h @@ -39,17 +39,7 @@ class CaptivePortal : public AsyncWebHandler, public Component { if (request->method() == HTTP_GET) { if (request->url() == "/") return true; - if (request->url() == "/stylesheet.css") - return true; - if (request->url() == "/wifi-strength-1.svg") - return true; - if (request->url() == "/wifi-strength-2.svg") - return true; - if (request->url() == "/wifi-strength-3.svg") - return true; - if (request->url() == "/wifi-strength-4.svg") - return true; - if (request->url() == "/lock.svg") + if (request->url() == "/config.json") return true; if (request->url() == "/wifisave") return true; From 603d0d0c7c1462166bcfeda0fe6cec87b779acf1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 12 May 2022 17:00:14 +1200 Subject: [PATCH 247/273] Bump version to 2022.5.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index c254edf1ee..7717f709ec 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.5.0b1" +__version__ = "2022.5.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 01222dbab75a376c2be07cdca0b4c50b98aa15fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=5Bp=CA=B2=C9=B5s=5D?= Date: Sun, 15 May 2022 09:53:43 +0200 Subject: [PATCH 248/273] Increase JSON buffer size on overflow (#3475) --- esphome/components/json/json_util.cpp | 40 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 2bd8112255..7e701af48b 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -26,21 +26,33 @@ std::string build_json(const json_build_t &f) { const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); #endif - const size_t request_size = std::min(free_heap, (size_t) 512); - - DynamicJsonDocument json_document(request_size); - if (json_document.capacity() == 0) { - ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, largest free heap block: %u bytes", - request_size, free_heap); - return "{}"; + size_t request_size = std::min(free_heap, (size_t) 512); + while (true) { + ESP_LOGV(TAG, "Attempting to allocate %u bytes for JSON serialization", request_size); + DynamicJsonDocument json_document(request_size); + if (json_document.capacity() == 0) { + ESP_LOGE(TAG, + "Could not allocate memory for JSON document! Requested %u bytes, largest free heap block: %u bytes", + request_size, free_heap); + return "{}"; + } + JsonObject root = json_document.to(); + f(root); + if (json_document.overflowed()) { + if (request_size == free_heap) { + ESP_LOGE(TAG, "Could not allocate memory for JSON document! Overflowed largest free heap block: %u bytes", + free_heap); + return "{}"; + } + request_size = std::min(request_size * 2, free_heap); + continue; + } + json_document.shrinkToFit(); + ESP_LOGV(TAG, "Size after shrink %u bytes", json_document.capacity()); + std::string output; + serializeJson(json_document, output); + return output; } - JsonObject root = json_document.to(); - f(root); - json_document.shrinkToFit(); - ESP_LOGV(TAG, "Size after shrink %u bytes", json_document.capacity()); - std::string output; - serializeJson(json_document, output); - return output; } void parse_json(const std::string &data, const json_parse_t &f) { From a639690716d3a598025cbd6fc18a69415bf4dee0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 May 2022 13:05:20 +1200 Subject: [PATCH 249/273] Mark improv_serial and ESP-IDF usb based serial on c3/s2/s3 unsupported (#3477) --- esphome/components/improv_serial/__init__.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/esphome/components/improv_serial/__init__.py b/esphome/components/improv_serial/__init__.py index 21073a8ab3..67a0f7f4ed 100644 --- a/esphome/components/improv_serial/__init__.py +++ b/esphome/components/improv_serial/__init__.py @@ -1,6 +1,8 @@ -from esphome.const import CONF_BAUD_RATE, CONF_ID, CONF_LOGGER +from esphome.components.logger import USB_CDC, USB_SERIAL_JTAG +from esphome.const import CONF_BAUD_RATE, CONF_HARDWARE_UART, CONF_ID, CONF_LOGGER import esphome.codegen as cg import esphome.config_validation as cv +from esphome.core import CORE import esphome.final_validate as fv CODEOWNERS = ["@esphome/core"] @@ -17,14 +19,19 @@ CONFIG_SCHEMA = cv.Schema( ).extend(cv.COMPONENT_SCHEMA) -def validate_logger_baud_rate(config): +def validate_logger(config): logger_conf = fv.full_config.get()[CONF_LOGGER] if logger_conf[CONF_BAUD_RATE] == 0: raise cv.Invalid("improv_serial requires the logger baud_rate to be not 0") + if CORE.using_esp_idf: + if logger_conf[CONF_HARDWARE_UART] in [USB_SERIAL_JTAG, USB_CDC]: + raise cv.Invalid( + "improv_serial does not support the selected logger hardware_uart" + ) return config -FINAL_VALIDATE_SCHEMA = validate_logger_baud_rate +FINAL_VALIDATE_SCHEMA = validate_logger async def to_code(config): From c707e646854f04bf9463ca050ae373570c36c0b3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 May 2022 13:07:12 +1200 Subject: [PATCH 250/273] Bump version to 2022.5.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 7717f709ec..03d8c98712 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.5.0b2" +__version__ = "2022.5.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 17b8bd83161fa9e2fddd1bbb91cdd7dcddc591aa Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Tue, 17 May 2022 00:59:36 +0200 Subject: [PATCH 251/273] ESP32: Only save to NVS if data was changed (#3479) --- esphome/components/esp32/preferences.cpp | 33 +++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index 8c2b67a942..a78159825e 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -118,12 +118,17 @@ class ESP32Preferences : public ESPPreferences { // go through vector from back to front (makes erase easier/more efficient) for (ssize_t i = s_pending_save.size() - 1; i >= 0; i--) { const auto &save = s_pending_save[i]; - esp_err_t err = nvs_set_blob(nvs_handle, save.key.c_str(), save.data.data(), save.data.size()); - if (err != 0) { - ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", save.key.c_str(), save.data.size(), - esp_err_to_name(err)); - any_failed = true; - continue; + ESP_LOGVV(TAG, "Checking if NVS data %s has changed", save.key.c_str()); + if (is_changed(nvs_handle, save)) { + esp_err_t err = nvs_set_blob(nvs_handle, save.key.c_str(), save.data.data(), save.data.size()); + if (err != 0) { + ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", save.key.c_str(), save.data.size(), + esp_err_to_name(err)); + any_failed = true; + continue; + } + } else { + ESP_LOGD(TAG, "NVS data not changed skipping %s len=%u", save.key.c_str(), save.data.size()); } s_pending_save.erase(s_pending_save.begin() + i); } @@ -137,6 +142,22 @@ class ESP32Preferences : public ESPPreferences { return !any_failed; } + bool is_changed(const uint32_t nvs_handle, const NVSData &to_save) { + NVSData stored_data{}; + size_t actual_len; + esp_err_t err = nvs_get_blob(nvs_handle, to_save.key.c_str(), nullptr, &actual_len); + if (err != 0) { + ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", to_save.key.c_str(), esp_err_to_name(err)); + return true; + } + stored_data.data.reserve(actual_len); + err = nvs_get_blob(nvs_handle, to_save.key.c_str(), stored_data.data.data(), &actual_len); + if (err != 0) { + ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", to_save.key.c_str(), esp_err_to_name(err)); + return true; + } + return to_save.data == stored_data.data; + } }; void setup_preferences() { From 6f49f5465b61581d5f357c891e3c4aa134fec477 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 17 May 2022 01:15:02 -0700 Subject: [PATCH 252/273] Retry Tuya init commands (#3482) Co-authored-by: Samuel Sieb --- esphome/components/tuya/tuya.cpp | 30 ++++++++++++++++++++++++------ esphome/components/tuya/tuya.h | 2 ++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index 1fbca7796d..78e9d9e568 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -1,7 +1,7 @@ #include "tuya.h" -#include "esphome/core/log.h" #include "esphome/components/network/util.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/core/util.h" namespace esphome { @@ -10,6 +10,7 @@ namespace tuya { static const char *const TAG = "tuya"; static const int COMMAND_DELAY = 10; static const int RECEIVE_TIMEOUT = 300; +static const int MAX_RETRIES = 5; void Tuya::setup() { this->set_interval("heartbeat", 15000, [this] { this->send_empty_command_(TuyaCommandType::HEARTBEAT); }); @@ -27,8 +28,12 @@ void Tuya::loop() { void Tuya::dump_config() { ESP_LOGCONFIG(TAG, "Tuya:"); if (this->init_state_ != TuyaInitState::INIT_DONE) { - ESP_LOGCONFIG(TAG, " Configuration will be reported when setup is complete. Current init_state: %u", - static_cast(this->init_state_)); + if (this->init_failed_) { + ESP_LOGCONFIG(TAG, " Initialization failed. Current init_state: %u", static_cast(this->init_state_)); + } else { + ESP_LOGCONFIG(TAG, " Configuration will be reported when setup is complete. Current init_state: %u", + static_cast(this->init_state_)); + } ESP_LOGCONFIG(TAG, " If no further output is received, confirm that this is a supported Tuya device."); return; } @@ -127,6 +132,8 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff if (this->expected_response_.has_value() && this->expected_response_ == command_type) { this->expected_response_.reset(); + this->command_queue_.erase(command_queue_.begin()); + this->init_retries_ = 0; } switch (command_type) { @@ -332,8 +339,8 @@ void Tuya::handle_datapoints_(const uint8_t *buffer, size_t len) { } void Tuya::send_raw_command_(TuyaCommand command) { - uint8_t len_hi = (uint8_t)(command.payload.size() >> 8); - uint8_t len_lo = (uint8_t)(command.payload.size() & 0xFF); + uint8_t len_hi = (uint8_t) (command.payload.size() >> 8); + uint8_t len_lo = (uint8_t) (command.payload.size() & 0xFF); uint8_t version = 0; this->last_command_timestamp_ = millis(); @@ -378,13 +385,24 @@ void Tuya::process_command_queue_() { if (this->expected_response_.has_value() && delay > RECEIVE_TIMEOUT) { this->expected_response_.reset(); + if (init_state_ != TuyaInitState::INIT_DONE) { + if (++this->init_retries_ >= MAX_RETRIES) { + this->init_failed_ = true; + ESP_LOGE(TAG, "Initialization failed at init_state %u", static_cast(this->init_state_)); + this->command_queue_.erase(command_queue_.begin()); + this->init_retries_ = 0; + } + } else { + this->command_queue_.erase(command_queue_.begin()); + } } // Left check of delay since last command in case there's ever a command sent by calling send_raw_command_ directly if (delay > COMMAND_DELAY && !this->command_queue_.empty() && this->rx_message_.empty() && !this->expected_response_.has_value()) { this->send_raw_command_(command_queue_.front()); - this->command_queue_.erase(command_queue_.begin()); + if (!this->expected_response_.has_value()) + this->command_queue_.erase(command_queue_.begin()); } } diff --git a/esphome/components/tuya/tuya.h b/esphome/components/tuya/tuya.h index 3828c49b48..cdff523f90 100644 --- a/esphome/components/tuya/tuya.h +++ b/esphome/components/tuya/tuya.h @@ -122,6 +122,8 @@ class Tuya : public Component, public uart::UARTDevice { optional time_id_{}; #endif TuyaInitState init_state_ = TuyaInitState::INIT_HEARTBEAT; + bool init_failed_{false}; + int init_retries_{0}; uint8_t protocol_version_ = -1; int gpio_status_ = -1; int gpio_reset_ = -1; From 72fcf2cbe1f58113eca966bd62969c20a391f8ab Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 May 2022 23:23:37 +1200 Subject: [PATCH 253/273] Bump version to 2022.5.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 03d8c98712..3e5630c470 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.5.0b3" +__version__ = "2022.5.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 282d9e138caf4cdbd8769703b29ff3c04ea71a46 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 May 2022 23:31:55 +1200 Subject: [PATCH 254/273] Revert adding spaces --- esphome/components/tuya/tuya.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index 78e9d9e568..f8379a93f2 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -339,8 +339,8 @@ void Tuya::handle_datapoints_(const uint8_t *buffer, size_t len) { } void Tuya::send_raw_command_(TuyaCommand command) { - uint8_t len_hi = (uint8_t) (command.payload.size() >> 8); - uint8_t len_lo = (uint8_t) (command.payload.size() & 0xFF); + uint8_t len_hi = (uint8_t)(command.payload.size() >> 8); + uint8_t len_lo = (uint8_t)(command.payload.size() & 0xFF); uint8_t version = 0; this->last_command_timestamp_ = millis(); From ae2f6ad4d12d2f6959218e490c7525ac4497c724 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 May 2022 16:30:20 +1200 Subject: [PATCH 255/273] Bump version to 2022.5.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 3e5630c470..913f0fd0dc 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.5.0b4" +__version__ = "2022.5.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 6617d576a7aaa4f0d4319781f83df87f7c94224b Mon Sep 17 00:00:00 2001 From: user897943 Date: Wed, 18 May 2022 23:25:42 +0100 Subject: [PATCH 256/273] Update bedjet_const.h to remove blank spaces before speed steps, fixes Unknown Error when using climate.set_fan_mode in HA (#3476) --- esphome/components/bedjet/bedjet_const.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/bedjet/bedjet_const.h b/esphome/components/bedjet/bedjet_const.h index e6bfa45d3a..ae10ca1885 100644 --- a/esphome/components/bedjet/bedjet_const.h +++ b/esphome/components/bedjet/bedjet_const.h @@ -66,8 +66,8 @@ enum BedjetCommand : uint8_t { #define BEDJET_FAN_STEP_NAMES_ \ { \ - " 5%", " 10%", " 15%", " 20%", " 25%", " 30%", " 35%", " 40%", " 45%", " 50%", " 55%", " 60%", " 65%", " 70%", \ - " 75%", " 80%", " 85%", " 90%", " 95%", "100%" \ + "5%", "10%", "15%", "20%", "25%", "30%", "35%", "40%", "45%", "50%", "55%", "60%", "65%", "70%", "75%", "80%", \ + "85%", "90%", "95%", "100%" \ } static const char *const BEDJET_FAN_STEP_NAMES[20] = BEDJET_FAN_STEP_NAMES_; From b66af9fb4df4e093a6bd23672621301bb1662548 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 19 May 2022 16:23:40 +1200 Subject: [PATCH 257/273] Add missing import to bedjet (#3490) --- esphome/components/bedjet/bedjet.h | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/bedjet/bedjet.h b/esphome/components/bedjet/bedjet.h index 0565be6045..750a20594f 100644 --- a/esphome/components/bedjet/bedjet.h +++ b/esphome/components/bedjet/bedjet.h @@ -4,6 +4,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/climate/climate.h" #include "esphome/core/component.h" +#include "esphome/core/defines.h" #include "esphome/core/hal.h" #include "bedjet_base.h" From fb0fec1f25fc24d8a7130cab60e99de3567d6b43 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Mon, 23 May 2022 10:56:26 +0200 Subject: [PATCH 258/273] esp32: fix NVS (#3497) --- esphome/components/esp32/preferences.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index a78159825e..aa03c5acc7 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -156,7 +156,7 @@ class ESP32Preferences : public ESPPreferences { ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", to_save.key.c_str(), esp_err_to_name(err)); return true; } - return to_save.data == stored_data.data; + return to_save.data != stored_data.data; } }; From f3f6e54818342e8a19328b5f069ac49a0451a312 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 24 May 2022 21:56:18 +1200 Subject: [PATCH 259/273] Bump version to 2022.5.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 913f0fd0dc..3911a5fa4c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.5.0" +__version__ = "2022.5.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 746fd1122fa9f337e787fde79bc203d21b16ea53 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 8 Jun 2022 22:46:20 +1200 Subject: [PATCH 260/273] Bump version to 2022.6.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index b73d7e33bd..035c46f860 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.6.0-dev" +__version__ = "2022.6.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 93421f0fa71e09f173f8e8e15b29abe67e9f578a Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 8 Jun 2022 20:27:04 -0700 Subject: [PATCH 261/273] publish fan speed count for discovery (#3537) Co-authored-by: Samuel Sieb --- esphome/components/mqtt/mqtt_fan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 6433ead6b2..32892199fe 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -114,6 +114,7 @@ void MQTTFanComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig if (this->state_->get_traits().supports_speed()) { root[MQTT_PERCENTAGE_COMMAND_TOPIC] = this->get_speed_level_command_topic(); root[MQTT_PERCENTAGE_STATE_TOPIC] = this->get_speed_level_state_topic(); + root[MQTT_SPEED_RANGE_MAX] = this->state_->get_traits().supported_speed_count(); } } bool MQTTFanComponent::publish_state() { From 5942a3898c70b6ecc52ecf7617f45a90b585471f Mon Sep 17 00:00:00 2001 From: Viktor Nagy <126671+nagyv@users.noreply.github.com> Date: Thu, 9 Jun 2022 06:20:05 +0200 Subject: [PATCH 262/273] Nextion brightness setting requires an assignment (#3533) --- esphome/components/nextion/nextion_commands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/nextion/nextion_commands.cpp b/esphome/components/nextion/nextion_commands.cpp index f83aafc595..308e02bce8 100644 --- a/esphome/components/nextion/nextion_commands.cpp +++ b/esphome/components/nextion/nextion_commands.cpp @@ -115,7 +115,7 @@ void Nextion::set_backlight_brightness(float brightness) { ESP_LOGD(TAG, "Brightness out of bounds, percentage range 0-1.0"); return; } - this->add_no_result_to_queue_with_set("backlight_brightness", "dim", static_cast(brightness * 100)); + this->add_no_result_to_queue_with_printf_("backlight_brightness", "dim=%d", static_cast(brightness * 100)); } void Nextion::set_auto_wake_on_touch(bool auto_wake) { From d2d4eb4eae93ff7aa09e7edaa9fc4d59b8f637ab Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 9 Jun 2022 20:27:22 +1200 Subject: [PATCH 263/273] Bump version to 2022.6.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 035c46f860..80842f83be 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.6.0b1" +__version__ = "2022.6.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 041bef8bcda8683d9284962f7762550c05c3f002 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 13 Jun 2022 13:28:55 +1200 Subject: [PATCH 264/273] Implement media player volume actions (#3551) --- .../i2s_audio/i2s_audio_media_player.cpp | 16 ++++++++ esphome/components/media_player/__init__.py | 40 +++++++++++++++++-- esphome/components/media_player/automation.h | 35 +++++++--------- .../components/media_player/media_player.h | 4 +- tests/test4.yaml | 8 ++++ 5 files changed, 79 insertions(+), 24 deletions(-) diff --git a/esphome/components/i2s_audio/i2s_audio_media_player.cpp b/esphome/components/i2s_audio/i2s_audio_media_player.cpp index 9ddc8419bf..2b624a3917 100644 --- a/esphome/components/i2s_audio/i2s_audio_media_player.cpp +++ b/esphome/components/i2s_audio/i2s_audio_media_player.cpp @@ -51,6 +51,22 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) { this->state = media_player::MEDIA_PLAYER_STATE_PAUSED; } break; + case media_player::MEDIA_PLAYER_COMMAND_VOLUME_UP: { + float new_volume = this->volume + 0.1f; + if (new_volume > 1.0f) + new_volume = 1.0f; + this->set_volume_(new_volume); + this->unmute_(); + break; + } + case media_player::MEDIA_PLAYER_COMMAND_VOLUME_DOWN: { + float new_volume = this->volume - 0.1f; + if (new_volume < 0.0f) + new_volume = 0.0f; + this->set_volume_(new_volume); + this->unmute_(); + break; + } } } this->publish_state(); diff --git a/esphome/components/media_player/__init__.py b/esphome/components/media_player/__init__.py index 98b93a7ee7..877dd693e3 100644 --- a/esphome/components/media_player/__init__.py +++ b/esphome/components/media_player/__init__.py @@ -29,6 +29,17 @@ PauseAction = media_player_ns.class_( StopAction = media_player_ns.class_( "StopAction", automation.Action, cg.Parented.template(MediaPlayer) ) +VolumeUpAction = media_player_ns.class_( + "VolumeUpAction", automation.Action, cg.Parented.template(MediaPlayer) +) +VolumeDownAction = media_player_ns.class_( + "VolumeDownAction", automation.Action, cg.Parented.template(MediaPlayer) +) +VolumeSetAction = media_player_ns.class_( + "VolumeSetAction", automation.Action, cg.Parented.template(MediaPlayer) +) + +CONF_VOLUME = "volume" async def setup_media_player_core_(var, config): @@ -45,9 +56,7 @@ async def register_media_player(var, config): MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.Schema({})) -MEDIA_PLAYER_ACTION_SCHEMA = maybe_simple_id( - {cv.Required(CONF_ID): cv.use_id(MediaPlayer)} -) +MEDIA_PLAYER_ACTION_SCHEMA = maybe_simple_id({cv.GenerateID(): cv.use_id(MediaPlayer)}) @automation.register_action("media_player.play", PlayAction, MEDIA_PLAYER_ACTION_SCHEMA) @@ -58,12 +67,37 @@ MEDIA_PLAYER_ACTION_SCHEMA = maybe_simple_id( "media_player.pause", PauseAction, MEDIA_PLAYER_ACTION_SCHEMA ) @automation.register_action("media_player.stop", StopAction, MEDIA_PLAYER_ACTION_SCHEMA) +@automation.register_action( + "media_player.volume_up", VolumeUpAction, MEDIA_PLAYER_ACTION_SCHEMA +) +@automation.register_action( + "media_player.volume_down", VolumeDownAction, MEDIA_PLAYER_ACTION_SCHEMA +) async def media_player_action(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) await cg.register_parented(var, config[CONF_ID]) return var +@automation.register_action( + "media_player.volume_set", + VolumeSetAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(MediaPlayer), + cv.Required(CONF_VOLUME): cv.templatable(cv.percentage), + }, + key=CONF_VOLUME, + ), +) +async def media_player_volume_set_action(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + volume = await cg.templatable(config[CONF_VOLUME], args, float) + cg.add(var.set_volume(volume)) + return var + + @coroutine_with_priority(100.0) async def to_code(config): cg.add_global(media_player_ns.using) diff --git a/esphome/components/media_player/automation.h b/esphome/components/media_player/automation.h index e2b40d6a00..4dc324eb9c 100644 --- a/esphome/components/media_player/automation.h +++ b/esphome/components/media_player/automation.h @@ -7,28 +7,23 @@ namespace esphome { namespace media_player { -template class PlayAction : public Action, public Parented { - void play(Ts... x) override { - this->parent_->make_call().set_command(MediaPlayerCommand::MEDIA_PLAYER_COMMAND_PLAY).perform(); - } -}; +#define MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(ACTION_CLASS, ACTION_COMMAND) \ + template class ACTION_CLASS : public Action, public Parented { \ + void play(Ts... x) override { \ + this->parent_->make_call().set_command(MediaPlayerCommand::MEDIA_PLAYER_COMMAND_##ACTION_COMMAND).perform(); \ + } \ + }; -template class ToggleAction : public Action, public Parented { - void play(Ts... x) override { - this->parent_->make_call().set_command(MediaPlayerCommand::MEDIA_PLAYER_COMMAND_TOGGLE).perform(); - } -}; +MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(PlayAction, PLAY) +MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(PauseAction, PAUSE) +MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(StopAction, STOP) +MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(ToggleAction, TOGGLE) +MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(VolumeUpAction, VOLUME_UP) +MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(VolumeDownAction, VOLUME_DOWN) -template class PauseAction : public Action, public Parented { - void play(Ts... x) override { - this->parent_->make_call().set_command(MediaPlayerCommand::MEDIA_PLAYER_COMMAND_PAUSE).perform(); - } -}; - -template class StopAction : public Action, public Parented { - void play(Ts... x) override { - this->parent_->make_call().set_command(MediaPlayerCommand::MEDIA_PLAYER_COMMAND_STOP).perform(); - } +template class VolumeSetAction : public Action, public Parented { + TEMPLATABLE_VALUE(float, volume) + void play(Ts... x) override { this->parent_->make_call().set_volume(this->volume_.value(x...)).perform(); } }; } // namespace media_player diff --git a/esphome/components/media_player/media_player.h b/esphome/components/media_player/media_player.h index 6a2643b713..88114d5337 100644 --- a/esphome/components/media_player/media_player.h +++ b/esphome/components/media_player/media_player.h @@ -20,7 +20,9 @@ enum MediaPlayerCommand : uint8_t { MEDIA_PLAYER_COMMAND_STOP = 2, MEDIA_PLAYER_COMMAND_MUTE = 3, MEDIA_PLAYER_COMMAND_UNMUTE = 4, - MEDIA_PLAYER_COMMAND_TOGGLE = 5 + MEDIA_PLAYER_COMMAND_TOGGLE = 5, + MEDIA_PLAYER_COMMAND_VOLUME_UP = 6, + MEDIA_PLAYER_COMMAND_VOLUME_DOWN = 7, }; const char *media_player_command_to_string(MediaPlayerCommand command); diff --git a/tests/test4.yaml b/tests/test4.yaml index ce7a87411e..6b633fbe9b 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -604,6 +604,14 @@ touchscreen: - logger.log: format: Touch at (%d, %d) args: ["touch.x", "touch.y"] + - media_player.play: + - media_player.pause: + - media_player.stop: + - media_player.toggle: + - media_player.volume_up: + - media_player.volume_down: + - media_player.volume_set: 50% + media_player: - platform: i2s_audio From f3a25de11d549c7cd2b73fedca18d0e7816dcbd4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 13 Jun 2022 13:32:43 +1200 Subject: [PATCH 265/273] Bump version to 2022.6.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 80842f83be..126909f1e7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.6.0b2" +__version__ = "2022.6.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 7fceb070e54281b4ad18bc4439cd7d115b865bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= Date: Tue, 14 Jun 2022 10:36:38 +0200 Subject: [PATCH 266/273] Fix compilation with ESP32-S3 (#3543) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/logger/logger.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 08c83035b6..c97677c887 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -178,7 +178,8 @@ void Logger::pre_setup() { Serial1.setDebugOutput(ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE); #endif break; -#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) +#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) && \ + !defined(USE_ESP32_VARIANT_ESP32S3) case UART_SELECTION_UART2: this->hw_serial_ = &Serial2; Serial2.begin(this->baud_rate_); From b1d614e6c46d68d9bc4603303390bcd1695c6d56 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Tue, 14 Jun 2022 10:38:09 +0200 Subject: [PATCH 267/273] Bm3xx: Fix typo (#3559) --- esphome/components/bmp3xx/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/bmp3xx/sensor.py b/esphome/components/bmp3xx/sensor.py index 736e6df3d8..f0da1c3c24 100644 --- a/esphome/components/bmp3xx/sensor.py +++ b/esphome/components/bmp3xx/sensor.py @@ -25,7 +25,7 @@ OVERSAMPLING_OPTIONS = { "4X": Oversampling.OVERSAMPLING_X4, "8X": Oversampling.OVERSAMPLING_X8, "16X": Oversampling.OVERSAMPLING_X16, - "32x": Oversampling.OVERSAMPLING_X32, + "32X": Oversampling.OVERSAMPLING_X32, } IIRFilter = bmp3xx_ns.enum("IIRFilter") From 94f6c6861a277bc9d36e0a116efbf2ed16d52dcb Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 14 Jun 2022 20:41:46 +1200 Subject: [PATCH 268/273] Bump version to 2022.6.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 126909f1e7..5098658958 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.6.0b3" +__version__ = "2022.6.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 2d1abaa68e43b7d7615d7a303e5cd377d5317c8a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 16 Jun 2022 11:31:38 +1200 Subject: [PATCH 269/273] Bump version to 2022.6.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5098658958..32bb27b35e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.6.0b4" +__version__ = "2022.6.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From aa0c2dedd92d993deae103e37811321280313f7c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 17 Jun 2022 13:30:21 +1200 Subject: [PATCH 270/273] Setup the mute pin if configured (#3568) --- esphome/components/i2s_audio/i2s_audio_media_player.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/i2s_audio/i2s_audio_media_player.cpp b/esphome/components/i2s_audio/i2s_audio_media_player.cpp index 2b624a3917..f1f1dc0d51 100644 --- a/esphome/components/i2s_audio/i2s_audio_media_player.cpp +++ b/esphome/components/i2s_audio/i2s_audio_media_player.cpp @@ -109,6 +109,10 @@ void I2SAudioMediaPlayer::setup() { this->audio_ = make_unique