From 0ebb6efee691a4330050073ce74d60a8d63d65a4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:33:42 +1300 Subject: [PATCH 0001/1106] Bump version to 2024.3.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 918cf94ed3..c5e86444ab 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.3.0-dev" +__version__ = "2024.3.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From e33e09a6859a7bc1bd11d163e8139b1019140e9f Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:42:54 +1100 Subject: [PATCH 0002/1106] SPI: Revert clk_pin to standard output pin schema (#6368) --- esphome/components/spi/__init__.py | 17 ++--------------- tests/components/spi/test.esp32-s3-idf.yaml | 3 ++- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index c2335bd92a..2847c5bfa1 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -29,7 +29,6 @@ from esphome.const import ( PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040, - CONF_ALLOW_OTHER_USES, CONF_DATA_PINS, ) from esphome.core import ( @@ -199,8 +198,6 @@ def get_hw_spi(config, available): def validate_spi_config(config): available = list(range(len(get_hw_interface_list()))) for spi in config: - # map pin number to schema - spi[CONF_CLK_PIN] = pins.gpio_output_pin_schema(spi[CONF_CLK_PIN]) interface = spi[CONF_INTERFACE] if interface == "software": pass @@ -257,21 +254,11 @@ def get_spi_interface(index): return "new SPIClass(HSPI)" -# Do not use a pin schema for the number, as that will trigger a pin reuse error due to duplication of the -# clock pin in the standard and quad schemas. -clk_pin_validator = cv.maybe_simple_value( - { - cv.Required(CONF_NUMBER): cv.Any(cv.int_, cv.string), - cv.Optional(CONF_ALLOW_OTHER_USES): cv.boolean, - }, - key=CONF_NUMBER, -) - SPI_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(SPIComponent), - cv.Required(CONF_CLK_PIN): clk_pin_validator, + cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema, cv.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_FORCE_SW): cv.invalid( @@ -291,7 +278,7 @@ SPI_QUAD_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(QuadSPIComponent), - cv.Required(CONF_CLK_PIN): clk_pin_validator, + cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_DATA_PINS): cv.All( cv.ensure_list(pins.internal_gpio_output_pin_number), cv.Length(min=4, max=4), diff --git a/tests/components/spi/test.esp32-s3-idf.yaml b/tests/components/spi/test.esp32-s3-idf.yaml index 3aae21cbeb..8db934023a 100644 --- a/tests/components/spi/test.esp32-s3-idf.yaml +++ b/tests/components/spi/test.esp32-s3-idf.yaml @@ -2,7 +2,8 @@ spi: - id: spi_id_1 type: single clk_pin: - number: GPIO7 + number: GPIO0 + ignore_strapping_warning: true allow_other_uses: false mosi_pin: GPIO6 interface: hardware From 76c4bfbed368850a63048c1ddfce4e631d3cef2e Mon Sep 17 00:00:00 2001 From: Attila Farago Date: Thu, 14 Mar 2024 03:56:17 +0100 Subject: [PATCH 0003/1106] Allow button press action in web_server to be executed via GET method (#5938) --- esphome/components/web_server/web_server.cpp | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index ba787239da..69d601ed49 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -486,7 +486,7 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { + if (request->method() == HTTP_GET && match.method.empty()) { std::string data = this->switch_json(obj, obj->state, DETAIL_STATE); request->send(200, "application/json", data.c_str()); } else if (match.method == "toggle") { @@ -517,7 +517,7 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM for (button::Button *obj : App.get_buttons()) { if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_POST && match.method == "press") { + if (match.method == "press") { this->schedule_([obj]() { obj->press(); }); request->send(200); return; @@ -572,7 +572,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { + if (request->method() == HTTP_GET && match.method.empty()) { std::string data = this->fan_json(obj, DETAIL_STATE); request->send(200, "application/json", data.c_str()); } else if (match.method == "toggle") { @@ -630,7 +630,7 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { + if (request->method() == HTTP_GET && match.method.empty()) { std::string data = this->light_json(obj, DETAIL_STATE); request->send(200, "application/json", data.c_str()); } else if (match.method == "toggle") { @@ -736,7 +736,7 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { + if (request->method() == HTTP_GET && match.method.empty()) { std::string data = this->cover_json(obj, DETAIL_STATE); request->send(200, "application/json", data.c_str()); continue; @@ -805,7 +805,7 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { + if (request->method() == HTTP_GET && match.method.empty()) { std::string data = this->number_json(obj, obj->state, DETAIL_STATE); request->send(200, "application/json", data.c_str()); return; @@ -910,7 +910,7 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { + if (request->method() == HTTP_GET && match.method.empty()) { std::string data = this->text_json(obj, obj->state, DETAIL_STATE); request->send(200, "text/json", data.c_str()); return; @@ -961,7 +961,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { + if (request->method() == HTTP_GET && match.method.empty()) { auto detail = DETAIL_STATE; auto *param = request->getParam("detail"); if (param && param->value() == "all") { @@ -1016,7 +1016,7 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { + if (request->method() == HTTP_GET && match.method.empty()) { std::string data = this->climate_json(obj, DETAIL_STATE); request->send(200, "application/json", data.c_str()); return; @@ -1162,7 +1162,7 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { + if (request->method() == HTTP_GET && match.method.empty()) { std::string data = this->lock_json(obj, obj->state, DETAIL_STATE); request->send(200, "application/json", data.c_str()); } else if (match.method == "lock") { @@ -1201,7 +1201,7 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { + if (request->method() == HTTP_GET && match.method.empty()) { std::string data = this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE); request->send(200, "application/json", data.c_str()); return; @@ -1251,7 +1251,7 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { #endif #ifdef USE_BUTTON - if (request->method() == HTTP_POST && match.domain == "button") + if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "button") return true; #endif From 92fbc61c46d4229e00bb5fbc42b0f22b55126330 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 13 Mar 2024 22:08:57 -0700 Subject: [PATCH 0004/1106] fix servo restore (#6370) --- esphome/components/servo/servo.cpp | 35 +++++++++++++++++++++++++----- esphome/components/servo/servo.h | 22 +++++-------------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/esphome/components/servo/servo.cpp b/esphome/components/servo/servo.cpp index 666c017dea..18e8c8087e 100644 --- a/esphome/components/servo/servo.cpp +++ b/esphome/components/servo/servo.cpp @@ -19,13 +19,28 @@ void Servo::dump_config() { ESP_LOGCONFIG(TAG, " run duration: %" PRIu32 " ms", this->transition_length_); } +void Servo::setup() { + float v; + if (this->restore_) { + this->rtc_ = global_preferences->make_preference(global_servo_id); + global_servo_id++; + if (this->rtc_.load(&v)) { + this->target_value_ = v; + this->internal_write(v); + this->state_ = STATE_ATTACHED; + this->start_millis_ = millis(); + return; + } + } + this->detach(); +} + void Servo::loop() { // check if auto_detach_time_ is set and servo reached target if (this->auto_detach_time_ && this->state_ == STATE_TARGET_REACHED) { if (millis() - this->start_millis_ > this->auto_detach_time_) { this->detach(); this->start_millis_ = 0; - this->state_ = STATE_DETACHED; ESP_LOGD(TAG, "Servo detached on auto_detach_time"); } } @@ -54,8 +69,11 @@ void Servo::loop() { void Servo::write(float value) { value = clamp(value, -1.0f, 1.0f); - if (this->target_value_ == value) + if ((this->state_ == STATE_DETACHED) && (this->target_value_ == value)) { this->internal_write(value); + } else { + this->save_level_(value); + } this->target_value_ = value; this->source_value_ = this->current_value_; this->state_ = STATE_ATTACHED; @@ -72,11 +90,18 @@ void Servo::internal_write(float value) { level = lerp(value, this->idle_level_, this->max_level_); } this->output_->set_level(level); - if (this->target_value_ == this->current_value_) { - this->save_level_(level); - } this->current_value_ = value; } +void Servo::detach() { + this->state_ = STATE_DETACHED; + this->output_->set_level(0.0f); +} + +void Servo::save_level_(float v) { + if (this->restore_) + this->rtc_.save(&v); +} + } // namespace servo } // namespace esphome diff --git a/esphome/components/servo/servo.h b/esphome/components/servo/servo.h index e2e3823158..13a7472ae5 100644 --- a/esphome/components/servo/servo.h +++ b/esphome/components/servo/servo.h @@ -17,22 +17,8 @@ class Servo : public Component { void loop() override; void write(float value); void internal_write(float value); - void detach() { - this->output_->set_level(0.0f); - this->save_level_(0.0f); - } - void setup() override { - float v; - if (this->restore_) { - this->rtc_ = global_preferences->make_preference(global_servo_id); - global_servo_id++; - if (this->rtc_.load(&v)) { - this->output_->set_level(v); - return; - } - } - this->detach(); - } + void detach(); + void setup() override; void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } void set_min_level(float min_level) { min_level_ = min_level; } @@ -42,8 +28,10 @@ class Servo : public Component { void set_auto_detach_time(uint32_t auto_detach_time) { auto_detach_time_ = auto_detach_time; } void set_transition_length(uint32_t transition_length) { transition_length_ = transition_length; } + bool has_reached_target() { return this->current_value_ == this->target_value_; } + protected: - void save_level_(float v) { this->rtc_.save(&v); } + void save_level_(float v); output::FloatOutput *output_; float min_level_ = 0.0300f; From ca85a41a724db49582e82b31d4e4a813dd272250 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Thu, 14 Mar 2024 21:26:29 +0100 Subject: [PATCH 0005/1106] Don't try to get IPv6 addresses when disabled (#6366) --- esphome/components/wifi/wifi_component_esp32_arduino.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index d7241fb66c..35e6c57e62 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -157,7 +157,7 @@ network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { } else { addresses[0] = network::IPAddress(&ip.ip); } -#if LWIP_IPV6 +#if USE_NETWORK_IPV6 ip6_addr_t ipv6; err = tcpip_adapter_get_ip6_global(TCPIP_ADAPTER_IF_STA, &ipv6); if (err != ESP_OK) { @@ -171,7 +171,7 @@ network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { } else { addresses[2] = network::IPAddress(&ipv6); } -#endif /* LWIP_IPV6 */ +#endif /* USE_NETWORK_IPV6 */ return addresses; } From 83cc7b9d48815d7da0c606ef26450344e575386f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:23:12 +1300 Subject: [PATCH 0006/1106] Bump version to 2024.3.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index c5e86444ab..4259c82ad7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.3.0b1" +__version__ = "2024.3.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From a5553827f1262742abb9921f68d849c321e57d43 Mon Sep 17 00:00:00 2001 From: "Federico G. Schwindt" Date: Sat, 16 Mar 2024 01:21:44 +0000 Subject: [PATCH 0007/1106] Use AQI device class (#6376) --- esphome/components/sen5x/sensor.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/esphome/components/sen5x/sensor.py b/esphome/components/sen5x/sensor.py index 4bc4a138a3..67bd627f7f 100644 --- a/esphome/components/sen5x/sensor.py +++ b/esphome/components/sen5x/sensor.py @@ -14,13 +14,12 @@ from esphome.const import ( CONF_PM_4_0, CONF_STORE_BASELINE, CONF_TEMPERATURE, + DEVICE_CLASS_AQI, DEVICE_CLASS_HUMIDITY, - DEVICE_CLASS_NITROUS_OXIDE, DEVICE_CLASS_PM1, DEVICE_CLASS_PM10, DEVICE_CLASS_PM25, DEVICE_CLASS_TEMPERATURE, - DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, ICON_CHEMICAL_WEAPON, ICON_RADIATOR, ICON_THERMOMETER, @@ -132,13 +131,13 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_VOC): sensor.sensor_schema( icon=ICON_RADIATOR, accuracy_decimals=0, - device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + device_class=DEVICE_CLASS_AQI, state_class=STATE_CLASS_MEASUREMENT, ).extend(GAS_SENSOR), cv.Optional(CONF_NOX): sensor.sensor_schema( icon=ICON_RADIATOR, accuracy_decimals=0, - device_class=DEVICE_CLASS_NITROUS_OXIDE, + device_class=DEVICE_CLASS_AQI, state_class=STATE_CLASS_MEASUREMENT, ).extend(GAS_SENSOR), cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean, From 4de58559c6bad70a61efb3a32e1b04f6e98f1474 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 16 Mar 2024 14:22:34 +1300 Subject: [PATCH 0008/1106] Fix list-components when PR is not targeting dev (#6375) --- .github/workflows/ci.yml | 15 ++++++++++----- script/helpers.py | 4 ++-- script/list-components.py | 11 ++++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3de879176..47709739dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -398,6 +398,7 @@ jobs: runs-on: ubuntu-latest needs: - common + if: github.event_name == 'pull_request' outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -406,10 +407,14 @@ jobs: with: # Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works. fetch-depth: 500 - - name: Fetch dev branch + - name: Get target branch + id: target-branch run: | - git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +refs/heads/dev*:refs/remotes/origin/dev* +refs/tags/dev*:refs/tags/dev* - git merge-base refs/remotes/origin/dev HEAD + echo "branch=${{ github.event.pull_request.base.ref }}" >> $GITHUB_OUTPUT + - name: Fetch ${{ steps.target-branch.outputs.branch }} branch + run: | + git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +refs/heads/${{ steps.target-branch.outputs.branch }}:refs/remotes/origin/${{ steps.target-branch.outputs.branch }} + git merge-base refs/remotes/origin/${{ steps.target-branch.outputs.branch }} HEAD - name: Restore Python uses: ./.github/actions/restore-python with: @@ -419,7 +424,7 @@ jobs: id: set-matrix run: | . venv/bin/activate - echo "matrix=$(script/list-components.py --changed | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT + echo "matrix=$(script/list-components.py --changed --branch ${{ steps.target-branch.outputs.branch }} | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT test-build-components: name: Component test ${{ matrix.file }} @@ -427,7 +432,7 @@ jobs: needs: - common - list-components - if: ${{ needs.list-components.outputs.matrix != '[]' && needs.list-components.outputs.matrix != '' }} + if: ${{ github.event_name == 'pull_request' && needs.list-components.outputs.matrix != '[]' && needs.list-components.outputs.matrix != '' }} strategy: fail-fast: false max-parallel: 2 diff --git a/script/helpers.py b/script/helpers.py index a971fdf475..52b0658fb6 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -70,11 +70,11 @@ def splitlines_no_ends(string): return [s.strip() for s in string.splitlines()] -def changed_files(): +def changed_files(branch="dev"): check_remotes = ["upstream", "origin"] check_remotes.extend(splitlines_no_ends(get_output("git", "remote"))) for remote in check_remotes: - command = ["git", "merge-base", f"refs/remotes/{remote}/dev", "HEAD"] + command = ["git", "merge-base", f"refs/remotes/{remote}/{branch}", "HEAD"] try: merge_base = splitlines_no_ends(get_output(*command))[0] break diff --git a/script/list-components.py b/script/list-components.py index 3e55c0e5f7..8e2d47c6b3 100755 --- a/script/list-components.py +++ b/script/list-components.py @@ -120,13 +120,22 @@ def main(): parser.add_argument( "-c", "--changed", action="store_true", help="Only run on changed files" ) + parser.add_argument( + "-b", "--branch", help="Branch to compare changed files against" + ) args = parser.parse_args() + if args.branch and not args.changed: + parser.error("--branch requires --changed") + files = git_ls_files() files = filter(filter_component_files, files) if args.changed: - changed = changed_files() + if args.branch: + changed = changed_files(args.branch) + else: + changed = changed_files() files = [f for f in files if f in changed] components = extract_component_names_array_from_files_array(files) From d121fa5d05604df28ab470a4fb79946eb3d8858b Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 17 Mar 2024 12:10:47 -0700 Subject: [PATCH 0009/1106] allow negative ppm for sensair (#6385) --- esphome/components/senseair/senseair.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/senseair/senseair.cpp b/esphome/components/senseair/senseair.cpp index e0504eb2b9..e58ee157f7 100644 --- a/esphome/components/senseair/senseair.cpp +++ b/esphome/components/senseair/senseair.cpp @@ -54,9 +54,9 @@ void SenseAirComponent::update() { this->status_clear_warning(); const uint8_t length = response[2]; const uint16_t status = (uint16_t(response[3]) << 8) | response[4]; - const uint16_t ppm = (uint16_t(response[length + 1]) << 8) | response[length + 2]; + const int16_t ppm = int16_t((response[length + 1] << 8) | response[length + 2]); - ESP_LOGD(TAG, "SenseAir Received CO₂=%uppm Status=0x%02X", ppm, status); + ESP_LOGD(TAG, "SenseAir Received CO₂=%dppm Status=0x%02X", ppm, status); if (this->co2_sensor_ != nullptr) this->co2_sensor_->publish_state(ppm); } From 9e378189c3157a548a6dc8ef335a6dfacf546fc6 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Sun, 17 Mar 2024 15:13:55 -0400 Subject: [PATCH 0010/1106] microWakeWord - add new ops and small improvements (#6360) --- .../micro_wake_word/micro_wake_word.cpp | 66 ++++++++----------- .../micro_wake_word/micro_wake_word.h | 3 +- 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/esphome/components/micro_wake_word/micro_wake_word.cpp b/esphome/components/micro_wake_word/micro_wake_word.cpp index 7321e5b05b..f637f8b2bb 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.cpp +++ b/esphome/components/micro_wake_word/micro_wake_word.cpp @@ -93,11 +93,18 @@ int MicroWakeWord::read_microphone_() { return 0; } - size_t bytes_written = this->ring_buffer_->write((void *) this->input_buffer_, bytes_read); - if (bytes_written != bytes_read) { - ESP_LOGW(TAG, "Failed to write some data to ring buffer (written=%d, expected=%d)", bytes_written, bytes_read); + size_t bytes_free = this->ring_buffer_->free(); + + if (bytes_free < bytes_read) { + ESP_LOGW(TAG, + "Not enough free bytes in ring buffer to store incoming audio data (free bytes=%d, incoming bytes=%d). " + "Resetting the ring buffer. Wake word detection accuracy will be reduced.", + bytes_free, bytes_read); + + this->ring_buffer_->reset(); } - return bytes_written; + + return this->ring_buffer_->write((void *) this->input_buffer_, bytes_read); } void MicroWakeWord::loop() { @@ -206,12 +213,6 @@ bool MicroWakeWord::initialize_models() { return false; } - this->preprocessor_stride_buffer_ = audio_samples_allocator.allocate(HISTORY_SAMPLES_TO_KEEP); - if (this->preprocessor_stride_buffer_ == nullptr) { - ESP_LOGE(TAG, "Could not allocate the audio preprocessor's stride buffer."); - return false; - } - this->preprocessor_model_ = tflite::GetModel(G_AUDIO_PREPROCESSOR_INT8_TFLITE); if (this->preprocessor_model_->version() != TFLITE_SCHEMA_VERSION) { ESP_LOGE(TAG, "Wake word's audio preprocessor model's schema is not supported"); @@ -225,7 +226,7 @@ bool MicroWakeWord::initialize_models() { } static tflite::MicroMutableOpResolver<18> preprocessor_op_resolver; - static tflite::MicroMutableOpResolver<14> streaming_op_resolver; + static tflite::MicroMutableOpResolver<17> streaming_op_resolver; if (!this->register_preprocessor_ops_(preprocessor_op_resolver)) return false; @@ -329,7 +330,6 @@ bool MicroWakeWord::detect_wake_word_() { } // Perform inference - uint32_t streaming_size = micros(); float streaming_prob = this->perform_streaming_inference_(); // Add the most recent probability to the sliding window @@ -357,6 +357,9 @@ bool MicroWakeWord::detect_wake_word_() { for (auto &prob : this->recent_streaming_probabilities_) { prob = 0; } + + ESP_LOGD(TAG, "Wake word sliding average probability is %.3f and most recent probability is %.3f", + sliding_window_average, streaming_prob); return true; } @@ -371,23 +374,6 @@ void MicroWakeWord::set_sliding_window_average_size(size_t size) { bool MicroWakeWord::slice_available_() { size_t available = this->ring_buffer_->available(); - size_t free = this->ring_buffer_->free(); - - if (free < NEW_SAMPLES_TO_GET * sizeof(int16_t)) { - // If the ring buffer is within one audio slice of being full, then wake word detection will have issues. - // If this is constantly occuring, then some possibilities why are - // 1) there are too many other slow components configured - // 2) the ESP32 isn't fast enough; e.g., an ESP32 is much slower than an ESP32-S3 at inferences. - // 3) the model is too large - // 4) the model uses operations that are not optimized - ESP_LOGW(TAG, - "Audio buffer is nearly full. Wake word detection may be less accurate and have slower reponse times. " -#if !defined(USE_ESP32_VARIANT_ESP32S3) - "microWakeWord is designed for the ESP32-S3. The current platform is too slow for this model." -#endif - ); - } - return available > (NEW_SAMPLES_TO_GET * sizeof(int16_t)); } @@ -396,13 +382,12 @@ bool MicroWakeWord::stride_audio_samples_(int16_t **audio_samples) { return false; } - // Copy 320 bytes (160 samples over 10 ms) into preprocessor_audio_buffer_ from history in - // preprocessor_stride_buffer_ - memcpy((void *) (this->preprocessor_audio_buffer_), (void *) (this->preprocessor_stride_buffer_), + // Copy the last 320 bytes (160 samples over 10 ms) from the audio buffer to the start of the audio buffer + memcpy((void *) (this->preprocessor_audio_buffer_), (void *) (this->preprocessor_audio_buffer_ + NEW_SAMPLES_TO_GET), HISTORY_SAMPLES_TO_KEEP * sizeof(int16_t)); - // Copy 640 bytes (320 samples over 20 ms) from the ring buffer - // The first 320 bytes (160 samples over 10 ms) will be from history + // Copy 640 bytes (320 samples over 20 ms) from the ring buffer into the audio buffer offset 320 bytes (160 samples + // over 10 ms) size_t bytes_read = this->ring_buffer_->read((void *) (this->preprocessor_audio_buffer_ + HISTORY_SAMPLES_TO_KEEP), NEW_SAMPLES_TO_GET * sizeof(int16_t), pdMS_TO_TICKS(200)); @@ -415,11 +400,6 @@ bool MicroWakeWord::stride_audio_samples_(int16_t **audio_samples) { return false; } - // Copy the last 320 bytes (160 samples over 10 ms) from the audio buffer into history stride buffer for the next - // iteration - memcpy((void *) (this->preprocessor_stride_buffer_), (void *) (this->preprocessor_audio_buffer_ + NEW_SAMPLES_TO_GET), - HISTORY_SAMPLES_TO_KEEP * sizeof(int16_t)); - *audio_samples = this->preprocessor_audio_buffer_; return true; } @@ -480,7 +460,7 @@ bool MicroWakeWord::register_preprocessor_ops_(tflite::MicroMutableOpResolver<18 return true; } -bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<14> &op_resolver) { +bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<17> &op_resolver) { if (op_resolver.AddCallOnce() != kTfLiteOk) return false; if (op_resolver.AddVarHandle() != kTfLiteOk) @@ -509,6 +489,12 @@ bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<14> & return false; if (op_resolver.AddQuantize() != kTfLiteOk) return false; + if (op_resolver.AddDepthwiseConv2D() != kTfLiteOk) + return false; + if (op_resolver.AddAveragePool2D() != kTfLiteOk) + return false; + if (op_resolver.AddMaxPool2D() != kTfLiteOk) + return false; return true; } diff --git a/esphome/components/micro_wake_word/micro_wake_word.h b/esphome/components/micro_wake_word/micro_wake_word.h index 27d05c3e09..1d7c18d686 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.h +++ b/esphome/components/micro_wake_word/micro_wake_word.h @@ -128,7 +128,6 @@ class MicroWakeWord : public Component { // Stores audio fed into feature generator preprocessor int16_t *preprocessor_audio_buffer_; - int16_t *preprocessor_stride_buffer_; bool detected_{false}; @@ -181,7 +180,7 @@ class MicroWakeWord : public Component { bool register_preprocessor_ops_(tflite::MicroMutableOpResolver<18> &op_resolver); /// @brief Returns true if successfully registered the streaming model's TensorFlow operations - bool register_streaming_ops_(tflite::MicroMutableOpResolver<14> &op_resolver); + bool register_streaming_ops_(tflite::MicroMutableOpResolver<17> &op_resolver); }; template class StartAction : public Action, public Parented { From 3908a9ce9da17572d2176d85874ebd52406ad567 Mon Sep 17 00:00:00 2001 From: Stefan Rado <628587+kroimon@users.noreply.github.com> Date: Sun, 17 Mar 2024 22:00:07 +0100 Subject: [PATCH 0011/1106] Fix compilation for uponor_smatrix without time component (#6389) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/uponor_smatrix/uponor_smatrix.cpp | 2 ++ esphome/components/uponor_smatrix/uponor_smatrix.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/esphome/components/uponor_smatrix/uponor_smatrix.cpp b/esphome/components/uponor_smatrix/uponor_smatrix.cpp index 10cd787c7f..df81691fb1 100644 --- a/esphome/components/uponor_smatrix/uponor_smatrix.cpp +++ b/esphome/components/uponor_smatrix/uponor_smatrix.cpp @@ -61,9 +61,11 @@ void UponorSmatrixComponent::loop() { // Send packets during bus silence if ((now - this->last_rx_ > 300) && (now - this->last_poll_start_ < 9500) && (now - this->last_tx_ > 200)) { +#ifdef USE_TIME // Only build time packet when bus is silent and queue is empty to make sure we can send it right away if (this->send_time_requested_ && this->tx_queue_.empty() && this->do_send_time_()) this->send_time_requested_ = false; +#endif // Send the next packet in the queue if (!this->tx_queue_.empty()) { auto packet = std::move(this->tx_queue_.front()); diff --git a/esphome/components/uponor_smatrix/uponor_smatrix.h b/esphome/components/uponor_smatrix/uponor_smatrix.h index b6675199b5..b7667b5b87 100644 --- a/esphome/components/uponor_smatrix/uponor_smatrix.h +++ b/esphome/components/uponor_smatrix/uponor_smatrix.h @@ -4,6 +4,8 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" +#include "esphome/core/defines.h" + #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" #include "esphome/core/time.h" From 22f427165fa8fc0e9407c66db682d3b005f54d43 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 17 Mar 2024 22:01:25 +0100 Subject: [PATCH 0012/1106] Shows component operation time in `ms` (#6388) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/core/component.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index b0406e6502..d737ec0ae3 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -5,6 +5,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" #include +#include namespace esphome { @@ -211,8 +212,8 @@ WarnIfComponentBlockingGuard::~WarnIfComponentBlockingGuard() { uint32_t now = millis(); if (now - started_ > 50) { const char *src = component_ == nullptr ? "" : component_->get_component_source(); - ESP_LOGW(TAG, "Component %s took a long time for an operation (%.2f s).", src, (now - started_) / 1e3f); - ESP_LOGW(TAG, "Components should block for at most 20-30ms."); + ESP_LOGW(TAG, "Component %s took a long time for an operation (%" PRIu32 " ms).", src, (now - started_)); + ESP_LOGW(TAG, "Components should block for at most 30 ms."); ; } } From 4429e5ae5612d3e0b5acf459ea213f4762ab106d Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Sun, 17 Mar 2024 22:06:02 +0100 Subject: [PATCH 0013/1106] IPv6 can't be enabled for libretiny (#6387) --- esphome/components/network/__init__.py | 38 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 4e87ff1c12..36144ff0a4 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -6,6 +6,9 @@ from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.const import ( CONF_ENABLE_IPV6, CONF_MIN_IPV6_ADDR_COUNT, + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_RP2040, ) CODEOWNERS = ["@esphome/core"] @@ -16,25 +19,30 @@ IPAddress = network_ns.class_("IPAddress") CONFIG_SCHEMA = cv.Schema( { - cv.Optional(CONF_ENABLE_IPV6, default=False): cv.boolean, + cv.SplitDefault(CONF_ENABLE_IPV6): cv.All( + cv.boolean, cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]) + ), cv.Optional(CONF_MIN_IPV6_ADDR_COUNT, default=0): cv.positive_int, } ) async def to_code(config): - cg.add_define("USE_NETWORK_IPV6", config[CONF_ENABLE_IPV6]) - cg.add_define("USE_NETWORK_MIN_IPV6_ADDR_COUNT", config[CONF_MIN_IPV6_ADDR_COUNT]) - if CORE.using_esp_idf: - add_idf_sdkconfig_option("CONFIG_LWIP_IPV6", config[CONF_ENABLE_IPV6]) - add_idf_sdkconfig_option( - "CONFIG_LWIP_IPV6_AUTOCONFIG", config[CONF_ENABLE_IPV6] + if CONF_ENABLE_IPV6 in config: + cg.add_define("USE_NETWORK_IPV6", config[CONF_ENABLE_IPV6]) + cg.add_define( + "USE_NETWORK_MIN_IPV6_ADDR_COUNT", config[CONF_MIN_IPV6_ADDR_COUNT] ) - else: - if config[CONF_ENABLE_IPV6]: - cg.add_build_flag("-DCONFIG_LWIP_IPV6") - cg.add_build_flag("-DCONFIG_LWIP_IPV6_AUTOCONFIG") - if CORE.is_rp2040: - cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_ENABLE_IPV6") - if CORE.is_esp8266: - cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_LOW_MEMORY") + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_LWIP_IPV6", config[CONF_ENABLE_IPV6]) + add_idf_sdkconfig_option( + "CONFIG_LWIP_IPV6_AUTOCONFIG", config[CONF_ENABLE_IPV6] + ) + else: + if config[CONF_ENABLE_IPV6]: + cg.add_build_flag("-DCONFIG_LWIP_IPV6") + cg.add_build_flag("-DCONFIG_LWIP_IPV6_AUTOCONFIG") + if CORE.is_rp2040: + cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_ENABLE_IPV6") + if CORE.is_esp8266: + cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_LOW_MEMORY") From 690a7d46cebec6d02df9d75277b492a9360b5c08 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Mar 2024 13:00:59 +1300 Subject: [PATCH 0014/1106] Replace name and friendly name in full adopted configs (#4456) --- .../components/dashboard_import/__init__.py | 130 +++++++++--------- esphome/dashboard/web_server.py | 3 +- requirements.txt | 1 + 3 files changed, 68 insertions(+), 66 deletions(-) diff --git a/esphome/components/dashboard_import/__init__.py b/esphome/components/dashboard_import/__init__.py index e0994be6a0..b1b22b816b 100644 --- a/esphome/components/dashboard_import/__init__.py +++ b/esphome/components/dashboard_import/__init__.py @@ -2,8 +2,10 @@ import base64 import secrets from pathlib import Path from typing import Optional +import re import requests +from ruamel.yaml import YAML import esphome.codegen as cg import esphome.config_validation as cv @@ -11,7 +13,6 @@ import esphome.final_validate as fv from esphome import git from esphome.components.packages import validate_source_shorthand from esphome.const import CONF_REF, CONF_WIFI, CONF_ESPHOME, CONF_PROJECT -from esphome.wizard import wizard_file from esphome.yaml_util import dump dashboard_import_ns = cg.esphome_ns.namespace("dashboard_import") @@ -94,75 +95,74 @@ def import_config( if p.exists(): raise FileExistsError - if project_name == "esphome.web": - if "esp32c3" in import_url: - board = "esp32-c3-devkitm-1" - platform = "ESP32" - elif "esp32s2" in import_url: - board = "esp32-s2-saola-1" - platform = "ESP32" - elif "esp32s3" in import_url: - board = "esp32-s3-devkitc-1" - platform = "ESP32" - elif "esp32" in import_url: - board = "esp32dev" - platform = "ESP32" - elif "esp8266" in import_url: - board = "esp01_1m" - platform = "ESP8266" - elif "pico-w" in import_url: - board = "pico-w" - platform = "RP2040" + git_file = git.GitFile.from_shorthand(import_url) - kwargs = { - "name": name, - "friendly_name": friendly_name, - "platform": platform, - "board": board, - "ssid": "!secret wifi_ssid", - "psk": "!secret wifi_password", + if git_file.query and "full_config" in git_file.query: + url = git_file.raw_url + try: + req = requests.get(url, timeout=30) + req.raise_for_status() + except requests.exceptions.RequestException as e: + raise ValueError(f"Error while fetching {url}: {e}") from e + + contents = req.text + yaml = YAML() + loaded_yaml = yaml.load(contents) + if ( + "name_add_mac_suffix" in loaded_yaml["esphome"] + and loaded_yaml["esphome"]["name_add_mac_suffix"] + ): + loaded_yaml["esphome"]["name_add_mac_suffix"] = False + name_val = loaded_yaml["esphome"]["name"] + sub_pattern = re.compile(r"\$\{?([a-zA-Z-_]+)\}?") + if match := sub_pattern.match(name_val): + name_sub = match.group(1) + if name_sub in loaded_yaml["substitutions"]: + loaded_yaml["substitutions"][name_sub] = name + else: + raise ValueError( + f"Name substitution {name_sub} not found in substitutions" + ) + else: + loaded_yaml["esphome"]["name"] = name + if friendly_name is not None: + friendly_name_val = loaded_yaml["esphome"]["friendly_name"] + if match := sub_pattern.match(friendly_name_val): + friendly_name_sub = match.group(1) + if friendly_name_sub in loaded_yaml["substitutions"]: + loaded_yaml["substitutions"][friendly_name_sub] = friendly_name + else: + raise ValueError( + f"Friendly name substitution {friendly_name_sub} not found in substitutions" + ) + else: + loaded_yaml["esphome"]["friendly_name"] = friendly_name + + with p.open("w", encoding="utf8") as f: + yaml.dump(loaded_yaml, f) + else: + with p.open("w", encoding="utf8") as f: + f.write(contents) + + else: + substitutions = {"name": name} + esphome_core = {"name": "${name}", "name_add_mac_suffix": False} + if friendly_name: + substitutions["friendly_name"] = friendly_name + esphome_core["friendly_name"] = "${friendly_name}" + config = { + "substitutions": substitutions, + "packages": {project_name: import_url}, + "esphome": esphome_core, } if encryption: noise_psk = secrets.token_bytes(32) key = base64.b64encode(noise_psk).decode() - kwargs["api_encryption_key"] = key + config["api"] = {"encryption": {"key": key}} - p.write_text( - wizard_file(**kwargs), - encoding="utf8", - ) - else: - git_file = git.GitFile.from_shorthand(import_url) + output = dump(config) - if git_file.query and "full_config" in git_file.query: - url = git_file.raw_url - try: - req = requests.get(url, timeout=30) - req.raise_for_status() - except requests.exceptions.RequestException as e: - raise ValueError(f"Error while fetching {url}: {e}") from e + if network == CONF_WIFI: + output += WIFI_CONFIG - p.write_text(req.text, encoding="utf8") - - else: - substitutions = {"name": name} - esphome_core = {"name": "${name}", "name_add_mac_suffix": False} - if friendly_name: - substitutions["friendly_name"] = friendly_name - esphome_core["friendly_name"] = "${friendly_name}" - config = { - "substitutions": substitutions, - "packages": {project_name: import_url}, - "esphome": esphome_core, - } - if encryption: - noise_psk = secrets.token_bytes(32) - key = base64.b64encode(noise_psk).decode() - config["api"] = {"encryption": {"key": key}} - - output = dump(config) - - if network == CONF_WIFI: - output += WIFI_CONFIG - - p.write_text(output, encoding="utf8") + p.write_text(output, encoding="utf8") diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index de3fe6e8ae..3de1d69115 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -516,7 +516,8 @@ class ImportRequestHandler(BaseHandler): self.set_status(500) self.write("File already exists") return - except ValueError: + except ValueError as e: + _LOGGER.error(e) self.set_status(422) self.write("Invalid package url") return diff --git a/requirements.txt b/requirements.txt index 32955bf6e7..9b5e06fc59 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,6 +16,7 @@ esphome-dashboard==20231107.0 aioesphomeapi==23.1.1 zeroconf==0.131.0 python-magic==0.4.27 +ruamel.yaml==0.18.6 # dashboard_import # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From 8a8bfe01c7e16295de9a8256b811b1c6f1a500ee Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Mar 2024 15:54:39 +1300 Subject: [PATCH 0015/1106] Bump version to 2024.3.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 4259c82ad7..60ea6f0423 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.3.0b2" +__version__ = "2024.3.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From b3aa950c60714a4e49028946656588611761def7 Mon Sep 17 00:00:00 2001 From: swoboda1337 <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 18 Mar 2024 02:35:06 -0400 Subject: [PATCH 0016/1106] Fix bug in `remote_base` conditional (#6281) Co-authored-by: Jonathan Swoboda --- esphome/components/remote_base/remote_base.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index 095f95053f..f3e86aaab6 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -16,7 +16,7 @@ RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_b } void RemoteRMTChannel::config_rmt(rmt_config_t &rmt) { - if (rmt_channel_t(int(this->channel_) + this->mem_block_num_) >= RMT_CHANNEL_MAX) { + if (rmt_channel_t(int(this->channel_) + this->mem_block_num_) > RMT_CHANNEL_MAX) { this->mem_block_num_ = int(RMT_CHANNEL_MAX) - int(this->channel_); ESP_LOGW(TAG, "Not enough RMT memory blocks available, reduced to %i blocks.", this->mem_block_num_); } From 9442f7a2713d6ed324fbdf4ef6da70a3ca563fb6 Mon Sep 17 00:00:00 2001 From: Stefan Rado <628587+kroimon@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:04:53 +0100 Subject: [PATCH 0017/1106] Fix sending packets to uponor_smatrix devices (#6392) --- esphome/components/uponor_smatrix/uponor_smatrix.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/uponor_smatrix/uponor_smatrix.cpp b/esphome/components/uponor_smatrix/uponor_smatrix.cpp index df81691fb1..a7014dc96c 100644 --- a/esphome/components/uponor_smatrix/uponor_smatrix.cpp +++ b/esphome/components/uponor_smatrix/uponor_smatrix.cpp @@ -173,7 +173,9 @@ bool UponorSmatrixComponent::send(uint16_t device_address, const UponorSmatrixDa return false; // Assemble packet for send queue. All fields are big-endian except for the little-endian checksum. - std::vector packet(6 + 3 * data_len); + std::vector packet; + packet.reserve(6 + 3 * data_len); + packet.push_back(this->address_ >> 8); packet.push_back(this->address_ >> 0); packet.push_back(device_address >> 8); From db1b187e80029ab9aad61fe347d24fe3fcd24dfe Mon Sep 17 00:00:00 2001 From: Stefan Rado <628587+kroimon@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:06:17 +0100 Subject: [PATCH 0018/1106] Fix wrong initialization of vectors in ade7953_i2c (#6393) --- .../components/ade7953_i2c/ade7953_i2c.cpp | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/esphome/components/ade7953_i2c/ade7953_i2c.cpp b/esphome/components/ade7953_i2c/ade7953_i2c.cpp index 572337428a..ae381824db 100644 --- a/esphome/components/ade7953_i2c/ade7953_i2c.cpp +++ b/esphome/components/ade7953_i2c/ade7953_i2c.cpp @@ -13,29 +13,29 @@ void AdE7953I2c::dump_config() { ade7953_base::ADE7953::dump_config(); } bool AdE7953I2c::ade_write_8(uint16_t reg, uint8_t value) { - std::vector data(3); - data.push_back(reg >> 8); - data.push_back(reg >> 0); - data.push_back(value); - return this->write(data.data(), data.size()) != i2c::ERROR_OK; + uint8_t data[3]; + data[0] = reg >> 8; + data[1] = reg >> 0; + data[2] = value; + return this->write(data, 3) != i2c::ERROR_OK; } bool AdE7953I2c::ade_write_16(uint16_t reg, uint16_t value) { - std::vector data(4); - data.push_back(reg >> 8); - data.push_back(reg >> 0); - data.push_back(value >> 8); - data.push_back(value >> 0); - return this->write(data.data(), data.size()) != i2c::ERROR_OK; + uint8_t data[4]; + data[0] = reg >> 8; + data[1] = reg >> 0; + data[2] = value >> 8; + data[3] = value >> 0; + return this->write(data, 4) != i2c::ERROR_OK; } bool AdE7953I2c::ade_write_32(uint16_t reg, uint32_t value) { - std::vector data(6); - data.push_back(reg >> 8); - data.push_back(reg >> 0); - data.push_back(value >> 24); - data.push_back(value >> 16); - data.push_back(value >> 8); - data.push_back(value >> 0); - return this->write(data.data(), data.size()) != i2c::ERROR_OK; + uint8_t data[6]; + data[0] = reg >> 8; + data[1] = reg >> 0; + data[2] = value >> 24; + data[3] = value >> 16; + data[4] = value >> 8; + data[5] = value >> 0; + return this->write(data, 6) != i2c::ERROR_OK; } bool AdE7953I2c::ade_read_8(uint16_t reg, uint8_t *value) { uint8_t reg_data[2]; From a3bd8ad02526a831bc0ab52e76ddfcec78f39440 Mon Sep 17 00:00:00 2001 From: Mike La Spina Date: Mon, 18 Mar 2024 13:26:39 -0500 Subject: [PATCH 0019/1106] ld2420: Firmware v1.5.4+ bug workaround (#6168) --- esphome/components/ld2420/ld2420.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/esphome/components/ld2420/ld2420.cpp b/esphome/components/ld2420/ld2420.cpp index bda1764cfc..bf1412ee9f 100644 --- a/esphome/components/ld2420/ld2420.cpp +++ b/esphome/components/ld2420/ld2420.cpp @@ -211,10 +211,11 @@ void LD2420Component::factory_reset_action() { void LD2420Component::restart_module_action() { ESP_LOGCONFIG(TAG, "Restarting LD2420 module..."); this->send_module_restart(); - delay_microseconds_safe(45000); - this->set_config_mode(true); - this->set_system_mode(system_mode_); - this->set_config_mode(false); + this->set_timeout(250, [this]() { + this->set_config_mode(true); + this->set_system_mode(system_mode_); + this->set_config_mode(false); + }); ESP_LOGCONFIG(TAG, "LD2420 Restarted."); } @@ -527,18 +528,16 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) { this->write_byte(cmd_buffer[index]); } - delay_microseconds_safe(500); // give the module a moment to process it error = 0; if (frame.command == CMD_RESTART) { - delay_microseconds_safe(25000); // Wait for the restart - return 0; // restart does not reply exit now + return 0; // restart does not reply exit now } while (!this->cmd_reply_.ack) { while (available()) { this->readline_(read(), ack_buffer, sizeof(ack_buffer)); } - delay_microseconds_safe(250); + delay_microseconds_safe(1450); if (loop_count <= 0) { error = LD2420_ERROR_TIMEOUT; retry--; From c56c40cb824e34ed2b89ba1cb8a3a5eb31459c74 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 19 Mar 2024 13:39:01 +1300 Subject: [PATCH 0020/1106] Require xsrf/csrf when using a password (#6396) --- esphome/dashboard/web_server.py | 6 ++++++ requirements.txt | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 3de1d69115..9ee2312781 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -688,6 +688,11 @@ class MainRequestHandler(BaseHandler): @authenticated def get(self) -> None: begin = bool(self.get_argument("begin", False)) + if settings.using_password: + # Simply accessing the xsrf_token sets the cookie for us + self.xsrf_token # pylint: disable=pointless-statement + else: + self.clear_cookie("_xsrf") self.render( "index.template.html", @@ -1102,6 +1107,7 @@ def make_app(debug=get_bool_env(ENV_DEV)) -> tornado.web.Application: "log_function": log_function, "websocket_ping_interval": 30.0, "template_path": get_base_frontend_path(), + "xsrf_cookies": settings.using_password, } rel = settings.relative_url return tornado.web.Application( diff --git a/requirements.txt b/requirements.txt index 9b5e06fc59..4b7e501e97 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.13 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20231107.0 +esphome-dashboard==20240319.0 aioesphomeapi==23.1.1 zeroconf==0.131.0 python-magic==0.4.27 From 855b1fd7062e1fabfe60c57bad08749aa7c1f390 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:22:28 +1300 Subject: [PATCH 0021/1106] Bump version to 2024.3.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 60ea6f0423..2b5724c9f3 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.3.0b3" +__version__ = "2024.3.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 507568db647d24a58e765a256a43ca04dcc5aeb3 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:53:01 +1100 Subject: [PATCH 0022/1106] AHT10: Use state machine to avoid blocking delay (#6401) --- esphome/components/aht10/aht10.cpp | 107 +++++++++++++++-------------- esphome/components/aht10/aht10.h | 5 ++ 2 files changed, 61 insertions(+), 51 deletions(-) diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index 57102e6d27..d5af06c2a2 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -36,6 +36,7 @@ static const uint8_t AHT10_INIT_ATTEMPTS = 10; static const uint8_t AHT10_STATUS_BUSY = 0x80; void AHT10Component::setup() { + this->read_delay_ = this->humidity_sensor_ != nullptr ? AHT10_HUMIDITY_DELAY : AHT10_DEFAULT_DELAY; if (this->write(AHT10_SOFTRESET_CMD, sizeof(AHT10_SOFTRESET_CMD)) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Reset AHT10 failed!"); } @@ -83,74 +84,78 @@ void AHT10Component::setup() { ESP_LOGV(TAG, "AHT10 initialization"); } -void AHT10Component::update() { - if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with AHT10 failed!"); - this->status_set_warning(); - return; - } - uint8_t data[6]; - uint8_t delay_ms = AHT10_DEFAULT_DELAY; - if (this->humidity_sensor_ != nullptr) - delay_ms = AHT10_HUMIDITY_DELAY; - bool success = false; - for (int i = 0; i < AHT10_ATTEMPTS; ++i) { - ESP_LOGVV(TAG, "Attempt %d at %6" PRIu32, i, millis()); - delay(delay_ms); - if (this->read(data, 6) != i2c::ERROR_OK) { - ESP_LOGD(TAG, "Communication with AHT10 failed, waiting..."); - continue; - } - - if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy - ESP_LOGD(TAG, "AHT10 is busy, waiting..."); - } else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) { - // Unrealistic humidity (0x0) - if (this->humidity_sensor_ == nullptr) { - ESP_LOGVV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required"); - break; - } else { - ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying..."); - if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with AHT10 failed!"); - this->status_set_warning(); - return; - } - } - } else { - // data is valid, we can break the loop - ESP_LOGVV(TAG, "Answer at %6" PRIu32, millis()); - success = true; - break; - } - } - if (!success || (data[0] & 0x80) == 0x80) { +void AHT10Component::restart_read_() { + if (this->read_count_ == AHT10_ATTEMPTS) { + this->read_count_ = 0; ESP_LOGE(TAG, "Measurements reading timed-out!"); - this->status_set_warning(); + this->status_set_error(); + return; + } + this->read_count_++; + this->set_timeout(this->read_delay_, [this]() { this->read_data_(); }); +} + +void AHT10Component::read_data_() { + uint8_t data[6]; + ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_)); + if (this->read(data, 6) != i2c::ERROR_OK) { + ESP_LOGD(TAG, "Communication with AHT10 failed, waiting..."); + this->restart_read_(); return; } + if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy + ESP_LOGD(TAG, "AHT10 is busy, waiting..."); + this->restart_read_(); + return; + } + if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) { + // Unrealistic humidity (0x0) + if (this->humidity_sensor_ == nullptr) { + ESP_LOGV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required"); + } else { + ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying..."); + if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "Communication with AHT10 failed!"); + this->status_set_warning(); + } + this->restart_read_(); + return; + } + } + ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_)); uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]; uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4; - float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f; - float humidity; - if (raw_humidity == 0) { // unrealistic value - humidity = NAN; - } else { - humidity = (float) raw_humidity * 100.0f / 1048576.0f; - } - if (this->temperature_sensor_ != nullptr) { + float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f; this->temperature_sensor_->publish_state(temperature); } if (this->humidity_sensor_ != nullptr) { + float humidity; + if (raw_humidity == 0) { // unrealistic value + humidity = NAN; + } else { + humidity = (float) raw_humidity * 100.0f / 1048576.0f; + } if (std::isnan(humidity)) { ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum"); } this->humidity_sensor_->publish_state(humidity); } this->status_clear_warning(); + this->read_count_ = 0; +} +void AHT10Component::update() { + if (this->read_count_ != 0) + return; + this->start_time_ = millis(); + if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "Communication with AHT10 failed!"); + this->status_set_warning(); + return; + } + this->restart_read_(); } float AHT10Component::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/aht10/aht10.h b/esphome/components/aht10/aht10.h index 3840609d56..76f051878e 100644 --- a/esphome/components/aht10/aht10.h +++ b/esphome/components/aht10/aht10.h @@ -26,6 +26,11 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice { sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *humidity_sensor_{nullptr}; AHT10Variant variant_{}; + unsigned read_count_{}; + unsigned read_delay_{}; + void read_data_(); + void restart_read_(); + uint32_t start_time_{}; }; } // namespace aht10 From e27e3429276b9b5eea17721262b62bafe52a7ce5 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:56:43 +1100 Subject: [PATCH 0023/1106] Show component warnings and errors in the log; (#6400) --- esphome/core/component.cpp | 29 +++++++++++++++++++++++------ esphome/core/component.h | 8 ++++---- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index d737ec0ae3..b9a7697015 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -141,18 +141,35 @@ bool Component::is_ready() { (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_SETUP; } bool Component::can_proceed() { return true; } -bool Component::status_has_warning() { return this->component_state_ & STATUS_LED_WARNING; } -bool Component::status_has_error() { return this->component_state_ & STATUS_LED_ERROR; } -void Component::status_set_warning() { +bool Component::status_has_warning() const { return this->component_state_ & STATUS_LED_WARNING; } +bool Component::status_has_error() const { return this->component_state_ & STATUS_LED_ERROR; } +void Component::status_set_warning(const char *message) { + // Don't spam the log. This risks missing different warning messages though. + if ((this->component_state_ & STATUS_LED_WARNING) != 0) + return; this->component_state_ |= STATUS_LED_WARNING; App.app_state_ |= STATUS_LED_WARNING; + ESP_LOGW(this->get_component_source(), "Warning set: %s", message); } -void Component::status_set_error() { +void Component::status_set_error(const char *message) { + if ((this->component_state_ & STATUS_LED_ERROR) != 0) + return; this->component_state_ |= STATUS_LED_ERROR; App.app_state_ |= STATUS_LED_ERROR; + ESP_LOGE(this->get_component_source(), "Error set: %s", message); +} +void Component::status_clear_warning() { + if ((this->component_state_ & STATUS_LED_WARNING) == 0) + return; + this->component_state_ &= ~STATUS_LED_WARNING; + ESP_LOGW(this->get_component_source(), "Warning cleared"); +} +void Component::status_clear_error() { + if ((this->component_state_ & STATUS_LED_ERROR) == 0) + return; + this->component_state_ &= ~STATUS_LED_ERROR; + ESP_LOGE(this->get_component_source(), "Error cleared"); } -void Component::status_clear_warning() { this->component_state_ &= ~STATUS_LED_WARNING; } -void Component::status_clear_error() { this->component_state_ &= ~STATUS_LED_ERROR; } void Component::status_momentary_warning(const std::string &name, uint32_t length) { this->status_set_warning(); this->set_timeout(name, length, [this]() { this->status_clear_warning(); }); diff --git a/esphome/core/component.h b/esphome/core/component.h index deefedf3d8..4f244e5fcb 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -124,13 +124,13 @@ class Component { virtual bool can_proceed(); - bool status_has_warning(); + bool status_has_warning() const; - bool status_has_error(); + bool status_has_error() const; - void status_set_warning(); + void status_set_warning(const char *message = "unspecified"); - void status_set_error(); + void status_set_error(const char *message = "unspecified"); void status_clear_warning(); From ccca5458623077a77e63f3e5be20229e1cd26b3a Mon Sep 17 00:00:00 2001 From: RFDarter Date: Wed, 20 Mar 2024 04:37:18 +0100 Subject: [PATCH 0024/1106] web_server support for v3 (#6203) --- esphome/components/web_server/__init__.py | 9 +++++++-- esphome/components/web_server/web_server.cpp | 2 +- esphome/components/web_server/web_server.h | 2 +- tests/test10.yaml | 3 +++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index 2708b5d06e..bbd5bc662e 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -44,6 +44,11 @@ def default_url(config): config[CONF_CSS_URL] = "" if not (CONF_JS_URL in config): config[CONF_JS_URL] = "https://oi.esphome.io/v2/www.js" + if config[CONF_VERSION] == 3: + if not (CONF_CSS_URL in config): + config[CONF_CSS_URL] = "" + if not (CONF_JS_URL in config): + config[CONF_JS_URL] = "https://oi.esphome.io/v3/www.js" return config @@ -64,7 +69,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, int=True), + cv.Optional(CONF_VERSION, default=2): cv.one_of(1, 2, 3, int=True), cv.Optional(CONF_CSS_URL): cv.string, cv.Optional(CONF_CSS_INCLUDE): cv.file_, cv.Optional(CONF_JS_URL): cv.string, @@ -152,7 +157,7 @@ async def to_code(config): cg.add_define("USE_WEBSERVER") cg.add_define("USE_WEBSERVER_PORT", config[CONF_PORT]) cg.add_define("USE_WEBSERVER_VERSION", version) - if version == 2: + if version >= 2: # Don't compress the index HTML as the data sizes are almost the same. add_resource_as_progmem("INDEX_HTML", build_index_html(config), compress=False) else: diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 69d601ed49..f065dc6684 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -358,7 +358,7 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) { stream->print(F("")); request->send(stream); } -#elif USE_WEBSERVER_VERSION == 2 +#elif USE_WEBSERVER_VERSION >= 2 void WebServer::handle_index_request(AsyncWebServerRequest *request) { AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE); diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 06c59ecaca..57cbbe1339 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -13,7 +13,7 @@ #include #endif -#if USE_WEBSERVER_VERSION == 2 +#if USE_WEBSERVER_VERSION >= 2 extern const uint8_t ESPHOME_WEBSERVER_INDEX_HTML[] PROGMEM; extern const size_t ESPHOME_WEBSERVER_INDEX_HTML_SIZE; #endif diff --git a/tests/test10.yaml b/tests/test10.yaml index 7e3a685b36..854173cfe9 100644 --- a/tests/test10.yaml +++ b/tests/test10.yaml @@ -23,6 +23,9 @@ logger: api: reboot_timeout: 10min +web_server: + version: 3 + time: - platform: sntp From 6f7273d9cb00d1c6a4618c2b819d00b01601ffd1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Mar 2024 16:38:15 +1300 Subject: [PATCH 0025/1106] Bump version to 2024.3.0b5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 2b5724c9f3..e573a64e1d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.3.0b4" +__version__ = "2024.3.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 9541df9d88ed99677335c4dc9affef30373dce8e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Mar 2024 17:15:19 +1300 Subject: [PATCH 0026/1106] Bump version to 2024.3.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e573a64e1d..e386448462 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.3.0b5" +__version__ = "2024.3.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 37345e11ebd0a8f80754af8f08aff04d858d8354 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 25 Mar 2024 09:44:05 +1100 Subject: [PATCH 0027/1106] AHT10: Fix bug (#6409) --- esphome/components/aht10/aht10.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index d5af06c2a2..d2fad91cb5 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -42,19 +42,18 @@ void AHT10Component::setup() { } delay(AHT10_SOFTRESET_DELAY); - const uint8_t *init_cmd; + i2c::ErrorCode error_code = i2c::ERROR_INVALID_ARGUMENT; switch (this->variant_) { case AHT10Variant::AHT20: - init_cmd = AHT20_INITIALIZE_CMD; ESP_LOGCONFIG(TAG, "Setting up AHT20"); + error_code = this->write(AHT20_INITIALIZE_CMD, sizeof(AHT20_INITIALIZE_CMD)); break; case AHT10Variant::AHT10: - default: - init_cmd = AHT10_INITIALIZE_CMD; ESP_LOGCONFIG(TAG, "Setting up AHT10"); + error_code = this->write(AHT10_INITIALIZE_CMD, sizeof(AHT10_INITIALIZE_CMD)); + break; } - - if (this->write(init_cmd, sizeof(init_cmd)) != i2c::ERROR_OK) { + if (error_code != i2c::ERROR_OK) { ESP_LOGE(TAG, "Communication with AHT10 failed!"); this->mark_failed(); return; From 7abb82c1ca379575f379bfd99a2a6ddde9b3bd60 Mon Sep 17 00:00:00 2001 From: ebw44 Date: Tue, 26 Mar 2024 11:20:15 +1300 Subject: [PATCH 0028/1106] microWakeWord: Fix model path joining (#6426) --- esphome/components/micro_wake_word/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py index 209a1412ca..9073d103f1 100644 --- a/esphome/components/micro_wake_word/__init__.py +++ b/esphome/components/micro_wake_word/__init__.py @@ -287,7 +287,7 @@ def _load_model_data(manifest_path: Path): except cv.Invalid as e: raise EsphomeError(f"Invalid manifest file: {e}") from e - model_path = urljoin(str(manifest_path), manifest[CONF_MODEL]) + model_path = manifest_path.parent / manifest[CONF_MODEL] with open(model_path, "rb") as f: model = f.read() From d304e529408c816ef6c22ddcd9cc2305dc89d449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Poczkodi?= Date: Mon, 25 Mar 2024 23:34:47 +0100 Subject: [PATCH 0029/1106] Don't compile strptime unless its required (#6424) --- esphome/core/time.cpp | 6 ++++++ esphome/core/time.h | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/esphome/core/time.cpp b/esphome/core/time.cpp index 2e46a611e6..ae4fabac52 100644 --- a/esphome/core/time.cpp +++ b/esphome/core/time.cpp @@ -1,4 +1,6 @@ +#ifdef USE_DATETIME #include +#endif #include "helpers.h" #include "time.h" // NOLINT @@ -64,6 +66,8 @@ std::string ESPTime::strftime(const std::string &format) { return timestr; } +#ifdef USE_DATETIME + bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) { // clang-format off std::regex dt_regex(R"(^ @@ -102,6 +106,8 @@ bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) { return true; } +#endif + void ESPTime::increment_second() { this->timestamp++; if (!increment_time_value(this->second, 0, 60)) diff --git a/esphome/core/time.h b/esphome/core/time.h index 4300cf26b7..738a0261c7 100644 --- a/esphome/core/time.h +++ b/esphome/core/time.h @@ -67,6 +67,8 @@ struct ESPTime { this->day_of_year < 367 && this->month > 0 && this->month < 13; } +#ifdef USE_DATETIME + /** Convert a string to ESPTime struct as specified by the format argument. * @param time_to_parse null-terminated c string formatet like this: 2020-08-25 05:30:00. * @param esp_time an instance of a ESPTime struct @@ -74,6 +76,8 @@ struct ESPTime { */ static bool strptime(const std::string &time_to_parse, ESPTime &esp_time); +#endif + /// Convert a C tm struct instance with a C unix epoch timestamp to an ESPTime instance. static ESPTime from_c_tm(struct tm *c_tm, time_t c_time); From f00d8760807b9def3e837ccfa32d49faf73f9943 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 25 Mar 2024 21:24:58 -1000 Subject: [PATCH 0030/1106] Fix editor live validation (#6431) --- esphome/config.py | 24 ++++++++++++----------- esphome/config_helpers.py | 24 ----------------------- esphome/vscode.py | 41 +++++++++++++++++++++++++++++++++------ esphome/yaml_util.py | 25 ++++++++++++++---------- 4 files changed, 63 insertions(+), 51 deletions(-) diff --git a/esphome/config.py b/esphome/config.py index f5a1ebb8d7..c5764dd4f2 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -1,10 +1,11 @@ +from __future__ import annotations import abc import functools import heapq import logging import re -from typing import Optional, Union +from typing import Union, Any from contextlib import contextmanager import contextvars @@ -76,7 +77,7 @@ def _path_begins_with(path: ConfigPath, other: ConfigPath) -> bool: @functools.total_ordering class _ValidationStepTask: - def __init__(self, priority: float, id_number: int, step: "ConfigValidationStep"): + def __init__(self, priority: float, id_number: int, step: ConfigValidationStep): self.priority = priority self.id_number = id_number self.step = step @@ -130,7 +131,7 @@ class Config(OrderedDict, fv.FinalValidateConfig): ) self.errors.append(error) - def add_validation_step(self, step: "ConfigValidationStep"): + def add_validation_step(self, step: ConfigValidationStep): id_num = self._validation_tasks_id self._validation_tasks_id += 1 heapq.heappush( @@ -172,7 +173,7 @@ class Config(OrderedDict, fv.FinalValidateConfig): conf = conf[key] conf[path[-1]] = value - def get_error_for_path(self, path: ConfigPath) -> Optional[vol.Invalid]: + def get_error_for_path(self, path: ConfigPath) -> vol.Invalid | None: for err in self.errors: if self.get_deepest_path(err.path) == path: self.errors.remove(err) @@ -181,7 +182,7 @@ class Config(OrderedDict, fv.FinalValidateConfig): def get_deepest_document_range_for_path( self, path: ConfigPath, get_key: bool = False - ) -> Optional[ESPHomeDataBase]: + ) -> ESPHomeDataBase | None: data = self doc_range = None for index, path_item in enumerate(path): @@ -733,7 +734,9 @@ class PinUseValidationCheck(ConfigValidationStep): pins.PIN_SCHEMA_REGISTRY.final_validate(result) -def validate_config(config, command_line_substitutions) -> Config: +def validate_config( + config: dict[str, Any], command_line_substitutions: dict[str, Any] +) -> Config: result = Config() loader.clear_component_meta_finders() @@ -897,24 +900,23 @@ class InvalidYAMLError(EsphomeError): self.base_exc = base_exc -def _load_config(command_line_substitutions): +def _load_config(command_line_substitutions: dict[str, Any]) -> Config: + """Load the configuration file.""" try: config = yaml_util.load_yaml(CORE.config_path) except EsphomeError as e: raise InvalidYAMLError(e) from e try: - result = validate_config(config, command_line_substitutions) + return validate_config(config, command_line_substitutions) except EsphomeError: raise except Exception: _LOGGER.error("Unexpected exception while reading configuration:") raise - return result - -def load_config(command_line_substitutions): +def load_config(command_line_substitutions: dict[str, Any]) -> Config: try: return _load_config(command_line_substitutions) except vol.Invalid as err: diff --git a/esphome/config_helpers.py b/esphome/config_helpers.py index ac52c6ede2..7b47e097c8 100644 --- a/esphome/config_helpers.py +++ b/esphome/config_helpers.py @@ -1,9 +1,4 @@ -import json -import os - from esphome.const import CONF_ID -from esphome.core import CORE -from esphome.helpers import read_file class Extend: @@ -38,25 +33,6 @@ class Remove: return isinstance(b, Remove) and self.value == b.value -def read_config_file(path: str) -> str: - if CORE.vscode and ( - not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path) - ): - print( - json.dumps( - { - "type": "read_file", - "path": path, - } - ) - ) - data = json.loads(input()) - assert data["type"] == "file_response" - return data["content"] - - return read_file(path) - - def merge_config(full_old, full_new): def merge(old, new): if isinstance(new, dict): diff --git a/esphome/vscode.py b/esphome/vscode.py index cb2f51976f..8198d2659a 100644 --- a/esphome/vscode.py +++ b/esphome/vscode.py @@ -1,20 +1,22 @@ +from __future__ import annotations import json import os +from io import StringIO +from typing import Any -from typing import Optional - -from esphome.config import load_config, _format_vol_invalid, Config +from esphome.yaml_util import parse_yaml +from esphome.config import validate_config, _format_vol_invalid, Config from esphome.core import CORE, DocumentRange import esphome.config_validation as cv -def _get_invalid_range(res: Config, invalid: cv.Invalid) -> Optional[DocumentRange]: +def _get_invalid_range(res: Config, invalid: cv.Invalid) -> DocumentRange | None: return res.get_deepest_document_range_for_path( invalid.path, invalid.error_message == "extra keys not allowed" ) -def _dump_range(range: Optional[DocumentRange]) -> Optional[dict]: +def _dump_range(range: DocumentRange | None) -> dict | None: if range is None: return None return { @@ -56,6 +58,25 @@ class VSCodeResult: ) +def _read_file_content_from_json_on_stdin() -> str: + """Read the content of a json encoded file from stdin.""" + data = json.loads(input()) + assert data["type"] == "file_response" + return data["content"] + + +def _print_file_read_event(path: str) -> None: + """Print a file read event.""" + print( + json.dumps( + { + "type": "read_file", + "path": path, + } + ) + ) + + def read_config(args): while True: CORE.reset() @@ -68,9 +89,17 @@ def read_config(args): CORE.config_path = os.path.join(args.configuration, f) else: CORE.config_path = data["file"] + + file_name = CORE.config_path + _print_file_read_event(file_name) + raw_yaml = _read_file_content_from_json_on_stdin() + command_line_substitutions: dict[str, Any] = ( + dict(args.substitution) if args.substitution else {} + ) vs = VSCodeResult() try: - res = load_config(dict(args.substitution) if args.substitution else {}) + config = parse_yaml(file_name, StringIO(raw_yaml)) + res = validate_config(config, command_line_substitutions) except Exception as err: # pylint: disable=broad-except vs.add_yaml_error(str(err)) else: diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index 60705082b6..c7aa78201f 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -417,20 +417,25 @@ def load_yaml(fname: str, clear_secrets: bool = True) -> Any: return _load_yaml_internal(fname) +def parse_yaml(file_name: str, file_handle: TextIOWrapper) -> Any: + """Parse a YAML file.""" + try: + return _load_yaml_internal_with_type(ESPHomeLoader, file_name, file_handle) + except EsphomeError: + # Loading failed, so we now load with the Python loader which has more + # readable exceptions + # Rewind the stream so we can try again + file_handle.seek(0, 0) + return _load_yaml_internal_with_type( + ESPHomePurePythonLoader, file_name, file_handle + ) + + def _load_yaml_internal(fname: str) -> Any: """Load a YAML file.""" try: with open(fname, encoding="utf-8") as f_handle: - try: - return _load_yaml_internal_with_type(ESPHomeLoader, fname, f_handle) - except EsphomeError: - # Loading failed, so we now load with the Python loader which has more - # readable exceptions - # Rewind the stream so we can try again - f_handle.seek(0, 0) - return _load_yaml_internal_with_type( - ESPHomePurePythonLoader, fname, f_handle - ) + return parse_yaml(fname, f_handle) except (UnicodeDecodeError, OSError) as err: raise EsphomeError(f"Error reading file {fname}: {err}") from err From 4d30c81b0b181b38f2d0079445c2499c63216c57 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:17:31 +1300 Subject: [PATCH 0031/1106] Bump version to 2024.3.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e386448462..b09d0a8eb0 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.3.0" +__version__ = "2024.3.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 4c9bcc71cbcf285daa7a73e30b7eadf834503e64 Mon Sep 17 00:00:00 2001 From: DAVe3283 Date: Wed, 20 Mar 2024 17:57:27 -0600 Subject: [PATCH 0032/1106] Fix logger compile error on ESP32-C6 (#6323) --- esphome/components/logger/logger_esp32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/logger/logger_esp32.cpp b/esphome/components/logger/logger_esp32.cpp index 7c4d6781c9..740e086f92 100644 --- a/esphome/components/logger/logger_esp32.cpp +++ b/esphome/components/logger/logger_esp32.cpp @@ -129,7 +129,7 @@ void Logger::pre_setup() { this->uart_num_ = UART_NUM_2; break; #endif -#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#ifdef USE_LOGGER_USB_CDC case UART_SELECTION_USB_CDC: this->uart_num_ = -1; break; From 87c4ad025698b2e240c397d54332a3a62e92cd81 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Thu, 28 Mar 2024 12:51:01 -0700 Subject: [PATCH 0033/1106] Add missing ethernet types (#6444) --- esphome/components/ethernet/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index de6040339a..ade94cb9f5 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -155,6 +155,8 @@ CONFIG_SCHEMA = cv.All( "DP83848": RMII_SCHEMA, "IP101": RMII_SCHEMA, "JL1101": RMII_SCHEMA, + "KSZ8081": RMII_SCHEMA, + "KSZ8081RNA": RMII_SCHEMA, "W5500": SPI_SCHEMA, }, upper=True, From d2b386146509837264079d93d9d5d3fb598f1137 Mon Sep 17 00:00:00 2001 From: NewoPL <27411874+NewoPL@users.noreply.github.com> Date: Mon, 1 Apr 2024 01:43:49 +0200 Subject: [PATCH 0034/1106] fix: changing the content source when playing is paused blocks the player (#6454) --- .../i2s_audio/media_player/i2s_audio_media_player.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp index 9e2e3f136a..6e07983920 100644 --- a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +++ b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp @@ -12,12 +12,12 @@ static const char *const TAG = "audio"; void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) { if (call.get_media_url().has_value()) { this->current_url_ = call.get_media_url(); - - if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && this->audio_ != nullptr) { + if (this->i2s_state_ != I2S_STATE_STOPPED && this->audio_ != nullptr) { if (this->audio_->isRunning()) { this->audio_->stopSong(); } this->audio_->connecttohost(this->current_url_.value().c_str()); + this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; } else { this->start(); } From c029ef51181412dd8e35ef8e5280d5cd2a38abdc Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 4 Apr 2024 18:12:28 +1300 Subject: [PATCH 0035/1106] Bump version to 2024.3.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index b09d0a8eb0..5368f0bc9a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.3.1" +__version__ = "2024.3.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 270fb5e7ac24cfea3841e8e1f173a308c5437d2e Mon Sep 17 00:00:00 2001 From: Mat931 <49403702+Mat931@users.noreply.github.com> Date: Mon, 8 Apr 2024 19:21:51 +0000 Subject: [PATCH 0036/1106] Internal temperature: Support Beken platform (#6491) --- .../internal_temperature/internal_temperature.cpp | 15 +++++++++++++++ esphome/components/internal_temperature/sensor.py | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/esphome/components/internal_temperature/internal_temperature.cpp b/esphome/components/internal_temperature/internal_temperature.cpp index a387708263..255d95f6f8 100644 --- a/esphome/components/internal_temperature/internal_temperature.cpp +++ b/esphome/components/internal_temperature/internal_temperature.cpp @@ -14,6 +14,11 @@ uint8_t temprature_sens_read(); #ifdef USE_RP2040 #include "Arduino.h" #endif // USE_RP2040 +#ifdef USE_BK72XX +extern "C" { +uint32_t temp_single_get_current_temperature(uint32_t *temp_value); +} +#endif // USE_BK72XX namespace esphome { namespace internal_temperature { @@ -46,6 +51,16 @@ void InternalTemperatureSensor::update() { temperature = analogReadTemp(); success = (temperature != 0.0f); #endif // USE_RP2040 +#ifdef USE_BK72XX + uint32_t raw, result; + result = temp_single_get_current_temperature(&raw); + success = (result == 0); +#ifdef USE_LIBRETINY_VARIANT_BK7231T + temperature = raw * 0.04f; +#else + temperature = raw * 0.128f; +#endif // USE_LIBRETINY_VARIANT_BK7231T +#endif // USE_BK72XX if (success && std::isfinite(temperature)) { this->publish_state(temperature); } else { diff --git a/esphome/components/internal_temperature/sensor.py b/esphome/components/internal_temperature/sensor.py index 2daf816538..809d7a40b9 100644 --- a/esphome/components/internal_temperature/sensor.py +++ b/esphome/components/internal_temperature/sensor.py @@ -14,6 +14,7 @@ from esphome.const import ( KEY_FRAMEWORK_VERSION, PLATFORM_ESP32, PLATFORM_RP2040, + PLATFORM_BK72XX, ) from esphome.core import CORE @@ -51,7 +52,7 @@ CONFIG_SCHEMA = cv.All( state_class=STATE_CLASS_MEASUREMENT, entity_category=ENTITY_CATEGORY_DIAGNOSTIC, ).extend(cv.polling_component_schema("60s")), - cv.only_on([PLATFORM_ESP32, PLATFORM_RP2040]), + cv.only_on([PLATFORM_ESP32, PLATFORM_RP2040, PLATFORM_BK72XX]), validate_config, ) From 708d5034cb5eb92654056c546bdb1e5c0b25ecb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 09:48:04 +1200 Subject: [PATCH 0037/1106] Bump docker/setup-buildx-action from 3.2.0 to 3.3.0 (#6502) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index d8fd4efa0e..6063d1e052 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -46,7 +46,7 @@ jobs: with: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.2.0 + uses: docker/setup-buildx-action@v3.3.0 - name: Set up QEMU uses: docker/setup-qemu-action@v3.0.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cb7defc2b0..7c0ec0270b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,7 +84,7 @@ jobs: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.2.0 + uses: docker/setup-buildx-action@v3.3.0 - name: Set up QEMU if: matrix.platform != 'linux/amd64' uses: docker/setup-qemu-action@v3.0.0 @@ -162,7 +162,7 @@ jobs: name: digests-${{ matrix.image.target }}-${{ matrix.registry }} path: /tmp/digests - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.2.0 + uses: docker/setup-buildx-action@v3.3.0 - name: Log in to docker hub if: matrix.registry == 'dockerhub' From efc9fd060d3f46d7a843fd0dfc523910075ccca9 Mon Sep 17 00:00:00 2001 From: fariouche Date: Tue, 9 Apr 2024 00:17:51 +0200 Subject: [PATCH 0038/1106] add support for Tuya pink version of miflora (#5402) --- CODEOWNERS | 1 + .../components/xiaomi_hhccjcy10/__init__.py | 1 + esphome/components/xiaomi_hhccjcy10/sensor.py | 96 +++++++++++++++++++ .../xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp | 68 +++++++++++++ .../xiaomi_hhccjcy10/xiaomi_hhccjcy10.h | 38 ++++++++ tests/test2.yaml | 12 +++ 6 files changed, 216 insertions(+) create mode 100644 esphome/components/xiaomi_hhccjcy10/__init__.py create mode 100644 esphome/components/xiaomi_hhccjcy10/sensor.py create mode 100644 esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp create mode 100644 esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h diff --git a/CODEOWNERS b/CODEOWNERS index fafbaae6c6..d2a86cd3d9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -404,6 +404,7 @@ esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard esphome/components/wl_134/* @hobbypunk90 esphome/components/x9c/* @EtienneMD esphome/components/xgzp68xx/* @gcormier +esphome/components/xiaomi_hhccjcy10/* @fariouche esphome/components/xiaomi_lywsd03mmc/* @ahpohl esphome/components/xiaomi_mhoc303/* @drug123 esphome/components/xiaomi_mhoc401/* @vevsvevs diff --git a/esphome/components/xiaomi_hhccjcy10/__init__.py b/esphome/components/xiaomi_hhccjcy10/__init__.py new file mode 100644 index 0000000000..d47cef13c6 --- /dev/null +++ b/esphome/components/xiaomi_hhccjcy10/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@fariouche"] diff --git a/esphome/components/xiaomi_hhccjcy10/sensor.py b/esphome/components/xiaomi_hhccjcy10/sensor.py new file mode 100644 index 0000000000..4f77fa8103 --- /dev/null +++ b/esphome/components/xiaomi_hhccjcy10/sensor.py @@ -0,0 +1,96 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, esp32_ble_tracker +from esphome.const import ( + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + DEVICE_CLASS_ILLUMINANCE, + DEVICE_CLASS_TEMPERATURE, + ENTITY_CATEGORY_DIAGNOSTIC, + ICON_WATER_PERCENT, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_PERCENT, + CONF_ID, + CONF_MOISTURE, + CONF_ILLUMINANCE, + UNIT_LUX, + CONF_CONDUCTIVITY, + UNIT_MICROSIEMENS_PER_CENTIMETER, + ICON_FLOWER, + DEVICE_CLASS_BATTERY, + CONF_BATTERY_LEVEL, +) + +DEPENDENCIES = ["esp32_ble_tracker"] + +xiaomi_hhccjcy10_ns = cg.esphome_ns.namespace("xiaomi_hhccjcy10") +XiaomiHHCCJCY10 = xiaomi_hhccjcy10_ns.class_( + "XiaomiHHCCJCY10", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiHHCCJCY10), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_MOISTURE): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_WATER_PERCENT, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema( + unit_of_measurement=UNIT_LUX, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ILLUMINANCE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_CONDUCTIVITY): sensor.sensor_schema( + unit_of_measurement=UNIT_MICROSIEMENS_PER_CENTIMETER, + icon=ICON_FLOWER, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_BATTERY, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await esp32_ble_tracker.register_ble_device(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + + if temperature_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temperature_config) + cg.add(var.set_temperature(sens)) + if moisture_config := config.get(CONF_MOISTURE): + sens = await sensor.new_sensor(moisture_config) + cg.add(var.set_moisture(sens)) + if illuminance_config := config.get(CONF_ILLUMINANCE): + sens = await sensor.new_sensor(illuminance_config) + cg.add(var.set_illuminance(sens)) + if conductivity_config := config.get(CONF_CONDUCTIVITY): + sens = await sensor.new_sensor(conductivity_config) + cg.add(var.set_conductivity(sens)) + if battery_level_config := config.get(CONF_BATTERY_LEVEL): + sens = await sensor.new_sensor(battery_level_config) + cg.add(var.set_battery_level(sens)) diff --git a/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp b/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp new file mode 100644 index 0000000000..45d4cceb52 --- /dev/null +++ b/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp @@ -0,0 +1,68 @@ +#include "xiaomi_hhccjcy10.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace xiaomi_hhccjcy10 { + +static const char *const TAG = "xiaomi_hhccjcy10"; + +void XiaomiHHCCJCY10::dump_config() { + ESP_LOGCONFIG(TAG, "Xiaomi HHCCJCY10"); + LOG_SENSOR(" ", "Temperature", this->temperature_); + LOG_SENSOR(" ", "Moisture", this->moisture_); + LOG_SENSOR(" ", "Conductivity", this->conductivity_); + LOG_SENSOR(" ", "Illuminance", this->illuminance_); + LOG_SENSOR(" ", "Battery Level", this->battery_level_); +} + +bool XiaomiHHCCJCY10::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { + if (device.address_uint64() != this->address_) { + ESP_LOGVV(TAG, "parse_device(): unknown MAC address."); + return false; + } + ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); + + bool success = false; + for (auto &service_data : device.get_service_datas()) { + if (!service_data.uuid.contains(0x50, 0xFD)) { + ESP_LOGVV(TAG, "no tuya service data UUID."); + continue; + } + if (service_data.data.size() != 9) { // tuya alternate between two service data + continue; + } + const uint8_t *data = service_data.data.data(); + + if (this->temperature_ != nullptr) { + const int16_t temperature = encode_uint16(data[1], data[2]); + this->temperature_->publish_state((float) temperature / 10.0f); + } + + if (this->moisture_ != nullptr) + this->moisture_->publish_state(data[0]); + + if (this->conductivity_ != nullptr) { + const uint16_t conductivity = encode_uint16(data[7], data[8]); + this->conductivity_->publish_state((float) conductivity); + } + + if (this->illuminance_ != nullptr) { + const uint32_t illuminance = encode_uint24(data[3], data[4], data[5]); + this->illuminance_->publish_state((float) illuminance); + } + + if (this->battery_level_ != nullptr) + this->battery_level_->publish_state(data[6]); + success = true; + } + + return success; +} + +} // namespace xiaomi_hhccjcy10 +} // namespace esphome + +#endif diff --git a/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h b/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h new file mode 100644 index 0000000000..bc1e580ce4 --- /dev/null +++ b/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h @@ -0,0 +1,38 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace xiaomi_hhccjcy10 { + +class XiaomiHHCCJCY10 : public Component, public esp32_ble_tracker::ESPBTDeviceListener { + public: + void set_address(uint64_t address) { this->address_ = address; } + + bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; + + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void set_temperature(sensor::Sensor *temperature) { this->temperature_ = temperature; } + void set_moisture(sensor::Sensor *moisture) { this->moisture_ = moisture; } + void set_conductivity(sensor::Sensor *conductivity) { this->conductivity_ = conductivity; } + void set_illuminance(sensor::Sensor *illuminance) { this->illuminance_ = illuminance; } + void set_battery_level(sensor::Sensor *battery_level) { this->battery_level_ = battery_level; } + + protected: + uint64_t address_; + sensor::Sensor *temperature_{nullptr}; + sensor::Sensor *moisture_{nullptr}; + sensor::Sensor *conductivity_{nullptr}; + sensor::Sensor *illuminance_{nullptr}; + sensor::Sensor *battery_level_{nullptr}; +}; + +} // namespace xiaomi_hhccjcy10 +} // namespace esphome + +#endif diff --git a/tests/test2.yaml b/tests/test2.yaml index a1e310be9c..2fdef72c08 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -203,6 +203,18 @@ sensor: name: Xiaomi HHCCJCY01 Soil Conductivity battery_level: name: Xiaomi HHCCJCY01 Battery Level + - platform: xiaomi_hhccjcy10 + mac_address: DD:25:6D:E4:FF:8F + temperature: + name: "Xiaomi HHCCJCY10 Temperature" + moisture: + name: "Xiaomi HHCCJCY10 Moisture" + illuminance: + name: "Xiaomi HHCCJCY10 Illuminance" + conductivity: + name: "Xiaomi HHCCJCY10 Soil Conductivity" + battery_level: + name: "Xiaomi HHCCJCY10 Battery Level" - platform: xiaomi_lywsdcgq mac_address: 7A:80:8E:19:36:BA temperature: From 16d154e2e5ec31957f233173dfcf55b37eead1c8 Mon Sep 17 00:00:00 2001 From: cvwillegen Date: Tue, 9 Apr 2024 03:07:04 +0200 Subject: [PATCH 0039/1106] Add MAC address to WiFi config reply (#6489) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../components/captive_portal/captive_index.h | 190 +++++++++--------- .../captive_portal/captive_portal.cpp | 2 +- 2 files changed, 97 insertions(+), 95 deletions(-) diff --git a/esphome/components/captive_portal/captive_index.h b/esphome/components/captive_portal/captive_index.h index 56071f3d2a..d262a89b09 100644 --- a/esphome/components/captive_portal/captive_index.h +++ b/esphome/components/captive_portal/captive_index.h @@ -6,100 +6,102 @@ namespace esphome { namespace captive_portal { const uint8_t INDEX_GZ[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x5b, 0x8f, 0xdb, 0x36, 0x16, 0x7e, 0xef, - 0xaf, 0xe0, 0x2a, 0x49, 0x2d, 0x37, 0x23, 0xea, 0x66, 0xf9, 0x2a, 0xa9, 0x48, 0xb2, 0x29, 0x5a, 0x20, 0x69, 0x03, - 0xcc, 0xb4, 0xfb, 0x10, 0x04, 0x18, 0x5a, 0xa2, 0x2c, 0x66, 0x24, 0x4a, 0x15, 0xe9, 0x5b, 0x0c, 0xef, 0x6f, 0xdf, - 0x43, 0x52, 0xf6, 0x38, 0xb3, 0x99, 0x05, 0x52, 0xec, 0x62, 0xd1, 0x4e, 0x26, 0x1c, 0x92, 0x3a, 0xd7, 0x4f, 0x3c, - 0x17, 0x2a, 0xfe, 0x5b, 0xde, 0x64, 0x72, 0xdf, 0x52, 0x54, 0xca, 0xba, 0x4a, 0x63, 0x35, 0xa2, 0x8a, 0xf0, 0x55, - 0x42, 0x39, 0xac, 0x28, 0xc9, 0xd3, 0xb8, 0xa6, 0x92, 0xa0, 0xac, 0x24, 0x9d, 0xa0, 0x32, 0xf9, 0xf5, 0xe6, 0x07, - 0x67, 0x8a, 0xdc, 0x34, 0xae, 0x18, 0xbf, 0x43, 0x1d, 0xad, 0x12, 0x96, 0x35, 0x1c, 0x95, 0x1d, 0x2d, 0x92, 0x9c, - 0x48, 0x32, 0x67, 0x35, 0x59, 0x51, 0x45, 0xa0, 0xd9, 0x38, 0xa9, 0x69, 0xb2, 0x61, 0x74, 0xdb, 0x36, 0x9d, 0x44, - 0x40, 0x29, 0x29, 0x97, 0x89, 0xb5, 0x65, 0xb9, 0x2c, 0x93, 0x9c, 0x6e, 0x58, 0x46, 0x1d, 0xbd, 0xb8, 0x62, 0x9c, - 0x49, 0x46, 0x2a, 0x47, 0x64, 0xa4, 0xa2, 0x89, 0x7f, 0xb5, 0x16, 0xb4, 0xd3, 0x0b, 0xb2, 0x84, 0x35, 0x6f, 0x2c, - 0x10, 0x29, 0xb2, 0x8e, 0xb5, 0x12, 0x29, 0x7b, 0x93, 0xba, 0xc9, 0xd7, 0x15, 0x4d, 0x5d, 0x97, 0x08, 0xb0, 0x4b, - 0xb8, 0x8c, 0xe7, 0x74, 0x87, 0xa7, 0xb3, 0x68, 0x32, 0x9e, 0xe6, 0x13, 0xfc, 0x51, 0x7c, 0x03, 0x9e, 0xad, 0x6b, - 0x50, 0x87, 0xab, 0x26, 0x23, 0x92, 0x35, 0x1c, 0x0b, 0x4a, 0xba, 0xac, 0x4c, 0x92, 0xc4, 0xfa, 0x5e, 0x90, 0x0d, - 0xb5, 0xbe, 0xfd, 0xd6, 0x3e, 0x13, 0xad, 0xa8, 0x7c, 0x5d, 0x51, 0x35, 0x15, 0x2f, 0xf7, 0x37, 0x64, 0xf5, 0x33, - 0x58, 0x6e, 0x5b, 0x44, 0xb0, 0x9c, 0x5a, 0xc3, 0xf7, 0xde, 0x07, 0x2c, 0xe4, 0xbe, 0xa2, 0x38, 0x67, 0xa2, 0xad, - 0xc8, 0x3e, 0xb1, 0x96, 0x20, 0xf5, 0xce, 0x1a, 0x2e, 0x8a, 0x35, 0xcf, 0x94, 0x70, 0x24, 0x6c, 0x3a, 0x3c, 0x54, - 0x14, 0xcc, 0x4b, 0xde, 0x12, 0x59, 0xe2, 0x9a, 0xec, 0x6c, 0x33, 0x61, 0xdc, 0x0e, 0xbe, 0xb3, 0xe9, 0x73, 0xdf, - 0xf3, 0x86, 0x57, 0x7a, 0xf0, 0x86, 0x2e, 0xfc, 0x5d, 0x74, 0x54, 0xae, 0x3b, 0x8e, 0x88, 0x7d, 0x1b, 0xb7, 0x40, - 0x89, 0xf2, 0xc4, 0xaa, 0xfd, 0x00, 0x7b, 0xde, 0x14, 0xf9, 0x33, 0x1c, 0x44, 0x8e, 0xef, 0xe3, 0xd0, 0xf1, 0xa3, - 0x6c, 0xe2, 0x44, 0xc8, 0x1f, 0xc1, 0x10, 0x04, 0x38, 0x42, 0xde, 0x27, 0x0b, 0x15, 0xac, 0xaa, 0x12, 0x8b, 0x37, - 0x9c, 0x5a, 0x48, 0xc8, 0xae, 0xb9, 0xa3, 0x89, 0x95, 0xad, 0xbb, 0x0e, 0xec, 0x7f, 0xd5, 0x54, 0x4d, 0x07, 0x70, - 0x7d, 0x83, 0x3e, 0xfb, 0xf9, 0x6a, 0x15, 0xb2, 0x23, 0x5c, 0x14, 0x4d, 0x57, 0x27, 0x96, 0x7e, 0x29, 0xf6, 0xd3, - 0x83, 0x3c, 0x22, 0x35, 0x0c, 0x2f, 0x1e, 0x3a, 0x4d, 0xc7, 0x56, 0x8c, 0x27, 0x96, 0x1f, 0x20, 0x7f, 0x0a, 0x6a, - 0x6f, 0x87, 0xc7, 0x33, 0x26, 0x44, 0x61, 0xd2, 0x7b, 0xd9, 0xd8, 0xef, 0x6f, 0x63, 0xb1, 0x59, 0xa1, 0x5d, 0x5d, - 0x71, 0x91, 0x58, 0xa5, 0x94, 0xed, 0xdc, 0x75, 0xb7, 0xdb, 0x2d, 0xde, 0x86, 0xb8, 0xe9, 0x56, 0x6e, 0xe0, 0x79, - 0x9e, 0x0b, 0x14, 0x16, 0x32, 0xe7, 0xc3, 0x0a, 0x46, 0x16, 0x2a, 0x29, 0x5b, 0x95, 0x52, 0xcf, 0xd3, 0xa7, 0x07, - 0x7a, 0x8c, 0x15, 0x45, 0x7a, 0xfb, 0xe1, 0x42, 0x4b, 0x77, 0xa1, 0x85, 0x7e, 0x7f, 0x81, 0xe6, 0xe0, 0xad, 0x32, - 0x6a, 0x42, 0x02, 0x14, 0x20, 0x4f, 0xff, 0x0b, 0x1c, 0x35, 0xef, 0x57, 0xce, 0x83, 0x15, 0xba, 0x58, 0xc1, 0x5f, - 0xc0, 0x2f, 0xa8, 0xc7, 0xce, 0xec, 0xcc, 0xee, 0xab, 0xc7, 0x1b, 0xdf, 0xbb, 0xdf, 0x50, 0x3c, 0x3f, 0x8e, 0x2f, - 0xd7, 0x4e, 0xf0, 0x9b, 0x22, 0x50, 0xd8, 0x9f, 0x99, 0x9c, 0xa0, 0xf4, 0x7f, 0x1b, 0x93, 0x08, 0x45, 0xfd, 0x4e, - 0xe4, 0xa8, 0xf9, 0x79, 0xa5, 0x34, 0xa1, 0x68, 0x03, 0x54, 0xb5, 0x33, 0x76, 0x22, 0x12, 0xa2, 0xb0, 0x37, 0x09, - 0x66, 0xb0, 0x3d, 0x06, 0xe6, 0x8b, 0x3d, 0x27, 0xfc, 0x34, 0x50, 0x30, 0xcf, 0x2d, 0xeb, 0x1e, 0x83, 0xe6, 0x12, - 0x03, 0xfc, 0xb1, 0x81, 0x33, 0x67, 0x59, 0x80, 0x11, 0x95, 0x59, 0x69, 0x5b, 0x2e, 0x44, 0x5e, 0xc1, 0x56, 0x10, - 0x15, 0x0d, 0xb7, 0x86, 0x58, 0x96, 0x94, 0xdb, 0x27, 0x56, 0xc5, 0x48, 0xf5, 0x13, 0xfb, 0xe1, 0x13, 0x39, 0x3c, - 0x9c, 0xe3, 0x43, 0x32, 0x09, 0x71, 0x28, 0xb1, 0x8a, 0xe8, 0xab, 0xf3, 0xee, 0xb2, 0xc9, 0xf7, 0x8f, 0x84, 0x4e, - 0xe9, 0x9b, 0xb8, 0x61, 0x9c, 0xd3, 0xee, 0x86, 0xee, 0xe0, 0x1d, 0xfe, 0x83, 0xfd, 0xc0, 0xd0, 0xcf, 0x54, 0x6e, - 0x9b, 0xee, 0x4e, 0xcc, 0x91, 0xf5, 0xdc, 0x88, 0x5b, 0xa8, 0xa8, 0x61, 0x20, 0x9b, 0xb4, 0x02, 0x8b, 0x0a, 0x72, - 0x82, 0xed, 0x0f, 0x21, 0x7e, 0xda, 0x7b, 0x4b, 0xf8, 0xc9, 0xb9, 0xdb, 0x38, 0x67, 0x1b, 0x94, 0x55, 0x10, 0xf5, - 0x70, 0xfc, 0x8d, 0x28, 0x0b, 0xf5, 0x47, 0xbd, 0xe1, 0x19, 0x70, 0xdf, 0x25, 0xd6, 0x17, 0xa2, 0xfa, 0xe5, 0xfe, - 0xa7, 0xdc, 0x1e, 0x08, 0x88, 0xe7, 0xc1, 0x10, 0x6f, 0x48, 0xb5, 0xa6, 0x28, 0x41, 0xb2, 0x64, 0xe2, 0xde, 0xc0, - 0xc5, 0xa3, 0x6c, 0xad, 0xb8, 0x03, 0xae, 0x02, 0x1e, 0x0b, 0x7b, 0x68, 0x9d, 0x22, 0x2b, 0x26, 0x26, 0xef, 0x59, - 0x4f, 0xac, 0x07, 0x16, 0x39, 0x15, 0x2d, 0xa4, 0x75, 0x1f, 0x81, 0x4f, 0x0f, 0xc2, 0xe6, 0xb8, 0x03, 0xed, 0xc3, - 0xe3, 0x79, 0x33, 0x16, 0x2d, 0xe1, 0x0f, 0x19, 0x95, 0x81, 0xea, 0xa0, 0x43, 0xb2, 0x82, 0x99, 0x3a, 0xed, 0x40, - 0x74, 0x56, 0xe8, 0x92, 0xd3, 0xf4, 0xe9, 0xa1, 0x03, 0x89, 0x2a, 0x07, 0x9d, 0x25, 0xc6, 0x2e, 0x40, 0x93, 0xde, - 0x1e, 0x87, 0xf7, 0x7e, 0xfc, 0xbe, 0xa6, 0xdd, 0xfe, 0x9a, 0x56, 0x34, 0x93, 0x4d, 0x67, 0x5b, 0x4f, 0x40, 0x0b, - 0xbc, 0x7e, 0xed, 0xf0, 0x8f, 0x37, 0x6f, 0xdf, 0x24, 0x8d, 0xcd, 0x86, 0x57, 0x8f, 0x51, 0xab, 0x0c, 0xff, 0x1e, - 0x32, 0xfc, 0x3f, 0x93, 0x81, 0xca, 0xf1, 0x83, 0x0f, 0xc0, 0xaa, 0xfd, 0xbd, 0xbd, 0x4f, 0xf4, 0x2a, 0x18, 0x9f, - 0x43, 0x40, 0x5f, 0x29, 0x0f, 0x9d, 0x71, 0x34, 0x3c, 0x82, 0x7e, 0xb0, 0x00, 0xec, 0xd6, 0xb9, 0x1a, 0x72, 0xb6, - 0x4a, 0x9b, 0xe9, 0x77, 0x87, 0x65, 0xb3, 0x73, 0x04, 0xfb, 0xc4, 0xf8, 0x6a, 0xce, 0x78, 0x49, 0x3b, 0x26, 0x8f, - 0x60, 0x2e, 0xa4, 0xfd, 0x76, 0x2d, 0x0f, 0x2d, 0xc9, 0x73, 0xf5, 0x24, 0x6a, 0x77, 0x8b, 0x02, 0x8a, 0x84, 0xa2, - 0xa4, 0x73, 0x9f, 0xd6, 0x47, 0xf3, 0x5c, 0xe7, 0x83, 0xf9, 0x2c, 0x7a, 0x76, 0x54, 0x07, 0xee, 0x20, 0xe1, 0x65, - 0x39, 0xa4, 0x62, 0x2b, 0x3e, 0xcf, 0xc0, 0x70, 0xda, 0x19, 0xa6, 0x82, 0xd4, 0xac, 0xda, 0xcf, 0x05, 0x64, 0x26, - 0x07, 0xaa, 0x07, 0x2b, 0x8e, 0xcb, 0xb5, 0x94, 0x0d, 0x07, 0xdd, 0x5d, 0x4e, 0xbb, 0xb9, 0xb7, 0x30, 0x13, 0xa7, - 0x23, 0x39, 0x5b, 0x8b, 0x39, 0x0e, 0x3b, 0x5a, 0x2f, 0x96, 0x24, 0xbb, 0x5b, 0x75, 0xcd, 0x9a, 0xe7, 0x4e, 0xa6, - 0x32, 0xe7, 0xfc, 0x89, 0x5f, 0x90, 0x90, 0x66, 0x8b, 0x7e, 0x55, 0x14, 0xc5, 0x02, 0xa0, 0xa0, 0x8e, 0xc9, 0x44, - 0xf3, 0x00, 0x8f, 0x14, 0xdb, 0x85, 0x99, 0x38, 0x50, 0x1b, 0xc6, 0x46, 0x48, 0xeb, 0xcf, 0x16, 0x27, 0x77, 0xbc, - 0x05, 0xa4, 0x64, 0x01, 0x42, 0x5a, 0x88, 0x47, 0x30, 0xf3, 0x58, 0x13, 0xc6, 0x2f, 0xad, 0x57, 0xc7, 0x64, 0xd1, - 0x97, 0x14, 0x80, 0x45, 0xab, 0xd1, 0x85, 0x65, 0x01, 0x45, 0xc3, 0x14, 0xc6, 0x79, 0x30, 0xf6, 0xda, 0xdd, 0x11, - 0xf7, 0x07, 0xe4, 0x70, 0xa2, 0x2e, 0x2a, 0xba, 0x5b, 0x7c, 0x5c, 0x0b, 0xc9, 0x8a, 0xbd, 0xd3, 0x17, 0xd6, 0x39, - 0x1c, 0x16, 0x28, 0xa8, 0x4b, 0x20, 0xa5, 0x94, 0x2f, 0xb4, 0x0e, 0x87, 0x49, 0x5a, 0x8b, 0x1e, 0xa7, 0xb3, 0x18, - 0x7d, 0x40, 0x3f, 0x97, 0xf5, 0x9f, 0xa8, 0xd5, 0x59, 0x3c, 0xd4, 0xa4, 0x83, 0x44, 0xef, 0x2c, 0x1b, 0xc0, 0xb4, - 0x9e, 0x3b, 0x13, 0x78, 0x57, 0xfd, 0x96, 0x12, 0x06, 0x9e, 0x83, 0x99, 0xba, 0x5e, 0x9e, 0xf0, 0xf6, 0xdb, 0x1d, - 0x12, 0x4d, 0xc5, 0xf2, 0x9e, 0x4e, 0x93, 0x20, 0xef, 0x0c, 0x8f, 0x0f, 0xaf, 0x1b, 0xa9, 0xbd, 0x13, 0xd4, 0xa3, - 0x62, 0x4a, 0x7c, 0xef, 0x0b, 0x6f, 0x24, 0x2f, 0x8a, 0x60, 0x59, 0x9c, 0x91, 0x52, 0x65, 0xef, 0xc8, 0xfa, 0x53, - 0x11, 0x8c, 0x40, 0xc0, 0xe9, 0xdd, 0xc0, 0xfc, 0xc8, 0x74, 0x58, 0x1c, 0x2e, 0xa4, 0xe8, 0xa3, 0x3a, 0x5f, 0x77, - 0x95, 0x6d, 0x7d, 0xe1, 0xe8, 0x3e, 0x0b, 0x5f, 0xdd, 0x97, 0xa5, 0xc1, 0xe3, 0x65, 0x69, 0x80, 0x54, 0x23, 0xf3, - 0xb2, 0xd9, 0x25, 0x03, 0x5d, 0x20, 0x46, 0xf0, 0x3b, 0x78, 0x16, 0xbe, 0x06, 0xfe, 0xff, 0x4a, 0xbd, 0xf9, 0xc3, - 0xc5, 0xe6, 0x2b, 0x2a, 0xcd, 0x57, 0x56, 0x19, 0xe3, 0x9d, 0x72, 0x1e, 0x66, 0x50, 0x4e, 0x18, 0x16, 0x6c, 0xe5, - 0xff, 0x2f, 0xa0, 0xfd, 0x77, 0x1c, 0xc3, 0x17, 0xfe, 0x14, 0xcf, 0x90, 0x1e, 0x0c, 0x44, 0x38, 0x9c, 0xa2, 0xc9, - 0xab, 0x11, 0x1e, 0xf9, 0x48, 0xb5, 0x30, 0x63, 0x34, 0x81, 0x7e, 0x0f, 0xf9, 0x63, 0x1c, 0x4e, 0x60, 0x03, 0x05, - 0x3e, 0x8e, 0xde, 0x04, 0x21, 0x1e, 0x47, 0x40, 0x15, 0x78, 0x38, 0x0c, 0x90, 0xa1, 0x1d, 0xe3, 0x00, 0xc4, 0x29, - 0x92, 0xb0, 0x06, 0xa0, 0xb3, 0x10, 0x7b, 0x13, 0x10, 0x37, 0xc6, 0xde, 0x0c, 0x4f, 0xc7, 0x68, 0x8a, 0x27, 0x00, - 0x1d, 0x1e, 0x45, 0x95, 0x13, 0x61, 0x1f, 0xb6, 0xc3, 0x31, 0x99, 0xe2, 0x51, 0x88, 0xf4, 0x60, 0xe0, 0x98, 0x80, - 0x08, 0x07, 0x7b, 0xfe, 0x9b, 0x10, 0x07, 0x13, 0xd0, 0x3b, 0x1a, 0xbd, 0x00, 0xb1, 0xb3, 0x11, 0x32, 0xa3, 0x81, - 0x17, 0x14, 0x44, 0x8f, 0x81, 0x16, 0xfc, 0x75, 0x41, 0x03, 0x48, 0x7c, 0x14, 0xe2, 0x19, 0xc4, 0xae, 0xaf, 0xf8, - 0xcd, 0x68, 0x70, 0xf3, 0x7d, 0xe4, 0xfd, 0x61, 0xcc, 0xc2, 0xbf, 0x2e, 0x66, 0xbe, 0x42, 0x00, 0xa6, 0xa0, 0x1b, - 0xe4, 0x20, 0x3d, 0x18, 0xdd, 0xc0, 0x3c, 0x7d, 0x35, 0x43, 0x53, 0xe0, 0x1a, 0x4f, 0xd1, 0x0c, 0x45, 0x0a, 0x5d, - 0x60, 0x1f, 0x19, 0x26, 0x07, 0x98, 0xbe, 0x12, 0xc6, 0xd1, 0x9f, 0x18, 0xc6, 0xc7, 0x7c, 0xfa, 0x13, 0xbb, 0xf4, - 0xff, 0x48, 0x41, 0xd0, 0x8e, 0xe9, 0x36, 0x2c, 0x76, 0xcd, 0x95, 0x5e, 0x75, 0x51, 0x70, 0x43, 0x87, 0x6e, 0x04, - 0x2e, 0xf9, 0x3e, 0x62, 0x79, 0x52, 0xfa, 0xe9, 0x67, 0xdd, 0x39, 0x50, 0xfa, 0x69, 0xac, 0xcb, 0x79, 0x7a, 0x53, - 0x52, 0xf4, 0xfa, 0xfa, 0x1d, 0xdc, 0xca, 0xaa, 0x0a, 0xf1, 0x66, 0x0b, 0x97, 0xbf, 0x3d, 0x92, 0x8d, 0xba, 0xce, - 0x73, 0xe8, 0x15, 0xd5, 0x14, 0xee, 0x0d, 0xa8, 0x6f, 0x16, 0x30, 0xc6, 0xf1, 0xb2, 0x4b, 0xdf, 0x55, 0x94, 0x08, - 0x8a, 0x56, 0x6c, 0x43, 0x11, 0x93, 0xd0, 0x07, 0xd4, 0x14, 0x49, 0xa6, 0x86, 0x33, 0xa3, 0xa6, 0x83, 0x9e, 0x56, - 0x2b, 0x31, 0xdd, 0x30, 0x58, 0x02, 0x62, 0xd2, 0xbe, 0xed, 0x8d, 0xcb, 0xd0, 0x58, 0x75, 0x4d, 0xa5, 0x84, 0x8e, - 0x41, 0x59, 0x15, 0xa6, 0xb1, 0xba, 0x76, 0x22, 0xa2, 0x2f, 0x06, 0x89, 0xbb, 0x65, 0x05, 0x53, 0x97, 0xf9, 0x34, - 0xd6, 0xad, 0xa2, 0x92, 0xa0, 0xba, 0x15, 0xf3, 0xe5, 0x41, 0xcf, 0x2a, 0xca, 0x57, 0x70, 0x9b, 0x84, 0x77, 0x01, - 0xcd, 0x43, 0x46, 0xcb, 0xa6, 0x82, 0xe6, 0x24, 0xb9, 0xbe, 0xfe, 0xe9, 0xef, 0xea, 0x33, 0x85, 0x32, 0xe1, 0xcc, - 0x09, 0x7d, 0xbe, 0x61, 0x54, 0x93, 0x9e, 0x6f, 0x3c, 0x32, 0x1f, 0x1c, 0x5a, 0xe8, 0xd3, 0xc1, 0xbf, 0xfc, 0x33, - 0x29, 0xef, 0x4e, 0x9b, 0xbd, 0x24, 0xfd, 0x5f, 0x37, 0x9d, 0x86, 0x49, 0xac, 0x97, 0x35, 0x93, 0xe9, 0x35, 0x18, - 0x18, 0xbb, 0xe6, 0x01, 0x38, 0xa7, 0x1c, 0x30, 0xb4, 0x65, 0xcf, 0x03, 0x60, 0xff, 0x72, 0xf3, 0x02, 0xfd, 0xda, - 0xc2, 0x09, 0xa6, 0x06, 0x7b, 0xed, 0x65, 0x4d, 0x65, 0xd9, 0xe4, 0xc9, 0xbb, 0x5f, 0xae, 0x6f, 0xce, 0x1e, 0xaf, - 0x35, 0x11, 0xa2, 0x3c, 0x33, 0x1f, 0x42, 0xd6, 0x95, 0x64, 0x2d, 0xe9, 0xa4, 0x16, 0xeb, 0xa8, 0x10, 0x38, 0x79, - 0xa4, 0x9f, 0x17, 0xac, 0xa2, 0xc6, 0xa9, 0x9e, 0xd1, 0x4d, 0xd1, 0x97, 0x6c, 0x3c, 0xe9, 0x7e, 0x60, 0xa5, 0x6b, - 0x4e, 0x89, 0x6b, 0x8e, 0x8c, 0xab, 0x3f, 0x13, 0xfd, 0x0b, 0x65, 0x37, 0xa3, 0x8e, 0x36, 0x12, 0x00, 0x00}; + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x69, 0x6f, 0xdc, 0x36, 0x1a, 0xfe, 0xde, + 0x5f, 0xc1, 0x2a, 0x49, 0x47, 0xd3, 0x58, 0xd4, 0x35, 0x9a, 0x53, 0x9a, 0xc2, 0xf1, 0xa6, 0x68, 0x81, 0xa4, 0x0d, + 0x60, 0xb7, 0xfd, 0x10, 0x04, 0x30, 0x47, 0xa2, 0x46, 0x8c, 0x25, 0x4a, 0x2b, 0x72, 0xae, 0x0c, 0x66, 0x7f, 0xfb, + 0xbe, 0x24, 0x35, 0xe3, 0xb1, 0x37, 0x5e, 0x6c, 0x8a, 0x2d, 0x8a, 0xd6, 0x71, 0x68, 0x1e, 0xef, 0xf9, 0x88, 0xef, + 0x21, 0xc5, 0x5f, 0x67, 0x75, 0x2a, 0x77, 0x0d, 0x45, 0x85, 0xac, 0xca, 0x79, 0xac, 0x46, 0x54, 0x12, 0xbe, 0x4c, + 0x28, 0x87, 0x15, 0x25, 0xd9, 0x3c, 0xae, 0xa8, 0x24, 0x28, 0x2d, 0x48, 0x2b, 0xa8, 0x4c, 0x7e, 0xb9, 0xf9, 0xde, + 0x19, 0x23, 0x77, 0x1e, 0x97, 0x8c, 0xdf, 0xa1, 0x96, 0x96, 0x09, 0x4b, 0x6b, 0x8e, 0x8a, 0x96, 0xe6, 0x49, 0x46, + 0x24, 0x99, 0xb2, 0x8a, 0x2c, 0xa9, 0x22, 0xd0, 0x6c, 0x9c, 0x54, 0x34, 0x59, 0x33, 0xba, 0x69, 0xea, 0x56, 0x22, + 0xa0, 0x94, 0x94, 0xcb, 0xc4, 0xda, 0xb0, 0x4c, 0x16, 0x49, 0x46, 0xd7, 0x2c, 0xa5, 0x8e, 0x5e, 0x5c, 0x30, 0xce, + 0x24, 0x23, 0xa5, 0x23, 0x52, 0x52, 0xd2, 0xc4, 0xbf, 0x58, 0x09, 0xda, 0xea, 0x05, 0x59, 0xc0, 0x9a, 0xd7, 0x16, + 0x88, 0x14, 0x69, 0xcb, 0x1a, 0x89, 0x94, 0xbd, 0x49, 0x55, 0x67, 0xab, 0x92, 0xce, 0x5d, 0x97, 0x08, 0xb0, 0x4b, + 0xb8, 0x8c, 0x67, 0x74, 0x8b, 0x87, 0x61, 0x98, 0x06, 0x64, 0x94, 0xe3, 0x8f, 0xe2, 0x2b, 0xf0, 0x6c, 0x55, 0x81, + 0x3a, 0x5c, 0xd6, 0x29, 0x91, 0xac, 0xe6, 0x58, 0x50, 0xd2, 0xa6, 0x45, 0x92, 0x24, 0xd6, 0x77, 0x82, 0xac, 0xa9, + 0xf5, 0xcd, 0x37, 0xf6, 0x89, 0x68, 0x49, 0xe5, 0xeb, 0x92, 0xaa, 0xa9, 0x78, 0xb5, 0xbb, 0x21, 0xcb, 0x9f, 0xc0, + 0x72, 0xdb, 0x22, 0x82, 0x65, 0xd4, 0xea, 0xbf, 0xf7, 0x3e, 0x60, 0x21, 0x77, 0x25, 0xc5, 0x19, 0x13, 0x4d, 0x49, + 0x76, 0x89, 0xb5, 0x00, 0xa9, 0x77, 0x56, 0x7f, 0x96, 0xaf, 0x78, 0xaa, 0x84, 0x23, 0x61, 0xd3, 0xfe, 0xbe, 0xa4, + 0x60, 0x5e, 0xf2, 0x96, 0xc8, 0x02, 0x57, 0x64, 0x6b, 0x9b, 0x09, 0xe3, 0x76, 0xf0, 0xad, 0x4d, 0x5f, 0xfa, 0x9e, + 0xd7, 0xbf, 0xd0, 0x83, 0xd7, 0x77, 0xe1, 0xef, 0xac, 0xa5, 0x72, 0xd5, 0x72, 0x44, 0xec, 0xdb, 0xb8, 0x01, 0x4a, + 0x94, 0x25, 0x56, 0xe5, 0x07, 0xd8, 0xf3, 0xc6, 0xc8, 0x9f, 0xe0, 0x20, 0x72, 0x7c, 0x1f, 0x87, 0x8e, 0x1f, 0xa5, + 0x23, 0x27, 0x42, 0xfe, 0x00, 0x86, 0x20, 0xc0, 0x11, 0xf2, 0x3e, 0x59, 0x28, 0x67, 0x65, 0x99, 0x58, 0xbc, 0xe6, + 0xd4, 0x42, 0x42, 0xb6, 0xf5, 0x1d, 0x4d, 0xac, 0x74, 0xd5, 0xb6, 0x60, 0xff, 0x55, 0x5d, 0xd6, 0x2d, 0xc0, 0xf5, + 0x15, 0x7a, 0xf0, 0xf3, 0xc5, 0x2a, 0x64, 0x4b, 0xb8, 0xc8, 0xeb, 0xb6, 0x4a, 0x2c, 0xfd, 0x50, 0xec, 0xe7, 0x7b, + 0x79, 0x40, 0x6a, 0xe8, 0x9f, 0x1d, 0x3a, 0x75, 0xcb, 0x96, 0x8c, 0x27, 0x96, 0x1f, 0x20, 0x7f, 0x0c, 0x6a, 0x6f, + 0xfb, 0x87, 0x13, 0x26, 0x44, 0x61, 0xd2, 0x79, 0x59, 0xdb, 0xef, 0x6f, 0x63, 0xb1, 0x5e, 0xa2, 0x6d, 0x55, 0x72, + 0x91, 0x58, 0x85, 0x94, 0xcd, 0xd4, 0x75, 0x37, 0x9b, 0x0d, 0xde, 0x84, 0xb8, 0x6e, 0x97, 0x6e, 0xe0, 0x79, 0x9e, + 0x0b, 0x14, 0x16, 0x32, 0xf7, 0xc3, 0x0a, 0x06, 0x16, 0x2a, 0x28, 0x5b, 0x16, 0x52, 0xcf, 0xe7, 0xcf, 0xf7, 0xf4, + 0x10, 0x2b, 0x8a, 0xf9, 0xed, 0x87, 0x33, 0x2d, 0xec, 0x4c, 0x0b, 0xfd, 0xee, 0x0c, 0xcd, 0xde, 0x5b, 0x65, 0xd4, + 0x88, 0x04, 0x28, 0x40, 0x9e, 0xfe, 0x17, 0x38, 0x6a, 0xde, 0xad, 0x9c, 0x47, 0x2b, 0x74, 0xb6, 0x82, 0xbf, 0x80, + 0x5f, 0x50, 0x0d, 0x9d, 0xc9, 0x89, 0xdd, 0x57, 0xc7, 0x6b, 0xdf, 0xbb, 0xdf, 0x50, 0x3c, 0x3f, 0x0c, 0xcf, 0xd7, + 0x4e, 0xf0, 0xab, 0x22, 0x50, 0xd8, 0x9f, 0x98, 0x9c, 0xa0, 0xf0, 0x7f, 0x1d, 0x92, 0x08, 0x45, 0xdd, 0x4e, 0xe4, + 0xa8, 0xf9, 0x69, 0xa5, 0x34, 0xa1, 0x68, 0x0d, 0x54, 0x95, 0x33, 0x74, 0x22, 0x12, 0xa2, 0xb0, 0x33, 0x09, 0x66, + 0xb0, 0x3d, 0x04, 0xe6, 0xb3, 0x3d, 0x27, 0xfc, 0xd4, 0x53, 0x30, 0x4f, 0x2d, 0xeb, 0x1e, 0x83, 0xfa, 0x1c, 0x03, + 0xfc, 0xb1, 0x86, 0x3b, 0x67, 0x59, 0x80, 0x11, 0x95, 0x69, 0x61, 0x5b, 0x2e, 0x44, 0x5e, 0xce, 0x96, 0x10, 0x15, + 0x35, 0xb7, 0xfa, 0x58, 0x16, 0x94, 0xdb, 0x47, 0x56, 0xc5, 0x48, 0xf5, 0x89, 0xfd, 0xf8, 0x44, 0xf6, 0xf7, 0xa7, + 0xf8, 0x90, 0x4c, 0x42, 0x1c, 0x4a, 0xac, 0x22, 0xfa, 0xe2, 0xb4, 0xbb, 0xa8, 0xb3, 0xdd, 0x13, 0xa1, 0x53, 0xf8, + 0x26, 0x6e, 0x18, 0xe7, 0xb4, 0xbd, 0xa1, 0x5b, 0x78, 0x86, 0x6f, 0x2f, 0xaf, 0xd0, 0x65, 0x96, 0xb5, 0x54, 0x88, + 0x29, 0xb2, 0x5e, 0x4a, 0x88, 0x91, 0xf4, 0x7f, 0x97, 0xe5, 0x3f, 0x90, 0xf5, 0x1b, 0xfb, 0x9e, 0xa1, 0x9f, 0xa8, + 0xdc, 0xd4, 0xed, 0x5d, 0x27, 0x4d, 0x99, 0x36, 0x53, 0x11, 0xd8, 0x82, 0x9d, 0xa4, 0x11, 0x58, 0x94, 0x90, 0x5f, + 0x6c, 0xbf, 0x0f, 0x7a, 0x9a, 0x7b, 0xaf, 0xf8, 0x11, 0xa8, 0xdb, 0x38, 0x63, 0x6b, 0x94, 0x96, 0x90, 0x41, 0x20, + 0x94, 0x8c, 0x28, 0x0b, 0x75, 0x61, 0x53, 0xf3, 0x14, 0xb8, 0xef, 0x12, 0xeb, 0x33, 0x19, 0xe2, 0xd5, 0xee, 0xc7, + 0xcc, 0xee, 0x09, 0xc8, 0x0d, 0xbd, 0x3e, 0x5e, 0x93, 0x72, 0x45, 0x51, 0x82, 0x64, 0xc1, 0xc4, 0xbd, 0x81, 0xb3, + 0x27, 0xd9, 0x1a, 0x71, 0x07, 0x5c, 0x39, 0x1c, 0x0b, 0xbb, 0x6f, 0x1d, 0xa3, 0x34, 0x26, 0x26, 0x87, 0x5a, 0xcf, + 0xac, 0x47, 0x16, 0x39, 0x25, 0xcd, 0xa5, 0x75, 0x1f, 0xcd, 0xcf, 0xf7, 0xc2, 0xe6, 0xb8, 0x05, 0xed, 0xfd, 0xc3, + 0x69, 0x33, 0x16, 0x0d, 0xe1, 0x8f, 0x19, 0x95, 0x81, 0x2a, 0x68, 0x20, 0xf1, 0xc1, 0x4c, 0x45, 0x0e, 0x10, 0x9d, + 0x14, 0xba, 0xe4, 0x38, 0x7d, 0xbe, 0x67, 0x20, 0x51, 0xe5, 0xb3, 0x93, 0xc4, 0xd8, 0x05, 0x68, 0xe6, 0xb7, 0x87, + 0xfe, 0xbd, 0x1f, 0xff, 0x5c, 0xd1, 0x76, 0x77, 0x4d, 0x4b, 0x9a, 0xca, 0xba, 0xb5, 0xad, 0x67, 0xa0, 0x05, 0xae, + 0x92, 0x76, 0xf8, 0x87, 0x9b, 0xb7, 0x6f, 0x92, 0xda, 0x6e, 0xfb, 0x17, 0x4f, 0x51, 0xab, 0x6a, 0xf1, 0x1e, 0xaa, + 0xc5, 0xbf, 0x92, 0x9e, 0xaa, 0x17, 0xbd, 0x0f, 0xc0, 0xaa, 0xfd, 0xbd, 0xbd, 0x2f, 0x1a, 0x2a, 0xb0, 0x5f, 0x42, + 0x72, 0xb8, 0x50, 0x1e, 0x3a, 0xc3, 0xa8, 0x7f, 0x00, 0xfd, 0x60, 0x01, 0xd8, 0xad, 0xf3, 0x3e, 0xe4, 0x7f, 0x95, + 0x82, 0xe7, 0xdf, 0xee, 0x17, 0xf5, 0xd6, 0x11, 0xec, 0x13, 0xe3, 0xcb, 0x29, 0xe3, 0x05, 0x6d, 0x99, 0x3c, 0x80, + 0xb9, 0x50, 0x42, 0x9a, 0x95, 0xdc, 0x37, 0x24, 0xcb, 0xd4, 0x49, 0xd4, 0x6c, 0x67, 0x39, 0x14, 0x1c, 0x45, 0x49, + 0xa7, 0x3e, 0xad, 0x0e, 0xe6, 0x5c, 0xe7, 0x96, 0xe9, 0x24, 0x7a, 0x71, 0x50, 0x17, 0x6e, 0x2f, 0xe1, 0x61, 0x39, + 0xa4, 0x64, 0x4b, 0x3e, 0x4d, 0xc1, 0x70, 0xda, 0x1a, 0xa6, 0x9c, 0x54, 0xac, 0xdc, 0x4d, 0x05, 0x64, 0x39, 0x07, + 0x2a, 0x11, 0xcb, 0x0f, 0x8b, 0x95, 0x94, 0x35, 0x07, 0xdd, 0x6d, 0x46, 0xdb, 0xa9, 0x37, 0x33, 0x13, 0xa7, 0x25, + 0x19, 0x5b, 0x89, 0x29, 0x0e, 0x5b, 0x5a, 0xcd, 0x16, 0x24, 0xbd, 0x5b, 0xb6, 0xf5, 0x8a, 0x67, 0x4e, 0xaa, 0xb2, + 0xf0, 0xf4, 0x99, 0x9f, 0x93, 0x90, 0xa6, 0xb3, 0x6e, 0x95, 0xe7, 0xf9, 0x0c, 0xa0, 0xa0, 0x8e, 0xc9, 0x6a, 0xd3, + 0x00, 0x0f, 0x14, 0xdb, 0x99, 0x99, 0x38, 0x50, 0x1b, 0xc6, 0x46, 0x28, 0x11, 0x2f, 0x66, 0x47, 0x77, 0xbc, 0x19, + 0xa4, 0x77, 0x01, 0x42, 0x1a, 0x88, 0x6d, 0x30, 0xf3, 0x50, 0x11, 0xc6, 0xcf, 0xad, 0x57, 0xd7, 0x64, 0xd6, 0x95, + 0x27, 0x80, 0x45, 0xab, 0xd1, 0x45, 0x6a, 0x06, 0x05, 0xc8, 0x14, 0xd9, 0x69, 0x30, 0xf4, 0x9a, 0xed, 0x01, 0x77, + 0x17, 0x64, 0x7f, 0xa4, 0xce, 0x4b, 0xba, 0x9d, 0x7d, 0x5c, 0x09, 0xc9, 0xf2, 0x9d, 0xd3, 0x15, 0xe9, 0x29, 0x5c, + 0x16, 0x28, 0xce, 0x0b, 0x20, 0xa5, 0x94, 0xcf, 0xb4, 0x0e, 0x87, 0x49, 0x5a, 0x89, 0x0e, 0xa7, 0x93, 0x18, 0x7d, + 0x41, 0x1f, 0xca, 0xfa, 0x6f, 0xd4, 0xea, 0x2e, 0xee, 0x2b, 0xd2, 0x42, 0xd1, 0x70, 0x16, 0x35, 0x60, 0x5a, 0x4d, + 0x9d, 0x11, 0x3c, 0xab, 0x6e, 0x4b, 0x09, 0x03, 0xcf, 0xc1, 0x4c, 0x5d, 0x7b, 0x8f, 0x78, 0xfb, 0xcd, 0x16, 0x89, + 0xba, 0x64, 0x59, 0x47, 0xa7, 0x49, 0x90, 0x77, 0x82, 0xc7, 0x87, 0xc7, 0x8d, 0xd4, 0xde, 0x11, 0xea, 0x41, 0x3e, + 0x26, 0xbe, 0xf7, 0x99, 0x27, 0x92, 0xe5, 0x79, 0xb0, 0xc8, 0x4f, 0x48, 0xa9, 0x12, 0x7a, 0x60, 0xdd, 0xad, 0x08, + 0x06, 0x20, 0xe0, 0xf8, 0x6c, 0x60, 0x7e, 0x60, 0x3a, 0x2c, 0xf6, 0x67, 0x52, 0xf4, 0x55, 0x9d, 0xae, 0xda, 0xd2, + 0xb6, 0x3e, 0x73, 0x75, 0x5f, 0x84, 0x57, 0xf7, 0x25, 0xae, 0xf7, 0x74, 0x89, 0xeb, 0x21, 0xd5, 0x14, 0xbd, 0xaa, + 0xb7, 0x49, 0x4f, 0x17, 0x9b, 0x01, 0xfc, 0xf6, 0x5e, 0x84, 0xaf, 0x81, 0xff, 0xff, 0x52, 0xbb, 0x7e, 0x77, 0xe1, + 0xfa, 0x82, 0xaa, 0xf5, 0x85, 0x15, 0xcb, 0x78, 0xa7, 0x9c, 0x87, 0x19, 0x94, 0x26, 0x86, 0x05, 0x5b, 0xfa, 0x7f, + 0x04, 0xb4, 0xff, 0x89, 0x63, 0x78, 0xe9, 0x8f, 0xf1, 0x04, 0xe9, 0xc1, 0x40, 0x84, 0xc3, 0x31, 0x1a, 0x5d, 0x0d, + 0xf0, 0xc0, 0x47, 0xaa, 0x1d, 0x1a, 0xa2, 0x11, 0x1e, 0x03, 0xc1, 0x10, 0x87, 0x23, 0xd8, 0x40, 0x81, 0x8f, 0xa3, + 0x37, 0x41, 0x88, 0x87, 0x11, 0x50, 0x05, 0x1e, 0x0e, 0x03, 0x64, 0x68, 0x87, 0x38, 0x00, 0x71, 0x8a, 0x24, 0xac, + 0x00, 0xe8, 0x34, 0xc4, 0xde, 0x08, 0xc4, 0x0d, 0xb1, 0x37, 0xc1, 0xe3, 0x21, 0x1a, 0xe3, 0x11, 0x40, 0x87, 0x07, + 0x51, 0xe9, 0x44, 0xd8, 0x87, 0xed, 0x70, 0x48, 0xc6, 0x78, 0x10, 0x22, 0x3d, 0x18, 0x38, 0x46, 0x20, 0xc2, 0xc1, + 0x9e, 0xff, 0x26, 0xc4, 0xc1, 0x08, 0xf4, 0x0e, 0x06, 0x97, 0x20, 0x76, 0x32, 0x40, 0x66, 0x34, 0xf0, 0x82, 0x82, + 0xe8, 0x29, 0xd0, 0x82, 0xbf, 0x2f, 0x68, 0x00, 0x89, 0x8f, 0x42, 0x3c, 0x81, 0xd8, 0xf5, 0x15, 0xbf, 0x19, 0x0d, + 0x6e, 0xbe, 0x8f, 0xbc, 0xdf, 0x8d, 0x59, 0xf8, 0xf7, 0xc5, 0xcc, 0x57, 0x08, 0xc0, 0x14, 0x74, 0x83, 0x1c, 0xa4, + 0x07, 0xa3, 0x1b, 0x98, 0xc7, 0x57, 0x13, 0x34, 0x06, 0xae, 0xe1, 0x18, 0x4d, 0x50, 0xa4, 0xd0, 0x05, 0xf6, 0x81, + 0x61, 0x72, 0x80, 0xe9, 0x0b, 0x61, 0x1c, 0xfc, 0x85, 0x61, 0x7c, 0xca, 0xa7, 0xbf, 0xb0, 0x4b, 0x7f, 0x46, 0x0a, + 0x82, 0x76, 0x4c, 0xb7, 0x61, 0xb1, 0x6b, 0x3e, 0x0f, 0xa8, 0x2e, 0x0a, 0xde, 0xf6, 0xa1, 0x1b, 0x99, 0xc7, 0x85, + 0x8f, 0x58, 0x96, 0x40, 0x57, 0x3f, 0x3f, 0x6b, 0xf5, 0x81, 0xd0, 0x3f, 0x1e, 0xc1, 0xec, 0x41, 0xe3, 0x6e, 0xce, + 0x74, 0xa5, 0x9f, 0xdf, 0x14, 0x14, 0xbd, 0xbe, 0x7e, 0x07, 0x2f, 0x7f, 0x65, 0x89, 0x78, 0xbd, 0x81, 0x77, 0xcc, + 0x1d, 0x92, 0xb5, 0xfa, 0x6a, 0xc0, 0xa1, 0x8d, 0x54, 0x53, 0x78, 0x3d, 0x41, 0x5d, 0x1f, 0x81, 0x31, 0x8e, 0x17, + 0xed, 0xfc, 0x5d, 0x49, 0x89, 0xa0, 0x68, 0xc9, 0xd6, 0x14, 0x31, 0x09, 0x2d, 0x42, 0x45, 0x91, 0x64, 0x6a, 0x38, + 0x31, 0x6a, 0x3a, 0x68, 0x77, 0xb5, 0x12, 0xd3, 0x28, 0x83, 0x25, 0x20, 0x66, 0xde, 0x75, 0xc4, 0x71, 0x11, 0x1a, + 0xab, 0xae, 0xa9, 0x94, 0xd0, 0x4c, 0x28, 0xab, 0xc2, 0x79, 0xac, 0xde, 0x6e, 0x11, 0xd1, 0xef, 0x0c, 0x89, 0xbb, + 0x61, 0x39, 0x53, 0xdf, 0x0c, 0xe6, 0xb1, 0xee, 0x22, 0x95, 0x04, 0xd5, 0xc8, 0x98, 0x0f, 0x1c, 0x7a, 0x56, 0x52, + 0xbe, 0x84, 0x97, 0x56, 0x78, 0x4c, 0xd0, 0x57, 0xa4, 0xb4, 0xa8, 0x4b, 0xe8, 0x5b, 0x92, 0xeb, 0xeb, 0x1f, 0xff, + 0xa1, 0xbe, 0x86, 0x28, 0x13, 0x4e, 0x9c, 0xf0, 0x0a, 0x60, 0x18, 0xd5, 0xa4, 0xe3, 0x1b, 0x0e, 0xcc, 0x77, 0x8d, + 0x06, 0x5a, 0x78, 0xf0, 0x2f, 0x7b, 0x20, 0xe5, 0xdd, 0x71, 0xb3, 0x93, 0xa4, 0xff, 0xeb, 0x7e, 0xd4, 0x30, 0x89, + 0xd5, 0xa2, 0x62, 0x72, 0x7e, 0x0d, 0x06, 0xc6, 0xae, 0x39, 0x00, 0xe7, 0x94, 0x03, 0x86, 0xb6, 0xe8, 0x78, 0x00, + 0xec, 0x9f, 0x6f, 0x2e, 0xd1, 0x2f, 0x0d, 0x5c, 0x6e, 0x6a, 0xb0, 0xd7, 0x5e, 0x56, 0x54, 0x16, 0x75, 0x96, 0xbc, + 0xfb, 0xf9, 0xfa, 0xe6, 0xe4, 0xf1, 0x4a, 0x13, 0x21, 0xca, 0x53, 0xf3, 0xbd, 0x65, 0x55, 0x4a, 0xd6, 0x90, 0x56, + 0x6a, 0xb1, 0x8e, 0x8a, 0x8e, 0xa3, 0x47, 0xfa, 0x3c, 0x67, 0x25, 0x35, 0x4e, 0x75, 0x8c, 0xee, 0x1c, 0x7d, 0xce, + 0xc6, 0xa3, 0xee, 0x47, 0x56, 0xba, 0xe6, 0x02, 0xb9, 0xe6, 0x36, 0xb9, 0xfa, 0x6b, 0xd4, 0xbf, 0x01, 0x14, 0xee, + 0xbc, 0x64, 0x9d, 0x12, 0x00, 0x00}; } // namespace captive_portal } // namespace esphome diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 78eee4b226..630e00f0b7 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -12,7 +12,7 @@ static const char *const TAG = "captive_portal"; void CaptivePortal::handle_config(AsyncWebServerRequest *request) { AsyncResponseStream *stream = request->beginResponseStream("application/json"); stream->addHeader("cache-control", "public, max-age=0, must-revalidate"); - stream->printf(R"({"name":"%s","aps":[{})", App.get_name().c_str()); + stream->printf(R"({"mac":"%s","name":"%s","aps":[{})", get_mac_address_pretty().c_str(), App.get_name().c_str()); for (auto &scan : wifi::global_wifi_component->get_scan_result()) { if (scan.get_is_hidden()) From 5441213b270d933f8f35830ee425010612adc935 Mon Sep 17 00:00:00 2001 From: tracestep <16390082+tracestep@users.noreply.github.com> Date: Mon, 8 Apr 2024 22:11:46 -0300 Subject: [PATCH 0040/1106] Adds i2c timeout config (#4614) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/i2c/__init__.py | 45 ++++++++++++++++++++-- esphome/components/i2c/i2c_bus_arduino.cpp | 21 ++++++++++ esphome/components/i2c/i2c_bus_arduino.h | 2 + esphome/components/i2c/i2c_bus_esp_idf.cpp | 29 +++++++++++--- esphome/components/i2c/i2c_bus_esp_idf.h | 2 + 5 files changed, 90 insertions(+), 9 deletions(-) diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index 0a1f049b93..f52a0edb9f 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -4,6 +4,7 @@ import esphome.final_validate as fv from esphome import pins from esphome.const import ( CONF_FREQUENCY, + CONF_TIMEOUT, CONF_ID, CONF_INPUT, CONF_OUTPUT, @@ -59,6 +60,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All( cv.frequency, cv.Range(min=0, min_included=False) ), + cv.Optional(CONF_TIMEOUT): cv.positive_time_period, cv.Optional(CONF_SCAN, default=True): cv.boolean, } ).extend(cv.COMPONENT_SCHEMA), @@ -81,6 +83,8 @@ async def to_code(config): cg.add(var.set_frequency(int(config[CONF_FREQUENCY]))) cg.add(var.set_scan(config[CONF_SCAN])) + if CONF_TIMEOUT in config: + cg.add(var.set_timeout(int(config[CONF_TIMEOUT].total_microseconds))) if CORE.using_arduino: cg.add_library("Wire", None) @@ -119,23 +123,56 @@ async def register_i2c_device(var, config): def final_validate_device_schema( - name: str, *, min_frequency: cv.frequency = None, max_frequency: cv.frequency = None + name: str, + *, + min_frequency: cv.frequency = None, + max_frequency: cv.frequency = None, + min_timeout: cv.time_period = None, + max_timeout: cv.time_period = None, ): hub_schema = {} - if min_frequency is not None: + if (min_frequency is not None) and (max_frequency is not None): + hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range( + min=cv.frequency(min_frequency), + min_included=True, + max=cv.frequency(max_frequency), + max_included=True, + msg=f"Component {name} requires a frequency between {min_frequency} and {max_frequency} for the I2C bus", + ) + elif min_frequency is not None: hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range( min=cv.frequency(min_frequency), min_included=True, msg=f"Component {name} requires a minimum frequency of {min_frequency} for the I2C bus", ) - - if max_frequency is not None: + elif max_frequency is not None: hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range( max=cv.frequency(max_frequency), max_included=True, msg=f"Component {name} cannot be used with a frequency of over {max_frequency} for the I2C bus", ) + if (min_timeout is not None) and (max_timeout is not None): + hub_schema[cv.Required(CONF_TIMEOUT)] = cv.Range( + min=cv.time_period(min_timeout), + min_included=True, + max=cv.time_period(max_timeout), + max_included=True, + msg=f"Component {name} requires a timeout between {min_timeout} and {max_timeout} for the I2C bus", + ) + elif min_timeout is not None: + hub_schema[cv.Required(CONF_TIMEOUT)] = cv.Range( + min=cv.time_period(min_timeout), + min_included=True, + msg=f"Component {name} requires a minimum timeout of {min_timeout} for the I2C bus", + ) + elif max_timeout is not None: + hub_schema[cv.Required(CONF_TIMEOUT)] = cv.Range( + max=cv.time_period(max_timeout), + max_included=True, + msg=f"Component {name} cannot be used with a timeout of over {max_timeout} for the I2C bus", + ) + return cv.Schema( {cv.Required(CONF_I2C_ID): fv.id_declaration_match_schema(hub_schema)}, extra=cv.ALLOW_EXTRA, diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index 0966bd4d97..cd1b2aacc7 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -52,6 +52,18 @@ void ArduinoI2CBus::set_pins_and_clock_() { #else wire_->begin(static_cast(sda_pin_), static_cast(scl_pin_)); #endif + if (timeout_ > 0) { // if timeout specified in yaml +#if defined(USE_ESP32) + // https://github.com/espressif/arduino-esp32/blob/master/libraries/Wire/src/Wire.cpp + wire_->setTimeOut(timeout_ / 1000); // unit: ms +#elif defined(USE_ESP8266) + // https://github.com/esp8266/Arduino/blob/master/libraries/Wire/Wire.h + wire_->setClockStretchLimit(timeout_); // unit: us +#elif defined(USE_RP2040) + // https://github.com/earlephilhower/ArduinoCore-API/blob/e37df85425e0ac020bfad226d927f9b00d2e0fb7/api/Stream.h + wire_->setTimeout(timeout_ / 1000); // unit: ms +#endif + } wire_->setClock(frequency_); } @@ -60,6 +72,15 @@ void ArduinoI2CBus::dump_config() { ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); + if (timeout_ > 0) { +#if defined(USE_ESP32) + ESP_LOGCONFIG(TAG, " Timeout: %u ms", this->timeout_ / 1000); +#elif defined(USE_ESP8266) + ESP_LOGCONFIG(TAG, " Timeout: %u us", this->timeout_); +#elif defined(USE_RP2040) + ESP_LOGCONFIG(TAG, " Timeout: %u ms", this->timeout_ / 1000); +#endif + } switch (this->recovery_result_) { case RECOVERY_COMPLETED: ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered"); diff --git a/esphome/components/i2c/i2c_bus_arduino.h b/esphome/components/i2c/i2c_bus_arduino.h index 6304c2b039..6a670a2a05 100644 --- a/esphome/components/i2c/i2c_bus_arduino.h +++ b/esphome/components/i2c/i2c_bus_arduino.h @@ -27,6 +27,7 @@ class ArduinoI2CBus : public I2CBus, public Component { void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; } void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } void set_frequency(uint32_t frequency) { frequency_ = frequency; } + void set_timeout(uint32_t timeout) { timeout_ = timeout; } private: void recover_(); @@ -38,6 +39,7 @@ class ArduinoI2CBus : public I2CBus, public Component { uint8_t sda_pin_; uint8_t scl_pin_; uint32_t frequency_; + uint32_t timeout_ = 0; bool initialized_ = false; }; diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index 5d35c1968b..cbb748cca1 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -1,12 +1,12 @@ #ifdef USE_ESP_IDF #include "i2c_bus_esp_idf.h" -#include "esphome/core/hal.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" -#include "esphome/core/application.h" -#include #include +#include +#include "esphome/core/application.h" +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace i2c { @@ -45,6 +45,20 @@ void IDFI2CBus::setup() { this->mark_failed(); return; } + if (timeout_ > 0) { // if timeout specified in yaml: + if (timeout_ > 13000) { + ESP_LOGW(TAG, "i2c timeout of %" PRIu32 "us greater than max of 13ms on esp-idf, setting to max", timeout_); + timeout_ = 13000; + } + err = i2c_set_timeout(port_, timeout_ * 80); // unit: APB 80MHz clock cycle + if (err != ESP_OK) { + ESP_LOGW(TAG, "i2c_set_timeout failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } else { + ESP_LOGV(TAG, "i2c_timeout set to %d ticks (%d us)", timeout_ * 80, timeout_); + } + } err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, ESP_INTR_FLAG_IRAM); if (err != ESP_OK) { ESP_LOGW(TAG, "i2c_driver_install failed: %s", esp_err_to_name(err)); @@ -62,6 +76,9 @@ void IDFI2CBus::dump_config() { ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); ESP_LOGCONFIG(TAG, " Frequency: %" PRIu32 " Hz", this->frequency_); + if (timeout_ > 0) { + ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 "us", this->timeout_); + } switch (this->recovery_result_) { case RECOVERY_COMPLETED: ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered"); @@ -127,6 +144,8 @@ ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { return ERROR_UNKNOWN; } err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS); + // i2c_master_cmd_begin() will block for a whole second if no ack: + // https://github.com/espressif/esp-idf/issues/4999 i2c_cmd_link_delete(cmd); if (err == ESP_FAIL) { // transfer not acked diff --git a/esphome/components/i2c/i2c_bus_esp_idf.h b/esphome/components/i2c/i2c_bus_esp_idf.h index c80ea8c99d..afb4c2d22b 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.h +++ b/esphome/components/i2c/i2c_bus_esp_idf.h @@ -29,6 +29,7 @@ class IDFI2CBus : public I2CBus, public Component { void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; } void set_frequency(uint32_t frequency) { frequency_ = frequency; } + void set_timeout(uint32_t timeout) { timeout_ = timeout; } private: void recover_(); @@ -41,6 +42,7 @@ class IDFI2CBus : public I2CBus, public Component { uint8_t scl_pin_; bool scl_pullup_enabled_; uint32_t frequency_; + uint32_t timeout_ = 0; bool initialized_ = false; }; From 3b6e8fa66649ab5e842c2e1c02e810f3af48ca5e Mon Sep 17 00:00:00 2001 From: Mat931 <49403702+Mat931@users.noreply.github.com> Date: Tue, 9 Apr 2024 01:43:53 +0000 Subject: [PATCH 0041/1106] Add ABB-Welcome / Busch-Welcome Door Intercom Protocol (#4689) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/remote_base/__init__.py | 102 +++++++ .../remote_base/abbwelcome_protocol.cpp | 123 +++++++++ .../remote_base/abbwelcome_protocol.h | 251 ++++++++++++++++++ 3 files changed, 476 insertions(+) create mode 100644 esphome/components/remote_base/abbwelcome_protocol.cpp create mode 100644 esphome/components/remote_base/abbwelcome_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 08652bbfc9..c771f406d8 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1718,3 +1718,105 @@ 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_)) + + +# ABBWelcome +( + ABBWelcomeData, + ABBWelcomeBinarySensor, + ABBWelcomeTrigger, + ABBWelcomeAction, + ABBWelcomeDumper, +) = declare_protocol("ABBWelcome") + +CONF_SOURCE_ADDRESS = "source_address" +CONF_DESTINATION_ADDRESS = "destination_address" +CONF_THREE_BYTE_ADDRESS = "three_byte_address" +CONF_MESSAGE_TYPE = "message_type" +CONF_MESSAGE_ID = "message_id" +CONF_RETRANSMISSION = "retransmission" + +ABB_WELCOME_SCHEMA = cv.Schema( + { + cv.Required(CONF_SOURCE_ADDRESS): cv.hex_uint32_t, + cv.Required(CONF_DESTINATION_ADDRESS): cv.hex_uint32_t, + cv.Optional(CONF_RETRANSMISSION, default=False): cv.boolean, + cv.Optional(CONF_THREE_BYTE_ADDRESS, default=False): cv.boolean, + cv.Required(CONF_MESSAGE_TYPE): cv.Any(cv.hex_uint8_t, cv.uint8_t), + cv.Optional(CONF_MESSAGE_ID): cv.Any(cv.hex_uint8_t, cv.uint8_t), + cv.Optional(CONF_DATA): cv.All( + [cv.Any(cv.hex_uint8_t, cv.uint8_t)], + cv.Length(min=0, max=7), + ), + } +) + + +@register_binary_sensor("abbwelcome", ABBWelcomeBinarySensor, ABB_WELCOME_SCHEMA) +def abbwelcome_binary_sensor(var, config): + cg.add(var.set_three_byte_address(config[CONF_THREE_BYTE_ADDRESS])) + cg.add(var.set_source_address(config[CONF_SOURCE_ADDRESS])) + cg.add(var.set_destination_address(config[CONF_DESTINATION_ADDRESS])) + cg.add(var.set_retransmission(config[CONF_RETRANSMISSION])) + cg.add(var.set_message_type(config[CONF_MESSAGE_TYPE])) + cg.add(var.set_auto_message_id(CONF_MESSAGE_ID not in config)) + if CONF_MESSAGE_ID in config: + cg.add(var.set_message_id(config[CONF_MESSAGE_ID])) + if CONF_DATA in config: + cg.add(var.set_data(config[CONF_DATA])) + cg.add(var.finalize()) + + +@register_trigger("abbwelcome", ABBWelcomeTrigger, ABBWelcomeData) +def abbwelcome_trigger(var, config): + pass + + +@register_dumper("abbwelcome", ABBWelcomeDumper) +def abbwelcome_dumper(var, config): + pass + + +@register_action("abbwelcome", ABBWelcomeAction, ABB_WELCOME_SCHEMA) +async def abbwelcome_action(var, config, args): + cg.add( + var.set_three_byte_address( + await cg.templatable(config[CONF_THREE_BYTE_ADDRESS], args, cg.bool_) + ) + ) + cg.add( + var.set_source_address( + await cg.templatable(config[CONF_SOURCE_ADDRESS], args, cg.uint16) + ) + ) + cg.add( + var.set_destination_address( + await cg.templatable(config[CONF_DESTINATION_ADDRESS], args, cg.uint16) + ) + ) + cg.add( + var.set_retransmission( + await cg.templatable(config[CONF_RETRANSMISSION], args, cg.bool_) + ) + ) + cg.add( + var.set_message_type( + await cg.templatable(config[CONF_MESSAGE_TYPE], args, cg.uint8) + ) + ) + cg.add(var.set_auto_message_id(CONF_MESSAGE_ID not in config)) + if CONF_MESSAGE_ID in config: + cg.add( + var.set_message_id( + await cg.templatable(config[CONF_MESSAGE_ID], args, cg.uint8) + ) + ) + if CONF_DATA in config: + data_ = config[CONF_DATA] + if cg.is_template(data_): + template_ = await cg.templatable( + data_, args, cg.std_vector.template(cg.uint8) + ) + cg.add(var.set_data_template(template_)) + else: + cg.add(var.set_data_static(data_)) diff --git a/esphome/components/remote_base/abbwelcome_protocol.cpp b/esphome/components/remote_base/abbwelcome_protocol.cpp new file mode 100644 index 0000000000..88f928901b --- /dev/null +++ b/esphome/components/remote_base/abbwelcome_protocol.cpp @@ -0,0 +1,123 @@ +#include "abbwelcome_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.abbwelcome"; + +static const uint32_t BIT_ONE_SPACE_US = 102; +static const uint32_t BIT_ZERO_MARK_US = 32; // 18-44 +static const uint32_t BIT_ZERO_SPACE_US = BIT_ONE_SPACE_US - BIT_ZERO_MARK_US; +static const uint16_t BYTE_SPACE_US = 210; + +uint8_t ABBWelcomeData::calc_cs_() const { + uint8_t checksum = 0; + for (uint8_t i = 0; i < this->size() - 1; i++) { + uint16_t temp = checksum ^ (this->data_[i]); + temp = temp ^ (uint16_t) (((uint32_t) temp << 0x11) >> 0x10) ^ (uint16_t) (((uint32_t) temp << 0x12) >> 0x10) ^ + (uint16_t) (((uint32_t) temp << 0x13) >> 0x10) ^ (uint16_t) (((uint32_t) temp << 0x14) >> 0x10) ^ + (uint16_t) (((uint32_t) temp << 0x15) >> 0x10) ^ (uint16_t) (((uint32_t) temp << 0x16) >> 0x10) ^ + (uint16_t) (((uint32_t) temp << 0x17) >> 0x10); + checksum = (temp & 0xfe) ^ ((temp >> 8) & 1); + } + return ~checksum; +} + +void ABBWelcomeProtocol::encode_byte_(RemoteTransmitData *dst, uint8_t data) const { + // space = bus high, mark = activate bus pulldown + dst->mark(BIT_ZERO_MARK_US); + uint32_t next_space = BIT_ZERO_SPACE_US; + for (uint8_t mask = 1 << 7; mask; mask >>= 1) { + if (data & mask) { + next_space += BIT_ONE_SPACE_US; + } else { + dst->space(next_space); + dst->mark(BIT_ZERO_MARK_US); + next_space = BIT_ZERO_SPACE_US; + } + } + next_space += BYTE_SPACE_US; + dst->space(next_space); +} + +void ABBWelcomeProtocol::encode(RemoteTransmitData *dst, const ABBWelcomeData &src) { + dst->set_carrier_frequency(0); + uint32_t reserve_count = 0; + for (size_t i = 0; i < src.size(); i++) { + reserve_count += 2 * (9 - (src[i] & 1) - ((src[i] >> 1) & 1) - ((src[i] >> 2) & 1) - ((src[i] >> 3) & 1) - + ((src[i] >> 4) & 1) - ((src[i] >> 5) & 1) - ((src[i] >> 6) & 1) - ((src[i] >> 7) & 1)); + } + dst->reserve(reserve_count); + for (size_t i = 0; i < src.size(); i++) + this->encode_byte_(dst, src[i]); + ESP_LOGD(TAG, "Transmitting: %s", src.to_string().c_str()); +} + +bool ABBWelcomeProtocol::decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data) { + if (!src.expect_mark(BIT_ZERO_MARK_US)) + return false; + uint32_t next_space = BIT_ZERO_SPACE_US; + for (uint8_t mask = 1 << 7; mask; mask >>= 1) { + // if (!src.peek_space_at_least(next_space, 0)) + // return false; + if (src.expect_space(next_space)) { + if (!src.expect_mark(BIT_ZERO_MARK_US)) + return false; + next_space = BIT_ZERO_SPACE_US; + } else { + data |= mask; + next_space += BIT_ONE_SPACE_US; + } + } + next_space += BYTE_SPACE_US; + // if (!src.peek_space_at_least(next_space, 0)) + // return false; + done = !(src.expect_space(next_space)); + return true; +} + +optional ABBWelcomeProtocol::decode(RemoteReceiveData src) { + if (src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US) && + src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US) && + src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US) && + src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US) && + src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US + BYTE_SPACE_US) && + src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + 8 * BIT_ONE_SPACE_US + BYTE_SPACE_US)) { + ESP_LOGVV(TAG, "Received Header: 0x55FF"); + ABBWelcomeData out; + out[0] = 0x55; + out[1] = 0xff; + bool done = false; + uint8_t length = 10; + uint8_t received_bytes = 2; + for (; (received_bytes < length) && !done; received_bytes++) { + uint8_t data = 0; + if (!this->decode_byte_(src, done, data)) { + ESP_LOGW(TAG, "Received incomplete packet: %s", out.to_string(received_bytes).c_str()); + return {}; + } + if (received_bytes == 2) { + length += std::min(static_cast(data & DATA_LENGTH_MASK), MAX_DATA_LENGTH); + if (data & 0x40) { + length += 2; + } + } + ESP_LOGVV(TAG, "Received Byte: 0x%02X", data); + out[received_bytes] = data; + } + if (out.is_valid()) { + ESP_LOGI(TAG, "Received: %s", out.to_string().c_str()); + return out; + } + ESP_LOGW(TAG, "Received malformed packet: %s", out.to_string(received_bytes).c_str()); + } + return {}; +} + +void ABBWelcomeProtocol::dump(const ABBWelcomeData &data) { + ESP_LOGD(TAG, "Received ABBWelcome: %s", data.to_string().c_str()); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/abbwelcome_protocol.h b/esphome/components/remote_base/abbwelcome_protocol.h new file mode 100644 index 0000000000..0493993926 --- /dev/null +++ b/esphome/components/remote_base/abbwelcome_protocol.h @@ -0,0 +1,251 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" +#include "remote_base.h" +#include +#include +#include + +namespace esphome { +namespace remote_base { + +static const uint8_t MAX_DATA_LENGTH = 15; +static const uint8_t DATA_LENGTH_MASK = 0x3f; + +/* +Message Format: + 2 bytes: Sync (0x55FF) + 1 bit: Retransmission flag (High means retransmission) + 1 bit: Address length flag (Low means 2 bytes, High means 3 bytes) + 2 bits: Unknown + 4 bits: Data length (in bytes) + 1 bit: Reply flag (High means this is a reply to a previous message with the same message type) + 7 bits: Message type + 2-3 bytes: Destination address + 2-3 bytes: Source address + 1 byte: Message ID (randomized, does not change for retransmissions) + 0-? bytes: Data + 1 byte: Checksum +*/ + +class ABBWelcomeData { + public: + // Make default + ABBWelcomeData() { + std::fill(std::begin(this->data_), std::end(this->data_), 0); + this->data_[0] = 0x55; + this->data_[1] = 0xff; + } + // Make from initializer_list + ABBWelcomeData(std::initializer_list data) { + std::fill(std::begin(this->data_), std::end(this->data_), 0); + std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin()); + } + // Make from vector + ABBWelcomeData(const std::vector &data) { + std::fill(std::begin(this->data_), std::end(this->data_), 0); + std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin()); + } + // Default copy constructor + ABBWelcomeData(const ABBWelcomeData &) = default; + + bool auto_message_id{false}; + + uint8_t *data() { return this->data_.data(); } + const uint8_t *data() const { return this->data_.data(); } + uint8_t size() const { + return std::min(static_cast(6 + (2 * this->get_address_length()) + (this->data_[2] & DATA_LENGTH_MASK)), + static_cast(this->data_.size())); + } + bool is_valid() const { + return this->data_[0] == 0x55 && this->data_[1] == 0xff && + ((this->data_[2] & DATA_LENGTH_MASK) <= MAX_DATA_LENGTH) && + (this->data_[this->size() - 1] == this->calc_cs_()); + } + void set_retransmission(bool retransmission) { + if (retransmission) { + this->data_[2] |= 0x80; + } else { + this->data_[2] &= 0x7f; + } + } + bool get_retransmission() const { return this->data_[2] & 0x80; } + // set_three_byte_address must be called before set_source_address, set_destination_address, set_message_id and + // set_data! + void set_three_byte_address(bool three_byte_address) { + if (three_byte_address) { + this->data_[2] |= 0x40; + } else { + this->data_[2] &= 0xbf; + } + } + uint8_t get_three_byte_address() const { return (this->data_[2] & 0x40); } + uint8_t get_address_length() const { return this->get_three_byte_address() ? 3 : 2; } + void set_message_type(uint8_t message_type) { this->data_[3] = message_type; } + uint8_t get_message_type() const { return this->data_[3]; } + void set_destination_address(uint32_t address) { + if (this->get_address_length() == 2) { + this->data_[4] = (address >> 8) & 0xff; + this->data_[5] = address & 0xff; + } else { + this->data_[4] = (address >> 16) & 0xff; + this->data_[5] = (address >> 8) & 0xff; + this->data_[6] = address & 0xff; + } + } + uint32_t get_destination_address() const { + if (this->get_address_length() == 2) { + return (this->data_[4] << 8) + this->data_[5]; + } + return (this->data_[4] << 16) + (this->data_[5] << 8) + this->data_[6]; + } + void set_source_address(uint32_t address) { + if (this->get_address_length() == 2) { + this->data_[6] = (address >> 8) & 0xff; + this->data_[7] = address & 0xff; + } else { + this->data_[7] = (address >> 16) & 0xff; + this->data_[8] = (address >> 8) & 0xff; + this->data_[9] = address & 0xff; + } + } + uint32_t get_source_address() const { + if (this->get_address_length() == 2) { + return (this->data_[6] << 8) + this->data_[7]; + } + return (this->data_[7] << 16) + (this->data_[8] << 8) + this->data_[9]; + } + void set_message_id(uint8_t message_id) { this->data_[4 + 2 * this->get_address_length()] = message_id; } + uint8_t get_message_id() const { return this->data_[4 + 2 * this->get_address_length()]; } + void set_data(std::vector data) { + uint8_t size = std::min(MAX_DATA_LENGTH, static_cast(data.size())); + this->data_[2] &= (0xff ^ DATA_LENGTH_MASK); + this->data_[2] |= (size & DATA_LENGTH_MASK); + if (size) + std::copy_n(data.begin(), size, this->data_.begin() + 5 + 2 * this->get_address_length()); + } + std::vector get_data() const { + std::vector data(this->data_.begin() + 5 + 2 * this->get_address_length(), + this->data_.begin() + 5 + 2 * this->get_address_length() + this->get_data_size()); + return data; + } + uint8_t get_data_size() const { + return std::min(MAX_DATA_LENGTH, static_cast(this->data_[2] & DATA_LENGTH_MASK)); + } + void finalize() { + if (this->auto_message_id && !this->get_retransmission() && !(this->data_[3] & 0x80)) { + this->set_message_id(static_cast(random_uint32())); + } + this->data_[0] = 0x55; + this->data_[1] = 0xff; + this->data_[this->size() - 1] = this->calc_cs_(); + } + std::string to_string(uint8_t max_print_bytes = 255) const { + std::string info; + if (this->is_valid()) { + info = str_sprintf(this->get_three_byte_address() ? "[%06X %s %06X] Type: %02X" : "[%04X %s %04X] Type: %02X", + this->get_source_address(), this->get_retransmission() ? "»" : ">", + this->get_destination_address(), this->get_message_type()); + if (this->get_data_size()) + info += str_sprintf(", Data: %s", format_hex_pretty(this->get_data()).c_str()); + } else { + info = "[Invalid]"; + } + uint8_t print_bytes = std::min(this->size(), max_print_bytes); + if (print_bytes) + info = str_sprintf("%s %s", format_hex_pretty(this->data_.data(), print_bytes).c_str(), info.c_str()); + return info; + } + bool operator==(const ABBWelcomeData &rhs) const { + if (std::equal(this->data_.begin(), this->data_.begin() + this->size(), rhs.data_.begin())) + return true; + return (this->auto_message_id || rhs.auto_message_id) && this->is_valid() && rhs.is_valid() && + (this->get_message_type() == rhs.get_message_type()) && + (this->get_source_address() == rhs.get_source_address()) && + (this->get_destination_address() == rhs.get_destination_address()) && (this->get_data() == rhs.get_data()); + } + uint8_t &operator[](size_t idx) { return this->data_[idx]; } + const uint8_t &operator[](size_t idx) const { return this->data_[idx]; } + + protected: + std::array data_; + // Calculate checksum + uint8_t calc_cs_() const; +}; + +class ABBWelcomeProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const ABBWelcomeData &src) override; + optional decode(RemoteReceiveData src) override; + void dump(const ABBWelcomeData &data) override; + + protected: + void encode_byte_(RemoteTransmitData *dst, uint8_t data) const; + bool decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data); +}; + +class ABBWelcomeBinarySensor : public RemoteReceiverBinarySensorBase { + public: + bool matches(RemoteReceiveData src) override { + auto data = ABBWelcomeProtocol().decode(src); + return data.has_value() && data.value() == this->data_; + } + void set_source_address(const uint32_t source_address) { this->data_.set_source_address(source_address); } + void set_destination_address(const uint32_t destination_address) { + this->data_.set_destination_address(destination_address); + } + void set_retransmission(const bool retransmission) { this->data_.set_retransmission(retransmission); } + void set_three_byte_address(const bool three_byte_address) { this->data_.set_three_byte_address(three_byte_address); } + void set_message_type(const uint8_t message_type) { this->data_.set_message_type(message_type); } + void set_message_id(const uint8_t message_id) { this->data_.set_message_id(message_id); } + void set_auto_message_id(const bool auto_message_id) { this->data_.auto_message_id = auto_message_id; } + void set_data(const std::vector &data) { this->data_.set_data(data); } + void finalize() { this->data_.finalize(); } + + protected: + ABBWelcomeData data_; +}; + +using ABBWelcomeTrigger = RemoteReceiverTrigger; +using ABBWelcomeDumper = RemoteReceiverDumper; + +template class ABBWelcomeAction : public RemoteTransmitterActionBase { + TEMPLATABLE_VALUE(uint32_t, source_address) + TEMPLATABLE_VALUE(uint32_t, destination_address) + TEMPLATABLE_VALUE(bool, retransmission) + TEMPLATABLE_VALUE(bool, three_byte_address) + TEMPLATABLE_VALUE(uint8_t, message_type) + TEMPLATABLE_VALUE(uint8_t, message_id) + TEMPLATABLE_VALUE(bool, auto_message_id) + void set_data_static(std::vector data) { data_static_ = std::move(data); } + void set_data_template(std::function(Ts...)> func) { + this->data_func_ = func; + has_data_func_ = true; + } + void encode(RemoteTransmitData *dst, Ts... x) override { + ABBWelcomeData data; + data.set_three_byte_address(this->three_byte_address_.value(x...)); + data.set_source_address(this->source_address_.value(x...)); + data.set_destination_address(this->destination_address_.value(x...)); + data.set_retransmission(this->retransmission_.value(x...)); + data.set_message_type(this->message_type_.value(x...)); + data.set_message_id(this->message_id_.value(x...)); + data.auto_message_id = this->auto_message_id_.value(x...); + if (has_data_func_) { + data.set_data(this->data_func_(x...)); + } else { + data.set_data(this->data_static_); + } + data.finalize(); + ABBWelcomeProtocol().encode(dst, data); + } + + protected: + std::function(Ts...)> data_func_{}; + std::vector data_static_{}; + bool has_data_func_{false}; +}; + +} // namespace remote_base +} // namespace esphome From 76c53379877c0c8f40200ec0b1253ba0e5d39151 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:46:35 +1200 Subject: [PATCH 0042/1106] Add support for time entities (#6399) * Add time entities * Add tests * Add myself to datetime codeowners * Fix publishing times with 0 values * Log performing TimeCall * Implement `on_time` trigger * Rename var * Fix initial value for time * Add arg name for clarity * Remove useless checks --- CODEOWNERS | 2 +- esphome/components/api/api.proto | 42 ++++ esphome/components/api/api_connection.cpp | 37 +++ esphome/components/api/api_connection.h | 5 + esphome/components/api/api_pb2.cpp | 219 ++++++++++++++++++ esphome/components/api/api_pb2.h | 50 ++++ esphome/components/api/api_pb2_service.cpp | 42 ++++ esphome/components/api/api_pb2_service.h | 15 ++ esphome/components/api/api_server.cpp | 9 + esphome/components/api/api_server.h | 3 + esphome/components/api/list_entities.cpp | 4 + esphome/components/api/list_entities.h | 3 + esphome/components/api/subscribe_state.cpp | 3 + esphome/components/api/subscribe_state.h | 3 + esphome/components/datetime/__init__.py | 83 ++++++- esphome/components/datetime/time_entity.cpp | 156 +++++++++++++ esphome/components/datetime/time_entity.h | 137 +++++++++++ esphome/components/mqtt/__init__.py | 3 +- esphome/components/mqtt/mqtt_date.h | 4 +- esphome/components/mqtt/mqtt_time.cpp | 68 ++++++ esphome/components/mqtt/mqtt_time.h | 45 ++++ .../components/template/datetime/__init__.py | 38 ++- .../template/datetime/template_time.cpp | 111 +++++++++ .../template/datetime/template_time.h | 46 ++++ .../components/web_server/list_entities.cpp | 7 + esphome/components/web_server/list_entities.h | 3 + esphome/components/web_server/web_server.cpp | 58 +++++ esphome/components/web_server/web_server.h | 11 +- esphome/core/application.h | 19 ++ esphome/core/component_iterator.cpp | 15 ++ esphome/core/component_iterator.h | 6 + esphome/core/controller.cpp | 6 + esphome/core/controller.h | 6 + esphome/core/defines.h | 1 + script/ci-custom.py | 1 + .../datetime/{date.all.yaml => test.all.yaml} | 0 tests/components/template/test.all.yaml | 13 ++ 37 files changed, 1251 insertions(+), 23 deletions(-) create mode 100644 esphome/components/datetime/time_entity.cpp create mode 100644 esphome/components/datetime/time_entity.h create mode 100644 esphome/components/mqtt/mqtt_time.cpp create mode 100644 esphome/components/mqtt/mqtt_time.h create mode 100644 esphome/components/template/datetime/template_time.cpp create mode 100644 esphome/components/template/datetime/template_time.h rename tests/components/datetime/{date.all.yaml => test.all.yaml} (100%) diff --git a/CODEOWNERS b/CODEOWNERS index d2a86cd3d9..4a3205acf0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -91,7 +91,7 @@ esphome/components/daikin_arc/* @MagicBear esphome/components/daikin_brc/* @hagak esphome/components/daly_bms/* @s1lvi0 esphome/components/dashboard_import/* @esphome/core -esphome/components/datetime/* @rfdarter +esphome/components/datetime/* @jesserockz @rfdarter esphome/components/debug/* @OttoWinter esphome/components/delonghi/* @grob6000 esphome/components/dfplayer/* @glmnet diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 6b685b8974..8d5459e717 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -45,6 +45,7 @@ service APIConnection { rpc lock_command (LockCommandRequest) returns (void) {} rpc media_player_command (MediaPlayerCommandRequest) returns (void) {} rpc date_command (DateCommandRequest) returns (void) {} + rpc time_command (TimeCommandRequest) returns (void) {} rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {} rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {} @@ -1658,3 +1659,44 @@ message DateCommandRequest { uint32 month = 3; uint32 day = 4; } + +// ==================== DATETIME TIME ==================== +message ListEntitiesTimeResponse { + option (id) = 103; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_DATETIME_TIME"; + + string object_id = 1; + fixed32 key = 2; + string name = 3; + string unique_id = 4; + + string icon = 5; + bool disabled_by_default = 6; + EntityCategory entity_category = 7; +} +message TimeStateResponse { + option (id) = 104; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_DATETIME_TIME"; + option (no_delay) = true; + + fixed32 key = 1; + // If the time does not have a valid state yet. + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller + bool missing_state = 2; + uint32 hour = 3; + uint32 minute = 4; + uint32 second = 5; +} +message TimeCommandRequest { + option (id) = 105; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_DATETIME_TIME"; + option (no_delay) = true; + + fixed32 key = 1; + uint32 hour = 2; + uint32 minute = 3; + uint32 second = 4; +} diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index e9607f7f77..e51fa8c154 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -735,6 +735,43 @@ void APIConnection::date_command(const DateCommandRequest &msg) { } #endif +#ifdef USE_DATETIME_TIME +bool APIConnection::send_time_state(datetime::TimeEntity *time) { + if (!this->state_subscription_) + return false; + + TimeStateResponse resp{}; + resp.key = time->get_object_id_hash(); + resp.missing_state = !time->has_state(); + resp.hour = time->hour; + resp.minute = time->minute; + resp.second = time->second; + return this->send_time_state_response(resp); +} +bool APIConnection::send_time_info(datetime::TimeEntity *time) { + ListEntitiesTimeResponse msg; + msg.key = time->get_object_id_hash(); + msg.object_id = time->get_object_id(); + if (time->has_own_name()) + msg.name = time->get_name(); + msg.unique_id = get_default_unique_id("time", time); + msg.icon = time->get_icon(); + msg.disabled_by_default = time->is_disabled_by_default(); + msg.entity_category = static_cast(time->get_entity_category()); + + return this->send_list_entities_time_response(msg); +} +void APIConnection::time_command(const TimeCommandRequest &msg) { + datetime::TimeEntity *time = App.get_time_by_key(msg.key); + if (time == nullptr) + return; + + auto call = time->make_call(); + call.set_time(msg.hour, msg.minute, msg.second); + call.perform(); +} +#endif + #ifdef USE_TEXT bool APIConnection::send_text_state(text::Text *text, std::string state) { if (!this->state_subscription_) diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index c19c209969..5c0a78015d 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -77,6 +77,11 @@ class APIConnection : public APIServerConnection { bool send_date_info(datetime::DateEntity *date); void date_command(const DateCommandRequest &msg) override; #endif +#ifdef USE_DATETIME_TIME + bool send_time_state(datetime::TimeEntity *time); + bool send_time_info(datetime::TimeEntity *time); + void time_command(const TimeCommandRequest &msg) override; +#endif #ifdef USE_TEXT bool send_text_state(text::Text *text, std::string state); bool send_text_info(text::Text *text); diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index f6325d0854..884396bda3 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -7476,6 +7476,225 @@ void DateCommandRequest::dump_to(std::string &out) const { out.append("}"); } #endif +bool ListEntitiesTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 6: { + this->disabled_by_default = value.as_bool(); + return true; + } + case 7: { + this->entity_category = value.as_enum(); + return true; + } + default: + return false; + } +} +bool ListEntitiesTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + case 5: { + this->icon = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_string(5, this->icon); + buffer.encode_bool(6, this->disabled_by_default); + buffer.encode_enum(7, this->entity_category); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void ListEntitiesTimeResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("ListEntitiesTimeResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%" PRIu32, this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); + + out.append(" disabled_by_default: "); + out.append(YESNO(this->disabled_by_default)); + out.append("\n"); + + out.append(" entity_category: "); + out.append(proto_enum_to_string(this->entity_category)); + out.append("\n"); + out.append("}"); +} +#endif +bool TimeStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->missing_state = value.as_bool(); + return true; + } + case 3: { + this->hour = value.as_uint32(); + return true; + } + case 4: { + this->minute = value.as_uint32(); + return true; + } + case 5: { + this->second = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool TimeStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void TimeStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_bool(2, this->missing_state); + buffer.encode_uint32(3, this->hour); + buffer.encode_uint32(4, this->minute); + buffer.encode_uint32(5, this->second); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void TimeStateResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("TimeStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%" PRIu32, this->key); + out.append(buffer); + out.append("\n"); + + out.append(" missing_state: "); + out.append(YESNO(this->missing_state)); + out.append("\n"); + + out.append(" hour: "); + sprintf(buffer, "%" PRIu32, this->hour); + out.append(buffer); + out.append("\n"); + + out.append(" minute: "); + sprintf(buffer, "%" PRIu32, this->minute); + out.append(buffer); + out.append("\n"); + + out.append(" second: "); + sprintf(buffer, "%" PRIu32, this->second); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool TimeCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->hour = value.as_uint32(); + return true; + } + case 3: { + this->minute = value.as_uint32(); + return true; + } + case 4: { + this->second = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + default: + return false; + } +} +void TimeCommandRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_uint32(2, this->hour); + buffer.encode_uint32(3, this->minute); + buffer.encode_uint32(4, this->second); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void TimeCommandRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("TimeCommandRequest {\n"); + out.append(" key: "); + sprintf(buffer, "%" PRIu32, this->key); + out.append(buffer); + out.append("\n"); + + out.append(" hour: "); + sprintf(buffer, "%" PRIu32, this->hour); + out.append(buffer); + out.append("\n"); + + out.append(" minute: "); + sprintf(buffer, "%" PRIu32, this->minute); + out.append(buffer); + out.append("\n"); + + out.append(" second: "); + sprintf(buffer, "%" PRIu32, this->second); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index e361e6b8e1..2ae6fd2bb6 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1919,6 +1919,56 @@ class DateCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class ListEntitiesTimeResponse : public ProtoMessage { + public: + std::string object_id{}; + uint32_t key{0}; + std::string name{}; + std::string unique_id{}; + std::string icon{}; + bool disabled_by_default{false}; + enums::EntityCategory entity_category{}; + 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_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class TimeStateResponse : public ProtoMessage { + public: + uint32_t key{0}; + bool missing_state{false}; + uint32_t hour{0}; + uint32_t minute{0}; + uint32_t second{0}; + 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 TimeCommandRequest : public ProtoMessage { + public: + uint32_t key{0}; + uint32_t hour{0}; + uint32_t minute{0}; + uint32_t second{0}; + 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; +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 2067f530ce..7a97df1ce0 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -539,6 +539,24 @@ bool APIServerConnectionBase::send_date_state_response(const DateStateResponse & #endif #ifdef USE_DATETIME_DATE #endif +#ifdef USE_DATETIME_TIME +bool APIServerConnectionBase::send_list_entities_time_response(const ListEntitiesTimeResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_list_entities_time_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 103); +} +#endif +#ifdef USE_DATETIME_TIME +bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_time_state_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 104); +} +#endif +#ifdef USE_DATETIME_TIME +#endif bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { @@ -979,6 +997,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, ESP_LOGVV(TAG, "on_date_command_request: %s", msg.dump().c_str()); #endif this->on_date_command_request(msg); +#endif + break; + } + case 105: { +#ifdef USE_DATETIME_TIME + TimeCommandRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_time_command_request: %s", msg.dump().c_str()); +#endif + this->on_time_command_request(msg); #endif break; } @@ -1279,6 +1308,19 @@ void APIServerConnection::on_date_command_request(const DateCommandRequest &msg) this->date_command(msg); } #endif +#ifdef USE_DATETIME_TIME +void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->time_command(msg); +} +#endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request( const SubscribeBluetoothLEAdvertisementsRequest &msg) { diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index effcfc30f4..095ce51b0f 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -270,6 +270,15 @@ class APIServerConnectionBase : public ProtoService { #endif #ifdef USE_DATETIME_DATE virtual void on_date_command_request(const DateCommandRequest &value){}; +#endif +#ifdef USE_DATETIME_TIME + bool send_list_entities_time_response(const ListEntitiesTimeResponse &msg); +#endif +#ifdef USE_DATETIME_TIME + bool send_time_state_response(const TimeStateResponse &msg); +#endif +#ifdef USE_DATETIME_TIME + virtual void on_time_command_request(const TimeCommandRequest &value){}; #endif protected: bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; @@ -328,6 +337,9 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_DATETIME_DATE virtual void date_command(const DateCommandRequest &msg) = 0; #endif +#ifdef USE_DATETIME_TIME + virtual void time_command(const TimeCommandRequest &msg) = 0; +#endif #ifdef USE_BLUETOOTH_PROXY virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0; #endif @@ -417,6 +429,9 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_DATETIME_DATE void on_date_command_request(const DateCommandRequest &msg) override; #endif +#ifdef USE_DATETIME_TIME + void on_time_command_request(const TimeCommandRequest &msg) override; +#endif #ifdef USE_BLUETOOTH_PROXY void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; #endif diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index b17555bb49..4c809126e6 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -264,6 +264,15 @@ void APIServer::on_date_update(datetime::DateEntity *obj) { } #endif +#ifdef USE_DATETIME_TIME +void APIServer::on_time_update(datetime::TimeEntity *obj) { + if (obj->is_internal()) + return; + for (auto &c : this->clients_) + c->send_time_state(obj); +} +#endif + #ifdef USE_TEXT void APIServer::on_text_update(text::Text *obj, const std::string &state) { if (obj->is_internal()) diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index c12355cc8b..8a9c26af73 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -69,6 +69,9 @@ class APIServer : public Component, public Controller { #ifdef USE_DATETIME_DATE void on_date_update(datetime::DateEntity *obj) override; #endif +#ifdef USE_DATETIME_TIME + void on_time_update(datetime::TimeEntity *obj) override; +#endif #ifdef USE_TEXT void on_text_update(text::Text *obj, const std::string &state) override; #endif diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index cd1841de5e..18685ee4d1 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -64,6 +64,10 @@ bool ListEntitiesIterator::on_number(number::Number *number) { return this->clie bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_info(date); } #endif +#ifdef USE_DATETIME_TIME +bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_info(time); } +#endif + #ifdef USE_TEXT bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); } #endif diff --git a/esphome/components/api/list_entities.h b/esphome/components/api/list_entities.h index b49867048d..95a09fc25b 100644 --- a/esphome/components/api/list_entities.h +++ b/esphome/components/api/list_entities.h @@ -49,6 +49,9 @@ class ListEntitiesIterator : public ComponentIterator { #ifdef USE_DATETIME_DATE bool on_date(datetime::DateEntity *date) override; #endif +#ifdef USE_DATETIME_TIME + bool on_time(datetime::TimeEntity *time) override; +#endif #ifdef USE_TEXT bool on_text(text::Text *text) override; #endif diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index 4e7216ddef..5eb40cfa7e 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -45,6 +45,9 @@ bool InitialStateIterator::on_number(number::Number *number) { #ifdef USE_DATETIME_DATE bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); } #endif +#ifdef USE_DATETIME_TIME +bool InitialStateIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_state(time); } +#endif #ifdef USE_TEXT bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); } #endif diff --git a/esphome/components/api/subscribe_state.h b/esphome/components/api/subscribe_state.h index 4a96659b76..447f1707d2 100644 --- a/esphome/components/api/subscribe_state.h +++ b/esphome/components/api/subscribe_state.h @@ -46,6 +46,9 @@ class InitialStateIterator : public ComponentIterator { #ifdef USE_DATETIME_DATE bool on_date(datetime::DateEntity *date) override; #endif +#ifdef USE_DATETIME_TIME + bool on_time(datetime::TimeEntity *time) override; +#endif #ifdef USE_TEXT bool on_text(text::Text *text) override; #endif diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index 3ae99cfff6..b255a27303 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -3,38 +3,50 @@ import esphome.codegen as cg # import cpp_generator as cpp import esphome.config_validation as cv from esphome import automation -from esphome.components import mqtt +from esphome.components import mqtt, time from esphome.const import ( CONF_ID, + CONF_ON_TIME, CONF_ON_VALUE, + CONF_TIME_ID, CONF_TRIGGER_ID, CONF_TYPE, CONF_MQTT_ID, CONF_DATE, + CONF_TIME, CONF_YEAR, CONF_MONTH, CONF_DAY, + CONF_SECOND, + CONF_HOUR, + CONF_MINUTE, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_generator import MockObjClass from esphome.cpp_helpers import setup_entity -CODEOWNERS = ["@rfdarter"] +CODEOWNERS = ["@rfdarter", "@jesserockz"] IS_PLATFORM_COMPONENT = True datetime_ns = cg.esphome_ns.namespace("datetime") DateTimeBase = datetime_ns.class_("DateTimeBase", cg.EntityBase) DateEntity = datetime_ns.class_("DateEntity", DateTimeBase) +TimeEntity = datetime_ns.class_("TimeEntity", DateTimeBase) # Actions DateSetAction = datetime_ns.class_("DateSetAction", automation.Action) +TimeSetAction = datetime_ns.class_("TimeSetAction", automation.Action) DateTimeStateTrigger = datetime_ns.class_( "DateTimeStateTrigger", automation.Trigger.template(cg.ESPTime) ) +OnTimeTrigger = datetime_ns.class_( + "OnTimeTrigger", automation.Trigger, cg.Component, cg.Parented.template(TimeEntity) +) + DATETIME_MODES = [ "DATE", "TIME", @@ -44,7 +56,6 @@ DATETIME_MODES = [ _DATETIME_SCHEMA = cv.Schema( { - cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTDatetimeComponent), cv.Optional(CONF_ON_VALUE): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger), @@ -57,6 +68,7 @@ _DATETIME_SCHEMA = cv.Schema( def date_schema(class_: MockObjClass) -> cv.Schema: schema = { cv.GenerateID(): cv.declare_id(class_), + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTDateComponent), cv.Optional(CONF_TYPE, default="DATE"): cv.one_of("DATE", upper=True), } return _DATETIME_SCHEMA.extend(schema) @@ -65,7 +77,20 @@ def date_schema(class_: MockObjClass) -> cv.Schema: def time_schema(class_: MockObjClass) -> cv.Schema: schema = { cv.GenerateID(): cv.declare_id(class_), + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTimeComponent), cv.Optional(CONF_TYPE, default="TIME"): cv.one_of("TIME", upper=True), + cv.Inclusive( + CONF_ON_TIME, + group_of_inclusion=CONF_ON_TIME, + msg="`on_time` and `time_id` must both be specified", + ): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OnTimeTrigger), + } + ), + cv.Inclusive(CONF_TIME_ID, group_of_inclusion=CONF_ON_TIME): cv.use_id( + time.RealTimeClock + ), } return _DATETIME_SCHEMA.extend(schema) @@ -88,6 +113,17 @@ async def setup_datetime_core_(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf) + rtc_id = config.get(CONF_TIME_ID) + rtc = None + if rtc_id is not None: + rtc = await cg.get_variable(rtc_id) + for conf in config.get(CONF_ON_TIME, []): + assert rtc is not None + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], rtc) + await automation.build_automation(trigger, [], conf) + await cg.register_component(trigger, conf) + await cg.register_parented(trigger, var) + async def register_datetime(var, config): if not CORE.has_id(config[CONF_ID]): @@ -109,18 +145,12 @@ async def to_code(config): cg.add_global(datetime_ns.using) -OPERATION_BASE_SCHEMA = cv.Schema( - { - cv.Required(CONF_ID): cv.use_id(DateEntity), - } -) - - @automation.register_action( "datetime.date.set", DateSetAction, - OPERATION_BASE_SCHEMA.extend( + cv.Schema( { + cv.Required(CONF_ID): cv.use_id(DateEntity), cv.Required(CONF_DATE): cv.Any( cv.returning_lambda, cv.date_time(allowed_time=False) ), @@ -144,3 +174,34 @@ async def datetime_date_set_to_code(config, action_id, template_arg, args): ) cg.add(action_var.set_date(date_struct)) return action_var + + +@automation.register_action( + "datetime.time.set", + TimeSetAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(TimeEntity), + cv.Required(CONF_TIME): cv.Any( + cv.returning_lambda, cv.date_time(allowed_date=False) + ), + } + ), +) +async def datetime_time_set_to_code(config, action_id, template_arg, args): + action_var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(action_var, config[CONF_ID]) + + time_config = config[CONF_TIME] + if cg.is_template(time_config): + template_ = await cg.templatable(config[CONF_TIME], [], cg.ESPTime) + cg.add(action_var.set_time(template_)) + else: + time_struct = cg.StructInitializer( + cg.ESPTime, + ("second", time_config[CONF_SECOND]), + ("minute", time_config[CONF_MINUTE]), + ("hour", time_config[CONF_HOUR]), + ) + cg.add(action_var.set_time(time_struct)) + return action_var diff --git a/esphome/components/datetime/time_entity.cpp b/esphome/components/datetime/time_entity.cpp new file mode 100644 index 0000000000..98558152d7 --- /dev/null +++ b/esphome/components/datetime/time_entity.cpp @@ -0,0 +1,156 @@ +#include "time_entity.h" + +#ifdef USE_DATETIME_TIME + +#include "esphome/core/log.h" + +namespace esphome { +namespace datetime { + +static const char *const TAG = "datetime.time_entity"; + +void TimeEntity::publish_state() { + if (this->hour_ > 23) { + this->has_state_ = false; + ESP_LOGE(TAG, "Hour must be between 0 and 23"); + return; + } + if (this->minute_ > 59) { + this->has_state_ = false; + ESP_LOGE(TAG, "Minute must be between 0 and 59"); + return; + } + if (this->second_ > 59) { + this->has_state_ = false; + ESP_LOGE(TAG, "Second must be between 0 and 59"); + return; + } + this->has_state_ = true; + ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_, + this->second_); + this->state_callback_.call(); +} + +TimeCall TimeEntity::make_call() { return TimeCall(this); } + +void TimeCall::validate_() { + if (this->hour_.has_value() && this->hour_ > 23) { + ESP_LOGE(TAG, "Hour must be between 0 and 23"); + this->hour_.reset(); + } + if (this->minute_.has_value() && this->minute_ > 59) { + ESP_LOGE(TAG, "Minute must be between 0 and 59"); + this->minute_.reset(); + } + if (this->second_.has_value() && this->second_ > 59) { + ESP_LOGE(TAG, "Second must be between 0 and 59"); + this->second_.reset(); + } +} + +void TimeCall::perform() { + this->validate_(); + ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); + if (this->hour_.has_value()) { + ESP_LOGD(TAG, " Hour: %d", *this->hour_); + } + if (this->minute_.has_value()) { + ESP_LOGD(TAG, " Minute: %d", *this->minute_); + } + if (this->second_.has_value()) { + ESP_LOGD(TAG, " Second: %d", *this->second_); + } + this->parent_->control(*this); +} + +TimeCall &TimeCall::set_time(uint8_t hour, uint8_t minute, uint8_t second) { + this->hour_ = hour; + this->minute_ = minute; + this->second_ = second; + return *this; +}; + +TimeCall &TimeCall::set_time(ESPTime time) { return this->set_time(time.hour, time.minute, time.second); }; + +TimeCall &TimeCall::set_time(const std::string &time) { + ESPTime val{}; + if (!ESPTime::strptime(time, val)) { + ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object"); + return *this; + } + return this->set_time(val); +} + +TimeCall TimeEntityRestoreState::to_call(TimeEntity *time) { + TimeCall call = time->make_call(); + call.set_time(this->hour, this->minute, this->second); + return call; +} + +void TimeEntityRestoreState::apply(TimeEntity *time) { + time->hour_ = this->hour; + time->minute_ = this->minute; + time->second_ = this->second; + time->publish_state(); +} + +#ifdef USE_TIME + +static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider + // there has been a drastic time synchronization + +void OnTimeTrigger::loop() { + if (!this->parent_->has_state()) { + return; + } + ESPTime time = this->rtc_->now(); + if (!time.is_valid()) { + return; + } + if (this->last_check_.has_value()) { + if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) { + // We went back in time (a lot), probably caused by time synchronization + ESP_LOGW(TAG, "Time has jumped back!"); + } else if (*this->last_check_ >= time) { + // already handled this one + return; + } else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) { + // We went ahead in time (a lot), probably caused by time synchronization + ESP_LOGW(TAG, "Time has jumped ahead!"); + this->last_check_ = time; + return; + } + + while (true) { + this->last_check_->increment_second(); + if (*this->last_check_ >= time) + break; + + if (this->matches_(*this->last_check_)) { + this->trigger(); + break; + } + } + } + + this->last_check_ = time; + if (!time.fields_in_range()) { + ESP_LOGW(TAG, "Time is out of range!"); + ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u", time.second, time.minute, time.hour); + } + + if (this->matches_(time)) + this->trigger(); +} + +bool OnTimeTrigger::matches_(const ESPTime &time) const { + return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute && + time.second == this->parent_->second; +} + +#endif + +} // namespace datetime +} // namespace esphome + +#endif // USE_DATETIME_TIME diff --git a/esphome/components/datetime/time_entity.h b/esphome/components/datetime/time_entity.h new file mode 100644 index 0000000000..956c09e2b4 --- /dev/null +++ b/esphome/components/datetime/time_entity.h @@ -0,0 +1,137 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_DATETIME_TIME + +#include "esphome/core/automation.h" +#include "esphome/core/helpers.h" +#include "esphome/core/time.h" + +#include "datetime_base.h" + +#ifdef USE_TIME +#include "esphome/components/time/real_time_clock.h" +#endif + +namespace esphome { +namespace datetime { + +#define LOG_DATETIME_TIME(prefix, type, obj) \ + if ((obj) != nullptr) { \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ + if (!(obj)->get_icon().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ + } \ + } + +class TimeCall; +class TimeEntity; + +struct TimeEntityRestoreState { + uint8_t hour; + uint8_t minute; + uint8_t second; + + TimeCall to_call(TimeEntity *time); + void apply(TimeEntity *time); +} __attribute__((packed)); + +class TimeEntity : public DateTimeBase { + protected: + uint8_t hour_; + uint8_t minute_; + uint8_t second_; + + public: + void publish_state(); + TimeCall make_call(); + + ESPTime state_as_esptime() const override { + ESPTime obj; + obj.hour = this->hour_; + obj.minute = this->minute_; + obj.second = this->second_; + return obj; + } + + const uint8_t &hour = hour_; + const uint8_t &minute = minute_; + const uint8_t &second = second_; + + protected: + friend class TimeCall; + friend struct TimeEntityRestoreState; + + virtual void control(const TimeCall &call) = 0; +}; + +class TimeCall { + public: + explicit TimeCall(TimeEntity *parent) : parent_(parent) {} + void perform(); + TimeCall &set_time(uint8_t hour, uint8_t minute, uint8_t second); + TimeCall &set_time(ESPTime time); + TimeCall &set_time(const std::string &time); + + TimeCall &set_hour(uint8_t hour) { + this->hour_ = hour; + return *this; + } + TimeCall &set_minute(uint8_t minute) { + this->minute_ = minute; + return *this; + } + TimeCall &set_second(uint8_t second) { + this->second_ = second; + return *this; + } + + optional get_hour() const { return this->hour_; } + optional get_minute() const { return this->minute_; } + optional get_second() const { return this->second_; } + + protected: + void validate_(); + + TimeEntity *parent_; + + optional hour_; + optional minute_; + optional second_; +}; + +template class TimeSetAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(ESPTime, time) + + void play(Ts... x) override { + auto call = this->parent_->make_call(); + + if (this->time_.has_value()) { + call.set_time(this->time_.value(x...)); + } + call.perform(); + } +}; + +#ifdef USE_TIME + +class OnTimeTrigger : public Trigger<>, public Component, public Parented { + public: + explicit OnTimeTrigger(time::RealTimeClock *rtc) : rtc_(rtc) {} + void loop() override; + + protected: + bool matches_(const ESPTime &time) const; + + time::RealTimeClock *rtc_; + optional last_check_; +}; + +#endif + +} // namespace datetime +} // namespace esphome + +#endif // USE_DATETIME_TIME diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index e442eb9146..b2c03c1546 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -113,7 +113,8 @@ MQTTSensorComponent = mqtt_ns.class_("MQTTSensorComponent", MQTTComponent) MQTTSwitchComponent = mqtt_ns.class_("MQTTSwitchComponent", MQTTComponent) MQTTTextSensor = mqtt_ns.class_("MQTTTextSensor", MQTTComponent) MQTTNumberComponent = mqtt_ns.class_("MQTTNumberComponent", MQTTComponent) -MQTTDatetimeComponent = mqtt_ns.class_("MQTTDatetimeComponent", MQTTComponent) +MQTTDateComponent = mqtt_ns.class_("MQTTDateComponent", MQTTComponent) +MQTTTimeComponent = mqtt_ns.class_("MQTTTimeComponent", MQTTComponent) MQTTTextComponent = mqtt_ns.class_("MQTTTextComponent", MQTTComponent) MQTTSelectComponent = mqtt_ns.class_("MQTTSelectComponent", MQTTComponent) MQTTButtonComponent = mqtt_ns.class_("MQTTButtonComponent", MQTTComponent) diff --git a/esphome/components/mqtt/mqtt_date.h b/esphome/components/mqtt/mqtt_date.h index 2776893d32..5147afe7e7 100644 --- a/esphome/components/mqtt/mqtt_date.h +++ b/esphome/components/mqtt/mqtt_date.h @@ -13,9 +13,9 @@ namespace mqtt { class MQTTDateComponent : public mqtt::MQTTComponent { public: - /** Construct this MQTTDatetimeComponent instance with the provided friendly_name and datetime + /** Construct this MQTTDateComponent instance with the provided friendly_name and date * - * @param datetime The datetime component. + * @param date The date component. */ explicit MQTTDateComponent(datetime::DateEntity *date); diff --git a/esphome/components/mqtt/mqtt_time.cpp b/esphome/components/mqtt/mqtt_time.cpp new file mode 100644 index 0000000000..332ef53cbc --- /dev/null +++ b/esphome/components/mqtt/mqtt_time.cpp @@ -0,0 +1,68 @@ +#include "mqtt_time.h" + +#include +#include "esphome/core/log.h" + +#include "mqtt_const.h" + +#ifdef USE_MQTT +#ifdef USE_DATETIME_TIME + +namespace esphome { +namespace mqtt { + +static const char *const TAG = "mqtt.datetime.time"; + +using namespace esphome::datetime; + +MQTTTimeComponent::MQTTTimeComponent(TimeEntity *time) : time_(time) {} + +void MQTTTimeComponent::setup() { + this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { + auto call = this->time_->make_call(); + if (root.containsKey("hour")) { + call.set_hour(root["hour"]); + } + if (root.containsKey("minute")) { + call.set_minute(root["minute"]); + } + if (root.containsKey("second")) { + call.set_second(root["second"]); + } + call.perform(); + }); + this->time_->add_on_state_callback( + [this]() { this->publish_state(this->time_->hour, this->time_->minute, this->time_->second); }); +} + +void MQTTTimeComponent::dump_config() { + ESP_LOGCONFIG(TAG, "MQTT Time '%s':", this->time_->get_name().c_str()); + LOG_MQTT_COMPONENT(true, true) +} + +std::string MQTTTimeComponent::component_type() const { return "time"; } +const EntityBase *MQTTTimeComponent::get_entity() const { return this->time_; } + +void MQTTTimeComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // Nothing extra to add here +} +bool MQTTTimeComponent::send_initial_state() { + if (this->time_->has_state()) { + return this->publish_state(this->time_->hour, this->time_->minute, this->time_->second); + } else { + return true; + } +} +bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) { + return this->publish_json(this->get_state_topic_(), [hour, minute, second](JsonObject root) { + root["hour"] = hour; + root["minute"] = minute; + root["second"] = second; + }); +} + +} // namespace mqtt +} // namespace esphome + +#endif // USE_DATETIME_TIME +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_time.h b/esphome/components/mqtt/mqtt_time.h new file mode 100644 index 0000000000..b9dd822a73 --- /dev/null +++ b/esphome/components/mqtt/mqtt_time.h @@ -0,0 +1,45 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_MQTT +#ifdef USE_DATETIME_TIME + +#include "esphome/components/datetime/time_entity.h" +#include "mqtt_component.h" + +namespace esphome { +namespace mqtt { + +class MQTTTimeComponent : public mqtt::MQTTComponent { + public: + /** Construct this MQTTTimeComponent instance with the provided friendly_name and time + * + * @param time The time entity. + */ + explicit MQTTTimeComponent(datetime::TimeEntity *time); + + // ========== INTERNAL METHODS ========== + // (In most use cases you won't need these) + /// Override setup. + void setup() override; + void dump_config() override; + + void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override; + + bool send_initial_state() override; + + bool publish_state(uint8_t hour, uint8_t minute, uint8_t second); + + protected: + std::string component_type() const override; + const EntityBase *get_entity() const override; + + datetime::TimeEntity *time_; +}; + +} // namespace mqtt +} // namespace esphome + +#endif // USE_DATETIME_DATE +#endif // USE_MQTT diff --git a/esphome/components/template/datetime/__init__.py b/esphome/components/template/datetime/__init__.py index 16f341301e..53d9d1b9d3 100644 --- a/esphome/components/template/datetime/__init__.py +++ b/esphome/components/template/datetime/__init__.py @@ -9,7 +9,11 @@ from esphome.const import ( CONF_RESTORE_VALUE, CONF_SET_ACTION, CONF_DAY, + CONF_HOUR, + CONF_MINUTE, CONF_MONTH, + CONF_SECOND, + CONF_TYPE, CONF_YEAR, ) @@ -23,6 +27,10 @@ TemplateDate = template_ns.class_( "TemplateDate", datetime.DateEntity, cg.PollingComponent ) +TemplateTime = template_ns.class_( + "TemplateTime", datetime.TimeEntity, cg.PollingComponent +) + def validate(config): config = config.copy() @@ -63,6 +71,13 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_INITIAL_VALUE): cv.date_time(allowed_time=False), } ), + "TIME": datetime.time_schema(TemplateTime) + .extend(_BASE_SCHEMA) + .extend( + { + cv.Optional(CONF_INITIAL_VALUE): cv.date_time(allowed_date=False), + } + ), }, upper=True, ), @@ -85,13 +100,22 @@ async def to_code(config): cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE])) if initial_value := config.get(CONF_INITIAL_VALUE): - date_struct = cg.StructInitializer( - cg.ESPTime, - ("day_of_month", initial_value[CONF_DAY]), - ("month", initial_value[CONF_MONTH]), - ("year", initial_value[CONF_YEAR]), - ) - cg.add(var.set_initial_value(date_struct)) + if config[CONF_TYPE] == "DATE": + date_struct = cg.StructInitializer( + cg.ESPTime, + ("day_of_month", initial_value[CONF_DAY]), + ("month", initial_value[CONF_MONTH]), + ("year", initial_value[CONF_YEAR]), + ) + cg.add(var.set_initial_value(date_struct)) + elif config[CONF_TYPE] == "TIME": + time_struct = cg.StructInitializer( + cg.ESPTime, + ("second", initial_value[CONF_SECOND]), + ("minute", initial_value[CONF_MINUTE]), + ("hour", initial_value[CONF_HOUR]), + ) + cg.add(var.set_initial_value(time_struct)) if CONF_SET_ACTION in config: await automation.build_automation( diff --git a/esphome/components/template/datetime/template_time.cpp b/esphome/components/template/datetime/template_time.cpp new file mode 100644 index 0000000000..0e4d734d16 --- /dev/null +++ b/esphome/components/template/datetime/template_time.cpp @@ -0,0 +1,111 @@ +#include "template_time.h" + +#ifdef USE_DATETIME_TIME + +#include "esphome/core/log.h" + +namespace esphome { +namespace template_ { + +static const char *const TAG = "template.time"; + +void TemplateTime::setup() { + if (this->f_.has_value()) + return; + + ESPTime state{}; + + if (!this->restore_value_) { + state = this->initial_value_; + } else { + datetime::TimeEntityRestoreState temp; + this->pref_ = + global_preferences->make_preference(194434060U ^ this->get_object_id_hash()); + if (this->pref_.load(&temp)) { + temp.apply(this); + return; + } else { + // set to inital value if loading from pref failed + state = this->initial_value_; + } + } + + this->hour_ = state.hour; + this->minute_ = state.minute; + this->second_ = state.second; + this->publish_state(); +} + +void TemplateTime::update() { + if (!this->f_.has_value()) + return; + + auto val = (*this->f_)(); + if (!val.has_value()) + return; + + this->hour_ = val->hour; + this->minute_ = val->minute; + this->second_ = val->second; + this->publish_state(); +} + +void TemplateTime::control(const datetime::TimeCall &call) { + bool has_hour = call.get_hour().has_value(); + bool has_minute = call.get_minute().has_value(); + bool has_second = call.get_second().has_value(); + + ESPTime value = {}; + if (has_hour) + value.hour = *call.get_hour(); + + if (has_minute) + value.minute = *call.get_minute(); + + if (has_second) + value.second = *call.get_second(); + + this->set_trigger_->trigger(value); + + if (this->optimistic_) { + if (has_hour) + this->hour_ = *call.get_hour(); + if (has_minute) + this->minute_ = *call.get_minute(); + if (has_second) + this->second_ = *call.get_second(); + this->publish_state(); + } + + if (this->restore_value_) { + datetime::TimeEntityRestoreState temp = {}; + if (has_hour) { + temp.hour = *call.get_hour(); + } else { + temp.hour = this->hour_; + } + if (has_minute) { + temp.minute = *call.get_minute(); + } else { + temp.minute = this->minute_; + } + if (has_second) { + temp.second = *call.get_second(); + } else { + temp.second = this->second_; + } + + this->pref_.save(&temp); + } +} + +void TemplateTime::dump_config() { + LOG_DATETIME_TIME("", "Template Time", this); + ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_)); + LOG_UPDATE_INTERVAL(this); +} + +} // namespace template_ +} // namespace esphome + +#endif // USE_DATETIME_TIME diff --git a/esphome/components/template/datetime/template_time.h b/esphome/components/template/datetime/template_time.h new file mode 100644 index 0000000000..4a7c0098ec --- /dev/null +++ b/esphome/components/template/datetime/template_time.h @@ -0,0 +1,46 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_DATETIME_TIME + +#include "esphome/components/datetime/time_entity.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/preferences.h" +#include "esphome/core/time.h" + +namespace esphome { +namespace template_ { + +class TemplateTime : public datetime::TimeEntity, public PollingComponent { + public: + void set_template(std::function()> &&f) { this->f_ = f; } + + void setup() override; + void update() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::HARDWARE; } + + Trigger *get_set_trigger() const { return this->set_trigger_; } + void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } + + void set_initial_value(ESPTime initial_value) { this->initial_value_ = initial_value; } + void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; } + + protected: + void control(const datetime::TimeCall &call) override; + + bool optimistic_{false}; + ESPTime initial_value_{}; + bool restore_value_{false}; + Trigger *set_trigger_ = new Trigger(); + optional()>> f_; + + ESPPreferenceObject pref_; +}; + +} // namespace template_ +} // namespace esphome + +#endif // USE_DATETIME_TIME diff --git a/esphome/components/web_server/list_entities.cpp b/esphome/components/web_server/list_entities.cpp index 2252f55008..dd9fd4afe4 100644 --- a/esphome/components/web_server/list_entities.cpp +++ b/esphome/components/web_server/list_entities.cpp @@ -113,6 +113,13 @@ bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { } #endif +#ifdef USE_DATETIME_TIME +bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) { + this->web_server_->events_.send(this->web_server_->time_json(time, DETAIL_ALL).c_str(), "state"); + return true; +} +#endif + #ifdef USE_TEXT bool ListEntitiesIterator::on_text(text::Text *text) { if (this->web_server_->events_.count() == 0) diff --git a/esphome/components/web_server/list_entities.h b/esphome/components/web_server/list_entities.h index cd7c9099d6..fc48186b32 100644 --- a/esphome/components/web_server/list_entities.h +++ b/esphome/components/web_server/list_entities.h @@ -44,6 +44,9 @@ class ListEntitiesIterator : public ComponentIterator { #ifdef USE_DATETIME_DATE bool on_date(datetime::DateEntity *date) override; #endif +#ifdef USE_DATETIME_TIME + bool on_time(datetime::TimeEntity *time) override; +#endif #ifdef USE_TEXT bool on_text(text::Text *text) override; #endif diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 6c3e4e5eec..b48a39cbcb 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -921,6 +921,52 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con } #endif // USE_DATETIME_DATE +#ifdef USE_DATETIME_TIME +void WebServer::on_time_update(datetime::TimeEntity *obj) { + this->events_.send(this->time_json(obj, DETAIL_STATE).c_str(), "state"); +} +void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) { + for (auto *obj : App.get_times()) { + if (obj->get_object_id() != match.id) + continue; + if (request->method() == HTTP_GET && match.method.empty()) { + std::string data = this->time_json(obj, DETAIL_STATE); + request->send(200, "application/json", data.c_str()); + return; + } + if (match.method != "set") { + request->send(404); + return; + } + + auto call = obj->make_call(); + + if (!request->hasParam("value")) { + request->send(409); + return; + } + + if (request->hasParam("value")) { + std::string value = request->getParam("value")->value().c_str(); + call.set_time(value); + } + + this->schedule_([call]() mutable { call.perform(); }); + request->send(200); + return; + } + request->send(404); +} +std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) { + return json::build_json([obj, start_config](JsonObject root) { + set_json_id(root, obj, "time-" + obj->get_object_id(), start_config); + std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second); + root["value"] = value; + root["state"] = value; + }); +} +#endif // USE_DATETIME_TIME + #ifdef USE_TEXT void WebServer::on_text_update(text::Text *obj, const std::string &state) { if (this->events_.count() == 0) @@ -1320,6 +1366,11 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { return true; #endif +#ifdef USE_DATETIME_TIME + if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "time") + return true; +#endif + #ifdef USE_TEXT if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "text") return true; @@ -1445,6 +1496,13 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { } #endif +#ifdef USE_DATETIME_TIME + if (match.domain == "time") { + this->handle_time_request(request, match); + return; + } +#endif + #ifdef USE_TEXT if (match.domain == "text") { this->handle_text_request(request, match); diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 57cbbe1339..1935f8d076 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -8,9 +8,9 @@ #include #ifdef USE_ESP32 -#include #include #include +#include #endif #if USE_WEBSERVER_VERSION >= 2 @@ -230,6 +230,15 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { std::string date_json(datetime::DateEntity *obj, JsonDetail start_config); #endif +#ifdef USE_DATETIME_TIME + void on_time_update(datetime::TimeEntity *obj) override; + /// Handle a time request under '/time/'. + void handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match); + + /// Dump the time state with its value as a JSON string. + std::string time_json(datetime::TimeEntity *obj, JsonDetail start_config); +#endif + #ifdef USE_TEXT void on_text_update(text::Text *obj, const std::string &state) override; /// Handle a text input request under '/text/'. diff --git a/esphome/core/application.h b/esphome/core/application.h index 26125dd935..73330d27e3 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -42,6 +42,9 @@ #ifdef USE_DATETIME_DATE #include "esphome/components/datetime/date_entity.h" #endif +#ifdef USE_DATETIME_TIME +#include "esphome/components/datetime/time_entity.h" +#endif #ifdef USE_TEXT #include "esphome/components/text/text.h" #endif @@ -128,6 +131,10 @@ class Application { void register_date(datetime::DateEntity *date) { this->dates_.push_back(date); } #endif +#ifdef USE_DATETIME_TIME + void register_time(datetime::TimeEntity *time) { this->times_.push_back(time); } +#endif + #ifdef USE_TEXT void register_text(text::Text *text) { this->texts_.push_back(text); } #endif @@ -305,6 +312,15 @@ class Application { return nullptr; } #endif +#ifdef USE_DATETIME_TIME + const std::vector &get_times() { return this->times_; } + datetime::TimeEntity *get_time_by_key(uint32_t key, bool include_internal = false) { + for (auto *obj : this->times_) + if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) + return obj; + return nullptr; + } +#endif #ifdef USE_TEXT const std::vector &get_texts() { return this->texts_; } text::Text *get_text_by_key(uint32_t key, bool include_internal = false) { @@ -401,6 +417,9 @@ class Application { #ifdef USE_DATETIME_DATE std::vector dates_{}; #endif +#ifdef USE_DATETIME_TIME + std::vector times_{}; +#endif #ifdef USE_SELECT std::vector selects_{}; #endif diff --git a/esphome/core/component_iterator.cpp b/esphome/core/component_iterator.cpp index 1e06221af6..228cf64d54 100644 --- a/esphome/core/component_iterator.cpp +++ b/esphome/core/component_iterator.cpp @@ -217,6 +217,21 @@ void ComponentIterator::advance() { } break; #endif +#ifdef USE_DATETIME_TIME + case IteratorState::DATETIME_TIME: + if (this->at_ >= App.get_times().size()) { + advance_platform = true; + } else { + auto *time = App.get_times()[this->at_]; + if (time->is_internal() && !this->include_internal_) { + success = true; + break; + } else { + success = this->on_time(time); + } + } + break; +#endif #ifdef USE_TEXT case IteratorState::TEXT: if (this->at_ >= App.get_texts().size()) { diff --git a/esphome/core/component_iterator.h b/esphome/core/component_iterator.h index 02c6dddacb..d7f19f2850 100644 --- a/esphome/core/component_iterator.h +++ b/esphome/core/component_iterator.h @@ -60,6 +60,9 @@ class ComponentIterator { #ifdef USE_DATETIME_DATE virtual bool on_date(datetime::DateEntity *date) = 0; #endif +#ifdef USE_DATETIME_TIME + virtual bool on_time(datetime::TimeEntity *time) = 0; +#endif #ifdef USE_TEXT virtual bool on_text(text::Text *text) = 0; #endif @@ -120,6 +123,9 @@ class ComponentIterator { #ifdef USE_DATETIME_DATE DATETIME_DATE, #endif +#ifdef USE_DATETIME_TIME + DATETIME_TIME, +#endif #ifdef USE_TEXT TEXT, #endif diff --git a/esphome/core/controller.cpp b/esphome/core/controller.cpp index 43b8fea50c..db5818d455 100644 --- a/esphome/core/controller.cpp +++ b/esphome/core/controller.cpp @@ -65,6 +65,12 @@ void Controller::setup_controller(bool include_internal) { obj->add_on_state_callback([this, obj]() { this->on_date_update(obj); }); } #endif +#ifdef USE_DATETIME_TIME + for (auto *obj : App.get_times()) { + if (include_internal || !obj->is_internal()) + obj->add_on_state_callback([this, obj]() { this->on_time_update(obj); }); + } +#endif #ifdef USE_TEXT for (auto *obj : App.get_texts()) { if (include_internal || !obj->is_internal()) diff --git a/esphome/core/controller.h b/esphome/core/controller.h index c31cd22d07..9b1cfd93c6 100644 --- a/esphome/core/controller.h +++ b/esphome/core/controller.h @@ -34,6 +34,9 @@ #ifdef USE_DATETIME_DATE #include "esphome/components/datetime/date_entity.h" #endif +#ifdef USE_DATETIME_TIME +#include "esphome/components/datetime/time_entity.h" +#endif #ifdef USE_TEXT #include "esphome/components/text/text.h" #endif @@ -85,6 +88,9 @@ class Controller { #ifdef USE_DATETIME_DATE virtual void on_date_update(datetime::DateEntity *obj){}; #endif +#ifdef USE_DATETIME_TIME + virtual void on_time_update(datetime::TimeEntity *obj){}; +#endif #ifdef USE_TEXT virtual void on_text_update(text::Text *obj, const std::string &state){}; #endif diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 501dccc6fa..22153de5de 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -36,6 +36,7 @@ #define USE_NUMBER #define USE_DATETIME #define USE_DATETIME_DATE +#define USE_DATETIME_TIME #define USE_OTA #define USE_OTA_PASSWORD #define USE_OTA_STATE_CALLBACK diff --git a/script/ci-custom.py b/script/ci-custom.py index 1ad44dc930..3be7be76a2 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -622,6 +622,7 @@ def lint_trailing_whitespace(fname, match): "esphome/components/climate/climate.h", "esphome/components/cover/cover.h", "esphome/components/datetime/date_entity.h", + "esphome/components/datetime/time_entity.h", "esphome/components/display/display.h", "esphome/components/fan/fan.h", "esphome/components/i2c/i2c.h", diff --git a/tests/components/datetime/date.all.yaml b/tests/components/datetime/test.all.yaml similarity index 100% rename from tests/components/datetime/date.all.yaml rename to tests/components/datetime/test.all.yaml diff --git a/tests/components/template/test.all.yaml b/tests/components/template/test.all.yaml index e50ffd7f67..29dc83b649 100644 --- a/tests/components/template/test.all.yaml +++ b/tests/components/template/test.all.yaml @@ -153,3 +153,16 @@ datetime: - x.year - x.month - x.day_of_month + - platform: template + name: Time + id: test_time + type: time + set_action: + - logger.log: "set_value" + on_value: + - logger.log: + format: "Time: %02d:%02d:%02d" + args: + - x.hour + - x.minute + - x.second From 55c49281a224966b4e40b38c0e2e56f39cf93e3b Mon Sep 17 00:00:00 2001 From: MRemy2 <95053616+MRemy2@users.noreply.github.com> Date: Tue, 9 Apr 2024 04:49:37 +0300 Subject: [PATCH 0043/1106] Fix Match by IRK (#6499) Co-authored-by: Remus --- esphome/components/ble_presence/ble_presence_device.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esphome/components/ble_presence/ble_presence_device.h b/esphome/components/ble_presence/ble_presence_device.h index 0d86f6a40d..e74c2f4f45 100644 --- a/esphome/components/ble_presence/ble_presence_device.h +++ b/esphome/components/ble_presence/ble_presence_device.h @@ -73,8 +73,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff, break; case MATCH_BY_IRK: if (resolve_irk_(device.address_uint64(), this->irk_)) { - this->publish_state(true); - this->found_ = true; + this->set_found_(true); return true; } break; From c66b2c52c1fb22fcc872edf8bf0caa5f3a369344 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:53:57 +1200 Subject: [PATCH 0044/1106] Add rmt_channel to remote_transmitter and remote_receiver (#6497) * Add rmt_channel to remote_transmitter and remote_receiver * Add codeowner * Add tests --- CODEOWNERS | 1 + esphome/components/esp32_rmt/__init__.py | 55 ++++++ .../components/esp32_rmt_led_strip/light.py | 26 +-- .../components/remote_base/remote_base.cpp | 3 + esphome/components/remote_base/remote_base.h | 5 +- .../components/remote_receiver/__init__.py | 11 +- .../remote_receiver/remote_receiver.h | 5 +- .../components/remote_transmitter/__init__.py | 10 +- .../remote_transmitter/remote_transmitter.h | 11 +- esphome/const.py | 1 + .../remote_receiver/esp32-common.yaml | 18 ++ .../remote_receiver/test.esp32-c3-idf.yaml | 6 + .../remote_receiver/test.esp32-c3.yaml | 6 + .../remote_receiver/test.esp32-idf.yaml | 6 + .../remote_receiver/test.esp32-s3-idf.yaml | 6 + .../remote_receiver/test.esp32.yaml | 6 + .../remote_receiver/test.esp8266.yaml | 17 ++ .../remote_transmitter/common-buttons.yaml | 178 ++++++++++++++++++ .../remote_transmitter/esp32-common.yaml | 8 + .../remote_transmitter/test.esp32-c3-idf.yaml | 6 + .../remote_transmitter/test.esp32-c3.yaml | 6 + .../remote_transmitter/test.esp32-idf.yaml | 6 + .../remote_transmitter/test.esp32-s3-idf.yaml | 6 + .../remote_transmitter/test.esp32.yaml | 6 + .../remote_transmitter/test.esp8266.yaml | 7 + 25 files changed, 383 insertions(+), 33 deletions(-) create mode 100644 esphome/components/esp32_rmt/__init__.py create mode 100644 tests/components/remote_receiver/esp32-common.yaml create mode 100644 tests/components/remote_receiver/test.esp32-c3-idf.yaml create mode 100644 tests/components/remote_receiver/test.esp32-c3.yaml create mode 100644 tests/components/remote_receiver/test.esp32-idf.yaml create mode 100644 tests/components/remote_receiver/test.esp32-s3-idf.yaml create mode 100644 tests/components/remote_receiver/test.esp32.yaml create mode 100644 tests/components/remote_receiver/test.esp8266.yaml create mode 100644 tests/components/remote_transmitter/common-buttons.yaml create mode 100644 tests/components/remote_transmitter/esp32-common.yaml create mode 100644 tests/components/remote_transmitter/test.esp32-c3-idf.yaml create mode 100644 tests/components/remote_transmitter/test.esp32-c3.yaml create mode 100644 tests/components/remote_transmitter/test.esp32-idf.yaml create mode 100644 tests/components/remote_transmitter/test.esp32-s3-idf.yaml create mode 100644 tests/components/remote_transmitter/test.esp32.yaml create mode 100644 tests/components/remote_transmitter/test.esp8266.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 4a3205acf0..126513943f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -115,6 +115,7 @@ esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz esphome/components/esp32_camera_web_server/* @ayufan esphome/components/esp32_can/* @Sympatron esphome/components/esp32_improv/* @jesserockz +esphome/components/esp32_rmt/* @jesserockz esphome/components/esp32_rmt_led_strip/* @jesserockz esphome/components/esp8266/* @esphome/core esphome/components/ethernet_info/* @gtjadsonsantos diff --git a/esphome/components/esp32_rmt/__init__.py b/esphome/components/esp32_rmt/__init__.py new file mode 100644 index 0000000000..bda240680b --- /dev/null +++ b/esphome/components/esp32_rmt/__init__.py @@ -0,0 +1,55 @@ +import esphome.config_validation as cv +import esphome.codegen as cg + +from esphome.components import esp32 + +CODEOWNERS = ["@jesserockz"] + +RMT_TX_CHANNELS = { + esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7], + esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3], + 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], +} + +RMT_RX_CHANNELS = { + esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7], + esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3], + esp32.const.VARIANT_ESP32S3: [4, 5, 6, 7], + esp32.const.VARIANT_ESP32C3: [2, 3], + esp32.const.VARIANT_ESP32C6: [2, 3], + esp32.const.VARIANT_ESP32H2: [2, 3], +} + +rmt_channel_t = cg.global_ns.enum("rmt_channel_t") +RMT_CHANNEL_ENUMS = { + 0: rmt_channel_t.RMT_CHANNEL_0, + 1: rmt_channel_t.RMT_CHANNEL_1, + 2: rmt_channel_t.RMT_CHANNEL_2, + 3: rmt_channel_t.RMT_CHANNEL_3, + 4: rmt_channel_t.RMT_CHANNEL_4, + 5: rmt_channel_t.RMT_CHANNEL_5, + 6: rmt_channel_t.RMT_CHANNEL_6, + 7: rmt_channel_t.RMT_CHANNEL_7, +} + + +def validate_rmt_channel(*, tx: bool): + + rmt_channels = RMT_TX_CHANNELS if tx else RMT_RX_CHANNELS + + def _validator(value): + cv.only_on_esp32(value) + value = cv.int_(value) + variant = esp32.get_esp32_variant() + if variant not in rmt_channels: + raise cv.Invalid(f"ESP32 variant {variant} does not support RMT.") + if value not in rmt_channels[variant]: + raise cv.Invalid( + f"RMT channel {value} does not support {'transmitting' if tx else 'receiving'} for ESP32 variant {variant}." + ) + return cv.enum(RMT_CHANNEL_ENUMS)(value) + + return _validator diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index d38c7abeb8..3442940e3f 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -3,7 +3,7 @@ from dataclasses import dataclass import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.components import esp32, light +from esphome.components import esp32_rmt, light from esphome.const import ( CONF_CHIPSET, CONF_MAX_REFRESH_RATE, @@ -11,6 +11,7 @@ from esphome.const import ( CONF_OUTPUT_ID, CONF_PIN, CONF_RGB_ORDER, + CONF_RMT_CHANNEL, ) CODEOWNERS = ["@jesserockz"] @@ -57,27 +58,6 @@ CONF_BIT0_HIGH = "bit0_high" CONF_BIT0_LOW = "bit0_low" CONF_BIT1_HIGH = "bit1_high" CONF_BIT1_LOW = "bit1_low" -CONF_RMT_CHANNEL = "rmt_channel" - -RMT_CHANNELS = { - esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7], - esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3], - 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], -} - - -def _validate_rmt_channel(value): - variant = esp32.get_esp32_variant() - if variant not in RMT_CHANNELS: - raise cv.Invalid(f"ESP32 variant {variant} does not support RMT.") - if value not in RMT_CHANNELS[variant]: - raise cv.Invalid( - f"RMT channel {value} is not supported for ESP32 variant {variant}." - ) - return value CONFIG_SCHEMA = cv.All( @@ -87,7 +67,7 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number, cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True), - cv.Required(CONF_RMT_CHANNEL): _validate_rmt_channel, + cv.Required(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True), cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), cv.Optional(CONF_IS_RGBW, default=False): cv.boolean, diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index f3e86aaab6..0e9cef8cca 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -15,6 +15,9 @@ RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_b next_rmt_channel = rmt_channel_t(int(next_rmt_channel) + mem_block_num); } +RemoteRMTChannel::RemoteRMTChannel(rmt_channel_t channel, uint8_t mem_block_num) + : channel_(channel), mem_block_num_(mem_block_num) {} + void RemoteRMTChannel::config_rmt(rmt_config_t &rmt) { if (rmt_channel_t(int(this->channel_) + this->mem_block_num_) > RMT_CHANNEL_MAX) { this->mem_block_num_ = int(RMT_CHANNEL_MAX) - int(this->channel_); diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index ebbb528a23..b2a4b543ea 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -3,10 +3,10 @@ #pragma once +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" -#include "esphome/core/automation.h" -#include "esphome/components/binary_sensor/binary_sensor.h" #ifdef USE_ESP32 #include @@ -86,6 +86,7 @@ class RemoteComponentBase { class RemoteRMTChannel { public: explicit RemoteRMTChannel(uint8_t mem_block_num = 1); + explicit RemoteRMTChannel(rmt_channel_t channel, uint8_t mem_block_num = 1); void config_rmt(rmt_config_t &rmt); void set_clock_divider(uint8_t clock_divider) { this->clock_divider_ = clock_divider; } diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index 5737957adb..6a68c8b254 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.components import remote_base +from esphome.components import remote_base, esp32_rmt from esphome.const import ( CONF_BUFFER_SIZE, CONF_DUMP, @@ -11,6 +11,7 @@ from esphome.const import ( CONF_PIN, CONF_TOLERANCE, CONF_MEMORY_BLOCKS, + CONF_RMT_CHANNEL, ) from esphome.core import CORE, TimePeriod @@ -45,6 +46,7 @@ CONFIG_SCHEMA = remote_base.validate_triggers( CONF_IDLE, default="10ms" ): cv.positive_time_period_microseconds, cv.Optional(CONF_MEMORY_BLOCKS, default=3): cv.Range(min=1, max=8), + cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=False), } ).extend(cv.COMPONENT_SCHEMA) ) @@ -53,7 +55,12 @@ CONFIG_SCHEMA = remote_base.validate_triggers( async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) if CORE.is_esp32: - var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS]) + if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: + var = cg.new_Pvariable( + config[CONF_ID], pin, rmt_channel, config[CONF_MEMORY_BLOCKS] + ) + else: + var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS]) else: var = cg.new_Pvariable(config[CONF_ID], pin) diff --git a/esphome/components/remote_receiver/remote_receiver.h b/esphome/components/remote_receiver/remote_receiver.h index c1343a8603..f29145a59e 100644 --- a/esphome/components/remote_receiver/remote_receiver.h +++ b/esphome/components/remote_receiver/remote_receiver.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/components/remote_base/remote_base.h" +#include "esphome/core/component.h" #include @@ -38,6 +38,9 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase, #ifdef USE_ESP32 RemoteReceiverComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1) : RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {} + + RemoteReceiverComponent(InternalGPIOPin *pin, rmt_channel_t channel, uint8_t mem_block_num = 1) + : RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(channel, mem_block_num) {} #else RemoteReceiverComponent(InternalGPIOPin *pin) : RemoteReceiverBase(pin) {} #endif diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index e09e4c7f55..d203ff3417 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.components import remote_base -from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN +from esphome.components import remote_base, esp32_rmt +from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, CONF_RMT_CHANNEL AUTO_LOAD = ["remote_base"] remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter") @@ -18,13 +18,17 @@ CONFIG_SCHEMA = cv.Schema( cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All( cv.percentage_int, cv.Range(min=1, max=100) ), + cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True), } ).extend(cv.COMPONENT_SCHEMA) async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) - var = cg.new_Pvariable(config[CONF_ID], pin) + if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: + var = cg.new_Pvariable(config[CONF_ID], pin, rmt_channel) + else: + var = cg.new_Pvariable(config[CONF_ID], pin) await cg.register_component(var, config) cg.add(var.set_carrier_duty_percent(config[CONF_CARRIER_DUTY_PERCENT])) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index 686a6ec09b..e736172cda 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/components/remote_base/remote_base.h" +#include "esphome/core/component.h" #include @@ -16,8 +16,15 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #endif { public: - explicit RemoteTransmitterComponent(InternalGPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {} +#ifdef USE_ESP32 + RemoteTransmitterComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1) + : remote_base::RemoteTransmitterBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {} + RemoteTransmitterComponent(InternalGPIOPin *pin, rmt_channel_t channel, uint8_t mem_block_num = 1) + : remote_base::RemoteTransmitterBase(pin), remote_base::RemoteRMTChannel(channel, mem_block_num) {} +#else + explicit RemoteTransmitterComponent(InternalGPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {} +#endif void setup() override; void dump_config() override; diff --git a/esphome/const.py b/esphome/const.py index 8e3fd59ff0..7304653363 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -676,6 +676,7 @@ CONF_REVERSED = "reversed" CONF_RGB_ORDER = "rgb_order" CONF_RGBW = "rgbw" CONF_RISING_EDGE = "rising_edge" +CONF_RMT_CHANNEL = "rmt_channel" CONF_ROTATION = "rotation" CONF_RS_PIN = "rs_pin" CONF_RTD_NOMINAL_RESISTANCE = "rtd_nominal_resistance" diff --git a/tests/components/remote_receiver/esp32-common.yaml b/tests/components/remote_receiver/esp32-common.yaml new file mode 100644 index 0000000000..d1d47661c5 --- /dev/null +++ b/tests/components/remote_receiver/esp32-common.yaml @@ -0,0 +1,18 @@ +remote_receiver: + id: rcvr + pin: ${pin} + rmt_channel: ${rmt_channel} + dump: all + on_coolix: + then: + delay: !lambda "return x.first + x.second;" + on_rc_switch: + then: + delay: !lambda "return uint32_t(x.code) + x.protocol;" + +binary_sensor: + - platform: remote_receiver + name: Panasonic Remote Input + panasonic: + address: 0x4004 + command: 0x100BCBD diff --git a/tests/components/remote_receiver/test.esp32-c3-idf.yaml b/tests/components/remote_receiver/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..16d276958a --- /dev/null +++ b/tests/components/remote_receiver/test.esp32-c3-idf.yaml @@ -0,0 +1,6 @@ +substitutions: + pin: GPIO2 + rmt_channel: "2" + +packages: + common: !include esp32-common.yaml diff --git a/tests/components/remote_receiver/test.esp32-c3.yaml b/tests/components/remote_receiver/test.esp32-c3.yaml new file mode 100644 index 0000000000..16d276958a --- /dev/null +++ b/tests/components/remote_receiver/test.esp32-c3.yaml @@ -0,0 +1,6 @@ +substitutions: + pin: GPIO2 + rmt_channel: "2" + +packages: + common: !include esp32-common.yaml diff --git a/tests/components/remote_receiver/test.esp32-idf.yaml b/tests/components/remote_receiver/test.esp32-idf.yaml new file mode 100644 index 0000000000..16d276958a --- /dev/null +++ b/tests/components/remote_receiver/test.esp32-idf.yaml @@ -0,0 +1,6 @@ +substitutions: + pin: GPIO2 + rmt_channel: "2" + +packages: + common: !include esp32-common.yaml diff --git a/tests/components/remote_receiver/test.esp32-s3-idf.yaml b/tests/components/remote_receiver/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..265ecda771 --- /dev/null +++ b/tests/components/remote_receiver/test.esp32-s3-idf.yaml @@ -0,0 +1,6 @@ +substitutions: + pin: GPIO38 + rmt_channel: "5" + +packages: + common: !include esp32-common.yaml diff --git a/tests/components/remote_receiver/test.esp32.yaml b/tests/components/remote_receiver/test.esp32.yaml new file mode 100644 index 0000000000..16d276958a --- /dev/null +++ b/tests/components/remote_receiver/test.esp32.yaml @@ -0,0 +1,6 @@ +substitutions: + pin: GPIO2 + rmt_channel: "2" + +packages: + common: !include esp32-common.yaml diff --git a/tests/components/remote_receiver/test.esp8266.yaml b/tests/components/remote_receiver/test.esp8266.yaml new file mode 100644 index 0000000000..a7c283da1e --- /dev/null +++ b/tests/components/remote_receiver/test.esp8266.yaml @@ -0,0 +1,17 @@ +remote_receiver: + id: rcvr + pin: GPIO5 + dump: all + on_coolix: + then: + delay: !lambda "return x.first + x.second;" + on_rc_switch: + then: + delay: !lambda "return uint32_t(x.code) + x.protocol;" + +binary_sensor: + - platform: remote_receiver + name: Panasonic Remote Input + panasonic: + address: 0x4004 + command: 0x100BCBD diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml new file mode 100644 index 0000000000..5f655acb7c --- /dev/null +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -0,0 +1,178 @@ +button: + - platform: template + name: JVC Off + id: living_room_lights_on + on_press: + remote_transmitter.transmit_jvc: + data: 0x10EF + - platform: template + name: MagiQuest + on_press: + remote_transmitter.transmit_magiquest: + wand_id: 0x01234567 + - platform: template + name: NEC + id: living_room_lights_off + on_press: + remote_transmitter.transmit_nec: + address: 0x4242 + command: 0x8484 + - platform: template + name: LG + on_press: + remote_transmitter.transmit_lg: + data: 4294967295 + nbits: 28 + - platform: template + name: Samsung + on_press: + remote_transmitter.transmit_samsung: + data: 0xABCDEF + - platform: template + name: Samsung36 + on_press: + remote_transmitter.transmit_samsung36: + address: 0x0400 + command: 0x000E00FF + - platform: template + name: ToshibaAC + on_press: + - remote_transmitter.transmit_toshiba_ac: + rc_code_1: 0xB24DBF4050AF + rc_code_2: 0xD5660001003C + - platform: template + name: Sony + on_press: + remote_transmitter.transmit_sony: + data: 0xABCDEF + nbits: 12 + - platform: template + name: Panasonic + on_press: + remote_transmitter.transmit_panasonic: + address: 0x4004 + command: 0x1000BCD + - platform: template + name: Pioneer + on_press: + - remote_transmitter.transmit_pioneer: + rc_code_1: 0xA556 + rc_code_2: 0xA506 + repeat: + times: 2 + - platform: template + name: RC Switch Raw + on_press: + remote_transmitter.transmit_rc_switch_raw: + code: "00101001100111110101xxxx" + protocol: 1 + - platform: template + name: RC Switch Type A + on_press: + remote_transmitter.transmit_rc_switch_type_a: + group: "11001" + device: "01000" + state: true + protocol: + pulse_length: 175 + sync: [1, 31] + zero: [1, 3] + one: [3, 1] + inverted: false + - platform: template + name: RC Switch Type B + on_press: + remote_transmitter.transmit_rc_switch_type_b: + address: 4 + channel: 2 + state: true + - platform: template + name: RC Switch Type C + on_press: + remote_transmitter.transmit_rc_switch_type_c: + family: "a" + group: 1 + device: 2 + state: true + - platform: template + name: RC Switch Type D + on_press: + remote_transmitter.transmit_rc_switch_type_d: + group: "a" + device: 2 + state: true + - platform: template + name: RC5 + on_press: + remote_transmitter.transmit_rc5: + address: 0x00 + command: 0x0B + - platform: template + name: RC5 + on_press: + remote_transmitter.transmit_raw: + code: [1000, -1000] + - platform: template + name: AEHA + id: eaha_hitachi_climate_power_on + on_press: + remote_transmitter.transmit_aeha: + address: 0x8008 + data: + [ + 0x00, + 0x02, + 0xFD, + 0xFF, + 0x00, + 0x33, + 0xCC, + 0x49, + 0xB6, + 0xC8, + 0x37, + 0x16, + 0xE9, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0xCA, + 0x35, + 0x8F, + 0x70, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0xFF, + ] + - platform: template + name: Haier + on_press: + remote_transmitter.transmit_haier: + code: + [ + 0xA6, + 0xDA, + 0x00, + 0x00, + 0x40, + 0x40, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x05, + ] diff --git a/tests/components/remote_transmitter/esp32-common.yaml b/tests/components/remote_transmitter/esp32-common.yaml new file mode 100644 index 0000000000..3f3cd3f8c7 --- /dev/null +++ b/tests/components/remote_transmitter/esp32-common.yaml @@ -0,0 +1,8 @@ +remote_transmitter: + id: rcvr + pin: ${pin} + rmt_channel: ${rmt_channel} + carrier_duty_percent: 50% + +packages: + buttons: !include common-buttons.yaml diff --git a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..3e2dc88e5a --- /dev/null +++ b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml @@ -0,0 +1,6 @@ +substitutions: + pin: GPIO2 + rmt_channel: "1" + +packages: + common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp32-c3.yaml b/tests/components/remote_transmitter/test.esp32-c3.yaml new file mode 100644 index 0000000000..3e2dc88e5a --- /dev/null +++ b/tests/components/remote_transmitter/test.esp32-c3.yaml @@ -0,0 +1,6 @@ +substitutions: + pin: GPIO2 + rmt_channel: "1" + +packages: + common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp32-idf.yaml b/tests/components/remote_transmitter/test.esp32-idf.yaml new file mode 100644 index 0000000000..16d276958a --- /dev/null +++ b/tests/components/remote_transmitter/test.esp32-idf.yaml @@ -0,0 +1,6 @@ +substitutions: + pin: GPIO2 + rmt_channel: "2" + +packages: + common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..31851dc54c --- /dev/null +++ b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml @@ -0,0 +1,6 @@ +substitutions: + pin: GPIO38 + rmt_channel: "3" + +packages: + common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp32.yaml b/tests/components/remote_transmitter/test.esp32.yaml new file mode 100644 index 0000000000..16d276958a --- /dev/null +++ b/tests/components/remote_transmitter/test.esp32.yaml @@ -0,0 +1,6 @@ +substitutions: + pin: GPIO2 + rmt_channel: "2" + +packages: + common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp8266.yaml b/tests/components/remote_transmitter/test.esp8266.yaml new file mode 100644 index 0000000000..de494485f4 --- /dev/null +++ b/tests/components/remote_transmitter/test.esp8266.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + id: trns + pin: GPIO5 + carrier_duty_percent: 50% + +packages: + buttons: !include common-buttons.yaml From 12aa2722349fd494b5d0e8063d57c77bb46186d1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:51:54 +1200 Subject: [PATCH 0045/1106] Rework tlc5947 to remove AUTO_LOAD (#6503) --- esphome/components/tlc5947/__init__.py | 1 - .../tlc5947/{output.py => output/__init__.py} | 13 ++++---- .../tlc5947/output/tlc5947_output.cpp | 12 ++++++++ .../tlc5947/output/tlc5947_output.h | 22 ++++++++++++++ esphome/components/tlc5947/tlc5947.cpp | 9 ++++++ esphome/components/tlc5947/tlc5947.h | 30 ++----------------- tests/components/tlc5947/common.yaml | 13 ++++++++ .../components/tlc5947/test.esp32-c3-idf.yaml | 7 +++++ tests/components/tlc5947/test.esp32-c3.yaml | 7 +++++ tests/components/tlc5947/test.esp32-idf.yaml | 7 +++++ tests/components/tlc5947/test.esp32.yaml | 7 +++++ tests/components/tlc5947/test.esp8266.yaml | 7 +++++ tests/components/tlc5947/test.rp2040.yaml | 7 +++++ 13 files changed, 107 insertions(+), 35 deletions(-) rename esphome/components/tlc5947/{output.py => output/__init__.py} (69%) create mode 100644 esphome/components/tlc5947/output/tlc5947_output.cpp create mode 100644 esphome/components/tlc5947/output/tlc5947_output.h create mode 100644 tests/components/tlc5947/common.yaml create mode 100644 tests/components/tlc5947/test.esp32-c3-idf.yaml create mode 100644 tests/components/tlc5947/test.esp32-c3.yaml create mode 100644 tests/components/tlc5947/test.esp32-idf.yaml create mode 100644 tests/components/tlc5947/test.esp32.yaml create mode 100644 tests/components/tlc5947/test.esp8266.yaml create mode 100644 tests/components/tlc5947/test.rp2040.yaml diff --git a/esphome/components/tlc5947/__init__.py b/esphome/components/tlc5947/__init__.py index 84380bdace..8a4bd5e0ce 100644 --- a/esphome/components/tlc5947/__init__.py +++ b/esphome/components/tlc5947/__init__.py @@ -14,7 +14,6 @@ from esphome.const import ( CONF_LAT_PIN = "lat_pin" CONF_OE_PIN = "oe_pin" -AUTO_LOAD = ["output"] CODEOWNERS = ["@rnauber"] tlc5947_ns = cg.esphome_ns.namespace("tlc5947") diff --git a/esphome/components/tlc5947/output.py b/esphome/components/tlc5947/output/__init__.py similarity index 69% rename from esphome/components/tlc5947/output.py rename to esphome/components/tlc5947/output/__init__.py index ece47fa63d..1b5dff1854 100644 --- a/esphome/components/tlc5947/output.py +++ b/esphome/components/tlc5947/output/__init__.py @@ -2,18 +2,19 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import output from esphome.const import CONF_CHANNEL, CONF_ID -from . import TLC5947 +from .. import TLC5947, tlc5947_ns DEPENDENCIES = ["tlc5947"] -CODEOWNERS = ["@rnauber"] -Channel = TLC5947.class_("Channel", output.FloatOutput) +TLC5947Channel = tlc5947_ns.class_( + "TLC5947Channel", output.FloatOutput, cg.Parented.template(TLC5947) +) CONF_TLC5947_ID = "tlc5947_id" CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( { cv.GenerateID(CONF_TLC5947_ID): cv.use_id(TLC5947), - cv.Required(CONF_ID): cv.declare_id(Channel), + cv.Required(CONF_ID): cv.declare_id(TLC5947Channel), cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=65535), } ).extend(cv.COMPONENT_SCHEMA) @@ -22,7 +23,5 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await output.register_output(var, config) - - parent = await cg.get_variable(config[CONF_TLC5947_ID]) - cg.add(var.set_parent(parent)) + await cg.register_parented(var, config[CONF_TLC5947_ID]) cg.add(var.set_channel(config[CONF_CHANNEL])) diff --git a/esphome/components/tlc5947/output/tlc5947_output.cpp b/esphome/components/tlc5947/output/tlc5947_output.cpp new file mode 100644 index 0000000000..9630fb8c1e --- /dev/null +++ b/esphome/components/tlc5947/output/tlc5947_output.cpp @@ -0,0 +1,12 @@ +#include "tlc5947_output.h" + +namespace esphome { +namespace tlc5947 { + +void TLC5947Channel::write_state(float state) { + auto amount = static_cast(state * 0xfff); + this->parent_->set_channel_value(this->channel_, amount); +} + +} // namespace tlc5947 +} // namespace esphome diff --git a/esphome/components/tlc5947/output/tlc5947_output.h b/esphome/components/tlc5947/output/tlc5947_output.h new file mode 100644 index 0000000000..5b2c51020c --- /dev/null +++ b/esphome/components/tlc5947/output/tlc5947_output.h @@ -0,0 +1,22 @@ +#pragma once + +#include "esphome/core/helpers.h" + +#include "esphome/components/output/float_output.h" + +#include "../tlc5947.h" + +namespace esphome { +namespace tlc5947 { + +class TLC5947Channel : public output::FloatOutput, public Parented { + public: + void set_channel(uint8_t channel) { this->channel_ = channel; } + + protected: + void write_state(float state) override; + uint8_t channel_; +}; + +} // namespace tlc5947 +} // namespace esphome diff --git a/esphome/components/tlc5947/tlc5947.cpp b/esphome/components/tlc5947/tlc5947.cpp index 8f3f60f087..5a5c0c17c0 100644 --- a/esphome/components/tlc5947/tlc5947.cpp +++ b/esphome/components/tlc5947/tlc5947.cpp @@ -60,5 +60,14 @@ void TLC5947::loop() { this->update_ = false; } +void TLC5947::set_channel_value(uint16_t channel, uint16_t value) { + if (channel >= this->num_chips_ * N_CHANNELS_PER_CHIP) + return; + if (this->pwm_amounts_[channel] != value) { + this->update_ = true; + } + this->pwm_amounts_[channel] = value; +} + } // namespace tlc5947 } // namespace esphome diff --git a/esphome/components/tlc5947/tlc5947.h b/esphome/components/tlc5947/tlc5947.h index 0eb7f10604..95d76408c9 100644 --- a/esphome/components/tlc5947/tlc5947.h +++ b/esphome/components/tlc5947/tlc5947.h @@ -2,18 +2,16 @@ // TLC5947 24-Channel, 12-Bit PWM LED Driver // https://www.ti.com/lit/ds/symlink/tlc5947.pdf +#include +#include "esphome/components/output/float_output.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" -#include "esphome/components/output/float_output.h" -#include namespace esphome { namespace tlc5947 { class TLC5947 : public Component { public: - class Channel; - const uint8_t N_CHANNELS_PER_CHIP = 24; void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; } @@ -31,31 +29,9 @@ class TLC5947 : public Component { /// Send new values if they were updated. void loop() override; - class Channel : public output::FloatOutput { - public: - void set_parent(TLC5947 *parent) { parent_ = parent; } - void set_channel(uint8_t channel) { channel_ = channel; } - - protected: - void write_state(float state) override { - auto amount = static_cast(state * 0xfff); - this->parent_->set_channel_value_(this->channel_, amount); - } - - TLC5947 *parent_; - uint8_t channel_; - }; + void set_channel_value(uint16_t channel, uint16_t value); protected: - void set_channel_value_(uint16_t channel, uint16_t value) { - if (channel >= this->num_chips_ * N_CHANNELS_PER_CHIP) - return; - if (this->pwm_amounts_[channel] != value) { - this->update_ = true; - } - this->pwm_amounts_[channel] = value; - } - GPIOPin *data_pin_; GPIOPin *clock_pin_; GPIOPin *lat_pin_; diff --git a/tests/components/tlc5947/common.yaml b/tests/components/tlc5947/common.yaml new file mode 100644 index 0000000000..89588f3c76 --- /dev/null +++ b/tests/components/tlc5947/common.yaml @@ -0,0 +1,13 @@ +tlc5947: + clock_pin: ${clock_pin} + data_pin: ${data_pin} + lat_pin: ${lat_pin} + +output: + - platform: tlc5947 + id: output_1 + channel: 0 + max_power: 0.8 + - platform: tlc5947 + id: output_2 + channel: 1 diff --git a/tests/components/tlc5947/test.esp32-c3-idf.yaml b/tests/components/tlc5947/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..4694c43642 --- /dev/null +++ b/tests/components/tlc5947/test.esp32-c3-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + lat_pin: GPIO3 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5947/test.esp32-c3.yaml b/tests/components/tlc5947/test.esp32-c3.yaml new file mode 100644 index 0000000000..4694c43642 --- /dev/null +++ b/tests/components/tlc5947/test.esp32-c3.yaml @@ -0,0 +1,7 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + lat_pin: GPIO3 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5947/test.esp32-idf.yaml b/tests/components/tlc5947/test.esp32-idf.yaml new file mode 100644 index 0000000000..52411bc1e9 --- /dev/null +++ b/tests/components/tlc5947/test.esp32-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + clock_pin: GPIO15 + data_pin: GPIO14 + lat_pin: GPIO13 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5947/test.esp32.yaml b/tests/components/tlc5947/test.esp32.yaml new file mode 100644 index 0000000000..52411bc1e9 --- /dev/null +++ b/tests/components/tlc5947/test.esp32.yaml @@ -0,0 +1,7 @@ +substitutions: + clock_pin: GPIO15 + data_pin: GPIO14 + lat_pin: GPIO13 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5947/test.esp8266.yaml b/tests/components/tlc5947/test.esp8266.yaml new file mode 100644 index 0000000000..44da5a07b3 --- /dev/null +++ b/tests/components/tlc5947/test.esp8266.yaml @@ -0,0 +1,7 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + lat_pin: GPIO13 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5947/test.rp2040.yaml b/tests/components/tlc5947/test.rp2040.yaml new file mode 100644 index 0000000000..4694c43642 --- /dev/null +++ b/tests/components/tlc5947/test.rp2040.yaml @@ -0,0 +1,7 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + lat_pin: GPIO3 + +packages: + common: !include common.yaml From 0ba4e8c0bac001a2c59a2e209cc2904ae1d8b7e8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:55:20 +1200 Subject: [PATCH 0046/1106] UART: ignore require_tx/rx if not a native uart implementation (#6504) --- esphome/components/uart/__init__.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index 9005422ce6..82bc6caaa4 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -46,6 +46,14 @@ LibreTinyUARTComponent = uart_ns.class_( "LibreTinyUARTComponent", UARTComponent, cg.Component ) +NATIVE_UART_CLASSES = ( + str(IDFUARTComponent), + str(ESP32ArduinoUARTComponent), + str(ESP8266UartComponent), + str(RP2040UartComponent), + str(LibreTinyUARTComponent), +) + UARTDevice = uart_ns.class_("UARTDevice") UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action) UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action) @@ -299,17 +307,18 @@ def final_validate_device_schema( def validate_hub(hub_config): hub_schema = {} uart_id = hub_config[CONF_ID] + uart_id_type_str = str(uart_id.type) devices = fv.full_config.get().data.setdefault(KEY_UART_DEVICES, {}) device = devices.setdefault(uart_id, {}) - if require_tx: + if require_tx and uart_id_type_str in NATIVE_UART_CLASSES: hub_schema[ cv.Required( CONF_TX_PIN, msg=f"Component {name} requires this uart bus to declare a tx_pin", ) ] = validate_pin(CONF_TX_PIN, device) - if require_rx: + if require_rx and uart_id_type_str in NATIVE_UART_CLASSES: hub_schema[ cv.Required( CONF_RX_PIN, From 857b8ef363e93b5755533f400406d490526ca6dc Mon Sep 17 00:00:00 2001 From: Mat931 <49403702+Mat931@users.noreply.github.com> Date: Tue, 9 Apr 2024 19:14:56 +0000 Subject: [PATCH 0047/1106] esp32_rmt_led_strip bugfixes (#6506) --- esphome/components/esp32_rmt_led_strip/led_strip.h | 2 +- esphome/components/esp32_rmt_led_strip/light.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.h b/esphome/components/esp32_rmt_led_strip/led_strip.h index 51eed8e01c..e9b19c9399 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.h +++ b/esphome/components/esp32_rmt_led_strip/led_strip.h @@ -64,7 +64,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { protected: light::ESPColorView get_view_internal(int32_t index) const override; - size_t get_buffer_size_() const { return this->num_leds_ * (3 + this->is_rgbw_); } + size_t get_buffer_size_() const { return this->num_leds_ * (this->is_rgbw_ || this->is_wrgb_ ? 4 : 3); } uint8_t *buf_{nullptr}; uint8_t *effect_data_{nullptr}; diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 3442940e3f..1c15a468d9 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -48,7 +48,7 @@ CHIPSETS = { "WS2812": LEDStripTimings(400, 1000, 1000, 400), "SK6812": LEDStripTimings(300, 900, 600, 600), "APA106": LEDStripTimings(350, 1360, 1360, 350), - "SM16703": LEDStripTimings(300, 900, 1360, 350), + "SM16703": LEDStripTimings(300, 900, 900, 300), } From 3adfed3675b3515f74ca9f8147c28092d69bda3d Mon Sep 17 00:00:00 2001 From: IJssel Date: Tue, 9 Apr 2024 22:03:18 +0200 Subject: [PATCH 0048/1106] Implemented support for the TLC5971 as an output component (#6494) --- CODEOWNERS | 1 + esphome/components/tlc5971/__init__.py | 40 +++++++ esphome/components/tlc5971/output/__init__.py | 27 +++++ .../tlc5971/output/tlc5971_output.cpp | 12 +++ .../tlc5971/output/tlc5971_output.h | 22 ++++ esphome/components/tlc5971/tlc5971.cpp | 101 ++++++++++++++++++ esphome/components/tlc5971/tlc5971.h | 43 ++++++++ tests/components/tlc5971/common.yaml | 12 +++ .../components/tlc5971/test.esp32-c3-idf.yaml | 6 ++ tests/components/tlc5971/test.esp32-c3.yaml | 6 ++ tests/components/tlc5971/test.esp32-idf.yaml | 6 ++ tests/components/tlc5971/test.esp32-s2.yaml | 6 ++ tests/components/tlc5971/test.esp32.yaml | 7 ++ tests/components/tlc5971/test.esp8266.yaml | 7 ++ tests/components/tlc5971/test.rp2040.yaml | 7 ++ 15 files changed, 303 insertions(+) create mode 100644 esphome/components/tlc5971/__init__.py create mode 100644 esphome/components/tlc5971/output/__init__.py create mode 100644 esphome/components/tlc5971/output/tlc5971_output.cpp create mode 100644 esphome/components/tlc5971/output/tlc5971_output.h create mode 100644 esphome/components/tlc5971/tlc5971.cpp create mode 100644 esphome/components/tlc5971/tlc5971.h create mode 100644 tests/components/tlc5971/common.yaml create mode 100644 tests/components/tlc5971/test.esp32-c3-idf.yaml create mode 100644 tests/components/tlc5971/test.esp32-c3.yaml create mode 100644 tests/components/tlc5971/test.esp32-idf.yaml create mode 100644 tests/components/tlc5971/test.esp32-s2.yaml create mode 100644 tests/components/tlc5971/test.esp32.yaml create mode 100644 tests/components/tlc5971/test.esp8266.yaml create mode 100644 tests/components/tlc5971/test.rp2040.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 126513943f..9f770d4efc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -364,6 +364,7 @@ esphome/components/text/* @mauritskorse esphome/components/thermostat/* @kbx81 esphome/components/time/* @OttoWinter esphome/components/tlc5947/* @rnauber +esphome/components/tlc5971/* @IJIJI esphome/components/tm1621/* @Philippe12 esphome/components/tm1637/* @glmnet esphome/components/tm1638/* @skykingjwc diff --git a/esphome/components/tlc5971/__init__.py b/esphome/components/tlc5971/__init__.py new file mode 100644 index 0000000000..0ff2a5d176 --- /dev/null +++ b/esphome/components/tlc5971/__init__.py @@ -0,0 +1,40 @@ +# this component is for the "TLC5971 12-Channel, 12-Bit PWM LED Driver" [https://www.ti.com/lit/ds/symlink/tlc5971.pdf], +# which is used e.g. on [https://www.adafruit.com/product/1455]. The code is based on the TLC5947 component by @rnauber. + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.const import ( + CONF_CLOCK_PIN, + CONF_DATA_PIN, + CONF_ID, + CONF_NUM_CHIPS, +) + + +CODEOWNERS = ["@IJIJI"] + +tlc5971_ns = cg.esphome_ns.namespace("tlc5971") +TLC5971 = tlc5971_ns.class_("TLC5971", cg.Component) + +MULTI_CONF = True +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(TLC5971), + cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=85), + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + data = await cg.gpio_pin_expression(config[CONF_DATA_PIN]) + cg.add(var.set_data_pin(data)) + clock = await cg.gpio_pin_expression(config[CONF_CLOCK_PIN]) + cg.add(var.set_clock_pin(clock)) + + cg.add(var.set_num_chips(config[CONF_NUM_CHIPS])) diff --git a/esphome/components/tlc5971/output/__init__.py b/esphome/components/tlc5971/output/__init__.py new file mode 100644 index 0000000000..9fe7b18294 --- /dev/null +++ b/esphome/components/tlc5971/output/__init__.py @@ -0,0 +1,27 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import output +from esphome.const import CONF_CHANNEL, CONF_ID +from .. import TLC5971, tlc5971_ns + +DEPENDENCIES = ["tlc5971"] + +TLC5971Channel = tlc5971_ns.class_( + "TLC5971Channel", output.FloatOutput, cg.Parented.template(TLC5971) +) + +CONF_TLC5971_ID = "tlc5971_id" +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.GenerateID(CONF_TLC5971_ID): cv.use_id(TLC5971), + cv.Required(CONF_ID): cv.declare_id(TLC5971Channel), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=65535), + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await output.register_output(var, config) + await cg.register_parented(var, config[CONF_TLC5971_ID]) + cg.add(var.set_channel(config[CONF_CHANNEL])) diff --git a/esphome/components/tlc5971/output/tlc5971_output.cpp b/esphome/components/tlc5971/output/tlc5971_output.cpp new file mode 100644 index 0000000000..b437889072 --- /dev/null +++ b/esphome/components/tlc5971/output/tlc5971_output.cpp @@ -0,0 +1,12 @@ +#include "tlc5971_output.h" + +namespace esphome { +namespace tlc5971 { + +void TLC5971Channel::write_state(float state) { + auto amount = static_cast(state * 0xffff); + this->parent_->set_channel_value(this->channel_, amount); +} + +} // namespace tlc5971 +} // namespace esphome diff --git a/esphome/components/tlc5971/output/tlc5971_output.h b/esphome/components/tlc5971/output/tlc5971_output.h new file mode 100644 index 0000000000..944ee19b2d --- /dev/null +++ b/esphome/components/tlc5971/output/tlc5971_output.h @@ -0,0 +1,22 @@ +#pragma once + +#include "esphome/core/helpers.h" + +#include "esphome/components/output/float_output.h" + +#include "../tlc5971.h" + +namespace esphome { +namespace tlc5971 { + +class TLC5971Channel : public output::FloatOutput, public Parented { + public: + void set_channel(uint8_t channel) { this->channel_ = channel; } + + protected: + void write_state(float state) override; + uint8_t channel_; +}; + +} // namespace tlc5971 +} // namespace esphome diff --git a/esphome/components/tlc5971/tlc5971.cpp b/esphome/components/tlc5971/tlc5971.cpp new file mode 100644 index 0000000000..ebcc3af361 --- /dev/null +++ b/esphome/components/tlc5971/tlc5971.cpp @@ -0,0 +1,101 @@ +#include "tlc5971.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace tlc5971 { + +static const char *const TAG = "tlc5971"; + +void TLC5971::setup() { + this->data_pin_->setup(); + this->data_pin_->digital_write(true); + this->clock_pin_->setup(); + this->clock_pin_->digital_write(true); + + this->pwm_amounts_.resize(this->num_chips_ * N_CHANNELS_PER_CHIP, 0); + + ESP_LOGCONFIG(TAG, "Done setting up TLC5971 output component."); +} +void TLC5971::dump_config() { + ESP_LOGCONFIG(TAG, "TLC5971:"); + LOG_PIN(" Data Pin: ", this->data_pin_); + LOG_PIN(" Clock Pin: ", this->clock_pin_); + ESP_LOGCONFIG(TAG, " Number of chips: %u", this->num_chips_); +} + +void TLC5971::loop() { + if (!this->update_) + return; + + uint32_t command; + + // Magic word for write + command = 0x25; + + command <<= 5; + // OUTTMG = 1, EXTGCK = 0, TMGRST = 1, DSPRPT = 1, BLANK = 0 -> 0x16 + command |= 0x16; + + command <<= 7; + command |= 0x7F; // default 100% brigthness + + command <<= 7; + command |= 0x7F; // default 100% brigthness + + command <<= 7; + command |= 0x7F; // default 100% brigthness + + for (uint8_t n = 0; n < num_chips_; n++) { + this->transfer_(command >> 24); + this->transfer_(command >> 16); + this->transfer_(command >> 8); + this->transfer_(command); + + // 12 channels per TLC59711 + for (int8_t c = 11; c >= 0; c--) { + // 16 bits per channel, send MSB first + this->transfer_(pwm_amounts_.at(n * 12 + c) >> 8); + this->transfer_(pwm_amounts_.at(n * 12 + c)); + } + } + + this->update_ = false; +} + +void TLC5971::transfer_(uint8_t send) { + uint8_t startbit = 0x80; + + bool towrite, lastmosi = !(send & startbit); + uint8_t bitdelay_us = (1000000 / 1000000) / 2; + + for (uint8_t b = startbit; b != 0; b = b >> 1) { + if (bitdelay_us) { + delayMicroseconds(bitdelay_us); + } + + towrite = send & b; + if ((lastmosi != towrite)) { + this->data_pin_->digital_write(towrite); + lastmosi = towrite; + } + + this->clock_pin_->digital_write(true); + + if (bitdelay_us) { + delayMicroseconds(bitdelay_us); + } + + this->clock_pin_->digital_write(false); + } +} +void TLC5971::set_channel_value(uint16_t channel, uint16_t value) { + if (channel >= this->num_chips_ * N_CHANNELS_PER_CHIP) + return; + if (this->pwm_amounts_[channel] != value) { + this->update_ = true; + } + this->pwm_amounts_[channel] = value; +} + +} // namespace tlc5971 +} // namespace esphome diff --git a/esphome/components/tlc5971/tlc5971.h b/esphome/components/tlc5971/tlc5971.h new file mode 100644 index 0000000000..6b0daf10d1 --- /dev/null +++ b/esphome/components/tlc5971/tlc5971.h @@ -0,0 +1,43 @@ +#pragma once +// TLC5971 12-Channel, 16-Bit PWM LED Driver +// https://www.ti.com/lit/ds/symlink/tlc5971.pdf + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/components/output/float_output.h" +#include + +namespace esphome { +namespace tlc5971 { + +class TLC5971 : public Component { + public: + const uint8_t N_CHANNELS_PER_CHIP = 12; + + void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; } + void set_clock_pin(GPIOPin *clock_pin) { clock_pin_ = clock_pin; } + void set_num_chips(uint8_t num_chips) { num_chips_ = num_chips; } + + void setup() override; + + void dump_config() override; + + float get_setup_priority() const override { return setup_priority::HARDWARE; } + + /// Send new values if they were updated. + void loop() override; + + void set_channel_value(uint16_t channel, uint16_t value); + + protected: + void transfer_(uint8_t send); + + GPIOPin *data_pin_; + GPIOPin *clock_pin_; + uint8_t num_chips_; + + std::vector pwm_amounts_; + bool update_{true}; +}; +} // namespace tlc5971 +} // namespace esphome diff --git a/tests/components/tlc5971/common.yaml b/tests/components/tlc5971/common.yaml new file mode 100644 index 0000000000..fe7fe25f0e --- /dev/null +++ b/tests/components/tlc5971/common.yaml @@ -0,0 +1,12 @@ +tlc5971: + clock_pin: ${clock_pin} + data_pin: ${data_pin} + +output: + - platform: tlc5971 + id: output_1 + channel: 0 + max_power: 0.8 + - platform: tlc5971 + id: output_2 + channel: 1 diff --git a/tests/components/tlc5971/test.esp32-c3-idf.yaml b/tests/components/tlc5971/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..d898a21d46 --- /dev/null +++ b/tests/components/tlc5971/test.esp32-c3-idf.yaml @@ -0,0 +1,6 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5971/test.esp32-c3.yaml b/tests/components/tlc5971/test.esp32-c3.yaml new file mode 100644 index 0000000000..d898a21d46 --- /dev/null +++ b/tests/components/tlc5971/test.esp32-c3.yaml @@ -0,0 +1,6 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5971/test.esp32-idf.yaml b/tests/components/tlc5971/test.esp32-idf.yaml new file mode 100644 index 0000000000..e8943a572a --- /dev/null +++ b/tests/components/tlc5971/test.esp32-idf.yaml @@ -0,0 +1,6 @@ +substitutions: + clock_pin: GPIO15 + data_pin: GPIO14 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5971/test.esp32-s2.yaml b/tests/components/tlc5971/test.esp32-s2.yaml new file mode 100644 index 0000000000..7bba0b0117 --- /dev/null +++ b/tests/components/tlc5971/test.esp32-s2.yaml @@ -0,0 +1,6 @@ +substitutions: + clock_pin: GPIO36 + data_pin: GPIO35 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5971/test.esp32.yaml b/tests/components/tlc5971/test.esp32.yaml new file mode 100644 index 0000000000..52411bc1e9 --- /dev/null +++ b/tests/components/tlc5971/test.esp32.yaml @@ -0,0 +1,7 @@ +substitutions: + clock_pin: GPIO15 + data_pin: GPIO14 + lat_pin: GPIO13 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5971/test.esp8266.yaml b/tests/components/tlc5971/test.esp8266.yaml new file mode 100644 index 0000000000..52411bc1e9 --- /dev/null +++ b/tests/components/tlc5971/test.esp8266.yaml @@ -0,0 +1,7 @@ +substitutions: + clock_pin: GPIO15 + data_pin: GPIO14 + lat_pin: GPIO13 + +packages: + common: !include common.yaml diff --git a/tests/components/tlc5971/test.rp2040.yaml b/tests/components/tlc5971/test.rp2040.yaml new file mode 100644 index 0000000000..4694c43642 --- /dev/null +++ b/tests/components/tlc5971/test.rp2040.yaml @@ -0,0 +1,7 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + lat_pin: GPIO3 + +packages: + common: !include common.yaml From 522b43bb41d8ef37bc6492ee895980d921db239d Mon Sep 17 00:00:00 2001 From: bukureckid Date: Tue, 9 Apr 2024 23:04:35 +0200 Subject: [PATCH 0049/1106] Add Dooya protocol to remote_base (#6488) --- esphome/components/remote_base/__init__.py | 54 ++++++++ .../components/remote_base/dooya_protocol.cpp | 120 ++++++++++++++++++ .../components/remote_base/dooya_protocol.h | 49 +++++++ .../xiaomi_rtcgq02lm/binary_sensor.py | 3 +- esphome/const.py | 3 + 5 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 esphome/components/remote_base/dooya_protocol.cpp create mode 100644 esphome/components/remote_base/dooya_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index c771f406d8..6deab63c60 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -33,6 +33,9 @@ from esphome.const import ( CONF_WAND_ID, CONF_LEVEL, CONF_DELTA, + CONF_ID, + CONF_BUTTON, + CONF_CHECK, ) from esphome.core import coroutine from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor @@ -512,6 +515,57 @@ async def dish_action(var, config, args): cg.add(var.set_command(template_)) +# Dooya +DooyaData, DooyaBinarySensor, DooyaTrigger, DooyaAction, DooyaDumper = declare_protocol( + "Dooya" +) +DOOYA_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.hex_int_range(0, 16777215), + cv.Required(CONF_CHANNEL): cv.hex_int_range(0, 255), + cv.Required(CONF_BUTTON): cv.hex_int_range(0, 15), + cv.Required(CONF_CHECK): cv.hex_int_range(0, 15), + } +) + + +@register_binary_sensor("dooya", DooyaBinarySensor, DOOYA_SCHEMA) +def dooya_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + DooyaData, + ("id", config[CONF_ID]), + ("channel", config[CONF_CHANNEL]), + ("button", config[CONF_BUTTON]), + ("check", config[CONF_CHECK]), + ) + ) + ) + + +@register_trigger("dooya", DooyaTrigger, DooyaData) +def dooya_trigger(var, config): + pass + + +@register_dumper("dooya", DooyaDumper) +def dooya_dumper(var, config): + pass + + +@register_action("dooya", DooyaAction, DOOYA_SCHEMA) +async def dooya_action(var, config, args): + template_ = await cg.templatable(config[CONF_ID], args, cg.uint32) + cg.add(var.set_id(template_)) + template_ = await cg.templatable(config[CONF_CHANNEL], args, cg.uint8) + cg.add(var.set_channel(template_)) + template_ = await cg.templatable(config[CONF_BUTTON], args, cg.uint8) + cg.add(var.set_button(template_)) + template_ = await cg.templatable(config[CONF_CHECK], args, cg.uint8) + cg.add(var.set_check(template_)) + + # JVC JVCData, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol("JVC") JVC_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t}) diff --git a/esphome/components/remote_base/dooya_protocol.cpp b/esphome/components/remote_base/dooya_protocol.cpp new file mode 100644 index 0000000000..d979bca8c5 --- /dev/null +++ b/esphome/components/remote_base/dooya_protocol.cpp @@ -0,0 +1,120 @@ +#include "dooya_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.dooya"; + +static const uint32_t HEADER_HIGH_US = 5000; +static const uint32_t HEADER_LOW_US = 1500; +static const uint32_t BIT_ZERO_HIGH_US = 750; +static const uint32_t BIT_ZERO_LOW_US = 350; +static const uint32_t BIT_ONE_HIGH_US = 350; +static const uint32_t BIT_ONE_LOW_US = 750; + +void DooyaProtocol::encode(RemoteTransmitData *dst, const DooyaData &data) { + dst->set_carrier_frequency(0); + dst->reserve(2 + 40 * 2u); + + dst->item(HEADER_HIGH_US, HEADER_LOW_US); + + for (uint32_t mask = 1UL << (23); mask != 0; mask >>= 1) { + if (data.id & mask) { + dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US); + } + } + + for (uint32_t mask = 1UL << (7); mask != 0; mask >>= 1) { + if (data.channel & mask) { + dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US); + } + } + + for (uint32_t mask = 1UL << (3); mask != 0; mask >>= 1) { + if (data.button & mask) { + dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US); + } + } + + for (uint32_t mask = 1UL << (3); mask != 0; mask >>= 1) { + if (data.check & mask) { + dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US); + } + } +} +optional DooyaProtocol::decode(RemoteReceiveData src) { + DooyaData out{ + .id = 0, + .channel = 0, + .button = 0, + .check = 0, + }; + if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) + return {}; + + for (uint8_t i = 0; i < 24; i++) { + if (src.expect_item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US)) { + out.id = (out.id << 1) | 1; + } else if (src.expect_item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US)) { + out.id = (out.id << 1) | 0; + } else { + return {}; + } + } + + for (uint8_t i = 0; i < 8; i++) { + if (src.expect_item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US)) { + out.channel = (out.channel << 1) | 1; + } else if (src.expect_item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US)) { + out.channel = (out.channel << 1) | 0; + } else { + return {}; + } + } + + for (uint8_t i = 0; i < 4; i++) { + if (src.expect_item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US)) { + out.button = (out.button << 1) | 1; + } else if (src.expect_item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US)) { + out.button = (out.button << 1) | 0; + } else { + return {}; + } + } + + for (uint8_t i = 0; i < 3; i++) { + if (src.expect_item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US)) { + out.check = (out.check << 1) | 1; + } else if (src.expect_item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US)) { + out.check = (out.check << 1) | 0; + } else { + return {}; + } + } + // Last bit is not received properly but can be decoded + if (src.expect_mark(BIT_ONE_HIGH_US)) { + out.check = (out.check << 1) | 1; + } else if (src.expect_mark(BIT_ZERO_HIGH_US)) { + out.check = (out.check << 1) | 0; + } else { + return {}; + } + + return out; +} +void DooyaProtocol::dump(const DooyaData &data) { + ESP_LOGI(TAG, "Received Dooya: id=0x%08" PRIX32 ", channel=%d, button=%d, check=%d", data.id, data.channel, + data.button, data.check); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/dooya_protocol.h b/esphome/components/remote_base/dooya_protocol.h new file mode 100644 index 0000000000..9d17ad5d5e --- /dev/null +++ b/esphome/components/remote_base/dooya_protocol.h @@ -0,0 +1,49 @@ +#pragma once + +#include "esphome/core/component.h" +#include "remote_base.h" + +#include + +namespace esphome { +namespace remote_base { + +struct DooyaData { + uint32_t id; + uint8_t channel; + uint8_t button; + uint8_t check; + + bool operator==(const DooyaData &rhs) const { + return id == rhs.id && channel == rhs.channel && button == rhs.button && check == rhs.check; + } +}; + +class DooyaProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const DooyaData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const DooyaData &data) override; +}; + +DECLARE_REMOTE_PROTOCOL(Dooya) + +template class DooyaAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint32_t, id) + TEMPLATABLE_VALUE(uint8_t, channel) + TEMPLATABLE_VALUE(uint8_t, button) + TEMPLATABLE_VALUE(uint8_t, check) + + void encode(RemoteTransmitData *dst, Ts... x) override { + DooyaData data{}; + data.id = this->id_.value(x...); + data.channel = this->channel_.value(x...); + data.button = this->button_.value(x...); + data.check = this->check_.value(x...); + DooyaProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/xiaomi_rtcgq02lm/binary_sensor.py b/esphome/components/xiaomi_rtcgq02lm/binary_sensor.py index 8eee10685e..ef8a472d66 100644 --- a/esphome/components/xiaomi_rtcgq02lm/binary_sensor.py +++ b/esphome/components/xiaomi_rtcgq02lm/binary_sensor.py @@ -8,6 +8,7 @@ from esphome.const import ( DEVICE_CLASS_LIGHT, DEVICE_CLASS_MOTION, CONF_ID, + CONF_BUTTON, ) from esphome.core import TimePeriod @@ -15,8 +16,6 @@ from . import XiaomiRTCGQ02LM DEPENDENCIES = ["xiaomi_rtcgq02lm"] -CONF_BUTTON = "button" - CONFIG_SCHEMA = cv.Schema( { diff --git a/esphome/const.py b/esphome/const.py index 7304653363..992d148bdc 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -96,6 +96,7 @@ CONF_BUFFER_SIZE = "buffer_size" CONF_BUILD_PATH = "build_path" CONF_BUS_VOLTAGE = "bus_voltage" CONF_BUSY_PIN = "busy_pin" +CONF_BUTTON = "button" CONF_BYTES = "bytes" CONF_CALCULATED_LUX = "calculated_lux" CONF_CALIBRATE_LINEAR = "calibrate_linear" @@ -110,6 +111,7 @@ CONF_CHANGE_MODE_EVERY = "change_mode_every" CONF_CHANNEL = "channel" CONF_CHANNELS = "channels" CONF_CHARACTERISTIC_UUID = "characteristic_uuid" +CONF_CHECK = "check" CONF_CHIPSET = "chipset" CONF_CLEAR_IMPEDANCE = "clear_impedance" CONF_CLIENT_CERTIFICATE = "client_certificate" @@ -220,6 +222,7 @@ CONF_DNS_ADDRESS = "dns_address" CONF_DNS1 = "dns1" CONF_DNS2 = "dns2" CONF_DOMAIN = "domain" +CONF_DOOYA = "dooya" CONF_DRY_ACTION = "dry_action" CONF_DRY_MODE = "dry_mode" CONF_DUMMY_RECEIVER = "dummy_receiver" From e5e8bc85152ae93d3531a329ab85d08752338162 Mon Sep 17 00:00:00 2001 From: leejoow Date: Wed, 10 Apr 2024 01:22:18 +0200 Subject: [PATCH 0050/1106] Only give error for connected sensors at startup (#6474) Co-authored-by: Leo Schelvis --- esphome/components/dallas/dallas_component.cpp | 18 +++++++++++++++--- esphome/components/dallas/dallas_component.h | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/esphome/components/dallas/dallas_component.cpp b/esphome/components/dallas/dallas_component.cpp index b1983fef72..a51bc369a1 100644 --- a/esphome/components/dallas/dallas_component.cpp +++ b/esphome/components/dallas/dallas_component.cpp @@ -60,7 +60,7 @@ void DallasComponent::setup() { for (auto *sensor : this->sensors_) { if (sensor->get_index().has_value()) { if (*sensor->get_index() >= this->found_sensors_.size()) { - this->status_set_error(); + this->status_set_error("Sensor configured by index but not found"); continue; } sensor->set_address(this->found_sensors_[*sensor->get_index()]); @@ -109,8 +109,12 @@ void DallasComponent::update() { result = this->one_wire_->reset(); } if (!result) { - ESP_LOGE(TAG, "Requesting conversion failed"); - this->status_set_warning(); + if (!this->found_sensors_.empty()) { + // Only log error if at the start sensors were found (and thus are disconnected during uptime) + ESP_LOGE(TAG, "Requesting conversion failed"); + this->status_set_warning(); + } + for (auto *sensor : this->sensors_) { sensor->publish_state(NAN); } @@ -124,6 +128,12 @@ void DallasComponent::update() { } for (auto *sensor : this->sensors_) { + if (sensor->get_address() == 0) { + ESP_LOGV(TAG, "'%s' - Indexed sensor not found at startup, skipping update", sensor->get_name().c_str()); + sensor->publish_state(NAN); + continue; + } + this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] { bool res = sensor->read_scratch_pad(); @@ -152,6 +162,8 @@ void DallasTemperatureSensor::set_resolution(uint8_t resolution) { this->resolut optional DallasTemperatureSensor::get_index() const { return this->index_; } void DallasTemperatureSensor::set_index(uint8_t index) { this->index_ = index; } uint8_t *DallasTemperatureSensor::get_address8() { return reinterpret_cast(&this->address_); } +uint64_t DallasTemperatureSensor::get_address() { return this->address_; } + const std::string &DallasTemperatureSensor::get_address_name() { if (this->address_name_.empty()) { this->address_name_ = std::string("0x") + format_hex(this->address_); diff --git a/esphome/components/dallas/dallas_component.h b/esphome/components/dallas/dallas_component.h index b21bc02e54..10bde7338b 100644 --- a/esphome/components/dallas/dallas_component.h +++ b/esphome/components/dallas/dallas_component.h @@ -37,6 +37,7 @@ class DallasTemperatureSensor : public sensor::Sensor { void set_parent(DallasComponent *parent) { parent_ = parent; } /// Helper to get a pointer to the address as uint8_t. uint8_t *get_address8(); + uint64_t get_address(); /// Helper to create (and cache) the name for this sensor. For example "0xfe0000031f1eaf29". const std::string &get_address_name(); From b4b4e81c1c443add8811c7e9dd36875ff93d71fc Mon Sep 17 00:00:00 2001 From: RFDarter Date: Wed, 10 Apr 2024 01:33:26 +0200 Subject: [PATCH 0051/1106] Webserver float to string fix (#6507) --- esphome/components/web_server/web_server.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index b48a39cbcb..2e6206b691 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -851,9 +851,12 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail return json::build_json([obj, value, start_config](JsonObject root) { set_json_id(root, obj, "number-" + obj->get_object_id(), start_config); if (start_config == DETAIL_ALL) { - root["min_value"] = obj->traits.get_min_value(); - root["max_value"] = obj->traits.get_max_value(); - root["step"] = obj->traits.get_step(); + root["min_value"] = + value_accuracy_to_string(obj->traits.get_min_value(), step_to_accuracy_decimals(obj->traits.get_step())); + root["max_value"] = + value_accuracy_to_string(obj->traits.get_max_value(), step_to_accuracy_decimals(obj->traits.get_step())); + root["step"] = + value_accuracy_to_string(obj->traits.get_step(), step_to_accuracy_decimals(obj->traits.get_step())); root["mode"] = (int) obj->traits.get_mode(); if (!obj->traits.get_unit_of_measurement().empty()) root["uom"] = obj->traits.get_unit_of_measurement(); @@ -862,7 +865,7 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail root["value"] = "\"NaN\""; root["state"] = "NA"; } else { - root["value"] = value; + root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); std::string state = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); if (!obj->traits.get_unit_of_measurement().empty()) state += " " + obj->traits.get_unit_of_measurement(); From 9af083af037fab54d9973691aaf59e1069bfe040 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:49:45 +1200 Subject: [PATCH 0052/1106] Bump version to 2024.4.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 992d148bdc..5f8082c395 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.4.0-dev" +__version__ = "2024.4.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From a102e982b380da03fe1bd566efa6d647def82c91 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:49:45 +1200 Subject: [PATCH 0053/1106] Bump version to 2024.5.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 992d148bdc..8410d36708 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.4.0-dev" +__version__ = "2024.5.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 1e0f6e139a67313f80715196c366236aae467106 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 10 Apr 2024 19:25:35 +1200 Subject: [PATCH 0054/1106] Add dooya remote transmitter test (#6508) --- tests/components/remote_transmitter/common-buttons.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml index 5f655acb7c..27683b387f 100644 --- a/tests/components/remote_transmitter/common-buttons.yaml +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -176,3 +176,11 @@ button: 0x00, 0x05, ] + - platform: template + name: Dooya + on_press: + remote_transmitter.transmit_dooya: + id: 0x123456 + channel: 1 + button: 1 + check: 1 From e59b81612fb5d6b6d7611269b6834afbbeeabbb4 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 10 Apr 2024 03:57:22 -0500 Subject: [PATCH 0055/1106] Add some components to the new testing framework (H) (#6179) * Add some components to the new testing framework (H) * Remove C3 * Fix indentation --------- Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../havells_solar/test.esp32-c3-idf.yaml | 79 +++++++++++++++++++ .../havells_solar/test.esp32-c3.yaml | 79 +++++++++++++++++++ .../havells_solar/test.esp32-idf.yaml | 79 +++++++++++++++++++ .../components/havells_solar/test.esp32.yaml | 79 +++++++++++++++++++ .../havells_solar/test.esp8266.yaml | 79 +++++++++++++++++++ .../components/havells_solar/test.rp2040.yaml | 79 +++++++++++++++++++ .../components/hbridge/test.esp32-c3-idf.yaml | 33 ++++++++ tests/components/hbridge/test.esp32-c3.yaml | 33 ++++++++ tests/components/hbridge/test.esp32-idf.yaml | 33 ++++++++ tests/components/hbridge/test.esp32.yaml | 33 ++++++++ tests/components/hbridge/test.esp8266.yaml | 33 ++++++++ tests/components/hbridge/test.rp2040.yaml | 33 ++++++++ .../components/hdc1080/test.esp32-c3-idf.yaml | 12 +++ tests/components/hdc1080/test.esp32-c3.yaml | 12 +++ tests/components/hdc1080/test.esp32-idf.yaml | 12 +++ tests/components/hdc1080/test.esp32.yaml | 12 +++ tests/components/hdc1080/test.esp8266.yaml | 12 +++ tests/components/hdc1080/test.rp2040.yaml | 12 +++ tests/components/he60r/test.esp32-c3-idf.yaml | 13 +++ tests/components/he60r/test.esp32-c3.yaml | 13 +++ tests/components/he60r/test.esp32-idf.yaml | 13 +++ tests/components/he60r/test.esp32.yaml | 13 +++ tests/components/he60r/test.esp8266.yaml | 13 +++ tests/components/he60r/test.rp2040.yaml | 13 +++ tests/components/heatpumpir/test.esp32.yaml | 19 +++++ tests/components/heatpumpir/test.esp8266.yaml | 19 +++++ .../hitachi_ac344/test.esp32-c3-idf.yaml | 7 ++ .../hitachi_ac344/test.esp32-c3.yaml | 7 ++ .../hitachi_ac344/test.esp32-idf.yaml | 7 ++ .../components/hitachi_ac344/test.esp32.yaml | 7 ++ .../hitachi_ac344/test.esp8266.yaml | 7 ++ .../hitachi_ac424/test.esp32-c3-idf.yaml | 7 ++ .../hitachi_ac424/test.esp32-c3.yaml | 7 ++ .../hitachi_ac424/test.esp32-idf.yaml | 7 ++ .../components/hitachi_ac424/test.esp32.yaml | 7 ++ .../hitachi_ac424/test.esp8266.yaml | 7 ++ .../components/hlw8012/test.esp32-c3-idf.yaml | 21 +++++ tests/components/hlw8012/test.esp32-c3.yaml | 21 +++++ tests/components/hlw8012/test.esp32-idf.yaml | 21 +++++ tests/components/hlw8012/test.esp32.yaml | 21 +++++ tests/components/hlw8012/test.esp8266.yaml | 21 +++++ tests/components/hlw8012/test.rp2040.yaml | 21 +++++ .../components/hm3301/test.esp32-c3-idf.yaml | 16 ++++ tests/components/hm3301/test.esp32-c3.yaml | 16 ++++ tests/components/hm3301/test.esp32-idf.yaml | 16 ++++ tests/components/hm3301/test.esp32.yaml | 16 ++++ tests/components/hm3301/test.esp8266.yaml | 16 ++++ tests/components/hm3301/test.rp2040.yaml | 16 ++++ .../hmc5883l/test.esp32-c3-idf.yaml | 19 +++++ tests/components/hmc5883l/test.esp32-c3.yaml | 19 +++++ tests/components/hmc5883l/test.esp32-idf.yaml | 19 +++++ tests/components/hmc5883l/test.esp32.yaml | 19 +++++ tests/components/hmc5883l/test.esp8266.yaml | 19 +++++ tests/components/hmc5883l/test.rp2040.yaml | 19 +++++ .../homeassistant/test.esp32-c3-idf.yaml | 39 +++++++++ .../homeassistant/test.esp32-c3.yaml | 39 +++++++++ .../homeassistant/test.esp32-idf.yaml | 39 +++++++++ .../components/homeassistant/test.esp32.yaml | 39 +++++++++ .../homeassistant/test.esp8266.yaml | 39 +++++++++ .../components/homeassistant/test.rp2040.yaml | 39 +++++++++ .../honeywell_hih_i2c/test.esp32-c3-idf.yaml | 12 +++ .../honeywell_hih_i2c/test.esp32-c3.yaml | 12 +++ .../honeywell_hih_i2c/test.esp32-idf.yaml | 12 +++ .../honeywell_hih_i2c/test.esp32.yaml | 12 +++ .../honeywell_hih_i2c/test.esp8266.yaml | 12 +++ .../honeywell_hih_i2c/test.rp2040.yaml | 12 +++ .../honeywellabp/test.esp32-c3-idf.yaml | 15 ++++ .../honeywellabp/test.esp32-c3.yaml | 15 ++++ .../honeywellabp/test.esp32-idf.yaml | 15 ++++ tests/components/honeywellabp/test.esp32.yaml | 15 ++++ .../components/honeywellabp/test.esp8266.yaml | 15 ++++ .../components/honeywellabp/test.rp2040.yaml | 15 ++++ .../honeywellabp2_i2c/test.esp32-c3-idf.yaml | 15 ++++ .../honeywellabp2_i2c/test.esp32-c3.yaml | 15 ++++ .../honeywellabp2_i2c/test.esp32-idf.yaml | 15 ++++ .../honeywellabp2_i2c/test.esp32.yaml | 15 ++++ .../honeywellabp2_i2c/test.esp8266.yaml | 15 ++++ .../honeywellabp2_i2c/test.rp2040.yaml | 15 ++++ .../hrxl_maxsonar_wr/test.esp32-c3-idf.yaml | 10 +++ .../hrxl_maxsonar_wr/test.esp32-c3.yaml | 10 +++ .../hrxl_maxsonar_wr/test.esp32-idf.yaml | 10 +++ .../hrxl_maxsonar_wr/test.esp32.yaml | 10 +++ .../hrxl_maxsonar_wr/test.esp8266.yaml | 10 +++ .../hrxl_maxsonar_wr/test.rp2040.yaml | 10 +++ .../components/hte501/test.esp32-c3-idf.yaml | 12 +++ tests/components/hte501/test.esp32-c3.yaml | 12 +++ tests/components/hte501/test.esp32-idf.yaml | 12 +++ tests/components/hte501/test.esp32.yaml | 12 +++ tests/components/hte501/test.esp8266.yaml | 12 +++ tests/components/hte501/test.rp2040.yaml | 12 +++ .../http_request/test.esp32-c3.yaml | 7 ++ tests/components/http_request/test.esp32.yaml | 7 ++ .../components/http_request/test.esp8266.yaml | 7 ++ .../components/htu21d/test.esp32-c3-idf.yaml | 14 ++++ tests/components/htu21d/test.esp32-c3.yaml | 14 ++++ tests/components/htu21d/test.esp32-idf.yaml | 14 ++++ tests/components/htu21d/test.esp32.yaml | 14 ++++ tests/components/htu21d/test.esp8266.yaml | 14 ++++ tests/components/htu21d/test.rp2040.yaml | 14 ++++ tests/components/hx711/test.esp32-c3-idf.yaml | 7 ++ tests/components/hx711/test.esp32-c3.yaml | 7 ++ tests/components/hx711/test.esp32-idf.yaml | 7 ++ tests/components/hx711/test.esp32.yaml | 7 ++ tests/components/hx711/test.esp8266.yaml | 7 ++ tests/components/hx711/test.rp2040.yaml | 7 ++ .../hydreon_rgxx/test.esp32-c3-idf.yaml | 37 +++++++++ .../hydreon_rgxx/test.esp32-c3.yaml | 37 +++++++++ .../hydreon_rgxx/test.esp32-idf.yaml | 37 +++++++++ tests/components/hydreon_rgxx/test.esp32.yaml | 37 +++++++++ .../components/hydreon_rgxx/test.esp8266.yaml | 37 +++++++++ .../components/hydreon_rgxx/test.rp2040.yaml | 37 +++++++++ .../components/hyt271/test.esp32-c3-idf.yaml | 11 +++ tests/components/hyt271/test.esp32-c3.yaml | 11 +++ tests/components/hyt271/test.esp32-idf.yaml | 11 +++ tests/components/hyt271/test.esp32.yaml | 11 +++ tests/components/hyt271/test.esp8266.yaml | 11 +++ tests/components/hyt271/test.rp2040.yaml | 11 +++ 117 files changed, 2319 insertions(+) create mode 100644 tests/components/havells_solar/test.esp32-c3-idf.yaml create mode 100644 tests/components/havells_solar/test.esp32-c3.yaml create mode 100644 tests/components/havells_solar/test.esp32-idf.yaml create mode 100644 tests/components/havells_solar/test.esp32.yaml create mode 100644 tests/components/havells_solar/test.esp8266.yaml create mode 100644 tests/components/havells_solar/test.rp2040.yaml create mode 100644 tests/components/hbridge/test.esp32-c3-idf.yaml create mode 100644 tests/components/hbridge/test.esp32-c3.yaml create mode 100644 tests/components/hbridge/test.esp32-idf.yaml create mode 100644 tests/components/hbridge/test.esp32.yaml create mode 100644 tests/components/hbridge/test.esp8266.yaml create mode 100644 tests/components/hbridge/test.rp2040.yaml create mode 100644 tests/components/hdc1080/test.esp32-c3-idf.yaml create mode 100644 tests/components/hdc1080/test.esp32-c3.yaml create mode 100644 tests/components/hdc1080/test.esp32-idf.yaml create mode 100644 tests/components/hdc1080/test.esp32.yaml create mode 100644 tests/components/hdc1080/test.esp8266.yaml create mode 100644 tests/components/hdc1080/test.rp2040.yaml create mode 100644 tests/components/he60r/test.esp32-c3-idf.yaml create mode 100644 tests/components/he60r/test.esp32-c3.yaml create mode 100644 tests/components/he60r/test.esp32-idf.yaml create mode 100644 tests/components/he60r/test.esp32.yaml create mode 100644 tests/components/he60r/test.esp8266.yaml create mode 100644 tests/components/he60r/test.rp2040.yaml create mode 100644 tests/components/heatpumpir/test.esp32.yaml create mode 100644 tests/components/heatpumpir/test.esp8266.yaml create mode 100644 tests/components/hitachi_ac344/test.esp32-c3-idf.yaml create mode 100644 tests/components/hitachi_ac344/test.esp32-c3.yaml create mode 100644 tests/components/hitachi_ac344/test.esp32-idf.yaml create mode 100644 tests/components/hitachi_ac344/test.esp32.yaml create mode 100644 tests/components/hitachi_ac344/test.esp8266.yaml create mode 100644 tests/components/hitachi_ac424/test.esp32-c3-idf.yaml create mode 100644 tests/components/hitachi_ac424/test.esp32-c3.yaml create mode 100644 tests/components/hitachi_ac424/test.esp32-idf.yaml create mode 100644 tests/components/hitachi_ac424/test.esp32.yaml create mode 100644 tests/components/hitachi_ac424/test.esp8266.yaml create mode 100644 tests/components/hlw8012/test.esp32-c3-idf.yaml create mode 100644 tests/components/hlw8012/test.esp32-c3.yaml create mode 100644 tests/components/hlw8012/test.esp32-idf.yaml create mode 100644 tests/components/hlw8012/test.esp32.yaml create mode 100644 tests/components/hlw8012/test.esp8266.yaml create mode 100644 tests/components/hlw8012/test.rp2040.yaml create mode 100644 tests/components/hm3301/test.esp32-c3-idf.yaml create mode 100644 tests/components/hm3301/test.esp32-c3.yaml create mode 100644 tests/components/hm3301/test.esp32-idf.yaml create mode 100644 tests/components/hm3301/test.esp32.yaml create mode 100644 tests/components/hm3301/test.esp8266.yaml create mode 100644 tests/components/hm3301/test.rp2040.yaml create mode 100644 tests/components/hmc5883l/test.esp32-c3-idf.yaml create mode 100644 tests/components/hmc5883l/test.esp32-c3.yaml create mode 100644 tests/components/hmc5883l/test.esp32-idf.yaml create mode 100644 tests/components/hmc5883l/test.esp32.yaml create mode 100644 tests/components/hmc5883l/test.esp8266.yaml create mode 100644 tests/components/hmc5883l/test.rp2040.yaml create mode 100644 tests/components/homeassistant/test.esp32-c3-idf.yaml create mode 100644 tests/components/homeassistant/test.esp32-c3.yaml create mode 100644 tests/components/homeassistant/test.esp32-idf.yaml create mode 100644 tests/components/homeassistant/test.esp32.yaml create mode 100644 tests/components/homeassistant/test.esp8266.yaml create mode 100644 tests/components/homeassistant/test.rp2040.yaml create mode 100644 tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml create mode 100644 tests/components/honeywell_hih_i2c/test.esp32-c3.yaml create mode 100644 tests/components/honeywell_hih_i2c/test.esp32-idf.yaml create mode 100644 tests/components/honeywell_hih_i2c/test.esp32.yaml create mode 100644 tests/components/honeywell_hih_i2c/test.esp8266.yaml create mode 100644 tests/components/honeywell_hih_i2c/test.rp2040.yaml create mode 100644 tests/components/honeywellabp/test.esp32-c3-idf.yaml create mode 100644 tests/components/honeywellabp/test.esp32-c3.yaml create mode 100644 tests/components/honeywellabp/test.esp32-idf.yaml create mode 100644 tests/components/honeywellabp/test.esp32.yaml create mode 100644 tests/components/honeywellabp/test.esp8266.yaml create mode 100644 tests/components/honeywellabp/test.rp2040.yaml create mode 100644 tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml create mode 100644 tests/components/honeywellabp2_i2c/test.esp32-c3.yaml create mode 100644 tests/components/honeywellabp2_i2c/test.esp32-idf.yaml create mode 100644 tests/components/honeywellabp2_i2c/test.esp32.yaml create mode 100644 tests/components/honeywellabp2_i2c/test.esp8266.yaml create mode 100644 tests/components/honeywellabp2_i2c/test.rp2040.yaml create mode 100644 tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml create mode 100644 tests/components/hrxl_maxsonar_wr/test.esp32-c3.yaml create mode 100644 tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml create mode 100644 tests/components/hrxl_maxsonar_wr/test.esp32.yaml create mode 100644 tests/components/hrxl_maxsonar_wr/test.esp8266.yaml create mode 100644 tests/components/hrxl_maxsonar_wr/test.rp2040.yaml create mode 100644 tests/components/hte501/test.esp32-c3-idf.yaml create mode 100644 tests/components/hte501/test.esp32-c3.yaml create mode 100644 tests/components/hte501/test.esp32-idf.yaml create mode 100644 tests/components/hte501/test.esp32.yaml create mode 100644 tests/components/hte501/test.esp8266.yaml create mode 100644 tests/components/hte501/test.rp2040.yaml create mode 100644 tests/components/http_request/test.esp32-c3.yaml create mode 100644 tests/components/http_request/test.esp32.yaml create mode 100644 tests/components/http_request/test.esp8266.yaml create mode 100644 tests/components/htu21d/test.esp32-c3-idf.yaml create mode 100644 tests/components/htu21d/test.esp32-c3.yaml create mode 100644 tests/components/htu21d/test.esp32-idf.yaml create mode 100644 tests/components/htu21d/test.esp32.yaml create mode 100644 tests/components/htu21d/test.esp8266.yaml create mode 100644 tests/components/htu21d/test.rp2040.yaml create mode 100644 tests/components/hx711/test.esp32-c3-idf.yaml create mode 100644 tests/components/hx711/test.esp32-c3.yaml create mode 100644 tests/components/hx711/test.esp32-idf.yaml create mode 100644 tests/components/hx711/test.esp32.yaml create mode 100644 tests/components/hx711/test.esp8266.yaml create mode 100644 tests/components/hx711/test.rp2040.yaml create mode 100644 tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml create mode 100644 tests/components/hydreon_rgxx/test.esp32-c3.yaml create mode 100644 tests/components/hydreon_rgxx/test.esp32-idf.yaml create mode 100644 tests/components/hydreon_rgxx/test.esp32.yaml create mode 100644 tests/components/hydreon_rgxx/test.esp8266.yaml create mode 100644 tests/components/hydreon_rgxx/test.rp2040.yaml create mode 100644 tests/components/hyt271/test.esp32-c3-idf.yaml create mode 100644 tests/components/hyt271/test.esp32-c3.yaml create mode 100644 tests/components/hyt271/test.esp32-idf.yaml create mode 100644 tests/components/hyt271/test.esp32.yaml create mode 100644 tests/components/hyt271/test.esp8266.yaml create mode 100644 tests/components/hyt271/test.rp2040.yaml diff --git a/tests/components/havells_solar/test.esp32-c3-idf.yaml b/tests/components/havells_solar/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..5cb911cf71 --- /dev/null +++ b/tests/components/havells_solar/test.esp32-c3-idf.yaml @@ -0,0 +1,79 @@ +uart: + - id: uart_havells_solar + tx_pin: 4 + rx_pin: 5 + baud_rate: 9600 + +modbus: + flow_control_pin: 3 + +sensor: + - platform: havells_solar + update_interval: 60s + phase_a: + voltage: + name: Phase A Voltage + current: + name: Phase A Current + phase_b: + voltage: + name: Voltage Phase B + current: + name: Current Phase B + phase_c: + voltage: + name: Voltage Phase C + current: + name: Current Phase C + pv1: + voltage: + name: PV1 Voltage + current: + name: PV1 Current + active_power: + name: PV1 Active Power + voltage_sampled_by_secondary_cpu: + name: PV1 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV1 Insulation Of +VE To Ground + pv2: + voltage: + name: PV2 Voltage + current: + name: PV2 Current + active_power: + name: PV2 Active Power + voltage_sampled_by_secondary_cpu: + name: PV2 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV2 Insulation Of +VE To Ground + active_power: + name: Active Power + reactive_power: + name: Reactive Power + frequency: + name: Frequency + energy_production_day: + name: Today's Generation + total_energy_production: + name: Total Energy Production + total_generation_time: + name: Total Generation Time + today_generation_time: + name: Today Generation Time + inverter_module_temp: + name: Inverter Module Temp + inverter_inner_temp: + name: Inverter Inner Temp + inverter_bus_voltage: + name: Inverter BUS Voltage + insulation_of_pv_n_to_ground: + name: Insulation Of PV- To Ground + gfci_value: + name: GFCI Value + dci_of_r: + name: DCI Of R + dci_of_s: + name: DCI Of S + dci_of_t: + name: DCI Of T diff --git a/tests/components/havells_solar/test.esp32-c3.yaml b/tests/components/havells_solar/test.esp32-c3.yaml new file mode 100644 index 0000000000..5cb911cf71 --- /dev/null +++ b/tests/components/havells_solar/test.esp32-c3.yaml @@ -0,0 +1,79 @@ +uart: + - id: uart_havells_solar + tx_pin: 4 + rx_pin: 5 + baud_rate: 9600 + +modbus: + flow_control_pin: 3 + +sensor: + - platform: havells_solar + update_interval: 60s + phase_a: + voltage: + name: Phase A Voltage + current: + name: Phase A Current + phase_b: + voltage: + name: Voltage Phase B + current: + name: Current Phase B + phase_c: + voltage: + name: Voltage Phase C + current: + name: Current Phase C + pv1: + voltage: + name: PV1 Voltage + current: + name: PV1 Current + active_power: + name: PV1 Active Power + voltage_sampled_by_secondary_cpu: + name: PV1 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV1 Insulation Of +VE To Ground + pv2: + voltage: + name: PV2 Voltage + current: + name: PV2 Current + active_power: + name: PV2 Active Power + voltage_sampled_by_secondary_cpu: + name: PV2 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV2 Insulation Of +VE To Ground + active_power: + name: Active Power + reactive_power: + name: Reactive Power + frequency: + name: Frequency + energy_production_day: + name: Today's Generation + total_energy_production: + name: Total Energy Production + total_generation_time: + name: Total Generation Time + today_generation_time: + name: Today Generation Time + inverter_module_temp: + name: Inverter Module Temp + inverter_inner_temp: + name: Inverter Inner Temp + inverter_bus_voltage: + name: Inverter BUS Voltage + insulation_of_pv_n_to_ground: + name: Insulation Of PV- To Ground + gfci_value: + name: GFCI Value + dci_of_r: + name: DCI Of R + dci_of_s: + name: DCI Of S + dci_of_t: + name: DCI Of T diff --git a/tests/components/havells_solar/test.esp32-idf.yaml b/tests/components/havells_solar/test.esp32-idf.yaml new file mode 100644 index 0000000000..2cda8e37be --- /dev/null +++ b/tests/components/havells_solar/test.esp32-idf.yaml @@ -0,0 +1,79 @@ +uart: + - id: uart_havells_solar + tx_pin: 17 + rx_pin: 16 + baud_rate: 9600 + +modbus: + flow_control_pin: 3 + +sensor: + - platform: havells_solar + update_interval: 60s + phase_a: + voltage: + name: Phase A Voltage + current: + name: Phase A Current + phase_b: + voltage: + name: Voltage Phase B + current: + name: Current Phase B + phase_c: + voltage: + name: Voltage Phase C + current: + name: Current Phase C + pv1: + voltage: + name: PV1 Voltage + current: + name: PV1 Current + active_power: + name: PV1 Active Power + voltage_sampled_by_secondary_cpu: + name: PV1 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV1 Insulation Of +VE To Ground + pv2: + voltage: + name: PV2 Voltage + current: + name: PV2 Current + active_power: + name: PV2 Active Power + voltage_sampled_by_secondary_cpu: + name: PV2 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV2 Insulation Of +VE To Ground + active_power: + name: Active Power + reactive_power: + name: Reactive Power + frequency: + name: Frequency + energy_production_day: + name: Today's Generation + total_energy_production: + name: Total Energy Production + total_generation_time: + name: Total Generation Time + today_generation_time: + name: Today Generation Time + inverter_module_temp: + name: Inverter Module Temp + inverter_inner_temp: + name: Inverter Inner Temp + inverter_bus_voltage: + name: Inverter BUS Voltage + insulation_of_pv_n_to_ground: + name: Insulation Of PV- To Ground + gfci_value: + name: GFCI Value + dci_of_r: + name: DCI Of R + dci_of_s: + name: DCI Of S + dci_of_t: + name: DCI Of T diff --git a/tests/components/havells_solar/test.esp32.yaml b/tests/components/havells_solar/test.esp32.yaml new file mode 100644 index 0000000000..2cda8e37be --- /dev/null +++ b/tests/components/havells_solar/test.esp32.yaml @@ -0,0 +1,79 @@ +uart: + - id: uart_havells_solar + tx_pin: 17 + rx_pin: 16 + baud_rate: 9600 + +modbus: + flow_control_pin: 3 + +sensor: + - platform: havells_solar + update_interval: 60s + phase_a: + voltage: + name: Phase A Voltage + current: + name: Phase A Current + phase_b: + voltage: + name: Voltage Phase B + current: + name: Current Phase B + phase_c: + voltage: + name: Voltage Phase C + current: + name: Current Phase C + pv1: + voltage: + name: PV1 Voltage + current: + name: PV1 Current + active_power: + name: PV1 Active Power + voltage_sampled_by_secondary_cpu: + name: PV1 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV1 Insulation Of +VE To Ground + pv2: + voltage: + name: PV2 Voltage + current: + name: PV2 Current + active_power: + name: PV2 Active Power + voltage_sampled_by_secondary_cpu: + name: PV2 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV2 Insulation Of +VE To Ground + active_power: + name: Active Power + reactive_power: + name: Reactive Power + frequency: + name: Frequency + energy_production_day: + name: Today's Generation + total_energy_production: + name: Total Energy Production + total_generation_time: + name: Total Generation Time + today_generation_time: + name: Today Generation Time + inverter_module_temp: + name: Inverter Module Temp + inverter_inner_temp: + name: Inverter Inner Temp + inverter_bus_voltage: + name: Inverter BUS Voltage + insulation_of_pv_n_to_ground: + name: Insulation Of PV- To Ground + gfci_value: + name: GFCI Value + dci_of_r: + name: DCI Of R + dci_of_s: + name: DCI Of S + dci_of_t: + name: DCI Of T diff --git a/tests/components/havells_solar/test.esp8266.yaml b/tests/components/havells_solar/test.esp8266.yaml new file mode 100644 index 0000000000..5cb911cf71 --- /dev/null +++ b/tests/components/havells_solar/test.esp8266.yaml @@ -0,0 +1,79 @@ +uart: + - id: uart_havells_solar + tx_pin: 4 + rx_pin: 5 + baud_rate: 9600 + +modbus: + flow_control_pin: 3 + +sensor: + - platform: havells_solar + update_interval: 60s + phase_a: + voltage: + name: Phase A Voltage + current: + name: Phase A Current + phase_b: + voltage: + name: Voltage Phase B + current: + name: Current Phase B + phase_c: + voltage: + name: Voltage Phase C + current: + name: Current Phase C + pv1: + voltage: + name: PV1 Voltage + current: + name: PV1 Current + active_power: + name: PV1 Active Power + voltage_sampled_by_secondary_cpu: + name: PV1 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV1 Insulation Of +VE To Ground + pv2: + voltage: + name: PV2 Voltage + current: + name: PV2 Current + active_power: + name: PV2 Active Power + voltage_sampled_by_secondary_cpu: + name: PV2 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV2 Insulation Of +VE To Ground + active_power: + name: Active Power + reactive_power: + name: Reactive Power + frequency: + name: Frequency + energy_production_day: + name: Today's Generation + total_energy_production: + name: Total Energy Production + total_generation_time: + name: Total Generation Time + today_generation_time: + name: Today Generation Time + inverter_module_temp: + name: Inverter Module Temp + inverter_inner_temp: + name: Inverter Inner Temp + inverter_bus_voltage: + name: Inverter BUS Voltage + insulation_of_pv_n_to_ground: + name: Insulation Of PV- To Ground + gfci_value: + name: GFCI Value + dci_of_r: + name: DCI Of R + dci_of_s: + name: DCI Of S + dci_of_t: + name: DCI Of T diff --git a/tests/components/havells_solar/test.rp2040.yaml b/tests/components/havells_solar/test.rp2040.yaml new file mode 100644 index 0000000000..5cb911cf71 --- /dev/null +++ b/tests/components/havells_solar/test.rp2040.yaml @@ -0,0 +1,79 @@ +uart: + - id: uart_havells_solar + tx_pin: 4 + rx_pin: 5 + baud_rate: 9600 + +modbus: + flow_control_pin: 3 + +sensor: + - platform: havells_solar + update_interval: 60s + phase_a: + voltage: + name: Phase A Voltage + current: + name: Phase A Current + phase_b: + voltage: + name: Voltage Phase B + current: + name: Current Phase B + phase_c: + voltage: + name: Voltage Phase C + current: + name: Current Phase C + pv1: + voltage: + name: PV1 Voltage + current: + name: PV1 Current + active_power: + name: PV1 Active Power + voltage_sampled_by_secondary_cpu: + name: PV1 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV1 Insulation Of +VE To Ground + pv2: + voltage: + name: PV2 Voltage + current: + name: PV2 Current + active_power: + name: PV2 Active Power + voltage_sampled_by_secondary_cpu: + name: PV2 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV2 Insulation Of +VE To Ground + active_power: + name: Active Power + reactive_power: + name: Reactive Power + frequency: + name: Frequency + energy_production_day: + name: Today's Generation + total_energy_production: + name: Total Energy Production + total_generation_time: + name: Total Generation Time + today_generation_time: + name: Today Generation Time + inverter_module_temp: + name: Inverter Module Temp + inverter_inner_temp: + name: Inverter Inner Temp + inverter_bus_voltage: + name: Inverter BUS Voltage + insulation_of_pv_n_to_ground: + name: Insulation Of PV- To Ground + gfci_value: + name: GFCI Value + dci_of_r: + name: DCI Of R + dci_of_s: + name: DCI Of S + dci_of_t: + name: DCI Of T diff --git a/tests/components/hbridge/test.esp32-c3-idf.yaml b/tests/components/hbridge/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..70cfd6ab6f --- /dev/null +++ b/tests/components/hbridge/test.esp32-c3-idf.yaml @@ -0,0 +1,33 @@ +output: + - platform: ledc + pin: 4 + id: gpio_output1 + - platform: ledc + pin: 5 + id: gpio_output2 + - platform: ledc + pin: 6 + id: gpio_output3 + - platform: ledc + pin: 7 + id: gpio_output4 + +light: + - platform: hbridge + name: Icicle Lights + pin_a: gpio_output3 + pin_b: gpio_output4 + +fan: + - platform: hbridge + id: fan_hbridge + speed_count: 4 + name: H-bridge Fan with Presets + pin_a: gpio_output1 + pin_b: gpio_output2 + preset_modes: + - Preset 1 + - Preset 2 + on_preset_set: + then: + - logger.log: Preset mode was changed! diff --git a/tests/components/hbridge/test.esp32-c3.yaml b/tests/components/hbridge/test.esp32-c3.yaml new file mode 100644 index 0000000000..70cfd6ab6f --- /dev/null +++ b/tests/components/hbridge/test.esp32-c3.yaml @@ -0,0 +1,33 @@ +output: + - platform: ledc + pin: 4 + id: gpio_output1 + - platform: ledc + pin: 5 + id: gpio_output2 + - platform: ledc + pin: 6 + id: gpio_output3 + - platform: ledc + pin: 7 + id: gpio_output4 + +light: + - platform: hbridge + name: Icicle Lights + pin_a: gpio_output3 + pin_b: gpio_output4 + +fan: + - platform: hbridge + id: fan_hbridge + speed_count: 4 + name: H-bridge Fan with Presets + pin_a: gpio_output1 + pin_b: gpio_output2 + preset_modes: + - Preset 1 + - Preset 2 + on_preset_set: + then: + - logger.log: Preset mode was changed! diff --git a/tests/components/hbridge/test.esp32-idf.yaml b/tests/components/hbridge/test.esp32-idf.yaml new file mode 100644 index 0000000000..6a80aaaf3b --- /dev/null +++ b/tests/components/hbridge/test.esp32-idf.yaml @@ -0,0 +1,33 @@ +output: + - platform: ledc + pin: 14 + id: gpio_output1 + - platform: ledc + pin: 15 + id: gpio_output2 + - platform: ledc + pin: 12 + id: gpio_output3 + - platform: ledc + pin: 13 + id: gpio_output4 + +light: + - platform: hbridge + name: Icicle Lights + pin_a: gpio_output3 + pin_b: gpio_output4 + +fan: + - platform: hbridge + id: fan_hbridge + speed_count: 4 + name: H-bridge Fan with Presets + pin_a: gpio_output1 + pin_b: gpio_output2 + preset_modes: + - Preset 1 + - Preset 2 + on_preset_set: + then: + - logger.log: Preset mode was changed! diff --git a/tests/components/hbridge/test.esp32.yaml b/tests/components/hbridge/test.esp32.yaml new file mode 100644 index 0000000000..6a80aaaf3b --- /dev/null +++ b/tests/components/hbridge/test.esp32.yaml @@ -0,0 +1,33 @@ +output: + - platform: ledc + pin: 14 + id: gpio_output1 + - platform: ledc + pin: 15 + id: gpio_output2 + - platform: ledc + pin: 12 + id: gpio_output3 + - platform: ledc + pin: 13 + id: gpio_output4 + +light: + - platform: hbridge + name: Icicle Lights + pin_a: gpio_output3 + pin_b: gpio_output4 + +fan: + - platform: hbridge + id: fan_hbridge + speed_count: 4 + name: H-bridge Fan with Presets + pin_a: gpio_output1 + pin_b: gpio_output2 + preset_modes: + - Preset 1 + - Preset 2 + on_preset_set: + then: + - logger.log: Preset mode was changed! diff --git a/tests/components/hbridge/test.esp8266.yaml b/tests/components/hbridge/test.esp8266.yaml new file mode 100644 index 0000000000..4f8915879d --- /dev/null +++ b/tests/components/hbridge/test.esp8266.yaml @@ -0,0 +1,33 @@ +output: + - platform: esp8266_pwm + pin: 4 + id: gpio_output1 + - platform: esp8266_pwm + pin: 5 + id: gpio_output2 + - platform: esp8266_pwm + pin: 12 + id: gpio_output3 + - platform: esp8266_pwm + pin: 13 + id: gpio_output4 + +light: + - platform: hbridge + name: Icicle Lights + pin_a: gpio_output3 + pin_b: gpio_output4 + +fan: + - platform: hbridge + id: fan_hbridge + speed_count: 4 + name: H-bridge Fan with Presets + pin_a: gpio_output1 + pin_b: gpio_output2 + preset_modes: + - Preset 1 + - Preset 2 + on_preset_set: + then: + - logger.log: Preset mode was changed! diff --git a/tests/components/hbridge/test.rp2040.yaml b/tests/components/hbridge/test.rp2040.yaml new file mode 100644 index 0000000000..e21b55091d --- /dev/null +++ b/tests/components/hbridge/test.rp2040.yaml @@ -0,0 +1,33 @@ +output: + - platform: rp2040_pwm + pin: 4 + id: gpio_output1 + - platform: rp2040_pwm + pin: 5 + id: gpio_output2 + - platform: rp2040_pwm + pin: 6 + id: gpio_output3 + - platform: rp2040_pwm + pin: 7 + id: gpio_output4 + +light: + - platform: hbridge + name: Icicle Lights + pin_a: gpio_output3 + pin_b: gpio_output4 + +fan: + - platform: hbridge + id: fan_hbridge + speed_count: 4 + name: H-bridge Fan with Presets + pin_a: gpio_output1 + pin_b: gpio_output2 + preset_modes: + - Preset 1 + - Preset 2 + on_preset_set: + then: + - logger.log: Preset mode was changed! diff --git a/tests/components/hdc1080/test.esp32-c3-idf.yaml b/tests/components/hdc1080/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..7bf7af6fa7 --- /dev/null +++ b/tests/components/hdc1080/test.esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hdc1080 + scl: 5 + sda: 4 + +sensor: + - platform: hdc1080 + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/hdc1080/test.esp32-c3.yaml b/tests/components/hdc1080/test.esp32-c3.yaml new file mode 100644 index 0000000000..7bf7af6fa7 --- /dev/null +++ b/tests/components/hdc1080/test.esp32-c3.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hdc1080 + scl: 5 + sda: 4 + +sensor: + - platform: hdc1080 + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/hdc1080/test.esp32-idf.yaml b/tests/components/hdc1080/test.esp32-idf.yaml new file mode 100644 index 0000000000..8e313dfa40 --- /dev/null +++ b/tests/components/hdc1080/test.esp32-idf.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hdc1080 + scl: 16 + sda: 17 + +sensor: + - platform: hdc1080 + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/hdc1080/test.esp32.yaml b/tests/components/hdc1080/test.esp32.yaml new file mode 100644 index 0000000000..8e313dfa40 --- /dev/null +++ b/tests/components/hdc1080/test.esp32.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hdc1080 + scl: 16 + sda: 17 + +sensor: + - platform: hdc1080 + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/hdc1080/test.esp8266.yaml b/tests/components/hdc1080/test.esp8266.yaml new file mode 100644 index 0000000000..7bf7af6fa7 --- /dev/null +++ b/tests/components/hdc1080/test.esp8266.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hdc1080 + scl: 5 + sda: 4 + +sensor: + - platform: hdc1080 + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/hdc1080/test.rp2040.yaml b/tests/components/hdc1080/test.rp2040.yaml new file mode 100644 index 0000000000..7bf7af6fa7 --- /dev/null +++ b/tests/components/hdc1080/test.rp2040.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hdc1080 + scl: 5 + sda: 4 + +sensor: + - platform: hdc1080 + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/he60r/test.esp32-c3-idf.yaml b/tests/components/he60r/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..bcbb544442 --- /dev/null +++ b/tests/components/he60r/test.esp32-c3-idf.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_he60r + tx_pin: 4 + rx_pin: 5 + baud_rate: 1200 + parity: EVEN + +cover: + - platform: he60r + id: garage_door + name: Garage Door + open_duration: 14s + close_duration: 14s diff --git a/tests/components/he60r/test.esp32-c3.yaml b/tests/components/he60r/test.esp32-c3.yaml new file mode 100644 index 0000000000..bcbb544442 --- /dev/null +++ b/tests/components/he60r/test.esp32-c3.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_he60r + tx_pin: 4 + rx_pin: 5 + baud_rate: 1200 + parity: EVEN + +cover: + - platform: he60r + id: garage_door + name: Garage Door + open_duration: 14s + close_duration: 14s diff --git a/tests/components/he60r/test.esp32-idf.yaml b/tests/components/he60r/test.esp32-idf.yaml new file mode 100644 index 0000000000..840387ae36 --- /dev/null +++ b/tests/components/he60r/test.esp32-idf.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_he60r + tx_pin: 17 + rx_pin: 16 + baud_rate: 1200 + parity: EVEN + +cover: + - platform: he60r + id: garage_door + name: Garage Door + open_duration: 14s + close_duration: 14s diff --git a/tests/components/he60r/test.esp32.yaml b/tests/components/he60r/test.esp32.yaml new file mode 100644 index 0000000000..840387ae36 --- /dev/null +++ b/tests/components/he60r/test.esp32.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_he60r + tx_pin: 17 + rx_pin: 16 + baud_rate: 1200 + parity: EVEN + +cover: + - platform: he60r + id: garage_door + name: Garage Door + open_duration: 14s + close_duration: 14s diff --git a/tests/components/he60r/test.esp8266.yaml b/tests/components/he60r/test.esp8266.yaml new file mode 100644 index 0000000000..bcbb544442 --- /dev/null +++ b/tests/components/he60r/test.esp8266.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_he60r + tx_pin: 4 + rx_pin: 5 + baud_rate: 1200 + parity: EVEN + +cover: + - platform: he60r + id: garage_door + name: Garage Door + open_duration: 14s + close_duration: 14s diff --git a/tests/components/he60r/test.rp2040.yaml b/tests/components/he60r/test.rp2040.yaml new file mode 100644 index 0000000000..bcbb544442 --- /dev/null +++ b/tests/components/he60r/test.rp2040.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_he60r + tx_pin: 4 + rx_pin: 5 + baud_rate: 1200 + parity: EVEN + +cover: + - platform: he60r + id: garage_door + name: Garage Door + open_duration: 14s + close_duration: 14s diff --git a/tests/components/heatpumpir/test.esp32.yaml b/tests/components/heatpumpir/test.esp32.yaml new file mode 100644 index 0000000000..db3f81f6a0 --- /dev/null +++ b/tests/components/heatpumpir/test.esp32.yaml @@ -0,0 +1,19 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: heatpumpir + protocol: mitsubishi_heavy_zm + horizontal_default: left + vertical_default: up + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 + - platform: heatpumpir + protocol: greeyt + horizontal_default: left + vertical_default: up + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 diff --git a/tests/components/heatpumpir/test.esp8266.yaml b/tests/components/heatpumpir/test.esp8266.yaml new file mode 100644 index 0000000000..26a01cb198 --- /dev/null +++ b/tests/components/heatpumpir/test.esp8266.yaml @@ -0,0 +1,19 @@ +remote_transmitter: + pin: 5 + carrier_duty_percent: 50% + +climate: + - platform: heatpumpir + protocol: mitsubishi_heavy_zm + horizontal_default: left + vertical_default: up + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 + - platform: heatpumpir + protocol: greeyt + horizontal_default: left + vertical_default: up + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 diff --git a/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml b/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..684d5899de --- /dev/null +++ b/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac344 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac344/test.esp32-c3.yaml b/tests/components/hitachi_ac344/test.esp32-c3.yaml new file mode 100644 index 0000000000..684d5899de --- /dev/null +++ b/tests/components/hitachi_ac344/test.esp32-c3.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac344 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac344/test.esp32-idf.yaml b/tests/components/hitachi_ac344/test.esp32-idf.yaml new file mode 100644 index 0000000000..684d5899de --- /dev/null +++ b/tests/components/hitachi_ac344/test.esp32-idf.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac344 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac344/test.esp32.yaml b/tests/components/hitachi_ac344/test.esp32.yaml new file mode 100644 index 0000000000..684d5899de --- /dev/null +++ b/tests/components/hitachi_ac344/test.esp32.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac344 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac344/test.esp8266.yaml b/tests/components/hitachi_ac344/test.esp8266.yaml new file mode 100644 index 0000000000..e6203e3084 --- /dev/null +++ b/tests/components/hitachi_ac344/test.esp8266.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 5 + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac344 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml b/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..a09821b9c6 --- /dev/null +++ b/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac424 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac424/test.esp32-c3.yaml b/tests/components/hitachi_ac424/test.esp32-c3.yaml new file mode 100644 index 0000000000..a09821b9c6 --- /dev/null +++ b/tests/components/hitachi_ac424/test.esp32-c3.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac424 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac424/test.esp32-idf.yaml b/tests/components/hitachi_ac424/test.esp32-idf.yaml new file mode 100644 index 0000000000..a09821b9c6 --- /dev/null +++ b/tests/components/hitachi_ac424/test.esp32-idf.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac424 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac424/test.esp32.yaml b/tests/components/hitachi_ac424/test.esp32.yaml new file mode 100644 index 0000000000..a09821b9c6 --- /dev/null +++ b/tests/components/hitachi_ac424/test.esp32.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac424 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac424/test.esp8266.yaml b/tests/components/hitachi_ac424/test.esp8266.yaml new file mode 100644 index 0000000000..78b9e7c98c --- /dev/null +++ b/tests/components/hitachi_ac424/test.esp8266.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 5 + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac424 + name: Hitachi Climate diff --git a/tests/components/hlw8012/test.esp32-c3-idf.yaml b/tests/components/hlw8012/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..da6053a1b9 --- /dev/null +++ b/tests/components/hlw8012/test.esp32-c3-idf.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: hlw8012 + model: hlw8012 + sel_pin: 2 + cf_pin: 4 + cf1_pin: 3 + current: + name: HLW8012 Current + voltage: + name: HLW8012 Voltage + power: + name: HLW8012 Power + id: hlw8012_power + energy: + name: HLW8012 Energy + id: hlw8012_energy + update_interval: 15s + current_resistor: 0.001 ohm + voltage_divider: 2351 + change_mode_every: "never" + initial_mode: VOLTAGE diff --git a/tests/components/hlw8012/test.esp32-c3.yaml b/tests/components/hlw8012/test.esp32-c3.yaml new file mode 100644 index 0000000000..da6053a1b9 --- /dev/null +++ b/tests/components/hlw8012/test.esp32-c3.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: hlw8012 + model: hlw8012 + sel_pin: 2 + cf_pin: 4 + cf1_pin: 3 + current: + name: HLW8012 Current + voltage: + name: HLW8012 Voltage + power: + name: HLW8012 Power + id: hlw8012_power + energy: + name: HLW8012 Energy + id: hlw8012_energy + update_interval: 15s + current_resistor: 0.001 ohm + voltage_divider: 2351 + change_mode_every: "never" + initial_mode: VOLTAGE diff --git a/tests/components/hlw8012/test.esp32-idf.yaml b/tests/components/hlw8012/test.esp32-idf.yaml new file mode 100644 index 0000000000..5b2d865722 --- /dev/null +++ b/tests/components/hlw8012/test.esp32-idf.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: hlw8012 + model: hlw8012 + sel_pin: 12 + cf_pin: 14 + cf1_pin: 13 + current: + name: HLW8012 Current + voltage: + name: HLW8012 Voltage + power: + name: HLW8012 Power + id: hlw8012_power + energy: + name: HLW8012 Energy + id: hlw8012_energy + update_interval: 15s + current_resistor: 0.001 ohm + voltage_divider: 2351 + change_mode_every: "never" + initial_mode: VOLTAGE diff --git a/tests/components/hlw8012/test.esp32.yaml b/tests/components/hlw8012/test.esp32.yaml new file mode 100644 index 0000000000..5b2d865722 --- /dev/null +++ b/tests/components/hlw8012/test.esp32.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: hlw8012 + model: hlw8012 + sel_pin: 12 + cf_pin: 14 + cf1_pin: 13 + current: + name: HLW8012 Current + voltage: + name: HLW8012 Voltage + power: + name: HLW8012 Power + id: hlw8012_power + energy: + name: HLW8012 Energy + id: hlw8012_energy + update_interval: 15s + current_resistor: 0.001 ohm + voltage_divider: 2351 + change_mode_every: "never" + initial_mode: VOLTAGE diff --git a/tests/components/hlw8012/test.esp8266.yaml b/tests/components/hlw8012/test.esp8266.yaml new file mode 100644 index 0000000000..5b2d865722 --- /dev/null +++ b/tests/components/hlw8012/test.esp8266.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: hlw8012 + model: hlw8012 + sel_pin: 12 + cf_pin: 14 + cf1_pin: 13 + current: + name: HLW8012 Current + voltage: + name: HLW8012 Voltage + power: + name: HLW8012 Power + id: hlw8012_power + energy: + name: HLW8012 Energy + id: hlw8012_energy + update_interval: 15s + current_resistor: 0.001 ohm + voltage_divider: 2351 + change_mode_every: "never" + initial_mode: VOLTAGE diff --git a/tests/components/hlw8012/test.rp2040.yaml b/tests/components/hlw8012/test.rp2040.yaml new file mode 100644 index 0000000000..da6053a1b9 --- /dev/null +++ b/tests/components/hlw8012/test.rp2040.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: hlw8012 + model: hlw8012 + sel_pin: 2 + cf_pin: 4 + cf1_pin: 3 + current: + name: HLW8012 Current + voltage: + name: HLW8012 Voltage + power: + name: HLW8012 Power + id: hlw8012_power + energy: + name: HLW8012 Energy + id: hlw8012_energy + update_interval: 15s + current_resistor: 0.001 ohm + voltage_divider: 2351 + change_mode_every: "never" + initial_mode: VOLTAGE diff --git a/tests/components/hm3301/test.esp32-c3-idf.yaml b/tests/components/hm3301/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..eb6c4a14e6 --- /dev/null +++ b/tests/components/hm3301/test.esp32-c3-idf.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_hm3301 + scl: 5 + sda: 4 + +sensor: + - platform: hm3301 + pm_1_0: + name: PM1.0 + pm_2_5: + name: PM2.5 + pm_10_0: + name: PM10.0 + aqi: + name: AQI + calculation_type: CAQI diff --git a/tests/components/hm3301/test.esp32-c3.yaml b/tests/components/hm3301/test.esp32-c3.yaml new file mode 100644 index 0000000000..eb6c4a14e6 --- /dev/null +++ b/tests/components/hm3301/test.esp32-c3.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_hm3301 + scl: 5 + sda: 4 + +sensor: + - platform: hm3301 + pm_1_0: + name: PM1.0 + pm_2_5: + name: PM2.5 + pm_10_0: + name: PM10.0 + aqi: + name: AQI + calculation_type: CAQI diff --git a/tests/components/hm3301/test.esp32-idf.yaml b/tests/components/hm3301/test.esp32-idf.yaml new file mode 100644 index 0000000000..413e88a265 --- /dev/null +++ b/tests/components/hm3301/test.esp32-idf.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_hm3301 + scl: 16 + sda: 17 + +sensor: + - platform: hm3301 + pm_1_0: + name: PM1.0 + pm_2_5: + name: PM2.5 + pm_10_0: + name: PM10.0 + aqi: + name: AQI + calculation_type: CAQI diff --git a/tests/components/hm3301/test.esp32.yaml b/tests/components/hm3301/test.esp32.yaml new file mode 100644 index 0000000000..413e88a265 --- /dev/null +++ b/tests/components/hm3301/test.esp32.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_hm3301 + scl: 16 + sda: 17 + +sensor: + - platform: hm3301 + pm_1_0: + name: PM1.0 + pm_2_5: + name: PM2.5 + pm_10_0: + name: PM10.0 + aqi: + name: AQI + calculation_type: CAQI diff --git a/tests/components/hm3301/test.esp8266.yaml b/tests/components/hm3301/test.esp8266.yaml new file mode 100644 index 0000000000..eb6c4a14e6 --- /dev/null +++ b/tests/components/hm3301/test.esp8266.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_hm3301 + scl: 5 + sda: 4 + +sensor: + - platform: hm3301 + pm_1_0: + name: PM1.0 + pm_2_5: + name: PM2.5 + pm_10_0: + name: PM10.0 + aqi: + name: AQI + calculation_type: CAQI diff --git a/tests/components/hm3301/test.rp2040.yaml b/tests/components/hm3301/test.rp2040.yaml new file mode 100644 index 0000000000..eb6c4a14e6 --- /dev/null +++ b/tests/components/hm3301/test.rp2040.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_hm3301 + scl: 5 + sda: 4 + +sensor: + - platform: hm3301 + pm_1_0: + name: PM1.0 + pm_2_5: + name: PM2.5 + pm_10_0: + name: PM10.0 + aqi: + name: AQI + calculation_type: CAQI diff --git a/tests/components/hmc5883l/test.esp32-c3-idf.yaml b/tests/components/hmc5883l/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..e65b2e5c5b --- /dev/null +++ b/tests/components/hmc5883l/test.esp32-c3-idf.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_hmc5883l + scl: 5 + sda: 4 + +sensor: + - platform: hmc5883l + address: 0x68 + field_strength_x: + name: HMC5883L Field Strength X + field_strength_y: + name: HMC5883L Field Strength Y + field_strength_z: + name: HMC5883L Field Strength Z + heading: + name: HMC5883L Heading + range: 130uT + oversampling: 8x + update_interval: 15s diff --git a/tests/components/hmc5883l/test.esp32-c3.yaml b/tests/components/hmc5883l/test.esp32-c3.yaml new file mode 100644 index 0000000000..e65b2e5c5b --- /dev/null +++ b/tests/components/hmc5883l/test.esp32-c3.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_hmc5883l + scl: 5 + sda: 4 + +sensor: + - platform: hmc5883l + address: 0x68 + field_strength_x: + name: HMC5883L Field Strength X + field_strength_y: + name: HMC5883L Field Strength Y + field_strength_z: + name: HMC5883L Field Strength Z + heading: + name: HMC5883L Heading + range: 130uT + oversampling: 8x + update_interval: 15s diff --git a/tests/components/hmc5883l/test.esp32-idf.yaml b/tests/components/hmc5883l/test.esp32-idf.yaml new file mode 100644 index 0000000000..db632fc411 --- /dev/null +++ b/tests/components/hmc5883l/test.esp32-idf.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_hmc5883l + scl: 16 + sda: 17 + +sensor: + - platform: hmc5883l + address: 0x68 + field_strength_x: + name: HMC5883L Field Strength X + field_strength_y: + name: HMC5883L Field Strength Y + field_strength_z: + name: HMC5883L Field Strength Z + heading: + name: HMC5883L Heading + range: 130uT + oversampling: 8x + update_interval: 15s diff --git a/tests/components/hmc5883l/test.esp32.yaml b/tests/components/hmc5883l/test.esp32.yaml new file mode 100644 index 0000000000..db632fc411 --- /dev/null +++ b/tests/components/hmc5883l/test.esp32.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_hmc5883l + scl: 16 + sda: 17 + +sensor: + - platform: hmc5883l + address: 0x68 + field_strength_x: + name: HMC5883L Field Strength X + field_strength_y: + name: HMC5883L Field Strength Y + field_strength_z: + name: HMC5883L Field Strength Z + heading: + name: HMC5883L Heading + range: 130uT + oversampling: 8x + update_interval: 15s diff --git a/tests/components/hmc5883l/test.esp8266.yaml b/tests/components/hmc5883l/test.esp8266.yaml new file mode 100644 index 0000000000..e65b2e5c5b --- /dev/null +++ b/tests/components/hmc5883l/test.esp8266.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_hmc5883l + scl: 5 + sda: 4 + +sensor: + - platform: hmc5883l + address: 0x68 + field_strength_x: + name: HMC5883L Field Strength X + field_strength_y: + name: HMC5883L Field Strength Y + field_strength_z: + name: HMC5883L Field Strength Z + heading: + name: HMC5883L Heading + range: 130uT + oversampling: 8x + update_interval: 15s diff --git a/tests/components/hmc5883l/test.rp2040.yaml b/tests/components/hmc5883l/test.rp2040.yaml new file mode 100644 index 0000000000..e65b2e5c5b --- /dev/null +++ b/tests/components/hmc5883l/test.rp2040.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_hmc5883l + scl: 5 + sda: 4 + +sensor: + - platform: hmc5883l + address: 0x68 + field_strength_x: + name: HMC5883L Field Strength X + field_strength_y: + name: HMC5883L Field Strength Y + field_strength_z: + name: HMC5883L Field Strength Z + heading: + name: HMC5883L Heading + range: 130uT + oversampling: 8x + update_interval: 15s diff --git a/tests/components/homeassistant/test.esp32-c3-idf.yaml b/tests/components/homeassistant/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..d2608f077c --- /dev/null +++ b/tests/components/homeassistant/test.esp32-c3-idf.yaml @@ -0,0 +1,39 @@ +wifi: + ssid: MySSID + password: password1 + +api: + +binary_sensor: + - platform: homeassistant + entity_id: binary_sensor.hello_world + id: ha_hello_world_binary + - platform: homeassistant + entity_id: binary_sensor.hello + attribute: world + id: ha_hello_world_binary_attribute + +sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world + - platform: homeassistant + entity_id: climate.living_room + attribute: temperature + id: ha_hello_world_temperature + +text_sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world_text + - platform: homeassistant + entity_id: sensor.hello_world1 + id: ha_hello_world_text2 + attribute: some_attribute + +time: + - platform: homeassistant + on_time: + - at: "16:00:00" + then: + - logger.log: It's 16:00 diff --git a/tests/components/homeassistant/test.esp32-c3.yaml b/tests/components/homeassistant/test.esp32-c3.yaml new file mode 100644 index 0000000000..d2608f077c --- /dev/null +++ b/tests/components/homeassistant/test.esp32-c3.yaml @@ -0,0 +1,39 @@ +wifi: + ssid: MySSID + password: password1 + +api: + +binary_sensor: + - platform: homeassistant + entity_id: binary_sensor.hello_world + id: ha_hello_world_binary + - platform: homeassistant + entity_id: binary_sensor.hello + attribute: world + id: ha_hello_world_binary_attribute + +sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world + - platform: homeassistant + entity_id: climate.living_room + attribute: temperature + id: ha_hello_world_temperature + +text_sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world_text + - platform: homeassistant + entity_id: sensor.hello_world1 + id: ha_hello_world_text2 + attribute: some_attribute + +time: + - platform: homeassistant + on_time: + - at: "16:00:00" + then: + - logger.log: It's 16:00 diff --git a/tests/components/homeassistant/test.esp32-idf.yaml b/tests/components/homeassistant/test.esp32-idf.yaml new file mode 100644 index 0000000000..d2608f077c --- /dev/null +++ b/tests/components/homeassistant/test.esp32-idf.yaml @@ -0,0 +1,39 @@ +wifi: + ssid: MySSID + password: password1 + +api: + +binary_sensor: + - platform: homeassistant + entity_id: binary_sensor.hello_world + id: ha_hello_world_binary + - platform: homeassistant + entity_id: binary_sensor.hello + attribute: world + id: ha_hello_world_binary_attribute + +sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world + - platform: homeassistant + entity_id: climate.living_room + attribute: temperature + id: ha_hello_world_temperature + +text_sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world_text + - platform: homeassistant + entity_id: sensor.hello_world1 + id: ha_hello_world_text2 + attribute: some_attribute + +time: + - platform: homeassistant + on_time: + - at: "16:00:00" + then: + - logger.log: It's 16:00 diff --git a/tests/components/homeassistant/test.esp32.yaml b/tests/components/homeassistant/test.esp32.yaml new file mode 100644 index 0000000000..d2608f077c --- /dev/null +++ b/tests/components/homeassistant/test.esp32.yaml @@ -0,0 +1,39 @@ +wifi: + ssid: MySSID + password: password1 + +api: + +binary_sensor: + - platform: homeassistant + entity_id: binary_sensor.hello_world + id: ha_hello_world_binary + - platform: homeassistant + entity_id: binary_sensor.hello + attribute: world + id: ha_hello_world_binary_attribute + +sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world + - platform: homeassistant + entity_id: climate.living_room + attribute: temperature + id: ha_hello_world_temperature + +text_sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world_text + - platform: homeassistant + entity_id: sensor.hello_world1 + id: ha_hello_world_text2 + attribute: some_attribute + +time: + - platform: homeassistant + on_time: + - at: "16:00:00" + then: + - logger.log: It's 16:00 diff --git a/tests/components/homeassistant/test.esp8266.yaml b/tests/components/homeassistant/test.esp8266.yaml new file mode 100644 index 0000000000..d2608f077c --- /dev/null +++ b/tests/components/homeassistant/test.esp8266.yaml @@ -0,0 +1,39 @@ +wifi: + ssid: MySSID + password: password1 + +api: + +binary_sensor: + - platform: homeassistant + entity_id: binary_sensor.hello_world + id: ha_hello_world_binary + - platform: homeassistant + entity_id: binary_sensor.hello + attribute: world + id: ha_hello_world_binary_attribute + +sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world + - platform: homeassistant + entity_id: climate.living_room + attribute: temperature + id: ha_hello_world_temperature + +text_sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world_text + - platform: homeassistant + entity_id: sensor.hello_world1 + id: ha_hello_world_text2 + attribute: some_attribute + +time: + - platform: homeassistant + on_time: + - at: "16:00:00" + then: + - logger.log: It's 16:00 diff --git a/tests/components/homeassistant/test.rp2040.yaml b/tests/components/homeassistant/test.rp2040.yaml new file mode 100644 index 0000000000..d2608f077c --- /dev/null +++ b/tests/components/homeassistant/test.rp2040.yaml @@ -0,0 +1,39 @@ +wifi: + ssid: MySSID + password: password1 + +api: + +binary_sensor: + - platform: homeassistant + entity_id: binary_sensor.hello_world + id: ha_hello_world_binary + - platform: homeassistant + entity_id: binary_sensor.hello + attribute: world + id: ha_hello_world_binary_attribute + +sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world + - platform: homeassistant + entity_id: climate.living_room + attribute: temperature + id: ha_hello_world_temperature + +text_sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world_text + - platform: homeassistant + entity_id: sensor.hello_world1 + id: ha_hello_world_text2 + attribute: some_attribute + +time: + - platform: homeassistant + on_time: + - at: "16:00:00" + then: + - logger.log: It's 16:00 diff --git a/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml b/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..5dae3d5d52 --- /dev/null +++ b/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_honeywell_hih + scl: 5 + sda: 4 + +sensor: + - platform: honeywell_hih_i2c + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/honeywell_hih_i2c/test.esp32-c3.yaml b/tests/components/honeywell_hih_i2c/test.esp32-c3.yaml new file mode 100644 index 0000000000..5dae3d5d52 --- /dev/null +++ b/tests/components/honeywell_hih_i2c/test.esp32-c3.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_honeywell_hih + scl: 5 + sda: 4 + +sensor: + - platform: honeywell_hih_i2c + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml b/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml new file mode 100644 index 0000000000..0119aec3f3 --- /dev/null +++ b/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_honeywell_hih + scl: 16 + sda: 17 + +sensor: + - platform: honeywell_hih_i2c + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/honeywell_hih_i2c/test.esp32.yaml b/tests/components/honeywell_hih_i2c/test.esp32.yaml new file mode 100644 index 0000000000..0119aec3f3 --- /dev/null +++ b/tests/components/honeywell_hih_i2c/test.esp32.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_honeywell_hih + scl: 16 + sda: 17 + +sensor: + - platform: honeywell_hih_i2c + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/honeywell_hih_i2c/test.esp8266.yaml b/tests/components/honeywell_hih_i2c/test.esp8266.yaml new file mode 100644 index 0000000000..5dae3d5d52 --- /dev/null +++ b/tests/components/honeywell_hih_i2c/test.esp8266.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_honeywell_hih + scl: 5 + sda: 4 + +sensor: + - platform: honeywell_hih_i2c + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/honeywell_hih_i2c/test.rp2040.yaml b/tests/components/honeywell_hih_i2c/test.rp2040.yaml new file mode 100644 index 0000000000..5dae3d5d52 --- /dev/null +++ b/tests/components/honeywell_hih_i2c/test.rp2040.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_honeywell_hih + scl: 5 + sda: 4 + +sensor: + - platform: honeywell_hih_i2c + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/honeywellabp/test.esp32-c3-idf.yaml b/tests/components/honeywellabp/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..a53e3296dd --- /dev/null +++ b/tests/components/honeywellabp/test.esp32-c3-idf.yaml @@ -0,0 +1,15 @@ +spi: + - id: spi_honeywellabp + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: honeywellabp + cs_pin: 8 + pressure: + name: Honeywell pressure + min_pressure: 0 + max_pressure: 15 + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp/test.esp32-c3.yaml b/tests/components/honeywellabp/test.esp32-c3.yaml new file mode 100644 index 0000000000..a53e3296dd --- /dev/null +++ b/tests/components/honeywellabp/test.esp32-c3.yaml @@ -0,0 +1,15 @@ +spi: + - id: spi_honeywellabp + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: honeywellabp + cs_pin: 8 + pressure: + name: Honeywell pressure + min_pressure: 0 + max_pressure: 15 + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp/test.esp32-idf.yaml b/tests/components/honeywellabp/test.esp32-idf.yaml new file mode 100644 index 0000000000..6bf9fa0f4d --- /dev/null +++ b/tests/components/honeywellabp/test.esp32-idf.yaml @@ -0,0 +1,15 @@ +spi: + - id: spi_bme280 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: honeywellabp + cs_pin: 12 + pressure: + name: Honeywell pressure + min_pressure: 0 + max_pressure: 15 + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp/test.esp32.yaml b/tests/components/honeywellabp/test.esp32.yaml new file mode 100644 index 0000000000..6bf9fa0f4d --- /dev/null +++ b/tests/components/honeywellabp/test.esp32.yaml @@ -0,0 +1,15 @@ +spi: + - id: spi_bme280 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: honeywellabp + cs_pin: 12 + pressure: + name: Honeywell pressure + min_pressure: 0 + max_pressure: 15 + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp/test.esp8266.yaml b/tests/components/honeywellabp/test.esp8266.yaml new file mode 100644 index 0000000000..31988c035e --- /dev/null +++ b/tests/components/honeywellabp/test.esp8266.yaml @@ -0,0 +1,15 @@ +spi: + - id: spi_bme280 + clk_pin: 14 + mosi_pin: 13 + miso_pin: 12 + +sensor: + - platform: honeywellabp + cs_pin: 15 + pressure: + name: Honeywell pressure + min_pressure: 0 + max_pressure: 15 + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp/test.rp2040.yaml b/tests/components/honeywellabp/test.rp2040.yaml new file mode 100644 index 0000000000..2e0c471fa0 --- /dev/null +++ b/tests/components/honeywellabp/test.rp2040.yaml @@ -0,0 +1,15 @@ +spi: + - id: spi_bme280 + clk_pin: 2 + mosi_pin: 3 + miso_pin: 4 + +sensor: + - platform: honeywellabp + cs_pin: 6 + pressure: + name: Honeywell pressure + min_pressure: 0 + max_pressure: 15 + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml b/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..b47411c238 --- /dev/null +++ b/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_honeywellabp2 + scl: 5 + sda: 4 + +sensor: + - platform: honeywellabp2_i2c + address: 0x28 + pressure: + name: Honeywell2 pressure + min_pressure: 0 + max_pressure: 16000 + transfer_function: A + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp2_i2c/test.esp32-c3.yaml b/tests/components/honeywellabp2_i2c/test.esp32-c3.yaml new file mode 100644 index 0000000000..b47411c238 --- /dev/null +++ b/tests/components/honeywellabp2_i2c/test.esp32-c3.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_honeywellabp2 + scl: 5 + sda: 4 + +sensor: + - platform: honeywellabp2_i2c + address: 0x28 + pressure: + name: Honeywell2 pressure + min_pressure: 0 + max_pressure: 16000 + transfer_function: A + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml b/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml new file mode 100644 index 0000000000..0f0d61ca06 --- /dev/null +++ b/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_honeywellabp2 + scl: 16 + sda: 17 + +sensor: + - platform: honeywellabp2_i2c + address: 0x28 + pressure: + name: Honeywell2 pressure + min_pressure: 0 + max_pressure: 16000 + transfer_function: A + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp2_i2c/test.esp32.yaml b/tests/components/honeywellabp2_i2c/test.esp32.yaml new file mode 100644 index 0000000000..0f0d61ca06 --- /dev/null +++ b/tests/components/honeywellabp2_i2c/test.esp32.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_honeywellabp2 + scl: 16 + sda: 17 + +sensor: + - platform: honeywellabp2_i2c + address: 0x28 + pressure: + name: Honeywell2 pressure + min_pressure: 0 + max_pressure: 16000 + transfer_function: A + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp2_i2c/test.esp8266.yaml b/tests/components/honeywellabp2_i2c/test.esp8266.yaml new file mode 100644 index 0000000000..b47411c238 --- /dev/null +++ b/tests/components/honeywellabp2_i2c/test.esp8266.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_honeywellabp2 + scl: 5 + sda: 4 + +sensor: + - platform: honeywellabp2_i2c + address: 0x28 + pressure: + name: Honeywell2 pressure + min_pressure: 0 + max_pressure: 16000 + transfer_function: A + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp2_i2c/test.rp2040.yaml b/tests/components/honeywellabp2_i2c/test.rp2040.yaml new file mode 100644 index 0000000000..b47411c238 --- /dev/null +++ b/tests/components/honeywellabp2_i2c/test.rp2040.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_honeywellabp2 + scl: 5 + sda: 4 + +sensor: + - platform: honeywellabp2_i2c + address: 0x28 + pressure: + name: Honeywell2 pressure + min_pressure: 0 + max_pressure: 16000 + transfer_function: A + temperature: + name: Honeywell temperature diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..729cb96120 --- /dev/null +++ b/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml @@ -0,0 +1,10 @@ +uart: + - id: uart_hrxl_maxsonar_wr + tx_pin: 4 + rx_pin: 5 + baud_rate: 115200 + +sensor: + - platform: hrxl_maxsonar_wr + id: hrxl_maxsonar_wr_sensor + name: Rainwater Tank Level diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-c3.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-c3.yaml new file mode 100644 index 0000000000..729cb96120 --- /dev/null +++ b/tests/components/hrxl_maxsonar_wr/test.esp32-c3.yaml @@ -0,0 +1,10 @@ +uart: + - id: uart_hrxl_maxsonar_wr + tx_pin: 4 + rx_pin: 5 + baud_rate: 115200 + +sensor: + - platform: hrxl_maxsonar_wr + id: hrxl_maxsonar_wr_sensor + name: Rainwater Tank Level diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml new file mode 100644 index 0000000000..da283cc072 --- /dev/null +++ b/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml @@ -0,0 +1,10 @@ +uart: + - id: uart_hrxl_maxsonar_wr + tx_pin: 17 + rx_pin: 16 + baud_rate: 115200 + +sensor: + - platform: hrxl_maxsonar_wr + id: hrxl_maxsonar_wr_sensor + name: Rainwater Tank Level diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32.yaml new file mode 100644 index 0000000000..da283cc072 --- /dev/null +++ b/tests/components/hrxl_maxsonar_wr/test.esp32.yaml @@ -0,0 +1,10 @@ +uart: + - id: uart_hrxl_maxsonar_wr + tx_pin: 17 + rx_pin: 16 + baud_rate: 115200 + +sensor: + - platform: hrxl_maxsonar_wr + id: hrxl_maxsonar_wr_sensor + name: Rainwater Tank Level diff --git a/tests/components/hrxl_maxsonar_wr/test.esp8266.yaml b/tests/components/hrxl_maxsonar_wr/test.esp8266.yaml new file mode 100644 index 0000000000..729cb96120 --- /dev/null +++ b/tests/components/hrxl_maxsonar_wr/test.esp8266.yaml @@ -0,0 +1,10 @@ +uart: + - id: uart_hrxl_maxsonar_wr + tx_pin: 4 + rx_pin: 5 + baud_rate: 115200 + +sensor: + - platform: hrxl_maxsonar_wr + id: hrxl_maxsonar_wr_sensor + name: Rainwater Tank Level diff --git a/tests/components/hrxl_maxsonar_wr/test.rp2040.yaml b/tests/components/hrxl_maxsonar_wr/test.rp2040.yaml new file mode 100644 index 0000000000..729cb96120 --- /dev/null +++ b/tests/components/hrxl_maxsonar_wr/test.rp2040.yaml @@ -0,0 +1,10 @@ +uart: + - id: uart_hrxl_maxsonar_wr + tx_pin: 4 + rx_pin: 5 + baud_rate: 115200 + +sensor: + - platform: hrxl_maxsonar_wr + id: hrxl_maxsonar_wr_sensor + name: Rainwater Tank Level diff --git a/tests/components/hte501/test.esp32-c3-idf.yaml b/tests/components/hte501/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..e14b589dbd --- /dev/null +++ b/tests/components/hte501/test.esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hte501 + scl: 5 + sda: 4 + +sensor: + - platform: hte501 + address: 0x40 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hte501/test.esp32-c3.yaml b/tests/components/hte501/test.esp32-c3.yaml new file mode 100644 index 0000000000..e14b589dbd --- /dev/null +++ b/tests/components/hte501/test.esp32-c3.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hte501 + scl: 5 + sda: 4 + +sensor: + - platform: hte501 + address: 0x40 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hte501/test.esp32-idf.yaml b/tests/components/hte501/test.esp32-idf.yaml new file mode 100644 index 0000000000..83e4d26603 --- /dev/null +++ b/tests/components/hte501/test.esp32-idf.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hte501 + scl: 16 + sda: 17 + +sensor: + - platform: hte501 + address: 0x40 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hte501/test.esp32.yaml b/tests/components/hte501/test.esp32.yaml new file mode 100644 index 0000000000..83e4d26603 --- /dev/null +++ b/tests/components/hte501/test.esp32.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hte501 + scl: 16 + sda: 17 + +sensor: + - platform: hte501 + address: 0x40 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hte501/test.esp8266.yaml b/tests/components/hte501/test.esp8266.yaml new file mode 100644 index 0000000000..e14b589dbd --- /dev/null +++ b/tests/components/hte501/test.esp8266.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hte501 + scl: 5 + sda: 4 + +sensor: + - platform: hte501 + address: 0x40 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hte501/test.rp2040.yaml b/tests/components/hte501/test.rp2040.yaml new file mode 100644 index 0000000000..e14b589dbd --- /dev/null +++ b/tests/components/hte501/test.rp2040.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hte501 + scl: 5 + sda: 4 + +sensor: + - platform: hte501 + address: 0x40 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/http_request/test.esp32-c3.yaml b/tests/components/http_request/test.esp32-c3.yaml new file mode 100644 index 0000000000..19fc6af2c4 --- /dev/null +++ b/tests/components/http_request/test.esp32-c3.yaml @@ -0,0 +1,7 @@ +wifi: + ssid: MySSID + password: password1 + +http_request: + useragent: esphome/tagreader + timeout: 10s diff --git a/tests/components/http_request/test.esp32.yaml b/tests/components/http_request/test.esp32.yaml new file mode 100644 index 0000000000..19fc6af2c4 --- /dev/null +++ b/tests/components/http_request/test.esp32.yaml @@ -0,0 +1,7 @@ +wifi: + ssid: MySSID + password: password1 + +http_request: + useragent: esphome/tagreader + timeout: 10s diff --git a/tests/components/http_request/test.esp8266.yaml b/tests/components/http_request/test.esp8266.yaml new file mode 100644 index 0000000000..19fc6af2c4 --- /dev/null +++ b/tests/components/http_request/test.esp8266.yaml @@ -0,0 +1,7 @@ +wifi: + ssid: MySSID + password: password1 + +http_request: + useragent: esphome/tagreader + timeout: 10s diff --git a/tests/components/htu21d/test.esp32-c3-idf.yaml b/tests/components/htu21d/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..d9fbb09550 --- /dev/null +++ b/tests/components/htu21d/test.esp32-c3-idf.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_htu21d + scl: 5 + sda: 4 + +sensor: + - platform: htu21d + temperature: + name: Temperature + humidity: + name: Humidity + heater: + name: Heater + update_interval: 15s diff --git a/tests/components/htu21d/test.esp32-c3.yaml b/tests/components/htu21d/test.esp32-c3.yaml new file mode 100644 index 0000000000..d9fbb09550 --- /dev/null +++ b/tests/components/htu21d/test.esp32-c3.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_htu21d + scl: 5 + sda: 4 + +sensor: + - platform: htu21d + temperature: + name: Temperature + humidity: + name: Humidity + heater: + name: Heater + update_interval: 15s diff --git a/tests/components/htu21d/test.esp32-idf.yaml b/tests/components/htu21d/test.esp32-idf.yaml new file mode 100644 index 0000000000..48f03eb368 --- /dev/null +++ b/tests/components/htu21d/test.esp32-idf.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_htu21d + scl: 16 + sda: 17 + +sensor: + - platform: htu21d + temperature: + name: Temperature + humidity: + name: Humidity + heater: + name: Heater + update_interval: 15s diff --git a/tests/components/htu21d/test.esp32.yaml b/tests/components/htu21d/test.esp32.yaml new file mode 100644 index 0000000000..48f03eb368 --- /dev/null +++ b/tests/components/htu21d/test.esp32.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_htu21d + scl: 16 + sda: 17 + +sensor: + - platform: htu21d + temperature: + name: Temperature + humidity: + name: Humidity + heater: + name: Heater + update_interval: 15s diff --git a/tests/components/htu21d/test.esp8266.yaml b/tests/components/htu21d/test.esp8266.yaml new file mode 100644 index 0000000000..d9fbb09550 --- /dev/null +++ b/tests/components/htu21d/test.esp8266.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_htu21d + scl: 5 + sda: 4 + +sensor: + - platform: htu21d + temperature: + name: Temperature + humidity: + name: Humidity + heater: + name: Heater + update_interval: 15s diff --git a/tests/components/htu21d/test.rp2040.yaml b/tests/components/htu21d/test.rp2040.yaml new file mode 100644 index 0000000000..d9fbb09550 --- /dev/null +++ b/tests/components/htu21d/test.rp2040.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_htu21d + scl: 5 + sda: 4 + +sensor: + - platform: htu21d + temperature: + name: Temperature + humidity: + name: Humidity + heater: + name: Heater + update_interval: 15s diff --git a/tests/components/hx711/test.esp32-c3-idf.yaml b/tests/components/hx711/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..9850417440 --- /dev/null +++ b/tests/components/hx711/test.esp32-c3-idf.yaml @@ -0,0 +1,7 @@ +sensor: + - platform: hx711 + name: HX711 Value + dout_pin: 4 + clk_pin: 5 + gain: 128 + update_interval: 15s diff --git a/tests/components/hx711/test.esp32-c3.yaml b/tests/components/hx711/test.esp32-c3.yaml new file mode 100644 index 0000000000..9850417440 --- /dev/null +++ b/tests/components/hx711/test.esp32-c3.yaml @@ -0,0 +1,7 @@ +sensor: + - platform: hx711 + name: HX711 Value + dout_pin: 4 + clk_pin: 5 + gain: 128 + update_interval: 15s diff --git a/tests/components/hx711/test.esp32-idf.yaml b/tests/components/hx711/test.esp32-idf.yaml new file mode 100644 index 0000000000..554b184422 --- /dev/null +++ b/tests/components/hx711/test.esp32-idf.yaml @@ -0,0 +1,7 @@ +sensor: + - platform: hx711 + name: HX711 Value + dout_pin: 14 + clk_pin: 15 + gain: 128 + update_interval: 15s diff --git a/tests/components/hx711/test.esp32.yaml b/tests/components/hx711/test.esp32.yaml new file mode 100644 index 0000000000..554b184422 --- /dev/null +++ b/tests/components/hx711/test.esp32.yaml @@ -0,0 +1,7 @@ +sensor: + - platform: hx711 + name: HX711 Value + dout_pin: 14 + clk_pin: 15 + gain: 128 + update_interval: 15s diff --git a/tests/components/hx711/test.esp8266.yaml b/tests/components/hx711/test.esp8266.yaml new file mode 100644 index 0000000000..9850417440 --- /dev/null +++ b/tests/components/hx711/test.esp8266.yaml @@ -0,0 +1,7 @@ +sensor: + - platform: hx711 + name: HX711 Value + dout_pin: 4 + clk_pin: 5 + gain: 128 + update_interval: 15s diff --git a/tests/components/hx711/test.rp2040.yaml b/tests/components/hx711/test.rp2040.yaml new file mode 100644 index 0000000000..9850417440 --- /dev/null +++ b/tests/components/hx711/test.rp2040.yaml @@ -0,0 +1,7 @@ +sensor: + - platform: hx711 + name: HX711 Value + dout_pin: 4 + clk_pin: 5 + gain: 128 + update_interval: 15s diff --git a/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml b/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..f62f4942db --- /dev/null +++ b/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml @@ -0,0 +1,37 @@ +uart: + - id: uart_hydreon_rgxx + tx_pin: 4 + rx_pin: 5 + baud_rate: 115200 + +binary_sensor: + - platform: hydreon_rgxx + hydreon_rgxx_id: hydreon_rg9 + too_cold: + name: rg9_toocold + em_sat: + name: rg9_emsat + lens_bad: + name: rg9_lens_bad + +sensor: + - platform: hydreon_rgxx + id: hydreon_rg9 + model: RG 9 + moisture: + name: hydreon_rain + id: hydreon_rain + temperature: + name: hydreon_temperature + disable_led: true + - platform: hydreon_rgxx + id: hydreon_rg15 + model: RG_15 + acc: + name: hydreon_acc + event_acc: + name: hydreon_event_acc + total_acc: + name: hydreon_total_acc + r_int: + name: hydreon_r_int diff --git a/tests/components/hydreon_rgxx/test.esp32-c3.yaml b/tests/components/hydreon_rgxx/test.esp32-c3.yaml new file mode 100644 index 0000000000..f62f4942db --- /dev/null +++ b/tests/components/hydreon_rgxx/test.esp32-c3.yaml @@ -0,0 +1,37 @@ +uart: + - id: uart_hydreon_rgxx + tx_pin: 4 + rx_pin: 5 + baud_rate: 115200 + +binary_sensor: + - platform: hydreon_rgxx + hydreon_rgxx_id: hydreon_rg9 + too_cold: + name: rg9_toocold + em_sat: + name: rg9_emsat + lens_bad: + name: rg9_lens_bad + +sensor: + - platform: hydreon_rgxx + id: hydreon_rg9 + model: RG 9 + moisture: + name: hydreon_rain + id: hydreon_rain + temperature: + name: hydreon_temperature + disable_led: true + - platform: hydreon_rgxx + id: hydreon_rg15 + model: RG_15 + acc: + name: hydreon_acc + event_acc: + name: hydreon_event_acc + total_acc: + name: hydreon_total_acc + r_int: + name: hydreon_r_int diff --git a/tests/components/hydreon_rgxx/test.esp32-idf.yaml b/tests/components/hydreon_rgxx/test.esp32-idf.yaml new file mode 100644 index 0000000000..b6f9486d86 --- /dev/null +++ b/tests/components/hydreon_rgxx/test.esp32-idf.yaml @@ -0,0 +1,37 @@ +uart: + - id: uart_hydreon_rgxx + tx_pin: 17 + rx_pin: 16 + baud_rate: 115200 + +binary_sensor: + - platform: hydreon_rgxx + hydreon_rgxx_id: hydreon_rg9 + too_cold: + name: rg9_toocold + em_sat: + name: rg9_emsat + lens_bad: + name: rg9_lens_bad + +sensor: + - platform: hydreon_rgxx + id: hydreon_rg9 + model: RG 9 + moisture: + name: hydreon_rain + id: hydreon_rain + temperature: + name: hydreon_temperature + disable_led: true + - platform: hydreon_rgxx + id: hydreon_rg15 + model: RG_15 + acc: + name: hydreon_acc + event_acc: + name: hydreon_event_acc + total_acc: + name: hydreon_total_acc + r_int: + name: hydreon_r_int diff --git a/tests/components/hydreon_rgxx/test.esp32.yaml b/tests/components/hydreon_rgxx/test.esp32.yaml new file mode 100644 index 0000000000..b6f9486d86 --- /dev/null +++ b/tests/components/hydreon_rgxx/test.esp32.yaml @@ -0,0 +1,37 @@ +uart: + - id: uart_hydreon_rgxx + tx_pin: 17 + rx_pin: 16 + baud_rate: 115200 + +binary_sensor: + - platform: hydreon_rgxx + hydreon_rgxx_id: hydreon_rg9 + too_cold: + name: rg9_toocold + em_sat: + name: rg9_emsat + lens_bad: + name: rg9_lens_bad + +sensor: + - platform: hydreon_rgxx + id: hydreon_rg9 + model: RG 9 + moisture: + name: hydreon_rain + id: hydreon_rain + temperature: + name: hydreon_temperature + disable_led: true + - platform: hydreon_rgxx + id: hydreon_rg15 + model: RG_15 + acc: + name: hydreon_acc + event_acc: + name: hydreon_event_acc + total_acc: + name: hydreon_total_acc + r_int: + name: hydreon_r_int diff --git a/tests/components/hydreon_rgxx/test.esp8266.yaml b/tests/components/hydreon_rgxx/test.esp8266.yaml new file mode 100644 index 0000000000..f62f4942db --- /dev/null +++ b/tests/components/hydreon_rgxx/test.esp8266.yaml @@ -0,0 +1,37 @@ +uart: + - id: uart_hydreon_rgxx + tx_pin: 4 + rx_pin: 5 + baud_rate: 115200 + +binary_sensor: + - platform: hydreon_rgxx + hydreon_rgxx_id: hydreon_rg9 + too_cold: + name: rg9_toocold + em_sat: + name: rg9_emsat + lens_bad: + name: rg9_lens_bad + +sensor: + - platform: hydreon_rgxx + id: hydreon_rg9 + model: RG 9 + moisture: + name: hydreon_rain + id: hydreon_rain + temperature: + name: hydreon_temperature + disable_led: true + - platform: hydreon_rgxx + id: hydreon_rg15 + model: RG_15 + acc: + name: hydreon_acc + event_acc: + name: hydreon_event_acc + total_acc: + name: hydreon_total_acc + r_int: + name: hydreon_r_int diff --git a/tests/components/hydreon_rgxx/test.rp2040.yaml b/tests/components/hydreon_rgxx/test.rp2040.yaml new file mode 100644 index 0000000000..f62f4942db --- /dev/null +++ b/tests/components/hydreon_rgxx/test.rp2040.yaml @@ -0,0 +1,37 @@ +uart: + - id: uart_hydreon_rgxx + tx_pin: 4 + rx_pin: 5 + baud_rate: 115200 + +binary_sensor: + - platform: hydreon_rgxx + hydreon_rgxx_id: hydreon_rg9 + too_cold: + name: rg9_toocold + em_sat: + name: rg9_emsat + lens_bad: + name: rg9_lens_bad + +sensor: + - platform: hydreon_rgxx + id: hydreon_rg9 + model: RG 9 + moisture: + name: hydreon_rain + id: hydreon_rain + temperature: + name: hydreon_temperature + disable_led: true + - platform: hydreon_rgxx + id: hydreon_rg15 + model: RG_15 + acc: + name: hydreon_acc + event_acc: + name: hydreon_event_acc + total_acc: + name: hydreon_total_acc + r_int: + name: hydreon_r_int diff --git a/tests/components/hyt271/test.esp32-c3-idf.yaml b/tests/components/hyt271/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..c44f0a1f5d --- /dev/null +++ b/tests/components/hyt271/test.esp32-c3-idf.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_hyt271 + scl: 5 + sda: 4 + +sensor: + - platform: hyt271 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hyt271/test.esp32-c3.yaml b/tests/components/hyt271/test.esp32-c3.yaml new file mode 100644 index 0000000000..c44f0a1f5d --- /dev/null +++ b/tests/components/hyt271/test.esp32-c3.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_hyt271 + scl: 5 + sda: 4 + +sensor: + - platform: hyt271 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hyt271/test.esp32-idf.yaml b/tests/components/hyt271/test.esp32-idf.yaml new file mode 100644 index 0000000000..297611a89b --- /dev/null +++ b/tests/components/hyt271/test.esp32-idf.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_hyt271 + scl: 16 + sda: 17 + +sensor: + - platform: hyt271 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hyt271/test.esp32.yaml b/tests/components/hyt271/test.esp32.yaml new file mode 100644 index 0000000000..297611a89b --- /dev/null +++ b/tests/components/hyt271/test.esp32.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_hyt271 + scl: 16 + sda: 17 + +sensor: + - platform: hyt271 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hyt271/test.esp8266.yaml b/tests/components/hyt271/test.esp8266.yaml new file mode 100644 index 0000000000..c44f0a1f5d --- /dev/null +++ b/tests/components/hyt271/test.esp8266.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_hyt271 + scl: 5 + sda: 4 + +sensor: + - platform: hyt271 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hyt271/test.rp2040.yaml b/tests/components/hyt271/test.rp2040.yaml new file mode 100644 index 0000000000..c44f0a1f5d --- /dev/null +++ b/tests/components/hyt271/test.rp2040.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_hyt271 + scl: 5 + sda: 4 + +sensor: + - platform: hyt271 + temperature: + name: Temperature + humidity: + name: Humidity From 6d480c5f058323933bc1f24de891928f86b36e95 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:09:25 +1200 Subject: [PATCH 0056/1106] ads1115: remove auto-load and split sensor into platform folder (#5981) --- esphome/components/ads1115/__init__.py | 3 +- esphome/components/ads1115/ads1115.cpp | 31 +++++----------- esphome/components/ads1115/ads1115.h | 31 ++-------------- .../ads1115/{sensor.py => sensor/__init__.py} | 21 +++-------- .../ads1115/sensor/ads1115_sensor.cpp | 30 ++++++++++++++++ .../ads1115/sensor/ads1115_sensor.h | 35 +++++++++++++++++++ 6 files changed, 82 insertions(+), 69 deletions(-) rename esphome/components/ads1115/{sensor.py => sensor/__init__.py} (82%) create mode 100644 esphome/components/ads1115/sensor/ads1115_sensor.cpp create mode 100644 esphome/components/ads1115/sensor/ads1115_sensor.h diff --git a/esphome/components/ads1115/__init__.py b/esphome/components/ads1115/__init__.py index e8861a2f67..a463d8390d 100644 --- a/esphome/components/ads1115/__init__.py +++ b/esphome/components/ads1115/__init__.py @@ -4,13 +4,14 @@ from esphome.components import i2c from esphome.const import CONF_ID DEPENDENCIES = ["i2c"] -AUTO_LOAD = ["sensor", "voltage_sampler"] MULTI_CONF = True ads1115_ns = cg.esphome_ns.namespace("ads1115") ADS1115Component = ads1115_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice) CONF_CONTINUOUS_MODE = "continuous_mode" +CONF_ADS1115_ID = "ads1115_id" + CONFIG_SCHEMA = ( cv.Schema( { diff --git a/esphome/components/ads1115/ads1115.cpp b/esphome/components/ads1115/ads1115.cpp index c3f3c00c63..218edc4c81 100644 --- a/esphome/components/ads1115/ads1115.cpp +++ b/esphome/components/ads1115/ads1115.cpp @@ -1,6 +1,6 @@ #include "ads1115.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/log.h" namespace esphome { namespace ads1115 { @@ -75,25 +75,19 @@ void ADS1115Component::dump_config() { if (this->is_failed()) { ESP_LOGE(TAG, "Communication with ADS1115 failed!"); } - - for (auto *sensor : this->sensors_) { - LOG_SENSOR(" ", "Sensor", sensor); - ESP_LOGCONFIG(TAG, " Multiplexer: %u", sensor->get_multiplexer()); - ESP_LOGCONFIG(TAG, " Gain: %u", sensor->get_gain()); - ESP_LOGCONFIG(TAG, " Resolution: %u", sensor->get_resolution()); - } } -float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { +float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, + ADS1115Resolution resolution) { uint16_t config = this->prev_config_; // Multiplexer // 0bxBBBxxxxxxxxxxxx config &= 0b1000111111111111; - config |= (sensor->get_multiplexer() & 0b111) << 12; + config |= (multiplexer & 0b111) << 12; // Gain // 0bxxxxBBBxxxxxxxxx config &= 0b1111000111111111; - config |= (sensor->get_gain() & 0b111) << 9; + config |= (gain & 0b111) << 9; if (!this->continuous_mode_) { // Start conversion @@ -132,7 +126,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { return NAN; } - if (sensor->get_resolution() == ADS1015_12_BITS) { + if (resolution == ADS1015_12_BITS) { bool negative = (raw_conversion >> 15) == 1; // shift raw_conversion as it's only 12-bits, left justified @@ -151,8 +145,8 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { auto signed_conversion = static_cast(raw_conversion); float millivolts; - float divider = (sensor->get_resolution() == ADS1115_16_BITS) ? 32768.0f : 2048.0f; - switch (sensor->get_gain()) { + float divider = (resolution == ADS1115_16_BITS) ? 32768.0f : 2048.0f; + switch (gain) { case ADS1115_GAIN_6P144: millivolts = (signed_conversion * 6144) / divider; break; @@ -179,14 +173,5 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { return millivolts / 1e3f; } -float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); } -void ADS1115Sensor::update() { - float v = this->parent_->request_measurement(this); - if (!std::isnan(v)) { - ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v); - this->publish_state(v); - } -} - } // namespace ads1115 } // namespace esphome diff --git a/esphome/components/ads1115/ads1115.h b/esphome/components/ads1115/ads1115.h index 0b8bfb339b..509333d2c8 100644 --- a/esphome/components/ads1115/ads1115.h +++ b/esphome/components/ads1115/ads1115.h @@ -1,9 +1,7 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" -#include "esphome/components/voltage_sampler/voltage_sampler.h" +#include "esphome/core/component.h" #include @@ -35,12 +33,8 @@ enum ADS1115Resolution { ADS1015_12_BITS = 12, }; -class ADS1115Sensor; - class ADS1115Component : public Component, public i2c::I2CDevice { public: - void register_sensor(ADS1115Sensor *obj) { this->sensors_.push_back(obj); } - /// Set up the internal sensor array. void setup() override; void dump_config() override; /// HARDWARE_LATE setup priority @@ -48,33 +42,12 @@ class ADS1115Component : public Component, public i2c::I2CDevice { void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; } /// Helper method to request a measurement from a sensor. - float request_measurement(ADS1115Sensor *sensor); + float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution); protected: - std::vector sensors_; uint16_t prev_config_{0}; bool continuous_mode_; }; -/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors. -class ADS1115Sensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler { - public: - ADS1115Sensor(ADS1115Component *parent) : parent_(parent) {} - void update() override; - void set_multiplexer(ADS1115Multiplexer multiplexer) { multiplexer_ = multiplexer; } - void set_gain(ADS1115Gain gain) { gain_ = gain; } - void set_resolution(ADS1115Resolution resolution) { resolution_ = resolution; } - float sample() override; - uint8_t get_multiplexer() const { return multiplexer_; } - uint8_t get_gain() const { return gain_; } - uint8_t get_resolution() const { return resolution_; } - - protected: - ADS1115Component *parent_; - ADS1115Multiplexer multiplexer_; - ADS1115Gain gain_; - ADS1115Resolution resolution_; -}; - } // namespace ads1115 } // namespace esphome diff --git a/esphome/components/ads1115/sensor.py b/esphome/components/ads1115/sensor/__init__.py similarity index 82% rename from esphome/components/ads1115/sensor.py rename to esphome/components/ads1115/sensor/__init__.py index f0d894e2af..baec31d35c 100644 --- a/esphome/components/ads1115/sensor.py +++ b/esphome/components/ads1115/sensor/__init__.py @@ -10,8 +10,9 @@ from esphome.const import ( UNIT_VOLT, CONF_ID, ) -from . import ads1115_ns, ADS1115Component +from .. import ads1115_ns, ADS1115Component, CONF_ADS1115_ID +AUTO_LOAD = ["voltage_sampler"] DEPENDENCIES = ["ads1115"] ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer") @@ -43,20 +44,10 @@ RESOLUTION = { } -def validate_gain(value): - if isinstance(value, float): - value = f"{value:0.03f}" - elif not isinstance(value, str): - raise cv.Invalid(f'invalid gain "{value}"') - - return cv.enum(GAIN)(value) - - ADS1115Sensor = ads1115_ns.class_( "ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler ) -CONF_ADS1115_ID = "ads1115_id" CONFIG_SCHEMA = ( sensor.sensor_schema( ADS1115Sensor, @@ -69,7 +60,7 @@ CONFIG_SCHEMA = ( { cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component), cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"), - cv.Required(CONF_GAIN): validate_gain, + cv.Required(CONF_GAIN): cv.enum(GAIN, string=True), cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum( RESOLUTION, upper=True, space="_" ), @@ -80,13 +71,11 @@ CONFIG_SCHEMA = ( async def to_code(config): - paren = await cg.get_variable(config[CONF_ADS1115_ID]) - var = cg.new_Pvariable(config[CONF_ID], paren) + var = cg.new_Pvariable(config[CONF_ID]) await sensor.register_sensor(var, config) await cg.register_component(var, config) + await cg.register_parented(var, config[CONF_ADS1115_ID]) cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER])) cg.add(var.set_gain(config[CONF_GAIN])) cg.add(var.set_resolution(config[CONF_RESOLUTION])) - - cg.add(paren.register_sensor(var)) diff --git a/esphome/components/ads1115/sensor/ads1115_sensor.cpp b/esphome/components/ads1115/sensor/ads1115_sensor.cpp new file mode 100644 index 0000000000..335fca4845 --- /dev/null +++ b/esphome/components/ads1115/sensor/ads1115_sensor.cpp @@ -0,0 +1,30 @@ +#include "ads1115_sensor.h" + +#include "esphome/core/log.h" + +namespace esphome { +namespace ads1115 { + +static const char *const TAG = "ads1115.sensor"; + +float ADS1115Sensor::sample() { + return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_); +} + +void ADS1115Sensor::update() { + float v = this->sample(); + if (!std::isnan(v)) { + ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v); + this->publish_state(v); + } +} + +void ADS1115Sensor::dump_config() { + LOG_SENSOR(" ", "ADS1115 Sensor", this); + ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_); + ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_); + ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_); +} + +} // namespace ads1115 +} // namespace esphome diff --git a/esphome/components/ads1115/sensor/ads1115_sensor.h b/esphome/components/ads1115/sensor/ads1115_sensor.h new file mode 100644 index 0000000000..191afc3de6 --- /dev/null +++ b/esphome/components/ads1115/sensor/ads1115_sensor.h @@ -0,0 +1,35 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" + +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/voltage_sampler/voltage_sampler.h" + +#include "../ads1115.h" + +namespace esphome { +namespace ads1115 { + +/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors. +class ADS1115Sensor : public sensor::Sensor, + public PollingComponent, + public voltage_sampler::VoltageSampler, + public Parented { + public: + void update() override; + void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; } + void set_gain(ADS1115Gain gain) { this->gain_ = gain; } + void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; } + float sample() override; + + void dump_config() override; + + protected: + ADS1115Multiplexer multiplexer_; + ADS1115Gain gain_; + ADS1115Resolution resolution_; +}; + +} // namespace ads1115 +} // namespace esphome From e6bfa275fca4056cd2417023850c3c94b622c291 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 12 Apr 2024 07:57:41 +1200 Subject: [PATCH 0057/1106] Bump esphome-dashboard to 20240412.0 (#6517) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 702127eca8..4abc4d98d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.13 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20240319.0 +esphome-dashboard==20240412.0 aioesphomeapi==23.2.0 zeroconf==0.131.0 python-magic==0.4.27 From 68b4d8865c14ecde4f8d9bdc1febae981962b0a6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 10 Apr 2024 19:25:35 +1200 Subject: [PATCH 0058/1106] Add dooya remote transmitter test (#6508) --- tests/components/remote_transmitter/common-buttons.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml index 5f655acb7c..27683b387f 100644 --- a/tests/components/remote_transmitter/common-buttons.yaml +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -176,3 +176,11 @@ button: 0x00, 0x05, ] + - platform: template + name: Dooya + on_press: + remote_transmitter.transmit_dooya: + id: 0x123456 + channel: 1 + button: 1 + check: 1 From 1d4c074ee6aa301dea5fb7f9f161e83abd83f313 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:09:25 +1200 Subject: [PATCH 0059/1106] ads1115: remove auto-load and split sensor into platform folder (#5981) --- esphome/components/ads1115/__init__.py | 3 +- esphome/components/ads1115/ads1115.cpp | 31 +++++----------- esphome/components/ads1115/ads1115.h | 31 ++-------------- .../ads1115/{sensor.py => sensor/__init__.py} | 21 +++-------- .../ads1115/sensor/ads1115_sensor.cpp | 30 ++++++++++++++++ .../ads1115/sensor/ads1115_sensor.h | 35 +++++++++++++++++++ 6 files changed, 82 insertions(+), 69 deletions(-) rename esphome/components/ads1115/{sensor.py => sensor/__init__.py} (82%) create mode 100644 esphome/components/ads1115/sensor/ads1115_sensor.cpp create mode 100644 esphome/components/ads1115/sensor/ads1115_sensor.h diff --git a/esphome/components/ads1115/__init__.py b/esphome/components/ads1115/__init__.py index e8861a2f67..a463d8390d 100644 --- a/esphome/components/ads1115/__init__.py +++ b/esphome/components/ads1115/__init__.py @@ -4,13 +4,14 @@ from esphome.components import i2c from esphome.const import CONF_ID DEPENDENCIES = ["i2c"] -AUTO_LOAD = ["sensor", "voltage_sampler"] MULTI_CONF = True ads1115_ns = cg.esphome_ns.namespace("ads1115") ADS1115Component = ads1115_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice) CONF_CONTINUOUS_MODE = "continuous_mode" +CONF_ADS1115_ID = "ads1115_id" + CONFIG_SCHEMA = ( cv.Schema( { diff --git a/esphome/components/ads1115/ads1115.cpp b/esphome/components/ads1115/ads1115.cpp index c3f3c00c63..218edc4c81 100644 --- a/esphome/components/ads1115/ads1115.cpp +++ b/esphome/components/ads1115/ads1115.cpp @@ -1,6 +1,6 @@ #include "ads1115.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/log.h" namespace esphome { namespace ads1115 { @@ -75,25 +75,19 @@ void ADS1115Component::dump_config() { if (this->is_failed()) { ESP_LOGE(TAG, "Communication with ADS1115 failed!"); } - - for (auto *sensor : this->sensors_) { - LOG_SENSOR(" ", "Sensor", sensor); - ESP_LOGCONFIG(TAG, " Multiplexer: %u", sensor->get_multiplexer()); - ESP_LOGCONFIG(TAG, " Gain: %u", sensor->get_gain()); - ESP_LOGCONFIG(TAG, " Resolution: %u", sensor->get_resolution()); - } } -float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { +float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, + ADS1115Resolution resolution) { uint16_t config = this->prev_config_; // Multiplexer // 0bxBBBxxxxxxxxxxxx config &= 0b1000111111111111; - config |= (sensor->get_multiplexer() & 0b111) << 12; + config |= (multiplexer & 0b111) << 12; // Gain // 0bxxxxBBBxxxxxxxxx config &= 0b1111000111111111; - config |= (sensor->get_gain() & 0b111) << 9; + config |= (gain & 0b111) << 9; if (!this->continuous_mode_) { // Start conversion @@ -132,7 +126,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { return NAN; } - if (sensor->get_resolution() == ADS1015_12_BITS) { + if (resolution == ADS1015_12_BITS) { bool negative = (raw_conversion >> 15) == 1; // shift raw_conversion as it's only 12-bits, left justified @@ -151,8 +145,8 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { auto signed_conversion = static_cast(raw_conversion); float millivolts; - float divider = (sensor->get_resolution() == ADS1115_16_BITS) ? 32768.0f : 2048.0f; - switch (sensor->get_gain()) { + float divider = (resolution == ADS1115_16_BITS) ? 32768.0f : 2048.0f; + switch (gain) { case ADS1115_GAIN_6P144: millivolts = (signed_conversion * 6144) / divider; break; @@ -179,14 +173,5 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { return millivolts / 1e3f; } -float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); } -void ADS1115Sensor::update() { - float v = this->parent_->request_measurement(this); - if (!std::isnan(v)) { - ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v); - this->publish_state(v); - } -} - } // namespace ads1115 } // namespace esphome diff --git a/esphome/components/ads1115/ads1115.h b/esphome/components/ads1115/ads1115.h index 0b8bfb339b..509333d2c8 100644 --- a/esphome/components/ads1115/ads1115.h +++ b/esphome/components/ads1115/ads1115.h @@ -1,9 +1,7 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" -#include "esphome/components/voltage_sampler/voltage_sampler.h" +#include "esphome/core/component.h" #include @@ -35,12 +33,8 @@ enum ADS1115Resolution { ADS1015_12_BITS = 12, }; -class ADS1115Sensor; - class ADS1115Component : public Component, public i2c::I2CDevice { public: - void register_sensor(ADS1115Sensor *obj) { this->sensors_.push_back(obj); } - /// Set up the internal sensor array. void setup() override; void dump_config() override; /// HARDWARE_LATE setup priority @@ -48,33 +42,12 @@ class ADS1115Component : public Component, public i2c::I2CDevice { void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; } /// Helper method to request a measurement from a sensor. - float request_measurement(ADS1115Sensor *sensor); + float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution); protected: - std::vector sensors_; uint16_t prev_config_{0}; bool continuous_mode_; }; -/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors. -class ADS1115Sensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler { - public: - ADS1115Sensor(ADS1115Component *parent) : parent_(parent) {} - void update() override; - void set_multiplexer(ADS1115Multiplexer multiplexer) { multiplexer_ = multiplexer; } - void set_gain(ADS1115Gain gain) { gain_ = gain; } - void set_resolution(ADS1115Resolution resolution) { resolution_ = resolution; } - float sample() override; - uint8_t get_multiplexer() const { return multiplexer_; } - uint8_t get_gain() const { return gain_; } - uint8_t get_resolution() const { return resolution_; } - - protected: - ADS1115Component *parent_; - ADS1115Multiplexer multiplexer_; - ADS1115Gain gain_; - ADS1115Resolution resolution_; -}; - } // namespace ads1115 } // namespace esphome diff --git a/esphome/components/ads1115/sensor.py b/esphome/components/ads1115/sensor/__init__.py similarity index 82% rename from esphome/components/ads1115/sensor.py rename to esphome/components/ads1115/sensor/__init__.py index f0d894e2af..baec31d35c 100644 --- a/esphome/components/ads1115/sensor.py +++ b/esphome/components/ads1115/sensor/__init__.py @@ -10,8 +10,9 @@ from esphome.const import ( UNIT_VOLT, CONF_ID, ) -from . import ads1115_ns, ADS1115Component +from .. import ads1115_ns, ADS1115Component, CONF_ADS1115_ID +AUTO_LOAD = ["voltage_sampler"] DEPENDENCIES = ["ads1115"] ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer") @@ -43,20 +44,10 @@ RESOLUTION = { } -def validate_gain(value): - if isinstance(value, float): - value = f"{value:0.03f}" - elif not isinstance(value, str): - raise cv.Invalid(f'invalid gain "{value}"') - - return cv.enum(GAIN)(value) - - ADS1115Sensor = ads1115_ns.class_( "ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler ) -CONF_ADS1115_ID = "ads1115_id" CONFIG_SCHEMA = ( sensor.sensor_schema( ADS1115Sensor, @@ -69,7 +60,7 @@ CONFIG_SCHEMA = ( { cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component), cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"), - cv.Required(CONF_GAIN): validate_gain, + cv.Required(CONF_GAIN): cv.enum(GAIN, string=True), cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum( RESOLUTION, upper=True, space="_" ), @@ -80,13 +71,11 @@ CONFIG_SCHEMA = ( async def to_code(config): - paren = await cg.get_variable(config[CONF_ADS1115_ID]) - var = cg.new_Pvariable(config[CONF_ID], paren) + var = cg.new_Pvariable(config[CONF_ID]) await sensor.register_sensor(var, config) await cg.register_component(var, config) + await cg.register_parented(var, config[CONF_ADS1115_ID]) cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER])) cg.add(var.set_gain(config[CONF_GAIN])) cg.add(var.set_resolution(config[CONF_RESOLUTION])) - - cg.add(paren.register_sensor(var)) diff --git a/esphome/components/ads1115/sensor/ads1115_sensor.cpp b/esphome/components/ads1115/sensor/ads1115_sensor.cpp new file mode 100644 index 0000000000..335fca4845 --- /dev/null +++ b/esphome/components/ads1115/sensor/ads1115_sensor.cpp @@ -0,0 +1,30 @@ +#include "ads1115_sensor.h" + +#include "esphome/core/log.h" + +namespace esphome { +namespace ads1115 { + +static const char *const TAG = "ads1115.sensor"; + +float ADS1115Sensor::sample() { + return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_); +} + +void ADS1115Sensor::update() { + float v = this->sample(); + if (!std::isnan(v)) { + ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v); + this->publish_state(v); + } +} + +void ADS1115Sensor::dump_config() { + LOG_SENSOR(" ", "ADS1115 Sensor", this); + ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_); + ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_); + ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_); +} + +} // namespace ads1115 +} // namespace esphome diff --git a/esphome/components/ads1115/sensor/ads1115_sensor.h b/esphome/components/ads1115/sensor/ads1115_sensor.h new file mode 100644 index 0000000000..191afc3de6 --- /dev/null +++ b/esphome/components/ads1115/sensor/ads1115_sensor.h @@ -0,0 +1,35 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" + +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/voltage_sampler/voltage_sampler.h" + +#include "../ads1115.h" + +namespace esphome { +namespace ads1115 { + +/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors. +class ADS1115Sensor : public sensor::Sensor, + public PollingComponent, + public voltage_sampler::VoltageSampler, + public Parented { + public: + void update() override; + void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; } + void set_gain(ADS1115Gain gain) { this->gain_ = gain; } + void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; } + float sample() override; + + void dump_config() override; + + protected: + ADS1115Multiplexer multiplexer_; + ADS1115Gain gain_; + ADS1115Resolution resolution_; +}; + +} // namespace ads1115 +} // namespace esphome From 4ebbd4ebd8c1e4960f4ee081a8c46fa6d3a8d7ee Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 12 Apr 2024 07:57:41 +1200 Subject: [PATCH 0060/1106] Bump esphome-dashboard to 20240412.0 (#6517) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 702127eca8..4abc4d98d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.13 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20240319.0 +esphome-dashboard==20240412.0 aioesphomeapi==23.2.0 zeroconf==0.131.0 python-magic==0.4.27 From 8ada8f5e11f3ce4045a3752d0ff8fbf11d748a2a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 12 Apr 2024 08:02:52 +1200 Subject: [PATCH 0061/1106] Bump version to 2024.4.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5f8082c395..08a1a42ea3 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.4.0b1" +__version__ = "2024.4.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 55433463d75c65405296972b4fd389b9f0e39cdd Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 12 Apr 2024 10:19:49 +1200 Subject: [PATCH 0062/1106] Fix missing ifdefs in voice assistant (#6520) --- esphome/components/voice_assistant/voice_assistant.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 34a26eec01..e68e00948e 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -729,6 +729,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { } void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) { +#ifdef USE_SPEAKER // We should never get to this function if there is no speaker anyway if (this->speaker_buffer_index_ + msg.data.length() < SPEAKER_BUFFER_SIZE) { memcpy(this->speaker_buffer_ + this->speaker_buffer_index_, msg.data.data(), msg.data.length()); this->speaker_buffer_index_ += msg.data.length(); @@ -737,6 +738,7 @@ void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) { } else { ESP_LOGE(TAG, "Cannot receive audio, buffer is full"); } +#endif } VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) From 39947a163440e3bbdee2d4082c11599de46ffbd0 Mon Sep 17 00:00:00 2001 From: MRemy2 <95053616+MRemy2@users.noreply.github.com> Date: Fri, 12 Apr 2024 02:28:59 +0300 Subject: [PATCH 0063/1106] Added Htu21d model option (#6511) Co-authored-by: Remus Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/htu21d/htu21d.cpp | 21 ++++++++++++++++--- esphome/components/htu21d/htu21d.h | 4 ++++ esphome/components/htu21d/sensor.py | 11 +++++++++- .../components/htu21d/test.esp32-c3-idf.yaml | 1 + tests/components/htu21d/test.esp32-c3.yaml | 1 + tests/components/htu21d/test.esp32-idf.yaml | 1 + tests/components/htu21d/test.esp32.yaml | 1 + tests/components/htu21d/test.esp8266.yaml | 1 + tests/components/htu21d/test.rp2040.yaml | 1 + 9 files changed, 38 insertions(+), 4 deletions(-) diff --git a/esphome/components/htu21d/htu21d.cpp b/esphome/components/htu21d/htu21d.cpp index d0dbb15a43..411d1e1d6a 100644 --- a/esphome/components/htu21d/htu21d.cpp +++ b/esphome/components/htu21d/htu21d.cpp @@ -76,12 +76,27 @@ void HTU21DComponent::update() { float humidity = (float(raw_humidity & 0xFFFC)) * 125.0f / 65536.0f - 6.0f; - int8_t heater_level = this->get_heater_level(); - - ESP_LOGD(TAG, "Got Humidity=%.1f%% Heater Level=%d", humidity, heater_level); + ESP_LOGD(TAG, "Got Humidity=%.1f%%", humidity); if (this->humidity_ != nullptr) this->humidity_->publish_state(humidity); + + int8_t heater_level; + + // HTU21D does have a heater module but does not have heater level + // Setting heater level to 1 in case the heater is ON + if (this->sensor_model_ == HTU21D_SENSOR_MODEL_HTU21D) { + if (this->is_heater_enabled()) { + heater_level = 1; + } else { + heater_level = 0; + } + } else { + heater_level = this->get_heater_level(); + } + + ESP_LOGD(TAG, "Heater Level=%d", heater_level); + if (this->heater_ != nullptr) this->heater_->publish_state(heater_level); this->status_clear_warning(); diff --git a/esphome/components/htu21d/htu21d.h b/esphome/components/htu21d/htu21d.h index a77a8e3ada..8533875d43 100644 --- a/esphome/components/htu21d/htu21d.h +++ b/esphome/components/htu21d/htu21d.h @@ -8,6 +8,8 @@ namespace esphome { namespace htu21d { +enum HTU21DSensorModels { HTU21D_SENSOR_MODEL_HTU21D = 0, HTU21D_SENSOR_MODEL_SI7021, HTU21D_SENSOR_MODEL_SHT21 }; + class HTU21DComponent : public PollingComponent, public i2c::I2CDevice { public: void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } @@ -17,6 +19,7 @@ class HTU21DComponent : public PollingComponent, public i2c::I2CDevice { /// Setup (reset) the sensor and check connection. void setup() override; void dump_config() override; + void set_sensor_model(HTU21DSensorModels sensor_model) { sensor_model_ = sensor_model; } /// Update the sensor values (temperature+humidity). void update() override; @@ -31,6 +34,7 @@ class HTU21DComponent : public PollingComponent, public i2c::I2CDevice { sensor::Sensor *temperature_{nullptr}; sensor::Sensor *humidity_{nullptr}; sensor::Sensor *heater_{nullptr}; + HTU21DSensorModels sensor_model_{HTU21D_SENSOR_MODEL_HTU21D}; }; template class SetHeaterLevelAction : public Action, public Parented { diff --git a/esphome/components/htu21d/sensor.py b/esphome/components/htu21d/sensor.py index 1f878230f8..bf0b9a23fb 100644 --- a/esphome/components/htu21d/sensor.py +++ b/esphome/components/htu21d/sensor.py @@ -5,6 +5,7 @@ from esphome import automation from esphome.const import ( CONF_HUMIDITY, CONF_ID, + CONF_MODEL, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, @@ -23,10 +24,15 @@ htu21d_ns = cg.esphome_ns.namespace("htu21d") HTU21DComponent = htu21d_ns.class_( "HTU21DComponent", cg.PollingComponent, i2c.I2CDevice ) - SetHeaterLevelAction = htu21d_ns.class_("SetHeaterLevelAction", automation.Action) SetHeaterAction = htu21d_ns.class_("SetHeaterAction", automation.Action) +HTU21DSensorModels = htu21d_ns.enum("HTU21DSensorModels") +MODELS = { + "HTU21D": HTU21DSensorModels.HTU21D_SENSOR_MODEL_HTU21D, + "SI7021": HTU21DSensorModels.HTU21D_SENSOR_MODEL_SI7021, + "SHT21": HTU21DSensorModels.HTU21D_SENSOR_MODEL_SHT21, +} CONFIG_SCHEMA = ( cv.Schema( @@ -49,6 +55,7 @@ CONFIG_SCHEMA = ( accuracy_decimals=1, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_MODEL, default="HTU21D"): cv.enum(MODELS, upper=True), } ) .extend(cv.polling_component_schema("60s")) @@ -73,6 +80,8 @@ async def to_code(config): sens = await sensor.new_sensor(config[CONF_HEATER]) cg.add(var.set_heater(sens)) + cg.add(var.set_sensor_model(config[CONF_MODEL])) + @automation.register_action( "htu21d.set_heater_level", diff --git a/tests/components/htu21d/test.esp32-c3-idf.yaml b/tests/components/htu21d/test.esp32-c3-idf.yaml index d9fbb09550..8131d13661 100644 --- a/tests/components/htu21d/test.esp32-c3-idf.yaml +++ b/tests/components/htu21d/test.esp32-c3-idf.yaml @@ -5,6 +5,7 @@ i2c: sensor: - platform: htu21d + model: htu21d temperature: name: Temperature humidity: diff --git a/tests/components/htu21d/test.esp32-c3.yaml b/tests/components/htu21d/test.esp32-c3.yaml index d9fbb09550..8131d13661 100644 --- a/tests/components/htu21d/test.esp32-c3.yaml +++ b/tests/components/htu21d/test.esp32-c3.yaml @@ -5,6 +5,7 @@ i2c: sensor: - platform: htu21d + model: htu21d temperature: name: Temperature humidity: diff --git a/tests/components/htu21d/test.esp32-idf.yaml b/tests/components/htu21d/test.esp32-idf.yaml index 48f03eb368..6655a1cc1a 100644 --- a/tests/components/htu21d/test.esp32-idf.yaml +++ b/tests/components/htu21d/test.esp32-idf.yaml @@ -5,6 +5,7 @@ i2c: sensor: - platform: htu21d + model: htu21d temperature: name: Temperature humidity: diff --git a/tests/components/htu21d/test.esp32.yaml b/tests/components/htu21d/test.esp32.yaml index 48f03eb368..6655a1cc1a 100644 --- a/tests/components/htu21d/test.esp32.yaml +++ b/tests/components/htu21d/test.esp32.yaml @@ -5,6 +5,7 @@ i2c: sensor: - platform: htu21d + model: htu21d temperature: name: Temperature humidity: diff --git a/tests/components/htu21d/test.esp8266.yaml b/tests/components/htu21d/test.esp8266.yaml index d9fbb09550..8131d13661 100644 --- a/tests/components/htu21d/test.esp8266.yaml +++ b/tests/components/htu21d/test.esp8266.yaml @@ -5,6 +5,7 @@ i2c: sensor: - platform: htu21d + model: htu21d temperature: name: Temperature humidity: diff --git a/tests/components/htu21d/test.rp2040.yaml b/tests/components/htu21d/test.rp2040.yaml index d9fbb09550..8131d13661 100644 --- a/tests/components/htu21d/test.rp2040.yaml +++ b/tests/components/htu21d/test.rp2040.yaml @@ -5,6 +5,7 @@ i2c: sensor: - platform: htu21d + model: htu21d temperature: name: Temperature humidity: From 810cf3b0a4d705a35d0cd2285ff304485e7905df Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 12 Apr 2024 11:56:18 +1200 Subject: [PATCH 0064/1106] Add bk72xx base test file (#6522) --- .../build_components_base.bk72xx.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/test_build_components/build_components_base.bk72xx.yaml diff --git a/tests/test_build_components/build_components_base.bk72xx.yaml b/tests/test_build_components/build_components_base.bk72xx.yaml new file mode 100644 index 0000000000..7fdaebc768 --- /dev/null +++ b/tests/test_build_components/build_components_base.bk72xx.yaml @@ -0,0 +1,18 @@ +esphome: + name: componenttestespbk72xx + friendly_name: $component_name + +bk72xx: + board: cb3s + +logger: + level: VERY_VERBOSE + +packages: + component_under_test: !include + file: $component_test_file + vars: + component_name: $component_name + test_name: $test_name + target_platform: $target_platform + component_test_file: $component_test_file From 7eb524f920143ce7be075c84ac13c4856544ac91 Mon Sep 17 00:00:00 2001 From: Peter Zich Date: Thu, 11 Apr 2024 20:46:59 -0700 Subject: [PATCH 0065/1106] Add "log" alias for "logs" command (#6519) --- esphome/__main__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/__main__.py b/esphome/__main__.py index 95d444ca9b..dcd2dddb4b 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -785,6 +785,7 @@ def parse_args(argv): parser_logs = subparsers.add_parser( "logs", help="Validate the configuration and show all logs.", + aliases=["log"], parents=[mqtt_options], ) parser_logs.add_argument( From 76daefe21c5f7a405d3f7df232bb7df0a2f4fe09 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Fri, 12 Apr 2024 06:03:08 +0200 Subject: [PATCH 0066/1106] Add ethernet DNS text sensor and simplify DNS display format (#6450) --- .../ethernet/ethernet_component.cpp | 5 +++++ .../components/ethernet/ethernet_component.h | 1 + .../ethernet_info_text_sensor.cpp | 1 + .../ethernet_info/ethernet_info_text_sensor.h | 21 +++++++++++++++++++ .../components/ethernet_info/text_sensor.py | 17 ++++++++++++--- .../wifi_info/wifi_info_text_sensor.h | 7 +------ .../ethernet_info/test.esp32-idf.yaml | 2 ++ .../components/ethernet_info/test.esp32.yaml | 2 ++ 8 files changed, 47 insertions(+), 9 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 3c61fbe0a6..243135de89 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -340,6 +340,11 @@ network::IPAddresses EthernetComponent::get_ip_addresses() { return addresses; } +network::IPAddress EthernetComponent::get_dns_address(uint8_t num) { + const ip_addr_t *dns_ip = dns_getserver(num); + return dns_ip; +} + void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) { const char *event_name; diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 621ac87c10..daeb5a2029 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -70,6 +70,7 @@ class EthernetComponent : public Component { void set_manual_ip(const ManualIP &manual_ip); network::IPAddresses get_ip_addresses(); + network::IPAddress get_dns_address(uint8_t num); std::string get_use_address() const; void set_use_address(const std::string &use_address); bool powerdown(); diff --git a/esphome/components/ethernet_info/ethernet_info_text_sensor.cpp b/esphome/components/ethernet_info/ethernet_info_text_sensor.cpp index f841875396..c8b2b5885b 100644 --- a/esphome/components/ethernet_info/ethernet_info_text_sensor.cpp +++ b/esphome/components/ethernet_info/ethernet_info_text_sensor.cpp @@ -9,6 +9,7 @@ namespace ethernet_info { static const char *const TAG = "ethernet_info"; void IPAddressEthernetInfo::dump_config() { LOG_TEXT_SENSOR("", "EthernetInfo IPAddress", this); } +void DNSAddressEthernetInfo::dump_config() { LOG_TEXT_SENSOR("", "EthernetInfo DNS Address", this); } } // namespace ethernet_info } // namespace esphome diff --git a/esphome/components/ethernet_info/ethernet_info_text_sensor.h b/esphome/components/ethernet_info/ethernet_info_text_sensor.h index b5764d2519..82a7dcf56e 100644 --- a/esphome/components/ethernet_info/ethernet_info_text_sensor.h +++ b/esphome/components/ethernet_info/ethernet_info_text_sensor.h @@ -38,6 +38,27 @@ class IPAddressEthernetInfo : public PollingComponent, public text_sensor::TextS std::array ip_sensors_; }; +class DNSAddressEthernetInfo : public PollingComponent, public text_sensor::TextSensor { + public: + void update() override { + auto dns_one = ethernet::global_eth_component->get_dns_address(0); + auto dns_two = ethernet::global_eth_component->get_dns_address(1); + + std::string dns_results = dns_one.str() + " " + dns_two.str(); + + if (dns_results != this->last_results_) { + this->last_results_ = dns_results; + this->publish_state(dns_results); + } + } + float get_setup_priority() const override { return setup_priority::ETHERNET; } + std::string unique_id() override { return get_mac_address() + "-ethernetinfo-dns"; } + void dump_config() override; + + protected: + std::string last_results_; +}; + } // namespace ethernet_info } // namespace esphome diff --git a/esphome/components/ethernet_info/text_sensor.py b/esphome/components/ethernet_info/text_sensor.py index b802c427e8..292673c182 100644 --- a/esphome/components/ethernet_info/text_sensor.py +++ b/esphome/components/ethernet_info/text_sensor.py @@ -3,6 +3,7 @@ import esphome.config_validation as cv from esphome.components import text_sensor from esphome.const import ( CONF_IP_ADDRESS, + CONF_DNS_ADDRESS, ENTITY_CATEGORY_DIAGNOSTIC, ) @@ -10,14 +11,18 @@ DEPENDENCIES = ["ethernet"] ethernet_info_ns = cg.esphome_ns.namespace("ethernet_info") -IPAddressEsthernetInfo = ethernet_info_ns.class_( +IPAddressEthernetInfo = ethernet_info_ns.class_( "IPAddressEthernetInfo", text_sensor.TextSensor, cg.PollingComponent ) +DNSAddressEthernetInfo = ethernet_info_ns.class_( + "DNSAddressEthernetInfo", text_sensor.TextSensor, cg.PollingComponent +) + CONFIG_SCHEMA = cv.Schema( { cv.Optional(CONF_IP_ADDRESS): text_sensor.text_sensor_schema( - IPAddressEsthernetInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + IPAddressEthernetInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC ) .extend(cv.polling_component_schema("1s")) .extend( @@ -27,7 +32,10 @@ CONFIG_SCHEMA = cv.Schema( ) for x in range(5) } - ) + ), + cv.Optional(CONF_DNS_ADDRESS): text_sensor.text_sensor_schema( + DNSAddressEthernetInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), } ) @@ -40,3 +48,6 @@ async def to_code(config): if sensor_conf := conf.get(f"address_{x}"): sens = await text_sensor.new_text_sensor(sensor_conf) cg.add(ip_info.add_ip_sensors(x, sens)) + if conf := config.get(CONF_DNS_ADDRESS): + dns_info = await text_sensor.new_text_sensor(config[CONF_DNS_ADDRESS]) + await cg.register_component(dns_info, config[CONF_DNS_ADDRESS]) diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.h b/esphome/components/wifi_info/wifi_info_text_sensor.h index 6f189da3a3..0f31a57cc5 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.h +++ b/esphome/components/wifi_info/wifi_info_text_sensor.h @@ -39,15 +39,10 @@ class IPAddressWiFiInfo : public PollingComponent, public text_sensor::TextSenso class DNSAddressWifiInfo : public PollingComponent, public text_sensor::TextSensor { public: void update() override { - std::string dns_results; - auto dns_one = wifi::global_wifi_component->get_dns_address(0); auto dns_two = wifi::global_wifi_component->get_dns_address(1); - dns_results += "DNS1: "; - dns_results += dns_one.str(); - dns_results += " DNS2: "; - dns_results += dns_two.str(); + std::string dns_results = dns_one.str() + " " + dns_two.str(); if (dns_results != this->last_results_) { this->last_results_ = dns_results; diff --git a/tests/components/ethernet_info/test.esp32-idf.yaml b/tests/components/ethernet_info/test.esp32-idf.yaml index c5da2bb666..dade4d7ca5 100644 --- a/tests/components/ethernet_info/test.esp32-idf.yaml +++ b/tests/components/ethernet_info/test.esp32-idf.yaml @@ -15,3 +15,5 @@ text_sensor: - platform: ethernet_info ip_address: name: IP Address + dns_address: + name: DNS Address diff --git a/tests/components/ethernet_info/test.esp32.yaml b/tests/components/ethernet_info/test.esp32.yaml index c5da2bb666..dade4d7ca5 100644 --- a/tests/components/ethernet_info/test.esp32.yaml +++ b/tests/components/ethernet_info/test.esp32.yaml @@ -15,3 +15,5 @@ text_sensor: - platform: ethernet_info ip_address: name: IP Address + dns_address: + name: DNS Address From 1ab4fc8faf8de4c1d505522e16256c1e810a6b17 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 12 Apr 2024 04:35:12 -0500 Subject: [PATCH 0067/1106] Add all missing `remote_receiver` `on_...` tests (#6524) --- .../remote_receiver/esp32-common.yaml | 138 +++++++++++++++++- .../remote_receiver/test.esp8266.yaml | 138 +++++++++++++++++- 2 files changed, 272 insertions(+), 4 deletions(-) diff --git a/tests/components/remote_receiver/esp32-common.yaml b/tests/components/remote_receiver/esp32-common.yaml index d1d47661c5..0e71143fc3 100644 --- a/tests/components/remote_receiver/esp32-common.yaml +++ b/tests/components/remote_receiver/esp32-common.yaml @@ -3,12 +3,146 @@ remote_receiver: pin: ${pin} rmt_channel: ${rmt_channel} dump: all + on_abbwelcome: + then: + - logger.log: + format: "on_abbwelcome: %u" + args: ["x.data()[0]"] + on_aeha: + then: + - logger.log: + format: "on_aeha: %u %u" + args: ["x.address", "x.data.front()"] + on_byronsx: + then: + - logger.log: + format: "on_byronsx: %u %u" + args: ["x.address", "x.command"] + on_canalsat: + then: + - logger.log: + format: "on_canalsat: %u %u" + args: ["x.address", "x.command"] + # on_canalsatld: + # then: + # - logger.log: + # format: "on_canalsatld: %u %u" + # args: ["x.address", "x.command"] on_coolix: then: - delay: !lambda "return x.first + x.second;" + - logger.log: + format: "on_coolix: %u %u" + args: ["x.first", "x.second"] + on_dish: + then: + - logger.log: + format: "on_dish: %u %u" + args: ["x.address", "x.command"] + on_dooya: + then: + - logger.log: + format: "on_dooya: %u %u %u" + args: ["x.channel", "x.button", "x.check"] + on_drayton: + then: + - logger.log: + format: "on_drayton: %u %u %u" + args: ["x.address", "x.channel", "x.command"] + on_jvc: + then: + - logger.log: + format: "on_jvc: %u" + args: ["x.data"] + on_keeloq: + then: + - logger.log: + format: "on_keeloq: %u %u %u" + args: ["x.encrypted", "x.address", "x.command"] + on_haier: + then: + - logger.log: + format: "on_haier: %u" + args: ["x.data.front()"] + on_lg: + then: + - logger.log: + format: "on_lg: %u %u" + args: ["x.data", "x.nbits"] + on_magiquest: + then: + - logger.log: + format: "on_magiquest: %u %u" + args: ["x.magnitude", "x.wand_id"] + on_midea: + then: + - logger.log: + format: "on_midea: %u %u" + args: ["x.size()", "x.data()[0]"] + on_nec: + then: + - logger.log: + format: "on_nec: %u %u" + args: ["x.address", "x.command"] + on_nexa: + then: + - logger.log: + format: "on_nexa: %u %u %u %u %u" + args: ["x.device", "x.group", "x.state", "x.channel", "x.level"] + on_panasonic: + then: + - logger.log: + format: "on_panasonic: %u %u" + args: ["x.address", "x.command"] + on_pioneer: + then: + - logger.log: + format: "on_pioneer: %u %u" + args: ["x.rc_code_1", "x.rc_code_2"] + on_pronto: + then: + - logger.log: + format: "on_pronto: %s" + args: ["x.data.c_str()"] + on_raw: + then: + - logger.log: + format: "on_raw: %u" + args: ["x.front()"] + on_rc5: + then: + - logger.log: + format: "on_rc5: %u %u" + args: ["x.address", "x.command"] + on_rc6: + then: + - logger.log: + format: "on_rc6: %u %u" + args: ["x.address", "x.command"] on_rc_switch: then: - delay: !lambda "return uint32_t(x.code) + x.protocol;" + - logger.log: + format: "on_rc_switch: %llu %u" + args: ["x.code", "x.protocol"] + on_samsung: + then: + - logger.log: + format: "on_samsung: %llu %u" + args: ["x.data", "x.nbits"] + on_samsung36: + then: + - logger.log: + format: "on_samsung36: %u %u" + args: ["x.address", "x.command"] + on_sony: + then: + - logger.log: + format: "on_sony: %u %u" + args: ["x.data", "x.nbits"] + on_toshiba_ac: + then: + - logger.log: + format: "on_toshiba_ac: %llu %llu" + args: ["x.rc_code_1", "x.rc_code_2"] binary_sensor: - platform: remote_receiver diff --git a/tests/components/remote_receiver/test.esp8266.yaml b/tests/components/remote_receiver/test.esp8266.yaml index a7c283da1e..e96f031e90 100644 --- a/tests/components/remote_receiver/test.esp8266.yaml +++ b/tests/components/remote_receiver/test.esp8266.yaml @@ -2,12 +2,146 @@ remote_receiver: id: rcvr pin: GPIO5 dump: all + on_abbwelcome: + then: + - logger.log: + format: "on_abbwelcome: %u" + args: ["x.data()[0]"] + on_aeha: + then: + - logger.log: + format: "on_aeha: %u %u" + args: ["x.address", "x.data.front()"] + on_byronsx: + then: + - logger.log: + format: "on_byronsx: %u %u" + args: ["x.address", "x.command"] + on_canalsat: + then: + - logger.log: + format: "on_canalsat: %u %u" + args: ["x.address", "x.command"] + # on_canalsatld: + # then: + # - logger.log: + # format: "on_canalsatld: %u %u" + # args: ["x.address", "x.command"] on_coolix: then: - delay: !lambda "return x.first + x.second;" + - logger.log: + format: "on_coolix: %u %u" + args: ["x.first", "x.second"] + on_dish: + then: + - logger.log: + format: "on_dish: %u %u" + args: ["x.address", "x.command"] + on_dooya: + then: + - logger.log: + format: "on_dooya: %u %u %u" + args: ["x.channel", "x.button", "x.check"] + on_drayton: + then: + - logger.log: + format: "on_drayton: %u %u %u" + args: ["x.address", "x.channel", "x.command"] + on_jvc: + then: + - logger.log: + format: "on_jvc: %u" + args: ["x.data"] + on_keeloq: + then: + - logger.log: + format: "on_keeloq: %u %u %u" + args: ["x.encrypted", "x.address", "x.command"] + on_haier: + then: + - logger.log: + format: "on_haier: %u" + args: ["x.data.front()"] + on_lg: + then: + - logger.log: + format: "on_lg: %u %u" + args: ["x.data", "x.nbits"] + on_magiquest: + then: + - logger.log: + format: "on_magiquest: %u %u" + args: ["x.magnitude", "x.wand_id"] + on_midea: + then: + - logger.log: + format: "on_midea: %u %u" + args: ["x.size()", "x.data()[0]"] + on_nec: + then: + - logger.log: + format: "on_nec: %u %u" + args: ["x.address", "x.command"] + on_nexa: + then: + - logger.log: + format: "on_nexa: %u %u %u %u %u" + args: ["x.device", "x.group", "x.state", "x.channel", "x.level"] + on_panasonic: + then: + - logger.log: + format: "on_panasonic: %u %u" + args: ["x.address", "x.command"] + on_pioneer: + then: + - logger.log: + format: "on_pioneer: %u %u" + args: ["x.rc_code_1", "x.rc_code_2"] + on_pronto: + then: + - logger.log: + format: "on_pronto: %s" + args: ["x.data.c_str()"] + on_raw: + then: + - logger.log: + format: "on_raw: %u" + args: ["x.front()"] + on_rc5: + then: + - logger.log: + format: "on_rc5: %u %u" + args: ["x.address", "x.command"] + on_rc6: + then: + - logger.log: + format: "on_rc6: %u %u" + args: ["x.address", "x.command"] on_rc_switch: then: - delay: !lambda "return uint32_t(x.code) + x.protocol;" + - logger.log: + format: "on_rc_switch: %llu %u" + args: ["x.code", "x.protocol"] + on_samsung: + then: + - logger.log: + format: "on_samsung: %llu %u" + args: ["x.data", "x.nbits"] + on_samsung36: + then: + - logger.log: + format: "on_samsung36: %u %u" + args: ["x.address", "x.command"] + on_sony: + then: + - logger.log: + format: "on_sony: %u %u" + args: ["x.data", "x.nbits"] + on_toshiba_ac: + then: + - logger.log: + format: "on_toshiba_ac: %llu %llu" + args: ["x.rc_code_1", "x.rc_code_2"] binary_sensor: - platform: remote_receiver From 6370e68670f35b82cd02a29f814a75561d1c856b Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 14 Apr 2024 16:38:31 -0500 Subject: [PATCH 0068/1106] Add actions to http_request tests (#6529) --- tests/components/http_request/common.yaml | 37 +++++++++++++++++++ .../http_request/test.esp32-c3.yaml | 9 +---- tests/components/http_request/test.esp32.yaml | 9 +---- .../components/http_request/test.esp8266.yaml | 9 +---- 4 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 tests/components/http_request/common.yaml diff --git a/tests/components/http_request/common.yaml b/tests/components/http_request/common.yaml new file mode 100644 index 0000000000..848fe3f509 --- /dev/null +++ b/tests/components/http_request/common.yaml @@ -0,0 +1,37 @@ +esphome: + on_boot: + then: + - http_request.get: + url: https://esphome.io + headers: + Content-Type: application/json + verify_ssl: false + on_response: + then: + - logger.log: + format: 'Response status: %d, Duration: %u ms' + args: + - status_code + - duration_ms + - http_request.post: + url: https://esphome.io + headers: + Content-Type: application/json + json: + key: value + verify_ssl: false + - http_request.send: + method: PUT + url: https://esphome.io + headers: + Content-Type: application/json + body: "Some data" + verify_ssl: false + +wifi: + ssid: MySSID + password: password1 + +http_request: + useragent: esphome/tagreader + timeout: 10s diff --git a/tests/components/http_request/test.esp32-c3.yaml b/tests/components/http_request/test.esp32-c3.yaml index 19fc6af2c4..25cb37a0b4 100644 --- a/tests/components/http_request/test.esp32-c3.yaml +++ b/tests/components/http_request/test.esp32-c3.yaml @@ -1,7 +1,2 @@ -wifi: - ssid: MySSID - password: password1 - -http_request: - useragent: esphome/tagreader - timeout: 10s +packages: + common: !include common.yaml diff --git a/tests/components/http_request/test.esp32.yaml b/tests/components/http_request/test.esp32.yaml index 19fc6af2c4..25cb37a0b4 100644 --- a/tests/components/http_request/test.esp32.yaml +++ b/tests/components/http_request/test.esp32.yaml @@ -1,7 +1,2 @@ -wifi: - ssid: MySSID - password: password1 - -http_request: - useragent: esphome/tagreader - timeout: 10s +packages: + common: !include common.yaml diff --git a/tests/components/http_request/test.esp8266.yaml b/tests/components/http_request/test.esp8266.yaml index 19fc6af2c4..25cb37a0b4 100644 --- a/tests/components/http_request/test.esp8266.yaml +++ b/tests/components/http_request/test.esp8266.yaml @@ -1,7 +1,2 @@ -wifi: - ssid: MySSID - password: password1 - -http_request: - useragent: esphome/tagreader - timeout: 10s +packages: + common: !include common.yaml From dc200948fa2c779e0c2b2bb4eded62ba347254dc Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:02:19 +1200 Subject: [PATCH 0069/1106] Fix project version longer than 30 characters breaking compilation (#6535) --- esphome/core/base_automation.h | 2 +- esphome/core/config.py | 1 + esphome/core/defines.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index b0ae0aff84..1bf0efb9a4 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -134,7 +134,7 @@ class ProjectUpdateTrigger : public Trigger, public Component { uint32_t hash = fnv1_hash(ESPHOME_PROJECT_NAME); ESPPreferenceObject pref = global_preferences->make_preference(hash, true); char previous_version[30]; - char current_version[30] = ESPHOME_PROJECT_VERSION; + char current_version[30] = ESPHOME_PROJECT_VERSION_30; if (pref.load(&previous_version)) { int cmp = strcmp(previous_version, current_version); if (cmp < 0) { diff --git a/esphome/core/config.py b/esphome/core/config.py index 792f9da6dd..2d87796987 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -394,6 +394,7 @@ async def to_code(config): if project_conf := config.get(CONF_PROJECT): cg.add_define("ESPHOME_PROJECT_NAME", project_conf[CONF_NAME]) cg.add_define("ESPHOME_PROJECT_VERSION", project_conf[CONF_VERSION]) + cg.add_define("ESPHOME_PROJECT_VERSION_30", project_conf[CONF_VERSION][:30]) for conf in project_conf.get(CONF_ON_UPDATE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) await cg.register_component(trigger, conf) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 22153de5de..f13ae968f0 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -11,6 +11,7 @@ #define ESPHOME_BOARD "dummy_board" #define ESPHOME_PROJECT_NAME "dummy project" #define ESPHOME_PROJECT_VERSION "v2" +#define ESPHOME_PROJECT_VERSION_30 "v2" #define ESPHOME_VARIANT "ESP32" // Feature flags From b43ad5da6d91b082290a9fca877c9d182af3185f Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 14 Apr 2024 19:25:10 -0500 Subject: [PATCH 0070/1106] Update homeassistant component tests with actions (#6528) --- tests/components/homeassistant/common.yaml | 67 +++++++++++++++++++ .../components/homeassistant/test.bk72xx.yaml | 2 + .../homeassistant/test.esp32-c3-idf.yaml | 41 +----------- .../homeassistant/test.esp32-c3.yaml | 41 +----------- .../homeassistant/test.esp32-idf.yaml | 41 +----------- .../components/homeassistant/test.esp32.yaml | 41 +----------- .../homeassistant/test.esp8266.yaml | 41 +----------- .../components/homeassistant/test.rp2040.yaml | 41 +----------- 8 files changed, 81 insertions(+), 234 deletions(-) create mode 100644 tests/components/homeassistant/common.yaml create mode 100644 tests/components/homeassistant/test.bk72xx.yaml diff --git a/tests/components/homeassistant/common.yaml b/tests/components/homeassistant/common.yaml new file mode 100644 index 0000000000..ae016a3bea --- /dev/null +++ b/tests/components/homeassistant/common.yaml @@ -0,0 +1,67 @@ +esphome: + on_boot: + then: + - homeassistant.event: + event: esphome.button_pressed + data: + message: Button was pressed + - homeassistant.event: + event: esphome.html5 + data: + message: New Humidity + data_template: + message: The humidity is {{ my_variable }}%. + variables: + my_variable: "return id(ha_hello_world_temperature).state;" + - homeassistant.service: + service: notify.html5 + data: + message: Button was pressed + - homeassistant.service: + service: notify.html5 + data: + title: New Humidity + data_template: + message: The humidity is {{ my_variable }}%. + variables: + my_variable: "return id(ha_hello_world_temperature).state;" + +wifi: + ssid: MySSID + password: password1 + +api: + +binary_sensor: + - platform: homeassistant + entity_id: binary_sensor.hello_world + id: ha_hello_world_binary + - platform: homeassistant + entity_id: binary_sensor.hello + attribute: world + id: ha_hello_world_binary_attribute + +sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world + - platform: homeassistant + entity_id: climate.living_room + attribute: temperature + id: ha_hello_world_temperature + +text_sensor: + - platform: homeassistant + entity_id: sensor.hello_world + id: ha_hello_world_text + - platform: homeassistant + entity_id: sensor.hello_world1 + id: ha_hello_world_text2 + attribute: some_attribute + +time: + - platform: homeassistant + on_time: + - at: "16:00:00" + then: + - logger.log: It's 16:00 diff --git a/tests/components/homeassistant/test.bk72xx.yaml b/tests/components/homeassistant/test.bk72xx.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/homeassistant/test.bk72xx.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/homeassistant/test.esp32-c3-idf.yaml b/tests/components/homeassistant/test.esp32-c3-idf.yaml index d2608f077c..25cb37a0b4 100644 --- a/tests/components/homeassistant/test.esp32-c3-idf.yaml +++ b/tests/components/homeassistant/test.esp32-c3-idf.yaml @@ -1,39 +1,2 @@ -wifi: - ssid: MySSID - password: password1 - -api: - -binary_sensor: - - platform: homeassistant - entity_id: binary_sensor.hello_world - id: ha_hello_world_binary - - platform: homeassistant - entity_id: binary_sensor.hello - attribute: world - id: ha_hello_world_binary_attribute - -sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world - - platform: homeassistant - entity_id: climate.living_room - attribute: temperature - id: ha_hello_world_temperature - -text_sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world_text - - platform: homeassistant - entity_id: sensor.hello_world1 - id: ha_hello_world_text2 - attribute: some_attribute - -time: - - platform: homeassistant - on_time: - - at: "16:00:00" - then: - - logger.log: It's 16:00 +packages: + common: !include common.yaml diff --git a/tests/components/homeassistant/test.esp32-c3.yaml b/tests/components/homeassistant/test.esp32-c3.yaml index d2608f077c..25cb37a0b4 100644 --- a/tests/components/homeassistant/test.esp32-c3.yaml +++ b/tests/components/homeassistant/test.esp32-c3.yaml @@ -1,39 +1,2 @@ -wifi: - ssid: MySSID - password: password1 - -api: - -binary_sensor: - - platform: homeassistant - entity_id: binary_sensor.hello_world - id: ha_hello_world_binary - - platform: homeassistant - entity_id: binary_sensor.hello - attribute: world - id: ha_hello_world_binary_attribute - -sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world - - platform: homeassistant - entity_id: climate.living_room - attribute: temperature - id: ha_hello_world_temperature - -text_sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world_text - - platform: homeassistant - entity_id: sensor.hello_world1 - id: ha_hello_world_text2 - attribute: some_attribute - -time: - - platform: homeassistant - on_time: - - at: "16:00:00" - then: - - logger.log: It's 16:00 +packages: + common: !include common.yaml diff --git a/tests/components/homeassistant/test.esp32-idf.yaml b/tests/components/homeassistant/test.esp32-idf.yaml index d2608f077c..25cb37a0b4 100644 --- a/tests/components/homeassistant/test.esp32-idf.yaml +++ b/tests/components/homeassistant/test.esp32-idf.yaml @@ -1,39 +1,2 @@ -wifi: - ssid: MySSID - password: password1 - -api: - -binary_sensor: - - platform: homeassistant - entity_id: binary_sensor.hello_world - id: ha_hello_world_binary - - platform: homeassistant - entity_id: binary_sensor.hello - attribute: world - id: ha_hello_world_binary_attribute - -sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world - - platform: homeassistant - entity_id: climate.living_room - attribute: temperature - id: ha_hello_world_temperature - -text_sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world_text - - platform: homeassistant - entity_id: sensor.hello_world1 - id: ha_hello_world_text2 - attribute: some_attribute - -time: - - platform: homeassistant - on_time: - - at: "16:00:00" - then: - - logger.log: It's 16:00 +packages: + common: !include common.yaml diff --git a/tests/components/homeassistant/test.esp32.yaml b/tests/components/homeassistant/test.esp32.yaml index d2608f077c..25cb37a0b4 100644 --- a/tests/components/homeassistant/test.esp32.yaml +++ b/tests/components/homeassistant/test.esp32.yaml @@ -1,39 +1,2 @@ -wifi: - ssid: MySSID - password: password1 - -api: - -binary_sensor: - - platform: homeassistant - entity_id: binary_sensor.hello_world - id: ha_hello_world_binary - - platform: homeassistant - entity_id: binary_sensor.hello - attribute: world - id: ha_hello_world_binary_attribute - -sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world - - platform: homeassistant - entity_id: climate.living_room - attribute: temperature - id: ha_hello_world_temperature - -text_sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world_text - - platform: homeassistant - entity_id: sensor.hello_world1 - id: ha_hello_world_text2 - attribute: some_attribute - -time: - - platform: homeassistant - on_time: - - at: "16:00:00" - then: - - logger.log: It's 16:00 +packages: + common: !include common.yaml diff --git a/tests/components/homeassistant/test.esp8266.yaml b/tests/components/homeassistant/test.esp8266.yaml index d2608f077c..25cb37a0b4 100644 --- a/tests/components/homeassistant/test.esp8266.yaml +++ b/tests/components/homeassistant/test.esp8266.yaml @@ -1,39 +1,2 @@ -wifi: - ssid: MySSID - password: password1 - -api: - -binary_sensor: - - platform: homeassistant - entity_id: binary_sensor.hello_world - id: ha_hello_world_binary - - platform: homeassistant - entity_id: binary_sensor.hello - attribute: world - id: ha_hello_world_binary_attribute - -sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world - - platform: homeassistant - entity_id: climate.living_room - attribute: temperature - id: ha_hello_world_temperature - -text_sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world_text - - platform: homeassistant - entity_id: sensor.hello_world1 - id: ha_hello_world_text2 - attribute: some_attribute - -time: - - platform: homeassistant - on_time: - - at: "16:00:00" - then: - - logger.log: It's 16:00 +packages: + common: !include common.yaml diff --git a/tests/components/homeassistant/test.rp2040.yaml b/tests/components/homeassistant/test.rp2040.yaml index d2608f077c..25cb37a0b4 100644 --- a/tests/components/homeassistant/test.rp2040.yaml +++ b/tests/components/homeassistant/test.rp2040.yaml @@ -1,39 +1,2 @@ -wifi: - ssid: MySSID - password: password1 - -api: - -binary_sensor: - - platform: homeassistant - entity_id: binary_sensor.hello_world - id: ha_hello_world_binary - - platform: homeassistant - entity_id: binary_sensor.hello - attribute: world - id: ha_hello_world_binary_attribute - -sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world - - platform: homeassistant - entity_id: climate.living_room - attribute: temperature - id: ha_hello_world_temperature - -text_sensor: - - platform: homeassistant - entity_id: sensor.hello_world - id: ha_hello_world_text - - platform: homeassistant - entity_id: sensor.hello_world1 - id: ha_hello_world_text2 - attribute: some_attribute - -time: - - platform: homeassistant - on_time: - - at: "16:00:00" - then: - - logger.log: It's 16:00 +packages: + common: !include common.yaml From 86f9af13aa1642b58bf2759f2df984e38bc5f11a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:08:35 +1000 Subject: [PATCH 0071/1106] Fix no-release bug on ft6x36 (#6527) --- esphome/components/ft63x6/ft63x6.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/ft63x6/ft63x6.cpp b/esphome/components/ft63x6/ft63x6.cpp index fe64f76fac..e5f7613901 100644 --- a/esphome/components/ft63x6/ft63x6.cpp +++ b/esphome/components/ft63x6/ft63x6.cpp @@ -32,7 +32,7 @@ void FT63X6Touchscreen::setup() { if (this->interrupt_pin_ != nullptr) { this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); this->interrupt_pin_->setup(); - this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); + this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_ANY_EDGE); } if (this->reset_pin_ != nullptr) { @@ -78,13 +78,12 @@ void FT63X6Touchscreen::update_touches() { uint16_t touch_id, x, y; uint8_t touches = this->read_touch_number_(); + ESP_LOGV(TAG, "Touches found: %d", touches); if ((touches == 0x00) || (touches == 0xff)) { // ESP_LOGD(TAG, "No touches detected"); return; } - ESP_LOGV(TAG, "Touches found: %d", touches); - for (auto point = 0; point < touches; point++) { if (((this->read_touch_event_(point)) & 0x01) == 0) { // checking event flag bit 6 if it is null touch_id = this->read_touch_id_(point); // id1 = 0 or 1 From ff0d33ffe32595402f5cbd4bd4b50853ec673a69 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 12 Apr 2024 10:19:49 +1200 Subject: [PATCH 0072/1106] Fix missing ifdefs in voice assistant (#6520) --- esphome/components/voice_assistant/voice_assistant.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 34a26eec01..e68e00948e 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -729,6 +729,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { } void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) { +#ifdef USE_SPEAKER // We should never get to this function if there is no speaker anyway if (this->speaker_buffer_index_ + msg.data.length() < SPEAKER_BUFFER_SIZE) { memcpy(this->speaker_buffer_ + this->speaker_buffer_index_, msg.data.data(), msg.data.length()); this->speaker_buffer_index_ += msg.data.length(); @@ -737,6 +738,7 @@ void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) { } else { ESP_LOGE(TAG, "Cannot receive audio, buffer is full"); } +#endif } VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) From ed02747ebc1d5808d13d7010d20116a758fd1447 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:02:19 +1200 Subject: [PATCH 0073/1106] Fix project version longer than 30 characters breaking compilation (#6535) --- esphome/core/base_automation.h | 2 +- esphome/core/config.py | 1 + esphome/core/defines.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index b0ae0aff84..1bf0efb9a4 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -134,7 +134,7 @@ class ProjectUpdateTrigger : public Trigger, public Component { uint32_t hash = fnv1_hash(ESPHOME_PROJECT_NAME); ESPPreferenceObject pref = global_preferences->make_preference(hash, true); char previous_version[30]; - char current_version[30] = ESPHOME_PROJECT_VERSION; + char current_version[30] = ESPHOME_PROJECT_VERSION_30; if (pref.load(&previous_version)) { int cmp = strcmp(previous_version, current_version); if (cmp < 0) { diff --git a/esphome/core/config.py b/esphome/core/config.py index 792f9da6dd..2d87796987 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -394,6 +394,7 @@ async def to_code(config): if project_conf := config.get(CONF_PROJECT): cg.add_define("ESPHOME_PROJECT_NAME", project_conf[CONF_NAME]) cg.add_define("ESPHOME_PROJECT_VERSION", project_conf[CONF_VERSION]) + cg.add_define("ESPHOME_PROJECT_VERSION_30", project_conf[CONF_VERSION][:30]) for conf in project_conf.get(CONF_ON_UPDATE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) await cg.register_component(trigger, conf) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 22153de5de..f13ae968f0 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -11,6 +11,7 @@ #define ESPHOME_BOARD "dummy_board" #define ESPHOME_PROJECT_NAME "dummy project" #define ESPHOME_PROJECT_VERSION "v2" +#define ESPHOME_PROJECT_VERSION_30 "v2" #define ESPHOME_VARIANT "ESP32" // Feature flags From 09fbddea21356d506f68b638c425818fd84df415 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:08:35 +1000 Subject: [PATCH 0074/1106] Fix no-release bug on ft6x36 (#6527) --- esphome/components/ft63x6/ft63x6.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/ft63x6/ft63x6.cpp b/esphome/components/ft63x6/ft63x6.cpp index fe64f76fac..e5f7613901 100644 --- a/esphome/components/ft63x6/ft63x6.cpp +++ b/esphome/components/ft63x6/ft63x6.cpp @@ -32,7 +32,7 @@ void FT63X6Touchscreen::setup() { if (this->interrupt_pin_ != nullptr) { this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); this->interrupt_pin_->setup(); - this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); + this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_ANY_EDGE); } if (this->reset_pin_ != nullptr) { @@ -78,13 +78,12 @@ void FT63X6Touchscreen::update_touches() { uint16_t touch_id, x, y; uint8_t touches = this->read_touch_number_(); + ESP_LOGV(TAG, "Touches found: %d", touches); if ((touches == 0x00) || (touches == 0xff)) { // ESP_LOGD(TAG, "No touches detected"); return; } - ESP_LOGV(TAG, "Touches found: %d", touches); - for (auto point = 0; point < touches; point++) { if (((this->read_touch_event_(point)) & 0x01) == 0) { // checking event flag bit 6 if it is null touch_id = this->read_touch_id_(point); // id1 = 0 or 1 From b6f1cfd69fbff80979f500d2fdd1fc3beb83dba9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:27:01 +1200 Subject: [PATCH 0075/1106] Bump version to 2024.4.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 08a1a42ea3..66da96bc82 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.4.0b2" +__version__ = "2024.4.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 6876c65edada3710687260c91302ccfa432d4f08 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Mon, 15 Apr 2024 07:13:31 +0200 Subject: [PATCH 0076/1106] Define `USE_PSRAM` (#6526) --- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h | 5 +++-- esphome/components/psram/__init__.py | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 0d986804ce..76dee875c5 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -2,6 +2,7 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" +#include "esphome/core/defines.h" #include "esphome/core/helpers.h" #include @@ -248,11 +249,11 @@ class ESP32BLETracker : public Component, SemaphoreHandle_t scan_result_lock_; SemaphoreHandle_t scan_end_lock_; size_t scan_result_index_{0}; -#if CONFIG_SPIRAM +#ifdef USE_PSRAM const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32; #else const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 16; -#endif // CONFIG_SPIRAM +#endif // USE_PSRAM esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_; esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS}; esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS}; diff --git a/esphome/components/psram/__init__.py b/esphome/components/psram/__init__.py index f7a2ef7b92..796957c315 100644 --- a/esphome/components/psram/__init__.py +++ b/esphome/components/psram/__init__.py @@ -54,5 +54,7 @@ async def to_code(config): if CONF_SPEED in config: add_idf_sdkconfig_option(f"{SPIRAM_SPEEDS[config[CONF_SPEED]]}", True) + cg.add_define("USE_PSRAM") + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) From b3f02e54cdb07e9536413c0933895fc1392f6386 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 08:58:10 +1200 Subject: [PATCH 0077/1106] Bump black from 24.2.0 to 24.4.0 (#6539) 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 8fb59683b4..f94b2a5c51 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ pylint==3.0.3 flake8==7.0.0 # also change in .pre-commit-config.yaml when updating -black==24.2.0 # also change in .pre-commit-config.yaml when updating +black==24.4.0 # also change in .pre-commit-config.yaml when updating pyupgrade==3.15.1 # also change in .pre-commit-config.yaml when updating pre-commit From cca5f818e53fea55df60668455dde3fd3580d0f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 09:39:27 +1200 Subject: [PATCH 0078/1106] Bump peter-evans/create-pull-request from 6.0.2 to 6.0.3 (#6525) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/sync-device-classes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index c12f1f31b5..6d3449b84a 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -36,7 +36,7 @@ jobs: python ./script/sync-device_class.py - name: Commit changes - uses: peter-evans/create-pull-request@v6.0.2 + uses: peter-evans/create-pull-request@v6.0.3 with: commit-message: "Synchronise Device Classes from Home Assistant" committer: esphomebot From 27b286b57f832ed7f3b301f6616e912c0bdd5268 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Apr 2024 09:42:15 +1200 Subject: [PATCH 0079/1106] Bump python version in sync-device-classes workflow to 3.12 for HA (#6541) --- .github/workflows/sync-device-classes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index 6d3449b84a..62b9c7df9b 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -24,7 +24,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5.1.0 with: - python-version: 3.11 + python-version: 3.12 - name: Install Home Assistant run: | From 01419822f73e8784ed7047950c437b5416efb861 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:59:44 +1200 Subject: [PATCH 0080/1106] Bump pylint from 3.0.3 to 3.1.0 (#6287) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/font/__init__.py | 3 +-- esphome/components/time/__init__.py | 2 +- esphome/loader.py | 13 ++++++++++--- requirements_test.txt | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index 76eb05e6ad..b3a5beb199 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -400,8 +400,7 @@ class BitmapFontWrapper: for glyph in glyphs: mask = self.getmask(glyph, mode="1") _, height = mask.size - if height > max_height: - max_height = height + max_height = max(max_height, height) return (max_height, 0) diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index 2bb3a0cd63..c888705ba2 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -49,7 +49,7 @@ def _load_tzdata(iana_key: str) -> Optional[bytes]: package = "tzdata.zoneinfo." + package_loc.replace("/", ".") try: - return resources.read_binary(package, resource) + return (resources.files(package) / resource).read_bytes() except (FileNotFoundError, ModuleNotFoundError): return None diff --git a/esphome/loader.py b/esphome/loader.py index 40a38d0a14..e0457eb425 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -23,7 +23,9 @@ class FileResource: resource: str def path(self) -> ContextManager[Path]: - return importlib.resources.path(self.package, self.resource) + return importlib.resources.as_file( + importlib.resources.files(self.package) / self.resource + ) class ComponentManifest: @@ -101,10 +103,15 @@ class ComponentManifest: loaded .py file (does not look through subdirectories) """ ret = [] - for resource in importlib.resources.contents(self.package): + + for resource in ( + r.name + for r in importlib.resources.files(self.package).iterdir() + if r.is_file() + ): if Path(resource).suffix not in SOURCE_FILE_EXTENSIONS: continue - if not importlib.resources.is_resource(self.package, resource): + if not importlib.resources.files(self.package).joinpath(resource).is_file(): # Not a resource = this is a directory (yeah this is confusing) continue ret.append(FileResource(self.package, resource)) diff --git a/requirements_test.txt b/requirements_test.txt index f94b2a5c51..16e9ec7422 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -pylint==3.0.3 +pylint==3.1.0 flake8==7.0.0 # also change in .pre-commit-config.yaml when updating black==24.4.0 # also change in .pre-commit-config.yaml when updating pyupgrade==3.15.1 # also change in .pre-commit-config.yaml when updating From 80488a2b721727f4bc9615263feb7515ae87728a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 19:34:56 +0000 Subject: [PATCH 0081/1106] Bump aioesphomeapi from 23.2.0 to 24.0.0 (#6544) 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 4abc4d98d2..7424267d08 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ platformio==6.1.13 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 esphome-dashboard==20240412.0 -aioesphomeapi==23.2.0 +aioesphomeapi==24.0.0 zeroconf==0.131.0 python-magic==0.4.27 ruamel.yaml==0.18.6 # dashboard_import From 7d99676fe88858ef29e7bd21c81eb5b1bebb8349 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 19:50:22 +0000 Subject: [PATCH 0082/1106] Bump pyupgrade from 3.15.1 to 3.15.2 (#6543) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- requirements_test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7865c52abd..69aafd4d15 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: - --branch=release - --branch=beta - repo: https://github.com/asottile/pyupgrade - rev: v3.15.1 + rev: v3.15.2 hooks: - id: pyupgrade args: [--py39-plus] diff --git a/requirements_test.txt b/requirements_test.txt index 16e9ec7422..767018d07a 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,7 +1,7 @@ pylint==3.1.0 flake8==7.0.0 # also change in .pre-commit-config.yaml when updating black==24.4.0 # also change in .pre-commit-config.yaml when updating -pyupgrade==3.15.1 # also change in .pre-commit-config.yaml when updating +pyupgrade==3.15.2 # also change in .pre-commit-config.yaml when updating pre-commit # Unit tests From 6a1ea0674469a5c487ee3b83da40e96f6dac792f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 11:04:10 +1200 Subject: [PATCH 0083/1106] Add enum option to typed_schema (#6546) * Add enum option to typed_schema * Assert keys all match --- esphome/config_validation.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 358608cd35..bc58979f33 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1590,6 +1590,10 @@ def typed_schema(schemas, **kwargs): """Create a schema that has a key to distinguish between schemas""" key = kwargs.pop("key", CONF_TYPE) default_schema_option = kwargs.pop("default_type", None) + enum_mapping = kwargs.pop("enum", None) + if enum_mapping is not None: + assert isinstance(enum_mapping, dict) + assert set(enum_mapping.keys()) == set(schemas.keys()) key_validator = one_of(*schemas, **kwargs) def validator(value): @@ -1600,6 +1604,9 @@ def typed_schema(schemas, **kwargs): if schema_option is None: raise Invalid(f"{key} not specified!") key_v = key_validator(schema_option) + if enum_mapping is not None: + key_v = add_class_to_obj(key_v, core.EnumValue) + key_v.enum_value = enum_mapping[key_v] value = Schema(schemas[key_v])(value) value[key] = key_v return value From f2a12589f3619601ade5f698a9c2f75f1d980ba3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 13:08:27 +1200 Subject: [PATCH 0084/1106] Bump version to 2024.4.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 66da96bc82..76320e6c71 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.4.0b3" +__version__ = "2024.4.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 0af26fdfd41671b571755ea906c744f7d1a3fc2c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 14:43:29 +1200 Subject: [PATCH 0085/1106] Move esphome-fork startup script to main repo. (#6523) Co-authored-by: Blair McBride --- .../etc/cont-init.d/30-esphome-fork.sh | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100755 docker/ha-addon-rootfs/etc/cont-init.d/30-esphome-fork.sh diff --git a/docker/ha-addon-rootfs/etc/cont-init.d/30-esphome-fork.sh b/docker/ha-addon-rootfs/etc/cont-init.d/30-esphome-fork.sh new file mode 100755 index 0000000000..03dbb34c8f --- /dev/null +++ b/docker/ha-addon-rootfs/etc/cont-init.d/30-esphome-fork.sh @@ -0,0 +1,47 @@ +#!/usr/bin/with-contenv bashio +# ============================================================================== +# This file installs the user ESPHome fork if specified. +# The fork must be up to date with the latest ESPHome dev branch +# and have no conflicts. +# This config option only exists in the ESPHome Dev add-on. +# ============================================================================== + +declare esphome_fork + +if bashio::config.has_value 'esphome_fork'; then + esphome_fork=$(bashio::config 'esphome_fork') + # format: [username][/repository]:ref + if [[ "$esphome_fork" =~ ^(([^/]+)(/([^:]+))?:)?([^:/]+)$ ]]; then + username="${BASH_REMATCH[2]:-esphome}" + repository="${BASH_REMATCH[4]:-esphome}" + ref="${BASH_REMATCH[5]}" + else + bashio::exit.nok "Invalid esphome_fork format: $esphome_fork" + fi + full_url="https://github.com/${username}/${repository}/archive/${ref}.tar.gz" + bashio::log.info "Checking forked ESPHome" + dev_version=$(python3 -c "from esphome.const import __version__; print(__version__)") + bashio::log.info "Downloading ESPHome from fork '${esphome_fork}' (${full_url})..." + curl -L -o /tmp/esphome.tar.gz "${full_url}" -qq || + bashio::exit.nok "Failed downloading ESPHome fork." + bashio::log.info "Installing ESPHome from fork '${esphome_fork}' (${full_url})..." + rm -rf /esphome || bashio::exit.nok "Failed to remove ESPHome." + mkdir /esphome + tar -zxf /tmp/esphome.tar.gz -C /esphome --strip-components=1 || + bashio::exit.nok "Failed installing ESPHome from fork." + pip install -U -e /esphome || bashio::exit.nok "Failed installing ESPHome from fork." + rm -f /tmp/esphome.tar.gz + fork_version=$(python3 -c "from esphome.const import __version__; print(__version__)") + + if [[ "$fork_version" != "$dev_version" ]]; then + bashio::log.error "############################" + bashio::log.error "Uninstalled fork as version does not match" + bashio::log.error "Update (or ask the author to update) the branch" + bashio::log.error "This is important as the dev addon and the dev ESPHome" + bashio::log.error "branch can have changes that are not compatible with old forks" + bashio::log.error "and get reported as bugs which we cannot solve easily." + bashio::log.error "############################" + bashio::exit.nok + fi + bashio::log.info "Installed ESPHome from fork '${esphome_fork}' (${full_url})..." +fi From ca5d38f413f3b7d723ed376a2f7a0da968c95669 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:32:19 +1200 Subject: [PATCH 0086/1106] Call workflow for addon with dev version (#6549) --- .github/workflows/release.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7c0ec0270b..2e1177d13f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,14 +17,16 @@ jobs: runs-on: ubuntu-latest outputs: tag: ${{ steps.tag.outputs.tag }} + branch_build: ${{ steps.tag.outputs.branch_build }} steps: - uses: actions/checkout@v4.1.1 - name: Get tag id: tag # yamllint disable rule:line-length run: | - if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then - TAG="${GITHUB_REF#refs/tags/}" + if [[ "${{ github.event_name }}" = "release" ]]; then + TAG="${{ github.event.release.tag_name}}" + BRANCH_BUILD="false" else TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p") today="$(date --utc '+%Y%m%d')" @@ -32,9 +34,13 @@ jobs: BRANCH=${GITHUB_REF#refs/heads/} if [[ "$BRANCH" != "dev" ]]; then TAG="${TAG}-${BRANCH}" + BRANCH_BUILD="true" + else + BRANCH_BUILD="false" fi fi echo "tag=${TAG}" >> $GITHUB_OUTPUT + echo "branch_build=${BRANCH_BUILD}" >> $GITHUB_OUTPUT # yamllint enable rule:line-length deploy-pypi: @@ -197,22 +203,28 @@ jobs: $(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *) deploy-ha-addon-repo: - if: github.repository == 'esphome/esphome' && github.event_name == 'release' + if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false' runs-on: ubuntu-latest - needs: [deploy-manifest] + needs: + - init + - deploy-manifest steps: - name: Trigger Workflow uses: actions/github-script@v7.0.1 with: github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }} script: | + let description = "ESPHome"; + if (context.eventName == "release") { + description = ${{ toJSON(github.event.release.body) }}; + } github.rest.actions.createWorkflowDispatch({ owner: "esphome", repo: "home-assistant-addon", workflow_id: "bump-version.yml", ref: "main", inputs: { - version: "${{ github.event.release.tag_name }}", - content: ${{ toJSON(github.event.release.body) }} + version: "${{ needs.init.outputs.tag }}", + content: description } }) From 83feae4eb2b043f63e710acdc2959143cb746fc8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:55:13 +1200 Subject: [PATCH 0087/1106] Use trusted publishing token for pypi (#6545) --- .github/workflows/release.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e1177d13f..72b06ab4fe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,6 +47,9 @@ jobs: name: Build and publish to PyPi if: github.repository == 'esphome/esphome' && github.event_name == 'release' runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - uses: actions/checkout@v4.1.1 - name: Set up Python @@ -56,16 +59,11 @@ jobs: - name: Set up python environment env: ESPHOME_NO_VENV: 1 - run: | - script/setup - pip install twine + run: script/setup - name: Build run: python setup.py sdist bdist_wheel - - name: Upload - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} - run: twine upload dist/* + - name: Publish + uses: pypa/gh-action-pypi-publish@v1.8.14 deploy-docker: name: Build ESPHome ${{ matrix.platform }} From 6104e7591eb02d1e530241998600c89c0a8a2869 Mon Sep 17 00:00:00 2001 From: luar123 <49960470+luar123@users.noreply.github.com> Date: Wed, 17 Apr 2024 06:57:26 +0200 Subject: [PATCH 0088/1106] Fix uart to work with new enum definition in esp-idf-v5.2.1 (#6487) --- esphome/components/logger/logger_esp32.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/esphome/components/logger/logger_esp32.cpp b/esphome/components/logger/logger_esp32.cpp index 740e086f92..b0f1051d34 100644 --- a/esphome/components/logger/logger_esp32.cpp +++ b/esphome/components/logger/logger_esp32.cpp @@ -120,30 +120,28 @@ void Logger::pre_setup() { switch (this->uart_) { case UART_SELECTION_UART0: this->uart_num_ = UART_NUM_0; + init_uart(this->uart_num_, baud_rate_, tx_buffer_size_); break; case UART_SELECTION_UART1: this->uart_num_ = UART_NUM_1; + init_uart(this->uart_num_, baud_rate_, tx_buffer_size_); break; #ifdef USE_ESP32_VARIANT_ESP32 case UART_SELECTION_UART2: this->uart_num_ = UART_NUM_2; + init_uart(this->uart_num_, baud_rate_, tx_buffer_size_); break; #endif #ifdef USE_LOGGER_USB_CDC case UART_SELECTION_USB_CDC: - this->uart_num_ = -1; break; #endif #ifdef USE_LOGGER_USB_SERIAL_JTAG case UART_SELECTION_USB_SERIAL_JTAG: - this->uart_num_ = -1; init_usb_serial_jtag_(); break; #endif } - if (this->uart_num_ >= 0) { - init_uart(this->uart_num_, baud_rate_, tx_buffer_size_); - } #endif // USE_ESP_IDF } From 717cea548f99b0953853378bdaec889d2c0812b4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 17:42:40 +1200 Subject: [PATCH 0089/1106] Housecleaning: Use walrus operator in datetime (#6552) --- esphome/components/datetime/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index b255a27303..a22c60aae9 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -106,8 +106,8 @@ def datetime_schema(class_: MockObjClass) -> cv.Schema: async def setup_datetime_core_(var, config): await setup_entity(var, config) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) for conf in config.get(CONF_ON_VALUE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) From 2fc2d5839fabdd47fdf2154d4d216a9a8adc5b2b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:22:52 +1200 Subject: [PATCH 0090/1106] Housecleaning: Use walrus operator in text (#6560) --- esphome/components/text/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/text/__init__.py b/esphome/components/text/__init__.py index 21c23ce73b..c0140ff082 100644 --- a/esphome/components/text/__init__.py +++ b/esphome/components/text/__init__.py @@ -73,8 +73,8 @@ async def setup_text_core_( trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(cg.std_string, "x")], conf) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) From 3f015562d7109d3c245fcf38ab9bc150390dbce8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:26:36 +1200 Subject: [PATCH 0091/1106] Housecleaning: Use walrus operator in light (#6556) --- esphome/components/light/__init__.py | 34 +++++++++++++--------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index ba3a26ebe5..fdc4676758 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -137,18 +137,16 @@ async def setup_light_core_(light_var, output_var, config): cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE])) - if CONF_DEFAULT_TRANSITION_LENGTH in config: - cg.add( - light_var.set_default_transition_length( - config[CONF_DEFAULT_TRANSITION_LENGTH] - ) - ) - if CONF_FLASH_TRANSITION_LENGTH in config: - cg.add( - light_var.set_flash_transition_length(config[CONF_FLASH_TRANSITION_LENGTH]) - ) - if CONF_GAMMA_CORRECT in config: - cg.add(light_var.set_gamma_correct(config[CONF_GAMMA_CORRECT])) + if ( + default_transition_length := config.get(CONF_DEFAULT_TRANSITION_LENGTH) + ) is not None: + cg.add(light_var.set_default_transition_length(default_transition_length)) + if ( + flash_transition_length := config.get(CONF_FLASH_TRANSITION_LENGTH) + ) is not None: + cg.add(light_var.set_flash_transition_length(flash_transition_length)) + if (gamma_correct := config.get(CONF_GAMMA_CORRECT)) is not None: + cg.add(light_var.set_gamma_correct(gamma_correct)) effects = await cg.build_registry_list( EFFECTS_REGISTRY, config.get(CONF_EFFECTS, []) ) @@ -164,15 +162,15 @@ async def setup_light_core_(light_var, output_var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], light_var) await auto.build_automation(trigger, [], conf) - if CONF_COLOR_CORRECT in config: - cg.add(output_var.set_correction(*config[CONF_COLOR_CORRECT])) + if (color_correct := config.get(CONF_COLOR_CORRECT)) is not None: + cg.add(output_var.set_correction(*color_correct)) - if CONF_POWER_SUPPLY in config: - var_ = await cg.get_variable(config[CONF_POWER_SUPPLY]) + if (power_supply_id := config.get(CONF_POWER_SUPPLY)) is not None: + var_ = await cg.get_variable(power_supply_id) cg.add(output_var.set_power_supply(var_)) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], light_var) + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, light_var) await mqtt.register_mqtt_component(mqtt_, config) From 8eff3435e74194a02aa5b16c2508e996c1c90416 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:26:49 +1200 Subject: [PATCH 0092/1106] Housecleaning: Use walrus operator in select (#6557) --- esphome/components/select/__init__.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index 760f7600b7..7ad14f2440 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -95,8 +95,8 @@ async def setup_select_core_(var, config, *, options: list[str]): trigger, [(cg.std_string, "x"), (cg.size_t, "i")], conf ) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) @@ -223,14 +223,14 @@ async def select_set_index_to_code(config, action_id, template_arg, args): async def select_operation_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) - if CONF_OPERATION in config: - op_ = await cg.templatable(config[CONF_OPERATION], args, SelectOperation) + if (operation := config.get(CONF_OPERATION)) is not None: + op_ = await cg.templatable(operation, args, SelectOperation) cg.add(var.set_operation(op_)) - if CONF_CYCLE in config: - cycle_ = await cg.templatable(config[CONF_CYCLE], args, bool) - cg.add(var.set_cycle(cycle_)) - if CONF_MODE in config: - cg.add(var.set_operation(SELECT_OPERATION_OPTIONS[config[CONF_MODE]])) - if CONF_CYCLE in config: - cg.add(var.set_cycle(config[CONF_CYCLE])) + if (cycle := config.get(CONF_CYCLE)) is not None: + template_ = await cg.templatable(cycle, args, bool) + cg.add(var.set_cycle(template_)) + if (mode := config.get(CONF_MODE)) is not None: + cg.add(var.set_operation(SELECT_OPERATION_OPTIONS[mode])) + if (cycle := config.get(CONF_CYCLE)) is not None: + cg.add(var.set_cycle(cycle)) return var From 21e3faad3876fdbc194b344da4bace6ff01d82d7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:27:04 +1200 Subject: [PATCH 0093/1106] Housecleaning: Use walrus operator in number (#6561) --- esphome/components/number/__init__.py | 39 ++++++++++++++------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index ecc2ab2ee7..6d7ec97c90 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -239,13 +239,14 @@ async def setup_number_core_( cg.add(trigger.set_max(template_)) await automation.build_automation(trigger, [(float, "x")], conf) - if CONF_UNIT_OF_MEASUREMENT in config: - cg.add(var.traits.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT])) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if (unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)) is not None: + cg.add(var.traits.set_unit_of_measurement(unit_of_measurement)) + if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: + cg.add(var.traits.set_device_class(device_class)) + + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if CONF_DEVICE_CLASS in config: - cg.add(var.traits.set_device_class(config[CONF_DEVICE_CLASS])) async def register_number( @@ -284,10 +285,10 @@ async def number_in_range_to_code(config, condition_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(condition_id, template_arg, paren) - if CONF_ABOVE in config: - cg.add(var.set_min(config[CONF_ABOVE])) - if CONF_BELOW in config: - cg.add(var.set_max(config[CONF_BELOW])) + if (above := config.get(CONF_ABOVE)) is not None: + cg.add(var.set_min(above)) + if (below := config.get(CONF_BELOW)) is not None: + cg.add(var.set_max(below)) return var @@ -391,14 +392,14 @@ async def number_set_to_code(config, action_id, template_arg, args): async def number_to_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) - if CONF_OPERATION in config: - to_ = await cg.templatable(config[CONF_OPERATION], args, NumberOperation) + if (operation := config.get(CONF_OPERATION)) is not None: + to_ = await cg.templatable(operation, args, NumberOperation) cg.add(var.set_operation(to_)) - if CONF_CYCLE in config: - cycle_ = await cg.templatable(config[CONF_CYCLE], args, bool) - cg.add(var.set_cycle(cycle_)) - if CONF_MODE in config: - cg.add(var.set_operation(NUMBER_OPERATION_OPTIONS[config[CONF_MODE]])) - if CONF_CYCLE in config: - cg.add(var.set_cycle(config[CONF_CYCLE])) + if (cycle := config.get(CONF_CYCLE)) is not None: + template_ = await cg.templatable(cycle, args, bool) + cg.add(var.set_cycle(template_)) + if (mode := config.get(CONF_MODE)) is not None: + cg.add(var.set_operation(NUMBER_OPERATION_OPTIONS[mode])) + if (cycle := config.get(CONF_CYCLE)) is not None: + cg.add(var.set_cycle(cycle)) return var From fa1adf752816555e28fc4a055161430730d26c8a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:28:01 +1200 Subject: [PATCH 0094/1106] Housecleaning: Use walrus operator in cover (#6562) --- esphome/components/cover/__init__.py | 48 +++++++++++++--------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index 90e5ee1f03..8e0371017d 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -122,8 +122,8 @@ COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ex async def setup_cover_core_(var, config): await setup_entity(var, config) - if CONF_DEVICE_CLASS in config: - cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) + if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: + cg.add(var.set_device_class(device_class)) for conf in config.get(CONF_ON_OPEN, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) @@ -132,24 +132,20 @@ async def setup_cover_core_(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if CONF_POSITION_STATE_TOPIC in config: - cg.add( - mqtt_.set_custom_position_state_topic(config[CONF_POSITION_STATE_TOPIC]) - ) - if CONF_POSITION_COMMAND_TOPIC in config: - cg.add( - mqtt_.set_custom_position_command_topic( - config[CONF_POSITION_COMMAND_TOPIC] - ) - ) - if CONF_TILT_STATE_TOPIC in config: - cg.add(mqtt_.set_custom_tilt_state_topic(config[CONF_TILT_STATE_TOPIC])) - if CONF_TILT_COMMAND_TOPIC in config: - cg.add(mqtt_.set_custom_tilt_command_topic(config[CONF_TILT_COMMAND_TOPIC])) + if (position_state_topic := config.get(CONF_POSITION_STATE_TOPIC)) is not None: + cg.add(mqtt_.set_custom_position_state_topic(position_state_topic)) + if ( + position_command_topic := config.get(CONF_POSITION_COMMAND_TOPIC) + ) is not None: + cg.add(mqtt_.set_custom_position_command_topic(position_command_topic)) + if (tilt_state_topic := config.get(CONF_TILT_STATE_TOPIC)) is not None: + cg.add(mqtt_.set_custom_tilt_state_topic(tilt_state_topic)) + if (tilt_command_topic := config.get(CONF_TILT_COMMAND_TOPIC)) is not None: + cg.add(mqtt_.set_custom_tilt_command_topic(tilt_command_topic)) async def register_cover(var, config): @@ -205,17 +201,17 @@ COVER_CONTROL_ACTION_SCHEMA = cv.Schema( async def cover_control_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) - if CONF_STOP in config: - template_ = await cg.templatable(config[CONF_STOP], args, bool) + if (stop := config.get(CONF_STOP)) is not None: + template_ = await cg.templatable(stop, args, bool) cg.add(var.set_stop(template_)) - if CONF_STATE in config: - template_ = await cg.templatable(config[CONF_STATE], args, float) + if (state := config.get(CONF_STATE)) is not None: + template_ = await cg.templatable(state, args, float) cg.add(var.set_position(template_)) - if CONF_POSITION in config: - template_ = await cg.templatable(config[CONF_POSITION], args, float) + if (position := config.get(CONF_POSITION)) is not None: + template_ = await cg.templatable(position, args, float) cg.add(var.set_position(template_)) - if CONF_TILT in config: - template_ = await cg.templatable(config[CONF_TILT], args, float) + if (tilt := config.get(CONF_TILT)) is not None: + template_ = await cg.templatable(tilt, args, float) cg.add(var.set_tilt(template_)) return var From 77ade12ee9e3a62b708fad712d90e9364a9cedfa Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:58:20 +1200 Subject: [PATCH 0095/1106] Housecleaning: Use walrus operator in climate (#6551) --- esphome/components/climate/__init__.py | 218 +++++++++++++------------ 1 file changed, 118 insertions(+), 100 deletions(-) diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index c9c3900a0c..7b0a27feae 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -244,122 +244,150 @@ async def setup_climate_core_(var, config): await setup_entity(var, config) visual = config[CONF_VISUAL] - if CONF_MIN_TEMPERATURE in visual: - cg.add(var.set_visual_min_temperature_override(visual[CONF_MIN_TEMPERATURE])) - if CONF_MAX_TEMPERATURE in visual: - cg.add(var.set_visual_max_temperature_override(visual[CONF_MAX_TEMPERATURE])) - if CONF_TEMPERATURE_STEP in visual: + if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None: + cg.add(var.set_visual_min_temperature_override(min_temp)) + if (max_temp := visual.get(CONF_MAX_TEMPERATURE)) is not None: + cg.add(var.set_visual_max_temperature_override(max_temp)) + if (temp_step := visual.get(CONF_TEMPERATURE_STEP)) is not None: cg.add( var.set_visual_temperature_step_override( - visual[CONF_TEMPERATURE_STEP][CONF_TARGET_TEMPERATURE], - visual[CONF_TEMPERATURE_STEP][CONF_CURRENT_TEMPERATURE], + temp_step[CONF_TARGET_TEMPERATURE], + temp_step[CONF_CURRENT_TEMPERATURE], ) ) - if CONF_MIN_HUMIDITY in visual: - cg.add(var.set_visual_min_humidity_override(visual[CONF_MIN_HUMIDITY])) - if CONF_MAX_HUMIDITY in visual: - cg.add(var.set_visual_max_humidity_override(visual[CONF_MAX_HUMIDITY])) + if (min_humidity := visual.get(CONF_MIN_HUMIDITY)) is not None: + cg.add(var.set_visual_min_humidity_override(min_humidity)) + if (max_humidity := visual.get(CONF_MAX_HUMIDITY)) is not None: + cg.add(var.set_visual_max_humidity_override(max_humidity)) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if CONF_ACTION_STATE_TOPIC in config: - cg.add(mqtt_.set_custom_action_state_topic(config[CONF_ACTION_STATE_TOPIC])) - if CONF_AWAY_COMMAND_TOPIC in config: - cg.add(mqtt_.set_custom_away_command_topic(config[CONF_AWAY_COMMAND_TOPIC])) - if CONF_AWAY_STATE_TOPIC in config: - cg.add(mqtt_.set_custom_away_state_topic(config[CONF_AWAY_STATE_TOPIC])) - if CONF_CURRENT_TEMPERATURE_STATE_TOPIC in config: + if (action_state_topic := config.get(CONF_ACTION_STATE_TOPIC)) is not None: + cg.add(mqtt_.set_custom_action_state_topic(action_state_topic)) + if (away_command_topic := config.get(CONF_AWAY_COMMAND_TOPIC)) is not None: + cg.add(mqtt_.set_custom_away_command_topic(away_command_topic)) + if (away_state_topic := config.get(CONF_AWAY_STATE_TOPIC)) is not None: + cg.add(mqtt_.set_custom_away_state_topic(away_state_topic)) + if ( + current_temperature_state_topic := config.get( + CONF_CURRENT_TEMPERATURE_STATE_TOPIC + ) + ) is not None: cg.add( mqtt_.set_custom_current_temperature_state_topic( - config[CONF_CURRENT_TEMPERATURE_STATE_TOPIC] + current_temperature_state_topic ) ) - if CONF_CURRENT_HUMIDITY_STATE_TOPIC in config: + if ( + current_humidity_state_topic := config.get( + CONF_CURRENT_HUMIDITY_STATE_TOPIC + ) + ) is not None: cg.add( mqtt_.set_custom_current_humidity_state_topic( - config[CONF_CURRENT_HUMIDITY_STATE_TOPIC] + current_humidity_state_topic ) ) - if CONF_FAN_MODE_COMMAND_TOPIC in config: - cg.add( - mqtt_.set_custom_fan_mode_command_topic( - config[CONF_FAN_MODE_COMMAND_TOPIC] - ) + if ( + fan_mode_command_topic := config.get(CONF_FAN_MODE_COMMAND_TOPIC) + ) is not None: + cg.add(mqtt_.set_custom_fan_mode_command_topic(fan_mode_command_topic)) + if (fan_mode_state_topic := config.get(CONF_FAN_MODE_STATE_TOPIC)) is not None: + cg.add(mqtt_.set_custom_fan_mode_state_topic(fan_mode_state_topic)) + if (mode_command_topic := config.get(CONF_MODE_COMMAND_TOPIC)) is not None: + cg.add(mqtt_.set_custom_mode_command_topic(mode_command_topic)) + if (mode_state_topic := config.get(CONF_MODE_STATE_TOPIC)) is not None: + cg.add(mqtt_.set_custom_mode_state_topic(mode_state_topic)) + if (preset_command_topic := config.get(CONF_PRESET_COMMAND_TOPIC)) is not None: + cg.add(mqtt_.set_custom_preset_command_topic(preset_command_topic)) + if (preset_state_topic := config.get(CONF_PRESET_STATE_TOPIC)) is not None: + cg.add(mqtt_.set_custom_preset_state_topic(preset_state_topic)) + if ( + swing_mode_command_topic := config.get(CONF_SWING_MODE_COMMAND_TOPIC) + ) is not None: + cg.add(mqtt_.set_custom_swing_mode_command_topic(swing_mode_command_topic)) + if ( + swing_mode_state_topic := config.get(CONF_SWING_MODE_STATE_TOPIC) + ) is not None: + cg.add(mqtt_.set_custom_swing_mode_state_topic(swing_mode_state_topic)) + if ( + target_temperature_command_topic := config.get( + CONF_TARGET_TEMPERATURE_COMMAND_TOPIC ) - if CONF_FAN_MODE_STATE_TOPIC in config: - cg.add( - mqtt_.set_custom_fan_mode_state_topic(config[CONF_FAN_MODE_STATE_TOPIC]) - ) - if CONF_MODE_COMMAND_TOPIC in config: - cg.add(mqtt_.set_custom_mode_command_topic(config[CONF_MODE_COMMAND_TOPIC])) - if CONF_MODE_STATE_TOPIC in config: - cg.add(mqtt_.set_custom_mode_state_topic(config[CONF_MODE_STATE_TOPIC])) - if CONF_PRESET_COMMAND_TOPIC in config: - cg.add( - mqtt_.set_custom_preset_command_topic(config[CONF_PRESET_COMMAND_TOPIC]) - ) - if CONF_PRESET_STATE_TOPIC in config: - cg.add(mqtt_.set_custom_preset_state_topic(config[CONF_PRESET_STATE_TOPIC])) - if CONF_SWING_MODE_COMMAND_TOPIC in config: - cg.add( - mqtt_.set_custom_swing_mode_command_topic( - config[CONF_SWING_MODE_COMMAND_TOPIC] - ) - ) - if CONF_SWING_MODE_STATE_TOPIC in config: - cg.add( - mqtt_.set_custom_swing_mode_state_topic( - config[CONF_SWING_MODE_STATE_TOPIC] - ) - ) - if CONF_TARGET_TEMPERATURE_COMMAND_TOPIC in config: + ) is not None: cg.add( mqtt_.set_custom_target_temperature_command_topic( - config[CONF_TARGET_TEMPERATURE_COMMAND_TOPIC] + target_temperature_command_topic ) ) - if CONF_TARGET_TEMPERATURE_STATE_TOPIC in config: + if ( + target_temperature_state_topic := config.get( + CONF_TARGET_TEMPERATURE_STATE_TOPIC + ) + ) is not None: cg.add( mqtt_.set_custom_target_temperature_state_topic( - config[CONF_TARGET_TEMPERATURE_STATE_TOPIC] + target_temperature_state_topic ) ) - if CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC in config: + if ( + target_temperature_high_command_topic := config.get( + CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC + ) + ) is not None: cg.add( mqtt_.set_custom_target_temperature_high_command_topic( - config[CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC] + target_temperature_high_command_topic ) ) - if CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC in config: + if ( + target_temperature_high_state_topic := config.get( + CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC + ) + ) is not None: cg.add( mqtt_.set_custom_target_temperature_high_state_topic( - config[CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC] + target_temperature_high_state_topic ) ) - if CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC in config: + if ( + target_temperature_low_command_topic := config.get( + CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC + ) + ) is not None: cg.add( mqtt_.set_custom_target_temperature_low_command_topic( - config[CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC] + target_temperature_low_command_topic ) ) - if CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC in config: + if ( + target_temperature_low_state_topic := config.get( + CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC + ) + ) is not None: cg.add( mqtt_.set_custom_target_temperature_state_topic( - config[CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC] + target_temperature_low_state_topic ) ) - if CONF_TARGET_HUMIDITY_COMMAND_TOPIC in config: + if ( + target_humidity_command_topic := config.get( + CONF_TARGET_HUMIDITY_COMMAND_TOPIC + ) + ) is not None: cg.add( mqtt_.set_custom_target_humidity_command_topic( - config[CONF_TARGET_HUMIDITY_COMMAND_TOPIC] + target_humidity_command_topic ) ) - if CONF_TARGET_HUMIDITY_STATE_TOPIC in config: + if ( + target_humidity_state_topic := config.get(CONF_TARGET_HUMIDITY_STATE_TOPIC) + ) is not None: cg.add( mqtt_.set_custom_target_humidity_state_topic( - config[CONF_TARGET_HUMIDITY_STATE_TOPIC] + target_humidity_state_topic ) ) @@ -411,45 +439,35 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema( async def climate_control_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) - if CONF_MODE in config: - template_ = await cg.templatable(config[CONF_MODE], args, ClimateMode) + if (mode := config.get(CONF_MODE)) is not None: + template_ = await cg.templatable(mode, args, ClimateMode) cg.add(var.set_mode(template_)) - if CONF_TARGET_TEMPERATURE in config: - template_ = await cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float) + if (target_temp := config.get(CONF_TARGET_TEMPERATURE)) is not None: + template_ = await cg.templatable(target_temp, args, float) cg.add(var.set_target_temperature(template_)) - if CONF_TARGET_TEMPERATURE_LOW in config: - template_ = await cg.templatable( - config[CONF_TARGET_TEMPERATURE_LOW], args, float - ) + if (target_temp_low := config.get(CONF_TARGET_TEMPERATURE_LOW)) is not None: + template_ = await cg.templatable(target_temp_low, args, float) cg.add(var.set_target_temperature_low(template_)) - if CONF_TARGET_TEMPERATURE_HIGH in config: - template_ = await cg.templatable( - config[CONF_TARGET_TEMPERATURE_HIGH], args, float - ) + if (target_temp_high := config.get(CONF_TARGET_TEMPERATURE_HIGH)) is not None: + template_ = await cg.templatable(target_temp_high, args, float) cg.add(var.set_target_temperature_high(template_)) - if CONF_TARGET_HUMIDITY in config: - template_ = await cg.templatable(config[CONF_TARGET_HUMIDITY], args, float) + if (target_humidity := config.get(CONF_TARGET_HUMIDITY)) is not None: + template_ = await cg.templatable(target_humidity, args, float) cg.add(var.set_target_humidity(template_)) - if CONF_FAN_MODE in config: - template_ = await cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode) + if (fan_mode := config.get(CONF_FAN_MODE)) is not None: + template_ = await cg.templatable(fan_mode, args, ClimateFanMode) cg.add(var.set_fan_mode(template_)) - if CONF_CUSTOM_FAN_MODE in config: - template_ = await cg.templatable( - config[CONF_CUSTOM_FAN_MODE], args, cg.std_string - ) + if (custom_fan_mode := config.get(CONF_CUSTOM_FAN_MODE)) is not None: + template_ = await cg.templatable(custom_fan_mode, args, cg.std_string) cg.add(var.set_custom_fan_mode(template_)) - if CONF_PRESET in config: - template_ = await cg.templatable(config[CONF_PRESET], args, ClimatePreset) + if (preset := config.get(CONF_PRESET)) is not None: + template_ = await cg.templatable(preset, args, ClimatePreset) cg.add(var.set_preset(template_)) - if CONF_CUSTOM_PRESET in config: - template_ = await cg.templatable( - config[CONF_CUSTOM_PRESET], args, cg.std_string - ) + if (custom_preset := config.get(CONF_CUSTOM_PRESET)) is not None: + template_ = await cg.templatable(custom_preset, args, cg.std_string) cg.add(var.set_custom_preset(template_)) - if CONF_SWING_MODE in config: - template_ = await cg.templatable( - config[CONF_SWING_MODE], args, ClimateSwingMode - ) + if (swing_mode := config.get(CONF_SWING_MODE)) is not None: + template_ = await cg.templatable(swing_mode, args, ClimateSwingMode) cg.add(var.set_swing_mode(template_)) return var From 214c237c8dc978970fcff7cf958043c3abe36ef2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:03:57 +1200 Subject: [PATCH 0096/1106] Housecleaning: Use walrus operator in fan (#6555) Co-authored-by: Keith Burzinski --- esphome/components/fan/__init__.py | 62 ++++++++++++++---------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 05e276d987..14cf6cc9c9 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -180,40 +180,34 @@ async def setup_fan_core_(var, config): cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if CONF_OSCILLATION_STATE_TOPIC in config: + if ( + oscillation_state_topic := config.get(CONF_OSCILLATION_STATE_TOPIC) + ) is not None: + cg.add(mqtt_.set_custom_oscillation_state_topic(oscillation_state_topic)) + if ( + oscillation_command_topic := config.get(CONF_OSCILLATION_COMMAND_TOPIC) + ) is not None: cg.add( - mqtt_.set_custom_oscillation_state_topic( - config[CONF_OSCILLATION_STATE_TOPIC] - ) + mqtt_.set_custom_oscillation_command_topic(oscillation_command_topic) ) - if CONF_OSCILLATION_COMMAND_TOPIC in config: + if ( + speed_level_state_topic := config.get(CONF_SPEED_LEVEL_STATE_TOPIC) + ) is not None: + cg.add(mqtt_.set_custom_speed_level_state_topic(speed_level_state_topic)) + if ( + speed_level_command_topic := config.get(CONF_SPEED_LEVEL_COMMAND_TOPIC) + ) is not None: cg.add( - mqtt_.set_custom_oscillation_command_topic( - config[CONF_OSCILLATION_COMMAND_TOPIC] - ) - ) - if CONF_SPEED_LEVEL_STATE_TOPIC in config: - cg.add( - mqtt_.set_custom_speed_level_state_topic( - config[CONF_SPEED_LEVEL_STATE_TOPIC] - ) - ) - if CONF_SPEED_LEVEL_COMMAND_TOPIC in config: - cg.add( - mqtt_.set_custom_speed_level_command_topic( - config[CONF_SPEED_LEVEL_COMMAND_TOPIC] - ) - ) - if CONF_SPEED_STATE_TOPIC in config: - cg.add(mqtt_.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC])) - if CONF_SPEED_COMMAND_TOPIC in config: - cg.add( - mqtt_.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]) + mqtt_.set_custom_speed_level_command_topic(speed_level_command_topic) ) + if (speed_state_topic := config.get(CONF_SPEED_STATE_TOPIC)) is not None: + cg.add(mqtt_.set_custom_speed_state_topic(speed_state_topic)) + if (speed_command_topic := config.get(CONF_SPEED_COMMAND_TOPIC)) is not None: + cg.add(mqtt_.set_custom_speed_command_topic(speed_command_topic)) for conf in config.get(CONF_ON_STATE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) @@ -288,14 +282,14 @@ async def fan_turn_off_to_code(config, action_id, template_arg, args): async def fan_turn_on_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) - if CONF_OSCILLATING in config: - template_ = await cg.templatable(config[CONF_OSCILLATING], args, bool) + if (oscillating := config.get(CONF_OSCILLATING)) is not None: + template_ = await cg.templatable(oscillating, args, bool) cg.add(var.set_oscillating(template_)) - if CONF_SPEED in config: - template_ = await cg.templatable(config[CONF_SPEED], args, int) + if (speed := config.get(CONF_SPEED)) is not None: + template_ = await cg.templatable(speed, args, int) cg.add(var.set_speed(template_)) - if CONF_DIRECTION in config: - template_ = await cg.templatable(config[CONF_DIRECTION], args, FanDirection) + if (direction := config.get(CONF_DIRECTION)) is not None: + template_ = await cg.templatable(direction, args, FanDirection) cg.add(var.set_direction(template_)) return var From 7733781e091199d3638e1fa78a181eefff4b2b19 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:51:33 +1200 Subject: [PATCH 0097/1106] Housecleaning: Use walrus operator in text_sensor (#6559) --- esphome/components/text_sensor/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index cf7d23aec7..6c28b57b3d 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -186,8 +186,8 @@ async def build_filters(config): async def setup_text_sensor_core_(var, config): await setup_entity(var, config) - if CONF_DEVICE_CLASS in config: - cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) + if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: + cg.add(var.set_device_class(device_class)) if config.get(CONF_FILTERS): # must exist and not be empty filters = await build_filters(config[CONF_FILTERS]) @@ -201,8 +201,8 @@ async def setup_text_sensor_core_(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(cg.std_string, "x")], conf) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) From 987ffcbaba01be4642c36b68fc14e02bae553103 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 17 Apr 2024 16:09:42 -0500 Subject: [PATCH 0098/1106] Bump zeroconf to 0.132.2 (#6548) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7424267d08..68041675f6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ esptool==4.7.0 click==8.1.7 esphome-dashboard==20240412.0 aioesphomeapi==24.0.0 -zeroconf==0.131.0 +zeroconf==0.132.2 python-magic==0.4.27 ruamel.yaml==0.18.6 # dashboard_import From 51ed6d62d928ec66496bb972ec3c1be36c6f8bb3 Mon Sep 17 00:00:00 2001 From: zry98 Date: Wed, 17 Apr 2024 23:31:20 +0200 Subject: [PATCH 0099/1106] [Tuya Climate] Fix compilation error caused by codegen (#6568) --- esphome/components/tuya/climate/__init__.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/esphome/components/tuya/climate/__init__.py b/esphome/components/tuya/climate/__init__.py index b3d401e5a4..56eb377ed7 100644 --- a/esphome/components/tuya/climate/__init__.py +++ b/esphome/components/tuya/climate/__init__.py @@ -208,7 +208,7 @@ async def to_code(config): cg.add(var.set_switch_id(switch_datapoint)) if active_state_config := config.get(CONF_ACTIVE_STATE): - cg.add(var.set_active_state_id(CONF_DATAPOINT)) + cg.add(var.set_active_state_id(active_state_config.get(CONF_DATAPOINT))) if (heating_value := active_state_config.get(CONF_HEATING_VALUE)) is not None: cg.add(var.set_active_state_heating_value(heating_value)) if (cooling_value := active_state_config.get(CONF_COOLING_VALUE)) is not None: @@ -219,14 +219,10 @@ async def to_code(config): cg.add(var.set_active_state_fanonly_value(fanonly_value)) else: if heating_state_pin_config := config.get(CONF_HEATING_STATE_PIN): - heating_state_pin = await cg.gpio_pin_expression( - config(heating_state_pin_config) - ) + heating_state_pin = await cg.gpio_pin_expression(heating_state_pin_config) cg.add(var.set_heating_state_pin(heating_state_pin)) if cooling_state_pin_config := config.get(CONF_COOLING_STATE_PIN): - cooling_state_pin = await cg.gpio_pin_expression( - config(cooling_state_pin_config) - ) + cooling_state_pin = await cg.gpio_pin_expression(cooling_state_pin_config) cg.add(var.set_cooling_state_pin(cooling_state_pin)) if target_temperature_datapoint := config.get(CONF_TARGET_TEMPERATURE_DATAPOINT): @@ -254,11 +250,11 @@ async def to_code(config): if preset_config := config.get(CONF_PRESET, {}): if eco_config := preset_config.get(CONF_ECO, {}): - cg.add(var.set_eco_id(CONF_DATAPOINT)) + cg.add(var.set_eco_id(eco_config.get(CONF_DATAPOINT))) if eco_temperature := eco_config.get(CONF_TEMPERATURE): cg.add(var.set_eco_temperature(eco_temperature)) - if CONF_SLEEP in preset_config: - cg.add(var.set_sleep_id(CONF_DATAPOINT)) + if sleep_config := preset_config.get(CONF_SLEEP, {}): + cg.add(var.set_sleep_id(sleep_config.get(CONF_DATAPOINT))) if swing_mode_config := config.get(CONF_SWING_MODE): if swing_vertical_datapoint := swing_mode_config.get(CONF_VERTICAL_DATAPOINT): @@ -268,7 +264,7 @@ async def to_code(config): ): cg.add(var.set_swing_horizontal_id(swing_horizontal_datapoint)) if fan_mode_config := config.get(CONF_FAN_MODE): - cg.add(var.set_fan_speed_id(CONF_DATAPOINT)) + cg.add(var.set_fan_speed_id(fan_mode_config.get(CONF_DATAPOINT))) if (fan_auto_value := fan_mode_config.get(CONF_AUTO_VALUE)) is not None: cg.add(var.set_fan_speed_auto_value(fan_auto_value)) if (fan_low_value := fan_mode_config.get(CONF_LOW_VALUE)) is not None: From c8cdb30459146c9e1e854098328d7cdf817df448 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:43:00 +1200 Subject: [PATCH 0100/1106] Housecleaning: Use walrus operator in switch (#6558) --- esphome/components/switch/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index 21cbe3dfe4..e997ec7ca5 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -138,8 +138,8 @@ SWITCH_SCHEMA = switch_schema() # for compatibility async def setup_switch_core_(var, config): await setup_entity(var, config) - if CONF_INVERTED in config: - cg.add(var.set_inverted(config[CONF_INVERTED])) + if (inverted := config.get(CONF_INVERTED)) is not None: + cg.add(var.set_inverted(inverted)) for conf in config.get(CONF_ON_TURN_ON, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) @@ -147,12 +147,12 @@ async def setup_switch_core_(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if CONF_DEVICE_CLASS in config: - cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) + if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: + cg.add(var.set_device_class(device_class)) cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) From 72c1c3f0910ff12448857574f2386ff72de3682b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:43:11 +1200 Subject: [PATCH 0101/1106] Housecleaning: Use walrus operator in lock (#6554) Co-authored-by: Keith Burzinski --- esphome/components/lock/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/lock/__init__.py b/esphome/components/lock/__init__.py index f659c48a6e..457ffa278a 100644 --- a/esphome/components/lock/__init__.py +++ b/esphome/components/lock/__init__.py @@ -57,8 +57,8 @@ async def setup_lock_core_(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if mqtt_id := config.get(CONF_MQTT_ID): + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) From 4559e963b340e2dae8a0edd3bc9893b0d4e140d0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:43:18 +1200 Subject: [PATCH 0102/1106] Housecleaning: Use walrus operator in sensor (#6553) Co-authored-by: Keith Burzinski --- esphome/components/sensor/__init__.py | 42 +++++++++++++-------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 46295fe958..ece232e1a6 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -732,14 +732,14 @@ async def build_filters(config): async def setup_sensor_core_(var, config): await setup_entity(var, config) - if CONF_DEVICE_CLASS in config: - cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) - if CONF_STATE_CLASS in config: - cg.add(var.set_state_class(config[CONF_STATE_CLASS])) - if CONF_UNIT_OF_MEASUREMENT in config: - cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT])) - if CONF_ACCURACY_DECIMALS in config: - cg.add(var.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS])) + if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: + cg.add(var.set_device_class(device_class)) + if (state_class := config.get(CONF_STATE_CLASS)) is not None: + cg.add(var.set_state_class(state_class)) + if (unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)) is not None: + cg.add(var.set_unit_of_measurement(unit_of_measurement)) + if (accuracy_decimals := config.get(CONF_ACCURACY_DECIMALS)) is not None: + cg.add(var.set_accuracy_decimals(accuracy_decimals)) cg.add(var.set_force_update(config[CONF_FORCE_UPDATE])) if config.get(CONF_FILTERS): # must exist and not be empty filters = await build_filters(config[CONF_FILTERS]) @@ -754,23 +754,23 @@ async def setup_sensor_core_(var, config): for conf in config.get(CONF_ON_VALUE_RANGE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await cg.register_component(trigger, conf) - if CONF_ABOVE in conf: - template_ = await cg.templatable(conf[CONF_ABOVE], [(float, "x")], float) + if (above := conf.get(CONF_ABOVE)) is not None: + template_ = await cg.templatable(above, [(float, "x")], float) cg.add(trigger.set_min(template_)) - if CONF_BELOW in conf: - template_ = await cg.templatable(conf[CONF_BELOW], [(float, "x")], float) + if (below := conf.get(CONF_BELOW)) is not None: + template_ = await cg.templatable(below, [(float, "x")], float) cg.add(trigger.set_max(template_)) await automation.build_automation(trigger, [(float, "x")], conf) - if CONF_MQTT_ID in config: - mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if CONF_EXPIRE_AFTER in config: - if config[CONF_EXPIRE_AFTER] is None: + if (expire_after := config.get(CONF_EXPIRE_AFTER, _UNDEF)) is not _UNDEF: + if expire_after is None: cg.add(mqtt_.disable_expire_after()) else: - cg.add(mqtt_.set_expire_after(config[CONF_EXPIRE_AFTER])) + cg.add(mqtt_.set_expire_after(expire_after)) async def register_sensor(var, config): @@ -803,10 +803,10 @@ async def sensor_in_range_to_code(config, condition_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(condition_id, template_arg, paren) - if CONF_ABOVE in config: - cg.add(var.set_min(config[CONF_ABOVE])) - if CONF_BELOW in config: - cg.add(var.set_max(config[CONF_BELOW])) + if (above := config.get(CONF_ABOVE)) is not None: + cg.add(var.set_min(above)) + if (below := config.get(CONF_BELOW)) is not None: + cg.add(var.set_max(below)) return var From 09def255dd70c8f406e046b87fe1cd77953fd9ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:02:37 +1200 Subject: [PATCH 0103/1106] Bump pytest-mock from 3.12.0 to 3.14.0 (#6572) 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 767018d07a..78820765f4 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -7,7 +7,7 @@ pre-commit # Unit tests pytest==8.1.1 pytest-cov==4.1.0 -pytest-mock==3.12.0 +pytest-mock==3.14.0 pytest-asyncio==0.23.6 asyncmock==0.4.2 hypothesis==6.92.1 From 6075067e843788eb7fae90333ae8ff7f59b25a8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:02:54 +1200 Subject: [PATCH 0104/1106] Bump peter-evans/create-pull-request from 6.0.3 to 6.0.4 (#6569) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/sync-device-classes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index 62b9c7df9b..c9614618d5 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -36,7 +36,7 @@ jobs: python ./script/sync-device_class.py - name: Commit changes - uses: peter-evans/create-pull-request@v6.0.3 + uses: peter-evans/create-pull-request@v6.0.4 with: commit-message: "Synchronise Device Classes from Home Assistant" committer: esphomebot From 8c323e2e4c9941f3e300ba4aa98887a3a09bfda7 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 18 Apr 2024 01:07:05 +0200 Subject: [PATCH 0105/1106] Nextion - Review set_protocol_reparse_mode() (#6567) --- esphome/components/nextion/nextion.h | 22 ++++++++++---- .../components/nextion/nextion_commands.cpp | 29 ++++++++++--------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index f9f01de72c..d84f1df3de 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -878,12 +878,24 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ void sleep(bool sleep); /** - * Sets Nextion Protocol Reparse mode between active or passive - * @param True or false. - * active_mode=true to enter active protocol reparse mode - * active_mode=false to enter passive protocol reparse mode. + * @brief Sets the Nextion display's protocol reparse mode. + * + * This function toggles the Nextion display's protocol reparse mode between active and passive. + * In active mode, the display actively parses incoming data. + * In passive mode, it does not parse data unless specifically instructed to do so. + * This is useful for managing how the Nextion display interprets incoming commands, + * especially during initialization or in scenarios where precise control over command processing is needed. + * + * @param active_mode A boolean value indicating the desired reparse mode. + * - true to set the display to active protocol reparse mode, where it actively parses incoming commands. + * - false to set the display to passive protocol reparse mode, where command parsing is done only on explicit + * instruction. + * + * @return bool Returns true if all commands were sent successfully to the Nextion display, indicating that the mode + * was set as expected. Returns false if any of the commands failed to send, indicating that the desired reparse mode + * may not be correctly set. */ - void set_protocol_reparse_mode(bool active_mode); + bool set_protocol_reparse_mode(bool active_mode); // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) diff --git a/esphome/components/nextion/nextion_commands.cpp b/esphome/components/nextion/nextion_commands.cpp index e378111376..a2325861a3 100644 --- a/esphome/components/nextion/nextion_commands.cpp +++ b/esphome/components/nextion/nextion_commands.cpp @@ -36,22 +36,23 @@ void Nextion::sleep(bool sleep) { // End sleep safe commands // Protocol reparse mode -void Nextion::set_protocol_reparse_mode(bool active_mode) { - const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF}; +bool Nextion::set_protocol_reparse_mode(bool active_mode) { + ESP_LOGV(TAG, "Set Nextion protocol reparse mode: %s", YESNO(active_mode)); + this->ignore_is_setup_ = true; // if not in reparse mode setup will fail, so it should be ignored + bool all_commands_sent = true; if (active_mode) { // Sets active protocol reparse mode - this->write_str( - "recmod=1"); // send_command_ cannot be used as Nextion might not be setup if incorrect reparse mode - this->write_array(to_send, sizeof(to_send)); - } else { // Sets passive protocol reparse mode - this->write_str("DRAKJHSUYDGBNCJHGJKSHBDN"); // To exit active reparse mode this sequence must be sent - this->write_array(to_send, sizeof(to_send)); - this->write_str("recmod=0"); // Sending recmode=0 twice is recommended - this->write_array(to_send, sizeof(to_send)); - this->write_str("recmod=0"); - this->write_array(to_send, sizeof(to_send)); + all_commands_sent &= this->send_command_("recmod=1"); + } else { // Sets passive protocol reparse mode + all_commands_sent &= + this->send_command_("DRAKJHSUYDGBNCJHGJKSHBDN"); // To exit active reparse mode this sequence must be sent + all_commands_sent &= this->send_command_("recmod=0"); // Sending recmode=0 twice is recommended + all_commands_sent &= this->send_command_("recmod=0"); } - this->write_str("connect"); - this->write_array(to_send, sizeof(to_send)); + if (!this->nextion_reports_is_setup_) { // No need to connect if is already setup + all_commands_sent &= this->send_command_("connect"); + } + this->ignore_is_setup_ = false; + return all_commands_sent; } void Nextion::set_exit_reparse_on_start(bool exit_reparse) { this->exit_reparse_on_start_ = exit_reparse; } From abc09a15c38b4c77a1316ccad32b97a4638b03d8 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 17 Apr 2024 18:47:34 -0500 Subject: [PATCH 0106/1106] Allow component final_validate (#6475) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/config.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/esphome/config.py b/esphome/config.py index c5764dd4f2..1f6867eb59 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -333,10 +333,11 @@ class LoadValidationStep(ConfigValidationStep): if load not in result: result.add_validation_step(AutoLoadValidationStep(load)) + result.add_validation_step( + MetadataValidationStep([self.domain], self.domain, self.conf, component) + ) + if not component.is_platform_component: - result.add_validation_step( - MetadataValidationStep([self.domain], self.domain, self.conf, component) - ) return # This is a platform component, proceed to reading platform entries @@ -520,8 +521,6 @@ class SchemaValidationStep(ConfigValidationStep): self.comp = comp def run(self, result: Config) -> None: - if self.comp.config_schema is None: - return token = path_context.set(self.path) with result.catch_error(self.path): if self.comp.is_platform: @@ -536,7 +535,7 @@ class SchemaValidationStep(ConfigValidationStep): validated["platform"] = platform_val validated.move_to_end("platform", last=False) result.set_by_path(self.path, validated) - else: + elif self.comp.config_schema is not None: schema = cv.Schema(self.comp.config_schema) validated = schema(self.conf) result.set_by_path(self.path, validated) From 5a093acbf509f958fb79af5d4d705f6e49befbd4 Mon Sep 17 00:00:00 2001 From: Cossid <83468485+Cossid@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:03:59 -0500 Subject: [PATCH 0107/1106] SM2135 - Use standard channel ordering. (#6573) --- esphome/components/sm2135/sm2135.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/esphome/components/sm2135/sm2135.cpp b/esphome/components/sm2135/sm2135.cpp index 9a576859ac..ee5948bb3a 100644 --- a/esphome/components/sm2135/sm2135.cpp +++ b/esphome/components/sm2135/sm2135.cpp @@ -106,23 +106,23 @@ void SM2135::loop() { delay(1); this->sm2135_start_(); this->write_byte_(SM2135_ADDR_C); - this->write_byte_(this->pwm_amounts_[4]); // Warm - this->write_byte_(this->pwm_amounts_[3]); // Cold + this->write_byte_(this->pwm_amounts_[3]); + this->write_byte_(this->pwm_amounts_[4]); } else { // Color this->write_byte_(SM2135_RGB); - this->write_byte_(this->pwm_amounts_[1]); // Green - this->write_byte_(this->pwm_amounts_[0]); // Red - this->write_byte_(this->pwm_amounts_[2]); // Blue + this->write_byte_(this->pwm_amounts_[0]); + this->write_byte_(this->pwm_amounts_[1]); + this->write_byte_(this->pwm_amounts_[2]); } } else { this->write_byte_(SM2135_RGB); - this->write_byte_(this->pwm_amounts_[1]); // Green - this->write_byte_(this->pwm_amounts_[0]); // Red - this->write_byte_(this->pwm_amounts_[2]); // Blue - this->write_byte_(this->pwm_amounts_[4]); // Warm - this->write_byte_(this->pwm_amounts_[3]); // Cold + this->write_byte_(this->pwm_amounts_[0]); + this->write_byte_(this->pwm_amounts_[1]); + this->write_byte_(this->pwm_amounts_[2]); + this->write_byte_(this->pwm_amounts_[3]); + this->write_byte_(this->pwm_amounts_[4]); } this->sm2135_stop_(); From 39deb8910868d72ab043e579f861eef0e8227508 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 18 Apr 2024 02:05:37 +0200 Subject: [PATCH 0108/1106] Nextion - Do not refresh sensors while updating (#6566) --- .../binary_sensor/nextion_binarysensor.cpp | 4 ++-- esphome/components/nextion/nextion.cpp | 2 ++ esphome/components/nextion/nextion.h | 20 +++++++++++++++++++ esphome/components/nextion/nextion_base.h | 2 ++ .../nextion/sensor/nextion_sensor.cpp | 4 ++-- .../nextion/switch/nextion_switch.cpp | 4 ++-- .../text_sensor/nextion_textsensor.cpp | 4 ++-- 7 files changed, 32 insertions(+), 8 deletions(-) diff --git a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp index bf6e74cb38..499cd901c0 100644 --- a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +++ b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp @@ -27,7 +27,7 @@ void NextionBinarySensor::process_touch(uint8_t page_id, uint8_t component_id, b } void NextionBinarySensor::update() { - if (!this->nextion_->is_setup()) + if (!this->nextion_->is_setup() || this->nextion_->is_updating()) return; if (this->variable_name_.empty()) // This is a touch component @@ -37,7 +37,7 @@ void NextionBinarySensor::update() { } void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nextion) { - if (!this->nextion_->is_setup()) + if (!this->nextion_->is_setup() || this->nextion_->is_updating()) return; if (this->component_id_ == 0) // This is a legacy touch component diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 29dcfa6cef..ac8a96fa7f 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -1137,5 +1137,7 @@ void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = write ESPDEPRECATED("set_wait_for_ack(bool) is deprecated and has no effect", "v1.20") void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "This command is deprecated"); } +bool Nextion::is_updating() { return this->is_updating_; } + } // namespace nextion } // namespace esphome diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index d84f1df3de..0769b3f93f 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -1031,6 +1031,26 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ size_t queue_size() { return this->nextion_queue_.size(); } + /** + * @brief Check if the TFT update process is currently running. + * + * This method provides a way to determine if the Nextion display is in the + * process of updating its TFT firmware. When a TFT update is in progress, + * certain operations or commands may be restricted or could interfere with the + * update process. By checking the state of the update process, the system can + * make informed decisions about performing actions that involve communication + * with the Nextion display. + * + * @return true if the TFT update process is active, indicating that the Nextion + * display is currently updating its firmware. This implies that caution + * should be taken with commands sent to the display to avoid interrupting + * the update process. + * @return false if the TFT update process is not active, indicating that the Nextion + * display is not currently updating its firmware and is in a normal operational + * state, ready to receive and process commands as usual. + */ + bool is_updating() override; + protected: std::deque nextion_queue_; std::deque waveform_queue_; diff --git a/esphome/components/nextion/nextion_base.h b/esphome/components/nextion/nextion_base.h index b5729a1df1..f16eabc003 100644 --- a/esphome/components/nextion/nextion_base.h +++ b/esphome/components/nextion/nextion_base.h @@ -48,6 +48,8 @@ class NextionBase { virtual void show_component(const char *component) = 0; virtual void hide_component(const char *component) = 0; + virtual bool is_updating() { return false; } + bool is_sleeping() { return this->is_sleeping_; } bool is_setup() { return this->is_setup_; } bool is_detected() { return this->is_detected_; } diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp index 566dd30acf..6cc641fcf3 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.cpp +++ b/esphome/components/nextion/sensor/nextion_sensor.cpp @@ -30,7 +30,7 @@ void NextionSensor::add_to_wave_buffer(float state) { } void NextionSensor::update() { - if (!this->nextion_->is_setup()) + if (!this->nextion_->is_setup() || this->nextion_->is_updating()) return; if (this->wave_chan_id_ == UINT8_MAX) { @@ -45,7 +45,7 @@ void NextionSensor::update() { } void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { - if (!this->nextion_->is_setup()) + if (!this->nextion_->is_setup() || this->nextion_->is_updating()) return; if (std::isnan(state)) diff --git a/esphome/components/nextion/switch/nextion_switch.cpp b/esphome/components/nextion/switch/nextion_switch.cpp index 1f32ad3425..63c1882b48 100644 --- a/esphome/components/nextion/switch/nextion_switch.cpp +++ b/esphome/components/nextion/switch/nextion_switch.cpp @@ -18,13 +18,13 @@ void NextionSwitch::process_bool(const std::string &variable_name, bool on) { } void NextionSwitch::update() { - if (!this->nextion_->is_setup()) + if (!this->nextion_->is_setup() || this->nextion_->is_updating()) return; this->nextion_->add_to_get_queue(this); } void NextionSwitch::set_state(bool state, bool publish, bool send_to_nextion) { - if (!this->nextion_->is_setup()) + if (!this->nextion_->is_setup() || this->nextion_->is_updating()) return; if (send_to_nextion) { diff --git a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp index 08f032df74..a3fc9390f5 100644 --- a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp +++ b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp @@ -16,13 +16,13 @@ void NextionTextSensor::process_text(const std::string &variable_name, const std } void NextionTextSensor::update() { - if (!this->nextion_->is_setup()) + if (!this->nextion_->is_setup() || this->nextion_->is_updating()) return; this->nextion_->add_to_get_queue(this); } void NextionTextSensor::set_state(const std::string &state, bool publish, bool send_to_nextion) { - if (!this->nextion_->is_setup()) + if (!this->nextion_->is_setup() || this->nextion_->is_updating()) return; if (send_to_nextion) { From 197f9d6d0322210984de4c50681a7afb72cd0105 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 18 Apr 2024 02:10:10 +0200 Subject: [PATCH 0109/1106] Nextion - Review types (#6565) --- esphome/components/nextion/display.py | 4 +- esphome/components/nextion/nextion.cpp | 12 +- esphome/components/nextion/nextion.h | 137 ++++++++++++------ esphome/components/nextion/nextion_base.h | 4 +- .../components/nextion/nextion_commands.cpp | 137 ++++++++++-------- 5 files changed, 183 insertions(+), 111 deletions(-) diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index 27f2030f0d..6487c12f36 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -68,8 +68,8 @@ CONFIG_SCHEMA = ( } ), cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.int_range(min=3, max=65535), - cv.Optional(CONF_WAKE_UP_PAGE): cv.positive_int, - cv.Optional(CONF_START_UP_PAGE): cv.positive_int, + cv.Optional(CONF_WAKE_UP_PAGE): cv.uint8_t, + cv.Optional(CONF_START_UP_PAGE): cv.uint8_t, cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean, cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean, } diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index ac8a96fa7f..8de5ca9143 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -138,11 +138,11 @@ void Nextion::dump_config() { } if (this->wake_up_page_ != -1) { - ESP_LOGCONFIG(TAG, " Wake Up Page: %d", this->wake_up_page_); + ESP_LOGCONFIG(TAG, " Wake Up Page: %" PRId16, this->wake_up_page_); } if (this->start_up_page_ != -1) { - ESP_LOGCONFIG(TAG, " Start Up Page: %d", this->start_up_page_); + ESP_LOGCONFIG(TAG, " Start Up Page: %" PRId16, this->start_up_page_); } } @@ -1024,23 +1024,23 @@ bool Nextion::add_no_result_to_queue_with_printf_(const std::string &variable_na * @param is_sleep_safe The command is safe to send when the Nextion is sleeping */ -void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) { +void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, int32_t state_value) { this->add_no_result_to_queue_with_set(component->get_variable_name(), component->get_variable_name_to_send(), state_value); } void Nextion::add_no_result_to_queue_with_set(const std::string &variable_name, - const std::string &variable_name_to_send, int state_value) { + const std::string &variable_name_to_send, int32_t state_value) { this->add_no_result_to_queue_with_set_internal_(variable_name, variable_name_to_send, state_value); } void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &variable_name, - const std::string &variable_name_to_send, int state_value, + const std::string &variable_name_to_send, int32_t state_value, bool is_sleep_safe) { if ((!this->is_setup() && !this->ignore_is_setup_) || (!is_sleep_safe && this->is_sleeping())) return; - this->add_no_result_to_queue_with_ignore_sleep_printf_(variable_name, "%s=%d", variable_name_to_send.c_str(), + this->add_no_result_to_queue_with_ignore_sleep_printf_(variable_name, "%s=%" PRId32, variable_name_to_send.c_str(), state_value); } diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 0769b3f93f..bc75df3ce6 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -50,6 +50,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * This will set the `txt` property of the component `textview` to `Hello World`. */ void set_component_text(const char *component, const char *text); + /** * Set the text of a component to a formatted string * @param component The component name. @@ -66,6 +67,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * For example when `uptime_sensor` = 506, then, `The uptime is: 506` will be displayed. */ void set_component_text_printf(const char *component, const char *format, ...) __attribute__((format(printf, 3, 4))); + /** * Set the integer value of a component * @param component The component name. @@ -78,7 +80,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * * This will change the property `value` of the component `gauge` to 50. */ - void set_component_value(const char *component, int value); + void set_component_value(const char *component, int32_t value); + /** * Set the picture of an image component. * @param component The component name. @@ -92,6 +95,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * This will change the image of the component `pic` to the image with ID `4`. */ void set_component_picture(const char *component, uint8_t picture_id); + /** * Set the background color of a component. * @param component The component name. @@ -107,6 +111,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Nextion HMI colors. */ void set_component_background_color(const char *component, uint16_t color); + /** * Set the background color of a component. * @param component The component name. @@ -121,6 +126,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ void set_component_background_color(const char *component, const char *color); + /** * Set the background color of a component. * @param component The component name. @@ -135,6 +141,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * This will change the background color of the component `button` to blue. */ void set_component_background_color(const char *component, Color color) override; + /** * Set the pressed background color of a component. * @param component The component name. @@ -151,6 +158,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Nextion HMI colors. */ void set_component_pressed_background_color(const char *component, uint16_t color); + /** * Set the pressed background color of a component. * @param component The component name. @@ -166,6 +174,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ void set_component_pressed_background_color(const char *component, const char *color); + /** * Set the pressed background color of a component. * @param component The component name. @@ -181,6 +190,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * is shown when the component is pressed. */ void set_component_pressed_background_color(const char *component, Color color) override; + /** * Set the foreground color of a component. * @param component The component name. @@ -196,6 +206,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Nextion HMI colors. */ void set_component_foreground_color(const char *component, uint16_t color); + /** * Set the foreground color of a component. * @param component The component name. @@ -210,6 +221,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ void set_component_foreground_color(const char *component, const char *color); + /** * Set the foreground color of a component. * @param component The component name. @@ -223,6 +235,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * This will change the foreground color of the component `button` to black. */ void set_component_foreground_color(const char *component, Color color) override; + /** * Set the pressed foreground color of a component. * @param component The component name. @@ -239,6 +252,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Nextion HMI colors. */ void set_component_pressed_foreground_color(const char *component, uint16_t color); + /** * Set the pressed foreground color of a component. * @param component The component name. @@ -254,6 +268,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ void set_component_pressed_foreground_color(const char *component, const char *color); + /** * Set the pressed foreground color of a component. * @param component The component name. @@ -283,6 +298,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * This will change the picture id of the component `textview`. */ void set_component_pic(const char *component, uint8_t pic_id); + /** * Set the background picture id of component. * @param component The component name. @@ -312,6 +328,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Nextion HMI colors. */ void set_component_font_color(const char *component, uint16_t color); + /** * Set the font color of a component. * @param component The component name. @@ -326,6 +343,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ void set_component_font_color(const char *component, const char *color); + /** * Set the font color of a component. * @param component The component name. @@ -339,6 +357,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * This will change the font color of the component `textview` to black. */ void set_component_font_color(const char *component, Color color) override; + /** * Set the pressed font color of a component. * @param component The component name. @@ -354,6 +373,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Nextion HMI colors. */ void set_component_pressed_font_color(const char *component, uint16_t color); + /** * Set the pressed font color of a component. * @param component The component name. @@ -368,6 +388,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ void set_component_pressed_font_color(const char *component, const char *color); + /** * Set the pressed font color of a component. * @param component The component name. @@ -381,6 +402,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * This will change the pressed font color of the component `button` to black. */ void set_component_pressed_font_color(const char *component, Color color) override; + /** * Set the coordinates of a component on screen. * @param component The component name. @@ -394,7 +416,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * * This will move the position of the component `pic` to the x coordinate `55` and y coordinate `100`. */ - void set_component_coordinates(const char *component, int x, int y); + void set_component_coordinates(const char *component, uint16_t x, uint16_t y); + /** * Set the font id for a component. * @param component The component name. @@ -408,6 +431,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Changes the font of the component named `textveiw`. Font IDs are set in the Nextion Editor. */ void set_component_font(const char *component, uint8_t font_id) override; + /** * Send the current time to the nextion display. * @param time The time instance to send (get this with id(my_time).now() ). @@ -426,6 +450,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Switches to the page named `main`. Pages are named in the Nextion Editor. */ void goto_page(const char *page); + /** * Show the page with a given id. * @param page The id of the page. @@ -438,6 +463,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Switches to the page named `main`. Pages are named in the Nextion Editor. */ void goto_page(uint8_t page); + /** * Hide a component. * @param component The component name. @@ -450,6 +476,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Hides the component named `button`. */ void hide_component(const char *component) override; + /** * Show a component. * @param component The component name. @@ -462,6 +489,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Shows the component named `button`. */ void show_component(const char *component) override; + /** * Enable touch for a component. * @param component The component name. @@ -474,6 +502,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Enables touch for component named `button`. */ void enable_component_touch(const char *component); + /** * Disable touch for a component. * @param component The component name. @@ -486,14 +515,17 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Disables touch for component named `button`. */ void disable_component_touch(const char *component); + /** * Add waveform data to a waveform component * @param component_id The integer component id. * @param channel_number The channel number to write to. * @param value The value to write. */ - void add_waveform_data(int component_id, uint8_t channel_number, uint8_t value); - void open_waveform_channel(int component_id, uint8_t channel_number, uint8_t value); + void add_waveform_data(uint8_t component_id, uint8_t channel_number, uint8_t value); + + void open_waveform_channel(uint8_t component_id, uint8_t channel_number, uint8_t value); + /** * Display a picture at coordinates. * @param picture_id The picture id. @@ -507,7 +539,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * * Displays the picture who has the id `2` at the x coordinates `15` and y coordinates `25`. */ - void display_picture(int picture_id, int x_start, int y_start); + void display_picture(uint16_t picture_id, uint16_t x_start, uint16_t y_start); + /** * Fill a rectangle with a color. * @param x1 The starting x coordinate. @@ -526,7 +559,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to * Nextion HMI colors. */ - void fill_area(int x1, int y1, int width, int height, uint16_t color); + void fill_area(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, uint16_t color); + /** * Fill a rectangle with a color. * @param x1 The starting x coordinate. @@ -544,7 +578,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * the red color. * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ - void fill_area(int x1, int y1, int width, int height, const char *color); + void fill_area(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, const char *color); + /** * Fill a rectangle with a color. * @param x1 The starting x coordinate. @@ -562,7 +597,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Fills an area that starts at x coordinate `50` and y coordinate `50` with a height of `100` and width of `100` with * blue color. */ - void fill_area(int x1, int y1, int width, int height, Color color); + void fill_area(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, Color color); + /** * Draw a line on the screen. * @param x1 The starting x coordinate. @@ -581,7 +617,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to * Nextion HMI colors. */ - void line(int x1, int y1, int x2, int y2, uint16_t color); + void line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); + /** * Draw a line on the screen. * @param x1 The starting x coordinate. @@ -599,7 +636,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * `75` with the blue color. * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ - void line(int x1, int y1, int x2, int y2, const char *color); + void line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, const char *color); + /** * Draw a line on the screen. * @param x1 The starting x coordinate. @@ -617,7 +655,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Makes a line that starts at x coordinate `50` and y coordinate `50` and ends at x coordinate `75` and y coordinate * `75` with blue color. */ - void line(int x1, int y1, int x2, int y2, Color color); + void line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, Color color); + /** * Draw a rectangle outline. * @param x1 The starting x coordinate. @@ -636,7 +675,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to * Nextion HMI colors. */ - void rectangle(int x1, int y1, int width, int height, uint16_t color); + void rectangle(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, uint16_t color); + /** * Draw a rectangle outline. * @param x1 The starting x coordinate. @@ -654,7 +694,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * length of `50` with the blue color. * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ - void rectangle(int x1, int y1, int width, int height, const char *color); + void rectangle(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, const char *color); + /** * Draw a rectangle outline. * @param x1 The starting x coordinate. @@ -672,7 +713,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Makes a outline of a rectangle that starts at x coordinate `25` and y coordinate `35` and has a width of `40` and a * length of `50` with blue color. */ - void rectangle(int x1, int y1, int width, int height, Color color); + void rectangle(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, Color color); + /** * Draw a circle outline * @param center_x The center x coordinate. @@ -682,7 +724,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to * Nextion HMI colors. */ - void circle(int center_x, int center_y, int radius, uint16_t color); + void circle(uint16_t center_x, uint16_t center_y, uint16_t radius, uint16_t color); + /** * Draw a circle outline * @param center_x The center x coordinate. @@ -691,7 +734,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * @param color The color to draw with (as a string). * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ - void circle(int center_x, int center_y, int radius, const char *color); + void circle(uint16_t center_x, uint16_t center_y, uint16_t radius, const char *color); + /** * Draw a circle outline * @param center_x The center x coordinate. @@ -699,7 +743,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * @param radius The circle radius. * @param color The color to draw with (as Color). */ - void circle(int center_x, int center_y, int radius, Color color); + void circle(uint16_t center_x, uint16_t center_y, uint16_t radius, Color color); + /** * Draw a filled circled. * @param center_x The center x coordinate. @@ -716,7 +761,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to * Nextion HMI colors. */ - void filled_circle(int center_x, int center_y, int radius, uint16_t color); + void filled_circle(uint16_t center_x, uint16_t center_y, uint16_t radius, uint16_t color); + /** * Draw a filled circled. * @param center_x The center x coordinate. @@ -732,7 +778,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Makes a filled circle at the x coordinate `25` and y coordinate `25` with a radius of `10` with the blue color. * Use [Nextion Instruction Set](https://nextion.tech/instruction-set/#s5) for a list of Nextion HMI colors constants. */ - void filled_circle(int center_x, int center_y, int radius, const char *color); + void filled_circle(uint16_t center_x, uint16_t center_y, uint16_t radius, const char *color); + /** * Draw a filled circled. * @param center_x The center x coordinate. @@ -748,7 +795,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * * Makes a filled circle at the x coordinate `25` and y coordinate `25` with a radius of `10` with blue color. */ - void filled_circle(int center_x, int center_y, int radius, Color color); + void filled_circle(uint16_t center_x, uint16_t center_y, uint16_t radius, Color color); /** * Draws a QR code in the screen @@ -768,8 +815,9 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * * Draws a QR code with a Wi-Fi network credentials starting at the given coordinates (25,25). */ - void qrcode(int x1, int y1, const char *content, int size = 200, uint16_t background_color = 65535, - uint16_t foreground_color = 0, int logo_pic = -1, uint8_t border_width = 8); + void qrcode(uint16_t x1, uint16_t y1, const char *content, uint16_t size = 200, uint16_t background_color = 65535, + uint16_t foreground_color = 0, uint8_t logo_pic = -1, uint8_t border_width = 8); + /** * Draws a QR code in the screen * @param x1 The top left x coordinate to start the QR code. @@ -791,8 +839,9 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Draws a QR code with a Wi-Fi network credentials starting at the given coordinates (25,25) with size of 150px in * red on a blue background. */ - void qrcode(int x1, int y1, const char *content, int size, Color background_color = Color(255, 255, 255), - Color foreground_color = Color(0, 0, 0), int logo_pic = -1, uint8_t border_width = 8); + void qrcode(uint16_t x1, uint16_t y1, const char *content, uint16_t size, + Color background_color = Color(255, 255, 255), Color foreground_color = Color(0, 0, 0), + uint8_t logo_pic = -1, uint8_t border_width = 8); /** Set the brightness of the backlight. * @@ -806,6 +855,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Changes the brightness of the display to 30%. */ void set_backlight_brightness(float brightness); + /** * Set the touch sleep timeout of the display. * @param timeout Timeout in seconds. @@ -819,6 +869,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * `thup`. */ void set_touch_sleep_timeout(uint16_t timeout); + /** * Sets which page Nextion loads when exiting sleep mode. Note this can be set even when Nextion is in sleep mode. * @param page_id The page id, from 0 to the lage page in Nextion. Set 255 (not set to any existing page) to @@ -832,6 +883,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * The display will wake up to page 2. */ void set_wake_up_page(uint8_t page_id = 255); + /** * Sets which page Nextion loads when connecting to ESPHome. * @param page_id The page id, from 0 to the lage page in Nextion. Set 255 (not set to any existing page) to @@ -859,6 +911,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * The display will wake up by touch. */ void set_auto_wake_on_touch(bool auto_wake); + /** * Sets if Nextion should exit the active reparse mode before the "connect" command is sent * @param exit_reparse True or false. When exit_reparse is true, the exit reparse command @@ -872,11 +925,13 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * The display will be requested to leave active reparse mode before setup. */ void set_exit_reparse_on_start(bool exit_reparse); + /** * Sets Nextion mode between sleep and awake * @param True or false. Sleep=true to enter sleep mode or sleep=false to exit sleep mode. */ void sleep(bool sleep); + /** * @brief Sets the Nextion display's protocol reparse mode. * @@ -928,7 +983,6 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe * Set the tft file URL. https seems problematic with arduino.. */ void set_tft_url(const std::string &tft_url) { this->tft_url_ = tft_url; } - #endif /** @@ -992,9 +1046,9 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void set_nextion_sensor_state(NextionQueueType queue_type, const std::string &name, float state); void set_nextion_text_state(const std::string &name, const std::string &state); - void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) override; + void add_no_result_to_queue_with_set(NextionComponentBase *component, int32_t state_value) override; void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, - int state_value) override; + int32_t state_value) override; void add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) override; void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, @@ -1070,8 +1124,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void process_serial_(); bool is_updating_ = false; uint32_t touch_sleep_timeout_ = 0; - int wake_up_page_ = -1; - int start_up_page_ = -1; + int16_t wake_up_page_ = -1; + int16_t start_up_page_ = -1; bool auto_wake_on_touch_ = true; bool exit_reparse_on_start_ = false; @@ -1089,7 +1143,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe __attribute__((format(printf, 3, 4))); void add_no_result_to_queue_with_set_internal_(const std::string &variable_name, - const std::string &variable_name_to_send, int state_value, + const std::string &variable_name_to_send, int32_t state_value, bool is_sleep_safe = false); void add_no_result_to_queue_with_set_internal_(const std::string &variable_name, @@ -1099,13 +1153,21 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void check_pending_waveform_(); #ifdef USE_NEXTION_TFT_UPLOAD + uint32_t content_length_ = 0; + int tft_size_ = 0; + uint32_t original_baud_rate_ = 0; + bool upload_first_chunk_sent_ = false; + + std::string tft_url_; + uint8_t *transfer_buffer_{nullptr}; + size_t transfer_buffer_size_; + #ifdef USE_ESP8266 WiFiClient *wifi_client_{nullptr}; BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr}; WiFiClient *get_wifi_client_(); #endif - int content_length_ = 0; - int tft_size_ = 0; + #ifdef ARDUINO /** * will request chunk_size chunks from the web server @@ -1178,13 +1240,6 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void remove_front_no_sensors_(); -#ifdef USE_NEXTION_TFT_UPLOAD - std::string tft_url_; - uint8_t *transfer_buffer_{nullptr}; - size_t transfer_buffer_size_; - bool upload_first_chunk_sent_ = false; -#endif - #ifdef NEXTION_PROTOCOL_LOG void print_queue_members_(); #endif @@ -1192,8 +1247,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe std::string command_data_; bool is_connected_ = false; - uint32_t startup_override_ms_ = 8000; - uint32_t max_q_age_ms_ = 8000; + const uint16_t startup_override_ms_ = 8000; + const uint16_t max_q_age_ms_ = 8000; uint32_t started_ms_ = 0; bool sent_setup_commands_ = false; }; diff --git a/esphome/components/nextion/nextion_base.h b/esphome/components/nextion/nextion_base.h index f16eabc003..b88dd399f8 100644 --- a/esphome/components/nextion/nextion_base.h +++ b/esphome/components/nextion/nextion_base.h @@ -24,9 +24,9 @@ class NextionBase; class NextionBase { public: - virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) = 0; + virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, int32_t state_value) = 0; virtual void add_no_result_to_queue_with_set(const std::string &variable_name, - const std::string &variable_name_to_send, int state_value) = 0; + const std::string &variable_name_to_send, int32_t state_value) = 0; virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) = 0; virtual void add_no_result_to_queue_with_set(const std::string &variable_name, diff --git a/esphome/components/nextion/nextion_commands.cpp b/esphome/components/nextion/nextion_commands.cpp index a2325861a3..fdd6c74d99 100644 --- a/esphome/components/nextion/nextion_commands.cpp +++ b/esphome/components/nextion/nextion_commands.cpp @@ -144,11 +144,11 @@ void Nextion::set_component_pressed_font_color(const char *component, Color colo // Set picture void Nextion::set_component_pic(const char *component, uint8_t pic_id) { - this->add_no_result_to_queue_with_printf_("set_component_pic", "%s.pic=%d", component, pic_id); + this->add_no_result_to_queue_with_printf_("set_component_pic", "%s.pic=%" PRIu8, component, pic_id); } void Nextion::set_component_picc(const char *component, uint8_t pic_id) { - this->add_no_result_to_queue_with_printf_("set_component_pic", "%s.picc=%d", component, pic_id); + this->add_no_result_to_queue_with_printf_("set_component_pic", "%s.picc=%" PRIu8, component, pic_id); } void Nextion::set_component_text_printf(const char *component, const char *format, ...) { @@ -179,7 +179,7 @@ void Nextion::set_auto_wake_on_touch(bool auto_wake) { // General Component void Nextion::set_component_font(const char *component, uint8_t font_id) { - this->add_no_result_to_queue_with_printf_("set_component_font", "%s.font=%d", component, font_id); + this->add_no_result_to_queue_with_printf_("set_component_font", "%s.font=%" PRIu8, component, font_id); } void Nextion::hide_component(const char *component) { @@ -199,113 +199,130 @@ void Nextion::disable_component_touch(const char *component) { } void Nextion::set_component_picture(const char *component, uint8_t picture_id) { - this->add_no_result_to_queue_with_printf_("set_component_picture", "%s.pic=%d", component, picture_id); + this->add_no_result_to_queue_with_printf_("set_component_picture", "%s.pic=%" PRIu8, component, picture_id); } void Nextion::set_component_text(const char *component, const char *text) { this->add_no_result_to_queue_with_printf_("set_component_text", "%s.txt=\"%s\"", component, text); } -void Nextion::set_component_value(const char *component, int value) { - this->add_no_result_to_queue_with_printf_("set_component_value", "%s.val=%d", component, value); +void Nextion::set_component_value(const char *component, int32_t value) { + this->add_no_result_to_queue_with_printf_("set_component_value", "%s.val=%" PRId32, component, value); } -void Nextion::add_waveform_data(int component_id, uint8_t channel_number, uint8_t value) { - this->add_no_result_to_queue_with_printf_("add_waveform_data", "add %d,%u,%u", component_id, channel_number, value); +void Nextion::add_waveform_data(uint8_t component_id, uint8_t channel_number, uint8_t value) { + this->add_no_result_to_queue_with_printf_("add_waveform_data", "add %" PRIu8 ",%" PRIu8 ",%" PRIu8, component_id, + channel_number, value); } -void Nextion::open_waveform_channel(int component_id, uint8_t channel_number, uint8_t value) { - this->add_no_result_to_queue_with_printf_("open_waveform_channel", "addt %d,%u,%u", component_id, channel_number, - value); +void Nextion::open_waveform_channel(uint8_t component_id, uint8_t channel_number, uint8_t value) { + this->add_no_result_to_queue_with_printf_("open_waveform_channel", "addt %" PRIu8 ",%" PRIu8 ",%" PRIu8, component_id, + channel_number, value); } -void Nextion::set_component_coordinates(const char *component, int x, int y) { - this->add_no_result_to_queue_with_printf_("set_component_coordinates command 1", "%s.xcen=%d", component, x); - this->add_no_result_to_queue_with_printf_("set_component_coordinates command 2", "%s.ycen=%d", component, y); +void Nextion::set_component_coordinates(const char *component, uint16_t x, uint16_t y) { + this->add_no_result_to_queue_with_printf_("set_component_coordinates command 1", "%s.xcen=%" PRIu16, component, x); + this->add_no_result_to_queue_with_printf_("set_component_coordinates command 2", "%s.ycen=%" PRIu16, component, y); } // Drawing -void Nextion::display_picture(int picture_id, int x_start, int y_start) { - this->add_no_result_to_queue_with_printf_("display_picture", "pic %d, %d, %d", x_start, y_start, picture_id); +void Nextion::display_picture(uint16_t picture_id, uint16_t x_start, uint16_t y_start) { + this->add_no_result_to_queue_with_printf_("display_picture", "pic %" PRIu16 ", %" PRIu16 ", %" PRIu16, x_start, + y_start, picture_id); } -void Nextion::fill_area(int x1, int y1, int width, int height, uint16_t color) { - this->add_no_result_to_queue_with_printf_("fill_area", "fill %d,%d,%d,%d,%" PRIu16, x1, y1, width, height, color); +void Nextion::fill_area(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, uint16_t color) { + this->add_no_result_to_queue_with_printf_( + "fill_area", "fill %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16, x1, y1, width, height, color); } -void Nextion::fill_area(int x1, int y1, int width, int height, const char *color) { - this->add_no_result_to_queue_with_printf_("fill_area", "fill %d,%d,%d,%d,%s", x1, y1, width, height, color); +void Nextion::fill_area(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, const char *color) { + this->add_no_result_to_queue_with_printf_("fill_area", "fill %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%s", x1, + y1, width, height, color); } -void Nextion::fill_area(int x1, int y1, int width, int height, Color color) { - this->add_no_result_to_queue_with_printf_("fill_area", "fill %d,%d,%d,%d,%d", x1, y1, width, height, - display::ColorUtil::color_to_565(color)); +void Nextion::fill_area(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, Color color) { + this->add_no_result_to_queue_with_printf_("fill_area", + "fill %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16, x1, y1, + width, height, display::ColorUtil::color_to_565(color)); } -void Nextion::line(int x1, int y1, int x2, int y2, uint16_t color) { - this->add_no_result_to_queue_with_printf_("line", "line %d,%d,%d,%d,%" PRIu16, x1, y1, x2, y2, color); +void Nextion::line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { + this->add_no_result_to_queue_with_printf_("line", "line %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16, x1, + y1, x2, y2, color); } -void Nextion::line(int x1, int y1, int x2, int y2, const char *color) { - this->add_no_result_to_queue_with_printf_("line", "line %d,%d,%d,%d,%s", x1, y1, x2, y2, color); +void Nextion::line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, const char *color) { + this->add_no_result_to_queue_with_printf_("line", "line %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%s", x1, y1, + x2, y2, color); } -void Nextion::line(int x1, int y1, int x2, int y2, Color color) { - this->add_no_result_to_queue_with_printf_("line", "line %d,%d,%d,%d,%d", x1, y1, x2, y2, - display::ColorUtil::color_to_565(color)); +void Nextion::line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, Color color) { + this->add_no_result_to_queue_with_printf_("line", "line %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16, x1, + y1, x2, y2, display::ColorUtil::color_to_565(color)); } -void Nextion::rectangle(int x1, int y1, int width, int height, uint16_t color) { - this->add_no_result_to_queue_with_printf_("draw", "draw %d,%d,%d,%d,%" PRIu16, x1, y1, x1 + width, y1 + height, +void Nextion::rectangle(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, uint16_t color) { + this->add_no_result_to_queue_with_printf_("draw", "draw %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16, x1, + y1, static_cast(x1 + width), static_cast(y1 + height), color); } -void Nextion::rectangle(int x1, int y1, int width, int height, const char *color) { - this->add_no_result_to_queue_with_printf_("draw", "draw %d,%d,%d,%d,%s", x1, y1, x1 + width, y1 + height, color); +void Nextion::rectangle(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, const char *color) { + this->add_no_result_to_queue_with_printf_("draw", "draw %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%s", x1, y1, + static_cast(x1 + width), static_cast(y1 + height), + color); } -void Nextion::rectangle(int x1, int y1, int width, int height, Color color) { - this->add_no_result_to_queue_with_printf_("draw", "draw %d,%d,%d,%d,%d", x1, y1, x1 + width, y1 + height, +void Nextion::rectangle(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, Color color) { + this->add_no_result_to_queue_with_printf_("draw", "draw %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16, x1, + y1, static_cast(x1 + width), static_cast(y1 + height), display::ColorUtil::color_to_565(color)); } -void Nextion::circle(int center_x, int center_y, int radius, uint16_t color) { - this->add_no_result_to_queue_with_printf_("cir", "cir %d,%d,%d,%" PRIu16, center_x, center_y, radius, color); +void Nextion::circle(uint16_t center_x, uint16_t center_y, uint16_t radius, uint16_t color) { + this->add_no_result_to_queue_with_printf_("cir", "cir %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16, center_x, + center_y, radius, color); } -void Nextion::circle(int center_x, int center_y, int radius, const char *color) { - this->add_no_result_to_queue_with_printf_("cir", "cir %d,%d,%d,%s", center_x, center_y, radius, color); +void Nextion::circle(uint16_t center_x, uint16_t center_y, uint16_t radius, const char *color) { + this->add_no_result_to_queue_with_printf_("cir", "cir %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%s", center_x, center_y, + radius, color); } -void Nextion::circle(int center_x, int center_y, int radius, Color color) { - this->add_no_result_to_queue_with_printf_("cir", "cir %d,%d,%d,%d", center_x, center_y, radius, - display::ColorUtil::color_to_565(color)); +void Nextion::circle(uint16_t center_x, uint16_t center_y, uint16_t radius, Color color) { + this->add_no_result_to_queue_with_printf_("cir", "cir %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16, center_x, + center_y, radius, display::ColorUtil::color_to_565(color)); } -void Nextion::filled_circle(int center_x, int center_y, int radius, uint16_t color) { - this->add_no_result_to_queue_with_printf_("cirs", "cirs %d,%d,%d,%" PRIu16, center_x, center_y, radius, color); +void Nextion::filled_circle(uint16_t center_x, uint16_t center_y, uint16_t radius, uint16_t color) { + this->add_no_result_to_queue_with_printf_("cirs", "cirs %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16, center_x, + center_y, radius, color); } -void Nextion::filled_circle(int center_x, int center_y, int radius, const char *color) { - this->add_no_result_to_queue_with_printf_("cirs", "cirs %d,%d,%d,%s", center_x, center_y, radius, color); +void Nextion::filled_circle(uint16_t center_x, uint16_t center_y, uint16_t radius, const char *color) { + this->add_no_result_to_queue_with_printf_("cirs", "cirs %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%s", center_x, center_y, + radius, color); } -void Nextion::filled_circle(int center_x, int center_y, int radius, Color color) { - this->add_no_result_to_queue_with_printf_("cirs", "cirs %d,%d,%d,%d", center_x, center_y, radius, - display::ColorUtil::color_to_565(color)); +void Nextion::filled_circle(uint16_t center_x, uint16_t center_y, uint16_t radius, Color color) { + this->add_no_result_to_queue_with_printf_("cirs", "cirs %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16, center_x, + center_y, radius, display::ColorUtil::color_to_565(color)); } -void Nextion::qrcode(int x1, int y1, const char *content, int size, uint16_t background_color, - uint16_t foreground_color, int logo_pic, uint8_t border_width) { - this->add_no_result_to_queue_with_printf_("qrcode", "qrcode %d,%d,%d,%d,%d,%d,%d,\"%s\"", x1, y1, size, - background_color, foreground_color, logo_pic, border_width, content); -} - -void Nextion::qrcode(int x1, int y1, const char *content, int size, Color background_color, Color foreground_color, - int logo_pic, uint8_t border_width) { +void Nextion::qrcode(uint16_t x1, uint16_t y1, const char *content, uint16_t size, uint16_t background_color, + uint16_t foreground_color, uint8_t logo_pic, uint8_t border_width) { this->add_no_result_to_queue_with_printf_( - "qrcode", "qrcode %d,%d,%d,%d,%d,%d,%d,\"%s\"", x1, y1, size, display::ColorUtil::color_to_565(background_color), - display::ColorUtil::color_to_565(foreground_color), logo_pic, border_width, content); + "qrcode", "qrcode %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu8 ",%" PRIu8 ",\"%s\"", x1, + y1, size, background_color, foreground_color, logo_pic, border_width, content); +} + +void Nextion::qrcode(uint16_t x1, uint16_t y1, const char *content, uint16_t size, Color background_color, + Color foreground_color, uint8_t logo_pic, uint8_t border_width) { + this->add_no_result_to_queue_with_printf_( + "qrcode", "qrcode %" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRIu8 ",%" PRIu8 ",\"%s\"", x1, + y1, size, display::ColorUtil::color_to_565(background_color), display::ColorUtil::color_to_565(foreground_color), + logo_pic, border_width, content); } void Nextion::set_nextion_rtc_time(ESPTime time) { From 2fed6955de41aad58f353d3feed5e5fcb1e70ddc Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:11:00 +1000 Subject: [PATCH 0110/1106] On failure, dump the output of preceding jobs in CI status (#6564) --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7df57acb08..d3ac7981d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -472,6 +472,7 @@ jobs: - compile-tests - clang-tidy - test-build-components + - list-components if: always() steps: - name: Success @@ -479,4 +480,8 @@ jobs: run: exit 0 - name: Failure if: ${{ contains(needs.*.result, 'failure') }} - run: exit 1 + env: + JSON_DOC: ${{ toJSON(needs) }} + run: | + echo $JSON_DOC | jq + exit 1 From 2e7ac26adacd5a7c1aa02ff47de785f4b9e79fee Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 18 Apr 2024 04:16:49 +0200 Subject: [PATCH 0111/1106] Nextion `send_command` method (#6540) This is a simplified version of `send_command_printf` without the `printf` support. Manually send a raw command to the display. param command The pcommand, like "page 0" return Whether the send was successful. --- esphome/components/nextion/nextion.cpp | 11 +++++++++++ esphome/components/nextion/nextion.h | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 8de5ca9143..ddbd3328ef 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -194,6 +194,17 @@ void Nextion::update_all_components() { } } +bool Nextion::send_command(const char *command) { + if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) + return false; + + if (this->send_command_(command)) { + this->add_no_result_to_queue_("send_command"); + return true; + } + return false; +} + bool Nextion::send_command_printf(const char *format, ...) { if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) return false; diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index bc75df3ce6..a8f0ea8ba9 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -970,6 +970,12 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe // This function has been deprecated void set_wait_for_ack(bool wait_for_ack); + /** + * Manually send a raw command to the display. + * @param command The pcommand, like "page 0" + * @return Whether the send was successful. + */ + bool send_command(const char *command); /** * Manually send a raw formatted command to the display. * @param format The printf-style command format, like "vis %s,0" From 8c31aea94fca4d83e7357593c0567e0b381135d0 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:03:15 +1000 Subject: [PATCH 0112/1106] Fix some printf formats for size_t. (#6542) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/select/select.cpp | 2 +- esphome/components/select/select_call.cpp | 2 +- esphome/components/sensor/filter.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/select/select.cpp b/esphome/components/select/select.cpp index f4583b4e2e..806882ad94 100644 --- a/esphome/components/select/select.cpp +++ b/esphome/components/select/select.cpp @@ -12,7 +12,7 @@ void Select::publish_state(const std::string &state) { if (index.has_value()) { this->has_state_ = true; this->state = state; - ESP_LOGD(TAG, "'%s': Sending state %s (index %d)", name, state.c_str(), index.value()); + ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", name, state.c_str(), index.value()); this->state_callback_.call(state, index.value()); } else { ESP_LOGE(TAG, "'%s': invalid state for publish_state(): %s", name, state.c_str()); diff --git a/esphome/components/select/select_call.cpp b/esphome/components/select/select_call.cpp index 6ee41b1029..85f755645c 100644 --- a/esphome/components/select/select_call.cpp +++ b/esphome/components/select/select_call.cpp @@ -71,7 +71,7 @@ void SelectCall::perform() { return; } if (this->index_.value() >= options.size()) { - ESP_LOGW(TAG, "'%s' - Index value %d out of bounds", name, this->index_.value()); + ESP_LOGW(TAG, "'%s' - Index value %zu out of bounds", name, this->index_.value()); return; } target_value = options[this->index_.value()]; diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index 6d7acad7e0..3f67af59be 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -79,7 +79,7 @@ SkipInitialFilter::SkipInitialFilter(size_t num_to_ignore) : num_to_ignore_(num_ optional SkipInitialFilter::new_value(float value) { if (num_to_ignore_ > 0) { num_to_ignore_--; - ESP_LOGV(TAG, "SkipInitialFilter(%p)::new_value(%f) SKIPPING, %u left", this, value, num_to_ignore_); + ESP_LOGV(TAG, "SkipInitialFilter(%p)::new_value(%f) SKIPPING, %zu left", this, value, num_to_ignore_); return {}; } From 655dbc48b5f7e53a4ceee83f4aa159e276d8b864 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Thu, 18 Apr 2024 14:52:22 -0700 Subject: [PATCH 0113/1106] remove delay from tmp102 (#6577) Co-authored-by: Samuel Sieb --- esphome/components/tmp102/tmp102.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/esphome/components/tmp102/tmp102.cpp b/esphome/components/tmp102/tmp102.cpp index be60a2d8d4..f35fbf5d4b 100644 --- a/esphome/components/tmp102/tmp102.cpp +++ b/esphome/components/tmp102/tmp102.cpp @@ -28,23 +28,24 @@ void TMP102Component::dump_config() { } void TMP102Component::update() { - int16_t raw_temperature; if (this->write(&TMP102_REGISTER_TEMPERATURE, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } - delay(50); // NOLINT - if (this->read(reinterpret_cast(&raw_temperature), 2) != i2c::ERROR_OK) { - this->status_set_warning(); - return; - } - raw_temperature = i2c::i2ctohs(raw_temperature); - raw_temperature = raw_temperature >> 4; - float temperature = raw_temperature * TMP102_CONVERSION_FACTOR; - ESP_LOGD(TAG, "Got Temperature=%.1f°C", temperature); + this->set_timeout("read_temp", 50, [this]() { + int16_t raw_temperature; + if (this->read(reinterpret_cast(&raw_temperature), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_temperature = i2c::i2ctohs(raw_temperature); + raw_temperature = raw_temperature >> 4; + float temperature = raw_temperature * TMP102_CONVERSION_FACTOR; + ESP_LOGD(TAG, "Got Temperature=%.1f°C", temperature); - this->publish_state(temperature); - this->status_clear_warning(); + this->publish_state(temperature); + this->status_clear_warning(); + }); } float TMP102Component::get_setup_priority() const { return setup_priority::DATA; } From 45ae78de03cbed77809f51ee89f461d68fd66907 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:29:56 +1200 Subject: [PATCH 0114/1106] =?UTF-8?q?Create=20``component=5Fdir``=20substi?= =?UTF-8?q?tution=20for=20local=20files=20to=20be=20included=20in=E2=80=A6?= =?UTF-8?q?=20(#6575)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/ci-custom.py | 2 +- tests/components/font/Monocraft.ttf | Bin 0 -> 202764 bytes tests/components/font/common.yaml | 38 ++++++++++++++++ tests/components/font/test.esp32-c3-idf.yaml | 24 +++------- tests/components/font/test.esp32-c3.yaml | 24 +++------- tests/components/font/test.esp32-idf.yaml | 24 +++------- tests/components/font/test.esp32.yaml | 43 +++--------------- tests/components/font/test.esp8266.yaml | 24 +++------- tests/components/font/test.rp2040.yaml | 24 +++------- .../build_components_base.bk72xx.yaml | 1 + .../build_components_base.esp32-ard.yaml | 1 + .../build_components_base.esp32-c3-ard.yaml | 1 + .../build_components_base.esp32-c3-idf.yaml | 1 + .../build_components_base.esp32-idf.yaml | 1 + .../build_components_base.esp32-s2-ard.yaml | 1 + .../build_components_base.esp32-s2-idf.yaml | 1 + .../build_components_base.esp32-s3-ard.yaml | 1 + .../build_components_base.esp32-s3-idf.yaml | 1 + .../build_components_base.esp8266.yaml | 1 + .../build_components_base.host.yaml | 1 + .../build_components_base.rp2040.yaml | 1 + 21 files changed, 87 insertions(+), 128 deletions(-) create mode 100644 tests/components/font/Monocraft.ttf create mode 100644 tests/components/font/common.yaml diff --git a/script/ci-custom.py b/script/ci-custom.py index 3be7be76a2..c3cfc7a331 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -58,7 +58,7 @@ file_types = ( ) cpp_include = ("*.h", "*.c", "*.cpp", "*.tcc") py_include = ("*.py",) -ignore_types = (".ico", ".png", ".woff", ".woff2", "") +ignore_types = (".ico", ".png", ".woff", ".woff2", "", ".ttf", ".otf") LINT_FILE_CHECKS = [] LINT_CONTENT_CHECKS = [] diff --git a/tests/components/font/Monocraft.ttf b/tests/components/font/Monocraft.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4066b0a9889c2505d31b953487ac1d48fafaf9e9 GIT binary patch literal 202764 zcmZQzWME(rU}RumVPJ4`3-O)5@W3htX7LXU3=A^vF0O8DJGes`n7+3#FfgdN2lxly zl)ov*z$|`%fq}umJvh|q{=9uB7?{4VU|>)X^AFZH(h2;i!oa}T!N9HP9*Dx?JaHQu{rZuez?`B|d zlwn|ERLMw9OsOi<5M^Lsa30 z%Y5K}2SXyu28Is_42=S*&seXOp01!%t1B}nT@R11}QvXY!D5?$m-D9gyhk~ z0mMf)1DhDS8gxFgKgiLKZXP-xWIi%Rm!~x!6t2ja*7nicd{Eee{D+Ok<`$4zm^h4$ zi-xJAH4U?$+V+6Lgcvi4Rfo?kP~L%=17ahq1&Nd5M^e-x+e5BCkQpF1g6N@!LGA`& zn7fhLFfnS-Aag;O8fFn=1}F`oV?z39?LK;#Pis2}g(0c(1MOa9`$731gh}-SrRtI0 zK&e?HNk1suuwin-0Avn1`lzJ`R94|~1G*fDkIi0?I4(1=sRyY=#@N)1ib48nX7;9Y+}qn*Mo~ssGcAskIP(`IHmew=74Bqvj~L|NIfA8 zG6RHR>d@IBJ_y6)k!f^sWIj3@#)r|!=D^q>IZ(WUXb^^pgT!ELbQ+`%9fQ<>#6UDY z3{r!R@u?q`!(D!Y!jT@~LykQlJs=EoA2J&zhA)l5)Zn5)=7BISc}m5JH5*+Wh!4V) z+CQovGu^||3ydZ=jEIdNkUhw1k=Y=-(fJIl3m6zU3K)=RMjh4#APfy@c;vZ$^-@m^#le6EdvGyT?PgQ{R<2X zMhh4iObZwoEF2gZtOXbtY#%T%IBZ~GaBg5=a0_5y@Kj)6@czKS;CFz5A#ef%Lr4Mx zL%0D0188J_)ZXD6&akva?{qa#_A)Rqfaa4xSOzpV#lXPH#J~(@$uO{RJY!&BT*1h| zz|OFOp^jkRb8mM zUG<>qdDR=L&(zq|IMhVc6x7VsoYd0Q^3__^rmHPgJE3-7?S(p%I;T3Hx`eumx{rE_ z`VGxk%@bM%T0vSzv;(vwv|sC_=@ja!>PhM8=~?JG>UkRo8LBSVTyD7BdU^En%H@sA zS1#Z1oAtN+Z|&cvzpa0}{Pz3L{r~U(|KNGP1q|yLg%~v$4H%6W9T|NXBN$T{GZ;%4 z8yWi;XE82k+{Cz@aW~^h#>sL#4?Fx5z9K3T`UJ!F0foIz|IPn5{$KxpEd#^O8{5Suv_g~?^z<-v13I8Jg z`Tz6#=l;*^pU^*ne@qMvfA9Rg^!L=?V}E!4-Trs$-_3v5{;mC6{kQ6G*iX3AspV7C+l6mC-xj=i`sT@-#~}Njl|GAk`sV4gr&pd{dU}e1;pw`kE1oWR zI^k*G)1Ig8PuremJvC!scvAnQ;z{C@s3*Zsd>9y>2tN^e!pFeygz53C$1ff~e|+un zmB*JK*E}wIoXo)R*!QvLV~@w~kL4aqJbL-){-gbmwlXk0TJvZr$i7FF3=EI_7#JSO zgLn)K4@DUm9(=za0^!})zAty5^*+nJpZDI~yU)OIZ|}W5_trqEzKyk%gx5zfGHJ&=Lny5n_=t3_85Ai5bC z;>2Mr1_rKT1_rJ)t~dq;wmIPWd4v=ii?NS^ff1wzNt|&8<1!>Jkxa%(_!NTIQQ%O( zSc0yXaRxS}sHzyJK-4hqfY7KU4$)O0Rg7yG_c1Uq9%5i%Ji@@hc#iQ3;|&G|#yeoS zCyehHe}GsB%=ibwX5wHHW0HfgK_rt5h{TSWxEL6i1Q-~YgkbVa0!(5|5+FTHGE8zz z3QS5&It&a<;58FWMhpy077PrGpvD~oqaLF^BX~swqamXiWc>r91*0WnC}SANG{$ho z2*yapD8^{U7{*wT2m=FS9Ai8K17iYXB4ZLrgfW>hg{gw662xYzVyXtyHB7b4>>w61 z2Q%2W3=GV7neQ<$FyCijV1B^-koge<1M_1B2IeQsPZ=1PpD{mYegRU?z`*>H`4#hP z=0D7Tng20^N@xZa1{OvZEf#H%Jc|yCE{h(EK8qOx1B*FitqB7IizSN{i!}oSiw%n{ ziyZ?4i#>}2iz7%q0|Sc_OCU=SOE3#KXRw5_gfTF%gtJ7jOasZYOlR4}z`(MbWe>{{ z1_qX+EXP=mGcd560Lz_ZImL3Cfq~@=%Q*%Hmh&vv7#LWtv)lm7-DJ57rth)bXJBA? z!19oRf#ng)W0t2Z&%ol(SzfTbWckMO9nAZ|@{{Ek%Wp9I56fSce=K01F@P4YFfcH% zGO{wUinB_vN-{98N`X|dN;5F9%CH)-8Z$7kny{LJ#mrd2D^wU5SS?sBK_UzctX8ZJ zP?jTW2m=FaC?PBd_?O|YG?Pcv_?Ps0HI*D~M z>lD_htkYPhv(8|h$vTUHfps?P90mr~xvcX+dO>{%*2Sz#SeG&|ur6a=!N3MeZJ=d1 z;8qVr34~;10Ikmfv0*fbjSYiV@PJxUEDWp+Yz*uS91NTcTnyX{JPf=Hd<^^y0t|u- zLJYzTA`GGoVhrL85)6_IQVh}zG7Pc|at!he3Ji)2N({;jDh#R&Y7FWO8Vs5YS`69@ zIt;oDdJOsu1`LJ_MhwObCJd$wW(?*G77UgQRt(k*HVn25b`16m4h)VAP7KZrE)1>= zZVc`W9t@rgUJTw0J`BDLehmH$0Sti*K@7nRAq=4mVGQ965e$(GQ4G-xF$}Q`aSZVc z2@Hu03=FLdZ44a@T?~^LrZdcCSj4c9VKKu}h9wNk7*;T>Vpz$rnqdvYI)=3j>lrpM zY+~5Lu$f^i!*+&k47(Y2G3;U3%VN(^$dJO2%8<$8$WYF(fFYkDn`H_^DMJ>^IfizI znJkAHiok)F&QQj(onoEe%Kx*3`ndKfwx z`WX5dCNT6eOktSHz`!t>VK&1YhFKuv85kG}7#J8qDIT29K`Y5XYtq1SpnQdlLF>{M zFfcHDVPIf%U|?XJz`(%xfq{XEhk=2~gMopmfq{W(2Ll7s15hGhU|^PEU|=?3U|`N+ zU|`7J zz`!oRz`!2Cz`#C-fr0%B0|SQ$0|Q3@0|Q3`0|Unz1_q8R3=EtC3=Et$3=EtZ3=Eu8 z7#KKjFfefbU|`@fVPN1YU|`@{!N9a|3j>3|9R>zL5e5c98wLi!5(Wmr4Gau| zZx|SaWEdEPA{ZEidKeglwlFXVePCb^R$yQdu7LF1h2JnRh^R0yh&V7Xh*U5zh-`r3 z8w?DhDhv#wDGUsvI~W+m1Q-~^Y#12CrZ6yw?OasdN_+GVPH@>!N8y@z`&rIz`&q7g@Hl!4g-Ul4g-T)4FiMP6$S=%69xwL z4h9DG7YqylgV7xZ z24f8d2ICe62IB_|3?@1Z3??}Y3??fW7)-t}Fqk?pFqqC@U@*PHz+fiBz+e``z+kq5 zfx+wx1B1B_1B3Yz1_tv#3=9?_3=9@)7#J+RFfdr!FfdqlFfdr&U|_IPVPLQ-VPLR2 z0vV~X_F-VKZed`sKEuFZBf!95}Gy-5v%8yEhCB_6iIP_6ZCO_DdKT?C&rzILI(CI21reejFJX7#wXF7#wFXFgQM7 zU~tl4U~np6U~oFZz~C&wz~EfLz~Fp?fx$(Efx)GOfx+bj1B0so1A}W01B2@x1_n0? z1_rkr1_rka3=Hl%3=HlK3=HmP7#KVZ7#KWy7#KXxFfe%XFfe!~Ffe#-U|{fk!@%IB z!oc8Fz`)?OgMq=Dhk?O6hJnF*3j>1>2Lppo0t1831_lOS4h9C_7zPI44Gau^JPZte z2@DK=TNoJp85kJ+Qy3Wh4=^zJKVV=8&|zQ*NMT?ISi-;%@PL6KP=SFV&<8Sl6?lh% zA&7^8A;^J&A!q^vL(m=uhM+$T48bl848aWy48cbj7{JAy1p`A!4Ff~S0S1Op1_p-E z5C(?O84L`e7Z@1A1Q-~?0vH&=au^uGrZ6ysonc@I`@+Bw?!v$jK8JxJ{0jp^gbf2j zL<0ju#1#gHNDT&t$PxyI$UO`Ukv|w1qC6NFqLwf)MEzl4hz?<3h~B}#5F^3B5L3ax z5OalrAy$TgAvTABA$AV~LmUSKLtG34L);t&hPW>b4Dk^R4DlNn7~=mhFeGR&FeKzK zFeL0@U`XU(U`PyMU`SlSz>s)_fgwqQfg!1Yfgx!F14Gge28Lt@28QGo28QGl3=Am( z3=Am|3=Anf3=An37#LCo7#LDR7#LDlFfgQkU|>j7U|>i~U|>j_!N8Dqfq@}ifq@}C zgMlG^2?Im=69$G16$XZk1O|qT6$}g+Zx|Rd4Hy_QOBfh3w=ghd{$OCp@?c=dYGGi= z+QYz*^@V{U+k$~1yM%!u`v3z&4hsWAjtc`rP6Go&&K?GaTm}Y)TpI?4+zJMU+&v87 zN-xiZfg!Jkfgx`P14G^i28MhC28R3$28R443=H{i7#Ipv7#Ioy7#IqsFfbHcVPGiu z!N5>x!N5>hz`#(rhJm5*4Ff}w0s}))2m?dW1O|qp3k(d!5)2H*84L`?a~K$kFEB8a z@GvlxI504jchZLTEW0jx`%RNP=-s1#sesPtf9sO(^1s64~K zP{qT*P!+(yP&I{tq3QqwLp28jL$wbBLv;=VL-hs*hUy;-3^fi63^fxN7;4rqFw`7j zV5oV(z);J|tPNdBDKXs=&a|n!v!& zI)Qwk=)=G;v4w$Q z;uQvlNd^oIlM)yhCQV>qm~?=FVbTuS|fnkaQ1H+UU28Jm! z7#OD9U|^Uk!N4%pf`MUb0RzL-H4F?>zc4UNb6{YYR>QzB?FIwGbO{EA=`IWm(;FBV zrmtaOnEr%;VFm{S!weS&h8YbE3^SH6FwD5az%WyVfnjD11H;TU3=A`GFfh#GU|^W# zz`!u8fPrDw3I>K*Hy9XZ%P=s^PGDe|J%fQ^_6r7vIW`Oob2=Cp=Imi$n9IPxFxQ5G zVQv8f!`uxF40B&FFwE0nV3?P}z%XwG0|TgmKR<+lVLpg`gMndz1Ovl@2nL1)GZ+{a z++bi>D8s<8FoA(#;RFVTg?ktn7MU4hLv|17*@G3Fsxd^z_98I1H)91IL=6BroQ?qFb8C&Iw6u7H7I-3|tZ^$ZLQ>jM}V)-PaS z0Bt$gkifvOVFd%jhBpih8yy%JHcnw+*m#72VG{!b!zL33hD{|544c+4Fl>6jz_3|? zfnl=;1HlX%wZ7K{5+X@&Mwk=>_*!F~hVY>hW!}bUUhV4BJ4BM|TFzn!9VAx^8z_25MfnmoQ z28JCs7#Mb{Ffi=&U|`regMne^4F-l?0t^hh5*QeEwJj(qGt|tr(yEzyb zb{jA-><(aH*xkXvu=@Z5!|p!}40{|H81|GfFzngDz_8~71H)bg28O*c3=Df$Ffi^0|pEX2Vxi)4snZv+v>0tFdSRKz;NsW1H*9^28QDb3=GE|7#NP{Ffbfnz`$_)3IxW zhEp>b7)~8vU^w-Jf#Ea@1H)+(28PoG3=F5&Ffg2c!oYBbhk@Zt2m`~J3I>KVD;OBg zykTHCtHHo<)`NlJYy$(s*%J&5=Xe+x&N(nJoSVSFaP9~L!?_;}4Cf6P7|zEqFr1&l zz;ONs1H%Oc28IhE3=9_<7#J>WU|_iLfq~(o37_J;(V7SV_z;M-pf#K>L z28OG57#OZ;Ffd#zU|_hmg@NJP2L^`gCJYSM=P)o_f5O0ULxX|gMhOGMjROn}H(3}M zZYD4=+?>L|aPtWR!z}{_hFcRD7;c?mV7RToz;HW(f#LQB28P>T7#Qw2FfiPyVPLrP zfPvwz3j@R53I>L|I~W-5aWF94D`8-`cY=Z8J_iHC{TK#@`&$?o9`Gh28KsR7#JQ4FfcrhU|@LM!@%(P z1Ovkp76yhV9t;dmDi|1^Y++z{@`8cksSX3f(+mcNr)wA(p0O}6JhNe7cvix|@azl& z!*dY^hUWI?(Js}~FmuSFObURN+MygtCd@cIt}!y63-hBq+`3~%NzFub|J z!0=Xqf#Gck1H;<_28Op&7#QAOU|@L1!@%&)f`Q>(0RzLkJq!%*zA!Mnw_sp+-@w4| zeh&k~2Mz{?4=xN0A2JviK1^U>_;7-O;iCit!^aK=hL0B*7(O{LFnnrZVEA;0f#I_Z z1HmLS& zZy^i}-3!0=-Z1H+FC3=BVc7#M!q zFfjbgU|{$;fq~)Y9tMV=e;62kSuimCDqvvvwSa-)*Bu51(16o#9R`Nq84L`+moPB= ze!#%+M}~pnPX+_SpB)Sge;F7U{@O4w{B2=i_`8RJ;qM;?hJPLm4F9GuF#Nl~!0=y$ zf#H7yc!V7^YQxA7!NAC{hJlgc4+A5k2LmJH1O`UNI}D6WA`Fa7F$|1MYZw@r-Y_sS zn=mjkH!v_VA7Ef)5n*6tNnl`PnZUrva)E)7)qsJKbpit;>k|e>HXQ~=wgv`9wi67D z>@p0D>?sV4>>C&u**`Eaa>Ot&avWh`1e1EZD>1Ebay21acb21e}+21e~G42(J+42(J}7#MX$7#MY17#Q_f z7#Q^$7#Q`wFfi)dFfi(GVPMq%z`$sr!@y{e!N6#+gn`lE4FjX01p}jD4+Ep&2?j|tOuWno}6O<`a( zUBSR;dW3<|^bZ50SpWm0*&GH&vpWoo<|+(~<~|IJ<}D12=6e_z&0jDuT8J<(TI4V= zS}b5-w79~+Xeq$JXz9biXxYNRXnBEw(TazG(aM8?(P{w$qty=vMr#uWM(Y9wM(ZgI zjMh6C7_ILxFxqf1Fxmt#Fxt#uV6=I_z-Y_Ez-a5jz-W7ffzeKYfzfUS1Ebv=21a`a z21fe|21ffG42<@F7#JNq7#JO3L)(Z_^=(Wixh z(dP&Qqb~;oqi+TSqwf|5M&BS7#M@sFfax`VPFgiU|scZ2~v z*%EKVz!=}cz!<-QfieCK17m^;17ku017ku517pGg2F8Rx42+3342+2-42+3u7#I_8 zFfb8QV9Yzfz?jd$z?kpAz?h%Gz?i>)fieF917m>%17m>$17krA z17pDv2F8Ln42*>#42*>}42*>v7#IsbFfbOmFfbN%FfbNfU|=kkVPGuIU|=lX!oXOf z!N6Ej!oXN^fPt};hk>y)f`PGg2?Jy42L{G68wSR*4hF`uD-4X~3Ji?pISh>DI~W)% zSQr>90vH%8<}ff;ykKCgG+|(@Y+zulJj1|PCBeX0mBPSSwSj@L>JI~BwFd)Z^%Mri z>IV#rH98E8H5CkuHAfg2YXuk>YhxG~YgaHZ)_!4NtaD&stm|Q5th>R$Sg*psSYN=v zSigsXv4Mkuu_1(kv0(uNW5XK;#zqSU#>N%~#>NW_j7>5Oj7=E~j7?h@7@HXw7@K_< z7@KD>Fg8D7U~Dm9U~H*jU~D z4269&ef1_s8SGYpKq5)6#JDGZFg8yFaS|1dE2c`z{cO<`c{ zd%(chufxFDU%|lGe}sW?f&c^Kgct_K2`d;FCwyUGoan&7II)L;apDaI#z`s+jFSo& z7$@yvV4Tdsz&JUCfpPKz2FA&67#OEmFfdMOVPKqcfq`+V33|HiLn2+7kxG=>`mp(`y(Qr=MV8oc@FXG~vrQLxzEIh6MxTj1UIK83hcCGkO>p zXRKggoN9yKWiT+#YGGiUwSa+f)*c4NSvMFMXMJH{obAKFIQs|#GFwT3zz&QU81LHyy2F67_42(-y7#Nq%VPISy!oavvhk8wSSp zB@B!k&oD4-b2 z7_YBkV7$GAf${bO2F5!&42*Yr7#Q#TVPL%5z`%G`d1LKbx2F71K42*x)Ffjgm06KqwfeEx{p3#Nr0s}JxJA>XvMh1PGP3$ZT zZ;dvxvEH%S#LUF-)`(#vGf0>fB)o~8jq#7oCPt9Bv7n-$y0M_L3ZufG8#kCP{54Qv z>Huxz{{T9VfY}3VCWES=vY@!BvM6IlMMcF2rii~QK2%hIrlOb_DwrY|AHdWxnktJb zgVa5!sHk9y`0(KaXc`Kv5VXginL(a`LD4%C65Cs+h2LWg*_ybb}V+J_P89?r2%m9Zq zD3}=;Di|2R@gWSdTiH}u5bl0KV^Da2Vg_tlMMVWDXe+?J!O#oRWh|%+4o_oIV?mJL z;SNF7%E(Z`_yA@nD0UJ41;wYbC_*12lI`Gds$hy>y1>BAz@Q3C2_QFSfWi|Lpdgw7 z;-?IT4-D)K3?O$Qy9KfWhA{(_3PEcH8BG;MLFp6ZHLx5w9WzvbV?J&EQmm<4HN?)*MQA~>4T&@L1j}=Is>^#Sr8J|#-_%CilQK& z{+j`E1;nI(Gb)%Oz)Z%Bzbh)hp^jo6H#ok);S36GrU+1|eW+l%P*L$u0i4%B{Xs}N z1>3DG3QM0LHzc8J<53(GejtB9LIn~dAiu!kq5_Bki8%biUEk7 zpb!A13{X0Ng&)YxQ2Rk~0!rK<2Oz=#>IZNrfXXm%+5(vgEgxV8FlK<_5o9!^Sgc@R zVgRRerU*!R4Js9cVW}OY?wH|plK=L^_9;6X@5@IW;90Az{ zj#Z@c1j9B^dIYbA0h=StAPy;6kmCkq2D~nVgeb@uP>djGP?BI|VEEqwE}w)M7+6g~ zX%bYPDuVO8vY@djQ^Y?7Q2JoZ03~g3jrVWH2T*FQsQ9}A`XheDF^Jl-DaN{(@GAA<_gWPl56UI7fg&9+E#m@dVNe3Oi7(2CCX% z#m%1`NcKYXgYqFbRfEe4K}ejz^n<(&EuBElM%WE5lZ*wG!FDSO!tDH~PyvcKricpA zh!&DvVD+FJq-?5aswfBwpAR1xA3*aDNHxN2PEdG&=W9V_ zQBde9f=Vce>p|HN6lkEj=t0H58KBY?U?rRLFw; z4Qp9}Ohr!*?2tMJl*$-0KKxzr0hH^}@;o>#Lh>D`oB=rq>H$%7F42U-)1h;NXeK=m-BJqB-sf^