From 11433c8c178df6604788f91aceeeb99755befe0c Mon Sep 17 00:00:00 2001 From: vr6racer <12117307+vr6racer@users.noreply.github.com> Date: Wed, 13 Sep 2023 03:14:54 +0100 Subject: [PATCH 01/95] SX1509 component (#5385) --- esphome/components/sx1509/binary_sensor/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/sx1509/binary_sensor/__init__.py b/esphome/components/sx1509/binary_sensor/__init__.py index bbf0e5d0bc..fa620fa202 100644 --- a/esphome/components/sx1509/binary_sensor/__init__.py +++ b/esphome/components/sx1509/binary_sensor/__init__.py @@ -14,8 +14,8 @@ SX1509BinarySensor = sx1509_ns.class_("SX1509BinarySensor", binary_sensor.Binary CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(SX1509BinarySensor).extend( { cv.GenerateID(CONF_SX1509_ID): cv.use_id(SX1509Component), - cv.Required(CONF_ROW): cv.int_range(min=0, max=4), - cv.Required(CONF_COL): cv.int_range(min=0, max=4), + cv.Required(CONF_ROW): cv.int_range(min=0, max=7), + cv.Required(CONF_COL): cv.int_range(min=0, max=7), } ) From 9d978075875b0fdb4fa72e07c657fa108f306296 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:05:06 +1200 Subject: [PATCH 02/95] Bump version to 2023.10.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index bbc6e71885..2865b369e8 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.9.0-dev" +__version__ = "2023.10.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 280b090dfce2576f056ac0b04c011ee225235fad Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Wed, 13 Sep 2023 18:13:55 -0500 Subject: [PATCH 03/95] Add patch to apt install (#5389) --- docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index bf64897af7..a0bb007641 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -29,7 +29,8 @@ RUN \ curl=7.74.0-1.3+deb11u7 \ openssh-client=1:8.4p1-5+deb11u1 \ python3-cffi=1.14.5-1 \ - libcairo2=1.16.0-5; \ + libcairo2=1.16.0-5 \ + patch=2.7.6-7; \ if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ apt-get install -y --no-install-recommends \ build-essential=12.9 \ From 55b5c0fc32210fea202e930fe2d1e7ca374cc635 Mon Sep 17 00:00:00 2001 From: phoenixswiss <52887628+phoenixswiss@users.noreply.github.com> Date: Thu, 14 Sep 2023 08:20:21 +0200 Subject: [PATCH 04/95] Fix Waveshare 7.5v2 epaper screens are always powered on (#5283) --- .../waveshare_epaper/waveshare_epaper.cpp | 66 ++++++++++++++++--- .../waveshare_epaper/waveshare_epaper.h | 4 ++ 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 73c2680add..f52808d295 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -1561,6 +1561,23 @@ void WaveshareEPaper7P5In::dump_config() { LOG_PIN(" Busy Pin: ", this->busy_pin_); LOG_UPDATE_INTERVAL(this); } +bool WaveshareEPaper7P5InV2::wait_until_idle_() { + if (this->busy_pin_ == nullptr) { + return true; + } + + const uint32_t start = millis(); + while (this->busy_pin_->digital_read()) { + this->command(0x71); + if (millis() - start > this->idle_timeout_()) { + ESP_LOGE(TAG, "Timeout while displaying image!"); + return false; + } + App.feed_wdt(); + delay(10); + } + return true; +} void WaveshareEPaper7P5InV2::initialize() { // COMMAND POWER SETTING this->command(0x01); @@ -1568,10 +1585,21 @@ void WaveshareEPaper7P5InV2::initialize() { this->data(0x07); this->data(0x3f); this->data(0x3f); - this->command(0x04); + + // We don't want the display to be powered at this point delay(100); // NOLINT this->wait_until_idle_(); + + // COMMAND VCOM AND DATA INTERVAL SETTING + this->command(0x50); + this->data(0x10); + this->data(0x07); + + // COMMAND TCON SETTING + this->command(0x60); + this->data(0x22); + // COMMAND PANEL SETTING this->command(0x00); this->data(0x1F); @@ -1582,19 +1610,30 @@ void WaveshareEPaper7P5InV2::initialize() { this->data(0x20); this->data(0x01); this->data(0xE0); - // COMMAND ...? + + // COMMAND DUAL SPI MM_EN, DUSPI_EN this->command(0x15); this->data(0x00); - // COMMAND VCOM AND DATA INTERVAL SETTING - this->command(0x50); - this->data(0x10); - this->data(0x07); - // COMMAND TCON SETTING - this->command(0x60); - this->data(0x22); + + // COMMAND POWER DRIVER HAT DOWN + // This command will turn off booster, controller, source driver, gate driver, VCOM, and + // temperature sensor, but register data will be kept until VDD turned OFF or Deep Sleep Mode. + // Source/Gate/Border/VCOM will be released to floating. + this->command(0x02); } void HOT WaveshareEPaper7P5InV2::display() { uint32_t buf_len = this->get_buffer_length_(); + + // COMMAND POWER ON + ESP_LOGI(TAG, "Power on the display and hat"); + + // This command will turn on booster, controller, regulators, and temperature sensor will be + // activated for one-time sensing before enabling booster. When all voltages are ready, the + // BUSY_N signal will return to high. + this->command(0x04); + delay(200); // NOLINT + this->wait_until_idle_(); + // COMMAND DATA START TRANSMISSION NEW DATA this->command(0x13); delay(2); @@ -1602,14 +1641,23 @@ void HOT WaveshareEPaper7P5InV2::display() { this->data(~(this->buffer_[i])); } + delay(100); // NOLINT + this->wait_until_idle_(); + // COMMAND DISPLAY REFRESH this->command(0x12); delay(100); // NOLINT this->wait_until_idle_(); + + ESP_LOGV(TAG, "Before command(0x02) (>> power off)"); + this->command(0x02); + this->wait_until_idle_(); + ESP_LOGV(TAG, "After command(0x02) (>> power off)"); } int WaveshareEPaper7P5InV2::get_width_internal() { return 800; } int WaveshareEPaper7P5InV2::get_height_internal() { return 480; } +uint32_t WaveshareEPaper7P5InV2::idle_timeout_() { return 10000; } void WaveshareEPaper7P5InV2::dump_config() { LOG_DISPLAY("", "Waveshare E-Paper", this); ESP_LOGCONFIG(TAG, " Model: 7.5inV2rev2"); diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 315af9ea82..b3325d69eb 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -472,6 +472,8 @@ class WaveshareEPaper7P5InBC : public WaveshareEPaper { class WaveshareEPaper7P5InV2 : public WaveshareEPaper { public: + bool wait_until_idle_(); + void initialize() override; void display() override; @@ -491,6 +493,8 @@ class WaveshareEPaper7P5InV2 : public WaveshareEPaper { int get_width_internal() override; int get_height_internal() override; + + uint32_t idle_timeout_() override; }; class WaveshareEPaper7P5InV2alt : public WaveshareEPaper7P5InV2 { From b5f2d69ca529a04616262d96a066080a3b6ad847 Mon Sep 17 00:00:00 2001 From: rmmacias <46213351+rmmacias@users.noreply.github.com> Date: Sun, 17 Sep 2023 07:18:51 +0200 Subject: [PATCH 05/95] Update radon_eye_listener.cpp (#5401) New devices identifiers do not star by the hardcoded string. FR:RE222 is the 8-char length string of my devices bought in 2023. This proposal aims at solve the topic by making the detection track devices starting only by FR:R --- esphome/components/radon_eye_ble/radon_eye_listener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/radon_eye_ble/radon_eye_listener.cpp b/esphome/components/radon_eye_ble/radon_eye_listener.cpp index b10986c9cb..340322c188 100644 --- a/esphome/components/radon_eye_ble/radon_eye_listener.cpp +++ b/esphome/components/radon_eye_ble/radon_eye_listener.cpp @@ -10,7 +10,7 @@ static const char *const TAG = "radon_eye_ble"; bool RadonEyeListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { if (not device.get_name().empty()) { - if (device.get_name().rfind("FR:R20:SN", 0) == 0) { + if (device.get_name().rfind("FR:R", 0) == 0) { // This is an RD200, I think ESP_LOGD(TAG, "Found Radon Eye RD200 device Name: %s (MAC: %s)", device.get_name().c_str(), device.address_str().c_str()); From a61e3fadf66d38f5aa823be3a70b23dffa03dad5 Mon Sep 17 00:00:00 2001 From: Trevor North Date: Sun, 17 Sep 2023 06:20:31 +0100 Subject: [PATCH 06/95] Add shelly-dimmer-stm32 51.7 to known versions (#5400) This version removes support for no-neutral setups in favor of fixing flickering some users have experienced. --- esphome/components/shelly_dimmer/light.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/shelly_dimmer/light.py b/esphome/components/shelly_dimmer/light.py index 467a3c3531..5bdb54baf5 100644 --- a/esphome/components/shelly_dimmer/light.py +++ b/esphome/components/shelly_dimmer/light.py @@ -57,6 +57,10 @@ KNOWN_FIRMWARE = { "https://github.com/jamesturton/shelly-dimmer-stm32/releases/download/v51.6/shelly-dimmer-stm32_v51.6.bin", "eda483e111c914723a33f5088f1397d5c0b19333db4a88dc965636b976c16c36", ), + "51.7": ( + "https://github.com/jamesturton/shelly-dimmer-stm32/releases/download/v51.7/shelly-dimmer-stm32_v51.7.bin", + "7a20f1c967c469917368a79bc56498009045237080408cef7190743e08031889", + ), } From 164631fcec7f286c0ef6575e9c07c76c369e2b38 Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 17 Sep 2023 07:24:31 +0200 Subject: [PATCH 07/95] Ci find YAML tests dynamically (#5399) * Find all YAML test files dynamically. * Merge error * GitHub set-ouput syntax upgrade. --------- Co-authored-by: Your Name --- .github/workflows/ci.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e4e6c0ece..792d972311 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -210,6 +210,17 @@ jobs: run: script/ci-suggest-changes if: always() + compile-tests-list: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Check out code from GitHub + uses: actions/checkout@v4.0.0 + - name: Find all YAML test files + id: set-matrix + run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT + compile-tests: name: Run YAML test ${{ matrix.file }} runs-on: ubuntu-latest @@ -222,11 +233,12 @@ jobs: - pylint - pytest - pyupgrade + - compile-tests-list strategy: fail-fast: false max-parallel: 2 matrix: - file: [1, 2, 3, 3.1, 4, 5, 6, 7, 8, 10, 11.5] + file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }} steps: - name: Check out code from GitHub uses: actions/checkout@v4.0.0 @@ -235,10 +247,10 @@ jobs: with: python-version: ${{ env.DEFAULT_PYTHON }} cache-key: ${{ needs.common.outputs.cache-key }} - - name: Run esphome compile tests/test${{ matrix.file }}.yaml + - name: Run esphome compile ${{ matrix.file }} run: | . venv/bin/activate - esphome compile tests/test${{ matrix.file }}.yaml + esphome compile ${{ matrix.file }} clang-tidy: name: ${{ matrix.name }} From 11f6e555f9607767991a93d9c6259ef627a39bda Mon Sep 17 00:00:00 2001 From: Philipp Helo Rehs Date: Sun, 17 Sep 2023 07:30:52 +0200 Subject: [PATCH 08/95] Add E-Trailer Gaslevel support to Mopeka Std Check (#5397) * Add E-Trailer Gaslevel support to Mopeka Std Check Signed-off-by: Philipp Helo Rehs * fix format --------- Signed-off-by: Philipp Helo Rehs Co-authored-by: Philipp Helo Rehs --- esphome/components/mopeka_std_check/mopeka_std_check.cpp | 3 ++- esphome/components/mopeka_std_check/mopeka_std_check.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/mopeka_std_check/mopeka_std_check.cpp b/esphome/components/mopeka_std_check/mopeka_std_check.cpp index 67e749c68b..9dd1718cb2 100644 --- a/esphome/components/mopeka_std_check/mopeka_std_check.cpp +++ b/esphome/components/mopeka_std_check/mopeka_std_check.cpp @@ -71,7 +71,8 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) const auto *mopeka_data = (const mopeka_std_package *) manu_data.data.data(); const u_int8_t hardware_id = mopeka_data->data_1 & 0xCF; - if (static_cast(hardware_id) != STANDARD && static_cast(hardware_id) != XL) { + if (static_cast(hardware_id) != STANDARD && static_cast(hardware_id) != XL && + static_cast(hardware_id) != ETRAILER) { ESP_LOGE(TAG, "[%s] Unsupported Sensor Type (0x%X)", device.address_str().c_str(), hardware_id); return false; } diff --git a/esphome/components/mopeka_std_check/mopeka_std_check.h b/esphome/components/mopeka_std_check/mopeka_std_check.h index e4d81afbd7..ee588c8e5f 100644 --- a/esphome/components/mopeka_std_check/mopeka_std_check.h +++ b/esphome/components/mopeka_std_check/mopeka_std_check.h @@ -14,6 +14,7 @@ namespace mopeka_std_check { enum SensorType { STANDARD = 0x02, XL = 0x03, + ETRAILER = 0x46, }; // 4 values in one struct so it aligns to 8 byte. One `mopeka_std_values` is 40 bit long. From e3eef1cc6dbbce019fc18168e61c71e5ba3d960e Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 20 Sep 2023 14:20:54 -0700 Subject: [PATCH 09/95] fix disabled wifi power on 8266 (#5409) Co-authored-by: Samuel Sieb --- esphome/components/wifi/wifi_component_esp8266.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 1afa439567..6e7c491967 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -98,6 +98,7 @@ bool WiFiComponent::wifi_apply_power_save_() { power_save = NONE_SLEEP_T; break; } + wifi_fpm_auto_sleep_set_in_null_mode(1); return wifi_set_sleep_type(power_save); } From bf253c21fac829da986e0e914bff7f1d36eba996 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 20 Sep 2023 14:25:16 -0700 Subject: [PATCH 10/95] fix handling of web server version (#5405) Co-authored-by: Samuel Sieb --- esphome/components/web_server/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index c6d9c31e93..966c978836 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -59,7 +59,7 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(WebServer), cv.Optional(CONF_PORT, default=80): cv.port, - cv.Optional(CONF_VERSION, default=2): cv.one_of(1, 2), + cv.Optional(CONF_VERSION, default=2): cv.one_of(1, 2, int=True), cv.Optional(CONF_CSS_URL): cv.string, cv.Optional(CONF_CSS_INCLUDE): cv.file_, cv.Optional(CONF_JS_URL): cv.string, From 397f57ce74e87c447508e940c2f9a44d6ed7bfe6 Mon Sep 17 00:00:00 2001 From: Joris S <100357138+Jorre05@users.noreply.github.com> Date: Wed, 20 Sep 2023 23:28:03 +0200 Subject: [PATCH 11/95] Climate preset fix (#5407) --- esphome/components/thermostat/thermostat_climate.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index 51da663a0c..386e13dc37 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -986,6 +986,7 @@ void ThermostatClimate::change_preset_(climate::ClimatePreset preset) { // Fire any preset changed trigger if defined Trigger<> *trig = this->preset_change_trigger_; assert(trig != nullptr); + this->preset = preset; trig->trigger(); this->refresh(); @@ -1010,6 +1011,7 @@ void ThermostatClimate::change_custom_preset_(const std::string &custom_preset) // Fire any preset changed trigger if defined Trigger<> *trig = this->preset_change_trigger_; assert(trig != nullptr); + this->custom_preset = custom_preset; trig->trigger(); this->refresh(); From 61edf8c196613c2f38da96ca7ce311e3f24c6bb7 Mon Sep 17 00:00:00 2001 From: Anthony Date: Wed, 20 Sep 2023 16:30:22 -0500 Subject: [PATCH 12/95] Remove Wi-Fi dependency from Midea component (#5394) --- esphome/components/midea/climate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/midea/climate.py b/esphome/components/midea/climate.py index 80b1461576..074ab8abb2 100644 --- a/esphome/components/midea/climate.py +++ b/esphome/components/midea/climate.py @@ -35,7 +35,7 @@ from esphome.components.climate import ( ) CODEOWNERS = ["@dudanov"] -DEPENDENCIES = ["climate", "uart", "wifi"] +DEPENDENCIES = ["climate", "uart"] AUTO_LOAD = ["sensor"] CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" CONF_POWER_USAGE = "power_usage" From 157a3e53ddde620f25522d0f218e692d86d7d291 Mon Sep 17 00:00:00 2001 From: "Kevin P. Fleming" Date: Wed, 20 Sep 2023 18:02:29 -0400 Subject: [PATCH 13/95] http_request: Cleanups and safety improvements (#5360) --- .../components/http_request/http_request.h | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 0958c07683..b885de18e6 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -80,8 +80,6 @@ template class HttpRequestSendAction : public Action { TEMPLATABLE_VALUE(std::string, url) TEMPLATABLE_VALUE(const char *, method) TEMPLATABLE_VALUE(std::string, body) - TEMPLATABLE_VALUE(const char *, useragent) - TEMPLATABLE_VALUE(uint16_t, timeout) void add_header(const char *key, TemplatableValue value) { this->headers_.insert({key, value}); } @@ -105,25 +103,18 @@ template class HttpRequestSendAction : public Action { auto f = std::bind(&HttpRequestSendAction::encode_json_func_, this, x..., std::placeholders::_1); this->parent_->set_body(json::build_json(f)); } - if (this->useragent_.has_value()) { - this->parent_->set_useragent(this->useragent_.value(x...)); - } - if (this->timeout_.has_value()) { - this->parent_->set_timeout(this->timeout_.value(x...)); - } - if (!this->headers_.empty()) { - std::list
headers; - for (const auto &item : this->headers_) { - auto val = item.second; - Header header; - header.name = item.first; - header.value = val.value(x...); - headers.push_back(header); - } - this->parent_->set_headers(headers); + std::list
headers; + for (const auto &item : this->headers_) { + auto val = item.second; + Header header; + header.name = item.first; + header.value = val.value(x...); + headers.push_back(header); } + this->parent_->set_headers(headers); this->parent_->send(this->response_triggers_); this->parent_->close(); + this->parent_->set_body(""); } protected: From 2c2821cd961b51cb303996fdb62e17f323eb8908 Mon Sep 17 00:00:00 2001 From: Trent Houliston Date: Thu, 21 Sep 2023 08:04:03 +1000 Subject: [PATCH 14/95] Make the pulse meter timeout on startup when no pulses are received (#5388) --- .../pulse_meter/pulse_meter_sensor.cpp | 35 +++++++++++++------ .../pulse_meter/pulse_meter_sensor.h | 3 +- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.cpp b/esphome/components/pulse_meter/pulse_meter_sensor.cpp index 7eef18e5e0..be5fad6fe5 100644 --- a/esphome/components/pulse_meter/pulse_meter_sensor.cpp +++ b/esphome/components/pulse_meter/pulse_meter_sensor.cpp @@ -11,6 +11,9 @@ void PulseMeterSensor::setup() { this->pin_->setup(); this->isr_pin_ = pin_->to_isr(); + // Set the last processed edge to now for the first timeout + this->last_processed_edge_us_ = micros(); + if (this->filter_mode_ == FILTER_EDGE) { this->pin_->attach_interrupt(PulseMeterSensor::edge_intr, this, gpio::INTERRUPT_RISING_EDGE); } else if (this->filter_mode_ == FILTER_PULSE) { @@ -38,12 +41,16 @@ void PulseMeterSensor::loop() { } // We need to detect at least two edges to have a valid pulse width - if (!this->initialized_) { - this->initialized_ = true; - } else { - uint32_t delta_us = this->get_->last_detected_edge_us_ - this->last_processed_edge_us_; - float pulse_width_us = delta_us / float(this->get_->count_); - this->publish_state((60.0f * 1000000.0f) / pulse_width_us); + switch (this->meter_state_) { + case MeterState::INITIAL: + case MeterState::TIMED_OUT: { + this->meter_state_ = MeterState::RUNNING; + } break; + case MeterState::RUNNING: { + uint32_t delta_us = this->get_->last_detected_edge_us_ - this->last_processed_edge_us_; + float pulse_width_us = delta_us / float(this->get_->count_); + this->publish_state((60.0f * 1000000.0f) / pulse_width_us); + } break; } this->last_processed_edge_us_ = this->get_->last_detected_edge_us_; @@ -53,10 +60,18 @@ void PulseMeterSensor::loop() { const uint32_t now = micros(); const uint32_t time_since_valid_edge_us = now - this->last_processed_edge_us_; - if (this->initialized_ && time_since_valid_edge_us > this->timeout_us_) { - ESP_LOGD(TAG, "No pulse detected for %us, assuming 0 pulses/min", time_since_valid_edge_us / 1000000); - this->initialized_ = false; - this->publish_state(0.0f); + switch (this->meter_state_) { + // Running and initial states can timeout + case MeterState::INITIAL: + case MeterState::RUNNING: { + if (time_since_valid_edge_us > this->timeout_us_) { + this->meter_state_ = MeterState::TIMED_OUT; + ESP_LOGD(TAG, "No pulse detected for %us, assuming 0 pulses/min", time_since_valid_edge_us / 1000000); + this->publish_state(0.0f); + } + } break; + default: + break; } } } diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.h b/esphome/components/pulse_meter/pulse_meter_sensor.h index ddd42c2ed5..f376ea48a5 100644 --- a/esphome/components/pulse_meter/pulse_meter_sensor.h +++ b/esphome/components/pulse_meter/pulse_meter_sensor.h @@ -38,7 +38,8 @@ class PulseMeterSensor : public sensor::Sensor, public Component { InternalFilterMode filter_mode_{FILTER_EDGE}; // Variables used in the loop - bool initialized_ = false; + enum class MeterState { INITIAL, RUNNING, TIMED_OUT }; + MeterState meter_state_ = MeterState::INITIAL; uint32_t total_pulses_ = 0; uint32_t last_processed_edge_us_ = 0; From 056a28906ba9de193da4aa78f6034984654bc4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Thu, 21 Sep 2023 00:09:23 +0200 Subject: [PATCH 15/95] Wizard: fix colored text in input prompts (#5313) --- esphome/util.py | 14 ++++++++++---- esphome/wizard.py | 18 ++++++++++-------- tests/unit_tests/test_wizard.py | 12 ++++++------ 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/esphome/util.py b/esphome/util.py index 0d60212f50..480618aca0 100644 --- a/esphome/util.py +++ b/esphome/util.py @@ -57,7 +57,7 @@ class SimpleRegistry(dict): return decorator -def safe_print(message=""): +def safe_print(message="", end="\n"): from esphome.core import CORE if CORE.dashboard: @@ -67,20 +67,26 @@ def safe_print(message=""): pass try: - print(message) + print(message, end=end) return except UnicodeEncodeError: pass try: - print(message.encode("utf-8", "backslashreplace")) + print(message.encode("utf-8", "backslashreplace"), end=end) except UnicodeEncodeError: try: - print(message.encode("ascii", "backslashreplace")) + print(message.encode("ascii", "backslashreplace"), end=end) except UnicodeEncodeError: print("Cannot print line because of invalid locale!") +def safe_input(prompt=""): + if prompt: + safe_print(prompt, end="") + return input() + + def shlex_quote(s): if not s: return "''" diff --git a/esphome/wizard.py b/esphome/wizard.py index aa05e513a7..1308338ad0 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -11,7 +11,7 @@ from esphome.core import CORE from esphome.helpers import get_bool_env, write_file from esphome.log import Fore, color from esphome.storage_json import StorageJSON, ext_storage_path -from esphome.util import safe_print +from esphome.util import safe_input, safe_print CORE_BIG = r""" _____ ____ _____ ______ / ____/ __ \| __ \| ____| @@ -252,7 +252,7 @@ def safe_print_step(step, big): def default_input(text, default): safe_print() safe_print(f"Press ENTER for default ({default})") - return input(text.format(default)) or default + return safe_input(text.format(default)) or default # From https://stackoverflow.com/a/518232/8924614 @@ -306,7 +306,7 @@ def wizard(path): ) safe_print() sleep(1) - name = input(color(Fore.BOLD_WHITE, "(name): ")) + name = safe_input(color(Fore.BOLD_WHITE, "(name): ")) while True: try: @@ -343,7 +343,9 @@ def wizard(path): while True: sleep(0.5) safe_print() - platform = input(color(Fore.BOLD_WHITE, f"({'/'.join(wizard_platforms)}): ")) + platform = safe_input( + color(Fore.BOLD_WHITE, f"({'/'.join(wizard_platforms)}): ") + ) try: platform = vol.All(vol.Upper, vol.Any(*wizard_platforms))(platform.upper()) break @@ -397,7 +399,7 @@ def wizard(path): boards.append(board_id) while True: - board = input(color(Fore.BOLD_WHITE, "(board): ")) + board = safe_input(color(Fore.BOLD_WHITE, "(board): ")) try: board = vol.All(vol.Lower, vol.Any(*boards))(board) break @@ -423,7 +425,7 @@ def wizard(path): sleep(1.5) safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'Abraham Linksys')}\".") while True: - ssid = input(color(Fore.BOLD_WHITE, "(ssid): ")) + ssid = safe_input(color(Fore.BOLD_WHITE, "(ssid): ")) try: ssid = cv.ssid(ssid) break @@ -449,7 +451,7 @@ def wizard(path): safe_print() safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'PASSWORD42')}\"") sleep(0.5) - psk = input(color(Fore.BOLD_WHITE, "(PSK): ")) + psk = safe_input(color(Fore.BOLD_WHITE, "(PSK): ")) safe_print( "Perfect! WiFi is now set up (you can create static IPs and so on later)." ) @@ -466,7 +468,7 @@ def wizard(path): safe_print() sleep(0.25) safe_print("Press ENTER for no password") - password = input(color(Fore.BOLD_WHITE, "(password): ")) + password = safe_input(color(Fore.BOLD_WHITE, "(password): ")) if not wizard_write( path=path, diff --git a/tests/unit_tests/test_wizard.py b/tests/unit_tests/test_wizard.py index 8bbce08ae5..46700a3ba8 100644 --- a/tests/unit_tests/test_wizard.py +++ b/tests/unit_tests/test_wizard.py @@ -319,7 +319,7 @@ def test_wizard_accepts_default_answers_esp8266(tmpdir, monkeypatch, wizard_answ config_file = tmpdir.join("test.yaml") input_mock = MagicMock(side_effect=wizard_answers) monkeypatch.setattr("builtins.input", input_mock) - monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) + monkeypatch.setattr(wz, "safe_print", lambda t=None, end=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) monkeypatch.setattr(wz, "wizard_write", MagicMock()) @@ -341,7 +341,7 @@ def test_wizard_accepts_default_answers_esp32(tmpdir, monkeypatch, wizard_answer config_file = tmpdir.join("test.yaml") input_mock = MagicMock(side_effect=wizard_answers) monkeypatch.setattr("builtins.input", input_mock) - monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) + monkeypatch.setattr(wz, "safe_print", lambda t=None, end=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) monkeypatch.setattr(wz, "wizard_write", MagicMock()) @@ -371,7 +371,7 @@ def test_wizard_offers_better_node_name(tmpdir, monkeypatch, wizard_answers): config_file = tmpdir.join("test.yaml") input_mock = MagicMock(side_effect=wizard_answers) monkeypatch.setattr("builtins.input", input_mock) - monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) + monkeypatch.setattr(wz, "safe_print", lambda t=None, end=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) monkeypatch.setattr(wz, "wizard_write", MagicMock()) @@ -394,7 +394,7 @@ def test_wizard_requires_correct_platform(tmpdir, monkeypatch, wizard_answers): config_file = tmpdir.join("test.yaml") input_mock = MagicMock(side_effect=wizard_answers) monkeypatch.setattr("builtins.input", input_mock) - monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) + monkeypatch.setattr(wz, "safe_print", lambda t=None, end=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) monkeypatch.setattr(wz, "wizard_write", MagicMock()) @@ -416,7 +416,7 @@ def test_wizard_requires_correct_board(tmpdir, monkeypatch, wizard_answers): config_file = tmpdir.join("test.yaml") input_mock = MagicMock(side_effect=wizard_answers) monkeypatch.setattr("builtins.input", input_mock) - monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) + monkeypatch.setattr(wz, "safe_print", lambda t=None, end=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) monkeypatch.setattr(wz, "wizard_write", MagicMock()) @@ -438,7 +438,7 @@ def test_wizard_requires_valid_ssid(tmpdir, monkeypatch, wizard_answers): config_file = tmpdir.join("test.yaml") input_mock = MagicMock(side_effect=wizard_answers) monkeypatch.setattr("builtins.input", input_mock) - monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) + monkeypatch.setattr(wz, "safe_print", lambda t=None, end=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) monkeypatch.setattr(wz, "wizard_write", MagicMock()) From 1100f67b66de7fa944c446335f6f1254ba78a78a Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 20 Sep 2023 15:26:36 -0700 Subject: [PATCH 16/95] support keypads with pulldowns (#5404) Co-authored-by: Samuel Sieb --- esphome/components/matrix_keypad/__init__.py | 4 ++++ .../components/matrix_keypad/matrix_keypad.cpp | 17 +++++++++++------ .../components/matrix_keypad/matrix_keypad.h | 2 ++ tests/test5.yaml | 1 + 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/esphome/components/matrix_keypad/__init__.py b/esphome/components/matrix_keypad/__init__.py index 1c549007b9..5250a45732 100644 --- a/esphome/components/matrix_keypad/__init__.py +++ b/esphome/components/matrix_keypad/__init__.py @@ -21,6 +21,7 @@ CONF_COLUMNS = "columns" CONF_KEYS = "keys" CONF_DEBOUNCE_TIME = "debounce_time" CONF_HAS_DIODES = "has_diodes" +CONF_HAS_PULLDOWNS = "has_pulldowns" def check_keys(obj): @@ -45,6 +46,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_KEYS): cv.string, cv.Optional(CONF_DEBOUNCE_TIME, default=1): cv.int_range(min=1, max=100), cv.Optional(CONF_HAS_DIODES): cv.boolean, + cv.Optional(CONF_HAS_PULLDOWNS): cv.boolean, } ), check_keys, @@ -69,3 +71,5 @@ async def to_code(config): cg.add(var.set_debounce_time(config[CONF_DEBOUNCE_TIME])) if CONF_HAS_DIODES in config: cg.add(var.set_has_diodes(config[CONF_HAS_DIODES])) + if CONF_HAS_PULLDOWNS in config: + cg.add(var.set_has_pulldowns(config[CONF_HAS_PULLDOWNS])) diff --git a/esphome/components/matrix_keypad/matrix_keypad.cpp b/esphome/components/matrix_keypad/matrix_keypad.cpp index 4f8962a782..902e574846 100644 --- a/esphome/components/matrix_keypad/matrix_keypad.cpp +++ b/esphome/components/matrix_keypad/matrix_keypad.cpp @@ -11,11 +11,16 @@ void MatrixKeypad::setup() { if (!has_diodes_) { pin->pin_mode(gpio::FLAG_INPUT); } else { - pin->digital_write(true); + pin->digital_write(!has_pulldowns_); + } + } + for (auto *pin : this->columns_) { + if (has_pulldowns_) { + pin->pin_mode(gpio::FLAG_INPUT); + } else { + pin->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); } } - for (auto *pin : this->columns_) - pin->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); } void MatrixKeypad::loop() { @@ -28,9 +33,9 @@ void MatrixKeypad::loop() { for (auto *row : this->rows_) { if (!has_diodes_) row->pin_mode(gpio::FLAG_OUTPUT); - row->digital_write(false); + row->digital_write(has_pulldowns_); for (auto *col : this->columns_) { - if (!col->digital_read()) { + if (col->digital_read() == has_pulldowns_) { if (key != -1) { error = true; } else { @@ -39,7 +44,7 @@ void MatrixKeypad::loop() { } pos++; } - row->digital_write(true); + row->digital_write(!has_pulldowns_); if (!has_diodes_) row->pin_mode(gpio::FLAG_INPUT); } diff --git a/esphome/components/matrix_keypad/matrix_keypad.h b/esphome/components/matrix_keypad/matrix_keypad.h index 9f5942be9a..d506040b7c 100644 --- a/esphome/components/matrix_keypad/matrix_keypad.h +++ b/esphome/components/matrix_keypad/matrix_keypad.h @@ -28,6 +28,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component { void set_keys(std::string keys) { keys_ = std::move(keys); }; void set_debounce_time(int debounce_time) { debounce_time_ = debounce_time; }; void set_has_diodes(int has_diodes) { has_diodes_ = has_diodes; }; + void set_has_pulldowns(int has_pulldowns) { has_pulldowns_ = has_pulldowns; }; void register_listener(MatrixKeypadListener *listener); @@ -37,6 +38,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component { std::string keys_; int debounce_time_ = 0; bool has_diodes_{false}; + bool has_pulldowns_{false}; int pressed_key_ = -1; std::vector listeners_{}; diff --git a/tests/test5.yaml b/tests/test5.yaml index 274570aad6..5727d30e61 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -667,6 +667,7 @@ matrix_keypad: - pin: 17 - pin: 16 keys: "1234" + has_pulldowns: true key_collector: - id: reader From 518ecb4cc4489c8a76b899bfda7576b05d84c226 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:15:50 +1000 Subject: [PATCH 17/95] Fix SPI inverted clock on ESP8266 (#5416) --- esphome/components/spi/spi.h | 6 ++---- esphome/components/spi/spi_arduino.cpp | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 2761c2d604..56aa746fc9 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -351,6 +351,7 @@ class SPIClient { : bit_order_(bit_order), mode_(mode), data_rate_(data_rate) {} virtual void spi_setup() { + esph_log_d("spi_device", "mode %u, data_rate %ukHz", (unsigned) this->mode_, (unsigned) (this->data_rate_ / 1000)); this->delegate_ = this->parent_->register_device(this, this->mode_, this->bit_order_, this->data_rate_, this->cs_); } @@ -398,10 +399,7 @@ class SPIDevice : public SPIClient { void set_data_rate(uint32_t data_rate) { this->data_rate_ = data_rate; } - void set_bit_order(SPIBitOrder order) { - this->bit_order_ = order; - esph_log_d("spi.h", "bit order set to %d", order); - } + void set_bit_order(SPIBitOrder order) { this->bit_order_ = order; } void set_mode(SPIMode mode) { this->mode_ = mode; } diff --git a/esphome/components/spi/spi_arduino.cpp b/esphome/components/spi/spi_arduino.cpp index 40ed9e6062..2e6b2d6064 100644 --- a/esphome/components/spi/spi_arduino.cpp +++ b/esphome/components/spi/spi_arduino.cpp @@ -15,6 +15,11 @@ class SPIDelegateHw : public SPIDelegate { void begin_transaction() override { #ifdef USE_RP2040 SPISettings const settings(this->data_rate_, static_cast(this->bit_order_), this->mode_); +#elif defined(ESP8266) + // Arduino ESP8266 library has mangled values for SPI modes :-( + auto mode = (this->mode_ & 0x01) + ((this->mode_ & 0x02) << 3); + ESP_LOGV(TAG, "8266 mangled SPI mode 0x%X", mode); + SPISettings const settings(this->data_rate_, this->bit_order_, mode); #else SPISettings const settings(this->data_rate_, this->bit_order_, this->mode_); #endif From 3c7c4e1dbaf25c9fa35247328aa76c03fda53fac Mon Sep 17 00:00:00 2001 From: Andrew Garrett Date: Sun, 24 Sep 2023 19:34:37 +1000 Subject: [PATCH 18/95] Make ESPHome data dir configurable (#5417) --- esphome/core/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index cca758e3c1..4897b073fa 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -21,7 +21,7 @@ from esphome.coroutine import FakeEventLoop as _FakeEventLoop # pylint: disable=unused-import from esphome.coroutine import coroutine, coroutine_with_priority # noqa -from esphome.helpers import ensure_unique_string, is_ha_addon +from esphome.helpers import ensure_unique_string, get_str_env, is_ha_addon from esphome.util import OrderedDict if TYPE_CHECKING: @@ -558,6 +558,8 @@ class EsphomeCore: def data_dir(self): if is_ha_addon(): return os.path.join("/data") + if get_str_env("ESPHOME_DATA_DIR", None) is not None: + return get_str_env("ESPHOME_DATA_DIR", None) return self.relative_config_path(".esphome") @property From 0ca8dcd08e96b431f8034acc16a7f9c2ab64eb84 Mon Sep 17 00:00:00 2001 From: Ilia Sotnikov Date: Sun, 24 Sep 2023 12:44:55 +0300 Subject: [PATCH 19/95] [RP2040W] Fix WiFi bootloop upon LibreTiny support (#5414) --- esphome/components/rp2040/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index b31192f73f..5d8608c44d 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -152,6 +152,9 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): cg.add(rp2040_ns.setup_preferences()) + # Allow LDF to properly discover dependency including those in preprocessor + # conditionals + cg.add_platformio_option("lib_ldf_mode", "chain+") cg.add_platformio_option("board", config[CONF_BOARD]) cg.add_build_flag("-DUSE_RP2040") cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) From 727056a28c8c59351ac3be6a9ee2fa1e4343ef47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Sun, 24 Sep 2023 23:15:28 +0200 Subject: [PATCH 20/95] dallas: limit addresses to 64 bits (#5413) --- esphome/components/dallas/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/dallas/sensor.py b/esphome/components/dallas/sensor.py index 9288f0a3a6..c6ebda62c8 100644 --- a/esphome/components/dallas/sensor.py +++ b/esphome/components/dallas/sensor.py @@ -24,7 +24,7 @@ CONFIG_SCHEMA = cv.All( ).extend( { cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent), - cv.Optional(CONF_ADDRESS): cv.hex_int, + cv.Optional(CONF_ADDRESS): cv.hex_uint64_t, cv.Optional(CONF_INDEX): cv.positive_int, cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12), } From c34d5111fceb3f16f147c9aeb7bd7684a39dc454 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:23:40 +1300 Subject: [PATCH 21/95] Bump actions/checkout from 4.0.0 to 4.1.0 (#5420) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/ci.yml | 22 +++++++++++----------- .github/workflows/release.yml | 6 +++--- .github/workflows/sync-device-classes.yml | 4 ++-- .github/workflows/yaml-lint.yml | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index b53eaf8e1a..7fe51163ba 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -40,7 +40,7 @@ jobs: arch: [amd64, armv7, aarch64] build_type: ["ha-addon", "docker", "lint"] steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 - name: Set up Python uses: actions/setup-python@v4.7.0 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 792d972311..26fcbbf458 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: cache-key: ${{ steps.cache-key.outputs.key }} steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Generate cache-key id: cache-key run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT @@ -66,7 +66,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -87,7 +87,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -108,7 +108,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -129,7 +129,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -150,7 +150,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -171,7 +171,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -191,7 +191,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -216,7 +216,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Find all YAML test files id: set-matrix run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT @@ -241,7 +241,7 @@ jobs: file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }} steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -296,7 +296,7 @@ jobs: steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Restore Python uses: ./.github/actions/restore-python with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 99d1594f03..6934d36686 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: outputs: tag: ${{ steps.tag.outputs.tag }} steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 - name: Get tag id: tag # yamllint disable rule:line-length @@ -43,7 +43,7 @@ jobs: if: github.repository == 'esphome/esphome' && github.event_name == 'release' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 - name: Set up Python uses: actions/setup-python@v4.7.0 with: @@ -88,7 +88,7 @@ jobs: target: "lint" baseimg: "docker" steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 - name: Set up Python uses: actions/setup-python@v4.7.0 with: diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index 943e93a0b7..25d36bc6d0 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -13,10 +13,10 @@ jobs: if: github.repository == 'esphome/esphome' steps: - name: Checkout - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Checkout Home Assistant - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 with: repository: home-assistant/core path: lib/home-assistant diff --git a/.github/workflows/yaml-lint.yml b/.github/workflows/yaml-lint.yml index 77b3c1dcb5..65628a22bb 100644 --- a/.github/workflows/yaml-lint.yml +++ b/.github/workflows/yaml-lint.yml @@ -17,6 +17,6 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code from GitHub - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Run yamllint uses: frenck/action-yamllint@v1.4.1 From a031cc3b848b942119d63b6673027e39d2c26259 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:25:51 +1300 Subject: [PATCH 22/95] Bump zeroconf from 0.108.0 to 0.112.0 (#5392) 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 19c05bf8f7..63199680cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ esptool==4.6.2 click==8.1.7 esphome-dashboard==20230904.0 aioesphomeapi==15.0.0 -zeroconf==0.108.0 +zeroconf==0.112.0 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From 17e1d4c2455997a0a15c875950be1942c6249d0d Mon Sep 17 00:00:00 2001 From: Kapil Yedidi Date: Sun, 24 Sep 2023 16:05:56 -0700 Subject: [PATCH 23/95] Fix typo in documentation (#5425) --- esphome/components/pmsa003i/pmsa003i.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/pmsa003i/pmsa003i.h b/esphome/components/pmsa003i/pmsa003i.h index 10176218ed..1fe4139951 100644 --- a/esphome/components/pmsa003i/pmsa003i.h +++ b/esphome/components/pmsa003i/pmsa003i.h @@ -17,12 +17,12 @@ struct PM25AQIData { uint16_t pm10_env, ///< Environmental PM1.0 pm25_env, ///< Environmental PM2.5 pm100_env; ///< Environmental PM10.0 - uint16_t particles_03um, ///< 0.3um Particle Count - particles_05um, ///< 0.5um Particle Count - particles_10um, ///< 1.0um Particle Count - particles_25um, ///< 2.5um Particle Count - particles_50um, ///< 5.0um Particle Count - particles_100um; ///< 10.0um Particle Count + uint16_t particles_03um, ///> 0.3um Particle Count + particles_05um, ///> 0.5um Particle Count + particles_10um, ///> 1.0um Particle Count + particles_25um, ///> 2.5um Particle Count + particles_50um, ///> 5.0um Particle Count + particles_100um; ///> 10.0um Particle Count uint16_t unused; ///< Unused uint16_t checksum; ///< Packet checksum }; From 607d0b426402ff27324f05c881c617162c2a8a7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 09:28:25 +1300 Subject: [PATCH 24/95] Bump pylint from 2.17.5 to 2.17.6 (#5429) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index a07815df54..92cb8b1be2 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -pylint==2.17.5 +pylint==2.17.6 flake8==6.1.0 # also change in .pre-commit-config.yaml when updating black==23.9.1 # also change in .pre-commit-config.yaml when updating pyupgrade==3.10.1 # also change in .pre-commit-config.yaml when updating From 2f7a378c7b87cd11b750b36475f6129599ffda8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 26 Sep 2023 23:23:21 +0200 Subject: [PATCH 25/95] LibreTiny: enable MQTT, bump to v1.4.1 (#5419) --- esphome/components/libretiny/__init__.py | 2 +- esphome/components/mqtt/__init__.py | 8 +- .../components/mqtt/mqtt_backend_libretiny.h | 74 +++++++++++++++++++ esphome/components/mqtt/mqtt_client.cpp | 5 +- esphome/components/mqtt/mqtt_client.h | 4 + 5 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 esphome/components/mqtt/mqtt_backend_libretiny.h diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 3c1c0ac3f0..b01d342a87 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -170,7 +170,7 @@ def _notify_old_style(config): ARDUINO_VERSIONS = { "dev": (cv.Version(0, 0, 0), "https://github.com/libretiny-eu/libretiny.git"), "latest": (cv.Version(0, 0, 0), None), - "recommended": (cv.Version(1, 4, 0), None), + "recommended": (cv.Version(1, 4, 1), None), } diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 9df2067832..10ae8ac40d 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -250,7 +250,7 @@ CONFIG_SCHEMA = cv.All( } ), validate_config, - cv.only_on(["esp32", "esp8266"]), + cv.only_on(["esp32", "esp8266", "bk72xx"]), ) @@ -271,10 +271,10 @@ def exp_mqtt_message(config): async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) - # Add required libraries for ESP8266 - if CORE.is_esp8266: + # Add required libraries for ESP8266 and LibreTiny + if CORE.is_esp8266 or CORE.is_libretiny: # https://github.com/heman/async-mqtt-client/blob/master/library.json - cg.add_library("heman/AsyncMqttClient-esphome", "1.0.0") + cg.add_library("heman/AsyncMqttClient-esphome", "2.0.0") cg.add_define("USE_MQTT") cg.add_global(mqtt_ns.using) diff --git a/esphome/components/mqtt/mqtt_backend_libretiny.h b/esphome/components/mqtt/mqtt_backend_libretiny.h new file mode 100644 index 0000000000..5373a1926a --- /dev/null +++ b/esphome/components/mqtt/mqtt_backend_libretiny.h @@ -0,0 +1,74 @@ +#pragma once + +#ifdef USE_LIBRETINY + +#include "mqtt_backend.h" +#include + +namespace esphome { +namespace mqtt { + +class MQTTBackendLibreTiny final : public MQTTBackend { + public: + void set_keep_alive(uint16_t keep_alive) final { mqtt_client_.setKeepAlive(keep_alive); } + void set_client_id(const char *client_id) final { mqtt_client_.setClientId(client_id); } + void set_clean_session(bool clean_session) final { mqtt_client_.setCleanSession(clean_session); } + void set_credentials(const char *username, const char *password) final { + mqtt_client_.setCredentials(username, password); + } + void set_will(const char *topic, uint8_t qos, bool retain, const char *payload) final { + mqtt_client_.setWill(topic, qos, retain, payload); + } + void set_server(network::IPAddress ip, uint16_t port) final { + mqtt_client_.setServer(IPAddress(static_cast(ip)), port); + } + void set_server(const char *host, uint16_t port) final { mqtt_client_.setServer(host, port); } +#if ASYNC_TCP_SSL_ENABLED + void set_secure(bool secure) { mqtt_client.setSecure(secure); } + void add_server_fingerprint(const uint8_t *fingerprint) { mqtt_client.addServerFingerprint(fingerprint); } +#endif + + void set_on_connect(std::function &&callback) final { + this->mqtt_client_.onConnect(std::move(callback)); + } + void set_on_disconnect(std::function &&callback) final { + auto async_callback = [callback](AsyncMqttClientDisconnectReason reason) { + // int based enum so casting isn't a problem + callback(static_cast(reason)); + }; + this->mqtt_client_.onDisconnect(std::move(async_callback)); + } + void set_on_subscribe(std::function &&callback) final { + this->mqtt_client_.onSubscribe(std::move(callback)); + } + void set_on_unsubscribe(std::function &&callback) final { + this->mqtt_client_.onUnsubscribe(std::move(callback)); + } + void set_on_message(std::function &&callback) final { + auto async_callback = [callback](const char *topic, const char *payload, + AsyncMqttClientMessageProperties async_properties, size_t len, size_t index, + size_t total) { callback(topic, payload, len, index, total); }; + mqtt_client_.onMessage(std::move(async_callback)); + } + void set_on_publish(std::function &&callback) final { + this->mqtt_client_.onPublish(std::move(callback)); + } + + bool connected() const final { return mqtt_client_.connected(); } + void connect() final { mqtt_client_.connect(); } + void disconnect() final { mqtt_client_.disconnect(true); } + bool subscribe(const char *topic, uint8_t qos) final { return mqtt_client_.subscribe(topic, qos) != 0; } + bool unsubscribe(const char *topic) final { return mqtt_client_.unsubscribe(topic) != 0; } + bool publish(const char *topic, const char *payload, size_t length, uint8_t qos, bool retain) final { + return mqtt_client_.publish(topic, qos, retain, payload, length, false, 0) != 0; + } + using MQTTBackend::publish; + + protected: + AsyncMqttClient mqtt_client_; +}; + +} // namespace mqtt +} // namespace esphome + +#endif // defined(USE_LIBRETINY) diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 0c6da42328..fd5e13ecc7 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -106,6 +106,9 @@ void MQTTClientComponent::send_device_info_() { #ifdef USE_ESP32 root["platform"] = "ESP32"; #endif +#ifdef USE_LIBRETINY + root["platform"] = lt_cpu_get_model_name(); +#endif root["board"] = ESPHOME_BOARD; #if defined(USE_WIFI) @@ -156,7 +159,7 @@ void MQTTClientComponent::start_dnslookup_() { this->dns_resolve_error_ = false; this->dns_resolved_ = false; ip_addr_t addr; -#ifdef USE_ESP32 +#if defined(USE_ESP32) || defined(USE_LIBRETINY) err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4); #endif diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 00eb3fdd40..bcb44ab4c2 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -13,6 +13,8 @@ #include "mqtt_backend_esp32.h" #elif defined(USE_ESP8266) #include "mqtt_backend_esp8266.h" +#elif defined(USE_LIBRETINY) +#include "mqtt_backend_libretiny.h" #endif #include "lwip/ip_addr.h" @@ -300,6 +302,8 @@ class MQTTClientComponent : public Component { MQTTBackendESP32 mqtt_backend_; #elif defined(USE_ESP8266) MQTTBackendESP8266 mqtt_backend_; +#elif defined(USE_LIBRETINY) + MQTTBackendLibreTiny mqtt_backend_; #endif MQTTClientState state_{MQTT_CLIENT_DISCONNECTED}; From 86db559f6e8edf1f0e69cb300dedd1954ab766bd Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Tue, 26 Sep 2023 20:25:00 -0300 Subject: [PATCH 26/95] Wireguard keepalive remove uint16 type (#5430) --- esphome/components/wireguard/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/wireguard/__init__.py b/esphome/components/wireguard/__init__.py index 717fe50d2c..acb5f690ec 100644 --- a/esphome/components/wireguard/__init__.py +++ b/esphome/components/wireguard/__init__.py @@ -9,6 +9,7 @@ from esphome.const import ( CONF_REBOOT_TIMEOUT, ) from esphome.components import time +from esphome.core import TimePeriod CONF_NETMASK = "netmask" CONF_PRIVATE_KEY = "private_key" @@ -59,9 +60,9 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_PEER_ALLOWED_IPS, default=["0.0.0.0/0"]): cv.ensure_list( _cidr_network ), - cv.Optional(CONF_PEER_PERSISTENT_KEEPALIVE, default=0): cv.Any( + cv.Optional(CONF_PEER_PERSISTENT_KEEPALIVE, default="0s"): cv.All( cv.positive_time_period_seconds, - cv.uint16_t, + cv.Range(max=TimePeriod(seconds=65535)), ), cv.Optional( CONF_REBOOT_TIMEOUT, default="15min" From 5360e14a9ce3f6e0b3e169ef0c555323a68ed81b Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 27 Sep 2023 09:25:14 +1000 Subject: [PATCH 27/95] Fix #4896 and #4903 (#5433) --- CODEOWNERS | 2 +- esphome/components/ili9xxx/display.py | 9 +++---- .../components/ili9xxx/ili9xxx_display.cpp | 12 ++++++++++ esphome/components/ili9xxx/ili9xxx_display.h | 6 +++++ esphome/components/ili9xxx/ili9xxx_init.h | 24 +++++++++++++++++++ tests/test1.yaml | 1 + 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 22e46aa2f0..3920a9100e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -131,7 +131,7 @@ esphome/components/i2s_audio/* @jesserockz esphome/components/i2s_audio/media_player/* @jesserockz esphome/components/i2s_audio/microphone/* @jesserockz esphome/components/i2s_audio/speaker/* @jesserockz -esphome/components/ili9xxx/* @nielsnl68 +esphome/components/ili9xxx/* @clydebarrow @nielsnl68 esphome/components/improv_base/* @esphome/core esphome/components/improv_serial/* @esphome/core esphome/components/ina260/* @mreditor97 diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 0435460b6a..89a6b2d1b9 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -25,7 +25,7 @@ def AUTO_LOAD(): return [] -CODEOWNERS = ["@nielsnl68"] +CODEOWNERS = ["@nielsnl68", "@clydebarrow"] ili9XXX_ns = cg.esphome_ns.namespace("ili9xxx") ili9XXXSPI = ili9XXX_ns.class_( @@ -42,6 +42,7 @@ MODELS = { "ILI9341": ili9XXX_ns.class_("ILI9XXXILI9341", ili9XXXSPI), "ILI9342": ili9XXX_ns.class_("ILI9XXXILI9342", ili9XXXSPI), "ILI9481": ili9XXX_ns.class_("ILI9XXXILI9481", ili9XXXSPI), + "ILI9481-18": ili9XXX_ns.class_("ILI9XXXILI948118", ili9XXXSPI), "ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI), "ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI), "ILI9488_A": ili9XXX_ns.class_("ILI9XXXILI9488A", ili9XXXSPI), @@ -140,8 +141,6 @@ async def to_code(config): rhs = [] for x in range(256): rhs.extend([HexInt(x), HexInt(x), HexInt(x)]) - prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) - cg.add(var.set_palette(prog_arr)) elif config[CONF_COLOR_PALETTE] == "IMAGE_ADAPTIVE": cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8_INDEXED)) from PIL import Image @@ -178,6 +177,4 @@ async def to_code(config): if rhs is not None: prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) cg.add(var.set_palette(prog_arr)) - - spi_data_rate = str(spi.SPI_DATA_RATE_OPTIONS[config[CONF_DATA_RATE]]) - cg.add_define("ILI9XXXDisplay_DATA_RATE", cg.RawExpression(spi_data_rate)) + cg.add(var.set_data_rate(config[CONF_DATA_RATE])) diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index 750f629db2..abe01ea8c3 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -59,6 +59,7 @@ void ILI9XXXDisplay::dump_config() { if (this->is_18bitdisplay_) { ESP_LOGCONFIG(TAG, " 18-Bit Mode: YES"); } + ESP_LOGCONFIG(TAG, " Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000)); LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" DC Pin: ", this->dc_pin_); @@ -387,6 +388,17 @@ void ILI9XXXILI9481::initialize() { } } +void ILI9XXXILI948118::initialize() { + this->init_lcd_(INITCMD_ILI9481_18); + if (this->width_ == 0) { + this->width_ = 320; + } + if (this->height_ == 0) { + this->height_ = 480; + } + this->is_18bitdisplay_ = true; +} + // 35_TFT display void ILI9XXXILI9486::initialize() { this->init_lcd_(INITCMD_ILI9486); diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index 15b08e6c76..4e8355b9a5 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -120,6 +120,12 @@ class ILI9XXXILI9481 : public ILI9XXXDisplay { void initialize() override; }; +//----------- ILI9481 in 18 bit mode -------------- +class ILI9XXXILI948118 : public ILI9XXXDisplay { + protected: + void initialize() override; +}; + //----------- ILI9XXX_35_TFT rotated display -------------- class ILI9XXXILI9486 : public ILI9XXXDisplay { protected: diff --git a/esphome/components/ili9xxx/ili9xxx_init.h b/esphome/components/ili9xxx/ili9xxx_init.h index 1856fb06ab..031dc25f91 100644 --- a/esphome/components/ili9xxx/ili9xxx_init.h +++ b/esphome/components/ili9xxx/ili9xxx_init.h @@ -94,12 +94,36 @@ static const uint8_t PROGMEM INITCMD_ILI9481[] = { ILI9XXX_IFCTR , 1, 0x83, ILI9XXX_GMCTR ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00, ILI9XXX_IFMODE , 1, 0x00, // CommandAccessProtect + ILI9XXX_PTLAR , 4, 0, 0, 1, 0xDF, 0xE4 , 1, 0xA0, + ILI9XXX_MADCTL , 1, MADCTL_MV | MADCTL_BGR, // Memory Access Control ILI9XXX_CSCON , 1, 0x01, + ILI9XXX_PIXFMT, 1, 0x55, // 16 bit mode + ILI9XXX_INVON, 0, ILI9XXX_DISPON, 0x80, // Set display on 0x00 // end }; +static const uint8_t PROGMEM INITCMD_ILI9481_18[] = { + ILI9XXX_SLPOUT , 0x80, // Exit sleep mode + ILI9XXX_PWSET , 3, 0x07, 0x41, 0x1D, + ILI9XXX_VMCTR , 3, 0x00, 0x1C, 0x1F, + ILI9XXX_PWSETN , 2, 0x01, 0x11, + ILI9XXX_PWCTR1 , 5, 0x10, 0x3B, 0x00, 0x02, 0x11, + ILI9XXX_VMCTR1 , 1, 0x03, + ILI9XXX_IFCTR , 1, 0x83, + ILI9XXX_GMCTR ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00, + ILI9XXX_IFMODE , 1, 0x00, // CommandAccessProtect + ILI9XXX_PTLAR , 4, 0, 0, 1, 0xDF, + 0xE4 , 1, 0xA0, + ILI9XXX_MADCTL , 1, MADCTL_MX| MADCTL_BGR, // Memory Access Control + ILI9XXX_CSCON , 1, 0x01, + ILI9XXX_PIXFMT, 1, 0x66, // 18 bit mode + ILI9XXX_INVON, 0, + ILI9XXX_DISPON, 0x80, // Set display on + 0x00 // end +}; + static const uint8_t PROGMEM INITCMD_ILI9486[] = { ILI9XXX_SLPOUT, 0x80, ILI9XXX_PIXFMT, 1, 0x55, diff --git a/tests/test1.yaml b/tests/test1.yaml index fe983cf421..350057e3cc 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2972,6 +2972,7 @@ display: model: TFT 2.4 cs_pin: GPIO5 dc_pin: GPIO4 + color_palette: GRAYSCALE reset_pin: GPIO22 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); From 9d4f471855fcf3cab19368d0558f17a25629f0dc Mon Sep 17 00:00:00 2001 From: Marc J Date: Wed, 27 Sep 2023 00:45:21 -0700 Subject: [PATCH 28/95] Tuya Number Scaling by step value (#5108) --- esphome/components/tuya/number/tuya_number.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/tuya/number/tuya_number.cpp b/esphome/components/tuya/number/tuya_number.cpp index 5c7cafbf7a..30ef8b8f72 100644 --- a/esphome/components/tuya/number/tuya_number.cpp +++ b/esphome/components/tuya/number/tuya_number.cpp @@ -10,7 +10,7 @@ void TuyaNumber::setup() { this->parent_->register_listener(this->number_id_, [this](const TuyaDatapoint &datapoint) { if (datapoint.type == TuyaDatapointType::INTEGER) { ESP_LOGV(TAG, "MCU reported number %u is: %d", datapoint.id, datapoint.value_int); - this->publish_state(datapoint.value_int); + this->publish_state(datapoint.value_int * this->traits.get_step()); } else if (datapoint.type == TuyaDatapointType::ENUM) { ESP_LOGV(TAG, "MCU reported number %u is: %u", datapoint.id, datapoint.value_enum); this->publish_state(datapoint.value_enum); @@ -22,7 +22,8 @@ void TuyaNumber::setup() { void TuyaNumber::control(float value) { ESP_LOGV(TAG, "Setting number %u: %f", this->number_id_, value); if (this->type_ == TuyaDatapointType::INTEGER) { - this->parent_->set_integer_datapoint_value(this->number_id_, value); + int integer_value = lround(value / this->traits.get_step()); + this->parent_->set_integer_datapoint_value(this->number_id_, integer_value); } else if (this->type_ == TuyaDatapointType::ENUM) { this->parent_->set_enum_datapoint_value(this->number_id_, value); } From 57b7dd0fa24d749420747729be854eb381a3238e Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Wed, 27 Sep 2023 10:38:43 +0200 Subject: [PATCH 29/95] Refactor ip address representation (#5252) --- .../captive_portal/captive_portal.cpp | 2 +- esphome/components/e131/e131_packet.cpp | 7 +- .../ethernet/ethernet_component.cpp | 41 +++---- esphome/components/mdns/mdns_rp2040.cpp | 3 +- .../components/mqtt/mqtt_backend_esp8266.h | 4 +- esphome/components/mqtt/mqtt_client.cpp | 12 +- esphome/components/network/ip_address.h | 110 ++++++++++++++---- .../components/wake_on_lan/wake_on_lan.cpp | 5 +- .../wifi/wifi_component_esp32_arduino.cpp | 55 ++++----- .../wifi/wifi_component_esp8266.cpp | 46 ++++---- .../wifi/wifi_component_esp_idf.cpp | 53 ++++----- .../wifi/wifi_component_libretiny.cpp | 7 +- .../components/wifi/wifi_component_pico_w.cpp | 22 ++-- tests/test1.yaml | 3 + tests/test10.yaml | 3 + tests/test11.5.yaml | 3 + tests/test2.yaml | 3 + tests/test3.1.yaml | 3 + tests/test3.yaml | 3 + tests/test4.yaml | 3 + tests/test5.yaml | 3 + tests/test6.yaml | 3 + tests/test7.yaml | 3 + tests/test8.yaml | 3 + 24 files changed, 225 insertions(+), 175 deletions(-) diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 74c6606fc0..cc78528e46 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -48,7 +48,7 @@ void CaptivePortal::start() { this->dns_server_ = make_unique(); this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError); network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); - this->dns_server_->start(53, "*", (uint32_t) ip); + this->dns_server_->start(53, "*", IPAddress(ip)); #endif this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) { diff --git a/esphome/components/e131/e131_packet.cpp b/esphome/components/e131/e131_packet.cpp index ac8b72f6e7..e1ae41cbaf 100644 --- a/esphome/components/e131/e131_packet.cpp +++ b/esphome/components/e131/e131_packet.cpp @@ -67,8 +67,8 @@ bool E131Component::join_igmp_groups_() { if (!universe.second) continue; - ip4_addr_t multicast_addr = {static_cast( - network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)))}; + ip4_addr_t multicast_addr = + network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)); auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr); @@ -101,8 +101,7 @@ void E131Component::leave_(int universe) { } if (listen_method_ == E131_MULTICAST) { - ip4_addr_t multicast_addr = { - static_cast(network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)))}; + ip4_addr_t multicast_addr = network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)); igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr); } diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 59d2e4c4d6..03fdc49338 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -236,7 +236,7 @@ bool EthernetComponent::can_proceed() { return this->is_connected(); } network::IPAddress EthernetComponent::get_ip_address() { esp_netif_ip_info_t ip; esp_netif_get_ip_info(this->eth_netif_, &ip); - return {ip.ip.addr}; + return network::IPAddress(&ip.ip); } void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) { @@ -293,9 +293,9 @@ void EthernetComponent::start_connect_() { esp_netif_ip_info_t info; if (this->manual_ip_.has_value()) { - info.ip.addr = static_cast(this->manual_ip_->static_ip); - info.gw.addr = static_cast(this->manual_ip_->gateway); - info.netmask.addr = static_cast(this->manual_ip_->subnet); + info.ip = this->manual_ip_->static_ip; + info.gw = this->manual_ip_->gateway; + info.netmask = this->manual_ip_->subnet; } else { info.ip.addr = 0; info.gw.addr = 0; @@ -318,24 +318,14 @@ void EthernetComponent::start_connect_() { ESPHL_ERROR_CHECK(err, "DHCPC set IP info error"); if (this->manual_ip_.has_value()) { - if (uint32_t(this->manual_ip_->dns1) != 0) { + if (this->manual_ip_->dns1.is_set()) { ip_addr_t d; -#if LWIP_IPV6 - d.type = IPADDR_TYPE_V4; - d.u_addr.ip4.addr = static_cast(this->manual_ip_->dns1); -#else - d.addr = static_cast(this->manual_ip_->dns1); -#endif + d = this->manual_ip_->dns1; dns_setserver(0, &d); } - if (uint32_t(this->manual_ip_->dns2) != 0) { + if (this->manual_ip_->dns2.is_set()) { ip_addr_t d; -#if LWIP_IPV6 - d.type = IPADDR_TYPE_V4; - d.u_addr.ip4.addr = static_cast(this->manual_ip_->dns2); -#else - d.addr = static_cast(this->manual_ip_->dns2); -#endif + d = this->manual_ip_->dns2; dns_setserver(1, &d); } } else { @@ -360,21 +350,16 @@ bool EthernetComponent::is_connected() { return this->state_ == EthernetComponen void EthernetComponent::dump_connect_params_() { esp_netif_ip_info_t ip; esp_netif_get_ip_info(this->eth_netif_, &ip); - ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(ip.ip.addr).str().c_str()); + ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(&ip.ip).str().c_str()); ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); - ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(ip.netmask.addr).str().c_str()); - ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(ip.gw.addr).str().c_str()); + ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(&ip.netmask).str().c_str()); + ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(&ip.gw).str().c_str()); const ip_addr_t *dns_ip1 = dns_getserver(0); const ip_addr_t *dns_ip2 = dns_getserver(1); -#if LWIP_IPV6 - ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->u_addr.ip4.addr).str().c_str()); - ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->u_addr.ip4.addr).str().c_str()); -#else - ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->addr).str().c_str()); - ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->addr).str().c_str()); -#endif + ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1).str().c_str()); + ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2).str().c_str()); #if ENABLE_IPV6 if (this->ipv6_count_ > 0) { diff --git a/esphome/components/mdns/mdns_rp2040.cpp b/esphome/components/mdns/mdns_rp2040.cpp index b30a3a4ee7..56afd6f5e1 100644 --- a/esphome/components/mdns/mdns_rp2040.cpp +++ b/esphome/components/mdns/mdns_rp2040.cpp @@ -13,8 +13,7 @@ namespace mdns { void MDNSComponent::setup() { this->compile_records_(); - network::IPAddress addr = network::get_ip_address(); - MDNS.begin(this->hostname_.c_str(), (uint32_t) addr); + MDNS.begin(this->hostname_.c_str()); for (const auto &service : this->services_) { // Strip the leading underscore from the proto and service_type. While it is diff --git a/esphome/components/mqtt/mqtt_backend_esp8266.h b/esphome/components/mqtt/mqtt_backend_esp8266.h index 2d91877e9d..981d27693f 100644 --- a/esphome/components/mqtt/mqtt_backend_esp8266.h +++ b/esphome/components/mqtt/mqtt_backend_esp8266.h @@ -19,9 +19,7 @@ class MQTTBackendESP8266 final : public MQTTBackend { void set_will(const char *topic, uint8_t qos, bool retain, const char *payload) final { mqtt_client_.setWill(topic, qos, retain, payload); } - void set_server(network::IPAddress ip, uint16_t port) final { - mqtt_client_.setServer(IPAddress(static_cast(ip)), port); - } + void set_server(network::IPAddress ip, uint16_t port) final { mqtt_client_.setServer(IPAddress(ip), port); } void set_server(const char *host, uint16_t port) final { mqtt_client_.setServer(host, port); } #if ASYNC_TCP_SSL_ENABLED void set_secure(bool secure) { mqtt_client.setSecure(secure); } diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index fd5e13ecc7..6f63935e6e 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -171,11 +171,7 @@ void MQTTClientComponent::start_dnslookup_() { case ERR_OK: { // Got IP immediately this->dns_resolved_ = true; -#if LWIP_IPV6 - this->ip_ = addr.u_addr.ip4.addr; -#else - this->ip_ = addr.addr; -#endif + this->ip_ = network::IPAddress(&addr); this->start_connect_(); return; } @@ -226,11 +222,7 @@ void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t * if (ipaddr == nullptr) { a_this->dns_resolve_error_ = true; } else { -#if LWIP_IPV6 - a_this->ip_ = ipaddr->u_addr.ip4.addr; -#else - a_this->ip_ = ipaddr->addr; -#endif + a_this->ip_ = network::IPAddress(ipaddr); a_this->dns_resolved_ = true; } } diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index af198179ba..8b05237c05 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -3,42 +3,104 @@ #include #include #include +#include + +#if USE_ARDUINO +#include +#include +#endif /* USE_ADRDUINO */ + +#if USE_ESP32_FRAMEWORK_ARDUINO +#define arduino_ns Arduino_h +#elif USE_LIBRETINY +#define arduino_ns arduino +#elif USE_ARDUINO +#define arduino_ns +#endif + +#ifdef USE_ESP32 +#include +#include +#endif namespace esphome { namespace network { struct IPAddress { public: - IPAddress() : addr_({0, 0, 0, 0}) {} - IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) : addr_({first, second, third, fourth}) {} - IPAddress(uint32_t raw) { - addr_[0] = (uint8_t) (raw >> 0); - addr_[1] = (uint8_t) (raw >> 8); - addr_[2] = (uint8_t) (raw >> 16); - addr_[3] = (uint8_t) (raw >> 24); + IPAddress() { ip_addr_set_zero(&ip_addr_); } + IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) { + IP_ADDR4(&ip_addr_, first, second, third, fourth); } - operator uint32_t() const { - uint32_t res = 0; - res |= ((uint32_t) addr_[0]) << 0; - res |= ((uint32_t) addr_[1]) << 8; - res |= ((uint32_t) addr_[2]) << 16; - res |= ((uint32_t) addr_[3]) << 24; - return res; + IPAddress(const ip_addr_t *other_ip) { ip_addr_copy(ip_addr_, *other_ip); } + IPAddress(const std::string &in_address) { ipaddr_aton(in_address.c_str(), &ip_addr_); } + IPAddress(ip4_addr_t *other_ip) { memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip4_addr_t)); } +#if USE_ARDUINO + IPAddress(const arduino_ns::IPAddress &other_ip) { ip_addr_set_ip4_u32(&ip_addr_, other_ip); } +#endif +#if LWIP_IPV6 + IPAddress(ip6_addr_t *other_ip) { + memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip6_addr_t)); + ip_addr_.type = IPADDR_TYPE_V6; } - std::string str() const { - char buffer[24]; - snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d", addr_[0], addr_[1], addr_[2], addr_[3]); - return buffer; +#endif /* LWIP_IPV6 */ + +#ifdef USE_ESP32 +#if LWIP_IPV6 + IPAddress(esp_ip6_addr_t *other_ip) { + memcpy((void *) &ip_addr_.u_addr.ip6, (void *) other_ip, sizeof(esp_ip6_addr_t)); + ip_addr_.type = IPADDR_TYPE_V6; } - bool operator==(const IPAddress &other) const { - return addr_[0] == other.addr_[0] && addr_[1] == other.addr_[1] && addr_[2] == other.addr_[2] && - addr_[3] == other.addr_[3]; +#endif /* LWIP_IPV6 */ + IPAddress(esp_ip4_addr_t *other_ip) { memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(esp_ip4_addr_t)); } + operator esp_ip_addr_t() const { + esp_ip_addr_t tmp; +#if LWIP_IPV6 + memcpy((void *) &tmp, (void *) &ip_addr_, sizeof(ip_addr_)); +#else + memcpy((void *) &tmp.u_addr.ip4, (void *) &ip_addr_, sizeof(ip_addr_)); +#endif /* LWIP_IPV6 */ + return tmp; + } + operator esp_ip4_addr_t() const { + esp_ip4_addr_t tmp; +#if LWIP_IPV6 + memcpy((void *) &tmp, (void *) &ip_addr_.u_addr.ip4, sizeof(esp_ip4_addr_t)); +#else + memcpy((void *) &tmp, (void *) &ip_addr_, sizeof(ip_addr_)); +#endif /* LWIP_IPV6 */ + return tmp; + } +#endif /* USE_ESP32 */ + + operator ip_addr_t() const { return ip_addr_; } +#if LWIP_IPV6 + operator ip4_addr_t() const { return *ip_2_ip4(&ip_addr_); } +#endif /* LWIP_IPV6 */ + +#if USE_ARDUINO + operator arduino_ns::IPAddress() const { return ip_addr_get_ip4_u32(&ip_addr_); } +#endif + + bool is_set() { return !ip_addr_isany(&ip_addr_); } + bool is_ip4() { return IP_IS_V4(&ip_addr_); } + bool is_ip6() { return IP_IS_V6(&ip_addr_); } + std::string str() const { return ipaddr_ntoa(&ip_addr_); } + bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); } + bool operator!=(const IPAddress &other) const { return !(&ip_addr_ == &other.ip_addr_); } + IPAddress &operator+=(uint8_t increase) { + if (IP_IS_V4(&ip_addr_)) { +#if LWIP_IPV6 + (((u8_t *) (&ip_addr_.u_addr.ip4))[3]) += increase; +#else + (((u8_t *) (&ip_addr_.addr))[3]) += increase; +#endif /* LWIP_IPV6 */ + } + return *this; } - uint8_t operator[](int index) const { return addr_[index]; } - uint8_t &operator[](int index) { return addr_[index]; } protected: - std::array addr_; + ip_addr_t ip_addr_; }; } // namespace network diff --git a/esphome/components/wake_on_lan/wake_on_lan.cpp b/esphome/components/wake_on_lan/wake_on_lan.cpp index 893aa75895..a4dd0f3b6f 100644 --- a/esphome/components/wake_on_lan/wake_on_lan.cpp +++ b/esphome/components/wake_on_lan/wake_on_lan.cpp @@ -30,11 +30,10 @@ void WakeOnLanButton::press_action() { ESP_LOGI(TAG, "Sending Wake-on-LAN Packet..."); bool begin_status = false; bool end_status = false; - uint32_t interface = esphome::network::get_ip_address(); - IPAddress interface_ip = IPAddress(interface); IPAddress broadcast = IPAddress(255, 255, 255, 255); #ifdef USE_ESP8266 - begin_status = this->udp_client_.beginPacketMulticast(broadcast, 9, interface_ip, 128); + begin_status = this->udp_client_.beginPacketMulticast(broadcast, 9, + IPAddress((ip_addr_t) esphome::network::get_ip_address()), 128); #endif #ifdef USE_ESP32 begin_status = this->udp_client_.beginPacket(broadcast, 9); diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index 5b147b20c6..17b15757ef 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -113,9 +113,9 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { tcpip_adapter_ip_info_t info; memset(&info, 0, sizeof(info)); - info.ip.addr = static_cast(manual_ip->static_ip); - info.gw.addr = static_cast(manual_ip->gateway); - info.netmask.addr = static_cast(manual_ip->subnet); + info.ip = manual_ip->static_ip; + info.gw = manual_ip->gateway; + info.netmask = manual_ip->subnet; esp_err_t dhcp_stop_ret = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); if (dhcp_stop_ret != ESP_OK && dhcp_stop_ret != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) { @@ -128,23 +128,16 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { } ip_addr_t dns; +// TODO: is this needed? #if LWIP_IPV6 dns.type = IPADDR_TYPE_V4; #endif - if (uint32_t(manual_ip->dns1) != 0) { -#if LWIP_IPV6 - dns.u_addr.ip4.addr = static_cast(manual_ip->dns1); -#else - dns.addr = static_cast(manual_ip->dns1); -#endif + if (manual_ip->dns1.is_set()) { + dns = manual_ip->dns1; dns_setserver(0, &dns); } - if (uint32_t(manual_ip->dns2) != 0) { -#if LWIP_IPV6 - dns.u_addr.ip4.addr = static_cast(manual_ip->dns2); -#else - dns.addr = static_cast(manual_ip->dns2); -#endif + if (manual_ip->dns2.is_set()) { + dns = manual_ip->dns2; dns_setserver(1, &dns); } @@ -156,7 +149,7 @@ network::IPAddress WiFiComponent::wifi_sta_ip() { return {}; tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip); - return {ip.ip.addr}; + return network::IPAddress(&ip.ip); } bool WiFiComponent::wifi_apply_hostname_() { @@ -614,13 +607,13 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { tcpip_adapter_ip_info_t info; memset(&info, 0, sizeof(info)); if (manual_ip.has_value()) { - info.ip.addr = static_cast(manual_ip->static_ip); - info.gw.addr = static_cast(manual_ip->gateway); - info.netmask.addr = static_cast(manual_ip->subnet); + info.ip = manual_ip->static_ip; + info.gw = manual_ip->gateway; + info.netmask = manual_ip->subnet; } else { - info.ip.addr = static_cast(network::IPAddress(192, 168, 4, 1)); - info.gw.addr = static_cast(network::IPAddress(192, 168, 4, 1)); - info.netmask.addr = static_cast(network::IPAddress(255, 255, 255, 0)); + info.ip = network::IPAddress(192, 168, 4, 1); + info.gw = network::IPAddress(192, 168, 4, 1); + info.netmask = network::IPAddress(255, 255, 255, 0); } tcpip_adapter_dhcp_status_t dhcp_status; tcpip_adapter_dhcps_get_status(TCPIP_ADAPTER_IF_AP, &dhcp_status); @@ -638,12 +631,12 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { dhcps_lease_t lease; lease.enable = true; - network::IPAddress start_address = info.ip.addr; - start_address[3] += 99; - lease.start_ip.addr = static_cast(start_address); + network::IPAddress start_address = network::IPAddress(&info.ip); + start_address += 99; + lease.start_ip = start_address; ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str()); - start_address[3] += 100; - lease.end_ip.addr = static_cast(start_address); + start_address += 100; + lease.end_ip = start_address; ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str()); err = tcpip_adapter_dhcps_option(TCPIP_ADAPTER_OP_SET, TCPIP_ADAPTER_REQUESTED_IP_ADDRESS, &lease, sizeof(lease)); @@ -702,7 +695,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { network::IPAddress WiFiComponent::wifi_soft_ap_ip() { tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip); - return {ip.ip.addr}; + return network::IPAddress(&ip.ip); } bool WiFiComponent::wifi_disconnect_() { return esp_wifi_disconnect(); } @@ -718,9 +711,9 @@ bssid_t WiFiComponent::wifi_bssid() { std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } -network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; } -network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } -network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {WiFi.dnsIP(num)}; } +network::IPAddress WiFiComponent::wifi_subnet_mask_() { return network::IPAddress(WiFi.subnetMask()); } +network::IPAddress WiFiComponent::wifi_gateway_ip_() { return network::IPAddress(WiFi.gatewayIP()); } +network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return network::IPAddress(WiFi.dnsIP(num)); } void WiFiComponent::wifi_loop_() {} } // namespace wifi diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 6e7c491967..a48c6c711d 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -147,9 +147,9 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { #endif struct ip_info info {}; - info.ip.addr = static_cast(manual_ip->static_ip); - info.gw.addr = static_cast(manual_ip->gateway); - info.netmask.addr = static_cast(manual_ip->subnet); + info.ip = manual_ip->static_ip; + info.gw = manual_ip->gateway; + info.netmask = manual_ip->subnet; if (dhcp_status == DHCP_STARTED) { bool dhcp_stop_ret = wifi_station_dhcpc_stop(); @@ -165,12 +165,12 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { } ip_addr_t dns; - if (uint32_t(manual_ip->dns1) != 0) { - ip_addr_set_ip4_u32_val(dns, static_cast(manual_ip->dns1)); + if (manual_ip->dns1.is_set()) { + dns = manual_ip->dns1; dns_setserver(0, &dns); } - if (uint32_t(manual_ip->dns2) != 0) { - ip_addr_set_ip4_u32_val(dns, static_cast(manual_ip->dns2)); + if (manual_ip->dns2.is_set()) { + dns = manual_ip->dns2; dns_setserver(1, &dns); } @@ -190,7 +190,7 @@ network::IPAddress WiFiComponent::wifi_sta_ip() { return {}; struct ip_info ip {}; wifi_get_ip_info(STATION_IF, &ip); - return {ip.ip.addr}; + return network::IPAddress(&ip.ip); } bool WiFiComponent::wifi_apply_hostname_() { const std::string &hostname = App.get_name(); @@ -695,13 +695,13 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { struct ip_info info {}; if (manual_ip.has_value()) { - info.ip.addr = static_cast(manual_ip->static_ip); - info.gw.addr = static_cast(manual_ip->gateway); - info.netmask.addr = static_cast(manual_ip->subnet); + info.ip = manual_ip->static_ip; + info.gw = manual_ip->gateway; + info.netmask = manual_ip->subnet; } else { - info.ip.addr = static_cast(network::IPAddress(192, 168, 4, 1)); - info.gw.addr = static_cast(network::IPAddress(192, 168, 4, 1)); - info.netmask.addr = static_cast(network::IPAddress(255, 255, 255, 0)); + info.ip = network::IPAddress(192, 168, 4, 1); + info.gw = network::IPAddress(192, 168, 4, 1); + info.netmask = network::IPAddress(255, 255, 255, 0); } if (wifi_softap_dhcps_status() == DHCP_STARTED) { @@ -721,12 +721,12 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { struct dhcps_lease lease {}; lease.enable = true; - network::IPAddress start_address = info.ip.addr; - start_address[3] += 99; - lease.start_ip.addr = static_cast(start_address); + network::IPAddress start_address = network::IPAddress(&info.ip); + start_address += 99; + lease.start_ip = start_address; ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str()); - start_address[3] += 100; - lease.end_ip.addr = static_cast(start_address); + start_address += 100; + lease.end_ip = start_address; ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str()); if (!wifi_softap_set_dhcps_lease(&lease)) { ESP_LOGV(TAG, "Setting SoftAP DHCP lease failed!"); @@ -793,7 +793,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { network::IPAddress WiFiComponent::wifi_soft_ap_ip() { struct ip_info ip {}; wifi_get_ip_info(SOFTAP_IF, &ip); - return {ip.ip.addr}; + return network::IPAddress(&ip.ip); } bssid_t WiFiComponent::wifi_bssid() { bssid_t bssid{}; @@ -807,9 +807,9 @@ bssid_t WiFiComponent::wifi_bssid() { std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } -network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; } -network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } -network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {WiFi.dnsIP(num)}; } +network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {(const ip_addr_t *) WiFi.subnetMask()}; } +network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {(const ip_addr_t *) WiFi.gatewayIP()}; } +network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {(const ip_addr_t *) WiFi.dnsIP(num)}; } void WiFiComponent::wifi_loop_() {} } // namespace wifi diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 0ff9e932b2..34ecaf887d 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -437,9 +437,9 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { } esp_netif_ip_info_t info; // struct of ip4_addr_t with ip, netmask, gw - info.ip.addr = static_cast(manual_ip->static_ip); - info.gw.addr = static_cast(manual_ip->gateway); - info.netmask.addr = static_cast(manual_ip->subnet); + info.ip = manual_ip->static_ip; + info.gw = manual_ip->gateway; + info.netmask = manual_ip->subnet; err = esp_netif_dhcpc_stop(s_sta_netif); if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) { ESP_LOGV(TAG, "esp_netif_dhcpc_stop failed: %s", esp_err_to_name(err)); @@ -452,12 +452,12 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { } esp_netif_dns_info_t dns; - if (uint32_t(manual_ip->dns1) != 0) { - dns.ip.u_addr.ip4.addr = static_cast(manual_ip->dns1); + if (manual_ip->dns1.is_set()) { + dns.ip = manual_ip->dns1; esp_netif_set_dns_info(s_sta_netif, ESP_NETIF_DNS_MAIN, &dns); } - if (uint32_t(manual_ip->dns2) != 0) { - dns.ip.u_addr.ip4.addr = static_cast(manual_ip->dns2); + if (manual_ip->dns2.is_set()) { + dns.ip = manual_ip->dns2; esp_netif_set_dns_info(s_sta_netif, ESP_NETIF_DNS_BACKUP, &dns); } @@ -471,9 +471,10 @@ network::IPAddress WiFiComponent::wifi_sta_ip() { esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip); if (err != ESP_OK) { ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); - return false; + // TODO: do something smarter + // return false; } - return {ip.ip.addr}; + return network::IPAddress(&ip.ip); } bool WiFiComponent::wifi_apply_hostname_() { @@ -769,13 +770,13 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { esp_netif_ip_info_t info; if (manual_ip.has_value()) { - info.ip.addr = static_cast(manual_ip->static_ip); - info.gw.addr = static_cast(manual_ip->gateway); - info.netmask.addr = static_cast(manual_ip->subnet); + info.ip = manual_ip->static_ip; + info.gw = manual_ip->gateway; + info.netmask = manual_ip->subnet; } else { - info.ip.addr = static_cast(network::IPAddress(192, 168, 4, 1)); - info.gw.addr = static_cast(network::IPAddress(192, 168, 4, 1)); - info.netmask.addr = static_cast(network::IPAddress(255, 255, 255, 0)); + info.ip = network::IPAddress(192, 168, 4, 1); + info.gw = network::IPAddress(192, 168, 4, 1); + info.netmask = network::IPAddress(255, 255, 255, 0); } err = esp_netif_dhcpc_stop(s_sta_netif); @@ -792,12 +793,12 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { dhcps_lease_t lease; lease.enable = true; - network::IPAddress start_address = info.ip.addr; - start_address[3] += 99; - lease.start_ip.addr = static_cast(start_address); + network::IPAddress start_address = network::IPAddress(&info.ip); + start_address += 99; + lease.start_ip = start_address; ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str()); - start_address[3] += 100; - lease.end_ip.addr = static_cast(start_address); + start_address += 100; + lease.end_ip = start_address; ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str()); err = esp_netif_dhcps_option(s_sta_netif, ESP_NETIF_OP_SET, ESP_NETIF_REQUESTED_IP_ADDRESS, &lease, sizeof(lease)); @@ -855,7 +856,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { network::IPAddress WiFiComponent::wifi_soft_ap_ip() { esp_netif_ip_info_t ip; esp_netif_get_ip_info(s_sta_netif, &ip); - return {ip.ip.addr}; + return network::IPAddress(&ip.ip); } bool WiFiComponent::wifi_disconnect_() { return esp_wifi_disconnect(); } @@ -907,7 +908,7 @@ network::IPAddress WiFiComponent::wifi_subnet_mask_() { ESP_LOGW(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); return {}; } - return {ip.netmask.addr}; + return network::IPAddress(&ip.netmask); } network::IPAddress WiFiComponent::wifi_gateway_ip_() { esp_netif_ip_info_t ip; @@ -916,15 +917,11 @@ network::IPAddress WiFiComponent::wifi_gateway_ip_() { ESP_LOGW(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); return {}; } - return {ip.gw.addr}; + return network::IPAddress(&ip.gw); } network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { const ip_addr_t *dns_ip = dns_getserver(num); -#if LWIP_IPV6 - return {dns_ip->u_addr.ip4.addr}; -#else - return {dns_ip->addr}; -#endif + return network::IPAddress(dns_ip); } } // namespace wifi diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index abad5aca9c..d7f4406540 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -76,9 +76,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { return true; } - WiFi.config(static_cast(manual_ip->static_ip), static_cast(manual_ip->gateway), - static_cast(manual_ip->subnet), static_cast(manual_ip->dns1), - static_cast(manual_ip->dns2)); + WiFi.config(manual_ip->static_ip, manual_ip->gateway, manual_ip->subnet, manual_ip->dns1, manual_ip->dns2); return true; } @@ -420,8 +418,7 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { return false; if (manual_ip.has_value()) { - return WiFi.softAPConfig(static_cast(manual_ip->static_ip), static_cast(manual_ip->gateway), - static_cast(manual_ip->subnet)); + return WiFi.softAPConfig(manual_ip->static_ip, manual_ip->gateway, manual_ip->subnet); } else { return WiFi.softAPConfig(IPAddress(192, 168, 4, 1), IPAddress(192, 168, 4, 1), IPAddress(255, 255, 255, 0)); } diff --git a/esphome/components/wifi/wifi_component_pico_w.cpp b/esphome/components/wifi/wifi_component_pico_w.cpp index 149ca61cd5..d67b466d6c 100644 --- a/esphome/components/wifi/wifi_component_pico_w.cpp +++ b/esphome/components/wifi/wifi_component_pico_w.cpp @@ -70,11 +70,11 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { return true; } - IPAddress ip_address = IPAddress(manual_ip->static_ip); - IPAddress gateway = IPAddress(manual_ip->gateway); - IPAddress subnet = IPAddress(manual_ip->subnet); + IPAddress ip_address = manual_ip->static_ip; + IPAddress gateway = manual_ip->gateway; + IPAddress subnet = manual_ip->subnet; - IPAddress dns = IPAddress(manual_ip->dns1); + IPAddress dns = manual_ip->dns1; WiFi.config(ip_address, dns, gateway, subnet); return true; @@ -151,7 +151,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return true; } -network::IPAddress WiFiComponent::wifi_soft_ap_ip() { return {WiFi.localIP()}; } +network::IPAddress WiFiComponent::wifi_soft_ap_ip() { return {(const ip_addr_t *) WiFi.localIP()}; } bool WiFiComponent::wifi_disconnect_() { int err = cyw43_wifi_leave(&cyw43_state, CYW43_ITF_STA); @@ -170,16 +170,12 @@ std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } -network::IPAddress WiFiComponent::wifi_sta_ip() { return {WiFi.localIP()}; } -network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; } -network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } +network::IPAddress WiFiComponent::wifi_sta_ip() { return {(const ip_addr_t *) WiFi.localIP()}; } +network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {(const ip_addr_t *) WiFi.subnetMask()}; } +network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {(const ip_addr_t *) WiFi.gatewayIP()}; } network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { const ip_addr_t *dns_ip = dns_getserver(num); -#ifdef PIO_FRAMEWORK_ARDUINO_ENABLE_IPV6 - return {dns_ip->u_addr.ip4.addr}; -#else - return {dns_ip->addr}; -#endif + return network::IPAddress(dns_ip); } void WiFiComponent::wifi_loop_() { diff --git a/tests/test1.yaml b/tests/test1.yaml index 350057e3cc..96dda707b6 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -82,6 +82,9 @@ wifi: reboot_timeout: 120s power_save_mode: light +network: + enable_ipv6: true + mdns: disabled: false diff --git a/tests/test10.yaml b/tests/test10.yaml index 0470e37e6c..fc74d95d84 100644 --- a/tests/test10.yaml +++ b/tests/test10.yaml @@ -14,6 +14,9 @@ wifi: reboot_timeout: 3min power_save_mode: high +network: + enable_ipv6: true + logger: level: VERBOSE diff --git a/tests/test11.5.yaml b/tests/test11.5.yaml index 544dc10930..06985611e7 100644 --- a/tests/test11.5.yaml +++ b/tests/test11.5.yaml @@ -25,6 +25,9 @@ wifi: gateway: 192.168.1.1 subnet: 255.255.255.0 +network: + enable_ipv6: true + api: ota: diff --git a/tests/test2.yaml b/tests/test2.yaml index c04e6726b1..5485711c2e 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -28,6 +28,9 @@ ethernet: subnet: 255.255.255.0 domain: .local +network: + enable_ipv6: true + mdns: disabled: true diff --git a/tests/test3.1.yaml b/tests/test3.1.yaml index 16f31409d8..8884479f61 100644 --- a/tests/test3.1.yaml +++ b/tests/test3.1.yaml @@ -21,6 +21,9 @@ wifi: ssid: "MySSID" password: "password1" +network: + enable_ipv6: true + web_server: port: 80 version: 2 diff --git a/tests/test3.yaml b/tests/test3.yaml index 5d30e415fb..e7cf24a95a 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -215,6 +215,9 @@ wifi: ssid: "MySSID" password: "password1" +network: + enable_ipv6: true + uart: - id: uart_1 tx_pin: diff --git a/tests/test4.yaml b/tests/test4.yaml index 341e613785..1b809256e7 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -21,6 +21,9 @@ ethernet: subnet: 255.255.255.0 domain: .local +network: + enable_ipv6: true + api: i2c: diff --git a/tests/test5.yaml b/tests/test5.yaml index 5727d30e61..d87a7f50d4 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -22,6 +22,9 @@ wifi: gateway: 192.168.1.1 subnet: 255.255.255.0 +network: + enable_ipv6: true + api: ota: diff --git a/tests/test6.yaml b/tests/test6.yaml index 3d6a1ceb1f..7c72151860 100644 --- a/tests/test6.yaml +++ b/tests/test6.yaml @@ -16,6 +16,9 @@ wifi: - ssid: "MySSID" password: "password1" +network: + enable_ipv6: true + api: ota: diff --git a/tests/test7.yaml b/tests/test7.yaml index 2355dd6feb..b22fbfbcb4 100644 --- a/tests/test7.yaml +++ b/tests/test7.yaml @@ -3,6 +3,9 @@ wifi: ssid: 'ssid' +network: + enable_ipv6: true + esp32: board: lolin_c3_mini framework: diff --git a/tests/test8.yaml b/tests/test8.yaml index 01d12ea330..3ba8aa19ef 100644 --- a/tests/test8.yaml +++ b/tests/test8.yaml @@ -3,6 +3,9 @@ wifi: ssid: "ssid" +network: + enable_ipv6: true + esp32: board: esp32s3box variant: ESP32S3 From 4ac4492241deaecfdf1dd2597650a009659eb94d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 28 Sep 2023 11:20:44 +1300 Subject: [PATCH 30/95] Fix .esphome path when not using envvar (#5440) --- esphome/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 4897b073fa..4c99aff011 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -558,7 +558,7 @@ class EsphomeCore: def data_dir(self): if is_ha_addon(): return os.path.join("/data") - if get_str_env("ESPHOME_DATA_DIR", None) is not None: + if "ESPHOME_DATA_DIR" in os.environ: return get_str_env("ESPHOME_DATA_DIR", None) return self.relative_config_path(".esphome") From 12365976c4dda765c22e0b51075196931da8e3ed Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 28 Sep 2023 11:40:36 +1300 Subject: [PATCH 31/95] Migrate dashboard json files to /data folder instead of wiping out (#5441) --- docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/run | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/run b/docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/run index 775c2fa0d6..edb98a8d9b 100755 --- a/docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/run +++ b/docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/run @@ -42,7 +42,12 @@ fi mkdir -p "${pio_cache_base}" if bashio::fs.directory_exists '/config/esphome/.esphome'; then - bashio::log.info "Removing old .esphome directory..." + bashio::log.info "Migrating old .esphome directory..." + if bashio::fs.file_exists '/config/esphome/.esphome/esphome.json'; then + mv /config/esphome/.esphome/esphome.json /data/esphome.json + fi + mkdir -p "/data/storage" + mv /config/esphome/.esphome/*.json /data/storage/ || true rm -rf /config/esphome/.esphome fi From 43355435756afee62d68368ee5985a4c420da875 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 10:58:51 +1300 Subject: [PATCH 32/95] Bump zeroconf from 0.112.0 to 0.115.0 (#5432) 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 63199680cf..821e7c5786 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ esptool==4.6.2 click==8.1.7 esphome-dashboard==20230904.0 aioesphomeapi==15.0.0 -zeroconf==0.112.0 +zeroconf==0.115.0 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From 507dc5f4961751cdf651327ac76ca1c70561b4f2 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 29 Sep 2023 10:36:31 +1000 Subject: [PATCH 33/95] SPI fixes for buggy components (#5446) --- CODEOWNERS | 2 +- esphome/components/max7219/max7219.cpp | 5 +---- esphome/components/spi/__init__.py | 2 +- esphome/components/spi/spi.h | 1 + 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 3920a9100e..ce19f14c05 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -270,7 +270,7 @@ esphome/components/sn74hc165/* @jesserockz esphome/components/socket/* @esphome/core esphome/components/sonoff_d1/* @anatoly-savchenkov esphome/components/speaker/* @jesserockz -esphome/components/spi/* @esphome/core +esphome/components/spi/* @clydebarrow @esphome/core esphome/components/spi_device/* @clydebarrow esphome/components/spi_led_strip/* @clydebarrow esphome/components/sprinkler/* @kbx81 diff --git a/esphome/components/max7219/max7219.cpp b/esphome/components/max7219/max7219.cpp index 38b4a165cb..b08723f1d4 100644 --- a/esphome/components/max7219/max7219.cpp +++ b/esphome/components/max7219/max7219.cpp @@ -217,10 +217,7 @@ uint8_t MAX7219Component::printf(const char *format, ...) { return 0; } void MAX7219Component::set_writer(max7219_writer_t &&writer) { this->writer_ = writer; } -void MAX7219Component::set_intensity(uint8_t intensity) { - this->intensity_ = intensity; - this->send_to_all_(MAX7219_REGISTER_INTENSITY, this->intensity_); -} +void MAX7219Component::set_intensity(uint8_t intensity) { this->intensity_ = intensity; } void MAX7219Component::set_num_chips(uint8_t num_chips) { this->num_chips_ = num_chips; } uint8_t MAX7219Component::strftime(uint8_t pos, const char *format, ESPTime time) { diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 79e7a5b034..a5aa610462 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -28,7 +28,7 @@ from esphome.const import ( ) from esphome.core import coroutine_with_priority, CORE -CODEOWNERS = ["@esphome/core"] +CODEOWNERS = ["@esphome/core", "@clydebarrow"] spi_ns = cg.esphome_ns.namespace("spi") SPIComponent = spi_ns.class_("SPIComponent", cg.Component) SPIDevice = spi_ns.class_("SPIDevice") diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 56aa746fc9..107ffb7cb5 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -248,6 +248,7 @@ class SPIDelegateDummy : public SPIDelegate { SPIDelegateDummy() = default; uint8_t transfer(uint8_t data) override { return 0; } + void end_transaction() override{}; void begin_transaction() override; }; From 4d81153150a1bfd5f970bb930976d1e4c7a1077f Mon Sep 17 00:00:00 2001 From: Avri Chen-Roth Date: Fri, 29 Sep 2023 04:17:32 +0300 Subject: [PATCH 34/95] Fix an Issue with IR Remote Climate and Whirlpool protocol toggle (#5447) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/whirlpool/whirlpool.cpp | 7 +++++++ esphome/components/whirlpool/whirlpool.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/esphome/components/whirlpool/whirlpool.cpp b/esphome/components/whirlpool/whirlpool.cpp index 225423b4db..1ac32f30da 100644 --- a/esphome/components/whirlpool/whirlpool.cpp +++ b/esphome/components/whirlpool/whirlpool.cpp @@ -33,6 +33,7 @@ const uint8_t WHIRLPOOL_SWING_MASK = 128; const uint8_t WHIRLPOOL_POWER = 0x04; void WhirlpoolClimate::transmit_state() { + this->last_transmit_time_ = millis(); // setting the time of the last transmission. uint8_t remote_state[WHIRLPOOL_STATE_LENGTH] = {0}; remote_state[0] = 0x83; remote_state[1] = 0x06; @@ -149,6 +150,12 @@ void WhirlpoolClimate::transmit_state() { } bool WhirlpoolClimate::on_receive(remote_base::RemoteReceiveData data) { + // Check if the esp isn't currently transmitting. + if (millis() - this->last_transmit_time_ < 500) { + ESP_LOGV(TAG, "Blocked receive because of current trasmittion"); + return false; + } + // Validate header if (!data.expect_item(WHIRLPOOL_HEADER_MARK, WHIRLPOOL_HEADER_SPACE)) { ESP_LOGV(TAG, "Header fail"); diff --git a/esphome/components/whirlpool/whirlpool.h b/esphome/components/whirlpool/whirlpool.h index 7f31894df9..907a21225c 100644 --- a/esphome/components/whirlpool/whirlpool.h +++ b/esphome/components/whirlpool/whirlpool.h @@ -47,6 +47,8 @@ class WhirlpoolClimate : public climate_ir::ClimateIR { void transmit_state() override; /// Handle received IR Buffer bool on_receive(remote_base::RemoteReceiveData data) override; + /// Set the time of the last transmission. + int32_t last_transmit_time_{}; bool send_swing_cmd_{false}; Model model_; From 2c94c3d96f932056a1b8099906b3fabc57e6c79b Mon Sep 17 00:00:00 2001 From: leoshusar Date: Fri, 29 Sep 2023 05:40:56 +0200 Subject: [PATCH 35/95] [BP1658CJ] Missing clock line delays and ack bit (#5448) * fix: missing clock line delays and ack bit * chore: remove esphome namespace from delay methods * style: removed trailing whitespace --- esphome/components/bp1658cj/bp1658cj.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/esphome/components/bp1658cj/bp1658cj.cpp b/esphome/components/bp1658cj/bp1658cj.cpp index 5b9e4a5a2c..d3f3e71fed 100644 --- a/esphome/components/bp1658cj/bp1658cj.cpp +++ b/esphome/components/bp1658cj/bp1658cj.cpp @@ -12,6 +12,8 @@ static const uint8_t BP1658CJ_ADDR_START_3CH = 0x10; static const uint8_t BP1658CJ_ADDR_START_2CH = 0x20; static const uint8_t BP1658CJ_ADDR_START_5CH = 0x30; +static const uint8_t BP1658CJ_DELAY = 2; + void BP1658CJ::setup() { ESP_LOGCONFIG(TAG, "Setting up BP1658CJ Output Component..."); this->data_pin_->setup(); @@ -81,27 +83,41 @@ void BP1658CJ::set_channel_value_(uint8_t channel, uint16_t value) { } this->pwm_amounts_[channel] = value; } + void BP1658CJ::write_bit_(bool value) { - this->clock_pin_->digital_write(false); this->data_pin_->digital_write(value); this->clock_pin_->digital_write(true); + + delayMicroseconds(BP1658CJ_DELAY); + + this->clock_pin_->digital_write(false); } void BP1658CJ::write_byte_(uint8_t data) { for (uint8_t mask = 0x80; mask; mask >>= 1) { this->write_bit_(data & mask); + delayMicroseconds(BP1658CJ_DELAY); } - this->clock_pin_->digital_write(false); - this->data_pin_->digital_write(true); + + // ack bit + this->data_pin_->pin_mode(gpio::FLAG_INPUT); this->clock_pin_->digital_write(true); + + delayMicroseconds(BP1658CJ_DELAY); + + this->clock_pin_->digital_write(false); + this->data_pin_->pin_mode(gpio::FLAG_OUTPUT); } void BP1658CJ::write_buffer_(uint8_t *buffer, uint8_t size) { this->data_pin_->digital_write(false); + this->clock_pin_->digital_write(false); + for (uint32_t i = 0; i < size; i++) { this->write_byte_(buffer[i]); + delayMicroseconds(BP1658CJ_DELAY); } - this->clock_pin_->digital_write(false); + this->clock_pin_->digital_write(true); this->data_pin_->digital_write(true); } From d3913be7e531e07f90941eeba4f85c5272d79e90 Mon Sep 17 00:00:00 2001 From: De Cock Xavier Date: Sat, 30 Sep 2023 00:08:56 +0200 Subject: [PATCH 36/95] =?UTF-8?q?[ssd1351]=20fix:=20wait=20for=20the=20com?= =?UTF-8?q?ponent=20to=20be=20at=20least=20in=20setup=20phase=20b=E2=80=A6?= =?UTF-8?q?=20(#5454)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- esphome/components/ssd1351_base/ssd1351_base.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/ssd1351_base/ssd1351_base.cpp b/esphome/components/ssd1351_base/ssd1351_base.cpp index 036a6a0e82..38ea05a0b8 100644 --- a/esphome/components/ssd1351_base/ssd1351_base.cpp +++ b/esphome/components/ssd1351_base/ssd1351_base.cpp @@ -112,6 +112,9 @@ void SSD1351::set_brightness(float brightness) { } else { this->brightness_ = brightness; } + if (!this->is_ready()) { + return; // Component is not yet setup skip the command + } // now write the new brightness level to the display this->command(SSD1351_CONTRASTMASTER); this->data(int(SSD1351_MAX_CONTRAST * (this->brightness_))); From b3dc2d43a52b181b700477db78a762a228ec2747 Mon Sep 17 00:00:00 2001 From: "John K. Luebs" Date: Fri, 29 Sep 2023 18:27:40 -0500 Subject: [PATCH 37/95] Do not enable SHT3x heater by default. Fixes #4886. (#5445) --- esphome/components/sht3xd/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sht3xd/sensor.py b/esphome/components/sht3xd/sensor.py index 5c73a63f1a..80e15a1ab9 100644 --- a/esphome/components/sht3xd/sensor.py +++ b/esphome/components/sht3xd/sensor.py @@ -38,7 +38,7 @@ CONFIG_SCHEMA = ( device_class=DEVICE_CLASS_HUMIDITY, state_class=STATE_CLASS_MEASUREMENT, ), - cv.Optional(CONF_HEATER_ENABLED, default=True): cv.boolean, + cv.Optional(CONF_HEATER_ENABLED, default=False): cv.boolean, }, ) .extend(cv.polling_component_schema("60s")) From 0b5a57ead403103050288dfe959201b8ead08965 Mon Sep 17 00:00:00 2001 From: Angel Nunez Mencias Date: Sat, 30 Sep 2023 01:34:56 +0200 Subject: [PATCH 38/95] Fix SPI support for second bus on 2023.9.1 (#5456) --- esphome/components/spi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index a5aa610462..fb30755511 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -187,7 +187,7 @@ def get_spi_interface(index): # Following code can't apply to C2, H2 or 8266 since they have only one SPI if get_target_variant() in (VARIANT_ESP32S3, VARIANT_ESP32S2): return "new SPIClass(FSPI)" - return "return new SPIClass(HSPI)" + return "new SPIClass(HSPI)" SPI_SCHEMA = cv.All( From 2513ede3ec2afc8a6d2f0561c1a5425dc0830589 Mon Sep 17 00:00:00 2001 From: Angel Nunez Mencias Date: Sat, 30 Sep 2023 03:48:51 +0200 Subject: [PATCH 39/95] Add testcases for multiple SPI buses on ESP32 Arduino (#5457) --- tests/test4.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test4.yaml b/tests/test4.yaml index 1b809256e7..bb4357e28d 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -32,10 +32,15 @@ i2c: scan: false spi: +- id: spi_id_1 clk_pin: GPIO21 mosi_pin: GPIO22 miso_pin: GPIO23 interface: hardware +- id: spi_id_2 + clk_pin: GPIO32 + mosi_pin: GPIO33 + interface: hardware uart: - id: uart115200 @@ -92,6 +97,7 @@ sx1509: address: 0x3E mcp3204: + spi_id: spi_id_1 cs_pin: GPIO23 dac7678: @@ -495,6 +501,7 @@ display: update_interval: 16ms - platform: waveshare_epaper + spi_id: spi_id_1 cs_pin: GPIO23 dc_pin: GPIO23 busy_pin: GPIO23 @@ -504,6 +511,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: waveshare_epaper + spi_id: spi_id_1 cs_pin: GPIO23 dc_pin: GPIO23 busy_pin: GPIO23 @@ -514,6 +522,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: waveshare_epaper + spi_id: spi_id_1 cs_pin: GPIO23 dc_pin: GPIO23 busy_pin: GPIO23 @@ -523,6 +532,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: waveshare_epaper + spi_id: spi_id_1 cs_pin: GPIO23 dc_pin: GPIO23 busy_pin: GPIO23 @@ -673,6 +683,7 @@ touchscreen: - platform: xpt2046 id: xpt_touchscreen + spi_id: spi_id_2 cs_pin: 17 interrupt_pin: 16 display: inkplate_display From 589b9e10b2c877909083c6551c9d510d02d23ca5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 2 Oct 2023 13:45:24 +1300 Subject: [PATCH 40/95] Ensure esphome directory exists on addon startup (#5464) --- docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/run | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/run b/docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/run index edb98a8d9b..f973dfcaf8 100755 --- a/docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/run +++ b/docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/run @@ -41,6 +41,8 @@ fi mkdir -p "${pio_cache_base}" +mkdir -p /config/esphome + if bashio::fs.directory_exists '/config/esphome/.esphome'; then bashio::log.info "Migrating old .esphome directory..." if bashio::fs.file_exists '/config/esphome/.esphome/esphome.json'; then From a33b8abce8ab0ec7b3bb81e62c58ab213c44193c Mon Sep 17 00:00:00 2001 From: De Cock Xavier Date: Mon, 2 Oct 2023 22:25:13 +0200 Subject: [PATCH 41/95] Feat/component poller suspend (#5423) --- esphome/automation.py | 38 ++++++++++++++++++++++++++++++++++ esphome/core/base_automation.h | 34 ++++++++++++++++++++++++++++++ esphome/core/component.cpp | 10 +++++++++ esphome/core/component.h | 6 ++++++ tests/test1.yaml | 16 ++++++++++++++ 5 files changed, 104 insertions(+) diff --git a/esphome/automation.py b/esphome/automation.py index 0c4bda09d1..d90a9cb99a 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -11,6 +11,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_TYPE_ID, CONF_TIME, + CONF_UPDATE_INTERVAL, ) from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from esphome.util import Registry @@ -69,6 +70,8 @@ WhileAction = cg.esphome_ns.class_("WhileAction", Action) RepeatAction = cg.esphome_ns.class_("RepeatAction", Action) WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component) UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action) +SuspendComponentAction = cg.esphome_ns.class_("SuspendComponentAction", Action) +ResumeComponentAction = cg.esphome_ns.class_("ResumeComponentAction", Action) Automation = cg.esphome_ns.class_("Automation") LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition) @@ -303,6 +306,41 @@ async def component_update_action_to_code(config, action_id, template_arg, args) return cg.new_Pvariable(action_id, template_arg, comp) +@register_action( + "component.suspend", + SuspendComponentAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(cg.PollingComponent), + } + ), +) +async def component_suspend_action_to_code(config, action_id, template_arg, args): + comp = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, comp) + + +@register_action( + "component.resume", + ResumeComponentAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(cg.PollingComponent), + cv.Optional(CONF_UPDATE_INTERVAL): cv.templatable( + cv.positive_time_period_milliseconds + ), + } + ), +) +async def component_resume_action_to_code(config, action_id, template_arg, args): + comp = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, comp) + if CONF_UPDATE_INTERVAL in config: + template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, int) + cg.add(var.set_update_interval(template_)) + return var + + async def build_action(full_config, template_arg, args): registry_entry, config = cg.extract_registry_entry_config( ACTION_REGISTRY, full_config diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index a17b6a6f85..af618af99a 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -330,4 +330,38 @@ template class UpdateComponentAction : public Action { PollingComponent *component_; }; +template class SuspendComponentAction : public Action { + public: + SuspendComponentAction(PollingComponent *component) : component_(component) {} + + void play(Ts... x) override { + if (!this->component_->is_ready()) + return; + this->component_->stop_poller(); + } + + protected: + PollingComponent *component_; +}; + +template class ResumeComponentAction : public Action { + public: + ResumeComponentAction(PollingComponent *component) : component_(component) {} + TEMPLATABLE_VALUE(uint32_t, update_interval) + + void play(Ts... x) override { + if (!this->component_->is_ready()) { + return; + } + optional update_interval = this->update_interval_.optional_value(x...); + if (update_interval.has_value()) { + this->component_->set_update_interval(update_interval.value()); + } + this->component_->start_poller(); + } + + protected: + PollingComponent *component_; +}; + } // namespace esphome diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index ae85d55498..e2f27f9828 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -188,10 +188,20 @@ void PollingComponent::call_setup() { // Let the polling component subclass setup their HW. this->setup(); + // init the poller + this->start_poller(); +} + +void PollingComponent::start_poller() { // Register interval. this->set_interval("update", this->get_update_interval(), [this]() { this->update(); }); } +void PollingComponent::stop_poller() { + // Clear the interval to suspend component + this->cancel_interval("update"); +} + uint32_t PollingComponent::get_update_interval() const { return this->update_interval_; } void PollingComponent::set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; } diff --git a/esphome/core/component.h b/esphome/core/component.h index 7382f1c617..51a6296811 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -308,6 +308,12 @@ class PollingComponent : public Component { /// Get the update interval in ms of this sensor virtual uint32_t get_update_interval() const; + // Start the poller, used for component.suspend + void start_poller(); + + // Stop the poller, used for component.suspend + void stop_poller(); + protected: uint32_t update_interval_; }; diff --git a/tests/test1.yaml b/tests/test1.yaml index 96dda707b6..b84aa21439 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -3566,6 +3566,22 @@ button: name: Midea Power Inverse on_press: midea_ac.power_toggle: + - platform: template + name: Poller component suspend test + on_press: + - component.suspend: myteleinfo + - delay: 20s + - component.update: myteleinfo + - delay: 20s + - component.resume: myteleinfo + - delay: 20s + - component.resume: + id: myteleinfo + update_interval: 2s + - delay: 20s + - component.resume: + id: myteleinfo + update_interval: !lambda return 2500; - platform: ld2410 factory_reset: name: "factory reset" From e87c8d550bbc9cdf30a18b6ba1cf2f2792cc53e4 Mon Sep 17 00:00:00 2001 From: Maxime Gauduin Date: Mon, 2 Oct 2023 23:15:29 +0200 Subject: [PATCH 42/95] add pin config for denky_d4 (#5471) --- esphome/components/esp32/boards.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/esp32/boards.py b/esphome/components/esp32/boards.py index 61cb8cdc3f..e6c23c4d96 100644 --- a/esphome/components/esp32/boards.py +++ b/esphome/components/esp32/boards.py @@ -235,6 +235,7 @@ ESP32_BOARD_PINS = { "SDA": 5, "SS": 15, }, + "denky_d4": {"RX": 8, "LED": 14}, "esp-wrover-kit": {}, "esp32-devkitlipo": {}, "esp32-evb": { From 205f41509b06fa243971690d54f39e46a02a9439 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 3 Oct 2023 08:24:20 +1100 Subject: [PATCH 43/95] Support RP2040 hardware SPI (#5466) --- esphome/components/spi/__init__.py | 64 +++++++++++++++++++++++++----- tests/test6.yaml | 8 ++++ 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index fb30755511..07e8982f6e 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -54,6 +54,21 @@ CONF_FORCE_SW = "force_sw" CONF_INTERFACE = "interface" CONF_INTERFACE_INDEX = "interface_index" +# RP2040 SPI pin assignments are complicated. Refer to https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf + +RP_SPI_PINSETS = [ + { + CONF_MISO_PIN: [0, 4, 16, 20, -1], + CONF_CLK_PIN: [2, 6, 18, 22], + CONF_MOSI_PIN: [3, 7, 19, 23, -1], + }, + { + CONF_MISO_PIN: [8, 12, 24, 28, -1], + CONF_CLK_PIN: [10, 14, 26], + CONF_MOSI_PIN: [11, 23, 27, -1], + }, +] + def get_target_platform(): return ( @@ -85,7 +100,7 @@ def get_hw_interface_list(): return [["spi", "spi2"]] return [["spi", "spi2"], ["spi3"]] if target_platform == "rp2040": - return [["spi"]] + return [["spi"], ["spi1"]] return [] @@ -99,8 +114,10 @@ def get_spi_index(name): # Check that pins are suitable for HW spi +# \param spi the config data for the spi instance +# \param index the selected hw interface number, -1 if not yet known # TODO verify that the pins are internal -def validate_hw_pins(spi): +def validate_hw_pins(spi, index=-1): clk_pin = spi[CONF_CLK_PIN] if clk_pin[CONF_INVERTED]: return False @@ -129,9 +146,30 @@ def validate_hw_pins(spi): if target_platform == "esp32": return clk_pin_no >= 0 + if target_platform == "rp2040": + pin_set = ( + list(filter(lambda s: clk_pin_no in s[CONF_CLK_PIN], RP_SPI_PINSETS))[0] + if index == -1 + else RP_SPI_PINSETS[index] + ) + if pin_set is None: + return False + if sdo_pin_no not in pin_set[CONF_MOSI_PIN]: + return False + if sdi_pin_no not in pin_set[CONF_MISO_PIN]: + return False + return True return False +def get_hw_spi(config, available): + """Get an available hardware spi interface suitable for this config""" + matching = list(filter(lambda idx: validate_hw_pins(config, idx), available)) + if len(matching) != 0: + return matching[0] + return None + + def validate_spi_config(config): available = list(range(len(get_hw_interface_list()))) for spi in config: @@ -147,9 +185,10 @@ def validate_spi_config(config): if not validate_hw_pins(spi): spi[CONF_INTERFACE] = "software" elif interface == "hardware": - if len(available) == 0: - raise cv.Invalid("No hardware interface available") - index = spi[CONF_INTERFACE_INDEX] = available[0] + index = get_hw_spi(spi, available) + if index is None: + raise cv.Invalid("No suitable hardware interface available") + spi[CONF_INTERFACE_INDEX] = index available.remove(index) else: # Must be a specific name @@ -164,11 +203,14 @@ def validate_spi_config(config): # Any specific names and any 'hardware' requests will have already been filled, # so just need to assign remaining hardware to 'any' requests. for spi in config: - if spi[CONF_INTERFACE] == "any" and len(available) != 0: - index = available[0] - spi[CONF_INTERFACE_INDEX] = index - available.remove(index) - if CONF_INTERFACE_INDEX in spi and not validate_hw_pins(spi): + if spi[CONF_INTERFACE] == "any": + index = get_hw_spi(spi, available) + if index is not None: + spi[CONF_INTERFACE_INDEX] = index + available.remove(index) + if CONF_INTERFACE_INDEX in spi and not validate_hw_pins( + spi, spi[CONF_INTERFACE_INDEX] + ): raise cv.Invalid("Invalid pin selections for hardware SPI interface") return config @@ -181,7 +223,7 @@ def get_spi_interface(index): # Arduino code follows platform = get_target_platform() if platform == "rp2040": - return "&spi1" + return ["&SPI", "&SPI1"][index] if index == 0: return "&SPI" # Following code can't apply to C2, H2 or 8266 since they have only one SPI diff --git a/tests/test6.yaml b/tests/test6.yaml index 7c72151860..b0ec04eb6a 100644 --- a/tests/test6.yaml +++ b/tests/test6.yaml @@ -42,6 +42,14 @@ switch: output: pin_4 id: pin_4_switch + +spi: # Pins are for SPI1 on the RP2040 Pico-W + miso_pin: 8 + clk_pin: 10 + mosi_pin: 11 + id: spi_0 + interface: hardware + #light: # - platform: rp2040_pio_led_strip # id: led_strip From 401a3862194b8b7711be607aec027b02fe4aa418 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 10:25:15 +1300 Subject: [PATCH 44/95] Bump actions/setup-python from 4.7.0 to 4.7.1 (#5467) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 4 ++-- .github/workflows/sync-device-classes.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 7fe51163ba..897d01398e 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -42,7 +42,7 @@ jobs: steps: - uses: actions/checkout@v4.1.0 - name: Set up Python - uses: actions/setup-python@v4.7.0 + uses: actions/setup-python@v4.7.1 with: python-version: "3.9" - name: Set up Docker Buildx diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26fcbbf458..435a2fdbec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v4.7.0 + uses: actions/setup-python@v4.7.1 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6934d36686..f7652bf478 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,7 +45,7 @@ jobs: steps: - uses: actions/checkout@v4.1.0 - name: Set up Python - uses: actions/setup-python@v4.7.0 + uses: actions/setup-python@v4.7.1 with: python-version: "3.x" - name: Set up python environment @@ -90,7 +90,7 @@ jobs: steps: - uses: actions/checkout@v4.1.0 - name: Set up Python - uses: actions/setup-python@v4.7.0 + uses: actions/setup-python@v4.7.1 with: python-version: "3.9" diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index 25d36bc6d0..082c63ae41 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -22,7 +22,7 @@ jobs: path: lib/home-assistant - name: Setup Python - uses: actions/setup-python@v4.7.0 + uses: actions/setup-python@v4.7.1 with: python-version: 3.11 From 49132565979f2e0bab96a135b3a68cf1484f88c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 10:51:29 +1300 Subject: [PATCH 45/95] Bump zeroconf from 0.115.0 to 0.115.1 (#5470) 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 821e7c5786..97e42663d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ esptool==4.6.2 click==8.1.7 esphome-dashboard==20230904.0 aioesphomeapi==15.0.0 -zeroconf==0.115.0 +zeroconf==0.115.1 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From e95ba57a6150ba83108435e3454adb04749d04fa Mon Sep 17 00:00:00 2001 From: dwildstr <65917913+dwildstr@users.noreply.github.com> Date: Mon, 2 Oct 2023 19:06:54 -0400 Subject: [PATCH 46/95] Sleep mode fix for BP5758D driver (#5461) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/bp5758d/bp5758d.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/bp5758d/bp5758d.cpp b/esphome/components/bp5758d/bp5758d.cpp index 111fd6b68e..71a81f7e6c 100644 --- a/esphome/components/bp5758d/bp5758d.cpp +++ b/esphome/components/bp5758d/bp5758d.cpp @@ -39,10 +39,14 @@ void BP5758D::loop() { uint8_t data[17]; if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) { - // Off / Sleep - data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_STANDBY; for (int i = 1; i < 16; i++) data[i] = 0; + + // First turn all channels off + data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_3CH; + this->write_buffer_(data, 17); + // Then sleep + data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_STANDBY; this->write_buffer_(data, 17); } else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && (this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) { From f38849828d8d7f069527b6cf1543af6a84c1d924 Mon Sep 17 00:00:00 2001 From: Faidon Liambotis Date: Tue, 3 Oct 2023 03:23:18 +0300 Subject: [PATCH 47/95] Tuya Number: split "multiply" to a separate option (#5458) --- esphome/components/tuya/number/__init__.py | 7 +++++-- esphome/components/tuya/number/tuya_number.cpp | 4 ++-- esphome/components/tuya/number/tuya_number.h | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/esphome/components/tuya/number/__init__.py b/esphome/components/tuya/number/__init__.py index 42ac9fcfbe..4dae6d8d60 100644 --- a/esphome/components/tuya/number/__init__.py +++ b/esphome/components/tuya/number/__init__.py @@ -6,6 +6,7 @@ from esphome.const import ( CONF_NUMBER_DATAPOINT, CONF_MAX_VALUE, CONF_MIN_VALUE, + CONF_MULTIPLY, CONF_STEP, ) from .. import tuya_ns, CONF_TUYA_ID, Tuya @@ -31,6 +32,7 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_MAX_VALUE): cv.float_, cv.Required(CONF_MIN_VALUE): cv.float_, cv.Required(CONF_STEP): cv.positive_float, + cv.Optional(CONF_MULTIPLY, default=1.0): cv.float_, } ) .extend(cv.COMPONENT_SCHEMA), @@ -49,7 +51,8 @@ async def to_code(config): step=config[CONF_STEP], ) - paren = await cg.get_variable(config[CONF_TUYA_ID]) - cg.add(var.set_tuya_parent(paren)) + cg.add(var.set_write_multiply(config[CONF_MULTIPLY])) + parent = await cg.get_variable(config[CONF_TUYA_ID]) + cg.add(var.set_tuya_parent(parent)) cg.add(var.set_number_id(config[CONF_NUMBER_DATAPOINT])) diff --git a/esphome/components/tuya/number/tuya_number.cpp b/esphome/components/tuya/number/tuya_number.cpp index 30ef8b8f72..e883c72d3d 100644 --- a/esphome/components/tuya/number/tuya_number.cpp +++ b/esphome/components/tuya/number/tuya_number.cpp @@ -10,7 +10,7 @@ void TuyaNumber::setup() { this->parent_->register_listener(this->number_id_, [this](const TuyaDatapoint &datapoint) { if (datapoint.type == TuyaDatapointType::INTEGER) { ESP_LOGV(TAG, "MCU reported number %u is: %d", datapoint.id, datapoint.value_int); - this->publish_state(datapoint.value_int * this->traits.get_step()); + this->publish_state(datapoint.value_int / multiply_by_); } else if (datapoint.type == TuyaDatapointType::ENUM) { ESP_LOGV(TAG, "MCU reported number %u is: %u", datapoint.id, datapoint.value_enum); this->publish_state(datapoint.value_enum); @@ -22,7 +22,7 @@ void TuyaNumber::setup() { void TuyaNumber::control(float value) { ESP_LOGV(TAG, "Setting number %u: %f", this->number_id_, value); if (this->type_ == TuyaDatapointType::INTEGER) { - int integer_value = lround(value / this->traits.get_step()); + int integer_value = lround(value * multiply_by_); this->parent_->set_integer_datapoint_value(this->number_id_, integer_value); } else if (this->type_ == TuyaDatapointType::ENUM) { this->parent_->set_enum_datapoint_value(this->number_id_, value); diff --git a/esphome/components/tuya/number/tuya_number.h b/esphome/components/tuya/number/tuya_number.h index 7cca9fc646..f64dac8957 100644 --- a/esphome/components/tuya/number/tuya_number.h +++ b/esphome/components/tuya/number/tuya_number.h @@ -12,6 +12,7 @@ class TuyaNumber : public number::Number, public Component { void setup() override; void dump_config() override; void set_number_id(uint8_t number_id) { this->number_id_ = number_id; } + void set_write_multiply(float factor) { multiply_by_ = factor; } void set_tuya_parent(Tuya *parent) { this->parent_ = parent; } @@ -20,6 +21,7 @@ class TuyaNumber : public number::Number, public Component { Tuya *parent_; uint8_t number_id_{0}; + float multiply_by_{1.0}; TuyaDatapointType type_{}; }; From 88bb051f376eb600f6169b907e7490dda6acba13 Mon Sep 17 00:00:00 2001 From: kahrendt Date: Tue, 3 Oct 2023 05:58:11 -0400 Subject: [PATCH 48/95] Add xor automation condition (#5453) --- esphome/automation.py | 7 +++++++ esphome/core/base_automation.h | 16 +++++++++++++++ tests/test1.yaml | 36 ++++++++++++++++++++++++++++++---- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/esphome/automation.py b/esphome/automation.py index d90a9cb99a..8475858a9c 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -141,6 +141,7 @@ AUTOMATION_SCHEMA = cv.Schema( AndCondition = cg.esphome_ns.class_("AndCondition", Condition) OrCondition = cg.esphome_ns.class_("OrCondition", Condition) NotCondition = cg.esphome_ns.class_("NotCondition", Condition) +XorCondition = cg.esphome_ns.class_("XorCondition", Condition) @register_condition("and", AndCondition, validate_condition_list) @@ -161,6 +162,12 @@ async def not_condition_to_code(config, condition_id, template_arg, args): return cg.new_Pvariable(condition_id, template_arg, condition) +@register_condition("xor", XorCondition, validate_condition_list) +async def xor_condition_to_code(config, condition_id, template_arg, args): + conditions = await build_condition_list(config, template_arg, args) + return cg.new_Pvariable(condition_id, template_arg, conditions) + + @register_condition("lambda", LambdaCondition, cv.returning_lambda) async def lambda_condition_to_code(config, condition_id, template_arg, args): lambda_ = await cg.process_lambda(config, args, return_type=bool) diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index af618af99a..9b3377f694 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -48,6 +48,22 @@ template class NotCondition : public Condition { Condition *condition_; }; +template class XorCondition : public Condition { + public: + explicit XorCondition(const std::vector *> &conditions) : conditions_(conditions) {} + bool check(Ts... x) override { + bool xor_state = false; + for (auto *condition : this->conditions_) { + xor_state = xor_state ^ condition->check(x...); + } + + return xor_state; + } + + protected: + std::vector *> conditions_; +}; + template class LambdaCondition : public Condition { public: explicit LambdaCondition(std::function &&f) : f_(std::move(f)) {} diff --git a/tests/test1.yaml b/tests/test1.yaml index b84aa21439..11ce86c5f3 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -3104,11 +3104,39 @@ time: - platform: ds1307 id: ds1307_time update_interval: never - on_time: - seconds: 0 - then: ds1307.read_time i2c_id: i2c_bus - + on_time: + - seconds: 0 + then: ds1307.read_time + - at: "16:00:00" + then: + - if: + condition: + or: + - binary_sensor.is_on: close_sensor + - binary_sensor.is_on: open_sensor + then: + logger.log: "close_sensor or open_sensor is on" + - if: + condition: + and: + - binary_sensor.is_on: close_sensor + - binary_sensor.is_on: open_sensor + then: + logger.log: "close_sensor and open_sensor are both on" + - if: + condition: + xor: + - binary_sensor.is_on: close_sensor + - binary_sensor.is_on: open_sensor + then: + logger.log: "close_sensor or open_sensor is exclusively on" + - if: + condition: + not: + - binary_sensor.is_on: close_sensor + then: + logger.log: "close_sensor is not on" cover: - platform: template name: Template Cover From 050fa0d4c17ac240abf2ebafdc27fdab1d53135d Mon Sep 17 00:00:00 2001 From: kahrendt Date: Tue, 3 Oct 2023 06:01:57 -0400 Subject: [PATCH 49/95] Fix units for SPS30 number concentration sensors (#5452) --- esphome/components/sps30/sensor.py | 12 ++++++------ esphome/const.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/esphome/components/sps30/sensor.py b/esphome/components/sps30/sensor.py index ff8d5a3594..0f01bab514 100644 --- a/esphome/components/sps30/sensor.py +++ b/esphome/components/sps30/sensor.py @@ -20,7 +20,7 @@ from esphome.const import ( DEVICE_CLASS_PM25, STATE_CLASS_MEASUREMENT, UNIT_MICROGRAMS_PER_CUBIC_METER, - UNIT_COUNTS_PER_CUBIC_METER, + UNIT_COUNTS_PER_CUBIC_CENTIMETER, UNIT_MICROMETER, ICON_CHEMICAL_WEAPON, ICON_COUNTER, @@ -73,31 +73,31 @@ CONFIG_SCHEMA = ( state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PMC_0_5): sensor.sensor_schema( - unit_of_measurement=UNIT_COUNTS_PER_CUBIC_METER, + unit_of_measurement=UNIT_COUNTS_PER_CUBIC_CENTIMETER, icon=ICON_COUNTER, accuracy_decimals=2, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PMC_1_0): sensor.sensor_schema( - unit_of_measurement=UNIT_COUNTS_PER_CUBIC_METER, + unit_of_measurement=UNIT_COUNTS_PER_CUBIC_CENTIMETER, icon=ICON_COUNTER, accuracy_decimals=2, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PMC_2_5): sensor.sensor_schema( - unit_of_measurement=UNIT_COUNTS_PER_CUBIC_METER, + unit_of_measurement=UNIT_COUNTS_PER_CUBIC_CENTIMETER, icon=ICON_COUNTER, accuracy_decimals=2, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PMC_4_0): sensor.sensor_schema( - unit_of_measurement=UNIT_COUNTS_PER_CUBIC_METER, + unit_of_measurement=UNIT_COUNTS_PER_CUBIC_CENTIMETER, icon=ICON_COUNTER, accuracy_decimals=2, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PMC_10_0): sensor.sensor_schema( - unit_of_measurement=UNIT_COUNTS_PER_CUBIC_METER, + unit_of_measurement=UNIT_COUNTS_PER_CUBIC_CENTIMETER, icon=ICON_COUNTER, accuracy_decimals=2, state_class=STATE_CLASS_MEASUREMENT, diff --git a/esphome/const.py b/esphome/const.py index 2865b369e8..e188ba478e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -915,7 +915,7 @@ UNIT_BYTES = "B" UNIT_CELSIUS = "°C" UNIT_CENTIMETER = "cm" UNIT_COUNT_DECILITRE = "/dL" -UNIT_COUNTS_PER_CUBIC_METER = "#/m³" +UNIT_COUNTS_PER_CUBIC_CENTIMETER = "#/cm³" UNIT_CUBIC_METER = "m³" UNIT_CUBIC_METER_PER_HOUR = "m³/h" UNIT_DECIBEL = "dB" From 506c2ba6c7064692a11bf18b0a9ed935a6055ffc Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 4 Oct 2023 12:10:26 +1100 Subject: [PATCH 50/95] ST7789v - Allow predefined backlight pin to be disabled. (#5476) * Allow predefined backlight pin to be disabled. * Add test * Update esphome/components/st7789v/display.py --- esphome/components/st7789v/display.py | 7 +++++-- tests/test1.yaml | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/esphome/components/st7789v/display.py b/esphome/components/st7789v/display.py index ad152bf356..a4c08974c6 100644 --- a/esphome/components/st7789v/display.py +++ b/esphome/components/st7789v/display.py @@ -138,7 +138,10 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_MODEL): cv.one_of(*MODELS.keys(), upper=True, space="_"), cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_DC_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BACKLIGHT_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BACKLIGHT_PIN): cv.Any( + cv.boolean, + pins.gpio_output_pin_schema, + ), cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply), cv.Optional(CONF_EIGHTBITCOLOR, default=False): cv.boolean, cv.Optional(CONF_HEIGHT): cv.int_, @@ -174,7 +177,7 @@ async def to_code(config): reset = await cg.gpio_pin_expression(config[CONF_RESET_PIN]) cg.add(var.set_reset_pin(reset)) - if CONF_BACKLIGHT_PIN in config: + if CONF_BACKLIGHT_PIN in config and config[CONF_BACKLIGHT_PIN]: bl = await cg.gpio_pin_expression(config[CONF_BACKLIGHT_PIN]) cg.add(var.set_backlight_pin(bl)) diff --git a/tests/test1.yaml b/tests/test1.yaml index 11ce86c5f3..ea69343dbf 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2948,7 +2948,7 @@ display: cs_pin: GPIO5 dc_pin: GPIO16 reset_pin: GPIO23 - backlight_pin: GPIO4 + backlight_pin: no lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: st7920 From 4e8cba49f1240c76388a9db49b1cb903e702a1af Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 4 Oct 2023 12:15:44 +1100 Subject: [PATCH 51/95] MAX7219 - Update intensity (#5477) --- esphome/components/max7219/max7219.cpp | 12 +++++++++++- esphome/components/max7219/max7219.h | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/esphome/components/max7219/max7219.cpp b/esphome/components/max7219/max7219.cpp index b08723f1d4..d3cf6f5c48 100644 --- a/esphome/components/max7219/max7219.cpp +++ b/esphome/components/max7219/max7219.cpp @@ -164,6 +164,10 @@ void MAX7219Component::send_to_all_(uint8_t a_register, uint8_t data) { this->disable(); } void MAX7219Component::update() { + if (this->intensity_changed_) { + this->send_to_all_(MAX7219_REGISTER_INTENSITY, this->intensity_); + this->intensity_changed_ = false; + } for (uint8_t i = 0; i < this->num_chips_ * 8; i++) this->buffer_[i] = 0; if (this->writer_.has_value()) @@ -217,7 +221,13 @@ uint8_t MAX7219Component::printf(const char *format, ...) { return 0; } void MAX7219Component::set_writer(max7219_writer_t &&writer) { this->writer_ = writer; } -void MAX7219Component::set_intensity(uint8_t intensity) { this->intensity_ = intensity; } +void MAX7219Component::set_intensity(uint8_t intensity) { + intensity &= 0xF; + if (intensity != this->intensity_) { + this->intensity_changed_ = true; + this->intensity_ = intensity; + } +} void MAX7219Component::set_num_chips(uint8_t num_chips) { this->num_chips_ = num_chips; } uint8_t MAX7219Component::strftime(uint8_t pos, const char *format, ESPTime time) { diff --git a/esphome/components/max7219/max7219.h b/esphome/components/max7219/max7219.h index 1b724cef69..270edf3282 100644 --- a/esphome/components/max7219/max7219.h +++ b/esphome/components/max7219/max7219.h @@ -52,7 +52,8 @@ class MAX7219Component : public PollingComponent, void send_byte_(uint8_t a_register, uint8_t data); void send_to_all_(uint8_t a_register, uint8_t data); - uint8_t intensity_{15}; /// Intensity of the display from 0 to 15 (most) + uint8_t intensity_{15}; // Intensity of the display from 0 to 15 (most) + bool intensity_changed_{}; // True if we need to re-send the intensity uint8_t num_chips_{1}; uint8_t *buffer_; bool reverse_{false}; From b6d5cb4142c42de8c6b6a12af4caf4c87546b673 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 5 Oct 2023 07:18:33 +1100 Subject: [PATCH 52/95] St7789v and SPI data rate (#5472) --- esphome/components/ili9xxx/display.py | 5 +---- esphome/components/max6675/max6675.h | 2 +- esphome/components/spi/__init__.py | 27 ++++++++++++++++++++++- esphome/components/spi_device/__init__.py | 6 ++--- esphome/components/spi_led_strip/light.py | 6 ++--- esphome/components/st7789v/st7789v.cpp | 1 + tests/test2.yaml | 1 + 7 files changed, 34 insertions(+), 14 deletions(-) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 89a6b2d1b9..1a18978d94 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -13,7 +13,6 @@ from esphome.const import ( CONF_PAGES, CONF_RESET_PIN, CONF_DIMENSIONS, - CONF_DATA_RATE, ) DEPENDENCIES = ["spi"] @@ -100,11 +99,10 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list( cv.file_ ), - cv.Optional(CONF_DATA_RATE, default="40MHz"): spi.SPI_DATA_RATE_SCHEMA, } ) .extend(cv.polling_component_schema("1s")) - .extend(spi.spi_device_schema(False)), + .extend(spi.spi_device_schema(False, "40MHz")), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), _validate, ) @@ -177,4 +175,3 @@ async def to_code(config): if rhs is not None: prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) cg.add(var.set_palette(prog_arr)) - cg.add(var.set_data_rate(config[CONF_DATA_RATE])) diff --git a/esphome/components/max6675/max6675.h b/esphome/components/max6675/max6675.h index 09bd9df3b8..ab0f06b041 100644 --- a/esphome/components/max6675/max6675.h +++ b/esphome/components/max6675/max6675.h @@ -10,7 +10,7 @@ namespace max6675 { class MAX6675Sensor : public sensor::Sensor, public PollingComponent, public spi::SPIDevice { + spi::DATA_RATE_1MHZ> { public: void setup() override; void dump_config() override; diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 07e8982f6e..dc9d560874 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -25,6 +25,7 @@ from esphome.const import ( KEY_CORE, KEY_TARGET_PLATFORM, KEY_VARIANT, + CONF_DATA_RATE, ) from esphome.core import coroutine_with_priority, CORE @@ -33,6 +34,7 @@ spi_ns = cg.esphome_ns.namespace("spi") SPIComponent = spi_ns.class_("SPIComponent", cg.Component) SPIDevice = spi_ns.class_("SPIDevice") SPIDataRate = spi_ns.enum("SPIDataRate") +SPIMode = spi_ns.enum("SPIMode") SPI_DATA_RATE_OPTIONS = { 80e6: SPIDataRate.DATA_RATE_80MHZ, @@ -50,6 +52,18 @@ SPI_DATA_RATE_OPTIONS = { } SPI_DATA_RATE_SCHEMA = cv.All(cv.frequency, cv.enum(SPI_DATA_RATE_OPTIONS)) +SPI_MODE_OPTIONS = { + "MODE0": SPIMode.MODE0, + "MODE1": SPIMode.MODE1, + "MODE2": SPIMode.MODE2, + "MODE3": SPIMode.MODE3, + 0: SPIMode.MODE0, + 1: SPIMode.MODE1, + 2: SPIMode.MODE2, + 3: SPIMode.MODE3, +} + +CONF_SPI_MODE = "spi_mode" CONF_FORCE_SW = "force_sw" CONF_INTERFACE = "interface" CONF_INTERFACE_INDEX = "interface_index" @@ -286,13 +300,20 @@ async def to_code(configs): cg.add_library("SPI", None) -def spi_device_schema(cs_pin_required=True): +def spi_device_schema( + cs_pin_required=True, default_data_rate=cv.UNDEFINED, default_mode=cv.UNDEFINED +): """Create a schema for an SPI device. :param cs_pin_required: If true, make the CS_PIN required in the config. + :param default_data_rate: Optional data_rate to use as default :return: The SPI device schema, `extend` this in your config schema. """ schema = { cv.GenerateID(CONF_SPI_ID): cv.use_id(SPIComponent), + cv.Optional(CONF_DATA_RATE, default=default_data_rate): SPI_DATA_RATE_SCHEMA, + cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum( + SPI_MODE_OPTIONS, upper=True + ), } if cs_pin_required: schema[cv.Required(CONF_CS_PIN)] = pins.gpio_output_pin_schema @@ -307,6 +328,10 @@ async def register_spi_device(var, config): if CONF_CS_PIN in config: pin = await cg.gpio_pin_expression(config[CONF_CS_PIN]) cg.add(var.set_cs_pin(pin)) + if CONF_DATA_RATE in config: + cg.add(var.set_data_rate(config[CONF_DATA_RATE])) + if CONF_SPI_MODE in config: + cg.add(var.set_mode(config[CONF_SPI_MODE])) def final_validate_device_schema(name: str, *, require_mosi: bool, require_miso: bool): diff --git a/esphome/components/spi_device/__init__.py b/esphome/components/spi_device/__init__.py index 428b5bfbda..65e7ee6fc6 100644 --- a/esphome/components/spi_device/__init__.py +++ b/esphome/components/spi_device/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import spi -from esphome.const import CONF_ID, CONF_DATA_RATE, CONF_MODE +from esphome.const import CONF_ID, CONF_MODE DEPENDENCIES = ["spi"] CODEOWNERS = ["@clydebarrow"] @@ -33,17 +33,15 @@ CONF_BIT_ORDER = "bit_order" CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(CONF_ID): cv.declare_id(spi_device), - cv.Optional(CONF_DATA_RATE, default="1MHz"): spi.SPI_DATA_RATE_SCHEMA, cv.Optional(CONF_BIT_ORDER, default="msb_first"): cv.enum(ORDERS, lower=True), cv.Optional(CONF_MODE, default="0"): cv.enum(MODES, upper=True), } -).extend(spi.spi_device_schema(False)) +).extend(spi.spi_device_schema(False, "1MHz")) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) - cg.add(var.set_data_rate(config[CONF_DATA_RATE])) cg.add(var.set_mode(config[CONF_MODE])) cg.add(var.set_bit_order(config[CONF_BIT_ORDER])) await spi.register_spi_device(var, config) diff --git a/esphome/components/spi_led_strip/light.py b/esphome/components/spi_led_strip/light.py index 7420b0c929..78642935de 100644 --- a/esphome/components/spi_led_strip/light.py +++ b/esphome/components/spi_led_strip/light.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import light from esphome.components import spi -from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_DATA_RATE +from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS spi_led_strip_ns = cg.esphome_ns.namespace("spi_led_strip") SpiLedStrip = spi_led_strip_ns.class_( @@ -13,14 +13,12 @@ CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( { cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpiLedStrip), cv.Optional(CONF_NUM_LEDS, default=1): cv.positive_not_null_int, - cv.Optional(CONF_DATA_RATE, default="1MHz"): spi.SPI_DATA_RATE_SCHEMA, } -).extend(spi.spi_device_schema(False)) +).extend(spi.spi_device_schema(False, "1MHz")) async def to_code(config): var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) - cg.add(var.set_data_rate(spi.SPI_DATA_RATE_OPTIONS[config[CONF_DATA_RATE]])) cg.add(var.set_num_leds(config[CONF_NUM_LEDS])) await light.register_light(var, config) await spi.register_spi_device(var, config) diff --git a/esphome/components/st7789v/st7789v.cpp b/esphome/components/st7789v/st7789v.cpp index a181723546..74c7a4e9e3 100644 --- a/esphome/components/st7789v/st7789v.cpp +++ b/esphome/components/st7789v/st7789v.cpp @@ -133,6 +133,7 @@ void ST7789V::dump_config() { LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" B/L Pin: ", this->backlight_pin_); LOG_UPDATE_INTERVAL(this); + ESP_LOGCONFIG(TAG, " Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000)); #ifdef USE_POWER_SUPPLY ESP_LOGCONFIG(TAG, " Power Supply Configured: yes"); #endif diff --git a/tests/test2.yaml b/tests/test2.yaml index 5485711c2e..fe577c2188 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -724,6 +724,7 @@ interval: display: - platform: st7789v model: LILYGO_T-EMBED_170X320 + spi_mode: mode0 height: 320 width: 170 offset_height: 35 From 44e5b0c74540bdabfd55c0f68ed84e77277477d0 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 6 Oct 2023 00:28:51 -0500 Subject: [PATCH 53/95] Move CONF_IRQ_PIN into const.py (#5488) --- esphome/components/ade7953/sensor.py | 4 ++-- esphome/components/as3935/__init__.py | 12 ++++++------ esphome/components/xpt2046/touchscreen.py | 3 +-- esphome/const.py | 1 + 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/esphome/components/ade7953/sensor.py b/esphome/components/ade7953/sensor.py index 878f2f8e2d..8a43baf475 100644 --- a/esphome/components/ade7953/sensor.py +++ b/esphome/components/ade7953/sensor.py @@ -4,13 +4,14 @@ from esphome.components import sensor, i2c from esphome import pins from esphome.const import ( CONF_ID, + CONF_IRQ_PIN, CONF_VOLTAGE, DEVICE_CLASS_CURRENT, DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT, - UNIT_VOLT, UNIT_AMPERE, + UNIT_VOLT, UNIT_WATT, ) @@ -19,7 +20,6 @@ DEPENDENCIES = ["i2c"] ade7953_ns = cg.esphome_ns.namespace("ade7953") ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice) -CONF_IRQ_PIN = "irq_pin" CONF_CURRENT_A = "current_a" CONF_CURRENT_B = "current_b" CONF_ACTIVE_POWER_A = "active_power_a" diff --git a/esphome/components/as3935/__init__.py b/esphome/components/as3935/__init__.py index cf0580ca62..5cec1bfaba 100644 --- a/esphome/components/as3935/__init__.py +++ b/esphome/components/as3935/__init__.py @@ -2,14 +2,15 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.const import ( + CONF_CAPACITANCE, + CONF_DIV_RATIO, CONF_INDOOR, - CONF_WATCHDOG_THRESHOLD, - CONF_NOISE_LEVEL, - CONF_SPIKE_REJECTION, + CONF_IRQ_PIN, CONF_LIGHTNING_THRESHOLD, CONF_MASK_DISTURBER, - CONF_DIV_RATIO, - CONF_CAPACITANCE, + CONF_NOISE_LEVEL, + CONF_SPIKE_REJECTION, + CONF_WATCHDOG_THRESHOLD, ) MULTI_CONF = True @@ -19,7 +20,6 @@ CONF_AS3935_ID = "as3935_id" as3935_ns = cg.esphome_ns.namespace("as3935") AS3935 = as3935_ns.class_("AS3935Component", cg.Component) -CONF_IRQ_PIN = "irq_pin" AS3935_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(AS3935), diff --git a/esphome/components/xpt2046/touchscreen.py b/esphome/components/xpt2046/touchscreen.py index e45b723179..150d1cf396 100644 --- a/esphome/components/xpt2046/touchscreen.py +++ b/esphome/components/xpt2046/touchscreen.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import spi, touchscreen -from esphome.const import CONF_ID, CONF_THRESHOLD, CONF_INTERRUPT_PIN +from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_IRQ_PIN, CONF_THRESHOLD CODEOWNERS = ["@numo68", "@nielsnl68"] DEPENDENCIES = ["spi"] @@ -26,7 +26,6 @@ CONF_SWAP_X_Y = "swap_x_y" # obsolete Keys CONF_DIMENSION_X = "dimension_x" CONF_DIMENSION_Y = "dimension_y" -CONF_IRQ_PIN = "irq_pin" def validate_xpt2046(config): diff --git a/esphome/const.py b/esphome/const.py index e188ba478e..75ed874cfd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -360,6 +360,7 @@ CONF_INVALID_COOLDOWN = "invalid_cooldown" CONF_INVERT = "invert" CONF_INVERTED = "inverted" CONF_IP_ADDRESS = "ip_address" +CONF_IRQ_PIN = "irq_pin" CONF_JS_INCLUDE = "js_include" CONF_JS_URL = "js_url" CONF_JVC = "jvc" From fa0dcac2c75d81f9b2a1b287c9792f1bc281bed4 Mon Sep 17 00:00:00 2001 From: Austin Date: Sun, 8 Oct 2023 14:34:12 -0400 Subject: [PATCH 54/95] Initial ESP32-H2 Support (#5498) --- esphome/components/deep_sleep/__init__.py | 2 + esphome/components/esp32/gpio_esp32_h2.py | 50 +++++++++++++++++-- .../components/esp32_rmt_led_strip/light.py | 1 + esphome/components/logger/__init__.py | 2 + esphome/components/logger/logger.cpp | 18 ++++--- esphome/components/logger/logger.h | 11 ++-- esphome/components/wifi/__init__.py | 12 ++++- 7 files changed, 80 insertions(+), 16 deletions(-) diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 6e71f7bbf6..bb7084282c 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -24,6 +24,7 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S3, VARIANT_ESP32C2, VARIANT_ESP32C6, + VARIANT_ESP32H2, ) WAKEUP_PINS = { @@ -98,6 +99,7 @@ WAKEUP_PINS = { ], VARIANT_ESP32C2: [0, 1, 2, 3, 4, 5], VARIANT_ESP32C6: [0, 1, 2, 3, 4, 5, 6, 7], + VARIANT_ESP32H2: [7, 8, 9, 10, 11, 12, 13, 14], } diff --git a/esphome/components/esp32/gpio_esp32_h2.py b/esphome/components/esp32/gpio_esp32_h2.py index 5196ef0c09..d18ee8a2a6 100644 --- a/esphome/components/esp32/gpio_esp32_h2.py +++ b/esphome/components/esp32/gpio_esp32_h2.py @@ -1,11 +1,53 @@ +import logging + +from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER + import esphome.config_validation as cv +_ESP32H2_SPI_FLASH_PINS = {6, 7, 15, 16, 17, 18, 19, 20, 21} + +_ESP32H2_USB_JTAG_PINS = {26, 27} + +_ESP32H2_STRAPPING_PINS = {2, 3, 8, 9, 25} + +_LOGGER = logging.getLogger(__name__) + def esp32_h2_validate_gpio_pin(value): - # ESP32-H2 not yet supported - raise cv.Invalid("ESP32-H2 isn't supported yet") + if value < 0 or value > 27: + raise cv.Invalid(f"Invalid pin number: {value} (must be 0-27)") + if value in _ESP32H2_STRAPPING_PINS: + _LOGGER.warning( + "GPIO%d is a Strapping PIN and should be avoided.\n" + "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n" + "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins", + value, + ) + if value in _ESP32H2_SPI_FLASH_PINS: + _LOGGER.warning( + "GPIO%d is reserved for SPI Flash communication on some ESP32-H2 chip variants.\n" + "Utilizing SPI-reserved pins could cause unexpected failures.\n" + "See https://docs.espressif.com/projects/esp-idf/en/latest/esp32h2/api-reference/peripherals/gpio.html", + value, + ) + if value in _ESP32H2_USB_JTAG_PINS: + _LOGGER.warning( + "GPIO%d is reserved for the USB-Serial-JTAG interface.\n" + "To use this pin as GPIO, USB-Serial-JTAG will be disabled.", + value, + ) + + return value def esp32_h2_validate_supports(value): - # ESP32-H2 not yet supported - raise cv.Invalid("ESP32-H2 isn't supported yet") + num = value[CONF_NUMBER] + mode = value[CONF_MODE] + is_input = mode[CONF_INPUT] + + if num < 0 or num > 27: + raise cv.Invalid(f"Invalid pin number: {value} (must be 0-27)") + if is_input: + # All ESP32 pins support input mode + pass + return value diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 5b65ecdabf..43629bec51 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -64,6 +64,7 @@ RMT_CHANNELS = { esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3], esp32.const.VARIANT_ESP32C3: [0, 1], esp32.const.VARIANT_ESP32C6: [0, 1], + esp32.const.VARIANT_ESP32H2: [0, 1], } diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 5225a227ec..e431997276 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -32,6 +32,7 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S3, VARIANT_ESP32C2, VARIANT_ESP32C6, + VARIANT_ESP32H2, ) from esphome.components.libretiny import get_libretiny_component, get_libretiny_family from esphome.components.libretiny.const import ( @@ -86,6 +87,7 @@ UART_SELECTION_ESP32 = { VARIANT_ESP32C3: [UART0, UART1, USB_SERIAL_JTAG], VARIANT_ESP32C2: [UART0, UART1], VARIANT_ESP32C6: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], + VARIANT_ESP32H2: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], } UART_SELECTION_ESP8266 = [UART0, UART0_SWAP, UART1] diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index df4662024f..d1f3149d84 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -121,7 +121,7 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) { if ( #if defined(USE_ESP32_VARIANT_ESP32S2) uart_ == UART_SELECTION_USB_CDC -#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) +#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H2) uart_ == UART_SELECTION_USB_SERIAL_JTAG #elif defined(USE_ESP32_VARIANT_ESP32S3) uart_ == UART_SELECTION_USB_CDC || uart_ == UART_SELECTION_USB_SERIAL_JTAG @@ -218,21 +218,24 @@ void Logger::pre_setup() { uart_num_ = UART_NUM_1; break; #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ - !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) + !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) && !defined(USE_ESP32_VARIANT_ESP32H2) case UART_SELECTION_UART2: uart_num_ = UART_NUM_2; break; -#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 +#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 && + // !USE_ESP32_VARIANT_ESP32H2 #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) case UART_SELECTION_USB_CDC: uart_num_ = -1; break; #endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 -#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \ + defined(USE_ESP32_VARIANT_ESP32H2) case UART_SELECTION_USB_SERIAL_JTAG: uart_num_ = -1; break; -#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 +#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || + // USE_ESP32_VARIANT_ESP32H2 } if (uart_num_ >= 0) { uart_config_t uart_config{}; @@ -331,9 +334,10 @@ const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DE const char *const UART_SELECTIONS[] = { "UART0", "UART1", #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ - !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) + !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) && !defined(USE_ESP32_VARIANT_ESP32H2) "UART2", -#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 +#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARINT_ESP32C6 && !USE_ESP32_VARIANT_ESP32S2 && + // !USE_ESP32_VARIANT_ESP32S3 && !USE_ESP32_VARIANT_ESP32H2 #if defined(USE_ESP_IDF) #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) "USB_CDC", diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index 4a7a43c7c2..de272934bf 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -41,16 +41,19 @@ enum UARTSelection { UART_SELECTION_UART1, #if defined(USE_ESP32) #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ - !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) + !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) && !defined(USE_ESP32_VARIANT_ESP32H2) UART_SELECTION_UART2, -#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 +#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32C6 && !USE_ESP32_VARIANT_ESP32S2 && + // !USE_ESP32_VARIANT_ESP32S3 && !USE_ESP32_VARIANT_ESP32H2 #ifdef USE_ESP_IDF #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) UART_SELECTION_USB_CDC, #endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 -#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \ + defined(USE_ESP32_VARIANT_ESP32H2) UART_SELECTION_USB_SERIAL_JTAG, -#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3 +#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || + // USE_ESP32_VARIANT_ESP32H2 #endif // USE_ESP_IDF #endif // USE_ESP32 #ifdef USE_ESP8266 diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 1baffcbfcc..86ce53b804 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -34,12 +34,14 @@ from esphome.const import ( CONF_EAP, ) from esphome.core import CORE, HexInt, coroutine_with_priority -from esphome.components.esp32 import add_idf_sdkconfig_option +from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const from esphome.components.network import IPAddress from . import wpa2_eap AUTO_LOAD = ["network"] +NO_WIFI_VARIANTS = [const.VARIANT_ESP32H2] + wifi_ns = cg.esphome_ns.namespace("wifi") EAPAuth = wifi_ns.struct("EAPAuth") ManualIP = wifi_ns.struct("ManualIP") @@ -148,6 +150,13 @@ WIFI_NETWORK_STA = WIFI_NETWORK_BASE.extend( ) +def validate_variant(_): + if CORE.is_esp32: + variant = get_esp32_variant() + if variant in NO_WIFI_VARIANTS: + raise cv.Invalid(f"{variant} does not support WiFi") + + def final_validate(config): has_sta = bool(config.get(CONF_NETWORKS, True)) has_ap = CONF_AP in config @@ -199,6 +208,7 @@ FINAL_VALIDATE_SCHEMA = cv.All( extra=cv.ALLOW_EXTRA, ), final_validate, + validate_variant, ) From ee4ccf2762a565ecc8f8d58646127626b7978601 Mon Sep 17 00:00:00 2001 From: Luke Ansell <33194087+lukeansell@users.noreply.github.com> Date: Sun, 8 Oct 2023 22:20:43 +0200 Subject: [PATCH 55/95] Increased debug message precision (#5496) --- esphome/components/ultrasonic/ultrasonic_sensor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ultrasonic/ultrasonic_sensor.cpp b/esphome/components/ultrasonic/ultrasonic_sensor.cpp index 9f47f9f6b9..dc828aed6b 100644 --- a/esphome/components/ultrasonic/ultrasonic_sensor.cpp +++ b/esphome/components/ultrasonic/ultrasonic_sensor.cpp @@ -37,7 +37,7 @@ void UltrasonicSensorComponent::update() { this->publish_state(NAN); } else { float result = UltrasonicSensorComponent::us_to_m(pulse_end - pulse_start); - ESP_LOGD(TAG, "'%s' - Got distance: %.2f m", this->name_.c_str(), result); + ESP_LOGD(TAG, "'%s' - Got distance: %.3f m", this->name_.c_str(), result); this->publish_state(result); } } From 7e7c83b3ca87a7eda8d76b20b994abb8170aa252 Mon Sep 17 00:00:00 2001 From: Pavlo Dudnytskyi Date: Sun, 8 Oct 2023 22:49:55 +0200 Subject: [PATCH 56/95] Support for Haier IR protocol added (#5403) --- esphome/components/remote_base/__init__.py | 34 ++++++++ .../components/remote_base/haier_protocol.cpp | 84 +++++++++++++++++++ .../components/remote_base/haier_protocol.h | 40 +++++++++ tests/test1.yaml | 5 ++ 4 files changed, 163 insertions(+) create mode 100644 esphome/components/remote_base/haier_protocol.cpp create mode 100644 esphome/components/remote_base/haier_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index e2d96c9472..5a703066a1 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1558,3 +1558,37 @@ async def aeha_action(var, config, args): config[CONF_DATA], args, cg.std_vector.template(cg.uint8) ) cg.add(var.set_data(template_)) + + +# Haier +HaierData, HaierBinarySensor, HaierTrigger, HaierAction, HaierDumper = declare_protocol( + "Haier" +) +HaierAction = ns.class_("HaierAction", RemoteTransmitterActionBase) +HAIER_SCHEMA = cv.Schema( + { + cv.Required(CONF_CODE): cv.All([cv.hex_uint8_t], cv.Length(min=13, max=13)), + } +) + + +@register_binary_sensor("haier", HaierBinarySensor, HAIER_SCHEMA) +def haier_binary_sensor(var, config): + cg.add(var.set_code(config[CONF_CODE])) + + +@register_trigger("haier", HaierTrigger, HaierData) +def haier_trigger(var, config): + pass + + +@register_dumper("haier", HaierDumper) +def haier_dumper(var, config): + pass + + +@register_action("haier", HaierAction, HAIER_SCHEMA) +async def haier_action(var, config, args): + vec_ = cg.std_vector.template(cg.uint8) + template_ = await cg.templatable(config[CONF_CODE], args, vec_, vec_) + cg.add(var.set_code(template_)) diff --git a/esphome/components/remote_base/haier_protocol.cpp b/esphome/components/remote_base/haier_protocol.cpp new file mode 100644 index 0000000000..ec5cb5775c --- /dev/null +++ b/esphome/components/remote_base/haier_protocol.cpp @@ -0,0 +1,84 @@ +#include "haier_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.haier"; + +constexpr uint32_t HEADER_LOW_US = 3100; +constexpr uint32_t HEADER_HIGH_US = 4400; +constexpr uint32_t BIT_MARK_US = 540; +constexpr uint32_t BIT_ONE_SPACE_US = 1650; +constexpr uint32_t BIT_ZERO_SPACE_US = 580; +constexpr unsigned int HAIER_IR_PACKET_BIT_SIZE = 112; + +void HaierProtocol::encode_byte_(RemoteTransmitData *dst, uint8_t item) { + for (uint8_t mask = 1 << 7; mask != 0; mask >>= 1) { + if (item & mask) { + dst->space(BIT_ONE_SPACE_US); + } else { + dst->space(BIT_ZERO_SPACE_US); + } + dst->mark(BIT_MARK_US); + } +} + +void HaierProtocol::encode(RemoteTransmitData *dst, const HaierData &data) { + dst->set_carrier_frequency(38000); + dst->reserve(5 + ((data.data.size() + 1) * 2)); + dst->mark(HEADER_LOW_US); + dst->space(HEADER_LOW_US); + dst->mark(HEADER_LOW_US); + dst->space(HEADER_HIGH_US); + dst->mark(BIT_MARK_US); + uint8_t checksum = 0; + for (uint8_t item : data.data) { + this->encode_byte_(dst, item); + checksum += item; + } + this->encode_byte_(dst, checksum); +} + +optional HaierProtocol::decode(RemoteReceiveData src) { + if (!src.expect_item(HEADER_LOW_US, HEADER_LOW_US) || !src.expect_item(HEADER_LOW_US, HEADER_HIGH_US)) { + return {}; + } + if (!src.expect_mark(BIT_MARK_US)) { + return {}; + } + size_t size = src.size() - src.get_index() - 1; + if (size < HAIER_IR_PACKET_BIT_SIZE * 2) + return {}; + size = HAIER_IR_PACKET_BIT_SIZE * 2; + uint8_t checksum = 0; + HaierData out; + while (size > 0) { + uint8_t data = 0; + for (uint8_t mask = 0x80; mask != 0; mask >>= 1) { + if (src.expect_space(BIT_ONE_SPACE_US)) { + data |= mask; + } else if (!src.expect_space(BIT_ZERO_SPACE_US)) { + return {}; + } + if (!src.expect_mark(BIT_MARK_US)) { + return {}; + } + size -= 2; + } + if (size > 0) { + checksum += data; + out.data.push_back(data); + } else if (checksum != data) { + return {}; + } + } + return out; +} + +void HaierProtocol::dump(const HaierData &data) { + ESP_LOGI(TAG, "Received Haier: %s", format_hex_pretty(data.data).c_str()); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/haier_protocol.h b/esphome/components/remote_base/haier_protocol.h new file mode 100644 index 0000000000..6a1c4bea72 --- /dev/null +++ b/esphome/components/remote_base/haier_protocol.h @@ -0,0 +1,40 @@ +#pragma once + +#include "remote_base.h" +#include + +namespace esphome { +namespace remote_base { + +struct HaierData { + std::vector data; + + bool operator==(const HaierData &rhs) const { return data == rhs.data; } +}; + +class HaierProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const HaierData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const HaierData &data) override; + + protected: + void encode_byte_(RemoteTransmitData *dst, uint8_t item); +}; + +DECLARE_REMOTE_PROTOCOL(Haier) + +template class HaierAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(std::vector, data) + + void set_code(const std::vector &code) { data_ = code; } + void encode(RemoteTransmitData *dst, Ts... x) override { + HaierData data{}; + data.data = this->data_.value(x...); + HaierProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index ea69343dbf..5110500e26 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2622,6 +2622,11 @@ switch: 0x00, 0xFF, ] + - platform: template + name: Haier + turn_on_action: + remote_transmitter.transmit_haier: + code: [0xA6, 0xDA, 0x00, 0x00, 0x40, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05] - platform: template name: Living Room Lights id: livingroom_lights From aba3cd557abe4fd48531a7dc0c90aa3dfcb75722 Mon Sep 17 00:00:00 2001 From: Angel Nunez Mencias Date: Sun, 8 Oct 2023 23:01:26 +0200 Subject: [PATCH 57/95] add USE_SPI define (#5500) --- esphome/components/spi/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index dc9d560874..00b23747d4 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -272,6 +272,7 @@ CONFIG_SCHEMA = cv.All( @coroutine_with_priority(1.0) async def to_code(configs): + cg.add_define("USE_SPI") cg.add_global(spi_ns.using) for spi in configs: var = cg.new_Pvariable(spi[CONF_ID]) From af62c2d9cf20c5d4fc68402e62ebf20884aa8519 Mon Sep 17 00:00:00 2001 From: Adam Goode Date: Sun, 8 Oct 2023 17:26:58 -0400 Subject: [PATCH 58/95] Implement sensor component for MMC5983 (#5361) --- CODEOWNERS | 1 + esphome/components/hmc5883l/sensor.py | 6 +- esphome/components/mmc5603/sensor.py | 6 +- esphome/components/mmc5983/__init__.py | 1 + esphome/components/mmc5983/mmc5983.cpp | 141 +++++++++++++++++++++++++ esphome/components/mmc5983/mmc5983.h | 28 +++++ esphome/components/mmc5983/sensor.py | 55 ++++++++++ esphome/components/qmc5883l/sensor.py | 6 +- esphome/const.py | 3 + tests/test1.yaml | 11 ++ 10 files changed, 249 insertions(+), 9 deletions(-) create mode 100644 esphome/components/mmc5983/__init__.py create mode 100644 esphome/components/mmc5983/mmc5983.cpp create mode 100644 esphome/components/mmc5983/mmc5983.h create mode 100644 esphome/components/mmc5983/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index ce19f14c05..d7cf7269ab 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -186,6 +186,7 @@ esphome/components/mitsubishi/* @RubyBailey esphome/components/mlx90393/* @functionpointer esphome/components/mlx90614/* @jesserockz esphome/components/mmc5603/* @benhoff +esphome/components/mmc5983/* @agoode esphome/components/modbus_controller/* @martgras esphome/components/modbus_controller/binary_sensor/* @martgras esphome/components/modbus_controller/number/* @martgras diff --git a/esphome/components/hmc5883l/sensor.py b/esphome/components/hmc5883l/sensor.py index 26e8e2b60c..7edd13965e 100644 --- a/esphome/components/hmc5883l/sensor.py +++ b/esphome/components/hmc5883l/sensor.py @@ -3,6 +3,9 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import ( CONF_ADDRESS, + CONF_FIELD_STRENGTH_X, + CONF_FIELD_STRENGTH_Y, + CONF_FIELD_STRENGTH_Z, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, @@ -18,9 +21,6 @@ DEPENDENCIES = ["i2c"] hmc5883l_ns = cg.esphome_ns.namespace("hmc5883l") -CONF_FIELD_STRENGTH_X = "field_strength_x" -CONF_FIELD_STRENGTH_Y = "field_strength_y" -CONF_FIELD_STRENGTH_Z = "field_strength_z" CONF_HEADING = "heading" HMC5883LComponent = hmc5883l_ns.class_( diff --git a/esphome/components/mmc5603/sensor.py b/esphome/components/mmc5603/sensor.py index 348a0e7dcc..db4e5cbf26 100644 --- a/esphome/components/mmc5603/sensor.py +++ b/esphome/components/mmc5603/sensor.py @@ -3,6 +3,9 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import ( CONF_ADDRESS, + CONF_FIELD_STRENGTH_X, + CONF_FIELD_STRENGTH_Y, + CONF_FIELD_STRENGTH_Z, CONF_ID, ICON_MAGNET, STATE_CLASS_MEASUREMENT, @@ -16,9 +19,6 @@ DEPENDENCIES = ["i2c"] mmc5603_ns = cg.esphome_ns.namespace("mmc5603") -CONF_FIELD_STRENGTH_X = "field_strength_x" -CONF_FIELD_STRENGTH_Y = "field_strength_y" -CONF_FIELD_STRENGTH_Z = "field_strength_z" CONF_HEADING = "heading" MMC5603Component = mmc5603_ns.class_( diff --git a/esphome/components/mmc5983/__init__.py b/esphome/components/mmc5983/__init__.py new file mode 100644 index 0000000000..c8db8c4300 --- /dev/null +++ b/esphome/components/mmc5983/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@agoode"] diff --git a/esphome/components/mmc5983/mmc5983.cpp b/esphome/components/mmc5983/mmc5983.cpp new file mode 100644 index 0000000000..5b045ae38b --- /dev/null +++ b/esphome/components/mmc5983/mmc5983.cpp @@ -0,0 +1,141 @@ +// See https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library/tree/main +// for datasheets and an Arduino implementation. + +#include "mmc5983.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mmc5983 { + +static const char *const TAG = "mmc5983"; + +namespace { +constexpr uint8_t IC0_ADDR = 0x09; +constexpr uint8_t IC1_ADDR = 0x0a; +constexpr uint8_t IC2_ADDR = 0x0b; +constexpr uint8_t IC3_ADDR = 0x0c; +constexpr uint8_t PRODUCT_ID_ADDR = 0x2f; + +float convert_data_to_millitesla(uint8_t data_17_10, uint8_t data_9_2, uint8_t data_1_0) { + int32_t counts = (data_17_10 << 10) | (data_9_2 << 2) | data_1_0; + counts -= 131072; // "Null Field Output" from datasheet. + + // Sensitivity is 16384 counts/gauss, which is 163840 counts/mT. + return counts / 163840.0f; +} +} // namespace + +void MMC5983Component::update() { + // Schedule a SET/RESET. This will recalibrate the sensor. + // We are supposed to be able to set this once, and have it automatically continue every reading, but + // this does not appear to work in continuous mode, even with En_prd_set turned on in Internal Control 2. + // Bit 5 = Auto_SR_en (automatic SET/RESET enable). + const uint8_t ic0_value = 0b10000; + i2c::ErrorCode err = this->write_register(IC0_ADDR, &ic0_value, 1); + if (err != i2c::ErrorCode::ERROR_OK) { + ESP_LOGW(TAG, "Writing Internal Control 0 failed with i2c error %d", err); + this->status_set_warning(); + } + + // Read out the data, 7 bytes starting from 0x00. + uint8_t data[7]; + err = this->read_register(0x00, data, sizeof(data)); + if (err != i2c::ErrorCode::ERROR_OK) { + ESP_LOGW(TAG, "Reading data failed with i2c error %d", err); + this->status_set_warning(); + return; + } + + // Unpack the data and publish to sensors. + // Data is in this format: + // data[0]: Xout[17:10] + // data[1]: Xout[9:2] + // data[2]: Yout[17:10] + // data[3]: Yout[9:2] + // data[4]: Zout[17:10] + // data[5]: Zout[9:2] + // data[6]: { Xout[1], Xout[0], Yout[1], Yout[0], Zout[1], Zout[0], 0, 0 } + if (this->x_sensor_) { + this->x_sensor_->publish_state(convert_data_to_millitesla(data[0], data[1], (data[6] & 0b11000000) >> 6)); + } + if (this->y_sensor_) { + this->y_sensor_->publish_state(convert_data_to_millitesla(data[2], data[3], (data[6] & 0b00110000) >> 4)); + } + if (this->z_sensor_) { + this->z_sensor_->publish_state(convert_data_to_millitesla(data[4], data[5], (data[6] & 0b00001100) >> 2)); + } +} + +void MMC5983Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up MMC5983..."); + + // Verify product id. + const uint8_t mmc5983_product_id = 0x30; + uint8_t id; + i2c::ErrorCode err = this->read_register(PRODUCT_ID_ADDR, &id, 1); + if (err != i2c::ErrorCode::ERROR_OK) { + ESP_LOGE(TAG, "Reading product id failed with i2c error %d", err); + this->mark_failed(); + return; + } + if (id != mmc5983_product_id) { + ESP_LOGE(TAG, "Product id 0x%02x does not match expected value 0x%02x", id, mmc5983_product_id); + this->mark_failed(); + return; + } + + // Initialize Internal Control registers to 0. + // Internal Control 0. + const uint8_t zero = 0; + err = this->write_register(IC0_ADDR, &zero, 1); + if (err != i2c::ErrorCode::ERROR_OK) { + ESP_LOGE(TAG, "Initializing Internal Control 0 failed with i2c error %d", err); + this->mark_failed(); + return; + } + // Internal Control 1. + err = this->write_register(IC1_ADDR, &zero, 1); + if (err != i2c::ErrorCode::ERROR_OK) { + ESP_LOGE(TAG, "Initializing Internal Control 1 failed with i2c error %d", err); + this->mark_failed(); + return; + } + // Internal Control 2. + err = this->write_register(IC2_ADDR, &zero, 1); + if (err != i2c::ErrorCode::ERROR_OK) { + ESP_LOGE(TAG, "Initializing Internal Control 2 failed with i2c error %d", err); + this->mark_failed(); + return; + } + // Internal Control 3. + err = this->write_register(IC3_ADDR, &zero, 1); + if (err != i2c::ErrorCode::ERROR_OK) { + ESP_LOGE(TAG, "Initializing Internal Control 3 failed with i2c error %d", err); + this->mark_failed(); + return; + } + + // Enable continuous mode at 100 Hz, using Internal Control 2. + // Bit 3 = Cmm_en (continuous mode enable). + // Bit [2:0] = Cm_freq. 0b101 = 100 Hz, the fastest reading speed at Bandwidth=100 Hz. + const uint8_t ic2_value = 0b00001101; + err = this->write_register(IC2_ADDR, &ic2_value, 1); + if (err != i2c::ErrorCode::ERROR_OK) { + ESP_LOGE(TAG, "Writing Internal Control 2 failed with i2c error %d", err); + this->mark_failed(); + return; + } +} + +void MMC5983Component::dump_config() { + ESP_LOGD(TAG, "MMC5983:"); + LOG_I2C_DEVICE(this); + LOG_SENSOR(" ", "X", this->x_sensor_); + LOG_SENSOR(" ", "Y", this->y_sensor_); + LOG_SENSOR(" ", "Z", this->z_sensor_); +} + +float MMC5983Component::get_setup_priority() const { return setup_priority::DATA; } + +} // namespace mmc5983 +} // namespace esphome diff --git a/esphome/components/mmc5983/mmc5983.h b/esphome/components/mmc5983/mmc5983.h new file mode 100644 index 0000000000..d425418904 --- /dev/null +++ b/esphome/components/mmc5983/mmc5983.h @@ -0,0 +1,28 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace mmc5983 { + +class MMC5983Component : public PollingComponent, public i2c::I2CDevice { + public: + void update() override; + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + + void set_x_sensor(sensor::Sensor *x_sensor) { x_sensor_ = x_sensor; } + void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; } + void set_z_sensor(sensor::Sensor *z_sensor) { z_sensor_ = z_sensor; } + + protected: + sensor::Sensor *x_sensor_{nullptr}; + sensor::Sensor *y_sensor_{nullptr}; + sensor::Sensor *z_sensor_{nullptr}; +}; + +} // namespace mmc5983 +} // namespace esphome diff --git a/esphome/components/mmc5983/sensor.py b/esphome/components/mmc5983/sensor.py new file mode 100644 index 0000000000..e3f4209cf9 --- /dev/null +++ b/esphome/components/mmc5983/sensor.py @@ -0,0 +1,55 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_FIELD_STRENGTH_X, + CONF_FIELD_STRENGTH_Y, + CONF_FIELD_STRENGTH_Z, + CONF_ID, + ICON_MAGNET, + STATE_CLASS_MEASUREMENT, + UNIT_MICROTESLA, +) + +DEPENDENCIES = ["i2c"] + +mmc5983_ns = cg.esphome_ns.namespace("mmc5983") +MMC5983Component = mmc5983_ns.class_( + "MMC5983Component", cg.PollingComponent, i2c.I2CDevice +) + +field_strength_schema = sensor.sensor_schema( + unit_of_measurement=UNIT_MICROTESLA, + icon=ICON_MAGNET, + accuracy_decimals=4, + state_class=STATE_CLASS_MEASUREMENT, +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MMC5983Component), + cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema, + cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema, + cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x30)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + if x_config := config.get(CONF_FIELD_STRENGTH_X): + sens = await sensor.new_sensor(x_config) + cg.add(var.set_x_sensor(sens)) + if y_config := config.get(CONF_FIELD_STRENGTH_Y): + sens = await sensor.new_sensor(y_config) + cg.add(var.set_y_sensor(sens)) + if z_config := config.get(CONF_FIELD_STRENGTH_Z): + sens = await sensor.new_sensor(z_config) + cg.add(var.set_z_sensor(sens)) diff --git a/esphome/components/qmc5883l/sensor.py b/esphome/components/qmc5883l/sensor.py index cce4d93843..b819fecfe1 100644 --- a/esphome/components/qmc5883l/sensor.py +++ b/esphome/components/qmc5883l/sensor.py @@ -3,6 +3,9 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import ( CONF_ADDRESS, + CONF_FIELD_STRENGTH_X, + CONF_FIELD_STRENGTH_Y, + CONF_FIELD_STRENGTH_Z, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, @@ -18,9 +21,6 @@ DEPENDENCIES = ["i2c"] qmc5883l_ns = cg.esphome_ns.namespace("qmc5883l") -CONF_FIELD_STRENGTH_X = "field_strength_x" -CONF_FIELD_STRENGTH_Y = "field_strength_y" -CONF_FIELD_STRENGTH_Z = "field_strength_z" CONF_HEADING = "heading" QMC5883LComponent = qmc5883l_ns.class_( diff --git a/esphome/const.py b/esphome/const.py index 75ed874cfd..01555c35ec 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -265,6 +265,9 @@ CONF_FAN_ONLY_MODE = "fan_only_mode" CONF_FAN_WITH_COOLING = "fan_with_cooling" CONF_FAN_WITH_HEATING = "fan_with_heating" CONF_FAST_CONNECT = "fast_connect" +CONF_FIELD_STRENGTH_X = "field_strength_x" +CONF_FIELD_STRENGTH_Y = "field_strength_y" +CONF_FIELD_STRENGTH_Z = "field_strength_z" CONF_FILE = "file" CONF_FILES = "files" CONF_FILTER = "filter" diff --git a/tests/test1.yaml b/tests/test1.yaml index 5110500e26..c504012481 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1482,6 +1482,17 @@ sensor: name: "Loop Time" psram: name: "PSRAM Free" + - platform: mmc5983 + i2c_id: i2c_bus + field_strength_x: + name: "Magnet X" + id: magnet_x + field_strength_y: + name: "Magnet Y" + id: magnet_y + field_strength_z: + name: "Magnet Z" + id: magnet_z esp32_touch: setup_mode: false From e09c217fdeb533015d55b22bbb9b4065581f97e3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:13:12 +1300 Subject: [PATCH 59/95] Bump docker dependencies (#5501) --- docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index a0bb007641..99f15d64b2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -26,8 +26,8 @@ RUN \ python3-venv=3.9.2-3 \ iputils-ping=3:20210202-1 \ git=1:2.30.2-1+deb11u2 \ - curl=7.74.0-1.3+deb11u7 \ - openssh-client=1:8.4p1-5+deb11u1 \ + curl=7.74.0-1.3+deb11u9 \ + openssh-client=1:8.4p1-5+deb11u2 \ python3-cffi=1.14.5-1 \ libcairo2=1.16.0-5 \ patch=2.7.6-7; \ From 412a866de8ccdeb19cfe38d8056fc41ac16afd04 Mon Sep 17 00:00:00 2001 From: Ilia Sotnikov Date: Mon, 9 Oct 2023 03:47:52 +0300 Subject: [PATCH 60/95] Move to Pillow 10.x (#5489) Co-authored-by: Franck Nijhof Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/animation/__init__.py | 4 ++-- esphome/components/font/__init__.py | 13 ++++--------- esphome/components/ili9xxx/display.py | 5 +++-- esphome/components/image/__init__.py | 6 +++++- requirements_optional.txt | 2 +- tests/test2.yaml | 5 +++++ 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/esphome/components/animation/__init__.py b/esphome/components/animation/__init__.py index 52e14f0a43..9151d6e56d 100644 --- a/esphome/components/animation/__init__.py +++ b/esphome/components/animation/__init__.py @@ -151,7 +151,7 @@ async def to_code(config): pos = 0 for frameIndex in range(frames): image.seek(frameIndex) - frame = image.convert("LA", dither=Image.NONE) + frame = image.convert("LA", dither=Image.Dither.NONE) if CONF_RESIZE in config: frame = frame.resize([width, height]) pixels = list(frame.getdata()) @@ -259,7 +259,7 @@ async def to_code(config): if transparent: alpha = image.split()[-1] has_alpha = alpha.getextrema()[0] < 0xFF - frame = image.convert("1", dither=Image.NONE) + frame = image.convert("1", dither=Image.Dither.NONE) if CONF_RESIZE in config: frame = frame.resize([width, height]) if transparent: diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index e6244d8d44..2bd6beeaeb 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -67,18 +67,13 @@ def validate_pillow_installed(value): except ImportError as err: raise cv.Invalid( "Please install the pillow python package to use this feature. " - '(pip install pillow">4.0.0,<10.0.0")' + '(pip install "pillow==10.0.1")' ) from err - if version.parse(PIL.__version__) < version.parse("4.0.0"): + if version.parse(PIL.__version__) != version.parse("10.0.1"): raise cv.Invalid( - "Please update your pillow installation to at least 4.0.x. " - '(pip install pillow">4.0.0,<10.0.0")' - ) - if version.parse(PIL.__version__) >= version.parse("10.0.0"): - raise cv.Invalid( - "Please downgrade your pillow installation to below 10.0.0. " - '(pip install pillow">4.0.0,<10.0.0")' + "Please update your pillow installation to 10.0.1. " + '(pip install "pillow==10.0.1")' ) return value diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 1a18978d94..241f56e018 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import core, pins -from esphome.components import display, spi +from esphome.components import display, spi, font from esphome.core import CORE, HexInt from esphome.const import ( CONF_COLOR_PALETTE, @@ -84,6 +84,7 @@ def _validate(config): CONFIG_SCHEMA = cv.All( + font.validate_pillow_installed, display.FULL_DISPLAY_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(ili9XXXSPI), @@ -162,7 +163,7 @@ async def to_code(config): x = x + i.width # reduce the colors on combined image to 256. - converted = ref_image.convert("P", palette=Image.ADAPTIVE, colors=256) + converted = ref_image.convert("P", palette=Image.Palette.ADAPTIVE, colors=256) # if you want to verify how the images look use # ref_image.save("ref_in.png") # converted.save("ref_out.png") diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index aa402ee329..1b7c654b0b 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -255,7 +255,11 @@ async def to_code(config): transparent = config[CONF_USE_TRANSPARENCY] - dither = Image.NONE if config[CONF_DITHER] == "NONE" else Image.FLOYDSTEINBERG + dither = ( + Image.Dither.NONE + if config[CONF_DITHER] == "NONE" + else Image.Dither.FLOYDSTEINBERG + ) if config[CONF_TYPE] == "GRAYSCALE": image = image.convert("LA", dither=dither) pixels = list(image.getdata()) diff --git a/requirements_optional.txt b/requirements_optional.txt index 8bbf0a6809..236f5e3f13 100644 --- a/requirements_optional.txt +++ b/requirements_optional.txt @@ -1,3 +1,3 @@ -pillow>4.0.0,<10.0.0 +pillow==10.0.1 cairosvg>=2.2.0 cryptography>=2.0.0,<4 diff --git a/tests/test2.yaml b/tests/test2.yaml index fe577c2188..fe1ad91f8d 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -760,6 +760,11 @@ image: file: mdi:alert-outline type: BINARY +font: + - file: "gfonts://Roboto" + id: roboto + size: 20 + graph: - id: my_graph sensor: ha_hello_world_temperature From c65d78f568fcb9bdd7bea001e05601df941714ec Mon Sep 17 00:00:00 2001 From: Ilia Sotnikov Date: Mon, 9 Oct 2023 06:37:48 +0300 Subject: [PATCH 61/95] [Sprinkler] Initialize timers early to avoid crash (#5499) --- esphome/components/sprinkler/__init__.py | 16 +++++----------- esphome/components/sprinkler/sprinkler.cpp | 9 +++++++-- esphome/components/sprinkler/sprinkler.h | 4 ++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/esphome/components/sprinkler/__init__.py b/esphome/components/sprinkler/__init__.py index e1d855778a..02c80ad5a0 100644 --- a/esphome/components/sprinkler/__init__.py +++ b/esphome/components/sprinkler/__init__.py @@ -571,18 +571,12 @@ async def sprinkler_simple_action_to_code(config, action_id, template_arg, args) async def to_code(config): for sprinkler_controller in config: - var = cg.new_Pvariable(sprinkler_controller[CONF_ID]) - - if CONF_NAME in sprinkler_controller: - cg.add(var.set_name(sprinkler_controller[CONF_NAME])) + if len(sprinkler_controller[CONF_VALVES]) > 1: + name = sprinkler_controller[CONF_MAIN_SWITCH][CONF_NAME] else: - if len(sprinkler_controller[CONF_VALVES]) > 1: - name = sprinkler_controller[CONF_MAIN_SWITCH][CONF_NAME] - else: - name = sprinkler_controller[CONF_VALVES][0][CONF_VALVE_SWITCH][ - CONF_NAME - ] - cg.add(var.set_name(name)) + name = sprinkler_controller[CONF_VALVES][0][CONF_VALVE_SWITCH][CONF_NAME] + name = sprinkler_controller.get(CONF_NAME, name) + var = cg.new_Pvariable(sprinkler_controller[CONF_ID], name) await cg.register_component(var, sprinkler_controller) diff --git a/esphome/components/sprinkler/sprinkler.cpp b/esphome/components/sprinkler/sprinkler.cpp index 8afafcb5ce..6900c9461b 100644 --- a/esphome/components/sprinkler/sprinkler.cpp +++ b/esphome/components/sprinkler/sprinkler.cpp @@ -386,12 +386,17 @@ SprinklerValveOperator *SprinklerValveRunRequest::valve_operator() { return this SprinklerValveRunRequestOrigin SprinklerValveRunRequest::request_is_from() { return this->origin_; } -void Sprinkler::setup() { +Sprinkler::Sprinkler() {} +Sprinkler::Sprinkler(const std::string &name) { + // The `name` is needed to set timers up, hence non-default constructor + // replaces `set_name()` method previously existed + this->name_ = name; this->timer_.push_back({this->name_ + "sm", false, 0, 0, std::bind(&Sprinkler::sm_timer_callback_, this)}); this->timer_.push_back({this->name_ + "vs", false, 0, 0, std::bind(&Sprinkler::valve_selection_callback_, this)}); - this->all_valves_off_(true); } +void Sprinkler::setup() { this->all_valves_off_(true); } + void Sprinkler::loop() { for (auto &p : this->pump_) { p.loop(); diff --git a/esphome/components/sprinkler/sprinkler.h b/esphome/components/sprinkler/sprinkler.h index ae7554d3af..5311ae4c05 100644 --- a/esphome/components/sprinkler/sprinkler.h +++ b/esphome/components/sprinkler/sprinkler.h @@ -204,12 +204,12 @@ class SprinklerValveRunRequest { class Sprinkler : public Component { public: + Sprinkler(); + Sprinkler(const std::string &name); void setup() override; void loop() override; void dump_config() override; - void set_name(const std::string &name) { this->name_ = name; } - /// add a valve to the controller void add_valve(SprinklerControllerSwitch *valve_sw, SprinklerControllerSwitch *enable_sw = nullptr); From be7e167c638d301b6c6f244fb463e94fc95f736a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 10 Oct 2023 09:41:28 +1300 Subject: [PATCH 62/95] Deep sleep is only available on esp32 and esp8266 (#5507) --- esphome/components/deep_sleep/__init__.py | 61 +++++++++++++---------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index bb7084282c..fd7ef6fcce 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -14,6 +14,8 @@ from esphome.const import ( CONF_SLEEP_DURATION, CONF_TIME_ID, CONF_WAKEUP_PIN, + PLATFORM_ESP32, + PLATFORM_ESP8266, ) from esphome.components.esp32 import get_esp32_variant @@ -165,34 +167,39 @@ WAKEUP_CAUSES_SCHEMA = cv.Schema( } ) -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(DeepSleepComponent), - cv.Optional(CONF_RUN_DURATION): cv.Any( - cv.All(cv.only_on_esp32, WAKEUP_CAUSES_SCHEMA), - cv.positive_time_period_milliseconds, - ), - cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, - cv.Optional(CONF_WAKEUP_PIN): cv.All( - cv.only_on_esp32, pins.internal_gpio_input_pin_schema, validate_pin_number - ), - cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All( - cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True - ), - cv.Optional(CONF_ESP32_EXT1_WAKEUP): cv.All( - cv.only_on_esp32, - cv.Schema( - { - cv.Required(CONF_PINS): cv.ensure_list( - pins.internal_gpio_input_pin_schema, validate_pin_number - ), - cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True), - } +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(DeepSleepComponent), + cv.Optional(CONF_RUN_DURATION): cv.Any( + cv.All(cv.only_on_esp32, WAKEUP_CAUSES_SCHEMA), + cv.positive_time_period_milliseconds, ), - ), - cv.Optional(CONF_TOUCH_WAKEUP): cv.All(cv.only_on_esp32, cv.boolean), - } -).extend(cv.COMPONENT_SCHEMA) + cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, + cv.Optional(CONF_WAKEUP_PIN): cv.All( + cv.only_on_esp32, + pins.internal_gpio_input_pin_schema, + validate_pin_number, + ), + cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All( + cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True + ), + cv.Optional(CONF_ESP32_EXT1_WAKEUP): cv.All( + cv.only_on_esp32, + cv.Schema( + { + cv.Required(CONF_PINS): cv.ensure_list( + pins.internal_gpio_input_pin_schema, validate_pin_number + ), + cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True), + } + ), + ), + cv.Optional(CONF_TOUCH_WAKEUP): cv.All(cv.only_on_esp32, cv.boolean), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]), +) async def to_code(config): From 46be886ca69c59d074dcbfdbaa07f64b5b7311d6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:54:15 +1300 Subject: [PATCH 63/95] Use platform consts (#5508) --- esphome/components/async_tcp/__init__.py | 8 +++++++- esphome/components/captive_portal/__init__.py | 10 ++++++++-- esphome/components/esp32/__init__.py | 3 ++- esphome/components/esp8266/__init__.py | 3 ++- esphome/components/host/__init__.py | 3 ++- esphome/components/i2c/__init__.py | 5 ++++- .../components/internal_temperature/sensor.py | 4 +++- esphome/components/mqtt/__init__.py | 5 ++++- esphome/components/rp2040/__init__.py | 3 ++- esphome/components/spi/__init__.py | 19 +++++++++++-------- esphome/components/web_server/__init__.py | 6 +++++- esphome/config_validation.py | 9 ++++++--- esphome/core/__init__.py | 18 ++++++++++++------ esphome/dashboard/dashboard.py | 12 ++++++------ 14 files changed, 74 insertions(+), 34 deletions(-) diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index fa74f7103f..0a69943a25 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -2,13 +2,19 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.core import CORE, coroutine_with_priority +from esphome.const import ( + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_BK72XX, + PLATFORM_RTL87XX, +) CODEOWNERS = ["@OttoWinter"] CONFIG_SCHEMA = cv.All( cv.Schema({}), cv.only_with_arduino, - cv.only_on(["esp32", "esp8266", "bk72xx", "rtl87xx"]), + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]), ) diff --git a/esphome/components/captive_portal/__init__.py b/esphome/components/captive_portal/__init__.py index 6af741c6b3..a90ea14c4e 100644 --- a/esphome/components/captive_portal/__init__.py +++ b/esphome/components/captive_portal/__init__.py @@ -2,7 +2,13 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import web_server_base from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID -from esphome.const import CONF_ID +from esphome.const import ( + CONF_ID, + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_BK72XX, + PLATFORM_RTL87XX, +) from esphome.core import coroutine_with_priority, CORE AUTO_LOAD = ["web_server_base"] @@ -21,7 +27,7 @@ CONFIG_SCHEMA = cv.All( ), } ).extend(cv.COMPONENT_SCHEMA), - cv.only_on(["esp32", "esp8266", "bk72xx", "rtl87xx"]), + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]), ) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 0b067dc78f..cda02f80c2 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -25,6 +25,7 @@ from esphome.const import ( KEY_NAME, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, + PLATFORM_ESP32, TYPE_GIT, TYPE_LOCAL, __version__, @@ -62,7 +63,7 @@ AUTO_LOAD = ["preferences"] def set_core_data(config): CORE.data[KEY_ESP32] = {} - CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp32" + CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP32 conf = config[CONF_FRAMEWORK] if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF: CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "esp-idf" diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 412c2d903f..5336842b9d 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -11,6 +11,7 @@ from esphome.const import ( KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, + PLATFORM_ESP8266, ) from esphome.core import CORE, coroutine_with_priority import esphome.config_validation as cv @@ -38,7 +39,7 @@ AUTO_LOAD = ["preferences"] def set_core_data(config): CORE.data[KEY_ESP8266] = {} - CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp8266" + CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = 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] diff --git a/esphome/components/host/__init__.py b/esphome/components/host/__init__.py index 46f763d255..14d2597866 100644 --- a/esphome/components/host/__init__.py +++ b/esphome/components/host/__init__.py @@ -3,6 +3,7 @@ from esphome.const import ( KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, + PLATFORM_HOST, ) from esphome.core import CORE import esphome.config_validation as cv @@ -20,7 +21,7 @@ AUTO_LOAD = ["network"] def set_core_data(config): CORE.data[KEY_HOST] = {} - CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "host" + CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_HOST CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "host" CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version(1, 0, 0) return config diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index e38cfd23fa..676190b0e5 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -12,6 +12,9 @@ from esphome.const import ( CONF_SDA, CONF_ADDRESS, CONF_I2C_ID, + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_RP2040, ) from esphome.core import coroutine_with_priority, CORE @@ -60,7 +63,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_SCAN, default=True): cv.boolean, } ).extend(cv.COMPONENT_SCHEMA), - cv.only_on(["esp32", "esp8266", "rp2040"]), + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]), ) diff --git a/esphome/components/internal_temperature/sensor.py b/esphome/components/internal_temperature/sensor.py index 8d462bd801..2daf816538 100644 --- a/esphome/components/internal_temperature/sensor.py +++ b/esphome/components/internal_temperature/sensor.py @@ -12,6 +12,8 @@ from esphome.const import ( ENTITY_CATEGORY_DIAGNOSTIC, KEY_CORE, KEY_FRAMEWORK_VERSION, + PLATFORM_ESP32, + PLATFORM_RP2040, ) from esphome.core import CORE @@ -49,7 +51,7 @@ CONFIG_SCHEMA = cv.All( state_class=STATE_CLASS_MEASUREMENT, entity_category=ENTITY_CATEGORY_DIAGNOSTIC, ).extend(cv.polling_component_schema("60s")), - cv.only_on(["esp32", "rp2040"]), + cv.only_on([PLATFORM_ESP32, PLATFORM_RP2040]), validate_config, ) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 10ae8ac40d..2d1a3dc5fd 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -43,6 +43,9 @@ from esphome.const import ( CONF_USE_ABBREVIATIONS, CONF_USERNAME, CONF_WILL_MESSAGE, + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_BK72XX, ) from esphome.core import coroutine_with_priority, CORE from esphome.components.esp32 import add_idf_sdkconfig_option @@ -250,7 +253,7 @@ CONFIG_SCHEMA = cv.All( } ), validate_config, - cv.only_on(["esp32", "esp8266", "bk72xx"]), + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX]), ) diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index 5d8608c44d..62f199b040 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -14,6 +14,7 @@ from esphome.const import ( KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, + PLATFORM_RP2040, ) from esphome.core import CORE, coroutine_with_priority, EsphomeError from esphome.helpers import mkdir_p, write_file, copy_file_if_changed @@ -30,7 +31,7 @@ AUTO_LOAD = [] def set_core_data(config): CORE.data[KEY_RP2040] = {} - CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "rp2040" + CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_RP2040 CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( config[CONF_FRAMEWORK][CONF_VERSION] diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 00b23747d4..d116641373 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -26,6 +26,9 @@ from esphome.const import ( KEY_TARGET_PLATFORM, KEY_VARIANT, CONF_DATA_RATE, + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_RP2040, ) from esphome.core import coroutine_with_priority, CORE @@ -102,9 +105,9 @@ def get_target_variant(): # The returned value is a list of lists of names def get_hw_interface_list(): target_platform = get_target_platform() - if target_platform == "esp8266": + if target_platform == PLATFORM_ESP8266: return [["spi", "hspi"]] - if target_platform == "esp32": + if target_platform == PLATFORM_ESP32: if get_target_variant() in [ VARIANT_ESP32C2, VARIANT_ESP32C3, @@ -113,7 +116,7 @@ def get_hw_interface_list(): ]: return [["spi", "spi2"]] return [["spi", "spi2"], ["spi3"]] - if target_platform == "rp2040": + if target_platform == PLATFORM_RP2040: return [["spi"], ["spi1"]] return [] @@ -150,17 +153,17 @@ def validate_hw_pins(spi, index=-1): sdi_pin_no = sdi_pin[CONF_NUMBER] target_platform = get_target_platform() - if target_platform == "esp8266": + if target_platform == PLATFORM_ESP8266: if clk_pin_no == 6: return sdo_pin_no in (-1, 8) and sdi_pin_no in (-1, 7) if clk_pin_no == 14: return sdo_pin_no in (-1, 13) and sdi_pin_no in (-1, 12) return False - if target_platform == "esp32": + if target_platform == PLATFORM_ESP32: return clk_pin_no >= 0 - if target_platform == "rp2040": + if target_platform == PLATFORM_RP2040: pin_set = ( list(filter(lambda s: clk_pin_no in s[CONF_CLK_PIN], RP_SPI_PINSETS))[0] if index == -1 @@ -236,7 +239,7 @@ def get_spi_interface(index): return ["SPI2_HOST", "SPI3_HOST"][index] # Arduino code follows platform = get_target_platform() - if platform == "rp2040": + if platform == PLATFORM_RP2040: return ["&SPI", "&SPI1"][index] if index == 0: return "&SPI" @@ -261,7 +264,7 @@ SPI_SCHEMA = cv.All( } ), cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN), - cv.only_on(["esp32", "esp8266", "rp2040"]), + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]), ) CONFIG_SCHEMA = cv.All( diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index 966c978836..b8698438e2 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -18,6 +18,10 @@ from esphome.const import ( CONF_LOG, CONF_VERSION, CONF_LOCAL, + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_BK72XX, + PLATFORM_RTL87XX, ) from esphome.core import CORE, coroutine_with_priority @@ -90,7 +94,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_LOCAL): cv.boolean, } ).extend(cv.COMPONENT_SCHEMA), - cv.only_on(["esp32", "esp8266", "bk72xx", "rtl87xx"]), + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]), default_url, validate_local, validate_ota, diff --git a/esphome/config_validation.py b/esphome/config_validation.py index b3f24d9d17..46f3f6a7bd 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -51,6 +51,9 @@ from esphome.const import ( KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_RP2040, TYPE_GIT, TYPE_LOCAL, VALID_SUBSTITUTIONS_CHARACTERS, @@ -583,9 +586,9 @@ def only_with_framework(frameworks): return validator_ -only_on_esp32 = only_on("esp32") -only_on_esp8266 = only_on("esp8266") -only_on_rp2040 = only_on("rp2040") +only_on_esp32 = only_on(PLATFORM_ESP32) +only_on_esp8266 = only_on(PLATFORM_ESP8266) +only_on_rp2040 = only_on(PLATFORM_RP2040) only_with_arduino = only_with_framework("arduino") only_with_esp_idf = only_with_framework("esp-idf") diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 4c99aff011..52c58cb54a 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -15,6 +15,12 @@ from esphome.const import ( KEY_CORE, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_BK72XX, + PLATFORM_RTL87XX, + PLATFORM_RP2040, + PLATFORM_HOST, ) from esphome.coroutine import FakeAwaitable as _FakeAwaitable from esphome.coroutine import FakeEventLoop as _FakeEventLoop @@ -598,23 +604,23 @@ class EsphomeCore: @property def is_esp8266(self): - return self.target_platform == "esp8266" + return self.target_platform == PLATFORM_ESP8266 @property def is_esp32(self): - return self.target_platform == "esp32" + return self.target_platform == PLATFORM_ESP32 @property def is_rp2040(self): - return self.target_platform == "rp2040" + return self.target_platform == PLATFORM_RP2040 @property def is_bk72xx(self): - return self.target_platform == "bk72xx" + return self.target_platform == PLATFORM_BK72XX @property def is_rtl87xx(self): - return self.target_platform == "rtl87xx" + return self.target_platform == PLATFORM_RTL87XX @property def is_libretiny(self): @@ -622,7 +628,7 @@ class EsphomeCore: @property def is_host(self): - return self.target_platform == "host" + return self.target_platform == PLATFORM_HOST @property def target_framework(self): diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 8049fb7f4c..ce8976cb0f 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -838,17 +838,17 @@ class BoardsRequestHandler(BaseHandler): from esphome.components.rtl87xx.boards import BOARDS as RTL87XX_BOARDS platform_to_boards = { - "esp32": ESP32_BOARDS, - "esp8266": ESP8266_BOARDS, - "rp2040": RP2040_BOARDS, - "bk72xx": BK72XX_BOARDS, - "rtl87xx": RTL87XX_BOARDS, + const.PLATFORM_ESP32: ESP32_BOARDS, + const.PLATFORM_ESP8266: ESP8266_BOARDS, + const.PLATFORM_RP2040: RP2040_BOARDS, + const.PLATFORM_BK72XX: BK72XX_BOARDS, + const.PLATFORM_RTL87XX: RTL87XX_BOARDS, } # filter all ESP32 variants by requested platform if platform.startswith("esp32"): boards = { k: v - for k, v in platform_to_boards["esp32"].items() + for k, v in platform_to_boards[const.PLATFORM_ESP32].items() if v[const.KEY_VARIANT] == platform.upper() } else: From 6b96089f024c44716b594dfe03007c634414df84 Mon Sep 17 00:00:00 2001 From: Oleg Tarasov Date: Tue, 10 Oct 2023 03:16:12 +0300 Subject: [PATCH 64/95] Fixed precision for Nextion sensor with float values (#5497) --- esphome/components/nextion/sensor/nextion_sensor.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp index 32bfccf9f8..566dd30acf 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.cpp +++ b/esphome/components/nextion/sensor/nextion_sensor.cpp @@ -76,9 +76,15 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { } } + float published_state = state; if (this->wave_chan_id_ == UINT8_MAX) { if (publish) { - this->publish_state(state); + if (this->precision_ > 0) { + double to_multiply = pow(10, -this->precision_); + published_state = (float) (state * to_multiply); + } + + this->publish_state(published_state); } else { this->raw_state = state; this->state = state; @@ -87,7 +93,7 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { } this->update_component_settings(); - ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %lf", this->variable_name_.c_str(), state); + ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %lf", this->variable_name_.c_str(), published_state); } void NextionSensor::wave_update_() { From 511af5845ecb54c36c8b575013cbbd7e145fa3af Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 10 Oct 2023 19:52:42 +1300 Subject: [PATCH 65/95] Remote wake word support for voice assistant (#5229) --- esphome/components/api/api.proto | 19 +- esphome/components/api/api_connection.cpp | 13 +- esphome/components/api/api_connection.h | 2 +- esphome/components/api/api_pb2.cpp | 274 +++++++----- esphome/components/api/api_pb2.h | 26 +- esphome/components/api/api_server.cpp | 20 +- esphome/components/api/api_server.h | 11 +- .../microphone/i2s_audio_microphone.cpp | 9 +- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 4 +- .../components/voice_assistant/__init__.py | 86 +++- .../voice_assistant/voice_assistant.cpp | 391 +++++++++++++++--- .../voice_assistant/voice_assistant.h | 97 ++++- tests/test4.yaml | 3 + 13 files changed, 744 insertions(+), 211 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 86685aa5e6..ec4a0f7cc9 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1413,6 +1413,18 @@ message SubscribeVoiceAssistantRequest { bool subscribe = 1; } +enum VoiceAssistantRequestFlag { + VOICE_ASSISTANT_REQUEST_NONE = 0; + VOICE_ASSISTANT_REQUEST_USE_VAD = 1; + VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD = 2; +} + +message VoiceAssistantAudioSettings { + uint32 noise_suppression_level = 1; + uint32 auto_gain = 2; + float volume_multiplier = 3; +} + message VoiceAssistantRequest { option (id) = 90; option (source) = SOURCE_SERVER; @@ -1420,7 +1432,8 @@ message VoiceAssistantRequest { bool start = 1; string conversation_id = 2; - bool use_vad = 3; + uint32 flags = 3; + VoiceAssistantAudioSettings audio_settings = 4; } message VoiceAssistantResponse { @@ -1442,6 +1455,10 @@ enum VoiceAssistantEvent { VOICE_ASSISTANT_INTENT_END = 6; VOICE_ASSISTANT_TTS_START = 7; VOICE_ASSISTANT_TTS_END = 8; + VOICE_ASSISTANT_WAKE_WORD_START = 9; + VOICE_ASSISTANT_WAKE_WORD_END = 10; + VOICE_ASSISTANT_STT_VAD_START = 11; + VOICE_ASSISTANT_STT_VAD_END = 12; } message VoiceAssistantEventData { diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index ceec53bb65..3172b71fa2 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -907,21 +907,22 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_ #endif #ifdef USE_VOICE_ASSISTANT -bool APIConnection::request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad) { +bool APIConnection::request_voice_assistant(const VoiceAssistantRequest &msg) { if (!this->voice_assistant_subscription_) return false; - VoiceAssistantRequest msg; - msg.start = start; - msg.conversation_id = conversation_id; - msg.use_vad = use_vad; + return this->send_voice_assistant_request(msg); } void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) { if (voice_assistant::global_voice_assistant != nullptr) { + if (msg.error) { + voice_assistant::global_voice_assistant->failed_to_start(); + return; + } struct sockaddr_storage storage; socklen_t len = sizeof(storage); this->helper_->getpeername((struct sockaddr *) &storage, &len); - voice_assistant::global_voice_assistant->start(&storage, msg.port); + voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port); } }; void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) { diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index acc4578661..2a62c2faff 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -124,7 +124,7 @@ class APIConnection : public APIServerConnection { void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override { this->voice_assistant_subscription_ = msg.subscribe; } - bool request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad); + bool request_voice_assistant(const VoiceAssistantRequest &msg); void on_voice_assistant_response(const VoiceAssistantResponse &msg) override; void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override; #endif diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 6149a970ee..d0711aba7b 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -3,8 +3,6 @@ #include "api_pb2.h" #include "esphome/core/log.h" -#include - namespace esphome { namespace api { @@ -410,6 +408,20 @@ const char *proto_enum_to_string(enums::Bluet } #endif #ifdef HAS_PROTO_MESSAGE_DUMP +template<> const char *proto_enum_to_string(enums::VoiceAssistantRequestFlag value) { + switch (value) { + case enums::VOICE_ASSISTANT_REQUEST_NONE: + return "VOICE_ASSISTANT_REQUEST_NONE"; + case enums::VOICE_ASSISTANT_REQUEST_USE_VAD: + return "VOICE_ASSISTANT_REQUEST_USE_VAD"; + case enums::VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD: + return "VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD"; + default: + return "UNKNOWN"; + } +} +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::VoiceAssistantEvent value) { switch (value) { case enums::VOICE_ASSISTANT_ERROR: @@ -430,6 +442,14 @@ template<> const char *proto_enum_to_string(enums::V return "VOICE_ASSISTANT_TTS_START"; case enums::VOICE_ASSISTANT_TTS_END: return "VOICE_ASSISTANT_TTS_END"; + case enums::VOICE_ASSISTANT_WAKE_WORD_START: + return "VOICE_ASSISTANT_WAKE_WORD_START"; + case enums::VOICE_ASSISTANT_WAKE_WORD_END: + return "VOICE_ASSISTANT_WAKE_WORD_END"; + case enums::VOICE_ASSISTANT_STT_VAD_START: + return "VOICE_ASSISTANT_STT_VAD_START"; + case enums::VOICE_ASSISTANT_STT_VAD_END: + return "VOICE_ASSISTANT_STT_VAD_END"; default: return "UNKNOWN"; } @@ -524,12 +544,12 @@ void HelloRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" api_version_major: "); - sprintf(buffer, "%" PRIu32, this->api_version_major); + sprintf(buffer, "%u", this->api_version_major); out.append(buffer); out.append("\n"); out.append(" api_version_minor: "); - sprintf(buffer, "%" PRIu32, this->api_version_minor); + sprintf(buffer, "%u", this->api_version_minor); out.append(buffer); out.append("\n"); out.append("}"); @@ -574,12 +594,12 @@ void HelloResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("HelloResponse {\n"); out.append(" api_version_major: "); - sprintf(buffer, "%" PRIu32, this->api_version_major); + sprintf(buffer, "%u", this->api_version_major); out.append(buffer); out.append("\n"); out.append(" api_version_minor: "); - sprintf(buffer, "%" PRIu32, this->api_version_minor); + sprintf(buffer, "%u", this->api_version_minor); out.append(buffer); out.append("\n"); @@ -785,17 +805,17 @@ void DeviceInfoResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" webserver_port: "); - sprintf(buffer, "%" PRIu32, this->webserver_port); + sprintf(buffer, "%u", this->webserver_port); out.append(buffer); out.append("\n"); out.append(" legacy_bluetooth_proxy_version: "); - sprintf(buffer, "%" PRIu32, this->legacy_bluetooth_proxy_version); + sprintf(buffer, "%u", this->legacy_bluetooth_proxy_version); out.append(buffer); out.append("\n"); out.append(" bluetooth_proxy_feature_flags: "); - sprintf(buffer, "%" PRIu32, this->bluetooth_proxy_feature_flags); + sprintf(buffer, "%u", this->bluetooth_proxy_feature_flags); out.append(buffer); out.append("\n"); @@ -808,7 +828,7 @@ void DeviceInfoResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" voice_assistant_version: "); - sprintf(buffer, "%" PRIu32, this->voice_assistant_version); + sprintf(buffer, "%u", this->voice_assistant_version); out.append(buffer); out.append("\n"); out.append("}"); @@ -900,7 +920,7 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -968,7 +988,7 @@ void BinarySensorStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("BinarySensorStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -1071,7 +1091,7 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -1161,7 +1181,7 @@ void CoverStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("CoverStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -1244,7 +1264,7 @@ void CoverCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("CoverCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -1364,7 +1384,7 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -1389,7 +1409,7 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" supported_speed_count: "); - sprintf(buffer, "%" PRId32, this->supported_speed_count); + sprintf(buffer, "%d", this->supported_speed_count); out.append(buffer); out.append("\n"); @@ -1456,7 +1476,7 @@ void FanStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("FanStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -1477,7 +1497,7 @@ void FanStateResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" speed_level: "); - sprintf(buffer, "%" PRId32, this->speed_level); + sprintf(buffer, "%d", this->speed_level); out.append(buffer); out.append("\n"); out.append("}"); @@ -1557,7 +1577,7 @@ void FanCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("FanCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -1598,7 +1618,7 @@ void FanCommandRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" speed_level: "); - sprintf(buffer, "%" PRId32, this->speed_level); + sprintf(buffer, "%d", this->speed_level); out.append(buffer); out.append("\n"); out.append("}"); @@ -1712,7 +1732,7 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -1866,7 +1886,7 @@ void LightStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("LightStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -2089,7 +2109,7 @@ void LightCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("LightCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -2187,7 +2207,7 @@ void LightCommandRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" transition_length: "); - sprintf(buffer, "%" PRIu32, this->transition_length); + sprintf(buffer, "%u", this->transition_length); out.append(buffer); out.append("\n"); @@ -2196,7 +2216,7 @@ void LightCommandRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" flash_length: "); - sprintf(buffer, "%" PRIu32, this->flash_length); + sprintf(buffer, "%u", this->flash_length); out.append(buffer); out.append("\n"); @@ -2304,7 +2324,7 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -2325,7 +2345,7 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" accuracy_decimals: "); - sprintf(buffer, "%" PRId32, this->accuracy_decimals); + sprintf(buffer, "%d", this->accuracy_decimals); out.append(buffer); out.append("\n"); @@ -2389,7 +2409,7 @@ void SensorStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("SensorStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -2478,7 +2498,7 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -2541,7 +2561,7 @@ void SwitchStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("SwitchStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -2580,7 +2600,7 @@ void SwitchCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("SwitchCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -2654,7 +2674,7 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -2720,7 +2740,7 @@ void TextSensorStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("TextSensorStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -3027,7 +3047,7 @@ void GetTimeResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("GetTimeResponse {\n"); out.append(" epoch_seconds: "); - sprintf(buffer, "%" PRIu32, this->epoch_seconds); + sprintf(buffer, "%u", this->epoch_seconds); out.append(buffer); out.append("\n"); out.append("}"); @@ -3111,7 +3131,7 @@ void ListEntitiesServicesResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -3205,7 +3225,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const { out.append("\n"); out.append(" legacy_int: "); - sprintf(buffer, "%" PRId32, this->legacy_int); + sprintf(buffer, "%d", this->legacy_int); out.append(buffer); out.append("\n"); @@ -3219,7 +3239,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const { out.append("\n"); out.append(" int_: "); - sprintf(buffer, "%" PRId32, this->int_); + sprintf(buffer, "%d", this->int_); out.append(buffer); out.append("\n"); @@ -3231,7 +3251,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const { for (const auto &it : this->int_array) { out.append(" int_array: "); - sprintf(buffer, "%" PRId32, it); + sprintf(buffer, "%d", it); out.append(buffer); out.append("\n"); } @@ -3282,7 +3302,7 @@ void ExecuteServiceRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("ExecuteServiceRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -3358,7 +3378,7 @@ void ListEntitiesCameraResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -3424,7 +3444,7 @@ void CameraImageResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("CameraImageResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -3616,7 +3636,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -3804,7 +3824,7 @@ void ClimateStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("ClimateStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -3992,7 +4012,7 @@ void ClimateCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("ClimateCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4175,7 +4195,7 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4262,7 +4282,7 @@ void NumberStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("NumberStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4300,7 +4320,7 @@ void NumberCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("NumberCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4382,7 +4402,7 @@ void ListEntitiesSelectResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4454,7 +4474,7 @@ void SelectStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("SelectStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4497,7 +4517,7 @@ void SelectCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("SelectCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4591,7 +4611,7 @@ void ListEntitiesLockResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4662,7 +4682,7 @@ void LockStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("LockStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4717,7 +4737,7 @@ void LockCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("LockCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4804,7 +4824,7 @@ void ListEntitiesButtonResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4850,7 +4870,7 @@ void ButtonCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("ButtonCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); out.append("}"); @@ -4925,7 +4945,7 @@ void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -4994,7 +5014,7 @@ void MediaPlayerStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("MediaPlayerStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -5073,7 +5093,7 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("MediaPlayerCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -5122,7 +5142,7 @@ void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const __attribute__((unused)) char buffer[64]; out.append("SubscribeBluetoothLEAdvertisementsRequest {\n"); out.append(" flags: "); - sprintf(buffer, "%" PRIu32, this->flags); + sprintf(buffer, "%u", this->flags); out.append(buffer); out.append("\n"); out.append("}"); @@ -5169,7 +5189,7 @@ void BluetoothServiceData::dump_to(std::string &out) const { for (const auto &it : this->legacy_data) { out.append(" legacy_data: "); - sprintf(buffer, "%" PRIu32, it); + sprintf(buffer, "%u", it); out.append(buffer); out.append("\n"); } @@ -5249,7 +5269,7 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" rssi: "); - sprintf(buffer, "%" PRId32, this->rssi); + sprintf(buffer, "%d", this->rssi); out.append(buffer); out.append("\n"); @@ -5272,7 +5292,7 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const { } out.append(" address_type: "); - sprintf(buffer, "%" PRIu32, this->address_type); + sprintf(buffer, "%u", this->address_type); out.append(buffer); out.append("\n"); out.append("}"); @@ -5322,12 +5342,12 @@ void BluetoothLERawAdvertisement::dump_to(std::string &out) const { out.append("\n"); out.append(" rssi: "); - sprintf(buffer, "%" PRId32, this->rssi); + sprintf(buffer, "%d", this->rssi); out.append(buffer); out.append("\n"); out.append(" address_type: "); - sprintf(buffer, "%" PRIu32, this->address_type); + sprintf(buffer, "%u", this->address_type); out.append(buffer); out.append("\n"); @@ -5410,7 +5430,7 @@ void BluetoothDeviceRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" address_type: "); - sprintf(buffer, "%" PRIu32, this->address_type); + sprintf(buffer, "%u", this->address_type); out.append(buffer); out.append("\n"); out.append("}"); @@ -5458,12 +5478,12 @@ void BluetoothDeviceConnectionResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" mtu: "); - sprintf(buffer, "%" PRIu32, this->mtu); + sprintf(buffer, "%u", this->mtu); out.append(buffer); out.append("\n"); out.append(" error: "); - sprintf(buffer, "%" PRId32, this->error); + sprintf(buffer, "%d", this->error); out.append(buffer); out.append("\n"); out.append("}"); @@ -5523,7 +5543,7 @@ void BluetoothGATTDescriptor::dump_to(std::string &out) const { } out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); out.append("}"); @@ -5579,12 +5599,12 @@ void BluetoothGATTCharacteristic::dump_to(std::string &out) const { } out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); out.append(" properties: "); - sprintf(buffer, "%" PRIu32, this->properties); + sprintf(buffer, "%u", this->properties); out.append(buffer); out.append("\n"); @@ -5641,7 +5661,7 @@ void BluetoothGATTService::dump_to(std::string &out) const { } out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); @@ -5748,7 +5768,7 @@ void BluetoothGATTReadRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); out.append("}"); @@ -5793,7 +5813,7 @@ void BluetoothGATTReadResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); @@ -5847,7 +5867,7 @@ void BluetoothGATTWriteRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); @@ -5889,7 +5909,7 @@ void BluetoothGATTReadDescriptorRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); out.append("}"); @@ -5934,7 +5954,7 @@ void BluetoothGATTWriteDescriptorRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); @@ -5977,7 +5997,7 @@ void BluetoothGATTNotifyRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); @@ -6026,7 +6046,7 @@ void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); @@ -6065,12 +6085,12 @@ void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("BluetoothConnectionsFreeResponse {\n"); out.append(" free: "); - sprintf(buffer, "%" PRIu32, this->free); + sprintf(buffer, "%u", this->free); out.append(buffer); out.append("\n"); out.append(" limit: "); - sprintf(buffer, "%" PRIu32, this->limit); + sprintf(buffer, "%u", this->limit); out.append(buffer); out.append("\n"); out.append("}"); @@ -6109,12 +6129,12 @@ void BluetoothGATTErrorResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); out.append(" error: "); - sprintf(buffer, "%" PRId32, this->error); + sprintf(buffer, "%d", this->error); out.append(buffer); out.append("\n"); out.append("}"); @@ -6148,7 +6168,7 @@ void BluetoothGATTWriteResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); out.append("}"); @@ -6182,7 +6202,7 @@ void BluetoothGATTNotifyResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%" PRIu32, this->handle); + sprintf(buffer, "%u", this->handle); out.append(buffer); out.append("\n"); out.append("}"); @@ -6225,7 +6245,7 @@ void BluetoothDevicePairingResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" error: "); - sprintf(buffer, "%" PRId32, this->error); + sprintf(buffer, "%d", this->error); out.append(buffer); out.append("\n"); out.append("}"); @@ -6268,7 +6288,7 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" error: "); - sprintf(buffer, "%" PRId32, this->error); + sprintf(buffer, "%d", this->error); out.append(buffer); out.append("\n"); out.append("}"); @@ -6317,7 +6337,7 @@ void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" error: "); - sprintf(buffer, "%" PRId32, this->error); + sprintf(buffer, "%d", this->error); out.append(buffer); out.append("\n"); out.append("}"); @@ -6344,6 +6364,56 @@ void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const { out.append("}"); } #endif +bool VoiceAssistantAudioSettings::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->noise_suppression_level = value.as_uint32(); + return true; + } + case 2: { + this->auto_gain = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool VoiceAssistantAudioSettings::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 3: { + this->volume_multiplier = value.as_float(); + return true; + } + default: + return false; + } +} +void VoiceAssistantAudioSettings::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint32(1, this->noise_suppression_level); + buffer.encode_uint32(2, this->auto_gain); + buffer.encode_float(3, this->volume_multiplier); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantAudioSettings::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantAudioSettings {\n"); + out.append(" noise_suppression_level: "); + sprintf(buffer, "%u", this->noise_suppression_level); + out.append(buffer); + out.append("\n"); + + out.append(" auto_gain: "); + sprintf(buffer, "%u", this->auto_gain); + out.append(buffer); + out.append("\n"); + + out.append(" volume_multiplier: "); + sprintf(buffer, "%g", this->volume_multiplier); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { @@ -6351,7 +6421,7 @@ bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) return true; } case 3: { - this->use_vad = value.as_bool(); + this->flags = value.as_uint32(); return true; } default: @@ -6364,6 +6434,10 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite this->conversation_id = value.as_string(); return true; } + case 4: { + this->audio_settings = value.as_message(); + return true; + } default: return false; } @@ -6371,7 +6445,8 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->start); buffer.encode_string(2, this->conversation_id); - buffer.encode_bool(3, this->use_vad); + buffer.encode_uint32(3, this->flags); + buffer.encode_message(4, this->audio_settings); } #ifdef HAS_PROTO_MESSAGE_DUMP void VoiceAssistantRequest::dump_to(std::string &out) const { @@ -6385,8 +6460,13 @@ void VoiceAssistantRequest::dump_to(std::string &out) const { out.append("'").append(this->conversation_id).append("'"); out.append("\n"); - out.append(" use_vad: "); - out.append(YESNO(this->use_vad)); + out.append(" flags: "); + sprintf(buffer, "%u", this->flags); + out.append(buffer); + out.append("\n"); + + out.append(" audio_settings: "); + this->audio_settings.dump_to(out); out.append("\n"); out.append("}"); } @@ -6414,7 +6494,7 @@ void VoiceAssistantResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("VoiceAssistantResponse {\n"); out.append(" port: "); - sprintf(buffer, "%" PRIu32, this->port); + sprintf(buffer, "%u", this->port); out.append(buffer); out.append("\n"); @@ -6577,7 +6657,7 @@ void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -6602,7 +6682,7 @@ void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" supported_features: "); - sprintf(buffer, "%" PRIu32, this->supported_features); + sprintf(buffer, "%u", this->supported_features); out.append(buffer); out.append("\n"); @@ -6645,7 +6725,7 @@ void AlarmControlPanelStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("AlarmControlPanelStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); @@ -6695,7 +6775,7 @@ void AlarmControlPanelCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("AlarmControlPanelCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%" PRIu32, this->key); + sprintf(buffer, "%u", this->key); out.append(buffer); out.append("\n"); diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 627165953d..a4826f09d2 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -165,6 +165,11 @@ enum BluetoothDeviceRequestType : uint32_t { BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5, BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6, }; +enum VoiceAssistantRequestFlag : uint32_t { + VOICE_ASSISTANT_REQUEST_NONE = 0, + VOICE_ASSISTANT_REQUEST_USE_VAD = 1, + VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD = 2, +}; enum VoiceAssistantEvent : uint32_t { VOICE_ASSISTANT_ERROR = 0, VOICE_ASSISTANT_RUN_START = 1, @@ -175,6 +180,10 @@ enum VoiceAssistantEvent : uint32_t { VOICE_ASSISTANT_INTENT_END = 6, VOICE_ASSISTANT_TTS_START = 7, VOICE_ASSISTANT_TTS_END = 8, + VOICE_ASSISTANT_WAKE_WORD_START = 9, + VOICE_ASSISTANT_WAKE_WORD_END = 10, + VOICE_ASSISTANT_STT_VAD_START = 11, + VOICE_ASSISTANT_STT_VAD_END = 12, }; enum AlarmControlPanelState : uint32_t { ALARM_STATE_DISARMED = 0, @@ -1651,11 +1660,26 @@ class SubscribeVoiceAssistantRequest : public ProtoMessage { protected: bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class VoiceAssistantAudioSettings : public ProtoMessage { + public: + uint32_t noise_suppression_level{0}; + uint32_t auto_gain{0}; + float volume_multiplier{0.0f}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; class VoiceAssistantRequest : public ProtoMessage { public: bool start{false}; std::string conversation_id{}; - bool use_vad{false}; + uint32_t flags{0}; + VoiceAssistantAudioSettings audio_settings{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index f70d45ecd0..54266ff0f0 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -1,13 +1,13 @@ #include "api_server.h" +#include #include "api_connection.h" +#include "esphome/components/network/util.h" #include "esphome/core/application.h" #include "esphome/core/defines.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" #include "esphome/core/util.h" #include "esphome/core/version.h" -#include "esphome/core/hal.h" -#include "esphome/components/network/util.h" -#include #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" @@ -323,16 +323,24 @@ void APIServer::on_shutdown() { } #ifdef USE_VOICE_ASSISTANT -bool APIServer::start_voice_assistant(const std::string &conversation_id, bool use_vad) { +bool APIServer::start_voice_assistant(const std::string &conversation_id, uint32_t flags, + const api::VoiceAssistantAudioSettings &audio_settings) { + VoiceAssistantRequest msg; + msg.start = true; + msg.conversation_id = conversation_id; + msg.flags = flags; + msg.audio_settings = audio_settings; for (auto &c : this->clients_) { - if (c->request_voice_assistant(true, conversation_id, use_vad)) + if (c->request_voice_assistant(msg)) return true; } return false; } void APIServer::stop_voice_assistant() { + VoiceAssistantRequest msg; + msg.start = false; for (auto &c : this->clients_) { - if (c->request_voice_assistant(false, "", false)) + if (c->request_voice_assistant(msg)) return; } } diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 9b40a5ef02..a4454d4b84 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -1,16 +1,16 @@ #pragma once +#include "api_noise_context.h" +#include "api_pb2.h" +#include "api_pb2_service.h" +#include "esphome/components/socket/socket.h" #include "esphome/core/component.h" #include "esphome/core/controller.h" #include "esphome/core/defines.h" #include "esphome/core/log.h" -#include "esphome/components/socket/socket.h" -#include "api_pb2.h" -#include "api_pb2_service.h" #include "list_entities.h" #include "subscribe_state.h" #include "user_services.h" -#include "api_noise_context.h" #include @@ -81,7 +81,8 @@ class APIServer : public Component, public Controller { #endif #ifdef USE_VOICE_ASSISTANT - bool start_voice_assistant(const std::string &conversation_id, bool use_vad); + bool start_voice_assistant(const std::string &conversation_id, uint32_t flags, + const api::VoiceAssistantAudioSettings &audio_settings); void stop_voice_assistant(); #endif diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index cf0628d638..44c73eb8fd 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -66,8 +66,9 @@ void I2SAudioMicrophone::start_() { i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_); i2s_adc_enable(this->parent_->get_port()); - } else { + } else #endif + { if (this->pdm_) config.mode = (i2s_mode_t) (config.mode | I2S_MODE_PDM); @@ -77,9 +78,7 @@ void I2SAudioMicrophone::start_() { pin_config.data_in_num = this->din_pin_; i2s_set_pin(this->parent_->get_port(), &pin_config); -#if SOC_I2S_SUPPORTS_ADC } -#endif this->state_ = microphone::STATE_RUNNING; this->high_freq_.start(); } @@ -110,6 +109,10 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) { this->status_set_warning(); return 0; } + if (bytes_read == 0) { + this->status_set_warning(); + return 0; + } this->status_clear_warning(); if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_16BIT) { return bytes_read; diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 43bc005136..a0934e3844 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -158,10 +158,10 @@ void I2SAudioSpeaker::watch_() { this->status_clear_warning(); break; case TaskEventType::STOPPED: - this->parent_->unlock(); this->state_ = speaker::STATE_STOPPED; vTaskDelete(this->player_task_handle_); this->player_task_handle_ = nullptr; + this->parent_->unlock(); break; case TaskEventType::WARNING: ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err)); @@ -177,9 +177,9 @@ void I2SAudioSpeaker::loop() { this->start_(); break; case speaker::STATE_RUNNING: + case speaker::STATE_STOPPING: this->watch_(); break; - case speaker::STATE_STOPPING: case speaker::STATE_STOPPED: break; } diff --git a/esphome/components/voice_assistant/__init__.py b/esphome/components/voice_assistant/__init__.py index 55d995be88..14176ad7cf 100644 --- a/esphome/components/voice_assistant/__init__.py +++ b/esphome/components/voice_assistant/__init__.py @@ -19,11 +19,18 @@ CODEOWNERS = ["@jesserockz"] CONF_SILENCE_DETECTION = "silence_detection" CONF_ON_LISTENING = "on_listening" CONF_ON_START = "on_start" +CONF_ON_WAKE_WORD_DETECTED = "on_wake_word_detected" CONF_ON_STT_END = "on_stt_end" CONF_ON_TTS_START = "on_tts_start" CONF_ON_TTS_END = "on_tts_end" CONF_ON_END = "on_end" CONF_ON_ERROR = "on_error" +CONF_USE_WAKE_WORD = "use_wake_word" +CONF_VAD_THRESHOLD = "vad_threshold" + +CONF_NOISE_SUPPRESSION_LEVEL = "noise_suppression_level" +CONF_AUTO_GAIN = "auto_gain" +CONF_VOLUME_MULTIPLIER = "volume_multiplier" voice_assistant_ns = cg.esphome_ns.namespace("voice_assistant") @@ -42,23 +49,40 @@ IsRunningCondition = voice_assistant_ns.class_( "IsRunningCondition", automation.Condition, cg.Parented.template(VoiceAssistant) ) - -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(VoiceAssistant), - cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone), - cv.Exclusive(CONF_SPEAKER, "output"): cv.use_id(speaker.Speaker), - cv.Exclusive(CONF_MEDIA_PLAYER, "output"): cv.use_id(media_player.MediaPlayer), - cv.Optional(CONF_SILENCE_DETECTION, default=True): cv.boolean, - cv.Optional(CONF_ON_LISTENING): automation.validate_automation(single=True), - cv.Optional(CONF_ON_START): automation.validate_automation(single=True), - cv.Optional(CONF_ON_STT_END): automation.validate_automation(single=True), - cv.Optional(CONF_ON_TTS_START): automation.validate_automation(single=True), - cv.Optional(CONF_ON_TTS_END): automation.validate_automation(single=True), - cv.Optional(CONF_ON_END): automation.validate_automation(single=True), - cv.Optional(CONF_ON_ERROR): automation.validate_automation(single=True), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(VoiceAssistant), + cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone), + cv.Exclusive(CONF_SPEAKER, "output"): cv.use_id(speaker.Speaker), + cv.Exclusive(CONF_MEDIA_PLAYER, "output"): cv.use_id( + media_player.MediaPlayer + ), + cv.Optional(CONF_USE_WAKE_WORD, default=False): cv.boolean, + cv.Optional(CONF_VAD_THRESHOLD): cv.All( + cv.requires_component("esp_adf"), cv.only_with_esp_idf, cv.uint8_t + ), + cv.Optional(CONF_NOISE_SUPPRESSION_LEVEL, default=0): cv.int_range(0, 4), + cv.Optional(CONF_AUTO_GAIN, default="0dBFS"): cv.All( + cv.float_with_unit("decibel full scale", "(dBFS|dbfs|DBFS)"), + cv.int_range(0, 31), + ), + cv.Optional(CONF_VOLUME_MULTIPLIER, default=1.0): cv.float_range( + min=0.0, min_included=False + ), + cv.Optional(CONF_ON_LISTENING): automation.validate_automation(single=True), + cv.Optional(CONF_ON_START): automation.validate_automation(single=True), + cv.Optional(CONF_ON_WAKE_WORD_DETECTED): automation.validate_automation( + single=True + ), + cv.Optional(CONF_ON_STT_END): automation.validate_automation(single=True), + cv.Optional(CONF_ON_TTS_START): automation.validate_automation(single=True), + cv.Optional(CONF_ON_TTS_END): automation.validate_automation(single=True), + cv.Optional(CONF_ON_END): automation.validate_automation(single=True), + cv.Optional(CONF_ON_ERROR): automation.validate_automation(single=True), + } + ).extend(cv.COMPONENT_SCHEMA), +) async def to_code(config): @@ -76,7 +100,14 @@ async def to_code(config): mp = await cg.get_variable(config[CONF_MEDIA_PLAYER]) cg.add(var.set_media_player(mp)) - cg.add(var.set_silence_detection(config[CONF_SILENCE_DETECTION])) + cg.add(var.set_use_wake_word(config[CONF_USE_WAKE_WORD])) + + if (vad_threshold := config.get(CONF_VAD_THRESHOLD)) is not None: + cg.add(var.set_vad_threshold(vad_threshold)) + + cg.add(var.set_noise_suppression_level(config[CONF_NOISE_SUPPRESSION_LEVEL])) + cg.add(var.set_auto_gain(config[CONF_AUTO_GAIN])) + cg.add(var.set_volume_multiplier(config[CONF_VOLUME_MULTIPLIER])) if CONF_ON_LISTENING in config: await automation.build_automation( @@ -88,6 +119,13 @@ async def to_code(config): var.get_start_trigger(), [], config[CONF_ON_START] ) + if CONF_ON_WAKE_WORD_DETECTED in config: + await automation.build_automation( + var.get_wake_word_detected_trigger(), + [], + config[CONF_ON_WAKE_WORD_DETECTED], + ) + if CONF_ON_STT_END in config: await automation.build_automation( var.get_stt_end_trigger(), [(cg.std_string, "x")], config[CONF_ON_STT_END] @@ -128,10 +166,20 @@ VOICE_ASSISTANT_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(VoiceAssis StartContinuousAction, VOICE_ASSISTANT_ACTION_SCHEMA, ) -@register_action("voice_assistant.start", StartAction, VOICE_ASSISTANT_ACTION_SCHEMA) +@register_action( + "voice_assistant.start", + StartAction, + VOICE_ASSISTANT_ACTION_SCHEMA.extend( + { + cv.Optional(CONF_SILENCE_DETECTION, default=True): cv.boolean, + } + ), +) async def voice_assistant_listen_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) await cg.register_parented(var, config[CONF_ID]) + if CONF_SILENCE_DETECTION in config: + cg.add(var.set_silence_detection(config[CONF_SILENCE_DETECTION])) return var diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 217ddb6354..802ae508ff 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -11,6 +11,17 @@ namespace voice_assistant { static const char *const TAG = "voice_assistant"; +#ifdef SAMPLE_RATE_HZ +#undef SAMPLE_RATE_HZ +#endif + +static const size_t SAMPLE_RATE_HZ = 16000; +static const size_t INPUT_BUFFER_SIZE = 32 * SAMPLE_RATE_HZ / 1000; // 32ms * 16kHz / 1000ms +static const size_t BUFFER_SIZE = 1000 * SAMPLE_RATE_HZ / 1000; // 1s +static const size_t SEND_BUFFER_SIZE = INPUT_BUFFER_SIZE * sizeof(int16_t); +static const size_t RECEIVE_SIZE = 1024; +static const size_t SPEAKER_BUFFER_SIZE = 16 * RECEIVE_SIZE; + float VoiceAssistant::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } void VoiceAssistant::setup() { @@ -47,7 +58,6 @@ void VoiceAssistant::setup() { this->mark_failed(); return; } - server.ss_family = AF_INET; err = socket_->bind((struct sockaddr *) &server, sizeof(server)); if (err != 0) { @@ -55,60 +65,262 @@ void VoiceAssistant::setup() { this->mark_failed(); return; } + + ExternalRAMAllocator speaker_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + this->speaker_buffer_ = speaker_allocator.allocate(SPEAKER_BUFFER_SIZE); + if (this->speaker_buffer_ == nullptr) { + ESP_LOGW(TAG, "Could not allocate speaker buffer."); + this->mark_failed(); + return; + } } #endif - this->mic_->add_data_callback([this](const std::vector &data) { - if (!this->running_) { - return; + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + this->input_buffer_ = allocator.allocate(INPUT_BUFFER_SIZE); + if (this->input_buffer_ == nullptr) { + ESP_LOGW(TAG, "Could not allocate input buffer."); + this->mark_failed(); + return; + } + +#ifdef USE_ESP_ADF + this->vad_instance_ = vad_create(VAD_MODE_4); + + this->ring_buffer_ = rb_create(BUFFER_SIZE, sizeof(int16_t)); + if (this->ring_buffer_ == nullptr) { + ESP_LOGW(TAG, "Could not allocate ring buffer."); + this->mark_failed(); + return; + } +#endif + + ExternalRAMAllocator send_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + this->send_buffer_ = send_allocator.allocate(SEND_BUFFER_SIZE); + if (send_buffer_ == nullptr) { + ESP_LOGW(TAG, "Could not allocate send buffer."); + this->mark_failed(); + return; + } +} + +int VoiceAssistant::read_microphone_() { + size_t bytes_read = 0; + if (this->mic_->is_running()) { // Read audio into input buffer + bytes_read = this->mic_->read(this->input_buffer_, INPUT_BUFFER_SIZE * sizeof(int16_t)); + if (bytes_read == 0) { + memset(this->input_buffer_, 0, INPUT_BUFFER_SIZE * sizeof(int16_t)); + return 0; } - this->socket_->sendto(data.data(), data.size() * sizeof(int16_t), 0, (struct sockaddr *) &this->dest_addr_, - sizeof(this->dest_addr_)); - }); +#ifdef USE_ESP_ADF + // Write audio into ring buffer + int available = rb_bytes_available(this->ring_buffer_); + if (available < bytes_read) { + rb_read(this->ring_buffer_, nullptr, bytes_read - available, 0); + } + rb_write(this->ring_buffer_, (char *) this->input_buffer_, bytes_read, 0); +#endif + } else { + ESP_LOGD(TAG, "microphone not running"); + } + return bytes_read; } void VoiceAssistant::loop() { -#ifdef USE_SPEAKER - if (this->speaker_ != nullptr) { - uint8_t buf[1024]; - auto len = this->socket_->read(buf, sizeof(buf)); - if (len == -1) { - return; + if (this->state_ != State::IDLE && this->state_ != State::STOP_MICROPHONE && + this->state_ != State::STOPPING_MICROPHONE && !api::global_api_server->is_connected()) { + if (this->mic_->is_running() || this->state_ == State::STARTING_MICROPHONE) { + this->set_state_(State::STOP_MICROPHONE, State::IDLE); + } else { + this->set_state_(State::IDLE, State::IDLE); } - this->speaker_->play(buf, len); - this->set_timeout("data-incoming", 200, [this]() { - if (this->continuous_) { - this->request_start(true); - } - }); + this->continuous_ = false; + this->signal_stop_(); return; } + switch (this->state_) { + case State::IDLE: { + if (this->continuous_ && this->desired_state_ == State::IDLE) { +#ifdef USE_ESP_ADF + if (this->use_wake_word_) { + rb_reset(this->ring_buffer_); + this->set_state_(State::START_MICROPHONE, State::WAIT_FOR_VAD); + } else +#endif + { + this->set_state_(State::START_PIPELINE, State::START_MICROPHONE); + } + } else { + this->high_freq_.stop(); + } + break; + } + case State::START_MICROPHONE: { + ESP_LOGD(TAG, "Starting Microphone"); + memset(this->send_buffer_, 0, SEND_BUFFER_SIZE); + memset(this->input_buffer_, 0, INPUT_BUFFER_SIZE * sizeof(int16_t)); + this->mic_->start(); + this->high_freq_.start(); + this->set_state_(State::STARTING_MICROPHONE); + break; + } + case State::STARTING_MICROPHONE: { + if (this->mic_->is_running()) { + this->set_state_(this->desired_state_); + } + break; + } +#ifdef USE_ESP_ADF + case State::WAIT_FOR_VAD: { + this->read_microphone_(); + ESP_LOGD(TAG, "Waiting for speech..."); + this->set_state_(State::WAITING_FOR_VAD); + break; + } + case State::WAITING_FOR_VAD: { + size_t bytes_read = this->read_microphone_(); + if (bytes_read > 0) { + vad_state_t vad_state = + vad_process(this->vad_instance_, this->input_buffer_, SAMPLE_RATE_HZ, VAD_FRAME_LENGTH_MS); + if (vad_state == VAD_SPEECH) { + if (this->vad_counter_ < this->vad_threshold_) { + this->vad_counter_++; + } else { + ESP_LOGD(TAG, "VAD detected speech"); + this->set_state_(State::START_PIPELINE, State::STREAMING_MICROPHONE); + } + } else { + if (this->vad_counter_ > 0) { + this->vad_counter_--; + } + } + } + break; + } +#endif + case State::START_PIPELINE: { + this->read_microphone_(); + ESP_LOGD(TAG, "Requesting start..."); + uint32_t flags = 0; + if (this->use_wake_word_) + flags |= api::enums::VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD; + if (this->silence_detection_) + flags |= api::enums::VOICE_ASSISTANT_REQUEST_USE_VAD; + api::VoiceAssistantAudioSettings audio_settings; + audio_settings.noise_suppression_level = this->noise_suppression_level_; + audio_settings.auto_gain = this->auto_gain_; + audio_settings.volume_multiplier = this->volume_multiplier_; + if (!api::global_api_server->start_voice_assistant(this->conversation_id_, flags, audio_settings)) { + ESP_LOGW(TAG, "Could not request start."); + this->error_trigger_->trigger("not-connected", "Could not request start."); + this->continuous_ = false; + this->set_state_(State::IDLE, State::IDLE); + break; + } + this->set_state_(State::STARTING_PIPELINE); + this->set_timeout("reset-conversation_id", 5 * 60 * 1000, [this]() { this->conversation_id_ = ""; }); + break; + } + case State::STARTING_PIPELINE: { + this->read_microphone_(); + break; // State changed when udp server port received + } + case State::STREAMING_MICROPHONE: { + size_t bytes_read = this->read_microphone_(); +#ifdef USE_ESP_ADF + if (rb_bytes_filled(this->ring_buffer_) >= SEND_BUFFER_SIZE) { + rb_read(this->ring_buffer_, (char *) this->send_buffer_, SEND_BUFFER_SIZE, 0); + this->socket_->sendto(this->send_buffer_, SEND_BUFFER_SIZE, 0, (struct sockaddr *) &this->dest_addr_, + sizeof(this->dest_addr_)); + } +#else + if (bytes_read > 0) { + this->socket_->sendto(this->input_buffer_, bytes_read, 0, (struct sockaddr *) &this->dest_addr_, + sizeof(this->dest_addr_)); + } +#endif + break; + } + case State::STOP_MICROPHONE: { + if (this->mic_->is_running()) { + this->mic_->stop(); + this->set_state_(State::STOPPING_MICROPHONE); + } else { + this->set_state_(this->desired_state_); + } + break; + } + case State::STOPPING_MICROPHONE: { + if (this->mic_->is_stopped()) { + this->set_state_(this->desired_state_); + } + break; + } + case State::AWAITING_RESPONSE: { + break; // State changed by events + } + case State::STREAMING_RESPONSE: { + bool playing = false; +#ifdef USE_SPEAKER + if (this->speaker_ != nullptr) { + if (this->speaker_buffer_index_ + RECEIVE_SIZE < SPEAKER_BUFFER_SIZE) { + auto len = this->socket_->read(this->speaker_buffer_ + this->speaker_buffer_index_, RECEIVE_SIZE); + if (len > 0) { + this->speaker_buffer_index_ += len; + this->speaker_buffer_size_ += len; + } + } else { + ESP_LOGW(TAG, "Speaker buffer full."); + } + if (this->speaker_buffer_size_ > 0) { + size_t written = this->speaker_->play(this->speaker_buffer_, this->speaker_buffer_size_); + memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); + this->speaker_buffer_size_ -= written; + this->speaker_buffer_index_ -= written; + } + playing = this->speaker_->is_running(); + } #endif #ifdef USE_MEDIA_PLAYER - if (this->media_player_ != nullptr) { - if (!this->playing_tts_ || - this->media_player_->state == media_player::MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING) { - return; - } - this->set_timeout("playing-media", 1000, [this]() { - this->playing_tts_ = false; - if (this->continuous_) { - this->request_start(true); + if (this->media_player_ != nullptr) { + playing = (this->media_player_->state == media_player::MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING); } - }); - return; - } #endif - // Set a 1 second timeout to start the voice assistant again. - this->set_timeout("continuous-no-sound", 1000, [this]() { - if (this->continuous_) { - this->request_start(true); + if (playing) { + this->set_timeout("playing", 100, [this]() { this->set_state_(State::IDLE, State::IDLE); }); + } + break; } - }); + default: + break; + } } -void VoiceAssistant::start(struct sockaddr_storage *addr, uint16_t port) { - ESP_LOGD(TAG, "Starting..."); +void VoiceAssistant::set_state_(State state) { + State old_state = this->state_; + this->state_ = state; + ESP_LOGD(TAG, "State changed from %d to %d", static_cast(old_state), static_cast(state)); +} + +void VoiceAssistant::set_state_(State state, State desired_state) { + this->set_state_(state); + this->desired_state_ = desired_state; + ESP_LOGD(TAG, "Desired state set to %d", static_cast(desired_state)); +} + +void VoiceAssistant::failed_to_start() { + ESP_LOGE(TAG, "Failed to start server. See Home Assistant logs for more details."); + this->error_trigger_->trigger("failed-to-start", "Failed to start server. See Home Assistant logs for more details."); + this->set_state_(State::STOP_MICROPHONE, State::IDLE); +} + +void VoiceAssistant::start_streaming(struct sockaddr_storage *addr, uint16_t port) { + if (this->state_ != State::STARTING_PIPELINE) { + this->signal_stop_(); + return; + } + + ESP_LOGD(TAG, "Client started, streaming microphone"); memcpy(&this->dest_addr_, addr, sizeof(this->dest_addr_)); if (this->dest_addr_.ss_family == AF_INET) { @@ -123,38 +335,90 @@ void VoiceAssistant::start(struct sockaddr_storage *addr, uint16_t port) { ESP_LOGW(TAG, "Unknown address family: %d", this->dest_addr_.ss_family); return; } - this->running_ = true; - this->mic_->start(); - this->listening_trigger_->trigger(); + + if (this->mic_->is_running()) { + this->set_state_(State::STREAMING_MICROPHONE, State::STREAMING_MICROPHONE); + } else { + this->set_state_(State::START_MICROPHONE, State::STREAMING_MICROPHONE); + } } -void VoiceAssistant::request_start(bool continuous) { - ESP_LOGD(TAG, "Requesting start..."); - if (!api::global_api_server->start_voice_assistant(this->conversation_id_, this->silence_detection_)) { - ESP_LOGW(TAG, "Could not request start."); - this->error_trigger_->trigger("not-connected", "Could not request start."); +void VoiceAssistant::request_start(bool continuous, bool silence_detection) { + if (!api::global_api_server->is_connected()) { + ESP_LOGE(TAG, "No API client connected"); + this->set_state_(State::IDLE, State::IDLE); this->continuous_ = false; return; } - this->continuous_ = continuous; - this->set_timeout("reset-conversation_id", 5 * 60 * 1000, [this]() { this->conversation_id_ = ""; }); + if (this->state_ == State::IDLE) { + this->continuous_ = continuous; + this->silence_detection_ = silence_detection; +#ifdef USE_ESP_ADF + if (this->use_wake_word_) { + rb_reset(this->ring_buffer_); + this->set_state_(State::START_MICROPHONE, State::WAIT_FOR_VAD); + } else +#endif + { + this->set_state_(State::START_PIPELINE, State::START_MICROPHONE); + } + } } -void VoiceAssistant::signal_stop() { +void VoiceAssistant::request_stop() { + this->continuous_ = false; + + switch (this->state_) { + case State::IDLE: + break; + case State::START_MICROPHONE: + case State::STARTING_MICROPHONE: + case State::WAIT_FOR_VAD: + case State::WAITING_FOR_VAD: + case State::START_PIPELINE: + this->set_state_(State::STOP_MICROPHONE, State::IDLE); + break; + case State::STARTING_PIPELINE: + case State::STREAMING_MICROPHONE: + this->signal_stop_(); + this->set_state_(State::STOP_MICROPHONE, State::IDLE); + break; + case State::STOP_MICROPHONE: + case State::STOPPING_MICROPHONE: + this->desired_state_ = State::IDLE; + break; + case State::AWAITING_RESPONSE: + case State::STREAMING_RESPONSE: + break; // Let the incoming audio stream finish then it will go to idle. + } +} + +void VoiceAssistant::signal_stop_() { ESP_LOGD(TAG, "Signaling stop..."); - this->mic_->stop(); - this->running_ = false; api::global_api_server->stop_voice_assistant(); memset(&this->dest_addr_, 0, sizeof(this->dest_addr_)); } void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { + ESP_LOGD(TAG, "Event Type: %d", msg.event_type); switch (msg.event_type) { case api::enums::VOICE_ASSISTANT_RUN_START: ESP_LOGD(TAG, "Assist Pipeline running"); this->start_trigger_->trigger(); break; + case api::enums::VOICE_ASSISTANT_WAKE_WORD_START: + break; + case api::enums::VOICE_ASSISTANT_WAKE_WORD_END: { + ESP_LOGD(TAG, "Wake word detected"); + this->wake_word_detected_trigger_->trigger(); + break; + } + case api::enums::VOICE_ASSISTANT_STT_START: + ESP_LOGD(TAG, "STT Started"); + this->listening_trigger_->trigger(); + break; case api::enums::VOICE_ASSISTANT_STT_END: { + this->set_state_(State::STOP_MICROPHONE, State::AWAITING_RESPONSE); std::string text; for (auto arg : msg.data) { if (arg.name == "text") { @@ -166,7 +430,6 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { return; } ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str()); - this->signal_stop(); this->stt_end_trigger_->trigger(text); break; } @@ -191,6 +454,9 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { } ESP_LOGD(TAG, "Response: \"%s\"", text.c_str()); this->tts_start_trigger_->trigger(text); +#ifdef USE_SPEAKER + this->speaker_->start(); +#endif break; } case api::enums::VOICE_ASSISTANT_TTS_END: { @@ -207,17 +473,22 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { ESP_LOGD(TAG, "Response URL: \"%s\"", url.c_str()); #ifdef USE_MEDIA_PLAYER if (this->media_player_ != nullptr) { - this->playing_tts_ = true; this->media_player_->make_call().set_media_url(url).perform(); } #endif + State new_state = this->local_output_ ? State::STREAMING_RESPONSE : State::IDLE; + this->set_state_(new_state, new_state); this->tts_end_trigger_->trigger(url); break; } - case api::enums::VOICE_ASSISTANT_RUN_END: + case api::enums::VOICE_ASSISTANT_RUN_END: { ESP_LOGD(TAG, "Assist Pipeline ended"); + if (this->state_ != State::STREAMING_RESPONSE && this->state_ != State::IDLE) { + this->set_state_(State::IDLE, State::IDLE); + } this->end_trigger_->trigger(); break; + } case api::enums::VOICE_ASSISTANT_ERROR: { std::string code = ""; std::string message = ""; @@ -228,12 +499,20 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { message = std::move(arg.value); } } + if (code == "wake-word-timeout" || code == "wake_word_detection_aborted") { + this->set_state_(State::STOP_MICROPHONE, State::IDLE); + return; + } ESP_LOGE(TAG, "Error: %s - %s", code.c_str(), message.c_str()); - this->continuous_ = false; - this->signal_stop(); + if (this->state_ != State::IDLE) { + this->signal_stop_(); + this->set_state_(State::STOP_MICROPHONE, State::IDLE); + } this->error_trigger_->trigger(code, message); + break; } default: + ESP_LOGD(TAG, "Unhandled event type: %d", msg.event_type); break; } } diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index 75c17965bc..ce22538a85 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -19,6 +19,11 @@ #endif #include "esphome/components/socket/socket.h" +#ifdef USE_ESP_ADF +#include +#include +#endif + namespace esphome { namespace voice_assistant { @@ -28,19 +33,41 @@ namespace voice_assistant { static const uint32_t INITIAL_VERSION = 1; static const uint32_t SPEAKER_SUPPORT = 2; +enum class State { + IDLE, + START_MICROPHONE, + STARTING_MICROPHONE, + WAIT_FOR_VAD, + WAITING_FOR_VAD, + START_PIPELINE, + STARTING_PIPELINE, + STREAMING_MICROPHONE, + STOP_MICROPHONE, + STOPPING_MICROPHONE, + AWAITING_RESPONSE, + STREAMING_RESPONSE, +}; + class VoiceAssistant : public Component { public: void setup() override; void loop() override; float get_setup_priority() const override; - void start(struct sockaddr_storage *addr, uint16_t port); + void start_streaming(struct sockaddr_storage *addr, uint16_t port); + void failed_to_start(); void set_microphone(microphone::Microphone *mic) { this->mic_ = mic; } #ifdef USE_SPEAKER - void set_speaker(speaker::Speaker *speaker) { this->speaker_ = speaker; } + void set_speaker(speaker::Speaker *speaker) { + this->speaker_ = speaker; + this->local_output_ = true; + } #endif #ifdef USE_MEDIA_PLAYER - void set_media_player(media_player::MediaPlayer *media_player) { this->media_player_ = media_player; } + void set_media_player(media_player::MediaPlayer *media_player) { + this->media_player_ = media_player; + this->local_output_ = true; + } #endif uint32_t get_version() const { @@ -52,19 +79,29 @@ class VoiceAssistant : public Component { return INITIAL_VERSION; } - void request_start(bool continuous = false); - void signal_stop(); + void request_start(bool continuous, bool silence_detection); + void request_stop(); void on_event(const api::VoiceAssistantEventResponse &msg); - bool is_running() const { return this->running_; } + bool is_running() const { return this->state_ != State::IDLE; } void set_continuous(bool continuous) { this->continuous_ = continuous; } bool is_continuous() const { return this->continuous_; } - void set_silence_detection(bool silence_detection) { this->silence_detection_ = silence_detection; } + void set_use_wake_word(bool use_wake_word) { this->use_wake_word_ = use_wake_word; } +#ifdef USE_ESP_ADF + void set_vad_threshold(uint8_t vad_threshold) { this->vad_threshold_ = vad_threshold; } +#endif + + void set_noise_suppression_level(uint8_t noise_suppression_level) { + this->noise_suppression_level_ = noise_suppression_level; + } + void set_auto_gain(uint8_t auto_gain) { this->auto_gain_ = auto_gain; } + void set_volume_multiplier(float volume_multiplier) { this->volume_multiplier_ = volume_multiplier; } Trigger<> *get_listening_trigger() const { return this->listening_trigger_; } Trigger<> *get_start_trigger() const { return this->start_trigger_; } + Trigger<> *get_wake_word_detected_trigger() const { return this->wake_word_detected_trigger_; } Trigger *get_stt_end_trigger() const { return this->stt_end_trigger_; } Trigger *get_tts_start_trigger() const { return this->tts_start_trigger_; } Trigger *get_tts_end_trigger() const { return this->tts_end_trigger_; } @@ -72,11 +109,17 @@ class VoiceAssistant : public Component { Trigger *get_error_trigger() const { return this->error_trigger_; } protected: + int read_microphone_(); + void set_state_(State state); + void set_state_(State state, State desired_state); + void signal_stop_(); + std::unique_ptr socket_ = nullptr; struct sockaddr_storage dest_addr_; Trigger<> *listening_trigger_ = new Trigger<>(); Trigger<> *start_trigger_ = new Trigger<>(); + Trigger<> *wake_word_detected_trigger_ = new Trigger<>(); Trigger *stt_end_trigger_ = new Trigger(); Trigger *tts_start_trigger_ = new Trigger(); Trigger *tts_end_trigger_ = new Trigger(); @@ -86,35 +129,61 @@ class VoiceAssistant : public Component { microphone::Microphone *mic_{nullptr}; #ifdef USE_SPEAKER speaker::Speaker *speaker_{nullptr}; + uint8_t *speaker_buffer_; + size_t speaker_buffer_index_{0}; + size_t speaker_buffer_size_{0}; #endif #ifdef USE_MEDIA_PLAYER media_player::MediaPlayer *media_player_{nullptr}; bool playing_tts_{false}; #endif + bool local_output_{false}; + std::string conversation_id_{""}; - bool running_{false}; + HighFrequencyLoopRequester high_freq_; + +#ifdef USE_ESP_ADF + vad_handle_t vad_instance_; + ringbuf_handle_t ring_buffer_; + uint8_t vad_threshold_{5}; + uint8_t vad_counter_{0}; +#endif + + bool use_wake_word_; + uint8_t noise_suppression_level_; + uint8_t auto_gain_; + float volume_multiplier_; + + uint8_t *send_buffer_; + int16_t *input_buffer_; + bool continuous_{false}; bool silence_detection_; + + State state_{State::IDLE}; + State desired_state_{State::IDLE}; }; template class StartAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->request_start(); } + void play(Ts... x) override { this->parent_->request_start(false, this->silence_detection_); } + + void set_silence_detection(bool silence_detection) { this->silence_detection_ = silence_detection; } + + protected: + bool silence_detection_; }; template class StartContinuousAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->request_start(true); } + void play(Ts... x) override { this->parent_->request_start(true, true); } }; template class StopAction : public Action, public Parented { public: - void play(Ts... x) override { - this->parent_->set_continuous(false); - this->parent_->signal_stop(); - } + void play(Ts... x) override { this->parent_->request_stop(); } }; template class IsRunningCondition : public Condition, public Parented { diff --git a/tests/test4.yaml b/tests/test4.yaml index bb4357e28d..3d0ed2f658 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -767,6 +767,9 @@ speaker: voice_assistant: microphone: mic_id_external + speaker: speaker_id + on_listening: + - logger.log: "Voice assistant microphone listening" on_start: - logger.log: "Voice assistant started" on_stt_end: From c77a9ad3630802376ab65d79d73d5663c79bf6a4 Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Tue, 10 Oct 2023 09:00:05 +0200 Subject: [PATCH 66/95] Don't allow entity category "CONFIG" for sensors (#5505) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/sensor/__init__.py | 13 ++++++++++++- esphome/config_validation.py | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 8f7d581b2d..ee42011038 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -85,6 +85,7 @@ from esphome.const import ( DEVICE_CLASS_WATER, DEVICE_CLASS_WEIGHT, DEVICE_CLASS_WIND_SPEED, + ENTITY_CATEGORY_CONFIG, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_generator import MockObjClass @@ -188,6 +189,15 @@ def validate_datapoint(value): return validate_datapoint({CONF_FROM: cv.float_(a), CONF_TO: cv.float_(b)}) +_SENSOR_ENTITY_CATEGORIES = { + k: v for k, v in cv.ENTITY_CATEGORIES.items() if k != ENTITY_CATEGORY_CONFIG +} + + +def sensor_entity_category(value): + return cv.enum(_SENSOR_ENTITY_CATEGORIES, lower=True)(value) + + # Base Sensor = sensor_ns.class_("Sensor", cg.EntityBase) SensorPtr = Sensor.operator("ptr") @@ -246,6 +256,7 @@ SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( cv.Optional(CONF_ACCURACY_DECIMALS): validate_accuracy_decimals, cv.Optional(CONF_DEVICE_CLASS): validate_device_class, cv.Optional(CONF_STATE_CLASS): validate_state_class, + cv.Optional(CONF_ENTITY_CATEGORY): sensor_entity_category, cv.Optional("last_reset_type"): cv.invalid( "last_reset_type has been removed since 2021.9.0. state_class: total_increasing should be used for total values." ), @@ -301,7 +312,7 @@ def sensor_schema( (CONF_ACCURACY_DECIMALS, accuracy_decimals, validate_accuracy_decimals), (CONF_DEVICE_CLASS, device_class, validate_device_class), (CONF_STATE_CLASS, state_class, validate_state_class), - (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category), + (CONF_ENTITY_CATEGORY, entity_category, sensor_entity_category), ]: if default is not _UNDEF: schema[cv.Optional(key, default=default)] = validator diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 46f3f6a7bd..d699a2647b 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1672,7 +1672,7 @@ def maybe_simple_value(*validators, **kwargs): return validate -_ENTITY_CATEGORIES = { +ENTITY_CATEGORIES = { ENTITY_CATEGORY_NONE: cg.EntityCategory.ENTITY_CATEGORY_NONE, ENTITY_CATEGORY_CONFIG: cg.EntityCategory.ENTITY_CATEGORY_CONFIG, ENTITY_CATEGORY_DIAGNOSTIC: cg.EntityCategory.ENTITY_CATEGORY_DIAGNOSTIC, @@ -1680,7 +1680,7 @@ _ENTITY_CATEGORIES = { def entity_category(value): - return enum(_ENTITY_CATEGORIES, lower=True)(value) + return enum(ENTITY_CATEGORIES, lower=True)(value) MQTT_COMPONENT_AVAILABILITY_SCHEMA = Schema( From c9a8911029698c239cff21a0a67bd952c526da14 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Wed, 11 Oct 2023 00:51:53 +0200 Subject: [PATCH 67/95] Prepare protobuf for ESP-IDF >= 5 (#5510) --- esphome/components/api/api_pb2.cpp | 190 ++++++++++++++-------------- script/api_protobuf/api_protobuf.py | 12 +- 2 files changed, 103 insertions(+), 99 deletions(-) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index d0711aba7b..225b213a67 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -3,6 +3,8 @@ #include "api_pb2.h" #include "esphome/core/log.h" +#include + namespace esphome { namespace api { @@ -544,12 +546,12 @@ void HelloRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" api_version_major: "); - sprintf(buffer, "%u", this->api_version_major); + sprintf(buffer, "%" PRIu32, this->api_version_major); out.append(buffer); out.append("\n"); out.append(" api_version_minor: "); - sprintf(buffer, "%u", this->api_version_minor); + sprintf(buffer, "%" PRIu32, this->api_version_minor); out.append(buffer); out.append("\n"); out.append("}"); @@ -594,12 +596,12 @@ void HelloResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("HelloResponse {\n"); out.append(" api_version_major: "); - sprintf(buffer, "%u", this->api_version_major); + sprintf(buffer, "%" PRIu32, this->api_version_major); out.append(buffer); out.append("\n"); out.append(" api_version_minor: "); - sprintf(buffer, "%u", this->api_version_minor); + sprintf(buffer, "%" PRIu32, this->api_version_minor); out.append(buffer); out.append("\n"); @@ -805,17 +807,17 @@ void DeviceInfoResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" webserver_port: "); - sprintf(buffer, "%u", this->webserver_port); + sprintf(buffer, "%" PRIu32, this->webserver_port); out.append(buffer); out.append("\n"); out.append(" legacy_bluetooth_proxy_version: "); - sprintf(buffer, "%u", this->legacy_bluetooth_proxy_version); + sprintf(buffer, "%" PRIu32, this->legacy_bluetooth_proxy_version); out.append(buffer); out.append("\n"); out.append(" bluetooth_proxy_feature_flags: "); - sprintf(buffer, "%u", this->bluetooth_proxy_feature_flags); + sprintf(buffer, "%" PRIu32, this->bluetooth_proxy_feature_flags); out.append(buffer); out.append("\n"); @@ -828,7 +830,7 @@ void DeviceInfoResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" voice_assistant_version: "); - sprintf(buffer, "%u", this->voice_assistant_version); + sprintf(buffer, "%" PRIu32, this->voice_assistant_version); out.append(buffer); out.append("\n"); out.append("}"); @@ -920,7 +922,7 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -988,7 +990,7 @@ void BinarySensorStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("BinarySensorStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -1091,7 +1093,7 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -1181,7 +1183,7 @@ void CoverStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("CoverStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -1264,7 +1266,7 @@ void CoverCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("CoverCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -1384,7 +1386,7 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -1409,7 +1411,7 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" supported_speed_count: "); - sprintf(buffer, "%d", this->supported_speed_count); + sprintf(buffer, "%" PRId32, this->supported_speed_count); out.append(buffer); out.append("\n"); @@ -1476,7 +1478,7 @@ void FanStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("FanStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -1497,7 +1499,7 @@ void FanStateResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" speed_level: "); - sprintf(buffer, "%d", this->speed_level); + sprintf(buffer, "%" PRId32, this->speed_level); out.append(buffer); out.append("\n"); out.append("}"); @@ -1577,7 +1579,7 @@ void FanCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("FanCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -1618,7 +1620,7 @@ void FanCommandRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" speed_level: "); - sprintf(buffer, "%d", this->speed_level); + sprintf(buffer, "%" PRId32, this->speed_level); out.append(buffer); out.append("\n"); out.append("}"); @@ -1732,7 +1734,7 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -1886,7 +1888,7 @@ void LightStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("LightStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -2109,7 +2111,7 @@ void LightCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("LightCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -2207,7 +2209,7 @@ void LightCommandRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" transition_length: "); - sprintf(buffer, "%u", this->transition_length); + sprintf(buffer, "%" PRIu32, this->transition_length); out.append(buffer); out.append("\n"); @@ -2216,7 +2218,7 @@ void LightCommandRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" flash_length: "); - sprintf(buffer, "%u", this->flash_length); + sprintf(buffer, "%" PRIu32, this->flash_length); out.append(buffer); out.append("\n"); @@ -2324,7 +2326,7 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -2345,7 +2347,7 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" accuracy_decimals: "); - sprintf(buffer, "%d", this->accuracy_decimals); + sprintf(buffer, "%" PRId32, this->accuracy_decimals); out.append(buffer); out.append("\n"); @@ -2409,7 +2411,7 @@ void SensorStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("SensorStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -2498,7 +2500,7 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -2561,7 +2563,7 @@ void SwitchStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("SwitchStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -2600,7 +2602,7 @@ void SwitchCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("SwitchCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -2674,7 +2676,7 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -2740,7 +2742,7 @@ void TextSensorStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("TextSensorStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -3047,7 +3049,7 @@ void GetTimeResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("GetTimeResponse {\n"); out.append(" epoch_seconds: "); - sprintf(buffer, "%u", this->epoch_seconds); + sprintf(buffer, "%" PRIu32, this->epoch_seconds); out.append(buffer); out.append("\n"); out.append("}"); @@ -3131,7 +3133,7 @@ void ListEntitiesServicesResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -3225,7 +3227,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const { out.append("\n"); out.append(" legacy_int: "); - sprintf(buffer, "%d", this->legacy_int); + sprintf(buffer, "%" PRId32, this->legacy_int); out.append(buffer); out.append("\n"); @@ -3239,7 +3241,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const { out.append("\n"); out.append(" int_: "); - sprintf(buffer, "%d", this->int_); + sprintf(buffer, "%" PRId32, this->int_); out.append(buffer); out.append("\n"); @@ -3251,7 +3253,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const { for (const auto &it : this->int_array) { out.append(" int_array: "); - sprintf(buffer, "%d", it); + sprintf(buffer, "%" PRId32, it); out.append(buffer); out.append("\n"); } @@ -3302,7 +3304,7 @@ void ExecuteServiceRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("ExecuteServiceRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -3378,7 +3380,7 @@ void ListEntitiesCameraResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -3444,7 +3446,7 @@ void CameraImageResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("CameraImageResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -3636,7 +3638,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -3824,7 +3826,7 @@ void ClimateStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("ClimateStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4012,7 +4014,7 @@ void ClimateCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("ClimateCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4195,7 +4197,7 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4282,7 +4284,7 @@ void NumberStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("NumberStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4320,7 +4322,7 @@ void NumberCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("NumberCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4402,7 +4404,7 @@ void ListEntitiesSelectResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4474,7 +4476,7 @@ void SelectStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("SelectStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4517,7 +4519,7 @@ void SelectCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("SelectCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4611,7 +4613,7 @@ void ListEntitiesLockResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4682,7 +4684,7 @@ void LockStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("LockStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4737,7 +4739,7 @@ void LockCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("LockCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4824,7 +4826,7 @@ void ListEntitiesButtonResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -4870,7 +4872,7 @@ void ButtonCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("ButtonCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); out.append("}"); @@ -4945,7 +4947,7 @@ void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -5014,7 +5016,7 @@ void MediaPlayerStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("MediaPlayerStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -5093,7 +5095,7 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("MediaPlayerCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -5142,7 +5144,7 @@ void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const __attribute__((unused)) char buffer[64]; out.append("SubscribeBluetoothLEAdvertisementsRequest {\n"); out.append(" flags: "); - sprintf(buffer, "%u", this->flags); + sprintf(buffer, "%" PRIu32, this->flags); out.append(buffer); out.append("\n"); out.append("}"); @@ -5189,7 +5191,7 @@ void BluetoothServiceData::dump_to(std::string &out) const { for (const auto &it : this->legacy_data) { out.append(" legacy_data: "); - sprintf(buffer, "%u", it); + sprintf(buffer, "%" PRIu32, it); out.append(buffer); out.append("\n"); } @@ -5269,7 +5271,7 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" rssi: "); - sprintf(buffer, "%d", this->rssi); + sprintf(buffer, "%" PRId32, this->rssi); out.append(buffer); out.append("\n"); @@ -5292,7 +5294,7 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const { } out.append(" address_type: "); - sprintf(buffer, "%u", this->address_type); + sprintf(buffer, "%" PRIu32, this->address_type); out.append(buffer); out.append("\n"); out.append("}"); @@ -5342,12 +5344,12 @@ void BluetoothLERawAdvertisement::dump_to(std::string &out) const { out.append("\n"); out.append(" rssi: "); - sprintf(buffer, "%d", this->rssi); + sprintf(buffer, "%" PRId32, this->rssi); out.append(buffer); out.append("\n"); out.append(" address_type: "); - sprintf(buffer, "%u", this->address_type); + sprintf(buffer, "%" PRIu32, this->address_type); out.append(buffer); out.append("\n"); @@ -5430,7 +5432,7 @@ void BluetoothDeviceRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" address_type: "); - sprintf(buffer, "%u", this->address_type); + sprintf(buffer, "%" PRIu32, this->address_type); out.append(buffer); out.append("\n"); out.append("}"); @@ -5478,12 +5480,12 @@ void BluetoothDeviceConnectionResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" mtu: "); - sprintf(buffer, "%u", this->mtu); + sprintf(buffer, "%" PRIu32, this->mtu); out.append(buffer); out.append("\n"); out.append(" error: "); - sprintf(buffer, "%d", this->error); + sprintf(buffer, "%" PRId32, this->error); out.append(buffer); out.append("\n"); out.append("}"); @@ -5543,7 +5545,7 @@ void BluetoothGATTDescriptor::dump_to(std::string &out) const { } out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); out.append("}"); @@ -5599,12 +5601,12 @@ void BluetoothGATTCharacteristic::dump_to(std::string &out) const { } out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); out.append(" properties: "); - sprintf(buffer, "%u", this->properties); + sprintf(buffer, "%" PRIu32, this->properties); out.append(buffer); out.append("\n"); @@ -5661,7 +5663,7 @@ void BluetoothGATTService::dump_to(std::string &out) const { } out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); @@ -5768,7 +5770,7 @@ void BluetoothGATTReadRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); out.append("}"); @@ -5813,7 +5815,7 @@ void BluetoothGATTReadResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); @@ -5867,7 +5869,7 @@ void BluetoothGATTWriteRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); @@ -5909,7 +5911,7 @@ void BluetoothGATTReadDescriptorRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); out.append("}"); @@ -5954,7 +5956,7 @@ void BluetoothGATTWriteDescriptorRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); @@ -5997,7 +5999,7 @@ void BluetoothGATTNotifyRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); @@ -6046,7 +6048,7 @@ void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); @@ -6085,12 +6087,12 @@ void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("BluetoothConnectionsFreeResponse {\n"); out.append(" free: "); - sprintf(buffer, "%u", this->free); + sprintf(buffer, "%" PRIu32, this->free); out.append(buffer); out.append("\n"); out.append(" limit: "); - sprintf(buffer, "%u", this->limit); + sprintf(buffer, "%" PRIu32, this->limit); out.append(buffer); out.append("\n"); out.append("}"); @@ -6129,12 +6131,12 @@ void BluetoothGATTErrorResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); out.append(" error: "); - sprintf(buffer, "%d", this->error); + sprintf(buffer, "%" PRId32, this->error); out.append(buffer); out.append("\n"); out.append("}"); @@ -6168,7 +6170,7 @@ void BluetoothGATTWriteResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); out.append("}"); @@ -6202,7 +6204,7 @@ void BluetoothGATTNotifyResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" handle: "); - sprintf(buffer, "%u", this->handle); + sprintf(buffer, "%" PRIu32, this->handle); out.append(buffer); out.append("\n"); out.append("}"); @@ -6245,7 +6247,7 @@ void BluetoothDevicePairingResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" error: "); - sprintf(buffer, "%d", this->error); + sprintf(buffer, "%" PRId32, this->error); out.append(buffer); out.append("\n"); out.append("}"); @@ -6288,7 +6290,7 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" error: "); - sprintf(buffer, "%d", this->error); + sprintf(buffer, "%" PRId32, this->error); out.append(buffer); out.append("\n"); out.append("}"); @@ -6337,7 +6339,7 @@ void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" error: "); - sprintf(buffer, "%d", this->error); + sprintf(buffer, "%" PRId32, this->error); out.append(buffer); out.append("\n"); out.append("}"); @@ -6398,12 +6400,12 @@ void VoiceAssistantAudioSettings::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("VoiceAssistantAudioSettings {\n"); out.append(" noise_suppression_level: "); - sprintf(buffer, "%u", this->noise_suppression_level); + sprintf(buffer, "%" PRIu32, this->noise_suppression_level); out.append(buffer); out.append("\n"); out.append(" auto_gain: "); - sprintf(buffer, "%u", this->auto_gain); + sprintf(buffer, "%" PRIu32, this->auto_gain); out.append(buffer); out.append("\n"); @@ -6461,7 +6463,7 @@ void VoiceAssistantRequest::dump_to(std::string &out) const { out.append("\n"); out.append(" flags: "); - sprintf(buffer, "%u", this->flags); + sprintf(buffer, "%" PRIu32, this->flags); out.append(buffer); out.append("\n"); @@ -6494,7 +6496,7 @@ void VoiceAssistantResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("VoiceAssistantResponse {\n"); out.append(" port: "); - sprintf(buffer, "%u", this->port); + sprintf(buffer, "%" PRIu32, this->port); out.append(buffer); out.append("\n"); @@ -6657,7 +6659,7 @@ void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -6682,7 +6684,7 @@ void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const { out.append("\n"); out.append(" supported_features: "); - sprintf(buffer, "%u", this->supported_features); + sprintf(buffer, "%" PRIu32, this->supported_features); out.append(buffer); out.append("\n"); @@ -6725,7 +6727,7 @@ void AlarmControlPanelStateResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("AlarmControlPanelStateResponse {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); @@ -6775,7 +6777,7 @@ void AlarmControlPanelCommandRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("AlarmControlPanelCommandRequest {\n"); out.append(" key: "); - sprintf(buffer, "%u", this->key); + sprintf(buffer, "%" PRIu32, this->key); out.append(buffer); out.append("\n"); diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 5a0c92350d..3c19034e96 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -263,7 +263,7 @@ class Int32Type(TypeInfo): encode_func = "encode_int32" def dump(self, name): - o = f'sprintf(buffer, "%d", {name});\n' + o = f'sprintf(buffer, "%" PRId32, {name});\n' o += f"out.append(buffer);" return o @@ -289,7 +289,7 @@ class Fixed32Type(TypeInfo): encode_func = "encode_fixed32" def dump(self, name): - o = f'sprintf(buffer, "%u", {name});\n' + o = f'sprintf(buffer, "%" PRIu32, {name});\n' o += f"out.append(buffer);" return o @@ -371,7 +371,7 @@ class UInt32Type(TypeInfo): encode_func = "encode_uint32" def dump(self, name): - o = f'sprintf(buffer, "%u", {name});\n' + o = f'sprintf(buffer, "%" PRIu32, {name});\n' o += f"out.append(buffer);" return o @@ -405,7 +405,7 @@ class SFixed32Type(TypeInfo): encode_func = "encode_sfixed32" def dump(self, name): - o = f'sprintf(buffer, "%d", {name});\n' + o = f'sprintf(buffer, "%" PRId32, {name});\n' o += f"out.append(buffer);" return o @@ -431,7 +431,7 @@ class SInt32Type(TypeInfo): encode_func = "encode_sint32" def dump(self, name): - o = f'sprintf(buffer, "%d", {name});\n' + o = f'sprintf(buffer, "%" PRId32, {name});\n' o += f"out.append(buffer);" return o @@ -701,6 +701,8 @@ cpp += """\ #include "api_pb2.h" #include "esphome/core/log.h" +#include + namespace esphome { namespace api { From 5d5cc960170ae0a4f68fbc3c37bf1ced820120a4 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Tue, 10 Oct 2023 19:52:40 -0300 Subject: [PATCH 68/95] fix build lang schema for spi and i2c (#5509) --- script/build_language_schema.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/script/build_language_schema.py b/script/build_language_schema.py index c6fcf5eb64..fb2010fe3e 100644 --- a/script/build_language_schema.py +++ b/script/build_language_schema.py @@ -82,6 +82,10 @@ def load_components(): components[domain] = get_component(domain) +from esphome.const import CONF_TYPE, KEY_CORE +from esphome.core import CORE + +CORE.data[KEY_CORE] = {} load_components() # Import esphome after loading components (so schema is tracked) @@ -91,7 +95,6 @@ import esphome.config_validation as cv from esphome import automation from esphome import pins from esphome.components import remote_base -from esphome.const import CONF_TYPE from esphome.loader import get_platform, CORE_COMPONENTS_PATH from esphome.helpers import write_file_if_changed from esphome.util import Registry @@ -868,8 +871,11 @@ def convert(schema, config_var, path): else: raise Exception("Unknown extracted schema type") elif config_var.get("key") == "GeneratedID": - if path == "i2c/CONFIG_SCHEMA/extL/all/id": - config_var["id_type"] = {"class": "i2c::I2CBus", "parents": ["Component"]} + if path.startswith("i2c/CONFIG_SCHEMA/") and path.endswith("/id"): + config_var["id_type"] = { + "class": "i2c::I2CBus", + "parents": ["Component"], + } elif path == "uart/CONFIG_SCHEMA/val 1/extL/all/id": config_var["id_type"] = { "class": "uart::UARTComponent", From 9d95f5c1daece493abd61f37feb1a2d47ceef15c Mon Sep 17 00:00:00 2001 From: Oleg Tarasov Date: Wed, 11 Oct 2023 01:56:53 +0300 Subject: [PATCH 69/95] Added Nextion display error handling during setup (#5493) --- esphome/components/nextion/nextion.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index f8beaeab78..6133ad1d7e 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -61,6 +61,11 @@ bool Nextion::check_connect_() { std::string response; this->recv_ret_string_(response, 0, false); + if (!response.empty() && response[0] == 0x1A) { + // Swallow invalid variable name responses that may be caused by the above commands + ESP_LOGD(TAG, "0x1A error ignored during setup"); + return false; + } if (response.empty() || response.find("comok") == std::string::npos) { #ifdef NEXTION_PROTOCOL_LOG ESP_LOGN(TAG, "Bad connect request %s", response.c_str()); From 41f29c46d024138ae8b3a1dd19d94f1c4ed96816 Mon Sep 17 00:00:00 2001 From: justdaniel-gh <68163487+justdaniel-gh@users.noreply.github.com> Date: Tue, 10 Oct 2023 18:42:38 -0500 Subject: [PATCH 70/95] Fix e131 and voice_assistant sockets (#5502) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/e131/e131.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/components/e131/e131.cpp b/esphome/components/e131/e131.cpp index 818006ced7..c3ff21c1a0 100644 --- a/esphome/components/e131/e131.cpp +++ b/esphome/components/e131/e131.cpp @@ -40,7 +40,6 @@ void E131Component::setup() { this->mark_failed(); return; } - server.ss_family = AF_INET; err = this->socket_->bind((struct sockaddr *) &server, sizeof(server)); if (err != 0) { From e35de626a46d595609556d83c1c18a9afe4d8183 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Thu, 12 Oct 2023 03:26:07 +0200 Subject: [PATCH 71/95] Allow manual set "Invert_display" (#5494) --- esphome/components/ili9xxx/display.py | 5 +++++ esphome/components/ili9xxx/ili9xxx_display.cpp | 15 +++++++++++---- esphome/components/ili9xxx/ili9xxx_display.h | 4 +++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 241f56e018..ec96d38cf8 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -54,6 +54,7 @@ COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE") CONF_LED_PIN = "led_pin" CONF_COLOR_PALETTE_IMAGES = "color_palette_images" +CONF_INVERT_DISPLAY = "invert_display" def _validate(config): @@ -100,6 +101,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list( cv.file_ ), + cv.Optional(CONF_INVERT_DISPLAY): cv.boolean, } ) .extend(cv.polling_component_schema("1s")) @@ -176,3 +178,6 @@ async def to_code(config): if rhs is not None: prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) cg.add(var.set_palette(prog_arr)) + + if CONF_INVERT_DISPLAY in config: + cg.add(var.invert_display(config[CONF_INVERT_DISPLAY])) diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index abe01ea8c3..fdbf3e3760 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -12,11 +12,13 @@ static const char *const TAG = "ili9xxx"; void ILI9XXXDisplay::setup() { this->setup_pins_(); this->initialize(); + this->command(this->pre_invertdisplay_ ? ILI9XXX_INVON : ILI9XXX_INVOFF); this->x_low_ = this->width_; this->y_low_ = this->height_; this->x_high_ = 0; this->y_high_ = 0; + if (this->buffer_color_mode_ == BITS_16) { this->init_internal_(this->get_buffer_length_() * 2); if (this->buffer_ != nullptr) { @@ -333,7 +335,12 @@ void ILI9XXXDisplay::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t w, uint this->command(ILI9XXX_RAMWR); // Write to RAM } -void ILI9XXXDisplay::invert_display_(bool invert) { this->command(invert ? ILI9XXX_INVON : ILI9XXX_INVOFF); } +void ILI9XXXDisplay::invert_display(bool invert) { + this->pre_invertdisplay_ = invert; + if (is_ready()) { + this->command(invert ? ILI9XXX_INVON : ILI9XXX_INVOFF); + } +} int ILI9XXXDisplay::get_width_internal() { return this->width_; } int ILI9XXXDisplay::get_height_internal() { return this->height_; } @@ -345,7 +352,7 @@ void ILI9XXXM5Stack::initialize() { this->width_ = 320; if (this->height_ == 0) this->height_ = 240; - this->invert_display_(true); + this->pre_invertdisplay_ = true; } // M5CORE display // Based on the configuration settings of M5stact's M5GFX code. @@ -355,7 +362,7 @@ void ILI9XXXM5CORE::initialize() { this->width_ = 320; if (this->height_ == 0) this->height_ = 240; - this->invert_display_(true); + this->pre_invertdisplay_ = true; } // 24_TFT display @@ -462,7 +469,7 @@ void ILI9XXXS3BoxLite::initialize() { if (this->height_ == 0) { this->height_ = 240; } - this->invert_display_(true); + this->pre_invertdisplay_ = true; } } // namespace ili9xxx diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index 4e8355b9a5..e43585afeb 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -33,6 +33,7 @@ class ILI9XXXDisplay : public PollingComponent, this->height_ = height; this->width_ = width; } + void invert_display(bool invert); void command(uint8_t value); void data(uint8_t value); void send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes); @@ -55,7 +56,7 @@ class ILI9XXXDisplay : public PollingComponent, void display_(); void init_lcd_(const uint8_t *init_cmd); void set_addr_window_(uint16_t x, uint16_t y, uint16_t w, uint16_t h); - void invert_display_(bool invert); + void reset_(); int16_t width_{0}; ///< Display width as modified by current rotation @@ -88,6 +89,7 @@ class ILI9XXXDisplay : public PollingComponent, bool prossing_update_ = false; bool need_update_ = false; bool is_18bitdisplay_ = false; + bool pre_invertdisplay_ = false; }; //----------- M5Stack display -------------- From 853d81c6ddb7d9d11e04156707a83451214c7356 Mon Sep 17 00:00:00 2001 From: Mark Veinot Date: Wed, 11 Oct 2023 22:27:36 -0300 Subject: [PATCH 72/95] As3935 calibration (#5366) --- esphome/components/as3935/__init__.py | 6 ++ esphome/components/as3935/as3935.cpp | 89 +++++++++++++++++++++++++++ esphome/components/as3935/as3935.h | 14 +++++ esphome/const.py | 1 + 4 files changed, 110 insertions(+) diff --git a/esphome/components/as3935/__init__.py b/esphome/components/as3935/__init__.py index 5cec1bfaba..2ec7c50859 100644 --- a/esphome/components/as3935/__init__.py +++ b/esphome/components/as3935/__init__.py @@ -8,6 +8,8 @@ from esphome.const import ( CONF_IRQ_PIN, CONF_LIGHTNING_THRESHOLD, CONF_MASK_DISTURBER, + CONF_CALIBRATION, + CONF_TUNE_ANTENNA, CONF_NOISE_LEVEL, CONF_SPIKE_REJECTION, CONF_WATCHDOG_THRESHOLD, @@ -34,6 +36,8 @@ AS3935_SCHEMA = cv.Schema( cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean, cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 32, 64, 128, int=True), cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15), + cv.Optional(CONF_TUNE_ANTENNA, default=False): cv.boolean, + cv.Optional(CONF_CALIBRATION, default=True): cv.boolean, } ) @@ -51,3 +55,5 @@ async def setup_as3935(var, config): cg.add(var.set_mask_disturber(config[CONF_MASK_DISTURBER])) cg.add(var.set_div_ratio(config[CONF_DIV_RATIO])) cg.add(var.set_capacitance(config[CONF_CAPACITANCE])) + cg.add(var.set_tune_antenna(config[CONF_TUNE_ANTENNA])) + cg.add(var.set_calibration(config[CONF_CALIBRATION])) diff --git a/esphome/components/as3935/as3935.cpp b/esphome/components/as3935/as3935.cpp index c5651caee3..29fc6ee685 100644 --- a/esphome/components/as3935/as3935.cpp +++ b/esphome/components/as3935/as3935.cpp @@ -21,6 +21,14 @@ void AS3935Component::setup() { this->write_mask_disturber(this->mask_disturber_); this->write_div_ratio(this->div_ratio_); this->write_capacitance(this->capacitance_); + + // Handle setting up tuning or auto-calibration + if (this->tune_antenna_) { + ESP_LOGCONFIG(TAG, " Antenna tuning: ENABLED - lightning detection will not function in this mode"); + this->tune_antenna(); + } else if (this->calibration_) { + this->calibrate_oscillator(); + } } void AS3935Component::dump_config() { @@ -227,6 +235,87 @@ uint32_t AS3935Component::get_lightning_energy_() { return pure_light; } +// REG0x03, bit [7:6], manufacturer default: 0 (16 division ratio). +// This function returns the current division ratio of the resonance frequency. +// The antenna resonance frequency should be within 3.5 percent of 500kHz, and +// so when modifying the resonance frequency with the internal capacitors +// (tuneCap()) it's important to keep in mind that the displayed frequency on +// the IRQ pin is divided by this number. +uint8_t AS3935Component::read_div_ratio() { + ESP_LOGV(TAG, "Calling read_div_ratio"); + uint8_t reg_val = this->read_register_(INT_MASK_ANT, DIV_MASK); + reg_val >>= 6; // Front of the line. + + if (reg_val == 0) { + return 16; + } else if (reg_val == 1) { + return 32; + } else if (reg_val == 2) { + return 64; + } else if (reg_val == 3) { + return 128; + } + ESP_LOGW(TAG, "Unknown response received for div_ratio"); + return 0; +} + +uint8_t AS3935Component::read_capacitance() { + ESP_LOGV(TAG, "Calling read_capacitance"); + uint8_t reg_val = this->read_register_(FREQ_DISP_IRQ, CAP_MASK) * 8; + return (reg_val); +} + +// REG0x08, bits [5,6,7], manufacturer default: 0. +// This will send the frequency of the oscillators to the IRQ pin. +// _osc 1, bit[5] = TRCO - System RCO at 32.768kHz +// _osc 2, bit[6] = SRCO - Timer RCO Oscillators 1.1MHz +// _osc 3, bit[7] = LCO - Frequency of the Antenna +void AS3935Component::display_oscillator(bool state, uint8_t osc) { + if ((osc < 1) || (osc > 3)) + return; + + this->write_register(FREQ_DISP_IRQ, OSC_MASK, state, 4 + osc); +} + +// REG0x3D, bits[7:0] +// This function calibrates both internal oscillators The oscillators are tuned +// based on the resonance frequency of the antenna and so it should be trimmed +// before the calibration is done. +bool AS3935Component::calibrate_oscillator() { + ESP_LOGI(TAG, "Starting oscillators calibration..."); + this->write_register(CALIB_RCO, WIPE_ALL, DIRECT_COMMAND, 0); // Send command to calibrate the oscillators + + this->display_oscillator(true, 2); + delay(2); // Give time for the internal oscillators to start up. + this->display_oscillator(false, 2); + + // Check it they were calibrated successfully. + uint8_t reg_val_srco = this->read_register_(CALIB_SRCO, CALIB_MASK_NOK); + uint8_t reg_val_trco = this->read_register_(CALIB_TRCO, CALIB_MASK_NOK); + + // reg_val_srco &= CALIB_MASK; + // reg_val_srco >>= 6; + // reg_val_trco &= CALIB_MASK; + // reg_val_trco >>= 6; + if (!reg_val_srco && !reg_val_trco) { // Zero upon success + ESP_LOGI(TAG, "Calibration was succesful"); + return true; + } else { + ESP_LOGW(TAG, "Calibration was NOT succesful"); + return false; + } +} + +void AS3935Component::tune_antenna() { + ESP_LOGI(TAG, "Starting antenna tuning..."); + uint8_t div_ratio = this->read_div_ratio(); + uint8_t tune_val = this->read_capacitance(); + ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio); + ESP_LOGI(TAG, "Internal Capacitor is set to: %d", tune_val); + ESP_LOGI(TAG, "Displaying oscillator on INT pin. Measure its frequency - multiply value by Division Ratio"); + this->display_oscillator(true, ANTFREQ); +} + uint8_t AS3935Component::read_register_(uint8_t reg, uint8_t mask) { uint8_t value = this->read_register(reg); value &= (~mask); diff --git a/esphome/components/as3935/as3935.h b/esphome/components/as3935/as3935.h index a8af703a59..dc590c268e 100644 --- a/esphome/components/as3935/as3935.h +++ b/esphome/components/as3935/as3935.h @@ -13,6 +13,9 @@ namespace esphome { namespace as3935 { +static const uint8_t DIRECT_COMMAND = 0x96; +static const uint8_t ANTFREQ = 3; + enum AS3935RegisterNames { AFE_GAIN = 0x00, THRESHOLD, @@ -30,6 +33,7 @@ enum AS3935RegisterNames { }; enum AS3935RegisterMasks { + WIPE_ALL = 0x0, GAIN_MASK = 0x3E, SPIKE_MASK = 0xF, IO_MASK = 0xC1, @@ -44,6 +48,7 @@ enum AS3935RegisterMasks { NOISE_FLOOR_MASK = 0x70, OSC_MASK = 0xE0, CALIB_MASK = 0x7F, + CALIB_MASK_NOK = 0xBF, DIV_MASK = 0x3F }; @@ -90,6 +95,13 @@ class AS3935Component : public Component { void write_div_ratio(uint8_t div_ratio); void set_capacitance(uint8_t capacitance) { capacitance_ = capacitance; } void write_capacitance(uint8_t capacitance); + uint8_t read_div_ratio(); + uint8_t read_capacitance(); + bool calibrate_oscillator(); + void display_oscillator(bool state, uint8_t osc); + void tune_antenna(); + void set_tune_antenna(bool tune_antenna) { tune_antenna_ = tune_antenna; } + void set_calibration(bool calibration) { calibration_ = calibration; } protected: uint8_t read_interrupt_register_(); @@ -112,6 +124,8 @@ class AS3935Component : public Component { bool mask_disturber_; uint8_t div_ratio_; uint8_t capacitance_; + bool tune_antenna_; + bool calibration_; }; } // namespace as3935 diff --git a/esphome/const.py b/esphome/const.py index 01555c35ec..fc67651193 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -783,6 +783,7 @@ CONF_TRACES = "traces" CONF_TRANSITION_LENGTH = "transition_length" CONF_TRIGGER_ID = "trigger_id" CONF_TRIGGER_PIN = "trigger_pin" +CONF_TUNE_ANTENNA = "tune_antenna" CONF_TURN_OFF_ACTION = "turn_off_action" CONF_TURN_ON_ACTION = "turn_on_action" CONF_TVOC = "tvoc" From 2fc4e8827131f3199a2e15c64201eed1312d0688 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Wed, 11 Oct 2023 20:51:29 -0500 Subject: [PATCH 73/95] Small fixes for voice assistant (#5513) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 26 +++++++++----- .../voice_assistant/voice_assistant.cpp | 36 ++++++++++++++----- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index a0934e3844..592a27b739 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -11,7 +11,7 @@ namespace esphome { namespace i2s_audio { -static const size_t BUFFER_COUNT = 10; +static const size_t BUFFER_COUNT = 20; static const char *const TAG = "i2s_audio.speaker"; @@ -19,7 +19,7 @@ void I2SAudioSpeaker::setup() { ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker..."); this->buffer_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(DataEvent)); - this->event_queue_ = xQueueCreate(20, sizeof(TaskEvent)); + this->event_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(TaskEvent)); } void I2SAudioSpeaker::start() { this->state_ = speaker::STATE_STARTING; } @@ -47,7 +47,7 @@ void I2SAudioSpeaker::player_task(void *params) { .communication_format = I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 8, - .dma_buf_len = 1024, + .dma_buf_len = 128, .use_apll = false, .tx_desc_auto_clear = true, .fixed_mclk = I2S_PIN_NO_CHANGE, @@ -60,7 +60,17 @@ void I2SAudioSpeaker::player_task(void *params) { } #endif - i2s_driver_install(this_speaker->parent_->get_port(), &config, 0, nullptr); + esp_err_t err = i2s_driver_install(this_speaker->parent_->get_port(), &config, 0, nullptr); + if (err != ESP_OK) { + event.type = TaskEventType::WARNING; + event.err = err; + xQueueSend(this_speaker->event_queue_, &event, 0); + event.type = TaskEventType::STOPPED; + xQueueSend(this_speaker->event_queue_, &event, 0); + while (true) { + delay(10); + } + } #if SOC_I2S_SUPPORTS_DAC if (this_speaker->internal_dac_mode_ == I2S_DAC_CHANNEL_DISABLE) { @@ -88,9 +98,7 @@ void I2SAudioSpeaker::player_task(void *params) { } if (data_event.stop) { // Stop signal from main thread - while (xQueueReceive(this_speaker->buffer_queue_, &data_event, 0) == pdTRUE) { - // Flush queue - } + xQueueReset(this_speaker->buffer_queue_); // Flush queue break; } size_t bytes_written; @@ -103,7 +111,7 @@ void I2SAudioSpeaker::player_task(void *params) { uint32_t sample = (buffer[current] << 16) | (buffer[current] & 0xFFFF); esp_err_t err = i2s_write(this_speaker->parent_->get_port(), &sample, sizeof(sample), &bytes_written, - (100 / portTICK_PERIOD_MS)); + (10 / portTICK_PERIOD_MS)); if (err != ESP_OK) { event = {.type = TaskEventType::WARNING, .err = err}; xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY); @@ -122,7 +130,6 @@ void I2SAudioSpeaker::player_task(void *params) { event.type = TaskEventType::STOPPING; xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY); - i2s_stop(this_speaker->parent_->get_port()); i2s_driver_uninstall(this_speaker->parent_->get_port()); event.type = TaskEventType::STOPPED; @@ -162,6 +169,7 @@ void I2SAudioSpeaker::watch_() { vTaskDelete(this->player_task_handle_); this->player_task_handle_ = nullptr; this->parent_->unlock(); + xQueueReset(this->buffer_queue_); break; case TaskEventType::WARNING: ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err)); diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 802ae508ff..448df61d80 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -188,6 +188,9 @@ void VoiceAssistant::loop() { } else { ESP_LOGD(TAG, "VAD detected speech"); this->set_state_(State::START_PIPELINE, State::STREAMING_MICROPHONE); + + // Reset for next time + this->vad_counter_ = 0; } } else { if (this->vad_counter_ > 0) { @@ -270,13 +273,18 @@ void VoiceAssistant::loop() { this->speaker_buffer_size_ += len; } } else { - ESP_LOGW(TAG, "Speaker buffer full."); + ESP_LOGW(TAG, "Receive buffer full."); } if (this->speaker_buffer_size_ > 0) { size_t written = this->speaker_->play(this->speaker_buffer_, this->speaker_buffer_size_); - memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); - this->speaker_buffer_size_ -= written; - this->speaker_buffer_index_ -= written; + if (written > 0) { + memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); + this->speaker_buffer_size_ -= written; + this->speaker_buffer_index_ -= written; + this->set_timeout("speaker-timeout", 1000, [this]() { this->speaker_->stop(); }); + } else { + ESP_LOGW(TAG, "Speaker buffer full."); + } } playing = this->speaker_->is_running(); } @@ -287,7 +295,10 @@ void VoiceAssistant::loop() { } #endif if (playing) { - this->set_timeout("playing", 100, [this]() { this->set_state_(State::IDLE, State::IDLE); }); + this->set_timeout("playing", 100, [this]() { + this->cancel_timeout("speaker-timeout"); + this->set_state_(State::IDLE, State::IDLE); + }); } break; } @@ -483,8 +494,17 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { } case api::enums::VOICE_ASSISTANT_RUN_END: { ESP_LOGD(TAG, "Assist Pipeline ended"); - if (this->state_ != State::STREAMING_RESPONSE && this->state_ != State::IDLE) { - this->set_state_(State::IDLE, State::IDLE); + if (this->state_ == State::STREAMING_MICROPHONE) { +#ifdef USE_ESP_ADF + if (this->use_wake_word_) { + rb_reset(this->ring_buffer_); + // No need to stop the microphone since we didn't use the speaker + this->set_state_(State::WAIT_FOR_VAD, State::WAITING_FOR_VAD); + } else +#endif + { + this->set_state_(State::IDLE, State::IDLE); + } } this->end_trigger_->trigger(); break; @@ -500,7 +520,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { } } if (code == "wake-word-timeout" || code == "wake_word_detection_aborted") { - this->set_state_(State::STOP_MICROPHONE, State::IDLE); + // Don't change state here since either the "tts-end" or "run-end" events will do it. return; } ESP_LOGE(TAG, "Error: %s - %s", code.c_str(), message.c_str()); From 689dcd1e2459f70f092222e3826367061b64d3e5 Mon Sep 17 00:00:00 2001 From: Daniel Baulig Date: Wed, 11 Oct 2023 18:55:01 -0700 Subject: [PATCH 74/95] Add detail param to allow listing of select options in WebServer REST API (#5503) --- esphome/components/web_server/web_server.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index e350e1b140..dec8fb8e21 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -805,7 +805,12 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM continue; if (request->method() == HTTP_GET) { - std::string data = this->select_json(obj, obj->state, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->select_json(obj, obj->state, detail); request->send(200, "application/json", data.c_str()); return; } From 11727391ad0b5d029eef2f54b4113b1b049a03ce Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 12 Oct 2023 15:14:43 +1300 Subject: [PATCH 75/95] Bump version to 2023.11.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index fc67651193..eb7a5dab09 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.10.0-dev" +__version__ = "2023.11.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 54363f1246eeab37375b0c37748b18e8c7570d80 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 12 Oct 2023 15:14:43 +1300 Subject: [PATCH 76/95] Bump version to 2023.10.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index fc67651193..5a5b683c2d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.10.0-dev" +__version__ = "2023.10.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From f4ce8b8b6c6ea414f9d05fa7dc0048e1582d4b40 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 12 Oct 2023 15:52:57 +1300 Subject: [PATCH 77/95] Bump curl to 7.74.0-1.3+deb11u10 (#5517) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 99f15d64b2..5ca36d1c13 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -26,7 +26,7 @@ RUN \ python3-venv=3.9.2-3 \ iputils-ping=3:20210202-1 \ git=1:2.30.2-1+deb11u2 \ - curl=7.74.0-1.3+deb11u9 \ + curl=7.74.0-1.3+deb11u10 \ openssh-client=1:8.4p1-5+deb11u2 \ python3-cffi=1.14.5-1 \ libcairo2=1.16.0-5 \ From d27e5e9c97ccb1035c3ef738f0fd22cb36227c7a Mon Sep 17 00:00:00 2001 From: Nippey Date: Thu, 12 Oct 2023 19:56:30 +0200 Subject: [PATCH 78/95] Update htu21d.cpp, fix publishing of heater level (#5520) --- esphome/components/htu21d/htu21d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/htu21d/htu21d.cpp b/esphome/components/htu21d/htu21d.cpp index 5030ac4d0f..a8133ae32e 100644 --- a/esphome/components/htu21d/htu21d.cpp +++ b/esphome/components/htu21d/htu21d.cpp @@ -76,7 +76,7 @@ void HTU21DComponent::update() { if (this->humidity_ != nullptr) this->humidity_->publish_state(humidity); if (this->heater_ != nullptr) - this->heater_->publish_state(humidity); + this->heater_->publish_state(heater_level); this->status_clear_warning(); } From 6cce6d4c364104082b77a6ec12ede5243cbe11bb Mon Sep 17 00:00:00 2001 From: Cossid <83468485+Cossid@users.noreply.github.com> Date: Thu, 12 Oct 2023 14:09:07 -0500 Subject: [PATCH 79/95] BD5758D - Add delays and ACKs (#5524) --- esphome/components/bp5758d/bp5758d.cpp | 30 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/esphome/components/bp5758d/bp5758d.cpp b/esphome/components/bp5758d/bp5758d.cpp index 71a81f7e6c..87c4165275 100644 --- a/esphome/components/bp5758d/bp5758d.cpp +++ b/esphome/components/bp5758d/bp5758d.cpp @@ -17,12 +17,16 @@ static const uint8_t BP5758D_ADDR_START_2CH = 0b00100000; static const uint8_t BP5758D_ADDR_START_5CH = 0b00110000; static const uint8_t BP5758D_ALL_DATA_CHANNEL_ENABLEMENT = 0b00011111; +static const uint8_t BP5758D_DELAY = 2; + void BP5758D::setup() { ESP_LOGCONFIG(TAG, "Setting up BP5758D Output Component..."); this->data_pin_->setup(); this->data_pin_->digital_write(false); + delayMicroseconds(BP5758D_DELAY); this->clock_pin_->setup(); this->clock_pin_->digital_write(false); + delayMicroseconds(BP5758D_DELAY); this->channel_current_.resize(5, 0); this->pwm_amounts_.resize(5, 0); } @@ -39,11 +43,11 @@ void BP5758D::loop() { uint8_t data[17]; if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) { - for (int i = 1; i < 16; i++) + for (int i = 1; i < 17; i++) data[i] = 0; // First turn all channels off - data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_3CH; + data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_5CH; this->write_buffer_(data, 17); // Then sleep data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_STANDBY; @@ -123,28 +127,42 @@ void BP5758D::set_channel_value_(uint8_t channel, uint16_t value) { void BP5758D::set_channel_current_(uint8_t channel, uint8_t current) { this->channel_current_[channel] = current; } void BP5758D::write_bit_(bool value) { - this->clock_pin_->digital_write(false); this->data_pin_->digital_write(value); + delayMicroseconds(BP5758D_DELAY); this->clock_pin_->digital_write(true); + delayMicroseconds(BP5758D_DELAY); + this->clock_pin_->digital_write(false); + delayMicroseconds(BP5758D_DELAY); } void BP5758D::write_byte_(uint8_t data) { for (uint8_t mask = 0x80; mask; mask >>= 1) { this->write_bit_(data & mask); } - this->clock_pin_->digital_write(false); - this->data_pin_->digital_write(true); + + // ack bit + this->data_pin_->pin_mode(gpio::FLAG_INPUT); this->clock_pin_->digital_write(true); + delayMicroseconds(BP5758D_DELAY); + this->clock_pin_->digital_write(false); + delayMicroseconds(BP5758D_DELAY); + this->data_pin_->pin_mode(gpio::FLAG_OUTPUT); } void BP5758D::write_buffer_(uint8_t *buffer, uint8_t size) { this->data_pin_->digital_write(false); + delayMicroseconds(BP5758D_DELAY); + this->clock_pin_->digital_write(false); + delayMicroseconds(BP5758D_DELAY); + for (uint32_t i = 0; i < size; i++) { this->write_byte_(buffer[i]); } - this->clock_pin_->digital_write(false); + this->clock_pin_->digital_write(true); + delayMicroseconds(BP5758D_DELAY); this->data_pin_->digital_write(true); + delayMicroseconds(BP5758D_DELAY); } } // namespace bp5758d From 969f6dbe131824ce0c9e6c23fe9a8b207887f991 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 13 Oct 2023 12:46:46 +1300 Subject: [PATCH 80/95] Update Improv BLE component (#5518) --- .../esp32_ble_server/ble_service.cpp | 2 + .../components/esp32_ble_server/ble_service.h | 2 +- esphome/components/esp32_improv/__init__.py | 2 +- .../esp32_improv/esp32_improv_component.cpp | 79 +++++++++++-------- .../esp32_improv/esp32_improv_component.h | 29 +++++-- esphome/components/output/__init__.py | 1 + esphome/core/defines.h | 3 +- 7 files changed, 79 insertions(+), 39 deletions(-) diff --git a/esphome/components/esp32_ble_server/ble_service.cpp b/esphome/components/esp32_ble_server/ble_service.cpp index 4fcd2e3e79..e5aaebc137 100644 --- a/esphome/components/esp32_ble_server/ble_service.cpp +++ b/esphome/components/esp32_ble_server/ble_service.cpp @@ -90,6 +90,8 @@ void BLEService::stop() { ESP_LOGE(TAG, "esp_ble_gatts_stop_service failed: %d", err); return; } + esp32_ble::global_ble->get_advertising()->remove_service_uuid(this->uuid_); + esp32_ble::global_ble->get_advertising()->start(); this->running_state_ = STOPPING; } diff --git a/esphome/components/esp32_ble_server/ble_service.h b/esphome/components/esp32_ble_server/ble_service.h index 2766c931a7..93b4217517 100644 --- a/esphome/components/esp32_ble_server/ble_service.h +++ b/esphome/components/esp32_ble_server/ble_service.h @@ -7,11 +7,11 @@ #ifdef USE_ESP32 +#include #include #include #include #include -#include namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index ae7f0b6427..fba2e55ae8 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -4,7 +4,7 @@ from esphome.components import binary_sensor, output, esp32_ble_server from esphome.const import CONF_ID -AUTO_LOAD = ["binary_sensor", "output", "esp32_ble_server"] +AUTO_LOAD = ["esp32_ble_server"] CODEOWNERS = ["@jesserockz"] CONFLICTS_WITH = ["esp32_ble_beacon"] DEPENDENCIES = ["wifi", "esp32"] diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 85013c006b..5bdf7d19fe 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -18,6 +18,17 @@ ESP32ImprovComponent::ESP32ImprovComponent() { global_improv_component = this; } void ESP32ImprovComponent::setup() { this->service_ = global_ble_server->create_service(improv::SERVICE_UUID, true); this->setup_characteristics(); + +#ifdef USE_BINARY_SENSOR + if (this->authorizer_ != nullptr) { + this->authorizer_->add_on_state_callback([this](bool state) { + if (state) { + this->authorized_start_ = millis(); + this->identify_start_ = 0; + } + }); + } +#endif } void ESP32ImprovComponent::setup_characteristics() { @@ -50,8 +61,10 @@ void ESP32ImprovComponent::setup_characteristics() { BLEDescriptor *capabilities_descriptor = new BLE2902(); this->capabilities_->add_descriptor(capabilities_descriptor); uint8_t capabilities = 0x00; +#ifdef USE_OUTPUT if (this->status_indicator_ != nullptr) capabilities |= improv::CAPABILITY_IDENTIFY; +#endif this->capabilities_->set_value(capabilities); this->setup_complete_ = true; } @@ -63,8 +76,7 @@ void ESP32ImprovComponent::loop() { switch (this->state_) { case improv::STATE_STOPPED: - if (this->status_indicator_ != nullptr) - this->status_indicator_->turn_off(); + this->set_status_indicator_state_(false); if (this->service_->is_created() && this->should_start_ && this->setup_complete_) { if (this->service_->is_running()) { @@ -80,14 +92,17 @@ void ESP32ImprovComponent::loop() { } break; case improv::STATE_AWAITING_AUTHORIZATION: { - if (this->authorizer_ == nullptr || this->authorizer_->state) { +#ifdef USE_BINARY_SENSOR + if (this->authorizer_ == nullptr || + (this->authorized_start_ != 0 && ((now - this->authorized_start_) < this->authorized_duration_))) { this->set_state_(improv::STATE_AUTHORIZED); - this->authorized_start_ = now; - } else { - if (this->status_indicator_ != nullptr) { - if (!this->check_identify_()) - this->status_indicator_->turn_on(); - } + } else +#else + this->set_state_(improv::STATE_AUTHORIZED); +#endif + { + if (!this->check_identify_()) + this->set_status_indicator_state_(true); } break; } @@ -99,25 +114,13 @@ void ESP32ImprovComponent::loop() { return; } } - if (this->status_indicator_ != nullptr) { - if (!this->check_identify_()) { - if ((now % 1000) < 500) { - this->status_indicator_->turn_on(); - } else { - this->status_indicator_->turn_off(); - } - } + if (!this->check_identify_()) { + this->set_status_indicator_state_((now % 1000) < 500); } break; } case improv::STATE_PROVISIONING: { - if (this->status_indicator_ != nullptr) { - if ((now % 200) < 100) { - this->status_indicator_->turn_on(); - } else { - this->status_indicator_->turn_off(); - } - } + this->set_status_indicator_state_((now % 200) < 100); if (wifi::global_wifi_component->is_connected()) { wifi::global_wifi_component->save_wifi_sta(this->connecting_sta_.get_ssid(), this->connecting_sta_.get_password()); @@ -142,13 +145,27 @@ void ESP32ImprovComponent::loop() { } case improv::STATE_PROVISIONED: { this->incoming_data_.clear(); - if (this->status_indicator_ != nullptr) - this->status_indicator_->turn_off(); + this->set_status_indicator_state_(false); break; } } } +void ESP32ImprovComponent::set_status_indicator_state_(bool state) { +#ifdef USE_OUTPUT + if (this->status_indicator_ == nullptr) + return; + if (this->status_indicator_state_ == state) + return; + this->status_indicator_state_ = state; + if (state) { + this->status_indicator_->turn_on(); + } else { + this->status_indicator_->turn_off(); + } +#endif +} + bool ESP32ImprovComponent::check_identify_() { uint32_t now = millis(); @@ -156,11 +173,7 @@ bool ESP32ImprovComponent::check_identify_() { if (identify) { uint32_t time = now % 1000; - if (time < 600 && time % 200 < 100) { - this->status_indicator_->turn_on(); - } else { - this->status_indicator_->turn_off(); - } + this->set_status_indicator_state_(time < 600 && time % 200 < 100); } return identify; } @@ -213,8 +226,12 @@ float ESP32ImprovComponent::get_setup_priority() const { return setup_priority:: void ESP32ImprovComponent::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 Improv:"); +#ifdef USE_BINARY_SENSOR LOG_BINARY_SENSOR(" ", "Authorizer", this->authorizer_); +#endif +#ifdef USE_OUTPUT ESP_LOGCONFIG(TAG, " Status Indicator: '%s'", YESNO(this->status_indicator_ != nullptr)); +#endif } void ESP32ImprovComponent::process_incoming_data_() { diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index 1a142c94b6..ba9892d6a5 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -1,14 +1,22 @@ #pragma once -#include "esphome/components/binary_sensor/binary_sensor.h" -#include "esphome/components/esp32_ble_server/ble_characteristic.h" -#include "esphome/components/esp32_ble_server/ble_server.h" -#include "esphome/components/output/binary_output.h" -#include "esphome/components/wifi/wifi_component.h" #include "esphome/core/component.h" +#include "esphome/core/defines.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" +#include "esphome/components/esp32_ble_server/ble_characteristic.h" +#include "esphome/components/esp32_ble_server/ble_server.h" +#include "esphome/components/wifi/wifi_component.h" + +#ifdef USE_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif + +#ifdef USE_OUTPUT +#include "esphome/components/output/binary_output.h" +#endif + #include #ifdef USE_ESP32 @@ -34,8 +42,12 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent { void stop() override; bool is_active() const { return this->state_ != improv::STATE_STOPPED; } +#ifdef USE_BINARY_SENSOR void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; } +#endif +#ifdef USE_OUTPUT void set_status_indicator(output::BinaryOutput *status_indicator) { this->status_indicator_ = status_indicator; } +#endif void set_identify_duration(uint32_t identify_duration) { this->identify_duration_ = identify_duration; } void set_authorized_duration(uint32_t authorized_duration) { this->authorized_duration_ = authorized_duration; } @@ -58,12 +70,19 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent { BLECharacteristic *rpc_response_; BLECharacteristic *capabilities_; +#ifdef USE_BINARY_SENSOR binary_sensor::BinarySensor *authorizer_{nullptr}; +#endif +#ifdef USE_OUTPUT output::BinaryOutput *status_indicator_{nullptr}; +#endif improv::State state_{improv::STATE_STOPPED}; improv::Error error_state_{improv::ERROR_NONE}; + bool status_indicator_state_{false}; + void set_status_indicator_state_(bool state); + void set_state_(improv::State state); void set_error_(improv::Error error); void send_response_(std::vector &response); diff --git a/esphome/components/output/__init__.py b/esphome/components/output/__init__.py index 4f1fb33fe7..726d1ac084 100644 --- a/esphome/components/output/__init__.py +++ b/esphome/components/output/__init__.py @@ -106,4 +106,5 @@ async def output_set_level_to_code(config, action_id, template_arg, args): async def to_code(config): + cg.add_define("USE_OUTPUT") cg.add_global(output_ns.using) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 1e0df74eec..71493119c0 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -37,6 +37,7 @@ #define USE_OTA #define USE_OTA_PASSWORD #define USE_OTA_STATE_CALLBACK +#define USE_OUTPUT #define USE_POWER_SUPPLY #define USE_QR_CODE #define USE_SELECT @@ -117,6 +118,6 @@ #endif // Disabled feature flags -//#define USE_BSEC // Requires a library with proprietary license. +// #define USE_BSEC // Requires a library with proprietary license. #define USE_DASHBOARD_IMPORT From 8c1ad1e9a6816d2d59cb768d8d9638359625a195 Mon Sep 17 00:00:00 2001 From: Cossid <83468485+Cossid@users.noreply.github.com> Date: Thu, 12 Oct 2023 18:51:19 -0500 Subject: [PATCH 81/95] SM10BIT_BASE - Add delays and ACKs, clear all channels before sleeping. (#5526) --- .../components/sm10bit_base/sm10bit_base.cpp | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/esphome/components/sm10bit_base/sm10bit_base.cpp b/esphome/components/sm10bit_base/sm10bit_base.cpp index 9c7abb48e2..d380f31c6f 100644 --- a/esphome/components/sm10bit_base/sm10bit_base.cpp +++ b/esphome/components/sm10bit_base/sm10bit_base.cpp @@ -11,6 +11,8 @@ static const uint8_t SM10BIT_ADDR_START_3CH = 0x8; static const uint8_t SM10BIT_ADDR_START_2CH = 0x10; static const uint8_t SM10BIT_ADDR_START_5CH = 0x18; +static const uint8_t SM10BIT_DELAY = 2; + // Power current values // HEX | Binary | RGB level | White level | Config value // 0x0 | 0000 | RGB 10mA | CW 5mA | 0 @@ -37,10 +39,13 @@ void Sm10BitBase::loop() { uint8_t data[12]; if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) { - // Off / Sleep - data[0] = this->model_id_ + SM10BIT_ADDR_STANDBY; for (int i = 1; i < 12; i++) data[i] = 0; + // First turn all channels off + data[0] = this->model_id_ + SM10BIT_ADDR_START_5CH; + this->write_buffer_(data, 12); + // Then sleep + data[0] = this->model_id_ + SM10BIT_ADDR_STANDBY; this->write_buffer_(data, 12); } else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && (this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) { @@ -84,28 +89,42 @@ void Sm10BitBase::set_channel_value_(uint8_t channel, uint16_t value) { this->pwm_amounts_[channel] = value; } void Sm10BitBase::write_bit_(bool value) { - this->clock_pin_->digital_write(false); this->data_pin_->digital_write(value); + delayMicroseconds(SM10BIT_DELAY); this->clock_pin_->digital_write(true); + delayMicroseconds(SM10BIT_DELAY); + this->clock_pin_->digital_write(false); + delayMicroseconds(SM10BIT_DELAY); } void Sm10BitBase::write_byte_(uint8_t data) { for (uint8_t mask = 0x80; mask; mask >>= 1) { this->write_bit_(data & mask); } - this->clock_pin_->digital_write(false); - this->data_pin_->digital_write(true); + + // ack bit + this->data_pin_->pin_mode(gpio::FLAG_INPUT); this->clock_pin_->digital_write(true); + delayMicroseconds(SM10BIT_DELAY); + this->clock_pin_->digital_write(false); + delayMicroseconds(SM10BIT_DELAY); + this->data_pin_->pin_mode(gpio::FLAG_OUTPUT); } void Sm10BitBase::write_buffer_(uint8_t *buffer, uint8_t size) { this->data_pin_->digital_write(false); + delayMicroseconds(SM10BIT_DELAY); + this->clock_pin_->digital_write(false); + delayMicroseconds(SM10BIT_DELAY); + for (uint32_t i = 0; i < size; i++) { this->write_byte_(buffer[i]); } - this->clock_pin_->digital_write(false); + this->clock_pin_->digital_write(true); + delayMicroseconds(SM10BIT_DELAY); this->data_pin_->digital_write(true); + delayMicroseconds(SM10BIT_DELAY); } } // namespace sm10bit_base From 5d7c3d16224ff4ce8b7afaa91907b95b9c29b7c2 Mon Sep 17 00:00:00 2001 From: Cossid <83468485+Cossid@users.noreply.github.com> Date: Thu, 12 Oct 2023 18:51:28 -0500 Subject: [PATCH 82/95] BP1658CJ - Clear all channels before sleeping. (#5525) --- esphome/components/bp1658cj/bp1658cj.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/bp1658cj/bp1658cj.cpp b/esphome/components/bp1658cj/bp1658cj.cpp index d3f3e71fed..05c3f790c2 100644 --- a/esphome/components/bp1658cj/bp1658cj.cpp +++ b/esphome/components/bp1658cj/bp1658cj.cpp @@ -37,10 +37,14 @@ void BP1658CJ::loop() { uint8_t data[12]; if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) { - // Off / Sleep - data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_STANDBY; for (int i = 1; i < 12; i++) data[i] = 0; + + // First turn all channels off + data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_START_5CH; + this->write_buffer_(data, 12); + // Then sleep + data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_STANDBY; this->write_buffer_(data, 12); } else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && (this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) { From 90315b3c401f608ae3dbe6f3cf89d6ef000c7510 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:16:22 +1300 Subject: [PATCH 83/95] Bump version to 2023.10.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5a5b683c2d..cef733b2f9 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.10.0b1" +__version__ = "2023.10.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 261c271d60caa35a496cca256f667157c3ee2a4f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Oct 2023 19:41:59 +1300 Subject: [PATCH 84/95] Prometheus fix for esp-idf and fix newlines (#5536) --- .../prometheus/prometheus_handler.cpp | 22 ++++++++----------- .../prometheus/prometheus_handler.h | 8 ++----- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index abb5111aaf..68bca95a21 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -1,5 +1,3 @@ -#ifdef USE_ARDUINO - #include "prometheus_handler.h" #include "esphome/core/application.h" @@ -89,7 +87,7 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor stream->print(obj->get_unit_of_measurement().c_str()); stream->print(F("\"} ")); stream->print(value_accuracy_to_string(obj->state, obj->get_accuracy_decimals()).c_str()); - stream->print('\n'); + stream->print(F("\n")); } else { // Invalid state stream->print(F("esphome_sensor_failed{id=\"")); @@ -124,7 +122,7 @@ void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_s stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->state); - stream->print('\n'); + stream->print(F("\n")); } else { // Invalid state stream->print(F("esphome_binary_sensor_failed{id=\"")); @@ -158,7 +156,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) { stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->state); - stream->print('\n'); + stream->print(F("\n")); // Speed if available if (obj->get_traits().supports_speed()) { stream->print(F("esphome_fan_speed{id=\"")); @@ -167,7 +165,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) { stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->speed); - stream->print('\n'); + stream->print(F("\n")); } // Oscillation if available if (obj->get_traits().supports_oscillation()) { @@ -177,7 +175,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) { stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->oscillating); - stream->print('\n'); + stream->print(F("\n")); } } #endif @@ -281,7 +279,7 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->position); - stream->print('\n'); + stream->print(F("\n")); if (obj->get_traits().get_supports_tilt()) { stream->print(F("esphome_cover_tilt{id=\"")); stream->print(relabel_id_(obj).c_str()); @@ -289,7 +287,7 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->tilt); - stream->print('\n'); + stream->print(F("\n")); } } else { // Invalid state @@ -322,7 +320,7 @@ void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->state); - stream->print('\n'); + stream->print(F("\n")); } #endif @@ -346,11 +344,9 @@ void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj) stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->state); - stream->print('\n'); + stream->print(F("\n")); } #endif } // namespace prometheus } // namespace esphome - -#endif // USE_ARDUINO diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index 0ae2856ce4..a9505a3572 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -1,14 +1,12 @@ #pragma once -#ifdef USE_ARDUINO - #include #include -#include "esphome/core/entity_base.h" #include "esphome/components/web_server_base/web_server_base.h" -#include "esphome/core/controller.h" #include "esphome/core/component.h" +#include "esphome/core/controller.h" +#include "esphome/core/entity_base.h" namespace esphome { namespace prometheus { @@ -119,5 +117,3 @@ class PrometheusHandler : public AsyncWebHandler, public Component { } // namespace prometheus } // namespace esphome - -#endif // USE_ARDUINO From 52e8a2e9e4ec73620c3a31dc8925eb9cae19e3c5 Mon Sep 17 00:00:00 2001 From: raineth Date: Mon, 16 Oct 2023 02:42:18 -0400 Subject: [PATCH 85/95] Make IPAddress's operator!= compare values, not memory addresses. (#5537) Co-authored-by: Ben Winslow --- esphome/components/network/ip_address.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index 8b05237c05..7a4d394805 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -87,7 +87,7 @@ struct IPAddress { bool is_ip6() { return IP_IS_V6(&ip_addr_); } std::string str() const { return ipaddr_ntoa(&ip_addr_); } bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); } - bool operator!=(const IPAddress &other) const { return !(&ip_addr_ == &other.ip_addr_); } + bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); } IPAddress &operator+=(uint8_t increase) { if (IP_IS_V4(&ip_addr_)) { #if LWIP_IPV6 From 97d624114d8722e84fd4ac62f841cccba5e7f725 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 16 Oct 2023 20:47:35 +0100 Subject: [PATCH 86/95] Add change i2c address and allow multi conf for TB6612FNG (#5492) --- .../components/grove_tb6612fng/__init__.py | 25 +++++++++++++++++++ .../grove_tb6612fng/grove_tb6612fng.h | 11 ++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/esphome/components/grove_tb6612fng/__init__.py b/esphome/components/grove_tb6612fng/__init__.py index 75610ce9d3..7db0198a89 100644 --- a/esphome/components/grove_tb6612fng/__init__.py +++ b/esphome/components/grove_tb6612fng/__init__.py @@ -8,12 +8,15 @@ from esphome.const import ( CONF_CHANNEL, CONF_SPEED, CONF_DIRECTION, + CONF_ADDRESS, ) DEPENDENCIES = ["i2c"] CODEOWNERS = ["@max246"] +MULTI_CONF = True + grove_tb6612fng_ns = cg.esphome_ns.namespace("grove_tb6612fng") GROVE_TB6612FNG = grove_tb6612fng_ns.class_( "GroveMotorDriveTB6612FNG", cg.Component, i2c.I2CDevice @@ -33,6 +36,9 @@ GROVETB6612FNGMotorStandbyAction = grove_tb6612fng_ns.class_( GROVETB6612FNGMotorNoStandbyAction = grove_tb6612fng_ns.class_( "GROVETB6612FNGMotorNoStandbyAction", automation.Action ) +GROVETB6612FNGMotorChangeAddressAction = grove_tb6612fng_ns.class_( + "GROVETB6612FNGMotorChangeAddressAction", automation.Action +) DIRECTION_TYPE = { "FORWARD": 1, @@ -150,3 +156,22 @@ async def grove_tb6612fng_no_standby_to_code(config, action_id, template_arg, ar await cg.register_parented(var, config[CONF_ID]) return var + + +@automation.register_action( + "grove_tb6612fng.change_address", + GROVETB6612FNGMotorChangeAddressAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG), + cv.Required(CONF_ADDRESS): cv.i2c_address, + } + ), +) +async def grove_tb6612fng_change_address_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + + template_channel = await cg.templatable(config[CONF_ADDRESS], args, int) + cg.add(var.set_address(template_channel)) + return var diff --git a/esphome/components/grove_tb6612fng/grove_tb6612fng.h b/esphome/components/grove_tb6612fng/grove_tb6612fng.h index ccdab6472a..2743ef4ed7 100644 --- a/esphome/components/grove_tb6612fng/grove_tb6612fng.h +++ b/esphome/components/grove_tb6612fng/grove_tb6612fng.h @@ -84,8 +84,7 @@ class GroveMotorDriveTB6612FNG : public Component, public i2c::I2CDevice { *************************************************************/ void set_i2c_addr(uint8_t addr); - /************************************************************* - Description + /***********************************change_address Drive a motor. Parameter chl: MOTOR_CHA or MOTOR_CHB @@ -204,5 +203,13 @@ class GROVETB6612FNGMotorNoStandbyAction : public Action, public Parented void play(Ts... x) override { this->parent_->not_standby(); } }; +template +class GROVETB6612FNGMotorChangeAddressAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(uint8_t, address) + + void play(Ts... x) override { this->parent_->set_i2c_addr(this->address_.value(x...)); } +}; + } // namespace grove_tb6612fng } // namespace esphome From 61cf566560539ba0549843d8e645242833879ddd Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 Oct 2023 17:18:05 +1300 Subject: [PATCH 87/95] Add stream start and end events (#5545) --- esphome/components/api/api.proto | 2 + esphome/components/api/api_pb2.cpp | 4 ++ esphome/components/api/api_pb2.h | 2 + .../i2s_audio/speaker/i2s_audio_speaker.cpp | 6 ++ .../voice_assistant/voice_assistant.cpp | 69 +++++++++++++++++-- .../voice_assistant/voice_assistant.h | 3 +- 6 files changed, 81 insertions(+), 5 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index ec4a0f7cc9..69765c7a94 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1459,6 +1459,8 @@ enum VoiceAssistantEvent { VOICE_ASSISTANT_WAKE_WORD_END = 10; VOICE_ASSISTANT_STT_VAD_START = 11; VOICE_ASSISTANT_STT_VAD_END = 12; + VOICE_ASSISTANT_TTS_STREAM_START = 98; + VOICE_ASSISTANT_TTS_STREAM_END = 99; } message VoiceAssistantEventData { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 225b213a67..65df2312e1 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -452,6 +452,10 @@ template<> const char *proto_enum_to_string(enums::V return "VOICE_ASSISTANT_STT_VAD_START"; case enums::VOICE_ASSISTANT_STT_VAD_END: return "VOICE_ASSISTANT_STT_VAD_END"; + case enums::VOICE_ASSISTANT_TTS_STREAM_START: + return "VOICE_ASSISTANT_TTS_STREAM_START"; + case enums::VOICE_ASSISTANT_TTS_STREAM_END: + return "VOICE_ASSISTANT_TTS_STREAM_END"; default: return "UNKNOWN"; } diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index a4826f09d2..4c70facf3d 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -184,6 +184,8 @@ enum VoiceAssistantEvent : uint32_t { VOICE_ASSISTANT_WAKE_WORD_END = 10, VOICE_ASSISTANT_STT_VAD_START = 11, VOICE_ASSISTANT_STT_VAD_END = 12, + VOICE_ASSISTANT_TTS_STREAM_START = 98, + VOICE_ASSISTANT_TTS_STREAM_END = 99, }; enum AlarmControlPanelState : uint32_t { ALARM_STATE_DISARMED = 0, diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 592a27b739..ed13e6b458 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -158,8 +158,13 @@ void I2SAudioSpeaker::watch_() { if (xQueueReceive(this->event_queue_, &event, 0) == pdTRUE) { switch (event.type) { case TaskEventType::STARTING: + ESP_LOGD(TAG, "Starting I2S Audio Speaker"); + break; case TaskEventType::STARTED: + ESP_LOGD(TAG, "Started I2S Audio Speaker"); + break; case TaskEventType::STOPPING: + ESP_LOGD(TAG, "Stopping I2S Audio Speaker"); break; case TaskEventType::PLAYING: this->status_clear_warning(); @@ -170,6 +175,7 @@ void I2SAudioSpeaker::watch_() { this->player_task_handle_ = nullptr; this->parent_->unlock(); xQueueReset(this->buffer_queue_); + ESP_LOGD(TAG, "Stopped I2S Audio Speaker"); break; case TaskEventType::WARNING: ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err)); diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 448df61d80..12fbdc97b4 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -281,11 +281,14 @@ void VoiceAssistant::loop() { memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); this->speaker_buffer_size_ -= written; this->speaker_buffer_index_ -= written; - this->set_timeout("speaker-timeout", 1000, [this]() { this->speaker_->stop(); }); + this->set_timeout("speaker-timeout", 2000, [this]() { this->speaker_->stop(); }); } else { ESP_LOGW(TAG, "Speaker buffer full."); } } + if (this->wait_for_stream_end_) { + break; // We dont want to timeout here as the STREAM_END event will take care of that. + } playing = this->speaker_->is_running(); } #endif @@ -295,28 +298,77 @@ void VoiceAssistant::loop() { } #endif if (playing) { - this->set_timeout("playing", 100, [this]() { + this->set_timeout("playing", 2000, [this]() { this->cancel_timeout("speaker-timeout"); this->set_state_(State::IDLE, State::IDLE); }); } break; } + case State::RESPONSE_FINISHED: { +#ifdef USE_SPEAKER + if (this->speaker_ != nullptr) { + this->speaker_->stop(); + this->cancel_timeout("speaker-timeout"); + this->cancel_timeout("playing"); + this->speaker_buffer_size_ = 0; + this->speaker_buffer_index_ = 0; + memset(this->speaker_buffer_, 0, SPEAKER_BUFFER_SIZE); + } +#endif + this->wait_for_stream_end_ = false; + this->set_state_(State::IDLE, State::IDLE); + break; + } default: break; } } +static const LogString *voice_assistant_state_to_string(State state) { + switch (state) { + case State::IDLE: + return LOG_STR("IDLE"); + case State::START_MICROPHONE: + return LOG_STR("START_MICROPHONE"); + case State::STARTING_MICROPHONE: + return LOG_STR("STARTING_MICROPHONE"); + case State::WAIT_FOR_VAD: + return LOG_STR("WAIT_FOR_VAD"); + case State::WAITING_FOR_VAD: + return LOG_STR("WAITING_FOR_VAD"); + case State::START_PIPELINE: + return LOG_STR("START_PIPELINE"); + case State::STARTING_PIPELINE: + return LOG_STR("STARTING_PIPELINE"); + case State::STREAMING_MICROPHONE: + return LOG_STR("STREAMING_MICROPHONE"); + case State::STOP_MICROPHONE: + return LOG_STR("STOP_MICROPHONE"); + case State::STOPPING_MICROPHONE: + return LOG_STR("STOPPING_MICROPHONE"); + case State::AWAITING_RESPONSE: + return LOG_STR("AWAITING_RESPONSE"); + case State::STREAMING_RESPONSE: + return LOG_STR("STREAMING_RESPONSE"); + case State::RESPONSE_FINISHED: + return LOG_STR("RESPONSE_FINISHED"); + default: + return LOG_STR("UNKNOWN"); + } +}; + void VoiceAssistant::set_state_(State state) { State old_state = this->state_; this->state_ = state; - ESP_LOGD(TAG, "State changed from %d to %d", static_cast(old_state), static_cast(state)); + ESP_LOGD(TAG, "State changed from %s to %s", LOG_STR_ARG(voice_assistant_state_to_string(old_state)), + LOG_STR_ARG(voice_assistant_state_to_string(state))); } void VoiceAssistant::set_state_(State state, State desired_state) { this->set_state_(state); this->desired_state_ = desired_state; - ESP_LOGD(TAG, "Desired state set to %d", static_cast(desired_state)); + ESP_LOGD(TAG, "Desired state set to %s", LOG_STR_ARG(voice_assistant_state_to_string(desired_state))); } void VoiceAssistant::failed_to_start() { @@ -400,6 +452,7 @@ void VoiceAssistant::request_stop() { break; case State::AWAITING_RESPONSE: case State::STREAMING_RESPONSE: + case State::RESPONSE_FINISHED: break; // Let the incoming audio stream finish then it will go to idle. } } @@ -531,6 +584,14 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { this->error_trigger_->trigger(code, message); break; } + case api::enums::VOICE_ASSISTANT_TTS_STREAM_START: { + this->wait_for_stream_end_ = true; + break; + } + case api::enums::VOICE_ASSISTANT_TTS_STREAM_END: { + this->set_state_(State::RESPONSE_FINISHED, State::IDLE); + break; + } default: ESP_LOGD(TAG, "Unhandled event type: %d", msg.event_type); break; diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index ce22538a85..cd448293db 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -46,6 +46,7 @@ enum class State { STOPPING_MICROPHONE, AWAITING_RESPONSE, STREAMING_RESPONSE, + RESPONSE_FINISHED, }; class VoiceAssistant : public Component { @@ -132,10 +133,10 @@ class VoiceAssistant : public Component { uint8_t *speaker_buffer_; size_t speaker_buffer_index_{0}; size_t speaker_buffer_size_{0}; + bool wait_for_stream_end_{false}; #endif #ifdef USE_MEDIA_PLAYER media_player::MediaPlayer *media_player_{nullptr}; - bool playing_tts_{false}; #endif bool local_output_{false}; From fd7d3c4332537f58a2a52518c1bfefdca5513551 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:03:39 +1300 Subject: [PATCH 88/95] Fix esp32_improv authorizer with no binary sensors in config (#5546) --- esphome/components/esp32_improv/esp32_improv_component.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 5bdf7d19fe..8a901a79e5 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -107,6 +107,7 @@ void ESP32ImprovComponent::loop() { break; } case improv::STATE_AUTHORIZED: { +#ifdef USE_BINARY_SENSOR if (this->authorizer_ != nullptr) { if (now - this->authorized_start_ > this->authorized_duration_) { ESP_LOGD(TAG, "Authorization timeout"); @@ -114,6 +115,7 @@ void ESP32ImprovComponent::loop() { return; } } +#endif if (!this->check_identify_()) { this->set_status_indicator_state_((now % 1000) < 500); } @@ -290,8 +292,10 @@ void ESP32ImprovComponent::process_incoming_data_() { void ESP32ImprovComponent::on_wifi_connect_timeout_() { this->set_error_(improv::ERROR_UNABLE_TO_CONNECT); this->set_state_(improv::STATE_AUTHORIZED); +#ifdef USE_BINARY_SENSOR if (this->authorizer_ != nullptr) this->authorized_start_ = millis(); +#endif ESP_LOGW(TAG, "Timed out trying to connect to given WiFi network"); wifi::global_wifi_component->clear_sta(); } From 1f02096edb29095c8ee5462c4a46a705bba163eb Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:11:37 +1300 Subject: [PATCH 89/95] More voice assistant fixes (#5547) --- .../components/i2s_audio/microphone/i2s_audio_microphone.cpp | 2 ++ esphome/components/voice_assistant/voice_assistant.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 44c73eb8fd..ec2fe258c9 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -37,6 +37,8 @@ void I2SAudioMicrophone::setup() { void I2SAudioMicrophone::start() { if (this->is_failed()) return; + if (this->state_ == microphone::STATE_RUNNING) + return; // Already running this->state_ = microphone::STATE_STARTING; } void I2SAudioMicrophone::start_() { diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 12fbdc97b4..27dc201073 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -287,6 +287,7 @@ void VoiceAssistant::loop() { } } if (this->wait_for_stream_end_) { + this->cancel_timeout("playing"); break; // We dont want to timeout here as the STREAM_END event will take care of that. } playing = this->speaker_->is_running(); From 5e7ce610a030307ca4cbe9347c42c6dc6fbec7d9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:15:14 +1300 Subject: [PATCH 90/95] Bump version to 2023.10.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index cef733b2f9..cf267a2424 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.10.0b2" +__version__ = "2023.10.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From cc4c0e3e0bee6a88aa898e86bba48755b4c71d80 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 Oct 2023 07:23:34 +1300 Subject: [PATCH 91/95] Fix default libretiny manufacturer reported to HA (#5549) --- esphome/components/libretiny/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index b01d342a87..e36c08d522 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -251,7 +251,7 @@ async def component_to_code(config): # setup board config cg.add_platformio_option("board", config[CONF_BOARD]) cg.add_build_flag("-DUSE_LIBRETINY") - cg.add_build_flag(f"-DUSE_{config[CONF_COMPONENT_ID]}") + cg.add_build_flag(f"-DUSE_{config[CONF_COMPONENT_ID].upper()}") cg.add_build_flag(f"-DUSE_LIBRETINY_VARIANT_{config[CONF_FAMILY]}") cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) cg.add_define("ESPHOME_VARIANT", FAMILY_FRIENDLY[config[CONF_FAMILY]]) From 51688d40780f2ef203e701b4d224c3abee283c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Bl=C3=A4se?= Date: Tue, 17 Oct 2023 20:39:05 +0200 Subject: [PATCH 92/95] SML: fix incomplete sign extension for abbreviated transmissions (#5544) --- esphome/components/sml/sml_parser.cpp | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/esphome/components/sml/sml_parser.cpp b/esphome/components/sml/sml_parser.cpp index 91b320a30e..3b23522b21 100644 --- a/esphome/components/sml/sml_parser.cpp +++ b/esphome/components/sml/sml_parser.cpp @@ -88,11 +88,6 @@ uint64_t bytes_to_uint(const bytes &buffer) { for (auto const value : buffer) { val = (val << 8) + value; } - // Some smart meters send 24 bit signed integers. Sign extend to 64 bit if the - // 24 bit value is negative. - if (buffer.size() == 3 && buffer[0] & 0x80) { - val |= 0xFFFFFFFFFF000000; - } return val; } @@ -100,19 +95,15 @@ int64_t bytes_to_int(const bytes &buffer) { uint64_t tmp = bytes_to_uint(buffer); int64_t val; - switch (buffer.size()) { - case 1: // int8 - val = (int8_t) tmp; - break; - case 2: // int16 - val = (int16_t) tmp; - break; - case 4: // int32 - val = (int32_t) tmp; - break; - default: // int64 - val = (int64_t) tmp; + // sign extension for abbreviations of leading ones (e.g. 3 byte transmissions, see 6.2.2 of SML protocol definition) + // see https://stackoverflow.com/questions/42534749/signed-extension-from-24-bit-to-32-bit-in-c + if (buffer.size() < 8) { + const int bits = buffer.size() * 8; + const uint64_t m = 1u << (bits - 1); + tmp = (tmp ^ m) - m; } + + val = (int64_t) tmp; return val; } From 2189a40a39951f931b692a5f9aa8326ec41e3726 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:26:47 +1300 Subject: [PATCH 93/95] esp32_improv advertise capabilities and state in ble service data (#5553) --- .../components/esp32_ble/ble_advertising.cpp | 21 +++++++++++++------ .../components/esp32_ble/ble_advertising.h | 1 + .../esp32_improv/esp32_improv_component.cpp | 19 +++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/esphome/components/esp32_ble/ble_advertising.cpp b/esphome/components/esp32_ble/ble_advertising.cpp index 072bb38c07..59d2398829 100644 --- a/esphome/components/esp32_ble/ble_advertising.cpp +++ b/esphome/components/esp32_ble/ble_advertising.cpp @@ -2,9 +2,9 @@ #ifdef USE_ESP32 -#include "ble_uuid.h" -#include #include +#include +#include "ble_uuid.h" #include "esphome/core/log.h" namespace esphome { @@ -16,8 +16,8 @@ BLEAdvertising::BLEAdvertising() { this->advertising_data_.set_scan_rsp = false; this->advertising_data_.include_name = true; this->advertising_data_.include_txpower = true; - this->advertising_data_.min_interval = 0x20; - this->advertising_data_.max_interval = 0x40; + this->advertising_data_.min_interval = 0; + this->advertising_data_.max_interval = 0; this->advertising_data_.appearance = 0x00; this->advertising_data_.manufacturer_len = 0; this->advertising_data_.p_manufacturer_data = nullptr; @@ -42,6 +42,17 @@ void BLEAdvertising::remove_service_uuid(ESPBTUUID uuid) { this->advertising_uuids_.end()); } +void BLEAdvertising::set_service_data(const std::vector &data) { + delete[] this->advertising_data_.p_service_data; + this->advertising_data_.p_service_data = nullptr; + this->advertising_data_.service_data_len = data.size(); + if (!data.empty()) { + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + this->advertising_data_.p_service_data = new uint8_t[data.size()]; + memcpy(this->advertising_data_.p_service_data, data.data(), data.size()); + } +} + void BLEAdvertising::set_manufacturer_data(const std::vector &data) { delete[] this->advertising_data_.p_manufacturer_data; this->advertising_data_.p_manufacturer_data = nullptr; @@ -85,8 +96,6 @@ void BLEAdvertising::start() { this->scan_response_data_.set_scan_rsp = true; this->scan_response_data_.include_name = true; this->scan_response_data_.include_txpower = true; - this->scan_response_data_.min_interval = 0; - this->scan_response_data_.max_interval = 0; this->scan_response_data_.manufacturer_len = 0; this->scan_response_data_.appearance = 0; this->scan_response_data_.flag = 0; diff --git a/esphome/components/esp32_ble/ble_advertising.h b/esphome/components/esp32_ble/ble_advertising.h index 9e4e2b7701..16a7dd1d8e 100644 --- a/esphome/components/esp32_ble/ble_advertising.h +++ b/esphome/components/esp32_ble/ble_advertising.h @@ -21,6 +21,7 @@ class BLEAdvertising { void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; } void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; } void set_manufacturer_data(const std::vector &data); + void set_service_data(const std::vector &data); void start(); void stop(); diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 8a901a79e5..19340c3dd8 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -189,6 +189,25 @@ void ESP32ImprovComponent::set_state_(improv::State state) { if (state != improv::STATE_STOPPED) this->status_->notify(); } + std::vector service_data(8, 0); + service_data[0] = 0x77; // PR + service_data[1] = 0x46; // IM + service_data[2] = static_cast(state); + + uint8_t capabilities = 0x00; +#ifdef USE_OUTPUT + if (this->status_indicator_ != nullptr) + capabilities |= improv::CAPABILITY_IDENTIFY; +#endif + + service_data[3] = capabilities; + service_data[4] = 0x00; // Reserved + service_data[5] = 0x00; // Reserved + service_data[6] = 0x00; // Reserved + service_data[7] = 0x00; // Reserved + + esp32_ble::global_ble->get_advertising()->set_service_data(service_data); + esp32_ble::global_ble->get_advertising()->start(); } void ESP32ImprovComponent::set_error_(improv::Error error) { From 2aa787f5f0546fbb550ececaffe9d43f019b3053 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:28:03 +1300 Subject: [PATCH 94/95] Bump version to 2023.10.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index cf267a2424..fead0a5f1f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.10.0b3" +__version__ = "2023.10.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 582b8383d248cd1a8cfd17c3bba39b6341868c52 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:47:03 +1300 Subject: [PATCH 95/95] Bump version to 2023.10.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index fead0a5f1f..30e7fd7b8e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.10.0b4" +__version__ = "2023.10.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = (