From 8e4b9c3c1e38f8aead90f19aa726143e86aad1a6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:45:26 +1300 Subject: [PATCH 001/193] Voice Assistant improvements (#5827) --- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 2 + .../i2s_audio/speaker/i2s_audio_speaker.h | 2 + esphome/components/speaker/speaker.h | 2 + .../voice_assistant/voice_assistant.cpp | 108 +++++++++++------- .../voice_assistant/voice_assistant.h | 3 + 5 files changed, 78 insertions(+), 39 deletions(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index ed13e6b458..e729cdf954 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -220,6 +220,8 @@ size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length) { return index; } +bool I2SAudioSpeaker::has_buffered_data() const { return uxQueueMessagesWaiting(this->buffer_queue_) > 0; } + } // namespace i2s_audio } // namespace esphome diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index b075722e1b..20c36a69d3 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -56,6 +56,8 @@ class I2SAudioSpeaker : public Component, public speaker::Speaker, public I2SAud size_t play(const uint8_t *data, size_t length) override; + bool has_buffered_data() const override; + protected: void start_(); // void stop_(); diff --git a/esphome/components/speaker/speaker.h b/esphome/components/speaker/speaker.h index 3f520e3c5e..b494873160 100644 --- a/esphome/components/speaker/speaker.h +++ b/esphome/components/speaker/speaker.h @@ -18,6 +18,8 @@ class Speaker { virtual void start() = 0; virtual void stop() = 0; + virtual bool has_buffered_data() const = 0; + bool is_running() const { return this->state_ == STATE_RUNNING; } protected: diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 9b13a71039..c0e706305d 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -273,28 +273,27 @@ void VoiceAssistant::loop() { bool playing = false; #ifdef USE_SPEAKER if (this->speaker_ != nullptr) { + ssize_t received_len = 0; if (this->speaker_buffer_index_ + RECEIVE_SIZE < SPEAKER_BUFFER_SIZE) { - auto len = this->socket_->read(this->speaker_buffer_ + this->speaker_buffer_index_, RECEIVE_SIZE); - if (len > 0) { - this->speaker_buffer_index_ += len; - this->speaker_buffer_size_ += len; + received_len = this->socket_->read(this->speaker_buffer_ + this->speaker_buffer_index_, RECEIVE_SIZE); + if (received_len > 0) { + this->speaker_buffer_index_ += received_len; + this->speaker_buffer_size_ += received_len; + this->speaker_bytes_received_ += received_len; } } else { - ESP_LOGW(TAG, "Receive buffer full"); - } - if (this->speaker_buffer_size_ > 0) { - size_t written = this->speaker_->play(this->speaker_buffer_, this->speaker_buffer_size_); - if (written > 0) { - memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); - this->speaker_buffer_size_ -= written; - this->speaker_buffer_index_ -= written; - this->set_timeout("speaker-timeout", 2000, [this]() { this->speaker_->stop(); }); - } else { - ESP_LOGW(TAG, "Speaker buffer full"); - } + ESP_LOGD(TAG, "Receive buffer full"); } + // Build a small buffer of audio before sending to the speaker + if (this->speaker_bytes_received_ > RECEIVE_SIZE * 4) + this->write_speaker_(); if (this->wait_for_stream_end_) { this->cancel_timeout("playing"); + if (this->stream_ended_ && received_len < 0) { + ESP_LOGD(TAG, "End of audio stream received"); + this->cancel_timeout("speaker-timeout"); + this->set_state_(State::RESPONSE_FINISHED, State::RESPONSE_FINISHED); + } break; // We dont want to timeout here as the STREAM_END event will take care of that. } playing = this->speaker_->is_running(); @@ -316,14 +315,26 @@ void VoiceAssistant::loop() { case State::RESPONSE_FINISHED: { #ifdef USE_SPEAKER if (this->speaker_ != nullptr) { + if (this->speaker_buffer_size_ > 0) { + this->write_speaker_(); + break; + } + if (this->speaker_->has_buffered_data() || this->speaker_->is_running()) { + break; + } + ESP_LOGD(TAG, "Speaker has finished outputting all audio"); this->speaker_->stop(); this->cancel_timeout("speaker-timeout"); this->cancel_timeout("playing"); this->speaker_buffer_size_ = 0; this->speaker_buffer_index_ = 0; + this->speaker_bytes_received_ = 0; memset(this->speaker_buffer_, 0, SPEAKER_BUFFER_SIZE); + this->wait_for_stream_end_ = false; + this->stream_ended_ = false; + + this->tts_stream_end_trigger_->trigger(); } - this->wait_for_stream_end_ = false; #endif this->set_state_(State::IDLE, State::IDLE); break; @@ -333,6 +344,20 @@ void VoiceAssistant::loop() { } } +void VoiceAssistant::write_speaker_() { + if (this->speaker_buffer_size_ > 0) { + size_t written = this->speaker_->play(this->speaker_buffer_, this->speaker_buffer_size_); + if (written > 0) { + memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); + this->speaker_buffer_size_ -= written; + this->speaker_buffer_index_ -= written; + this->set_timeout("speaker-timeout", 5000, [this]() { this->speaker_->stop(); }); + } else { + ESP_LOGD(TAG, "Speaker buffer full, trying again next loop"); + } + } +} + void VoiceAssistant::client_subscription(api::APIConnection *client, bool subscribe) { if (!subscribe) { if (this->api_client_ == nullptr || client != this->api_client_) { @@ -503,21 +528,20 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { switch (msg.event_type) { case api::enums::VOICE_ASSISTANT_RUN_START: ESP_LOGD(TAG, "Assist Pipeline running"); - this->start_trigger_->trigger(); + this->defer([this]() { this->start_trigger_->trigger(); }); break; case api::enums::VOICE_ASSISTANT_WAKE_WORD_START: break; case api::enums::VOICE_ASSISTANT_WAKE_WORD_END: { ESP_LOGD(TAG, "Wake word detected"); - this->wake_word_detected_trigger_->trigger(); + this->defer([this]() { this->wake_word_detected_trigger_->trigger(); }); break; } case api::enums::VOICE_ASSISTANT_STT_START: ESP_LOGD(TAG, "STT started"); - this->listening_trigger_->trigger(); + this->defer([this]() { this->listening_trigger_->trigger(); }); break; case api::enums::VOICE_ASSISTANT_STT_END: { - this->set_state_(State::STOP_MICROPHONE, State::AWAITING_RESPONSE); std::string text; for (auto arg : msg.data) { if (arg.name == "text") { @@ -529,12 +553,12 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { return; } ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str()); - this->stt_end_trigger_->trigger(text); + this->defer([this, text]() { this->stt_end_trigger_->trigger(text); }); break; } case api::enums::VOICE_ASSISTANT_INTENT_START: ESP_LOGD(TAG, "Intent started"); - this->intent_start_trigger_->trigger(); + this->defer([this]() { this->intent_start_trigger_->trigger(); }); break; case api::enums::VOICE_ASSISTANT_INTENT_END: { for (auto arg : msg.data) { @@ -542,7 +566,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { this->conversation_id_ = std::move(arg.value); } } - this->intent_end_trigger_->trigger(); + this->defer([this]() { this->intent_end_trigger_->trigger(); }); break; } case api::enums::VOICE_ASSISTANT_TTS_START: { @@ -557,10 +581,12 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { return; } ESP_LOGD(TAG, "Response: \"%s\"", text.c_str()); - this->tts_start_trigger_->trigger(text); + this->defer([this, text]() { + this->tts_start_trigger_->trigger(text); #ifdef USE_SPEAKER - this->speaker_->start(); + this->speaker_->start(); #endif + }); break; } case api::enums::VOICE_ASSISTANT_TTS_END: { @@ -575,14 +601,16 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { return; } ESP_LOGD(TAG, "Response URL: \"%s\"", url.c_str()); + this->defer([this, url]() { #ifdef USE_MEDIA_PLAYER - if (this->media_player_ != nullptr) { - this->media_player_->make_call().set_media_url(url).perform(); - } + if (this->media_player_ != nullptr) { + this->media_player_->make_call().set_media_url(url).perform(); + } #endif + this->tts_end_trigger_->trigger(url); + }); State new_state = this->local_output_ ? State::STREAMING_RESPONSE : State::IDLE; this->set_state_(new_state, new_state); - this->tts_end_trigger_->trigger(url); break; } case api::enums::VOICE_ASSISTANT_RUN_END: { @@ -599,7 +627,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { this->set_state_(State::IDLE, State::IDLE); } } - this->end_trigger_->trigger(); + this->defer([this]() { this->end_trigger_->trigger(); }); break; } case api::enums::VOICE_ASSISTANT_ERROR: { @@ -617,8 +645,10 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { return; } else if (code == "wake-provider-missing" || code == "wake-engine-missing") { // Wake word is not set up or not ready on Home Assistant so stop and do not retry until user starts again. - this->request_stop(); - this->error_trigger_->trigger(code, message); + this->defer([this, code, message]() { + this->request_stop(); + this->error_trigger_->trigger(code, message); + }); return; } ESP_LOGE(TAG, "Error: %s - %s", code.c_str(), message.c_str()); @@ -626,32 +656,32 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { this->signal_stop_(); this->set_state_(State::STOP_MICROPHONE, State::IDLE); } - this->error_trigger_->trigger(code, message); + this->defer([this, code, message]() { this->error_trigger_->trigger(code, message); }); break; } case api::enums::VOICE_ASSISTANT_TTS_STREAM_START: { #ifdef USE_SPEAKER this->wait_for_stream_end_ = true; ESP_LOGD(TAG, "TTS stream start"); - this->tts_stream_start_trigger_->trigger(); + this->defer([this] { this->tts_stream_start_trigger_->trigger(); }); #endif break; } case api::enums::VOICE_ASSISTANT_TTS_STREAM_END: { - this->set_state_(State::RESPONSE_FINISHED, State::IDLE); #ifdef USE_SPEAKER + this->stream_ended_ = true; ESP_LOGD(TAG, "TTS stream end"); - this->tts_stream_end_trigger_->trigger(); #endif break; } case api::enums::VOICE_ASSISTANT_STT_VAD_START: ESP_LOGD(TAG, "Starting STT by VAD"); - this->stt_vad_start_trigger_->trigger(); + this->defer([this]() { this->stt_vad_start_trigger_->trigger(); }); break; case api::enums::VOICE_ASSISTANT_STT_VAD_END: ESP_LOGD(TAG, "STT by VAD end"); - this->stt_vad_end_trigger_->trigger(); + this->set_state_(State::STOP_MICROPHONE, State::AWAITING_RESPONSE); + this->defer([this]() { this->stt_vad_end_trigger_->trigger(); }); break; default: ESP_LOGD(TAG, "Unhandled event type: %d", msg.event_type); diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index f6dcd1c563..66bf4c3c57 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -156,11 +156,14 @@ class VoiceAssistant : public Component { microphone::Microphone *mic_{nullptr}; #ifdef USE_SPEAKER + void write_speaker_(); speaker::Speaker *speaker_{nullptr}; uint8_t *speaker_buffer_; size_t speaker_buffer_index_{0}; size_t speaker_buffer_size_{0}; + size_t speaker_bytes_received_{0}; bool wait_for_stream_end_{false}; + bool stream_ended_{false}; #endif #ifdef USE_MEDIA_PLAYER media_player::MediaPlayer *media_player_{nullptr}; From ff97639f791963bb9e5f35318be960a100f024b0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 28 Nov 2023 09:43:41 +1300 Subject: [PATCH 002/193] Fix missing include in remote_base (#5843) --- esphome/components/remote_base/remote_base.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index 40c699e8ea..095f95053f 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -1,6 +1,8 @@ #include "remote_base.h" #include "esphome/core/log.h" +#include + namespace esphome { namespace remote_base { From 687f5ca6330c7b96e92823bd28f9d68731404152 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 28 Nov 2023 09:57:40 +1300 Subject: [PATCH 003/193] Add 'voice_assistant.connected' condition (#5845) --- esphome/components/voice_assistant/__init__.py | 12 ++++++++++++ esphome/components/voice_assistant/voice_assistant.h | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/esphome/components/voice_assistant/__init__.py b/esphome/components/voice_assistant/__init__.py index d05f39072c..59aef901f2 100644 --- a/esphome/components/voice_assistant/__init__.py +++ b/esphome/components/voice_assistant/__init__.py @@ -57,6 +57,9 @@ StopAction = voice_assistant_ns.class_( IsRunningCondition = voice_assistant_ns.class_( "IsRunningCondition", automation.Condition, cg.Parented.template(VoiceAssistant) ) +ConnectedCondition = voice_assistant_ns.class_( + "ConnectedCondition", automation.Condition, cg.Parented.template(VoiceAssistant) +) def tts_stream_validate(config): @@ -298,3 +301,12 @@ async def voice_assistant_is_running_to_code(config, condition_id, template_arg, var = cg.new_Pvariable(condition_id, template_arg) await cg.register_parented(var, config[CONF_ID]) return var + + +@register_condition( + "voice_assistant.connected", ConnectedCondition, VOICE_ASSISTANT_ACTION_SCHEMA +) +async def voice_assistant_connected_to_code(config, condition_id, template_arg, args): + var = cg.new_Pvariable(condition_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index 66bf4c3c57..f9325dff54 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -222,6 +222,11 @@ template class IsRunningCondition : public Condition, pub bool check(Ts... x) override { return this->parent_->is_running() || this->parent_->is_continuous(); } }; +template class ConnectedCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->get_api_connection() != nullptr; } +}; + extern VoiceAssistant *global_voice_assistant; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace voice_assistant From 28a3cddde374a07c880622cd13095ce7dfbbec4d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:14:26 +1300 Subject: [PATCH 004/193] Bump version to 2023.11.5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5596cbcd36..2e693d0ea0 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.11.4" +__version__ = "2023.11.5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 175f00f41bc8305cfa65bef7142ba7cdd4ee8140 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:28:24 +1300 Subject: [PATCH 005/193] Fix write_speaker without speaker in config (#5847) --- 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 c0e706305d..29fc664342 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -344,6 +344,7 @@ void VoiceAssistant::loop() { } } +#ifdef USE_SPEAKER void VoiceAssistant::write_speaker_() { if (this->speaker_buffer_size_ > 0) { size_t written = this->speaker_->play(this->speaker_buffer_, this->speaker_buffer_size_); @@ -357,6 +358,7 @@ void VoiceAssistant::write_speaker_() { } } } +#endif void VoiceAssistant::client_subscription(api::APIConnection *client, bool subscribe) { if (!subscribe) { From ed9fd173a9f791d0d77ef28e68117fa04c6f8e23 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:31:55 +1300 Subject: [PATCH 006/193] Bump version to 2023.11.6 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 2e693d0ea0..6f8d83170e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.11.5" +__version__ = "2023.11.6" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 8cc44766e6acc236bf2c7e70e24a4c5531a9ffba Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 14 Dec 2023 08:30:45 +0900 Subject: [PATCH 007/193] Bump version to 2023.12.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 101a690db5..b8495a3a7c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.12.0-dev" +__version__ = "2023.12.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 3c3ac920380dd07c38fa11860da21e944f2a1f01 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:24:16 +0900 Subject: [PATCH 008/193] Allow use of CDC/JTAG loggers on esp32 variants with Arduino (#4658) * Allow use of CDC/JTAG loggers on esp32 variants with Arduino * Only on s2/s3 * Separate C3 from S2/S3 * C code builds & runs correctly, still needs work though * Works on S2 * It works! * Remove unnecessary header --------- Co-authored-by: Keith Burzinski --- esphome/components/logger/__init__.py | 2 -- esphome/components/logger/logger.cpp | 32 ++++++++++++++++++++++++--- esphome/components/logger/logger.h | 2 -- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index e431997276..11a7f996f0 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -124,8 +124,6 @@ is_log_level = cv.one_of(*LOG_LEVELS, upper=True) def uart_selection(value): if CORE.is_esp32: - if value.upper() in ESP_IDF_UARTS and not CORE.using_esp_idf: - raise cv.Invalid(f"Only esp-idf framework supports {value}.") variant = get_esp32_variant() if variant in UART_SELECTION_ESP32: return cv.one_of(*UART_SELECTION_ESP32[variant], upper=True)(value) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 2d2524b5f4..e0ca0806cb 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -236,8 +236,13 @@ void Logger::pre_setup() { this->hw_serial_ = &Serial1; Serial1.begin(this->baud_rate_); #else +#if ARDUINO_USB_CDC_ON_BOOT this->hw_serial_ = &Serial; Serial.begin(this->baud_rate_); +#else + this->hw_serial_ = &Serial; + Serial.begin(this->baud_rate_); +#endif #endif #ifdef USE_ESP8266 if (this->uart_ == UART_SELECTION_UART0_SWAP) { @@ -265,12 +270,35 @@ void Logger::pre_setup() { Serial2.begin(this->baud_rate_); break; #endif +#if defined(USE_ESP32) && \ + (defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C3)) +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) + case UART_SELECTION_USB_CDC: +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) + case UART_SELECTION_USB_SERIAL_JTAG: +#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3 +#ifdef USE_ESP32_VARIANT_ESP32C3 + this->hw_serial_ = &Serial; + Serial.begin(this->baud_rate_); +#endif // USE_ESP32_VARIANT_ESP32C3 +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#if ARDUINO_USB_CDC_ON_BOOT + this->hw_serial_ = &Serial; + Serial.begin(this->baud_rate_); +#else + this->hw_serial_ = &Serial; + Serial.begin(this->baud_rate_); +#endif // ARDUINO_USB_CDC_ON_BOOT +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 + break; +#endif // USE_ESP32 && (USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3) #ifdef USE_RP2040 case UART_SELECTION_USB_CDC: this->hw_serial_ = &Serial; Serial.begin(this->baud_rate_); break; -#endif +#endif // USE_RP2040 } #endif // USE_ARDUINO #ifdef USE_ESP_IDF @@ -393,14 +421,12 @@ const char *const UART_SELECTIONS[] = { "UART2", #endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARINT_ESP32C6 && !USE_ESP32_VARIANT_ESP32S2 && // !USE_ESP32_VARIANT_ESP32S3 && !USE_ESP32_VARIANT_ESP32H2 -#if defined(USE_ESP_IDF) #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) "USB_CDC", #endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) "USB_SERIAL_JTAG", #endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3 -#endif // USE_ESP_IDF }; #endif // USE_ESP32 #ifdef USE_ESP8266 diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index 3816b1dd14..68efc056df 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -45,7 +45,6 @@ enum UARTSelection { UART_SELECTION_UART2, #endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32C6 && !USE_ESP32_VARIANT_ESP32S2 && // !USE_ESP32_VARIANT_ESP32S3 && !USE_ESP32_VARIANT_ESP32H2 -#ifdef USE_ESP_IDF #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) UART_SELECTION_USB_CDC, #endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 @@ -54,7 +53,6 @@ enum UARTSelection { UART_SELECTION_USB_SERIAL_JTAG, #endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || // USE_ESP32_VARIANT_ESP32H2 -#endif // USE_ESP_IDF #endif // USE_ESP32 #ifdef USE_ESP8266 UART_SELECTION_UART0_SWAP, From 3e475c21ff9664c95b347375fe43b5647afdad03 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 14 Dec 2023 05:47:31 +0100 Subject: [PATCH 009/193] [Logger] ESP32 S3 serial logger (#4853) * Add support for ESP32 S3 logger. * fix default * Remove cpp & h changes to combine with PR #4658 * Not enough attention to details. * Add build flag * Validation fix * Fix validation for real this time --------- Co-authored-by: Your Name Co-authored-by: Keith Burzinski --- esphome/components/logger/__init__.py | 10 ++++- esphome/config_validation.py | 53 +++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 11a7f996f0..be302bd489 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -97,7 +97,7 @@ UART_SELECTION_LIBRETINY = { COMPONENT_RTL87XX: [DEFAULT, UART0, UART1, UART2], } -ESP_IDF_UARTS = [USB_CDC, USB_SERIAL_JTAG] +ESP_ARDUINO_UNSUPPORTED_USB_UARTS = [USB_SERIAL_JTAG] UART_SELECTION_RP2040 = [USB_CDC, UART0, UART1] @@ -124,6 +124,8 @@ is_log_level = cv.one_of(*LOG_LEVELS, upper=True) def uart_selection(value): if CORE.is_esp32: + if CORE.using_arduino and value.upper() in ESP_ARDUINO_UNSUPPORTED_USB_UARTS: + raise cv.Invalid(f"Arduino framework does not support {value}.") variant = get_esp32_variant() if variant in UART_SELECTION_ESP32: return cv.one_of(*UART_SELECTION_ESP32[variant], upper=True)(value) @@ -169,6 +171,8 @@ CONFIG_SCHEMA = cv.All( CONF_HARDWARE_UART, esp8266=UART0, esp32=UART0, + esp32_s2=USB_CDC, + esp32_s3=USB_CDC, rp2040=USB_CDC, bk72xx=DEFAULT, rtl87xx=DEFAULT, @@ -256,6 +260,10 @@ async def to_code(config): if config.get(CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH): cg.add_build_flag("-DUSE_STORE_LOG_STR_IN_FLASH") + if CORE.using_arduino: + if config[CONF_HARDWARE_UART] == USB_CDC: + cg.add_build_flag("-DARDUINO_USB_CDC_ON_BOOT=1") + if CORE.using_esp_idf: if config[CONF_HARDWARE_UART] == USB_CDC: add_idf_sdkconfig_option("CONFIG_ESP_CONSOLE_USB_CDC", True) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index ad2ee11512..fdbe7d6320 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1528,6 +1528,12 @@ class SplitDefault(Optional): esp32=vol.UNDEFINED, esp32_arduino=vol.UNDEFINED, esp32_idf=vol.UNDEFINED, + esp32_s2=vol.UNDEFINED, + esp32_s2_arduino=vol.UNDEFINED, + esp32_s2_idf=vol.UNDEFINED, + esp32_s3=vol.UNDEFINED, + esp32_s3_arduino=vol.UNDEFINED, + esp32_s3_idf=vol.UNDEFINED, rp2040=vol.UNDEFINED, bk72xx=vol.UNDEFINED, rtl87xx=vol.UNDEFINED, @@ -1541,6 +1547,26 @@ class SplitDefault(Optional): self._esp32_idf_default = vol.default_factory( esp32_idf if esp32 is vol.UNDEFINED else esp32 ) + self._esp32_s2_arduino_default = vol.default_factory( + (esp32_s2_arduino if esp32 is vol.UNDEFINED else esp32) + if esp32_s2 is vol.UNDEFINED + else esp32_s2 + ) + self._esp32_s2_idf_default = vol.default_factory( + (esp32_s2_idf if esp32 is vol.UNDEFINED else esp32) + if esp32_s2 is vol.UNDEFINED + else esp32_s2 + ) + self._esp32_s3_arduino_default = vol.default_factory( + (esp32_s3_arduino if esp32 is vol.UNDEFINED else esp32) + if esp32_s3 is vol.UNDEFINED + else esp32_s3 + ) + self._esp32_s3_idf_default = vol.default_factory( + (esp32_s3_idf if esp32 is vol.UNDEFINED else esp32) + if esp32_s3 is vol.UNDEFINED + else esp32_s3 + ) self._rp2040_default = vol.default_factory(rp2040) self._bk72xx_default = vol.default_factory(bk72xx) self._rtl87xx_default = vol.default_factory(rtl87xx) @@ -1550,10 +1576,29 @@ class SplitDefault(Optional): def default(self): if CORE.is_esp8266: return self._esp8266_default - if CORE.is_esp32 and CORE.using_arduino: - return self._esp32_arduino_default - if CORE.is_esp32 and CORE.using_esp_idf: - return self._esp32_idf_default + if CORE.is_esp32: + from esphome.components.esp32 import get_esp32_variant + from esphome.components.esp32.const import ( + VARIANT_ESP32S2, + VARIANT_ESP32S3, + ) + + variant = get_esp32_variant() + if variant == VARIANT_ESP32S2: + if CORE.using_arduino: + return self._esp32_s2_arduino_default + if CORE.using_esp_idf: + return self._esp32_s2_idf_default + elif variant == VARIANT_ESP32S3: + if CORE.using_arduino: + return self._esp32_s3_arduino_default + if CORE.using_esp_idf: + return self._esp32_s3_idf_default + else: + if CORE.using_arduino: + return self._esp32_arduino_default + if CORE.using_esp_idf: + return self._esp32_idf_default if CORE.is_rp2040: return self._rp2040_default if CORE.is_bk72xx: From f28cf9348e486b3aab5c2b3e6a04f372c4ad4364 Mon Sep 17 00:00:00 2001 From: jochenvg Date: Thu, 14 Dec 2023 05:01:01 +0000 Subject: [PATCH 010/193] Support toggle action for template cover (#5917) --- esphome/components/template/cover/__init__.py | 7 +++++++ esphome/components/template/cover/template_cover.cpp | 10 ++++++++++ esphome/components/template/cover/template_cover.h | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/esphome/components/template/cover/__init__.py b/esphome/components/template/cover/__init__.py index 8844ddd6ab..43d0be99b4 100644 --- a/esphome/components/template/cover/__init__.py +++ b/esphome/components/template/cover/__init__.py @@ -31,6 +31,7 @@ RESTORE_MODES = { } CONF_HAS_POSITION = "has_position" +CONF_TOGGLE_ACTION = "toggle_action" CONFIG_SCHEMA = cover.COVER_SCHEMA.extend( { @@ -44,6 +45,7 @@ CONFIG_SCHEMA = cover.COVER_SCHEMA.extend( cv.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_TILT_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_TILT_LAMBDA): cv.returning_lambda, + cv.Optional(CONF_TOGGLE_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_POSITION_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_RESTORE_MODE, default="RESTORE"): cv.enum( RESTORE_MODES, upper=True @@ -74,6 +76,11 @@ async def to_code(config): var.get_stop_trigger(), [], config[CONF_STOP_ACTION] ) cg.add(var.set_has_stop(True)) + if CONF_TOGGLE_ACTION in config: + await automation.build_automation( + var.get_toggle_trigger(), [], config[CONF_TOGGLE_ACTION] + ) + cg.add(var.set_has_toggle(True)) if CONF_TILT_ACTION in config: await automation.build_automation( var.get_tilt_trigger(), [(float, "tilt")], config[CONF_TILT_ACTION] diff --git a/esphome/components/template/cover/template_cover.cpp b/esphome/components/template/cover/template_cover.cpp index b16e439943..2d6c3087ae 100644 --- a/esphome/components/template/cover/template_cover.cpp +++ b/esphome/components/template/cover/template_cover.cpp @@ -12,6 +12,7 @@ TemplateCover::TemplateCover() : open_trigger_(new Trigger<>()), close_trigger_(new Trigger<>), stop_trigger_(new Trigger<>()), + toggle_trigger_(new Trigger<>()), position_trigger_(new Trigger()), tilt_trigger_(new Trigger()) {} void TemplateCover::setup() { @@ -68,6 +69,7 @@ float TemplateCover::get_setup_priority() const { return setup_priority::HARDWAR Trigger<> *TemplateCover::get_open_trigger() const { return this->open_trigger_; } Trigger<> *TemplateCover::get_close_trigger() const { return this->close_trigger_; } Trigger<> *TemplateCover::get_stop_trigger() const { return this->stop_trigger_; } +Trigger<> *TemplateCover::get_toggle_trigger() const { return this->toggle_trigger_; } void TemplateCover::dump_config() { LOG_COVER("", "Template Cover", this); } void TemplateCover::control(const CoverCall &call) { if (call.get_stop()) { @@ -76,6 +78,12 @@ void TemplateCover::control(const CoverCall &call) { this->prev_command_trigger_ = this->stop_trigger_; this->publish_state(); } + if (call.get_toggle().has_value()) { + this->stop_prev_trigger_(); + this->toggle_trigger_->trigger(); + this->prev_command_trigger_ = this->toggle_trigger_; + this->publish_state(); + } if (call.get_position().has_value()) { auto pos = *call.get_position(); this->stop_prev_trigger_(); @@ -110,6 +118,7 @@ CoverTraits TemplateCover::get_traits() { auto traits = CoverTraits(); traits.set_is_assumed_state(this->assumed_state_); traits.set_supports_stop(this->has_stop_); + traits.set_supports_toggle(this->has_toggle_); traits.set_supports_position(this->has_position_); traits.set_supports_tilt(this->has_tilt_); return traits; @@ -118,6 +127,7 @@ Trigger *TemplateCover::get_position_trigger() const { return this->posit Trigger *TemplateCover::get_tilt_trigger() const { return this->tilt_trigger_; } void TemplateCover::set_tilt_lambda(std::function()> &&tilt_f) { this->tilt_f_ = tilt_f; } void TemplateCover::set_has_stop(bool has_stop) { this->has_stop_ = has_stop; } +void TemplateCover::set_has_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; } void TemplateCover::set_has_position(bool has_position) { this->has_position_ = has_position; } void TemplateCover::set_has_tilt(bool has_tilt) { this->has_tilt_ = has_tilt; } void TemplateCover::stop_prev_trigger_() { diff --git a/esphome/components/template/cover/template_cover.h b/esphome/components/template/cover/template_cover.h index 4ff5caf1db..958c94b0a6 100644 --- a/esphome/components/template/cover/template_cover.h +++ b/esphome/components/template/cover/template_cover.h @@ -21,6 +21,7 @@ class TemplateCover : public cover::Cover, public Component { Trigger<> *get_open_trigger() const; Trigger<> *get_close_trigger() const; Trigger<> *get_stop_trigger() const; + Trigger<> *get_toggle_trigger() const; Trigger *get_position_trigger() const; Trigger *get_tilt_trigger() const; void set_optimistic(bool optimistic); @@ -29,6 +30,7 @@ class TemplateCover : public cover::Cover, public Component { void set_has_stop(bool has_stop); void set_has_position(bool has_position); void set_has_tilt(bool has_tilt); + void set_has_toggle(bool has_toggle); void set_restore_mode(TemplateCoverRestoreMode restore_mode) { restore_mode_ = restore_mode; } void setup() override; @@ -50,7 +52,9 @@ class TemplateCover : public cover::Cover, public Component { Trigger<> *open_trigger_; Trigger<> *close_trigger_; bool has_stop_{false}; + bool has_toggle_{false}; Trigger<> *stop_trigger_; + Trigger<> *toggle_trigger_; Trigger<> *prev_command_trigger_{nullptr}; Trigger *position_trigger_; bool has_position_{false}; From 6b5eb7e656440ea03eaa6784172f662b1194bc3b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 14 Dec 2023 17:33:04 +0900 Subject: [PATCH 011/193] Fix SplitDefault with variants (#5928) --- esphome/config_validation.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index fdbe7d6320..7b94608509 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1518,6 +1518,13 @@ class GenerateID(Optional): super().__init__(key, default=lambda: None) +def _get_priority_default(*args): + for arg in args: + if arg is not vol.UNDEFINED: + return arg + return vol.UNDEFINED + + class SplitDefault(Optional): """Mark this key to have a split default for ESP8266/ESP32.""" @@ -1542,30 +1549,22 @@ class SplitDefault(Optional): super().__init__(key) self._esp8266_default = vol.default_factory(esp8266) self._esp32_arduino_default = vol.default_factory( - esp32_arduino if esp32 is vol.UNDEFINED else esp32 + _get_priority_default(esp32, esp32_arduino) ) self._esp32_idf_default = vol.default_factory( - esp32_idf if esp32 is vol.UNDEFINED else esp32 + _get_priority_default(esp32, esp32_idf) ) self._esp32_s2_arduino_default = vol.default_factory( - (esp32_s2_arduino if esp32 is vol.UNDEFINED else esp32) - if esp32_s2 is vol.UNDEFINED - else esp32_s2 + _get_priority_default(esp32_s2, esp32, esp32_s2_arduino, esp32_arduino) ) self._esp32_s2_idf_default = vol.default_factory( - (esp32_s2_idf if esp32 is vol.UNDEFINED else esp32) - if esp32_s2 is vol.UNDEFINED - else esp32_s2 + _get_priority_default(esp32_s2, esp32, esp32_s2_idf, esp32_idf) ) self._esp32_s3_arduino_default = vol.default_factory( - (esp32_s3_arduino if esp32 is vol.UNDEFINED else esp32) - if esp32_s3 is vol.UNDEFINED - else esp32_s3 + _get_priority_default(esp32_s3, esp32, esp32_s3_arduino, esp32_arduino) ) self._esp32_s3_idf_default = vol.default_factory( - (esp32_s3_idf if esp32 is vol.UNDEFINED else esp32) - if esp32_s3 is vol.UNDEFINED - else esp32_s3 + _get_priority_default(esp32_s3, esp32, esp32_s3_idf, esp32_idf) ) self._rp2040_default = vol.default_factory(rp2040) self._bk72xx_default = vol.default_factory(bk72xx) From e030c0fc45c1105cc21e2ddb12db9c0c90766a2b Mon Sep 17 00:00:00 2001 From: mrtoy-me <118446898+mrtoy-me@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:39:05 +1000 Subject: [PATCH 012/193] Update ENS160 TVOC device_class and AQI units to match required by HA (#5939) --- esphome/components/ens160/sensor.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/ens160/sensor.py b/esphome/components/ens160/sensor.py index 55f0ff7b6f..393b63bae1 100644 --- a/esphome/components/ens160/sensor.py +++ b/esphome/components/ens160/sensor.py @@ -9,7 +9,7 @@ from esphome.const import ( CONF_TVOC, DEVICE_CLASS_AQI, DEVICE_CLASS_CARBON_DIOXIDE, - DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, ICON_CHEMICAL_WEAPON, ICON_MOLECULE_CO2, ICON_RADIATOR, @@ -45,11 +45,10 @@ CONFIG_SCHEMA = ( unit_of_measurement=UNIT_PARTS_PER_BILLION, icon=ICON_RADIATOR, accuracy_decimals=0, - device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, state_class=STATE_CLASS_MEASUREMENT, ), cv.Required(CONF_AQI): sensor.sensor_schema( - unit_of_measurement=UNIT_INDEX, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, device_class=DEVICE_CLASS_AQI, From 514db8b26e8614902b0e8c849802d22709df018b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:02:47 +0900 Subject: [PATCH 013/193] Bump version to 2023.12.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index b8495a3a7c..6c2a520331 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.12.0b1" +__version__ = "2023.12.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 94904f44f9d3d4f596f93faa1f7f7ec0d98129d5 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Mon, 18 Dec 2023 00:19:30 +0100 Subject: [PATCH 014/193] UARTComponent inline doc (#5930) --- esphome/components/uart/uart_component.h | 88 +++++++++++++++++++++++- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/esphome/components/uart/uart_component.h b/esphome/components/uart/uart_component.h index 34bda42bb5..e03784fdd8 100644 --- a/esphome/components/uart/uart_component.h +++ b/esphome/components/uart/uart_component.h @@ -31,40 +31,122 @@ const LogString *parity_to_str(UARTParityOptions parity); class UARTComponent { public: + // Writes an array of bytes to the UART bus. + // @param data A vector of bytes to be written. void write_array(const std::vector &data) { this->write_array(&data[0], data.size()); } + + // Writes a single byte to the UART bus. + // @param data The byte to be written. void write_byte(uint8_t data) { this->write_array(&data, 1); }; + + // Writes a null-terminated string to the UART bus. + // @param str Pointer to the null-terminated string. void write_str(const char *str) { const auto *data = reinterpret_cast(str); this->write_array(data, strlen(str)); }; + // Pure virtual method to write an array of bytes to the UART bus. + // @param data Pointer to the array of bytes. + // @param len Length of the array. virtual void write_array(const uint8_t *data, size_t len) = 0; + // Reads a single byte from the UART bus. + // @param data Pointer to the byte where the read data will be stored. + // @return True if a byte was successfully read, false otherwise. bool read_byte(uint8_t *data) { return this->read_array(data, 1); }; + + // Pure virtual method to peek the next byte in the UART buffer without removing it. + // @param data Pointer to the byte where the peeked data will be stored. + // @return True if a byte is available to peek, false otherwise. virtual bool peek_byte(uint8_t *data) = 0; + + // Pure virtual method to read an array of bytes from the UART bus. + // @param data Pointer to the array where the read data will be stored. + // @param len Number of bytes to read. + // @return True if the specified number of bytes were successfully read, false otherwise. virtual bool read_array(uint8_t *data, size_t len) = 0; - /// Return available number of bytes. + // Pure virtual method to return the number of bytes available for reading. + // @return Number of available bytes. virtual int available() = 0; - /// Block until all bytes have been written to the UART bus. + + // Pure virtual method to block until all bytes have been written to the UART bus. virtual void flush() = 0; + // Sets the TX (transmit) pin for the UART bus. + // @param tx_pin Pointer to the internal GPIO pin used for transmission. void set_tx_pin(InternalGPIOPin *tx_pin) { this->tx_pin_ = tx_pin; } + + // Sets the RX (receive) pin for the UART bus. + // @param rx_pin Pointer to the internal GPIO pin used for reception. void set_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_pin; } + + // Sets the size of the RX buffer. + // @param rx_buffer_size Size of the RX buffer in bytes. void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; } + + // Gets the size of the RX buffer. + // @return Size of the RX buffer in bytes. size_t get_rx_buffer_size() { return this->rx_buffer_size_; } + // Sets the number of stop bits used in UART communication. + // @param stop_bits Number of stop bits. void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; } + + // Gets the number of stop bits used in UART communication. + // @return Number of stop bits. uint8_t get_stop_bits() const { return this->stop_bits_; } + + // Set the number of data bits used in UART communication. + // @param data_bits Number of data bits. void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; } + + // Get the number of data bits used in UART communication. + // @return Number of data bits. uint8_t get_data_bits() const { return this->data_bits_; } + + // Set the parity used in UART communication. + // @param parity Parity option. void set_parity(UARTParityOptions parity) { this->parity_ = parity; } + + // Get the parity used in UART communication. + // @return Parity option. UARTParityOptions get_parity() const { return this->parity_; } + + // Set the baud rate for UART communication. + // @param baud_rate Baud rate in bits per second. void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } + + // Get the baud rate for UART communication. + // @return Baud rate in bits per second. uint32_t get_baud_rate() const { return baud_rate_; } + #ifdef USE_ESP32 - virtual void load_settings() = 0; + /** + * Load the UART settings. + * @param dump_config If true (default), output the new settings to logs; otherwise, change settings quietly. + * + * Example: + * ```cpp + * id(uart1).load_settings(false); + * ``` + * + * This will load the current UART interface with the latest settings (baud_rate, parity, etc). + */ virtual void load_settings(bool dump_config) = 0; + + /** + * Load the UART settings. + * + * Example: + * ```cpp + * id(uart1).load_settings(); + * ``` + * + * This will load the current UART interface with the latest settings (baud_rate, parity, etc). + */ + virtual void load_settings() = 0; #endif // USE_ESP32 #ifdef USE_UART_DEBUGGER From 003d8b0cf573ce5a6397fe00c398d6a789012033 Mon Sep 17 00:00:00 2001 From: Grant Le Roux Date: Mon, 18 Dec 2023 07:28:48 +0800 Subject: [PATCH 015/193] Fix - Tuya Fan - Allow integer speed datapoint (#5948) Co-authored-by: Cram42 <5396871+cram42@users.noreply.github.com> --- esphome/components/tuya/fan/tuya_fan.cpp | 23 +++++++++++++++++------ esphome/components/tuya/fan/tuya_fan.h | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/esphome/components/tuya/fan/tuya_fan.cpp b/esphome/components/tuya/fan/tuya_fan.cpp index 1b03ea50fa..481c931f2e 100644 --- a/esphome/components/tuya/fan/tuya_fan.cpp +++ b/esphome/components/tuya/fan/tuya_fan.cpp @@ -9,13 +9,20 @@ static const char *const TAG = "tuya.fan"; void TuyaFan::setup() { if (this->speed_id_.has_value()) { this->parent_->register_listener(*this->speed_id_, [this](const TuyaDatapoint &datapoint) { - ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum); - if (datapoint.value_enum >= this->speed_count_) { - ESP_LOGE(TAG, "Speed has invalid value %d", datapoint.value_enum); - } else { - this->speed = datapoint.value_enum + 1; + if (datapoint.type == TuyaDatapointType::ENUM) { + ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum); + if (datapoint.value_enum >= this->speed_count_) { + ESP_LOGE(TAG, "Speed has invalid value %d", datapoint.value_enum); + } else { + this->speed = datapoint.value_enum + 1; + this->publish_state(); + } + } else if (datapoint.type == TuyaDatapointType::INTEGER) { + ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_int); + this->speed = datapoint.value_int; this->publish_state(); } + this->speed_type_ = datapoint.type; }); } if (this->switch_id_.has_value()) { @@ -80,7 +87,11 @@ void TuyaFan::control(const fan::FanCall &call) { this->parent_->set_enum_datapoint_value(*this->direction_id_, enable); } if (this->speed_id_.has_value() && call.get_speed().has_value()) { - this->parent_->set_enum_datapoint_value(*this->speed_id_, *call.get_speed() - 1); + if (this->speed_type_ == TuyaDatapointType::ENUM) { + this->parent_->set_enum_datapoint_value(*this->speed_id_, *call.get_speed() - 1); + } else if (this->speed_type_ == TuyaDatapointType::INTEGER) { + this->parent_->set_integer_datapoint_value(*this->speed_id_, *call.get_speed()); + } } } diff --git a/esphome/components/tuya/fan/tuya_fan.h b/esphome/components/tuya/fan/tuya_fan.h index 4aba1e1c07..77b2cc6383 100644 --- a/esphome/components/tuya/fan/tuya_fan.h +++ b/esphome/components/tuya/fan/tuya_fan.h @@ -28,6 +28,7 @@ class TuyaFan : public Component, public fan::Fan { optional oscillation_id_{}; optional direction_id_{}; int speed_count_{}; + TuyaDatapointType speed_type_{}; }; } // namespace tuya From 8a23b7e0c864fecc27a82f8f03c9af9afd9ea568 Mon Sep 17 00:00:00 2001 From: Alex Hermann Date: Mon, 18 Dec 2023 00:58:13 +0100 Subject: [PATCH 016/193] i2s_audio: Set player_task's prio to 1 (#5945) --- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index e729cdf954..95e63035fe 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -29,7 +29,7 @@ void I2SAudioSpeaker::start_() { } this->state_ = speaker::STATE_RUNNING; - xTaskCreate(I2SAudioSpeaker::player_task, "speaker_task", 8192, (void *) this, 0, &this->player_task_handle_); + xTaskCreate(I2SAudioSpeaker::player_task, "speaker_task", 8192, (void *) this, 1, &this->player_task_handle_); } void I2SAudioSpeaker::player_task(void *params) { From 8653972cb8d7ca4a896d260fbc753e30418449d9 Mon Sep 17 00:00:00 2001 From: Alex Hermann Date: Mon, 18 Dec 2023 01:00:42 +0100 Subject: [PATCH 017/193] esp32_camera: Set framebuffer task prio to 1 (#5943) --- esphome/components/esp32_camera/esp32_camera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 99cb811fe4..555f6ca5f1 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -37,7 +37,7 @@ void ESP32Camera::setup() { "framebuffer_task", // name 1024, // stack size nullptr, // task pv params - 0, // priority + 1, // priority nullptr, // handle 1 // core ); From 29fb2a53609e1c0e2ce234926d6402c0c0d86a9a Mon Sep 17 00:00:00 2001 From: dentra Date: Mon, 18 Dec 2023 03:01:21 +0300 Subject: [PATCH 018/193] web_server_idf: fix call with hardcoded http code (#5942) --- esphome/components/web_server_idf/web_server_idf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index bc64e5231e..2fbc5cd2e9 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -117,7 +117,7 @@ class AsyncWebServerRequest { // NOLINTNEXTLINE(readability-identifier-naming) AsyncWebServerResponse *beginResponse(int code, const char *content_type) { auto *res = new AsyncWebServerResponseEmpty(this); // NOLINT(cppcoreguidelines-owning-memory) - this->init_response_(res, 200, content_type); + this->init_response_(res, code, content_type); return res; } // NOLINTNEXTLINE(readability-identifier-naming) From 1d37edb63c95e9ccff69b13b849b499952bd3a34 Mon Sep 17 00:00:00 2001 From: Jean Louis-Guerin Date: Mon, 18 Dec 2023 01:03:01 +0100 Subject: [PATCH 019/193] Revert pure virtual functions in UART component from #5920 (#5932) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/uart/uart_component.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/uart/uart_component.h b/esphome/components/uart/uart_component.h index e03784fdd8..6f27f36bcb 100644 --- a/esphome/components/uart/uart_component.h +++ b/esphome/components/uart/uart_component.h @@ -134,7 +134,7 @@ class UARTComponent { * * This will load the current UART interface with the latest settings (baud_rate, parity, etc). */ - virtual void load_settings(bool dump_config) = 0; + virtual void load_settings(bool dump_config){}; /** * Load the UART settings. @@ -146,7 +146,7 @@ class UARTComponent { * * This will load the current UART interface with the latest settings (baud_rate, parity, etc). */ - virtual void load_settings() = 0; + virtual void load_settings(){}; #endif // USE_ESP32 #ifdef USE_UART_DEBUGGER From 2060d1ac89bdd4f593eb06ac90cfbfa0f8e06b6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:30:27 +0900 Subject: [PATCH 020/193] Bump esptool from 4.6.2 to 4.7.0 (#5935) 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 20a5514e71..e545a8023a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ tzlocal==5.2 # from time tzdata>=2021.1 # from time pyserial==3.5 platformio==6.1.11 # When updating platformio, also update Dockerfile -esptool==4.6.2 +esptool==4.7.0 click==8.1.7 esphome-dashboard==20231107.0 aioesphomeapi==21.0.0 From 323f8c9bdb134a801d8439e882eb533ab51464c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:30:50 +0900 Subject: [PATCH 021/193] Bump actions/download-artifact from 3.0.2 to 4.0.0 (#5936) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3.0.2 to 4.0.0. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3.0.2...v4.0.0) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 625a8c8ecb..2972ae7fac 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -158,7 +158,7 @@ jobs: steps: - uses: actions/checkout@v4.1.1 - name: Download digests - uses: actions/download-artifact@v3.0.2 + uses: actions/download-artifact@v4.0.0 with: name: digests-${{ matrix.image.target }}-${{ matrix.registry }} path: /tmp/digests From 0f4d7dadb38a1d5cffbe5f7aa5cdbd2302bee916 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Dec 2023 13:32:18 +0900 Subject: [PATCH 022/193] Bump build-image action versions (#5954) --- .github/actions/build-image/action.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index 4c98a47ecd..cc7909d724 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -36,7 +36,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v5.0.0 + uses: docker/build-push-action@v5.1.0 with: context: . file: ./docker/Dockerfile @@ -58,7 +58,7 @@ runs: touch "/tmp/digests/${{ inputs.target }}/ghcr/${digest#sha256:}" - name: Upload ghcr digest - uses: actions/upload-artifact@v3.1.3 + uses: actions/upload-artifact@v4.0.0 with: name: digests-${{ inputs.target }}-ghcr path: /tmp/digests/${{ inputs.target }}/ghcr/* @@ -67,7 +67,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v5.0.0 + uses: docker/build-push-action@v5.1.0 with: context: . file: ./docker/Dockerfile @@ -89,7 +89,7 @@ runs: touch "/tmp/digests/${{ inputs.target }}/dockerhub/${digest#sha256:}" - name: Upload dockerhub digest - uses: actions/upload-artifact@v3.1.3 + uses: actions/upload-artifact@v4.0.0 with: name: digests-${{ inputs.target }}-dockerhub path: /tmp/digests/${{ inputs.target }}/dockerhub/* From 89c6f3d45dce54dd2bae8904e67422ea7ff60fe1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:27:26 +0900 Subject: [PATCH 023/193] Revert "Bump build-image action versions" (#5955) --- .github/actions/build-image/action.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index cc7909d724..4c98a47ecd 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -36,7 +36,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v5.1.0 + uses: docker/build-push-action@v5.0.0 with: context: . file: ./docker/Dockerfile @@ -58,7 +58,7 @@ runs: touch "/tmp/digests/${{ inputs.target }}/ghcr/${digest#sha256:}" - name: Upload ghcr digest - uses: actions/upload-artifact@v4.0.0 + uses: actions/upload-artifact@v3.1.3 with: name: digests-${{ inputs.target }}-ghcr path: /tmp/digests/${{ inputs.target }}/ghcr/* @@ -67,7 +67,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v5.1.0 + uses: docker/build-push-action@v5.0.0 with: context: . file: ./docker/Dockerfile @@ -89,7 +89,7 @@ runs: touch "/tmp/digests/${{ inputs.target }}/dockerhub/${digest#sha256:}" - name: Upload dockerhub digest - uses: actions/upload-artifact@v4.0.0 + uses: actions/upload-artifact@v3.1.3 with: name: digests-${{ inputs.target }}-dockerhub path: /tmp/digests/${{ inputs.target }}/dockerhub/* From bf258230cd4ddffa38e8da633069ee2921c05ca3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:29:24 +0900 Subject: [PATCH 024/193] Revert "Bump actions/download-artifact from 3.0.2 to 4.0.0" (#5956) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2972ae7fac..625a8c8ecb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -158,7 +158,7 @@ jobs: steps: - uses: actions/checkout@v4.1.1 - name: Download digests - uses: actions/download-artifact@v4.0.0 + uses: actions/download-artifact@v3.0.2 with: name: digests-${{ matrix.image.target }}-${{ matrix.registry }} path: /tmp/digests From d99598bba62baeb01861250da243437685f091cf Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 18 Dec 2023 01:33:12 -0600 Subject: [PATCH 025/193] Use the correct UART/`Serial` when CDC is enabled (#5957) --- esphome/components/logger/logger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index e0ca0806cb..05b97a5f64 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -237,8 +237,8 @@ void Logger::pre_setup() { Serial1.begin(this->baud_rate_); #else #if ARDUINO_USB_CDC_ON_BOOT - this->hw_serial_ = &Serial; - Serial.begin(this->baud_rate_); + this->hw_serial_ = &Serial0; + Serial0.begin(this->baud_rate_); #else this->hw_serial_ = &Serial; Serial.begin(this->baud_rate_); From 70dac541139a7514dd06281152f233fc2efd3382 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 17 Dec 2023 16:27:08 +0000 Subject: [PATCH 026/193] Bump zeroconf from 0.128.4 to 0.130.0 (#5950) 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 f330ecbf3e..20a5514e71 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ esptool==4.6.2 click==8.1.7 esphome-dashboard==20231107.0 aioesphomeapi==21.0.0 -zeroconf==0.128.4 +zeroconf==0.130.0 python-magic==0.4.27 # esp-idf requires this, but doesn't bundle it by default From 917e0f93edd561b13fd25da0b5ee4beb58e10e1d Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Mon, 18 Dec 2023 00:19:30 +0100 Subject: [PATCH 027/193] UARTComponent inline doc (#5930) --- esphome/components/uart/uart_component.h | 88 +++++++++++++++++++++++- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/esphome/components/uart/uart_component.h b/esphome/components/uart/uart_component.h index 34bda42bb5..e03784fdd8 100644 --- a/esphome/components/uart/uart_component.h +++ b/esphome/components/uart/uart_component.h @@ -31,40 +31,122 @@ const LogString *parity_to_str(UARTParityOptions parity); class UARTComponent { public: + // Writes an array of bytes to the UART bus. + // @param data A vector of bytes to be written. void write_array(const std::vector &data) { this->write_array(&data[0], data.size()); } + + // Writes a single byte to the UART bus. + // @param data The byte to be written. void write_byte(uint8_t data) { this->write_array(&data, 1); }; + + // Writes a null-terminated string to the UART bus. + // @param str Pointer to the null-terminated string. void write_str(const char *str) { const auto *data = reinterpret_cast(str); this->write_array(data, strlen(str)); }; + // Pure virtual method to write an array of bytes to the UART bus. + // @param data Pointer to the array of bytes. + // @param len Length of the array. virtual void write_array(const uint8_t *data, size_t len) = 0; + // Reads a single byte from the UART bus. + // @param data Pointer to the byte where the read data will be stored. + // @return True if a byte was successfully read, false otherwise. bool read_byte(uint8_t *data) { return this->read_array(data, 1); }; + + // Pure virtual method to peek the next byte in the UART buffer without removing it. + // @param data Pointer to the byte where the peeked data will be stored. + // @return True if a byte is available to peek, false otherwise. virtual bool peek_byte(uint8_t *data) = 0; + + // Pure virtual method to read an array of bytes from the UART bus. + // @param data Pointer to the array where the read data will be stored. + // @param len Number of bytes to read. + // @return True if the specified number of bytes were successfully read, false otherwise. virtual bool read_array(uint8_t *data, size_t len) = 0; - /// Return available number of bytes. + // Pure virtual method to return the number of bytes available for reading. + // @return Number of available bytes. virtual int available() = 0; - /// Block until all bytes have been written to the UART bus. + + // Pure virtual method to block until all bytes have been written to the UART bus. virtual void flush() = 0; + // Sets the TX (transmit) pin for the UART bus. + // @param tx_pin Pointer to the internal GPIO pin used for transmission. void set_tx_pin(InternalGPIOPin *tx_pin) { this->tx_pin_ = tx_pin; } + + // Sets the RX (receive) pin for the UART bus. + // @param rx_pin Pointer to the internal GPIO pin used for reception. void set_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_pin; } + + // Sets the size of the RX buffer. + // @param rx_buffer_size Size of the RX buffer in bytes. void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; } + + // Gets the size of the RX buffer. + // @return Size of the RX buffer in bytes. size_t get_rx_buffer_size() { return this->rx_buffer_size_; } + // Sets the number of stop bits used in UART communication. + // @param stop_bits Number of stop bits. void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; } + + // Gets the number of stop bits used in UART communication. + // @return Number of stop bits. uint8_t get_stop_bits() const { return this->stop_bits_; } + + // Set the number of data bits used in UART communication. + // @param data_bits Number of data bits. void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; } + + // Get the number of data bits used in UART communication. + // @return Number of data bits. uint8_t get_data_bits() const { return this->data_bits_; } + + // Set the parity used in UART communication. + // @param parity Parity option. void set_parity(UARTParityOptions parity) { this->parity_ = parity; } + + // Get the parity used in UART communication. + // @return Parity option. UARTParityOptions get_parity() const { return this->parity_; } + + // Set the baud rate for UART communication. + // @param baud_rate Baud rate in bits per second. void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } + + // Get the baud rate for UART communication. + // @return Baud rate in bits per second. uint32_t get_baud_rate() const { return baud_rate_; } + #ifdef USE_ESP32 - virtual void load_settings() = 0; + /** + * Load the UART settings. + * @param dump_config If true (default), output the new settings to logs; otherwise, change settings quietly. + * + * Example: + * ```cpp + * id(uart1).load_settings(false); + * ``` + * + * This will load the current UART interface with the latest settings (baud_rate, parity, etc). + */ virtual void load_settings(bool dump_config) = 0; + + /** + * Load the UART settings. + * + * Example: + * ```cpp + * id(uart1).load_settings(); + * ``` + * + * This will load the current UART interface with the latest settings (baud_rate, parity, etc). + */ + virtual void load_settings() = 0; #endif // USE_ESP32 #ifdef USE_UART_DEBUGGER From 2309f15ce06b0fe633420b8061293427e62f0051 Mon Sep 17 00:00:00 2001 From: Grant Le Roux Date: Mon, 18 Dec 2023 07:28:48 +0800 Subject: [PATCH 028/193] Fix - Tuya Fan - Allow integer speed datapoint (#5948) Co-authored-by: Cram42 <5396871+cram42@users.noreply.github.com> --- esphome/components/tuya/fan/tuya_fan.cpp | 23 +++++++++++++++++------ esphome/components/tuya/fan/tuya_fan.h | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/esphome/components/tuya/fan/tuya_fan.cpp b/esphome/components/tuya/fan/tuya_fan.cpp index 1b03ea50fa..481c931f2e 100644 --- a/esphome/components/tuya/fan/tuya_fan.cpp +++ b/esphome/components/tuya/fan/tuya_fan.cpp @@ -9,13 +9,20 @@ static const char *const TAG = "tuya.fan"; void TuyaFan::setup() { if (this->speed_id_.has_value()) { this->parent_->register_listener(*this->speed_id_, [this](const TuyaDatapoint &datapoint) { - ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum); - if (datapoint.value_enum >= this->speed_count_) { - ESP_LOGE(TAG, "Speed has invalid value %d", datapoint.value_enum); - } else { - this->speed = datapoint.value_enum + 1; + if (datapoint.type == TuyaDatapointType::ENUM) { + ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum); + if (datapoint.value_enum >= this->speed_count_) { + ESP_LOGE(TAG, "Speed has invalid value %d", datapoint.value_enum); + } else { + this->speed = datapoint.value_enum + 1; + this->publish_state(); + } + } else if (datapoint.type == TuyaDatapointType::INTEGER) { + ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_int); + this->speed = datapoint.value_int; this->publish_state(); } + this->speed_type_ = datapoint.type; }); } if (this->switch_id_.has_value()) { @@ -80,7 +87,11 @@ void TuyaFan::control(const fan::FanCall &call) { this->parent_->set_enum_datapoint_value(*this->direction_id_, enable); } if (this->speed_id_.has_value() && call.get_speed().has_value()) { - this->parent_->set_enum_datapoint_value(*this->speed_id_, *call.get_speed() - 1); + if (this->speed_type_ == TuyaDatapointType::ENUM) { + this->parent_->set_enum_datapoint_value(*this->speed_id_, *call.get_speed() - 1); + } else if (this->speed_type_ == TuyaDatapointType::INTEGER) { + this->parent_->set_integer_datapoint_value(*this->speed_id_, *call.get_speed()); + } } } diff --git a/esphome/components/tuya/fan/tuya_fan.h b/esphome/components/tuya/fan/tuya_fan.h index 4aba1e1c07..77b2cc6383 100644 --- a/esphome/components/tuya/fan/tuya_fan.h +++ b/esphome/components/tuya/fan/tuya_fan.h @@ -28,6 +28,7 @@ class TuyaFan : public Component, public fan::Fan { optional oscillation_id_{}; optional direction_id_{}; int speed_count_{}; + TuyaDatapointType speed_type_{}; }; } // namespace tuya From 168e70413030060eedb3d723939ea7191767a8a0 Mon Sep 17 00:00:00 2001 From: Alex Hermann Date: Mon, 18 Dec 2023 00:58:13 +0100 Subject: [PATCH 029/193] i2s_audio: Set player_task's prio to 1 (#5945) --- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index e729cdf954..95e63035fe 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -29,7 +29,7 @@ void I2SAudioSpeaker::start_() { } this->state_ = speaker::STATE_RUNNING; - xTaskCreate(I2SAudioSpeaker::player_task, "speaker_task", 8192, (void *) this, 0, &this->player_task_handle_); + xTaskCreate(I2SAudioSpeaker::player_task, "speaker_task", 8192, (void *) this, 1, &this->player_task_handle_); } void I2SAudioSpeaker::player_task(void *params) { From e8ce780482c914f45df13bff31448352d5ee5e76 Mon Sep 17 00:00:00 2001 From: Alex Hermann Date: Mon, 18 Dec 2023 01:00:42 +0100 Subject: [PATCH 030/193] esp32_camera: Set framebuffer task prio to 1 (#5943) --- esphome/components/esp32_camera/esp32_camera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 99cb811fe4..555f6ca5f1 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -37,7 +37,7 @@ void ESP32Camera::setup() { "framebuffer_task", // name 1024, // stack size nullptr, // task pv params - 0, // priority + 1, // priority nullptr, // handle 1 // core ); From d0df73769d6e51f0efffab59011298e9f836e4f6 Mon Sep 17 00:00:00 2001 From: dentra Date: Mon, 18 Dec 2023 03:01:21 +0300 Subject: [PATCH 031/193] web_server_idf: fix call with hardcoded http code (#5942) --- esphome/components/web_server_idf/web_server_idf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index bc64e5231e..2fbc5cd2e9 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -117,7 +117,7 @@ class AsyncWebServerRequest { // NOLINTNEXTLINE(readability-identifier-naming) AsyncWebServerResponse *beginResponse(int code, const char *content_type) { auto *res = new AsyncWebServerResponseEmpty(this); // NOLINT(cppcoreguidelines-owning-memory) - this->init_response_(res, 200, content_type); + this->init_response_(res, code, content_type); return res; } // NOLINTNEXTLINE(readability-identifier-naming) From eefa1cd3aba5ff2fcf93328b35117cdbf70594b7 Mon Sep 17 00:00:00 2001 From: Jean Louis-Guerin Date: Mon, 18 Dec 2023 01:03:01 +0100 Subject: [PATCH 032/193] Revert pure virtual functions in UART component from #5920 (#5932) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/uart/uart_component.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/uart/uart_component.h b/esphome/components/uart/uart_component.h index e03784fdd8..6f27f36bcb 100644 --- a/esphome/components/uart/uart_component.h +++ b/esphome/components/uart/uart_component.h @@ -134,7 +134,7 @@ class UARTComponent { * * This will load the current UART interface with the latest settings (baud_rate, parity, etc). */ - virtual void load_settings(bool dump_config) = 0; + virtual void load_settings(bool dump_config){}; /** * Load the UART settings. @@ -146,7 +146,7 @@ class UARTComponent { * * This will load the current UART interface with the latest settings (baud_rate, parity, etc). */ - virtual void load_settings() = 0; + virtual void load_settings(){}; #endif // USE_ESP32 #ifdef USE_UART_DEBUGGER From ab22a3da349d3d60b687cbf70dd5a00ec44b67e2 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 18 Dec 2023 01:33:12 -0600 Subject: [PATCH 033/193] Use the correct UART/`Serial` when CDC is enabled (#5957) --- esphome/components/logger/logger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index e0ca0806cb..05b97a5f64 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -237,8 +237,8 @@ void Logger::pre_setup() { Serial1.begin(this->baud_rate_); #else #if ARDUINO_USB_CDC_ON_BOOT - this->hw_serial_ = &Serial; - Serial.begin(this->baud_rate_); + this->hw_serial_ = &Serial0; + Serial0.begin(this->baud_rate_); #else this->hw_serial_ = &Serial; Serial.begin(this->baud_rate_); From dbfa77cb4bac5f5fc6120a79320e52a142c01812 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Dec 2023 16:51:11 +0900 Subject: [PATCH 034/193] Bump version to 2023.12.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 6c2a520331..4153646aea 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.12.0b2" +__version__ = "2023.12.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 8961e8ab32cd398405b59fb6e806db91c9150439 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Mon, 18 Dec 2023 20:23:22 +0100 Subject: [PATCH 035/193] rename set_raw_touch_position_ to add_raw_touch_position_ (#5962) --- esphome/components/ektf2232/touchscreen/ektf2232.cpp | 2 +- esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.h | 2 +- esphome/components/ft63x6/ft63x6.cpp | 4 ++-- esphome/components/gt911/touchscreen/gt911_touchscreen.cpp | 2 +- .../lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp | 2 +- esphome/components/touchscreen/touchscreen.cpp | 2 +- esphome/components/touchscreen/touchscreen.h | 2 +- esphome/components/tt21100/touchscreen/tt21100.cpp | 2 +- esphome/components/xpt2046/touchscreen/xpt2046.cpp | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/esphome/components/ektf2232/touchscreen/ektf2232.cpp b/esphome/components/ektf2232/touchscreen/ektf2232.cpp index 1a2c0389af..00e00bc7e6 100644 --- a/esphome/components/ektf2232/touchscreen/ektf2232.cpp +++ b/esphome/components/ektf2232/touchscreen/ektf2232.cpp @@ -74,7 +74,7 @@ void EKTF2232Touchscreen::update_touches() { uint8_t *d = raw + 1 + (i * 3); x_raw = (d[0] & 0xF0) << 4 | d[1]; y_raw = (d[0] & 0x0F) << 8 | d[2]; - this->set_raw_touch_position_(i, x_raw, y_raw); + this->add_raw_touch_position_(i, x_raw, y_raw); } } diff --git a/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.h b/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.h index 497d6c906c..0b3a2c1b86 100644 --- a/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.h +++ b/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.h @@ -94,7 +94,7 @@ class FT5x06Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice esph_log_d(TAG, "Read %X status, id: %d, pos %d/%d", status, id, x, y); if (status == 0 || status == 2) { - this->set_raw_touch_position_(id, x, y); + this->add_raw_touch_position_(id, x, y); } } } diff --git a/esphome/components/ft63x6/ft63x6.cpp b/esphome/components/ft63x6/ft63x6.cpp index 9198954253..b674ded22c 100644 --- a/esphome/components/ft63x6/ft63x6.cpp +++ b/esphome/components/ft63x6/ft63x6.cpp @@ -53,13 +53,13 @@ void FT63X6Touchscreen::update_touches() { uint8_t touch_id = this->read_touch_id_(FT63X6_ADDR_TOUCH1_ID); // id1 = 0 or 1 int16_t x = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH1_X); int16_t y = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH1_Y); - this->set_raw_touch_position_(touch_id, x, y); + this->add_raw_touch_position_(touch_id, x, y); if (touch_count >= 2) { touch_id = this->read_touch_id_(FT63X6_ADDR_TOUCH2_ID); // id2 = 0 or 1(~id1 & 0x01) x = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH2_X); y = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH2_Y); - this->set_raw_touch_position_(touch_id, x, y); + this->add_raw_touch_position_(touch_id, x, y); } } diff --git a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp index adc577f5da..84854d5b0d 100644 --- a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +++ b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp @@ -92,7 +92,7 @@ void GT911Touchscreen::update_touches() { uint16_t id = data[i][0]; uint16_t x = encode_uint16(data[i][2], data[i][1]); uint16_t y = encode_uint16(data[i][4], data[i][3]); - this->set_raw_touch_position_(id, x, y); + this->add_raw_touch_position_(id, x, y); } auto keys = data[num_of_touches][0]; for (size_t i = 0; i != 4; i++) { diff --git a/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp index eb61b6f31e..64cc7ad4d1 100644 --- a/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +++ b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp @@ -84,7 +84,7 @@ void LilygoT547Touchscreen::update_touches() { id = (buffer[i * 5] >> 4) & 0x0F; y_raw = (uint16_t) ((buffer[i * 5 + 1] << 4) | ((buffer[i * 5 + 3] >> 4) & 0x0F)); x_raw = (uint16_t) ((buffer[i * 5 + 2] << 4) | (buffer[i * 5 + 3] & 0x0F)); - this->set_raw_touch_position_(id, x_raw, y_raw); + this->add_raw_touch_position_(id, x_raw, y_raw); } this->status_clear_warning(); diff --git a/esphome/components/touchscreen/touchscreen.cpp b/esphome/components/touchscreen/touchscreen.cpp index 140f46b6f6..f095c2af8c 100644 --- a/esphome/components/touchscreen/touchscreen.cpp +++ b/esphome/components/touchscreen/touchscreen.cpp @@ -51,7 +51,7 @@ void Touchscreen::loop() { } } -void Touchscreen::set_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw) { +void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw) { TouchPoint tp; uint16_t x, y; if (this->touches_.count(id) == 0) { diff --git a/esphome/components/touchscreen/touchscreen.h b/esphome/components/touchscreen/touchscreen.h index 1fe304d967..74747c589c 100644 --- a/esphome/components/touchscreen/touchscreen.h +++ b/esphome/components/touchscreen/touchscreen.h @@ -87,7 +87,7 @@ class Touchscreen : public PollingComponent { void attach_interrupt_(InternalGPIOPin *irq_pin, esphome::gpio::InterruptType type); - void set_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw = 0); + void add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw = 0); void send_touches_(); diff --git a/esphome/components/tt21100/touchscreen/tt21100.cpp b/esphome/components/tt21100/touchscreen/tt21100.cpp index ff688fd0b0..6b5cba74cd 100644 --- a/esphome/components/tt21100/touchscreen/tt21100.cpp +++ b/esphome/components/tt21100/touchscreen/tt21100.cpp @@ -109,7 +109,7 @@ void TT21100Touchscreen::update_touches() { i, touch->touch_type, touch->tip, touch->event_id, touch->touch_id, touch->x, touch->y, touch->pressure, touch->major_axis_length, touch->orientation); - this->set_raw_touch_position_(touch->tip, touch->x, touch->y, touch->pressure); + this->add_raw_touch_position_(touch->tip, touch->x, touch->y, touch->pressure); } } } diff --git a/esphome/components/xpt2046/touchscreen/xpt2046.cpp b/esphome/components/xpt2046/touchscreen/xpt2046.cpp index 1a9c202af0..a268da06dd 100644 --- a/esphome/components/xpt2046/touchscreen/xpt2046.cpp +++ b/esphome/components/xpt2046/touchscreen/xpt2046.cpp @@ -55,7 +55,7 @@ void XPT2046Component::update_touches() { ESP_LOGV(TAG, "Touchscreen Update [%d, %d], z = %d", x_raw, y_raw, z_raw); - this->set_raw_touch_position_(0, x_raw, y_raw, z_raw); + this->add_raw_touch_position_(0, x_raw, y_raw, z_raw); } } From 3ea5054cf2e1ad91175b0eafb7ac82e859bb5bc6 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 18 Dec 2023 17:11:07 -0600 Subject: [PATCH 036/193] Fix build issue with UART component when building with Arduino and CDC (#5964) --- esphome/components/uart/uart_component_esp32_arduino.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/uart/uart_component_esp32_arduino.cpp b/esphome/components/uart/uart_component_esp32_arduino.cpp index 75b67bf5c2..f77783e20e 100644 --- a/esphome/components/uart/uart_component_esp32_arduino.cpp +++ b/esphome/components/uart/uart_component_esp32_arduino.cpp @@ -88,7 +88,11 @@ void ESP32ArduinoUARTComponent::setup() { #endif static uint8_t next_uart_num = 0; if (is_default_tx && is_default_rx && next_uart_num == 0) { +#if ARDUINO_USB_CDC_ON_BOOT + this->hw_serial_ = &Serial0; +#else this->hw_serial_ = &Serial; +#endif next_uart_num++; } else { #ifdef USE_LOGGER From 0a117eb562204a28d1955aa8eb6d2963c625d95a Mon Sep 17 00:00:00 2001 From: Jean Louis-Guerin Date: Tue, 19 Dec 2023 00:14:42 +0100 Subject: [PATCH 037/193] Fix I2CBus::write() bug and add i2c documentation (#5947) --- esphome/components/i2c/i2c.h | 153 +++++++++++++++++++++++++++++-- esphome/components/i2c/i2c_bus.h | 79 ++++++++++++---- 2 files changed, 205 insertions(+), 27 deletions(-) diff --git a/esphome/components/i2c/i2c.h b/esphome/components/i2c/i2c.h index eb5d463b65..8d8e139c61 100644 --- a/esphome/components/i2c/i2c.h +++ b/esphome/components/i2c/i2c.h @@ -11,43 +11,116 @@ namespace i2c { #define LOG_I2C_DEVICE(this) ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); -class I2CDevice; +class I2CDevice; // forward declaration + +/// @brief This class is used to create I2CRegister objects that act as proxies to read/write internal registers on an +/// I2C device. +/// @details +/// @n typical usage: +/// @code +/// constexpr uint8_t ADDR_REGISTER_1 = 0x12; +/// i2c::I2CRegister reg_1 = this->reg(ADDR_REGISTER_1); // declare +/// reg_1 |= 0x01; // set bit +/// reg_1 &= ~0x01; // reset bit +/// reg_1 = 10; // Set value +/// uint val = reg_1.get(); // get value +/// @endcode +/// @details The I²C protocol specifies how to read/write in sets of 8-bits followed by an Acknowledgement (ACK/NACK) +/// from the device receiving the data. How the device interprets the bits read/written can vary greatly from +/// device to device. However most of the devices follow the same protocol for reading/writing 8 bit registers using as +/// implemented in the I2CRegister: after sending the device address, the controller sends one byte with the internal +/// register address and then read or write the specified register content. class I2CRegister { public: + /// @brief overloads the = operator. This allows to set the value of an i2c register + /// @param value value to be set in the register + /// @return pointer to current object I2CRegister &operator=(uint8_t value); + + /// @brief overloads the compound &= operator. This allows to reset specific bits of an I²C register + /// @param value used for the & operation + /// @return pointer to current object I2CRegister &operator&=(uint8_t value); + + /// @brief overloads the compound |= operator. This allows to set specific bits of an I²C register + /// @param value used for the & operation + /// @return pointer to current object I2CRegister &operator|=(uint8_t value); + /// @brief overloads the uint8_t() cast operator to return the I²C register value + /// @return pointer to current object explicit operator uint8_t() const { return get(); } + /// @brief returns the register value + /// @return the register value uint8_t get() const; protected: friend class I2CDevice; + /// @brief protected constructor that stores the owning object and the register address. Note as only friends can + /// create an I2CRegister @see I2CDevice::reg() + /// @param parent our parent + /// @param a_register address of the i2c register I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} - I2CDevice *parent_; - uint8_t register_; + I2CDevice *parent_; ///< I2CDevice object pointer + uint8_t register_; ///< the internal address of the register }; +/// @brief This class is used to create I2CRegister16 objects that act as proxies to read/write internal registers +/// (specified with a 16 bit address) on an I2C device. +/// @details +/// @n typical usage: +/// @code +/// constexpr uint16_t X16_BIT_ADDR_REGISTER_1 = 0x1234; +/// i2c::I2CRegister16 reg_1 = this->reg16(X16_BIT_ADDR_REGISTER_1); // declare +/// reg_1 |= 0x01; // set bit +/// reg_1 &= ~0x01; // reset bit +/// reg_1 = 10; // Set value +/// uint val = reg_1.get(); // get value +/// @endcode +/// @details The I²C protocol specification, reads/writes in sets of 8-bits followed by an Acknowledgement (ACK/NACK) +/// from the device receiving the data. How the device interprets the bits read/written to it can vary greatly from +/// device to device. This class can be used to access in the device 8 bits registers that uses a 16 bits internal +/// address. After sending the device address, the controller sends the internal register address (using two consecutive +/// bytes following the big indian convention) and then read or write the register content. class I2CRegister16 { public: + /// @brief overloads the = operator. This allows to set the value of an I²C register + /// @param value value to be set in the register + /// @return pointer to current object I2CRegister16 &operator=(uint8_t value); + + /// @brief overloads the compound &= operator. This allows to reset specific bits of an I²C register + /// @param value used for the & operation + /// @return pointer to current object I2CRegister16 &operator&=(uint8_t value); + + /// @brief overloads the compound |= operator. This allows to set bits of an I²C register + /// @param value used for the & operation + /// @return pointer to current object I2CRegister16 &operator|=(uint8_t value); + /// @brief overloads the uint8_t() cast operator to return the I²C register value + /// @return the register value explicit operator uint8_t() const { return get(); } + /// @brief returns the register value + /// @return the register value uint8_t get() const; protected: friend class I2CDevice; + /// @brief protected constructor that store the owning object and the register address. Only friends can create an + /// I2CRegister16 @see I2CDevice::reg16() + /// @param parent our parent + /// @param a_register 16 bits address of the i2c register I2CRegister16(I2CDevice *parent, uint16_t a_register) : parent_(parent), register_(a_register) {} - I2CDevice *parent_; - uint16_t register_; + I2CDevice *parent_; ///< I2CDevice object pointer + uint16_t register_; ///< the internal 16 bits address of the register }; // like ntohs/htons but without including networking headers. @@ -55,29 +128,91 @@ class I2CRegister16 { inline uint16_t i2ctohs(uint16_t i2cshort) { return convert_big_endian(i2cshort); } inline uint16_t htoi2cs(uint16_t hostshort) { return convert_big_endian(hostshort); } +/// @brief This Class provides the methods to read/write bytes from/to an i2c device. +/// Objects keep a list of devices found on bus as well as a pointer to the I2CBus in use. class I2CDevice { public: + /// @brief we use the C++ default constructor I2CDevice() = default; + /// @brief We store the address of the device on the bus + /// @param address of the device void set_i2c_address(uint8_t address) { address_ = address; } + + /// @brief we store the pointer to the I2CBus to use + /// @param bus pointer to the I2CBus object void set_i2c_bus(I2CBus *bus) { bus_ = bus; } + /// @brief calls the I2CRegister constructor + /// @param a_register address of the I²C register + /// @return an I2CRegister proxy object I2CRegister reg(uint8_t a_register) { return {this, a_register}; } + + /// @brief calls the I2CRegister16 constructor + /// @param a_register 16 bits address of the I²C register + /// @return an I2CRegister16 proxy object I2CRegister16 reg16(uint16_t a_register) { return {this, a_register}; } + /// @brief reads an array of bytes from the device using an I2CBus + /// @param data pointer to an array to store the bytes + /// @param len length of the buffer = number of bytes to read + /// @return an i2c::ErrorCode ErrorCode read(uint8_t *data, size_t len) { return bus_->read(address_, data, len); } + + /// @brief reads an array of bytes from a specific register in the I²C device + /// @param a_register an 8 bits internal address of the I²C register to read from + /// @param data pointer to an array to store the bytes + /// @param len length of the buffer = number of bytes to read + /// @param stop (true/false): True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop = true); + + /// @brief reads an array of bytes from a specific register in the I²C device + /// @param a_register the 16 bits internal address of the I²C register to read from + /// @param data pointer to an array of bytes to store the information + /// @param len length of the buffer = number of bytes to read + /// @param stop (true/false): True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode ErrorCode read_register16(uint16_t a_register, uint8_t *data, size_t len, bool stop = true); - ErrorCode write(const uint8_t *data, uint8_t len, bool stop = true) { return bus_->write(address_, data, len, stop); } + /// @brief writes an array of bytes to a device using an I2CBus + /// @param data pointer to an array that contains the bytes to send + /// @param len length of the buffer = number of bytes to write + /// @param stop (true/false): True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode + ErrorCode write(const uint8_t *data, size_t len, bool stop = true) { return bus_->write(address_, data, len, stop); } + + /// @brief writes an array of bytes to a specific register in the I²C device + /// @param a_register the internal address of the register to read from + /// @param data pointer to an array to store the bytes + /// @param len length of the buffer = number of bytes to read + /// @param stop (true/false): True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop = true); + + /// @brief write an array of bytes to a specific register in the I²C device + /// @param a_register the 16 bits internal address of the register to read from + /// @param data pointer to an array to store the bytes + /// @param len length of the buffer = number of bytes to read + /// @param stop (true/false): True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode ErrorCode write_register16(uint16_t a_register, const uint8_t *data, size_t len, bool stop = true); - // Compat APIs + /// + /// Compat APIs + /// All methods below have been added for compatibility reasons. They do not bring any functionality and therefore on + /// new code it is not recommend to use them. + /// bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len) { return read_register(a_register, data, len) == ERROR_OK; } + bool read_bytes_raw(uint8_t *data, uint8_t len) { return read(data, len) == ERROR_OK; } template optional> read_bytes(uint8_t a_register) { @@ -131,8 +266,8 @@ class I2CDevice { bool write_byte_16(uint8_t a_register, uint16_t data) { return write_bytes_16(a_register, &data, 1); } protected: - uint8_t address_{0x00}; - I2CBus *bus_{nullptr}; + uint8_t address_{0x00}; ///< store the address of the device on the bus + I2CBus *bus_{nullptr}; ///< pointer to I2CBus instance }; } // namespace i2c diff --git a/esphome/components/i2c/i2c_bus.h b/esphome/components/i2c/i2c_bus.h index 2633a7adf6..fbfc88323e 100644 --- a/esphome/components/i2c/i2c_bus.h +++ b/esphome/components/i2c/i2c_bus.h @@ -7,50 +7,93 @@ namespace esphome { namespace i2c { +/// @brief Error codes returned by I2CBus and I2CDevice methods enum ErrorCode { - ERROR_OK = 0, - ERROR_INVALID_ARGUMENT = 1, - ERROR_NOT_ACKNOWLEDGED = 2, - ERROR_TIMEOUT = 3, - ERROR_NOT_INITIALIZED = 4, - ERROR_TOO_LARGE = 5, - ERROR_UNKNOWN = 6, - ERROR_CRC = 7, + NO_ERROR = 0, ///< No error found during execution of method + ERROR_OK = 0, ///< No error found during execution of method + ERROR_INVALID_ARGUMENT = 1, ///< method called invalid argument(s) + ERROR_NOT_ACKNOWLEDGED = 2, ///< I2C bus acknowledgment not received + ERROR_TIMEOUT = 3, ///< timeout while waiting to receive bytes + ERROR_NOT_INITIALIZED = 4, ///< call method to a not initialized bus + ERROR_TOO_LARGE = 5, ///< requested a transfer larger than buffers can hold + ERROR_UNKNOWN = 6, ///< miscellaneous I2C error during execution + ERROR_CRC = 7, ///< bytes received with a CRC error }; +/// @brief the ReadBuffer structure stores a pointer to a read buffer and its length struct ReadBuffer { - uint8_t *data; - size_t len; -}; -struct WriteBuffer { - const uint8_t *data; - size_t len; + uint8_t *data; ///< pointer to the read buffer + size_t len; ///< length of the buffer }; +/// @brief the WriteBuffer structure stores a pointer to a write buffer and its length +struct WriteBuffer { + const uint8_t *data; ///< pointer to the write buffer + size_t len; ///< length of the buffer +}; + +/// @brief This Class provides the methods to read and write bytes from an I2CBus. +/// @note The I2CBus virtual class follows a *Factory design pattern* that provides all the interfaces methods required +/// by clients while deferring the actual implementation of these methods to a subclasses. I2C-bus specification and +/// user manual can be found here https://www.nxp.com/docs/en/user-guide/UM10204.pdf and an interesting I²C Application +/// note https://www.nxp.com/docs/en/application-note/AN10216.pdf class I2CBus { public: + /// @brief Creates a ReadBuffer and calls the virtual readv() method to read bytes into this buffer + /// @param address address of the I²C component on the i2c bus + /// @param buffer pointer to an array of bytes that will be used to store the data received + /// @param len length of the buffer = number of bytes to read + /// @return an i2c::ErrorCode virtual ErrorCode read(uint8_t address, uint8_t *buffer, size_t len) { ReadBuffer buf; buf.data = buffer; buf.len = len; return readv(address, &buf, 1); } - virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) = 0; + + /// @brief This virtual method reads bytes from an I2CBus into an array of ReadBuffer. + /// @param address address of the I²C component on the i2c bus + /// @param buffers pointer to an array of ReadBuffer + /// @param count number of ReadBuffer to read + /// @return an i2c::ErrorCode + /// @details This is a pure virtual method that must be implemented in a subclass. + virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t count) = 0; + virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len) { return write(address, buffer, len, true); } + + /// @brief Creates a WriteBuffer and calls the writev() method to send the bytes from this buffer + /// @param address address of the I²C component on the i2c bus + /// @param buffer pointer to an array of bytes that contains the data to be sent + /// @param len length of the buffer = number of bytes to write + /// @param stop true or false: True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len, bool stop) { WriteBuffer buf; buf.data = buffer; buf.len = len; return writev(address, &buf, 1, stop); } + virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { return writev(address, buffers, cnt, true); } - virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) = 0; + + /// @brief This virtual method writes bytes to an I2CBus from an array of WriteBuffer. + /// @param address address of the I²C component on the i2c bus + /// @param buffers pointer to an array of WriteBuffer + /// @param count number of WriteBuffer to write + /// @param stop true or false: True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode + /// @details This is a pure virtual method that must be implemented in the subclass. + virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t count, bool stop) = 0; protected: + /// @brief Scans the I2C bus for devices. Devices presence is kept in an array of std::pair + /// that contains the address and the corresponding bool presence flag. void i2c_scan_() { for (uint8_t address = 8; address < 120; address++) { auto err = writev(address, nullptr, 0); @@ -61,8 +104,8 @@ class I2CBus { } } } - std::vector> scan_results_; - bool scan_{false}; + std::vector> scan_results_; ///< array containing scan results + bool scan_{false}; ///< Should we scan ? Can be set in the yaml }; } // namespace i2c From 52b9668170f49e836c8f3dacb39dbd70c738fb50 Mon Sep 17 00:00:00 2001 From: mathieu-mp Date: Tue, 19 Dec 2023 04:29:00 +0100 Subject: [PATCH 038/193] Add deep sleep between updates for waveshare epaper 1.54in and 1.54inv2 (#5961) --- .../waveshare_epaper/waveshare_epaper.cpp | 33 +++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 25 ++++++++++---- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 53bfa57f4f..8fdb9a3ac0 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -167,6 +167,25 @@ void WaveshareEPaper::on_safe_shutdown() { this->deep_sleep(); } // ======================================================== void WaveshareEPaperTypeA::initialize() { + // Achieve display intialization + this->init_display_(); + // If a reset pin is configured, eligible displays can be set to deep sleep + // between updates, as recommended by the hardware provider + if (this->reset_pin_ != nullptr) { + switch (this->model_) { + // More models can be added here to enable deep sleep if eligible + case WAVESHARE_EPAPER_1_54_IN: + case WAVESHARE_EPAPER_1_54_IN_V2: + this->deep_sleep_between_updates_ = true; + ESP_LOGI(TAG, "Set the display to deep sleep"); + this->deep_sleep(); + break; + default: + break; + } + } +} +void WaveshareEPaperTypeA::init_display_() { if (this->model_ == TTGO_EPAPER_2_13_IN_B74) { this->reset_pin_->digital_write(false); delay(10); @@ -261,6 +280,13 @@ void HOT WaveshareEPaperTypeA::display() { bool full_update = this->at_update_ == 0; bool prev_full_update = this->at_update_ == 1; + if (this->deep_sleep_between_updates_) { + ESP_LOGI(TAG, "Wake up the display"); + this->reset_(); + this->wait_until_idle_(); + this->init_display_(); + } + if (!this->wait_until_idle_()) { this->status_set_warning(); return; @@ -384,6 +410,11 @@ void HOT WaveshareEPaperTypeA::display() { this->command(0xFF); this->status_clear_warning(); + + if (this->deep_sleep_between_updates_) { + ESP_LOGI(TAG, "Set the display back to deep sleep"); + this->deep_sleep(); + } } int WaveshareEPaperTypeA::get_width_internal() { switch (this->model_) { @@ -445,6 +476,8 @@ void WaveshareEPaperTypeA::set_full_update_every(uint32_t full_update_every) { uint32_t WaveshareEPaperTypeA::idle_timeout_() { switch (this->model_) { + case WAVESHARE_EPAPER_1_54_IN: + case WAVESHARE_EPAPER_1_54_IN_V2: case TTGO_EPAPER_2_13_IN_B1: return 2500; default: diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index f6ccf90861..42e8a16829 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -92,13 +92,20 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { void display() override; void deep_sleep() override { - if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2 || this->model_ == WAVESHARE_EPAPER_1_54_IN_V2) { - // COMMAND DEEP SLEEP MODE - this->command(0x10); - this->data(0x01); - } else { - // COMMAND DEEP SLEEP MODE - this->command(0x10); + switch (this->model_) { + // Models with specific deep sleep command and data + case WAVESHARE_EPAPER_1_54_IN: + case WAVESHARE_EPAPER_1_54_IN_V2: + case WAVESHARE_EPAPER_2_9_IN_V2: + // COMMAND DEEP SLEEP MODE + this->command(0x10); + this->data(0x01); + break; + // Other models default to simple deep sleep command + default: + // COMMAND DEEP SLEEP + this->command(0x10); + break; } this->wait_until_idle_(); } @@ -108,6 +115,8 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { protected: void write_lut_(const uint8_t *lut, uint8_t size); + void init_display_(); + int get_width_internal() override; int get_height_internal() override; @@ -118,6 +127,8 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { uint32_t at_update_{0}; WaveshareEPaperTypeAModel model_; uint32_t idle_timeout_() override; + + bool deep_sleep_between_updates_{false}; }; enum WaveshareEPaperTypeBModel { From cd06dc77ee6693595356a72032731701c66c91ce Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 18 Dec 2023 19:24:48 -1000 Subject: [PATCH 039/193] Speed up writing protobuf strings/bytes (#5828) --- esphome/components/api/proto.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index fea219ecb9..ccc6c0d52c 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -160,8 +160,7 @@ class ProtoWriteBuffer { this->encode_field_raw(field_id, 2); this->encode_varint_raw(len); auto *data = reinterpret_cast(string); - for (size_t i = 0; i < len; i++) - this->write(data[i]); + this->buffer_->insert(this->buffer_->end(), data, data + len); } void encode_string(uint32_t field_id, const std::string &value, bool force = false) { this->encode_string(field_id, value.data(), value.size()); From efda2033f7cdc61f3895d0e324bb1285a8417371 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 20:16:10 +0000 Subject: [PATCH 040/193] Bump zeroconf from 0.130.0 to 0.131.0 (#5967) 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 e545a8023a..fffe011c5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ esptool==4.7.0 click==8.1.7 esphome-dashboard==20231107.0 aioesphomeapi==21.0.0 -zeroconf==0.130.0 +zeroconf==0.131.0 python-magic==0.4.27 # esp-idf requires this, but doesn't bundle it by default From 16798bbfb461bf4b79799b715c2575654755548e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 19 Dec 2023 13:41:26 -1000 Subject: [PATCH 041/193] Bump aioesphomeapi to 21.0.1 (#5969) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fffe011c5a..115f85de3e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ platformio==6.1.11 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 esphome-dashboard==20231107.0 -aioesphomeapi==21.0.0 +aioesphomeapi==21.0.1 zeroconf==0.131.0 python-magic==0.4.27 From 7807f0d89250a890aaec97a524ad1d68310d07c3 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 18 Dec 2023 17:11:07 -0600 Subject: [PATCH 042/193] Fix build issue with UART component when building with Arduino and CDC (#5964) --- esphome/components/uart/uart_component_esp32_arduino.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/uart/uart_component_esp32_arduino.cpp b/esphome/components/uart/uart_component_esp32_arduino.cpp index 75b67bf5c2..f77783e20e 100644 --- a/esphome/components/uart/uart_component_esp32_arduino.cpp +++ b/esphome/components/uart/uart_component_esp32_arduino.cpp @@ -88,7 +88,11 @@ void ESP32ArduinoUARTComponent::setup() { #endif static uint8_t next_uart_num = 0; if (is_default_tx && is_default_rx && next_uart_num == 0) { +#if ARDUINO_USB_CDC_ON_BOOT + this->hw_serial_ = &Serial0; +#else this->hw_serial_ = &Serial; +#endif next_uart_num++; } else { #ifdef USE_LOGGER From 820f3282480d282914a6a0682908443ad3b5755a Mon Sep 17 00:00:00 2001 From: Jean Louis-Guerin Date: Tue, 19 Dec 2023 00:14:42 +0100 Subject: [PATCH 043/193] Fix I2CBus::write() bug and add i2c documentation (#5947) --- esphome/components/i2c/i2c.h | 153 +++++++++++++++++++++++++++++-- esphome/components/i2c/i2c_bus.h | 79 ++++++++++++---- 2 files changed, 205 insertions(+), 27 deletions(-) diff --git a/esphome/components/i2c/i2c.h b/esphome/components/i2c/i2c.h index eb5d463b65..8d8e139c61 100644 --- a/esphome/components/i2c/i2c.h +++ b/esphome/components/i2c/i2c.h @@ -11,43 +11,116 @@ namespace i2c { #define LOG_I2C_DEVICE(this) ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); -class I2CDevice; +class I2CDevice; // forward declaration + +/// @brief This class is used to create I2CRegister objects that act as proxies to read/write internal registers on an +/// I2C device. +/// @details +/// @n typical usage: +/// @code +/// constexpr uint8_t ADDR_REGISTER_1 = 0x12; +/// i2c::I2CRegister reg_1 = this->reg(ADDR_REGISTER_1); // declare +/// reg_1 |= 0x01; // set bit +/// reg_1 &= ~0x01; // reset bit +/// reg_1 = 10; // Set value +/// uint val = reg_1.get(); // get value +/// @endcode +/// @details The I²C protocol specifies how to read/write in sets of 8-bits followed by an Acknowledgement (ACK/NACK) +/// from the device receiving the data. How the device interprets the bits read/written can vary greatly from +/// device to device. However most of the devices follow the same protocol for reading/writing 8 bit registers using as +/// implemented in the I2CRegister: after sending the device address, the controller sends one byte with the internal +/// register address and then read or write the specified register content. class I2CRegister { public: + /// @brief overloads the = operator. This allows to set the value of an i2c register + /// @param value value to be set in the register + /// @return pointer to current object I2CRegister &operator=(uint8_t value); + + /// @brief overloads the compound &= operator. This allows to reset specific bits of an I²C register + /// @param value used for the & operation + /// @return pointer to current object I2CRegister &operator&=(uint8_t value); + + /// @brief overloads the compound |= operator. This allows to set specific bits of an I²C register + /// @param value used for the & operation + /// @return pointer to current object I2CRegister &operator|=(uint8_t value); + /// @brief overloads the uint8_t() cast operator to return the I²C register value + /// @return pointer to current object explicit operator uint8_t() const { return get(); } + /// @brief returns the register value + /// @return the register value uint8_t get() const; protected: friend class I2CDevice; + /// @brief protected constructor that stores the owning object and the register address. Note as only friends can + /// create an I2CRegister @see I2CDevice::reg() + /// @param parent our parent + /// @param a_register address of the i2c register I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} - I2CDevice *parent_; - uint8_t register_; + I2CDevice *parent_; ///< I2CDevice object pointer + uint8_t register_; ///< the internal address of the register }; +/// @brief This class is used to create I2CRegister16 objects that act as proxies to read/write internal registers +/// (specified with a 16 bit address) on an I2C device. +/// @details +/// @n typical usage: +/// @code +/// constexpr uint16_t X16_BIT_ADDR_REGISTER_1 = 0x1234; +/// i2c::I2CRegister16 reg_1 = this->reg16(X16_BIT_ADDR_REGISTER_1); // declare +/// reg_1 |= 0x01; // set bit +/// reg_1 &= ~0x01; // reset bit +/// reg_1 = 10; // Set value +/// uint val = reg_1.get(); // get value +/// @endcode +/// @details The I²C protocol specification, reads/writes in sets of 8-bits followed by an Acknowledgement (ACK/NACK) +/// from the device receiving the data. How the device interprets the bits read/written to it can vary greatly from +/// device to device. This class can be used to access in the device 8 bits registers that uses a 16 bits internal +/// address. After sending the device address, the controller sends the internal register address (using two consecutive +/// bytes following the big indian convention) and then read or write the register content. class I2CRegister16 { public: + /// @brief overloads the = operator. This allows to set the value of an I²C register + /// @param value value to be set in the register + /// @return pointer to current object I2CRegister16 &operator=(uint8_t value); + + /// @brief overloads the compound &= operator. This allows to reset specific bits of an I²C register + /// @param value used for the & operation + /// @return pointer to current object I2CRegister16 &operator&=(uint8_t value); + + /// @brief overloads the compound |= operator. This allows to set bits of an I²C register + /// @param value used for the & operation + /// @return pointer to current object I2CRegister16 &operator|=(uint8_t value); + /// @brief overloads the uint8_t() cast operator to return the I²C register value + /// @return the register value explicit operator uint8_t() const { return get(); } + /// @brief returns the register value + /// @return the register value uint8_t get() const; protected: friend class I2CDevice; + /// @brief protected constructor that store the owning object and the register address. Only friends can create an + /// I2CRegister16 @see I2CDevice::reg16() + /// @param parent our parent + /// @param a_register 16 bits address of the i2c register I2CRegister16(I2CDevice *parent, uint16_t a_register) : parent_(parent), register_(a_register) {} - I2CDevice *parent_; - uint16_t register_; + I2CDevice *parent_; ///< I2CDevice object pointer + uint16_t register_; ///< the internal 16 bits address of the register }; // like ntohs/htons but without including networking headers. @@ -55,29 +128,91 @@ class I2CRegister16 { inline uint16_t i2ctohs(uint16_t i2cshort) { return convert_big_endian(i2cshort); } inline uint16_t htoi2cs(uint16_t hostshort) { return convert_big_endian(hostshort); } +/// @brief This Class provides the methods to read/write bytes from/to an i2c device. +/// Objects keep a list of devices found on bus as well as a pointer to the I2CBus in use. class I2CDevice { public: + /// @brief we use the C++ default constructor I2CDevice() = default; + /// @brief We store the address of the device on the bus + /// @param address of the device void set_i2c_address(uint8_t address) { address_ = address; } + + /// @brief we store the pointer to the I2CBus to use + /// @param bus pointer to the I2CBus object void set_i2c_bus(I2CBus *bus) { bus_ = bus; } + /// @brief calls the I2CRegister constructor + /// @param a_register address of the I²C register + /// @return an I2CRegister proxy object I2CRegister reg(uint8_t a_register) { return {this, a_register}; } + + /// @brief calls the I2CRegister16 constructor + /// @param a_register 16 bits address of the I²C register + /// @return an I2CRegister16 proxy object I2CRegister16 reg16(uint16_t a_register) { return {this, a_register}; } + /// @brief reads an array of bytes from the device using an I2CBus + /// @param data pointer to an array to store the bytes + /// @param len length of the buffer = number of bytes to read + /// @return an i2c::ErrorCode ErrorCode read(uint8_t *data, size_t len) { return bus_->read(address_, data, len); } + + /// @brief reads an array of bytes from a specific register in the I²C device + /// @param a_register an 8 bits internal address of the I²C register to read from + /// @param data pointer to an array to store the bytes + /// @param len length of the buffer = number of bytes to read + /// @param stop (true/false): True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop = true); + + /// @brief reads an array of bytes from a specific register in the I²C device + /// @param a_register the 16 bits internal address of the I²C register to read from + /// @param data pointer to an array of bytes to store the information + /// @param len length of the buffer = number of bytes to read + /// @param stop (true/false): True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode ErrorCode read_register16(uint16_t a_register, uint8_t *data, size_t len, bool stop = true); - ErrorCode write(const uint8_t *data, uint8_t len, bool stop = true) { return bus_->write(address_, data, len, stop); } + /// @brief writes an array of bytes to a device using an I2CBus + /// @param data pointer to an array that contains the bytes to send + /// @param len length of the buffer = number of bytes to write + /// @param stop (true/false): True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode + ErrorCode write(const uint8_t *data, size_t len, bool stop = true) { return bus_->write(address_, data, len, stop); } + + /// @brief writes an array of bytes to a specific register in the I²C device + /// @param a_register the internal address of the register to read from + /// @param data pointer to an array to store the bytes + /// @param len length of the buffer = number of bytes to read + /// @param stop (true/false): True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop = true); + + /// @brief write an array of bytes to a specific register in the I²C device + /// @param a_register the 16 bits internal address of the register to read from + /// @param data pointer to an array to store the bytes + /// @param len length of the buffer = number of bytes to read + /// @param stop (true/false): True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode ErrorCode write_register16(uint16_t a_register, const uint8_t *data, size_t len, bool stop = true); - // Compat APIs + /// + /// Compat APIs + /// All methods below have been added for compatibility reasons. They do not bring any functionality and therefore on + /// new code it is not recommend to use them. + /// bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len) { return read_register(a_register, data, len) == ERROR_OK; } + bool read_bytes_raw(uint8_t *data, uint8_t len) { return read(data, len) == ERROR_OK; } template optional> read_bytes(uint8_t a_register) { @@ -131,8 +266,8 @@ class I2CDevice { bool write_byte_16(uint8_t a_register, uint16_t data) { return write_bytes_16(a_register, &data, 1); } protected: - uint8_t address_{0x00}; - I2CBus *bus_{nullptr}; + uint8_t address_{0x00}; ///< store the address of the device on the bus + I2CBus *bus_{nullptr}; ///< pointer to I2CBus instance }; } // namespace i2c diff --git a/esphome/components/i2c/i2c_bus.h b/esphome/components/i2c/i2c_bus.h index 2633a7adf6..fbfc88323e 100644 --- a/esphome/components/i2c/i2c_bus.h +++ b/esphome/components/i2c/i2c_bus.h @@ -7,50 +7,93 @@ namespace esphome { namespace i2c { +/// @brief Error codes returned by I2CBus and I2CDevice methods enum ErrorCode { - ERROR_OK = 0, - ERROR_INVALID_ARGUMENT = 1, - ERROR_NOT_ACKNOWLEDGED = 2, - ERROR_TIMEOUT = 3, - ERROR_NOT_INITIALIZED = 4, - ERROR_TOO_LARGE = 5, - ERROR_UNKNOWN = 6, - ERROR_CRC = 7, + NO_ERROR = 0, ///< No error found during execution of method + ERROR_OK = 0, ///< No error found during execution of method + ERROR_INVALID_ARGUMENT = 1, ///< method called invalid argument(s) + ERROR_NOT_ACKNOWLEDGED = 2, ///< I2C bus acknowledgment not received + ERROR_TIMEOUT = 3, ///< timeout while waiting to receive bytes + ERROR_NOT_INITIALIZED = 4, ///< call method to a not initialized bus + ERROR_TOO_LARGE = 5, ///< requested a transfer larger than buffers can hold + ERROR_UNKNOWN = 6, ///< miscellaneous I2C error during execution + ERROR_CRC = 7, ///< bytes received with a CRC error }; +/// @brief the ReadBuffer structure stores a pointer to a read buffer and its length struct ReadBuffer { - uint8_t *data; - size_t len; -}; -struct WriteBuffer { - const uint8_t *data; - size_t len; + uint8_t *data; ///< pointer to the read buffer + size_t len; ///< length of the buffer }; +/// @brief the WriteBuffer structure stores a pointer to a write buffer and its length +struct WriteBuffer { + const uint8_t *data; ///< pointer to the write buffer + size_t len; ///< length of the buffer +}; + +/// @brief This Class provides the methods to read and write bytes from an I2CBus. +/// @note The I2CBus virtual class follows a *Factory design pattern* that provides all the interfaces methods required +/// by clients while deferring the actual implementation of these methods to a subclasses. I2C-bus specification and +/// user manual can be found here https://www.nxp.com/docs/en/user-guide/UM10204.pdf and an interesting I²C Application +/// note https://www.nxp.com/docs/en/application-note/AN10216.pdf class I2CBus { public: + /// @brief Creates a ReadBuffer and calls the virtual readv() method to read bytes into this buffer + /// @param address address of the I²C component on the i2c bus + /// @param buffer pointer to an array of bytes that will be used to store the data received + /// @param len length of the buffer = number of bytes to read + /// @return an i2c::ErrorCode virtual ErrorCode read(uint8_t address, uint8_t *buffer, size_t len) { ReadBuffer buf; buf.data = buffer; buf.len = len; return readv(address, &buf, 1); } - virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) = 0; + + /// @brief This virtual method reads bytes from an I2CBus into an array of ReadBuffer. + /// @param address address of the I²C component on the i2c bus + /// @param buffers pointer to an array of ReadBuffer + /// @param count number of ReadBuffer to read + /// @return an i2c::ErrorCode + /// @details This is a pure virtual method that must be implemented in a subclass. + virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t count) = 0; + virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len) { return write(address, buffer, len, true); } + + /// @brief Creates a WriteBuffer and calls the writev() method to send the bytes from this buffer + /// @param address address of the I²C component on the i2c bus + /// @param buffer pointer to an array of bytes that contains the data to be sent + /// @param len length of the buffer = number of bytes to write + /// @param stop true or false: True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len, bool stop) { WriteBuffer buf; buf.data = buffer; buf.len = len; return writev(address, &buf, 1, stop); } + virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { return writev(address, buffers, cnt, true); } - virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) = 0; + + /// @brief This virtual method writes bytes to an I2CBus from an array of WriteBuffer. + /// @param address address of the I²C component on the i2c bus + /// @param buffers pointer to an array of WriteBuffer + /// @param count number of WriteBuffer to write + /// @param stop true or false: True will send a stop message, releasing the bus after + /// transmission. False will send a restart, keeping the connection active. + /// @return an i2c::ErrorCode + /// @details This is a pure virtual method that must be implemented in the subclass. + virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t count, bool stop) = 0; protected: + /// @brief Scans the I2C bus for devices. Devices presence is kept in an array of std::pair + /// that contains the address and the corresponding bool presence flag. void i2c_scan_() { for (uint8_t address = 8; address < 120; address++) { auto err = writev(address, nullptr, 0); @@ -61,8 +104,8 @@ class I2CBus { } } } - std::vector> scan_results_; - bool scan_{false}; + std::vector> scan_results_; ///< array containing scan results + bool scan_{false}; ///< Should we scan ? Can be set in the yaml }; } // namespace i2c From 977e0184a72880f917dbaa8cfe3d48b2cc862b52 Mon Sep 17 00:00:00 2001 From: mathieu-mp Date: Tue, 19 Dec 2023 04:29:00 +0100 Subject: [PATCH 044/193] Add deep sleep between updates for waveshare epaper 1.54in and 1.54inv2 (#5961) --- .../waveshare_epaper/waveshare_epaper.cpp | 33 +++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 25 ++++++++++---- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 53bfa57f4f..8fdb9a3ac0 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -167,6 +167,25 @@ void WaveshareEPaper::on_safe_shutdown() { this->deep_sleep(); } // ======================================================== void WaveshareEPaperTypeA::initialize() { + // Achieve display intialization + this->init_display_(); + // If a reset pin is configured, eligible displays can be set to deep sleep + // between updates, as recommended by the hardware provider + if (this->reset_pin_ != nullptr) { + switch (this->model_) { + // More models can be added here to enable deep sleep if eligible + case WAVESHARE_EPAPER_1_54_IN: + case WAVESHARE_EPAPER_1_54_IN_V2: + this->deep_sleep_between_updates_ = true; + ESP_LOGI(TAG, "Set the display to deep sleep"); + this->deep_sleep(); + break; + default: + break; + } + } +} +void WaveshareEPaperTypeA::init_display_() { if (this->model_ == TTGO_EPAPER_2_13_IN_B74) { this->reset_pin_->digital_write(false); delay(10); @@ -261,6 +280,13 @@ void HOT WaveshareEPaperTypeA::display() { bool full_update = this->at_update_ == 0; bool prev_full_update = this->at_update_ == 1; + if (this->deep_sleep_between_updates_) { + ESP_LOGI(TAG, "Wake up the display"); + this->reset_(); + this->wait_until_idle_(); + this->init_display_(); + } + if (!this->wait_until_idle_()) { this->status_set_warning(); return; @@ -384,6 +410,11 @@ void HOT WaveshareEPaperTypeA::display() { this->command(0xFF); this->status_clear_warning(); + + if (this->deep_sleep_between_updates_) { + ESP_LOGI(TAG, "Set the display back to deep sleep"); + this->deep_sleep(); + } } int WaveshareEPaperTypeA::get_width_internal() { switch (this->model_) { @@ -445,6 +476,8 @@ void WaveshareEPaperTypeA::set_full_update_every(uint32_t full_update_every) { uint32_t WaveshareEPaperTypeA::idle_timeout_() { switch (this->model_) { + case WAVESHARE_EPAPER_1_54_IN: + case WAVESHARE_EPAPER_1_54_IN_V2: case TTGO_EPAPER_2_13_IN_B1: return 2500; default: diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index f6ccf90861..42e8a16829 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -92,13 +92,20 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { void display() override; void deep_sleep() override { - if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2 || this->model_ == WAVESHARE_EPAPER_1_54_IN_V2) { - // COMMAND DEEP SLEEP MODE - this->command(0x10); - this->data(0x01); - } else { - // COMMAND DEEP SLEEP MODE - this->command(0x10); + switch (this->model_) { + // Models with specific deep sleep command and data + case WAVESHARE_EPAPER_1_54_IN: + case WAVESHARE_EPAPER_1_54_IN_V2: + case WAVESHARE_EPAPER_2_9_IN_V2: + // COMMAND DEEP SLEEP MODE + this->command(0x10); + this->data(0x01); + break; + // Other models default to simple deep sleep command + default: + // COMMAND DEEP SLEEP + this->command(0x10); + break; } this->wait_until_idle_(); } @@ -108,6 +115,8 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { protected: void write_lut_(const uint8_t *lut, uint8_t size); + void init_display_(); + int get_width_internal() override; int get_height_internal() override; @@ -118,6 +127,8 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { uint32_t at_update_{0}; WaveshareEPaperTypeAModel model_; uint32_t idle_timeout_() override; + + bool deep_sleep_between_updates_{false}; }; enum WaveshareEPaperTypeBModel { From e5414d70f5c9f3b6aab84b14a198611df3f0c34e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 18 Dec 2023 19:24:48 -1000 Subject: [PATCH 045/193] Speed up writing protobuf strings/bytes (#5828) --- esphome/components/api/proto.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index fea219ecb9..ccc6c0d52c 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -160,8 +160,7 @@ class ProtoWriteBuffer { this->encode_field_raw(field_id, 2); this->encode_varint_raw(len); auto *data = reinterpret_cast(string); - for (size_t i = 0; i < len; i++) - this->write(data[i]); + this->buffer_->insert(this->buffer_->end(), data, data + len); } void encode_string(uint32_t field_id, const std::string &value, bool force = false) { this->encode_string(field_id, value.data(), value.size()); From 1b3068a4092e72263dc252b43848ae959f9028cd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 19 Dec 2023 13:41:26 -1000 Subject: [PATCH 046/193] Bump aioesphomeapi to 21.0.1 (#5969) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 20a5514e71..b9bcd5dae0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ platformio==6.1.11 # When updating platformio, also update Dockerfile esptool==4.6.2 click==8.1.7 esphome-dashboard==20231107.0 -aioesphomeapi==21.0.0 +aioesphomeapi==21.0.1 zeroconf==0.130.0 python-magic==0.4.27 From e2a00f66b8c2150f49538af8282b80d6fd8aae30 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Dec 2023 09:59:58 +0900 Subject: [PATCH 047/193] Bump version to 2023.12.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 4153646aea..20218ab61d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.12.0b3" +__version__ = "2023.12.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From fbf3d03a33289aa8f47a664b11a724621f499142 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Mon, 18 Dec 2023 20:23:22 +0100 Subject: [PATCH 048/193] rename set_raw_touch_position_ to add_raw_touch_position_ (#5962) --- esphome/components/ektf2232/touchscreen/ektf2232.cpp | 2 +- esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.h | 2 +- esphome/components/ft63x6/ft63x6.cpp | 4 ++-- esphome/components/gt911/touchscreen/gt911_touchscreen.cpp | 2 +- .../lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp | 2 +- esphome/components/touchscreen/touchscreen.cpp | 2 +- esphome/components/touchscreen/touchscreen.h | 2 +- esphome/components/tt21100/touchscreen/tt21100.cpp | 2 +- esphome/components/xpt2046/touchscreen/xpt2046.cpp | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/esphome/components/ektf2232/touchscreen/ektf2232.cpp b/esphome/components/ektf2232/touchscreen/ektf2232.cpp index 1a2c0389af..00e00bc7e6 100644 --- a/esphome/components/ektf2232/touchscreen/ektf2232.cpp +++ b/esphome/components/ektf2232/touchscreen/ektf2232.cpp @@ -74,7 +74,7 @@ void EKTF2232Touchscreen::update_touches() { uint8_t *d = raw + 1 + (i * 3); x_raw = (d[0] & 0xF0) << 4 | d[1]; y_raw = (d[0] & 0x0F) << 8 | d[2]; - this->set_raw_touch_position_(i, x_raw, y_raw); + this->add_raw_touch_position_(i, x_raw, y_raw); } } diff --git a/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.h b/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.h index 497d6c906c..0b3a2c1b86 100644 --- a/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.h +++ b/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.h @@ -94,7 +94,7 @@ class FT5x06Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice esph_log_d(TAG, "Read %X status, id: %d, pos %d/%d", status, id, x, y); if (status == 0 || status == 2) { - this->set_raw_touch_position_(id, x, y); + this->add_raw_touch_position_(id, x, y); } } } diff --git a/esphome/components/ft63x6/ft63x6.cpp b/esphome/components/ft63x6/ft63x6.cpp index 9198954253..b674ded22c 100644 --- a/esphome/components/ft63x6/ft63x6.cpp +++ b/esphome/components/ft63x6/ft63x6.cpp @@ -53,13 +53,13 @@ void FT63X6Touchscreen::update_touches() { uint8_t touch_id = this->read_touch_id_(FT63X6_ADDR_TOUCH1_ID); // id1 = 0 or 1 int16_t x = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH1_X); int16_t y = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH1_Y); - this->set_raw_touch_position_(touch_id, x, y); + this->add_raw_touch_position_(touch_id, x, y); if (touch_count >= 2) { touch_id = this->read_touch_id_(FT63X6_ADDR_TOUCH2_ID); // id2 = 0 or 1(~id1 & 0x01) x = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH2_X); y = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH2_Y); - this->set_raw_touch_position_(touch_id, x, y); + this->add_raw_touch_position_(touch_id, x, y); } } diff --git a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp index adc577f5da..84854d5b0d 100644 --- a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +++ b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp @@ -92,7 +92,7 @@ void GT911Touchscreen::update_touches() { uint16_t id = data[i][0]; uint16_t x = encode_uint16(data[i][2], data[i][1]); uint16_t y = encode_uint16(data[i][4], data[i][3]); - this->set_raw_touch_position_(id, x, y); + this->add_raw_touch_position_(id, x, y); } auto keys = data[num_of_touches][0]; for (size_t i = 0; i != 4; i++) { diff --git a/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp index eb61b6f31e..64cc7ad4d1 100644 --- a/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +++ b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp @@ -84,7 +84,7 @@ void LilygoT547Touchscreen::update_touches() { id = (buffer[i * 5] >> 4) & 0x0F; y_raw = (uint16_t) ((buffer[i * 5 + 1] << 4) | ((buffer[i * 5 + 3] >> 4) & 0x0F)); x_raw = (uint16_t) ((buffer[i * 5 + 2] << 4) | (buffer[i * 5 + 3] & 0x0F)); - this->set_raw_touch_position_(id, x_raw, y_raw); + this->add_raw_touch_position_(id, x_raw, y_raw); } this->status_clear_warning(); diff --git a/esphome/components/touchscreen/touchscreen.cpp b/esphome/components/touchscreen/touchscreen.cpp index 140f46b6f6..f095c2af8c 100644 --- a/esphome/components/touchscreen/touchscreen.cpp +++ b/esphome/components/touchscreen/touchscreen.cpp @@ -51,7 +51,7 @@ void Touchscreen::loop() { } } -void Touchscreen::set_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw) { +void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw) { TouchPoint tp; uint16_t x, y; if (this->touches_.count(id) == 0) { diff --git a/esphome/components/touchscreen/touchscreen.h b/esphome/components/touchscreen/touchscreen.h index 1fe304d967..74747c589c 100644 --- a/esphome/components/touchscreen/touchscreen.h +++ b/esphome/components/touchscreen/touchscreen.h @@ -87,7 +87,7 @@ class Touchscreen : public PollingComponent { void attach_interrupt_(InternalGPIOPin *irq_pin, esphome::gpio::InterruptType type); - void set_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw = 0); + void add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_raw, int16_t z_raw = 0); void send_touches_(); diff --git a/esphome/components/tt21100/touchscreen/tt21100.cpp b/esphome/components/tt21100/touchscreen/tt21100.cpp index ff688fd0b0..6b5cba74cd 100644 --- a/esphome/components/tt21100/touchscreen/tt21100.cpp +++ b/esphome/components/tt21100/touchscreen/tt21100.cpp @@ -109,7 +109,7 @@ void TT21100Touchscreen::update_touches() { i, touch->touch_type, touch->tip, touch->event_id, touch->touch_id, touch->x, touch->y, touch->pressure, touch->major_axis_length, touch->orientation); - this->set_raw_touch_position_(touch->tip, touch->x, touch->y, touch->pressure); + this->add_raw_touch_position_(touch->tip, touch->x, touch->y, touch->pressure); } } } diff --git a/esphome/components/xpt2046/touchscreen/xpt2046.cpp b/esphome/components/xpt2046/touchscreen/xpt2046.cpp index 1a9c202af0..a268da06dd 100644 --- a/esphome/components/xpt2046/touchscreen/xpt2046.cpp +++ b/esphome/components/xpt2046/touchscreen/xpt2046.cpp @@ -55,7 +55,7 @@ void XPT2046Component::update_touches() { ESP_LOGV(TAG, "Touchscreen Update [%d, %d], z = %d", x_raw, y_raw, z_raw); - this->set_raw_touch_position_(0, x_raw, y_raw, z_raw); + this->add_raw_touch_position_(0, x_raw, y_raw, z_raw); } } From a64e96e7adbca4879c5d3b239272f8052e5d91ff Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Dec 2023 13:43:43 +0900 Subject: [PATCH 049/193] Bump version to 2023.12.0b5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 20218ab61d..0df9c27233 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.12.0b4" +__version__ = "2023.12.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From d582cfa30a0ec8406a7d55cbe35f342c36e438e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Wed, 20 Dec 2023 11:33:05 +0100 Subject: [PATCH 050/193] image: allow the image to by auto-loaded by animation (#5139) --- esphome/__main__.py | 4 ++-- esphome/components/image/__init__.py | 1 + esphome/config.py | 20 ++++++++++++++++++-- esphome/loader.py | 4 ++++ esphome/writer.py | 8 ++++---- tests/test8.yaml | 10 ++++++++++ 6 files changed, 39 insertions(+), 8 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 0796dead43..baa5ecde47 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -12,7 +12,7 @@ import argcomplete from esphome import const, writer, yaml_util import esphome.codegen as cg -from esphome.config import iter_components, read_config, strip_default_ids +from esphome.config import iter_component_configs, read_config, strip_default_ids from esphome.const import ( ALLOWED_NAME_CHARS, CONF_BAUD_RATE, @@ -196,7 +196,7 @@ def write_cpp(config): def generate_cpp_contents(config): _LOGGER.info("Generating C++ source...") - for name, component, conf in iter_components(CORE.config): + for name, component, conf in iter_component_configs(CORE.config): if component.to_code is not None: coro = wrap_to_code(name, component) CORE.add_job(coro, conf) diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index c11021fc9c..73dc73aa45 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -36,6 +36,7 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = "image" DEPENDENCIES = ["display"] MULTI_CONF = True +MULTI_CONF_NO_DEFAULT = True image_ns = cg.esphome_ns.namespace("image") diff --git a/esphome/config.py b/esphome/config.py index 745883c2ef..e9433d537e 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -39,6 +39,17 @@ _LOGGER = logging.getLogger(__name__) def iter_components(config): + for domain, conf in config.items(): + component = get_component(domain) + yield domain, component + if component.is_platform_component: + for p_config in conf: + p_name = f"{domain}.{p_config[CONF_PLATFORM]}" + platform = get_platform(domain, p_config[CONF_PLATFORM]) + yield p_name, platform + + +def iter_component_configs(config): for domain, conf in config.items(): component = get_component(domain) if component.multi_conf: @@ -303,8 +314,10 @@ class LoadValidationStep(ConfigValidationStep): # Ignore top-level keys starting with a dot return result.add_output_path([self.domain], self.domain) - result[self.domain] = self.conf component = get_component(self.domain) + if component.multi_conf_no_default and isinstance(self.conf, core.AutoLoad): + self.conf = [] + result[self.domain] = self.conf path = [self.domain] if component is None: result.add_str_error(f"Component not found: {self.domain}", path) @@ -424,7 +437,10 @@ class MetadataValidationStep(ConfigValidationStep): def run(self, result: Config) -> None: if self.conf is None: - result[self.domain] = self.conf = {} + if self.comp.multi_conf and self.comp.multi_conf_no_default: + result[self.domain] = self.conf = [] + else: + result[self.domain] = self.conf = {} success = True for dependency in self.comp.dependencies: diff --git a/esphome/loader.py b/esphome/loader.py index cd21e5a509..40a38d0a14 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -57,6 +57,10 @@ class ComponentManifest: def multi_conf(self) -> bool: return getattr(self.module, "MULTI_CONF", False) + @property + def multi_conf_no_default(self) -> bool: + return getattr(self.module, "MULTI_CONF_NO_DEFAULT", False) + @property def to_code(self) -> Optional[Callable[[Any], None]]: return getattr(self.module, "to_code", None) diff --git a/esphome/writer.py b/esphome/writer.py index 83e95614a6..3ad0e60d31 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -4,7 +4,7 @@ import re from pathlib import Path from typing import Union -from esphome.config import iter_components +from esphome.config import iter_components, iter_component_configs from esphome.const import ( HEADER_FILE_EXTENSIONS, SOURCE_FILE_EXTENSIONS, @@ -70,14 +70,14 @@ UPLOAD_SPEED_OVERRIDE = { def get_flags(key): flags = set() - for _, component, conf in iter_components(CORE.config): + for _, component, conf in iter_component_configs(CORE.config): flags |= getattr(component, key)(conf) return flags def get_include_text(): include_text = '#include "esphome.h"\nusing namespace esphome;\n' - for _, component, conf in iter_components(CORE.config): + for _, component, conf in iter_component_configs(CORE.config): if not hasattr(component, "includes"): continue includes = component.includes @@ -232,7 +232,7 @@ the custom_components folder or the external_components feature. def copy_src_tree(): source_files: list[loader.FileResource] = [] - for _, component, _ in iter_components(CORE.config): + for _, component in iter_components(CORE.config): source_files += component.resources source_files_map = { Path(x.package.replace(".", "/") + "/" + x.resource): x for x in source_files diff --git a/tests/test8.yaml b/tests/test8.yaml index 558e86e1f9..fafdb76e12 100644 --- a/tests/test8.yaml +++ b/tests/test8.yaml @@ -92,3 +92,13 @@ sensor: name: "Loop Time" psram: name: "PSRAM Free" + +# Purposely test that `animation:` does auto-load `image:` +# Keep the `image:` undefined. +# image: + +animation: + - id: rgb565_animation + file: pnglogo.png + type: RGB565 + use_transparency: no From 84174aeb8031db638bfa91b73ed8ec93e63ce518 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Dec 2023 19:42:27 +0900 Subject: [PATCH 051/193] Fix pin reuse error with pin expanders (#5973) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- esphome/pins.py | 20 +++++++++++--------- tests/test1.yaml | 4 ---- tests/test3.1.yaml | 2 -- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/esphome/pins.py b/esphome/pins.py index e2fd8e98e2..87f7084d4f 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -1,7 +1,7 @@ import operator from functools import reduce import esphome.config_validation as cv -from esphome.core import CORE, ID +from esphome.core import CORE from esphome.const import ( CONF_INPUT, @@ -25,15 +25,16 @@ class PinRegistry(dict): def reset(self): self.pins_used = {} - def get_count(self, key, number): + def get_count(self, key, id, number): """ Get the number of places a given pin is used. - :param key: The ID of the defining component + :param key: The key of the registered pin schema. + :param id: The ID of the defining component :param number: The pin number :return: The number of places the pin is used. """ - pin_key = (key, number) - return self.pins_used[pin_key] if pin_key in self.pins_used else 0 + pin_key = (key, id, number) + return len(self.pins_used[pin_key]) if pin_key in self.pins_used else 0 def register(self, name, schema, final_validate=None): """ @@ -65,9 +66,10 @@ class PinRegistry(dict): result = self[key][1](conf) if CONF_NUMBER in result: # key maps to the pin schema - if isinstance(key, ID): - key = key.id - pin_key = (key, result[CONF_NUMBER]) + if key != CORE.target_platform: + pin_key = (key, conf[key], result[CONF_NUMBER]) + else: + pin_key = (key, key, result[CONF_NUMBER]) if pin_key not in self.pins_used: self.pins_used[pin_key] = [] # client_id identifies the instance of the providing component @@ -101,7 +103,7 @@ class PinRegistry(dict): Run the final validation for all pins, and check for reuse :param fconf: The full config """ - for (key, _), pin_list in self.pins_used.items(): + for (key, _, _), pin_list in self.pins_used.items(): count = len(pin_list) # number of places same pin used. final_val_fun = self[key][2] # final validation function for pin_path, client_id, pin_config in pin_list: diff --git a/tests/test1.yaml b/tests/test1.yaml index f7b433cce2..3f8ae0151f 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1667,7 +1667,6 @@ binary_sensor: mcp23xxx: mcp23s08_hub # Use pin number 1 number: 1 - allow_other_uses: true # One of INPUT or INPUT_PULLUP mode: INPUT_PULLUP inverted: false @@ -2149,7 +2148,6 @@ output: pin: mcp23xxx: mcp23017_hub number: 0 - allow_other_uses: true mode: OUTPUT inverted: false - platform: gpio @@ -2157,7 +2155,6 @@ output: pin: mcp23xxx: mcp23008_hub number: 0 - allow_other_uses: true mode: OUTPUT inverted: false - platform: gpio @@ -2597,7 +2594,6 @@ switch: mcp23xxx: mcp23s08_hub # Use pin number 0 number: 0 - allow_other_uses: true mode: OUTPUT inverted: false - platform: gpio diff --git a/tests/test3.1.yaml b/tests/test3.1.yaml index 63ef4e8ce0..b5428abbfa 100644 --- a/tests/test3.1.yaml +++ b/tests/test3.1.yaml @@ -401,7 +401,6 @@ switch: pin: mcp23xxx: mcp23017_hub number: 0 - allow_other_uses: true mode: OUTPUT interlock: &interlock [gpio_switch1, gpio_switch2, gpio_switch3] - platform: gpio @@ -409,7 +408,6 @@ switch: pin: mcp23xxx: mcp23008_hub number: 0 - allow_other_uses: true mode: OUTPUT interlock: *interlock - platform: gpio From 23ceddafed185ab38a8326782994b9c7e1f07f3c Mon Sep 17 00:00:00 2001 From: Yorick Smilda Date: Wed, 20 Dec 2023 11:52:46 +0100 Subject: [PATCH 052/193] Add ability to lock to set mode (#5924) --- esphome/components/hlw8012/hlw8012.cpp | 2 +- esphome/components/hlw8012/sensor.py | 11 ++++++++--- tests/test1.yaml | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/esphome/components/hlw8012/hlw8012.cpp b/esphome/components/hlw8012/hlw8012.cpp index 1a9f47faaf..14e83f60e1 100644 --- a/esphome/components/hlw8012/hlw8012.cpp +++ b/esphome/components/hlw8012/hlw8012.cpp @@ -96,7 +96,7 @@ void HLW8012Component::update() { this->energy_sensor_->publish_state(energy); } - if (this->change_mode_at_++ == this->change_mode_every_) { + if (this->change_mode_every_ != 0 && this->change_mode_at_++ == this->change_mode_every_) { this->current_mode_ = !this->current_mode_; ESP_LOGV(TAG, "Changing mode to %s mode", this->current_mode_ ? "CURRENT" : "VOLTAGE"); this->change_mode_at_ = 0; diff --git a/esphome/components/hlw8012/sensor.py b/esphome/components/hlw8012/sensor.py index 033cccc3d4..2687edaca2 100644 --- a/esphome/components/hlw8012/sensor.py +++ b/esphome/components/hlw8012/sensor.py @@ -79,8 +79,9 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance, cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float, cv.Optional(CONF_MODEL, default="HLW8012"): cv.enum(MODELS, upper=True), - cv.Optional(CONF_CHANGE_MODE_EVERY, default=8): cv.All( - cv.uint32_t, cv.Range(min=1) + cv.Optional(CONF_CHANGE_MODE_EVERY, default=8): cv.Any( + "never", + cv.All(cv.uint32_t, cv.Range(min=1)), ), cv.Optional(CONF_INITIAL_MODE, default=CONF_VOLTAGE): cv.one_of( *INITIAL_MODES, lower=True @@ -114,6 +115,10 @@ async def to_code(config): cg.add(var.set_energy_sensor(sens)) cg.add(var.set_current_resistor(config[CONF_CURRENT_RESISTOR])) cg.add(var.set_voltage_divider(config[CONF_VOLTAGE_DIVIDER])) - cg.add(var.set_change_mode_every(config[CONF_CHANGE_MODE_EVERY])) cg.add(var.set_initial_mode(INITIAL_MODES[config[CONF_INITIAL_MODE]])) cg.add(var.set_sensor_model(config[CONF_MODEL])) + + interval = config[CONF_CHANGE_MODE_EVERY] + if interval == "never": + interval = 0 + cg.add(var.set_change_mode_every(interval)) diff --git a/tests/test1.yaml b/tests/test1.yaml index 3f8ae0151f..aaad10ff0a 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -794,7 +794,7 @@ sensor: update_interval: 15s current_resistor: 0.001 ohm voltage_divider: 2351 - change_mode_every: 16 + change_mode_every: "never" initial_mode: VOLTAGE model: hlw8012 - platform: total_daily_energy From ab25e32509798e8564bad1b556f8ad552f13880f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Wed, 20 Dec 2023 11:33:05 +0100 Subject: [PATCH 053/193] image: allow the image to by auto-loaded by animation (#5139) --- esphome/__main__.py | 4 ++-- esphome/components/image/__init__.py | 1 + esphome/config.py | 20 ++++++++++++++++++-- esphome/loader.py | 4 ++++ esphome/writer.py | 8 ++++---- tests/test8.yaml | 10 ++++++++++ 6 files changed, 39 insertions(+), 8 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 0796dead43..baa5ecde47 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -12,7 +12,7 @@ import argcomplete from esphome import const, writer, yaml_util import esphome.codegen as cg -from esphome.config import iter_components, read_config, strip_default_ids +from esphome.config import iter_component_configs, read_config, strip_default_ids from esphome.const import ( ALLOWED_NAME_CHARS, CONF_BAUD_RATE, @@ -196,7 +196,7 @@ def write_cpp(config): def generate_cpp_contents(config): _LOGGER.info("Generating C++ source...") - for name, component, conf in iter_components(CORE.config): + for name, component, conf in iter_component_configs(CORE.config): if component.to_code is not None: coro = wrap_to_code(name, component) CORE.add_job(coro, conf) diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index c11021fc9c..73dc73aa45 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -36,6 +36,7 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = "image" DEPENDENCIES = ["display"] MULTI_CONF = True +MULTI_CONF_NO_DEFAULT = True image_ns = cg.esphome_ns.namespace("image") diff --git a/esphome/config.py b/esphome/config.py index 745883c2ef..e9433d537e 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -39,6 +39,17 @@ _LOGGER = logging.getLogger(__name__) def iter_components(config): + for domain, conf in config.items(): + component = get_component(domain) + yield domain, component + if component.is_platform_component: + for p_config in conf: + p_name = f"{domain}.{p_config[CONF_PLATFORM]}" + platform = get_platform(domain, p_config[CONF_PLATFORM]) + yield p_name, platform + + +def iter_component_configs(config): for domain, conf in config.items(): component = get_component(domain) if component.multi_conf: @@ -303,8 +314,10 @@ class LoadValidationStep(ConfigValidationStep): # Ignore top-level keys starting with a dot return result.add_output_path([self.domain], self.domain) - result[self.domain] = self.conf component = get_component(self.domain) + if component.multi_conf_no_default and isinstance(self.conf, core.AutoLoad): + self.conf = [] + result[self.domain] = self.conf path = [self.domain] if component is None: result.add_str_error(f"Component not found: {self.domain}", path) @@ -424,7 +437,10 @@ class MetadataValidationStep(ConfigValidationStep): def run(self, result: Config) -> None: if self.conf is None: - result[self.domain] = self.conf = {} + if self.comp.multi_conf and self.comp.multi_conf_no_default: + result[self.domain] = self.conf = [] + else: + result[self.domain] = self.conf = {} success = True for dependency in self.comp.dependencies: diff --git a/esphome/loader.py b/esphome/loader.py index cd21e5a509..40a38d0a14 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -57,6 +57,10 @@ class ComponentManifest: def multi_conf(self) -> bool: return getattr(self.module, "MULTI_CONF", False) + @property + def multi_conf_no_default(self) -> bool: + return getattr(self.module, "MULTI_CONF_NO_DEFAULT", False) + @property def to_code(self) -> Optional[Callable[[Any], None]]: return getattr(self.module, "to_code", None) diff --git a/esphome/writer.py b/esphome/writer.py index 83e95614a6..3ad0e60d31 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -4,7 +4,7 @@ import re from pathlib import Path from typing import Union -from esphome.config import iter_components +from esphome.config import iter_components, iter_component_configs from esphome.const import ( HEADER_FILE_EXTENSIONS, SOURCE_FILE_EXTENSIONS, @@ -70,14 +70,14 @@ UPLOAD_SPEED_OVERRIDE = { def get_flags(key): flags = set() - for _, component, conf in iter_components(CORE.config): + for _, component, conf in iter_component_configs(CORE.config): flags |= getattr(component, key)(conf) return flags def get_include_text(): include_text = '#include "esphome.h"\nusing namespace esphome;\n' - for _, component, conf in iter_components(CORE.config): + for _, component, conf in iter_component_configs(CORE.config): if not hasattr(component, "includes"): continue includes = component.includes @@ -232,7 +232,7 @@ the custom_components folder or the external_components feature. def copy_src_tree(): source_files: list[loader.FileResource] = [] - for _, component, _ in iter_components(CORE.config): + for _, component in iter_components(CORE.config): source_files += component.resources source_files_map = { Path(x.package.replace(".", "/") + "/" + x.resource): x for x in source_files diff --git a/tests/test8.yaml b/tests/test8.yaml index 558e86e1f9..fafdb76e12 100644 --- a/tests/test8.yaml +++ b/tests/test8.yaml @@ -92,3 +92,13 @@ sensor: name: "Loop Time" psram: name: "PSRAM Free" + +# Purposely test that `animation:` does auto-load `image:` +# Keep the `image:` undefined. +# image: + +animation: + - id: rgb565_animation + file: pnglogo.png + type: RGB565 + use_transparency: no From 6a9e85438f95b14be54bdf9a961897bdd78224b9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Dec 2023 19:42:27 +0900 Subject: [PATCH 054/193] Fix pin reuse error with pin expanders (#5973) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- esphome/pins.py | 20 +++++++++++--------- tests/test1.yaml | 4 ---- tests/test3.1.yaml | 2 -- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/esphome/pins.py b/esphome/pins.py index e2fd8e98e2..87f7084d4f 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -1,7 +1,7 @@ import operator from functools import reduce import esphome.config_validation as cv -from esphome.core import CORE, ID +from esphome.core import CORE from esphome.const import ( CONF_INPUT, @@ -25,15 +25,16 @@ class PinRegistry(dict): def reset(self): self.pins_used = {} - def get_count(self, key, number): + def get_count(self, key, id, number): """ Get the number of places a given pin is used. - :param key: The ID of the defining component + :param key: The key of the registered pin schema. + :param id: The ID of the defining component :param number: The pin number :return: The number of places the pin is used. """ - pin_key = (key, number) - return self.pins_used[pin_key] if pin_key in self.pins_used else 0 + pin_key = (key, id, number) + return len(self.pins_used[pin_key]) if pin_key in self.pins_used else 0 def register(self, name, schema, final_validate=None): """ @@ -65,9 +66,10 @@ class PinRegistry(dict): result = self[key][1](conf) if CONF_NUMBER in result: # key maps to the pin schema - if isinstance(key, ID): - key = key.id - pin_key = (key, result[CONF_NUMBER]) + if key != CORE.target_platform: + pin_key = (key, conf[key], result[CONF_NUMBER]) + else: + pin_key = (key, key, result[CONF_NUMBER]) if pin_key not in self.pins_used: self.pins_used[pin_key] = [] # client_id identifies the instance of the providing component @@ -101,7 +103,7 @@ class PinRegistry(dict): Run the final validation for all pins, and check for reuse :param fconf: The full config """ - for (key, _), pin_list in self.pins_used.items(): + for (key, _, _), pin_list in self.pins_used.items(): count = len(pin_list) # number of places same pin used. final_val_fun = self[key][2] # final validation function for pin_path, client_id, pin_config in pin_list: diff --git a/tests/test1.yaml b/tests/test1.yaml index f7b433cce2..3f8ae0151f 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1667,7 +1667,6 @@ binary_sensor: mcp23xxx: mcp23s08_hub # Use pin number 1 number: 1 - allow_other_uses: true # One of INPUT or INPUT_PULLUP mode: INPUT_PULLUP inverted: false @@ -2149,7 +2148,6 @@ output: pin: mcp23xxx: mcp23017_hub number: 0 - allow_other_uses: true mode: OUTPUT inverted: false - platform: gpio @@ -2157,7 +2155,6 @@ output: pin: mcp23xxx: mcp23008_hub number: 0 - allow_other_uses: true mode: OUTPUT inverted: false - platform: gpio @@ -2597,7 +2594,6 @@ switch: mcp23xxx: mcp23s08_hub # Use pin number 0 number: 0 - allow_other_uses: true mode: OUTPUT inverted: false - platform: gpio diff --git a/tests/test3.1.yaml b/tests/test3.1.yaml index 63ef4e8ce0..b5428abbfa 100644 --- a/tests/test3.1.yaml +++ b/tests/test3.1.yaml @@ -401,7 +401,6 @@ switch: pin: mcp23xxx: mcp23017_hub number: 0 - allow_other_uses: true mode: OUTPUT interlock: &interlock [gpio_switch1, gpio_switch2, gpio_switch3] - platform: gpio @@ -409,7 +408,6 @@ switch: pin: mcp23xxx: mcp23008_hub number: 0 - allow_other_uses: true mode: OUTPUT interlock: *interlock - platform: gpio From bec1ad9396806086f61eae699ca6d3d8210682a7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Dec 2023 20:59:46 +0900 Subject: [PATCH 055/193] Bump version to 2023.12.0b6 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 0df9c27233..475f28c35e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.12.0b5" +__version__ = "2023.12.0b6" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 867595561417deb035fcde921a33db72d1b8c3fa Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 21 Dec 2023 08:09:10 +0900 Subject: [PATCH 056/193] Bump version to 2023.12.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 475f28c35e..55be91a4f9 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.12.0b6" +__version__ = "2023.12.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 991880d53fa2d21512dfeaf84ee1380a4bd32bbe Mon Sep 17 00:00:00 2001 From: Branden Cash <203336+ammmze@users.noreply.github.com> Date: Wed, 20 Dec 2023 17:07:40 -0700 Subject: [PATCH 057/193] feat: add AS5600 component/sensor (#5174) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 2 + esphome/components/as5600/__init__.py | 228 ++++++++++++++++++ esphome/components/as5600/as5600.cpp | 138 +++++++++++ esphome/components/as5600/as5600.h | 105 ++++++++ esphome/components/as5600/sensor/__init__.py | 119 +++++++++ .../as5600/sensor/as5600_sensor.cpp | 98 ++++++++ .../components/as5600/sensor/as5600_sensor.h | 43 ++++ tests/test1.yaml | 22 ++ 8 files changed, 755 insertions(+) create mode 100644 esphome/components/as5600/__init__.py create mode 100644 esphome/components/as5600/as5600.cpp create mode 100644 esphome/components/as5600/as5600.h create mode 100644 esphome/components/as5600/sensor/__init__.py create mode 100644 esphome/components/as5600/sensor/as5600_sensor.cpp create mode 100644 esphome/components/as5600/sensor/as5600_sensor.h diff --git a/CODEOWNERS b/CODEOWNERS index 320a23ffaa..fc14b2088e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -34,6 +34,8 @@ esphome/components/analog_threshold/* @ianchi esphome/components/animation/* @syndlex esphome/components/anova/* @buxtronix esphome/components/api/* @OttoWinter +esphome/components/as5600/* @ammmze +esphome/components/as5600/sensor/* @ammmze esphome/components/as7341/* @mrgnr esphome/components/async_tcp/* @OttoWinter esphome/components/atc_mithermometer/* @ahpohl diff --git a/esphome/components/as5600/__init__.py b/esphome/components/as5600/__init__.py new file mode 100644 index 0000000000..1840b22768 --- /dev/null +++ b/esphome/components/as5600/__init__.py @@ -0,0 +1,228 @@ +from esphome import pins +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c +from esphome.const import ( + CONF_ID, + CONF_DIR_PIN, + CONF_DIRECTION, + CONF_HYSTERESIS, + CONF_RANGE, +) + +CODEOWNERS = ["@ammmze"] +DEPENDENCIES = ["i2c"] +MULTI_CONF = True + +as5600_ns = cg.esphome_ns.namespace("as5600") +AS5600Component = as5600_ns.class_("AS5600Component", cg.Component, i2c.I2CDevice) + +DIRECTION = { + "CLOCKWISE": 0, + "COUNTERCLOCKWISE": 1, +} + +POWER_MODE = { + "NOMINAL": 0, + "LOW1": 1, + "LOW2": 2, + "LOW3": 3, +} + +HYSTERESIS = { + "NONE": 0, + "LSB1": 1, + "LSB2": 2, + "LSB3": 3, +} + +SLOW_FILTER = { + "16X": 0, + "8X": 1, + "4X": 2, + "2X": 3, +} + +FAST_FILTER = { + "NONE": 0, + "LSB6": 1, + "LSB7": 2, + "LSB9": 3, + "LSB18": 4, + "LSB21": 5, + "LSB24": 6, + "LSB10": 7, +} + +CONF_ANGLE = "angle" +CONF_RAW_ANGLE = "raw_angle" +CONF_RAW_POSITION = "raw_position" +CONF_WATCHDOG = "watchdog" +CONF_POWER_MODE = "power_mode" +CONF_SLOW_FILTER = "slow_filter" +CONF_FAST_FILTER = "fast_filter" +CONF_START_POSITION = "start_position" +CONF_END_POSITION = "end_position" + + +RESOLUTION = 4096 +MAX_POSITION = RESOLUTION - 1 +ANGLE_TO_POSITION = RESOLUTION / 360 +POSITION_TO_ANGLE = 360 / RESOLUTION +# validate min range of 18deg (per datasheet) ... though i seem to get valid values down to a range of 192steps (16.875deg) +MIN_RANGE = round(18 * ANGLE_TO_POSITION) + + +def angle(min=-360, max=360): + return cv.All( + cv.float_with_unit("angle", "(°|deg)"), cv.float_range(min=min, max=max) + ) + + +def angle_to_position(value, min=-360, max=360): + try: + value = angle(min=min, max=max)(value) + return (RESOLUTION + round(value * ANGLE_TO_POSITION)) % RESOLUTION + except cv.Invalid as e: + raise cv.Invalid(f"When using angle, {e.error_message}") + + +def percent_to_position(value): + value = cv.possibly_negative_percentage(value) + return (RESOLUTION + round(value * RESOLUTION)) % RESOLUTION + + +def position(min=-MAX_POSITION, max=MAX_POSITION): + """Validate that the config option is a position. + Accepts integers, degrees, or percentage (of 360 degrees). + """ + + def validator(value): + if isinstance(value, str) and value.endswith("%"): + value = percent_to_position(value) + + if isinstance(value, str) and (value.endswith("°") or value.endswith("deg")): + return angle_to_position( + value, + min=round(min * POSITION_TO_ANGLE), + max=round(max * POSITION_TO_ANGLE), + ) + + return cv.int_range(min=min, max=max)(value) + + return validator + + +def position_range(): + """Validate that value given is a valid range for the device. + A valid range is one of the following: + - a value of 0 (meaning full range) + - 18 thru 360 degrees + - negative 360 thru negative 18 degrees (notes: these are normalized to their positive values, accepting negatives is for convenience) + """ + zero_validator = position(min=0, max=0) + negative_validator = cv.Any( + position(min=-MAX_POSITION, max=-MIN_RANGE), + zero_validator, + ) + positive_validator = cv.Any( + position(min=MIN_RANGE, max=MAX_POSITION), + zero_validator, + ) + + def validator(value): + is_negative_str = isinstance(value, str) and value.startswith("-") + is_negative_num = isinstance(value, (float, int)) and value < 0 + if is_negative_str or is_negative_num: + return negative_validator(value) + return positive_validator(value) + + return validator + + +def has_valid_range_config(): + """Validate that that the config start + end position results in a valid + positional range, which must be >= 18degrees + """ + range_validator = position_range() + + def validator(config): + # if we don't have an end position, then there is nothing to do + if CONF_END_POSITION not in config: + return config + + # determine the range by taking the difference from the end and start + range = config[CONF_END_POSITION] - config[CONF_START_POSITION] + + # but need to account for start position being greater than end position + # where the range rolls back around the 0 position + if config[CONF_END_POSITION] < config[CONF_START_POSITION]: + range = RESOLUTION + config[CONF_END_POSITION] - config[CONF_START_POSITION] + + try: + range_validator(range) + return config + except cv.Invalid as e: + raise cv.Invalid( + f"The range between start and end position is invalid. It was was {range} but {e.error_message}" + ) + + return validator + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(AS5600Component), + cv.Optional(CONF_DIR_PIN): pins.gpio_input_pin_schema, + cv.Optional(CONF_DIRECTION, default="CLOCKWISE"): cv.enum( + DIRECTION, upper=True + ), + cv.Optional(CONF_WATCHDOG, default=False): cv.boolean, + cv.Optional(CONF_POWER_MODE, default="NOMINAL"): cv.enum( + POWER_MODE, upper=True, space="" + ), + cv.Optional(CONF_HYSTERESIS, default="NONE"): cv.enum( + HYSTERESIS, upper=True, space="" + ), + cv.Optional(CONF_SLOW_FILTER, default="16X"): cv.enum( + SLOW_FILTER, upper=True, space="" + ), + cv.Optional(CONF_FAST_FILTER, default="NONE"): cv.enum( + FAST_FILTER, upper=True, space="" + ), + cv.Optional(CONF_START_POSITION, default=0): position(), + cv.Optional(CONF_END_POSITION): position(), + cv.Optional(CONF_RANGE): position_range(), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x36)), + # ensure end_position and range are mutually exclusive + cv.has_at_most_one_key(CONF_END_POSITION, CONF_RANGE), + has_valid_range_config(), +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + cg.add(var.set_direction(config[CONF_DIRECTION])) + cg.add(var.set_watchdog(config[CONF_WATCHDOG])) + cg.add(var.set_power_mode(config[CONF_POWER_MODE])) + cg.add(var.set_hysteresis(config[CONF_HYSTERESIS])) + cg.add(var.set_slow_filter(config[CONF_SLOW_FILTER])) + cg.add(var.set_fast_filter(config[CONF_FAST_FILTER])) + cg.add(var.set_start_position(config[CONF_START_POSITION])) + + if dir_pin_config := config.get(CONF_DIR_PIN): + pin = await cg.gpio_pin_expression(dir_pin_config) + cg.add(var.set_dir_pin(pin)) + + if (end_position_config := config.get(CONF_END_POSITION, None)) is not None: + cg.add(var.set_end_position(end_position_config)) + + if (range_config := config.get(CONF_RANGE, None)) is not None: + cg.add(var.set_range(range_config)) diff --git a/esphome/components/as5600/as5600.cpp b/esphome/components/as5600/as5600.cpp new file mode 100644 index 0000000000..3fe7eab58d --- /dev/null +++ b/esphome/components/as5600/as5600.cpp @@ -0,0 +1,138 @@ +#include "as5600.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace as5600 { + +static const char *const TAG = "as5600"; + +// Configuration registers +static const uint8_t REGISTER_ZMCO = 0x00; // 8 bytes / R +static const uint8_t REGISTER_ZPOS = 0x01; // 16 bytes / RW +static const uint8_t REGISTER_MPOS = 0x03; // 16 bytes / RW +static const uint8_t REGISTER_MANG = 0x05; // 16 bytes / RW +static const uint8_t REGISTER_CONF = 0x07; // 16 bytes / RW + +// Output registers +static const uint8_t REGISTER_ANGLE_RAW = 0x0C; // 16 bytes / R +static const uint8_t REGISTER_ANGLE = 0x0E; // 16 bytes / R + +// Status registers +static const uint8_t REGISTER_STATUS = 0x0B; // 8 bytes / R +static const uint8_t REGISTER_AGC = 0x1A; // 8 bytes / R +static const uint8_t REGISTER_MAGNITUDE = 0x1B; // 16 bytes / R + +void AS5600Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up AS5600..."); + + if (!this->read_byte(REGISTER_STATUS).has_value()) { + this->mark_failed(); + return; + } + + // configuration direction pin, if given + // the dir pin on the chip should be low for clockwise + // and high for counterclockwise. If the pin is left floating + // the reported positions will be erratic. + if (this->dir_pin_ != nullptr) { + this->dir_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->dir_pin_->digital_write(this->direction_ == 1); + } + + // build config register + // take the value, shift it left, and add mask to it to ensure we + // are only changing the bits appropriate for that setting in the + // off chance we somehow have bad value in there and it makes for + // a nice visual for the bit positions. + uint16_t config = 0; + // clang-format off + config |= (this->watchdog_ << 13) & 0b0010000000000000; + config |= (this->fast_filter_ << 10) & 0b0001110000000000; + config |= (this->slow_filter_ << 8) & 0b0000001100000000; + config |= (this->pwm_frequency_ << 6) & 0b0000000011000000; + config |= (this->output_mode_ << 4) & 0b0000000000110000; + config |= (this->hysteresis_ << 2) & 0b0000000000001100; + config |= (this->power_mode_ << 0) & 0b0000000000000011; + // clang-format on + + // write config to config register + if (!this->write_byte_16(REGISTER_CONF, config)) { + this->mark_failed(); + return; + } + + // configure the start position + this->write_byte_16(REGISTER_ZPOS, this->start_position_); + + // configure either end position or max angle + if (this->end_mode_ == END_MODE_POSITION) { + this->write_byte_16(REGISTER_MPOS, this->end_position_); + } else { + this->write_byte_16(REGISTER_MANG, this->end_position_); + } + + // calculate the raw max from end position or start + range + this->raw_max_ = this->end_mode_ == END_MODE_POSITION ? this->end_position_ & 4095 + : (this->start_position_ + this->end_position_) & 4095; + + // calculate allowed range of motion by taking the start from the end + // but only if the end is greater than the start. If the start is greater + // than the end position, then that means we take the start all the way to + // reset point (i.e. 0 deg raw) and then we that with the end position + uint16_t range = this->raw_max_ > this->start_position_ ? this->raw_max_ - this->start_position_ + : (4095 - this->start_position_) + this->raw_max_; + + // range scale is ratio of actual allowed range to the full range + this->range_scale_ = range / 4095.0f; +} + +void AS5600Component::dump_config() { + ESP_LOGCONFIG(TAG, "AS5600:"); + LOG_I2C_DEVICE(this); + + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with AS5600 failed!"); + return; + } + + ESP_LOGCONFIG(TAG, " Watchdog: %d", this->watchdog_); + ESP_LOGCONFIG(TAG, " Fast Filter: %d", this->fast_filter_); + ESP_LOGCONFIG(TAG, " Slow Filter: %d", this->slow_filter_); + ESP_LOGCONFIG(TAG, " Hysteresis: %d", this->hysteresis_); + ESP_LOGCONFIG(TAG, " Start Position: %d", this->start_position_); + if (this->end_mode_ == END_MODE_POSITION) { + ESP_LOGCONFIG(TAG, " End Position: %d", this->end_position_); + } else { + ESP_LOGCONFIG(TAG, " Range: %d", this->end_position_); + } +} + +bool AS5600Component::in_range(uint16_t raw_position) { + return this->raw_max_ > this->start_position_ + ? raw_position >= this->start_position_ && raw_position <= this->raw_max_ + : raw_position >= this->start_position_ || raw_position <= this->raw_max_; +} + +AS5600MagnetStatus AS5600Component::read_magnet_status() { + uint8_t status = this->reg(REGISTER_STATUS).get() >> 3 & 0b000111; + return static_cast(status); +} + +optional AS5600Component::read_position() { + uint16_t pos = 0; + if (!this->read_byte_16(REGISTER_ANGLE, &pos)) { + return {}; + } + return pos; +} + +optional AS5600Component::read_raw_position() { + uint16_t pos = 0; + if (!this->read_byte_16(REGISTER_ANGLE_RAW, &pos)) { + return {}; + } + return pos; +} + +} // namespace as5600 +} // namespace esphome diff --git a/esphome/components/as5600/as5600.h b/esphome/components/as5600/as5600.h new file mode 100644 index 0000000000..fbfd18db40 --- /dev/null +++ b/esphome/components/as5600/as5600.h @@ -0,0 +1,105 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/preferences.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/sensor/sensor.h" + +namespace esphome { +namespace as5600 { + +static const uint16_t POSITION_COUNT = 4096; +static const float RAW_TO_DEGREES = 360.0 / POSITION_COUNT; +static const float DEGREES_TO_RAW = POSITION_COUNT / 360.0; + +enum EndPositionMode : uint8_t { + // In this mode, the end position is calculated by taking the start position + // and adding the range/positions. For example, you could say start at 90deg, + // and have a range of 180deg and effectively the sensor will report values + // from the physical 90deg thru 270deg. + END_MODE_RANGE, + // In this mode, the end position is explicitly set, and changing the start + // position will NOT change the end position. + END_MODE_POSITION, +}; + +enum OutRangeMode : uint8_t { + // In this mode, the AS5600 chip itself actually reports these values, but + // effectively it splits the out-of-range values in half, and when positioned + // over the half closest to the min/start position, it will report 0 and when + // positioned over the half closes to the max/end position, it will report the + // max/end value. + OUT_RANGE_MODE_MIN_MAX, + // In this mode, when the magnet is positioned outside the configured + // range, the sensor will report NAN, which translates to "Unknown" + // in Home Assistant. + OUT_RANGE_MODE_NAN, +}; + +enum AS5600MagnetStatus : uint8_t { + MAGNET_GONE = 2, // 0b010 / magnet not detected + MAGNET_OK = 4, // 0b100 / magnet just right + MAGNET_STRONG = 5, // 0b101 / magnet too strong + MAGNET_WEAK = 6, // 0b110 / magnet too weak +}; + +class AS5600Component : public Component, public i2c::I2CDevice { + public: + /// Set up the internal sensor array. + void setup() override; + void dump_config() override; + /// HARDWARE_LATE setup priority + float get_setup_priority() const override { return setup_priority::DATA; } + + // configuration setters + void set_dir_pin(InternalGPIOPin *pin) { this->dir_pin_ = pin; } + void set_direction(uint8_t direction) { this->direction_ = direction; } + void set_fast_filter(uint8_t fast_filter) { this->fast_filter_ = fast_filter; } + void set_hysteresis(uint8_t hysteresis) { this->hysteresis_ = hysteresis; } + void set_power_mode(uint8_t power_mode) { this->power_mode_ = power_mode; } + void set_slow_filter(uint8_t slow_filter) { this->slow_filter_ = slow_filter; } + void set_watchdog(bool watchdog) { this->watchdog_ = watchdog; } + bool get_watchdog() { return this->watchdog_; } + void set_start_position(uint16_t start_position) { this->start_position_ = start_position % POSITION_COUNT; } + void set_end_position(uint16_t end_position) { + this->end_position_ = end_position % POSITION_COUNT; + this->end_mode_ = END_MODE_POSITION; + } + void set_range(uint16_t range) { + this->end_position_ = range % POSITION_COUNT; + this->end_mode_ = END_MODE_RANGE; + } + + // Gets the scale value for the configured range. + // For example, if configured to start at 0deg and end at 180deg, the + // range is 50% of the native/raw range, so the range scale would be 0.5. + // If configured to use the full 360deg, the range scale would be 1.0. + float get_range_scale() { return this->range_scale_; } + + // Indicates whether the given *raw* position is within the configured range + bool in_range(uint16_t raw_position); + + AS5600MagnetStatus read_magnet_status(); + optional read_position(); + optional read_raw_position(); + + protected: + InternalGPIOPin *dir_pin_{nullptr}; + uint8_t direction_; + uint8_t fast_filter_; + uint8_t hysteresis_; + uint8_t power_mode_; + uint8_t slow_filter_; + uint8_t pwm_frequency_{0}; + uint8_t output_mode_{0}; + bool watchdog_; + uint16_t start_position_; + uint16_t end_position_{0}; + uint16_t raw_max_; + EndPositionMode end_mode_{END_MODE_RANGE}; + float range_scale_{1.0}; +}; + +} // namespace as5600 +} // namespace esphome diff --git a/esphome/components/as5600/sensor/__init__.py b/esphome/components/as5600/sensor/__init__.py new file mode 100644 index 0000000000..589a66950a --- /dev/null +++ b/esphome/components/as5600/sensor/__init__.py @@ -0,0 +1,119 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor +from esphome.const import ( + CONF_ID, + STATE_CLASS_MEASUREMENT, + ICON_MAGNET, + ICON_ROTATE_RIGHT, + CONF_GAIN, + ENTITY_CATEGORY_DIAGNOSTIC, + CONF_MAGNITUDE, + CONF_STATUS, + CONF_POSITION, +) +from .. import as5600_ns, AS5600Component + +CODEOWNERS = ["@ammmze"] +DEPENDENCIES = ["as5600"] + +AS5600Sensor = as5600_ns.class_("AS5600Sensor", sensor.Sensor, cg.PollingComponent) + +CONF_ANGLE = "angle" +CONF_RAW_ANGLE = "raw_angle" +CONF_RAW_POSITION = "raw_position" +CONF_WATCHDOG = "watchdog" +CONF_POWER_MODE = "power_mode" +CONF_SLOW_FILTER = "slow_filter" +CONF_FAST_FILTER = "fast_filter" +CONF_PWM_FREQUENCY = "pwm_frequency" +CONF_BURN_COUNT = "burn_count" +CONF_START_POSITION = "start_position" +CONF_END_POSITION = "end_position" +CONF_OUT_OF_RANGE_MODE = "out_of_range_mode" + +OutOfRangeMode = as5600_ns.enum("OutRangeMode") +OUT_OF_RANGE_MODES = { + "MIN_MAX": OutOfRangeMode.OUT_RANGE_MODE_MIN_MAX, + "NAN": OutOfRangeMode.OUT_RANGE_MODE_NAN, +} + + +CONF_AS5600_ID = "as5600_id" +CONFIG_SCHEMA = ( + sensor.sensor_schema( + AS5600Sensor, + accuracy_decimals=0, + icon=ICON_ROTATE_RIGHT, + state_class=STATE_CLASS_MEASUREMENT, + ) + .extend( + { + cv.GenerateID(CONF_AS5600_ID): cv.use_id(AS5600Component), + cv.Optional(CONF_OUT_OF_RANGE_MODE): cv.enum( + OUT_OF_RANGE_MODES, upper=True, space="_" + ), + cv.Optional(CONF_RAW_POSITION): sensor.sensor_schema( + accuracy_decimals=0, + icon=ICON_ROTATE_RIGHT, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_GAIN): sensor.sensor_schema( + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_MAGNITUDE): sensor.sensor_schema( + accuracy_decimals=0, + icon=ICON_MAGNET, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_STATUS): sensor.sensor_schema( + accuracy_decimals=0, + icon=ICON_MAGNET, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ) + .extend(cv.polling_component_schema("60s")) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_parented(var, config[CONF_AS5600_ID]) + await cg.register_component(var, config) + await sensor.register_sensor(var, config) + + if out_of_range_mode_config := config.get(CONF_OUT_OF_RANGE_MODE): + cg.add(var.set_out_of_range_mode(out_of_range_mode_config)) + + if angle_config := config.get(CONF_ANGLE): + sens = await sensor.new_sensor(angle_config) + cg.add(var.set_angle_sensor(sens)) + + if raw_angle_config := config.get(CONF_RAW_ANGLE): + sens = await sensor.new_sensor(raw_angle_config) + cg.add(var.set_raw_angle_sensor(sens)) + + if position_config := config.get(CONF_POSITION): + sens = await sensor.new_sensor(position_config) + cg.add(var.set_position_sensor(sens)) + + if raw_position_config := config.get(CONF_RAW_POSITION): + sens = await sensor.new_sensor(raw_position_config) + cg.add(var.set_raw_position_sensor(sens)) + + if gain_config := config.get(CONF_GAIN): + sens = await sensor.new_sensor(gain_config) + cg.add(var.set_gain_sensor(sens)) + + if magnitude_config := config.get(CONF_MAGNITUDE): + sens = await sensor.new_sensor(magnitude_config) + cg.add(var.set_magnitude_sensor(sens)) + + if status_config := config.get(CONF_STATUS): + sens = await sensor.new_sensor(status_config) + cg.add(var.set_status_sensor(sens)) diff --git a/esphome/components/as5600/sensor/as5600_sensor.cpp b/esphome/components/as5600/sensor/as5600_sensor.cpp new file mode 100644 index 0000000000..feb8f6cebf --- /dev/null +++ b/esphome/components/as5600/sensor/as5600_sensor.cpp @@ -0,0 +1,98 @@ +#include "as5600_sensor.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace as5600 { + +static const char *const TAG = "as5600.sensor"; + +// Configuration registers +static const uint8_t REGISTER_ZMCO = 0x00; // 8 bytes / R +static const uint8_t REGISTER_ZPOS = 0x01; // 16 bytes / RW +static const uint8_t REGISTER_MPOS = 0x03; // 16 bytes / RW +static const uint8_t REGISTER_MANG = 0x05; // 16 bytes / RW +static const uint8_t REGISTER_CONF = 0x07; // 16 bytes / RW + +// Output registers +static const uint8_t REGISTER_ANGLE_RAW = 0x0C; // 16 bytes / R +static const uint8_t REGISTER_ANGLE = 0x0E; // 16 bytes / R + +// Status registers +static const uint8_t REGISTER_STATUS = 0x0B; // 8 bytes / R +static const uint8_t REGISTER_AGC = 0x1A; // 8 bytes / R +static const uint8_t REGISTER_MAGNITUDE = 0x1B; // 16 bytes / R + +float AS5600Sensor::get_setup_priority() const { return setup_priority::DATA; } + +void AS5600Sensor::dump_config() { + LOG_SENSOR("", "AS5600 Sensor", this); + ESP_LOGCONFIG(TAG, " Out of Range Mode: %u", this->out_of_range_mode_); + if (this->angle_sensor_ != nullptr) { + LOG_SENSOR(" ", "Angle Sensor", this->angle_sensor_); + } + if (this->raw_angle_sensor_ != nullptr) { + LOG_SENSOR(" ", "Raw Angle Sensor", this->raw_angle_sensor_); + } + if (this->position_sensor_ != nullptr) { + LOG_SENSOR(" ", "Position Sensor", this->position_sensor_); + } + if (this->raw_position_sensor_ != nullptr) { + LOG_SENSOR(" ", "Raw Position Sensor", this->raw_position_sensor_); + } + if (this->gain_sensor_ != nullptr) { + LOG_SENSOR(" ", "Gain Sensor", this->gain_sensor_); + } + if (this->magnitude_sensor_ != nullptr) { + LOG_SENSOR(" ", "Magnitude Sensor", this->magnitude_sensor_); + } + if (this->status_sensor_ != nullptr) { + LOG_SENSOR(" ", "Status Sensor", this->status_sensor_); + } + LOG_UPDATE_INTERVAL(this); +} + +void AS5600Sensor::update() { + if (this->gain_sensor_ != nullptr) { + this->gain_sensor_->publish_state(this->parent_->reg(REGISTER_AGC).get()); + } + + if (this->magnitude_sensor_ != nullptr) { + uint16_t value = 0; + this->parent_->read_byte_16(REGISTER_MAGNITUDE, &value); + this->magnitude_sensor_->publish_state(value); + } + + // 2 = magnet not detected + // 4 = magnet just right + // 5 = magnet too strong + // 6 = magnet too weak + if (this->status_sensor_ != nullptr) { + this->status_sensor_->publish_state(this->parent_->read_magnet_status()); + } + + auto pos = this->parent_->read_position(); + if (!pos.has_value()) { + this->status_set_warning(); + return; + } + + auto raw = this->parent_->read_raw_position(); + if (!raw.has_value()) { + this->status_set_warning(); + return; + } + + if (this->out_of_range_mode_ == OUT_RANGE_MODE_NAN) { + this->publish_state(this->parent_->in_range(raw.value()) ? pos.value() : NAN); + } else { + this->publish_state(pos.value()); + } + + if (this->raw_position_sensor_ != nullptr) { + this->raw_position_sensor_->publish_state(raw.value()); + } + this->status_clear_warning(); +} + +} // namespace as5600 +} // namespace esphome diff --git a/esphome/components/as5600/sensor/as5600_sensor.h b/esphome/components/as5600/sensor/as5600_sensor.h new file mode 100644 index 0000000000..0af9b01ae5 --- /dev/null +++ b/esphome/components/as5600/sensor/as5600_sensor.h @@ -0,0 +1,43 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/preferences.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/as5600/as5600.h" + +namespace esphome { +namespace as5600 { + +class AS5600Sensor : public PollingComponent, public Parented, public sensor::Sensor { + public: + void update() override; + void dump_config() override; + float get_setup_priority() const override; + + void set_angle_sensor(sensor::Sensor *angle_sensor) { this->angle_sensor_ = angle_sensor; } + void set_raw_angle_sensor(sensor::Sensor *raw_angle_sensor) { this->raw_angle_sensor_ = raw_angle_sensor; } + void set_position_sensor(sensor::Sensor *position_sensor) { this->position_sensor_ = position_sensor; } + void set_raw_position_sensor(sensor::Sensor *raw_position_sensor) { + this->raw_position_sensor_ = raw_position_sensor; + } + void set_gain_sensor(sensor::Sensor *gain_sensor) { this->gain_sensor_ = gain_sensor; } + void set_magnitude_sensor(sensor::Sensor *magnitude_sensor) { this->magnitude_sensor_ = magnitude_sensor; } + void set_status_sensor(sensor::Sensor *status_sensor) { this->status_sensor_ = status_sensor; } + void set_out_of_range_mode(OutRangeMode oor_mode) { this->out_of_range_mode_ = oor_mode; } + OutRangeMode get_out_of_range_mode() { return this->out_of_range_mode_; } + + protected: + sensor::Sensor *angle_sensor_{nullptr}; + sensor::Sensor *raw_angle_sensor_{nullptr}; + sensor::Sensor *position_sensor_{nullptr}; + sensor::Sensor *raw_position_sensor_{nullptr}; + sensor::Sensor *gain_sensor_{nullptr}; + sensor::Sensor *magnitude_sensor_{nullptr}; + sensor::Sensor *status_sensor_{nullptr}; + OutRangeMode out_of_range_mode_{OUT_RANGE_MODE_MIN_MAX}; +}; + +} // namespace as5600 +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index aaad10ff0a..2748a09c59 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -322,6 +322,18 @@ ads1115: address: 0x48 i2c_id: i2c_bus +as5600: + i2c_id: i2c_bus + dir_pin: GPIO27 + direction: clockwise + start_position: 90deg + range: 180deg + watchdog: true + power_mode: low1 + hysteresis: lsb1 + slow_filter: 8x + fast_filter: lsb6 + dallas: pin: allow_other_uses: true @@ -555,6 +567,16 @@ sensor: state_topic: hi/me retain: false availability: + - platform: as5600 + name: AS5600 Position + raw_position: + name: AS5600 Raw Position + gain: + name: AS5600 Gain + magnitude: + name: AS5600 Magnitude + status: + name: AS5600 Status - platform: as7341 update_interval: 15s gain: X8 From 3c2383e26119c3c05341786aa335ea7653c0d120 Mon Sep 17 00:00:00 2001 From: mknjc Date: Thu, 21 Dec 2023 01:08:13 +0100 Subject: [PATCH 058/193] Add default substitutions for package includes (#5752) --- esphome/const.py | 1 + esphome/yaml_util.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5de34b86cd..58c6c47e3a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -185,6 +185,7 @@ CONF_DEFAULT_MODE = "default_mode" CONF_DEFAULT_TARGET_TEMPERATURE_HIGH = "default_target_temperature_high" CONF_DEFAULT_TARGET_TEMPERATURE_LOW = "default_target_temperature_low" CONF_DEFAULT_TRANSITION_LENGTH = "default_transition_length" +CONF_DEFAULTS = "defaults" CONF_DELAY = "delay" CONF_DELIMITER = "delimiter" CONF_DELTA = "delta" diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index f0f755dd61..aa9fe45ebb 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -282,7 +282,7 @@ class ESPHomeLoader(FastestAvailableSafeLoader): return file, vars def substitute_vars(config, vars): - from esphome.const import CONF_SUBSTITUTIONS + from esphome.const import CONF_SUBSTITUTIONS, CONF_DEFAULTS from esphome.components import substitutions org_subs = None @@ -294,7 +294,15 @@ class ESPHomeLoader(FastestAvailableSafeLoader): elif CONF_SUBSTITUTIONS in result: org_subs = result.pop(CONF_SUBSTITUTIONS) + defaults = {} + if CONF_DEFAULTS in result: + defaults = result.pop(CONF_DEFAULTS) + result[CONF_SUBSTITUTIONS] = vars + for k, v in defaults.items(): + if k not in result[CONF_SUBSTITUTIONS]: + result[CONF_SUBSTITUTIONS][k] = v + # Ignore missing vars that refer to the top level substitutions substitutions.do_substitution_pass(result, None, ignore_missing=True) result.pop(CONF_SUBSTITUTIONS) From c6a37da9da4061e5a9f663b800d694ca27b478f0 Mon Sep 17 00:00:00 2001 From: Matthew Campbell Date: Wed, 20 Dec 2023 16:08:44 -0800 Subject: [PATCH 059/193] Add gradient option to addressable color wipe effect (#5689) --- esphome/components/light/addressable_light_effect.h | 12 ++++++++++-- esphome/components/light/effects.py | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/esphome/components/light/addressable_light_effect.h b/esphome/components/light/addressable_light_effect.h index c2109b2d23..73083a58b7 100644 --- a/esphome/components/light/addressable_light_effect.h +++ b/esphome/components/light/addressable_light_effect.h @@ -100,6 +100,7 @@ struct AddressableColorWipeEffectColor { uint8_t r, g, b, w; bool random; size_t num_leds; + bool gradient; }; class AddressableColorWipeEffect : public AddressableLightEffect { @@ -117,8 +118,15 @@ class AddressableColorWipeEffect : public AddressableLightEffect { it.shift_left(1); else it.shift_right(1); - const AddressableColorWipeEffectColor color = this->colors_[this->at_color_]; - const Color esp_color = Color(color.r, color.g, color.b, color.w); + const AddressableColorWipeEffectColor &color = this->colors_[this->at_color_]; + Color esp_color = Color(color.r, color.g, color.b, color.w); + if (color.gradient) { + size_t next_color_index = (this->at_color_ + 1) % this->colors_.size(); + const AddressableColorWipeEffectColor &next_color = this->colors_[next_color_index]; + const Color next_esp_color = Color(next_color.r, next_color.g, next_color.b, next_color.w); + uint8_t gradient = 255 * ((float) this->leds_added_ / color.num_leds); + esp_color = esp_color.gradient(next_esp_color, gradient); + } if (this->reverse_) it[-1] = esp_color; else diff --git a/esphome/components/light/effects.py b/esphome/components/light/effects.py index 5212e90938..5093ace949 100644 --- a/esphome/components/light/effects.py +++ b/esphome/components/light/effects.py @@ -58,6 +58,7 @@ from .types import ( CONF_ADD_LED_INTERVAL = "add_led_interval" CONF_REVERSE = "reverse" +CONF_GRADIENT = "gradient" CONF_MOVE_INTERVAL = "move_interval" CONF_SCAN_WIDTH = "scan_width" CONF_TWINKLE_PROBABILITY = "twinkle_probability" @@ -386,6 +387,7 @@ async def addressable_rainbow_effect_to_code(config, effect_id): cv.Optional(CONF_WHITE, default=1.0): cv.percentage, cv.Optional(CONF_RANDOM, default=False): cv.boolean, cv.Required(CONF_NUM_LEDS): cv.All(cv.uint32_t, cv.Range(min=1)), + cv.Optional(CONF_GRADIENT, default=False): cv.boolean, } ), cv.Optional( @@ -409,6 +411,7 @@ async def addressable_color_wipe_effect_to_code(config, effect_id): ("w", int(round(color[CONF_WHITE] * 255))), ("random", color[CONF_RANDOM]), ("num_leds", color[CONF_NUM_LEDS]), + ("gradient", color[CONF_GRADIENT]), ) ) cg.add(var.set_colors(colors)) From b5932940eebb085317e67de24c3f93ade537419f Mon Sep 17 00:00:00 2001 From: Pavlo Dudnytskyi Date: Thu, 21 Dec 2023 01:10:46 +0100 Subject: [PATCH 060/193] Added alarm processing for Haier component (hOn protocol) (#5965) --- esphome/components/haier/climate.py | 51 ++++++-- esphome/components/haier/haier_base.cpp | 3 +- esphome/components/haier/haier_base.h | 3 +- esphome/components/haier/hon_climate.cpp | 117 +++++++++++++++++- esphome/components/haier/hon_climate.h | 27 ++++ esphome/components/haier/hon_packet.h | 56 +++++++++ .../components/haier/smartair2_climate.cpp | 19 ++- tests/test3.yaml | 23 +++- 8 files changed, 282 insertions(+), 17 deletions(-) diff --git a/esphome/components/haier/climate.py b/esphome/components/haier/climate.py index 49d42a231f..c6998ce0c5 100644 --- a/esphome/components/haier/climate.py +++ b/esphome/components/haier/climate.py @@ -18,6 +18,7 @@ from esphome.const import ( CONF_SUPPORTED_SWING_MODES, CONF_TARGET_TEMPERATURE, CONF_TEMPERATURE_STEP, + CONF_TRIGGER_ID, CONF_VISUAL, CONF_WIFI, DEVICE_CLASS_TEMPERATURE, @@ -49,6 +50,8 @@ CONF_CONTROL_METHOD = "control_method" CONF_CONTROL_PACKET_SIZE = "control_packet_size" CONF_DISPLAY = "display" CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow" +CONF_ON_ALARM_START = "on_alarm_start" +CONF_ON_ALARM_END = "on_alarm_end" CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" CONF_VERTICAL_AIRFLOW = "vertical_airflow" CONF_WIFI_SIGNAL = "wifi_signal" @@ -85,8 +88,8 @@ AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS = { } SUPPORTED_SWING_MODES_OPTIONS = { - "OFF": ClimateSwingMode.CLIMATE_SWING_OFF, # always available - "VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL, # always available + "OFF": ClimateSwingMode.CLIMATE_SWING_OFF, + "VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL, "HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL, "BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH, } @@ -101,13 +104,15 @@ SUPPORTED_CLIMATE_MODES_OPTIONS = { } SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = { + "AWAY": ClimatePreset.CLIMATE_PRESET_AWAY, "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST, "COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT, } SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = { - "ECO": ClimatePreset.CLIMATE_PRESET_ECO, + "AWAY": ClimatePreset.CLIMATE_PRESET_AWAY, "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST, + "ECO": ClimatePreset.CLIMATE_PRESET_ECO, "SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP, } @@ -118,6 +123,16 @@ SUPPORTED_HON_CONTROL_METHODS = { "SET_SINGLE_PARAMETER": HonControlMethod.SET_SINGLE_PARAMETER, } +HaierAlarmStartTrigger = haier_ns.class_( + "HaierAlarmStartTrigger", + automation.Trigger.template(cg.uint8, cg.const_char_ptr), +) + +HaierAlarmEndTrigger = haier_ns.class_( + "HaierAlarmEndTrigger", + automation.Trigger.template(cg.uint8, cg.const_char_ptr), +) + def validate_visual(config): if CONF_VISUAL in config: @@ -200,9 +215,7 @@ CONFIG_SCHEMA = cv.All( ): cv.boolean, cv.Optional( CONF_SUPPORTED_PRESETS, - default=list( - SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS.keys() - ), + default=list(["BOOST", "COMFORT"]), # No AWAY by default ): cv.ensure_list( cv.enum(SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS, upper=True) ), @@ -222,7 +235,7 @@ CONFIG_SCHEMA = cv.All( ): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50), cv.Optional( CONF_SUPPORTED_PRESETS, - default=list(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS.keys()), + default=list(["BOOST", "ECO", "SLEEP"]), # No AWAY by default ): cv.ensure_list( cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True) ), @@ -233,6 +246,20 @@ CONFIG_SCHEMA = cv.All( device_class=DEVICE_CLASS_TEMPERATURE, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_ON_ALARM_START): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + HaierAlarmStartTrigger + ), + } + ), + cv.Optional(CONF_ON_ALARM_END): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + HaierAlarmEndTrigger + ), + } + ), } ), }, @@ -457,5 +484,15 @@ async def to_code(config): config[CONF_CONTROL_PACKET_SIZE] - PROTOCOL_CONTROL_PACKET_SIZE ) ) + for conf in config.get(CONF_ON_ALARM_START, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, [(cg.uint8, "code"), (cg.const_char_ptr, "message")], conf + ) + for conf in config.get(CONF_ON_ALARM_END, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, [(cg.uint8, "code"), (cg.const_char_ptr, "message")], conf + ) # https://github.com/paveldn/HaierProtocol cg.add_library("pavlodn/HaierProtocol", "0.9.24") diff --git a/esphome/components/haier/haier_base.cpp b/esphome/components/haier/haier_base.cpp index 6943fc7d9c..a3f68bb081 100644 --- a/esphome/components/haier/haier_base.cpp +++ b/esphome/components/haier/haier_base.cpp @@ -25,13 +25,14 @@ const char *HaierClimateBase::phase_to_string_(ProtocolPhases phase) { "SENDING_INIT_1", "SENDING_INIT_2", "SENDING_FIRST_STATUS_REQUEST", - "SENDING_ALARM_STATUS_REQUEST", + "SENDING_FIRST_ALARM_STATUS_REQUEST", "IDLE", "SENDING_STATUS_REQUEST", "SENDING_UPDATE_SIGNAL_REQUEST", "SENDING_SIGNAL_LEVEL", "SENDING_CONTROL", "SENDING_ACTION_COMMAND", + "SENDING_ALARM_STATUS_REQUEST", "UNKNOWN" // Should be the last! }; static_assert( diff --git a/esphome/components/haier/haier_base.h b/esphome/components/haier/haier_base.h index 75abbc20fb..504c841e5f 100644 --- a/esphome/components/haier/haier_base.h +++ b/esphome/components/haier/haier_base.h @@ -64,7 +64,7 @@ class HaierClimateBase : public esphome::Component, SENDING_INIT_1 = 0, SENDING_INIT_2, SENDING_FIRST_STATUS_REQUEST, - SENDING_ALARM_STATUS_REQUEST, + SENDING_FIRST_ALARM_STATUS_REQUEST, // FUNCTIONAL STATE IDLE, SENDING_STATUS_REQUEST, @@ -72,6 +72,7 @@ class HaierClimateBase : public esphome::Component, SENDING_SIGNAL_LEVEL, SENDING_CONTROL, SENDING_ACTION_COMMAND, + SENDING_ALARM_STATUS_REQUEST, NUM_PROTOCOL_PHASES }; const char *phase_to_string_(ProtocolPhases phase); diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index 09f90fffa8..e5aa88e2c9 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -16,6 +16,7 @@ constexpr size_t SIGNAL_LEVEL_UPDATE_INTERVAL_MS = 10000; constexpr int PROTOCOL_OUTDOOR_TEMPERATURE_OFFSET = -64; constexpr uint8_t CONTROL_MESSAGE_RETRIES = 5; constexpr std::chrono::milliseconds CONTROL_MESSAGE_RETRIES_INTERVAL = std::chrono::milliseconds(500); +constexpr size_t ALARM_STATUS_REQUEST_INTERVAL_MS = 600000; hon_protocol::VerticalSwingMode get_vertical_swing_mode(AirflowVerticalDirection direction) { switch (direction) { @@ -110,6 +111,14 @@ void HonClimate::start_steri_cleaning() { } } +void HonClimate::add_alarm_start_callback(std::function &&callback) { + this->alarm_start_callback_.add(std::move(callback)); +} + +void HonClimate::add_alarm_end_callback(std::function &&callback) { + this->alarm_end_callback_.add(std::move(callback)); +} + haier_protocol::HandlerError HonClimate::get_device_version_answer_handler_(haier_protocol::FrameType request_type, haier_protocol::FrameType message_type, const uint8_t *data, size_t data_size) { @@ -194,7 +203,7 @@ haier_protocol::HandlerError HonClimate::status_handler_(haier_protocol::FrameTy switch (this->protocol_phase_) { case ProtocolPhases::SENDING_FIRST_STATUS_REQUEST: ESP_LOGI(TAG, "First HVAC status received"); - this->set_phase(ProtocolPhases::SENDING_ALARM_STATUS_REQUEST); + this->set_phase(ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST); break; case ProtocolPhases::SENDING_ACTION_COMMAND: // Do nothing, phase will be changed in process_phase @@ -251,12 +260,15 @@ haier_protocol::HandlerError HonClimate::get_alarm_status_answer_handler_(haier_ this->set_phase(ProtocolPhases::IDLE); return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE; } - if (this->protocol_phase_ != ProtocolPhases::SENDING_ALARM_STATUS_REQUEST) { + if ((this->protocol_phase_ != ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST) && + (this->protocol_phase_ != ProtocolPhases::SENDING_ALARM_STATUS_REQUEST)) { // Don't expect this answer now this->set_phase(ProtocolPhases::IDLE); return haier_protocol::HandlerError::UNEXPECTED_MESSAGE; } - memcpy(this->active_alarms_, data + 2, 8); + if (data_size < sizeof(active_alarms_) + 2) + return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE; + this->process_alarm_message_(data, data_size, this->protocol_phase_ >= ProtocolPhases::IDLE); this->set_phase(ProtocolPhases::IDLE); return haier_protocol::HandlerError::HANDLER_OK; } else { @@ -265,6 +277,19 @@ haier_protocol::HandlerError HonClimate::get_alarm_status_answer_handler_(haier_ } } +haier_protocol::HandlerError HonClimate::alarm_status_message_handler_(haier_protocol::FrameType type, + const uint8_t *buffer, size_t size) { + haier_protocol::HandlerError result = haier_protocol::HandlerError::HANDLER_OK; + if (size < sizeof(this->active_alarms_) + 2) { + // Log error but confirm anyway to avoid to many messages + result = haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE; + } + this->process_alarm_message_(buffer, size, true); + this->haier_protocol_.send_answer(haier_protocol::HaierMessage(haier_protocol::FrameType::CONFIRM)); + this->last_alarm_request_ = std::chrono::steady_clock::now(); + return result; +} + void HonClimate::set_handlers() { // Set handlers this->haier_protocol_.set_answer_handler( @@ -291,6 +316,10 @@ void HonClimate::set_handlers() { haier_protocol::FrameType::REPORT_NETWORK_STATUS, std::bind(&HonClimate::report_network_status_answer_handler_, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + this->haier_protocol_.set_message_handler( + haier_protocol::FrameType::ALARM_STATUS, + std::bind(&HonClimate::alarm_status_message_handler_, this, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); } void HonClimate::dump_config() { @@ -363,10 +392,12 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { this->set_phase(ProtocolPhases::IDLE); break; #endif + case ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST: case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { static const haier_protocol::HaierMessage ALARM_STATUS_REQUEST(haier_protocol::FrameType::GET_ALARM_STATUS); this->send_message_(ALARM_STATUS_REQUEST, this->use_crc_); + this->last_alarm_request_ = now; } break; case ProtocolPhases::SENDING_CONTROL: @@ -417,12 +448,16 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { if (this->forced_request_status_ || this->is_status_request_interval_exceeded_(now)) { this->set_phase(ProtocolPhases::SENDING_STATUS_REQUEST); this->forced_request_status_ = false; + } else if (std::chrono::duration_cast(now - this->last_alarm_request_).count() > + ALARM_STATUS_REQUEST_INTERVAL_MS) { + this->set_phase(ProtocolPhases::SENDING_ALARM_STATUS_REQUEST); } #ifdef USE_WIFI else if (this->send_wifi_signal_ && (std::chrono::duration_cast(now - this->last_signal_request_).count() > - SIGNAL_LEVEL_UPDATE_INTERVAL_MS)) + SIGNAL_LEVEL_UPDATE_INTERVAL_MS)) { this->set_phase(ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST); + } #endif } break; default: @@ -452,6 +487,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)]; memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl)); hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer; + control_out_buffer[4] = 0; // This byte should be cleared before setting values bool has_hvac_settings = false; if (this->current_hvac_settings_.valid) { has_hvac_settings = true; @@ -552,31 +588,41 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { out_data->quiet_mode = 0; out_data->fast_mode = 0; out_data->sleep_mode = 0; + out_data->ten_degree = 0; break; case CLIMATE_PRESET_ECO: // Eco is not supported in Fan only mode out_data->quiet_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0; out_data->fast_mode = 0; out_data->sleep_mode = 0; + out_data->ten_degree = 0; break; case CLIMATE_PRESET_BOOST: out_data->quiet_mode = 0; // Boost is not supported in Fan only mode out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0; out_data->sleep_mode = 0; + out_data->ten_degree = 0; break; case CLIMATE_PRESET_AWAY: out_data->quiet_mode = 0; out_data->fast_mode = 0; out_data->sleep_mode = 0; + // 10 degrees allowed only in heat mode + out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0; break; case CLIMATE_PRESET_SLEEP: out_data->quiet_mode = 0; out_data->fast_mode = 0; out_data->sleep_mode = 1; + out_data->ten_degree = 0; break; default: ESP_LOGE("Control", "Unsupported preset"); + out_data->quiet_mode = 0; + out_data->fast_mode = 0; + out_data->sleep_mode = 0; + out_data->ten_degree = 0; break; } } @@ -595,6 +641,50 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { control_out_buffer, sizeof(hon_protocol::HaierPacketControl)); } +void HonClimate::process_alarm_message_(const uint8_t *packet, uint8_t size, bool check_new) { + constexpr size_t active_alarms_size = sizeof(this->active_alarms_); + if (size >= active_alarms_size + 2) { + if (check_new) { + size_t alarm_code = 0; + for (int i = active_alarms_size - 1; i >= 0; i--) { + if (packet[2 + i] != active_alarms_[i]) { + uint8_t alarm_bit = 1; + for (int b = 0; b < 8; b++) { + if ((packet[2 + i] & alarm_bit) != (this->active_alarms_[i] & alarm_bit)) { + bool alarm_status = (packet[2 + i] & alarm_bit) != 0; + int log_level = alarm_status ? ESPHOME_LOG_LEVEL_WARN : ESPHOME_LOG_LEVEL_INFO; + const char *alarm_message = alarm_code < esphome::haier::hon_protocol::HON_ALARM_COUNT + ? esphome::haier::hon_protocol::HON_ALARM_MESSAGES[alarm_code].c_str() + : "Unknown"; + esp_log_printf_(log_level, TAG, __LINE__, "Alarm %s (%d): %s", alarm_status ? "activated" : "deactivated", + alarm_code, alarm_message); + if (alarm_status) { + this->alarm_start_callback_.call(alarm_code, alarm_message); + this->active_alarm_count_ += 1.0f; + } else { + this->alarm_end_callback_.call(alarm_code, alarm_message); + this->active_alarm_count_ -= 1.0f; + } + } + alarm_bit <<= 1; + alarm_code++; + } + active_alarms_[i] = packet[2 + i]; + } else + alarm_code += 8; + } + } else { + float alarm_count = 0.0f; + static uint8_t nibble_bits_count[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; + for (size_t i = 0; i < sizeof(this->active_alarms_); i++) { + alarm_count += (float) (nibble_bits_count[packet[2 + i] & 0x0F] + nibble_bits_count[packet[2 + i] >> 4]); + } + this->active_alarm_count_ = alarm_count; + memcpy(this->active_alarms_, packet + 2, sizeof(this->active_alarms_)); + } + } +} + haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) { if (size < hon_protocol::HAIER_STATUS_FRAME_SIZE + this->extra_control_packet_bytes_) return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE; @@ -626,6 +716,8 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * this->preset = CLIMATE_PRESET_BOOST; } else if (packet.control.sleep_mode != 0) { this->preset = CLIMATE_PRESET_SLEEP; + } else if (packet.control.ten_degree != 0) { + this->preset = CLIMATE_PRESET_AWAY; } else { this->preset = CLIMATE_PRESET_NONE; } @@ -882,25 +974,35 @@ void HonClimate::fill_control_messages_queue_() { // CLimate preset { uint8_t fast_mode_buf[] = {0x00, 0xFF}; + uint8_t away_mode_buf[] = {0x00, 0xFF}; if (!new_power) { // If AC is off - no presets allowed quiet_mode_buf[1] = 0x00; fast_mode_buf[1] = 0x00; + away_mode_buf[1] = 0x00; } else if (climate_control.preset.has_value()) { switch (climate_control.preset.value()) { case CLIMATE_PRESET_NONE: quiet_mode_buf[1] = 0x00; fast_mode_buf[1] = 0x00; + away_mode_buf[1] = 0x00; break; case CLIMATE_PRESET_ECO: // Eco is not supported in Fan only mode quiet_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00; fast_mode_buf[1] = 0x00; + away_mode_buf[1] = 0x00; break; case CLIMATE_PRESET_BOOST: quiet_mode_buf[1] = 0x00; // Boost is not supported in Fan only mode fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00; + away_mode_buf[1] = 0x00; + break; + case CLIMATE_PRESET_AWAY: + quiet_mode_buf[1] = 0x00; + fast_mode_buf[1] = 0x00; + away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00; break; default: ESP_LOGE("Control", "Unsupported preset"); @@ -921,6 +1023,13 @@ void HonClimate::fill_control_messages_queue_() { (uint8_t) hon_protocol::DataParameters::FAST_MODE, fast_mode_buf, 2)); } + if (away_mode_buf[1] != 0xFF) { + this->control_messages_queue_.push( + haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::TEN_DEGREE, + away_mode_buf, 2)); + } } // Target temperature if (climate_control.target_temperature.has_value()) { diff --git a/esphome/components/haier/hon_climate.h b/esphome/components/haier/hon_climate.h index 1ba6a8e041..9c05e59b87 100644 --- a/esphome/components/haier/hon_climate.h +++ b/esphome/components/haier/hon_climate.h @@ -2,6 +2,7 @@ #include #include "esphome/components/sensor/sensor.h" +#include "esphome/core/automation.h" #include "haier_base.h" namespace esphome { @@ -52,6 +53,9 @@ class HonClimate : public HaierClimateBase { void start_steri_cleaning(); void set_extra_control_packet_bytes_size(size_t size) { this->extra_control_packet_bytes_ = size; }; void set_control_method(HonControlMethod method) { this->control_method_ = method; }; + void add_alarm_start_callback(std::function &&callback); + void add_alarm_end_callback(std::function &&callback); + float get_active_alarm_count() const { return this->active_alarm_count_; } protected: void set_handlers() override; @@ -77,8 +81,11 @@ class HonClimate : public HaierClimateBase { haier_protocol::HandlerError get_alarm_status_answer_handler_(haier_protocol::FrameType request_type, haier_protocol::FrameType message_type, const uint8_t *data, size_t data_size); + haier_protocol::HandlerError alarm_status_message_handler_(haier_protocol::FrameType type, const uint8_t *buffer, + size_t size); // Helper functions haier_protocol::HandlerError process_status_message_(const uint8_t *packet, uint8_t size); + void process_alarm_message_(const uint8_t *packet, uint8_t size, bool check_new); void fill_control_messages_queue_(); void clear_control_messages_queue_(); @@ -101,6 +108,26 @@ class HonClimate : public HaierClimateBase { HonControlMethod control_method_; esphome::sensor::Sensor *outdoor_sensor_; std::queue control_messages_queue_; + CallbackManager alarm_start_callback_{}; + CallbackManager alarm_end_callback_{}; + float active_alarm_count_{NAN}; + std::chrono::steady_clock::time_point last_alarm_request_; +}; + +class HaierAlarmStartTrigger : public Trigger { + public: + explicit HaierAlarmStartTrigger(HonClimate *parent) { + parent->add_alarm_start_callback( + [this](uint8_t alarm_code, const char *alarm_message) { this->trigger(alarm_code, alarm_message); }); + } +}; + +class HaierAlarmEndTrigger : public Trigger { + public: + explicit HaierAlarmEndTrigger(HonClimate *parent) { + parent->add_alarm_end_callback( + [this](uint8_t alarm_code, const char *alarm_message) { this->trigger(alarm_code, alarm_message); }); + } }; } // namespace haier diff --git a/esphome/components/haier/hon_packet.h b/esphome/components/haier/hon_packet.h index 7724b43854..be1b0ae51c 100644 --- a/esphome/components/haier/hon_packet.h +++ b/esphome/components/haier/hon_packet.h @@ -163,6 +163,62 @@ enum class SubcommandsControl : uint16_t { // content: all values like in status packet) }; +const std::string HON_ALARM_MESSAGES[] = { + "Outdoor module failure", + "Outdoor defrost sensor failure", + "Outdoor compressor exhaust sensor failure", + "Outdoor EEPROM abnormality", + "Indoor coil sensor failure", + "Indoor-outdoor communication failure", + "Power supply overvoltage protection", + "Communication failure between panel and indoor unit", + "Outdoor compressor overheat protection", + "Outdoor environmental sensor abnormality", + "Full water protection", + "Indoor EEPROM failure", + "Outdoor out air sensor failure", + "CBD and module communication failure", + "Indoor DC fan failure", + "Outdoor DC fan failure", + "Door switch failure", + "Dust filter needs cleaning reminder", + "Water shortage protection", + "Humidity sensor failure", + "Indoor temperature sensor failure", + "Manipulator limit failure", + "Indoor PM2.5 sensor failure", + "Outdoor PM2.5 sensor failure", + "Indoor heating overload/high load alarm", + "Outdoor AC current protection", + "Outdoor compressor operation abnormality", + "Outdoor DC current protection", + "Outdoor no-load failure", + "CT current abnormality", + "Indoor cooling freeze protection", + "High and low pressure protection", + "Compressor out air temperature is too high", + "Outdoor evaporator sensor failure", + "Outdoor cooling overload", + "Water pump drainage failure", + "Three-phase power supply failure", + "Four-way valve failure", + "External alarm/scraper flow switch failure", + "Temperature cutoff protection alarm", + "Different mode operation failure", + "Electronic expansion valve failure", + "Dual heat source sensor Tw failure", + "Communication failure with the wired controller", + "Indoor unit address duplication failure", + "50Hz zero crossing failure", + "Outdoor unit failure", + "Formaldehyde sensor failure", + "VOC sensor failure", + "CO2 sensor failure", + "Firewall failure", +}; + +constexpr size_t HON_ALARM_COUNT = sizeof(HON_ALARM_MESSAGES) / sizeof(HON_ALARM_MESSAGES[0]); + } // namespace hon_protocol } // namespace haier } // namespace esphome diff --git a/esphome/components/haier/smartair2_climate.cpp b/esphome/components/haier/smartair2_climate.cpp index c2326883f7..00590694d5 100644 --- a/esphome/components/haier/smartair2_climate.cpp +++ b/esphome/components/haier/smartair2_climate.cpp @@ -95,7 +95,7 @@ haier_protocol::HandlerError Smartair2Climate::messages_timeout_handler_with_cyc ESP_LOGI(TAG, "Answer timeout for command %02X, phase %s", (uint8_t) message_type, phase_to_string_(this->protocol_phase_)); ProtocolPhases new_phase = (ProtocolPhases) ((int) this->protocol_phase_ + 1); - if (new_phase >= ProtocolPhases::SENDING_ALARM_STATUS_REQUEST) + if (new_phase >= ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST) new_phase = ProtocolPhases::SENDING_INIT_1; this->set_phase(new_phase); return haier_protocol::HandlerError::HANDLER_OK; @@ -170,9 +170,12 @@ void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now) case ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST: this->set_phase(ProtocolPhases::SENDING_SIGNAL_LEVEL); break; - case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: + case ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST: this->set_phase(ProtocolPhases::SENDING_INIT_1); break; + case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: + this->set_phase(ProtocolPhases::IDLE); + break; case ProtocolPhases::SENDING_CONTROL: if (this->can_send_message() && this->is_control_message_interval_exceeded_(now)) { ESP_LOGI(TAG, "Sending control packet"); @@ -343,19 +346,29 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() { } else if (climate_control.preset.has_value()) { switch (climate_control.preset.value()) { case CLIMATE_PRESET_NONE: + out_data->ten_degree = 0; out_data->turbo_mode = 0; out_data->quiet_mode = 0; break; case CLIMATE_PRESET_BOOST: + out_data->ten_degree = 0; out_data->turbo_mode = 1; out_data->quiet_mode = 0; break; case CLIMATE_PRESET_COMFORT: + out_data->ten_degree = 0; out_data->turbo_mode = 0; out_data->quiet_mode = 1; break; + case CLIMATE_PRESET_AWAY: + // Only allowed in heat mode + out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0; + out_data->turbo_mode = 0; + out_data->quiet_mode = 0; + break; default: ESP_LOGE("Control", "Unsupported preset"); + out_data->ten_degree = 0; out_data->turbo_mode = 0; out_data->quiet_mode = 0; break; @@ -381,6 +394,8 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin this->preset = CLIMATE_PRESET_BOOST; } else if (packet.control.quiet_mode != 0) { this->preset = CLIMATE_PRESET_COMFORT; + } else if (packet.control.ten_degree != 0) { + this->preset = CLIMATE_PRESET_AWAY; } else { this->preset = CLIMATE_PRESET_NONE; } diff --git a/tests/test3.yaml b/tests/test3.yaml index ab7f38d07f..2c7a7a81f7 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -1026,11 +1026,13 @@ climate: wifi_signal: true beeper: true outdoor_temperature: - name: Haier AC outdoor temperature + name: Haier AC outdoor temperature visual: min_temperature: 16 °C max_temperature: 30 °C - temperature_step: 1 °C + temperature_step: + target_temperature: 1 + current_temperature: 0.5 supported_modes: - 'OFF' - HEAT_COOL @@ -1043,6 +1045,23 @@ climate: - VERTICAL - HORIZONTAL - BOTH + supported_presets: + - AWAY + - BOOST + - ECO + - SLEEP + on_alarm_start: + then: + - logger.log: + level: DEBUG + format: "Alarm activated. Code: %d. Message: \"%s\"" + args: [ code, message] + on_alarm_end: + then: + - logger.log: + level: DEBUG + format: "Alarm deactivated. Code: %d. Message: \"%s\"" + args: [ code, message] sprinkler: - id: yard_sprinkler_ctrlr From 937a9c96ce87a5f3cb871d9d147022497696e2e1 Mon Sep 17 00:00:00 2001 From: Chris AtLee Date: Wed, 20 Dec 2023 19:11:32 -0500 Subject: [PATCH 061/193] Allow haier remote protocol to use lambdas (#5898) --- esphome/components/remote_base/haier_protocol.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/remote_base/haier_protocol.h b/esphome/components/remote_base/haier_protocol.h index 6a1c4bea72..7a4ee640e8 100644 --- a/esphome/components/remote_base/haier_protocol.h +++ b/esphome/components/remote_base/haier_protocol.h @@ -26,12 +26,11 @@ DECLARE_REMOTE_PROTOCOL(Haier) template class HaierAction : public RemoteTransmitterActionBase { public: - TEMPLATABLE_VALUE(std::vector, data) + TEMPLATABLE_VALUE(std::vector, code) - void set_code(const std::vector &code) { data_ = code; } void encode(RemoteTransmitData *dst, Ts... x) override { HaierData data{}; - data.data = this->data_.value(x...); + data.data = this->code_.value(x...); HaierProtocol().encode(dst, data); } }; From 2a1d16f17b90544e70dedffb33ba4f7097115e51 Mon Sep 17 00:00:00 2001 From: William Heimbigner Date: Wed, 20 Dec 2023 21:55:34 -0600 Subject: [PATCH 062/193] PMSx003 add relevant device and state classes to default config (#5633) --- esphome/components/pmsx003/sensor.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/esphome/components/pmsx003/sensor.py b/esphome/components/pmsx003/sensor.py index eefcb529f2..08ccd6096e 100644 --- a/esphome/components/pmsx003/sensor.py +++ b/esphome/components/pmsx003/sensor.py @@ -92,66 +92,78 @@ CONFIG_SCHEMA = ( icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, device_class=DEVICE_CLASS_PM1, + state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_2_5_STD): sensor.sensor_schema( unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, device_class=DEVICE_CLASS_PM25, + state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_10_0_STD): sensor.sensor_schema( unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, device_class=DEVICE_CLASS_PM10, + state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_1_0): sensor.sensor_schema( unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, + device_class=DEVICE_CLASS_PM1, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_2_5): sensor.sensor_schema( unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, + device_class=DEVICE_CLASS_PM25, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_10_0): sensor.sensor_schema( unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, + device_class=DEVICE_CLASS_PM10, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_0_3UM): sensor.sensor_schema( unit_of_measurement=UNIT_COUNT_DECILITRE, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_0_5UM): sensor.sensor_schema( unit_of_measurement=UNIT_COUNT_DECILITRE, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_1_0UM): sensor.sensor_schema( unit_of_measurement=UNIT_COUNT_DECILITRE, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_2_5UM): sensor.sensor_schema( unit_of_measurement=UNIT_COUNT_DECILITRE, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_5_0UM): sensor.sensor_schema( unit_of_measurement=UNIT_COUNT_DECILITRE, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_PM_10_0UM): sensor.sensor_schema( unit_of_measurement=UNIT_COUNT_DECILITRE, icon=ICON_CHEMICAL_WEAPON, accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, From 784dff7574ee39554759eb7681e8486bcc4219e6 Mon Sep 17 00:00:00 2001 From: Fabian Pflug Date: Thu, 21 Dec 2023 05:30:10 +0100 Subject: [PATCH 063/193] Add waveshare 2.7in V2 model (#5903) --- .../components/waveshare_epaper/display.py | 4 ++ .../waveshare_epaper/waveshare_epaper.cpp | 53 +++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 16 ++++++ 3 files changed, 73 insertions(+) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 519b07fca2..432c7b6119 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -26,6 +26,9 @@ WaveshareEPaperTypeA = waveshare_epaper_ns.class_( WaveshareEPaper2P7In = waveshare_epaper_ns.class_( "WaveshareEPaper2P7In", WaveshareEPaper ) +WaveshareEPaper2P7InV2 = waveshare_epaper_ns.class_( + "WaveshareEPaper2P7InV2", WaveshareEPaper +) WaveshareEPaper2P9InB = waveshare_epaper_ns.class_( "WaveshareEPaper2P9InB", WaveshareEPaper ) @@ -83,6 +86,7 @@ MODELS = { "2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2), "gdey029t94": ("c", GDEY029T94), "2.70in": ("b", WaveshareEPaper2P7In), + "2.70inv2": ("b", WaveshareEPaper2P7InV2), "2.90in-b": ("b", WaveshareEPaper2P9InB), "4.20in": ("b", WaveshareEPaper4P2In), "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 8fdb9a3ac0..4cd5e3366c 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -634,6 +634,59 @@ void WaveshareEPaper2P7In::dump_config() { LOG_UPDATE_INTERVAL(this); } +void WaveshareEPaper2P7InV2::initialize() { + this->reset_(); + this->wait_until_idle_(); + + this->command(0x12); // SWRESET + this->wait_until_idle_(); + + // SET WINDOWS + // XRAM_START_AND_END_POSITION + this->command(0x44); + this->data(0x00); + this->data(((get_width_internal() - 1) >> 3) & 0xFF); + // YRAM_START_AND_END_POSITION + this->command(0x45); + this->data(0x00); + this->data(0x00); + this->data((get_height_internal() - 1) & 0xFF); + this->data(((get_height_internal() - 1) >> 8) & 0xFF); + + // SET CURSOR + // XRAM_ADDRESS + this->command(0x4E); + this->data(0x00); + // YRAM_ADDRESS + this->command(0x4F); + this->data(0x00); + this->data(0x00); + + this->command(0x11); // data entry mode + this->data(0x03); +} +void HOT WaveshareEPaper2P7InV2::display() { + this->command(0x24); + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + + // COMMAND DISPLAY REFRESH + this->command(0x22); + this->data(0xF7); + this->command(0x20); +} +int WaveshareEPaper2P7InV2::get_width_internal() { return 176; } +int WaveshareEPaper2P7InV2::get_height_internal() { return 264; } +void WaveshareEPaper2P7InV2::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 2.7in V2"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + // ======================================================== // 2.90in Type B (LUT from OTP) // Datasheet: diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 42e8a16829..bc5dbea11d 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -160,6 +160,22 @@ class WaveshareEPaper2P7In : public WaveshareEPaper { int get_height_internal() override; }; +class WaveshareEPaper2P7InV2 : public WaveshareEPaper { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { ; } + + protected: + int get_width_internal() override; + + int get_height_internal() override; +}; + class GDEY029T94 : public WaveshareEPaper { public: void initialize() override; From c305f61020ecd390600763d930fcb47328842f01 Mon Sep 17 00:00:00 2001 From: Fabian Pflug Date: Thu, 21 Dec 2023 05:36:43 +0100 Subject: [PATCH 064/193] Add support for waveshare 2.9in B V3 version (#5902) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../components/waveshare_epaper/display.py | 4 ++ .../waveshare_epaper/waveshare_epaper.cpp | 69 +++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 20 ++++++ 3 files changed, 93 insertions(+) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 432c7b6119..1dd4b7fc54 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -32,6 +32,9 @@ WaveshareEPaper2P7InV2 = waveshare_epaper_ns.class_( WaveshareEPaper2P9InB = waveshare_epaper_ns.class_( "WaveshareEPaper2P9InB", WaveshareEPaper ) +WaveshareEPaper2P9InBV3 = waveshare_epaper_ns.class_( + "WaveshareEPaper2P9InBV3", WaveshareEPaper +) GDEY029T94 = waveshare_epaper_ns.class_("GDEY029T94", WaveshareEPaper) WaveshareEPaper4P2In = waveshare_epaper_ns.class_( "WaveshareEPaper4P2In", WaveshareEPaper @@ -88,6 +91,7 @@ MODELS = { "2.70in": ("b", WaveshareEPaper2P7In), "2.70inv2": ("b", WaveshareEPaper2P7InV2), "2.90in-b": ("b", WaveshareEPaper2P9InB), + "2.90in-bv3": ("b", WaveshareEPaper2P9InBV3), "4.20in": ("b", WaveshareEPaper4P2In), "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2), "5.83in": ("b", WaveshareEPaper5P8In), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 4cd5e3366c..0e9b129988 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -766,6 +766,75 @@ void WaveshareEPaper2P9InB::dump_config() { LOG_UPDATE_INTERVAL(this); } +// ======================================================== +// 2.90in Type B (LUT from OTP) +// Datasheet: +// - https://files.waveshare.com/upload/a/af/2.9inch-e-paper-b-v3-specification.pdf +// ======================================================== + +void WaveshareEPaper2P9InBV3::initialize() { + // from https://github.com/waveshareteam/e-Paper/blob/master/Arduino/epd2in9b_V3/epd2in9b_V3.cpp + this->reset_(); + + // COMMAND POWER ON + this->command(0x04); + this->wait_until_idle_(); + + // COMMAND PANEL SETTING + this->command(0x00); + this->data(0x0F); + this->data(0x89); + + // COMMAND RESOLUTION SETTING + this->command(0x61); + this->data(0x80); + this->data(0x01); + this->data(0x28); + + // COMMAND VCOM AND DATA INTERVAL SETTING + this->command(0x50); + this->data(0x77); +} +void HOT WaveshareEPaper2P9InBV3::display() { + // COMMAND DATA START TRANSMISSION 1 (B/W data) + this->command(0x10); + delay(2); + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + this->command(0x92); + delay(2); + + // COMMAND DATA START TRANSMISSION 2 (RED data) + this->command(0x13); + delay(2); + this->start_data_(); + for (size_t i = 0; i < this->get_buffer_length_(); i++) + this->write_byte(0xFF); + this->end_data_(); + this->command(0x92); + delay(2); + + // COMMAND DISPLAY REFRESH + this->command(0x12); + delay(2); + this->wait_until_idle_(); + + // COMMAND POWER OFF + // NOTE: power off < deep sleep + this->command(0x02); +} +int WaveshareEPaper2P9InBV3::get_width_internal() { return 128; } +int WaveshareEPaper2P9InBV3::get_height_internal() { return 296; } +void WaveshareEPaper2P9InBV3::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 2.9in (B) V3"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + // ======================================================== // Good Display 2.9in black/white/grey // Datasheet: diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index bc5dbea11d..ee9443e8be 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -256,6 +256,26 @@ class WaveshareEPaper2P9InB : public WaveshareEPaper { int get_height_internal() override; }; +class WaveshareEPaper2P9InBV3 : public WaveshareEPaper { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + // COMMAND DEEP SLEEP + this->command(0x07); + this->data(0xA5); // check byte + } + + protected: + int get_width_internal() override; + + int get_height_internal() override; +}; + class WaveshareEPaper4P2In : public WaveshareEPaper { public: void initialize() override; From c92715e403b766061669bd19658f0e1dafb37640 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 21 Dec 2023 13:37:02 +0900 Subject: [PATCH 065/193] Fix pin reuse in test1 (#5978) --- tests/test1.yaml | 64 +++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/tests/test1.yaml b/tests/test1.yaml index 2748a09c59..eb72f90b21 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -324,7 +324,9 @@ ads1115: as5600: i2c_id: i2c_bus - dir_pin: GPIO27 + dir_pin: + number: 27 + allow_other_uses: true direction: clockwise start_position: 90deg range: 180deg @@ -570,13 +572,13 @@ sensor: - platform: as5600 name: AS5600 Position raw_position: - name: AS5600 Raw Position + name: AS5600 Raw Position gain: - name: AS5600 Gain + name: AS5600 Gain magnitude: - name: AS5600 Magnitude + name: AS5600 Magnitude status: - name: AS5600 Status + name: AS5600 Status - platform: as7341 update_interval: 15s gain: X8 @@ -2037,21 +2039,21 @@ my9231: sm2235: data_pin: - allow_other_uses: true - number: GPIO4 + allow_other_uses: true + number: GPIO4 clock_pin: - allow_other_uses: true - number: GPIO5 + allow_other_uses: true + number: GPIO5 max_power_color_channels: 9 max_power_white_channels: 9 sm2335: data_pin: - allow_other_uses: true - number: GPIO4 + allow_other_uses: true + number: GPIO4 clock_pin: - allow_other_uses: true - number: GPIO5 + allow_other_uses: true + number: GPIO5 max_power_color_channels: 9 max_power_white_channels: 9 @@ -3040,17 +3042,13 @@ display: id: my_lcd_gpio dimensions: 18x4 data_pins: - - - allow_other_uses: true + - allow_other_uses: true number: GPIO19 - - - allow_other_uses: true + - allow_other_uses: true number: GPIO21 - - - allow_other_uses: true + - allow_other_uses: true number: GPIO22 - - - allow_other_uses: true + - allow_other_uses: true number: GPIO23 enable_pin: allow_other_uses: true @@ -4201,25 +4199,25 @@ graphical_display_menu: lambda: 'ESP_LOGI("graphical_display_menu", "root leave");' items: - type: back - text: 'Back' + text: "Back" - type: label - type: menu - text: 'Submenu 1' + text: "Submenu 1" items: - type: back - text: 'Back' + text: "Back" - type: menu - text: 'Submenu 21' + text: "Submenu 21" items: - type: back - text: 'Back' + text: "Back" - type: command - text: 'Show Main' + text: "Show Main" on_value: then: - display_menu.show_main: test_graphical_display_menu - type: select - text: 'Enum Item' + text: "Enum Item" immediate_edit: true select: test_select on_enter: @@ -4232,7 +4230,7 @@ graphical_display_menu: then: lambda: 'ESP_LOGI("graphical_display_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - type: number - text: 'Number' + text: "Number" number: test_number on_enter: then: @@ -4244,15 +4242,15 @@ graphical_display_menu: then: lambda: 'ESP_LOGI("graphical_display_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - type: command - text: 'Hide' + text: "Hide" on_value: then: - display_menu.hide: test_graphical_display_menu - type: switch - text: 'Switch' + text: "Switch" switch: my_switch - on_text: 'Bright' - off_text: 'Dark' + on_text: "Bright" + off_text: "Dark" immediate_edit: false on_value: then: From a784f1e69127daafc6457f0be897bf51bb1cb5e6 Mon Sep 17 00:00:00 2001 From: mrtoy-me <118446898+mrtoy-me@users.noreply.github.com> Date: Thu, 21 Dec 2023 14:38:11 +1000 Subject: [PATCH 066/193] Add Waveshare 1.47in 172x320 to ST7789v component (#5884) --- esphome/components/st7789v/display.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/esphome/components/st7789v/display.py b/esphome/components/st7789v/display.py index 41970afd26..5b2b5a126c 100644 --- a/esphome/components/st7789v/display.py +++ b/esphome/components/st7789v/display.py @@ -97,6 +97,19 @@ MODELS = { CONF_BACKLIGHT_PIN: "GPIO15", } ), + "WAVESHARE_1.47IN_172X320": model_spec( + presets={ + CONF_HEIGHT: 320, + CONF_WIDTH: 172, + CONF_OFFSET_HEIGHT: 34, + CONF_OFFSET_WIDTH: 0, + CONF_ROTATION: 90, + CONF_CS_PIN: "GPIO21", + CONF_DC_PIN: "GPIO22", + CONF_RESET_PIN: "GPIO23", + CONF_BACKLIGHT_PIN: "GPIO4", + } + ), "CUSTOM": model_spec(), } From 04b354799251808f85a3fe12c2a2579977de5770 Mon Sep 17 00:00:00 2001 From: Ruben van Dijk <15885455+RubenNL@users.noreply.github.com> Date: Thu, 21 Dec 2023 05:39:55 +0100 Subject: [PATCH 067/193] (fingerprint_grow) Added on_finger_scan_invalid automation. (#5885) --- esphome/components/fingerprint_grow/__init__.py | 16 ++++++++++++++++ .../fingerprint_grow/fingerprint_grow.cpp | 4 ++++ .../fingerprint_grow/fingerprint_grow.h | 11 +++++++++++ esphome/const.py | 1 + tests/test3.yaml | 3 +++ 5 files changed, 35 insertions(+) diff --git a/esphome/components/fingerprint_grow/__init__.py b/esphome/components/fingerprint_grow/__init__.py index ecbbc3d477..5249107f17 100644 --- a/esphome/components/fingerprint_grow/__init__.py +++ b/esphome/components/fingerprint_grow/__init__.py @@ -15,6 +15,7 @@ from esphome.const import ( CONF_ON_ENROLLMENT_SCAN, CONF_ON_FINGER_SCAN_MATCHED, CONF_ON_FINGER_SCAN_UNMATCHED, + CONF_ON_FINGER_SCAN_INVALID, CONF_PASSWORD, CONF_SENSING_PIN, CONF_SPEED, @@ -42,6 +43,10 @@ FingerScanUnmatchedTrigger = fingerprint_grow_ns.class_( "FingerScanUnmatchedTrigger", automation.Trigger.template() ) +FingerScanInvalidTrigger = fingerprint_grow_ns.class_( + "FingerScanInvalidTrigger", automation.Trigger.template() +) + EnrollmentScanTrigger = fingerprint_grow_ns.class_( "EnrollmentScanTrigger", automation.Trigger.template(cg.uint8, cg.uint16) ) @@ -108,6 +113,13 @@ CONFIG_SCHEMA = ( ), } ), + cv.Optional(CONF_ON_FINGER_SCAN_INVALID): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + FingerScanInvalidTrigger + ), + } + ), cv.Optional(CONF_ON_ENROLLMENT_SCAN): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( @@ -162,6 +174,10 @@ async def to_code(config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_FINGER_SCAN_INVALID, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_ENROLLMENT_SCAN, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation( diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index 8729e0f4bf..2486e02964 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -134,12 +134,14 @@ uint8_t FingerprintGrowComponent::scan_image_(uint8_t buffer) { case NO_FINGER: if (this->sensing_pin_ != nullptr) { ESP_LOGD(TAG, "No finger"); + this->finger_scan_invalid_callback_.call(); } else { ESP_LOGV(TAG, "No finger"); } return this->data_[0]; case IMAGE_FAIL: ESP_LOGE(TAG, "Imaging error"); + this->finger_scan_invalid_callback_.call(); default: return this->data_[0]; } @@ -152,10 +154,12 @@ uint8_t FingerprintGrowComponent::scan_image_(uint8_t buffer) { break; case IMAGE_MESS: ESP_LOGE(TAG, "Image too messy"); + this->finger_scan_invalid_callback_.call(); break; case FEATURE_FAIL: case INVALID_IMAGE: ESP_LOGE(TAG, "Could not find fingerprint features"); + this->finger_scan_invalid_callback_.call(); break; } return this->data_[0]; diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.h b/esphome/components/fingerprint_grow/fingerprint_grow.h index f414146e64..9aad94fc2a 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.h +++ b/esphome/components/fingerprint_grow/fingerprint_grow.h @@ -124,6 +124,9 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic void add_on_finger_scan_unmatched_callback(std::function callback) { this->finger_scan_unmatched_callback_.add(std::move(callback)); } + void add_on_finger_scan_invalid_callback(std::function callback) { + this->finger_scan_invalid_callback_.add(std::move(callback)); + } void add_on_enrollment_scan_callback(std::function callback) { this->enrollment_scan_callback_.add(std::move(callback)); } @@ -172,6 +175,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic sensor::Sensor *last_finger_id_sensor_{nullptr}; sensor::Sensor *last_confidence_sensor_{nullptr}; binary_sensor::BinarySensor *enrolling_binary_sensor_{nullptr}; + CallbackManager finger_scan_invalid_callback_; CallbackManager finger_scan_matched_callback_; CallbackManager finger_scan_unmatched_callback_; CallbackManager enrollment_scan_callback_; @@ -194,6 +198,13 @@ class FingerScanUnmatchedTrigger : public Trigger<> { } }; +class FingerScanInvalidTrigger : public Trigger<> { + public: + explicit FingerScanInvalidTrigger(FingerprintGrowComponent *parent) { + parent->add_on_finger_scan_invalid_callback([this]() { this->trigger(); }); + } +}; + class EnrollmentScanTrigger : public Trigger { public: explicit EnrollmentScanTrigger(FingerprintGrowComponent *parent) { diff --git a/esphome/const.py b/esphome/const.py index 58c6c47e3a..4a4a59f659 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -500,6 +500,7 @@ CONF_ON_DOUBLE_CLICK = "on_double_click" CONF_ON_ENROLLMENT_DONE = "on_enrollment_done" CONF_ON_ENROLLMENT_FAILED = "on_enrollment_failed" CONF_ON_ENROLLMENT_SCAN = "on_enrollment_scan" +CONF_ON_FINGER_SCAN_INVALID = "on_finger_scan_invalid" CONF_ON_FINGER_SCAN_MATCHED = "on_finger_scan_matched" CONF_ON_FINGER_SCAN_UNMATCHED = "on_finger_scan_unmatched" CONF_ON_JSON_MESSAGE = "on_json_message" diff --git a/tests/test3.yaml b/tests/test3.yaml index 2c7a7a81f7..e39e711ab3 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -1257,6 +1257,9 @@ fingerprint_grow: number: 4 password: 0x12FE37DC new_password: 0xA65B9840 + on_finger_scan_invalid: + - homeassistant.event: + event: esphome.${device_name}_fingerprint_grow_finger_scan_invalid on_finger_scan_matched: - homeassistant.event: event: esphome.${device_name}_fingerprint_grow_finger_scan_matched From 223e6e8f137ae442cce15125e80889ec76c85b65 Mon Sep 17 00:00:00 2001 From: Steve Rodgers Date: Wed, 20 Dec 2023 21:10:47 -0800 Subject: [PATCH 068/193] Alarm panel: Add changes to support enhanced features (#5671) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 4 +- .../alarm_control_panel/__init__.py | 53 +++++++- .../alarm_control_panel.cpp | 8 ++ .../alarm_control_panel/alarm_control_panel.h | 16 +++ .../alarm_control_panel/automation.h | 14 +++ .../template/alarm_control_panel/__init__.py | 25 +++- .../template_alarm_control_panel.cpp | 116 ++++++++++++++---- .../template_alarm_control_panel.h | 32 ++++- 8 files changed, 237 insertions(+), 31 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index fc14b2088e..b8c870f20f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -25,7 +25,7 @@ esphome/components/airthings_ble/* @jeromelaban esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau esphome/components/airthings_wave_mini/* @ncareau esphome/components/airthings_wave_plus/* @jeromelaban -esphome/components/alarm_control_panel/* @grahambrown11 +esphome/components/alarm_control_panel/* @grahambrown11 @hwstar esphome/components/alpha3/* @jan-hofmeier esphome/components/am43/* @buxtronix esphome/components/am43/cover/* @buxtronix @@ -327,7 +327,7 @@ esphome/components/tca9548a/* @andreashergert1984 esphome/components/tcl112/* @glmnet esphome/components/tee501/* @Stock-M esphome/components/teleinfo/* @0hax -esphome/components/template/alarm_control_panel/* @grahambrown11 +esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar esphome/components/text/* @mauritskorse esphome/components/thermostat/* @kbx81 esphome/components/time/* @OttoWinter diff --git a/esphome/components/alarm_control_panel/__init__.py b/esphome/components/alarm_control_panel/__init__.py index d9cafb4f30..35d239c267 100644 --- a/esphome/components/alarm_control_panel/__init__.py +++ b/esphome/components/alarm_control_panel/__init__.py @@ -11,7 +11,7 @@ from esphome.const import ( ) from esphome.cpp_helpers import setup_entity -CODEOWNERS = ["@grahambrown11"] +CODEOWNERS = ["@grahambrown11", "@hwstar"] IS_PLATFORM_COMPONENT = True CONF_ON_TRIGGERED = "on_triggered" @@ -22,6 +22,8 @@ CONF_ON_ARMED_HOME = "on_armed_home" CONF_ON_ARMED_NIGHT = "on_armed_night" CONF_ON_ARMED_AWAY = "on_armed_away" CONF_ON_DISARMED = "on_disarmed" +CONF_ON_CHIME = "on_chime" +CONF_ON_READY = "on_ready" alarm_control_panel_ns = cg.esphome_ns.namespace("alarm_control_panel") AlarmControlPanel = alarm_control_panel_ns.class_("AlarmControlPanel", cg.EntityBase) @@ -53,12 +55,22 @@ ArmedAwayTrigger = alarm_control_panel_ns.class_( DisarmedTrigger = alarm_control_panel_ns.class_( "DisarmedTrigger", automation.Trigger.template() ) +ChimeTrigger = alarm_control_panel_ns.class_( + "ChimeTrigger", automation.Trigger.template() +) +ReadyTrigger = alarm_control_panel_ns.class_( + "ReadyTrigger", automation.Trigger.template() +) + ArmAwayAction = alarm_control_panel_ns.class_("ArmAwayAction", automation.Action) ArmHomeAction = alarm_control_panel_ns.class_("ArmHomeAction", automation.Action) ArmNightAction = alarm_control_panel_ns.class_("ArmNightAction", automation.Action) DisarmAction = alarm_control_panel_ns.class_("DisarmAction", automation.Action) PendingAction = alarm_control_panel_ns.class_("PendingAction", automation.Action) TriggeredAction = alarm_control_panel_ns.class_("TriggeredAction", automation.Action) +ChimeAction = alarm_control_panel_ns.class_("ChimeAction", automation.Action) +ReadyAction = alarm_control_panel_ns.class_("ReadyAction", automation.Action) + AlarmControlPanelCondition = alarm_control_panel_ns.class_( "AlarmControlPanelCondition", automation.Condition ) @@ -111,6 +123,16 @@ ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger), } ), + cv.Optional(CONF_ON_CHIME): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChimeTrigger), + } + ), + cv.Optional(CONF_ON_READY): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReadyTrigger), + } + ), } ) @@ -157,6 +179,12 @@ async def setup_alarm_control_panel_core_(var, config): for conf in config.get(CONF_ON_CLEARED, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_CHIME, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_READY, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) async def register_alarm_control_panel(var, config): @@ -232,6 +260,29 @@ async def alarm_action_trigger_to_code(config, action_id, template_arg, args): return var +@automation.register_action( + "alarm_control_panel.chime", ChimeAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA +) +async def alarm_action_chime_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) + return var + + +@automation.register_action( + "alarm_control_panel.ready", ReadyAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA +) +@automation.register_condition( + "alarm_control_panel.ready", + AlarmControlPanelCondition, + ALARM_CONTROL_PANEL_CONDITION_SCHEMA, +) +async def alarm_action_ready_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) + return var + + @automation.register_condition( "alarm_control_panel.is_armed", AlarmControlPanelCondition, diff --git a/esphome/components/alarm_control_panel/alarm_control_panel.cpp b/esphome/components/alarm_control_panel/alarm_control_panel.cpp index 9dc083c004..9f1485ee90 100644 --- a/esphome/components/alarm_control_panel/alarm_control_panel.cpp +++ b/esphome/components/alarm_control_panel/alarm_control_panel.cpp @@ -96,6 +96,14 @@ void AlarmControlPanel::add_on_cleared_callback(std::function &&callback this->cleared_callback_.add(std::move(callback)); } +void AlarmControlPanel::add_on_chime_callback(std::function &&callback) { + this->chime_callback_.add(std::move(callback)); +} + +void AlarmControlPanel::add_on_ready_callback(std::function &&callback) { + this->ready_callback_.add(std::move(callback)); +} + void AlarmControlPanel::arm_away(optional code) { auto call = this->make_call(); call.arm_away(); diff --git a/esphome/components/alarm_control_panel/alarm_control_panel.h b/esphome/components/alarm_control_panel/alarm_control_panel.h index dc0b92df76..85c2b2148e 100644 --- a/esphome/components/alarm_control_panel/alarm_control_panel.h +++ b/esphome/components/alarm_control_panel/alarm_control_panel.h @@ -89,6 +89,18 @@ class AlarmControlPanel : public EntityBase { */ void add_on_cleared_callback(std::function &&callback); + /** Add a callback for when a chime zone goes from closed to open + * + * @param callback The callback function + */ + void add_on_chime_callback(std::function &&callback); + + /** Add a callback for when a ready state changes + * + * @param callback The callback function + */ + void add_on_ready_callback(std::function &&callback); + /** A numeric representation of the supported features as per HomeAssistant * */ @@ -178,6 +190,10 @@ class AlarmControlPanel : public EntityBase { CallbackManager disarmed_callback_{}; // clear callback CallbackManager cleared_callback_{}; + // chime callback + CallbackManager chime_callback_{}; + // ready callback + CallbackManager ready_callback_{}; }; } // namespace alarm_control_panel diff --git a/esphome/components/alarm_control_panel/automation.h b/esphome/components/alarm_control_panel/automation.h index 8538020c53..2177fb710f 100644 --- a/esphome/components/alarm_control_panel/automation.h +++ b/esphome/components/alarm_control_panel/automation.h @@ -69,6 +69,20 @@ class ClearedTrigger : public Trigger<> { } }; +class ChimeTrigger : public Trigger<> { + public: + explicit ChimeTrigger(AlarmControlPanel *alarm_control_panel) { + alarm_control_panel->add_on_chime_callback([this]() { this->trigger(); }); + } +}; + +class ReadyTrigger : public Trigger<> { + public: + explicit ReadyTrigger(AlarmControlPanel *alarm_control_panel) { + alarm_control_panel->add_on_ready_callback([this]() { this->trigger(); }); + } +}; + template class ArmAwayAction : public Action { public: explicit ArmAwayAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {} diff --git a/esphome/components/template/alarm_control_panel/__init__.py b/esphome/components/template/alarm_control_panel/__init__.py index 27b7e92b4f..3555f2fafd 100644 --- a/esphome/components/template/alarm_control_panel/__init__.py +++ b/esphome/components/template/alarm_control_panel/__init__.py @@ -12,11 +12,13 @@ from esphome.const import ( ) from .. import template_ns -CODEOWNERS = ["@grahambrown11"] +CODEOWNERS = ["@grahambrown11", "@hwstar"] CONF_CODES = "codes" CONF_BYPASS_ARMED_HOME = "bypass_armed_home" CONF_BYPASS_ARMED_NIGHT = "bypass_armed_night" +CONF_CHIME = "chime" +CONF_TRIGGER_MODE = "trigger_mode" CONF_REQUIRES_CODE_TO_ARM = "requires_code_to_arm" CONF_ARMING_HOME_TIME = "arming_home_time" CONF_ARMING_NIGHT_TIME = "arming_night_time" @@ -24,16 +26,20 @@ CONF_ARMING_AWAY_TIME = "arming_away_time" CONF_PENDING_TIME = "pending_time" CONF_TRIGGER_TIME = "trigger_time" + FLAG_NORMAL = "normal" FLAG_BYPASS_ARMED_HOME = "bypass_armed_home" FLAG_BYPASS_ARMED_NIGHT = "bypass_armed_night" +FLAG_CHIME = "chime" BinarySensorFlags = { FLAG_NORMAL: 1 << 0, FLAG_BYPASS_ARMED_HOME: 1 << 1, FLAG_BYPASS_ARMED_NIGHT: 1 << 2, + FLAG_CHIME: 1 << 3, } + TemplateAlarmControlPanel = template_ns.class_( "TemplateAlarmControlPanel", alarm_control_panel.AlarmControlPanel, cg.Component ) @@ -46,6 +52,14 @@ RESTORE_MODES = { "RESTORE_DEFAULT_DISARMED": TemplateAlarmControlPanelRestoreMode.ALARM_CONTROL_PANEL_RESTORE_DEFAULT_DISARMED, } +AlarmSensorType = template_ns.enum("AlarmSensorType") + +ALARM_SENSOR_TYPES = { + "DELAYED": AlarmSensorType.ALARM_SENSOR_TYPE_DELAYED, + "INSTANT": AlarmSensorType.ALARM_SENSOR_TYPE_INSTANT, + "DELAYED_FOLLOWER": AlarmSensorType.ALARM_SENSOR_TYPE_DELAYED_FOLLOWER, +} + def validate_config(config): if config.get(CONF_REQUIRES_CODE_TO_ARM, False) and not config.get(CONF_CODES, []): @@ -60,6 +74,10 @@ TEMPLATE_ALARM_CONTROL_PANEL_BINARY_SENSOR_SCHEMA = cv.maybe_simple_value( cv.Required(CONF_INPUT): cv.use_id(binary_sensor.BinarySensor), cv.Optional(CONF_BYPASS_ARMED_HOME, default=False): cv.boolean, cv.Optional(CONF_BYPASS_ARMED_NIGHT, default=False): cv.boolean, + cv.Optional(CONF_CHIME, default=False): cv.boolean, + cv.Optional(CONF_TRIGGER_MODE, default="DELAYED"): cv.enum( + ALARM_SENSOR_TYPES, upper=True, space="_" + ), }, key=CONF_INPUT, ) @@ -123,6 +141,7 @@ async def to_code(config): for sensor in config.get(CONF_BINARY_SENSORS, []): bs = await cg.get_variable(sensor[CONF_INPUT]) + flags = BinarySensorFlags[FLAG_NORMAL] if sensor[CONF_BYPASS_ARMED_HOME]: flags |= BinarySensorFlags[FLAG_BYPASS_ARMED_HOME] @@ -130,7 +149,9 @@ async def to_code(config): if sensor[CONF_BYPASS_ARMED_NIGHT]: flags |= BinarySensorFlags[FLAG_BYPASS_ARMED_NIGHT] supports_arm_night = True - cg.add(var.add_sensor(bs, flags)) + if sensor[CONF_CHIME]: + flags |= BinarySensorFlags[FLAG_CHIME] + cg.add(var.add_sensor(bs, flags, sensor[CONF_TRIGGER_MODE])) cg.add(var.set_supports_arm_home(supports_arm_home)) cg.add(var.set_supports_arm_night(supports_arm_night)) diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp index b39b587811..99843417fa 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp @@ -1,3 +1,4 @@ + #include "template_alarm_control_panel.h" #include #include "esphome/components/alarm_control_panel/alarm_control_panel.h" @@ -15,8 +16,14 @@ static const char *const TAG = "template.alarm_control_panel"; TemplateAlarmControlPanel::TemplateAlarmControlPanel(){}; #ifdef USE_BINARY_SENSOR -void TemplateAlarmControlPanel::add_sensor(binary_sensor::BinarySensor *sensor, uint16_t flags) { - this->sensor_map_[sensor] = flags; +void TemplateAlarmControlPanel::add_sensor(binary_sensor::BinarySensor *sensor, uint16_t flags, AlarmSensorType type) { + // Save the flags and type. Assign a store index for the per sensor data type. + SensorDataStore sd; + sd.last_chime_state = false; + this->sensor_map_[sensor].flags = flags; + this->sensor_map_[sensor].type = type; + this->sensor_data_.push_back(sd); + this->sensor_map_[sensor].store_index = this->next_store_index_++; }; #endif @@ -35,13 +42,27 @@ void TemplateAlarmControlPanel::dump_config() { ESP_LOGCONFIG(TAG, " Trigger Time: %" PRIu32 "s", (this->trigger_time_ / 1000)); ESP_LOGCONFIG(TAG, " Supported Features: %" PRIu32, this->get_supported_features()); #ifdef USE_BINARY_SENSOR - for (auto sensor_pair : this->sensor_map_) { - ESP_LOGCONFIG(TAG, " Binary Sesnsor:"); - ESP_LOGCONFIG(TAG, " Name: %s", sensor_pair.first->get_name().c_str()); + for (auto sensor_info : this->sensor_map_) { + ESP_LOGCONFIG(TAG, " Binary Sensor:"); + ESP_LOGCONFIG(TAG, " Name: %s", sensor_info.first->get_name().c_str()); ESP_LOGCONFIG(TAG, " Armed home bypass: %s", - TRUEFALSE(sensor_pair.second & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)); + TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)); ESP_LOGCONFIG(TAG, " Armed night bypass: %s", - TRUEFALSE(sensor_pair.second & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)); + TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)); + ESP_LOGCONFIG(TAG, " Chime mode: %s", TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)); + const char *sensor_type; + switch (sensor_info.second.type) { + case ALARM_SENSOR_TYPE_INSTANT: + sensor_type = "instant"; + break; + case ALARM_SENSOR_TYPE_DELAYED_FOLLOWER: + sensor_type = "delayed_follower"; + break; + case ALARM_SENSOR_TYPE_DELAYED: + default: + sensor_type = "delayed"; + } + ESP_LOGCONFIG(TAG, " Sensor type: %s", sensor_type); } #endif } @@ -92,31 +113,80 @@ void TemplateAlarmControlPanel::loop() { (millis() - this->last_update_) > this->trigger_time_) { future_state = this->desired_state_; } - bool trigger = false; + + bool delayed_sensor_not_ready = false; + bool instant_sensor_not_ready = false; + #ifdef USE_BINARY_SENSOR - if (this->is_state_armed(future_state)) { - // TODO might be better to register change for each sensor in setup... - for (auto sensor_pair : this->sensor_map_) { - if (sensor_pair.first->state) { - if (this->current_state_ == ACP_STATE_ARMED_HOME && - (sensor_pair.second & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)) { - continue; + // Test all of the sensors in the list regardless of the alarm panel state + for (auto sensor_info : this->sensor_map_) { + // Check for chime zones + if ((sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)) { + // Look for the transition from closed to open + if ((!this->sensor_data_[sensor_info.second.store_index].last_chime_state) && (sensor_info.first->state)) { + // Must be disarmed to chime + if (this->current_state_ == ACP_STATE_DISARMED) { + this->chime_callback_.call(); } - if (this->current_state_ == ACP_STATE_ARMED_NIGHT && - (sensor_pair.second & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)) { - continue; + } + // Record the sensor state change + this->sensor_data_[sensor_info.second.store_index].last_chime_state = sensor_info.first->state; + } + // Check for triggered sensors + if (sensor_info.first->state) { // Sensor triggered? + // Skip if bypass armed home + if (this->current_state_ == ACP_STATE_ARMED_HOME && + (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)) { + continue; + } + // Skip if bypass armed night + if (this->current_state_ == ACP_STATE_ARMED_NIGHT && + (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)) { + continue; + } + + // If sensor type is of type instant + if (sensor_info.second.type == ALARM_SENSOR_TYPE_INSTANT) { + instant_sensor_not_ready = true; + break; + } + // If sensor type is of type interior follower + if (sensor_info.second.type == ALARM_SENSOR_TYPE_DELAYED_FOLLOWER) { + // Look to see if we are in the pending state + if (this->current_state_ == ACP_STATE_PENDING) { + delayed_sensor_not_ready = true; + } else { + instant_sensor_not_ready = true; } - trigger = true; + } + // If sensor type is of type delayed + if (sensor_info.second.type == ALARM_SENSOR_TYPE_DELAYED) { + delayed_sensor_not_ready = true; break; } } } + // Update all sensors not ready flag + this->sensors_ready_ = ((!instant_sensor_not_ready) && (!delayed_sensor_not_ready)); + + // Call the ready state change callback if there was a change + if (this->sensors_ready_ != this->sensors_ready_last_) { + this->ready_callback_.call(); + this->sensors_ready_last_ = this->sensors_ready_; + } + #endif - if (trigger) { - if (this->pending_time_ > 0 && this->current_state_ != ACP_STATE_TRIGGERED) { - this->publish_state(ACP_STATE_PENDING); - } else { + if (this->is_state_armed(future_state) && (!this->sensors_ready_)) { + // Instant sensors + if (instant_sensor_not_ready) { this->publish_state(ACP_STATE_TRIGGERED); + } else if (delayed_sensor_not_ready) { + // Delayed sensors + if ((this->pending_time_ > 0) && (this->current_state_ != ACP_STATE_TRIGGERED)) { + this->publish_state(ACP_STATE_PENDING); + } else { + this->publish_state(ACP_STATE_TRIGGERED); + } } } else if (future_state != this->current_state_) { this->publish_state(future_state); diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h index 9582ed157c..9ae69a0422 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h @@ -21,7 +21,15 @@ enum BinarySensorFlags : uint16_t { BINARY_SENSOR_MODE_NORMAL = 1 << 0, BINARY_SENSOR_MODE_BYPASS_ARMED_HOME = 1 << 1, BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT = 1 << 2, + BINARY_SENSOR_MODE_CHIME = 1 << 3, }; + +enum AlarmSensorType : uint16_t { + ALARM_SENSOR_TYPE_DELAYED = 0, + ALARM_SENSOR_TYPE_INSTANT, + ALARM_SENSOR_TYPE_DELAYED_FOLLOWER +}; + #endif enum TemplateAlarmControlPanelRestoreMode { @@ -29,6 +37,16 @@ enum TemplateAlarmControlPanelRestoreMode { ALARM_CONTROL_PANEL_RESTORE_DEFAULT_DISARMED, }; +struct SensorDataStore { + bool last_chime_state; +}; + +struct SensorInfo { + uint16_t flags; + AlarmSensorType type; + uint8_t store_index; +}; + class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, public Component { public: TemplateAlarmControlPanel(); @@ -38,6 +56,7 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, uint32_t get_supported_features() const override; bool get_requires_code() const override; bool get_requires_code_to_arm() const override { return this->requires_code_to_arm_; } + bool get_all_sensors_ready() { return this->sensors_ready_; }; void set_restore_mode(TemplateAlarmControlPanelRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } #ifdef USE_BINARY_SENSOR @@ -46,7 +65,8 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, * @param sensor The BinarySensor instance. * @param ignore_when_home if this should be ignored when armed_home mode */ - void add_sensor(binary_sensor::BinarySensor *sensor, uint16_t flags = 0); + void add_sensor(binary_sensor::BinarySensor *sensor, uint16_t flags = 0, + AlarmSensorType type = ALARM_SENSOR_TYPE_DELAYED); #endif /** add a code @@ -98,8 +118,9 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, protected: void control(const alarm_control_panel::AlarmControlPanelCall &call) override; #ifdef USE_BINARY_SENSOR - // the map of binary sensors that the alarm_panel monitors with their modes - std::map sensor_map_; + // This maps a binary sensor to its type and attribute bits + std::map sensor_map_; + #endif TemplateAlarmControlPanelRestoreMode restore_mode_{}; @@ -115,10 +136,15 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, uint32_t trigger_time_; // a list of codes std::vector codes_; + // Per sensor data store + std::vector sensor_data_; // requires a code to arm bool requires_code_to_arm_ = false; bool supports_arm_home_ = false; bool supports_arm_night_ = false; + bool sensors_ready_ = false; + bool sensors_ready_last_ = false; + uint8_t next_store_index_ = 0; // check if the code is valid bool is_code_valid_(optional code); From f096f107e238236e75cb7f6b68b9f90433bb2e59 Mon Sep 17 00:00:00 2001 From: sbrudenell Date: Wed, 20 Dec 2023 21:13:54 -0900 Subject: [PATCH 069/193] support default pins for adafruit esp32 feather v2 (#5482) --- esphome/components/esp32/boards.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/esphome/components/esp32/boards.py b/esphome/components/esp32/boards.py index e6c23c4d96..d361ca9917 100644 --- a/esphome/components/esp32/boards.py +++ b/esphome/components/esp32/boards.py @@ -42,6 +42,34 @@ ESP32_BASE_PINS = { } ESP32_BOARD_PINS = { + "adafruit_feather_esp32_v2": { + "A0": 26, + "A1": 25, + "A2": 34, + "A3": 39, + "A4": 36, + "A5": 4, + "SCK": 5, + "MOSI": 19, + "MISO": 21, + "RX": 7, + "TX": 8, + "D37": 37, + "LED": 13, + "LED_BUILTIN": 13, + "D12": 12, + "D27": 27, + "D33": 33, + "D15": 15, + "D32": 32, + "D14": 14, + "SCL": 20, + "SDA": 22, + "BUTTON": 38, + "NEOPIXEL": 0, + "PIN_NEOPIXEL": 0, + "NEOPIXEL_POWER": 2, + }, "adafruit_feather_esp32s2_tft": { "BUTTON": 0, "A0": 18, From d73ad39aedebf0bb4941aca26e4fce789f637a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Bana=C5=9B?= Date: Thu, 21 Dec 2023 08:03:57 +0100 Subject: [PATCH 070/193] Bug: Unwanted change resistance in x9c component (#5483) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/x9c/x9c.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/x9c/x9c.cpp b/esphome/components/x9c/x9c.cpp index ff7777e71f..1b283a68e5 100644 --- a/esphome/components/x9c/x9c.cpp +++ b/esphome/components/x9c/x9c.cpp @@ -7,6 +7,10 @@ namespace x9c { static const char *const TAG = "x9c.output"; void X9cOutput::trim_value(int change_amount) { + if (change_amount == 0) { + return; + } + if (change_amount > 0) { // Set change direction this->ud_pin_->digital_write(true); } else { From 222bb9b495ceeac459c65bff08fd5187d0b4a797 Mon Sep 17 00:00:00 2001 From: marshn Date: Thu, 21 Dec 2023 07:17:01 +0000 Subject: [PATCH 071/193] Improvements to RF receiver for Drayton Digistat heating controller (#5504) --- .../remote_base/drayton_protocol.cpp | 142 ++++++++++-------- 1 file changed, 80 insertions(+), 62 deletions(-) diff --git a/esphome/components/remote_base/drayton_protocol.cpp b/esphome/components/remote_base/drayton_protocol.cpp index fb5f37b470..6c617f56c8 100644 --- a/esphome/components/remote_base/drayton_protocol.cpp +++ b/esphome/components/remote_base/drayton_protocol.cpp @@ -13,7 +13,8 @@ static const uint8_t NBITS_SYNC = 4; static const uint8_t NBITS_ADDRESS = 16; static const uint8_t NBITS_CHANNEL = 5; static const uint8_t NBITS_COMMAND = 7; -static const uint8_t NBITS = NBITS_ADDRESS + NBITS_CHANNEL + NBITS_COMMAND; +static const uint8_t NDATABITS = NBITS_ADDRESS + NBITS_CHANNEL + NBITS_COMMAND; +static const uint8_t MIN_RX_SRC = (NDATABITS * 2 + NBITS_SYNC / 2); static const uint8_t CMD_ON = 0x41; static const uint8_t CMD_OFF = 0x02; @@ -116,7 +117,7 @@ void DraytonProtocol::encode(RemoteTransmitData *dst, const DraytonData &data) { ESP_LOGV(TAG, "Send Drayton: out_data %08" PRIx32, out_data); - for (uint32_t mask = 1UL << (NBITS - 1); mask != 0; mask >>= 1) { + for (uint32_t mask = 1UL << (NDATABITS - 1); mask != 0; mask >>= 1) { if (out_data & mask) { dst->mark(BIT_TIME_US); dst->space(BIT_TIME_US); @@ -134,79 +135,96 @@ optional DraytonProtocol::decode(RemoteReceiveData src) { .command = 0, }; - if (src.size() < 45) { - return {}; - } + while (src.size() - src.get_index() > MIN_RX_SRC) { + ESP_LOGVV(TAG, + "Decode Drayton: %" PRId32 ", %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 + " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 + " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 "", + src.size() - src.get_index(), src.peek(0), src.peek(1), src.peek(2), src.peek(3), src.peek(4), + src.peek(5), src.peek(6), src.peek(7), src.peek(8), src.peek(9), src.peek(10), src.peek(11), src.peek(12), + src.peek(13), src.peek(14), src.peek(15), src.peek(16), src.peek(17), src.peek(18), src.peek(19)); - ESP_LOGVV(TAG, - "Decode Drayton: %" PRId32 ", %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 - " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 - " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 "", - src.size(), src.peek(0), src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6), - src.peek(7), src.peek(8), src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14), - src.peek(15), src.peek(16), src.peek(17), src.peek(18), src.peek(19)); + // If first preamble item is a space, skip it + if (src.peek_space_at_least(1)) { + src.advance(1); + } - // If first preamble item is a space, skip it - if (src.peek_space_at_least(1)) { - src.advance(1); - } + // Look for sync pulse, after. If sucessful index points to space of sync symbol + while (src.size() - src.get_index() >= NDATABITS) { + ESP_LOGVV(TAG, "Decode Drayton: sync search %d, %" PRId32 " %" PRId32, src.size() - src.get_index(), src.peek(), + src.peek(1)); + if (src.peek_mark(2 * BIT_TIME_US) && + (src.peek_space(2 * BIT_TIME_US, 1) || src.peek_space(3 * BIT_TIME_US, 1))) { + src.advance(1); + ESP_LOGVV(TAG, "Decode Drayton: Found SYNC, - %d", src.get_index()); + break; + } else { + src.advance(2); + } + } - // Look for sync pulse, after. If sucessful index points to space of sync symbol - for (uint16_t preamble = 0; preamble <= NBITS_PREAMBLE * 2; preamble += 2) { - ESP_LOGVV(TAG, "Decode Drayton: preamble %d %" PRId32 " %" PRId32, preamble, src.peek(preamble), - src.peek(preamble + 1)); - if (src.peek_mark(2 * BIT_TIME_US, preamble) && - (src.peek_space(2 * BIT_TIME_US, preamble + 1) || src.peek_space(3 * BIT_TIME_US, preamble + 1))) { - src.advance(preamble + 1); + // No point continuing if not enough samples remaining to complete a packet + if (src.size() - src.get_index() < NDATABITS) { + ESP_LOGV(TAG, "Decode Drayton: Fail 1, - %" PRIu32, src.get_index()); break; } - } - // Read data. Index points to space of sync symbol - // Extract first bit - // Checks next bit to leave index pointing correctly - uint32_t out_data = 0; - uint8_t bit = NBITS_ADDRESS + NBITS_COMMAND + NBITS_CHANNEL - 1; - if (src.expect_space(3 * BIT_TIME_US) && (src.expect_mark(BIT_TIME_US) || src.peek_mark(2 * BIT_TIME_US))) { - out_data |= 0 << bit; - } else if (src.expect_space(2 * BIT_TIME_US) && src.expect_mark(BIT_TIME_US) && - (src.expect_space(BIT_TIME_US) || src.peek_space(2 * BIT_TIME_US))) { - out_data |= 1 << bit; - } else { - ESP_LOGV(TAG, "Decode Drayton: Fail 1, - %" PRIu32, src.get_index()); - return {}; - } - - // Before/after each bit is read the index points to the transition at the start of the bit period or, - // if there is no transition at the start of the bit period, then the transition in the middle of - // the previous bit period. - while (--bit >= 1) { - ESP_LOGVV(TAG, "Decode Drayton: Data, %2d %08" PRIx32, bit, out_data); - if ((src.expect_space(BIT_TIME_US) || src.expect_space(2 * BIT_TIME_US)) && - (src.expect_mark(BIT_TIME_US) || src.peek_mark(2 * BIT_TIME_US))) { + // Read data. Index points to space of sync symbol + // Extract first bit + // Checks next bit to leave index pointing correctly + uint32_t out_data = 0; + uint8_t bit = NDATABITS - 1; + ESP_LOGVV(TAG, "Decode Drayton: first bit %d %" PRId32 ", %" PRId32, src.peek(0), src.peek(1), src.peek(2)); + if (src.expect_space(3 * BIT_TIME_US) && (src.expect_mark(BIT_TIME_US) || src.peek_mark(2 * BIT_TIME_US))) { out_data |= 0 << bit; - } else if ((src.expect_mark(BIT_TIME_US) || src.expect_mark(2 * BIT_TIME_US)) && + } else if (src.expect_space(2 * BIT_TIME_US) && src.expect_mark(BIT_TIME_US) && (src.expect_space(BIT_TIME_US) || src.peek_space(2 * BIT_TIME_US))) { out_data |= 1 << bit; } else { - ESP_LOGVV(TAG, "Decode Drayton: Fail 2, %2d %08" PRIx32, bit, out_data); - return {}; + ESP_LOGV(TAG, "Decode Drayton: Fail 2, - %d %d %d", src.peek(-1), src.peek(0), src.peek(1)); + continue; } - } - if (src.expect_space(BIT_TIME_US) || src.expect_space(2 * BIT_TIME_US)) { - out_data |= 0; - } else if (src.expect_mark(BIT_TIME_US) || src.expect_mark(2 * BIT_TIME_US)) { - out_data |= 1; - } - ESP_LOGV(TAG, "Decode Drayton: Data, %2d %08" PRIx32, bit, out_data); - out.channel = (uint8_t) (out_data & 0x1F); - out_data >>= NBITS_CHANNEL; - out.command = (uint8_t) (out_data & 0x7F); - out_data >>= NBITS_COMMAND; - out.address = (uint16_t) (out_data & 0xFFFF); + // Before/after each bit is read the index points to the transition at the start of the bit period or, + // if there is no transition at the start of the bit period, then the transition in the middle of + // the previous bit period. + while (--bit >= 1) { + ESP_LOGVV(TAG, "Decode Drayton: Data, %2d %08" PRIx32, bit, out_data); + if ((src.expect_space(BIT_TIME_US) || src.expect_space(2 * BIT_TIME_US)) && + (src.expect_mark(BIT_TIME_US) || src.peek_mark(2 * BIT_TIME_US))) { + out_data |= 0 << bit; + } else if ((src.expect_mark(BIT_TIME_US) || src.expect_mark(2 * BIT_TIME_US)) && + (src.expect_space(BIT_TIME_US) || src.peek_space(2 * BIT_TIME_US))) { + out_data |= 1 << bit; + } else { + break; + } + } - return out; + if (bit > 0) { + ESP_LOGVV(TAG, "Decode Drayton: Fail 3, %d %" PRId32 " %" PRId32, src.peek(-1), src.peek(0), src.peek(1)); + continue; + } + + if (src.expect_space(BIT_TIME_US) || src.expect_space(2 * BIT_TIME_US)) { + out_data |= 0; + } else if (src.expect_mark(BIT_TIME_US) || src.expect_mark(2 * BIT_TIME_US)) { + out_data |= 1; + } else { + continue; + } + + ESP_LOGV(TAG, "Decode Drayton: Data, %2d %08x", bit, out_data); + + out.channel = (uint8_t) (out_data & 0x1F); + out_data >>= NBITS_CHANNEL; + out.command = (uint8_t) (out_data & 0x7F); + out_data >>= NBITS_COMMAND; + out.address = (uint16_t) (out_data & 0xFFFF); + + return out; + } + return {}; } void DraytonProtocol::dump(const DraytonData &data) { ESP_LOGI(TAG, "Received Drayton: address=0x%04X (0x%04x), channel=0x%03x command=0x%03X", data.address, From 74281b93c4491f80bfd02db9e56696a86081083c Mon Sep 17 00:00:00 2001 From: kahrendt Date: Thu, 21 Dec 2023 02:19:15 -0500 Subject: [PATCH 072/193] Reduce memory usage with StringRef in MQTT Components (#5719) --- esphome/components/mqtt/mqtt_binary_sensor.cpp | 2 +- esphome/components/mqtt/mqtt_component.cpp | 12 ++++++------ esphome/components/mqtt/mqtt_component.h | 13 ++++++++----- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/esphome/components/mqtt/mqtt_binary_sensor.cpp b/esphome/components/mqtt/mqtt_binary_sensor.cpp index 79e6989a8f..6d12e88391 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.cpp +++ b/esphome/components/mqtt/mqtt_binary_sensor.cpp @@ -25,7 +25,7 @@ void MQTTBinarySensorComponent::dump_config() { MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor *binary_sensor) : binary_sensor_(binary_sensor) { if (this->binary_sensor_->is_status_binary_sensor()) { - this->set_custom_state_topic(mqtt::global_mqtt_client->get_availability().topic); + this->set_custom_state_topic(mqtt::global_mqtt_client->get_availability().topic.c_str()); } } diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index af4d6f13a5..8855b4d8e3 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -34,13 +34,13 @@ std::string MQTTComponent::get_default_topic_for_(const std::string &suffix) con std::string MQTTComponent::get_state_topic_() const { if (this->has_custom_state_topic_) - return this->custom_state_topic_; + return this->custom_state_topic_.str(); return this->get_default_topic_for_("state"); } std::string MQTTComponent::get_command_topic_() const { if (this->has_custom_command_topic_) - return this->custom_command_topic_; + return this->custom_command_topic_.str(); return this->get_default_topic_for_("command"); } @@ -180,12 +180,12 @@ MQTTComponent::MQTTComponent() = default; float MQTTComponent::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } void MQTTComponent::disable_discovery() { this->discovery_enabled_ = false; } -void MQTTComponent::set_custom_state_topic(const std::string &custom_state_topic) { - this->custom_state_topic_ = custom_state_topic; +void MQTTComponent::set_custom_state_topic(const char *custom_state_topic) { + this->custom_state_topic_ = StringRef(custom_state_topic); this->has_custom_state_topic_ = true; } -void MQTTComponent::set_custom_command_topic(const std::string &custom_command_topic) { - this->custom_command_topic_ = custom_command_topic; +void MQTTComponent::set_custom_command_topic(const char *custom_command_topic) { + this->custom_command_topic_ = StringRef(custom_command_topic); this->has_custom_command_topic_ = true; } void MQTTComponent::set_command_retain(bool command_retain) { this->command_retain_ = command_retain; } diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index aacfe8891f..9fc8c795d3 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -8,6 +8,7 @@ #include "esphome/core/component.h" #include "esphome/core/entity_base.h" +#include "esphome/core/string_ref.h" #include "mqtt_client.h" namespace esphome { @@ -88,9 +89,9 @@ class MQTTComponent : public Component { virtual std::string component_type() const = 0; /// Set a custom state topic. Set to "" for default behavior. - void set_custom_state_topic(const std::string &custom_state_topic); + void set_custom_state_topic(const char *custom_state_topic); /// Set a custom command topic. Set to "" for default behavior. - void set_custom_command_topic(const std::string &custom_command_topic); + void set_custom_command_topic(const char *custom_command_topic); /// Set whether command message should be retained. void set_command_retain(bool command_retain); @@ -188,15 +189,17 @@ class MQTTComponent : public Component { /// Generate the Home Assistant MQTT discovery object id by automatically transforming the friendly name. std::string get_default_object_id_() const; - std::string custom_state_topic_{}; - std::string custom_command_topic_{}; + StringRef custom_state_topic_{}; + StringRef custom_command_topic_{}; + + std::unique_ptr availability_; + bool has_custom_state_topic_{false}; bool has_custom_command_topic_{false}; bool command_retain_{false}; bool retain_{true}; bool discovery_enabled_{true}; - std::unique_ptr availability_; bool resend_state_{false}; }; From 5e2df0b6a27821bc978a8beb6e8d8c5e7e7c91b9 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 21 Dec 2023 09:34:33 +0100 Subject: [PATCH 073/193] Nextion allow underscore on names (#5979) --- esphome/components/nextion/base_component.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/nextion/base_component.py b/esphome/components/nextion/base_component.py index 5bd6643cb8..784da35371 100644 --- a/esphome/components/nextion/base_component.py +++ b/esphome/components/nextion/base_component.py @@ -33,14 +33,14 @@ CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start" def NextionName(value): - valid_chars = f"{ascii_letters + digits}." + valid_chars = f"{ascii_letters + digits + '_'}." if not isinstance(value, str) or len(value) > 29: raise cv.Invalid("Must be a string less than 29 characters") for char in value: if char not in valid_chars: raise cv.Invalid( - f"Must only consist of upper/lowercase characters, numbers and the period '.'. The character '{char}' cannot be used." + f"Must only consist of upper/lowercase characters, numbers, the underscore '_', and the period '.'. The character '{char}' cannot be used." ) return value From 442820deafd1b17d0da003ad4577f80c98d6c513 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 22 Dec 2023 03:28:25 +1300 Subject: [PATCH 074/193] Fix replaced - in allowed characters during object_id sanitizing (#5983) --- esphome/helpers.py | 2 +- tests/unit_tests/test_helpers.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/helpers.py b/esphome/helpers.py index 00416b591f..254c950b5d 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -357,7 +357,7 @@ def snake_case(value): return value.replace(" ", "_").lower() -_DISALLOWED_CHARS = re.compile(r"[^a-zA-Z0-9_]") +_DISALLOWED_CHARS = re.compile(r"[^a-zA-Z0-9-_]") def sanitize(value): diff --git a/tests/unit_tests/test_helpers.py b/tests/unit_tests/test_helpers.py index 79d39901f0..fc6bdbcdec 100644 --- a/tests/unit_tests/test_helpers.py +++ b/tests/unit_tests/test_helpers.py @@ -261,6 +261,7 @@ def test_snake_case(text, expected): ('!"§$%&/()=?foo_bar', "___________foo_bar"), ('foo_!"§$%&/()=?bar', "foo____________bar"), ('foo_bar!"§$%&/()=?', "foo_bar___________"), + ('foo-bar!"§$%&/()=?', "foo-bar___________"), ), ) def test_sanitize(text, expected): From 0a779a9299edf1b8af2813a245e6dfb51814e394 Mon Sep 17 00:00:00 2001 From: CVan Date: Thu, 21 Dec 2023 17:55:10 -0500 Subject: [PATCH 075/193] Update libtiff6 (#5985) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index ee7c70bb0f..7c162fc316 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -50,7 +50,7 @@ RUN \ libssl-dev=3.0.11-1~deb12u2 \ libffi-dev=3.4.4-1 \ libopenjp2-7=2.5.0-2 \ - libtiff6=4.5.0-6 \ + libtiff6=4.5.0-6+deb12u1 \ cargo=0.66.0+ds1-1 \ pkg-config=1.8.1-1 \ gcc-arm-linux-gnueabihf=4:12.2.0-3; \ From 31448a4fcd3f12e0d486e8197eddf7e022a8ac25 Mon Sep 17 00:00:00 2001 From: davidmonro Date: Fri, 22 Dec 2023 09:57:12 +1100 Subject: [PATCH 076/193] Override GPIOs 12 and 13 on the airm2m (LuatOS) board (#5982) Co-authored-by: David Monro --- esphome/components/esp32/boards.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/esp32/boards.py b/esphome/components/esp32/boards.py index d361ca9917..cd85f3da97 100644 --- a/esphome/components/esp32/boards.py +++ b/esphome/components/esp32/boards.py @@ -161,6 +161,10 @@ ESP32_BOARD_PINS = { "BUTTON": 0, "SWITCH": 0, }, + "airm2m_core_esp32c3": { + "LED1_BUILTIN": 12, + "LED2_BUILTIN": 13, + }, "alksesp32": { "A0": 32, "A1": 33, From 513a02ce11f8879e869afcba8bd118febf823cce Mon Sep 17 00:00:00 2001 From: marshn Date: Fri, 22 Dec 2023 00:30:23 +0000 Subject: [PATCH 077/193] Add Keeloq RF protocol (#5511) --- esphome/components/remote_base/__init__.py | 56 ++++++ .../remote_base/keeloq_protocol.cpp | 188 ++++++++++++++++++ .../components/remote_base/keeloq_protocol.h | 53 +++++ 3 files changed, 297 insertions(+) create mode 100644 esphome/components/remote_base/keeloq_protocol.cpp create mode 100644 esphome/components/remote_base/keeloq_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 3accd5038c..1d8c6e0967 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -633,6 +633,62 @@ async def magiquest_action(var, config, args): cg.add(var.set_magnitude(template_)) +# Microchip HCS301 KeeLoq OOK +( + KeeloqData, + KeeloqBinarySensor, + KeeloqTrigger, + KeeloqAction, + KeeloqDumper, +) = declare_protocol("Keeloq") +KEELOQ_SCHEMA = cv.Schema( + { + cv.Required(CONF_ADDRESS): cv.All(cv.hex_int, cv.Range(min=0, max=0xFFFFFFF)), + cv.Required(CONF_CODE): cv.All(cv.hex_int, cv.Range(min=0, max=0xFFFFFFFF)), + cv.Optional(CONF_COMMAND, default=0x10): cv.All( + cv.hex_int, + cv.Range(min=0, max=0x10), + ), + cv.Optional(CONF_LEVEL, default=False): cv.boolean, + } +) + + +@register_binary_sensor("keeloq", KeeloqBinarySensor, KEELOQ_SCHEMA) +def Keeloq_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + KeeloqData, + ("address", config[CONF_ADDRESS]), + ("command", config[CONF_COMMAND]), + ) + ) + ) + + +@register_trigger("keeloq", KeeloqTrigger, KeeloqData) +def keeloq_trigger(var, config): + pass + + +@register_dumper("keeloq", KeeloqDumper) +def keeloq_dumper(var, config): + pass + + +@register_action("keeloq", KeeloqAction, KEELOQ_SCHEMA) +async def keeloq_action(var, config, args): + template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint32) + cg.add(var.set_address(template_)) + template_ = await cg.templatable(config[CONF_CODE], args, cg.uint32) + cg.add(var.set_encrypted(template_)) + template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8) + cg.add(var.set_command(template_)) + template_ = await cg.templatable(config[CONF_LEVEL], args, bool) + cg.add(var.set_vlow(template_)) + + # NEC NECData, NECBinarySensor, NECTrigger, NECAction, NECDumper = declare_protocol("NEC") NEC_SCHEMA = cv.Schema( diff --git a/esphome/components/remote_base/keeloq_protocol.cpp b/esphome/components/remote_base/keeloq_protocol.cpp new file mode 100644 index 0000000000..77a2f9be6c --- /dev/null +++ b/esphome/components/remote_base/keeloq_protocol.cpp @@ -0,0 +1,188 @@ +#include "keeloq_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.keeloq"; + +static const uint32_t BIT_TIME_US = 380; +static const uint8_t NBITS_PREAMBLE = 12; +static const uint8_t NBITS_REPEAT = 1; +static const uint8_t NBITS_VLOW = 1; +static const uint8_t NBITS_SERIAL = 28; +static const uint8_t NBITS_BUTTONS = 4; +static const uint8_t NBITS_DISC = 12; +static const uint8_t NBITS_SYNC_CNT = 16; + +static const uint8_t NBITS_FIXED_DATA = NBITS_REPEAT + NBITS_VLOW + NBITS_BUTTONS + NBITS_SERIAL; +static const uint8_t NBITS_ENCRYPTED_DATA = NBITS_BUTTONS + NBITS_DISC + NBITS_SYNC_CNT; +static const uint8_t NBITS_DATA = NBITS_FIXED_DATA + NBITS_ENCRYPTED_DATA; + +/* +KeeLoq Protocol + +Coded using information from datasheet for Microchip HCS301 KeeLow Code Hopping Encoder + +Encoder - Hopping code is generated at random. + +Decoder - Hopping code is ignored and not checked when received. Serial number of +transmitter and nutton command is decoded. + +*/ + +void KeeloqProtocol::encode(RemoteTransmitData *dst, const KeeloqData &data) { + uint32_t out_data = 0x0; + + ESP_LOGD(TAG, "Send Keeloq: address=%07x command=%03x encrypted=%08x", data.address, data.command, data.encrypted); + ESP_LOGV(TAG, "Send Keeloq: data bits (%d + %d)", NBITS_ENCRYPTED_DATA, NBITS_FIXED_DATA); + + // Preamble = '01' x 12 + for (uint8_t cnt = NBITS_PREAMBLE; cnt; cnt--) { + dst->space(BIT_TIME_US); + dst->mark(BIT_TIME_US); + } + + // Header = 10 bit space + dst->space(10 * BIT_TIME_US); + + // Encrypted field + out_data = data.encrypted; + + ESP_LOGV(TAG, "Send Keeloq: Encrypted data %04x", out_data); + + for (uint32_t mask = 1, cnt = 0; cnt < NBITS_ENCRYPTED_DATA; cnt++, mask <<= 1) { + if (out_data & mask) { + dst->mark(1 * BIT_TIME_US); + dst->space(2 * BIT_TIME_US); + } else { + dst->mark(2 * BIT_TIME_US); + dst->space(1 * BIT_TIME_US); + } + } + + // first 32 bits of fixed portion + out_data = (data.command & 0x0f); + out_data <<= NBITS_SERIAL; + out_data |= data.address; + ESP_LOGV(TAG, "Send Keeloq: Fixed data %04x", out_data); + + for (uint32_t mask = 1, cnt = 0; cnt < (NBITS_FIXED_DATA - 2); cnt++, mask <<= 1) { + if (out_data & mask) { + dst->mark(1 * BIT_TIME_US); + dst->space(2 * BIT_TIME_US); + } else { + dst->mark(2 * BIT_TIME_US); + dst->space(1 * BIT_TIME_US); + } + } + + // low battery flag + if (data.vlow) { + dst->mark(1 * BIT_TIME_US); + dst->space(2 * BIT_TIME_US); + } else { + dst->mark(2 * BIT_TIME_US); + dst->space(1 * BIT_TIME_US); + } + + // repeat flag - always sent as a '1' + dst->mark(1 * BIT_TIME_US); + dst->space(2 * BIT_TIME_US); + + // Guard time at end of packet + dst->space(39 * BIT_TIME_US); +} + +optional KeeloqProtocol::decode(RemoteReceiveData src) { + KeeloqData out{ + .encrypted = 0, + .address = 0, + .command = 0, + .repeat = false, + .vlow = false, + + }; + + if (src.size() != (NBITS_PREAMBLE + NBITS_DATA) * 2) { + return {}; + } + + ESP_LOGVV(TAG, "%2d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", src.size(), src.peek(0), + src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6), src.peek(7), src.peek(8), + src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14), src.peek(15), + src.peek(16), src.peek(17), src.peek(18), src.peek(19)); + + // Check preamble bits + int8_t bit = NBITS_PREAMBLE - 1; + while (--bit >= 0) { + if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(BIT_TIME_US)) { + ESP_LOGV(TAG, "Decode KeeLoq: Fail 1, %d %d", bit + 1, src.peek()); + return {}; + } + } + if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(10 * BIT_TIME_US)) { + ESP_LOGV(TAG, "Decode KeeLoq: Fail 1, %d %d", bit + 1, src.peek()); + return {}; + } + + // Read encrypted bits + uint32_t out_data = 0; + for (bit = 0; bit < NBITS_ENCRYPTED_DATA; bit++) { + if (src.expect_mark(2 * BIT_TIME_US) && src.expect_space(BIT_TIME_US)) { + out_data |= 0 << bit; + } else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) { + out_data |= 1 << bit; + } else { + ESP_LOGV(TAG, "Decode KeeLoq: Fail 2, %d %d", src.get_index(), src.peek()); + return {}; + } + } + ESP_LOGVV(TAG, "Decode KeeLoq: Data, %d %08x", bit, out_data); + out.encrypted = out_data; + + // Read Serial Number and Button Status + out_data = 0; + for (bit = 0; bit < NBITS_SERIAL + NBITS_BUTTONS; bit++) { + if (src.expect_mark(2 * BIT_TIME_US) && src.expect_space(BIT_TIME_US)) { + out_data |= 0 << bit; + } else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) { + out_data |= 1 << bit; + } else { + ESP_LOGV(TAG, "Decode KeeLoq: Fail 3, %d %d", src.get_index(), src.peek()); + return {}; + } + } + ESP_LOGVV(TAG, "Decode KeeLoq: Data, %2d %08x", bit, out_data); + out.command = (out_data >> 28) & 0xf; + out.address = out_data & 0xfffffff; + + // Read Vlow bit + if (src.expect_mark(2 * BIT_TIME_US) && src.expect_space(BIT_TIME_US)) { + out.vlow = false; + } else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) { + out.vlow = true; + } else { + ESP_LOGV(TAG, "Decode KeeLoq: Fail 4, %08x", src.peek()); + return {}; + } + + // Read Repeat bit + if (src.expect_mark(2 * BIT_TIME_US) && src.peek_space_at_least(BIT_TIME_US)) { + out.repeat = false; + } else if (src.expect_mark(BIT_TIME_US) && src.peek_space_at_least(2 * BIT_TIME_US)) { + out.repeat = true; + } else { + ESP_LOGV(TAG, "Decode KeeLoq: Fail 5, %08x", src.peek()); + return {}; + } + + return out; +} + +void KeeloqProtocol::dump(const KeeloqData &data) { + ESP_LOGD(TAG, "Received Keeloq: address=0x%08X, command=0x%02x", data.address, data.command); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/keeloq_protocol.h b/esphome/components/remote_base/keeloq_protocol.h new file mode 100644 index 0000000000..47125c151b --- /dev/null +++ b/esphome/components/remote_base/keeloq_protocol.h @@ -0,0 +1,53 @@ +#pragma once + +#include "esphome/core/component.h" +#include "remote_base.h" + +namespace esphome { +namespace remote_base { + +struct KeeloqData { + uint32_t encrypted; // 32 bit encrypted field + uint32_t address; // 28 bit serial number + uint8_t command; // Button Status S2-S1-S0-S3 + bool repeat; // Repeated command bit + bool vlow; // Battery status bit + + bool operator==(const KeeloqData &rhs) const { + // Treat 0x10 as a special, wildcard button press + // This allows us to match on just the address if wanted. + if (address != rhs.address) { + return false; + } + return (rhs.command == 0x10 || command == rhs.command); + } +}; + +class KeeloqProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const KeeloqData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const KeeloqData &data) override; +}; + +DECLARE_REMOTE_PROTOCOL(Keeloq) + +template class KeeloqAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint32_t, address) + TEMPLATABLE_VALUE(uint32_t, encrypted) + TEMPLATABLE_VALUE(uint8_t, command) + TEMPLATABLE_VALUE(bool, vlow) + + void encode(RemoteTransmitData *dst, Ts... x) override { + KeeloqData data{}; + data.address = this->address_.value(x...); + data.encrypted = this->encrypted_.value(x...); + data.command = this->command_.value(x...); + data.vlow = this->vlow_.value(x...); + KeeloqProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome From 059e4cee58def2e433eb5fe02451981b45a6731f Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Thu, 21 Dec 2023 19:42:12 -0600 Subject: [PATCH 078/193] Add workaround for crash in Arduino 2.0.9 when CDC is configured (#5987) --- esphome/components/logger/logger.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 05b97a5f64..e0f7e77d2c 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -285,6 +285,7 @@ void Logger::pre_setup() { #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) #if ARDUINO_USB_CDC_ON_BOOT this->hw_serial_ = &Serial; + Serial.setTxTimeoutMs(0); // workaround for 2.0.9 crash when there's no data connection Serial.begin(this->baud_rate_); #else this->hw_serial_ = &Serial; From 70fdc3c3f8a1250fb5476d26d24f0ad21985b491 Mon Sep 17 00:00:00 2001 From: Jessica Hamilton Date: Fri, 22 Dec 2023 14:58:30 +1300 Subject: [PATCH 079/193] web_server.py: return empty content when file doesn't exist (#5980) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/dashboard/web_server.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 4552aebf7b..c4b84d3fe3 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -792,13 +792,22 @@ class EditRequestHandler(BaseHandler): """Get the content of a file.""" loop = asyncio.get_running_loop() filename = settings.rel_path(configuration) - content = await loop.run_in_executor(None, self._read_file, filename) - self.write(content) + content = await loop.run_in_executor( + None, self._read_file, filename, configuration + ) + if content is not None: + self.write(content) - def _read_file(self, filename: str) -> bytes: + def _read_file(self, filename: str, configuration: str) -> bytes | None: """Read a file and return the content as bytes.""" - with open(file=filename, encoding="utf-8") as f: - return f.read() + try: + with open(file=filename, encoding="utf-8") as f: + return f.read() + except FileNotFoundError: + if configuration in const.SECRETS_FILES: + return "" + self.set_status(404) + return None def _write_file(self, filename: str, content: bytes) -> None: """Write a file with the given content.""" From 3de5b26d7726128de941dbfa8c32ea168e497dff Mon Sep 17 00:00:00 2001 From: Scott K Logan Date: Thu, 21 Dec 2023 20:33:29 -0600 Subject: [PATCH 080/193] Add a Binary Sensor Filter for state settling (#5900) --- esphome/components/binary_sensor/__init__.py | 14 ++++++++++++++ esphome/components/binary_sensor/filter.cpp | 17 +++++++++++++++++ esphome/components/binary_sensor/filter.h | 13 +++++++++++++ tests/test1.yaml | 2 ++ 4 files changed, 46 insertions(+) diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index babe46e082..2f788d7103 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -141,6 +141,7 @@ DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Compon InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter) AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component) LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter) +SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component) FILTER_REGISTRY = Registry() validate_filters = cv.validate_registry("filter", FILTER_REGISTRY) @@ -259,6 +260,19 @@ async def lambda_filter_to_code(config, filter_id): return cg.new_Pvariable(filter_id, lambda_) +@register_filter( + "settle", + SettleFilter, + cv.templatable(cv.positive_time_period_milliseconds), +) +async def settle_filter_to_code(config, filter_id): + var = cg.new_Pvariable(filter_id) + await cg.register_component(var, {}) + template_ = await cg.templatable(config, [], cg.uint32) + cg.add(var.set_delay(template_)) + return var + + MULTI_CLICK_TIMING_SCHEMA = cv.Schema( { cv.Optional(CONF_STATE): cv.boolean, diff --git a/esphome/components/binary_sensor/filter.cpp b/esphome/components/binary_sensor/filter.cpp index 46957383c3..8f94b108ac 100644 --- a/esphome/components/binary_sensor/filter.cpp +++ b/esphome/components/binary_sensor/filter.cpp @@ -111,6 +111,23 @@ LambdaFilter::LambdaFilter(std::function(bool)> f) : f_(std::move optional LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); } +optional SettleFilter::new_value(bool value, bool is_initial) { + if (!this->steady_) { + this->set_timeout("SETTLE", this->delay_.value(), [this, value, is_initial]() { + this->steady_ = true; + this->output(value, is_initial); + }); + return {}; + } else { + this->steady_ = false; + this->output(value, is_initial); + this->set_timeout("SETTLE", this->delay_.value(), [this]() { this->steady_ = true; }); + return value; + } +} + +float SettleFilter::get_setup_priority() const { return setup_priority::HARDWARE; } + } // namespace binary_sensor } // namespace esphome diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index 9514cb3fe2..f7342db2fb 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -108,6 +108,19 @@ class LambdaFilter : public Filter { std::function(bool)> f_; }; +class SettleFilter : public Filter, public Component { + public: + optional new_value(bool value, bool is_initial) override; + + float get_setup_priority() const override; + + template void set_delay(T delay) { this->delay_ = delay; } + + protected: + TemplatableValue delay_{}; + bool steady_{true}; +}; + } // namespace binary_sensor } // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index eb72f90b21..720d4e8e82 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1732,6 +1732,8 @@ binary_sensor: - delayed_on_off: !lambda "return 10;" - delayed_on: !lambda "return 1000;" - delayed_off: !lambda "return 0;" + - settle: 40ms + - settle: !lambda "return 10;" on_press: then: - lambda: >- From 4fb7e945f86c143e69bb95e77213d45b2e278950 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 Dec 2023 16:59:24 -1000 Subject: [PATCH 081/193] Fix unexpected disconnects when outgoing buffer is full during keepalive (#5988) --- esphome/components/api/api_connection.cpp | 24 +++++++++++++++++++---- esphome/components/api/api_connection.h | 3 +++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index d5ab00a822..4ebfb7582e 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -118,7 +118,9 @@ void APIConnection::loop() { this->list_entities_iterator_.advance(); this->initial_state_iterator_.advance(); - const uint32_t keepalive = 60000; + static uint32_t keepalive = 60000; + static uint8_t max_ping_retries = 60; + static uint16_t ping_retry_interval = 1000; const uint32_t now = millis(); if (this->sent_ping_) { // Disconnect if not responded within 2.5*keepalive @@ -126,10 +128,24 @@ void APIConnection::loop() { on_fatal_error(); ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_combined_info_.c_str()); } - } else if (now - this->last_traffic_ > keepalive) { + } else if (now - this->last_traffic_ > keepalive && now > this->next_ping_retry_) { ESP_LOGVV(TAG, "Sending keepalive PING..."); - this->sent_ping_ = true; - this->send_ping_request(PingRequest()); + this->sent_ping_ = this->send_ping_request(PingRequest()); + if (!this->sent_ping_) { + this->next_ping_retry_ = now + ping_retry_interval; + this->ping_retries_++; + if (this->ping_retries_ >= max_ping_retries) { + on_fatal_error(); + ESP_LOGE(TAG, "%s: Sending keepalive failed %d time(s). Disconnecting...", this->client_combined_info_.c_str(), + this->ping_retries_); + } else if (this->ping_retries_ >= 10) { + ESP_LOGW(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms", + this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval); + } else { + ESP_LOGD(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms", + this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval); + } + } } #ifdef USE_ESP32_CAMERA diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 09b595bb71..9d01468807 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -140,6 +140,7 @@ class APIConnection : public APIServerConnection { void on_disconnect_response(const DisconnectResponse &value) override; void on_ping_response(const PingResponse &value) override { // we initiated ping + this->ping_retries_ = 0; this->sent_ping_ = false; } void on_home_assistant_state_response(const HomeAssistantStateResponse &msg) override; @@ -217,6 +218,8 @@ class APIConnection : public APIServerConnection { bool state_subscription_{false}; int log_subscription_{ESPHOME_LOG_LEVEL_NONE}; uint32_t last_traffic_; + uint32_t next_ping_retry_{0}; + uint8_t ping_retries_{0}; bool sent_ping_{false}; bool service_call_subscription_{false}; bool next_close_ = false; From bd6fa29f7765c0cc5b6f9ee1705c89f11b965179 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 22 Dec 2023 18:29:10 +1300 Subject: [PATCH 082/193] Regenerate api_pb2 after manual changes were added incorrectly in #5732 (#5990) --- esphome/components/api/api_pb2.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 8dd34e7ef1..f81bf04e99 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -3848,6 +3848,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const { sprintf(buffer, "%g", this->visual_max_humidity); out.append(buffer); out.append("\n"); + out.append("}"); } #endif bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -4015,6 +4016,7 @@ void ClimateStateResponse::dump_to(std::string &out) const { sprintf(buffer, "%g", this->target_humidity); out.append(buffer); out.append("\n"); + out.append("}"); } #endif bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { From 676ae6b26e1d759f809373d60b047a6f929266d2 Mon Sep 17 00:00:00 2001 From: matzman666 Date: Fri, 22 Dec 2023 07:58:17 +0100 Subject: [PATCH 083/193] Improved sensor readings in htu21d component. (#5839) --- esphome/components/htu21d/htu21d.cpp | 67 ++++++++++++++++------------ 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/esphome/components/htu21d/htu21d.cpp b/esphome/components/htu21d/htu21d.cpp index a8133ae32e..d0dbb15a43 100644 --- a/esphome/components/htu21d/htu21d.cpp +++ b/esphome/components/htu21d/htu21d.cpp @@ -39,45 +39,54 @@ void HTU21DComponent::dump_config() { LOG_SENSOR(" ", "Humidity", this->humidity_); } void HTU21DComponent::update() { - uint16_t raw_temperature; if (this->write(&HTU21D_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); - float temperature = (float(raw_temperature & 0xFFFC)) * 175.72f / 65536.0f - 46.85f; + // According to the datasheet sht21 temperature readings can take up to 85ms + this->set_timeout(85, [this]() { + uint16_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); - uint16_t raw_humidity; - if (this->write(&HTU21D_REGISTER_HUMIDITY, 1) != i2c::ERROR_OK) { - this->status_set_warning(); - return; - } - delay(50); // NOLINT - if (this->read(reinterpret_cast(&raw_humidity), 2) != i2c::ERROR_OK) { - this->status_set_warning(); - return; - } - raw_humidity = i2c::i2ctohs(raw_humidity); + float temperature = (float(raw_temperature & 0xFFFC)) * 175.72f / 65536.0f - 46.85f; - float humidity = (float(raw_humidity & 0xFFFC)) * 125.0f / 65536.0f - 6.0f; + ESP_LOGD(TAG, "Got Temperature=%.1f°C", temperature); - int8_t heater_level = this->get_heater_level(); + if (this->temperature_ != nullptr) + this->temperature_->publish_state(temperature); + this->status_clear_warning(); - ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%% Heater Level=%d", temperature, humidity, heater_level); + if (this->write(&HTU21D_REGISTER_HUMIDITY, 1) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } - if (this->temperature_ != nullptr) - this->temperature_->publish_state(temperature); - if (this->humidity_ != nullptr) - this->humidity_->publish_state(humidity); - if (this->heater_ != nullptr) - this->heater_->publish_state(heater_level); - this->status_clear_warning(); + this->set_timeout(50, [this]() { + uint16_t raw_humidity; + if (this->read(reinterpret_cast(&raw_humidity), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_humidity = i2c::i2ctohs(raw_humidity); + + 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); + + if (this->humidity_ != nullptr) + this->humidity_->publish_state(humidity); + if (this->heater_ != nullptr) + this->heater_->publish_state(heater_level); + this->status_clear_warning(); + }); + }); } bool HTU21DComponent::is_heater_enabled() { From d2d005838694774d9a6e4d0f1721a89d7588e79f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 22 Dec 2023 20:03:47 +1300 Subject: [PATCH 084/193] Lint the script folder files (#5991) --- script/api_protobuf/api_protobuf.py | 478 ++++++++++++++-------------- script/build_language_schema.py | 29 +- script/bump-version.py | 5 +- script/ci-custom.py | 233 +++++++------- script/helpers.py | 23 +- script/sync-device_class.py | 7 +- 6 files changed, 398 insertions(+), 377 deletions(-) diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index b1292095d8..a2bc3abf64 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -17,28 +17,22 @@ then run this script with python3 and the files will be generated, they still need to be formatted """ -import re import os +import re +import sys +from abc import ABC, abstractmethod from pathlib import Path -from textwrap import dedent from subprocess import call +from textwrap import dedent # Generate with # protoc --python_out=script/api_protobuf -I esphome/components/api/ api_options.proto - import aioesphomeapi.api_options_pb2 as pb import google.protobuf.descriptor_pb2 as descriptor -file_header = "// This file was automatically generated with a tool.\n" -file_header += "// See scripts/api_protobuf/api_protobuf.py\n" - -cwd = Path(__file__).resolve().parent -root = cwd.parent.parent / "esphome" / "components" / "api" -prot = root / "api.protoc" -call(["protoc", "-o", str(prot), "-I", str(root), "api.proto"]) -content = prot.read_bytes() - -d = descriptor.FileDescriptorSet.FromString(content) +FILE_HEADER = """// This file was automatically generated with a tool. +// See scripts/api_protobuf/api_protobuf.py +""" def indent_list(text, padding=" "): @@ -64,7 +58,7 @@ def camel_to_snake(name): return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() -class TypeInfo: +class TypeInfo(ABC): def __init__(self, field): self._field = field @@ -186,10 +180,12 @@ class TypeInfo: def dump_content(self): o = f'out.append(" {self.name}: ");\n' o += self.dump(f"this->{self.field_name}") + "\n" - o += f'out.append("\\n");\n' + o += 'out.append("\\n");\n' return o - dump = None + @abstractmethod + def dump(self, name: str): + pass TYPE_INFO = {} @@ -212,7 +208,7 @@ class DoubleType(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%g", {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -225,7 +221,7 @@ class FloatType(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%g", {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -238,7 +234,7 @@ class Int64Type(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%lld", {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -251,7 +247,7 @@ class UInt64Type(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%llu", {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -264,7 +260,7 @@ class Int32Type(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%" PRId32, {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -277,7 +273,7 @@ class Fixed64Type(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%llu", {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -290,7 +286,7 @@ class Fixed32Type(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%" PRIu32, {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -372,7 +368,7 @@ class UInt32Type(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%" PRIu32, {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -406,7 +402,7 @@ class SFixed32Type(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%" PRId32, {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -419,7 +415,7 @@ class SFixed64Type(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%lld", {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -432,7 +428,7 @@ class SInt32Type(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%" PRId32, {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -445,7 +441,7 @@ class SInt64Type(TypeInfo): def dump(self, name): o = f'sprintf(buffer, "%lld", {name});\n' - o += f"out.append(buffer);" + o += "out.append(buffer);" return o @@ -527,7 +523,7 @@ class RepeatedTypeInfo(TypeInfo): def encode_content(self): o = f"for (auto {'' if self._ti_is_bool else '&'}it : this->{self.field_name}) {{\n" o += f" buffer.{self._ti.encode_func}({self.number}, it, true);\n" - o += f"}}" + o += "}" return o @property @@ -535,10 +531,13 @@ class RepeatedTypeInfo(TypeInfo): o = f'for (const auto {"" if self._ti_is_bool else "&"}it : this->{self.field_name}) {{\n' o += f' out.append(" {self.name}: ");\n' o += indent(self._ti.dump("it")) + "\n" - o += f' out.append("\\n");\n' - o += f"}}\n" + o += ' out.append("\\n");\n' + o += "}\n" return o + def dump(self, _: str): + pass + def build_enum_type(desc): name = desc.name @@ -547,17 +546,17 @@ def build_enum_type(desc): out += f" {v.name} = {v.number},\n" out += "};\n" - cpp = f"#ifdef HAS_PROTO_MESSAGE_DUMP\n" + cpp = "#ifdef HAS_PROTO_MESSAGE_DUMP\n" cpp += f"template<> const char *proto_enum_to_string(enums::{name} value) {{\n" - cpp += f" switch (value) {{\n" + cpp += " switch (value) {\n" for v in desc.value: cpp += f" case enums::{v.name}:\n" cpp += f' return "{v.name}";\n' - cpp += f" default:\n" - cpp += f' return "UNKNOWN";\n' - cpp += f" }}\n" - cpp += f"}}\n" - cpp += f"#endif\n" + cpp += " default:\n" + cpp += ' return "UNKNOWN";\n' + cpp += " }\n" + cpp += "}\n" + cpp += "#endif\n" return out, cpp @@ -652,10 +651,10 @@ def build_message_type(desc): o += f" {dump[0]} " else: o += "\n" - o += f" __attribute__((unused)) char buffer[64];\n" + o += " __attribute__((unused)) char buffer[64];\n" o += f' out.append("{desc.name} {{\\n");\n' o += indent("\n".join(dump)) + "\n" - o += f' out.append("}}");\n' + o += ' out.append("}");\n' else: o2 = f'out.append("{desc.name} {{}}");' if len(o) + len(o2) + 3 < 120: @@ -664,9 +663,9 @@ def build_message_type(desc): o += "\n" o += f" {o2}\n" o += "}\n" - cpp += f"#ifdef HAS_PROTO_MESSAGE_DUMP\n" + cpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" cpp += o - cpp += f"#endif\n" + cpp += "#endif\n" prot = "#ifdef HAS_PROTO_MESSAGE_DUMP\n" prot += "void dump_to(std::string &out) const override;\n" prot += "#endif\n" @@ -684,71 +683,12 @@ def build_message_type(desc): return out, cpp -file = d.file[0] -content = file_header -content += """\ -#pragma once - -#include "proto.h" - -namespace esphome { -namespace api { - -""" - -cpp = file_header -cpp += """\ -#include "api_pb2.h" -#include "esphome/core/log.h" - -#include - -namespace esphome { -namespace api { - -""" - -content += "namespace enums {\n\n" - -for enum in file.enum_type: - s, c = build_enum_type(enum) - content += s - cpp += c - -content += "\n} // namespace enums\n\n" - -mt = file.message_type - -for m in mt: - s, c = build_message_type(m) - content += s - cpp += c - -content += """\ - -} // namespace api -} // namespace esphome -""" -cpp += """\ - -} // namespace api -} // namespace esphome -""" - -with open(root / "api_pb2.h", "w") as f: - f.write(content) - -with open(root / "api_pb2.cpp", "w") as f: - f.write(cpp) - SOURCE_BOTH = 0 SOURCE_SERVER = 1 SOURCE_CLIENT = 2 RECEIVE_CASES = {} -class_name = "APIServerConnectionBase" - ifdefs = {} @@ -768,7 +708,6 @@ def build_service_message_type(mt): ifdef = get_opt(mt, pb.ifdef) log = get_opt(mt, pb.log, True) - nodelay = get_opt(mt, pb.no_delay, False) hout = "" cout = "" @@ -781,14 +720,14 @@ def build_service_message_type(mt): # Generate send func = f"send_{snake}" hout += f"bool {func}(const {mt.name} &msg);\n" - cout += f"bool {class_name}::{func}(const {mt.name} &msg) {{\n" + cout += f"bool APIServerConnectionBase::{func}(const {mt.name} &msg) {{\n" if log: - cout += f"#ifdef HAS_PROTO_MESSAGE_DUMP\n" + cout += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" cout += f' ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n' - cout += f"#endif\n" + cout += "#endif\n" # cout += f' this->set_nodelay({str(nodelay).lower()});\n' cout += f" return this->send_message_<{mt.name}>(msg, {id_});\n" - cout += f"}}\n" + cout += "}\n" if source in (SOURCE_BOTH, SOURCE_CLIENT): # Generate receive func = f"on_{snake}" @@ -797,169 +736,242 @@ def build_service_message_type(mt): if ifdef is not None: case += f"#ifdef {ifdef}\n" case += f"{mt.name} msg;\n" - case += f"msg.decode(msg_data, msg_size);\n" + case += "msg.decode(msg_data, msg_size);\n" if log: - case += f"#ifdef HAS_PROTO_MESSAGE_DUMP\n" + case += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" case += f'ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n' - case += f"#endif\n" + case += "#endif\n" case += f"this->{func}(msg);\n" if ifdef is not None: - case += f"#endif\n" + case += "#endif\n" case += "break;" RECEIVE_CASES[id_] = case if ifdef is not None: - hout += f"#endif\n" - cout += f"#endif\n" + hout += "#endif\n" + cout += "#endif\n" return hout, cout -hpp = file_header -hpp += """\ -#pragma once +def main(): + cwd = Path(__file__).resolve().parent + root = cwd.parent.parent / "esphome" / "components" / "api" + prot_file = root / "api.protoc" + call(["protoc", "-o", str(prot_file), "-I", str(root), "api.proto"]) + proto_content = prot_file.read_bytes() -#include "api_pb2.h" -#include "esphome/core/defines.h" + # pylint: disable-next=no-member + d = descriptor.FileDescriptorSet.FromString(proto_content) -namespace esphome { -namespace api { + file = d.file[0] + content = FILE_HEADER + content += """\ + #pragma once -""" + #include "proto.h" -cpp = file_header -cpp += """\ -#include "api_pb2_service.h" -#include "esphome/core/log.h" + namespace esphome { + namespace api { -namespace esphome { -namespace api { + """ -static const char *const TAG = "api.service"; + cpp = FILE_HEADER + cpp += """\ + #include "api_pb2.h" + #include "esphome/core/log.h" -""" + #include -hpp += f"class {class_name} : public ProtoService {{\n" -hpp += " public:\n" + namespace esphome { + namespace api { -for mt in file.message_type: - obj = build_service_message_type(mt) - if obj is None: - continue - hout, cout = obj - hpp += indent(hout) + "\n" - cpp += cout + """ -cases = list(RECEIVE_CASES.items()) -cases.sort() -hpp += " protected:\n" -hpp += f" bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;\n" -out = f"bool {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {{\n" -out += f" switch (msg_type) {{\n" -for i, case in cases: - c = f"case {i}: {{\n" - c += indent(case) + "\n" - c += f"}}" - out += indent(c, " ") + "\n" -out += " default:\n" -out += " return false;\n" -out += " }\n" -out += " return true;\n" -out += "}\n" -cpp += out -hpp += "};\n" + content += "namespace enums {\n\n" -serv = file.service[0] -class_name = "APIServerConnection" -hpp += "\n" -hpp += f"class {class_name} : public {class_name}Base {{\n" -hpp += " public:\n" -hpp_protected = "" -cpp += "\n" + for enum in file.enum_type: + s, c = build_enum_type(enum) + content += s + cpp += c -m = serv.method[0] -for m in serv.method: - func = m.name - inp = m.input_type[1:] - ret = m.output_type[1:] - is_void = ret == "void" - snake = camel_to_snake(inp) - on_func = f"on_{snake}" - needs_conn = get_opt(m, pb.needs_setup_connection, True) - needs_auth = get_opt(m, pb.needs_authentication, True) + content += "\n} // namespace enums\n\n" - ifdef = ifdefs.get(inp, None) + mt = file.message_type - if ifdef is not None: - hpp += f"#ifdef {ifdef}\n" - hpp_protected += f"#ifdef {ifdef}\n" - cpp += f"#ifdef {ifdef}\n" + for m in mt: + s, c = build_message_type(m) + content += s + cpp += c - hpp_protected += f" void {on_func}(const {inp} &msg) override;\n" - hpp += f" virtual {ret} {func}(const {inp} &msg) = 0;\n" - cpp += f"void {class_name}::{on_func}(const {inp} &msg) {{\n" - body = "" - if needs_conn: - body += "if (!this->is_connection_setup()) {\n" - body += " this->on_no_setup_connection();\n" - body += " return;\n" - body += "}\n" - if needs_auth: - body += "if (!this->is_authenticated()) {\n" - body += " this->on_unauthenticated_access();\n" - body += " return;\n" - body += "}\n" + content += """\ - if is_void: - body += f"this->{func}(msg);\n" - else: - body += f"{ret} ret = this->{func}(msg);\n" - ret_snake = camel_to_snake(ret) - body += f"if (!this->send_{ret_snake}(ret)) {{\n" - body += f" this->on_fatal_error();\n" - body += "}\n" - cpp += indent(body) + "\n" + "}\n" + } // namespace api + } // namespace esphome + """ + cpp += """\ - if ifdef is not None: - hpp += f"#endif\n" - hpp_protected += f"#endif\n" - cpp += f"#endif\n" + } // namespace api + } // namespace esphome + """ -hpp += " protected:\n" -hpp += hpp_protected -hpp += "};\n" + with open(root / "api_pb2.h", "w", encoding="utf-8") as f: + f.write(content) -hpp += """\ + with open(root / "api_pb2.cpp", "w", encoding="utf-8") as f: + f.write(cpp) -} // namespace api -} // namespace esphome -""" -cpp += """\ + hpp = FILE_HEADER + hpp += """\ + #pragma once -} // namespace api -} // namespace esphome -""" + #include "api_pb2.h" + #include "esphome/core/defines.h" -with open(root / "api_pb2_service.h", "w") as f: - f.write(hpp) + namespace esphome { + namespace api { -with open(root / "api_pb2_service.cpp", "w") as f: - f.write(cpp) + """ -prot.unlink() + cpp = FILE_HEADER + cpp += """\ + #include "api_pb2_service.h" + #include "esphome/core/log.h" -try: - import clang_format + namespace esphome { + namespace api { - def exec_clang_format(path): - clang_format_path = os.path.join( - os.path.dirname(clang_format.__file__), "data", "bin", "clang-format" - ) - call([clang_format_path, "-i", path]) + static const char *const TAG = "api.service"; - exec_clang_format(root / "api_pb2_service.h") - exec_clang_format(root / "api_pb2_service.cpp") - exec_clang_format(root / "api_pb2.h") - exec_clang_format(root / "api_pb2.cpp") -except ImportError: - pass + """ + + class_name = "APIServerConnectionBase" + + hpp += f"class {class_name} : public ProtoService {{\n" + hpp += " public:\n" + + for mt in file.message_type: + obj = build_service_message_type(mt) + if obj is None: + continue + hout, cout = obj + hpp += indent(hout) + "\n" + cpp += cout + + cases = list(RECEIVE_CASES.items()) + cases.sort() + hpp += " protected:\n" + hpp += " bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;\n" + out = f"bool {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {{\n" + out += " switch (msg_type) {\n" + for i, case in cases: + c = f"case {i}: {{\n" + c += indent(case) + "\n" + c += "}" + out += indent(c, " ") + "\n" + out += " default:\n" + out += " return false;\n" + out += " }\n" + out += " return true;\n" + out += "}\n" + cpp += out + hpp += "};\n" + + serv = file.service[0] + class_name = "APIServerConnection" + hpp += "\n" + hpp += f"class {class_name} : public {class_name}Base {{\n" + hpp += " public:\n" + hpp_protected = "" + cpp += "\n" + + m = serv.method[0] + for m in serv.method: + func = m.name + inp = m.input_type[1:] + ret = m.output_type[1:] + is_void = ret == "void" + snake = camel_to_snake(inp) + on_func = f"on_{snake}" + needs_conn = get_opt(m, pb.needs_setup_connection, True) + needs_auth = get_opt(m, pb.needs_authentication, True) + + ifdef = ifdefs.get(inp, None) + + if ifdef is not None: + hpp += f"#ifdef {ifdef}\n" + hpp_protected += f"#ifdef {ifdef}\n" + cpp += f"#ifdef {ifdef}\n" + + hpp_protected += f" void {on_func}(const {inp} &msg) override;\n" + hpp += f" virtual {ret} {func}(const {inp} &msg) = 0;\n" + cpp += f"void {class_name}::{on_func}(const {inp} &msg) {{\n" + body = "" + if needs_conn: + body += "if (!this->is_connection_setup()) {\n" + body += " this->on_no_setup_connection();\n" + body += " return;\n" + body += "}\n" + if needs_auth: + body += "if (!this->is_authenticated()) {\n" + body += " this->on_unauthenticated_access();\n" + body += " return;\n" + body += "}\n" + + if is_void: + body += f"this->{func}(msg);\n" + else: + body += f"{ret} ret = this->{func}(msg);\n" + ret_snake = camel_to_snake(ret) + body += f"if (!this->send_{ret_snake}(ret)) {{\n" + body += " this->on_fatal_error();\n" + body += "}\n" + cpp += indent(body) + "\n" + "}\n" + + if ifdef is not None: + hpp += "#endif\n" + hpp_protected += "#endif\n" + cpp += "#endif\n" + + hpp += " protected:\n" + hpp += hpp_protected + hpp += "};\n" + + hpp += """\ + + } // namespace api + } // namespace esphome + """ + cpp += """\ + + } // namespace api + } // namespace esphome + """ + + with open(root / "api_pb2_service.h", "w", encoding="utf-8") as f: + f.write(hpp) + + with open(root / "api_pb2_service.cpp", "w", encoding="utf-8") as f: + f.write(cpp) + + prot_file.unlink() + + try: + import clang_format + + def exec_clang_format(path): + clang_format_path = os.path.join( + os.path.dirname(clang_format.__file__), "data", "bin", "clang-format" + ) + call([clang_format_path, "-i", path]) + + exec_clang_format(root / "api_pb2_service.h") + exec_clang_format(root / "api_pb2_service.cpp") + exec_clang_format(root / "api_pb2.h") + exec_clang_format(root / "api_pb2.cpp") + except ImportError: + pass + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/script/build_language_schema.py b/script/build_language_schema.py index fb2010fe3e..fc6ccadc5f 100644 --- a/script/build_language_schema.py +++ b/script/build_language_schema.py @@ -61,6 +61,7 @@ solve_registry = [] def get_component_names(): + # pylint: disable-next=redefined-outer-name,reimported from esphome.loader import CORE_COMPONENTS_PATH component_names = ["esphome", "sensor", "esp32", "esp8266"] @@ -82,9 +83,12 @@ def load_components(): components[domain] = get_component(domain) +# pylint: disable=wrong-import-position from esphome.const import CONF_TYPE, KEY_CORE from esphome.core import CORE +# pylint: enable=wrong-import-position + CORE.data[KEY_CORE] = {} load_components() @@ -114,7 +118,7 @@ def write_file(name, obj): def delete_extra_files(keep_names): for d in os.listdir(args.output_path): - if d.endswith(".json") and not d[:-5] in keep_names: + if d.endswith(".json") and d[:-5] not in keep_names: os.remove(os.path.join(args.output_path, d)) print(f"Deleted {d}") @@ -552,11 +556,11 @@ def shrink(): s = f"{domain}.{schema_name}" if ( not s.endswith("." + S_CONFIG_SCHEMA) - and s not in referenced_schemas.keys() + and s not in referenced_schemas and not is_platform_schema(s) ): print(f"Removing {s}") - output[domain][S_SCHEMAS].pop(schema_name) + domain_schemas[S_SCHEMAS].pop(schema_name) def build_schema(): @@ -564,7 +568,7 @@ def build_schema(): # check esphome was not loaded globally (IDE auto imports) if len(ejs.extended_schemas) == 0: - raise Exception( + raise LookupError( "no data collected. Did you globally import an ESPHome component?" ) @@ -703,7 +707,7 @@ def convert(schema, config_var, path): if schema_instance is schema: assert S_CONFIG_VARS not in config_var assert S_EXTENDS not in config_var - if not S_TYPE in config_var: + if S_TYPE not in config_var: config_var[S_TYPE] = S_SCHEMA # assert config_var[S_TYPE] == S_SCHEMA @@ -765,9 +769,9 @@ def convert(schema, config_var, path): elif schema == automation.validate_potentially_and_condition: config_var[S_TYPE] = "registry" config_var["registry"] = "condition" - elif schema == cv.int_ or schema == cv.int_range: + elif schema in (cv.int_, cv.int_range): config_var[S_TYPE] = "integer" - elif schema == cv.string or schema == cv.string_strict or schema == cv.valid_name: + elif schema in (cv.string, cv.string_strict, cv.valid_name): config_var[S_TYPE] = "string" elif isinstance(schema, vol.Schema): @@ -779,6 +783,7 @@ def convert(schema, config_var, path): config_var |= pin_validators[repr_schema] config_var[S_TYPE] = "pin" + # pylint: disable-next=too-many-nested-blocks elif repr_schema in ejs.hidden_schemas: schema_type = ejs.hidden_schemas[repr_schema] @@ -869,7 +874,7 @@ def convert(schema, config_var, path): config_var["use_id_type"] = str(data.base) config_var[S_TYPE] = "use_id" else: - raise Exception("Unknown extracted schema type") + raise TypeError("Unknown extracted schema type") elif config_var.get("key") == "GeneratedID": if path.startswith("i2c/CONFIG_SCHEMA/") and path.endswith("/id"): config_var["id_type"] = { @@ -884,7 +889,7 @@ def convert(schema, config_var, path): elif path == "pins/esp32/val 1/id": config_var["id_type"] = "pin" else: - raise Exception("Cannot determine id_type for " + path) + raise TypeError("Cannot determine id_type for " + path) elif repr_schema in ejs.registry_schemas: solve_registry.append((ejs.registry_schemas[repr_schema], config_var)) @@ -948,11 +953,7 @@ def convert_keys(converted, schema, path): result["key"] = "GeneratedID" elif isinstance(k, cv.Required): result["key"] = "Required" - elif ( - isinstance(k, cv.Optional) - or isinstance(k, cv.Inclusive) - or isinstance(k, cv.Exclusive) - ): + elif isinstance(k, (cv.Optional, cv.Inclusive, cv.Exclusive)): result["key"] = "Optional" else: converted["key"] = "String" diff --git a/script/bump-version.py b/script/bump-version.py index 3e1e473c4b..a55bb65cd6 100755 --- a/script/bump-version.py +++ b/script/bump-version.py @@ -2,7 +2,6 @@ import argparse import re -import subprocess from dataclasses import dataclass import sys @@ -40,12 +39,12 @@ class Version: def sub(path, pattern, repl, expected_count=1): - with open(path) as fh: + with open(path, encoding="utf-8") as fh: content = fh.read() content, count = re.subn(pattern, repl, content, flags=re.MULTILINE) if expected_count is not None: assert count == expected_count, f"Pattern {pattern} replacement failed!" - with open(path, "w") as fh: + with open(path, "w", encoding="utf-8") as fh: fh.write(content) diff --git a/script/ci-custom.py b/script/ci-custom.py index cc9bdcadbb..41ce030d48 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -1,10 +1,8 @@ #!/usr/bin/env python3 -from helpers import styled, print_error_for_file, git_ls_files, filter_changed import argparse import codecs import collections -import colorama import fnmatch import functools import os.path @@ -12,6 +10,9 @@ import re import sys import time +import colorama +from helpers import filter_changed, git_ls_files, print_error_for_file, styled + sys.path.append(os.path.dirname(__file__)) @@ -30,31 +31,6 @@ def find_all(a_str, sub): column += len(sub) -colorama.init() - -parser = argparse.ArgumentParser() -parser.add_argument( - "files", nargs="*", default=[], help="files to be processed (regex on path)" -) -parser.add_argument( - "-c", "--changed", action="store_true", help="Only run on changed files" -) -parser.add_argument( - "--print-slowest", action="store_true", help="Print the slowest checks" -) -args = parser.parse_args() - -EXECUTABLE_BIT = git_ls_files() -files = list(EXECUTABLE_BIT.keys()) -# Match against re -file_name_re = re.compile("|".join(args.files)) -files = [p for p in files if file_name_re.search(p)] - -if args.changed: - files = filter_changed(files) - -files.sort() - file_types = ( ".h", ".c", @@ -86,6 +62,30 @@ ignore_types = (".ico", ".png", ".woff", ".woff2", "") LINT_FILE_CHECKS = [] LINT_CONTENT_CHECKS = [] LINT_POST_CHECKS = [] +EXECUTABLE_BIT = {} + +errors = collections.defaultdict(list) + + +def add_errors(fname, errs): + if not isinstance(errs, list): + errs = [errs] + for err in errs: + if err is None: + continue + try: + lineno, col, msg = err + except ValueError: + lineno = 1 + col = 1 + msg = err + if not isinstance(msg, str): + raise ValueError("Error is not instance of string!") + if not isinstance(lineno, int): + raise ValueError("Line number is not an int!") + if not isinstance(col, int): + raise ValueError("Column number is not an int!") + errors[fname].append((lineno, col, msg)) def run_check(lint_obj, fname, *args): @@ -155,7 +155,7 @@ def lint_re_check(regex, **kwargs): def decorator(func): @functools.wraps(func) def new_func(fname, content): - errors = [] + errs = [] for match in prog.finditer(content): if "NOLINT" in match.group(0): continue @@ -165,8 +165,8 @@ def lint_re_check(regex, **kwargs): err = func(fname, match) if err is None: continue - errors.append((lineno, col + 1, err)) - return errors + errs.append((lineno, col + 1, err)) + return errs return decor(new_func) @@ -182,13 +182,13 @@ def lint_content_find_check(find, only_first=False, **kwargs): find_ = find if callable(find): find_ = find(fname, content) - errors = [] + errs = [] for line, col in find_all(content, find_): err = func(fname) - errors.append((line + 1, col + 1, err)) + errs.append((line + 1, col + 1, err)) if only_first: break - return errors + return errs return decor(new_func) @@ -235,8 +235,8 @@ def lint_executable_bit(fname): ex = EXECUTABLE_BIT[fname] if ex != 100644: return ( - "File has invalid executable bit {}. If running from a windows machine please " - "see disabling executable bit in git.".format(ex) + f"File has invalid executable bit {ex}. If running from a windows machine please " + "see disabling executable bit in git." ) return None @@ -285,8 +285,8 @@ def lint_no_defines(fname, match): s = highlight(f"static const uint8_t {match.group(1)} = {match.group(2)};") return ( "#define macros for integer constants are not allowed, please use " - "{} style instead (replace uint8_t with the appropriate " - "datatype). See also Google style guide.".format(s) + f"{s} style instead (replace uint8_t with the appropriate " + "datatype). See also Google style guide." ) @@ -296,11 +296,11 @@ def lint_no_long_delays(fname, match): if duration_ms < 50: return None return ( - "{} - long calls to delay() are not allowed in ESPHome because everything executes " - "in one thread. Calling delay() will block the main thread and slow down ESPHome.\n" + f"{highlight(match.group(0).strip())} - long calls to delay() are not allowed " + "in ESPHome because everything executes in one thread. Calling delay() will " + "block the main thread and slow down ESPHome.\n" "If there's no way to work around the delay() and it doesn't execute often, please add " "a '// NOLINT' comment to the line." - "".format(highlight(match.group(0).strip())) ) @@ -311,28 +311,28 @@ def lint_const_ordered(fname, content): Reason: Otherwise people add it to the end, and then that results in merge conflicts. """ lines = content.splitlines() - errors = [] + errs = [] for start in ["CONF_", "ICON_", "UNIT_"]: matching = [ (i + 1, line) for i, line in enumerate(lines) if line.startswith(start) ] ordered = list(sorted(matching, key=lambda x: x[1].replace("_", " "))) ordered = [(mi, ol) for (mi, _), (_, ol) in zip(matching, ordered)] - for (mi, ml), (oi, ol) in zip(matching, ordered): - if ml == ol: + for (mi, mline), (_, ol) in zip(matching, ordered): + if mline == ol: continue - target = next(i for i, l in ordered if l == ml) - target_text = next(l for i, l in matching if target == i) - errors.append( + target = next(i for i, line in ordered if line == mline) + target_text = next(line for i, line in matching if target == i) + errs.append( ( mi, 1, - f"Constant {highlight(ml)} is not ordered, please make sure all " + f"Constant {highlight(mline)} is not ordered, please make sure all " f"constants are ordered. See line {mi} (should go to line {target}, " f"{target_text})", ) ) - return errors + return errs @lint_re_check(r'^\s*CONF_([A-Z_0-9a-z]+)\s+=\s+[\'"](.*?)[\'"]\s*?$', include=["*.py"]) @@ -344,15 +344,14 @@ def lint_conf_matches(fname, match): if const_norm == value_norm: return None return ( - "Constant {} does not match value {}! Please make sure the constant's name matches its " - "value!" - "".format(highlight("CONF_" + const), highlight(value)) + f"Constant {highlight('CONF_' + const)} does not match value {highlight(value)}! " + "Please make sure the constant's name matches its value!" ) CONF_RE = r'^(CONF_[a-zA-Z0-9_]+)\s*=\s*[\'"].*?[\'"]\s*?$' -with codecs.open("esphome/const.py", "r", encoding="utf-8") as f_handle: - constants_content = f_handle.read() +with codecs.open("esphome/const.py", "r", encoding="utf-8") as const_f_handle: + constants_content = const_f_handle.read() CONSTANTS = [m.group(1) for m in re.finditer(CONF_RE, constants_content, re.MULTILINE)] CONSTANTS_USES = collections.defaultdict(list) @@ -365,8 +364,8 @@ def lint_conf_from_const_py(fname, match): CONSTANTS_USES[name].append(fname) return None return ( - "Constant {} has already been defined in const.py - please import the constant from " - "const.py directly.".format(highlight(name)) + f"Constant {highlight(name)} has already been defined in const.py - " + "please import the constant from const.py directly." ) @@ -473,16 +472,15 @@ def lint_no_byte_datatype(fname, match): @lint_post_check def lint_constants_usage(): - errors = [] + errs = [] for constant, uses in CONSTANTS_USES.items(): if len(uses) < 4: continue - errors.append( - "Constant {} is defined in {} files. Please move all definitions of the " - "constant to const.py (Uses: {})" - "".format(highlight(constant), len(uses), ", ".join(uses)) + errs.append( + f"Constant {highlight(constant)} is defined in {len(uses)} files. Please move all definitions of the " + f"constant to const.py (Uses: {', '.join(uses)})" ) - return errors + return errs def relative_cpp_search_text(fname, content): @@ -553,7 +551,7 @@ def lint_namespace(fname, content): return ( "Invalid namespace found in C++ file. All integration C++ files should put all " "functions in a separate namespace that matches the integration's name. " - "Please make sure the file contains {}".format(highlight(search)) + f"Please make sure the file contains {highlight(search)}" ) @@ -639,66 +637,73 @@ def lint_log_in_header(fname): ) -errors = collections.defaultdict(list) +def main(): + colorama.init() + parser = argparse.ArgumentParser() + parser.add_argument( + "files", nargs="*", default=[], help="files to be processed (regex on path)" + ) + parser.add_argument( + "-c", "--changed", action="store_true", help="Only run on changed files" + ) + parser.add_argument( + "--print-slowest", action="store_true", help="Print the slowest checks" + ) + args = parser.parse_args() -def add_errors(fname, errs): - if not isinstance(errs, list): - errs = [errs] - for err in errs: - if err is None: + global EXECUTABLE_BIT + EXECUTABLE_BIT = git_ls_files() + files = list(EXECUTABLE_BIT.keys()) + # Match against re + file_name_re = re.compile("|".join(args.files)) + files = [p for p in files if file_name_re.search(p)] + + if args.changed: + files = filter_changed(files) + + files.sort() + + for fname in files: + _, ext = os.path.splitext(fname) + run_checks(LINT_FILE_CHECKS, fname, fname) + if ext in ignore_types: continue try: - lineno, col, msg = err - except ValueError: - lineno = 1 - col = 1 - msg = err - if not isinstance(msg, str): - raise ValueError("Error is not instance of string!") - if not isinstance(lineno, int): - raise ValueError("Line number is not an int!") - if not isinstance(col, int): - raise ValueError("Column number is not an int!") - errors[fname].append((lineno, col, msg)) + with codecs.open(fname, "r", encoding="utf-8") as f_handle: + content = f_handle.read() + except UnicodeDecodeError: + add_errors( + fname, + "File is not readable as UTF-8. Please set your editor to UTF-8 mode.", + ) + continue + run_checks(LINT_CONTENT_CHECKS, fname, fname, content) + run_checks(LINT_POST_CHECKS, "POST") -for fname in files: - _, ext = os.path.splitext(fname) - run_checks(LINT_FILE_CHECKS, fname, fname) - if ext in ignore_types: - continue - try: - with codecs.open(fname, "r", encoding="utf-8") as f_handle: - content = f_handle.read() - except UnicodeDecodeError: - add_errors( - fname, - "File is not readable as UTF-8. Please set your editor to UTF-8 mode.", + for f, errs in sorted(errors.items()): + bold = functools.partial(styled, colorama.Style.BRIGHT) + bold_red = functools.partial(styled, (colorama.Style.BRIGHT, colorama.Fore.RED)) + err_str = ( + f"{bold(f'{f}:{lineno}:{col}:')} {bold_red('lint:')} {msg}\n" + for lineno, col, msg in errs ) - continue - run_checks(LINT_CONTENT_CHECKS, fname, fname, content) + print_error_for_file(f, "\n".join(err_str)) -run_checks(LINT_POST_CHECKS, "POST") + if args.print_slowest: + lint_times = [] + for lint in LINT_FILE_CHECKS + LINT_CONTENT_CHECKS + LINT_POST_CHECKS: + durations = lint.get("durations", []) + lint_times.append((sum(durations), len(durations), lint["func"].__name__)) + lint_times.sort(key=lambda x: -x[0]) + for i in range(min(len(lint_times), 10)): + dur, invocations, name = lint_times[i] + print(f" - '{name}' took {dur:.2f}s total (ran on {invocations} files)") + print(f"Total time measured: {sum(x[0] for x in lint_times):.2f}s") -for f, errs in sorted(errors.items()): - bold = functools.partial(styled, colorama.Style.BRIGHT) - bold_red = functools.partial(styled, (colorama.Style.BRIGHT, colorama.Fore.RED)) - err_str = ( - f"{bold(f'{f}:{lineno}:{col}:')} {bold_red('lint:')} {msg}\n" - for lineno, col, msg in errs - ) - print_error_for_file(f, "\n".join(err_str)) + return len(errors) -if args.print_slowest: - lint_times = [] - for lint in LINT_FILE_CHECKS + LINT_CONTENT_CHECKS + LINT_POST_CHECKS: - durations = lint.get("durations", []) - lint_times.append((sum(durations), len(durations), lint["func"].__name__)) - lint_times.sort(key=lambda x: -x[0]) - for i in range(min(len(lint_times), 10)): - dur, invocations, name = lint_times[i] - print(f" - '{name}' took {dur:.2f}s total (ran on {invocations} files)") - print(f"Total time measured: {sum(x[0] for x in lint_times):.2f}s") -sys.exit(len(errors)) +if __name__ == "__main__": + sys.exit(main()) diff --git a/script/helpers.py b/script/helpers.py index c042362aeb..b1908e9875 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -1,10 +1,11 @@ -import colorama +import json import os.path import re import subprocess -import json from pathlib import Path +import colorama + root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, "..", ".."))) basepath = os.path.join(root_path, "esphome") temp_folder = os.path.join(root_path, ".temp") @@ -44,7 +45,7 @@ def build_all_include(): content = "\n".join(headers) p = Path(temp_header_file) p.parent.mkdir(exist_ok=True) - p.write_text(content) + p.write_text(content, encoding="utf-8") def walk_files(path): @@ -54,14 +55,14 @@ def walk_files(path): def get_output(*args): - proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - output, err = proc.communicate() + with subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc: + output, _ = proc.communicate() return output.decode("utf-8") def get_err(*args): - proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - output, err = proc.communicate() + with subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc: + _, err = proc.communicate() return err.decode("utf-8") @@ -78,7 +79,7 @@ def changed_files(): merge_base = splitlines_no_ends(get_output(*command))[0] break # pylint: disable=bare-except - except: + except: # noqa: E722 pass else: raise ValueError("Git not configured") @@ -103,7 +104,7 @@ def filter_changed(files): def filter_grep(files, value): matched = [] for file in files: - with open(file) as handle: + with open(file, encoding="utf-8") as handle: contents = handle.read() if value in contents: matched.append(file) @@ -114,8 +115,8 @@ def git_ls_files(patterns=None): command = ["git", "ls-files", "-s"] if patterns is not None: command.extend(patterns) - proc = subprocess.Popen(command, stdout=subprocess.PIPE) - output, err = proc.communicate() + with subprocess.Popen(command, stdout=subprocess.PIPE) as proc: + output, _ = proc.communicate() lines = [x.split() for x in output.decode("utf-8").splitlines()] return {s[3].strip(): int(s[0]) for s in lines} diff --git a/script/sync-device_class.py b/script/sync-device_class.py index ae6f4be0c8..8f91b97997 100755 --- a/script/sync-device_class.py +++ b/script/sync-device_class.py @@ -2,6 +2,7 @@ import re +# pylint: disable=import-error from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.button import ButtonDeviceClass from homeassistant.components.cover import CoverDeviceClass @@ -9,6 +10,8 @@ from homeassistant.components.number import NumberDeviceClass from homeassistant.components.sensor import SensorDeviceClass from homeassistant.components.switch import SwitchDeviceClass +# pylint: enable=import-error + BLOCKLIST = ( # requires special support on HA side "enum", @@ -25,10 +28,10 @@ DOMAINS = { def sub(path, pattern, repl): - with open(path) as handle: + with open(path, encoding="utf-8") as handle: content = handle.read() content = re.sub(pattern, repl, content, flags=re.MULTILINE) - with open(path, "w") as handle: + with open(path, "w", encoding="utf-8") as handle: handle.write(content) From 46255ad4dfcd00fff079192d197879f91fd7dd3b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 Dec 2023 21:35:31 -1000 Subject: [PATCH 085/193] Fix dashboard logs when api is disabled and using MQTT (#5992) --- esphome/dashboard/web_server.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index c4b84d3fe3..6a80865906 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -301,11 +301,16 @@ class EsphomePortCommandWebSocket(EsphomeCommandWebSocket): config_file = settings.rel_path(configuration) port = json_message["port"] if ( - port == "OTA" + port == "OTA" # pylint: disable=too-many-boolean-expressions and (mdns := dashboard.mdns_status) and (entry := entries.get(config_file)) + and entry.loaded_integrations + and "api" in entry.loaded_integrations and (address := await mdns.async_resolve_host(entry.name)) ): + # Use the IP address if available but only + # if the API is loaded and the device is online + # since MQTT logging will not work otherwise port = address return [ From 46c4c61b40114f3cce38842c2900e9f1af4c8245 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 22 Dec 2023 21:10:35 +1300 Subject: [PATCH 086/193] Fix broken configs with non-existent components (#5993) --- esphome/config.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/config.py b/esphome/config.py index e9433d537e..4aca0d6056 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -315,7 +315,11 @@ class LoadValidationStep(ConfigValidationStep): return result.add_output_path([self.domain], self.domain) component = get_component(self.domain) - if component.multi_conf_no_default and isinstance(self.conf, core.AutoLoad): + if ( + component is not None + and component.multi_conf_no_default + and isinstance(self.conf, core.AutoLoad) + ): self.conf = [] result[self.domain] = self.conf path = [self.domain] From a97fc4f75810776227f862c78476aca565f2d0c1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 22 Dec 2023 15:43:17 -1000 Subject: [PATCH 087/193] dashboard: Only ping when polling is active (#6001) fixes https://github.com/esphome/issues/issues/5257 --- esphome/dashboard/status/ping.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/dashboard/status/ping.py b/esphome/dashboard/status/ping.py index d8281d9de1..989cd1570f 100644 --- a/esphome/dashboard/status/ping.py +++ b/esphome/dashboard/status/ping.py @@ -31,6 +31,7 @@ class PingStatus: while not dashboard.stop_event.is_set(): # Only ping if the dashboard is open await dashboard.ping_request.wait() + dashboard.ping_request.clear() current_entries = dashboard.entries.async_all() to_ping: list[DashboardEntry] = [ entry for entry in current_entries if entry.address is not None From 8e674990b0c829ee130927f4af06b9f02ec60a63 Mon Sep 17 00:00:00 2001 From: Attila Farago Date: Sat, 23 Dec 2023 15:17:00 +0100 Subject: [PATCH 088/193] web_server support for home assistant like styling (#5854) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/web_server/web_server.cpp | 38 ++++++++++++-------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 0d72e274cd..54e9e6ebcc 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -397,19 +397,21 @@ void WebServer::handle_js_request(AsyncWebServerRequest *request) { #define set_json_id(root, obj, sensor, start_config) \ (root)["id"] = sensor; \ - if (((start_config) == DETAIL_ALL)) \ - (root)["name"] = (obj)->get_name(); + if (((start_config) == DETAIL_ALL)) { \ + (root)["name"] = (obj)->get_name(); \ + (root)["icon"] = (obj)->get_icon(); \ + (root)["entity_category"] = (obj)->get_entity_category(); \ + if ((obj)->is_disabled_by_default()) \ + (root)["is_disabled_by_default"] = (obj)->is_disabled_by_default(); \ + } #define set_json_value(root, obj, sensor, value, start_config) \ - set_json_id((root), (obj), sensor, start_config)(root)["value"] = value; - -#define set_json_state_value(root, obj, sensor, state, value, start_config) \ - set_json_value(root, obj, sensor, value, start_config)(root)["state"] = state; + set_json_id((root), (obj), sensor, start_config); \ + (root)["value"] = value; #define set_json_icon_state_value(root, obj, sensor, state, value, start_config) \ - set_json_value(root, obj, sensor, value, start_config)(root)["state"] = state; \ - if (((start_config) == DETAIL_ALL)) \ - (root)["icon"] = (obj)->get_icon(); + set_json_value(root, obj, sensor, value, start_config); \ + (root)["state"] = state; #ifdef USE_SENSOR void WebServer::on_sensor_update(sensor::Sensor *obj, float state) { @@ -436,6 +438,10 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail state += " " + obj->get_unit_of_measurement(); } set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config); + if (start_config == DETAIL_ALL) { + if (!obj->get_unit_of_measurement().empty()) + root["uom"] = obj->get_unit_of_measurement(); + } }); } #endif @@ -529,7 +535,8 @@ void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool s } std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) { return json::build_json([obj, value, start_config](JsonObject root) { - set_json_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config); + set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value, + start_config); }); } void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -548,7 +555,8 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con void WebServer::on_fan_update(fan::Fan *obj) { this->events_.send(this->fan_json(obj, DETAIL_STATE).c_str(), "state"); } std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) { return json::build_json([obj, start_config](JsonObject root) { - set_json_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state, start_config); + set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state, + start_config); const auto traits = obj->get_traits(); if (traits.supports_speed()) { root["speed_level"] = obj->speed; @@ -773,8 +781,8 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa } std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) { return json::build_json([obj, start_config](JsonObject root) { - set_json_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN", - obj->position, start_config); + set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN", + obj->position, start_config); root["current_operation"] = cover::cover_operation_to_str(obj->current_operation); if (obj->get_traits().get_supports_tilt()) @@ -824,6 +832,8 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail root["max_value"] = obj->traits.get_max_value(); root["step"] = 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(); } if (std::isnan(value)) { root["value"] = "\"NaN\""; @@ -930,7 +940,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM } std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) { return json::build_json([obj, value, start_config](JsonObject root) { - set_json_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config); + set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config); if (start_config == DETAIL_ALL) { JsonArray opt = root.createNestedArray("option"); for (auto &option : obj->traits.get_options()) { From 6583026e1473f4e5246f850b813d3999ade49f95 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Sun, 24 Dec 2023 14:54:53 +0100 Subject: [PATCH 089/193] tt21100: restore init read (#6008) --- esphome/components/tt21100/touchscreen/tt21100.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/tt21100/touchscreen/tt21100.cpp b/esphome/components/tt21100/touchscreen/tt21100.cpp index 6b5cba74cd..ba4b0ee02d 100644 --- a/esphome/components/tt21100/touchscreen/tt21100.cpp +++ b/esphome/components/tt21100/touchscreen/tt21100.cpp @@ -64,6 +64,9 @@ void TT21100Touchscreen::setup() { // Update display dimensions if they were updated during display setup this->x_raw_max_ = this->get_width_(); this->y_raw_max_ = this->get_height_(); + + // Trigger initial read to activate the interrupt + this->store_.touched = true; } void TT21100Touchscreen::update_touches() { From fe15d993f9b79a59531fb0cf46fe02be7514316e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 24 Dec 2023 03:56:31 -1000 Subject: [PATCH 090/193] dashboard: Fix file writes on Windows (#6013) --- esphome/dashboard/util/file.py | 10 +++++++++- tests/dashboard/util/test_file.py | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/esphome/dashboard/util/file.py b/esphome/dashboard/util/file.py index 5f3c5f5f1b..661d5f34cf 100644 --- a/esphome/dashboard/util/file.py +++ b/esphome/dashboard/util/file.py @@ -30,6 +30,7 @@ def write_file( """ tmp_filename = "" + missing_fchmod = False try: # Modern versions of Python tempfile create this file with mode 0o600 with tempfile.NamedTemporaryFile( @@ -38,8 +39,15 @@ def write_file( fdesc.write(utf8_data) tmp_filename = fdesc.name if not private: - os.fchmod(fdesc.fileno(), 0o644) + try: + os.fchmod(fdesc.fileno(), 0o644) + except AttributeError: + # os.fchmod is not available on Windows + missing_fchmod = True + os.replace(tmp_filename, filename) + if missing_fchmod: + os.chmod(filename, 0o644) finally: if os.path.exists(tmp_filename): try: diff --git a/tests/dashboard/util/test_file.py b/tests/dashboard/util/test_file.py index 89e6b97086..270ab565f1 100644 --- a/tests/dashboard/util/test_file.py +++ b/tests/dashboard/util/test_file.py @@ -13,7 +13,7 @@ def test_write_utf8_file(tmp_path: Path) -> None: assert tmp_path.joinpath("foo.txt").read_text() == "foo" with pytest.raises(OSError): - write_utf8_file(Path("/not-writable"), "bar") + write_utf8_file(Path("/dev/not-writable"), "bar") def test_write_file(tmp_path: Path) -> None: From de6fc6b1dd2ea4edf077eb5e478d69946ea513e3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 24 Dec 2023 03:57:15 -1000 Subject: [PATCH 091/193] Fix docker builds (#6012) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 7c162fc316..468124e3ed 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -34,7 +34,7 @@ RUN \ python3-wheel=0.38.4-2 \ iputils-ping=3:20221126-1 \ git=1:2.39.2-1.1 \ - curl=7.88.1-10+deb12u4 \ + curl=7.88.1-10+deb12u5 \ openssh-client=1:9.2p1-2+deb12u1 \ python3-cffi=1.15.1-5 \ libcairo2=1.16.0-7 \ From 46fc37b6910491cefb3bb76c438db1ee05643c76 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Sun, 24 Dec 2023 14:58:27 +0100 Subject: [PATCH 092/193] Display: fix class inherence in Python script (#6009) --- esphome/components/display/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/display/__init__.py b/esphome/components/display/__init__.py index 9f4e922a37..91f10c5458 100644 --- a/esphome/components/display/__init__.py +++ b/esphome/components/display/__init__.py @@ -18,8 +18,8 @@ from esphome.core import coroutine_with_priority IS_PLATFORM_COMPONENT = True display_ns = cg.esphome_ns.namespace("display") -Display = display_ns.class_("Display") -DisplayBuffer = display_ns.class_("DisplayBuffer") +Display = display_ns.class_("Display", cg.PollingComponent) +DisplayBuffer = display_ns.class_("DisplayBuffer", Display) DisplayPage = display_ns.class_("DisplayPage") DisplayPagePtr = DisplayPage.operator("ptr") DisplayRef = Display.operator("ref") From 93ac765425e3e57b5ee14df804c6ce24d5331f5a Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 24 Dec 2023 20:16:53 +0100 Subject: [PATCH 093/193] [Touchscreen] Add expire of touch record. (#5986) * Add expire of touch record. * Implement suggested changes. * Alternative implementation to detect touch release. * add `cancel_timeout`. * Add touch timeout as configurable element. --------- Co-authored-by: Your Name --- .../lilygo_t5_47/touchscreen/__init__.py | 2 +- esphome/components/touchscreen/__init__.py | 42 ++++++++++++------- .../components/touchscreen/touchscreen.cpp | 8 ++++ esphome/components/touchscreen/touchscreen.h | 2 + 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/esphome/components/lilygo_t5_47/touchscreen/__init__.py b/esphome/components/lilygo_t5_47/touchscreen/__init__.py index 01b03c807f..17f7262785 100644 --- a/esphome/components/lilygo_t5_47/touchscreen/__init__.py +++ b/esphome/components/lilygo_t5_47/touchscreen/__init__.py @@ -18,7 +18,7 @@ LilygoT547Touchscreen = lilygo_t5_47_ns.class_( CONF_LILYGO_T5_47_TOUCHSCREEN_ID = "lilygo_t5_47_touchscreen_id" -CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend( +CONFIG_SCHEMA = touchscreen.touchscreen_schema("250ms").extend( cv.Schema( { cv.GenerateID(): cv.declare_id(LilygoT547Touchscreen), diff --git a/esphome/components/touchscreen/__init__.py b/esphome/components/touchscreen/__init__.py index bc09c6364d..99aee5c9fb 100644 --- a/esphome/components/touchscreen/__init__.py +++ b/esphome/components/touchscreen/__init__.py @@ -24,6 +24,7 @@ CONF_DISPLAY = "display" CONF_TOUCHSCREEN_ID = "touchscreen_id" CONF_REPORT_INTERVAL = "report_interval" # not used yet: CONF_ON_UPDATE = "on_update" +CONF_TOUCH_TIMEOUT = "touch_timeout" CONF_MIRROR_X = "mirror_x" CONF_MIRROR_Y = "mirror_y" @@ -31,21 +32,29 @@ CONF_SWAP_XY = "swap_xy" CONF_TRANSFORM = "transform" -TOUCHSCREEN_SCHEMA = cv.Schema( - { - cv.GenerateID(CONF_DISPLAY): cv.use_id(display.Display), - cv.Optional(CONF_TRANSFORM): cv.Schema( - { - cv.Optional(CONF_SWAP_XY, default=False): cv.boolean, - cv.Optional(CONF_MIRROR_X, default=False): cv.boolean, - cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean, - } - ), - cv.Optional(CONF_ON_TOUCH): automation.validate_automation(single=True), - cv.Optional(CONF_ON_UPDATE): automation.validate_automation(single=True), - cv.Optional(CONF_ON_RELEASE): automation.validate_automation(single=True), - } -).extend(cv.polling_component_schema("50ms")) +def touchscreen_schema(default_touch_timeout): + return cv.Schema( + { + cv.GenerateID(CONF_DISPLAY): cv.use_id(display.Display), + cv.Optional(CONF_TRANSFORM): cv.Schema( + { + cv.Optional(CONF_SWAP_XY, default=False): cv.boolean, + cv.Optional(CONF_MIRROR_X, default=False): cv.boolean, + cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean, + } + ), + cv.Optional(CONF_TOUCH_TIMEOUT, default=default_touch_timeout): cv.All( + cv.positive_time_period_milliseconds, + cv.Range(max=cv.TimePeriod(milliseconds=65535)), + ), + cv.Optional(CONF_ON_TOUCH): automation.validate_automation(single=True), + cv.Optional(CONF_ON_UPDATE): automation.validate_automation(single=True), + cv.Optional(CONF_ON_RELEASE): automation.validate_automation(single=True), + } + ).extend(cv.polling_component_schema("50ms")) + + +TOUCHSCREEN_SCHEMA = touchscreen_schema(cv.UNDEFINED) async def register_touchscreen(var, config): @@ -54,6 +63,9 @@ async def register_touchscreen(var, config): disp = await cg.get_variable(config[CONF_DISPLAY]) cg.add(var.set_display(disp)) + if CONF_TOUCH_TIMEOUT in config: + cg.add(var.set_touch_timeout(config[CONF_TOUCH_TIMEOUT])) + if CONF_TRANSFORM in config: transform = config[CONF_TRANSFORM] cg.add(var.set_swap_xy(transform[CONF_SWAP_XY])) diff --git a/esphome/components/touchscreen/touchscreen.cpp b/esphome/components/touchscreen/touchscreen.cpp index f095c2af8c..18a4230197 100644 --- a/esphome/components/touchscreen/touchscreen.cpp +++ b/esphome/components/touchscreen/touchscreen.cpp @@ -47,6 +47,11 @@ void Touchscreen::loop() { } else { this->store_.touched = false; this->defer([this]() { this->send_touches_(); }); + if (this->touch_timeout_ > 0) { + // Simulate a touch after touch_timeout_> ms. This will reset any existing timeout operation. + // This is to detect touch release. + this->set_timeout(TAG, this->touch_timeout_, [this]() { this->store_.touched = true; }); + } } } } @@ -90,6 +95,9 @@ void Touchscreen::add_raw_touch_position_(uint8_t id, int16_t x_raw, int16_t y_r void Touchscreen::send_touches_() { if (!this->is_touched_) { + if (this->touch_timeout_ > 0) { + this->cancel_timeout(TAG); + } this->release_trigger_.trigger(); for (auto *listener : this->touch_listeners_) listener->release(); diff --git a/esphome/components/touchscreen/touchscreen.h b/esphome/components/touchscreen/touchscreen.h index 74747c589c..06aff68f07 100644 --- a/esphome/components/touchscreen/touchscreen.h +++ b/esphome/components/touchscreen/touchscreen.h @@ -46,6 +46,7 @@ class Touchscreen : public PollingComponent { void set_display(display::Display *display) { this->display_ = display; } display::Display *get_display() const { return this->display_; } + void set_touch_timeout(uint16_t val) { this->touch_timeout_ = val; } void set_mirror_x(bool invert_x) { this->invert_x_ = invert_x; } void set_mirror_y(bool invert_y) { this->invert_y_ = invert_y; } void set_swap_xy(bool swap) { this->swap_x_y_ = swap; } @@ -100,6 +101,7 @@ class Touchscreen : public PollingComponent { display::Display *display_{nullptr}; int16_t x_raw_min_{0}, x_raw_max_{0}, y_raw_min_{0}, y_raw_max_{0}; + uint16_t touch_timeout_{0}; bool invert_x_{false}, invert_y_{false}, swap_x_y_{false}; Trigger touch_trigger_; From 3be97868fcfa0a2be59ffb5a31dd7f4961004620 Mon Sep 17 00:00:00 2001 From: Anton Viktorov Date: Wed, 27 Dec 2023 02:01:15 +0100 Subject: [PATCH 094/193] Support for ST7567 display 128x64 (I2C, SPI) (#5952) --- CODEOWNERS | 3 + esphome/components/st7567_base/__init__.py | 55 +++++++ .../components/st7567_base/st7567_base.cpp | 152 ++++++++++++++++++ esphome/components/st7567_base/st7567_base.h | 100 ++++++++++++ esphome/components/st7567_i2c/__init__.py | 1 + esphome/components/st7567_i2c/display.py | 29 ++++ esphome/components/st7567_i2c/st7567_i2c.cpp | 60 +++++++ esphome/components/st7567_i2c/st7567_i2c.h | 23 +++ esphome/components/st7567_spi/__init__.py | 1 + esphome/components/st7567_spi/display.py | 34 ++++ esphome/components/st7567_spi/st7567_spi.cpp | 66 ++++++++ esphome/components/st7567_spi/st7567_spi.h | 29 ++++ tests/test1.yaml | 19 +++ 13 files changed, 572 insertions(+) create mode 100644 esphome/components/st7567_base/__init__.py create mode 100644 esphome/components/st7567_base/st7567_base.cpp create mode 100644 esphome/components/st7567_base/st7567_base.h create mode 100644 esphome/components/st7567_i2c/__init__.py create mode 100644 esphome/components/st7567_i2c/display.py create mode 100644 esphome/components/st7567_i2c/st7567_i2c.cpp create mode 100644 esphome/components/st7567_i2c/st7567_i2c.h create mode 100644 esphome/components/st7567_spi/__init__.py create mode 100644 esphome/components/st7567_spi/display.py create mode 100644 esphome/components/st7567_spi/st7567_spi.cpp create mode 100644 esphome/components/st7567_spi/st7567_spi.h diff --git a/CODEOWNERS b/CODEOWNERS index b8c870f20f..dff5d8beb5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -316,6 +316,9 @@ esphome/components/ssd1331_base/* @kbx81 esphome/components/ssd1331_spi/* @kbx81 esphome/components/ssd1351_base/* @kbx81 esphome/components/ssd1351_spi/* @kbx81 +esphome/components/st7567_base/* @latonita +esphome/components/st7567_i2c/* @latonita +esphome/components/st7567_spi/* @latonita esphome/components/st7735/* @SenexCrenshaw esphome/components/st7789v/* @kbx81 esphome/components/st7920/* @marsjan155 diff --git a/esphome/components/st7567_base/__init__.py b/esphome/components/st7567_base/__init__.py new file mode 100644 index 0000000000..462d7be6a1 --- /dev/null +++ b/esphome/components/st7567_base/__init__.py @@ -0,0 +1,55 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import display +from esphome.const import ( + CONF_LAMBDA, + CONF_RESET_PIN, +) + +CODEOWNERS = ["@latonita"] + +st7567_base_ns = cg.esphome_ns.namespace("st7567_base") +ST7567 = st7567_base_ns.class_("ST7567", cg.PollingComponent, display.DisplayBuffer) +ST7567Model = st7567_base_ns.enum("ST7567Model") + +# todo in future: reuse following constants from const.py when they are released +CONF_INVERT_COLORS = "invert_colors" +CONF_TRANSFORM = "transform" +CONF_MIRROR_X = "mirror_x" +CONF_MIRROR_Y = "mirror_y" + + +ST7567_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + { + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_INVERT_COLORS, default=False): cv.boolean, + cv.Optional(CONF_TRANSFORM): cv.Schema( + { + cv.Optional(CONF_MIRROR_X, default=False): cv.boolean, + cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean, + } + ), + } +).extend(cv.polling_component_schema("1s")) + + +async def setup_st7567(var, config): + await display.register_display(var, config) + + if CONF_RESET_PIN in config: + reset = await cg.gpio_pin_expression(config[CONF_RESET_PIN]) + cg.add(var.set_reset_pin(reset)) + + cg.add(var.init_invert_colors(config[CONF_INVERT_COLORS])) + + if CONF_TRANSFORM in config: + transform = config[CONF_TRANSFORM] + cg.add(var.init_mirror_x(transform[CONF_MIRROR_X])) + cg.add(var.init_mirror_y(transform[CONF_MIRROR_Y])) + + if CONF_LAMBDA in config: + lambda_ = await cg.process_lambda( + config[CONF_LAMBDA], [(display.DisplayRef, "it")], return_type=cg.void + ) + cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/st7567_base/st7567_base.cpp b/esphome/components/st7567_base/st7567_base.cpp new file mode 100644 index 0000000000..b22a7d7fd5 --- /dev/null +++ b/esphome/components/st7567_base/st7567_base.cpp @@ -0,0 +1,152 @@ +#include "st7567_base.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace st7567_base { + +static const char *const TAG = "st7567"; + +void ST7567::setup() { + this->init_internal_(this->get_buffer_length_()); + this->display_init_(); +} + +void ST7567::display_init_() { + ESP_LOGD(TAG, "Initializing ST7567 display..."); + this->display_init_registers_(); + this->clear(); + this->write_display_data(); + this->command(ST7567_DISPLAY_ON); +} + +void ST7567::display_init_registers_() { + this->command(ST7567_BIAS_9); + this->command(this->mirror_x_ ? ST7567_SEG_REVERSE : ST7567_SEG_NORMAL); + this->command(this->mirror_y_ ? ST7567_COM_NORMAL : ST7567_COM_REMAP); + this->command(ST7567_POWER_CTL | 0x4); + this->command(ST7567_POWER_CTL | 0x6); + this->command(ST7567_POWER_CTL | 0x7); + + this->set_brightness(this->brightness_); + this->set_contrast(this->contrast_); + + this->command(ST7567_INVERT_OFF | this->invert_colors_); + + this->command(ST7567_BOOSTER_ON); + this->command(ST7567_REGULATOR_ON); + this->command(ST7567_POWER_ON); + + this->command(ST7567_SCAN_START_LINE); + this->command(ST7567_PIXELS_NORMAL | this->all_pixels_on_); +} + +void ST7567::display_sw_refresh_() { + ESP_LOGD(TAG, "Performing refresh sequence..."); + this->command(ST7567_SW_REFRESH); + this->display_init_registers_(); +} + +void ST7567::request_refresh() { + // as per datasheet: It is recommended to use the refresh sequence regularly in a specified interval. + this->refresh_requested_ = true; +} + +void ST7567::update() { + this->do_update_(); + if (this->refresh_requested_) { + this->refresh_requested_ = false; + this->display_sw_refresh_(); + } + this->write_display_data(); +} + +void ST7567::set_all_pixels_on(bool enable) { + this->all_pixels_on_ = enable; + this->command(ST7567_PIXELS_NORMAL | this->all_pixels_on_); +} + +void ST7567::set_invert_colors(bool invert_colors) { + this->invert_colors_ = invert_colors; + this->command(ST7567_INVERT_OFF | this->invert_colors_); +} + +void ST7567::set_contrast(uint8_t val) { + this->contrast_ = val & 0b111111; + // 0..63, 26 is normal + + // two byte command + // first byte 0x81 + // second byte 0-63 + + this->command(ST7567_SET_EV_CMD); + this->command(this->contrast_); +} + +void ST7567::set_brightness(uint8_t val) { + this->brightness_ = val & 0b111; + // 0..7, 5 normal + + //********Adjust display brightness******** + // 0x20-0x27 is the internal Rb/Ra resistance + // adjustment setting of V5 voltage RR=4.5V + + this->command(ST7567_RESISTOR_RATIO | this->brightness_); +} + +bool ST7567::is_on() { return this->is_on_; } + +void ST7567::turn_on() { + this->command(ST7567_DISPLAY_ON); + this->is_on_ = true; +} + +void ST7567::turn_off() { + this->command(ST7567_DISPLAY_OFF); + this->is_on_ = false; +} + +void ST7567::set_scroll(uint8_t line) { this->start_line_ = line % this->get_height_internal(); } + +int ST7567::get_width_internal() { return 128; } + +int ST7567::get_height_internal() { return 64; } + +// 128x64, but memory size 132x64, line starts from 0, but if mirrored then it starts from 131, not 127 +size_t ST7567::get_buffer_length_() { + return size_t(this->get_width_internal() + 4) * size_t(this->get_height_internal()) / 8u; +} + +void HOT ST7567::draw_absolute_pixel_internal(int x, int y, Color color) { + if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) { + return; + } + + uint16_t pos = x + (y / 8) * this->get_width_internal(); + uint8_t subpos = y & 0x07; + if (color.is_on()) { + this->buffer_[pos] |= (1 << subpos); + } else { + this->buffer_[pos] &= ~(1 << subpos); + } +} + +void ST7567::fill(Color color) { memset(buffer_, color.is_on() ? 0xFF : 0x00, this->get_buffer_length_()); } + +void ST7567::init_reset_() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); + this->reset_pin_->digital_write(true); + delay(1); + // Trigger Reset + this->reset_pin_->digital_write(false); + delay(10); + // Wake up + this->reset_pin_->digital_write(true); + } +} + +const char *ST7567::model_str_() { return "ST7567 128x64"; } + +} // namespace st7567_base +} // namespace esphome diff --git a/esphome/components/st7567_base/st7567_base.h b/esphome/components/st7567_base/st7567_base.h new file mode 100644 index 0000000000..e3053673d1 --- /dev/null +++ b/esphome/components/st7567_base/st7567_base.h @@ -0,0 +1,100 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/components/display/display_buffer.h" + +namespace esphome { +namespace st7567_base { + +static const uint8_t ST7567_BOOSTER_ON = 0x2C; // internal power supply on +static const uint8_t ST7567_REGULATOR_ON = 0x2E; // internal power supply on +static const uint8_t ST7567_POWER_ON = 0x2F; // internal power supply on + +static const uint8_t ST7567_DISPLAY_ON = 0xAF; // Display ON. Normal Display Mode. +static const uint8_t ST7567_DISPLAY_OFF = 0xAE; // Display OFF. All SEGs/COMs output with VSS +static const uint8_t ST7567_SET_START_LINE = 0x40; +static const uint8_t ST7567_POWER_CTL = 0x28; +static const uint8_t ST7567_SEG_NORMAL = 0xA0; // +static const uint8_t ST7567_SEG_REVERSE = 0xA1; // mirror X axis (horizontal) +static const uint8_t ST7567_COM_NORMAL = 0xC0; // +static const uint8_t ST7567_COM_REMAP = 0xC8; // mirror Y axis (vertical) +static const uint8_t ST7567_PIXELS_NORMAL = 0xA4; // display ram content +static const uint8_t ST7567_PIXELS_ALL_ON = 0xA5; // all pixels on +static const uint8_t ST7567_INVERT_OFF = 0xA6; // normal pixels +static const uint8_t ST7567_INVERT_ON = 0xA7; // inverted pixels +static const uint8_t ST7567_SCAN_START_LINE = 0x40; // scrolling = 0x40 + (0..63) +static const uint8_t ST7567_COL_ADDR_H = 0x10; // x pos (0..95) 4 MSB +static const uint8_t ST7567_COL_ADDR_L = 0x00; // x pos (0..95) 4 LSB +static const uint8_t ST7567_PAGE_ADDR = 0xB0; // y pos, 8.5 rows (0..8) +static const uint8_t ST7567_BIAS_9 = 0xA2; +static const uint8_t ST7567_CONTRAST = 0x80; // 0x80 + (0..31) +static const uint8_t ST7567_SET_EV_CMD = 0x81; +static const uint8_t ST7567_SET_EV_PARAM = 0x00; +static const uint8_t ST7567_RESISTOR_RATIO = 0x20; +static const uint8_t ST7567_SW_REFRESH = 0xE2; + +class ST7567 : public display::DisplayBuffer { + public: + void setup() override; + + void update() override; + + void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; } + void init_mirror_x(bool mirror_x) { this->mirror_x_ = mirror_x; } + void init_mirror_y(bool mirror_y) { this->mirror_y_ = mirror_y; } + void init_invert_colors(bool invert_colors) { this->invert_colors_ = invert_colors; } + + void set_invert_colors(bool invert_colors); // inversion of screen colors + void set_contrast(uint8_t val); // 0..63, 27-30 normal + void set_brightness(uint8_t val); // 0..7, 5 normal + void set_all_pixels_on(bool enable); // turn on all pixels, this doesn't affect RAM + void set_scroll(uint8_t line); // set display start line: for screen scrolling w/o affecting RAM + + bool is_on(); + void turn_on(); + void turn_off(); + + void request_refresh(); // from datasheet: It is recommended to use the refresh sequence regularly in a specified + // interval. + + float get_setup_priority() const override { return setup_priority::PROCESSOR; } + void fill(Color color) override; + + display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; } + + protected: + virtual void command(uint8_t value) = 0; + virtual void write_display_data() = 0; + + void init_reset_(); + void display_init_(); + void display_init_registers_(); + void display_sw_refresh_(); + + void draw_absolute_pixel_internal(int x, int y, Color color) override; + + int get_height_internal() override; + int get_width_internal() override; + size_t get_buffer_length_(); + + int get_offset_x_() { return mirror_x_ ? 4 : 0; }; + + const char *model_str_(); + + GPIOPin *reset_pin_{nullptr}; + bool is_on_{false}; + // float contrast_{1.0}; + // float brightness_{1.0}; + uint8_t contrast_{27}; + uint8_t brightness_{5}; + bool mirror_x_{true}; + bool mirror_y_{true}; + bool invert_colors_{false}; + bool all_pixels_on_{false}; + uint8_t start_line_{0}; + bool refresh_requested_{false}; +}; + +} // namespace st7567_base +} // namespace esphome diff --git a/esphome/components/st7567_i2c/__init__.py b/esphome/components/st7567_i2c/__init__.py new file mode 100644 index 0000000000..dd06cfffea --- /dev/null +++ b/esphome/components/st7567_i2c/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@latonita"] diff --git a/esphome/components/st7567_i2c/display.py b/esphome/components/st7567_i2c/display.py new file mode 100644 index 0000000000..fa92d652d7 --- /dev/null +++ b/esphome/components/st7567_i2c/display.py @@ -0,0 +1,29 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import st7567_base, i2c +from esphome.const import CONF_ID, CONF_LAMBDA, CONF_PAGES + +CODEOWNERS = ["@latonita"] + +AUTO_LOAD = ["st7567_base"] +DEPENDENCIES = ["i2c"] + +st7567_i2c = cg.esphome_ns.namespace("st7567_i2c") +I2CST7567 = st7567_i2c.class_("I2CST7567", st7567_base.ST7567, i2c.I2CDevice) + +CONFIG_SCHEMA = cv.All( + st7567_base.ST7567_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(I2CST7567), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x3F)), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await st7567_base.setup_st7567(var, config) + await i2c.register_i2c_device(var, config) diff --git a/esphome/components/st7567_i2c/st7567_i2c.cpp b/esphome/components/st7567_i2c/st7567_i2c.cpp new file mode 100644 index 0000000000..05173d1be5 --- /dev/null +++ b/esphome/components/st7567_i2c/st7567_i2c.cpp @@ -0,0 +1,60 @@ +#include "st7567_i2c.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace st7567_i2c { + +static const char *const TAG = "st7567_i2c"; + +void I2CST7567::setup() { + ESP_LOGCONFIG(TAG, "Setting up I2C ST7567 display..."); + this->init_reset_(); + + auto err = this->write(nullptr, 0); + if (err != i2c::ERROR_OK) { + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } + ST7567::setup(); +} + +void I2CST7567::dump_config() { + LOG_DISPLAY("", "I2CST7567", this); + LOG_I2C_DEVICE(this); + ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + ESP_LOGCONFIG(TAG, " Mirror X: %s", YESNO(this->mirror_x_)); + ESP_LOGCONFIG(TAG, " Mirror Y: %s", YESNO(this->mirror_y_)); + ESP_LOGCONFIG(TAG, " Invert Colors: %s", YESNO(this->invert_colors_)); + LOG_UPDATE_INTERVAL(this); + + if (this->error_code_ == COMMUNICATION_FAILED) { + ESP_LOGE(TAG, "Communication with I2C ST7567 failed!"); + } +} + +void I2CST7567::command(uint8_t value) { this->write_byte(0x00, value); } + +void HOT I2CST7567::write_display_data() { + // ST7567A has built-in RAM with 132x65 bit capacity which stores the display data. + // but only first 128 pixels from each line are shown on screen + // if screen got flipped horizontally then it shows last 128 pixels, + // so we need to write x coordinate starting from column 4, not column 0 + this->command(esphome::st7567_base::ST7567_SET_START_LINE + this->start_line_); + for (uint8_t y = 0; y < (uint8_t) this->get_height_internal() / 8; y++) { + this->command(esphome::st7567_base::ST7567_PAGE_ADDR + y); // Set Page + this->command(esphome::st7567_base::ST7567_COL_ADDR_H); // Set MSB Column address + this->command(esphome::st7567_base::ST7567_COL_ADDR_L + this->get_offset_x_()); // Set LSB Column address + + static const size_t BLOCK_SIZE = 64; + for (uint8_t x = 0; x < (uint8_t) this->get_width_internal(); x += BLOCK_SIZE) { + this->write_register(esphome::st7567_base::ST7567_SET_START_LINE, &buffer_[y * this->get_width_internal() + x], + this->get_width_internal() - x > BLOCK_SIZE ? BLOCK_SIZE : this->get_width_internal() - x, + true); + } + } +} + +} // namespace st7567_i2c +} // namespace esphome diff --git a/esphome/components/st7567_i2c/st7567_i2c.h b/esphome/components/st7567_i2c/st7567_i2c.h new file mode 100644 index 0000000000..f760a54945 --- /dev/null +++ b/esphome/components/st7567_i2c/st7567_i2c.h @@ -0,0 +1,23 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/st7567_base/st7567_base.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace st7567_i2c { + +class I2CST7567 : public st7567_base::ST7567, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + + protected: + void command(uint8_t value) override; + void write_display_data() override; + + enum ErrorCode { NONE = 0, COMMUNICATION_FAILED } error_code_{NONE}; +}; + +} // namespace st7567_i2c +} // namespace esphome diff --git a/esphome/components/st7567_spi/__init__.py b/esphome/components/st7567_spi/__init__.py new file mode 100644 index 0000000000..dd06cfffea --- /dev/null +++ b/esphome/components/st7567_spi/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@latonita"] diff --git a/esphome/components/st7567_spi/display.py b/esphome/components/st7567_spi/display.py new file mode 100644 index 0000000000..aabe02a2d8 --- /dev/null +++ b/esphome/components/st7567_spi/display.py @@ -0,0 +1,34 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import spi, st7567_base +from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES + +CODEOWNERS = ["@latonita"] + +AUTO_LOAD = ["st7567_base"] +DEPENDENCIES = ["spi"] + +st7567_spi = cg.esphome_ns.namespace("st7567_spi") +SPIST7567 = st7567_spi.class_("SPIST7567", st7567_base.ST7567, spi.SPIDevice) + +CONFIG_SCHEMA = cv.All( + st7567_base.ST7567_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SPIST7567), + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema()), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await st7567_base.setup_st7567(var, config) + await spi.register_spi_device(var, config) + + dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) + cg.add(var.set_dc_pin(dc)) diff --git a/esphome/components/st7567_spi/st7567_spi.cpp b/esphome/components/st7567_spi/st7567_spi.cpp new file mode 100644 index 0000000000..25698d0dd0 --- /dev/null +++ b/esphome/components/st7567_spi/st7567_spi.cpp @@ -0,0 +1,66 @@ +#include "st7567_spi.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace st7567_spi { + +static const char *const TAG = "st7567_spi"; + +void SPIST7567::setup() { + ESP_LOGCONFIG(TAG, "Setting up SPI ST7567 display..."); + this->spi_setup(); + this->dc_pin_->setup(); + if (this->cs_) + this->cs_->setup(); + + this->init_reset_(); + ST7567::setup(); +} + +void SPIST7567::dump_config() { + LOG_DISPLAY("", "SPI ST7567", this); + ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); + LOG_PIN(" CS Pin: ", this->cs_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + ESP_LOGCONFIG(TAG, " Mirror X: %s", YESNO(this->mirror_x_)); + ESP_LOGCONFIG(TAG, " Mirror Y: %s", YESNO(this->mirror_y_)); + ESP_LOGCONFIG(TAG, " Invert Colors: %s", YESNO(this->invert_colors_)); + LOG_UPDATE_INTERVAL(this); +} + +void SPIST7567::command(uint8_t value) { + if (this->cs_) + this->cs_->digital_write(true); + this->dc_pin_->digital_write(false); + delay(1); + this->enable(); + if (this->cs_) + this->cs_->digital_write(false); + this->write_byte(value); + if (this->cs_) + this->cs_->digital_write(true); + this->disable(); +} + +void HOT SPIST7567::write_display_data() { + // ST7567A has built-in RAM with 132x65 bit capacity which stores the display data. + // but only first 128 pixels from each line are shown on screen + // if screen got flipped horizontally then it shows last 128 pixels, + // so we need to write x coordinate starting from column 4, not column 0 + this->command(esphome::st7567_base::ST7567_SET_START_LINE + this->start_line_); + for (uint8_t y = 0; y < (uint8_t) this->get_height_internal() / 8; y++) { + this->dc_pin_->digital_write(false); + this->command(esphome::st7567_base::ST7567_PAGE_ADDR + y); // Set Page + this->command(esphome::st7567_base::ST7567_COL_ADDR_H); // Set MSB Column address + this->command(esphome::st7567_base::ST7567_COL_ADDR_L + this->get_offset_x_()); // Set LSB Column address + this->dc_pin_->digital_write(true); + + this->enable(); + this->write_array(&this->buffer_[y * this->get_width_internal()], this->get_width_internal()); + this->disable(); + } +} + +} // namespace st7567_spi +} // namespace esphome diff --git a/esphome/components/st7567_spi/st7567_spi.h b/esphome/components/st7567_spi/st7567_spi.h new file mode 100644 index 0000000000..e8d1ebe0cc --- /dev/null +++ b/esphome/components/st7567_spi/st7567_spi.h @@ -0,0 +1,29 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/st7567_base/st7567_base.h" +#include "esphome/components/spi/spi.h" + +namespace esphome { +namespace st7567_spi { + +class SPIST7567 : public st7567_base::ST7567, + public spi::SPIDevice { + public: + void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; } + + void setup() override; + + void dump_config() override; + + protected: + void command(uint8_t value) override; + + void write_display_data() override; + + GPIOPin *dc_pin_; +}; + +} // namespace st7567_spi +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index 720d4e8e82..7046ac8139 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -3259,6 +3259,25 @@ display: inverted: true lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: st7567_i2c + id: st7735_display_i2c + address: 0x3F + i2c_id: i2c_bus + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: st7567_spi + id: st7735_display_spi + cs_pin: + allow_other_uses: true + number: GPIO5 + dc_pin: + allow_other_uses: true + number: GPIO16 + reset_pin: + allow_other_uses: true + number: GPIO23 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: st7735 id: st7735_display model: INITR_BLACKTAB From d4d49e38fc4cb34794468bd2e2514d1f55154ea5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 27 Dec 2023 17:51:00 -1000 Subject: [PATCH 095/193] Fix device not requesting Home Assistant time at the update interval (#6022) --- esphome/components/api/api_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 0348112fcd..8df860bb09 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -319,7 +319,7 @@ void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeo #ifdef USE_HOMEASSISTANT_TIME void APIServer::request_time() { for (auto &client : this->clients_) { - if (!client->remove_ && client->connection_state_ == APIConnection::ConnectionState::CONNECTED) + if (!client->remove_ && client->is_authenticated()) client->send_time_request(); } } From 21ec42f495b33c9b2957b234581f5b19148b4eb6 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 29 Dec 2023 13:00:19 +1100 Subject: [PATCH 096/193] Add constants used by multiple display drivers to global const.py (#6033) * Add constants used by multiple display drivers to global const.py * Add further constants * Refactor st7789v and st7735v --- esphome/components/ili9xxx/display.py | 16 ++++++++-------- esphome/components/st7567_base/__init__.py | 8 ++++---- esphome/components/st7735/display.py | 2 +- esphome/components/st7789v/display.py | 4 ++-- esphome/components/touchscreen/__init__.py | 14 ++++++++------ esphome/const.py | 8 ++++++++ 6 files changed, 31 insertions(+), 21 deletions(-) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index cd68f1ae27..b3fe8b2b41 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -17,6 +17,14 @@ from esphome.const import ( CONF_WIDTH, CONF_HEIGHT, CONF_ROTATION, + CONF_MIRROR_X, + CONF_MIRROR_Y, + CONF_SWAP_XY, + CONF_COLOR_ORDER, + CONF_OFFSET_HEIGHT, + CONF_OFFSET_WIDTH, + CONF_TRANSFORM, + CONF_INVERT_COLORS, ) DEPENDENCIES = ["spi"] @@ -70,14 +78,6 @@ COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE") CONF_LED_PIN = "led_pin" CONF_COLOR_PALETTE_IMAGES = "color_palette_images" CONF_INVERT_DISPLAY = "invert_display" -CONF_INVERT_COLORS = "invert_colors" -CONF_MIRROR_X = "mirror_x" -CONF_MIRROR_Y = "mirror_y" -CONF_SWAP_XY = "swap_xy" -CONF_COLOR_ORDER = "color_order" -CONF_OFFSET_HEIGHT = "offset_height" -CONF_OFFSET_WIDTH = "offset_width" -CONF_TRANSFORM = "transform" def _validate(config): diff --git a/esphome/components/st7567_base/__init__.py b/esphome/components/st7567_base/__init__.py index 462d7be6a1..7ce50fd99f 100644 --- a/esphome/components/st7567_base/__init__.py +++ b/esphome/components/st7567_base/__init__.py @@ -5,6 +5,10 @@ from esphome.components import display from esphome.const import ( CONF_LAMBDA, CONF_RESET_PIN, + CONF_MIRROR_X, + CONF_MIRROR_Y, + CONF_TRANSFORM, + CONF_INVERT_COLORS, ) CODEOWNERS = ["@latonita"] @@ -14,10 +18,6 @@ ST7567 = st7567_base_ns.class_("ST7567", cg.PollingComponent, display.DisplayBuf ST7567Model = st7567_base_ns.enum("ST7567Model") # todo in future: reuse following constants from const.py when they are released -CONF_INVERT_COLORS = "invert_colors" -CONF_TRANSFORM = "transform" -CONF_MIRROR_X = "mirror_x" -CONF_MIRROR_Y = "mirror_y" ST7567_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( diff --git a/esphome/components/st7735/display.py b/esphome/components/st7735/display.py index 4ff5cafaf8..d5bb2fa3d6 100644 --- a/esphome/components/st7735/display.py +++ b/esphome/components/st7735/display.py @@ -10,6 +10,7 @@ from esphome.const import ( CONF_MODEL, CONF_RESET_PIN, CONF_PAGES, + CONF_INVERT_COLORS, ) from . import st7735_ns @@ -23,7 +24,6 @@ CONF_ROW_START = "row_start" CONF_COL_START = "col_start" CONF_EIGHT_BIT_COLOR = "eight_bit_color" CONF_USE_BGR = "use_bgr" -CONF_INVERT_COLORS = "invert_colors" SPIST7735 = st7735_ns.class_( "ST7735", cg.PollingComponent, display.DisplayBuffer, spi.SPIDevice diff --git a/esphome/components/st7789v/display.py b/esphome/components/st7789v/display.py index 5b2b5a126c..04dce2cf6c 100644 --- a/esphome/components/st7789v/display.py +++ b/esphome/components/st7789v/display.py @@ -14,12 +14,12 @@ from esphome.const import ( CONF_POWER_SUPPLY, CONF_ROTATION, CONF_CS_PIN, + CONF_OFFSET_HEIGHT, + CONF_OFFSET_WIDTH, ) from . import st7789v_ns CONF_EIGHTBITCOLOR = "eightbitcolor" -CONF_OFFSET_HEIGHT = "offset_height" -CONF_OFFSET_WIDTH = "offset_width" CODEOWNERS = ["@kbx81"] diff --git a/esphome/components/touchscreen/__init__.py b/esphome/components/touchscreen/__init__.py index 99aee5c9fb..c4945617f9 100644 --- a/esphome/components/touchscreen/__init__.py +++ b/esphome/components/touchscreen/__init__.py @@ -3,7 +3,14 @@ import esphome.codegen as cg from esphome.components import display from esphome import automation -from esphome.const import CONF_ON_TOUCH, CONF_ON_RELEASE +from esphome.const import ( + CONF_ON_TOUCH, + CONF_ON_RELEASE, + CONF_MIRROR_X, + CONF_MIRROR_Y, + CONF_SWAP_XY, + CONF_TRANSFORM, +) from esphome.core import coroutine_with_priority CODEOWNERS = ["@jesserockz", "@nielsnl68"] @@ -26,11 +33,6 @@ CONF_REPORT_INTERVAL = "report_interval" # not used yet: CONF_ON_UPDATE = "on_update" CONF_TOUCH_TIMEOUT = "touch_timeout" -CONF_MIRROR_X = "mirror_x" -CONF_MIRROR_Y = "mirror_y" -CONF_SWAP_XY = "swap_xy" -CONF_TRANSFORM = "transform" - def touchscreen_schema(default_touch_timeout): return cv.Schema( diff --git a/esphome/const.py b/esphome/const.py index 4a4a59f659..7e27254d76 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -127,6 +127,7 @@ CONF_COLOR_BRIGHTNESS = "color_brightness" CONF_COLOR_CORRECT = "color_correct" CONF_COLOR_INTERLOCK = "color_interlock" CONF_COLOR_MODE = "color_mode" +CONF_COLOR_ORDER = "color_order" CONF_COLOR_PALETTE = "color_palette" CONF_COLOR_TEMPERATURE = "color_temperature" CONF_COLORS = "colors" @@ -370,6 +371,7 @@ CONF_INTERRUPT_PIN = "interrupt_pin" CONF_INTERVAL = "interval" CONF_INVALID_COOLDOWN = "invalid_cooldown" CONF_INVERT = "invert" +CONF_INVERT_COLORS = "invert_colors" CONF_INVERTED = "inverted" CONF_IP_ADDRESS = "ip_address" CONF_IRQ_PIN = "irq_pin" @@ -454,6 +456,8 @@ CONF_MIN_VALUE = "min_value" CONF_MIN_VERSION = "min_version" CONF_MINUTE = "minute" CONF_MINUTES = "minutes" +CONF_MIRROR_X = "mirror_x" +CONF_MIRROR_Y = "mirror_y" CONF_MISO_PIN = "miso_pin" CONF_MODE = "mode" CONF_MODE_COMMAND_TOPIC = "mode_command_topic" @@ -485,6 +489,8 @@ CONF_NUMBER_DATAPOINT = "number_datapoint" CONF_OFF_MODE = "off_mode" CONF_OFF_SPEED_CYCLE = "off_speed_cycle" CONF_OFFSET = "offset" +CONF_OFFSET_HEIGHT = "offset_height" +CONF_OFFSET_WIDTH = "offset_width" CONF_ON = "on" CONF_ON_BLE_ADVERTISE = "on_ble_advertise" CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE = "on_ble_manufacturer_data_advertise" @@ -749,6 +755,7 @@ CONF_SUPPORTED_PRESETS = "supported_presets" CONF_SUPPORTED_SWING_MODES = "supported_swing_modes" CONF_SUPPORTS_COOL = "supports_cool" CONF_SUPPORTS_HEAT = "supports_heat" +CONF_SWAP_XY = "swap_xy" CONF_SWING_BOTH_ACTION = "swing_both_action" CONF_SWING_HORIZONTAL_ACTION = "swing_horizontal_action" CONF_SWING_MODE = "swing_mode" @@ -799,6 +806,7 @@ CONF_TOPIC_PREFIX = "topic_prefix" CONF_TOTAL = "total" CONF_TOTAL_POWER = "total_power" CONF_TRACES = "traces" +CONF_TRANSFORM = "transform" CONF_TRANSITION_LENGTH = "transition_length" CONF_TRIGGER_ID = "trigger_id" CONF_TRIGGER_PIN = "trigger_pin" From d3567f9ac6ad079fb59b69b2ef03b7d0eb1660f9 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 29 Dec 2023 06:15:06 +0100 Subject: [PATCH 097/193] Nextion queue size (#6029) * Nextion `queue_size` function Returns the size of Nextion queue. For troubleshooting only. * Move `queue_size` to `nextion.h` This is where the queue is * Inline doc * clang-format --- esphome/components/nextion/nextion.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index acbf394fc6..2f52a032c4 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -960,6 +960,21 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe this->exit_reparse_on_start_ = exit_reparse_on_start; } + /** + * @brief Retrieves the number of commands pending in the Nextion command queue. + * + * This function returns the current count of commands that have been queued but not yet processed + * for the Nextion display. The Nextion command queue is used to store commands that are sent to + * the Nextion display for various operations like updating the display, changing interface elements, + * or other interactive features. A larger queue size might indicate a higher processing time or potential + * delays in command execution. This function is useful for monitoring the command flow and managing + * the execution efficiency of the Nextion display interface. + * + * @return size_t The number of commands currently in the Nextion queue. This count includes all commands + * that have been added to the queue and are awaiting processing. + */ + size_t queue_size() { return this->nextion_queue_.size(); } + protected: std::deque nextion_queue_; std::deque waveform_queue_; From 5ebb68f4ffde847b3ac313d941f784ec4b0a4d44 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 29 Dec 2023 18:35:44 +1100 Subject: [PATCH 098/193] Ble client additions and fixes (#5277) * Add config to disable auto-connect of BLE client. Correct initialise MAC address of BLE client. * Checkpont * Fixes for automation progress. * Fixes for automation progress. * Checkpoint; fix notify for ble_client * Fix BLE client binary_output * Various fixes * Consider notifications on when receiving REG_FOR event. * Add testing branch to workflow * Add workflow * CI changes * CI changes * CI clang * CI changes * CI changes * Add comment about logging macros * Add test, sanitise comment * Revert testing change to ci config * Update codeowners * Revert ci config change * Fix some state changes * Add default case. * Minor fixes * Add auto-connect to logconfig --- CODEOWNERS | 2 +- esphome/components/ble_client/__init__.py | 34 ++- esphome/components/ble_client/automation.cpp | 68 +---- esphome/components/ble_client/automation.h | 248 ++++++++++++++---- esphome/components/ble_client/ble_client.cpp | 22 +- .../ble_client/output/ble_binary_output.cpp | 62 ++--- .../ble_client/output/ble_binary_output.h | 4 +- .../components/ble_client/sensor/automation.h | 16 +- .../ble_client/sensor/ble_rssi_sensor.cpp | 13 +- .../ble_client/sensor/ble_sensor.cpp | 20 +- .../ble_client/switch/ble_switch.cpp | 7 +- .../text_sensor/ble_text_sensor.cpp | 18 +- .../esp32_ble_client/ble_client_base.cpp | 134 +++++++--- .../esp32_ble_client/ble_client_base.h | 18 +- tests/test1.yaml | 12 + 15 files changed, 443 insertions(+), 235 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index dff5d8beb5..c655f94a1b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -52,7 +52,7 @@ esphome/components/bk72xx/* @kuba2k2 esphome/components/bl0939/* @ziceva esphome/components/bl0940/* @tobias- esphome/components/bl0942/* @dbuezas -esphome/components/ble_client/* @buxtronix +esphome/components/ble_client/* @buxtronix @clydebarrow esphome/components/bluetooth_proxy/* @jesserockz esphome/components/bme680_bsec/* @trvrnrth esphome/components/bmi160/* @flaviut diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 8f70ad3417..34b9868edc 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -1,5 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv +from esphome.automation import maybe_simple_id from esphome.components import esp32_ble_tracker, esp32_ble_client from esphome.const import ( CONF_CHARACTERISTIC_UUID, @@ -15,7 +16,7 @@ from esphome.const import ( from esphome import automation AUTO_LOAD = ["esp32_ble_client"] -CODEOWNERS = ["@buxtronix"] +CODEOWNERS = ["@buxtronix", "@clydebarrow"] DEPENDENCIES = ["esp32_ble_tracker"] ble_client_ns = cg.esphome_ns.namespace("ble_client") @@ -43,6 +44,10 @@ BLEClientNumericComparisonRequestTrigger = ble_client_ns.class_( # Actions BLEWriteAction = ble_client_ns.class_("BLEClientWriteAction", automation.Action) +BLEConnectAction = ble_client_ns.class_("BLEClientConnectAction", automation.Action) +BLEDisconnectAction = ble_client_ns.class_( + "BLEClientDisconnectAction", automation.Action +) BLEPasskeyReplyAction = ble_client_ns.class_( "BLEClientPasskeyReplyAction", automation.Action ) @@ -58,6 +63,7 @@ CONF_ACCEPT = "accept" CONF_ON_PASSKEY_REQUEST = "on_passkey_request" CONF_ON_PASSKEY_NOTIFICATION = "on_passkey_notification" CONF_ON_NUMERIC_COMPARISON_REQUEST = "on_numeric_comparison_request" +CONF_AUTO_CONNECT = "auto_connect" # Espressif platformio framework is built with MAX_BLE_CONN to 3, so # enforce this in yaml checks. @@ -69,6 +75,7 @@ CONFIG_SCHEMA = ( cv.GenerateID(): cv.declare_id(BLEClient), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, cv.Optional(CONF_NAME): cv.string, + cv.Optional(CONF_AUTO_CONNECT, default=True): cv.boolean, cv.Optional(CONF_ON_CONNECT): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( @@ -135,6 +142,12 @@ BLE_WRITE_ACTION_SCHEMA = cv.Schema( } ) +BLE_CONNECT_ACTION_SCHEMA = maybe_simple_id( + { + cv.GenerateID(CONF_ID): cv.use_id(BLEClient), + } +) + BLE_NUMERIC_COMPARISON_REPLY_ACTION_SCHEMA = cv.Schema( { cv.GenerateID(CONF_ID): cv.use_id(BLEClient), @@ -157,6 +170,24 @@ BLE_REMOVE_BOND_ACTION_SCHEMA = cv.Schema( ) +@automation.register_action( + "ble_client.disconnect", BLEDisconnectAction, BLE_CONNECT_ACTION_SCHEMA +) +async def ble_disconnect_to_code(config, action_id, template_arg, args): + parent = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, parent) + return var + + +@automation.register_action( + "ble_client.connect", BLEConnectAction, BLE_CONNECT_ACTION_SCHEMA +) +async def ble_connect_to_code(config, action_id, template_arg, args): + parent = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, parent) + return var + + @automation.register_action( "ble_client.ble_write", BLEWriteAction, BLE_WRITE_ACTION_SCHEMA ) @@ -261,6 +292,7 @@ async def to_code(config): await cg.register_component(var, config) await esp32_ble_tracker.register_client(var, config) cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + cg.add(var.set_auto_connect(config[CONF_AUTO_CONNECT])) for conf in config.get(CONF_ON_CONNECT, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) diff --git a/esphome/components/ble_client/automation.cpp b/esphome/components/ble_client/automation.cpp index 429f906a5f..9a0233eb70 100644 --- a/esphome/components/ble_client/automation.cpp +++ b/esphome/components/ble_client/automation.cpp @@ -2,76 +2,10 @@ #include "automation.h" -#include -#include -#include - -#include "esphome/core/log.h" - namespace esphome { namespace ble_client { -static const char *const TAG = "ble_client.automation"; -void BLEWriterClientNode::write(const std::vector &value) { - if (this->node_state != espbt::ClientState::ESTABLISHED) { - ESP_LOGW(TAG, "Cannot write to BLE characteristic - not connected"); - return; - } else if (this->ble_char_handle_ == 0) { - ESP_LOGW(TAG, "Cannot write to BLE characteristic - characteristic not found"); - return; - } - esp_gatt_write_type_t write_type; - if (this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE) { - write_type = ESP_GATT_WRITE_TYPE_RSP; - ESP_LOGD(TAG, "Write type: ESP_GATT_WRITE_TYPE_RSP"); - } else if (this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) { - write_type = ESP_GATT_WRITE_TYPE_NO_RSP; - ESP_LOGD(TAG, "Write type: ESP_GATT_WRITE_TYPE_NO_RSP"); - } else { - ESP_LOGE(TAG, "Characteristic %s does not allow writing", this->char_uuid_.to_string().c_str()); - return; - } - ESP_LOGVV(TAG, "Will write %d bytes: %s", value.size(), format_hex_pretty(value).c_str()); - esp_err_t err = - esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->ble_char_handle_, - value.size(), const_cast(value.data()), write_type, ESP_GATT_AUTH_REQ_NONE); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error writing to characteristic: %s!", esp_err_to_name(err)); - } -} - -void BLEWriterClientNode::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) { - switch (event) { - case ESP_GATTC_REG_EVT: - break; - case ESP_GATTC_OPEN_EVT: - this->node_state = espbt::ClientState::ESTABLISHED; - ESP_LOGD(TAG, "Connection established with %s", ble_client_->address_str().c_str()); - break; - case ESP_GATTC_SEARCH_CMPL_EVT: { - auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_); - if (chr == nullptr) { - ESP_LOGW("ble_write_action", "Characteristic %s was not found in service %s", - this->char_uuid_.to_string().c_str(), this->service_uuid_.to_string().c_str()); - break; - } - this->ble_char_handle_ = chr->handle; - this->char_props_ = chr->properties; - this->node_state = espbt::ClientState::ESTABLISHED; - ESP_LOGD(TAG, "Found characteristic %s on device %s", this->char_uuid_.to_string().c_str(), - ble_client_->address_str().c_str()); - break; - } - case ESP_GATTC_DISCONNECT_EVT: - this->node_state = espbt::ClientState::IDLE; - this->ble_char_handle_ = 0; - ESP_LOGD(TAG, "Disconnected from %s", ble_client_->address_str().c_str()); - break; - default: - break; - } -} +const char *const Automation::TAG = "ble_client.automation"; } // namespace ble_client } // namespace esphome diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index 423f74b85a..a5c661e2f5 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -7,9 +7,19 @@ #include "esphome/core/automation.h" #include "esphome/components/ble_client/ble_client.h" +#include "esphome/core/log.h" namespace esphome { namespace ble_client { + +// placeholder class for static TAG . +class Automation { + public: + // could be made inline with C++17 + static const char *const TAG; +}; + +// implement on_connect automation. class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode { public: explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); } @@ -23,17 +33,28 @@ class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode { } }; +// on_disconnect automation class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode { public: explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); } void loop() override {} void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override { - if (event == ESP_GATTC_DISCONNECT_EVT && - memcmp(param->disconnect.remote_bda, this->parent_->get_remote_bda(), 6) == 0) - this->trigger(); - if (event == ESP_GATTC_SEARCH_CMPL_EVT) - this->node_state = espbt::ClientState::ESTABLISHED; + // test for CLOSE and not DISCONNECT - DISCONNECT can occur even if no virtual connection (OPEN event) occurred. + // So this will not trigger unless a complete open has previously succeeded. + switch (event) { + case ESP_GATTC_SEARCH_CMPL_EVT: { + this->node_state = espbt::ClientState::ESTABLISHED; + break; + } + case ESP_GATTC_CLOSE_EVT: { + this->trigger(); + break; + } + default: { + break; + } + } } }; @@ -42,10 +63,8 @@ class BLEClientPasskeyRequestTrigger : public Trigger<>, public BLEClientNode { explicit BLEClientPasskeyRequestTrigger(BLEClient *parent) { parent->register_ble_node(this); } void loop() override {} void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override { - if (event == ESP_GAP_BLE_PASSKEY_REQ_EVT && - memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) { + if (event == ESP_GAP_BLE_PASSKEY_REQ_EVT && this->parent_->check_addr(param->ble_security.auth_cmpl.bd_addr)) this->trigger(); - } } }; @@ -54,10 +73,8 @@ class BLEClientPasskeyNotificationTrigger : public Trigger, public BLE explicit BLEClientPasskeyNotificationTrigger(BLEClient *parent) { parent->register_ble_node(this); } void loop() override {} void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override { - if (event == ESP_GAP_BLE_PASSKEY_NOTIF_EVT && - memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) { - uint32_t passkey = param->ble_security.key_notif.passkey; - this->trigger(passkey); + if (event == ESP_GAP_BLE_PASSKEY_NOTIF_EVT && this->parent_->check_addr(param->ble_security.auth_cmpl.bd_addr)) { + this->trigger(param->ble_security.key_notif.passkey); } } }; @@ -67,24 +84,20 @@ class BLEClientNumericComparisonRequestTrigger : public Trigger, publi explicit BLEClientNumericComparisonRequestTrigger(BLEClient *parent) { parent->register_ble_node(this); } void loop() override {} void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override { - if (event == ESP_GAP_BLE_NC_REQ_EVT && - memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) { - uint32_t passkey = param->ble_security.key_notif.passkey; - this->trigger(passkey); + if (event == ESP_GAP_BLE_NC_REQ_EVT && this->parent_->check_addr(param->ble_security.auth_cmpl.bd_addr)) { + this->trigger(param->ble_security.key_notif.passkey); } } }; -class BLEWriterClientNode : public BLEClientNode { +// implement the ble_client.ble_write action. +template class BLEClientWriteAction : public Action, public BLEClientNode { public: - BLEWriterClientNode(BLEClient *ble_client) { + BLEClientWriteAction(BLEClient *ble_client) { ble_client->register_ble_node(this); ble_client_ = ble_client; } - // Attempts to write the contents of value to char_uuid_. - void write(const std::vector &value); - void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); } void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } @@ -93,29 +106,6 @@ class BLEWriterClientNode : public BLEClientNode { void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } - void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) override; - - private: - BLEClient *ble_client_; - int ble_char_handle_ = 0; - esp_gatt_char_prop_t char_props_; - espbt::ESPBTUUID service_uuid_; - espbt::ESPBTUUID char_uuid_; -}; - -template class BLEClientWriteAction : public Action, public BLEWriterClientNode { - public: - BLEClientWriteAction(BLEClient *ble_client) : BLEWriterClientNode(ble_client) {} - - void play(Ts... x) override { - if (has_simple_value_) { - return write(this->value_simple_); - } else { - return write(this->value_template_(x...)); - } - } - void set_value_template(std::function(Ts...)> func) { this->value_template_ = std::move(func); has_simple_value_ = false; @@ -126,10 +116,94 @@ template class BLEClientWriteAction : public Action, publ has_simple_value_ = true; } + void play(Ts... x) override {} + + void play_complex(Ts... x) override { + this->num_running_++; + this->var_ = std::make_tuple(x...); + auto value = this->has_simple_value_ ? this->value_simple_ : this->value_template_(x...); + // on write failure, continue the automation chain rather than stopping so that e.g. disconnect can work. + if (!write(value)) + this->play_next_(x...); + } + + /** + * Note about logging: the esph_log_X macros are used here because the CI checks complain about use of the ESP LOG + * macros in header files (Can't even write it in a comment!) + * Not sure why, because they seem to work just fine. + * The problem is that the implementation of a templated class can't be placed in a .cpp file when using C++ less than + * 17, so the methods have to be here. The esph_log_X macros are equivalent in function, but don't trigger the CI + * errors. + */ + // initiate the write. Return true if all went well, will be followed by a WRITE_CHAR event. + bool write(const std::vector &value) { + if (this->node_state != espbt::ClientState::ESTABLISHED) { + esph_log_w(Automation::TAG, "Cannot write to BLE characteristic - not connected"); + return false; + } + esph_log_vv(Automation::TAG, "Will write %d bytes: %s", value.size(), format_hex_pretty(value).c_str()); + esp_err_t err = esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), + this->char_handle_, value.size(), const_cast(value.data()), + this->write_type_, ESP_GATT_AUTH_REQ_NONE); + if (err != ESP_OK) { + esph_log_e(Automation::TAG, "Error writing to characteristic: %s!", esp_err_to_name(err)); + return false; + } + return true; + } + + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override { + switch (event) { + case ESP_GATTC_WRITE_CHAR_EVT: + // upstream code checked the MAC address, verify the characteristic. + if (param->write.handle == this->char_handle_) + this->parent()->run_later([this]() { this->play_next_tuple_(this->var_); }); + break; + case ESP_GATTC_DISCONNECT_EVT: + if (this->num_running_ != 0) + this->stop_complex(); + break; + case ESP_GATTC_SEARCH_CMPL_EVT: { + auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_); + if (chr == nullptr) { + esph_log_w("ble_write_action", "Characteristic %s was not found in service %s", + this->char_uuid_.to_string().c_str(), this->service_uuid_.to_string().c_str()); + break; + } + this->char_handle_ = chr->handle; + this->char_props_ = chr->properties; + if (this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE) { + this->write_type_ = ESP_GATT_WRITE_TYPE_RSP; + esph_log_d(Automation::TAG, "Write type: ESP_GATT_WRITE_TYPE_RSP"); + } else if (this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) { + this->write_type_ = ESP_GATT_WRITE_TYPE_NO_RSP; + esph_log_d(Automation::TAG, "Write type: ESP_GATT_WRITE_TYPE_NO_RSP"); + } else { + esph_log_e(Automation::TAG, "Characteristic %s does not allow writing", this->char_uuid_.to_string().c_str()); + break; + } + this->node_state = espbt::ClientState::ESTABLISHED; + esph_log_d(Automation::TAG, "Found characteristic %s on device %s", this->char_uuid_.to_string().c_str(), + ble_client_->address_str().c_str()); + break; + } + default: + break; + } + } + private: + BLEClient *ble_client_; bool has_simple_value_ = true; std::vector value_simple_; std::function(Ts...)> value_template_{}; + espbt::ESPBTUUID service_uuid_; + espbt::ESPBTUUID char_uuid_; + std::tuple var_{}; + uint16_t char_handle_{}; + esp_gatt_char_prop_t char_props_{}; + esp_gatt_write_type_t write_type_{}; }; template class BLEClientPasskeyReplyAction : public Action { @@ -212,6 +286,92 @@ template class BLEClientRemoveBondAction : public Action BLEClient *parent_{nullptr}; }; +template class BLEClientConnectAction : public Action, public BLEClientNode { + public: + BLEClientConnectAction(BLEClient *ble_client) { + ble_client->register_ble_node(this); + ble_client_ = ble_client; + } + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override { + if (this->num_running_ == 0) + return; + switch (event) { + case ESP_GATTC_SEARCH_CMPL_EVT: + this->node_state = espbt::ClientState::ESTABLISHED; + this->parent()->run_later([this]() { this->play_next_tuple_(this->var_); }); + break; + // if the connection is closed, terminate the automation chain. + case ESP_GATTC_DISCONNECT_EVT: + this->stop_complex(); + break; + default: + break; + } + } + + // not used since we override play_complex_ + void play(Ts... x) override {} + + void play_complex(Ts... x) override { + // it makes no sense to have multiple instances of this running at the same time. + // this would occur only if the same automation was re-triggered while still + // running. So just cancel the second chain if this is detected. + if (this->num_running_ != 0) { + this->stop_complex(); + return; + } + this->num_running_++; + if (this->node_state == espbt::ClientState::ESTABLISHED) { + this->play_next_(x...); + } else { + this->var_ = std::make_tuple(x...); + this->ble_client_->connect(); + } + } + + private: + BLEClient *ble_client_; + std::tuple var_{}; +}; + +template class BLEClientDisconnectAction : public Action, public BLEClientNode { + public: + BLEClientDisconnectAction(BLEClient *ble_client) { + ble_client->register_ble_node(this); + ble_client_ = ble_client; + } + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override { + if (this->num_running_ == 0) + return; + switch (event) { + case ESP_GATTC_CLOSE_EVT: + case ESP_GATTC_DISCONNECT_EVT: + this->parent()->run_later([this]() { this->play_next_tuple_(this->var_); }); + break; + default: + break; + } + } + + // not used since we override play_complex_ + void play(Ts... x) override {} + + void play_complex(Ts... x) override { + this->num_running_++; + if (this->node_state == espbt::ClientState::IDLE) { + this->play_next_(x...); + } else { + this->var_ = std::make_tuple(x...); + this->ble_client_->disconnect(); + } + } + + private: + BLEClient *ble_client_; + std::tuple var_{}; +}; } // namespace ble_client } // namespace esphome diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index f3a9f01a1a..19cf2bc1f3 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -26,6 +26,7 @@ void BLEClient::loop() { void BLEClient::dump_config() { ESP_LOGCONFIG(TAG, "BLE Client:"); ESP_LOGCONFIG(TAG, " Address: %s", this->address_str().c_str()); + ESP_LOGCONFIG(TAG, " Auto-Connect: %s", TRUEFALSE(this->auto_connect_)); } bool BLEClient::parse_device(const espbt::ESPBTDevice &device) { @@ -37,31 +38,24 @@ bool BLEClient::parse_device(const espbt::ESPBTDevice &device) { void BLEClient::set_enabled(bool enabled) { if (enabled == this->enabled) return; - if (!enabled && this->state() != espbt::ClientState::IDLE) { - ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str()); - auto ret = esp_ble_gattc_close(this->gattc_if_, this->conn_id_); - if (ret) { - ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s status=%d", this->address_str().c_str(), ret); - } - } this->enabled = enabled; + if (!enabled) { + ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str()); + this->disconnect(); + } } bool BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, esp_ble_gattc_cb_param_t *param) { - bool all_established = this->all_nodes_established_(); - if (!BLEClientBase::gattc_event_handler(event, esp_gattc_if, param)) return false; for (auto *node : this->nodes_) node->gattc_event_handler(event, esp_gattc_if, param); - // Delete characteristics after clients have used them to save RAM. - if (!all_established && this->all_nodes_established_()) { - for (auto &svc : this->services_) - delete svc; // NOLINT(cppcoreguidelines-owning-memory) - this->services_.clear(); + if (!this->services_.empty() && this->all_nodes_established_()) { + this->release_services(); + ESP_LOGD(TAG, "All clients established, services released"); } return true; } diff --git a/esphome/components/ble_client/output/ble_binary_output.cpp b/esphome/components/ble_client/output/ble_binary_output.cpp index 6709803936..3f05bc4b84 100644 --- a/esphome/components/ble_client/output/ble_binary_output.cpp +++ b/esphome/components/ble_client/output/ble_binary_output.cpp @@ -19,26 +19,36 @@ void BLEBinaryOutput::dump_config() { void BLEBinaryOutput::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { switch (event) { - case ESP_GATTC_OPEN_EVT: - this->client_state_ = espbt::ClientState::ESTABLISHED; - ESP_LOGW(TAG, "[%s] Connected successfully!", this->char_uuid_.to_string().c_str()); - break; - case ESP_GATTC_DISCONNECT_EVT: - ESP_LOGW(TAG, "[%s] Disconnected", this->char_uuid_.to_string().c_str()); - this->client_state_ = espbt::ClientState::IDLE; - break; - case ESP_GATTC_WRITE_CHAR_EVT: { - if (param->write.status == 0) { - break; - } - + case ESP_GATTC_SEARCH_CMPL_EVT: { auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_); if (chr == nullptr) { - ESP_LOGW(TAG, "[%s] Characteristic not found.", this->char_uuid_.to_string().c_str()); + ESP_LOGW(TAG, "Characteristic %s was not found in service %s", this->char_uuid_.to_string().c_str(), + this->service_uuid_.to_string().c_str()); break; } - if (param->write.handle == chr->handle) { - ESP_LOGW(TAG, "[%s] Write error, status=%d", this->char_uuid_.to_string().c_str(), param->write.status); + this->char_handle_ = chr->handle; + this->char_props_ = chr->properties; + if (this->require_response_ && this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE) { + this->write_type_ = ESP_GATT_WRITE_TYPE_RSP; + ESP_LOGD(TAG, "Write type: ESP_GATT_WRITE_TYPE_RSP"); + } else if (!this->require_response_ && this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) { + this->write_type_ = ESP_GATT_WRITE_TYPE_NO_RSP; + ESP_LOGD(TAG, "Write type: ESP_GATT_WRITE_TYPE_NO_RSP"); + } else { + ESP_LOGE(TAG, "Characteristic %s does not allow writing with%s response", this->char_uuid_.to_string().c_str(), + this->require_response_ ? "" : "out"); + break; + } + this->node_state = espbt::ClientState::ESTABLISHED; + ESP_LOGD(TAG, "Found characteristic %s on device %s", this->char_uuid_.to_string().c_str(), + this->parent()->address_str().c_str()); + this->node_state = espbt::ClientState::ESTABLISHED; + break; + } + case ESP_GATTC_WRITE_CHAR_EVT: { + if (param->write.handle == this->char_handle_) { + if (param->write.status != 0) + ESP_LOGW(TAG, "[%s] Write error, status=%d", this->char_uuid_.to_string().c_str(), param->write.status); } break; } @@ -48,26 +58,18 @@ void BLEBinaryOutput::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i } void BLEBinaryOutput::write_state(bool state) { - if (this->client_state_ != espbt::ClientState::ESTABLISHED) { + if (this->node_state != espbt::ClientState::ESTABLISHED) { ESP_LOGW(TAG, "[%s] Not connected to BLE client. State update can not be written.", this->char_uuid_.to_string().c_str()); return; } - - auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_); - if (chr == nullptr) { - ESP_LOGW(TAG, "[%s] Characteristic not found. State update can not be written.", - this->char_uuid_.to_string().c_str()); - return; - } - uint8_t state_as_uint = (uint8_t) state; ESP_LOGV(TAG, "[%s] Write State: %d", this->char_uuid_.to_string().c_str(), state_as_uint); - if (this->require_response_) { - chr->write_value(&state_as_uint, sizeof(state_as_uint), ESP_GATT_WRITE_TYPE_RSP); - } else { - chr->write_value(&state_as_uint, sizeof(state_as_uint), ESP_GATT_WRITE_TYPE_NO_RSP); - } + esp_err_t err = + esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->char_handle_, + sizeof(state_as_uint), &state_as_uint, this->write_type_, ESP_GATT_AUTH_REQ_NONE); + if (err != ESP_GATT_OK) + ESP_LOGW(TAG, "[%s] Write error, err=%d", this->char_uuid_.to_string().c_str(), err); } } // namespace ble_client diff --git a/esphome/components/ble_client/output/ble_binary_output.h b/esphome/components/ble_client/output/ble_binary_output.h index 83eabcf5f2..0a1e186b26 100644 --- a/esphome/components/ble_client/output/ble_binary_output.h +++ b/esphome/components/ble_client/output/ble_binary_output.h @@ -32,7 +32,9 @@ class BLEBinaryOutput : public output::BinaryOutput, public BLEClientNode, publi bool require_response_; espbt::ESPBTUUID service_uuid_; espbt::ESPBTUUID char_uuid_; - espbt::ClientState client_state_; + uint16_t char_handle_{}; + esp_gatt_char_prop_t char_props_{}; + esp_gatt_write_type_t write_type_{}; }; } // namespace ble_client diff --git a/esphome/components/ble_client/sensor/automation.h b/esphome/components/ble_client/sensor/automation.h index d830165d30..56ab7ba4c9 100644 --- a/esphome/components/ble_client/sensor/automation.h +++ b/esphome/components/ble_client/sensor/automation.h @@ -14,15 +14,17 @@ class BLESensorNotifyTrigger : public Trigger, public BLESensor { void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override { switch (event) { - case ESP_GATTC_SEARCH_CMPL_EVT: { - this->sensor_->node_state = espbt::ClientState::ESTABLISHED; + case ESP_GATTC_NOTIFY_EVT: { + if (param->notify.handle == this->sensor_->handle) + this->trigger(this->sensor_->parent()->parse_char_value(param->notify.value, param->notify.value_len)); break; } - case ESP_GATTC_NOTIFY_EVT: { - if (param->notify.conn_id != this->sensor_->parent()->get_conn_id() || - param->notify.handle != this->sensor_->handle) - break; - this->trigger(this->sensor_->parent()->parse_char_value(param->notify.value, param->notify.value_len)); + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + // confirms notifications are being listened for. While enabling of notifications may still be in + // progress by the parent, we assume it will happen. + if (param->reg_for_notify.status == ESP_GATT_OK && param->reg_for_notify.handle == this->sensor_->handle) + this->node_state = espbt::ClientState::ESTABLISHED; + break; } default: break; diff --git a/esphome/components/ble_client/sensor/ble_rssi_sensor.cpp b/esphome/components/ble_client/sensor/ble_rssi_sensor.cpp index a36e191e32..81d244ce6d 100644 --- a/esphome/components/ble_client/sensor/ble_rssi_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_rssi_sensor.cpp @@ -22,26 +22,19 @@ void BLEClientRSSISensor::dump_config() { void BLEClientRSSISensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { switch (event) { - case ESP_GATTC_OPEN_EVT: { - if (param->open.status == ESP_GATT_OK) { - ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str()); - break; - } - break; - } - case ESP_GATTC_DISCONNECT_EVT: { - ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str()); + case ESP_GATTC_CLOSE_EVT: { this->status_set_warning(); this->publish_state(NAN); break; } - case ESP_GATTC_SEARCH_CMPL_EVT: + case ESP_GATTC_SEARCH_CMPL_EVT: { this->node_state = espbt::ClientState::ESTABLISHED; if (this->should_update_) { this->should_update_ = false; this->get_rssi_(); } break; + } default: break; } diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index a05efad60b..43f61f5304 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -33,7 +33,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga } break; } - case ESP_GATTC_DISCONNECT_EVT: { + case ESP_GATTC_CLOSE_EVT: { ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str()); this->status_set_warning(); this->publish_state(NAN); @@ -74,8 +74,6 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga break; } case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->parent()->get_conn_id()) - break; if (param->read.status != ESP_GATT_OK) { ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status); break; @@ -87,15 +85,23 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga break; } case ESP_GATTC_NOTIFY_EVT: { - if (param->notify.conn_id != this->parent()->get_conn_id() || param->notify.handle != this->handle) - break; - ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(), + ESP_LOGD(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(), param->notify.handle, param->notify.value[0]); + if (param->notify.handle != this->handle) + break; this->publish_state(this->parse_data_(param->notify.value, param->notify.value_len)); break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - this->node_state = espbt::ClientState::ESTABLISHED; + if (param->reg_for_notify.handle == this->handle) { + if (param->reg_for_notify.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "Error registering for notifications at handle %d, status=%d", param->reg_for_notify.handle, + param->reg_for_notify.status); + break; + } + this->node_state = espbt::ClientState::ESTABLISHED; + ESP_LOGD(TAG, "Register for notify on %s complete", this->char_uuid_.to_string().c_str()); + } break; } default: diff --git a/esphome/components/ble_client/switch/ble_switch.cpp b/esphome/components/ble_client/switch/ble_switch.cpp index 6de5252404..9d92b1b2b5 100644 --- a/esphome/components/ble_client/switch/ble_switch.cpp +++ b/esphome/components/ble_client/switch/ble_switch.cpp @@ -17,14 +17,11 @@ void BLEClientSwitch::write_state(bool state) { void BLEClientSwitch::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { switch (event) { - case ESP_GATTC_REG_EVT: + case ESP_GATTC_CLOSE_EVT: this->publish_state(this->parent_->enabled); break; - case ESP_GATTC_OPEN_EVT: + case ESP_GATTC_SEARCH_CMPL_EVT: this->node_state = espbt::ClientState::ESTABLISHED; - break; - case ESP_GATTC_DISCONNECT_EVT: - this->node_state = espbt::ClientState::IDLE; this->publish_state(this->parent_->enabled); break; default: diff --git a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp index 1a304593c7..33938ee7b7 100644 --- a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +++ b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp @@ -36,8 +36,7 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } break; } - case ESP_GATTC_DISCONNECT_EVT: { - ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str()); + case ESP_GATTC_CLOSE_EVT: { this->status_set_warning(); this->publish_state(EMPTY); break; @@ -77,20 +76,18 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; } case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->parent()->get_conn_id()) - break; - if (param->read.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status); - break; - } if (param->read.handle == this->handle) { + if (param->read.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status); + break; + } this->status_clear_warning(); this->publish_state(this->parse_data(param->read.value, param->read.value_len)); } break; } case ESP_GATTC_NOTIFY_EVT: { - if (param->notify.conn_id != this->parent()->get_conn_id() || param->notify.handle != this->handle) + if (param->notify.handle != this->handle) break; ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(), param->notify.handle, param->notify.value[0]); @@ -98,7 +95,8 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - this->node_state = espbt::ClientState::ESTABLISHED; + if (param->reg_for_notify.status == ESP_GATT_OK && param->reg_for_notify.handle == this->handle) + this->node_state = espbt::ClientState::ESTABLISHED; break; } default: diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index cc6d3d7d4d..ae83715aea 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -45,21 +45,19 @@ void BLEClientBase::loop() { float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) { + if (!this->auto_connect_) + return false; if (this->address_ == 0 || device.address_uint64() != this->address_) return false; if (this->state_ != espbt::ClientState::IDLE && this->state_ != espbt::ClientState::SEARCHING) return false; - ESP_LOGD(TAG, "[%d] [%s] Found device", this->connection_index_, this->address_str_.c_str()); - this->set_state(espbt::ClientState::DISCOVERED); + this->log_event_("Found device"); + if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG) + esp32_ble_tracker::global_esp32_ble_tracker->print_bt_device_info(device); - auto addr = device.address_uint64(); - this->remote_bda_[0] = (addr >> 40) & 0xFF; - this->remote_bda_[1] = (addr >> 32) & 0xFF; - this->remote_bda_[2] = (addr >> 24) & 0xFF; - this->remote_bda_[3] = (addr >> 16) & 0xFF; - this->remote_bda_[4] = (addr >> 8) & 0xFF; - this->remote_bda_[5] = (addr >> 0) & 0xFF; + this->set_state(espbt::ClientState::DISCOVERED); + this->set_address(device.address_uint64()); this->remote_addr_type_ = device.get_address_type(); return true; } @@ -108,6 +106,10 @@ void BLEClientBase::release_services() { #endif } +void BLEClientBase::log_event_(const char *name) { + ESP_LOGD(TAG, "[%d] [%s] %s", this->connection_index_, this->address_str_.c_str(), name); +} + bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, esp_ble_gattc_cb_param_t *param) { if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id) @@ -131,51 +133,73 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; } case ESP_GATTC_OPEN_EVT: { - ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_OPEN_EVT", this->connection_index_, this->address_str_.c_str()); + if (!this->check_addr(param->open.remote_bda)) + return false; + this->log_event_("ESP_GATTC_OPEN_EVT"); this->conn_id_ = param->open.conn_id; this->service_count_ = 0; if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { ESP_LOGW(TAG, "[%d] [%s] Connection failed, status=%d", this->connection_index_, this->address_str_.c_str(), param->open.status); this->set_state(espbt::ClientState::IDLE); - break; + return false; } auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->open.conn_id); if (ret) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_send_mtu_req failed, status=%x", this->connection_index_, this->address_str_.c_str(), ret); } + this->set_state(espbt::ClientState::CONNECTED); if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) { ESP_LOGI(TAG, "[%d] [%s] Connected", this->connection_index_, this->address_str_.c_str()); - this->set_state(espbt::ClientState::CONNECTED); + // only set our state, subclients might have more stuff to do yet. this->state_ = espbt::ClientState::ESTABLISHED; break; } esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr); break; } - case ESP_GATTC_CFG_MTU_EVT: { - if (param->cfg_mtu.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "[%d] [%s] cfg_mtu failed, mtu %d, status %d", this->connection_index_, - this->address_str_.c_str(), param->cfg_mtu.mtu, param->cfg_mtu.status); - this->set_state(espbt::ClientState::IDLE); - break; - } - ESP_LOGV(TAG, "[%d] [%s] cfg_mtu status %d, mtu %d", this->connection_index_, this->address_str_.c_str(), - param->cfg_mtu.status, param->cfg_mtu.mtu); - this->mtu_ = param->cfg_mtu.mtu; + case ESP_GATTC_CONNECT_EVT: { + if (!this->check_addr(param->connect.remote_bda)) + return false; + this->log_event_("ESP_GATTC_CONNECT_EVT"); break; } case ESP_GATTC_DISCONNECT_EVT: { - if (memcmp(param->disconnect.remote_bda, this->remote_bda_, 6) != 0) + if (!this->check_addr(param->disconnect.remote_bda)) return false; - ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->connection_index_, + ESP_LOGD(TAG, "[%d] [%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->connection_index_, this->address_str_.c_str(), param->disconnect.reason); this->release_services(); this->set_state(espbt::ClientState::IDLE); break; } + + case ESP_GATTC_CFG_MTU_EVT: { + if (this->conn_id_ != param->cfg_mtu.conn_id) + return false; + if (param->cfg_mtu.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "[%d] [%s] cfg_mtu failed, mtu %d, status %d", this->connection_index_, + this->address_str_.c_str(), param->cfg_mtu.mtu, param->cfg_mtu.status); + // No state change required here - disconnect event will follow if needed. + break; + } + ESP_LOGD(TAG, "[%d] [%s] cfg_mtu status %d, mtu %d", this->connection_index_, this->address_str_.c_str(), + param->cfg_mtu.status, param->cfg_mtu.mtu); + this->mtu_ = param->cfg_mtu.mtu; + break; + } + case ESP_GATTC_CLOSE_EVT: { + if (this->conn_id_ != param->close.conn_id) + return false; + this->log_event_("ESP_GATTC_CLOSE_EVT"); + this->release_services(); + this->set_state(espbt::ClientState::IDLE); + break; + } case ESP_GATTC_SEARCH_RES_EVT: { + if (this->conn_id_ != param->search_res.conn_id) + return false; this->service_count_++; if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) { // V3 clients don't need services initialized since @@ -191,7 +215,9 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; } case ESP_GATTC_SEARCH_CMPL_EVT: { - ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_SEARCH_CMPL_EVT", this->connection_index_, this->address_str_.c_str()); + if (this->conn_id_ != param->search_cmpl.conn_id) + return false; + this->log_event_("ESP_GATTC_SEARCH_CMPL_EVT"); for (auto &svc : this->services_) { ESP_LOGV(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_.c_str(), svc->uuid.to_string().c_str()); @@ -199,11 +225,41 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ this->address_str_.c_str(), svc->start_handle, svc->end_handle); } ESP_LOGI(TAG, "[%d] [%s] Connected", this->connection_index_, this->address_str_.c_str()); - this->set_state(espbt::ClientState::CONNECTED); this->state_ = espbt::ClientState::ESTABLISHED; break; } + case ESP_GATTC_READ_DESCR_EVT: { + if (this->conn_id_ != param->write.conn_id) + return false; + this->log_event_("ESP_GATTC_READ_DESCR_EVT"); + break; + } + case ESP_GATTC_WRITE_DESCR_EVT: { + if (this->conn_id_ != param->write.conn_id) + return false; + this->log_event_("ESP_GATTC_WRITE_DESCR_EVT"); + break; + } + case ESP_GATTC_WRITE_CHAR_EVT: { + if (this->conn_id_ != param->write.conn_id) + return false; + this->log_event_("ESP_GATTC_WRITE_CHAR_EVT"); + break; + } + case ESP_GATTC_READ_CHAR_EVT: { + if (this->conn_id_ != param->read.conn_id) + return false; + this->log_event_("ESP_GATTC_READ_CHAR_EVT"); + break; + } + case ESP_GATTC_NOTIFY_EVT: { + if (this->conn_id_ != param->notify.conn_id) + return false; + this->log_event_("ESP_GATTC_NOTIFY_EVT"); + break; + } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + this->log_event_("ESP_GATTC_REG_FOR_NOTIFY_EVT"); if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE || this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) { // Client is responsible for flipping the descriptor value @@ -212,9 +268,8 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } esp_gattc_descr_elem_t desc_result; uint16_t count = 1; - esp_gatt_status_t descr_status = - esp_ble_gattc_get_descr_by_char_handle(this->gattc_if_, this->connection_index_, param->reg_for_notify.handle, - NOTIFY_DESC_UUID, &desc_result, &count); + esp_gatt_status_t descr_status = esp_ble_gattc_get_descr_by_char_handle( + this->gattc_if_, this->conn_id_, param->reg_for_notify.handle, NOTIFY_DESC_UUID, &desc_result, &count); if (descr_status != ESP_GATT_OK) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_get_descr_by_char_handle error, status=%d", this->connection_index_, this->address_str_.c_str(), descr_status); @@ -222,7 +277,7 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } esp_gattc_char_elem_t char_result; esp_gatt_status_t char_status = - esp_ble_gattc_get_all_char(this->gattc_if_, this->connection_index_, param->reg_for_notify.handle, + esp_ble_gattc_get_all_char(this->gattc_if_, this->conn_id_, param->reg_for_notify.handle, param->reg_for_notify.handle, &char_result, &count, 0); if (char_status != ESP_GATT_OK) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_get_all_char error, status=%d", this->connection_index_, @@ -238,6 +293,7 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ esp_err_t status = esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, desc_result.handle, sizeof(notify_en), (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); + ESP_LOGD(TAG, "Wrote notify descriptor %d, properties=%d", notify_en, char_result.properties); if (status) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_write_char_descr error, status=%d", this->connection_index_, this->address_str_.c_str(), status); @@ -246,24 +302,31 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } default: + // ideally would check all other events for matching conn_id + ESP_LOGD(TAG, "[%d] [%s] Event %d", this->connection_index_, this->address_str_.c_str(), event); break; } return true; } +// clients can't call defer() directly since it's protected. +void BLEClientBase::run_later(std::function &&f) { // NOLINT + this->defer(std::move(f)); +} + void BLEClientBase::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { // This event is sent by the server when it requests security case ESP_GAP_BLE_SEC_REQ_EVT: - if (memcmp(param->ble_security.auth_cmpl.bd_addr, this->remote_bda_, 6) != 0) - break; + if (!this->check_addr(param->ble_security.auth_cmpl.bd_addr)) + return; ESP_LOGV(TAG, "[%d] [%s] ESP_GAP_BLE_SEC_REQ_EVT %x", this->connection_index_, this->address_str_.c_str(), event); esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); break; // This event is sent once authentication has completed case ESP_GAP_BLE_AUTH_CMPL_EVT: - if (memcmp(param->ble_security.auth_cmpl.bd_addr, this->remote_bda_, 6) != 0) - break; + if (!this->check_addr(param->ble_security.auth_cmpl.bd_addr)) + return; esp_bd_addr_t bd_addr; memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t)); ESP_LOGI(TAG, "[%d] [%s] auth complete. remote BD_ADDR: %s", this->connection_index_, this->address_str_.c_str(), @@ -273,11 +336,12 @@ void BLEClientBase::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_ param->ble_security.auth_cmpl.fail_reason); } else { this->paired_ = true; - ESP_LOGV(TAG, "[%d] [%s] auth success. address type = %d auth mode = %d", this->connection_index_, + ESP_LOGD(TAG, "[%d] [%s] auth success. address type = %d auth mode = %d", this->connection_index_, this->address_str_.c_str(), param->ble_security.auth_cmpl.addr_type, param->ble_security.auth_cmpl.auth_mode); } break; + // There are other events we'll want to implement at some point to support things like pass key // https://github.com/espressif/esp-idf/blob/cba69dd088344ed9d26739f04736ae7a37541b3a/examples/bluetooth/bluedroid/ble/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md default: diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index 97886d0b19..fd586e59d6 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -27,6 +27,7 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { void loop() override; float get_setup_priority() const override; + void run_later(std::function &&f); // NOLINT bool parse_device(const espbt::ESPBTDevice &device) override; void on_scan_end() override {} bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, @@ -39,10 +40,17 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { bool connected() { return this->state_ == espbt::ClientState::ESTABLISHED; } + void set_auto_connect(bool auto_connect) { this->auto_connect_ = auto_connect; } + void set_address(uint64_t address) { this->address_ = address; + this->remote_bda_[0] = (address >> 40) & 0xFF; + this->remote_bda_[1] = (address >> 32) & 0xFF; + this->remote_bda_[2] = (address >> 24) & 0xFF; + this->remote_bda_[3] = (address >> 16) & 0xFF; + this->remote_bda_[4] = (address >> 8) & 0xFF; + this->remote_bda_[5] = (address >> 0) & 0xFF; if (address == 0) { - memset(this->remote_bda_, 0, sizeof(this->remote_bda_)); this->address_str_ = ""; } else { this->address_str_ = @@ -79,20 +87,24 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { virtual void set_connection_type(espbt::ConnectionType ct) { this->connection_type_ = ct; } + bool check_addr(esp_bd_addr_t &addr) { return memcmp(addr, this->remote_bda_, sizeof(esp_bd_addr_t)) == 0; } + protected: int gattc_if_; esp_bd_addr_t remote_bda_; - esp_ble_addr_type_t remote_addr_type_; + esp_ble_addr_type_t remote_addr_type_{BLE_ADDR_TYPE_PUBLIC}; uint16_t conn_id_{0xFFFF}; uint64_t address_{0}; + bool auto_connect_{false}; std::string address_str_{}; uint8_t connection_index_; int16_t service_count_{0}; uint16_t mtu_{23}; bool paired_{false}; espbt::ConnectionType connection_type_{espbt::ConnectionType::V1}; - std::vector services_; + + void log_event_(const char *name); }; } // namespace esp32_ble_client diff --git a/tests/test1.yaml b/tests/test1.yaml index 7046ac8139..bc7a94bc5a 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -358,8 +358,10 @@ esp32_ble_tracker: ble_client: - mac_address: AA:BB:CC:DD:EE:FF id: ble_foo + auto_connect: true - mac_address: 11:22:33:44:55:66 id: ble_blah + auto_connect: false on_connect: then: - switch.turn_on: ble1_status @@ -3026,6 +3028,16 @@ interval: page_id: page1 then: - logger.log: Seeing page 1 + - interval: 60min + then: + - ble_client.connect: ble_blah + - ble_client.ble_write: + id: ble_blah + service_uuid: EBE0CCB0-7A0A-4B0C-8A1A-6FF2997DA3A6 + characteristic_uuid: EBE0CCB7-7A0A-4B0C-8A1A-6FF2997DA3A6 + value: !lambda |- + return {1, 0}; + - ble_client.disconnect: ble_blah color: - id: kbx_red From 2a43e55452b49f766d1cbbb00a7ff650a2aba1de Mon Sep 17 00:00:00 2001 From: Pavlo Dudnytskyi Date: Fri, 29 Dec 2023 22:08:26 +0100 Subject: [PATCH 099/193] HaierProtocol library updated to 0.9.25 to fix the answer_timeout bug (#6015) --- esphome/components/haier/climate.py | 2 +- platformio.ini | 2 +- tests/test3.yaml | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/haier/climate.py b/esphome/components/haier/climate.py index c6998ce0c5..1cb8773495 100644 --- a/esphome/components/haier/climate.py +++ b/esphome/components/haier/climate.py @@ -495,4 +495,4 @@ async def to_code(config): trigger, [(cg.uint8, "code"), (cg.const_char_ptr, "message")], conf ) # https://github.com/paveldn/HaierProtocol - cg.add_library("pavlodn/HaierProtocol", "0.9.24") + cg.add_library("pavlodn/HaierProtocol", "0.9.25") diff --git a/platformio.ini b/platformio.ini index 68c4220aab..2dfaa79a52 100644 --- a/platformio.ini +++ b/platformio.ini @@ -39,7 +39,7 @@ lib_deps = bblanchon/ArduinoJson@6.18.5 ; json wjtje/qr-code-generator-library@1.7.0 ; qr_code functionpointer/arduino-MLX90393@1.0.0 ; mlx90393 - pavlodn/HaierProtocol@0.9.24 ; haier + pavlodn/HaierProtocol@0.9.25 ; haier ; This is using the repository until a new release is published to PlatformIO https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library build_flags = diff --git a/tests/test3.yaml b/tests/test3.yaml index e39e711ab3..c31eb45fbd 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -1024,6 +1024,7 @@ climate: name: Haier AC uart_id: uart_12 wifi_signal: true + answer_timeout: 200ms beeper: true outdoor_temperature: name: Haier AC outdoor temperature From 773cd0f414bf917d150606b2b7b0166d60d98563 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sun, 31 Dec 2023 21:01:16 +1100 Subject: [PATCH 100/193] GT911 touchscreen: Fix bug causing touch button release to fail (#6042) * Fix bug causing gt911 touch button release to fail * Cache button state and report changes only --- .../gt911/touchscreen/gt911_touchscreen.cpp | 15 ++++++++------- .../gt911/touchscreen/gt911_touchscreen.h | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp index 84854d5b0d..68ed66a89f 100644 --- a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +++ b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp @@ -14,6 +14,7 @@ static const uint8_t GET_TOUCHES[2] = {0x81, 0x4F}; static const uint8_t GET_SWITCHES[2] = {0x80, 0x4D}; static const uint8_t GET_MAX_VALUES[2] = {0x80, 0x48}; static const size_t MAX_TOUCHES = 5; // max number of possible touches reported +static const size_t MAX_BUTTONS = 4; // max number of buttons scanned #define ERROR_CHECK(err) \ if ((err) != i2c::ERROR_OK) { \ @@ -79,9 +80,6 @@ void GT911Touchscreen::update_touches() { return; } - if (num_of_touches == 0) - return; - err = this->write(GET_TOUCHES, sizeof(GET_TOUCHES), false); ERROR_CHECK(err); // num_of_touches is guaranteed to be 0..5. Also read the key data @@ -94,10 +92,13 @@ void GT911Touchscreen::update_touches() { uint16_t y = encode_uint16(data[i][4], data[i][3]); this->add_raw_touch_position_(id, x, y); } - auto keys = data[num_of_touches][0]; - for (size_t i = 0; i != 4; i++) { - for (auto *listener : this->button_listeners_) - listener->update_button(i, (keys & (1 << i)) != 0); + auto keys = data[num_of_touches][0] & ((1 << MAX_BUTTONS) - 1); + if (keys != this->button_state_) { + this->button_state_ = keys; + for (size_t i = 0; i != MAX_BUTTONS; i++) { + for (auto *listener : this->button_listeners_) + listener->update_button(i, (keys & (1 << i)) != 0); + } } } diff --git a/esphome/components/gt911/touchscreen/gt911_touchscreen.h b/esphome/components/gt911/touchscreen/gt911_touchscreen.h index 44875de5f1..a9e1279ed3 100644 --- a/esphome/components/gt911/touchscreen/gt911_touchscreen.h +++ b/esphome/components/gt911/touchscreen/gt911_touchscreen.h @@ -26,6 +26,7 @@ class GT911Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice InternalGPIOPin *interrupt_pin_{}; std::vector button_listeners_; + uint8_t button_state_{0xFF}; // last button state. Initial FF guarantees first update. }; } // namespace gt911 From ae52164d9c9c15b8b15c4b6c2127509ed5b092d3 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 2 Jan 2024 10:34:40 +1100 Subject: [PATCH 101/193] Display: Introduce `draw_pixels_at()` method for fast block display rendering (#6034) * Introduce `draw_pixels_at()` method for fast block display rendering * Add check for 18 vs 16 bit display. --- esphome/components/display/display.cpp | 35 +++++++++++++++++++ esphome/components/display/display.h | 29 +++++++++++++++ .../components/ili9xxx/ili9xxx_display.cpp | 29 +++++++++++++++ esphome/components/ili9xxx/ili9xxx_display.h | 3 ++ 4 files changed, 96 insertions(+) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index 88ee64ea55..f32fda4794 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -35,6 +35,41 @@ void HOT Display::line(int x1, int y1, int x2, int y2, Color color) { } } } + +void Display::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorOrder order, + ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { + size_t line_stride = x_offset + w + x_pad; // length of each source line in pixels + uint32_t color_value; + for (int y = 0; y != h; y++) { + size_t source_idx = (y_offset + y) * line_stride + x_offset; + size_t source_idx_mod; + for (int x = 0; x != w; x++, source_idx++) { + switch (bitness) { + default: + color_value = ptr[source_idx]; + break; + case COLOR_BITNESS_565: + source_idx_mod = source_idx * 2; + if (big_endian) { + color_value = (ptr[source_idx_mod] << 8) + ptr[source_idx_mod + 1]; + } else { + color_value = ptr[source_idx_mod] + (ptr[source_idx_mod + 1] << 8); + } + break; + case COLOR_BITNESS_888: + source_idx_mod = source_idx * 3; + if (big_endian) { + color_value = (ptr[source_idx_mod + 0] << 16) + (ptr[source_idx_mod + 1] << 8) + ptr[source_idx_mod + 2]; + } else { + color_value = ptr[source_idx_mod + 0] + (ptr[source_idx_mod + 1] << 8) + (ptr[source_idx_mod + 2] << 16); + } + break; + } + this->draw_pixel_at(x + x_start, y + y_start, ColorUtil::to_color(color_value, order, bitness)); + } + } +} + void HOT Display::horizontal_line(int x, int y, int width, Color color) { // Future: Could be made more efficient by manipulating buffer directly in certain rotations. for (int i = x; i < x + width; i++) diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index 3afcfb9528..2a2a9b80c8 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -8,6 +8,7 @@ #include "esphome/core/color.h" #include "esphome/core/automation.h" #include "esphome/core/time.h" +#include "display_color_utils.h" #ifdef USE_GRAPH #include "esphome/components/graph/graph.h" @@ -185,6 +186,34 @@ class Display : public PollingComponent { /// Set a single pixel at the specified coordinates to the given color. virtual void draw_pixel_at(int x, int y, Color color) = 0; + /** Given an array of pixels encoded in the nominated format, draw these into the display's buffer. + * The naive implementation here will work in all cases, but can be overridden by sub-classes + * in order to optimise the procedure. + * The parameters describe a rectangular block of pixels, potentially within a larger buffer. + * + * \param x_start The starting destination x position + * \param y_start The starting destination y position + * \param w the width of the pixel block + * \param h the height of the pixel block + * \param ptr A pointer to the start of the data to be copied + * \param order The ordering of the colors + * \param bitness Defines the number of bits and their format for each pixel + * \param big_endian True if 16 bit values are stored big-endian + * \param x_offset The initial x-offset into the source buffer. + * \param y_offset The initial y-offset into the source buffer. + * \param x_pad How many pixels are in each line after the end of the pixels to be copied. + * + * The length of each source buffer line (stride) will be x_offset + w + x_pad. + */ + virtual void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorOrder order, + ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad); + + /// Convenience overload for base case where the pixels are packed into the buffer with no gaps (e.g. suits LVGL.) + void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorOrder order, + ColorBitness bitness, bool big_endian) { + this->draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, 0, 0, 0); + } + /// Draw a straight line from the point [x1,y1] to [x2,y2] with the given color. void line(int x1, int y1, int x2, int y2, Color color = COLOR_ON); diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index b315c8be87..ab577b3875 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -276,6 +276,35 @@ void ILI9XXXDisplay::display_() { this->y_high_ = 0; } +// note that this bypasses the buffer and writes directly to the display. +void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, + display::ColorOrder order, display::ColorBitness bitness, bool big_endian, + int x_offset, int y_offset, int x_pad) { + if (w <= 0 || h <= 0) + return; + // if color mapping or software rotation is required, hand this off to the parent implementation. This will + // do color conversion pixel-by-pixel into the buffer and draw it later. If this is happening the user has not + // configured the renderer well. + if (this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || bitness != display::COLOR_BITNESS_565 || !big_endian || + this->is_18bitdisplay_) { + return display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, + x_pad); + } + this->enable(); + this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); + // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display. + if (x_offset == 0 && x_pad == 0 && y_offset == 0) { + // we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't bother + this->write_array(ptr, w * h * 2); + } else { + auto stride = x_offset + w + x_pad; + for (size_t y = 0; y != h; y++) { + this->write_array(ptr + (y + y_offset) * stride + x_offset, w * 2); + } + } + this->disable(); +} + // should return the total size: return this->get_width_internal() * this->get_height_internal() * 2 // 16bit color // values per bit is huge uint32_t ILI9XXXDisplay::get_buffer_length_() { return this->get_width_internal() * this->get_height_internal(); } diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index bf4889afe1..590be3e364 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/components/spi/spi.h" #include "esphome/components/display/display_buffer.h" +#include "esphome/components/display/display_color_utils.h" #include "ili9xxx_defines.h" #include "ili9xxx_init.h" @@ -84,6 +85,8 @@ class ILI9XXXDisplay : public display::DisplayBuffer, void setup() override; display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } + void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, + display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; protected: void draw_absolute_pixel_internal(int x, int y, Color color) override; From a2e152ad1252444a9d12e3a129270f62076d4c12 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:00:52 +1100 Subject: [PATCH 102/193] clang-format and clang-tidy scripts: More robust algorithm to find correct executable (#6041) * More robust algorithm to find correct executable * Revise message wording * Add clang-tidy and clang-format to requirements.txt. Add to message explaining install process. * Extracted get_binary to helpers.py. Use execptions for clean exit. * Add parameter types * clang-{tidy,format} in requirements_test.txt clean up script exit * Kill processes on ^C * Move clang-tidy and clang-format into requirements_dev.txt --- requirements_dev.txt | 3 +++ requirements_test.txt | 2 -- script/clang-format | 41 +++++++++++++++++++---------------------- script/clang-tidy | 36 +++++++++++++++--------------------- script/helpers.py | 36 ++++++++++++++++++++++++++++++++++++ script/setup | 6 +++++- 6 files changed, 78 insertions(+), 46 deletions(-) create mode 100644 requirements_dev.txt diff --git a/requirements_dev.txt b/requirements_dev.txt new file mode 100644 index 0000000000..6b6319d0a0 --- /dev/null +++ b/requirements_dev.txt @@ -0,0 +1,3 @@ +# Useful stuff when working in a development environment +clang-format==13.0.1 +clang-tidy==14.0.6 diff --git a/requirements_test.txt b/requirements_test.txt index 18c6dedf3e..401f9cb30f 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -11,5 +11,3 @@ pytest-mock==3.12.0 pytest-asyncio==0.23.2 asyncmock==0.4.2 hypothesis==5.49.0 - -clang-format==13.0.1 ; platform_machine != 'armv7l' diff --git a/script/clang-format b/script/clang-format index 165fbd269f..b065d80795 100755 --- a/script/clang-format +++ b/script/clang-format @@ -1,6 +1,12 @@ #!/usr/bin/env python3 -from helpers import print_error_for_file, get_output, git_ls_files, filter_changed +from helpers import ( + print_error_for_file, + get_output, + git_ls_files, + filter_changed, + get_binary, +) import argparse import click import colorama @@ -13,11 +19,12 @@ import sys import threading -def run_format(args, queue, lock, failed_files): + +def run_format(executable, args, queue, lock, failed_files): """Takes filenames out of queue and runs clang-format on them.""" while True: path = queue.get() - invocation = ["clang-format-13"] + invocation = [executable] if args.inplace: invocation.append("-i") else: @@ -58,22 +65,6 @@ def main(): ) args = parser.parse_args() - try: - get_output("clang-format-13", "-version") - except: - print( - """ - Oops. It looks like clang-format is not installed. - - Please check you can run "clang-format-13 -version" in your terminal and install - clang-format (v13) if necessary. - - Note you can also upload your code as a pull request on GitHub and see the CI check - output to apply clang-format. - """ - ) - return 1 - files = [] for path in git_ls_files(["*.cpp", "*.h", "*.tcc"]): files.append(os.path.relpath(path, os.getcwd())) @@ -90,11 +81,12 @@ def main(): failed_files = [] try: + executable = get_binary("clang-format", 13) task_queue = queue.Queue(args.jobs) lock = threading.Lock() for _ in range(args.jobs): t = threading.Thread( - target=run_format, args=(args, task_queue, lock, failed_files) + target=run_format, args=(executable, args, task_queue, lock, failed_files) ) t.daemon = True t.start() @@ -109,13 +101,18 @@ def main(): # Wait for all threads to be done. task_queue.join() + except FileNotFoundError as ex: + return 1 except KeyboardInterrupt: print() print("Ctrl-C detected, goodbye.") + # Kill subprocesses (and ourselves!) + # No simple, clean alternative appears to be available. os.kill(0, 9) + return 2 # Will not execute. - sys.exit(len(failed_files)) + return len(failed_files) if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/script/clang-tidy b/script/clang-tidy index b025221fa8..97e4ba0d48 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -11,6 +11,7 @@ from helpers import ( load_idedata, root_path, basepath, + get_binary, ) import argparse import click @@ -26,6 +27,7 @@ import tempfile import threading + def clang_options(idedata): cmd = [] @@ -110,10 +112,12 @@ def clang_options(idedata): return cmd -def run_tidy(args, options, tmpdir, queue, lock, failed_files): +pids = set() + +def run_tidy(executable, args, options, tmpdir, queue, lock, failed_files): while True: path = queue.get() - invocation = ["clang-tidy-14"] + invocation = [executable] if tmpdir is not None: invocation.append("--export-fixes") @@ -193,22 +197,6 @@ def main(): ) args = parser.parse_args() - try: - get_output("clang-tidy-14", "-version") - except: - print( - """ - Oops. It looks like clang-tidy-14 is not installed. - - Please check you can run "clang-tidy-14 -version" in your terminal and install - clang-tidy (v14) if necessary. - - Note you can also upload your code as a pull request on GitHub and see the CI check - output to apply clang-tidy. - """ - ) - return 1 - idedata = load_idedata(args.environment) options = clang_options(idedata) @@ -242,12 +230,13 @@ def main(): failed_files = [] try: + executable = get_binary("clang-tidy", 14) task_queue = queue.Queue(args.jobs) lock = threading.Lock() for _ in range(args.jobs): t = threading.Thread( target=run_tidy, - args=(args, options, tmpdir, task_queue, lock, failed_files), + args=(executable, args, options, tmpdir, task_queue, lock, failed_files), ) t.daemon = True t.start() @@ -262,12 +251,17 @@ def main(): # Wait for all threads to be done. task_queue.join() + except FileNotFoundError as ex: + return 1 except KeyboardInterrupt: print() print("Ctrl-C detected, goodbye.") if tmpdir: shutil.rmtree(tmpdir) + # Kill subprocesses (and ourselves!) + # No simple, clean alternative appears to be available. os.kill(0, 9) + return 2 # Will not execute. if args.fix and failed_files: print("Applying fixes ...") @@ -277,8 +271,8 @@ def main(): print("Error applying fixes.\n", file=sys.stderr) raise - sys.exit(len(failed_files)) + return len(failed_files) if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/script/helpers.py b/script/helpers.py index b1908e9875..a971fdf475 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -153,3 +153,39 @@ def load_idedata(environment): temp_idedata.write_text(json.dumps(data, indent=2) + "\n") return data + + +def get_binary(name: str, version: str) -> str: + binary_file = f"{name}-{version}" + try: + result = subprocess.check_output([binary_file, "-version"]) + if result.returncode == 0: + return binary_file + except Exception: + pass + binary_file = name + try: + result = subprocess.run( + [binary_file, "-version"], text=True, capture_output=True + ) + if result.returncode == 0 and (f"version {version}") in result.stdout: + return binary_file + raise FileNotFoundError(f"{name} not found") + + except FileNotFoundError as ex: + print( + f""" + Oops. It looks like {name} is not installed. It should be available under venv/bin + and in PATH after running in turn: + script/setup + source venv/bin/activate. + + Please confirm you can run "{name} -version" or "{name}-{version} -version" + in your terminal and install + {name} (v{version}) if necessary. + + Note you can also upload your code as a pull request on GitHub and see the CI check + output to apply {name} + """ + ) + raise diff --git a/script/setup b/script/setup index ba3b544352..9f448cf5c4 100755 --- a/script/setup +++ b/script/setup @@ -15,10 +15,14 @@ if [ -n "$DEVCONTAINER" ];then git config --global --add safe.directory "$PWD" fi -pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt +pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt -r requirements_dev.txt pip3 install setuptools wheel pip3 install --no-use-pep517 -e . pre-commit install script/platformio_install_deps.py platformio.ini --libraries --tools --platforms + +echo +echo +echo "Virtual environment created; source venv/bin/activate to use it" From fdd54d74a3ac8893fec8719760698dd3554cb16f Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 7 Jan 2024 19:39:53 -0800 Subject: [PATCH 103/193] Don't crash with invalid adc pin (#6059) * Don't crash with invalid adc pin * lint --- esphome/components/adc/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/adc/__init__.py b/esphome/components/adc/__init__.py index 952fbdd9b9..87d769fec2 100644 --- a/esphome/components/adc/__init__.py +++ b/esphome/components/adc/__init__.py @@ -139,6 +139,9 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = { VARIANT_ESP32C3: { 5: adc2_channel_t.ADC2_CHANNEL_0, }, + VARIANT_ESP32C2: {}, + VARIANT_ESP32C6: {}, + VARIANT_ESP32H2: {}, } From 4202fe65b522f03c8bddbad301381d4685f015fa Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 9 Jan 2024 00:05:52 +0100 Subject: [PATCH 104/193] fix compilation error for libretiny (#6064) --- esphome/components/libretiny/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index e36c08d522..7dca370eff 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -309,7 +309,7 @@ async def component_to_code(config): lt_options["LT_UART_SILENT_ENABLED"] = 0 lt_options["LT_UART_SILENT_ALL"] = 0 # set default UART port - if uart_port := framework.get(CONF_UART_PORT, None) is not None: + if (uart_port := framework.get(CONF_UART_PORT, None)) is not None: lt_options["LT_UART_DEFAULT_PORT"] = uart_port # add custom options lt_options.update(framework[CONF_OPTIONS]) From 14bffaf8a7726ceaa6611fbd101c6dc4549b3b84 Mon Sep 17 00:00:00 2001 From: Ruben van Dijk <15885455+RubenNL@users.noreply.github.com> Date: Tue, 9 Jan 2024 00:12:28 +0100 Subject: [PATCH 105/193] Add questionmark to default glyphs. (#6053) --- esphome/components/font/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index 22a5f6b2c5..a803c7567b 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -235,7 +235,7 @@ FILE_SCHEMA = cv.Schema(_file_schema) DEFAULT_GLYPHS = ( - ' !"%()+=,-.:/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' + ' !"%()+=,-.:/?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' ) CONF_RAW_GLYPH_ID = "raw_glyph_id" From 696bfe6a87124f7cbde34180b05dc3e60def252f Mon Sep 17 00:00:00 2001 From: functionpointer Date: Tue, 9 Jan 2024 00:26:13 +0100 Subject: [PATCH 106/193] pylontech: Fix parsing error with US2000 (#6061) --- esphome/components/pylontech/__init__.py | 2 +- esphome/components/pylontech/pylontech.cpp | 57 +++++++++++++--------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/esphome/components/pylontech/__init__.py b/esphome/components/pylontech/__init__.py index 56fac92e89..197f7e7904 100644 --- a/esphome/components/pylontech/__init__.py +++ b/esphome/components/pylontech/__init__.py @@ -19,7 +19,7 @@ PylontechComponent = pylontech_ns.class_( ) PylontechBattery = pylontech_ns.class_("PylontechBattery") -CV_NUM_BATTERIES = cv.int_range(1, 6) +CV_NUM_BATTERIES = cv.int_range(1, 16) PYLONTECH_COMPONENT_SCHEMA = cv.Schema( { diff --git a/esphome/components/pylontech/pylontech.cpp b/esphome/components/pylontech/pylontech.cpp index 4bfa876110..b33f4d4874 100644 --- a/esphome/components/pylontech/pylontech.cpp +++ b/esphome/components/pylontech/pylontech.cpp @@ -1,5 +1,6 @@ #include "pylontech.h" #include "esphome/core/log.h" +#include "esphome/core/helpers.h" namespace esphome { namespace pylontech { @@ -34,26 +35,30 @@ void PylontechComponent::setup() { void PylontechComponent::update() { this->write_str("pwr\n"); } void PylontechComponent::loop() { - uint8_t data; - - // pylontech sends a lot of data very suddenly - // we need to quickly put it all into our own buffer, otherwise the uart's buffer will overflow - while (this->available() > 0) { - if (this->read_byte(&data)) { - buffer_[buffer_index_write_] += (char) data; - if (buffer_[buffer_index_write_].back() == static_cast(ASCII_LF) || - buffer_[buffer_index_write_].length() >= MAX_DATA_LENGTH_BYTES) { - // complete line received - buffer_index_write_ = (buffer_index_write_ + 1) % NUM_BUFFERS; + if (this->available() > 0) { + // pylontech sends a lot of data very suddenly + // we need to quickly put it all into our own buffer, otherwise the uart's buffer will overflow + uint8_t data; + int recv = 0; + while (this->available() > 0) { + if (this->read_byte(&data)) { + buffer_[buffer_index_write_] += (char) data; + recv++; + if (buffer_[buffer_index_write_].back() == static_cast(ASCII_LF) || + buffer_[buffer_index_write_].length() >= MAX_DATA_LENGTH_BYTES) { + // complete line received + buffer_index_write_ = (buffer_index_write_ + 1) % NUM_BUFFERS; + } } } - } - - // only process one line per call of loop() to not block esphome for too long - if (buffer_index_read_ != buffer_index_write_) { - this->process_line_(buffer_[buffer_index_read_]); - buffer_[buffer_index_read_].clear(); - buffer_index_read_ = (buffer_index_read_ + 1) % NUM_BUFFERS; + ESP_LOGV(TAG, "received %d bytes", recv); + } else { + // only process one line per call of loop() to not block esphome for too long + if (buffer_index_read_ != buffer_index_write_) { + this->process_line_(buffer_[buffer_index_read_]); + buffer_[buffer_index_read_].clear(); + buffer_index_read_ = (buffer_index_read_ + 1) % NUM_BUFFERS; + } } } @@ -66,10 +71,11 @@ void PylontechComponent::process_line_(std::string &buffer) { // clang-format on PylontechListener::LineContents l{}; - const int parsed = sscanf( // NOLINT - buffer.c_str(), "%d %d %d %d %d %d %d %d %7s %7s %7s %7s %d%% %*d-%*d-%*d %*d:%*d:%*d %*s %*s %d %*s", // NOLINT - &l.bat_num, &l.volt, &l.curr, &l.tempr, &l.tlow, &l.thigh, &l.vlow, &l.vhigh, l.base_st, l.volt_st, // NOLINT - l.curr_st, l.temp_st, &l.coulomb, &l.mostempr); // NOLINT + char mostempr_s[6]; + const int parsed = sscanf( // NOLINT + buffer.c_str(), "%d %d %d %d %d %d %d %d %7s %7s %7s %7s %d%% %*d-%*d-%*d %*d:%*d:%*d %*s %*s %5s %*s", // NOLINT + &l.bat_num, &l.volt, &l.curr, &l.tempr, &l.tlow, &l.thigh, &l.vlow, &l.vhigh, l.base_st, l.volt_st, // NOLINT + l.curr_st, l.temp_st, &l.coulomb, mostempr_s); // NOLINT if (l.bat_num <= 0) { ESP_LOGD(TAG, "invalid bat_num in line %s", buffer.substr(0, buffer.size() - 2).c_str()); @@ -79,6 +85,13 @@ void PylontechComponent::process_line_(std::string &buffer) { ESP_LOGW(TAG, "invalid line: found only %d items in %s", parsed, buffer.substr(0, buffer.size() - 2).c_str()); return; } + auto mostempr_parsed = parse_number(mostempr_s); + if (mostempr_parsed.has_value()) { + l.mostempr = mostempr_parsed.value(); + } else { + l.mostempr = -300; + ESP_LOGW(TAG, "bat_num %d: received no mostempr", l.bat_num); + } for (PylontechListener *listener : this->listeners_) { listener->on_line_read(&l); From 9bdb9dc1a37c0a73258c8756b264874dcc90ed75 Mon Sep 17 00:00:00 2001 From: functionpointer Date: Tue, 9 Jan 2024 00:30:37 +0100 Subject: [PATCH 107/193] pylontech: fix voltage_low and voltage_high wrong unit (#6060) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: だから <82636574+Dackara@users.noreply.github.com> --- esphome/components/pylontech/sensor/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/pylontech/sensor/__init__.py b/esphome/components/pylontech/sensor/__init__.py index 0423f3370c..a1477c627f 100644 --- a/esphome/components/pylontech/sensor/__init__.py +++ b/esphome/components/pylontech/sensor/__init__.py @@ -59,14 +59,14 @@ TYPES: dict[str, cv.Schema] = { device_class=DEVICE_CLASS_TEMPERATURE, ), CONF_VOLTAGE_LOW: sensor.sensor_schema( - unit_of_measurement=UNIT_CELSIUS, - accuracy_decimals=1, - device_class=DEVICE_CLASS_TEMPERATURE, + unit_of_measurement=UNIT_VOLT, + accuracy_decimals=3, + device_class=DEVICE_CLASS_VOLTAGE, ), CONF_VOLTAGE_HIGH: sensor.sensor_schema( - unit_of_measurement=UNIT_CELSIUS, - accuracy_decimals=1, - device_class=DEVICE_CLASS_TEMPERATURE, + unit_of_measurement=UNIT_VOLT, + accuracy_decimals=3, + device_class=DEVICE_CLASS_VOLTAGE, ), CONF_COULOMB: sensor.sensor_schema( unit_of_measurement=UNIT_PERCENT, From 886d1a2d00d77e1a572fdeda6341593c8557c119 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 09:38:50 +0900 Subject: [PATCH 108/193] Bump flake8 from 6.1.0 to 7.0.0 (#6058) 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 401f9cb30f..0348ef6cb2 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,5 +1,5 @@ pylint==3.0.3 -flake8==6.1.0 # also change in .pre-commit-config.yaml when updating +flake8==7.0.0 # also change in .pre-commit-config.yaml when updating black==23.12.0 # also change in .pre-commit-config.yaml when updating pyupgrade==3.15.0 # also change in .pre-commit-config.yaml when updating pre-commit From 6061699eff0a651643e03e1130b6b6bd45d2a4e1 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 9 Jan 2024 01:41:34 +0100 Subject: [PATCH 109/193] Nextion enable upload from https when using esp-idf (#6051) --- esphome/components/nextion/display.py | 6 ++++++ .../components/nextion/nextion_upload_idf.cpp | 20 +++++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index fd61dfa2be..27f2030f0d 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -2,6 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import display, uart +from esphome.components import esp32 from esphome.const import ( CONF_ID, CONF_LAMBDA, @@ -96,6 +97,11 @@ async def to_code(config): if CORE.is_esp32 and CORE.using_arduino: cg.add_library("WiFiClientSecure", None) cg.add_library("HTTPClient", None) + elif CORE.is_esp32 and CORE.using_esp_idf: + esp32.add_idf_sdkconfig_option("CONFIG_ESP_TLS_INSECURE", True) + esp32.add_idf_sdkconfig_option( + "CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY", True + ) elif CORE.is_esp8266 and CORE.using_arduino: cg.add_library("ESP8266HTTPClient", None) diff --git a/esphome/components/nextion/nextion_upload_idf.cpp b/esphome/components/nextion/nextion_upload_idf.cpp index 57bb9c45e8..709ff65b12 100644 --- a/esphome/components/nextion/nextion_upload_idf.cpp +++ b/esphome/components/nextion/nextion_upload_idf.cpp @@ -24,7 +24,7 @@ int Nextion::upload_range(const std::string &url, int range_start) { ESP_LOGVV(TAG, "url: %s", url.c_str()); uint range_size = this->tft_size_ - range_start; ESP_LOGVV(TAG, "tft_size_: %i", this->tft_size_); - ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size()); + ESP_LOGV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); int range_end = (range_start == 0) ? std::min(this->tft_size_, 16383) : this->tft_size_; if (range_size <= 0 or range_end <= range_start) { ESP_LOGE(TAG, "Invalid range"); @@ -37,6 +37,8 @@ int Nextion::upload_range(const std::string &url, int range_start) { esp_http_client_config_t config = { .url = url.c_str(), .cert_pem = nullptr, + .disable_auto_redirect = false, + .max_redirection_count = 10, }; esp_http_client_handle_t client = esp_http_client_init(&config); @@ -44,7 +46,7 @@ int Nextion::upload_range(const std::string &url, int range_start) { sprintf(range_header, "bytes=%d-%d", range_start, range_end); ESP_LOGV(TAG, "Requesting range: %s", range_header); esp_http_client_set_header(client, "Range", range_header); - ESP_LOGVV(TAG, "Available heap: %u", esp_get_free_heap_size()); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); ESP_LOGV(TAG, "Opening http connetion"); esp_err_t err; @@ -70,13 +72,13 @@ int Nextion::upload_range(const std::string &url, int range_start) { std::string recv_string; if (buffer == nullptr) { ESP_LOGE(TAG, "Failed to allocate memory for buffer"); - ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size()); + ESP_LOGV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); } else { ESP_LOGV(TAG, "Memory for buffer allocated successfully"); while (true) { App.feed_wdt(); - ESP_LOGVV(TAG, "Available heap: %u", esp_get_free_heap_size()); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); int read_len = esp_http_client_read(client, reinterpret_cast(buffer), 4096); ESP_LOGVV(TAG, "Read %d bytes from HTTP client, writing to UART", read_len); if (read_len > 0) { @@ -145,17 +147,19 @@ bool Nextion::upload_tft() { // Define the configuration for the HTTP client ESP_LOGV(TAG, "Establishing connection to HTTP server"); - ESP_LOGVV(TAG, "Available heap: %u", esp_get_free_heap_size()); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); esp_http_client_config_t config = { .url = this->tft_url_.c_str(), .cert_pem = nullptr, .method = HTTP_METHOD_HEAD, .timeout_ms = 15000, + .disable_auto_redirect = false, + .max_redirection_count = 10, }; // Initialize the HTTP client with the configuration ESP_LOGV(TAG, "Initializing HTTP client"); - ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size()); + ESP_LOGV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); esp_http_client_handle_t http = esp_http_client_init(&config); if (!http) { ESP_LOGE(TAG, "Failed to initialize HTTP client."); @@ -164,7 +168,7 @@ bool Nextion::upload_tft() { // Perform the HTTP request ESP_LOGV(TAG, "Check if the client could connect"); - ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size()); + ESP_LOGV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); esp_err_t err = esp_http_client_perform(http); if (err != ESP_OK) { ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err)); @@ -256,7 +260,7 @@ bool Nextion::upload_end(bool successful) { this->soft_reset(); vTaskDelay(pdMS_TO_TICKS(1500)); // NOLINT if (successful) { - ESP_LOGD(TAG, "Restarting esphome"); + ESP_LOGD(TAG, "Restarting ESPHome"); esp_restart(); // NOLINT(readability-static-accessed-through-instance) } return successful; From e3d146ee44e133d14cf797ab3bea42b7ff72d8c0 Mon Sep 17 00:00:00 2001 From: Robert Paskowitz Date: Mon, 8 Jan 2024 16:44:08 -0800 Subject: [PATCH 110/193] Support full (>460 char) dumps of Pronto IR commands (#6040) Co-authored-by: Rob Paskowitz --- .../remote_base/pronto_protocol.cpp | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/esphome/components/remote_base/pronto_protocol.cpp b/esphome/components/remote_base/pronto_protocol.cpp index 4b6977e1a2..ccae64449a 100644 --- a/esphome/components/remote_base/pronto_protocol.cpp +++ b/esphome/components/remote_base/pronto_protocol.cpp @@ -227,16 +227,17 @@ optional ProntoProtocol::decode(RemoteReceiveData src) { } void ProntoProtocol::dump(const ProntoData &data) { - std::string first, rest; - if (data.data.size() < 230) { - first = data.data; - } else { - first = data.data.substr(0, 229); - rest = data.data.substr(230); - } - ESP_LOGI(TAG, "Received Pronto: data=%s", first.c_str()); - if (!rest.empty()) { - ESP_LOGI(TAG, "%s", rest.c_str()); + std::string rest; + + rest = data.data; + ESP_LOGI(TAG, "Received Pronto: data="); + while (true) { + ESP_LOGI(TAG, "%s", rest.substr(0, 230).c_str()); + if (rest.size() > 230) { + rest = rest.substr(230); + } else { + break; + } } } From 2bb5343d2787f2d1d120aa81b808944247aa2672 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 9 Jan 2024 01:45:46 +0100 Subject: [PATCH 111/193] Extends UART change at runtime to ESP8266 (#6019) --- esphome/components/uart/uart_component.h | 4 ++-- .../uart/uart_component_esp8266.cpp | 20 +++++++++++++++++-- .../components/uart/uart_component_esp8266.h | 15 ++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/esphome/components/uart/uart_component.h b/esphome/components/uart/uart_component.h index 6f27f36bcb..a57910c1a1 100644 --- a/esphome/components/uart/uart_component.h +++ b/esphome/components/uart/uart_component.h @@ -122,7 +122,7 @@ class UARTComponent { // @return Baud rate in bits per second. uint32_t get_baud_rate() const { return baud_rate_; } -#ifdef USE_ESP32 +#if defined(USE_ESP8266) || defined(USE_ESP32) /** * Load the UART settings. * @param dump_config If true (default), output the new settings to logs; otherwise, change settings quietly. @@ -147,7 +147,7 @@ class UARTComponent { * This will load the current UART interface with the latest settings (baud_rate, parity, etc). */ virtual void load_settings(){}; -#endif // USE_ESP32 +#endif // USE_ESP8266 || USE_ESP32 #ifdef USE_UART_DEBUGGER void add_debug_callback(std::function &&callback) { diff --git a/esphome/components/uart/uart_component_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp index 529108f439..fa8dc3fb17 100644 --- a/esphome/components/uart/uart_component_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -98,10 +98,26 @@ void ESP8266UartComponent::setup() { } } +void ESP8266UartComponent::load_settings(bool dump_config) { + ESP_LOGCONFIG(TAG, "Loading UART bus settings..."); + if (this->hw_serial_ != nullptr) { + SerialConfig config = static_cast(get_config()); + this->hw_serial_->begin(this->baud_rate_, config); + this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); + } else { + this->sw_serial_->setup(this->tx_pin_, this->rx_pin_, this->baud_rate_, this->stop_bits_, this->data_bits_, + this->parity_, this->rx_buffer_size_); + } + if (dump_config) { + ESP_LOGCONFIG(TAG, "UART bus was reloaded."); + this->dump_config(); + } +} + void ESP8266UartComponent::dump_config() { ESP_LOGCONFIG(TAG, "UART Bus:"); - LOG_PIN(" TX Pin: ", tx_pin_); - LOG_PIN(" RX Pin: ", rx_pin_); + LOG_PIN(" TX Pin: ", this->tx_pin_); + LOG_PIN(" RX Pin: ", this->rx_pin_); if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); // NOLINT } diff --git a/esphome/components/uart/uart_component_esp8266.h b/esphome/components/uart/uart_component_esp8266.h index eed14f3265..749dd4c61e 100644 --- a/esphome/components/uart/uart_component_esp8266.h +++ b/esphome/components/uart/uart_component_esp8266.h @@ -63,6 +63,21 @@ class ESP8266UartComponent : public UARTComponent, public Component { uint32_t get_config(); + /** + * Load the UART with the current settings. + * @param dump_config (Optional, default `true`): True for displaying new settings or + * false to change it quitely + * + * Example: + * ```cpp + * id(uart1).load_settings(); + * ``` + * + * This will load the current UART interface with the latest settings (baud_rate, parity, etc). + */ + void load_settings(bool dump_config) override; + void load_settings() override { this->load_settings(true); } + protected: void check_logger_conflict() override; From 869cdf122de7beb9a77618d3fee9e020057eadf1 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 9 Jan 2024 01:47:48 +0100 Subject: [PATCH 112/193] Nextion draw QR code at runtime (#6027) --- esphome/components/nextion/nextion.h | 44 +++++++++++++++++++ .../components/nextion/nextion_commands.cpp | 13 ++++++ 2 files changed, 57 insertions(+) diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 2f52a032c4..eef2c61638 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -750,6 +750,50 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ void filled_circle(int center_x, int center_y, int radius, Color color); + /** + * Draws a QR code in the screen + * @param x1 The top left x coordinate to start the QR code. + * @param y1 The top left y coordinate to start the QR code. + * @param content The content of the QR code (as a plain text - Nextion will generate the QR code). + * @param size The size (in pixels) for the QR code. Defaults to 200px. + * @param background_color The background color to draw with (as rgb565 integer). Defaults to 65535 (white). + * @param foreground_color The foreground color to draw with (as rgb565 integer). Defaults to 0 (black). + * @param logo_pic The picture id for the logo in the center of the QR code. Defaults to -1 (no logo). + * @param border_width The border width (in pixels) for the QR code. Defaults to 8px. + * + * Example: + * ```cpp + * it.qrcode(25, 25, "WIFI:S:MySSID;T:WPA;P:MyPassW0rd;;"); + * ``` + * + * 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); + /** + * Draws a QR code in the screen + * @param x1 The top left x coordinate to start the QR code. + * @param y1 The top left y coordinate to start the QR code. + * @param content The content of the QR code (as a plain text - Nextion will generate the QR code). + * @param size The size (in pixels) for the QR code. Defaults to 200px. + * @param background_color The background color to draw with (as Color). Defaults to 65535 (white). + * @param foreground_color The foreground color to draw with (as Color). Defaults to 0 (black). + * @param logo_pic The picture id for the logo in the center of the QR code. Defaults to -1 (no logo). + * @param border_width The border width (in pixels) for the QR code. Defaults to 8px. + * + * Example: + * ```cpp + * auto blue = Color(0, 0, 255); + * auto red = Color(255, 0, 0); + * it.qrcode(25, 25, "WIFI:S:MySSID;T:WPA;P:MyPassW0rd;;", 150, blue, red); + * ``` + * + * 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); + /** Set the brightness of the backlight. * * @param brightness The brightness percentage from 0 to 1.0. diff --git a/esphome/components/nextion/nextion_commands.cpp b/esphome/components/nextion/nextion_commands.cpp index 8512ea5573..c4849d6050 100644 --- a/esphome/components/nextion/nextion_commands.cpp +++ b/esphome/components/nextion/nextion_commands.cpp @@ -294,6 +294,19 @@ void Nextion::filled_circle(int center_x, int center_y, int radius, Color color) 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) { + 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); +} + void Nextion::set_nextion_rtc_time(ESPTime time) { this->add_no_result_to_queue_with_printf_("rtc0", "rtc0=%u", time.year); this->add_no_result_to_queue_with_printf_("rtc1", "rtc1=%u", time.month); From 79d00ec9136a03dcd86b1714edbeb9cba4d6eb4a Mon Sep 17 00:00:00 2001 From: Dusan Cervenka Date: Tue, 9 Jan 2024 02:07:21 +0100 Subject: [PATCH 113/193] Extend i2s config options (#6056) --- esphome/components/i2s_audio/microphone/__init__.py | 6 ++++++ .../i2s_audio/microphone/i2s_audio_microphone.cpp | 4 ++-- .../components/i2s_audio/microphone/i2s_audio_microphone.h | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/esphome/components/i2s_audio/microphone/__init__.py b/esphome/components/i2s_audio/microphone/__init__.py index b917da3045..5ee359dc26 100644 --- a/esphome/components/i2s_audio/microphone/__init__.py +++ b/esphome/components/i2s_audio/microphone/__init__.py @@ -20,7 +20,9 @@ DEPENDENCIES = ["i2s_audio"] CONF_ADC_PIN = "adc_pin" CONF_ADC_TYPE = "adc_type" CONF_PDM = "pdm" +CONF_SAMPLE_RATE = "sample_rate" CONF_BITS_PER_SAMPLE = "bits_per_sample" +CONF_USE_APLL = "use_apll" I2SAudioMicrophone = i2s_audio_ns.class_( "I2SAudioMicrophone", I2SAudioIn, microphone.Microphone, cg.Component @@ -62,9 +64,11 @@ BASE_SCHEMA = microphone.MICROPHONE_SCHEMA.extend( cv.GenerateID(): cv.declare_id(I2SAudioMicrophone), cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent), cv.Optional(CONF_CHANNEL, default="right"): cv.enum(CHANNELS), + cv.Optional(CONF_SAMPLE_RATE, default=16000): cv.int_range(min=1), cv.Optional(CONF_BITS_PER_SAMPLE, default="32bit"): cv.All( _validate_bits, cv.enum(BITS_PER_SAMPLE) ), + cv.Optional(CONF_USE_APLL, default=False): cv.boolean, } ).extend(cv.COMPONENT_SCHEMA) @@ -105,6 +109,8 @@ async def to_code(config): cg.add(var.set_pdm(config[CONF_PDM])) cg.add(var.set_channel(config[CONF_CHANNEL])) + cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) + cg.add(var.set_use_apll(config[CONF_USE_APLL])) await microphone.register_microphone(var, config) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index ec2fe258c9..602d537bcb 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -47,14 +47,14 @@ void I2SAudioMicrophone::start_() { } i2s_driver_config_t config = { .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX), - .sample_rate = 16000, + .sample_rate = this->sample_rate_, .bits_per_sample = this->bits_per_sample_, .channel_format = this->channel_, .communication_format = I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 4, .dma_buf_len = 256, - .use_apll = false, + .use_apll = this->use_apll_, .tx_desc_auto_clear = false, .fixed_mclk = 0, .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index dc6b70047a..68b9a94fbd 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -31,7 +31,9 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub #endif void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; } + void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; } void set_bits_per_sample(i2s_bits_per_sample_t bits_per_sample) { this->bits_per_sample_ = bits_per_sample; } + void set_use_apll(uint32_t use_apll) { this->use_apll_ = use_apll; } protected: void start_(); @@ -45,7 +47,9 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub #endif bool pdm_{false}; i2s_channel_fmt_t channel_; + uint32_t sample_rate_; i2s_bits_per_sample_t bits_per_sample_; + bool use_apll_; HighFrequencyLoopRequester high_freq_; }; From 65e6f9cba98803fc75ee035a225213dbf47932cd Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 9 Jan 2024 12:07:45 +1100 Subject: [PATCH 114/193] Add getter for image data_start (#6036) --- esphome/components/image/image.h | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/image/image.h b/esphome/components/image/image.h index 4e869f5204..5f1f50a134 100644 --- a/esphome/components/image/image.h +++ b/esphome/components/image/image.h @@ -37,6 +37,7 @@ class Image : public display::BaseImage { Color get_pixel(int x, int y, Color color_on = display::COLOR_ON, Color color_off = display::COLOR_OFF) const; int get_width() const override; int get_height() const override; + const uint8_t *get_data_start() { return this->data_start_; } ImageType get_type() const; void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override; From d9def0cb3a757750df4bf2c2a73af761bd559a51 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 8 Jan 2024 15:08:50 -1000 Subject: [PATCH 115/193] Bump hypothesis to 6.92.1 (#6011) --- requirements_test.txt | 2 +- tests/unit_tests/test_core.py | 2 +- tests/unit_tests/test_helpers.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements_test.txt b/requirements_test.txt index 0348ef6cb2..9015152794 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -10,4 +10,4 @@ pytest-cov==4.1.0 pytest-mock==3.12.0 pytest-asyncio==0.23.2 asyncmock==0.4.2 -hypothesis==5.49.0 +hypothesis==6.92.1 diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index efa9ff5677..2860486efe 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -1,7 +1,7 @@ import pytest from hypothesis import given -from hypothesis.provisional import ip_addresses +from hypothesis.strategies import ip_addresses from strategies import mac_addr_strings from esphome import core, const diff --git a/tests/unit_tests/test_helpers.py b/tests/unit_tests/test_helpers.py index fc6bdbcdec..26ebdcf6af 100644 --- a/tests/unit_tests/test_helpers.py +++ b/tests/unit_tests/test_helpers.py @@ -1,7 +1,7 @@ import pytest from hypothesis import given -from hypothesis.provisional import ip_addresses +from hypothesis.strategies import ip_addresses from esphome import helpers From 2be19c4e458c6e86f9357b241792ba2fd0889d2b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 8 Jan 2024 15:13:18 -1000 Subject: [PATCH 116/193] Bump recommended ESP32 IDF to 4.4.6 (#6048) --- esphome/components/esp32/__init__.py | 6 +++--- .../components/esp32_ble_tracker/__init__.py | 20 ++++++++++++------- platformio.ini | 2 +- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 5d17633975..50d6d229f9 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -226,7 +226,7 @@ ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0) # The default/recommended esp-idf framework version # - https://github.com/espressif/esp-idf/releases # - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf -RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 5) +RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 6) # The platformio/espressif32 version to use for esp-idf frameworks # - https://github.com/platformio/platform-espressif32/releases # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 @@ -271,8 +271,8 @@ def _arduino_check_versions(value): def _esp_idf_check_versions(value): value = value.copy() lookups = { - "dev": (cv.Version(5, 1, 0), "https://github.com/espressif/esp-idf.git"), - "latest": (cv.Version(5, 1, 0), None), + "dev": (cv.Version(5, 1, 2), "https://github.com/espressif/esp-idf.git"), + "latest": (cv.Version(5, 1, 2), None), "recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None), } diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 2ead59c025..1edeaadbfd 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -1,23 +1,26 @@ import re + import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation +from esphome.components import esp32_ble +from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.const import ( CONF_ACTIVE, + CONF_DURATION, CONF_ID, CONF_INTERVAL, - CONF_DURATION, - CONF_TRIGGER_ID, CONF_MAC_ADDRESS, - CONF_SERVICE_UUID, CONF_MANUFACTURER_ID, CONF_ON_BLE_ADVERTISE, - CONF_ON_BLE_SERVICE_DATA_ADVERTISE, CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, + CONF_ON_BLE_SERVICE_DATA_ADVERTISE, + CONF_SERVICE_UUID, + CONF_TRIGGER_ID, + KEY_CORE, + KEY_FRAMEWORK_VERSION, ) -from esphome.components import esp32_ble from esphome.core import CORE -from esphome.components.esp32 import add_idf_sdkconfig_option AUTO_LOAD = ["esp32_ble"] DEPENDENCIES = ["esp32"] @@ -263,7 +266,10 @@ async def to_code(config): # https://github.com/espressif/esp-idf/issues/2503 # Match arduino CONFIG_BTU_TASK_STACK_SIZE # https://github.com/espressif/arduino-esp32/blob/fd72cf46ad6fc1a6de99c1d83ba8eba17d80a4ee/tools/sdk/esp32/sdkconfig#L1866 - add_idf_sdkconfig_option("CONFIG_BTU_TASK_STACK_SIZE", 8192) + if CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(4, 4, 6): + add_idf_sdkconfig_option("CONFIG_BT_BTU_TASK_STACK_SIZE", 8192) + else: + add_idf_sdkconfig_option("CONFIG_BTU_TASK_STACK_SIZE", 8192) add_idf_sdkconfig_option("CONFIG_BT_ACL_CONNECTIONS", 9) cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts diff --git a/platformio.ini b/platformio.ini index 2dfaa79a52..f5f510244c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -136,7 +136,7 @@ extra_scripts = post:esphome/components/esp32/post_build.py.script extends = common:idf platform = platformio/espressif32@5.4.0 platform_packages = - platformio/framework-espidf@~3.40405.0 + platformio/framework-espidf@~3.40406.0 framework = espidf lib_deps = From 87301a2e766435f97ece93deb200bc3bd69ed3e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:33:50 -1000 Subject: [PATCH 117/193] Bump pytest from 7.4.3 to 7.4.4 (#6046) 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 9015152794..bf7103f025 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -5,7 +5,7 @@ pyupgrade==3.15.0 # also change in .pre-commit-config.yaml when updating pre-commit # Unit tests -pytest==7.4.3 +pytest==7.4.4 pytest-cov==4.1.0 pytest-mock==3.12.0 pytest-asyncio==0.23.2 From 6dfdcff66caf3f62de6442f7ecb2f194d1232c11 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 8 Jan 2024 15:35:43 -1000 Subject: [PATCH 118/193] dashboard: refactor ping implementation to be more efficient (#6002) --- esphome/dashboard/const.py | 2 + esphome/dashboard/core.py | 5 +- esphome/dashboard/dashboard.py | 99 ++++++++++++++++++++++++++++++++ esphome/dashboard/dns.py | 43 ++++++++++++++ esphome/dashboard/settings.py | 15 +++++ esphome/dashboard/status/ping.py | 85 ++++++++++++++++++++++----- esphome/dashboard/web_server.py | 25 ++++++-- requirements.txt | 2 + 8 files changed, 255 insertions(+), 21 deletions(-) create mode 100644 esphome/dashboard/dns.py diff --git a/esphome/dashboard/const.py b/esphome/dashboard/const.py index ed2b81d3e8..190d6c4a9a 100644 --- a/esphome/dashboard/const.py +++ b/esphome/dashboard/const.py @@ -4,5 +4,7 @@ EVENT_ENTRY_ADDED = "entry_added" EVENT_ENTRY_REMOVED = "entry_removed" EVENT_ENTRY_UPDATED = "entry_updated" EVENT_ENTRY_STATE_CHANGED = "entry_state_changed" +MAX_EXECUTOR_WORKERS = 48 + SENTINEL = object() diff --git a/esphome/dashboard/core.py b/esphome/dashboard/core.py index ffec9784e8..e22d95fba9 100644 --- a/esphome/dashboard/core.py +++ b/esphome/dashboard/core.py @@ -8,6 +8,7 @@ from functools import partial from typing import TYPE_CHECKING, Any, Callable from ..zeroconf import DiscoveredImport +from .dns import DNSCache from .entries import DashboardEntries from .settings import DashboardSettings @@ -69,6 +70,7 @@ class ESPHomeDashboard: "mqtt_ping_request", "mdns_status", "settings", + "dns_cache", ) def __init__(self) -> None: @@ -81,7 +83,8 @@ class ESPHomeDashboard: self.ping_request: asyncio.Event | None = None self.mqtt_ping_request = threading.Event() self.mdns_status: MDNSStatus | None = None - self.settings: DashboardSettings = DashboardSettings() + self.settings = DashboardSettings() + self.dns_cache = DNSCache() async def async_setup(self) -> None: """Setup the dashboard.""" diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 789b14653c..2be98ab3e4 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -1,11 +1,19 @@ from __future__ import annotations import asyncio +import logging import os import socket +import threading +import traceback +from asyncio import events +from concurrent.futures import ThreadPoolExecutor +from time import monotonic +from typing import Any from esphome.storage_json import EsphomeStorageJSON, esphome_storage_path +from .const import MAX_EXECUTOR_WORKERS from .core import DASHBOARD from .web_server import make_app, start_web_server @@ -14,6 +22,95 @@ ENV_DEV = "ESPHOME_DASHBOARD_DEV" settings = DASHBOARD.settings +def can_use_pidfd() -> bool: + """Check if pidfd_open is available. + + Back ported from cpython 3.12 + """ + if not hasattr(os, "pidfd_open"): + return False + try: + pid = os.getpid() + os.close(os.pidfd_open(pid, 0)) + except OSError: + # blocked by security policy like SECCOMP + return False + return True + + +class DashboardEventLoopPolicy(asyncio.DefaultEventLoopPolicy): + """Event loop policy for Home Assistant.""" + + def __init__(self, debug: bool) -> None: + """Init the event loop policy.""" + super().__init__() + self.debug = debug + self._watcher: asyncio.AbstractChildWatcher | None = None + + def _init_watcher(self) -> None: + """Initialize the watcher for child processes. + + Back ported from cpython 3.12 + """ + with events._lock: # type: ignore[attr-defined] # pylint: disable=protected-access + if self._watcher is None: # pragma: no branch + if can_use_pidfd(): + self._watcher = asyncio.PidfdChildWatcher() + else: + self._watcher = asyncio.ThreadedChildWatcher() + if threading.current_thread() is threading.main_thread(): + self._watcher.attach_loop( + self._local._loop # type: ignore[attr-defined] # pylint: disable=protected-access + ) + + @property + def loop_name(self) -> str: + """Return name of the loop.""" + return self._loop_factory.__name__ # type: ignore[no-any-return,attr-defined] + + def new_event_loop(self) -> asyncio.AbstractEventLoop: + """Get the event loop.""" + loop: asyncio.AbstractEventLoop = super().new_event_loop() + loop.set_exception_handler(_async_loop_exception_handler) + + if self.debug: + loop.set_debug(True) + + executor = ThreadPoolExecutor( + thread_name_prefix="SyncWorker", max_workers=MAX_EXECUTOR_WORKERS + ) + loop.set_default_executor(executor) + # bind the built-in time.monotonic directly as loop.time to avoid the + # overhead of the additional method call since its the most called loop + # method and its roughly 10%+ of all the call time in base_events.py + loop.time = monotonic # type: ignore[method-assign] + return loop + + +def _async_loop_exception_handler(_: Any, context: dict[str, Any]) -> None: + """Handle all exception inside the core loop.""" + kwargs = {} + if exception := context.get("exception"): + kwargs["exc_info"] = (type(exception), exception, exception.__traceback__) + + logger = logging.getLogger(__package__) + if source_traceback := context.get("source_traceback"): + stack_summary = "".join(traceback.format_list(source_traceback)) + logger.error( + "Error doing job: %s: %s", + context["message"], + stack_summary, + **kwargs, # type: ignore[arg-type] + ) + return + + logger.error( + "Error doing job: %s", + context["message"], + **kwargs, # type: ignore[arg-type] + ) + + def start_dashboard(args) -> None: """Start the dashboard.""" settings.parse_args(args) @@ -26,6 +123,8 @@ def start_dashboard(args) -> None: storage.save(path) settings.cookie_secret = storage.cookie_secret + asyncio.set_event_loop_policy(DashboardEventLoopPolicy(settings.verbose)) + try: asyncio.run(async_start(args)) except KeyboardInterrupt: diff --git a/esphome/dashboard/dns.py b/esphome/dashboard/dns.py new file mode 100644 index 0000000000..b78a909220 --- /dev/null +++ b/esphome/dashboard/dns.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import asyncio +import sys + +from icmplib import NameLookupError, async_resolve + +if sys.version_info >= (3, 11): + from asyncio import timeout as async_timeout +else: + from async_timeout import timeout as async_timeout + + +async def _async_resolve_wrapper(hostname: str) -> list[str] | Exception: + """Wrap the icmplib async_resolve function.""" + try: + async with async_timeout(2): + return await async_resolve(hostname) + except (asyncio.TimeoutError, NameLookupError, UnicodeError) as ex: + return ex + + +class DNSCache: + """DNS cache for the dashboard.""" + + def __init__(self, ttl: int | None = 120) -> None: + """Initialize the DNSCache.""" + self._cache: dict[str, tuple[float, list[str] | Exception]] = {} + self._ttl = ttl + + async def async_resolve( + self, hostname: str, now_monotonic: float + ) -> list[str] | Exception: + """Resolve a hostname to a list of IP address.""" + if expire_time_addresses := self._cache.get(hostname): + expire_time, addresses = expire_time_addresses + if expire_time > now_monotonic: + return addresses + + expires = now_monotonic + self._ttl + addresses = await _async_resolve_wrapper(hostname) + self._cache[hostname] = (expires, addresses) + return addresses diff --git a/esphome/dashboard/settings.py b/esphome/dashboard/settings.py index 1a5b1620e8..1f05abab4c 100644 --- a/esphome/dashboard/settings.py +++ b/esphome/dashboard/settings.py @@ -14,7 +14,19 @@ from .util.password import password_hash class DashboardSettings: """Settings for the dashboard.""" + __slots__ = ( + "config_dir", + "password_hash", + "username", + "using_password", + "on_ha_addon", + "cookie_secret", + "absolute_config_dir", + "verbose", + ) + def __init__(self) -> None: + """Initialize the dashboard settings.""" self.config_dir: str = "" self.password_hash: str = "" self.username: str = "" @@ -22,8 +34,10 @@ class DashboardSettings: self.on_ha_addon: bool = False self.cookie_secret: str | None = None self.absolute_config_dir: Path | None = None + self.verbose: bool = False def parse_args(self, args: Any) -> None: + """Parse the arguments.""" self.on_ha_addon: bool = args.ha_addon password = args.password or os.getenv("PASSWORD") or "" if not self.on_ha_addon: @@ -33,6 +47,7 @@ class DashboardSettings: self.password_hash = password_hash(password) self.config_dir = args.configuration self.absolute_config_dir = Path(self.config_dir).resolve() + self.verbose = args.verbose CORE.config_path = os.path.join(self.config_dir, ".") @property diff --git a/esphome/dashboard/status/ping.py b/esphome/dashboard/status/ping.py index 989cd1570f..6630f03c9d 100644 --- a/esphome/dashboard/status/ping.py +++ b/esphome/dashboard/status/ping.py @@ -1,20 +1,20 @@ from __future__ import annotations import asyncio -import os +import logging +import time from typing import cast +from icmplib import Host, SocketPermissionError, async_ping + +from ..const import MAX_EXECUTOR_WORKERS from ..core import DASHBOARD -from ..entries import DashboardEntry, bool_to_entry_state +from ..entries import DashboardEntry, EntryState, bool_to_entry_state from ..util.itertools import chunked -from ..util.subprocess import async_system_command_status +_LOGGER = logging.getLogger(__name__) -async def _async_ping_host(host: str) -> bool: - """Ping a host.""" - return await async_system_command_status( - ["ping", "-n" if os.name == "nt" else "-c", "1", host] - ) +GROUP_SIZE = int(MAX_EXECUTOR_WORKERS / 2) class PingStatus: @@ -27,6 +27,10 @@ class PingStatus: """Run the ping status.""" dashboard = DASHBOARD entries = dashboard.entries + privileged = await _can_use_icmp_lib_with_privilege() + if privileged is None: + _LOGGER.warning("Cannot use icmplib because privileges are insufficient") + return while not dashboard.stop_event.is_set(): # Only ping if the dashboard is open @@ -36,15 +40,68 @@ class PingStatus: to_ping: list[DashboardEntry] = [ entry for entry in current_entries if entry.address is not None ] - for ping_group in chunked(to_ping, 16): + + # Resolve DNS for all entries + entries_with_addresses: dict[DashboardEntry, list[str]] = {} + for ping_group in chunked(to_ping, GROUP_SIZE): ping_group = cast(list[DashboardEntry], ping_group) - results = await asyncio.gather( - *(_async_ping_host(entry.address) for entry in ping_group), + now_monotonic = time.monotonic() + dns_results = await asyncio.gather( + *( + dashboard.dns_cache.async_resolve(entry.address, now_monotonic) + for entry in ping_group + ), return_exceptions=True, ) - for entry, result in zip(ping_group, results): + + for entry, result in zip(ping_group, dns_results): if isinstance(result, Exception): - result = False + entries.async_set_state(entry, EntryState.UNKNOWN) + continue + if isinstance(result, BaseException): + raise result + entries_with_addresses[entry] = result + + # Ping all entries with valid addresses + for ping_group in chunked(entries_with_addresses.items(), GROUP_SIZE): + entry_addresses = cast(tuple[DashboardEntry, list[str]], ping_group) + + results = await asyncio.gather( + *( + async_ping(addresses[0], privileged=privileged) + for _, addresses in entry_addresses + ), + return_exceptions=True, + ) + + for entry_addresses, result in zip(entry_addresses, results): + if isinstance(result, Exception): + ping_result = False elif isinstance(result, BaseException): raise result - entries.async_set_state(entry, bool_to_entry_state(result)) + else: + host: Host = result + ping_result = host.is_alive + entry, _ = entry_addresses + entries.async_set_state(entry, bool_to_entry_state(ping_result)) + + +async def _can_use_icmp_lib_with_privilege() -> None | bool: + """Verify we can create a raw socket.""" + try: + await async_ping("127.0.0.1", count=0, timeout=0, privileged=True) + except SocketPermissionError: + try: + await async_ping("127.0.0.1", count=0, timeout=0, privileged=False) + except SocketPermissionError: + _LOGGER.debug( + "Cannot use icmplib because privileges are insufficient to create the" + " socket" + ) + return None + + _LOGGER.debug("Using icmplib in privileged=False mode") + return False + + _LOGGER.debug("Using icmplib in privileged=True mode") + return True diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 6a80865906..c16461d174 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -9,6 +9,7 @@ import hashlib import json import logging import os +import time import secrets import shutil import subprocess @@ -302,16 +303,28 @@ class EsphomePortCommandWebSocket(EsphomeCommandWebSocket): port = json_message["port"] if ( port == "OTA" # pylint: disable=too-many-boolean-expressions - and (mdns := dashboard.mdns_status) and (entry := entries.get(config_file)) and entry.loaded_integrations and "api" in entry.loaded_integrations - and (address := await mdns.async_resolve_host(entry.name)) ): - # Use the IP address if available but only - # if the API is loaded and the device is online - # since MQTT logging will not work otherwise - port = address + if (mdns := dashboard.mdns_status) and ( + address := await mdns.async_resolve_host(entry.name) + ): + # Use the IP address if available but only + # if the API is loaded and the device is online + # since MQTT logging will not work otherwise + port = address + elif ( + entry.address + and ( + address_list := await dashboard.dns_cache.async_resolve( + entry.address, time.monotonic() + ) + ) + and not isinstance(address_list, Exception) + ): + # If mdns is not available, try to use the DNS cache + port = address_list[0] return [ *DASHBOARD_COMMAND, diff --git a/requirements.txt b/requirements.txt index 115f85de3e..5281b64e66 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,9 @@ +async_timeout==4.0.3; python_version <= "3.10" voluptuous==0.14.1 PyYAML==6.0.1 paho-mqtt==1.6.1 colorama==0.4.6 +icmplib==3.0.4 tornado==6.4 tzlocal==5.2 # from time tzdata>=2021.1 # from time From 97be209aec830789184df9e2174bee5427a24afd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 16:11:48 -1000 Subject: [PATCH 119/193] Bump pytest-asyncio from 0.23.2 to 0.23.3 (#6047) --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index bf7103f025..35f48e767f 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -8,6 +8,6 @@ pre-commit pytest==7.4.4 pytest-cov==4.1.0 pytest-mock==3.12.0 -pytest-asyncio==0.23.2 +pytest-asyncio==0.23.3 asyncmock==0.4.2 hypothesis==6.92.1 From aa8a533da6eb35f0ab6ffb64423a6bffe99c2f03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 16:12:08 -1000 Subject: [PATCH 120/193] Bump black from 23.12.0 to 23.12.1 (#6018) --- .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 36ec1894d8..b2f44d088f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/psf/black-pre-commit-mirror - rev: 23.12.0 + rev: 23.12.1 hooks: - id: black args: diff --git a/requirements_test.txt b/requirements_test.txt index 35f48e767f..74d66f5b25 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==23.12.0 # also change in .pre-commit-config.yaml when updating +black==23.12.1 # also change in .pre-commit-config.yaml when updating pyupgrade==3.15.0 # also change in .pre-commit-config.yaml when updating pre-commit From fcd549e5b6d5a776551acde2e38c59d04535b2b2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 8 Jan 2024 17:18:13 -1000 Subject: [PATCH 121/193] Run python tests on windows and macos (#6010) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .github/actions/restore-python/action.yml | 13 +++++-- .github/workflows/ci.yml | 44 +++++++++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index 18a2485dbb..3c1a5e2b04 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -28,11 +28,20 @@ runs: # yamllint disable-line rule:line-length key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ inputs.cache-key }} - name: Create Python virtual environment - if: steps.cache-venv.outputs.cache-hit != 'true' + if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os != 'Windows' shell: bash run: | python -m venv venv - . venv/bin/activate + source venv/bin/activate + python --version + pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt + pip install -e . + - name: Create Python virtual environment + if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os == 'Windows' + shell: bash + run: | + python -m venv venv + ./venv/Scripts/activate python --version pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt pip install -e . diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8182f92f94..1ddc49b504 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -166,7 +166,35 @@ jobs: pytest: name: Run pytest - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: + - "3.9" + - "3.10" + - "3.11" + - "3.12" + os: + - ubuntu-latest + - macOS-latest + - windows-latest + exclude: + # Minimize CI resource usage + # by only running the Python version + # version used for docker images on Windows and macOS + - python-version: "3.12" + os: windows-latest + - python-version: "3.10" + os: windows-latest + - python-version: "3.9" + os: windows-latest + - python-version: "3.12" + os: macOS-latest + - python-version: "3.10" + os: macOS-latest + - python-version: "3.9" + os: macOS-latest + runs-on: ${{ matrix.os }} needs: - common steps: @@ -175,14 +203,24 @@ jobs: - name: Restore Python uses: ./.github/actions/restore-python with: - python-version: ${{ env.DEFAULT_PYTHON }} + python-version: ${{ matrix.python-version }} cache-key: ${{ needs.common.outputs.cache-key }} - name: Register matcher run: echo "::add-matcher::.github/workflows/matchers/pytest.json" - name: Run pytest + if: matrix.os == 'windows-latest' + run: | + ./venv/Scripts/activate + pytest -vv --cov-report=xml --tb=native tests + - name: Run pytest + if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest' run: | . venv/bin/activate - pytest -vv --tb=native tests + pytest -vv --cov-report=xml --tb=native tests + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} clang-format: name: Check clang-format From 4b783c03723f50df6969e78ef26729b4b05fa70b Mon Sep 17 00:00:00 2001 From: Andrey Bodrov Date: Wed, 10 Jan 2024 07:31:38 +0300 Subject: [PATCH 122/193] BME280 SPI (#5538) * bme spi finally * linter * CO * tidy * lint * tidy [2] * tidy[-1] * final solution * Update test1.yaml remove failed test * Update test1.1.yaml add test to another file with free GPIO5 pin * fix spi read bytes * fix tests * rename bme280 to bme280_i2c --- CODEOWNERS | 2 + esphome/components/bme280/sensor.py | 116 ------------------ esphome/components/bme280_base/__init__.py | 1 + .../bme280_base.cpp} | 68 +++++----- .../bme280.h => bme280_base/bme280_base.h} | 14 ++- esphome/components/bme280_base/sensor.py | 106 ++++++++++++++++ .../{bme280 => bme280_i2c}/__init__.py | 0 esphome/components/bme280_i2c/bme280_i2c.cpp | 30 +++++ esphome/components/bme280_i2c/bme280_i2c.h | 20 +++ esphome/components/bme280_i2c/sensor.py | 19 +++ esphome/components/bme280_spi/__init__.py | 1 + esphome/components/bme280_spi/bme280_spi.cpp | 66 ++++++++++ esphome/components/bme280_spi/bme280_spi.h | 20 +++ esphome/components/bme280_spi/sensor.py | 24 ++++ tests/test1.yaml | 17 ++- 15 files changed, 350 insertions(+), 154 deletions(-) delete mode 100644 esphome/components/bme280/sensor.py create mode 100644 esphome/components/bme280_base/__init__.py rename esphome/components/{bme280/bme280.cpp => bme280_base/bme280_base.cpp} (93%) rename esphome/components/{bme280/bme280.h => bme280_base/bme280_base.h} (90%) create mode 100644 esphome/components/bme280_base/sensor.py rename esphome/components/{bme280 => bme280_i2c}/__init__.py (100%) create mode 100644 esphome/components/bme280_i2c/bme280_i2c.cpp create mode 100644 esphome/components/bme280_i2c/bme280_i2c.h create mode 100644 esphome/components/bme280_i2c/sensor.py create mode 100644 esphome/components/bme280_spi/__init__.py create mode 100644 esphome/components/bme280_spi/bme280_spi.cpp create mode 100644 esphome/components/bme280_spi/bme280_spi.h create mode 100644 esphome/components/bme280_spi/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index c655f94a1b..0ff5ce4508 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -54,6 +54,8 @@ esphome/components/bl0940/* @tobias- esphome/components/bl0942/* @dbuezas esphome/components/ble_client/* @buxtronix @clydebarrow esphome/components/bluetooth_proxy/* @jesserockz +esphome/components/bme280_base/* @esphome/core +esphome/components/bme280_spi/* @apbodrov esphome/components/bme680_bsec/* @trvrnrth esphome/components/bmi160/* @flaviut esphome/components/bmp3xx/* @martgras diff --git a/esphome/components/bme280/sensor.py b/esphome/components/bme280/sensor.py deleted file mode 100644 index 35744a436d..0000000000 --- a/esphome/components/bme280/sensor.py +++ /dev/null @@ -1,116 +0,0 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import i2c, sensor -from esphome.const import ( - CONF_HUMIDITY, - CONF_ID, - CONF_IIR_FILTER, - CONF_OVERSAMPLING, - CONF_PRESSURE, - CONF_TEMPERATURE, - DEVICE_CLASS_HUMIDITY, - DEVICE_CLASS_PRESSURE, - DEVICE_CLASS_TEMPERATURE, - STATE_CLASS_MEASUREMENT, - UNIT_CELSIUS, - UNIT_HECTOPASCAL, - UNIT_PERCENT, -) - -DEPENDENCIES = ["i2c"] - -bme280_ns = cg.esphome_ns.namespace("bme280") -BME280Oversampling = bme280_ns.enum("BME280Oversampling") -OVERSAMPLING_OPTIONS = { - "NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE, - "1X": BME280Oversampling.BME280_OVERSAMPLING_1X, - "2X": BME280Oversampling.BME280_OVERSAMPLING_2X, - "4X": BME280Oversampling.BME280_OVERSAMPLING_4X, - "8X": BME280Oversampling.BME280_OVERSAMPLING_8X, - "16X": BME280Oversampling.BME280_OVERSAMPLING_16X, -} - -BME280IIRFilter = bme280_ns.enum("BME280IIRFilter") -IIR_FILTER_OPTIONS = { - "OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF, - "2X": BME280IIRFilter.BME280_IIR_FILTER_2X, - "4X": BME280IIRFilter.BME280_IIR_FILTER_4X, - "8X": BME280IIRFilter.BME280_IIR_FILTER_8X, - "16X": BME280IIRFilter.BME280_IIR_FILTER_16X, -} - -BME280Component = bme280_ns.class_( - "BME280Component", cg.PollingComponent, i2c.I2CDevice -) - -CONFIG_SCHEMA = ( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(BME280Component), - 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, - ).extend( - { - cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( - OVERSAMPLING_OPTIONS, upper=True - ), - } - ), - cv.Optional(CONF_PRESSURE): sensor.sensor_schema( - unit_of_measurement=UNIT_HECTOPASCAL, - accuracy_decimals=1, - device_class=DEVICE_CLASS_PRESSURE, - state_class=STATE_CLASS_MEASUREMENT, - ).extend( - { - cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( - OVERSAMPLING_OPTIONS, upper=True - ), - } - ), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( - unit_of_measurement=UNIT_PERCENT, - accuracy_decimals=1, - device_class=DEVICE_CLASS_HUMIDITY, - state_class=STATE_CLASS_MEASUREMENT, - ).extend( - { - cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( - OVERSAMPLING_OPTIONS, upper=True - ), - } - ), - cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( - IIR_FILTER_OPTIONS, upper=True - ), - } - ) - .extend(cv.polling_component_schema("60s")) - .extend(i2c.i2c_device_schema(0x77)) -) - - -async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - await cg.register_component(var, config) - await i2c.register_i2c_device(var, config) - - if temperature_config := config.get(CONF_TEMPERATURE): - sens = await sensor.new_sensor(temperature_config) - cg.add(var.set_temperature_sensor(sens)) - cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING])) - - if pressure_config := config.get(CONF_PRESSURE): - sens = await sensor.new_sensor(pressure_config) - cg.add(var.set_pressure_sensor(sens)) - cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING])) - - if humidity_config := config.get(CONF_HUMIDITY): - sens = await sensor.new_sensor(humidity_config) - cg.add(var.set_humidity_sensor(sens)) - cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING])) - - cg.add(var.set_iir_filter(config[CONF_IIR_FILTER])) diff --git a/esphome/components/bme280_base/__init__.py b/esphome/components/bme280_base/__init__.py new file mode 100644 index 0000000000..f70ffa9520 --- /dev/null +++ b/esphome/components/bme280_base/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/bme280/bme280.cpp b/esphome/components/bme280_base/bme280_base.cpp similarity index 93% rename from esphome/components/bme280/bme280.cpp rename to esphome/components/bme280_base/bme280_base.cpp index 786fc01d28..3c6e15cbca 100644 --- a/esphome/components/bme280/bme280.cpp +++ b/esphome/components/bme280_base/bme280_base.cpp @@ -1,9 +1,14 @@ -#include "bme280.h" +#include +#include + +#include "bme280_base.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" +#include +#include namespace esphome { -namespace bme280 { +namespace bme280_base { static const char *const TAG = "bme280.sensor"; @@ -46,7 +51,24 @@ static const uint8_t BME280_STATUS_IM_UPDATE = 0b01; inline uint16_t combine_bytes(uint8_t msb, uint8_t lsb) { return ((msb & 0xFF) << 8) | (lsb & 0xFF); } -static const char *oversampling_to_str(BME280Oversampling oversampling) { +const char *iir_filter_to_str(BME280IIRFilter filter) { // NOLINT + switch (filter) { + case BME280_IIR_FILTER_OFF: + return "OFF"; + case BME280_IIR_FILTER_2X: + return "2x"; + case BME280_IIR_FILTER_4X: + return "4x"; + case BME280_IIR_FILTER_8X: + return "8x"; + case BME280_IIR_FILTER_16X: + return "16x"; + default: + return "UNKNOWN"; + } +} + +const char *oversampling_to_str(BME280Oversampling oversampling) { // NOLINT switch (oversampling) { case BME280_OVERSAMPLING_NONE: return "None"; @@ -65,23 +87,6 @@ static const char *oversampling_to_str(BME280Oversampling oversampling) { } } -static const char *iir_filter_to_str(BME280IIRFilter filter) { - switch (filter) { - case BME280_IIR_FILTER_OFF: - return "OFF"; - case BME280_IIR_FILTER_2X: - return "2x"; - case BME280_IIR_FILTER_4X: - return "4x"; - case BME280_IIR_FILTER_8X: - return "8x"; - case BME280_IIR_FILTER_16X: - return "16x"; - default: - return "UNKNOWN"; - } -} - void BME280Component::setup() { ESP_LOGCONFIG(TAG, "Setting up BME280..."); uint8_t chip_id = 0; @@ -112,7 +117,7 @@ void BME280Component::setup() { // Wait until the NVM data has finished loading. uint8_t status; uint8_t retry = 5; - do { + do { // NOLINT delay(2); if (!this->read_byte(BME280_REGISTER_STATUS, &status)) { ESP_LOGW(TAG, "Error reading status register."); @@ -175,7 +180,6 @@ void BME280Component::setup() { } void BME280Component::dump_config() { ESP_LOGCONFIG(TAG, "BME280:"); - LOG_I2C_DEVICE(this); switch (this->error_code_) { case COMMUNICATION_FAILED: ESP_LOGE(TAG, "Communication with BME280 failed!"); @@ -226,14 +230,14 @@ void BME280Component::update() { return; } int32_t t_fine = 0; - float temperature = this->read_temperature_(data, &t_fine); + float const temperature = this->read_temperature_(data, &t_fine); if (std::isnan(temperature)) { ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values."); this->status_set_warning(); return; } - float pressure = this->read_pressure_(data, t_fine); - float humidity = this->read_humidity_(data, t_fine); + float const pressure = this->read_pressure_(data, t_fine); + float const humidity = this->read_humidity_(data, t_fine); ESP_LOGV(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity); if (this->temperature_sensor_ != nullptr) @@ -257,11 +261,11 @@ float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) { const int32_t t2 = this->calibration_.t2; const int32_t t3 = this->calibration_.t3; - int32_t var1 = (((adc >> 3) - (t1 << 1)) * t2) >> 11; - int32_t var2 = (((((adc >> 4) - t1) * ((adc >> 4) - t1)) >> 12) * t3) >> 14; + int32_t const var1 = (((adc >> 3) - (t1 << 1)) * t2) >> 11; + int32_t const var2 = (((((adc >> 4) - t1) * ((adc >> 4) - t1)) >> 12) * t3) >> 14; *t_fine = var1 + var2; - float temperature = (*t_fine * 5 + 128) >> 8; + float const temperature = (*t_fine * 5 + 128) >> 8; return temperature / 100.0f; } @@ -303,11 +307,11 @@ float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) { } float BME280Component::read_humidity_(const uint8_t *data, int32_t t_fine) { - uint16_t raw_adc = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF); + uint16_t const raw_adc = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF); if (raw_adc == 0x8000) return NAN; - int32_t adc = raw_adc; + int32_t const adc = raw_adc; const int32_t h1 = this->calibration_.h1; const int32_t h2 = this->calibration_.h2; @@ -325,7 +329,7 @@ float BME280Component::read_humidity_(const uint8_t *data, int32_t t_fine) { v_x1_u32r = v_x1_u32r < 0 ? 0 : v_x1_u32r; v_x1_u32r = v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r; - float h = v_x1_u32r >> 12; + float const h = v_x1_u32r >> 12; return h / 1024.0f; } @@ -351,5 +355,5 @@ uint16_t BME280Component::read_u16_le_(uint8_t a_register) { } int16_t BME280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); } -} // namespace bme280 +} // namespace bme280_base } // namespace esphome diff --git a/esphome/components/bme280/bme280.h b/esphome/components/bme280_base/bme280_base.h similarity index 90% rename from esphome/components/bme280/bme280.h rename to esphome/components/bme280_base/bme280_base.h index 50d398c40f..0f55ad0101 100644 --- a/esphome/components/bme280/bme280.h +++ b/esphome/components/bme280_base/bme280_base.h @@ -2,10 +2,9 @@ #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" -#include "esphome/components/i2c/i2c.h" namespace esphome { -namespace bme280 { +namespace bme280_base { /// Internal struct storing the calibration values of an BME280. struct BME280CalibrationData { @@ -57,8 +56,8 @@ enum BME280IIRFilter { BME280_IIR_FILTER_16X = 0b100, }; -/// This class implements support for the BME280 Temperature+Pressure+Humidity i2c sensor. -class BME280Component : public PollingComponent, public i2c::I2CDevice { +/// This class implements support for the BME280 Temperature+Pressure+Humidity sensor. +class BME280Component : public PollingComponent { public: void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; } @@ -91,6 +90,11 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice { uint16_t read_u16_le_(uint8_t a_register); int16_t read_s16_le_(uint8_t a_register); + virtual bool read_byte(uint8_t a_register, uint8_t *data) = 0; + virtual bool write_byte(uint8_t a_register, uint8_t data) = 0; + virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0; + virtual bool read_byte_16(uint8_t a_register, uint16_t *data) = 0; + BME280CalibrationData calibration_; BME280Oversampling temperature_oversampling_{BME280_OVERSAMPLING_16X}; BME280Oversampling pressure_oversampling_{BME280_OVERSAMPLING_16X}; @@ -106,5 +110,5 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice { } error_code_{NONE}; }; -} // namespace bme280 +} // namespace bme280_base } // namespace esphome diff --git a/esphome/components/bme280_base/sensor.py b/esphome/components/bme280_base/sensor.py new file mode 100644 index 0000000000..3a745ed348 --- /dev/null +++ b/esphome/components/bme280_base/sensor.py @@ -0,0 +1,106 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_IIR_FILTER, + CONF_OVERSAMPLING, + CONF_PRESSURE, + CONF_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_HECTOPASCAL, + UNIT_PERCENT, +) + +bme280_ns = cg.esphome_ns.namespace("bme280_base") +BME280Oversampling = bme280_ns.enum("BME280Oversampling") +OVERSAMPLING_OPTIONS = { + "NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE, + "1X": BME280Oversampling.BME280_OVERSAMPLING_1X, + "2X": BME280Oversampling.BME280_OVERSAMPLING_2X, + "4X": BME280Oversampling.BME280_OVERSAMPLING_4X, + "8X": BME280Oversampling.BME280_OVERSAMPLING_8X, + "16X": BME280Oversampling.BME280_OVERSAMPLING_16X, +} + +BME280IIRFilter = bme280_ns.enum("BME280IIRFilter") +IIR_FILTER_OPTIONS = { + "OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF, + "2X": BME280IIRFilter.BME280_IIR_FILTER_2X, + "4X": BME280IIRFilter.BME280_IIR_FILTER_4X, + "8X": BME280IIRFilter.BME280_IIR_FILTER_8X, + "16X": BME280IIRFilter.BME280_IIR_FILTER_16X, +} + +CONFIG_SCHEMA_BASE = cv.Schema( + { + 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, + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + unit_of_measurement=UNIT_HECTOPASCAL, + accuracy_decimals=1, + device_class=DEVICE_CLASS_PRESSURE, + state_class=STATE_CLASS_MEASUREMENT, + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=1, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( + IIR_FILTER_OPTIONS, upper=True + ), + } +).extend(cv.polling_component_schema("60s")) + + +async def to_code(config, func=None): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + if func is not None: + await func(var, config) + + if temperature_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temperature_config) + cg.add(var.set_temperature_sensor(sens)) + cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING])) + + if pressure_config := config.get(CONF_PRESSURE): + sens = await sensor.new_sensor(pressure_config) + cg.add(var.set_pressure_sensor(sens)) + cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING])) + + if humidity_config := config.get(CONF_HUMIDITY): + sens = await sensor.new_sensor(humidity_config) + cg.add(var.set_humidity_sensor(sens)) + cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING])) + + cg.add(var.set_iir_filter(config[CONF_IIR_FILTER])) diff --git a/esphome/components/bme280/__init__.py b/esphome/components/bme280_i2c/__init__.py similarity index 100% rename from esphome/components/bme280/__init__.py rename to esphome/components/bme280_i2c/__init__.py diff --git a/esphome/components/bme280_i2c/bme280_i2c.cpp b/esphome/components/bme280_i2c/bme280_i2c.cpp new file mode 100644 index 0000000000..e29675b5b7 --- /dev/null +++ b/esphome/components/bme280_i2c/bme280_i2c.cpp @@ -0,0 +1,30 @@ +#include +#include + +#include "bme280_i2c.h" +#include "esphome/components/i2c/i2c.h" +#include "../bme280_base/bme280_base.h" + +namespace esphome { +namespace bme280_i2c { + +bool BME280I2CComponent::read_byte(uint8_t a_register, uint8_t *data) { + return I2CDevice::read_byte(a_register, data); +}; +bool BME280I2CComponent::write_byte(uint8_t a_register, uint8_t data) { + return I2CDevice::write_byte(a_register, data); +}; +bool BME280I2CComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) { + return I2CDevice::read_bytes(a_register, data, len); +}; +bool BME280I2CComponent::read_byte_16(uint8_t a_register, uint16_t *data) { + return I2CDevice::read_byte_16(a_register, data); +}; + +void BME280I2CComponent::dump_config() { + LOG_I2C_DEVICE(this); + BME280Component::dump_config(); +} + +} // namespace bme280_i2c +} // namespace esphome diff --git a/esphome/components/bme280_i2c/bme280_i2c.h b/esphome/components/bme280_i2c/bme280_i2c.h new file mode 100644 index 0000000000..c5e2f7e342 --- /dev/null +++ b/esphome/components/bme280_i2c/bme280_i2c.h @@ -0,0 +1,20 @@ +#pragma once + +#include "esphome/components/bme280_base/bme280_base.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace bme280_i2c { + +static const char *const TAG = "bme280_i2c.sensor"; + +class BME280I2CComponent : public esphome::bme280_base::BME280Component, public i2c::I2CDevice { + bool read_byte(uint8_t a_register, uint8_t *data) override; + bool write_byte(uint8_t a_register, uint8_t data) override; + bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; + bool read_byte_16(uint8_t a_register, uint16_t *data) override; + void dump_config() override; +}; + +} // namespace bme280_i2c +} // namespace esphome diff --git a/esphome/components/bme280_i2c/sensor.py b/esphome/components/bme280_i2c/sensor.py new file mode 100644 index 0000000000..489c52969d --- /dev/null +++ b/esphome/components/bme280_i2c/sensor.py @@ -0,0 +1,19 @@ +import esphome.codegen as cg +from esphome.components import i2c +from ..bme280_base.sensor import to_code as to_code_base, cv, CONFIG_SCHEMA_BASE + +DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["bme280_base"] + +bme280_ns = cg.esphome_ns.namespace("bme280_i2c") +BME280I2CComponent = bme280_ns.class_( + "BME280I2CComponent", cg.PollingComponent, i2c.I2CDevice +) + +CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend( + i2c.i2c_device_schema(default_address=0x77) +).extend({cv.GenerateID(): cv.declare_id(BME280I2CComponent)}) + + +async def to_code(config): + await to_code_base(config, func=i2c.register_i2c_device) diff --git a/esphome/components/bme280_spi/__init__.py b/esphome/components/bme280_spi/__init__.py new file mode 100644 index 0000000000..a1d33e4d7a --- /dev/null +++ b/esphome/components/bme280_spi/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@apbodrov"] diff --git a/esphome/components/bme280_spi/bme280_spi.cpp b/esphome/components/bme280_spi/bme280_spi.cpp new file mode 100644 index 0000000000..921128c8f5 --- /dev/null +++ b/esphome/components/bme280_spi/bme280_spi.cpp @@ -0,0 +1,66 @@ +#include +#include + +#include "bme280_spi.h" +#include + +int set_bit(uint8_t num, int position) { + int mask = 1 << position; + return num | mask; +} + +int clear_bit(uint8_t num, int position) { + int mask = 1 << position; + return num & ~mask; +} + +namespace esphome { +namespace bme280_spi { + +void BME280SPIComponent::setup() { + this->spi_setup(); + BME280Component::setup(); +}; + +// In SPI mode, only 7 bits of the register addresses are used; the MSB of register address is not used +// and replaced by a read/write bit (RW = ‘0’ for write and RW = ‘1’ for read). +// Example: address 0xF7 is accessed by using SPI register address 0x77. For write access, the byte +// 0x77 is transferred, for read access, the byte 0xF7 is transferred. +// https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf + +bool BME280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) { + this->enable(); + // cause: *data = this->delegate_->transfer(tmp) doesnt work + this->delegate_->transfer(set_bit(a_register, 7)); + *data = this->delegate_->transfer(0); + this->disable(); + return true; +} + +bool BME280SPIComponent::write_byte(uint8_t a_register, uint8_t data) { + this->enable(); + this->delegate_->transfer(clear_bit(a_register, 7)); + this->delegate_->transfer(data); + this->disable(); + return true; +} + +bool BME280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) { + this->enable(); + this->delegate_->transfer(set_bit(a_register, 7)); + this->delegate_->read_array(data, len); + this->disable(); + return true; +} + +bool BME280SPIComponent::read_byte_16(uint8_t a_register, uint16_t *data) { + this->enable(); + this->delegate_->transfer(set_bit(a_register, 7)); + ((uint8_t *) data)[1] = this->delegate_->transfer(0); + ((uint8_t *) data)[0] = this->delegate_->transfer(0); + this->disable(); + return true; +} + +} // namespace bme280_spi +} // namespace esphome diff --git a/esphome/components/bme280_spi/bme280_spi.h b/esphome/components/bme280_spi/bme280_spi.h new file mode 100644 index 0000000000..b6b8997fa7 --- /dev/null +++ b/esphome/components/bme280_spi/bme280_spi.h @@ -0,0 +1,20 @@ +#pragma once + +#include "esphome/components/bme280_base/bme280_base.h" +#include "esphome/components/spi/spi.h" + +namespace esphome { +namespace bme280_spi { + +class BME280SPIComponent : public esphome::bme280_base::BME280Component, + public spi::SPIDevice { + void setup() override; + bool read_byte(uint8_t a_register, uint8_t *data) override; + bool write_byte(uint8_t a_register, uint8_t data) override; + bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; + bool read_byte_16(uint8_t a_register, uint16_t *data) override; +}; + +} // namespace bme280_spi +} // namespace esphome diff --git a/esphome/components/bme280_spi/sensor.py b/esphome/components/bme280_spi/sensor.py new file mode 100644 index 0000000000..3cfe1b3cdd --- /dev/null +++ b/esphome/components/bme280_spi/sensor.py @@ -0,0 +1,24 @@ +import esphome.codegen as cg +from esphome.components import spi +from esphome.components.bme280_base.sensor import ( + to_code as to_code_base, + cv, + CONFIG_SCHEMA_BASE, +) + +DEPENDENCIES = ["spi"] +AUTO_LOAD = ["bme280_base"] + + +bme280_spi_ns = cg.esphome_ns.namespace("bme280_spi") +BME280SPIComponent = bme280_spi_ns.class_( + "BME280SPIComponent", cg.PollingComponent, spi.SPIDevice +) + +CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(spi.spi_device_schema()).extend( + {cv.GenerateID(): cv.declare_id(BME280SPIComponent)} +) + + +async def to_code(config): + await to_code_base(config, func=spi.register_spi_device) diff --git a/tests/test1.yaml b/tests/test1.yaml index bc7a94bc5a..3ca6faca8a 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -690,7 +690,7 @@ sensor: update_interval: 30s mode: low_power i2c_id: i2c_bus - - platform: bme280 + - platform: bme280_i2c temperature: name: Outside Temperature oversampling: 16x @@ -704,6 +704,21 @@ sensor: iir_filter: 16x update_interval: 15s i2c_id: i2c_bus + - platform: bme280_spi + temperature: + name: Outside Temperature + oversampling: 16x + pressure: + name: Outside Pressure + oversampling: none + humidity: + name: Outside Humidity + oversampling: 8x + cs_pin: + allow_other_uses: true + number: GPIO23 + iir_filter: 16x + update_interval: 15s - platform: bme680 temperature: name: Outside Temperature From 082d9fcf0e43de4311a2929d9f09b3aa7d92e9e2 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 10 Jan 2024 17:05:34 -0600 Subject: [PATCH 123/193] ESP32-C3 USB_CDC fixes (#6069) --- esphome/components/logger/__init__.py | 9 ++++++--- esphome/components/logger/logger.cpp | 12 ++++-------- esphome/components/logger/logger.h | 5 +++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 6cad783db9..fd64c65c77 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -84,7 +84,7 @@ UART_SELECTION_ESP32 = { VARIANT_ESP32: [UART0, UART1, UART2], VARIANT_ESP32S2: [UART0, UART1, USB_CDC], VARIANT_ESP32S3: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], - VARIANT_ESP32C3: [UART0, UART1, USB_SERIAL_JTAG], + VARIANT_ESP32C3: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], VARIANT_ESP32C2: [UART0, UART1], VARIANT_ESP32C6: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], VARIANT_ESP32H2: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], @@ -172,9 +172,10 @@ CONFIG_SCHEMA = cv.All( esp8266=UART0, esp32=UART0, esp32_s2=USB_CDC, - esp32_s3_idf=USB_SERIAL_JTAG, - esp32_c3_idf=USB_SERIAL_JTAG, esp32_s3_arduino=USB_CDC, + esp32_s3_idf=USB_SERIAL_JTAG, + esp32_c3_arduino=USB_CDC, + esp32_c3_idf=USB_SERIAL_JTAG, rp2040=USB_CDC, bk72xx=DEFAULT, rtl87xx=DEFAULT, @@ -265,6 +266,8 @@ async def to_code(config): if CORE.using_arduino: if config[CONF_HARDWARE_UART] == USB_CDC: cg.add_build_flag("-DARDUINO_USB_CDC_ON_BOOT=1") + if CORE.is_esp32 and get_esp32_variant() == VARIANT_ESP32C3: + cg.add_build_flag("-DARDUINO_USB_MODE=1") if CORE.using_esp_idf: if config[CONF_HARDWARE_UART] == USB_CDC: diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index e0f7e77d2c..d5f5c275eb 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -272,17 +272,13 @@ void Logger::pre_setup() { #endif #if defined(USE_ESP32) && \ (defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C3)) -#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C3) case UART_SELECTION_USB_CDC: -#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3 #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) case UART_SELECTION_USB_SERIAL_JTAG: #endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3 -#ifdef USE_ESP32_VARIANT_ESP32C3 - this->hw_serial_ = &Serial; - Serial.begin(this->baud_rate_); -#endif // USE_ESP32_VARIANT_ESP32C3 -#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C3) #if ARDUINO_USB_CDC_ON_BOOT this->hw_serial_ = &Serial; Serial.setTxTimeoutMs(0); // workaround for 2.0.9 crash when there's no data connection @@ -291,7 +287,7 @@ void Logger::pre_setup() { this->hw_serial_ = &Serial; Serial.begin(this->baud_rate_); #endif // ARDUINO_USB_CDC_ON_BOOT -#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3 break; #endif // USE_ESP32 && (USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3) #ifdef USE_RP2040 diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index 68efc056df..c7f0fe4139 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -45,9 +45,10 @@ enum UARTSelection { UART_SELECTION_UART2, #endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32C6 && !USE_ESP32_VARIANT_ESP32S2 && // !USE_ESP32_VARIANT_ESP32S3 && !USE_ESP32_VARIANT_ESP32H2 -#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \ + (defined(USE_ESP32_VARIANT_ESP32C3) && defined(USE_ARDUINO)) UART_SELECTION_USB_CDC, -#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3 #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \ defined(USE_ESP32_VARIANT_ESP32H2) UART_SELECTION_USB_SERIAL_JTAG, From d616025fede0b8f2ac7e1ba8010d3467d65019c9 Mon Sep 17 00:00:00 2001 From: Simone Rossetto Date: Thu, 11 Jan 2024 06:09:42 +0100 Subject: [PATCH 124/193] Actions to enable and disable WireGuard connection (#5690) --- esphome/components/wireguard/__init__.py | 55 +++++++++++++++++++ esphome/components/wireguard/binary_sensor.py | 9 +++ esphome/components/wireguard/wireguard.cpp | 46 ++++++++++++++-- esphome/components/wireguard/wireguard.h | 42 ++++++++++++++ tests/test10.yaml | 25 +++++++++ 5 files changed, 172 insertions(+), 5 deletions(-) diff --git a/esphome/components/wireguard/__init__.py b/esphome/components/wireguard/__init__.py index acb5f690ec..b59a6011cd 100644 --- a/esphome/components/wireguard/__init__.py +++ b/esphome/components/wireguard/__init__.py @@ -10,6 +10,7 @@ from esphome.const import ( ) from esphome.components import time from esphome.core import TimePeriod +from esphome import automation CONF_NETMASK = "netmask" CONF_PRIVATE_KEY = "private_key" @@ -30,6 +31,16 @@ _WG_KEY_REGEX = re.compile(r"^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw480]=$") wireguard_ns = cg.esphome_ns.namespace("wireguard") Wireguard = wireguard_ns.class_("Wireguard", cg.Component, cg.PollingComponent) +WireguardPeerOnlineCondition = wireguard_ns.class_( + "WireguardPeerOnlineCondition", automation.Condition +) +WireguardEnabledCondition = wireguard_ns.class_( + "WireguardEnabledCondition", automation.Condition +) +WireguardEnableAction = wireguard_ns.class_("WireguardEnableAction", automation.Action) +WireguardDisableAction = wireguard_ns.class_( + "WireguardDisableAction", automation.Action +) def _wireguard_key(value): @@ -112,3 +123,47 @@ async def to_code(config): cg.add_library("droscy/esp_wireguard", "0.3.2") await cg.register_component(var, config) + + +@automation.register_condition( + "wireguard.peer_online", + WireguardPeerOnlineCondition, + cv.Schema({cv.GenerateID(): cv.use_id(Wireguard)}), +) +async def wireguard_peer_up_to_code(config, condition_id, template_arg, args): + var = cg.new_Pvariable(condition_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var + + +@automation.register_condition( + "wireguard.enabled", + WireguardEnabledCondition, + cv.Schema({cv.GenerateID(): cv.use_id(Wireguard)}), +) +async def wireguard_enabled_to_code(config, condition_id, template_arg, args): + var = cg.new_Pvariable(condition_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var + + +@automation.register_action( + "wireguard.enable", + WireguardEnableAction, + cv.Schema({cv.GenerateID(): cv.use_id(Wireguard)}), +) +async def wireguard_enable_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var + + +@automation.register_action( + "wireguard.disable", + WireguardDisableAction, + cv.Schema({cv.GenerateID(): cv.use_id(Wireguard)}), +) +async def wireguard_disable_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var diff --git a/esphome/components/wireguard/binary_sensor.py b/esphome/components/wireguard/binary_sensor.py index 14ff2b0159..bf60aaa1d6 100644 --- a/esphome/components/wireguard/binary_sensor.py +++ b/esphome/components/wireguard/binary_sensor.py @@ -4,11 +4,13 @@ from esphome.components import binary_sensor from esphome.const import ( CONF_STATUS, DEVICE_CLASS_CONNECTIVITY, + ENTITY_CATEGORY_DIAGNOSTIC, ) from . import Wireguard CONF_WIREGUARD_ID = "wireguard_id" +CONF_ENABLED = "enabled" DEPENDENCIES = ["wireguard"] @@ -17,6 +19,9 @@ CONFIG_SCHEMA = { cv.Optional(CONF_STATUS): binary_sensor.binary_sensor_schema( device_class=DEVICE_CLASS_CONNECTIVITY, ), + cv.Optional(CONF_ENABLED): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), } @@ -26,3 +31,7 @@ async def to_code(config): if status_config := config.get(CONF_STATUS): sens = await binary_sensor.new_binary_sensor(status_config) cg.add(parent.set_status_sensor(sens)) + + if enabled_config := config.get(CONF_ENABLED): + sens = await binary_sensor.new_binary_sensor(enabled_config) + cg.add(parent.set_enabled_sensor(sens)) diff --git a/esphome/components/wireguard/wireguard.cpp b/esphome/components/wireguard/wireguard.cpp index f89a5ebbad..cca30d4310 100644 --- a/esphome/components/wireguard/wireguard.cpp +++ b/esphome/components/wireguard/wireguard.cpp @@ -48,6 +48,8 @@ void Wireguard::setup() { if (this->preshared_key_.length() > 0) this->wg_config_.preshared_key = this->preshared_key_.c_str(); + this->publish_enabled_state(); + this->wg_initialized_ = esp_wireguard_init(&(this->wg_config_), &(this->wg_ctx_)); if (this->wg_initialized_ == ESP_OK) { @@ -68,6 +70,10 @@ void Wireguard::setup() { } void Wireguard::loop() { + if (!this->enabled_) { + return; + } + if ((this->wg_initialized_ == ESP_OK) && (this->wg_connected_ == ESP_OK) && (!network::is_connected())) { ESP_LOGV(TAG, "local network connection has been lost, stopping WireGuard..."); this->stop_connection_(); @@ -79,8 +85,9 @@ void Wireguard::update() { time_t lhs = this->get_latest_handshake(); bool lhs_updated = (lhs > this->latest_saved_handshake_); - ESP_LOGV(TAG, "handshake: latest=%.0f, saved=%.0f, updated=%d", (double) lhs, (double) this->latest_saved_handshake_, - (int) lhs_updated); + ESP_LOGV(TAG, "enabled=%d, connected=%d, peer_up=%d, handshake: current=%.0f latest=%.0f updated=%d", + (int) this->enabled_, (int) (this->wg_connected_ == ESP_OK), (int) peer_up, (double) lhs, + (double) this->latest_saved_handshake_, (int) lhs_updated); if (lhs_updated) { this->latest_saved_handshake_ = lhs; @@ -102,13 +109,13 @@ void Wireguard::update() { if (this->wg_peer_offline_time_ == 0) { ESP_LOGW(TAG, LOGMSG_PEER_STATUS, LOGMSG_OFFLINE, latest_handshake.c_str()); this->wg_peer_offline_time_ = millis(); - } else { + } else if (this->enabled_) { ESP_LOGD(TAG, LOGMSG_PEER_STATUS, LOGMSG_OFFLINE, latest_handshake.c_str()); this->start_connection_(); } // check reboot timeout every time the peer is down - if (this->reboot_timeout_ > 0) { + if (this->enabled_ && this->reboot_timeout_ > 0) { if (millis() - this->wg_peer_offline_time_ > this->reboot_timeout_) { ESP_LOGE(TAG, "WireGuard remote peer is unreachable, rebooting..."); App.reboot(); @@ -154,7 +161,7 @@ void Wireguard::dump_config() { void Wireguard::on_shutdown() { this->stop_connection_(); } -bool Wireguard::can_proceed() { return (this->proceed_allowed_ || this->is_peer_up()); } +bool Wireguard::can_proceed() { return (this->proceed_allowed_ || this->is_peer_up() || !this->enabled_); } bool Wireguard::is_peer_up() const { return (this->wg_initialized_ == ESP_OK) && (this->wg_connected_ == ESP_OK) && @@ -187,6 +194,7 @@ void Wireguard::set_srctime(time::RealTimeClock *srctime) { this->srctime_ = src #ifdef USE_BINARY_SENSOR void Wireguard::set_status_sensor(binary_sensor::BinarySensor *sensor) { this->status_sensor_ = sensor; } +void Wireguard::set_enabled_sensor(binary_sensor::BinarySensor *sensor) { this->enabled_sensor_ = sensor; } #endif #ifdef USE_SENSOR @@ -199,7 +207,35 @@ void Wireguard::set_address_sensor(text_sensor::TextSensor *sensor) { this->addr void Wireguard::disable_auto_proceed() { this->proceed_allowed_ = false; } +void Wireguard::enable() { + this->enabled_ = true; + ESP_LOGI(TAG, "WireGuard enabled"); + this->publish_enabled_state(); +} + +void Wireguard::disable() { + this->enabled_ = false; + this->defer(std::bind(&Wireguard::stop_connection_, this)); // defer to avoid blocking running loop + ESP_LOGI(TAG, "WireGuard disabled"); + this->publish_enabled_state(); +} + +void Wireguard::publish_enabled_state() { +#ifdef USE_BINARY_SENSOR + if (this->enabled_sensor_ != nullptr) { + this->enabled_sensor_->publish_state(this->enabled_); + } +#endif +} + +bool Wireguard::is_enabled() { return this->enabled_; } + void Wireguard::start_connection_() { + if (!this->enabled_) { + ESP_LOGV(TAG, "WireGuard is disabled, cannot start connection"); + return; + } + if (this->wg_initialized_ != ESP_OK) { ESP_LOGE(TAG, "cannot start WireGuard, initialization in error with code %d", this->wg_initialized_); return; diff --git a/esphome/components/wireguard/wireguard.h b/esphome/components/wireguard/wireguard.h index c47d9e6603..7753a8dfc2 100644 --- a/esphome/components/wireguard/wireguard.h +++ b/esphome/components/wireguard/wireguard.h @@ -26,6 +26,7 @@ namespace esphome { namespace wireguard { +/// Main Wireguard component class. class Wireguard : public PollingComponent { public: void setup() override; @@ -53,6 +54,7 @@ class Wireguard : public PollingComponent { #ifdef USE_BINARY_SENSOR void set_status_sensor(binary_sensor::BinarySensor *sensor); + void set_enabled_sensor(binary_sensor::BinarySensor *sensor); #endif #ifdef USE_SENSOR @@ -66,6 +68,18 @@ class Wireguard : public PollingComponent { /// Block the setup step until peer is connected. void disable_auto_proceed(); + /// Enable the WireGuard component. + void enable(); + + /// Stop any running connection and disable the WireGuard component. + void disable(); + + /// Publish the enabled state if the enabled binary sensor is configured. + void publish_enabled_state(); + + /// Return if the WireGuard component is or is not enabled. + bool is_enabled(); + bool is_peer_up() const; time_t get_latest_handshake() const; @@ -87,6 +101,7 @@ class Wireguard : public PollingComponent { #ifdef USE_BINARY_SENSOR binary_sensor::BinarySensor *status_sensor_ = nullptr; + binary_sensor::BinarySensor *enabled_sensor_ = nullptr; #endif #ifdef USE_SENSOR @@ -100,6 +115,9 @@ class Wireguard : public PollingComponent { /// Set to false to block the setup step until peer is connected. bool proceed_allowed_ = true; + /// When false the wireguard link will not be established + bool enabled_ = true; + wireguard_config_t wg_config_ = ESP_WIREGUARD_CONFIG_DEFAULT(); wireguard_ctx_t wg_ctx_ = ESP_WIREGUARD_CONTEXT_DEFAULT(); @@ -128,6 +146,30 @@ void resume_wdt(); /// Strip most part of the key only for secure printing std::string mask_key(const std::string &key); +/// Condition to check if remote peer is online. +template class WireguardPeerOnlineCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->is_peer_up(); } +}; + +/// Condition to check if Wireguard component is enabled. +template class WireguardEnabledCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->is_enabled(); } +}; + +/// Action to enable Wireguard component. +template class WireguardEnableAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->enable(); } +}; + +/// Action to disable Wireguard component. +template class WireguardDisableAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->disable(); } +}; + } // namespace wireguard } // namespace esphome diff --git a/tests/test10.yaml b/tests/test10.yaml index dda7601048..7e3a685b36 100644 --- a/tests/test10.yaml +++ b/tests/test10.yaml @@ -44,6 +44,8 @@ binary_sensor: - platform: wireguard status: name: 'WireGuard Status' + enabled: + name: 'WireGuard Enabled' sensor: - platform: wireguard @@ -54,3 +56,26 @@ text_sensor: - platform: wireguard address: name: 'WireGuard Address' + +button: + - platform: template + name: 'Toggle WireGuard' + entity_category: config + on_press: + - if: + condition: wireguard.enabled + then: + - wireguard.disable: + else: + - wireguard.enable: + + - platform: template + name: 'Log WireGuard status' + entity_category: config + on_press: + - if: + condition: wireguard.peer_online + then: + - logger.log: 'wireguard remote peer is online' + else: + - logger.log: 'wireguard remote peer is offline' From 4cc17dac0dff22ef476aa7b98abf17b675749379 Mon Sep 17 00:00:00 2001 From: mrtoy-me <118446898+mrtoy-me@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:18:22 +1000 Subject: [PATCH 125/193] hydreon_rgxx - fix missing cg.add(var.set_model(...)) (#6065) --- esphome/components/hydreon_rgxx/hydreon_rgxx.cpp | 10 ++++++---- esphome/components/hydreon_rgxx/sensor.py | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp index 58e00ba7a5..c026d7cce6 100644 --- a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +++ b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp @@ -17,6 +17,12 @@ void HydreonRGxxComponent::dump_config() { if (this->is_failed()) { ESP_LOGE(TAG, "Connection with hydreon_rgxx failed!"); } + if (model_ == RG9) { + ESP_LOGCONFIG(TAG, " Model: RG9"); + ESP_LOGCONFIG(TAG, " Disable Led: %s", TRUEFALSE(this->disable_led_)); + } else { + ESP_LOGCONFIG(TAG, " Model: RG15"); + } LOG_UPDATE_INTERVAL(this); int i = 0; @@ -25,10 +31,6 @@ void HydreonRGxxComponent::dump_config() { LOG_SENSOR(" ", #s, this->sensors_[i - 1]); \ } HYDREON_RGXX_PROTOCOL_LIST(HYDREON_RGXX_LOG_SENSOR, ); - - if (this->model_ == RG9) { - ESP_LOGCONFIG(TAG, "disable_led: %s", TRUEFALSE(this->disable_led_)); - } } void HydreonRGxxComponent::setup() { diff --git a/esphome/components/hydreon_rgxx/sensor.py b/esphome/components/hydreon_rgxx/sensor.py index 0fc380f959..f9cb316c24 100644 --- a/esphome/components/hydreon_rgxx/sensor.py +++ b/esphome/components/hydreon_rgxx/sensor.py @@ -138,6 +138,7 @@ async def to_code(config): sens = await sensor.new_sensor(config[conf]) cg.add(var.set_sensor(sens, i)) + cg.add(var.set_model(config[CONF_MODEL])) cg.add(var.set_request_temperature(CONF_TEMPERATURE in config)) if CONF_DISABLE_LED in config: From 343a8c063e145ecf0d8e32478581a57ff421d50b Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Fri, 12 Jan 2024 01:11:42 -0800 Subject: [PATCH 126/193] fix sen5x negative temperature (#6082) --- esphome/components/sen5x/sen5x.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sen5x/sen5x.cpp b/esphome/components/sen5x/sen5x.cpp index c90880bc9f..0efc961943 100644 --- a/esphome/components/sen5x/sen5x.cpp +++ b/esphome/components/sen5x/sen5x.cpp @@ -352,7 +352,7 @@ void SEN5XComponent::update() { float humidity = measurements[4] / 100.0; if (measurements[4] == 0xFFFF) humidity = NAN; - float temperature = measurements[5] / 200.0; + float temperature = (int16_t) measurements[5] / 200.0; if (measurements[5] == 0xFFFF) temperature = NAN; float voc = measurements[6] / 10.0; From aa04a3caaf83ae71e4edac681bc9a534ada265be Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Fri, 12 Jan 2024 01:20:08 -0800 Subject: [PATCH 127/193] negative values for all DHT22 variants (#6074) Co-authored-by: Samuel Sieb --- esphome/components/dht/dht.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index c70b227330..07634cafdf 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -217,8 +217,12 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r uint16_t raw_humidity = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF); uint16_t raw_temperature = (uint16_t(data[2] & 0xFF) << 8) | (data[3] & 0xFF); - if (this->model_ != DHT_MODEL_DHT22_TYPE2 && (raw_temperature & 0x8000) != 0) - raw_temperature = ~(raw_temperature & 0x7FFF); + if (raw_temperature & 0x8000) { + if (!(raw_temperature & 0x4000)) + raw_temperature = ~(raw_temperature & 0x7FFF); + } else if (raw_temperature & 0x800) { + raw_temperature |= 0xf000; + } if (raw_temperature == 1 && raw_humidity == 10) { if (report_errors) { From ed2ab9e96287cd4ff6a96b0086cdf0ef9d845446 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Fri, 12 Jan 2024 01:47:50 -0800 Subject: [PATCH 128/193] fix negative temperature for pmsx003 (#6083) * fix negative temperature for pmsx003 * Update esphome/components/pmsx003/pmsx003.cpp --- esphome/components/pmsx003/pmsx003.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/pmsx003/pmsx003.cpp b/esphome/components/pmsx003/pmsx003.cpp index 04aba4382b..62488b765c 100644 --- a/esphome/components/pmsx003/pmsx003.cpp +++ b/esphome/components/pmsx003/pmsx003.cpp @@ -195,7 +195,7 @@ void PMSX003Component::send_command_(uint8_t cmd, uint16_t data) { void PMSX003Component::parse_data_() { switch (this->type_) { case PMSX003_TYPE_5003ST: { - float temperature = this->get_16_bit_uint_(30) / 10.0f; + float temperature = (int16_t) this->get_16_bit_uint_(30) / 10.0f; float humidity = this->get_16_bit_uint_(32) / 10.0f; ESP_LOGD(TAG, "Got Temperature: %.1f°C, Humidity: %.1f%%", temperature, humidity); From d551a2eba2c006758eb97cd3cf4c7110d1c15f50 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sat, 13 Jan 2024 02:21:28 -0600 Subject: [PATCH 129/193] Improv Serial -- don't wait for incoming bytes (#6089) --- esphome/components/improv_serial/improv_serial_component.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 600069b781..2318fd43cb 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -52,7 +52,7 @@ optional ImprovSerialComponent::read_byte_() { size_t available; uart_get_buffered_data_len(this->uart_num_, &available); if (available) { - uart_read_bytes(this->uart_num_, &data, 1, 20 / portTICK_PERIOD_MS); + uart_read_bytes(this->uart_num_, &data, 1, 0); byte = data; } } @@ -71,7 +71,7 @@ optional ImprovSerialComponent::read_byte_() { #endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) case logger::UART_SELECTION_USB_SERIAL_JTAG: { - if (usb_serial_jtag_read_bytes((char *) &data, 1, 20 / portTICK_PERIOD_MS)) { + if (usb_serial_jtag_read_bytes((char *) &data, 1, 0)) { byte = data; } break; From 8e83c7dd193083ace6d0faabf81dd60993fe8d5d Mon Sep 17 00:00:00 2001 From: guillempages Date: Sat, 13 Jan 2024 22:01:32 +0100 Subject: [PATCH 130/193] Let show_*_page actions depend on "Display" (#6092) Instead of forcing a DisplayBuffer, let the display page actions use Displays without buffer. --- esphome/components/display/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/display/__init__.py b/esphome/components/display/__init__.py index 91f10c5458..992799008a 100644 --- a/esphome/components/display/__init__.py +++ b/esphome/components/display/__init__.py @@ -145,7 +145,7 @@ async def display_page_show_to_code(config, action_id, template_arg, args): DisplayPageShowNextAction, maybe_simple_id( { - cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)), + cv.Required(CONF_ID): cv.templatable(cv.use_id(Display)), } ), ) @@ -159,7 +159,7 @@ async def display_page_show_next_to_code(config, action_id, template_arg, args): DisplayPageShowPrevAction, maybe_simple_id( { - cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)), + cv.Required(CONF_ID): cv.templatable(cv.use_id(Display)), } ), ) @@ -173,7 +173,7 @@ async def display_page_show_previous_to_code(config, action_id, template_arg, ar DisplayIsDisplayingPageCondition, cv.maybe_simple_value( { - cv.GenerateID(CONF_ID): cv.use_id(DisplayBuffer), + cv.GenerateID(CONF_ID): cv.use_id(Display), cv.Required(CONF_PAGE_ID): cv.use_id(DisplayPage), }, key=CONF_PAGE_ID, From f567b5d28b8c75340cb64a9237219c09ee9d6c50 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 15 Jan 2024 00:01:20 +0100 Subject: [PATCH 131/193] add STATE_CLASS_TOTAL_INCREASING to bl0940 and bl0942 (#6090) --- esphome/components/bl0940/sensor.py | 2 ++ esphome/components/bl0942/sensor.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/esphome/components/bl0940/sensor.py b/esphome/components/bl0940/sensor.py index 702230e020..a197becef8 100644 --- a/esphome/components/bl0940/sensor.py +++ b/esphome/components/bl0940/sensor.py @@ -18,6 +18,7 @@ from esphome.const import ( UNIT_KILOWATT_HOURS, UNIT_VOLT, UNIT_WATT, + STATE_CLASS_TOTAL_INCREASING, ) DEPENDENCIES = ["uart"] @@ -54,6 +55,7 @@ CONFIG_SCHEMA = ( unit_of_measurement=UNIT_KILOWATT_HOURS, accuracy_decimals=0, device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_TOTAL_INCREASING, ), cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, diff --git a/esphome/components/bl0942/sensor.py b/esphome/components/bl0942/sensor.py index 663eea0c4d..9612df6d4c 100644 --- a/esphome/components/bl0942/sensor.py +++ b/esphome/components/bl0942/sensor.py @@ -19,6 +19,7 @@ from esphome.const import ( UNIT_VOLT, UNIT_WATT, UNIT_HERTZ, + STATE_CLASS_TOTAL_INCREASING, ) DEPENDENCIES = ["uart"] @@ -52,6 +53,7 @@ CONFIG_SCHEMA = ( unit_of_measurement=UNIT_KILOWATT_HOURS, accuracy_decimals=0, device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_TOTAL_INCREASING, ), cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( unit_of_measurement=UNIT_HERTZ, From 5220c9edf82aad55e1df811260675486ac338fb1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 14 Jan 2024 13:06:13 -1000 Subject: [PATCH 132/193] Fallback to pure-python loader for better error when YAML loading fails (#6081) --- esphome/yaml_util.py | 113 ++++++++++-------- .../yaml_util/broken_includetest.yaml | 18 +++ .../includes/broken_included.yaml.txt | 5 + tests/unit_tests/test_yaml_util.py | 11 ++ 4 files changed, 98 insertions(+), 49 deletions(-) create mode 100644 tests/unit_tests/fixtures/yaml_util/broken_includetest.yaml create mode 100644 tests/unit_tests/fixtures/yaml_util/includes/broken_included.yaml.txt diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index aa9fe45ebb..f5e36b79e7 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -1,36 +1,37 @@ +from __future__ import annotations + import fnmatch import functools import inspect import logging import math import os - import uuid +from typing import Any + import yaml import yaml.constructor +from yaml import SafeLoader as PurePythonLoader + +try: + from yaml import CSafeLoader as FastestAvailableSafeLoader +except ImportError: + FastestAvailableSafeLoader = PurePythonLoader from esphome import core -from esphome.config_helpers import read_config_file, Extend, Remove +from esphome.config_helpers import Extend, Remove, read_config_file from esphome.core import ( + CORE, + DocumentRange, EsphomeError, IPAddress, Lambda, MACAddress, TimePeriod, - DocumentRange, - CORE, ) from esphome.helpers import add_class_to_obj from esphome.util import OrderedDict, filter_yaml_files -try: - from yaml import CSafeLoader as FastestAvailableSafeLoader -except ImportError: - from yaml import ( # type: ignore[assignment] - SafeLoader as FastestAvailableSafeLoader, - ) - - _LOGGER = logging.getLogger(__name__) # Mostly copied from Home Assistant because that code works fine and @@ -97,7 +98,7 @@ def _add_data_ref(fn): return wrapped -class ESPHomeLoader(FastestAvailableSafeLoader): +class ESPHomeLoaderMixin: """Loader class that keeps track of line numbers.""" @_add_data_ref @@ -282,8 +283,8 @@ class ESPHomeLoader(FastestAvailableSafeLoader): return file, vars def substitute_vars(config, vars): - from esphome.const import CONF_SUBSTITUTIONS, CONF_DEFAULTS from esphome.components import substitutions + from esphome.const import CONF_DEFAULTS, CONF_SUBSTITUTIONS org_subs = None result = config @@ -375,50 +376,64 @@ class ESPHomeLoader(FastestAvailableSafeLoader): return Remove(str(node.value)) -ESPHomeLoader.add_constructor("tag:yaml.org,2002:int", ESPHomeLoader.construct_yaml_int) -ESPHomeLoader.add_constructor( - "tag:yaml.org,2002:float", ESPHomeLoader.construct_yaml_float -) -ESPHomeLoader.add_constructor( - "tag:yaml.org,2002:binary", ESPHomeLoader.construct_yaml_binary -) -ESPHomeLoader.add_constructor( - "tag:yaml.org,2002:omap", ESPHomeLoader.construct_yaml_omap -) -ESPHomeLoader.add_constructor("tag:yaml.org,2002:str", ESPHomeLoader.construct_yaml_str) -ESPHomeLoader.add_constructor("tag:yaml.org,2002:seq", ESPHomeLoader.construct_yaml_seq) -ESPHomeLoader.add_constructor("tag:yaml.org,2002:map", ESPHomeLoader.construct_yaml_map) -ESPHomeLoader.add_constructor("!env_var", ESPHomeLoader.construct_env_var) -ESPHomeLoader.add_constructor("!secret", ESPHomeLoader.construct_secret) -ESPHomeLoader.add_constructor("!include", ESPHomeLoader.construct_include) -ESPHomeLoader.add_constructor( - "!include_dir_list", ESPHomeLoader.construct_include_dir_list -) -ESPHomeLoader.add_constructor( - "!include_dir_merge_list", ESPHomeLoader.construct_include_dir_merge_list -) -ESPHomeLoader.add_constructor( - "!include_dir_named", ESPHomeLoader.construct_include_dir_named -) -ESPHomeLoader.add_constructor( - "!include_dir_merge_named", ESPHomeLoader.construct_include_dir_merge_named -) -ESPHomeLoader.add_constructor("!lambda", ESPHomeLoader.construct_lambda) -ESPHomeLoader.add_constructor("!force", ESPHomeLoader.construct_force) -ESPHomeLoader.add_constructor("!extend", ESPHomeLoader.construct_extend) -ESPHomeLoader.add_constructor("!remove", ESPHomeLoader.construct_remove) +class ESPHomeLoader(ESPHomeLoaderMixin, FastestAvailableSafeLoader): + """Loader class that keeps track of line numbers.""" -def load_yaml(fname, clear_secrets=True): +class ESPHomePurePythonLoader(ESPHomeLoaderMixin, PurePythonLoader): + """Loader class that keeps track of line numbers.""" + + +for _loader in (ESPHomeLoader, ESPHomePurePythonLoader): + _loader.add_constructor("tag:yaml.org,2002:int", _loader.construct_yaml_int) + _loader.add_constructor("tag:yaml.org,2002:float", _loader.construct_yaml_float) + _loader.add_constructor("tag:yaml.org,2002:binary", _loader.construct_yaml_binary) + _loader.add_constructor("tag:yaml.org,2002:omap", _loader.construct_yaml_omap) + _loader.add_constructor("tag:yaml.org,2002:str", _loader.construct_yaml_str) + _loader.add_constructor("tag:yaml.org,2002:seq", _loader.construct_yaml_seq) + _loader.add_constructor("tag:yaml.org,2002:map", _loader.construct_yaml_map) + _loader.add_constructor("!env_var", _loader.construct_env_var) + _loader.add_constructor("!secret", _loader.construct_secret) + _loader.add_constructor("!include", _loader.construct_include) + _loader.add_constructor("!include_dir_list", _loader.construct_include_dir_list) + _loader.add_constructor( + "!include_dir_merge_list", _loader.construct_include_dir_merge_list + ) + _loader.add_constructor("!include_dir_named", _loader.construct_include_dir_named) + _loader.add_constructor( + "!include_dir_merge_named", _loader.construct_include_dir_merge_named + ) + _loader.add_constructor("!lambda", _loader.construct_lambda) + _loader.add_constructor("!force", _loader.construct_force) + _loader.add_constructor("!extend", _loader.construct_extend) + _loader.add_constructor("!remove", _loader.construct_remove) + + +def load_yaml(fname: str, clear_secrets: bool = True) -> Any: if clear_secrets: _SECRET_VALUES.clear() _SECRET_CACHE.clear() return _load_yaml_internal(fname) -def _load_yaml_internal(fname): +def _load_yaml_internal(fname: str) -> Any: + """Load a YAML file.""" content = read_config_file(fname) - loader = ESPHomeLoader(content) + try: + return _load_yaml_internal_with_type(ESPHomeLoader, fname, content) + except EsphomeError: + # Loading failed, so we now load with the Python loader which has more + # readable exceptions + return _load_yaml_internal_with_type(ESPHomePurePythonLoader, fname, content) + + +def _load_yaml_internal_with_type( + loader_type: type[ESPHomeLoader] | type[ESPHomePurePythonLoader], + fname: str, + content: str, +) -> Any: + """Load a YAML file.""" + loader = loader_type(content) loader.name = fname try: return loader.get_single_data() or OrderedDict() diff --git a/tests/unit_tests/fixtures/yaml_util/broken_includetest.yaml b/tests/unit_tests/fixtures/yaml_util/broken_includetest.yaml new file mode 100644 index 0000000000..aaca55b807 --- /dev/null +++ b/tests/unit_tests/fixtures/yaml_util/broken_includetest.yaml @@ -0,0 +1,18 @@ +--- +substitutions: + name: original + +wifi: !include + file: includes/broken_included.yaml.txt + vars: + name: my_custom_ssid + +esphome: + # should be substituted as 'original', + # not overwritten by vars in the !include above + name: ${name} + name_add_mac_suffix: true + platform: esp8266 + board: !include {file: includes/scalar.yaml, vars: {var1: nodemcu}} + + libraries: !include {file: includes/list.yaml, vars: {var1: Wire}} diff --git a/tests/unit_tests/fixtures/yaml_util/includes/broken_included.yaml.txt b/tests/unit_tests/fixtures/yaml_util/includes/broken_included.yaml.txt new file mode 100644 index 0000000000..6e53395c86 --- /dev/null +++ b/tests/unit_tests/fixtures/yaml_util/includes/broken_included.yaml.txt @@ -0,0 +1,5 @@ +--- +# yamllint disable-line + ssid: ${name} +# yamllint disable-line + fdf: error diff --git a/tests/unit_tests/test_yaml_util.py b/tests/unit_tests/test_yaml_util.py index 8ee991f5b3..78b6a2ad84 100644 --- a/tests/unit_tests/test_yaml_util.py +++ b/tests/unit_tests/test_yaml_util.py @@ -1,5 +1,6 @@ from esphome import yaml_util from esphome.components import substitutions +from esphome.core import EsphomeError def test_include_with_vars(fixture_path): @@ -11,3 +12,13 @@ def test_include_with_vars(fixture_path): assert actual["esphome"]["libraries"][0] == "Wire" assert actual["esphome"]["board"] == "nodemcu" assert actual["wifi"]["ssid"] == "my_custom_ssid" + + +def test_loading_a_broken_yaml_file(fixture_path): + """Ensure we fallback to pure python to give good errors.""" + yaml_file = fixture_path / "yaml_util" / "broken_includetest.yaml" + + try: + yaml_util.load_yaml(yaml_file) + except EsphomeError as err: + assert "broken_included.yaml" in str(err) From dd2dca4d08593dfddbac7145188a759acbba6423 Mon Sep 17 00:00:00 2001 From: Pieter Frenssen Date: Mon, 15 Jan 2024 01:48:02 +0200 Subject: [PATCH 133/193] Bump pillow to 10.2.0. (#6091) --- esphome/components/font/__init__.py | 8 ++++---- requirements_optional.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index a803c7567b..5b4682a808 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -67,13 +67,13 @@ def validate_pillow_installed(value): except ImportError as err: raise cv.Invalid( "Please install the pillow python package to use this feature. " - '(pip install "pillow==10.1.0")' + '(pip install "pillow==10.2.0")' ) from err - if version.parse(PIL.__version__) != version.parse("10.1.0"): + if version.parse(PIL.__version__) != version.parse("10.2.0"): raise cv.Invalid( - "Please update your pillow installation to 10.1.0. " - '(pip install "pillow==10.1.0")' + "Please update your pillow installation to 10.2.0. " + '(pip install "pillow==10.2.0")' ) return value diff --git a/requirements_optional.txt b/requirements_optional.txt index bc4ea08c92..54494b4585 100644 --- a/requirements_optional.txt +++ b/requirements_optional.txt @@ -1,3 +1,3 @@ -pillow==10.1.0 +pillow==10.2.0 cairosvg==2.7.1 cryptography==41.0.4 From 8b2d76e8cebca0055b08319076f5cc8d94f25383 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 14 Jan 2024 16:05:47 -0800 Subject: [PATCH 134/193] convert cse7766 to non-polling (#6095) Co-authored-by: Samuel Sieb --- esphome/components/cse7766/cse7766.cpp | 52 +++++--------------- esphome/components/cse7766/cse7766.h | 11 +---- esphome/components/cse7766/sensor.py | 66 ++++++++++++-------------- 3 files changed, 44 insertions(+), 85 deletions(-) diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index 60132fd98f..9c5016c503 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -113,8 +113,9 @@ void CSE7766Component::parse_data_() { bool have_voltage = adj & 0x40; if (have_voltage) { // voltage cycle of serial port outputted is a complete cycle; - this->voltage_acc_ += voltage_calib / float(voltage_cycle); - this->voltage_counts_ += 1; + float voltage = voltage_calib / float(voltage_cycle); + if (this->voltage_sensor_ != nullptr) + this->voltage_sensor_->publish_state(voltage); } bool have_power = adj & 0x10; @@ -126,8 +127,8 @@ void CSE7766Component::parse_data_() { if (!power_cycle_exceeds_range) { power = power_calib / float(power_cycle); } - this->power_acc_ += power; - this->power_counts_ += 1; + if (this->power_sensor_ != nullptr) + this->power_sensor_->publish_state(power); uint32_t difference; if (this->cf_pulses_last_ == 0) { @@ -141,7 +142,10 @@ void CSE7766Component::parse_data_() { } this->cf_pulses_last_ = cf_pulses; this->energy_total_ += difference * float(power_calib) / 1000000.0f / 3600.0f; - this->energy_total_counts_ += 1; + if (this->energy_sensor_ != nullptr) + this->energy_sensor_->publish_state(this->energy_total_); + } else if ((this->energy_sensor_ != nullptr) && !this->energy_sensor_->has_state()) { + this->energy_sensor_->publish_state(0); } if (adj & 0x20) { @@ -150,42 +154,13 @@ void CSE7766Component::parse_data_() { if (have_voltage && !have_power) { // Testing has shown that when we have voltage and current but not power, that means the power is 0. // We report a power of 0, which in turn means we should report a current of 0. - this->power_counts_ += 1; + if (this->power_sensor_ != nullptr) + this->power_sensor_->publish_state(0); } else if (power != 0.0f) { current = current_calib / float(current_cycle); } - this->current_acc_ += current; - this->current_counts_ += 1; - } -} -void CSE7766Component::update() { - const auto publish_state = [](const char *name, sensor::Sensor *sensor, float &acc, uint32_t &counts) { - if (counts != 0) { - const auto avg = acc / counts; - - ESP_LOGV(TAG, "Got %s_acc=%.2f %s_counts=%" PRIu32 " %s=%.1f", name, acc, name, counts, name, avg); - - if (sensor != nullptr) { - sensor->publish_state(avg); - } - - acc = 0.0f; - counts = 0; - } - }; - - publish_state("voltage", this->voltage_sensor_, this->voltage_acc_, this->voltage_counts_); - publish_state("current", this->current_sensor_, this->current_acc_, this->current_counts_); - publish_state("power", this->power_sensor_, this->power_acc_, this->power_counts_); - - if (this->energy_total_counts_ != 0) { - ESP_LOGV(TAG, "Got energy_total=%.2f energy_total_counts=%" PRIu32, this->energy_total_, - this->energy_total_counts_); - - if (this->energy_sensor_ != nullptr) { - this->energy_sensor_->publish_state(this->energy_total_); - } - this->energy_total_counts_ = 0; + if (this->current_sensor_ != nullptr) + this->current_sensor_->publish_state(current); } } @@ -196,7 +171,6 @@ uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) { void CSE7766Component::dump_config() { ESP_LOGCONFIG(TAG, "CSE7766:"); - LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Voltage", this->voltage_sensor_); LOG_SENSOR(" ", "Current", this->current_sensor_); LOG_SENSOR(" ", "Power", this->power_sensor_); diff --git a/esphome/components/cse7766/cse7766.h b/esphome/components/cse7766/cse7766.h index 2f30eec09f..3ab8d609bd 100644 --- a/esphome/components/cse7766/cse7766.h +++ b/esphome/components/cse7766/cse7766.h @@ -7,7 +7,7 @@ namespace esphome { namespace cse7766 { -class CSE7766Component : public PollingComponent, public uart::UARTDevice { +class CSE7766Component : public Component, public uart::UARTDevice { public: void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; } @@ -16,7 +16,6 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice { void loop() override; float get_setup_priority() const override; - void update() override; void dump_config() override; protected: @@ -31,16 +30,8 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice { sensor::Sensor *current_sensor_{nullptr}; sensor::Sensor *power_sensor_{nullptr}; sensor::Sensor *energy_sensor_{nullptr}; - float voltage_acc_{0.0f}; - float current_acc_{0.0f}; - float power_acc_{0.0f}; float energy_total_{0.0f}; uint32_t cf_pulses_last_{0}; - uint32_t voltage_counts_{0}; - uint32_t current_counts_{0}; - uint32_t power_counts_{0}; - // Setting this to 1 means it will always publish 0 once at startup - uint32_t energy_total_counts_{1}; }; } // namespace cse7766 diff --git a/esphome/components/cse7766/sensor.py b/esphome/components/cse7766/sensor.py index d98b351287..f2750bb4f2 100644 --- a/esphome/components/cse7766/sensor.py +++ b/esphome/components/cse7766/sensor.py @@ -22,43 +22,37 @@ from esphome.const import ( DEPENDENCIES = ["uart"] cse7766_ns = cg.esphome_ns.namespace("cse7766") -CSE7766Component = cse7766_ns.class_( - "CSE7766Component", cg.PollingComponent, uart.UARTDevice -) +CSE7766Component = cse7766_ns.class_("CSE7766Component", cg.Component, uart.UARTDevice) -CONFIG_SCHEMA = ( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(CSE7766Component), - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( - unit_of_measurement=UNIT_VOLT, - accuracy_decimals=1, - device_class=DEVICE_CLASS_VOLTAGE, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_CURRENT): sensor.sensor_schema( - unit_of_measurement=UNIT_AMPERE, - accuracy_decimals=2, - device_class=DEVICE_CLASS_CURRENT, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_POWER): sensor.sensor_schema( - unit_of_measurement=UNIT_WATT, - accuracy_decimals=1, - device_class=DEVICE_CLASS_POWER, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_ENERGY): sensor.sensor_schema( - unit_of_measurement=UNIT_WATT_HOURS, - accuracy_decimals=3, - device_class=DEVICE_CLASS_ENERGY, - state_class=STATE_CLASS_TOTAL_INCREASING, - ), - } - ) - .extend(cv.polling_component_schema("60s")) - .extend(uart.UART_DEVICE_SCHEMA) -) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CSE7766Component), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + unit_of_measurement=UNIT_VOLT, + accuracy_decimals=1, + device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + unit_of_measurement=UNIT_AMPERE, + accuracy_decimals=2, + device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + unit_of_measurement=UNIT_WATT, + accuracy_decimals=1, + device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_ENERGY): sensor.sensor_schema( + unit_of_measurement=UNIT_WATT_HOURS, + accuracy_decimals=3, + device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_TOTAL_INCREASING, + ), + } +).extend(uart.UART_DEVICE_SCHEMA) FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( "cse7766", baud_rate=4800, require_rx=True ) From 83baa24022ef56100dfb6ae2ce02d2bdc0fe0ebe Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Mon, 15 Jan 2024 03:07:06 +0100 Subject: [PATCH 135/193] Use touch state from ft63x6 driver. (#6055) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/ft63x6/ft63x6.cpp | 65 +++++++++++++--------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/esphome/components/ft63x6/ft63x6.cpp b/esphome/components/ft63x6/ft63x6.cpp index b674ded22c..f796f0242a 100644 --- a/esphome/components/ft63x6/ft63x6.cpp +++ b/esphome/components/ft63x6/ft63x6.cpp @@ -13,14 +13,14 @@ namespace esphome { namespace ft63x6 { -static const uint8_t FT63X6_ADDR_TOUCH_COUNT = 0x02; - -static const uint8_t FT63X6_ADDR_TOUCH1_ID = 0x05; +static const uint8_t FT63X6_ADDR_TOUCH1_STATE = 0x03; static const uint8_t FT63X6_ADDR_TOUCH1_X = 0x03; +static const uint8_t FT63X6_ADDR_TOUCH1_ID = 0x05; static const uint8_t FT63X6_ADDR_TOUCH1_Y = 0x05; -static const uint8_t FT63X6_ADDR_TOUCH2_ID = 0x0B; +static const uint8_t FT63X6_ADDR_TOUCH2_STATE = 0x09; static const uint8_t FT63X6_ADDR_TOUCH2_X = 0x09; +static const uint8_t FT63X6_ADDR_TOUCH2_ID = 0x0B; static const uint8_t FT63X6_ADDR_TOUCH2_Y = 0x0B; static const char *const TAG = "FT63X6Touchscreen"; @@ -40,26 +40,11 @@ void FT63X6Touchscreen::setup() { this->hard_reset_(); // Get touch resolution - this->x_raw_max_ = 320; - this->y_raw_max_ = 480; -} - -void FT63X6Touchscreen::update_touches() { - int touch_count = this->read_touch_count_(); - if (touch_count == 0) { - return; + if (this->x_raw_max_ == this->x_raw_min_) { + this->x_raw_max_ = 320; } - - uint8_t touch_id = this->read_touch_id_(FT63X6_ADDR_TOUCH1_ID); // id1 = 0 or 1 - int16_t x = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH1_X); - int16_t y = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH1_Y); - this->add_raw_touch_position_(touch_id, x, y); - - if (touch_count >= 2) { - touch_id = this->read_touch_id_(FT63X6_ADDR_TOUCH2_ID); // id2 = 0 or 1(~id1 & 0x01) - x = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH2_X); - y = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH2_Y); - this->add_raw_touch_position_(touch_id, x, y); + if (this->y_raw_max_ == this->y_raw_min_) { + this->y_raw_max_ = 480; } } @@ -76,23 +61,31 @@ void FT63X6Touchscreen::dump_config() { LOG_I2C_DEVICE(this); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_UPDATE_INTERVAL(this); } -uint8_t FT63X6Touchscreen::read_touch_count_() { return this->read_byte_(FT63X6_ADDR_TOUCH_COUNT); } +void FT63X6Touchscreen::update_touches() { + uint8_t data[15]; + uint16_t touch_id, x, y; -// Touch functions -uint16_t FT63X6Touchscreen::read_touch_coordinate_(uint8_t coordinate) { - uint8_t read_buf[2]; - read_buf[0] = this->read_byte_(coordinate); - read_buf[1] = this->read_byte_(coordinate + 1); - return ((read_buf[0] & 0x0f) << 8) | read_buf[1]; -} -uint8_t FT63X6Touchscreen::read_touch_id_(uint8_t id_address) { return this->read_byte_(id_address) >> 4; } + if (!this->read_bytes(0x00, (uint8_t *) data, 15)) { + ESP_LOGE(TAG, "Failed to read touch data"); + this->skip_update_ = true; + return; + } -uint8_t FT63X6Touchscreen::read_byte_(uint8_t addr) { - uint8_t byte = 0; - this->read_byte(addr, &byte); - return byte; + if (((data[FT63X6_ADDR_TOUCH1_STATE] >> 6) & 0x01) == 0) { + touch_id = data[FT63X6_ADDR_TOUCH1_ID] >> 4; // id1 = 0 or 1 + x = encode_uint16(data[FT63X6_ADDR_TOUCH1_X] & 0x0F, data[FT63X6_ADDR_TOUCH1_X + 1]); + y = encode_uint16(data[FT63X6_ADDR_TOUCH1_Y] & 0x0F, data[FT63X6_ADDR_TOUCH1_Y + 1]); + this->add_raw_touch_position_(touch_id, x, y); + } + if (((data[FT63X6_ADDR_TOUCH2_STATE] >> 6) & 0x01) == 0) { + touch_id = data[FT63X6_ADDR_TOUCH2_ID] >> 4; // id1 = 0 or 1 + x = encode_uint16(data[FT63X6_ADDR_TOUCH2_X] & 0x0F, data[FT63X6_ADDR_TOUCH2_X + 1]); + y = encode_uint16(data[FT63X6_ADDR_TOUCH2_Y] & 0x0F, data[FT63X6_ADDR_TOUCH2_Y + 1]); + this->add_raw_touch_position_(touch_id, x, y); + } } } // namespace ft63x6 From e39099137d04c1a7d2214cd33614b6b2449e8c79 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Mon, 15 Jan 2024 03:08:10 +0100 Subject: [PATCH 136/193] update script/setup so it works fine on windows (#6087) --- script/setup | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/script/setup b/script/setup index 9f448cf5c4..f286b4672a 100755 --- a/script/setup +++ b/script/setup @@ -4,10 +4,13 @@ set -e cd "$(dirname "$0")/.." - +location="venv/bin/activate" if [ ! -n "$DEVCONTAINER" ] && [ ! -n "$VIRTUAL_ENV" ] && [ ! "$ESPHOME_NO_VENV" ]; then python3 -m venv venv - source venv/bin/activate + if [ -f venv/Scripts/activate ]; then + location="venv/Scripts/activate" + fi + source $location; fi # Avoid unsafe git error when running inside devcontainer @@ -25,4 +28,4 @@ script/platformio_install_deps.py platformio.ini --libraries --tools --platforms echo echo -echo "Virtual environment created; source venv/bin/activate to use it" +echo "Virtual environment created. Run 'source $location' to use it." From 8cd17986740e6433e6b557965dfb1b7db79cad0c Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Mon, 15 Jan 2024 03:09:35 +0100 Subject: [PATCH 137/193] add Pico-ResTouch-LCD-3.5 (#6078) --- esphome/components/ili9xxx/display.py | 6 +++++ .../components/ili9xxx/ili9xxx_display.cpp | 3 +++ esphome/components/ili9xxx/ili9xxx_display.h | 10 ++++++++- esphome/components/ili9xxx/ili9xxx_init.h | 22 +++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index b3fe8b2b41..af8141f9fc 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -66,6 +66,7 @@ MODELS = { "ST7789V": ili9xxx_ns.class_("ILI9XXXST7789V", ILI9XXXDisplay), "S3BOX": ili9xxx_ns.class_("ILI9XXXS3Box", ILI9XXXDisplay), "S3BOX_LITE": ili9xxx_ns.class_("ILI9XXXS3BoxLite", ILI9XXXDisplay), + "WSPICOLCD": ili9xxx_ns.class_("ILI9XXXWSPICOLCD", ILI9XXXDisplay), } COLOR_ORDERS = { @@ -78,6 +79,7 @@ COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE") CONF_LED_PIN = "led_pin" CONF_COLOR_PALETTE_IMAGES = "color_palette_images" CONF_INVERT_DISPLAY = "invert_display" +CONF_18BIT_MODE = "18bit_mode" def _validate(config): @@ -139,6 +141,7 @@ CONFIG_SCHEMA = cv.All( "'invert_display' has been replaced by 'invert_colors'" ), cv.Optional(CONF_INVERT_COLORS): cv.boolean, + cv.Optional(CONF_18BIT_MODE): cv.boolean, cv.Optional(CONF_COLOR_ORDER): cv.one_of(*COLOR_ORDERS.keys(), upper=True), cv.Exclusive(CONF_ROTATION, CONF_ROTATION): validate_rotation, cv.Exclusive(CONF_TRANSFORM, CONF_ROTATION): cv.Schema( @@ -241,3 +244,6 @@ async def to_code(config): if CONF_INVERT_COLORS in config: cg.add(var.invert_colors(config[CONF_INVERT_COLORS])) + + if CONF_18BIT_MODE in config: + cg.add(var.set_18bit_mode(config[CONF_18BIT_MODE])) diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index ab577b3875..2844856246 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -34,6 +34,9 @@ void ILI9XXXDisplay::setup() { mad |= MADCTL_MY; this->send_command(ILI9XXX_MADCTL, &mad, 1); + mad = this->is_18bitdisplay_ ? 0x66 : 0x55; + this->send_command(ILI9XXX_PIXFMT, &mad, 1); + this->x_low_ = this->width_; this->y_low_ = this->height_; this->x_high_ = 0; diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index 590be3e364..c470f1e75d 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -53,9 +53,9 @@ class ILI9XXXDisplay : public display::DisplayBuffer, addr += num_args; } } + float get_setup_priority() const override; void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; } - float get_setup_priority() const override; void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; } void set_palette(const uint8_t *palette) { this->palette_ = palette; } void set_buffer_color_mode(ILI9XXXColorMode color_mode) { this->buffer_color_mode_ = color_mode; } @@ -67,7 +67,10 @@ class ILI9XXXDisplay : public display::DisplayBuffer, this->offset_x_ = offset_x; this->offset_y_ = offset_y; } + void set_18bit_mode(bool mode) { this->is_18bitdisplay_ = mode; }; + void invert_colors(bool invert); + void command(uint8_t value); void data(uint8_t value); void send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes); @@ -209,5 +212,10 @@ class ILI9XXXS3BoxLite : public ILI9XXXDisplay { ILI9XXXS3BoxLite() : ILI9XXXDisplay(INITCMD_S3BOXLITE, 320, 240, true) {} }; +class ILI9XXXWSPICOLCD : public ILI9XXXDisplay { + public: + ILI9XXXWSPICOLCD() : ILI9XXXDisplay(INITCMD_WSPICOLCD, 320, 480, true) {} +}; + } // namespace ili9xxx } // namespace esphome diff --git a/esphome/components/ili9xxx/ili9xxx_init.h b/esphome/components/ili9xxx/ili9xxx_init.h index a74824052f..0bf1d5761d 100644 --- a/esphome/components/ili9xxx/ili9xxx_init.h +++ b/esphome/components/ili9xxx/ili9xxx_init.h @@ -316,6 +316,28 @@ static const uint8_t PROGMEM INITCMD_ST7789V[] = { 0x00 // End of list }; +static const uint8_t PROGMEM INITCMD_WSPICOLCD[] = { +ILI9XXX_SLPOUT, 0x80, +ILI9XXX_PIXFMT, 1, 0x66, +ILI9XXX_PWCTR1, 2, 0x17, 0x15, // VRH1 VRH2 -ok +ILI9XXX_PWCTR2, 1, 0x41, // VGH, VGL - ok +ILI9XXX_PWCTR3, 1, 0x44, +//ILI9XXX_VMCTR1, 4, 0x00, 0x00, 0x00, 0x00, +ILI9XXX_VMCTR1, 3, 0x00, 0x12, 0x80, // nVM VCM_REG VCM_REG_EN - not ok? +ILI9XXX_IFMODE, 1, 0x00, // -ok +ILI9XXX_FRMCTR1, 1, 0xA0, // Frame rate = 60Hz -- seems to help the background! -ok +ILI9XXX_INVCTR, 1, 0x02, // Display Inversion Control = 2dot - ok +ILI9XXX_DFUNCTR, 2, 0x02, 0x02, // Nomal scan -ok +ILI9XXX_ADJCTL3, 4, 0xA9, 0x51, 0x2C, 0x82, // Adjust Control 3 +ILI9XXX_GMCTRP1,15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F, +ILI9XXX_GMCTRN1,15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F, + +ILI9XXX_INVON, 0x80, +ILI9XXX_MADCTL, 1, 0x48, +ILI9XXX_DISPON, 0x80, +0x00 // End of list +}; + // clang-format on } // namespace ili9xxx } // namespace esphome From 412c999f1497b2935fedaf27d62d1fd3259c54e7 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Mon, 15 Jan 2024 07:41:01 +0100 Subject: [PATCH 138/193] Revert "add Pico-ResTouch-LCD-3.5" (#6098) --- esphome/components/ili9xxx/display.py | 6 ----- .../components/ili9xxx/ili9xxx_display.cpp | 3 --- esphome/components/ili9xxx/ili9xxx_display.h | 10 +-------- esphome/components/ili9xxx/ili9xxx_init.h | 22 ------------------- 4 files changed, 1 insertion(+), 40 deletions(-) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index af8141f9fc..b3fe8b2b41 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -66,7 +66,6 @@ MODELS = { "ST7789V": ili9xxx_ns.class_("ILI9XXXST7789V", ILI9XXXDisplay), "S3BOX": ili9xxx_ns.class_("ILI9XXXS3Box", ILI9XXXDisplay), "S3BOX_LITE": ili9xxx_ns.class_("ILI9XXXS3BoxLite", ILI9XXXDisplay), - "WSPICOLCD": ili9xxx_ns.class_("ILI9XXXWSPICOLCD", ILI9XXXDisplay), } COLOR_ORDERS = { @@ -79,7 +78,6 @@ COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE") CONF_LED_PIN = "led_pin" CONF_COLOR_PALETTE_IMAGES = "color_palette_images" CONF_INVERT_DISPLAY = "invert_display" -CONF_18BIT_MODE = "18bit_mode" def _validate(config): @@ -141,7 +139,6 @@ CONFIG_SCHEMA = cv.All( "'invert_display' has been replaced by 'invert_colors'" ), cv.Optional(CONF_INVERT_COLORS): cv.boolean, - cv.Optional(CONF_18BIT_MODE): cv.boolean, cv.Optional(CONF_COLOR_ORDER): cv.one_of(*COLOR_ORDERS.keys(), upper=True), cv.Exclusive(CONF_ROTATION, CONF_ROTATION): validate_rotation, cv.Exclusive(CONF_TRANSFORM, CONF_ROTATION): cv.Schema( @@ -244,6 +241,3 @@ async def to_code(config): if CONF_INVERT_COLORS in config: cg.add(var.invert_colors(config[CONF_INVERT_COLORS])) - - if CONF_18BIT_MODE in config: - cg.add(var.set_18bit_mode(config[CONF_18BIT_MODE])) diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index 2844856246..ab577b3875 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -34,9 +34,6 @@ void ILI9XXXDisplay::setup() { mad |= MADCTL_MY; this->send_command(ILI9XXX_MADCTL, &mad, 1); - mad = this->is_18bitdisplay_ ? 0x66 : 0x55; - this->send_command(ILI9XXX_PIXFMT, &mad, 1); - this->x_low_ = this->width_; this->y_low_ = this->height_; this->x_high_ = 0; diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index c470f1e75d..590be3e364 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -53,9 +53,9 @@ class ILI9XXXDisplay : public display::DisplayBuffer, addr += num_args; } } - float get_setup_priority() const override; void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; } + float get_setup_priority() const override; void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; } void set_palette(const uint8_t *palette) { this->palette_ = palette; } void set_buffer_color_mode(ILI9XXXColorMode color_mode) { this->buffer_color_mode_ = color_mode; } @@ -67,10 +67,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, this->offset_x_ = offset_x; this->offset_y_ = offset_y; } - void set_18bit_mode(bool mode) { this->is_18bitdisplay_ = mode; }; - void invert_colors(bool invert); - void command(uint8_t value); void data(uint8_t value); void send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes); @@ -212,10 +209,5 @@ class ILI9XXXS3BoxLite : public ILI9XXXDisplay { ILI9XXXS3BoxLite() : ILI9XXXDisplay(INITCMD_S3BOXLITE, 320, 240, true) {} }; -class ILI9XXXWSPICOLCD : public ILI9XXXDisplay { - public: - ILI9XXXWSPICOLCD() : ILI9XXXDisplay(INITCMD_WSPICOLCD, 320, 480, true) {} -}; - } // namespace ili9xxx } // namespace esphome diff --git a/esphome/components/ili9xxx/ili9xxx_init.h b/esphome/components/ili9xxx/ili9xxx_init.h index 0bf1d5761d..a74824052f 100644 --- a/esphome/components/ili9xxx/ili9xxx_init.h +++ b/esphome/components/ili9xxx/ili9xxx_init.h @@ -316,28 +316,6 @@ static const uint8_t PROGMEM INITCMD_ST7789V[] = { 0x00 // End of list }; -static const uint8_t PROGMEM INITCMD_WSPICOLCD[] = { -ILI9XXX_SLPOUT, 0x80, -ILI9XXX_PIXFMT, 1, 0x66, -ILI9XXX_PWCTR1, 2, 0x17, 0x15, // VRH1 VRH2 -ok -ILI9XXX_PWCTR2, 1, 0x41, // VGH, VGL - ok -ILI9XXX_PWCTR3, 1, 0x44, -//ILI9XXX_VMCTR1, 4, 0x00, 0x00, 0x00, 0x00, -ILI9XXX_VMCTR1, 3, 0x00, 0x12, 0x80, // nVM VCM_REG VCM_REG_EN - not ok? -ILI9XXX_IFMODE, 1, 0x00, // -ok -ILI9XXX_FRMCTR1, 1, 0xA0, // Frame rate = 60Hz -- seems to help the background! -ok -ILI9XXX_INVCTR, 1, 0x02, // Display Inversion Control = 2dot - ok -ILI9XXX_DFUNCTR, 2, 0x02, 0x02, // Nomal scan -ok -ILI9XXX_ADJCTL3, 4, 0xA9, 0x51, 0x2C, 0x82, // Adjust Control 3 -ILI9XXX_GMCTRP1,15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F, -ILI9XXX_GMCTRN1,15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F, - -ILI9XXX_INVON, 0x80, -ILI9XXX_MADCTL, 1, 0x48, -ILI9XXX_DISPON, 0x80, -0x00 // End of list -}; - // clang-format on } // namespace ili9xxx } // namespace esphome From 87cab92af66ee4340f0cd578f4162eb64533ff27 Mon Sep 17 00:00:00 2001 From: aschmitz <29508+aschmitz@users.noreply.github.com> Date: Mon, 15 Jan 2024 01:08:19 -0600 Subject: [PATCH 139/193] fix: negative temperatures on PMS5003T sensors (#6100) --- esphome/components/pmsx003/pmsx003.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/pmsx003/pmsx003.cpp b/esphome/components/pmsx003/pmsx003.cpp index 62488b765c..de2b23b8eb 100644 --- a/esphome/components/pmsx003/pmsx003.cpp +++ b/esphome/components/pmsx003/pmsx003.cpp @@ -279,7 +279,7 @@ void PMSX003Component::parse_data_() { // Note the pm particles 50um & 100um are not returned, // as PMS5003T uses those data values for temperature and humidity. - float temperature = this->get_16_bit_uint_(24) / 10.0f; + float temperature = (int16_t) this->get_16_bit_uint_(24) / 10.0f; float humidity = this->get_16_bit_uint_(26) / 10.0f; ESP_LOGD(TAG, From 72ab1700e71305038d8f8c71f3adb53bed64c526 Mon Sep 17 00:00:00 2001 From: mathieu-mp Date: Tue, 16 Jan 2024 07:38:09 +0100 Subject: [PATCH 140/193] Add triangle shapes to display component (#6096) --- esphome/components/display/display.cpp | 116 +++++++++++++++++++++++++ esphome/components/display/display.h | 15 ++++ 2 files changed, 131 insertions(+) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index f32fda4794..0c3631342e 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -141,6 +141,122 @@ void Display::filled_circle(int center_x, int center_y, int radius, Color color) } } while (dx <= 0); } +void HOT Display::triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color) { + this->line(x1, y1, x2, y2); + this->line(x1, y1, x3, y3); + this->line(x2, y2, x3, y3); +} +void Display::sort_triangle_points_by_y_(int *x1, int *y1, int *x2, int *y2, int *x3, int *y3) { + if (*y1 > *y2) { + int x_temp = *x1, y_temp = *y1; + *x1 = *x2, *y1 = *y2; + *x2 = x_temp, *y2 = y_temp; + } + if (*y1 > *y3) { + int x_temp = *x1, y_temp = *y1; + *x1 = *x3, *y1 = *y3; + *x3 = x_temp, *y3 = y_temp; + } + if (*y2 > *y3) { + int x_temp = *x2, y_temp = *y2; + *x2 = *x3, *y2 = *y3; + *x3 = x_temp, *y3 = y_temp; + } +} +void Display::filled_flat_side_triangle_(int x1, int y1, int x2, int y2, int x3, int y3, Color color) { + // y2 must be equal to y3 (same horizontal line) + + // Initialize Bresenham's algorithm for side 1 + int s1_current_x = x1; + int s1_current_y = y1; + bool s1_axis_swap = false; + int s1_dx = abs(x2 - x1); + int s1_dy = abs(y2 - y1); + int s1_sign_x = ((x2 - x1) >= 0) ? 1 : -1; + int s1_sign_y = ((y2 - y1) >= 0) ? 1 : -1; + if (s1_dy > s1_dx) { // swap values + int tmp = s1_dx; + s1_dx = s1_dy; + s1_dy = tmp; + s1_axis_swap = true; + } + int s1_error = 2 * s1_dy - s1_dx; + + // Initialize Bresenham's algorithm for side 2 + int s2_current_x = x1; + int s2_current_y = y1; + bool s2_axis_swap = false; + int s2_dx = abs(x3 - x1); + int s2_dy = abs(y3 - y1); + int s2_sign_x = ((x3 - x1) >= 0) ? 1 : -1; + int s2_sign_y = ((y3 - y1) >= 0) ? 1 : -1; + if (s2_dy > s2_dx) { // swap values + int tmp = s2_dx; + s2_dx = s2_dy; + s2_dy = tmp; + s2_axis_swap = true; + } + int s2_error = 2 * s2_dy - s2_dx; + + // Iterate on side 1 and allow side 2 to be processed to match the advance of the y-axis. + for (int i = 0; i <= s1_dx; i++) { + if (s1_current_x <= s2_current_x) { + this->horizontal_line(s1_current_x, s1_current_y, s2_current_x - s1_current_x + 1, color); + } else { + this->horizontal_line(s2_current_x, s2_current_y, s1_current_x - s2_current_x + 1, color); + } + + // Bresenham's #1 + // Side 1 s1_current_x and s1_current_y calculation + while (s1_error >= 0) { + if (s1_axis_swap) { + s1_current_x += s1_sign_x; + } else { + s1_current_y += s1_sign_y; + } + s1_error = s1_error - 2 * s1_dx; + } + if (s1_axis_swap) { + s1_current_y += s1_sign_y; + } else { + s1_current_x += s1_sign_x; + } + s1_error = s1_error + 2 * s1_dy; + + // Bresenham's #2 + // Side 2 s2_current_x and s2_current_y calculation + while (s2_current_y != s1_current_y) { + while (s2_error >= 0) { + if (s2_axis_swap) { + s2_current_x += s2_sign_x; + } else { + s2_current_y += s2_sign_y; + } + s2_error = s2_error - 2 * s2_dx; + } + if (s2_axis_swap) { + s2_current_y += s2_sign_y; + } else { + s2_current_x += s2_sign_x; + } + s2_error = s2_error + 2 * s2_dy; + } + } +} +void Display::filled_triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color) { + // Sort the three points by y-coordinate ascending, so [x1,y1] is the topmost point + this->sort_triangle_points_by_y_(&x1, &y1, &x2, &y2, &x3, &y3); + + if (y2 == y3) { // Check for special case of a bottom-flat triangle + this->filled_flat_side_triangle_(x1, y1, x2, y2, x3, y3, color); + } else if (y1 == y2) { // Check for special case of a top-flat triangle + this->filled_flat_side_triangle_(x3, y3, x1, y1, x2, y2, color); + } else { // General case: split the no-flat-side triangle in a top-flat triangle and bottom-flat triangle + int x_temp = (int) (x1 + ((float) (y2 - y1) / (float) (y3 - y1)) * (x3 - x1)), y_temp = y2; + this->filled_flat_side_triangle_(x1, y1, x2, y2, x_temp, y_temp, color); + this->filled_flat_side_triangle_(x3, y3, x2, y2, x_temp, y_temp, color); + } +} void Display::print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text) { int x_start, y_start; diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index 2a2a9b80c8..daa5028d6b 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -236,6 +236,12 @@ class Display : public PollingComponent { /// Fill a circle centered around [center_x,center_y] with the radius radius with the given color. void filled_circle(int center_x, int center_y, int radius, Color color = COLOR_ON); + /// Draw the outline of a triangle contained between the points [x1,y1], [x2,y2] and [x3,y3] with the given color. + void triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color = COLOR_ON); + + /// Fill a triangle contained between the points [x1,y1], [x2,y2] and [x3,y3] with the given color. + void filled_triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color = COLOR_ON); + /** Print `text` with the anchor point at [x,y] with `font`. * * @param x The x coordinate of the text alignment anchor point. @@ -532,6 +538,15 @@ class Display : public PollingComponent { void do_update_(); void clear_clipping_(); + /** + * This method fills a triangle using only integer variables by using a + * modified bresenham algorithm. + * It is mandatory that [x2,y2] and [x3,y3] lie on the same horizontal line, + * so y2 must be equal to y3. + */ + void filled_flat_side_triangle_(int x1, int y1, int x2, int y2, int x3, int y3, Color color); + void sort_triangle_points_by_y_(int *x1, int *y1, int *x2, int *y2, int *x3, int *y3); + DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES}; optional writer_{}; DisplayPage *page_{nullptr}; From 249cd6758879c56b72cc1b165a02cd8df1e551f0 Mon Sep 17 00:00:00 2001 From: alexborro Date: Tue, 16 Jan 2024 08:38:19 +0100 Subject: [PATCH 141/193] Fingerprint_grow: Trigger on finger scan start and on finger scan misplaced (#6003) --- .../components/fingerprint_grow/__init__.py | 32 +++++++++++++ .../fingerprint_grow/fingerprint_grow.cpp | 46 +++++++++++++------ .../fingerprint_grow/fingerprint_grow.h | 24 ++++++++++ esphome/const.py | 2 + tests/test3.yaml | 6 +++ 5 files changed, 96 insertions(+), 14 deletions(-) diff --git a/esphome/components/fingerprint_grow/__init__.py b/esphome/components/fingerprint_grow/__init__.py index 5249107f17..26a01fc1d2 100644 --- a/esphome/components/fingerprint_grow/__init__.py +++ b/esphome/components/fingerprint_grow/__init__.py @@ -13,8 +13,10 @@ from esphome.const import ( CONF_ON_ENROLLMENT_DONE, CONF_ON_ENROLLMENT_FAILED, CONF_ON_ENROLLMENT_SCAN, + CONF_ON_FINGER_SCAN_START, CONF_ON_FINGER_SCAN_MATCHED, CONF_ON_FINGER_SCAN_UNMATCHED, + CONF_ON_FINGER_SCAN_MISPLACED, CONF_ON_FINGER_SCAN_INVALID, CONF_PASSWORD, CONF_SENSING_PIN, @@ -35,6 +37,10 @@ FingerprintGrowComponent = fingerprint_grow_ns.class_( "FingerprintGrowComponent", cg.PollingComponent, uart.UARTDevice ) +FingerScanStartTrigger = fingerprint_grow_ns.class_( + "FingerScanStartTrigger", automation.Trigger.template() +) + FingerScanMatchedTrigger = fingerprint_grow_ns.class_( "FingerScanMatchedTrigger", automation.Trigger.template(cg.uint16, cg.uint16) ) @@ -43,6 +49,10 @@ FingerScanUnmatchedTrigger = fingerprint_grow_ns.class_( "FingerScanUnmatchedTrigger", automation.Trigger.template() ) +FingerScanMisplacedTrigger = fingerprint_grow_ns.class_( + "FingerScanMisplacedTrigger", automation.Trigger.template() +) + FingerScanInvalidTrigger = fingerprint_grow_ns.class_( "FingerScanInvalidTrigger", automation.Trigger.template() ) @@ -99,6 +109,13 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_SENSING_PIN): pins.gpio_input_pin_schema, cv.Optional(CONF_PASSWORD): cv.uint32_t, cv.Optional(CONF_NEW_PASSWORD): cv.uint32_t, + cv.Optional(CONF_ON_FINGER_SCAN_START): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + FingerScanStartTrigger + ), + } + ), cv.Optional(CONF_ON_FINGER_SCAN_MATCHED): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( @@ -113,6 +130,13 @@ CONFIG_SCHEMA = ( ), } ), + cv.Optional(CONF_ON_FINGER_SCAN_MISPLACED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + FingerScanMisplacedTrigger + ), + } + ), cv.Optional(CONF_ON_FINGER_SCAN_INVALID): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( @@ -164,6 +188,10 @@ async def to_code(config): sensing_pin = await cg.gpio_pin_expression(config[CONF_SENSING_PIN]) cg.add(var.set_sensing_pin(sensing_pin)) + for conf in config.get(CONF_ON_FINGER_SCAN_START, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_FINGER_SCAN_MATCHED, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation( @@ -174,6 +202,10 @@ async def to_code(config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_FINGER_SCAN_MISPLACED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_FINGER_SCAN_INVALID, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index 2486e02964..0a46755bd3 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -15,16 +15,18 @@ void FingerprintGrowComponent::update() { return; } - if (this->sensing_pin_ != nullptr) { + if (this->has_sensing_pin_) { if (this->sensing_pin_->digital_read()) { ESP_LOGV(TAG, "No touch sensing"); this->waiting_removal_ = false; return; + } else if (!this->waiting_removal_) { + this->finger_scan_start_callback_.call(); } } if (this->waiting_removal_) { - if (this->scan_image_(1) == NO_FINGER) { + if ((!this->has_sensing_pin_) && (this->scan_image_(1) == NO_FINGER)) { ESP_LOGD(TAG, "Finger removed"); this->waiting_removal_ = false; } @@ -51,6 +53,7 @@ void FingerprintGrowComponent::update() { void FingerprintGrowComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Grow Fingerprint Reader..."); + this->has_sensing_pin_ = (this->sensing_pin_ != nullptr); if (this->check_password_()) { if (this->new_password_ != -1) { if (this->set_password_()) @@ -91,7 +94,7 @@ void FingerprintGrowComponent::finish_enrollment(uint8_t result) { } void FingerprintGrowComponent::scan_and_match_() { - if (this->sensing_pin_ != nullptr) { + if (this->has_sensing_pin_) { ESP_LOGD(TAG, "Scan and match"); } else { ESP_LOGV(TAG, "Scan and match"); @@ -122,33 +125,38 @@ void FingerprintGrowComponent::scan_and_match_() { } uint8_t FingerprintGrowComponent::scan_image_(uint8_t buffer) { - if (this->sensing_pin_ != nullptr) { + if (this->has_sensing_pin_) { ESP_LOGD(TAG, "Getting image %d", buffer); } else { ESP_LOGV(TAG, "Getting image %d", buffer); } this->data_ = {GET_IMAGE}; - switch (this->send_command_()) { + uint8_t send_result = this->send_command_(); + switch (send_result) { case OK: break; case NO_FINGER: - if (this->sensing_pin_ != nullptr) { - ESP_LOGD(TAG, "No finger"); - this->finger_scan_invalid_callback_.call(); + if (this->has_sensing_pin_) { + this->waiting_removal_ = true; + ESP_LOGD(TAG, "Finger Misplaced"); + this->finger_scan_misplaced_callback_.call(); } else { ESP_LOGV(TAG, "No finger"); } - return this->data_[0]; + return send_result; case IMAGE_FAIL: ESP_LOGE(TAG, "Imaging error"); this->finger_scan_invalid_callback_.call(); + return send_result; default: - return this->data_[0]; + ESP_LOGD(TAG, "Unknown Scan Error: %d", send_result); + return send_result; } ESP_LOGD(TAG, "Processing image %d", buffer); this->data_ = {IMAGE_2_TZ, buffer}; - switch (this->send_command_()) { + send_result = this->send_command_(); + switch (send_result) { case OK: ESP_LOGI(TAG, "Processed image %d", buffer); break; @@ -162,7 +170,7 @@ uint8_t FingerprintGrowComponent::scan_image_(uint8_t buffer) { this->finger_scan_invalid_callback_.call(); break; } - return this->data_[0]; + return send_result; } uint8_t FingerprintGrowComponent::save_fingerprint_() { @@ -225,10 +233,11 @@ bool FingerprintGrowComponent::get_parameters_() { ESP_LOGD(TAG, "Getting parameters"); this->data_ = {READ_SYS_PARAM}; if (this->send_command_() == OK) { - ESP_LOGD(TAG, "Got parameters"); - if (this->status_sensor_ != nullptr) { + ESP_LOGD(TAG, "Got parameters"); // Bear in mind data_[0] is the transfer status, + if (this->status_sensor_ != nullptr) { // the parameters table start at data_[1] this->status_sensor_->publish_state(((uint16_t) this->data_[1] << 8) | this->data_[2]); } + this->system_identifier_code_ = ((uint16_t) this->data_[3] << 8) | this->data_[4]; this->capacity_ = ((uint16_t) this->data_[5] << 8) | this->data_[6]; if (this->capacity_sensor_ != nullptr) { this->capacity_sensor_->publish_state(this->capacity_); @@ -430,13 +439,22 @@ uint8_t FingerprintGrowComponent::send_command_() { void FingerprintGrowComponent::dump_config() { ESP_LOGCONFIG(TAG, "GROW_FINGERPRINT_READER:"); + ESP_LOGCONFIG(TAG, " System Identifier Code: 0x%.4X", this->system_identifier_code_); + ESP_LOGCONFIG(TAG, " Touch Sensing Pin: %s", + this->has_sensing_pin_ ? this->sensing_pin_->dump_summary().c_str() : "None"); LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Fingerprint Count", this->fingerprint_count_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %d", (uint16_t) this->fingerprint_count_sensor_->get_state()); LOG_SENSOR(" ", "Status", this->status_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %d", (uint8_t) this->status_sensor_->get_state()); LOG_SENSOR(" ", "Capacity", this->capacity_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %d", (uint16_t) this->capacity_sensor_->get_state()); LOG_SENSOR(" ", "Security Level", this->security_level_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %d", (uint8_t) this->security_level_sensor_->get_state()); LOG_SENSOR(" ", "Last Finger ID", this->last_finger_id_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %d", (uint32_t) this->last_finger_id_sensor_->get_state()); LOG_SENSOR(" ", "Last Confidence", this->last_confidence_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %d", (uint32_t) this->last_confidence_sensor_->get_state()); } } // namespace fingerprint_grow diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.h b/esphome/components/fingerprint_grow/fingerprint_grow.h index 9aad94fc2a..1ab38d9fb5 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.h +++ b/esphome/components/fingerprint_grow/fingerprint_grow.h @@ -118,12 +118,18 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic void set_enrolling_binary_sensor(binary_sensor::BinarySensor *enrolling_binary_sensor) { this->enrolling_binary_sensor_ = enrolling_binary_sensor; } + void add_on_finger_scan_start_callback(std::function callback) { + this->finger_scan_start_callback_.add(std::move(callback)); + } void add_on_finger_scan_matched_callback(std::function callback) { this->finger_scan_matched_callback_.add(std::move(callback)); } void add_on_finger_scan_unmatched_callback(std::function callback) { this->finger_scan_unmatched_callback_.add(std::move(callback)); } + void add_on_finger_scan_misplaced_callback(std::function callback) { + this->finger_scan_misplaced_callback_.add(std::move(callback)); + } void add_on_finger_scan_invalid_callback(std::function callback) { this->finger_scan_invalid_callback_.add(std::move(callback)); } @@ -166,8 +172,10 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic uint16_t enrollment_slot_ = ENROLLMENT_SLOT_UNUSED; uint8_t enrollment_buffers_ = 5; bool waiting_removal_ = false; + bool has_sensing_pin_ = false; uint32_t last_aura_led_control_ = 0; uint16_t last_aura_led_duration_ = 0; + uint16_t system_identifier_code_ = 0; sensor::Sensor *fingerprint_count_sensor_{nullptr}; sensor::Sensor *status_sensor_{nullptr}; sensor::Sensor *capacity_sensor_{nullptr}; @@ -176,13 +184,22 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic sensor::Sensor *last_confidence_sensor_{nullptr}; binary_sensor::BinarySensor *enrolling_binary_sensor_{nullptr}; CallbackManager finger_scan_invalid_callback_; + CallbackManager finger_scan_start_callback_; CallbackManager finger_scan_matched_callback_; CallbackManager finger_scan_unmatched_callback_; + CallbackManager finger_scan_misplaced_callback_; CallbackManager enrollment_scan_callback_; CallbackManager enrollment_done_callback_; CallbackManager enrollment_failed_callback_; }; +class FingerScanStartTrigger : public Trigger<> { + public: + explicit FingerScanStartTrigger(FingerprintGrowComponent *parent) { + parent->add_on_finger_scan_start_callback([this]() { this->trigger(); }); + } +}; + class FingerScanMatchedTrigger : public Trigger { public: explicit FingerScanMatchedTrigger(FingerprintGrowComponent *parent) { @@ -198,6 +215,13 @@ class FingerScanUnmatchedTrigger : public Trigger<> { } }; +class FingerScanMisplacedTrigger : public Trigger<> { + public: + explicit FingerScanMisplacedTrigger(FingerprintGrowComponent *parent) { + parent->add_on_finger_scan_misplaced_callback([this]() { this->trigger(); }); + } +}; + class FingerScanInvalidTrigger : public Trigger<> { public: explicit FingerScanInvalidTrigger(FingerprintGrowComponent *parent) { diff --git a/esphome/const.py b/esphome/const.py index 7e27254d76..c35adc74ee 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -508,6 +508,8 @@ CONF_ON_ENROLLMENT_FAILED = "on_enrollment_failed" CONF_ON_ENROLLMENT_SCAN = "on_enrollment_scan" CONF_ON_FINGER_SCAN_INVALID = "on_finger_scan_invalid" CONF_ON_FINGER_SCAN_MATCHED = "on_finger_scan_matched" +CONF_ON_FINGER_SCAN_MISPLACED = "on_finger_scan_misplaced" +CONF_ON_FINGER_SCAN_START = "on_finger_scan_start" CONF_ON_FINGER_SCAN_UNMATCHED = "on_finger_scan_unmatched" CONF_ON_JSON_MESSAGE = "on_json_message" CONF_ON_LOCK = "on_lock" diff --git a/tests/test3.yaml b/tests/test3.yaml index c31eb45fbd..cbd3d15b8a 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -1258,6 +1258,9 @@ fingerprint_grow: number: 4 password: 0x12FE37DC new_password: 0xA65B9840 + on_finger_scan_start: + - homeassistant.event: + event: esphome.${device_name}_fingerprint_grow_finger_scan_start on_finger_scan_invalid: - homeassistant.event: event: esphome.${device_name}_fingerprint_grow_finger_scan_invalid @@ -1270,6 +1273,9 @@ fingerprint_grow: on_finger_scan_unmatched: - homeassistant.event: event: esphome.${device_name}_fingerprint_grow_finger_scan_unmatched + on_finger_scan_misplaced: + - homeassistant.event: + event: esphome.${device_name}_fingerprint_grow_finger_scan_misplaced on_enrollment_scan: - homeassistant.event: event: esphome.${device_name}_fingerprint_grow_enrollment_scan From 26acbbedbf2626eec1232343901a01348369a8ce Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 15 Jan 2024 23:44:12 -0800 Subject: [PATCH 142/193] Add continuous option to the graph (#6093) Co-authored-by: Samuel Sieb --- esphome/components/graph/__init__.py | 4 +++ esphome/components/graph/graph.cpp | 37 +++++++++++++++++++++++----- esphome/components/graph/graph.h | 3 +++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/esphome/components/graph/__init__.py b/esphome/components/graph/__init__.py index 046f59ca1a..0b83b71fe4 100644 --- a/esphome/components/graph/__init__.py +++ b/esphome/components/graph/__init__.py @@ -61,6 +61,7 @@ VALUE_POSITION_TYPE = { "BELOW": ValuePositionType.VALUE_POSITION_TYPE_BELOW, } +CONF_CONTINUOUS = "continuous" GRAPH_TRACE_SCHEMA = cv.Schema( { @@ -70,6 +71,7 @@ GRAPH_TRACE_SCHEMA = cv.Schema( cv.Optional(CONF_LINE_THICKNESS): cv.positive_int, cv.Optional(CONF_LINE_TYPE): cv.enum(LINE_TYPE, upper=True), cv.Optional(CONF_COLOR): cv.use_id(color.ColorStruct), + cv.Optional(CONF_CONTINUOUS): cv.boolean, } ) @@ -186,6 +188,8 @@ async def to_code(config): if CONF_COLOR in trace: c = await cg.get_variable(trace[CONF_COLOR]) cg.add(tr.set_line_color(c)) + if CONF_CONTINUOUS in trace: + cg.add(tr.set_continuous(trace[CONF_CONTINUOUS])) cg.add(var.add_trace(tr)) # Add legend if CONF_LEGEND in config: diff --git a/esphome/components/graph/graph.cpp b/esphome/components/graph/graph.cpp index 294e16dbb1..0e437a3425 100644 --- a/esphome/components/graph/graph.cpp +++ b/esphome/components/graph/graph.cpp @@ -165,17 +165,42 @@ void Graph::draw(Display *buff, uint16_t x_offset, uint16_t y_offset, Color colo for (auto *trace : traces_) { Color c = trace->get_line_color(); uint16_t thick = trace->get_line_thickness(); + bool continuous = trace->get_continuous(); + bool has_prev = false; + bool prev_b = false; + int16_t prev_y = 0; for (uint32_t i = 0; i < this->width_; i++) { float v = (trace->get_tracedata()->get_value(i) - ymin) / yrange; if (!std::isnan(v) && (thick > 0)) { - int16_t x = this->width_ - 1 - i; - uint8_t b = (i % (thick * LineType::PATTERN_LENGTH)) / thick; - if (((uint8_t) trace->get_line_type() & (1 << b)) == (1 << b)) { - int16_t y = (int16_t) roundf((this->height_ - 1) * (1.0 - v)) - thick / 2; - for (uint16_t t = 0; t < thick; t++) { - buff->draw_pixel_at(x_offset + x, y_offset + y + t, c); + int16_t x = this->width_ - 1 - i + x_offset; + uint8_t bit = 1 << ((i % (thick * LineType::PATTERN_LENGTH)) / thick); + bool b = (trace->get_line_type() & bit) == bit; + if (b) { + int16_t y = (int16_t) roundf((this->height_ - 1) * (1.0 - v)) - thick / 2 + y_offset; + if (!continuous || !has_prev || !prev_b || (abs(y - prev_y) <= thick)) { + for (uint16_t t = 0; t < thick; t++) { + buff->draw_pixel_at(x, y + t, c); + } + } else { + int16_t mid_y = (y + prev_y + thick) / 2; + if (y > prev_y) { + for (uint16_t t = prev_y + thick; t <= mid_y; t++) + buff->draw_pixel_at(x + 1, t, c); + for (uint16_t t = mid_y + 1; t < y + thick; t++) + buff->draw_pixel_at(x, t, c); + } else { + for (uint16_t t = prev_y - 1; t >= mid_y; t--) + buff->draw_pixel_at(x + 1, t, c); + for (uint16_t t = mid_y - 1; t >= y; t--) + buff->draw_pixel_at(x, t, c); + } } + prev_y = y; } + prev_b = b; + has_prev = true; + } else { + has_prev = false; } } } diff --git a/esphome/components/graph/graph.h b/esphome/components/graph/graph.h index 339a6f6d94..34accb7d3a 100644 --- a/esphome/components/graph/graph.h +++ b/esphome/components/graph/graph.h @@ -116,6 +116,8 @@ class GraphTrace { void set_line_type(enum LineType val) { this->line_type_ = val; } Color get_line_color() { return this->line_color_; } void set_line_color(Color val) { this->line_color_ = val; } + bool get_continuous() { return this->continuous_; } + void set_continuous(bool continuous) { this->continuous_ = continuous; } std::string get_name() { return name_; } const HistoryData *get_tracedata() { return &data_; } @@ -125,6 +127,7 @@ class GraphTrace { uint8_t line_thickness_{3}; enum LineType line_type_ { LINE_TYPE_SOLID }; Color line_color_{COLOR_ON}; + bool continuous_{false}; HistoryData data_; friend Graph; From e35cab018a464c0d39fe7916a2b5de9d7c9487e7 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 16 Jan 2024 02:05:13 -0600 Subject: [PATCH 143/193] Add NFC binary sensor platform (#6068) --- CODEOWNERS | 2 +- esphome/components/nfc/__init__.py | 5 +- .../components/nfc/binary_sensor/__init__.py | 72 +++++++++++ .../nfc/binary_sensor/binary_sensor.cpp | 114 ++++++++++++++++++ .../nfc/binary_sensor/binary_sensor.h | 38 ++++++ esphome/components/nfc/nfc.h | 14 +++ esphome/components/pn7150/__init__.py | 2 +- esphome/components/pn7150/pn7150.cpp | 6 + esphome/components/pn7150/pn7150.h | 2 +- esphome/components/pn7160/__init__.py | 2 +- esphome/components/pn7160/pn7160.cpp | 6 + esphome/components/pn7160/pn7160.h | 2 +- tests/test1.yaml | 13 ++ 13 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 esphome/components/nfc/binary_sensor/__init__.py create mode 100644 esphome/components/nfc/binary_sensor/binary_sensor.cpp create mode 100644 esphome/components/nfc/binary_sensor/binary_sensor.h diff --git a/CODEOWNERS b/CODEOWNERS index 0ff5ce4508..c497a82eab 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -227,7 +227,7 @@ esphome/components/nextion/binary_sensor/* @senexcrenshaw esphome/components/nextion/sensor/* @senexcrenshaw esphome/components/nextion/switch/* @senexcrenshaw esphome/components/nextion/text_sensor/* @senexcrenshaw -esphome/components/nfc/* @jesserockz +esphome/components/nfc/* @jesserockz @kbx81 esphome/components/noblex/* @AGalfra esphome/components/number/* @esphome/core esphome/components/ota/* @esphome/core diff --git a/esphome/components/nfc/__init__.py b/esphome/components/nfc/__init__.py index c3bbc50bf9..eea1a47b24 100644 --- a/esphome/components/nfc/__init__.py +++ b/esphome/components/nfc/__init__.py @@ -1,12 +1,13 @@ from esphome import automation import esphome.codegen as cg -CODEOWNERS = ["@jesserockz"] +CODEOWNERS = ["@jesserockz", "@kbx81"] nfc_ns = cg.esphome_ns.namespace("nfc") +Nfcc = nfc_ns.class_("Nfcc") NfcTag = nfc_ns.class_("NfcTag") - +NfcTagListener = nfc_ns.class_("NfcTagListener") NfcOnTagTrigger = nfc_ns.class_( "NfcOnTagTrigger", automation.Trigger.template(cg.std_string, NfcTag) ) diff --git a/esphome/components/nfc/binary_sensor/__init__.py b/esphome/components/nfc/binary_sensor/__init__.py new file mode 100644 index 0000000000..21c8298ea8 --- /dev/null +++ b/esphome/components/nfc/binary_sensor/__init__.py @@ -0,0 +1,72 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_UID +from esphome.core import HexInt +from .. import nfc_ns, Nfcc, NfcTagListener + +DEPENDENCIES = ["nfc"] + +CONF_NDEF_CONTAINS = "ndef_contains" +CONF_NFCC_ID = "nfcc_id" +CONF_TAG_ID = "tag_id" + +NfcTagBinarySensor = nfc_ns.class_( + "NfcTagBinarySensor", + binary_sensor.BinarySensor, + cg.Component, + NfcTagListener, + cg.Parented.template(Nfcc), +) + + +def validate_uid(value): + value = cv.string_strict(value) + for x in value.split("-"): + if len(x) != 2: + raise cv.Invalid( + "Each part (separated by '-') of the UID must be two characters " + "long." + ) + try: + x = int(x, 16) + except ValueError as err: + raise cv.Invalid( + "Valid characters for parts of a UID are 0123456789ABCDEF." + ) from err + if x < 0 or x > 255: + raise cv.Invalid( + "Valid values for UID parts (separated by '-') are 00 to FF" + ) + return value + + +CONFIG_SCHEMA = cv.All( + binary_sensor.binary_sensor_schema(NfcTagBinarySensor) + .extend( + { + cv.GenerateID(CONF_NFCC_ID): cv.use_id(Nfcc), + cv.Optional(CONF_NDEF_CONTAINS): cv.string, + cv.Optional(CONF_TAG_ID): cv.string, + cv.Optional(CONF_UID): validate_uid, + } + ) + .extend(cv.COMPONENT_SCHEMA), + cv.has_exactly_one_key(CONF_NDEF_CONTAINS, CONF_TAG_ID, CONF_UID), +) + + +async def to_code(config): + var = await binary_sensor.new_binary_sensor(config) + await cg.register_component(var, config) + await cg.register_parented(var, config[CONF_NFCC_ID]) + + hub = await cg.get_variable(config[CONF_NFCC_ID]) + cg.add(hub.register_listener(var)) + if CONF_NDEF_CONTAINS in config: + cg.add(var.set_ndef_match_string(config[CONF_NDEF_CONTAINS])) + if CONF_TAG_ID in config: + cg.add(var.set_tag_name(config[CONF_TAG_ID])) + elif CONF_UID in config: + addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split("-")] + cg.add(var.set_uid(addr)) diff --git a/esphome/components/nfc/binary_sensor/binary_sensor.cpp b/esphome/components/nfc/binary_sensor/binary_sensor.cpp new file mode 100644 index 0000000000..8f1f6acd51 --- /dev/null +++ b/esphome/components/nfc/binary_sensor/binary_sensor.cpp @@ -0,0 +1,114 @@ +#include "binary_sensor.h" +#include "../nfc_helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace nfc { + +static const char *const TAG = "nfc.binary_sensor"; + +void NfcTagBinarySensor::setup() { + this->parent_->register_listener(this); + this->publish_initial_state(false); +} + +void NfcTagBinarySensor::dump_config() { + std::string match_str = "name"; + + LOG_BINARY_SENSOR("", "NFC Tag Binary Sensor", this); + if (!this->match_string_.empty()) { + if (!this->match_tag_name_) { + match_str = "contains"; + } + ESP_LOGCONFIG(TAG, " Tag %s: %s", match_str.c_str(), this->match_string_.c_str()); + return; + } + if (!this->uid_.empty()) { + ESP_LOGCONFIG(TAG, " Tag UID: %s", format_bytes(this->uid_).c_str()); + } +} + +void NfcTagBinarySensor::set_ndef_match_string(const std::string &str) { + this->match_string_ = str; + this->match_tag_name_ = false; +} + +void NfcTagBinarySensor::set_tag_name(const std::string &str) { + this->match_string_ = str; + this->match_tag_name_ = true; +} + +void NfcTagBinarySensor::set_uid(const std::vector &uid) { this->uid_ = uid; } + +bool NfcTagBinarySensor::tag_match_ndef_string(const std::shared_ptr &msg) { + for (const auto &record : msg->get_records()) { + if (record->get_payload().find(this->match_string_) != std::string::npos) { + return true; + } + } + return false; +} + +bool NfcTagBinarySensor::tag_match_tag_name(const std::shared_ptr &msg) { + for (const auto &record : msg->get_records()) { + if (record->get_payload().find(HA_TAG_ID_PREFIX) != std::string::npos) { + auto rec_substr = record->get_payload().substr(sizeof(HA_TAG_ID_PREFIX) - 1); + if (rec_substr.find(this->match_string_) != std::string::npos) { + return true; + } + } + } + return false; +} + +bool NfcTagBinarySensor::tag_match_uid(const std::vector &data) { + if (data.size() != this->uid_.size()) { + return false; + } + + for (size_t i = 0; i < data.size(); i++) { + if (data[i] != this->uid_[i]) { + return false; + } + } + return true; +} + +void NfcTagBinarySensor::tag_off(NfcTag &tag) { + if (!this->match_string_.empty() && tag.has_ndef_message()) { + if (this->match_tag_name_) { + if (this->tag_match_tag_name(tag.get_ndef_message())) { + this->publish_state(false); + } + } else { + if (this->tag_match_ndef_string(tag.get_ndef_message())) { + this->publish_state(false); + } + } + return; + } + if (!this->uid_.empty() && this->tag_match_uid(tag.get_uid())) { + this->publish_state(false); + } +} + +void NfcTagBinarySensor::tag_on(NfcTag &tag) { + if (!this->match_string_.empty() && tag.has_ndef_message()) { + if (this->match_tag_name_) { + if (this->tag_match_tag_name(tag.get_ndef_message())) { + this->publish_state(true); + } + } else { + if (this->tag_match_ndef_string(tag.get_ndef_message())) { + this->publish_state(true); + } + } + return; + } + if (!this->uid_.empty() && this->tag_match_uid(tag.get_uid())) { + this->publish_state(true); + } +} + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/binary_sensor/binary_sensor.h b/esphome/components/nfc/binary_sensor/binary_sensor.h new file mode 100644 index 0000000000..cc313c2f2b --- /dev/null +++ b/esphome/components/nfc/binary_sensor/binary_sensor.h @@ -0,0 +1,38 @@ +#pragma once + +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/nfc/nfc.h" +#include "esphome/components/nfc/nfc_tag.h" +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace nfc { + +class NfcTagBinarySensor : public binary_sensor::BinarySensor, + public Component, + public NfcTagListener, + public Parented { + public: + void setup() override; + void dump_config() override; + + void set_ndef_match_string(const std::string &str); + void set_tag_name(const std::string &str); + void set_uid(const std::vector &uid); + + bool tag_match_ndef_string(const std::shared_ptr &msg); + bool tag_match_tag_name(const std::shared_ptr &msg); + bool tag_match_uid(const std::vector &data); + + void tag_off(NfcTag &tag) override; + void tag_on(NfcTag &tag) override; + + protected: + bool match_tag_name_{false}; + std::string match_string_; + std::vector uid_; +}; + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/nfc.h b/esphome/components/nfc/nfc.h index d4d66f970f..23bfdd8ef0 100644 --- a/esphome/components/nfc/nfc.h +++ b/esphome/components/nfc/nfc.h @@ -66,5 +66,19 @@ bool mifare_classic_is_trailer_block(uint8_t block_num); uint32_t get_mifare_ultralight_buffer_size(uint32_t message_length); +class NfcTagListener { + public: + virtual void tag_off(NfcTag &tag) {} + virtual void tag_on(NfcTag &tag) {} +}; + +class Nfcc { + public: + void register_listener(NfcTagListener *listener) { this->tag_listeners_.push_back(listener); } + + protected: + std::vector tag_listeners_; +}; + } // namespace nfc } // namespace esphome diff --git a/esphome/components/pn7150/__init__.py b/esphome/components/pn7150/__init__.py index 3b80b574e9..a136028011 100644 --- a/esphome/components/pn7150/__init__.py +++ b/esphome/components/pn7150/__init__.py @@ -34,7 +34,7 @@ CONF_TAG_TTL = "tag_ttl" CONF_VEN_PIN = "ven_pin" pn7150_ns = cg.esphome_ns.namespace("pn7150") -PN7150 = pn7150_ns.class_("PN7150", cg.Component) +PN7150 = pn7150_ns.class_("PN7150", nfc.Nfcc, cg.Component) EmulationOffAction = pn7150_ns.class_("EmulationOffAction", automation.Action) EmulationOnAction = pn7150_ns.class_("EmulationOnAction", automation.Action) diff --git a/esphome/components/pn7150/pn7150.cpp b/esphome/components/pn7150/pn7150.cpp index 6703ab6a12..be4d6c1bb7 100644 --- a/esphome/components/pn7150/pn7150.cpp +++ b/esphome/components/pn7150/pn7150.cpp @@ -566,6 +566,9 @@ void PN7150::erase_tag_(const uint8_t tag_index) { for (auto *trigger : this->triggers_ontagremoved_) { trigger->process(this->discovered_endpoint_[tag_index].tag); } + for (auto *listener : this->tag_listeners_) { + listener->tag_off(*this->discovered_endpoint_[tag_index].tag); + } ESP_LOGI(TAG, "Tag %s removed", nfc::format_uid(this->discovered_endpoint_[tag_index].tag->get_uid()).c_str()); this->discovered_endpoint_.erase(this->discovered_endpoint_.begin() + tag_index); } @@ -881,6 +884,9 @@ void PN7150::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi for (auto *trigger : this->triggers_ontag_) { trigger->process(working_endpoint.tag); } + for (auto *listener : this->tag_listeners_) { + listener->tag_on(*working_endpoint.tag); + } working_endpoint.trig_called = true; break; } diff --git a/esphome/components/pn7150/pn7150.h b/esphome/components/pn7150/pn7150.h index 4aad4e1720..54038f5085 100644 --- a/esphome/components/pn7150/pn7150.h +++ b/esphome/components/pn7150/pn7150.h @@ -142,7 +142,7 @@ struct DiscoveredEndpoint { bool trig_called; }; -class PN7150 : public Component { +class PN7150 : public nfc::Nfcc, public Component { public: void setup() override; void dump_config() override; diff --git a/esphome/components/pn7160/__init__.py b/esphome/components/pn7160/__init__.py index c91ca78b03..1639041b9e 100644 --- a/esphome/components/pn7160/__init__.py +++ b/esphome/components/pn7160/__init__.py @@ -36,7 +36,7 @@ CONF_VEN_PIN = "ven_pin" CONF_WKUP_REQ_PIN = "wkup_req_pin" pn7160_ns = cg.esphome_ns.namespace("pn7160") -PN7160 = pn7160_ns.class_("PN7160", cg.Component) +PN7160 = pn7160_ns.class_("PN7160", nfc.Nfcc, cg.Component) EmulationOffAction = pn7160_ns.class_("EmulationOffAction", automation.Action) EmulationOnAction = pn7160_ns.class_("EmulationOnAction", automation.Action) diff --git a/esphome/components/pn7160/pn7160.cpp b/esphome/components/pn7160/pn7160.cpp index ce5374d1d1..a7d3b38fb7 100644 --- a/esphome/components/pn7160/pn7160.cpp +++ b/esphome/components/pn7160/pn7160.cpp @@ -591,6 +591,9 @@ void PN7160::erase_tag_(const uint8_t tag_index) { for (auto *trigger : this->triggers_ontagremoved_) { trigger->process(this->discovered_endpoint_[tag_index].tag); } + for (auto *listener : this->tag_listeners_) { + listener->tag_off(*this->discovered_endpoint_[tag_index].tag); + } ESP_LOGI(TAG, "Tag %s removed", nfc::format_uid(this->discovered_endpoint_[tag_index].tag->get_uid()).c_str()); this->discovered_endpoint_.erase(this->discovered_endpoint_.begin() + tag_index); } @@ -905,6 +908,9 @@ void PN7160::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi for (auto *trigger : this->triggers_ontag_) { trigger->process(working_endpoint.tag); } + for (auto *listener : this->tag_listeners_) { + listener->tag_on(*working_endpoint.tag); + } working_endpoint.trig_called = true; break; } diff --git a/esphome/components/pn7160/pn7160.h b/esphome/components/pn7160/pn7160.h index 2b3cb99453..f2e05ea1d0 100644 --- a/esphome/components/pn7160/pn7160.h +++ b/esphome/components/pn7160/pn7160.h @@ -157,7 +157,7 @@ struct DiscoveredEndpoint { bool trig_called; }; -class PN7160 : public Component { +class PN7160 : public nfc::Nfcc, public Component { public: void setup() override; void dump_config() override; diff --git a/tests/test1.yaml b/tests/test1.yaml index 3ca6faca8a..471e2a71a5 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2028,6 +2028,18 @@ binary_sensor: - platform: dfrobot_sen0395 id: mmwave_detected_uart dfrobot_sen0395_id: mmwave + - platform: nfc + nfcc_id: nfcc_pn7160_i2c + ndef_contains: pulse + name: MFC Tag 1 + - platform: nfc + nfcc_id: nfcc_pn7160_i2c + tag_id: pulse + name: MFC Tag 2 + - platform: nfc + nfcc_id: nfcc_pn7160_i2c + uid: 59-FC-AB-15 + name: MFC Tag 3 pca9685: frequency: 500 @@ -3453,6 +3465,7 @@ pn532_i2c: i2c_id: i2c_bus pn7150_i2c: + id: nfcc_pn7150_i2c i2c_id: i2c_bus irq_pin: allow_other_uses: true From ea03058ace0d46de52c020b10daa39524bb5670c Mon Sep 17 00:00:00 2001 From: Piotr Majkrzak Date: Tue, 16 Jan 2024 09:10:44 +0100 Subject: [PATCH 144/193] Fix RMT timing clock base (#6101) --- esphome/components/esp32_rmt_led_strip/led_strip.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index df6ee2ce2f..3df4077c96 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp +++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp @@ -13,6 +13,8 @@ namespace esp32_rmt_led_strip { static const char *const TAG = "esp32_rmt_led_strip"; +static const uint32_t RMT_CLK_FREQ = 80000000; + static const uint8_t RMT_CLK_DIV = 2; void ESP32RMTLEDStripLightOutput::setup() { @@ -65,7 +67,7 @@ void ESP32RMTLEDStripLightOutput::setup() { void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, uint32_t bit1_low) { - float ratio = (float) APB_CLK_FREQ / RMT_CLK_DIV / 1e09f; + float ratio = (float) RMT_CLK_FREQ / RMT_CLK_DIV / 1e09f; // 0-bit this->bit0_.duration0 = (uint32_t) (ratio * bit0_high); From 21337ffc67096d9256b3dd34428953b47c8283bf Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Jan 2024 21:37:57 +1300 Subject: [PATCH 145/193] Create RingBuffer for VoiceAssistant (#6102) --- .../voice_assistant/voice_assistant.cpp | 32 ++++-------- .../voice_assistant/voice_assistant.h | 4 +- esphome/core/ring_buffer.cpp | 49 +++++++++++++++++++ esphome/core/ring_buffer.h | 34 +++++++++++++ 4 files changed, 95 insertions(+), 24 deletions(-) create mode 100644 esphome/core/ring_buffer.cpp create mode 100644 esphome/core/ring_buffer.h diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 29fc664342..299e624f5f 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -86,14 +86,14 @@ void VoiceAssistant::setup() { #ifdef USE_ESP_ADF this->vad_instance_ = vad_create(VAD_MODE_4); +#endif - this->ring_buffer_ = rb_create(BUFFER_SIZE, sizeof(int16_t)); + this->ring_buffer_ = RingBuffer::create(BUFFER_SIZE * sizeof(int16_t)); if (this->ring_buffer_ == nullptr) { ESP_LOGW(TAG, "Could not allocate ring buffer"); this->mark_failed(); return; } -#endif ExternalRAMAllocator send_allocator(ExternalRAMAllocator::ALLOW_FAILURE); this->send_buffer_ = send_allocator.allocate(SEND_BUFFER_SIZE); @@ -112,14 +112,8 @@ int VoiceAssistant::read_microphone_() { memset(this->input_buffer_, 0, INPUT_BUFFER_SIZE * sizeof(int16_t)); return 0; } -#ifdef USE_ESP_ADF // Write audio into ring buffer - int available = rb_bytes_available(this->ring_buffer_); - if (available < bytes_read) { - rb_read(this->ring_buffer_, nullptr, bytes_read - available, 0); - } - rb_write(this->ring_buffer_, (char *) this->input_buffer_, bytes_read, 0); -#endif + this->ring_buffer_->write((void *) this->input_buffer_, bytes_read); } else { ESP_LOGD(TAG, "microphone not running"); } @@ -141,9 +135,9 @@ void VoiceAssistant::loop() { switch (this->state_) { case State::IDLE: { if (this->continuous_ && this->desired_state_ == State::IDLE) { + this->ring_buffer_->reset(); #ifdef USE_ESP_ADF if (this->use_wake_word_) { - rb_reset(this->ring_buffer_); this->set_state_(State::START_MICROPHONE, State::WAIT_FOR_VAD); } else #endif @@ -236,19 +230,13 @@ void VoiceAssistant::loop() { break; // State changed when udp server port received } case State::STREAMING_MICROPHONE: { - size_t bytes_read = this->read_microphone_(); -#ifdef USE_ESP_ADF - if (rb_bytes_filled(this->ring_buffer_) >= SEND_BUFFER_SIZE) { - rb_read(this->ring_buffer_, (char *) this->send_buffer_, SEND_BUFFER_SIZE, 0); + this->read_microphone_(); + if (this->ring_buffer_->available() >= SEND_BUFFER_SIZE) { + this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0); this->socket_->sendto(this->send_buffer_, SEND_BUFFER_SIZE, 0, (struct sockaddr *) &this->dest_addr_, sizeof(this->dest_addr_)); } -#else - if (bytes_read > 0) { - this->socket_->sendto(this->input_buffer_, bytes_read, 0, (struct sockaddr *) &this->dest_addr_, - sizeof(this->dest_addr_)); - } -#endif + break; } case State::STOP_MICROPHONE: { @@ -473,9 +461,9 @@ void VoiceAssistant::request_start(bool continuous, bool silence_detection) { if (this->state_ == State::IDLE) { this->continuous_ = continuous; this->silence_detection_ = silence_detection; + this->ring_buffer_->reset(); #ifdef USE_ESP_ADF if (this->use_wake_word_) { - rb_reset(this->ring_buffer_); this->set_state_(State::START_MICROPHONE, State::WAIT_FOR_VAD); } else #endif @@ -618,9 +606,9 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { case api::enums::VOICE_ASSISTANT_RUN_END: { ESP_LOGD(TAG, "Assist Pipeline ended"); if (this->state_ == State::STREAMING_MICROPHONE) { + this->ring_buffer_->reset(); #ifdef USE_ESP_ADF if (this->use_wake_word_) { - rb_reset(this->ring_buffer_); // No need to stop the microphone since we didn't use the speaker this->set_state_(State::WAIT_FOR_VAD, State::WAITING_FOR_VAD); } else diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index f9325dff54..d996efe08e 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -7,6 +7,7 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" +#include "esphome/core/ring_buffer.h" #include "esphome/components/api/api_connection.h" #include "esphome/components/api/api_pb2.h" @@ -21,7 +22,6 @@ #ifdef USE_ESP_ADF #include -#include #endif namespace esphome { @@ -177,10 +177,10 @@ class VoiceAssistant : public Component { #ifdef USE_ESP_ADF vad_handle_t vad_instance_; - ringbuf_handle_t ring_buffer_; uint8_t vad_threshold_{5}; uint8_t vad_counter_{0}; #endif + std::unique_ptr ring_buffer_; bool use_wake_word_; uint8_t noise_suppression_level_; diff --git a/esphome/core/ring_buffer.cpp b/esphome/core/ring_buffer.cpp new file mode 100644 index 0000000000..d9c56d84c5 --- /dev/null +++ b/esphome/core/ring_buffer.cpp @@ -0,0 +1,49 @@ +#include "ring_buffer.h" + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#ifdef USE_ESP32 + +#include "helpers.h" + +namespace esphome { + +static const char *const TAG = "ring_buffer"; + +std::unique_ptr RingBuffer::create(size_t len) { + std::unique_ptr rb = make_unique(); + + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + rb->storage_ = allocator.allocate(len); + if (rb->storage_ == nullptr) { + return nullptr; + } + + rb->handle_ = xStreamBufferCreateStatic(len, 0, rb->storage_, &rb->structure_); + return rb; +} + +size_t RingBuffer::read(void *data, size_t size, TickType_t ticks_to_wait) { + return xStreamBufferReceive(this->handle_, data, size, ticks_to_wait); +} + +size_t RingBuffer::write(void *data, size_t len) { + size_t free = this->free(); + if (free < len) { + size_t needed = len - free; + uint8_t discard[needed]; + xStreamBufferReceive(this->handle_, discard, needed, 0); + } + return xStreamBufferSend(this->handle_, data, len, 0); +} + +size_t RingBuffer::available() const { return xStreamBufferBytesAvailable(this->handle_); } + +size_t RingBuffer::free() const { return xStreamBufferSpacesAvailable(this->handle_); } + +BaseType_t RingBuffer::reset() { return xStreamBufferReset(this->handle_); } + +} // namespace esphome + +#endif diff --git a/esphome/core/ring_buffer.h b/esphome/core/ring_buffer.h new file mode 100644 index 0000000000..6c6d04117a --- /dev/null +++ b/esphome/core/ring_buffer.h @@ -0,0 +1,34 @@ +#pragma once + +#ifdef USE_ESP32 + +#include +#include + +#include +#include + +namespace esphome { + +class RingBuffer { + public: + size_t read(void *data, size_t size, TickType_t ticks_to_wait = 0); + + size_t write(void *data, size_t len); + + size_t available() const; + size_t free() const; + + BaseType_t reset(); + + static std::unique_ptr create(size_t len); + + protected: + StreamBufferHandle_t handle_; + StaticStreamBuffer_t structure_; + uint8_t *storage_; +}; + +} // namespace esphome + +#endif From 385420303758e02a2acc9dd883c1561a51eaaf91 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 17 Jan 2024 10:12:31 +1100 Subject: [PATCH 146/193] Socket: Add recvfrom method to receive UDP with source address. (#6103) --- esphome/components/socket/bsd_sockets_impl.cpp | 7 +++++++ esphome/components/socket/socket.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/esphome/components/socket/bsd_sockets_impl.cpp b/esphome/components/socket/bsd_sockets_impl.cpp index 5d44cd7689..6c356106f3 100644 --- a/esphome/components/socket/bsd_sockets_impl.cpp +++ b/esphome/components/socket/bsd_sockets_impl.cpp @@ -86,6 +86,13 @@ class BSDSocketImpl : public Socket { } int listen(int backlog) override { return ::listen(fd_, backlog); } ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); } + ssize_t recvfrom(void *buf, size_t len, sockaddr *addr, socklen_t *addr_len) override { +#if defined(USE_ESP32) + return ::recvfrom(this->fd_, buf, len, 0, addr, addr_len); +#else + return ::lwip_recvfrom(this->fd_, buf, len, 0, addr, addr_len); +#endif + } ssize_t readv(const struct iovec *iov, int iovcnt) override { #if defined(USE_ESP32) return ::lwip_readv(fd_, iov, iovcnt); diff --git a/esphome/components/socket/socket.h b/esphome/components/socket/socket.h index c9b8be88a0..5c12210d15 100644 --- a/esphome/components/socket/socket.h +++ b/esphome/components/socket/socket.h @@ -31,6 +31,9 @@ class Socket { virtual int setsockopt(int level, int optname, const void *optval, socklen_t optlen) = 0; virtual int listen(int backlog) = 0; virtual ssize_t read(void *buf, size_t len) = 0; +#ifdef USE_SOCKET_IMPL_BSD_SOCKETS + virtual ssize_t recvfrom(void *buf, size_t len, sockaddr *addr, socklen_t *addr_len) = 0; +#endif virtual ssize_t readv(const struct iovec *iov, int iovcnt) = 0; virtual ssize_t write(const void *buf, size_t len) = 0; virtual ssize_t writev(const struct iovec *iov, int iovcnt) = 0; From 596943b683d45bf99b474e92f6dbc97e65a3ae3b Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 17 Jan 2024 10:23:36 +1100 Subject: [PATCH 147/193] Inkplate6: Fix crash with initial set of greyscale (#6038) --- esphome/components/inkplate6/inkplate.cpp | 3 +++ esphome/components/inkplate6/inkplate.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/inkplate6/inkplate.cpp b/esphome/components/inkplate6/inkplate.cpp index 92a226de87..f4d0fedf83 100644 --- a/esphome/components/inkplate6/inkplate.cpp +++ b/esphome/components/inkplate6/inkplate.cpp @@ -55,6 +55,9 @@ void Inkplate6::setup() { this->wakeup_pin_->digital_write(false); } +/** + * Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed. + */ void Inkplate6::initialize_() { ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); ExternalRAMAllocator allocator32(ExternalRAMAllocator::ALLOW_FAILURE); diff --git a/esphome/components/inkplate6/inkplate.h b/esphome/components/inkplate6/inkplate.h index 307d9671e6..2946c89e1c 100644 --- a/esphome/components/inkplate6/inkplate.h +++ b/esphome/components/inkplate6/inkplate.h @@ -68,8 +68,9 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice { void set_greyscale(bool greyscale) { this->greyscale_ = greyscale; - this->initialize_(); this->block_partial_ = true; + if (this->is_ready()) + this->initialize_(); } void set_partial_updating(bool partial_updating) { this->partial_updating_ = partial_updating; } void set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } From 0cd232cdf516a04574787843b6af20012f23350a Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 17 Jan 2024 00:50:53 -0600 Subject: [PATCH 148/193] Add support for VEML3235 lux sensor (#5959) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/veml3235/__init__.py | 0 esphome/components/veml3235/sensor.py | 84 +++++++++ esphome/components/veml3235/veml3235.cpp | 230 +++++++++++++++++++++++ esphome/components/veml3235/veml3235.h | 109 +++++++++++ tests/test1.yaml | 10 + 6 files changed, 434 insertions(+) create mode 100644 esphome/components/veml3235/__init__.py create mode 100644 esphome/components/veml3235/sensor.py create mode 100644 esphome/components/veml3235/veml3235.cpp create mode 100644 esphome/components/veml3235/veml3235.h diff --git a/CODEOWNERS b/CODEOWNERS index c497a82eab..fea1e6215c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -362,6 +362,7 @@ esphome/components/ufire_ec/* @pvizeli esphome/components/ufire_ise/* @pvizeli esphome/components/ultrasonic/* @OttoWinter esphome/components/vbus/* @ssieb +esphome/components/veml3235/* @kbx81 esphome/components/version/* @esphome/core esphome/components/voice_assistant/* @jesserockz esphome/components/wake_on_lan/* @willwill2will54 diff --git a/esphome/components/veml3235/__init__.py b/esphome/components/veml3235/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/veml3235/sensor.py b/esphome/components/veml3235/sensor.py new file mode 100644 index 0000000000..79ba510e41 --- /dev/null +++ b/esphome/components/veml3235/sensor.py @@ -0,0 +1,84 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_GAIN, + CONF_INTEGRATION_TIME, + DEVICE_CLASS_ILLUMINANCE, + STATE_CLASS_MEASUREMENT, + UNIT_LUX, +) + +CODEOWNERS = ["@kbx81"] +DEPENDENCIES = ["i2c"] + +CONF_AUTO_GAIN = "auto_gain" +CONF_AUTO_GAIN_THRESHOLD_HIGH = "auto_gain_threshold_high" +CONF_AUTO_GAIN_THRESHOLD_LOW = "auto_gain_threshold_low" +CONF_DIGITAL_GAIN = "digital_gain" + +veml3235_ns = cg.esphome_ns.namespace("veml3235") + +VEML3235Sensor = veml3235_ns.class_( + "VEML3235Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice +) +VEML3235IntegrationTime = veml3235_ns.enum("VEML3235IntegrationTime") +VEML3235_INTEGRATION_TIMES = { + "50ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_50MS, + "100ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_100MS, + "200ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_200MS, + "400ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_400MS, + "800ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_800MS, +} +VEML3235ComponentDigitalGain = veml3235_ns.enum("VEML3235ComponentDigitalGain") +DIGITAL_GAINS = { + "1X": VEML3235ComponentDigitalGain.VEML3235_DIGITAL_GAIN_1X, + "2X": VEML3235ComponentDigitalGain.VEML3235_DIGITAL_GAIN_2X, +} +VEML3235ComponentGain = veml3235_ns.enum("VEML3235ComponentGain") +GAINS = { + "1X": VEML3235ComponentGain.VEML3235_GAIN_1X, + "2X": VEML3235ComponentGain.VEML3235_GAIN_2X, + "4X": VEML3235ComponentGain.VEML3235_GAIN_4X, + "AUTO": VEML3235ComponentGain.VEML3235_GAIN_AUTO, +} + +CONFIG_SCHEMA = ( + sensor.sensor_schema( + VEML3235Sensor, + unit_of_measurement=UNIT_LUX, + accuracy_decimals=1, + device_class=DEVICE_CLASS_ILLUMINANCE, + state_class=STATE_CLASS_MEASUREMENT, + ) + .extend( + { + cv.Optional(CONF_DIGITAL_GAIN, default="1X"): cv.enum( + DIGITAL_GAINS, upper=True + ), + cv.Optional(CONF_AUTO_GAIN, default=True): cv.boolean, + cv.Optional(CONF_AUTO_GAIN_THRESHOLD_HIGH, default="90%"): cv.percentage, + cv.Optional(CONF_AUTO_GAIN_THRESHOLD_LOW, default="20%"): cv.percentage, + cv.Optional(CONF_GAIN, default="1X"): cv.enum(GAINS, upper=True), + cv.Optional(CONF_INTEGRATION_TIME, default="50ms"): cv.All( + cv.positive_time_period_milliseconds, + cv.enum(VEML3235_INTEGRATION_TIMES, lower=True), + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x10)) +) + + +async def to_code(config): + var = await sensor.new_sensor(config) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + cg.add(var.set_auto_gain(config[CONF_AUTO_GAIN])) + cg.add(var.set_auto_gain_threshold_high(config[CONF_AUTO_GAIN_THRESHOLD_HIGH])) + cg.add(var.set_auto_gain_threshold_low(config[CONF_AUTO_GAIN_THRESHOLD_LOW])) + cg.add(var.set_digital_gain(DIGITAL_GAINS[config[CONF_DIGITAL_GAIN]])) + cg.add(var.set_gain(GAINS[config[CONF_GAIN]])) + cg.add(var.set_integration_time(config[CONF_INTEGRATION_TIME])) diff --git a/esphome/components/veml3235/veml3235.cpp b/esphome/components/veml3235/veml3235.cpp new file mode 100644 index 0000000000..2410bfdda2 --- /dev/null +++ b/esphome/components/veml3235/veml3235.cpp @@ -0,0 +1,230 @@ +#include "veml3235.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace veml3235 { + +static const char *const TAG = "veml3235.sensor"; + +void VEML3235Sensor::setup() { + uint8_t device_id[] = {0, 0}; + + ESP_LOGCONFIG(TAG, "Setting up VEML3235 '%s'...", this->name_.c_str()); + + if (!this->refresh_config_reg()) { + ESP_LOGE(TAG, "Unable to write configuration"); + this->mark_failed(); + return; + } + if ((this->write(&ID_REG, 1, false) != i2c::ERROR_OK) || !this->read_bytes_raw(device_id, 2)) { + ESP_LOGE(TAG, "Unable to read ID"); + this->mark_failed(); + return; + } else if (device_id[0] != DEVICE_ID) { + ESP_LOGE(TAG, "Incorrect device ID - expected 0x%.2x, read 0x%.2x", DEVICE_ID, device_id[0]); + this->mark_failed(); + return; + } +} + +bool VEML3235Sensor::refresh_config_reg(bool force_on) { + uint16_t data = this->power_on_ || force_on ? 0 : SHUTDOWN_BITS; + + data |= (uint16_t(this->integration_time_ << CONFIG_REG_IT_BIT)); + data |= (uint16_t(this->digital_gain_ << CONFIG_REG_DG_BIT)); + data |= (uint16_t(this->gain_ << CONFIG_REG_G_BIT)); + data |= 0x1; // mandatory 1 here per RM + + ESP_LOGVV(TAG, "Writing 0x%.4x to register 0x%.2x", data, CONFIG_REG); + return this->write_byte_16(CONFIG_REG, data); +} + +float VEML3235Sensor::read_lx_() { + if (!this->power_on_) { // if off, turn on + if (!this->refresh_config_reg(true)) { + ESP_LOGW(TAG, "Turning on failed"); + this->status_set_warning(); + return NAN; + } + delay(4); // from RM: a wait time of 4 ms should be observed before the first measurement is picked up, to allow + // for a correct start of the signal processor and oscillator + } + + uint8_t als_regs[] = {0, 0}; + if ((this->write(&ALS_REG, 1, false) != i2c::ERROR_OK) || !this->read_bytes_raw(als_regs, 2)) { + this->status_set_warning(); + return NAN; + } + + this->status_clear_warning(); + + float als_raw_value_multiplier = LUX_MULTIPLIER_BASE; + uint16_t als_raw_value = encode_uint16(als_regs[1], als_regs[0]); + // determine multiplier value based on gains and integration time + if (this->digital_gain_ == VEML3235_DIGITAL_GAIN_1X) { + als_raw_value_multiplier *= 2; + } + switch (this->gain_) { + case VEML3235_GAIN_1X: + als_raw_value_multiplier *= 4; + break; + case VEML3235_GAIN_2X: + als_raw_value_multiplier *= 2; + break; + default: + break; + } + switch (this->integration_time_) { + case VEML3235_INTEGRATION_TIME_50MS: + als_raw_value_multiplier *= 16; + break; + case VEML3235_INTEGRATION_TIME_100MS: + als_raw_value_multiplier *= 8; + break; + case VEML3235_INTEGRATION_TIME_200MS: + als_raw_value_multiplier *= 4; + break; + case VEML3235_INTEGRATION_TIME_400MS: + als_raw_value_multiplier *= 2; + break; + default: + break; + } + // finally, determine and return the actual lux value + float lx = float(als_raw_value) * als_raw_value_multiplier; + ESP_LOGVV(TAG, "'%s': ALS raw = %u, multiplier = %.5f", this->get_name().c_str(), als_raw_value, + als_raw_value_multiplier); + ESP_LOGD(TAG, "'%s': Illuminance = %.4flx", this->get_name().c_str(), lx); + + if (!this->power_on_) { // turn off if required + if (!this->refresh_config_reg()) { + ESP_LOGW(TAG, "Turning off failed"); + this->status_set_warning(); + } + } + + if (this->auto_gain_) { + this->adjust_gain_(als_raw_value); + } + + return lx; +} + +void VEML3235Sensor::adjust_gain_(const uint16_t als_raw_value) { + if ((als_raw_value > UINT16_MAX * this->auto_gain_threshold_low_) && + (als_raw_value < UINT16_MAX * this->auto_gain_threshold_high_)) { + return; + } + + if (als_raw_value >= UINT16_MAX * 0.9) { // over-saturated, reset all gains and start over + this->digital_gain_ = VEML3235_DIGITAL_GAIN_1X; + this->gain_ = VEML3235_GAIN_1X; + this->integration_time_ = VEML3235_INTEGRATION_TIME_50MS; + this->refresh_config_reg(); + return; + } + + if (this->gain_ != VEML3235_GAIN_4X) { // increase gain if possible + switch (this->gain_) { + case VEML3235_GAIN_1X: + this->gain_ = VEML3235_GAIN_2X; + break; + case VEML3235_GAIN_2X: + this->gain_ = VEML3235_GAIN_4X; + break; + default: + break; + } + this->refresh_config_reg(); + return; + } + // gain is maxed out; reset it and try to increase digital gain + if (this->digital_gain_ != VEML3235_DIGITAL_GAIN_2X) { // increase digital gain if possible + this->digital_gain_ = VEML3235_DIGITAL_GAIN_2X; + this->gain_ = VEML3235_GAIN_1X; + this->refresh_config_reg(); + return; + } + // digital gain is maxed out; reset it and try to increase integration time + if (this->integration_time_ != VEML3235_INTEGRATION_TIME_800MS) { // increase integration time if possible + switch (this->integration_time_) { + case VEML3235_INTEGRATION_TIME_50MS: + this->integration_time_ = VEML3235_INTEGRATION_TIME_100MS; + break; + case VEML3235_INTEGRATION_TIME_100MS: + this->integration_time_ = VEML3235_INTEGRATION_TIME_200MS; + break; + case VEML3235_INTEGRATION_TIME_200MS: + this->integration_time_ = VEML3235_INTEGRATION_TIME_400MS; + break; + case VEML3235_INTEGRATION_TIME_400MS: + this->integration_time_ = VEML3235_INTEGRATION_TIME_800MS; + break; + default: + break; + } + this->digital_gain_ = VEML3235_DIGITAL_GAIN_1X; + this->gain_ = VEML3235_GAIN_1X; + this->refresh_config_reg(); + return; + } +} + +void VEML3235Sensor::dump_config() { + uint8_t digital_gain = 1; + uint8_t gain = 1; + uint16_t integration_time = 0; + + if (this->digital_gain_ == VEML3235_DIGITAL_GAIN_2X) { + digital_gain = 2; + } + switch (this->gain_) { + case VEML3235_GAIN_2X: + gain = 2; + break; + case VEML3235_GAIN_4X: + gain = 4; + break; + default: + break; + } + switch (this->integration_time_) { + case VEML3235_INTEGRATION_TIME_50MS: + integration_time = 50; + break; + case VEML3235_INTEGRATION_TIME_100MS: + integration_time = 100; + break; + case VEML3235_INTEGRATION_TIME_200MS: + integration_time = 200; + break; + case VEML3235_INTEGRATION_TIME_400MS: + integration_time = 400; + break; + case VEML3235_INTEGRATION_TIME_800MS: + integration_time = 800; + break; + default: + break; + } + + LOG_SENSOR("", "VEML3235", this); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication failed"); + } + LOG_UPDATE_INTERVAL(this); + ESP_LOGCONFIG(TAG, " Auto-gain enabled: %s", YESNO(this->auto_gain_)); + if (this->auto_gain_) { + ESP_LOGCONFIG(TAG, " Auto-gain upper threshold: %f%%", this->auto_gain_threshold_high_ * 100.0); + ESP_LOGCONFIG(TAG, " Auto-gain lower threshold: %f%%", this->auto_gain_threshold_low_ * 100.0); + ESP_LOGCONFIG(TAG, " Values below will be used as initial values only"); + } + ESP_LOGCONFIG(TAG, " Digital gain: %uX", digital_gain); + ESP_LOGCONFIG(TAG, " Gain: %uX", gain); + ESP_LOGCONFIG(TAG, " Integration time: %ums", integration_time); +} + +} // namespace veml3235 +} // namespace esphome diff --git a/esphome/components/veml3235/veml3235.h b/esphome/components/veml3235/veml3235.h new file mode 100644 index 0000000000..2b0d6b23ea --- /dev/null +++ b/esphome/components/veml3235/veml3235.h @@ -0,0 +1,109 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace veml3235 { + +// Register IDs/locations +// +static const uint8_t CONFIG_REG = 0x00; +static const uint8_t W_REG = 0x04; +static const uint8_t ALS_REG = 0x05; +static const uint8_t ID_REG = 0x09; + +// Bit offsets within CONFIG_REG +// +static const uint8_t CONFIG_REG_IT_BIT = 12; +static const uint8_t CONFIG_REG_DG_BIT = 5; +static const uint8_t CONFIG_REG_G_BIT = 3; + +// Other important constants +// +static const uint8_t DEVICE_ID = 0x35; +static const uint16_t SHUTDOWN_BITS = 0x0018; + +// Base multiplier value for lux computation +// +static const float LUX_MULTIPLIER_BASE = 0.00213; + +// Enum for conversion/integration time settings for the VEML3235. +// +// Specific values of the enum constants are register values taken from the VEML3235 datasheet. +// Longer times mean more accurate results, but will take more energy/more time. +// +enum VEML3235ComponentIntegrationTime { + VEML3235_INTEGRATION_TIME_50MS = 0b000, + VEML3235_INTEGRATION_TIME_100MS = 0b001, + VEML3235_INTEGRATION_TIME_200MS = 0b010, + VEML3235_INTEGRATION_TIME_400MS = 0b011, + VEML3235_INTEGRATION_TIME_800MS = 0b100, +}; + +// Enum for digital gain settings for the VEML3235. +// Higher values are better for low light situations, but can increase noise. +// +enum VEML3235ComponentDigitalGain { + VEML3235_DIGITAL_GAIN_1X = 0b0, + VEML3235_DIGITAL_GAIN_2X = 0b1, +}; + +// Enum for gain settings for the VEML3235. +// Higher values are better for low light situations, but can increase noise. +// +enum VEML3235ComponentGain { + VEML3235_GAIN_1X = 0b00, + VEML3235_GAIN_2X = 0b01, + VEML3235_GAIN_4X = 0b11, +}; + +class VEML3235Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + void update() override { this->publish_state(this->read_lx_()); } + float get_setup_priority() const override { return setup_priority::DATA; } + + // Used by ESPHome framework. Does NOT actually set the value on the device. + void set_auto_gain(bool auto_gain) { this->auto_gain_ = auto_gain; } + void set_auto_gain_threshold_high(float auto_gain_threshold_high) { + this->auto_gain_threshold_high_ = auto_gain_threshold_high; + } + void set_auto_gain_threshold_low(float auto_gain_threshold_low) { + this->auto_gain_threshold_low_ = auto_gain_threshold_low; + } + void set_power_on(bool power_on) { this->power_on_ = power_on; } + void set_digital_gain(VEML3235ComponentDigitalGain digital_gain) { this->digital_gain_ = digital_gain; } + void set_gain(VEML3235ComponentGain gain) { this->gain_ = gain; } + void set_integration_time(VEML3235ComponentIntegrationTime integration_time) { + this->integration_time_ = integration_time; + } + + bool auto_gain() { return this->auto_gain_; } + float auto_gain_threshold_high() { return this->auto_gain_threshold_high_; } + float auto_gain_threshold_low() { return this->auto_gain_threshold_low_; } + VEML3235ComponentDigitalGain digital_gain() { return this->digital_gain_; } + VEML3235ComponentGain gain() { return this->gain_; } + VEML3235ComponentIntegrationTime integration_time() { return this->integration_time_; } + + // Updates the configuration register on the device + bool refresh_config_reg(bool force_on = false); + + protected: + float read_lx_(); + void adjust_gain_(uint16_t als_raw_value); + + bool auto_gain_{true}; + bool power_on_{true}; + float auto_gain_threshold_high_{0.9}; + float auto_gain_threshold_low_{0.2}; + VEML3235ComponentDigitalGain digital_gain_{VEML3235_DIGITAL_GAIN_1X}; + VEML3235ComponentGain gain_{VEML3235_GAIN_1X}; + VEML3235ComponentIntegrationTime integration_time_{VEML3235_INTEGRATION_TIME_50MS}; +}; + +} // namespace veml3235 +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index 471e2a71a5..afd199d264 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1370,6 +1370,16 @@ sensor: name: tsl2591 calculated_lux id: tsl2591_cl i2c_id: i2c_bus + - platform: veml3235 + id: veml3235_sensor + name: VEML3235 Light Sensor + i2c_id: i2c_bus + auto_gain: true + auto_gain_threshold_high: 90% + auto_gain_threshold_low: 15% + digital_gain: 1X + gain: 1X + integration_time: 50ms - platform: tee501 name: Office Temperature 3 address: 0x48 From b606e976e1e90eaaa2637f3eb95592a3a888c04a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Jan 2024 20:28:46 +1300 Subject: [PATCH 149/193] CV: tidy up Schema wrapper (#6105) --- esphome/config_validation.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 8f2e080b46..fa1170fb93 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -2004,15 +2004,19 @@ def suppress_invalid(): pass -GIT_SCHEMA = { - Required(CONF_URL): url, - Optional(CONF_REF): git_ref, - Optional(CONF_USERNAME): string, - Optional(CONF_PASSWORD): string, -} -LOCAL_SCHEMA = { - Required(CONF_PATH): directory, -} +GIT_SCHEMA = Schema( + { + Required(CONF_URL): url, + Optional(CONF_REF): git_ref, + Optional(CONF_USERNAME): string, + Optional(CONF_PASSWORD): string, + } +) +LOCAL_SCHEMA = Schema( + { + Required(CONF_PATH): directory, + } +) def validate_source_shorthand(value): @@ -2053,8 +2057,8 @@ SOURCE_SCHEMA = Any( validate_source_shorthand, typed_schema( { - TYPE_GIT: Schema(GIT_SCHEMA), - TYPE_LOCAL: Schema(LOCAL_SCHEMA), + TYPE_GIT: GIT_SCHEMA, + TYPE_LOCAL: LOCAL_SCHEMA, } ), ) From e731a2ffaa62848e43c4723680225392f5217e66 Mon Sep 17 00:00:00 2001 From: h2zero <32826625+h2zero@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:26:56 -0700 Subject: [PATCH 150/193] Add support X.509 client certificates for MQTT. (#5778) Co-authored-by: h2zero Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/mqtt/__init__.py | 11 +++++++++++ esphome/components/mqtt/mqtt_backend_esp32.cpp | 10 ++++++++++ esphome/components/mqtt/mqtt_backend_esp32.h | 4 ++++ esphome/components/mqtt/mqtt_client.h | 2 ++ esphome/const.py | 2 ++ 5 files changed, 29 insertions(+) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index baf32097fa..02184c8a39 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -10,6 +10,8 @@ from esphome.const import ( CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CERTIFICATE_AUTHORITY, + CONF_CLIENT_CERTIFICATE, + CONF_CLIENT_CERTIFICATE_KEY, CONF_CLIENT_ID, CONF_COMMAND_TOPIC, CONF_COMMAND_RETAIN, @@ -199,6 +201,12 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_CERTIFICATE_AUTHORITY): cv.All( cv.string, cv.only_with_esp_idf ), + cv.Inclusive(CONF_CLIENT_CERTIFICATE, "cert-key-pair"): cv.All( + cv.string, cv.only_on_esp32 + ), + cv.Inclusive(CONF_CLIENT_CERTIFICATE_KEY, "cert-key-pair"): cv.All( + cv.string, cv.only_on_esp32 + ), cv.SplitDefault(CONF_SKIP_CERT_CN_CHECK, esp32_idf=False): cv.All( cv.boolean, cv.only_with_esp_idf ), @@ -378,6 +386,9 @@ async def to_code(config): if CONF_CERTIFICATE_AUTHORITY in config: cg.add(var.set_ca_certificate(config[CONF_CERTIFICATE_AUTHORITY])) cg.add(var.set_skip_cert_cn_check(config[CONF_SKIP_CERT_CN_CHECK])) + if CONF_CLIENT_CERTIFICATE in config: + cg.add(var.set_cl_certificate(config[CONF_CLIENT_CERTIFICATE])) + cg.add(var.set_cl_key(config[CONF_CLIENT_CERTIFICATE_KEY])) # prevent error -0x428e # See https://github.com/espressif/esp-idf/issues/139 diff --git a/esphome/components/mqtt/mqtt_backend_esp32.cpp b/esphome/components/mqtt/mqtt_backend_esp32.cpp index 2d4e6802f2..9c2e487ae7 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.cpp +++ b/esphome/components/mqtt/mqtt_backend_esp32.cpp @@ -45,6 +45,11 @@ bool MQTTBackendESP32::initialize_() { mqtt_cfg_.cert_pem = ca_certificate_.value().c_str(); mqtt_cfg_.skip_cert_common_name_check = skip_cert_cn_check_; mqtt_cfg_.transport = MQTT_TRANSPORT_OVER_SSL; + + if (this->cl_certificate_.has_value() && this->cl_key_.has_value()) { + mqtt_cfg_.client_cert_pem = this->cl_certificate_.value().c_str(); + mqtt_cfg_.client_key_pem = this->cl_key_.value().c_str(); + } } else { mqtt_cfg_.transport = MQTT_TRANSPORT_OVER_TCP; } @@ -79,6 +84,11 @@ bool MQTTBackendESP32::initialize_() { mqtt_cfg_.broker.verification.certificate = ca_certificate_.value().c_str(); mqtt_cfg_.broker.verification.skip_cert_common_name_check = skip_cert_cn_check_; mqtt_cfg_.broker.address.transport = MQTT_TRANSPORT_OVER_SSL; + + if (this->cl_certificate_.has_value() && this->cl_key_.has_value()) { + mqtt_cfg_.credentials.authentication.certificate = this->cl_certificate_.value().c_str(); + mqtt_cfg_.credentials.authentication.key = this->cl_key_.value().c_str(); + } } else { mqtt_cfg_.broker.address.transport = MQTT_TRANSPORT_OVER_TCP; } diff --git a/esphome/components/mqtt/mqtt_backend_esp32.h b/esphome/components/mqtt/mqtt_backend_esp32.h index a4ee96ca59..b1f672da10 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.h +++ b/esphome/components/mqtt/mqtt_backend_esp32.h @@ -124,6 +124,8 @@ class MQTTBackendESP32 final : public MQTTBackend { void loop() final; void set_ca_certificate(const std::string &cert) { ca_certificate_ = cert; } + void set_cl_certificate(const std::string &cert) { cl_certificate_ = cert; } + void set_cl_key(const std::string &key) { cl_key_ = key; } void set_skip_cert_cn_check(bool skip_check) { skip_cert_cn_check_ = skip_check; } protected: @@ -154,6 +156,8 @@ class MQTTBackendESP32 final : public MQTTBackend { uint16_t keep_alive_; bool clean_session_; optional ca_certificate_; + optional cl_certificate_; + optional cl_key_; bool skip_cert_cn_check_{false}; // callbacks diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index bcb44ab4c2..454316aa87 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -146,6 +146,8 @@ class MQTTClientComponent : public Component { #endif #ifdef USE_ESP32 void set_ca_certificate(const char *cert) { this->mqtt_backend_.set_ca_certificate(cert); } + void set_cl_certificate(const char *cert) { this->mqtt_backend_.set_cl_certificate(cert); } + void set_cl_key(const char *key) { this->mqtt_backend_.set_cl_key(key); } void set_skip_cert_cn_check(bool skip_check) { this->mqtt_backend_.set_skip_cert_cn_check(skip_check); } #endif const Availability &get_availability(); diff --git a/esphome/const.py b/esphome/const.py index c35adc74ee..a95d1d5ac3 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -112,6 +112,8 @@ CONF_CHANNELS = "channels" CONF_CHARACTERISTIC_UUID = "characteristic_uuid" CONF_CHIPSET = "chipset" CONF_CLEAR_IMPEDANCE = "clear_impedance" +CONF_CLIENT_CERTIFICATE = "client_certificate" +CONF_CLIENT_CERTIFICATE_KEY = "client_certificate_key" CONF_CLIENT_ID = "client_id" CONF_CLK_PIN = "clk_pin" CONF_CLOCK_PIN = "clock_pin" From 39bd829236a5dca57f3fc21caf8847b4af89a9c8 Mon Sep 17 00:00:00 2001 From: mathieu-mp Date: Thu, 18 Jan 2024 00:40:30 +0100 Subject: [PATCH 151/193] Fix color observation for triangle outline in display component (#6107) --- esphome/components/display/display.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index 0c3631342e..e531c5cf5c 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -142,9 +142,9 @@ void Display::filled_circle(int center_x, int center_y, int radius, Color color) } while (dx <= 0); } void HOT Display::triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color) { - this->line(x1, y1, x2, y2); - this->line(x1, y1, x3, y3); - this->line(x2, y2, x3, y3); + this->line(x1, y1, x2, y2, color); + this->line(x1, y1, x3, y3, color); + this->line(x2, y2, x3, y3, color); } void Display::sort_triangle_points_by_y_(int *x1, int *y1, int *x2, int *y2, int *x3, int *y3) { if (*y1 > *y2) { From c9c8d397784ac9f47d64cb15ed46ff9d82ad21f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Leforestier?= Date: Thu, 18 Jan 2024 01:56:56 +0100 Subject: [PATCH 152/193] Add support of Honeywell HumidIcon (I2C HIH series) Temperature & Humidity sensor (#5730) --- CODEOWNERS | 1 + .../components/honeywell_hih_i2c/__init__.py | 2 + .../honeywell_hih_i2c/honeywell_hih.cpp | 97 +++++++++++++++++++ .../honeywell_hih_i2c/honeywell_hih.h | 34 +++++++ .../components/honeywell_hih_i2c/sensor.py | 56 +++++++++++ tests/test1.yaml | 7 ++ 6 files changed, 197 insertions(+) create mode 100644 esphome/components/honeywell_hih_i2c/__init__.py create mode 100644 esphome/components/honeywell_hih_i2c/honeywell_hih.cpp create mode 100644 esphome/components/honeywell_hih_i2c/honeywell_hih.h create mode 100644 esphome/components/honeywell_hih_i2c/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index fea1e6215c..7e87679ad8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -137,6 +137,7 @@ esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/hm3301/* @freekode esphome/components/homeassistant/* @OttoWinter +esphome/components/honeywell_hih_i2c/* @Benichou34 esphome/components/honeywellabp/* @RubyBailey esphome/components/honeywellabp2_i2c/* @jpfaff esphome/components/host/* @esphome/core diff --git a/esphome/components/honeywell_hih_i2c/__init__.py b/esphome/components/honeywell_hih_i2c/__init__.py new file mode 100644 index 0000000000..fbf67230f7 --- /dev/null +++ b/esphome/components/honeywell_hih_i2c/__init__.py @@ -0,0 +1,2 @@ +"""Support for Honeywell HumidIcon HIH""" +CODEOWNERS = ["@Benichou34"] diff --git a/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp b/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp new file mode 100644 index 0000000000..64d5ddb541 --- /dev/null +++ b/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp @@ -0,0 +1,97 @@ +// Honeywell HumidIcon I2C Sensors +// https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/humidity-with-temperature-sensors/common/documents/sps-siot-i2c-comms-humidicon-tn-009061-2-en-ciid-142171.pdf +// + +#include "honeywell_hih.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace honeywell_hih_i2c { + +static const char *const TAG = "honeywell_hih.i2c"; + +static const uint8_t REQUEST_CMD[1] = {0x00}; // Measurement Request Format +static const uint16_t MAX_COUNT = 0x3FFE; // 2^14 - 2 + +void HoneywellHIComponent::read_sensor_data_() { + uint8_t data[4]; + + if (this->read(data, sizeof(data)) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "Communication with Honeywell HIH failed!"); + this->mark_failed(); + return; + } + + const uint16_t raw_humidity = (static_cast(data[0] & 0x3F) << 8) | data[1]; + float humidity = (static_cast(raw_humidity) / MAX_COUNT) * 100; + + const uint16_t raw_temperature = (static_cast(data[2]) << 6) | (data[3] >> 2); + float temperature = (static_cast(raw_temperature) / MAX_COUNT) * 165 - 40; + + ESP_LOGD(TAG, "Got temperature=%.2f°C humidity=%.2f%%", temperature, humidity); + if (this->temperature_sensor_ != nullptr) + this->temperature_sensor_->publish_state(temperature); + if (this->humidity_sensor_ != nullptr) + this->humidity_sensor_->publish_state(humidity); +} + +void HoneywellHIComponent::start_measurement_() { + if (this->write(REQUEST_CMD, sizeof(REQUEST_CMD)) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "Communication with Honeywell HIH failed!"); + this->mark_failed(); + return; + } + + this->measurement_running_ = true; +} + +bool HoneywellHIComponent::is_measurement_ready_() { + uint8_t data[1]; + + if (this->read(data, sizeof(data)) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "Communication with Honeywell HIH failed!"); + this->mark_failed(); + return false; + } + + // Check status bits + return ((data[0] & 0xC0) == 0x00); +} + +void HoneywellHIComponent::measurement_timeout_() { + ESP_LOGE(TAG, "Honeywell HIH Timeout!"); + this->measurement_running_ = false; + this->mark_failed(); +} + +void HoneywellHIComponent::update() { + ESP_LOGV(TAG, "Update Honeywell HIH Sensor"); + + this->start_measurement_(); + // The measurement cycle duration is typically 36.65 ms for temperature and humidity readings. + this->set_timeout("meas_timeout", 100, [this] { this->measurement_timeout_(); }); +} + +void HoneywellHIComponent::loop() { + if (this->measurement_running_ && this->is_measurement_ready_()) { + this->measurement_running_ = false; + this->cancel_timeout("meas_timeout"); + this->read_sensor_data_(); + } +} + +void HoneywellHIComponent::dump_config() { + ESP_LOGD(TAG, "Honeywell HIH:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with Honeywell HIH failed!"); + } + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); + LOG_UPDATE_INTERVAL(this); +} + +float HoneywellHIComponent::get_setup_priority() const { return setup_priority::DATA; } + +} // namespace honeywell_hih_i2c +} // namespace esphome diff --git a/esphome/components/honeywell_hih_i2c/honeywell_hih.h b/esphome/components/honeywell_hih_i2c/honeywell_hih.h new file mode 100644 index 0000000000..4457eab1da --- /dev/null +++ b/esphome/components/honeywell_hih_i2c/honeywell_hih.h @@ -0,0 +1,34 @@ +// Honeywell HumidIcon I2C Sensors +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace honeywell_hih_i2c { + +class HoneywellHIComponent : public PollingComponent, public i2c::I2CDevice { + public: + void dump_config() override; + float get_setup_priority() const override; + void loop() override; + void update() override; + + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; } + void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; } + + protected: + bool measurement_running_{false}; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; + + private: + void read_sensor_data_(); + void start_measurement_(); + bool is_measurement_ready_(); + void measurement_timeout_(); +}; + +} // namespace honeywell_hih_i2c +} // namespace esphome diff --git a/esphome/components/honeywell_hih_i2c/sensor.py b/esphome/components/honeywell_hih_i2c/sensor.py new file mode 100644 index 0000000000..f5a6ad2398 --- /dev/null +++ b/esphome/components/honeywell_hih_i2c/sensor.py @@ -0,0 +1,56 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_PERCENT, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, +) + +DEPENDENCIES = ["i2c"] + +honeywell_hih_ns = cg.esphome_ns.namespace("honeywell_hih_i2c") +HONEYWELLHIComponent = honeywell_hih_ns.class_( + "HoneywellHIComponent", cg.PollingComponent, i2c.I2CDevice +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HONEYWELLHIComponent), + 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_HUMIDITY): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=1, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x27)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + if temperature_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temperature_config) + cg.add(var.set_temperature_sensor(sens)) + + if humidity_config := config.get(CONF_HUMIDITY): + sens = await sensor.new_sensor(humidity_config) + cg.add(var.set_humidity_sensor(sens)) diff --git a/tests/test1.yaml b/tests/test1.yaml index afd199d264..038ac9c738 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -863,6 +863,13 @@ sensor: oversampling: 8x update_interval: 15s i2c_id: i2c_bus + - platform: honeywell_hih_i2c + temperature: + name: Living Room Temperature 7 + humidity: + name: Living Room Humidity 7 + update_interval: 15s + i2c_id: i2c_bus - platform: honeywellabp pressure: name: Honeywell pressure From c6f528583b2b527a4b9a91533d7aacf838c0a984 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 18 Jan 2024 08:13:40 +0100 Subject: [PATCH 153/193] Proposal: Test yaml for each component (#5398) * Test for each component. * When possible use commandline substitution. * Add wildcard support. * end file with new line. * Move component tests into subfolder. * Add component test to pipeline. * Remove trailing whitespace. * add restore python step. * Add `. venv/bin/activate` to pipeline. * step `changed-components` needs `common` step. * start `list-components-changed.py` different. * iterate on pipeline stage `list-components`. * Update `checkout` action. * Rename test folder from `tests` to `_test`. * validate file exists. * Move component test folder. * extend list-components to include child components. * File does not end with a newline * Handle empty list-components matrix. * list-components also check for changes in tests folder. * Improve `list-components.py`. * `*` is a forbidden character for filenames on windows. --------- Co-authored-by: Your Name Co-authored-by: Keith Burzinski --- .github/workflows/ci.yml | 57 +++++++ script/fulltest | 1 + script/list-components.py | 153 ++++++++++++++++++ script/test_build_components | 85 ++++++++++ tests/components/adc/test.esp32-c3.yaml | 5 + tests/components/adc/test.esp32-idf.yaml | 11 ++ tests/components/adc/test.esp32-s2.yaml | 5 + tests/components/adc/test.esp32-s3.yaml | 5 + tests/components/adc/test.esp32.yaml | 11 ++ tests/components/adc/test.esp8266.yaml | 4 + tests/components/adc/test.rp2040.yaml | 4 + .../mopeka_std_check/test.esp32.yaml | 16 ++ tests/components/template/test.all.yaml | 127 +++++++++++++++ .../build_components_base.esp32-ard.yaml | 20 +++ .../build_components_base.esp32-c3-ard.yaml | 20 +++ .../build_components_base.esp32-c3-idf.yaml | 20 +++ .../build_components_base.esp32-idf.yaml | 20 +++ .../build_components_base.esp32-s2-ard.yaml | 21 +++ .../build_components_base.esp32-s2-idf.yaml | 21 +++ .../build_components_base.esp32-s3-ard.yaml | 21 +++ .../build_components_base.esp32-s3-idf.yaml | 21 +++ .../build_components_base.esp8266.yaml | 18 +++ .../build_components_base.rp2040.yaml | 21 +++ 23 files changed, 687 insertions(+) create mode 100755 script/list-components.py create mode 100755 script/test_build_components create mode 100644 tests/components/adc/test.esp32-c3.yaml create mode 100644 tests/components/adc/test.esp32-idf.yaml create mode 100644 tests/components/adc/test.esp32-s2.yaml create mode 100644 tests/components/adc/test.esp32-s3.yaml create mode 100644 tests/components/adc/test.esp32.yaml create mode 100644 tests/components/adc/test.esp8266.yaml create mode 100644 tests/components/adc/test.rp2040.yaml create mode 100644 tests/components/mopeka_std_check/test.esp32.yaml create mode 100644 tests/components/template/test.all.yaml create mode 100644 tests/test_build_components/build_components_base.esp32-ard.yaml create mode 100644 tests/test_build_components/build_components_base.esp32-c3-ard.yaml create mode 100644 tests/test_build_components/build_components_base.esp32-c3-idf.yaml create mode 100644 tests/test_build_components/build_components_base.esp32-idf.yaml create mode 100644 tests/test_build_components/build_components_base.esp32-s2-ard.yaml create mode 100644 tests/test_build_components/build_components_base.esp32-s2-idf.yaml create mode 100644 tests/test_build_components/build_components_base.esp32-s3-ard.yaml create mode 100644 tests/test_build_components/build_components_base.esp32-s3-idf.yaml create mode 100644 tests/test_build_components/build_components_base.esp8266.yaml create mode 100644 tests/test_build_components/build_components_base.rp2040.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ddc49b504..2187573709 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -392,6 +392,62 @@ jobs: # yamllint disable-line rule:line-length if: always() + list-components: + runs-on: ubuntu-latest + needs: + - common + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Check out code from GitHub + uses: actions/checkout@v4.1.1 + with: + # Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works. + fetch-depth: 500 + - name: Fetch dev 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 + - name: Restore Python + uses: ./.github/actions/restore-python + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache-key: ${{ needs.common.outputs.cache-key }} + - name: Find changed components + id: set-matrix + run: | + . venv/bin/activate + echo "matrix=$(script/list-components.py --changed | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT + + test-build-components: + name: Component test ${{ matrix.file }} + runs-on: ubuntu-latest + needs: + - common + - list-components + if: ${{ needs.list-components.outputs.matrix != '[]' && needs.list-components.outputs.matrix != '' }} + strategy: + fail-fast: false + max-parallel: 2 + matrix: + file: ${{ fromJson(needs.list-components.outputs.matrix) }} + steps: + - name: Check out code from GitHub + uses: actions/checkout@v4.1.1 + - name: Restore Python + uses: ./.github/actions/restore-python + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache-key: ${{ needs.common.outputs.cache-key }} + - name: test_build_components -e config -c ${{ matrix.file }} + run: | + . venv/bin/activate + ./script/test_build_components -e config -c ${{ matrix.file }} + - name: test_build_components -e compile -c ${{ matrix.file }} + run: | + . venv/bin/activate + ./script/test_build_components -e compile -c ${{ matrix.file }} + ci-status: name: CI Status runs-on: ubuntu-latest @@ -406,6 +462,7 @@ jobs: - pyupgrade - compile-tests - clang-tidy + - test-build-components if: always() steps: - name: Success diff --git a/script/fulltest b/script/fulltest index a605beebfe..6440401e97 100755 --- a/script/fulltest +++ b/script/fulltest @@ -12,3 +12,4 @@ script/lint-cpp script/unit_test script/component_test script/test +script/test_build_components diff --git a/script/list-components.py b/script/list-components.py new file mode 100755 index 0000000000..3e55c0e5f7 --- /dev/null +++ b/script/list-components.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +from pathlib import Path +import sys +import argparse + +from helpers import git_ls_files, changed_files +from esphome.loader import get_component, get_platform +from esphome.core import CORE +from esphome.const import ( + KEY_CORE, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, + PLATFORM_ESP32, + PLATFORM_ESP8266, +) + + +def filter_component_files(str): + return str.startswith("esphome/components/") | str.startswith("tests/components/") + + +def extract_component_names_array_from_files_array(files): + components = [] + for file in files: + file_parts = file.split("/") + if len(file_parts) >= 4: + component_name = file_parts[2] + if component_name not in components: + components.append(component_name) + return components + + +def add_item_to_components_graph(components_graph, parent, child): + if not parent.startswith("__") and parent != child: + if parent not in components_graph: + components_graph[parent] = [] + if child not in components_graph[parent]: + components_graph[parent].append(child) + + +def create_components_graph(): + # The root directory of the repo + root = Path(__file__).parent.parent + components_dir = root / "esphome" / "components" + # Fake some directory so that get_component works + CORE.config_path = str(root) + # Various configuration to capture different outcomes used by `AUTO_LOAD` function. + TARGET_CONFIGURATIONS = [ + {KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: None}, + {KEY_TARGET_FRAMEWORK: "arduino", KEY_TARGET_PLATFORM: None}, + {KEY_TARGET_FRAMEWORK: "esp-idf", KEY_TARGET_PLATFORM: None}, + {KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: PLATFORM_ESP32}, + ] + CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] + + components_graph = {} + + for path in components_dir.iterdir(): + if not path.is_dir(): + continue + if not (path / "__init__.py").is_file(): + continue + name = path.name + comp = get_component(name) + if comp is None: + print( + f"Cannot find component {name}. Make sure current path is pip installed ESPHome" + ) + sys.exit(1) + + for dependency in comp.dependencies: + add_item_to_components_graph(components_graph, dependency, name) + + for target_config in TARGET_CONFIGURATIONS: + CORE.data[KEY_CORE] = target_config + for auto_load in comp.auto_load: + add_item_to_components_graph(components_graph, auto_load, name) + # restore config + CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] + + for platform_path in path.iterdir(): + platform_name = platform_path.stem + platform = get_platform(platform_name, name) + if platform is None: + continue + + add_item_to_components_graph(components_graph, platform_name, name) + + for dependency in platform.dependencies: + add_item_to_components_graph(components_graph, dependency, name) + + for target_config in TARGET_CONFIGURATIONS: + CORE.data[KEY_CORE] = target_config + for auto_load in platform.auto_load: + add_item_to_components_graph(components_graph, auto_load, name) + # restore config + CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] + + return components_graph + + +def find_children_of_component(components_graph, component_name, depth=0): + if component_name not in components_graph: + return [] + + children = [] + + for child in components_graph[component_name]: + children.append(child) + if depth < 10: + children.extend( + find_children_of_component(components_graph, child, depth + 1) + ) + # Remove duplicate values + return list(set(children)) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "-c", "--changed", action="store_true", help="Only run on changed files" + ) + args = parser.parse_args() + + files = git_ls_files() + files = filter(filter_component_files, files) + + if args.changed: + changed = changed_files() + files = [f for f in files if f in changed] + + components = extract_component_names_array_from_files_array(files) + + if args.changed: + components_graph = create_components_graph() + + all_changed_components = components.copy() + for c in components: + all_changed_components.extend( + find_children_of_component(components_graph, c) + ) + # Remove duplicate values + all_changed_components = list(set(all_changed_components)) + + for c in sorted(all_changed_components): + print(c) + else: + for c in sorted(components): + print(c) + + +if __name__ == "__main__": + main() diff --git a/script/test_build_components b/script/test_build_components new file mode 100755 index 0000000000..f951ba7545 --- /dev/null +++ b/script/test_build_components @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +set -e + +# Parse parameter: +# - `e` - Parameter for `esphome` command. Default `compile`. Common alternative is `config`. +# - `c` - Component folder name to test. Default `*`. +esphome_command="compile" +target_component="*" +while getopts e:c: flag +do + case $flag in + e) esphome_command=${OPTARG};; + c) target_component=${OPTARG};; + \?) echo "Usage: $0 [-e ] [-c ]" 1>&2; exit 1;; + esac +done + +cd "$(dirname "$0")/.." + +if ! [ -d "./tests/test_build_components/build" ]; then + mkdir ./tests/test_build_components/build +fi + +start_esphome() { + # create dynamic yaml file in `build` folder. + # `./tests/test_build_components/build/[target_component].[test_name].[target_platform].yaml` + component_test_file="./tests/test_build_components/build/$target_component.$test_name.$target_platform.yaml" + + cp $target_platform_file $component_test_file + sed -i "s!\$component_test_file!../../.$f!g" $component_test_file + + # Start esphome process + echo "> [$target_component] [$test_name] [$target_platform]" + echo "esphome -s component_name $target_component -s test_name $test_name -s target_platform $target_platform $esphome_command $component_test_file" + # TODO: Validate escape of Command line substitution value + esphome -s component_name $target_component -s test_name $test_name -s target_platform $target_platform $esphome_command $component_test_file +} + +# Find all test yaml files. +# - `./tests/components/[target_component]/[test_name].[target_platform].yaml` +# - `./tests/components/[target_component]/[test_name].all.yaml` +for f in ./tests/components/$target_component/*.*.yaml; do + [ -f "$f" ] || continue + IFS='/' read -r -a folder_name <<< "$f" + target_component="${folder_name[3]}" + + IFS='.' read -r -a file_name <<< "${folder_name[4]}" + test_name="${file_name[0]}" + target_platform="${file_name[1]}" + file_name_parts=${#file_name[@]} + + if [ "$target_platform" = "all" ] || [ $file_name_parts = 2 ]; then + # Test has *not* defined a specific target platform. Need to run tests for all possible target platforms. + + for target_platform_file in ./tests/test_build_components/build_components_base.*.yaml; do + IFS='/' read -r -a folder_name <<< "$target_platform_file" + IFS='.' read -r -a file_name <<< "${folder_name[3]}" + target_platform="${file_name[1]}" + + start_esphome + done + + else + # Test has defined a specific target platform. + + # Validate we have a base test yaml for selected platform. + # The target_platform is sourced from the following location. + # 1. `./tests/test_build_components/build_components_base.[target_platform].yaml` + # 2. `./tests/test_build_components/build_components_base.[target_platform]-ard.yaml` + target_platform_file="./tests/test_build_components/build_components_base.$target_platform.yaml" + if ! [ -f "$target_platform_file" ]; then + # Try find arduino test framework as platform. + target_platform_ard="$target_platform-ard" + target_platform_file="./tests/test_build_components/build_components_base.$target_platform_ard.yaml" + if ! [ -f "$target_platform_file" ]; then + echo "No base test file [./tests/test_build_components/build_components_base.$target_platform.yaml, ./tests/build_components_base.$target_platform_ard.yaml] for component test [$f] found." + exit 1 + fi + target_platform=$target_platform_ard + fi + + start_esphome + fi +done diff --git a/tests/components/adc/test.esp32-c3.yaml b/tests/components/adc/test.esp32-c3.yaml new file mode 100644 index 0000000000..18e5ab3561 --- /dev/null +++ b/tests/components/adc/test.esp32-c3.yaml @@ -0,0 +1,5 @@ +sensor: + - platform: adc + id: my_sensor + pin: 4 + attenuation: 11db diff --git a/tests/components/adc/test.esp32-idf.yaml b/tests/components/adc/test.esp32-idf.yaml new file mode 100644 index 0000000000..923fd0d706 --- /dev/null +++ b/tests/components/adc/test.esp32-idf.yaml @@ -0,0 +1,11 @@ +sensor: + - platform: adc + pin: A0 + name: Living Room Brightness + update_interval: "1:01" + attenuation: 2.5db + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc/test.esp32-s2.yaml b/tests/components/adc/test.esp32-s2.yaml new file mode 100644 index 0000000000..0119ad5e4d --- /dev/null +++ b/tests/components/adc/test.esp32-s2.yaml @@ -0,0 +1,5 @@ +sensor: + - platform: adc + id: my_sensor + pin: 1 + attenuation: 11db diff --git a/tests/components/adc/test.esp32-s3.yaml b/tests/components/adc/test.esp32-s3.yaml new file mode 100644 index 0000000000..0119ad5e4d --- /dev/null +++ b/tests/components/adc/test.esp32-s3.yaml @@ -0,0 +1,5 @@ +sensor: + - platform: adc + id: my_sensor + pin: 1 + attenuation: 11db diff --git a/tests/components/adc/test.esp32.yaml b/tests/components/adc/test.esp32.yaml new file mode 100644 index 0000000000..923fd0d706 --- /dev/null +++ b/tests/components/adc/test.esp32.yaml @@ -0,0 +1,11 @@ +sensor: + - platform: adc + pin: A0 + name: Living Room Brightness + update_interval: "1:01" + attenuation: 2.5db + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc/test.esp8266.yaml b/tests/components/adc/test.esp8266.yaml new file mode 100644 index 0000000000..1ef79c7ca1 --- /dev/null +++ b/tests/components/adc/test.esp8266.yaml @@ -0,0 +1,4 @@ +sensor: + - platform: adc + id: my_sensor + pin: VCC diff --git a/tests/components/adc/test.rp2040.yaml b/tests/components/adc/test.rp2040.yaml new file mode 100644 index 0000000000..200b802a4d --- /dev/null +++ b/tests/components/adc/test.rp2040.yaml @@ -0,0 +1,4 @@ +sensor: + - platform: adc + pin: VCC + name: VSYS diff --git a/tests/components/mopeka_std_check/test.esp32.yaml b/tests/components/mopeka_std_check/test.esp32.yaml new file mode 100644 index 0000000000..830adf952f --- /dev/null +++ b/tests/components/mopeka_std_check/test.esp32.yaml @@ -0,0 +1,16 @@ +esp32_ble_tracker: + +sensor: + # Example using 11kg 100% propane tank. + - platform: mopeka_std_check + mac_address: D3:75:F2:DC:16:91 + tank_type: Europe_11kg + temperature: + name: "Propane test temp" + level: + name: "Propane test level" + distance: + name: "Propane test distance" + battery_level: + name: "Propane test battery level" + diff --git a/tests/components/template/test.all.yaml b/tests/components/template/test.all.yaml new file mode 100644 index 0000000000..ad67b4e6ae --- /dev/null +++ b/tests/components/template/test.all.yaml @@ -0,0 +1,127 @@ +sensor: + - platform: template + name: "Template Sensor" + id: template_sens + lambda: |- + if (id(some_binary_sensor).state) { + return 42.0; + } else { + return 0.0; + } + update_interval: 60s + +esphome: + on_boot: + - sensor.template.publish: + id: template_sens + state: 42.0 + + # Templated + - sensor.template.publish: + id: template_sens + state: !lambda 'return 42.0;' + +binary_sensor: + - platform: template + id: some_binary_sensor + name: "Garage Door Open" + lambda: |- + if (id(template_sens).state > 30) { + // Garage Door is open. + return true; + } else { + // Garage Door is closed. + return false; + } + +output: + - platform: template + id: outputsplit + type: float + write_action: + - logger.log: "write_action" + +switch: + - platform: template + name: "Template Switch" + lambda: |- + if (id(some_binary_sensor).state) { + return true; + } else { + return false; + } + turn_on_action: + - logger.log: "turn_on_action" + turn_off_action: + - logger.log: "turn_off_action" + +button: + - platform: template + name: "Template Button" + on_press: + - logger.log: Button Pressed + +cover: + - platform: template + name: "Template Cover" + lambda: |- + if (id(some_binary_sensor).state) { + return COVER_OPEN; + } else { + return COVER_CLOSED; + } + open_action: + - logger.log: open_action + close_action: + - logger.log: close_action + stop_action: + - logger.log: stop_action + optimistic: true + +number: + - platform: template + name: "Template number" + optimistic: true + min_value: 0 + max_value: 100 + step: 1 + +select: + - platform: template + name: "Template select" + optimistic: true + options: + - one + - two + - three + initial_option: two + +lock: + - platform: template + name: "Template Lock" + lambda: |- + if (id(some_binary_sensor).state) { + return LOCK_STATE_LOCKED; + } else { + return LOCK_STATE_UNLOCKED; + } + lock_action: + - logger.log: lock_action + unlock_action: + - logger.log: unlock_action + open_action: + - logger.log: open_action + +text: + - platform: template + name: "Template text" + optimistic: true + min_length: 0 + max_length: 100 + mode: text + +alarm_control_panel: + - platform: template + name: Alarm Panel + codes: + - "1234" diff --git a/tests/test_build_components/build_components_base.esp32-ard.yaml b/tests/test_build_components/build_components_base.esp32-ard.yaml new file mode 100644 index 0000000000..f460c57298 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-ard.yaml @@ -0,0 +1,20 @@ +esphome: + name: componenttestesp32ard + friendly_name: $component_name + +esp32: + board: nodemcu-32s + framework: + type: arduino + +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 diff --git a/tests/test_build_components/build_components_base.esp32-c3-ard.yaml b/tests/test_build_components/build_components_base.esp32-c3-ard.yaml new file mode 100644 index 0000000000..8a52e0c916 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-c3-ard.yaml @@ -0,0 +1,20 @@ +esphome: + name: componenttestesp32c3ard + friendly_name: $component_name + +esp32: + board: lolin_c3_mini + framework: + type: arduino + +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 diff --git a/tests/test_build_components/build_components_base.esp32-c3-idf.yaml b/tests/test_build_components/build_components_base.esp32-c3-idf.yaml new file mode 100644 index 0000000000..6b4b61fe58 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-c3-idf.yaml @@ -0,0 +1,20 @@ +esphome: + name: componenttestesp32c3idf + friendly_name: $component_name + +esp32: + board: lolin_c3_mini + framework: + type: esp-idf + +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 diff --git a/tests/test_build_components/build_components_base.esp32-idf.yaml b/tests/test_build_components/build_components_base.esp32-idf.yaml new file mode 100644 index 0000000000..ab1bda2a19 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-idf.yaml @@ -0,0 +1,20 @@ +esphome: + name: componenttestesp32idf + friendly_name: $component_name + +esp32: + board: nodemcu-32s + framework: + type: esp-idf + +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 diff --git a/tests/test_build_components/build_components_base.esp32-s2-ard.yaml b/tests/test_build_components/build_components_base.esp32-s2-ard.yaml new file mode 100644 index 0000000000..ffb912d3d9 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-s2-ard.yaml @@ -0,0 +1,21 @@ +esphome: + name: componenttestesp32s2ard + friendly_name: $component_name + +esp32: + board: esp32-s2-saola-1 + variant: ESP32S2 + framework: + type: arduino + +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 diff --git a/tests/test_build_components/build_components_base.esp32-s2-idf.yaml b/tests/test_build_components/build_components_base.esp32-s2-idf.yaml new file mode 100644 index 0000000000..4d1378b2b2 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-s2-idf.yaml @@ -0,0 +1,21 @@ +esphome: + name: componenttestesp32s2ard + friendly_name: $component_name + +esp32: + board: esp32-s2-saola-1 + variant: ESP32S2 + framework: + type: esp-idf + +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 diff --git a/tests/test_build_components/build_components_base.esp32-s3-ard.yaml b/tests/test_build_components/build_components_base.esp32-s3-ard.yaml new file mode 100644 index 0000000000..c850c9665f --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-s3-ard.yaml @@ -0,0 +1,21 @@ +esphome: + name: componenttestesp32s3ard + friendly_name: $component_name + +esp32: + board: esp32s3box + variant: ESP32S3 + framework: + type: arduino + +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 diff --git a/tests/test_build_components/build_components_base.esp32-s3-idf.yaml b/tests/test_build_components/build_components_base.esp32-s3-idf.yaml new file mode 100644 index 0000000000..a43a2a6736 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-s3-idf.yaml @@ -0,0 +1,21 @@ +esphome: + name: componenttestesp32s3ard + friendly_name: $component_name + +esp32: + board: esp32s3box + variant: ESP32S3 + framework: + type: esp-idf + +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 diff --git a/tests/test_build_components/build_components_base.esp8266.yaml b/tests/test_build_components/build_components_base.esp8266.yaml new file mode 100644 index 0000000000..d7bdc03659 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp8266.yaml @@ -0,0 +1,18 @@ +esphome: + name: componenttestesp8266 + friendly_name: $component_name + +esp8266: + board: d1_mini + +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 diff --git a/tests/test_build_components/build_components_base.rp2040.yaml b/tests/test_build_components/build_components_base.rp2040.yaml new file mode 100644 index 0000000000..a02942ea35 --- /dev/null +++ b/tests/test_build_components/build_components_base.rp2040.yaml @@ -0,0 +1,21 @@ +esphome: + name: componenttestrp2040 + friendly_name: $component_name + +rp2040: + board: rpipicow + framework: + # Waiting for https://github.com/platformio/platform-raspberrypi/pull/36 + platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git + +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 e2f2feafdd912f5e249ccb1b47502e4201665cf2 Mon Sep 17 00:00:00 2001 From: Rene Guca <45061891+rguca@users.noreply.github.com> Date: Thu, 18 Jan 2024 08:30:58 +0100 Subject: [PATCH 154/193] WiFi fast_connect: save/load BSSID and channel for faster connect from sleep (#5931) --- esphome/components/wifi/wifi_component.cpp | 38 ++++++++++++++++++++++ esphome/components/wifi/wifi_component.h | 9 +++++ 2 files changed, 47 insertions(+) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 519489097a..05938d87a2 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -55,6 +55,9 @@ void WiFiComponent::start() { uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL; this->pref_ = global_preferences->make_preference(hash, true); + if (this->fast_connect_) { + this->fast_connect_pref_ = global_preferences->make_preference(hash, false); + } SavedWifiSettings save{}; if (this->pref_.load(&save)) { @@ -78,6 +81,7 @@ void WiFiComponent::start() { if (this->fast_connect_) { this->selected_ap_ = this->sta_[0]; + this->load_fast_connect_settings_(); this->start_connecting(this->selected_ap_, false); } else { this->start_scanning(); @@ -604,6 +608,11 @@ void WiFiComponent::check_connecting_finished() { this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTED; this->num_retried_ = 0; + + if (this->fast_connect_) { + this->save_fast_connect_settings_(); + } + return; } @@ -705,6 +714,35 @@ bool WiFiComponent::is_esp32_improv_active_() { #endif } +void WiFiComponent::load_fast_connect_settings_() { + SavedWifiFastConnectSettings fast_connect_save{}; + + if (this->fast_connect_pref_.load(&fast_connect_save)) { + bssid_t bssid{}; + std::copy(fast_connect_save.bssid, fast_connect_save.bssid + 6, bssid.begin()); + this->selected_ap_.set_bssid(bssid); + this->selected_ap_.set_channel(fast_connect_save.channel); + + ESP_LOGD(TAG, "Loaded saved fast_connect wifi settings"); + } +} + +void WiFiComponent::save_fast_connect_settings_() { + bssid_t bssid = wifi_bssid(); + uint8_t channel = wifi_channel_(); + + if (bssid != this->selected_ap_.get_bssid() || channel != this->selected_ap_.get_channel()) { + SavedWifiFastConnectSettings fast_connect_save{}; + + memcpy(fast_connect_save.bssid, bssid.data(), 6); + fast_connect_save.channel = channel; + + this->fast_connect_pref_.save(&fast_connect_save); + + ESP_LOGD(TAG, "Saved fast_connect wifi settings"); + } +} + void WiFiAP::set_ssid(const std::string &ssid) { this->ssid_ = ssid; } void WiFiAP::set_bssid(bssid_t bssid) { this->bssid_ = bssid; } void WiFiAP::set_bssid(optional bssid) { this->bssid_ = bssid; } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 6cbdc51caf..be5095105c 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -48,6 +48,11 @@ struct SavedWifiSettings { char password[65]; } PACKED; // NOLINT +struct SavedWifiFastConnectSettings { + uint8_t bssid[6]; + uint8_t channel; +} PACKED; // NOLINT + enum WiFiComponentState { /** Nothing has been initialized yet. Internal AP, if configured, is disabled at this point. */ WIFI_COMPONENT_STATE_OFF = 0, @@ -334,6 +339,9 @@ class WiFiComponent : public Component { bool is_captive_portal_active_(); bool is_esp32_improv_active_(); + void load_fast_connect_settings_(); + void save_fast_connect_settings_(); + #ifdef USE_ESP8266 static void wifi_event_callback(System_Event_t *event); void wifi_scan_done_callback_(void *arg, STATUS status); @@ -381,6 +389,7 @@ class WiFiComponent : public Component { optional output_power_; bool passive_scan_{false}; ESPPreferenceObject pref_; + ESPPreferenceObject fast_connect_pref_; bool has_saved_wifi_settings_{false}; #ifdef USE_WIFI_11KV_SUPPORT bool btm_{false}; From 45c0d10eb091c8717af55ef4651f22316f0ec4b4 Mon Sep 17 00:00:00 2001 From: "pofilo (vmerat)" Date: Thu, 18 Jan 2024 08:35:20 +0100 Subject: [PATCH 155/193] Fixes Waveshare 7.5in B V2 and V3 (#6079) --- esphome/components/waveshare_epaper/waveshare_epaper.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 0e9b129988..244b3b1ce2 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -1443,6 +1443,12 @@ void WaveshareEPaper7P5InBV2::initialize() { // COMMAND TCON SETTING this->command(0x60); this->data(0x22); + + this->command(0x82); + this->data(0x08); + this->command(0x30); + this->data(0x06); + // COMMAND RESOLUTION SETTING this->command(0x65); this->data(0x00); @@ -1472,6 +1478,7 @@ void HOT WaveshareEPaper7P5InBV2::display() { this->command(0x12); delay(100); // NOLINT this->wait_until_idle_(); + this->deep_sleep(); } int WaveshareEPaper7P5InBV2::get_width_internal() { return 800; } int WaveshareEPaper7P5InBV2::get_height_internal() { return 480; } @@ -1617,7 +1624,7 @@ void HOT WaveshareEPaper7P5InBV3::display() { this->command(0x13); // Start Transmission delay(2); for (uint32_t i = 0; i < buf_len; i++) { - this->data(this->buffer_[i]); + this->data(~this->buffer_[i]); } this->command(0x12); // Display Refresh From 045836c3fe4e088475c248256f7b2e537cbabe05 Mon Sep 17 00:00:00 2001 From: kahrendt Date: Thu, 18 Jan 2024 04:09:49 -0500 Subject: [PATCH 156/193] Add combination sensor and remove absorbed kalman_combinator component (#5438) --- CODEOWNERS | 2 +- esphome/components/combination/__init__.py | 0 .../components/combination/combination.cpp | 262 ++++++++++++++++++ esphome/components/combination/combination.h | 141 ++++++++++ esphome/components/combination/sensor.py | 176 ++++++++++++ .../components/kalman_combinator/__init__.py | 1 - .../kalman_combinator/kalman_combinator.cpp | 82 ------ .../kalman_combinator/kalman_combinator.h | 46 --- .../components/kalman_combinator/sensor.py | 92 +----- tests/test1.yaml | 54 +++- 10 files changed, 637 insertions(+), 219 deletions(-) create mode 100644 esphome/components/combination/__init__.py create mode 100644 esphome/components/combination/combination.cpp create mode 100644 esphome/components/combination/combination.h create mode 100644 esphome/components/combination/sensor.py delete mode 100644 esphome/components/kalman_combinator/kalman_combinator.cpp delete mode 100644 esphome/components/kalman_combinator/kalman_combinator.h diff --git a/CODEOWNERS b/CODEOWNERS index 7e87679ad8..95e3b35f56 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -71,6 +71,7 @@ esphome/components/cd74hc4067/* @asoehlke esphome/components/climate/* @esphome/core esphome/components/climate_ir/* @glmnet esphome/components/color_temperature/* @jesserockz +esphome/components/combination/* @Cat-Ion @kahrendt esphome/components/coolix/* @glmnet esphome/components/copy/* @OttoWinter esphome/components/cover/* @esphome/core @@ -161,7 +162,6 @@ esphome/components/integration/* @OttoWinter esphome/components/internal_temperature/* @Mat931 esphome/components/interval/* @esphome/core esphome/components/json/* @OttoWinter -esphome/components/kalman_combinator/* @Cat-Ion esphome/components/key_collector/* @ssieb esphome/components/key_provider/* @ssieb esphome/components/kuntze/* @ssieb diff --git a/esphome/components/combination/__init__.py b/esphome/components/combination/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/combination/combination.cpp b/esphome/components/combination/combination.cpp new file mode 100644 index 0000000000..716d270390 --- /dev/null +++ b/esphome/components/combination/combination.cpp @@ -0,0 +1,262 @@ +#include "combination.h" + +#include "esphome/core/log.h" +#include "esphome/core/hal.h" + +#include +#include +#include + +namespace esphome { +namespace combination { + +static const char *const TAG = "combination"; + +void CombinationComponent::log_config_(const LogString *combo_type) { + LOG_SENSOR("", "Combination Sensor:", this); + ESP_LOGCONFIG(TAG, " Combination Type: %s", LOG_STR_ARG(combo_type)); + this->log_source_sensors(); +} + +void CombinationNoParameterComponent::add_source(Sensor *sensor) { this->sensors_.emplace_back(sensor); } + +void CombinationOneParameterComponent::add_source(Sensor *sensor, std::function const &stddev) { + this->sensor_pairs_.emplace_back(sensor, stddev); +} + +void CombinationOneParameterComponent::add_source(Sensor *sensor, float stddev) { + this->add_source(sensor, std::function{[stddev](float x) -> float { return stddev; }}); +} + +void CombinationNoParameterComponent::log_source_sensors() { + ESP_LOGCONFIG(TAG, " Source Sensors:"); + for (const auto &sensor : this->sensors_) { + ESP_LOGCONFIG(TAG, " - %s", sensor->get_name().c_str()); + } +} + +void CombinationOneParameterComponent::log_source_sensors() { + ESP_LOGCONFIG(TAG, " Source Sensors:"); + for (const auto &sensor : this->sensor_pairs_) { + auto &entity = *sensor.first; + ESP_LOGCONFIG(TAG, " - %s", entity.get_name().c_str()); + } +} + +void CombinationNoParameterComponent::setup() { + for (const auto &sensor : this->sensors_) { + // All sensor updates are deferred until the next loop. This avoids publishing the combined sensor's result + // repeatedly in the same loop if multiple source senors update. + sensor->add_on_state_callback( + [this](float value) -> void { this->defer("update", [this, value]() { this->handle_new_value(value); }); }); + } +} + +void KalmanCombinationComponent::dump_config() { + this->log_config_(LOG_STR("kalman")); + ESP_LOGCONFIG(TAG, " Update variance: %f per ms", this->update_variance_value_); + + if (this->std_dev_sensor_ != nullptr) { + LOG_SENSOR(" ", "Standard Deviation Sensor:", this->std_dev_sensor_); + } +} + +void KalmanCombinationComponent::setup() { + for (const auto &sensor : this->sensor_pairs_) { + const auto stddev = sensor.second; + sensor.first->add_on_state_callback([this, stddev](float x) -> void { this->correct_(x, stddev(x)); }); + } +} + +void KalmanCombinationComponent::update_variance_() { + uint32_t now = millis(); + + // Variance increases by update_variance_ each millisecond + auto dt = now - this->last_update_; + auto dv = this->update_variance_value_ * dt; + this->variance_ += dv; + this->last_update_ = now; +} + +void KalmanCombinationComponent::correct_(float value, float stddev) { + if (std::isnan(value) || std::isinf(stddev)) { + return; + } + + if (std::isnan(this->state_) || std::isinf(this->variance_)) { + this->state_ = value; + this->variance_ = stddev * stddev; + if (this->std_dev_sensor_ != nullptr) { + this->std_dev_sensor_->publish_state(stddev); + } + return; + } + + this->update_variance_(); + + // Combine two gaussian distributions mu1+-var1, mu2+-var2 to a new one around mu + // Use the value with the smaller variance as mu1 to prevent precision errors + const bool this_first = this->variance_ < (stddev * stddev); + const float mu1 = this_first ? this->state_ : value; + const float mu2 = this_first ? value : this->state_; + + const float var1 = this_first ? this->variance_ : stddev * stddev; + const float var2 = this_first ? stddev * stddev : this->variance_; + + const float mu = mu1 + var1 * (mu2 - mu1) / (var1 + var2); + const float var = var1 - (var1 * var1) / (var1 + var2); + + // Update and publish state + this->state_ = mu; + this->variance_ = var; + + this->publish_state(mu); + if (this->std_dev_sensor_ != nullptr) { + this->std_dev_sensor_->publish_state(std::sqrt(var)); + } +} + +void LinearCombinationComponent::setup() { + for (const auto &sensor : this->sensor_pairs_) { + // All sensor updates are deferred until the next loop. This avoids publishing the combined sensor's result + // repeatedly in the same loop if multiple source senors update. + sensor.first->add_on_state_callback( + [this](float value) -> void { this->defer("update", [this, value]() { this->handle_new_value(value); }); }); + } +} + +void LinearCombinationComponent::handle_new_value(float value) { + // Multiplies each sensor state by a configured coeffecient and then sums + + if (!std::isfinite(value)) + return; + + float sum = 0.0; + + for (const auto &sensor : this->sensor_pairs_) { + const float sensor_state = sensor.first->state; + if (std::isfinite(sensor_state)) { + sum += sensor_state * sensor.second(sensor_state); + } + } + + this->publish_state(sum); +}; + +void MaximumCombinationComponent::handle_new_value(float value) { + if (!std::isfinite(value)) + return; + + float max_value = (-1) * std::numeric_limits::infinity(); // note x = max(x, -infinity) + + for (const auto &sensor : this->sensors_) { + if (std::isfinite(sensor->state)) { + max_value = std::max(max_value, sensor->state); + } + } + + this->publish_state(max_value); +} + +void MeanCombinationComponent::handle_new_value(float value) { + if (!std::isfinite(value)) + return; + + float sum = 0.0; + size_t count = 0.0; + + for (const auto &sensor : this->sensors_) { + if (std::isfinite(sensor->state)) { + ++count; + sum += sensor->state; + } + } + + float mean = sum / count; + + this->publish_state(mean); +} + +void MedianCombinationComponent::handle_new_value(float value) { + // Sorts sensor states in ascending order and determines the middle value + + if (!std::isfinite(value)) + return; + + std::vector sensor_states; + for (const auto &sensor : this->sensors_) { + if (std::isfinite(sensor->state)) { + sensor_states.push_back(sensor->state); + } + } + + sort(sensor_states.begin(), sensor_states.end()); + size_t sensor_states_size = sensor_states.size(); + + float median = NAN; + + if (sensor_states_size) { + if (sensor_states_size % 2) { + // Odd number of measurements, use middle measurement + median = sensor_states[sensor_states_size / 2]; + } else { + // Even number of measurements, use the average of the two middle measurements + median = (sensor_states[sensor_states_size / 2] + sensor_states[sensor_states_size / 2 - 1]) / 2.0; + } + } + + this->publish_state(median); +} + +void MinimumCombinationComponent::handle_new_value(float value) { + if (!std::isfinite(value)) + return; + + float min_value = std::numeric_limits::infinity(); // note x = min(x, infinity) + + for (const auto &sensor : this->sensors_) { + if (std::isfinite(sensor->state)) { + min_value = std::min(min_value, sensor->state); + } + } + + this->publish_state(min_value); +} + +void MostRecentCombinationComponent::handle_new_value(float value) { this->publish_state(value); } + +void RangeCombinationComponent::handle_new_value(float value) { + // Sorts sensor states then takes difference between largest and smallest states + + if (!std::isfinite(value)) + return; + + std::vector sensor_states; + for (const auto &sensor : this->sensors_) { + if (std::isfinite(sensor->state)) { + sensor_states.push_back(sensor->state); + } + } + + sort(sensor_states.begin(), sensor_states.end()); + + float range = sensor_states.back() - sensor_states.front(); + this->publish_state(range); +} + +void SumCombinationComponent::handle_new_value(float value) { + if (!std::isfinite(value)) + return; + + float sum = 0.0; + for (const auto &sensor : this->sensors_) { + if (std::isfinite(sensor->state)) { + sum += sensor->state; + } + } + + this->publish_state(sum); +} + +} // namespace combination +} // namespace esphome diff --git a/esphome/components/combination/combination.h b/esphome/components/combination/combination.h new file mode 100644 index 0000000000..901aeaf259 --- /dev/null +++ b/esphome/components/combination/combination.h @@ -0,0 +1,141 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" + +#include + +namespace esphome { +namespace combination { + +class CombinationComponent : public Component, public sensor::Sensor { + public: + float get_setup_priority() const override { return esphome::setup_priority::DATA; } + + /// @brief Logs all source sensor's names + virtual void log_source_sensors() = 0; + + protected: + /// @brief Logs the sensor for use in dump_config + /// @param combo_type Name of the combination operation + void log_config_(const LogString *combo_type); +}; + +/// @brief Base class for operations that do not require an extra parameter to compute the combination +class CombinationNoParameterComponent : public CombinationComponent { + public: + /// @brief Adds a callback to each source sensor + void setup() override; + + void add_source(Sensor *sensor); + + /// @brief Computes the combination + /// @param value Newest sensor measurement + virtual void handle_new_value(float value) = 0; + + /// @brief Logs all source sensor's names in sensors_ + void log_source_sensors() override; + + protected: + std::vector sensors_; +}; + +// Base class for opertions that require one parameter to compute the combination +class CombinationOneParameterComponent : public CombinationComponent { + public: + void add_source(Sensor *sensor, std::function const &stddev); + void add_source(Sensor *sensor, float stddev); + + /// @brief Logs all source sensor's names in sensor_pairs_ + void log_source_sensors() override; + + protected: + std::vector>> sensor_pairs_; +}; + +class KalmanCombinationComponent : public CombinationOneParameterComponent { + public: + void dump_config() override; + void setup() override; + + void set_process_std_dev(float process_std_dev) { + this->update_variance_value_ = process_std_dev * process_std_dev * 0.001f; + } + void set_std_dev_sensor(Sensor *sensor) { this->std_dev_sensor_ = sensor; } + + protected: + void update_variance_(); + void correct_(float value, float stddev); + + // Optional sensor for publishing the current error + sensor::Sensor *std_dev_sensor_{nullptr}; + + // Tick of the last update + uint32_t last_update_{0}; + // Change of the variance, per ms + float update_variance_value_{0.f}; + + // Best guess for the state and its variance + float state_{NAN}; + float variance_{INFINITY}; +}; + +class LinearCombinationComponent : public CombinationOneParameterComponent { + public: + void dump_config() override { this->log_config_(LOG_STR("linear")); } + void setup() override; + + void handle_new_value(float value); +}; + +class MaximumCombinationComponent : public CombinationNoParameterComponent { + public: + void dump_config() override { this->log_config_(LOG_STR("max")); } + + void handle_new_value(float value) override; +}; + +class MeanCombinationComponent : public CombinationNoParameterComponent { + public: + void dump_config() override { this->log_config_(LOG_STR("mean")); } + + void handle_new_value(float value) override; +}; + +class MedianCombinationComponent : public CombinationNoParameterComponent { + public: + void dump_config() override { this->log_config_(LOG_STR("median")); } + + void handle_new_value(float value) override; +}; + +class MinimumCombinationComponent : public CombinationNoParameterComponent { + public: + void dump_config() override { this->log_config_(LOG_STR("min")); } + + void handle_new_value(float value) override; +}; + +class MostRecentCombinationComponent : public CombinationNoParameterComponent { + public: + void dump_config() override { this->log_config_(LOG_STR("most_recently_updated")); } + + void handle_new_value(float value) override; +}; + +class RangeCombinationComponent : public CombinationNoParameterComponent { + public: + void dump_config() override { this->log_config_(LOG_STR("range")); } + + void handle_new_value(float value) override; +}; + +class SumCombinationComponent : public CombinationNoParameterComponent { + public: + void dump_config() override { this->log_config_(LOG_STR("sum")); } + + void handle_new_value(float value) override; +}; + +} // namespace combination +} // namespace esphome diff --git a/esphome/components/combination/sensor.py b/esphome/components/combination/sensor.py new file mode 100644 index 0000000000..fad0277061 --- /dev/null +++ b/esphome/components/combination/sensor.py @@ -0,0 +1,176 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor +from esphome.const import ( + CONF_ACCURACY_DECIMALS, + CONF_DEVICE_CLASS, + CONF_ENTITY_CATEGORY, + CONF_ICON, + CONF_ID, + CONF_RANGE, + CONF_SOURCE, + CONF_SUM, + CONF_TYPE, + CONF_UNIT_OF_MEASUREMENT, +) +from esphome.core.entity_helpers import inherit_property_from + +CODEOWNERS = ["@Cat-Ion", "@kahrendt"] + +combination_ns = cg.esphome_ns.namespace("combination") + +KalmanCombinationComponent = combination_ns.class_( + "KalmanCombinationComponent", cg.Component, sensor.Sensor +) +LinearCombinationComponent = combination_ns.class_( + "LinearCombinationComponent", cg.Component, sensor.Sensor +) +MaximumCombinationComponent = combination_ns.class_( + "MaximumCombinationComponent", cg.Component, sensor.Sensor +) +MeanCombinationComponent = combination_ns.class_( + "MeanCombinationComponent", cg.Component, sensor.Sensor +) +MedianCombinationComponent = combination_ns.class_( + "MedianCombinationComponent", cg.Component, sensor.Sensor +) +MinimumCombinationComponent = combination_ns.class_( + "MinimumCombinationComponent", cg.Component, sensor.Sensor +) +MostRecentCombinationComponent = combination_ns.class_( + "MostRecentCombinationComponent", cg.Component, sensor.Sensor +) +RangeCombinationComponent = combination_ns.class_( + "RangeCombinationComponent", cg.Component, sensor.Sensor +) +SumCombinationComponent = combination_ns.class_( + "SumCombinationComponent", cg.Component, sensor.Sensor +) + +CONF_COEFFECIENT = "coeffecient" +CONF_ERROR = "error" +CONF_KALMAN = "kalman" +CONF_LINEAR = "linear" +CONF_MAX = "max" +CONF_MEAN = "mean" +CONF_MEDIAN = "median" +CONF_MIN = "min" +CONF_MOST_RECENTLY_UPDATED = "most_recently_updated" +CONF_PROCESS_STD_DEV = "process_std_dev" +CONF_SOURCES = "sources" +CONF_STD_DEV = "std_dev" + + +KALMAN_SOURCE_SCHEMA = cv.Schema( + { + cv.Required(CONF_SOURCE): cv.use_id(sensor.Sensor), + cv.Required(CONF_ERROR): cv.templatable(cv.positive_float), + } +) + +LINEAR_SOURCE_SCHEMA = cv.Schema( + { + cv.Required(CONF_SOURCE): cv.use_id(sensor.Sensor), + cv.Required(CONF_COEFFECIENT): cv.templatable(cv.float_), + } +) + +SENSOR_ONLY_SOURCE_SCHEMA = cv.Schema( + { + cv.Required(CONF_SOURCE): cv.use_id(sensor.Sensor), + } +) + +CONFIG_SCHEMA = cv.typed_schema( + { + CONF_KALMAN: sensor.sensor_schema(KalmanCombinationComponent) + .extend(cv.COMPONENT_SCHEMA) + .extend( + { + cv.Required(CONF_PROCESS_STD_DEV): cv.positive_float, + cv.Required(CONF_SOURCES): cv.ensure_list(KALMAN_SOURCE_SCHEMA), + cv.Optional(CONF_STD_DEV): sensor.sensor_schema(), + } + ), + CONF_LINEAR: sensor.sensor_schema(LinearCombinationComponent) + .extend(cv.COMPONENT_SCHEMA) + .extend({cv.Required(CONF_SOURCES): cv.ensure_list(LINEAR_SOURCE_SCHEMA)}), + CONF_MAX: sensor.sensor_schema(MaximumCombinationComponent) + .extend(cv.COMPONENT_SCHEMA) + .extend({cv.Required(CONF_SOURCES): cv.ensure_list(SENSOR_ONLY_SOURCE_SCHEMA)}), + CONF_MEAN: sensor.sensor_schema(MeanCombinationComponent) + .extend(cv.COMPONENT_SCHEMA) + .extend({cv.Required(CONF_SOURCES): cv.ensure_list(SENSOR_ONLY_SOURCE_SCHEMA)}), + CONF_MEDIAN: sensor.sensor_schema(MedianCombinationComponent) + .extend(cv.COMPONENT_SCHEMA) + .extend({cv.Required(CONF_SOURCES): cv.ensure_list(SENSOR_ONLY_SOURCE_SCHEMA)}), + CONF_MIN: sensor.sensor_schema(MinimumCombinationComponent) + .extend(cv.COMPONENT_SCHEMA) + .extend({cv.Required(CONF_SOURCES): cv.ensure_list(SENSOR_ONLY_SOURCE_SCHEMA)}), + CONF_MOST_RECENTLY_UPDATED: sensor.sensor_schema(MostRecentCombinationComponent) + .extend(cv.COMPONENT_SCHEMA) + .extend({cv.Required(CONF_SOURCES): cv.ensure_list(SENSOR_ONLY_SOURCE_SCHEMA)}), + CONF_RANGE: sensor.sensor_schema(RangeCombinationComponent) + .extend(cv.COMPONENT_SCHEMA) + .extend({cv.Required(CONF_SOURCES): cv.ensure_list(SENSOR_ONLY_SOURCE_SCHEMA)}), + CONF_SUM: sensor.sensor_schema(SumCombinationComponent) + .extend(cv.COMPONENT_SCHEMA) + .extend({cv.Required(CONF_SOURCES): cv.ensure_list(SENSOR_ONLY_SOURCE_SCHEMA)}), + } +) + + +# Inherit some sensor values from the first source, for both the state and the error value +# CONF_STATE_CLASS could also be inherited, but might lead to unexpected behaviour with "total_increasing" +properties_to_inherit = [ + CONF_ACCURACY_DECIMALS, + CONF_DEVICE_CLASS, + CONF_ENTITY_CATEGORY, + CONF_ICON, + CONF_UNIT_OF_MEASUREMENT, +] +inherit_schema_for_state = [ + inherit_property_from(property, [CONF_SOURCES, 0, CONF_SOURCE]) + for property in properties_to_inherit +] +inherit_schema_for_std_dev = [ + inherit_property_from([CONF_STD_DEV, property], [CONF_SOURCES, 0, CONF_SOURCE]) + for property in properties_to_inherit +] + +FINAL_VALIDATE_SCHEMA = cv.All( + *inherit_schema_for_state, + *inherit_schema_for_std_dev, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await sensor.register_sensor(var, config) + + if proces_std_dev := config.get(CONF_PROCESS_STD_DEV): + cg.add(var.set_process_std_dev(proces_std_dev)) + + for source_conf in config[CONF_SOURCES]: + source = await cg.get_variable(source_conf[CONF_SOURCE]) + if config[CONF_TYPE] == CONF_KALMAN: + error = await cg.templatable( + source_conf[CONF_ERROR], + [(float, "x")], + cg.float_, + ) + cg.add(var.add_source(source, error)) + elif config[CONF_TYPE] == CONF_LINEAR: + coeffecient = await cg.templatable( + source_conf[CONF_COEFFECIENT], + [(float, "x")], + cg.float_, + ) + cg.add(var.add_source(source, coeffecient)) + else: + cg.add(var.add_source(source)) + + if CONF_STD_DEV in config: + sens = await sensor.new_sensor(config[CONF_STD_DEV]) + cg.add(var.set_std_dev_sensor(sens)) diff --git a/esphome/components/kalman_combinator/__init__.py b/esphome/components/kalman_combinator/__init__.py index 3356e61bb2..e69de29bb2 100644 --- a/esphome/components/kalman_combinator/__init__.py +++ b/esphome/components/kalman_combinator/__init__.py @@ -1 +0,0 @@ -CODEOWNERS = ["@Cat-Ion"] diff --git a/esphome/components/kalman_combinator/kalman_combinator.cpp b/esphome/components/kalman_combinator/kalman_combinator.cpp deleted file mode 100644 index 50d8f03a93..0000000000 --- a/esphome/components/kalman_combinator/kalman_combinator.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "kalman_combinator.h" -#include "esphome/core/hal.h" -#include -#include - -namespace esphome { -namespace kalman_combinator { - -void KalmanCombinatorComponent::dump_config() { - ESP_LOGCONFIG("kalman_combinator", "Kalman Combinator:"); - ESP_LOGCONFIG("kalman_combinator", " Update variance: %f per ms", this->update_variance_value_); - ESP_LOGCONFIG("kalman_combinator", " Sensors:"); - for (const auto &sensor : this->sensors_) { - auto &entity = *sensor.first; - ESP_LOGCONFIG("kalman_combinator", " - %s", entity.get_name().c_str()); - } -} - -void KalmanCombinatorComponent::setup() { - for (const auto &sensor : this->sensors_) { - const auto stddev = sensor.second; - sensor.first->add_on_state_callback([this, stddev](float x) -> void { this->correct_(x, stddev(x)); }); - } -} - -void KalmanCombinatorComponent::add_source(Sensor *sensor, std::function const &stddev) { - this->sensors_.emplace_back(sensor, stddev); -} - -void KalmanCombinatorComponent::add_source(Sensor *sensor, float stddev) { - this->add_source(sensor, std::function{[stddev](float x) -> float { return stddev; }}); -} - -void KalmanCombinatorComponent::update_variance_() { - uint32_t now = millis(); - - // Variance increases by update_variance_ each millisecond - auto dt = now - this->last_update_; - auto dv = this->update_variance_value_ * dt; - this->variance_ += dv; - this->last_update_ = now; -} - -void KalmanCombinatorComponent::correct_(float value, float stddev) { - if (std::isnan(value) || std::isinf(stddev)) { - return; - } - - if (std::isnan(this->state_) || std::isinf(this->variance_)) { - this->state_ = value; - this->variance_ = stddev * stddev; - if (this->std_dev_sensor_ != nullptr) { - this->std_dev_sensor_->publish_state(stddev); - } - return; - } - - this->update_variance_(); - - // Combine two gaussian distributions mu1+-var1, mu2+-var2 to a new one around mu - // Use the value with the smaller variance as mu1 to prevent precision errors - const bool this_first = this->variance_ < (stddev * stddev); - const float mu1 = this_first ? this->state_ : value; - const float mu2 = this_first ? value : this->state_; - - const float var1 = this_first ? this->variance_ : stddev * stddev; - const float var2 = this_first ? stddev * stddev : this->variance_; - - const float mu = mu1 + var1 * (mu2 - mu1) / (var1 + var2); - const float var = var1 - (var1 * var1) / (var1 + var2); - - // Update and publish state - this->state_ = mu; - this->variance_ = var; - - this->publish_state(mu); - if (this->std_dev_sensor_ != nullptr) { - this->std_dev_sensor_->publish_state(std::sqrt(var)); - } -} -} // namespace kalman_combinator -} // namespace esphome diff --git a/esphome/components/kalman_combinator/kalman_combinator.h b/esphome/components/kalman_combinator/kalman_combinator.h deleted file mode 100644 index afbe3ece92..0000000000 --- a/esphome/components/kalman_combinator/kalman_combinator.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once -#include "esphome/core/component.h" -#include "esphome/components/sensor/sensor.h" -#include -#include - -namespace esphome { -namespace kalman_combinator { - -class KalmanCombinatorComponent : public Component, public sensor::Sensor { - public: - KalmanCombinatorComponent() = default; - - float get_setup_priority() const override { return esphome::setup_priority::DATA; } - - void dump_config() override; - void setup() override; - - void add_source(Sensor *sensor, std::function const &stddev); - void add_source(Sensor *sensor, float stddev); - void set_process_std_dev(float process_std_dev) { - this->update_variance_value_ = process_std_dev * process_std_dev * 0.001f; - } - void set_std_dev_sensor(Sensor *sensor) { this->std_dev_sensor_ = sensor; } - - private: - void update_variance_(); - void correct_(float value, float stddev); - - // Source sensors and their error functions - std::vector>> sensors_; - - // Optional sensor for publishing the current error - sensor::Sensor *std_dev_sensor_{nullptr}; - - // Tick of the last update - uint32_t last_update_{0}; - // Change of the variance, per ms - float update_variance_value_{0.f}; - - // Best guess for the state and its variance - float state_{NAN}; - float variance_{INFINITY}; -}; -} // namespace kalman_combinator -} // namespace esphome diff --git a/esphome/components/kalman_combinator/sensor.py b/esphome/components/kalman_combinator/sensor.py index 28b96077cc..eca1ba7b85 100644 --- a/esphome/components/kalman_combinator/sensor.py +++ b/esphome/components/kalman_combinator/sensor.py @@ -1,90 +1,6 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor -from esphome.const import ( - CONF_ID, - CONF_SOURCE, - CONF_ACCURACY_DECIMALS, - CONF_DEVICE_CLASS, - CONF_ENTITY_CATEGORY, - CONF_ICON, - CONF_UNIT_OF_MEASUREMENT, + +CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid( + "The kalman_combinator sensor has moved.\nPlease use the combination platform instead with type: kalman.\n" + "See https://esphome.io/components/sensor/combination.html" ) -from esphome.core.entity_helpers import inherit_property_from - -kalman_combinator_ns = cg.esphome_ns.namespace("kalman_combinator") -KalmanCombinatorComponent = kalman_combinator_ns.class_( - "KalmanCombinatorComponent", cg.Component, sensor.Sensor -) - -CONF_ERROR = "error" -CONF_SOURCES = "sources" -CONF_PROCESS_STD_DEV = "process_std_dev" -CONF_STD_DEV = "std_dev" - - -CONFIG_SCHEMA = ( - sensor.sensor_schema(KalmanCombinatorComponent) - .extend(cv.COMPONENT_SCHEMA) - .extend( - { - cv.Required(CONF_PROCESS_STD_DEV): cv.positive_float, - cv.Required(CONF_SOURCES): cv.ensure_list( - cv.Schema( - { - cv.Required(CONF_SOURCE): cv.use_id(sensor.Sensor), - cv.Required(CONF_ERROR): cv.templatable(cv.positive_float), - } - ), - ), - cv.Optional(CONF_STD_DEV): sensor.sensor_schema(), - } - ) -) - -# Inherit some sensor values from the first source, for both the state and the error value -properties_to_inherit = [ - CONF_ACCURACY_DECIMALS, - CONF_DEVICE_CLASS, - CONF_ENTITY_CATEGORY, - CONF_ICON, - CONF_UNIT_OF_MEASUREMENT, - # CONF_STATE_CLASS could also be inherited, but might lead to unexpected behaviour with "total_increasing" -] -inherit_schema_for_state = [ - inherit_property_from(property, [CONF_SOURCES, 0, CONF_SOURCE]) - for property in properties_to_inherit -] -inherit_schema_for_std_dev = [ - inherit_property_from([CONF_STD_DEV, property], [CONF_SOURCES, 0, CONF_SOURCE]) - for property in properties_to_inherit -] - -FINAL_VALIDATE_SCHEMA = cv.All( - CONFIG_SCHEMA.extend( - {cv.Required(CONF_ID): cv.use_id(KalmanCombinatorComponent)}, - extra=cv.ALLOW_EXTRA, - ), - *inherit_schema_for_state, - *inherit_schema_for_std_dev, -) - - -async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - await cg.register_component(var, config) - await sensor.register_sensor(var, config) - - cg.add(var.set_process_std_dev(config[CONF_PROCESS_STD_DEV])) - for source_conf in config[CONF_SOURCES]: - source = await cg.get_variable(source_conf[CONF_SOURCE]) - error = await cg.templatable( - source_conf[CONF_ERROR], - [(float, "x")], - cg.float_, - ) - cg.add(var.add_source(source, error)) - - if CONF_STD_DEV in config: - sens = await sensor.new_sensor(config[CONF_STD_DEV]) - cg.add(var.set_std_dev_sensor(sens)) diff --git a/tests/test1.yaml b/tests/test1.yaml index 038ac9c738..3558fa328e 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -971,7 +971,8 @@ sensor: name: Internal Ttemperature update_interval: 15s i2c_id: i2c_bus - - platform: kalman_combinator + - platform: combination + type: kalman name: Kalman-filtered temperature process_std_dev: 0.00139 sources: @@ -980,6 +981,57 @@ sensor: return 0.4 + std::abs(x - 25) * 0.023; - source: scd4x_temperature error: 1.5 + - platform: combination + type: linear + name: Linearly combined temperatures + sources: + - source: scd30_temperature + coeffecient: !lambda |- + return 0.4 + std::abs(x - 25) * 0.023; + - source: scd4x_temperature + coeffecient: 1.5 + - platform: combination + type: max + name: Max of combined temperatures + sources: + - source: scd30_temperature + - source: scd4x_temperature + - platform: combination + type: mean + name: Mean of combined temperatures + sources: + - source: scd30_temperature + - source: scd4x_temperature + - platform: combination + type: median + name: Median of combined temperatures + sources: + - source: scd30_temperature + - source: scd4x_temperature + - platform: combination + type: min + name: Min of combined temperatures + sources: + - source: scd30_temperature + - source: scd4x_temperature + - platform: combination + type: most_recently_updated + name: Most recently updated of combined temperatures + sources: + - source: scd30_temperature + - source: scd4x_temperature + - platform: combination + type: range + name: Range of combined temperatures + sources: + - source: scd30_temperature + - source: scd4x_temperature + - platform: combination + type: sum + name: Sum of combined temperatures + sources: + - source: scd30_temperature + - source: scd4x_temperature - platform: htu21d temperature: name: Living Room Temperature 6 From ea9de45d164e32227bf4ea8b61bb475fbf426518 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:07:50 +0900 Subject: [PATCH 157/193] Bump platformio from 6.1.11 to 6.1.13 (#6086) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- docker/Dockerfile | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 468124e3ed..b28ca2ba66 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -81,7 +81,7 @@ RUN \ fi; \ pip3 install \ --break-system-packages --no-cache-dir \ - platformio==6.1.11 \ + platformio==6.1.13 \ # Change some platformio settings && platformio settings set enable_telemetry No \ && platformio settings set check_platformio_interval 1000000 \ diff --git a/requirements.txt b/requirements.txt index 5281b64e66..18e0295fb2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ tornado==6.4 tzlocal==5.2 # from time tzdata>=2021.1 # from time pyserial==3.5 -platformio==6.1.11 # When updating platformio, also update Dockerfile +platformio==6.1.13 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 esphome-dashboard==20231107.0 From 6a6a70f1e57055ff5de8cd065fb308927db327e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:08:29 +0900 Subject: [PATCH 158/193] Bump actions/cache from 3.3.2 to 4.0.0 (#6110) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2187573709..2a108b34dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v3.3.2 + uses: actions/cache@v4.0.0 with: path: venv # yamllint disable-line rule:line-length @@ -365,7 +365,7 @@ jobs: python-version: ${{ env.DEFAULT_PYTHON }} cache-key: ${{ needs.common.outputs.cache-key }} - name: Cache platformio - uses: actions/cache@v3.3.2 + uses: actions/cache@v4.0.0 with: path: ~/.platformio # yamllint disable-line rule:line-length From 8267b3274ce2c801000d16eca843a7c89561d811 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:10:23 +1100 Subject: [PATCH 159/193] Enable networking and some other components on host platform (#6114) --- esphome/components/host/__init__.py | 6 +++++- esphome/components/network/ip_address.h | 16 ++++++++++++++++ esphome/components/socket/bsd_sockets_impl.cpp | 2 +- esphome/core/component.cpp | 2 +- esphome/helpers.py | 5 +++++ platformio.ini | 1 + 6 files changed, 29 insertions(+), 3 deletions(-) diff --git a/esphome/components/host/__init__.py b/esphome/components/host/__init__.py index 14d2597866..eb44bcccd6 100644 --- a/esphome/components/host/__init__.py +++ b/esphome/components/host/__init__.py @@ -6,6 +6,7 @@ from esphome.const import ( PLATFORM_HOST, ) from esphome.core import CORE +from esphome.helpers import IS_MACOS import esphome.config_validation as cv import esphome.codegen as cg @@ -14,7 +15,6 @@ from .const import KEY_HOST # force import gpio to register pin schema from .gpio import host_pin_to_code # noqa - CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["network"] @@ -35,5 +35,9 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): cg.add_build_flag("-DUSE_HOST") + cg.add_build_flag("-std=c++17") + cg.add_build_flag("-lsodium") + if IS_MACOS: + cg.add_build_flag("-L/opt/homebrew/lib") cg.add_define("ESPHOME_BOARD", "host") cg.add_platformio_option("platform", "platformio/native") diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index 709524c9d1..02e71790a7 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -14,6 +14,13 @@ #include #endif /* USE_ADRDUINO */ +#ifdef USE_HOST +#include +using ip_addr_t = in_addr; +using ip4_addr_t = in_addr; +#define ipaddr_aton(x, y) inet_aton((x), (y)) +#endif + #if USE_ESP32_FRAMEWORK_ARDUINO #define arduino_ns Arduino_h #elif USE_LIBRETINY @@ -32,6 +39,14 @@ namespace network { struct IPAddress { public: +#ifdef USE_HOST + IPAddress() { ip_addr_.s_addr = 0; } + IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) { + this->ip_addr_.s_addr = htonl((first << 24) | (second << 16) | (third << 8) | fourth); + } + IPAddress(const std::string &in_address) { inet_aton(in_address.c_str(), &ip_addr_); } + IPAddress(const ip_addr_t *other_ip) { ip_addr_ = *other_ip; } +#else IPAddress() { ip_addr_set_zero(&ip_addr_); } IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) { IP_ADDR4(&ip_addr_, first, second, third, fourth); @@ -107,6 +122,7 @@ struct IPAddress { } return *this; } +#endif protected: ip_addr_t ip_addr_; diff --git a/esphome/components/socket/bsd_sockets_impl.cpp b/esphome/components/socket/bsd_sockets_impl.cpp index 6c356106f3..f07f5c8f81 100644 --- a/esphome/components/socket/bsd_sockets_impl.cpp +++ b/esphome/components/socket/bsd_sockets_impl.cpp @@ -87,7 +87,7 @@ class BSDSocketImpl : public Socket { int listen(int backlog) override { return ::listen(fd_, backlog); } ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); } ssize_t recvfrom(void *buf, size_t len, sockaddr *addr, socklen_t *addr_len) override { -#if defined(USE_ESP32) +#if defined(USE_ESP32) || defined(USE_HOST) return ::recvfrom(this->fd_, buf, len, 0, addr, addr_len); #else return ::lwip_recvfrom(this->fd_, buf, len, 0, addr, addr_len); diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index e2f27f9828..b0406e6502 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -169,7 +169,7 @@ float Component::get_actual_setup_priority() const { void Component::set_setup_priority(float priority) { this->setup_priority_override_ = priority; } bool Component::has_overridden_loop() const { -#ifdef CLANG_TIDY +#if defined(USE_HOST) || defined(CLANG_TIDY) bool loop_overridden = true; bool call_loop_overridden = true; #else diff --git a/esphome/helpers.py b/esphome/helpers.py index 254c950b5d..4c8cb4e2cc 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -3,6 +3,7 @@ from contextlib import suppress import logging import os +import platform from pathlib import Path from typing import Union import tempfile @@ -11,6 +12,10 @@ import re _LOGGER = logging.getLogger(__name__) +IS_MACOS = platform.system() == "Darwin" +IS_WINDOWS = platform.system() == "Windows" +IS_LINUX = platform.system() == "Linux" + def ensure_unique_string(preferred_string, current_strings): test_string = preferred_string diff --git a/platformio.ini b/platformio.ini index f5f510244c..e47527fe98 100644 --- a/platformio.ini +++ b/platformio.ini @@ -388,3 +388,4 @@ lib_deps = build_flags = ${common.build_flags} -DUSE_HOST + -std=c++17 From 2283b3b443c024fa4bb5f35d1f7310e5fd1db72a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:46:55 +1100 Subject: [PATCH 160/193] Fix time component for host platform (#6118) --- esphome/components/time/real_time_clock.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index 0573c7de9d..9b903d098b 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -1,6 +1,10 @@ #include "real_time_clock.h" #include "esphome/core/log.h" +#ifdef USE_HOST +#include +#else #include "lwip/opt.h" +#endif #ifdef USE_ESP8266 #include "sys/time.h" #endif @@ -25,7 +29,7 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) { .tv_sec = static_cast(epoch), .tv_usec = 0, }; ESP_LOGVV(TAG, "Got epoch %" PRIu32, epoch); - timezone tz = {0, 0}; + struct timezone tz = {0, 0}; int ret = settimeofday(&timev, &tz); if (ret == EINVAL) { // Some ESP8266 frameworks abort when timezone parameter is not NULL From 1fef769496ed89c0062d8e70f5964b8318ba4550 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:42:17 +1100 Subject: [PATCH 161/193] Add quad spi features (#5925) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/spi/__init__.py | 95 +++++++++++++++++++------- esphome/components/spi/spi.cpp | 6 +- esphome/components/spi/spi.h | 51 ++++++++++++-- esphome/components/spi/spi_arduino.cpp | 3 +- esphome/components/spi/spi_esp_idf.cpp | 83 ++++++++++++++++++++-- tests/test8.1.yaml | 9 +++ 6 files changed, 207 insertions(+), 40 deletions(-) diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index d116641373..10ea906a92 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -29,12 +29,15 @@ from esphome.const import ( PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040, + CONF_ALLOW_OTHER_USES, + CONF_DATA_PINS, ) from esphome.core import coroutine_with_priority, CORE CODEOWNERS = ["@esphome/core", "@clydebarrow"] spi_ns = cg.esphome_ns.namespace("spi") SPIComponent = spi_ns.class_("SPIComponent", cg.Component) +QuadSPIComponent = spi_ns.class_("QuadSPIComponent", cg.Component) SPIDevice = spi_ns.class_("SPIDevice") SPIDataRate = spi_ns.enum("SPIDataRate") SPIMode = spi_ns.enum("SPIMode") @@ -190,12 +193,9 @@ 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 spi[CONF_FORCE_SW]: - if interface == "any": - spi[CONF_INTERFACE] = interface = "software" - elif interface != "software": - raise cv.Invalid("force_sw is deprecated - use interface: software") if interface == "software": pass elif interface == "any": @@ -229,6 +229,8 @@ def validate_spi_config(config): spi, spi[CONF_INTERFACE_INDEX] ): raise cv.Invalid("Invalid pin selections for hardware SPI interface") + if CONF_DATA_PINS in spi and CONF_INTERFACE_INDEX not in spi: + raise cv.Invalid("Quad mode requires a hardware interface") return config @@ -249,14 +251,26 @@ 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): pins.gpio_output_pin_schema, + cv.Required(CONF_CLK_PIN): clk_pin_validator, 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, default=False): cv.boolean, + cv.Optional(CONF_FORCE_SW): cv.invalid( + "force_sw is deprecated - use interface: software" + ), cv.Optional(CONF_INTERFACE, default="any"): cv.one_of( *sum(get_hw_interface_list(), ["software", "hardware", "any"]), lower=True, @@ -267,8 +281,34 @@ SPI_SCHEMA = cv.All( cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]), ) +SPI_QUAD_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(QuadSPIComponent), + cv.Required(CONF_CLK_PIN): clk_pin_validator, + cv.Required(CONF_DATA_PINS): cv.All( + cv.ensure_list(pins.internal_gpio_output_pin_number), + cv.Length(min=4, max=4), + ), + cv.Optional(CONF_INTERFACE, default="hardware"): cv.one_of( + *sum(get_hw_interface_list(), ["hardware"]), + lower=True, + ), + } + ), + cv.only_with_esp_idf, +) + CONFIG_SCHEMA = cv.All( - cv.ensure_list(SPI_SCHEMA), + # Order is important. SPI_SCHEMA is the default. + cv.ensure_list( + cv.Any( + SPI_SCHEMA, + SPI_QUAD_SCHEMA, + msg="Standard SPI requires mosi_pin and/or miso_pin; quad SPI requires data_pins only." + + " A clock pin is always required", + ), + ), validate_spi_config, ) @@ -277,43 +317,46 @@ CONFIG_SCHEMA = cv.All( async def to_code(configs): cg.add_define("USE_SPI") cg.add_global(spi_ns.using) + if CORE.using_arduino: + cg.add_library("SPI", None) for spi in configs: var = cg.new_Pvariable(spi[CONF_ID]) await cg.register_component(var, spi) - clk = await cg.gpio_pin_expression(spi[CONF_CLK_PIN]) cg.add(var.set_clk(clk)) - if CONF_MISO_PIN in spi: - miso = await cg.gpio_pin_expression(spi[CONF_MISO_PIN]) - cg.add(var.set_miso(miso)) - if CONF_MOSI_PIN in spi: - mosi = await cg.gpio_pin_expression(spi[CONF_MOSI_PIN]) - cg.add(var.set_mosi(mosi)) - if CONF_INTERFACE_INDEX in spi: - index = spi[CONF_INTERFACE_INDEX] - cg.add(var.set_interface(cg.RawExpression(get_spi_interface(index)))) + if miso := spi.get(CONF_MISO_PIN): + cg.add(var.set_miso(await cg.gpio_pin_expression(miso))) + if mosi := spi.get(CONF_MOSI_PIN): + cg.add(var.set_mosi(await cg.gpio_pin_expression(mosi))) + if data_pins := spi.get(CONF_DATA_PINS): + cg.add(var.set_data_pins(data_pins)) + if (index := spi.get(CONF_INTERFACE_INDEX)) is not None: + interface = get_spi_interface(index) + cg.add(var.set_interface(cg.RawExpression(interface))) cg.add( var.set_interface_name( - re.sub( - r"\W", "", get_spi_interface(index).replace("new SPIClass", "") - ) + re.sub(r"\W", "", interface.replace("new SPIClass", "")) ) ) - if CORE.using_arduino: - cg.add_library("SPI", None) - def spi_device_schema( - cs_pin_required=True, default_data_rate=cv.UNDEFINED, default_mode=cv.UNDEFINED + cs_pin_required=True, + default_data_rate=cv.UNDEFINED, + default_mode=cv.UNDEFINED, + quad=False, ): """Create a schema for an SPI device. :param cs_pin_required: If true, make the CS_PIN required in the config. :param default_data_rate: Optional data_rate to use as default + :param default_mode Optional. The default SPI mode to use. + :param quad If set, will require an SPI component configured as quad data bits. :return: The SPI device schema, `extend` this in your config schema. """ schema = { - cv.GenerateID(CONF_SPI_ID): cv.use_id(SPIComponent), + cv.GenerateID(CONF_SPI_ID): cv.use_id( + QuadSPIComponent if quad else SPIComponent + ), cv.Optional(CONF_DATA_RATE, default=default_data_rate): SPI_DATA_RATE_SCHEMA, cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum( SPI_MODE_OPTIONS, upper=True diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 9d06ac0e45..b13826c443 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -49,7 +49,8 @@ void SPIComponent::setup() { } if (this->using_hw_) { - this->spi_bus_ = SPIComponent::get_bus(this->interface_, this->clk_pin_, this->sdo_pin_, this->sdi_pin_); + this->spi_bus_ = + SPIComponent::get_bus(this->interface_, this->clk_pin_, this->sdo_pin_, this->sdi_pin_, this->data_pins_); if (this->spi_bus_ == nullptr) { ESP_LOGE(TAG, "Unable to allocate SPI interface"); this->mark_failed(); @@ -68,6 +69,9 @@ void SPIComponent::dump_config() { LOG_PIN(" CLK Pin: ", this->clk_pin_) LOG_PIN(" SDI Pin: ", this->sdi_pin_) LOG_PIN(" SDO Pin: ", this->sdo_pin_) + for (size_t i = 0; i != this->data_pins_.size(); i++) { + ESP_LOGCONFIG(TAG, " Data pin %u: GPIO%d", i, this->data_pins_[i]); + } if (this->spi_bus_->is_hw()) { ESP_LOGCONFIG(TAG, " Using HW SPI: %s", this->interface_name_); } else { diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 0eb4cd7eb6..f581dc3f56 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -1,11 +1,12 @@ #pragma once +#include "esphome/core/application.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" -#include "esphome/core/application.h" -#include #include +#include +#include #ifdef USE_ARDUINO @@ -208,6 +209,10 @@ class SPIDelegate { esph_log_e("spi_device", "variable length write not implemented"); } + virtual void write_cmd_addr_data(size_t cmd_bits, uint32_t cmd, size_t addr_bits, uint32_t address, + const uint8_t *data, size_t length, uint8_t bus_width) { + esph_log_e("spi_device", "write_cmd_addr_data not implemented"); + } // write 16 bits virtual void write16(uint16_t data) { if (this->bit_order_ == BIT_ORDER_MSB_FIRST) { @@ -331,6 +336,7 @@ class SPIComponent : public Component { void set_miso(GPIOPin *sdi) { this->sdi_pin_ = sdi; } void set_mosi(GPIOPin *sdo) { this->sdo_pin_ = sdo; } + void set_data_pins(std::vector pins) { this->data_pins_ = std::move(pins); } void set_interface(SPIInterface interface) { this->interface_ = interface; @@ -348,15 +354,19 @@ class SPIComponent : public Component { GPIOPin *clk_pin_{nullptr}; GPIOPin *sdi_pin_{nullptr}; GPIOPin *sdo_pin_{nullptr}; + std::vector data_pins_{}; + SPIInterface interface_{}; bool using_hw_{false}; const char *interface_name_{nullptr}; SPIBus *spi_bus_{}; std::map devices_; - static SPIBus *get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi); + static SPIBus *get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, + const std::vector &data_pins); }; +using QuadSPIComponent = SPIComponent; /** * Base class for SPIDevice, un-templated. */ @@ -422,18 +432,49 @@ class SPIDevice : public SPIClient { void read_array(uint8_t *data, size_t length) { return this->delegate_->read_array(data, length); } + /** + * Write a single data item, up to 32 bits. + * @param data The data + * @param num_bits The number of bits to write. The lower num_bits of data will be sent. + */ void write(uint16_t data, size_t num_bits) { this->delegate_->write(data, num_bits); }; + /* Write command, address and data. Command and address will be written as single-bit SPI, + * data phase can be multiple bit (currently only 1 or 4) + * @param cmd_bits Number of bits to write in the command phase + * @param cmd The command value to write + * @param addr_bits Number of bits to write in addr phase + * @param address Address data + * @param data Plain data bytes + * @param length Number of data bytes + * @param bus_width The number of data lines to use for the data phase. + */ + void write_cmd_addr_data(size_t cmd_bits, uint32_t cmd, size_t addr_bits, uint32_t address, const uint8_t *data, + size_t length, uint8_t bus_width = 1) { + this->delegate_->write_cmd_addr_data(cmd_bits, cmd, addr_bits, address, data, length, bus_width); + } + void write_byte(uint8_t data) { this->delegate_->write_array(&data, 1); } + /** + * Write the array data, replace with received data. + * @param data + * @param length + */ void transfer_array(uint8_t *data, size_t length) { this->delegate_->transfer(data, length); } uint8_t transfer_byte(uint8_t data) { return this->delegate_->transfer(data); } - // the driver will byte-swap if required. + /** Write 16 bit data. The driver will byte-swap if required. + */ void write_byte16(uint16_t data) { this->delegate_->write16(data); } - // avoid use of this if possible. It's inefficient and ugly. + /** + * Write an array of data as 16 bit values, byte-swapping if required. Use of this should be avoided as + * it is horribly slow. + * @param data + * @param length + */ void write_array16(const uint16_t *data, size_t length) { this->delegate_->write_array16(data, length); } void enable() { this->delegate_->begin_transaction(); } diff --git a/esphome/components/spi/spi_arduino.cpp b/esphome/components/spi/spi_arduino.cpp index 4628486550..f7fe523a33 100644 --- a/esphome/components/spi/spi_arduino.cpp +++ b/esphome/components/spi/spi_arduino.cpp @@ -85,7 +85,8 @@ class SPIBusHw : public SPIBus { bool is_hw() override { return true; } }; -SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi) { +SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, + const std::vector &data_pins) { return new SPIBusHw(clk, sdo, sdi, interface); } diff --git a/esphome/components/spi/spi_esp_idf.cpp b/esphome/components/spi/spi_esp_idf.cpp index 03ab298019..55680f72d3 100644 --- a/esphome/components/spi/spi_esp_idf.cpp +++ b/esphome/components/spi/spi_esp_idf.cpp @@ -104,6 +104,60 @@ class SPIDelegateHw : public SPIDelegate { } } + /** + * Write command, address and data + * @param cmd_bits Number of bits to write in the command phase + * @param cmd The command value to write + * @param addr_bits Number of bits to write in addr phase + * @param address Address data + * @param data Remaining data bytes + * @param length Number of data bytes + * @param bus_width The number of data lines to use + */ + void write_cmd_addr_data(size_t cmd_bits, uint32_t cmd, size_t addr_bits, uint32_t address, const uint8_t *data, + size_t length, uint8_t bus_width) override { + spi_transaction_ext_t desc = {}; + if (length == 0 && cmd_bits == 0 && addr_bits == 0) { + esph_log_w(TAG, "Nothing to transfer"); + return; + } + desc.base.flags = SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_DUMMY; + if (bus_width == 4) { + desc.base.flags |= SPI_TRANS_MODE_QIO; + } else if (bus_width == 8) { + desc.base.flags |= SPI_TRANS_MODE_OCT; + } + desc.command_bits = cmd_bits; + desc.address_bits = addr_bits; + desc.dummy_bits = 0; + desc.base.rxlength = 0; + desc.base.cmd = cmd; + desc.base.addr = address; + do { + size_t chunk_size = std::min(length, MAX_TRANSFER_SIZE); + if (data != nullptr && chunk_size != 0) { + desc.base.length = chunk_size * 8; + desc.base.tx_buffer = data; + length -= chunk_size; + data += chunk_size; + } else { + length = 0; + desc.base.length = 0; + } + esp_err_t err = spi_device_polling_start(this->handle_, (spi_transaction_t *) &desc, portMAX_DELAY); + if (err == ESP_OK) { + err = spi_device_polling_end(this->handle_, portMAX_DELAY); + } + if (err != ESP_OK) { + ESP_LOGE(TAG, "Transmit failed - err %X", err); + return; + } + // if more data is to be sent, skip the command and address phases. + desc.command_bits = 0; + desc.address_bits = 0; + } while (length != 0); + } + void transfer(uint8_t *ptr, size_t length) override { this->transfer(ptr, ptr, length); } uint8_t transfer(uint8_t data) override { @@ -142,13 +196,27 @@ class SPIDelegateHw : public SPIDelegate { class SPIBusHw : public SPIBus { public: - SPIBusHw(GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, SPIInterface channel) : SPIBus(clk, sdo, sdi), channel_(channel) { + SPIBusHw(GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, SPIInterface channel, std::vector data_pins) + : SPIBus(clk, sdo, sdi), channel_(channel) { spi_bus_config_t buscfg = {}; - buscfg.mosi_io_num = Utility::get_pin_no(sdo); - buscfg.miso_io_num = Utility::get_pin_no(sdi); buscfg.sclk_io_num = Utility::get_pin_no(clk); - buscfg.quadwp_io_num = -1; - buscfg.quadhd_io_num = -1; + buscfg.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK; + if (data_pins.empty()) { + buscfg.mosi_io_num = Utility::get_pin_no(sdo); + buscfg.miso_io_num = Utility::get_pin_no(sdi); + buscfg.quadwp_io_num = -1; + buscfg.quadhd_io_num = -1; + } else { + buscfg.data0_io_num = data_pins[0]; + buscfg.data1_io_num = data_pins[1]; + buscfg.data2_io_num = data_pins[2]; + buscfg.data3_io_num = data_pins[3]; + buscfg.data4_io_num = -1; + buscfg.data5_io_num = -1; + buscfg.data6_io_num = -1; + buscfg.data7_io_num = -1; + buscfg.flags |= SPICOMMON_BUSFLAG_QUAD; + } buscfg.max_transfer_sz = MAX_TRANSFER_SIZE; auto err = spi_bus_initialize(channel, &buscfg, SPI_DMA_CH_AUTO); if (err != ESP_OK) @@ -166,8 +234,9 @@ class SPIBusHw : public SPIBus { bool is_hw() override { return true; } }; -SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi) { - return new SPIBusHw(clk, sdo, sdi, interface); +SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, + const std::vector &data_pins) { + return new SPIBusHw(clk, sdo, sdi, interface, data_pins); } #endif diff --git a/tests/test8.1.yaml b/tests/test8.1.yaml index bc1d2e22a4..839b1f3e6e 100644 --- a/tests/test8.1.yaml +++ b/tests/test8.1.yaml @@ -28,6 +28,15 @@ spi: allow_other_uses: false mosi_pin: GPIO6 interface: any + - id: quad_spi + clk_pin: 47 + data_pins: + - + number: 40 + allow_other_uses: false + - 41 + - 42 + - 43 spi_device: id: spidev From 6561746f97c8473c5ec4a1e65bca337a4ea736d7 Mon Sep 17 00:00:00 2001 From: alexbuit Date: Fri, 19 Jan 2024 03:50:00 +0100 Subject: [PATCH 162/193] add AM2120 device type (#6115) --- esphome/components/dht/dht.cpp | 2 +- esphome/components/dht/dht.h | 2 ++ esphome/components/dht/sensor.py | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 07634cafdf..5112092073 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -91,7 +91,7 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r delayMicroseconds(40); } else if (this->model_ == DHT_MODEL_DHT22_TYPE2) { delayMicroseconds(2000); - } else if (this->model_ == DHT_MODEL_AM2302) { + } else if (this->model_ == DHT_MODEL_AM2120 || this->model_ == DHT_MODEL_AM2302) { delayMicroseconds(1000); } else { delayMicroseconds(800); diff --git a/esphome/components/dht/dht.h b/esphome/components/dht/dht.h index f3a29f9ce9..327e8a4f5c 100644 --- a/esphome/components/dht/dht.h +++ b/esphome/components/dht/dht.h @@ -11,6 +11,7 @@ enum DHTModel { DHT_MODEL_AUTO_DETECT = 0, DHT_MODEL_DHT11, DHT_MODEL_DHT22, + DHT_MODEL_AM2120, DHT_MODEL_AM2302, DHT_MODEL_RHT03, DHT_MODEL_SI7021, @@ -27,6 +28,7 @@ class DHT : public PollingComponent { * - DHT_MODEL_AUTO_DETECT (default) * - DHT_MODEL_DHT11 * - DHT_MODEL_DHT22 + * - DHT_MODEL_AM2120 * - DHT_MODEL_AM2302 * - DHT_MODEL_RHT03 * - DHT_MODEL_SI7021 diff --git a/esphome/components/dht/sensor.py b/esphome/components/dht/sensor.py index cd1886728e..da92a97e1f 100644 --- a/esphome/components/dht/sensor.py +++ b/esphome/components/dht/sensor.py @@ -23,6 +23,7 @@ DHT_MODELS = { "AUTO_DETECT": DHTModel.DHT_MODEL_AUTO_DETECT, "DHT11": DHTModel.DHT_MODEL_DHT11, "DHT22": DHTModel.DHT_MODEL_DHT22, + "AM2120": DHTModel.DHT_MODEL_AM2120, "AM2302": DHTModel.DHT_MODEL_AM2302, "RHT03": DHTModel.DHT_MODEL_RHT03, "SI7021": DHTModel.DHT_MODEL_SI7021, From ed771abc8aff491cae27bade58aa847a42acdd74 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:10:53 +1100 Subject: [PATCH 163/193] Add support for Waveshare EPD 2.13" V3 (#5363) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + .../components/waveshare_epaper/__init__.py | 1 + .../components/waveshare_epaper/display.py | 4 + .../waveshare_epaper/waveshare_213v3.cpp | 186 ++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.cpp | 24 ++- .../waveshare_epaper/waveshare_epaper.h | 37 +++- tests/test4.yaml | 19 +- 7 files changed, 264 insertions(+), 8 deletions(-) create mode 100644 esphome/components/waveshare_epaper/waveshare_213v3.cpp diff --git a/CODEOWNERS b/CODEOWNERS index 95e3b35f56..db44317776 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -367,6 +367,7 @@ esphome/components/veml3235/* @kbx81 esphome/components/version/* @esphome/core esphome/components/voice_assistant/* @jesserockz esphome/components/wake_on_lan/* @willwill2will54 +esphome/components/waveshare_epaper/* @clydebarrow esphome/components/web_server_base/* @OttoWinter esphome/components/web_server_idf/* @dentra esphome/components/whirlpool/* @glmnet diff --git a/esphome/components/waveshare_epaper/__init__.py b/esphome/components/waveshare_epaper/__init__.py index e69de29bb2..c58ce8a01e 100644 --- a/esphome/components/waveshare_epaper/__init__.py +++ b/esphome/components/waveshare_epaper/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@clydebarrow"] diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 1dd4b7fc54..1645ce0b1d 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -72,6 +72,9 @@ WaveshareEPaper7P5InHDB = waveshare_epaper_ns.class_( WaveshareEPaper2P13InDKE = waveshare_epaper_ns.class_( "WaveshareEPaper2P13InDKE", WaveshareEPaper ) +WaveshareEPaper2P13InV3 = waveshare_epaper_ns.class_( + "WaveshareEPaper2P13InV3", WaveshareEPaper +) GDEW0154M09 = waveshare_epaper_ns.class_("GDEW0154M09", WaveshareEPaper) WaveshareEPaperTypeAModel = waveshare_epaper_ns.enum("WaveshareEPaperTypeAModel") @@ -104,6 +107,7 @@ MODELS = { "7.50inv2alt": ("b", WaveshareEPaper7P5InV2alt), "7.50in-hd-b": ("b", WaveshareEPaper7P5InHDB), "2.13in-ttgo-dke": ("c", WaveshareEPaper2P13InDKE), + "2.13inv3": ("c", WaveshareEPaper2P13InV3), "1.54in-m5coreink-m09": ("c", GDEW0154M09), } diff --git a/esphome/components/waveshare_epaper/waveshare_213v3.cpp b/esphome/components/waveshare_epaper/waveshare_213v3.cpp new file mode 100644 index 0000000000..196aeed3f7 --- /dev/null +++ b/esphome/components/waveshare_epaper/waveshare_213v3.cpp @@ -0,0 +1,186 @@ +#include "waveshare_epaper.h" +#include "esphome/core/log.h" +#include "esphome/core/application.h" + +namespace esphome { +namespace waveshare_epaper { + +static const char *const TAG = "waveshare_2.13v3"; + +static const uint8_t PARTIAL_LUT[] = { + 0x32, // cmd + 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, +}; + +static const uint8_t FULL_LUT[] = { + 0x32, // CMD + 0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x4A, 0x80, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0xF, 0x0, 0x0, 0x2, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, +}; + +static const uint8_t SW_RESET = 0x12; +static const uint8_t ACTIVATE = 0x20; +static const uint8_t WRITE_BUFFER = 0x24; +static const uint8_t WRITE_BASE = 0x26; + +static const uint8_t DRV_OUT_CTL[] = {0x01, 0x27, 0x01, 0x00}; // driver output control +static const uint8_t GATEV[] = {0x03, 0x17}; +static const uint8_t SRCV[] = {0x04, 0x41, 0x0C, 0x32}; +static const uint8_t SLEEP[] = {0x10, 0x01}; +static const uint8_t DATA_ENTRY[] = {0x11, 0x03}; // data entry mode +static const uint8_t TEMP_SENS[] = {0x18, 0x80}; // Temp sensor +static const uint8_t DISPLAY_UPDATE[] = {0x21, 0x00, 0x80}; // Display update control +static const uint8_t UPSEQ[] = {0x22, 0xC0}; +static const uint8_t ON_FULL[] = {0x22, 0xC7}; +static const uint8_t ON_PARTIAL[] = {0x22, 0x0F}; +static const uint8_t VCOM[] = {0x2C, 0x36}; +static const uint8_t CMD5[] = {0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00}; +static const uint8_t BORDER_PART[] = {0x3C, 0x80}; // border waveform +static const uint8_t BORDER_FULL[] = {0x3C, 0x05}; // border waveform +static const uint8_t CMD1[] = {0x3F, 0x22}; +static const uint8_t RAM_X_START[] = {0x44, 0x00, 121 / 8}; // set ram_x_address_start_end +static const uint8_t RAM_Y_START[] = {0x45, 0x00, 0x00, 250 - 1, 0}; // set ram_y_address_start_end +static const uint8_t RAM_X_POS[] = {0x4E, 0x00}; // set ram_x_address_counter +// static const uint8_t RAM_Y_POS[] = {0x4F, 0x00, 0x00}; // set ram_y_address_counter +#define SEND(x) this->cmd_data(x, sizeof(x)) + +void WaveshareEPaper2P13InV3::write_lut_(const uint8_t *lut) { + this->wait_until_idle_(); + this->cmd_data(lut, sizeof(PARTIAL_LUT)); + SEND(CMD1); + SEND(GATEV); + SEND(SRCV); + SEND(VCOM); +} + +// write the buffer starting on line top, up to line bottom. +void WaveshareEPaper2P13InV3::write_buffer_(uint8_t cmd, int top, int bottom) { + this->wait_until_idle_(); + this->set_window_(top, bottom); + this->command(cmd); + this->start_data_(); + auto width_bytes = this->get_width_internal() / 8; + this->write_array(this->buffer_ + top * width_bytes, (bottom - top) * width_bytes); + this->end_data_(); +} + +void WaveshareEPaper2P13InV3::send_reset_() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->digital_write(false); + delay(2); + this->reset_pin_->digital_write(true); + } +} + +void WaveshareEPaper2P13InV3::setup() { + setup_pins_(); + delay(20); + this->send_reset_(); + // as a one-off delay this is not worth working around. + delay(100); // NOLINT + this->wait_until_idle_(); + this->command(SW_RESET); + this->wait_until_idle_(); + + SEND(DRV_OUT_CTL); + SEND(DATA_ENTRY); + SEND(CMD5); + this->set_window_(0, this->get_height_internal()); + SEND(BORDER_FULL); + SEND(DISPLAY_UPDATE); + SEND(TEMP_SENS); + this->wait_until_idle_(); + this->write_lut_(FULL_LUT); +} + +// t and b are y positions, i.e. line numbers. +void WaveshareEPaper2P13InV3::set_window_(int t, int b) { + uint8_t buffer[3]; + + SEND(RAM_X_START); + SEND(RAM_Y_START); + SEND(RAM_X_POS); + buffer[0] = 0x4F; + buffer[1] = (uint8_t) t; + buffer[2] = (uint8_t) (t >> 8); + SEND(buffer); +} + +// must implement, but we override setup to have more control +void WaveshareEPaper2P13InV3::initialize() {} + +void WaveshareEPaper2P13InV3::partial_update_() { + this->send_reset_(); + this->set_timeout(100, [this] { + this->write_lut_(PARTIAL_LUT); + SEND(BORDER_PART); + SEND(UPSEQ); + this->command(ACTIVATE); + this->set_timeout(100, [this] { + this->wait_until_idle_(); + this->write_buffer_(WRITE_BUFFER, 0, this->get_height_internal()); + SEND(ON_PARTIAL); + this->command(ACTIVATE); // Activate Display Update Sequence + this->is_busy_ = false; + }); + }); +} + +void WaveshareEPaper2P13InV3::full_update_() { + ESP_LOGI(TAG, "Performing full e-paper update."); + this->write_lut_(FULL_LUT); + this->write_buffer_(WRITE_BUFFER, 0, this->get_height_internal()); + this->write_buffer_(WRITE_BASE, 0, this->get_height_internal()); + SEND(ON_FULL); + this->command(ACTIVATE); // don't wait here + this->is_busy_ = false; +} + +void WaveshareEPaper2P13InV3::display() { + if (this->is_busy_ || (this->busy_pin_ != nullptr && this->busy_pin_->digital_read())) + return; + this->is_busy_ = true; + const bool partial = this->at_update_ != 0; + this->at_update_ = (this->at_update_ + 1) % this->full_update_every_; + if (partial) { + this->partial_update_(); + } else { + this->full_update_(); + } +} + +int WaveshareEPaper2P13InV3::get_width_internal() { return 128; } + +int WaveshareEPaper2P13InV3::get_height_internal() { return 250; } + +uint32_t WaveshareEPaper2P13InV3::idle_timeout_() { return 5000; } + +void WaveshareEPaper2P13InV3::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this) + ESP_LOGCONFIG(TAG, " Model: 2.13inV3"); + LOG_PIN(" CS Pin: ", this->cs_) + LOG_PIN(" Reset Pin: ", this->reset_pin_) + LOG_PIN(" DC Pin: ", this->dc_pin_) + LOG_PIN(" Busy Pin: ", this->busy_pin_) + LOG_UPDATE_INTERVAL(this) +} + +void WaveshareEPaper2P13InV3::set_full_update_every(uint32_t full_update_every) { + this->full_update_every_ = full_update_every; +} + +} // namespace waveshare_epaper +} // namespace esphome diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 244b3b1ce2..b0946ad9d0 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -109,8 +109,20 @@ void WaveshareEPaper::data(uint8_t value) { this->write_byte(value); this->end_data_(); } + +// write a command followed by one or more bytes of data. +// The command is the first byte, length is the total including cmd. +void WaveshareEPaper::cmd_data(const uint8_t *c_data, size_t length) { + this->dc_pin_->digital_write(false); + this->enable(); + this->write_byte(c_data[0]); + this->dc_pin_->digital_write(true); + this->write_array(c_data + 1, length - 1); + this->disable(); +} + bool WaveshareEPaper::wait_until_idle_() { - if (this->busy_pin_ == nullptr) { + if (this->busy_pin_ == nullptr || !this->busy_pin_->digital_read()) { return true; } @@ -120,7 +132,7 @@ bool WaveshareEPaper::wait_until_idle_() { ESP_LOGE(TAG, "Timeout while displaying image!"); return false; } - delay(10); + delay(1); } return true; } @@ -2218,8 +2230,9 @@ void HOT WaveshareEPaper2P13InDKE::display() { } else { // set up partial update this->command(0x32); - for (uint8_t v : PART_UPDATE_LUT_TTGO_DKE) - this->data(v); + this->start_data_(); + this->write_array(PART_UPDATE_LUT_TTGO_DKE, sizeof(PART_UPDATE_LUT_TTGO_DKE)); + this->end_data_(); this->command(0x3F); this->data(0x22); @@ -2264,12 +2277,10 @@ void HOT WaveshareEPaper2P13InDKE::display() { this->wait_until_idle_(); // data must be sent again on partial update - delay(300); // NOLINT this->command(0x24); this->start_data_(); this->write_array(this->buffer_, this->get_buffer_length_()); this->end_data_(); - delay(300); // NOLINT } ESP_LOGI(TAG, "Completed e-paper update."); @@ -2281,6 +2292,7 @@ uint32_t WaveshareEPaper2P13InDKE::idle_timeout_() { return 5000; } void WaveshareEPaper2P13InDKE::dump_config() { LOG_DISPLAY("", "Waveshare E-Paper", this); ESP_LOGCONFIG(TAG, " Model: 2.13inDKE"); + LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Busy Pin: ", this->busy_pin_); diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index ee9443e8be..0f1144ccba 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -19,6 +19,7 @@ class WaveshareEPaper : public display::DisplayBuffer, void command(uint8_t value); void data(uint8_t value); + void cmd_data(const uint8_t *data, size_t length); virtual void display() = 0; virtual void initialize() = 0; @@ -49,7 +50,7 @@ class WaveshareEPaper : public display::DisplayBuffer, this->reset_pin_->digital_write(false); delay(reset_duration_); // NOLINT this->reset_pin_->digital_write(true); - delay(200); // NOLINT + delay(20); } } @@ -614,5 +615,39 @@ class WaveshareEPaper2P13InDKE : public WaveshareEPaper { uint32_t at_update_{0}; }; +class WaveshareEPaper2P13InV3 : public WaveshareEPaper { + public: + void display() override; + + void dump_config() override; + + void deep_sleep() override { + // COMMAND POWER DOWN + this->command(0x10); + this->data(0x01); + // cannot wait until idle here, the device no longer responds + } + + void set_full_update_every(uint32_t full_update_every); + + void setup() override; + void initialize() override; + + protected: + int get_width_internal() override; + int get_height_internal() override; + uint32_t idle_timeout_() override; + + void write_buffer_(uint8_t cmd, int top, int bottom); + void set_window_(int t, int b); + void send_reset_(); + void partial_update_(); + void full_update_(); + + uint32_t full_update_every_{30}; + uint32_t at_update_{0}; + bool is_busy_{false}; + void write_lut_(const uint8_t *lut); +}; } // namespace waveshare_epaper } // namespace esphome diff --git a/tests/test4.yaml b/tests/test4.yaml index 089caf073b..65068871dd 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -693,7 +693,6 @@ display: greyscale: false partial_updating: false update_interval: 60s - display_data_1_pin: number: GPIO5 allow_other_uses: true @@ -742,6 +741,24 @@ display: vcom_pin: number: GPIO1 allow_other_uses: true + - platform: waveshare_epaper + spi_id: spi_id_1 + cs_pin: + number: GPIO23 + allow_other_uses: true + dc_pin: + number: GPIO23 + allow_other_uses: true + busy_pin: + number: GPIO23 + allow_other_uses: true + reset_pin: + number: GPIO23 + allow_other_uses: true + model: 2.13inv3 + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); number: - platform: tuya From 6a8da17ea39146e5aedacdcc051fca1fd7c1cd51 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Fri, 19 Jan 2024 05:18:06 +0100 Subject: [PATCH 164/193] OTA 2 which confirm each written chunk (#6066) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/ota/__init__.py | 3 ++ esphome/components/ota/ota_component.cpp | 16 ++++++-- esphome/components/ota/ota_component.h | 47 +++++++++++----------- esphome/core/defines.h | 1 + esphome/espota2.py | 51 +++++++++++++----------- tests/test3.1.yaml | 1 + 6 files changed, 70 insertions(+), 49 deletions(-) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 039596d897..3c845490dc 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -12,6 +12,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_OTA, KEY_PAST_SAFE_MODE, + CONF_VERSION, ) from esphome.core import CORE, coroutine_with_priority @@ -41,6 +42,7 @@ CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(OTAComponent), cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean, + cv.Optional(CONF_VERSION, default=2): cv.one_of(1, 2, int=True), cv.SplitDefault( CONF_PORT, esp8266=8266, @@ -93,6 +95,7 @@ async def to_code(config): if CONF_PASSWORD in config: cg.add(var.set_auth_password(config[CONF_PASSWORD])) cg.add_define("USE_OTA_PASSWORD") + cg.add_define("USE_OTA_VERSION", config[CONF_VERSION]) await cg.register_component(var, config) diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 41cf333be9..15af14ff1a 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -20,8 +20,7 @@ namespace esphome { namespace ota { static const char *const TAG = "ota"; - -static const uint8_t OTA_VERSION_1_0 = 1; +static constexpr u_int16_t OTA_BLOCK_SIZE = 8192; OTAComponent *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) @@ -101,6 +100,7 @@ void OTAComponent::dump_config() { ESP_LOGCONFIG(TAG, " Using Password."); } #endif + ESP_LOGCONFIG(TAG, " OTA version: %d.", USE_OTA_VERSION); if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1 && this->safe_mode_rtc_value_ != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) { ESP_LOGW(TAG, "Last Boot was an unhandled reset, will proceed to safe mode in %" PRIu32 " restarts", @@ -132,6 +132,9 @@ void OTAComponent::handle_() { uint8_t ota_features; std::unique_ptr backend; (void) ota_features; +#if USE_OTA_VERSION == 2 + size_t size_acknowledged = 0; +#endif if (client_ == nullptr) { struct sockaddr_storage source_addr; @@ -168,7 +171,7 @@ void OTAComponent::handle_() { // Send OK and version - 2 bytes buf[0] = OTA_RESPONSE_OK; - buf[1] = OTA_VERSION_1_0; + buf[1] = USE_OTA_VERSION; this->writeall_(buf, 2); backend = make_ota_backend(); @@ -312,6 +315,13 @@ void OTAComponent::handle_() { goto error; // NOLINT(cppcoreguidelines-avoid-goto) } total += read; +#if USE_OTA_VERSION == 2 + while (size_acknowledged + OTA_BLOCK_SIZE <= total || (total == ota_size && size_acknowledged < ota_size)) { + buf[0] = OTA_RESPONSE_CHUNK_OK; + this->writeall_(buf, 1); + size_acknowledged += OTA_BLOCK_SIZE; + } +#endif uint32_t now = millis(); if (now - last_progress > 1000) { diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index 50d095be6c..c20f4f0709 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -10,31 +10,32 @@ namespace esphome { namespace ota { enum OTAResponseTypes { - OTA_RESPONSE_OK = 0, - OTA_RESPONSE_REQUEST_AUTH = 1, + OTA_RESPONSE_OK = 0x00, + OTA_RESPONSE_REQUEST_AUTH = 0x01, - OTA_RESPONSE_HEADER_OK = 64, - OTA_RESPONSE_AUTH_OK = 65, - OTA_RESPONSE_UPDATE_PREPARE_OK = 66, - OTA_RESPONSE_BIN_MD5_OK = 67, - OTA_RESPONSE_RECEIVE_OK = 68, - OTA_RESPONSE_UPDATE_END_OK = 69, - OTA_RESPONSE_SUPPORTS_COMPRESSION = 70, + OTA_RESPONSE_HEADER_OK = 0x40, + OTA_RESPONSE_AUTH_OK = 0x41, + OTA_RESPONSE_UPDATE_PREPARE_OK = 0x42, + OTA_RESPONSE_BIN_MD5_OK = 0x43, + OTA_RESPONSE_RECEIVE_OK = 0x44, + OTA_RESPONSE_UPDATE_END_OK = 0x45, + OTA_RESPONSE_SUPPORTS_COMPRESSION = 0x46, + OTA_RESPONSE_CHUNK_OK = 0x47, - OTA_RESPONSE_ERROR_MAGIC = 128, - OTA_RESPONSE_ERROR_UPDATE_PREPARE = 129, - OTA_RESPONSE_ERROR_AUTH_INVALID = 130, - OTA_RESPONSE_ERROR_WRITING_FLASH = 131, - OTA_RESPONSE_ERROR_UPDATE_END = 132, - OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING = 133, - OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = 134, - OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 135, - OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 136, - OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 137, - OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = 138, - OTA_RESPONSE_ERROR_MD5_MISMATCH = 139, - OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = 140, - OTA_RESPONSE_ERROR_UNKNOWN = 255, + OTA_RESPONSE_ERROR_MAGIC = 0x80, + OTA_RESPONSE_ERROR_UPDATE_PREPARE = 0x81, + OTA_RESPONSE_ERROR_AUTH_INVALID = 0x82, + OTA_RESPONSE_ERROR_WRITING_FLASH = 0x83, + OTA_RESPONSE_ERROR_UPDATE_END = 0x84, + OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING = 0x85, + OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = 0x86, + OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 0x87, + OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 0x88, + OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 0x89, + OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = 0x8A, + OTA_RESPONSE_ERROR_MD5_MISMATCH = 0x8B, + OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = 0x8C, + OTA_RESPONSE_ERROR_UNKNOWN = 0xFF, }; enum OTAState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; diff --git a/esphome/core/defines.h b/esphome/core/defines.h index e75abdb88f..75ed24ddfe 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -37,6 +37,7 @@ #define USE_OTA #define USE_OTA_PASSWORD #define USE_OTA_STATE_CALLBACK +#define USE_OTA_VERSION 1 #define USE_OUTPUT #define USE_POWER_SUPPLY #define USE_QR_CODE diff --git a/esphome/espota2.py b/esphome/espota2.py index dbf48a989a..cdf6d7df32 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -12,32 +12,34 @@ import time from esphome.core import EsphomeError from esphome.helpers import is_ip_address, resolve_ip_address -RESPONSE_OK = 0 -RESPONSE_REQUEST_AUTH = 1 +RESPONSE_OK = 0x00 +RESPONSE_REQUEST_AUTH = 0x01 -RESPONSE_HEADER_OK = 64 -RESPONSE_AUTH_OK = 65 -RESPONSE_UPDATE_PREPARE_OK = 66 -RESPONSE_BIN_MD5_OK = 67 -RESPONSE_RECEIVE_OK = 68 -RESPONSE_UPDATE_END_OK = 69 -RESPONSE_SUPPORTS_COMPRESSION = 70 +RESPONSE_HEADER_OK = 0x40 +RESPONSE_AUTH_OK = 0x41 +RESPONSE_UPDATE_PREPARE_OK = 0x42 +RESPONSE_BIN_MD5_OK = 0x43 +RESPONSE_RECEIVE_OK = 0x44 +RESPONSE_UPDATE_END_OK = 0x45 +RESPONSE_SUPPORTS_COMPRESSION = 0x46 +RESPONSE_CHUNK_OK = 0x47 -RESPONSE_ERROR_MAGIC = 128 -RESPONSE_ERROR_UPDATE_PREPARE = 129 -RESPONSE_ERROR_AUTH_INVALID = 130 -RESPONSE_ERROR_WRITING_FLASH = 131 -RESPONSE_ERROR_UPDATE_END = 132 -RESPONSE_ERROR_INVALID_BOOTSTRAPPING = 133 -RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = 134 -RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 135 -RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 136 -RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 137 -RESPONSE_ERROR_NO_UPDATE_PARTITION = 138 -RESPONSE_ERROR_MD5_MISMATCH = 139 -RESPONSE_ERROR_UNKNOWN = 255 +RESPONSE_ERROR_MAGIC = 0x80 +RESPONSE_ERROR_UPDATE_PREPARE = 0x81 +RESPONSE_ERROR_AUTH_INVALID = 0x82 +RESPONSE_ERROR_WRITING_FLASH = 0x83 +RESPONSE_ERROR_UPDATE_END = 0x84 +RESPONSE_ERROR_INVALID_BOOTSTRAPPING = 0x85 +RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = 0x86 +RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 0x87 +RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 0x88 +RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 0x89 +RESPONSE_ERROR_NO_UPDATE_PARTITION = 0x8A +RESPONSE_ERROR_MD5_MISMATCH = 0x8B +RESPONSE_ERROR_UNKNOWN = 0xFF OTA_VERSION_1_0 = 1 +OTA_VERSION_2_0 = 2 MAGIC_BYTES = [0x6C, 0x26, 0xF7, 0x5C, 0x45] @@ -203,7 +205,8 @@ def perform_ota( send_check(sock, MAGIC_BYTES, "magic bytes") _, version = receive_exactly(sock, 2, "version", RESPONSE_OK) - if version != OTA_VERSION_1_0: + _LOGGER.debug("Device support OTA version: %s", version) + if version not in (OTA_VERSION_1_0, OTA_VERSION_2_0): raise OTAError(f"Unsupported OTA version {version}") # Features @@ -279,6 +282,8 @@ def perform_ota( try: sock.sendall(chunk) + if version >= OTA_VERSION_2_0: + receive_exactly(sock, 1, "chunk OK", RESPONSE_CHUNK_OK) except OSError as err: sys.stderr.write("\n") raise OTAError(f"Error sending data: {err}") from err diff --git a/tests/test3.1.yaml b/tests/test3.1.yaml index b5428abbfa..5cbdca91c1 100644 --- a/tests/test3.1.yaml +++ b/tests/test3.1.yaml @@ -49,6 +49,7 @@ spi: number: GPIO14 ota: + version: 2 logger: From 2f09624c07d8b24c174b58ea27230f459b65df08 Mon Sep 17 00:00:00 2001 From: Stefan Rado <628587+kroimon@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:30:57 +0100 Subject: [PATCH 165/193] Remove optional<> for pointer types (#6120) --- esphome/components/bedjet/bedjet_hub.cpp | 12 +++++------- esphome/components/bedjet/bedjet_hub.h | 2 +- esphome/components/tuya/tuya.cpp | 20 ++++++++------------ esphome/components/tuya/tuya.h | 4 ++-- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/esphome/components/bedjet/bedjet_hub.cpp b/esphome/components/bedjet/bedjet_hub.cpp index 7933a35a97..6404298697 100644 --- a/esphome/components/bedjet/bedjet_hub.cpp +++ b/esphome/components/bedjet/bedjet_hub.cpp @@ -242,7 +242,7 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga this->set_notify_(true); #ifdef USE_TIME - if (this->time_id_.has_value()) { + if (this->time_id_ != nullptr) { this->send_local_time(); } #endif @@ -441,9 +441,8 @@ uint8_t BedJetHub::write_notify_config_descriptor_(bool enable) { #ifdef USE_TIME void BedJetHub::send_local_time() { - if (this->time_id_.has_value()) { - auto *time_id = *this->time_id_; - ESPTime now = time_id->now(); + if (this->time_id_ != nullptr) { + ESPTime now = this->time_id_->now(); if (now.is_valid()) { this->set_clock(now.hour, now.minute); ESP_LOGD(TAG, "Using time component to set BedJet clock: %d:%02d", now.hour, now.minute); @@ -454,10 +453,9 @@ void BedJetHub::send_local_time() { } void BedJetHub::setup_time_() { - if (this->time_id_.has_value()) { + if (this->time_id_ != nullptr) { this->send_local_time(); - auto *time_id = *this->time_id_; - time_id->add_on_time_sync_callback([this] { this->send_local_time(); }); + this->time_id_->add_on_time_sync_callback([this] { this->send_local_time(); }); } else { ESP_LOGI(TAG, "`time_id` is not configured: will not sync BedJet clock."); } diff --git a/esphome/components/bedjet/bedjet_hub.h b/esphome/components/bedjet/bedjet_hub.h index bb1349b2ac..6258795b02 100644 --- a/esphome/components/bedjet/bedjet_hub.h +++ b/esphome/components/bedjet/bedjet_hub.h @@ -141,7 +141,7 @@ class BedJetHub : public esphome::ble_client::BLEClientNode, public PollingCompo #ifdef USE_TIME /** Initializes time sync callbacks to support syncing current time to the BedJet. */ void setup_time_(); - optional time_id_{}; + time::RealTimeClock *time_id_{nullptr}; #endif uint32_t timeout_{DEFAULT_STATUS_TIMEOUT}; diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index da03e3faad..1cc9681d09 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -23,8 +23,8 @@ static const int MAX_RETRIES = 5; void Tuya::setup() { this->set_interval("heartbeat", 15000, [this] { this->send_empty_command_(TuyaCommandType::HEARTBEAT); }); - if (this->status_pin_.has_value()) { - this->status_pin_.value()->digital_write(false); + if (this->status_pin_ != nullptr) { + this->status_pin_->digital_write(false); } } @@ -70,9 +70,7 @@ void Tuya::dump_config() { ESP_LOGCONFIG(TAG, " GPIO Configuration: status: pin %d, reset: pin %d", this->status_pin_reported_, this->reset_pin_reported_); } - if (this->status_pin_.has_value()) { - LOG_PIN(" Status Pin: ", this->status_pin_.value()); - } + LOG_PIN(" Status Pin: ", this->status_pin_); ESP_LOGCONFIG(TAG, " Product: '%s'", this->product_.c_str()); } @@ -194,7 +192,7 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff this->init_state_ = TuyaInitState::INIT_DATAPOINT; this->send_empty_command_(TuyaCommandType::DATAPOINT_QUERY); bool is_pin_equals = - this->status_pin_.has_value() && this->status_pin_.value()->get_pin() == this->status_pin_reported_; + this->status_pin_ != nullptr && this->status_pin_->get_pin() == this->status_pin_reported_; // Configure status pin toggling (if reported and configured) or WIFI_STATE periodic send if (is_pin_equals) { ESP_LOGV(TAG, "Configured status pin %i", this->status_pin_reported_); @@ -244,13 +242,12 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff break; case TuyaCommandType::LOCAL_TIME_QUERY: #ifdef USE_TIME - if (this->time_id_.has_value()) { + if (this->time_id_ != nullptr) { this->send_local_time_(); if (!this->time_sync_callback_registered_) { // tuya mcu supports time, so we let them know when our time changed - auto *time_id = *this->time_id_; - time_id->add_on_time_sync_callback([this] { this->send_local_time_(); }); + this->time_id_->add_on_time_sync_callback([this] { this->send_local_time_(); }); this->time_sync_callback_registered_ = true; } } else @@ -463,7 +460,7 @@ void Tuya::send_empty_command_(TuyaCommandType command) { void Tuya::set_status_pin_() { bool is_network_ready = network::is_connected() && remote_is_connected(); - this->status_pin_.value()->digital_write(is_network_ready); + this->status_pin_->digital_write(is_network_ready); } uint8_t Tuya::get_wifi_status_code_() { @@ -511,8 +508,7 @@ void Tuya::send_wifi_status_() { #ifdef USE_TIME void Tuya::send_local_time_() { std::vector payload; - auto *time_id = *this->time_id_; - ESPTime now = time_id->now(); + ESPTime now = this->time_id_->now(); if (now.is_valid()) { uint8_t year = now.year - 2000; uint8_t month = now.month; diff --git a/esphome/components/tuya/tuya.h b/esphome/components/tuya/tuya.h index 27a97c3dc9..7dc405e3dd 100644 --- a/esphome/components/tuya/tuya.h +++ b/esphome/components/tuya/tuya.h @@ -130,14 +130,14 @@ class Tuya : public Component, public uart::UARTDevice { #ifdef USE_TIME void send_local_time_(); - optional time_id_{}; + time::RealTimeClock *time_id_{nullptr}; bool time_sync_callback_registered_{false}; #endif TuyaInitState init_state_ = TuyaInitState::INIT_HEARTBEAT; bool init_failed_{false}; int init_retries_{0}; uint8_t protocol_version_ = -1; - optional status_pin_{}; + InternalGPIOPin *status_pin_{nullptr}; int status_pin_reported_ = -1; int reset_pin_reported_ = -1; uint32_t last_command_timestamp_ = 0; From 0cbc06a9b91fd16e6dd9a48e75569b409039a957 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 20 Jan 2024 03:38:37 +1300 Subject: [PATCH 166/193] Fix some Voice Assistant bugs (#6121) --- esphome/components/voice_assistant/voice_assistant.cpp | 10 ++++++---- esphome/core/ring_buffer.cpp | 9 +++++---- esphome/core/ring_buffer.h | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 299e624f5f..9094b93c02 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -17,7 +17,7 @@ static const char *const TAG = "voice_assistant"; static const size_t SAMPLE_RATE_HZ = 16000; static const size_t INPUT_BUFFER_SIZE = 32 * SAMPLE_RATE_HZ / 1000; // 32ms * 16kHz / 1000ms -static const size_t BUFFER_SIZE = 1000 * SAMPLE_RATE_HZ / 1000; // 1s +static const size_t BUFFER_SIZE = 1024 * SAMPLE_RATE_HZ / 1000; static const size_t SEND_BUFFER_SIZE = INPUT_BUFFER_SIZE * sizeof(int16_t); static const size_t RECEIVE_SIZE = 1024; static const size_t SPEAKER_BUFFER_SIZE = 16 * RECEIVE_SIZE; @@ -231,10 +231,12 @@ void VoiceAssistant::loop() { } case State::STREAMING_MICROPHONE: { this->read_microphone_(); - if (this->ring_buffer_->available() >= SEND_BUFFER_SIZE) { - this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0); - this->socket_->sendto(this->send_buffer_, SEND_BUFFER_SIZE, 0, (struct sockaddr *) &this->dest_addr_, + size_t available = this->ring_buffer_->available(); + while (available >= SEND_BUFFER_SIZE) { + size_t read_bytes = this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0); + this->socket_->sendto(this->send_buffer_, read_bytes, 0, (struct sockaddr *) &this->dest_addr_, sizeof(this->dest_addr_)); + available = this->ring_buffer_->available(); } break; diff --git a/esphome/core/ring_buffer.cpp b/esphome/core/ring_buffer.cpp index d9c56d84c5..9bd3d9d853 100644 --- a/esphome/core/ring_buffer.cpp +++ b/esphome/core/ring_buffer.cpp @@ -15,17 +15,18 @@ std::unique_ptr RingBuffer::create(size_t len) { std::unique_ptr rb = make_unique(); ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); - rb->storage_ = allocator.allocate(len); + rb->storage_ = allocator.allocate(len + 1); if (rb->storage_ == nullptr) { return nullptr; } - rb->handle_ = xStreamBufferCreateStatic(len, 0, rb->storage_, &rb->structure_); + rb->handle_ = xStreamBufferCreateStatic(len + 1, 0, rb->storage_, &rb->structure_); + ESP_LOGD(TAG, "Created ring buffer with size %u", len); return rb; } -size_t RingBuffer::read(void *data, size_t size, TickType_t ticks_to_wait) { - return xStreamBufferReceive(this->handle_, data, size, ticks_to_wait); +size_t RingBuffer::read(void *data, size_t len, TickType_t ticks_to_wait) { + return xStreamBufferReceive(this->handle_, data, len, ticks_to_wait); } size_t RingBuffer::write(void *data, size_t len) { diff --git a/esphome/core/ring_buffer.h b/esphome/core/ring_buffer.h index 6c6d04117a..e602068844 100644 --- a/esphome/core/ring_buffer.h +++ b/esphome/core/ring_buffer.h @@ -12,7 +12,7 @@ namespace esphome { class RingBuffer { public: - size_t read(void *data, size_t size, TickType_t ticks_to_wait = 0); + size_t read(void *data, size_t len, TickType_t ticks_to_wait = 0); size_t write(void *data, size_t len); From c35a21773e3158484c60879a8fdcb806b9cc1163 Mon Sep 17 00:00:00 2001 From: jxl77 Date: Sun, 21 Jan 2024 02:57:39 +0100 Subject: [PATCH 167/193] Improve temperature precision in BME280 and BMP280 (#6124) * Update bme280_base.cpp Change read_temperature to get better precision float const temperature = (*t_fine * 5 + 128); return temperature / 25600.0f; * Update bmp280.cpp increase precision in read_temperature * Update bmp280.cpp clang-format correction --- esphome/components/bme280_base/bme280_base.cpp | 4 ++-- esphome/components/bmp280/bmp280.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/bme280_base/bme280_base.cpp b/esphome/components/bme280_base/bme280_base.cpp index 3c6e15cbca..76e20836c7 100644 --- a/esphome/components/bme280_base/bme280_base.cpp +++ b/esphome/components/bme280_base/bme280_base.cpp @@ -265,8 +265,8 @@ float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) { int32_t const var2 = (((((adc >> 4) - t1) * ((adc >> 4) - t1)) >> 12) * t3) >> 14; *t_fine = var1 + var2; - float const temperature = (*t_fine * 5 + 128) >> 8; - return temperature / 100.0f; + float const temperature = (*t_fine * 5 + 128); + return temperature / 25600.0f; } float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) { diff --git a/esphome/components/bmp280/bmp280.cpp b/esphome/components/bmp280/bmp280.cpp index a5b2517893..c92daa07fb 100644 --- a/esphome/components/bmp280/bmp280.cpp +++ b/esphome/components/bmp280/bmp280.cpp @@ -200,8 +200,8 @@ float BMP280Component::read_temperature_(int32_t *t_fine) { int32_t var2 = (((((adc >> 4) - t1) * ((adc >> 4) - t1)) >> 12) * t3) >> 14; *t_fine = var1 + var2; - float temperature = (*t_fine * 5 + 128) >> 8; - return temperature / 100.0f; + float temperature = (*t_fine * 5 + 128); + return temperature / 25600.0f; } float BMP280Component::read_pressure_(int32_t t_fine) { From 48129974294b26cf8528d03f971d714392c23521 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 23 Jan 2024 08:49:28 +0100 Subject: [PATCH 168/193] Nextion TFT upload IDF memory optimization (#6128) * Nextion TFT upload IDF memory optimization This optimizes the memory in use for TFT upload when using `esp-idf` framework. Basically, the engine establishes 3 connections to the the http/https server: 1. Fetch the file size (used to manage chunks and file size) 2. Transfer the 1st chunk (when it evaluates Nextion response to define either to continue from that point or to another point in the file) 3. Transfer the remaining data. Until now, connection 1 was kept open during the whole process taking aprox 40kb of heap in a esp32dev (NSPanel in my tests) and the same amount of memory was needed to the 2nd and 3rd connections (which never competes to each other). With this change, each connection is closed and released before opening the next one with a significant reduction on the required heap needed for this transfer. This can still be improved to use a persistent connection, but I will look at this in the future, so it is not part of this change. In addition to the better connection management, I've added quite a lot of log (mostly at VERBOSE level), which was used for troubleshooting here. I was unsure about removing this. As it can be useful for others, I decided to keep it, but I will be fine about removing it if this is now in line with ESPHome best practices. * clang-format * Log response length --- .../components/nextion/nextion_upload_idf.cpp | 87 ++++++++++++++----- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/esphome/components/nextion/nextion_upload_idf.cpp b/esphome/components/nextion/nextion_upload_idf.cpp index 709ff65b12..14b1b6cfaf 100644 --- a/esphome/components/nextion/nextion_upload_idf.cpp +++ b/esphome/components/nextion/nextion_upload_idf.cpp @@ -24,7 +24,7 @@ int Nextion::upload_range(const std::string &url, int range_start) { ESP_LOGVV(TAG, "url: %s", url.c_str()); uint range_size = this->tft_size_ - range_start; ESP_LOGVV(TAG, "tft_size_: %i", this->tft_size_); - ESP_LOGV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); int range_end = (range_start == 0) ? std::min(this->tft_size_, 16383) : this->tft_size_; if (range_size <= 0 or range_end <= range_start) { ESP_LOGE(TAG, "Invalid range"); @@ -67,12 +67,13 @@ int Nextion::upload_range(const std::string &url, int range_start) { int total_read_len = 0, read_len; + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); ESP_LOGV(TAG, "Allocate buffer"); uint8_t *buffer = new uint8_t[4096]; std::string recv_string; if (buffer == nullptr) { ESP_LOGE(TAG, "Failed to allocate memory for buffer"); - ESP_LOGV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); } else { ESP_LOGV(TAG, "Memory for buffer allocated successfully"); @@ -86,15 +87,14 @@ int Nextion::upload_range(const std::string &url, int range_start) { ESP_LOGVV(TAG, "Write to UART successful"); this->recv_ret_string_(recv_string, 5000, true); this->content_length_ -= read_len; - ESP_LOGD(TAG, "Uploaded %0.2f %%, remaining %d bytes", - 100.0 * (this->tft_size_ - this->content_length_) / this->tft_size_, this->content_length_); - if (recv_string[0] != 0x05) { // 0x05 == "ok" + ESP_LOGD(TAG, "Uploaded %0.2f %%, remaining %d bytes, heap is %" PRIu32 " bytes", + 100.0 * (this->tft_size_ - this->content_length_) / this->tft_size_, this->content_length_, + esp_get_free_heap_size()); + + if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request ESP_LOGD( TAG, "recv_string [%s]", format_hex_pretty(reinterpret_cast(recv_string.data()), recv_string.size()).c_str()); - } - // handle partial upload request - if (recv_string[0] == 0x08 && recv_string.size() == 5) { uint32_t result = 0; for (int j = 0; j < 4; ++j) { result += static_cast(recv_string[j + 1]) << (8 * j); @@ -103,13 +103,37 @@ int Nextion::upload_range(const std::string &url, int range_start) { ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result); this->content_length_ = this->tft_size_ - result; // Deallocate the buffer when done + ESP_LOGV(TAG, "Deallocate buffer"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); delete[] buffer; ESP_LOGVV(TAG, "Memory for buffer deallocated"); - esp_http_client_cleanup(client); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Close http client"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); esp_http_client_close(client); + esp_http_client_cleanup(client); + ESP_LOGVV(TAG, "Client closed"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); return result; } + } else if (recv_string[0] != 0x05) { // 0x05 == "ok" + ESP_LOGE( + TAG, "Invalid response from Nextion: [%s]", + format_hex_pretty(reinterpret_cast(recv_string.data()), recv_string.size()).c_str()); + ESP_LOGV(TAG, "Deallocate buffer"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + delete[] buffer; + ESP_LOGVV(TAG, "Memory for buffer deallocated"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Close http client"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + esp_http_client_close(client); + esp_http_client_cleanup(client); + ESP_LOGVV(TAG, "Client closed"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + return -1; } + recv_string.clear(); } else if (read_len == 0) { ESP_LOGV(TAG, "End of HTTP response reached"); @@ -121,11 +145,18 @@ int Nextion::upload_range(const std::string &url, int range_start) { } // Deallocate the buffer when done + ESP_LOGV(TAG, "Deallocate buffer"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); delete[] buffer; ESP_LOGVV(TAG, "Memory for buffer deallocated"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); } - esp_http_client_cleanup(client); + ESP_LOGV(TAG, "Close http client"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); esp_http_client_close(client); + esp_http_client_cleanup(client); + ESP_LOGVV(TAG, "Client closed"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); return range_end + 1; } @@ -159,7 +190,7 @@ bool Nextion::upload_tft() { // Initialize the HTTP client with the configuration ESP_LOGV(TAG, "Initializing HTTP client"); - ESP_LOGV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); esp_http_client_handle_t http = esp_http_client_init(&config); if (!http) { ESP_LOGE(TAG, "Failed to initialize HTTP client."); @@ -168,7 +199,7 @@ bool Nextion::upload_tft() { // Perform the HTTP request ESP_LOGV(TAG, "Check if the client could connect"); - ESP_LOGV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); esp_err_t err = esp_http_client_perform(http); if (err != ESP_OK) { ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err)); @@ -177,14 +208,22 @@ bool Nextion::upload_tft() { } // Check the HTTP Status Code + ESP_LOGV(TAG, "Check the HTTP Status Code"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); int status_code = esp_http_client_get_status_code(http); ESP_LOGV(TAG, "HTTP Status Code: %d", status_code); size_t tft_file_size = esp_http_client_get_content_length(http); ESP_LOGD(TAG, "TFT file size: %zu", tft_file_size); + ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + esp_http_client_close(http); + esp_http_client_cleanup(http); + ESP_LOGVV(TAG, "Connection closed"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); + if (tft_file_size < 4096) { ESP_LOGE(TAG, "File size check failed. Size: %zu", tft_file_size); - esp_http_client_cleanup(http); return this->upload_end(false); } else { ESP_LOGV(TAG, "File size check passed. Proceeding..."); @@ -193,8 +232,10 @@ bool Nextion::upload_tft() { this->tft_size_ = tft_file_size; ESP_LOGD(TAG, "Updating Nextion"); - // The Nextion will ignore the update command if it is sleeping + // The Nextion will ignore the update command if it is sleeping + ESP_LOGV(TAG, "Wake-up Nextion"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); this->send_command_("sleep=0"); this->set_backlight_brightness(1.0); vTaskDelay(pdMS_TO_TICKS(250)); // NOLINT @@ -207,26 +248,31 @@ bool Nextion::upload_tft() { sprintf(command, "whmi-wris %d,%" PRIu32 ",1", this->content_length_, this->parent_->get_baud_rate()); // Clear serial receive buffer + ESP_LOGV(TAG, "Clear serial receive buffer"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); uint8_t d; while (this->available()) { this->read_byte(&d); }; + ESP_LOGV(TAG, "Send update instruction: %s", command); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); this->send_command_(command); std::string response; ESP_LOGV(TAG, "Waiting for upgrade response"); - this->recv_ret_string_(response, 2048, true); // This can take some time to return + this->recv_ret_string_(response, 5000, true); // This can take some time to return // The Nextion display will, if it's ready to accept data, send a 0x05 byte. - ESP_LOGD(TAG, "Upgrade response is [%s]", - format_hex_pretty(reinterpret_cast(response.data()), response.size()).c_str()); + ESP_LOGD(TAG, "Upgrade response is [%s] - %zu bytes", + format_hex_pretty(reinterpret_cast(response.data()), response.size()).c_str(), + response.length()); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); if (response.find(0x05) != std::string::npos) { ESP_LOGV(TAG, "Preparation for tft update done"); } else { ESP_LOGE(TAG, "Preparation for tft update failed %d \"%s\"", response[0], response.c_str()); - esp_http_client_cleanup(http); return this->upload_end(false); } @@ -234,12 +280,12 @@ bool Nextion::upload_tft() { content_length_, esp_get_free_heap_size()); ESP_LOGV(TAG, "Starting transfer by chunks loop"); + ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); int result = 0; while (content_length_ > 0) { result = upload_range(this->tft_url_.c_str(), result); if (result < 0) { ESP_LOGE(TAG, "Error updating Nextion!"); - esp_http_client_cleanup(http); return this->upload_end(false); } App.feed_wdt(); @@ -248,9 +294,6 @@ bool Nextion::upload_tft() { ESP_LOGD(TAG, "Successfully updated Nextion!"); - ESP_LOGD(TAG, "Close HTTP connection"); - esp_http_client_close(http); - esp_http_client_cleanup(http); return upload_end(true); } From 23071e932adce4d5f7a708b7c252e5758faaf36d Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 24 Jan 2024 07:40:16 +1100 Subject: [PATCH 169/193] Add support for Pico-ResTouch-LCD-3.5 to ili9xxx driver (#6129) * Working version of Waveshare 3.5 Res Touch driver. * Default color order BGR --- esphome/components/ili9xxx/display.py | 1 + .../components/ili9xxx/ili9xxx_display.cpp | 83 ++++++++----------- esphome/components/ili9xxx/ili9xxx_display.h | 52 ++++++++++-- esphome/components/ili9xxx/ili9xxx_init.h | 28 +++---- 4 files changed, 94 insertions(+), 70 deletions(-) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index b3fe8b2b41..0bd810ea16 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -66,6 +66,7 @@ MODELS = { "ST7789V": ili9xxx_ns.class_("ILI9XXXST7789V", ILI9XXXDisplay), "S3BOX": ili9xxx_ns.class_("ILI9XXXS3Box", ILI9XXXDisplay), "S3BOX_LITE": ili9xxx_ns.class_("ILI9XXXS3BoxLite", ILI9XXXDisplay), + "WAVESHARE_RES_3_5": ili9xxx_ns.class_("WAVESHARERES35", ILI9XXXDisplay), } COLOR_ORDERS = { diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index ab577b3875..e3f2c94880 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -7,7 +7,6 @@ namespace esphome { namespace ili9xxx { -static const char *const TAG = "ili9xxx"; static const uint16_t SPI_SETUP_US = 100; // estimated fixed overhead in microseconds for an SPI write static const uint16_t SPI_MAX_BLOCK_SIZE = 4092; // Max size of continuous SPI transfer @@ -17,13 +16,7 @@ static inline void put16_be(uint8_t *buf, uint16_t value) { buf[1] = value; } -void ILI9XXXDisplay::setup() { - ESP_LOGD(TAG, "Setting up ILI9xxx"); - - this->setup_pins_(); - this->init_lcd_(); - - this->command(this->pre_invertcolors_ ? ILI9XXX_INVON : ILI9XXX_INVOFF); +void ILI9XXXDisplay::set_madctl() { // custom x/y transform and color order uint8_t mad = this->color_order_ == display::COLOR_ORDER_BGR ? MADCTL_BGR : MADCTL_RGB; if (this->swap_xy_) @@ -32,8 +25,19 @@ void ILI9XXXDisplay::setup() { mad |= MADCTL_MX; if (this->mirror_y_) mad |= MADCTL_MY; - this->send_command(ILI9XXX_MADCTL, &mad, 1); + this->command(ILI9XXX_MADCTL); + this->data(mad); + esph_log_d(TAG, "Wrote MADCTL 0x%02X", mad); +} +void ILI9XXXDisplay::setup() { + ESP_LOGD(TAG, "Setting up ILI9xxx"); + + this->setup_pins_(); + this->init_lcd_(); + + this->set_madctl(); + this->command(this->pre_invertcolors_ ? ILI9XXX_INVON : ILI9XXX_INVOFF); this->x_low_ = this->width_; this->y_low_ = this->height_; this->x_high_ = 0; @@ -89,6 +93,7 @@ void ILI9XXXDisplay::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Busy Pin: ", this->busy_pin_); + ESP_LOGCONFIG(TAG, " Color order: %s", this->color_order_ == display::COLOR_ORDER_BGR ? "BGR" : "RGB"); ESP_LOGCONFIG(TAG, " Swap_xy: %s", YESNO(this->swap_xy_)); ESP_LOGCONFIG(TAG, " Mirror_x: %s", YESNO(this->mirror_x_)); ESP_LOGCONFIG(TAG, " Mirror_y: %s", YESNO(this->mirror_y_)); @@ -196,7 +201,6 @@ void ILI9XXXDisplay::display_() { uint8_t transfer_buffer[ILI9XXX_TRANSFER_BUFFER_SIZE]; // check if something was displayed if ((this->x_high_ < this->x_low_) || (this->y_high_ < this->y_low_)) { - ESP_LOGV(TAG, "Nothing to display"); return; } @@ -211,14 +215,13 @@ void ILI9XXXDisplay::display_() { size_t mw_time = (w * h * 16) / mhz + w * h * 2 / ILI9XXX_TRANSFER_BUFFER_SIZE * SPI_SETUP_US; ESP_LOGV(TAG, "Start display(xlow:%d, ylow:%d, xhigh:%d, yhigh:%d, width:%d, " - "height:%d, mode=%d, 18bit=%d, sw_time=%dus, mw_time=%dus)", + "height:%zu, mode=%d, 18bit=%d, sw_time=%zuus, mw_time=%zuus)", this->x_low_, this->y_low_, this->x_high_, this->y_high_, w, h, this->buffer_color_mode_, this->is_18bitdisplay_, sw_time, mw_time); auto now = millis(); - this->enable(); if (this->buffer_color_mode_ == BITS_16 && !this->is_18bitdisplay_ && sw_time < mw_time) { // 16 bit mode maps directly to display format - ESP_LOGV(TAG, "Doing single write of %d bytes", this->width_ * h * 2); + ESP_LOGV(TAG, "Doing single write of %zu bytes", this->width_ * h * 2); set_addr_window_(0, this->y_low_, this->width_ - 1, this->y_high_); this->write_array(this->buffer_ + this->y_low_ * this->width_ * 2, h * this->width_ * 2); } else { @@ -267,7 +270,7 @@ void ILI9XXXDisplay::display_() { this->write_array(transfer_buffer, idx); } } - this->disable(); + this->end_data_(); ESP_LOGV(TAG, "Data write took %dms", (unsigned) (millis() - now)); // invalidate watermarks this->x_low_ = this->width_; @@ -290,7 +293,6 @@ void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, cons return display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); } - this->enable(); this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display. if (x_offset == 0 && x_pad == 0 && y_offset == 0) { @@ -302,7 +304,7 @@ void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, cons this->write_array(ptr + (y + y_offset) * stride + x_offset, w * 2); } } - this->disable(); + this->end_data_(); } // should return the total size: return this->get_width_internal() * this->get_height_internal() * 2 // 16bit color @@ -328,20 +330,6 @@ void ILI9XXXDisplay::send_command(uint8_t command_byte, const uint8_t *data_byte this->end_data_(); } -uint8_t ILI9XXXDisplay::read_command(uint8_t command_byte, uint8_t index) { - uint8_t data = 0x10 + index; - this->send_command(0xD9, &data, 1); // Set Index Register - uint8_t result; - this->start_command_(); - this->write_byte(command_byte); - this->start_data_(); - do { - result = this->read_byte(); - } while (index--); - this->end_data_(); - return result; -} - void ILI9XXXDisplay::start_command_() { this->dc_pin_->digital_write(false); this->enable(); @@ -357,9 +345,9 @@ void ILI9XXXDisplay::end_data_() { this->disable(); } void ILI9XXXDisplay::reset_() { if (this->reset_pin_ != nullptr) { this->reset_pin_->digital_write(false); - delay(10); + delay(20); this->reset_pin_->digital_write(true); - delay(10); + delay(20); } } @@ -369,7 +357,7 @@ void ILI9XXXDisplay::init_lcd_() { while ((cmd = *addr++) > 0) { x = *addr++; num_args = x & 0x7F; - send_command(cmd, addr, num_args); + this->send_command(cmd, addr, num_args); addr += num_args; if (x & 0x80) delay(150); // NOLINT @@ -377,24 +365,19 @@ void ILI9XXXDisplay::init_lcd_() { } // Tell the display controller where we want to draw pixels. -// when called, the SPI should have already been enabled, only the D/C pin will be toggled here. void ILI9XXXDisplay::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { - uint8_t buf[4]; - this->dc_pin_->digital_write(false); - this->write_byte(ILI9XXX_CASET); // Column address set - put16_be(buf, x1 + this->offset_x_); - put16_be(buf + 2, x2 + this->offset_x_); - this->dc_pin_->digital_write(true); - this->write_array(buf, sizeof buf); - this->dc_pin_->digital_write(false); - this->write_byte(ILI9XXX_PASET); // Row address set - put16_be(buf, y1 + this->offset_y_); - put16_be(buf + 2, y2 + this->offset_y_); - this->dc_pin_->digital_write(true); - this->write_array(buf, sizeof buf); - this->dc_pin_->digital_write(false); - this->write_byte(ILI9XXX_RAMWR); // Write to RAM - this->dc_pin_->digital_write(true); + this->command(ILI9XXX_CASET); + this->data(x1 >> 8); + this->data(x1 & 0xFF); + this->data(x2 >> 8); + this->data(x2 & 0xFF); + this->command(ILI9XXX_PASET); // Page address set + this->data(y1 >> 8); + this->data(y1 & 0xFF); + this->data(y2 >> 8); + this->data(y2 & 0xFF); + this->command(ILI9XXX_RAMWR); // Write to RAM + this->start_data_(); } void ILI9XXXDisplay::invert_colors(bool invert) { diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index 590be3e364..7b92bd2336 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -8,6 +8,7 @@ namespace esphome { namespace ili9xxx { +static const char *const TAG = "ili9xxx"; const size_t ILI9XXX_TRANSFER_BUFFER_SIZE = 126; // ensure this is divisible by 6 enum ILI9XXXColorMode { @@ -32,6 +33,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, while ((cmd = *addr++) != 0) { num_args = *addr++ & 0x7F; bits = *addr; + esph_log_d(TAG, "Command %02X, length %d, bits %02X", cmd, num_args, bits); switch (cmd) { case ILI9XXX_MADCTL: { this->swap_xy_ = (bits & MADCTL_MV) != 0; @@ -68,10 +70,9 @@ class ILI9XXXDisplay : public display::DisplayBuffer, this->offset_y_ = offset_y; } void invert_colors(bool invert); - void command(uint8_t value); - void data(uint8_t value); + virtual void command(uint8_t value); + virtual void data(uint8_t value); void send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes); - uint8_t read_command(uint8_t command_byte, uint8_t index); void set_color_order(display::ColorOrder color_order) { this->color_order_ = color_order; } void set_swap_xy(bool swap_xy) { this->swap_xy_ = swap_xy; } void set_mirror_x(bool mirror_x) { this->mirror_x_ = mirror_x; } @@ -92,6 +93,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, void draw_absolute_pixel_internal(int x, int y, Color color) override; void setup_pins_(); + virtual void set_madctl(); void display_(); void init_lcd_(); void set_addr_window_(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2); @@ -127,7 +129,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, bool need_update_ = false; bool is_18bitdisplay_ = false; bool pre_invertcolors_ = false; - display::ColorOrder color_order_{}; + display::ColorOrder color_order_{display::COLOR_ORDER_BGR}; bool swap_xy_{}; bool mirror_x_{}; bool mirror_y_{}; @@ -181,10 +183,48 @@ class ILI9XXXILI9486 : public ILI9XXXDisplay { ILI9XXXILI9486() : ILI9XXXDisplay(INITCMD_ILI9486, 480, 320, false) {} }; -//----------- ILI9XXX_35_TFT rotated display -------------- class ILI9XXXILI9488 : public ILI9XXXDisplay { public: - ILI9XXXILI9488() : ILI9XXXDisplay(INITCMD_ILI9488, 480, 320, true) {} + ILI9XXXILI9488(const uint8_t *seq = INITCMD_ILI9488) : ILI9XXXDisplay(seq, 480, 320, true) {} + + protected: + void set_madctl() override { + uint8_t mad = this->color_order_ == display::COLOR_ORDER_BGR ? MADCTL_BGR : MADCTL_RGB; + uint8_t dfun = 0x22; + this->width_ = 320; + this->height_ = 480; + if (!(this->swap_xy_ || this->mirror_x_ || this->mirror_y_)) { + // no transforms + } else if (this->mirror_y_ && this->mirror_x_) { + // rotate 180 + dfun = 0x42; + } else if (this->swap_xy_) { + this->width_ = 480; + this->height_ = 320; + mad |= 0x20; + if (this->mirror_x_) { + dfun = 0x02; + } else { + dfun = 0x62; + } + } + this->command(ILI9XXX_DFUNCTR); + this->data(0); + this->data(dfun); + this->command(ILI9XXX_MADCTL); + this->data(mad); + } +}; +//----------- Waveshare 3.5 Res Touch - ILI9488 interfaced via 16 bit shift register to parallel */ +class WAVESHARERES35 : public ILI9XXXILI9488 { + public: + WAVESHARERES35() : ILI9XXXILI9488(INITCMD_WAVESHARE_RES_3_5) {} + void data(uint8_t value) override { + this->start_data_(); + this->write_byte(0); + this->write_byte(value); + this->end_data_(); + } }; //----------- ILI9XXX_35_TFT origin colors rotated display -------------- diff --git a/esphome/components/ili9xxx/ili9xxx_init.h b/esphome/components/ili9xxx/ili9xxx_init.h index a74824052f..fe3f168c32 100644 --- a/esphome/components/ili9xxx/ili9xxx_init.h +++ b/esphome/components/ili9xxx/ili9xxx_init.h @@ -141,7 +141,8 @@ static const uint8_t PROGMEM INITCMD_ILI9486[] = { 0x00 // End of list }; -static const uint8_t PROGMEM INITCMD_ILI9488[] = { + +static const uint8_t INITCMD_ILI9488[] = { ILI9XXX_GMCTRP1,15, 0x0f, 0x24, 0x1c, 0x0a, 0x0f, 0x08, 0x43, 0x88, 0x32, 0x0f, 0x10, 0x06, 0x0f, 0x07, 0x00, ILI9XXX_GMCTRN1,15, 0x0F, 0x38, 0x30, 0x09, 0x0f, 0x0f, 0x4e, 0x77, 0x3c, 0x07, 0x10, 0x05, 0x23, 0x1b, 0x00, @@ -153,28 +154,27 @@ static const uint8_t PROGMEM INITCMD_ILI9488[] = { ILI9XXX_FRMCTR1, 1, 0xA0, // Frame rate = 60Hz ILI9XXX_INVCTR, 1, 0x02, // Display Inversion Control = 2dot - ILI9XXX_DFUNCTR, 2, 0x02, 0x02, // Nomal scan - 0xE9, 1, 0x00, // Set Image Functio. Disable 24 bit data ILI9XXX_ADJCTL3, 4, 0xA9, 0x51, 0x2C, 0x82, // Adjust Control 3 - - ILI9XXX_MADCTL, 1, 0x28, - //ILI9XXX_PIXFMT, 1, 0x55, // Interface Pixel Format = 16bit ILI9XXX_PIXFMT, 1, 0x66, //ILI9488 only supports 18-bit pixel format in 4/3 wire SPI mode - - - - // 5 frames - //ILI9XXX_ETMOD, 1, 0xC6, // - - ILI9XXX_SLPOUT, 0x80, // Exit sleep mode - //ILI9XXX_INVON , 0, ILI9XXX_DISPON, 0x80, // Set display on 0x00 // end }; +static const uint8_t INITCMD_WAVESHARE_RES_3_5[] = { + ILI9XXX_PWCTR3, 1, 0x33, + ILI9XXX_VMCTR1, 3, 0x00, 0x1e, 0x80, + ILI9XXX_FRMCTR1, 1, 0xA0, + ILI9XXX_GMCTRP1, 15, 0x0, 0x13, 0x18, 0x04, 0x0F, 0x06, 0x3a, 0x56, 0x4d, 0x03, 0x0a, 0x06, 0x30, 0x3e, 0x0f, + ILI9XXX_GMCTRN1, 15, 0x0, 0x13, 0x18, 0x01, 0x11, 0x06, 0x38, 0x34, 0x4d, 0x06, 0x0d, 0x0b, 0x31, 0x37, 0x0f, + ILI9XXX_PIXFMT, 1, 0x55, + ILI9XXX_SLPOUT, 0x80, // slpout, delay + ILI9XXX_DISPON, 0, + 0x00 // End of list +}; + static const uint8_t PROGMEM INITCMD_ILI9488_A[] = { ILI9XXX_GMCTRP1,15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F, ILI9XXX_GMCTRN1,15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F, From 25ab6f0297fcf3a21e07644854db5083ba5a0eed Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 23 Jan 2024 19:11:03 -1000 Subject: [PATCH 170/193] Ensure filename is shown when YAML raises an error (#6139) * Ensure filename is shown when YAML raises an error fixes #5423 fixes #5377 * Ensure filename is shown when YAML raises an error fixes #5423 fixes #5377 * Ensure filename is shown when YAML raises an error fixes #5423 fixes #5377 * Ensure filename is shown when YAML raises an error fixes #5423 fixes #5377 * Ensure filename is shown when YAML raises an error fixes #5423 fixes #5377 --- esphome/yaml_util.py | 24 ++++++++++++------- .../fixtures/yaml_util/missing_comp.yaml | 12 ++++++++++ tests/unit_tests/test_yaml_util.py | 20 ++++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 tests/unit_tests/fixtures/yaml_util/missing_comp.yaml diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index f5e36b79e7..60705082b6 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -7,6 +7,7 @@ import logging import math import os import uuid +from io import TextIOWrapper from typing import Any import yaml @@ -19,7 +20,7 @@ except ImportError: FastestAvailableSafeLoader = PurePythonLoader from esphome import core -from esphome.config_helpers import Extend, Remove, read_config_file +from esphome.config_helpers import Extend, Remove from esphome.core import ( CORE, DocumentRange, @@ -418,19 +419,26 @@ def load_yaml(fname: str, clear_secrets: bool = True) -> Any: def _load_yaml_internal(fname: str) -> Any: """Load a YAML file.""" - content = read_config_file(fname) try: - return _load_yaml_internal_with_type(ESPHomeLoader, fname, content) - except EsphomeError: - # Loading failed, so we now load with the Python loader which has more - # readable exceptions - return _load_yaml_internal_with_type(ESPHomePurePythonLoader, fname, content) + 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 + ) + except (UnicodeDecodeError, OSError) as err: + raise EsphomeError(f"Error reading file {fname}: {err}") from err def _load_yaml_internal_with_type( loader_type: type[ESPHomeLoader] | type[ESPHomePurePythonLoader], fname: str, - content: str, + content: TextIOWrapper, ) -> Any: """Load a YAML file.""" loader = loader_type(content) diff --git a/tests/unit_tests/fixtures/yaml_util/missing_comp.yaml b/tests/unit_tests/fixtures/yaml_util/missing_comp.yaml new file mode 100644 index 0000000000..d065901ed9 --- /dev/null +++ b/tests/unit_tests/fixtures/yaml_util/missing_comp.yaml @@ -0,0 +1,12 @@ +esphome: + name: test + +esp32: + board: esp32dev + +wifi: + ap: ~ + +image: + - id: its_a_bug + file: "mdi:bug" diff --git a/tests/unit_tests/test_yaml_util.py b/tests/unit_tests/test_yaml_util.py index 78b6a2ad84..9178726247 100644 --- a/tests/unit_tests/test_yaml_util.py +++ b/tests/unit_tests/test_yaml_util.py @@ -22,3 +22,23 @@ def test_loading_a_broken_yaml_file(fixture_path): yaml_util.load_yaml(yaml_file) except EsphomeError as err: assert "broken_included.yaml" in str(err) + + +def test_loading_a_yaml_file_with_a_missing_component(fixture_path): + """Ensure we show the filename for a yaml file with a missing component.""" + yaml_file = fixture_path / "yaml_util" / "missing_comp.yaml" + + try: + yaml_util.load_yaml(yaml_file) + except EsphomeError as err: + assert "missing_comp.yaml" in str(err) + + +def test_loading_a_missing_file(fixture_path): + """We throw EsphomeError when loading a missing file.""" + yaml_file = fixture_path / "yaml_util" / "missing.yaml" + + try: + yaml_util.load_yaml(yaml_file) + except EsphomeError as err: + assert "missing.yaml" in str(err) From f2caf13d39b02c4b0c3781ef2b3db7a51701cb45 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 26 Jan 2024 16:13:38 +1100 Subject: [PATCH 171/193] ILI9XXX: Restore offset usage in set_addr_window (#6147) --- esphome/components/ili9xxx/ili9xxx_display.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index e3f2c94880..9f06c9ce0f 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -366,6 +366,10 @@ void ILI9XXXDisplay::init_lcd_() { // Tell the display controller where we want to draw pixels. void ILI9XXXDisplay::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { + x1 += this->offset_x_; + x2 += this->offset_x_; + y1 += this->offset_y_; + y2 += this->offset_y_; this->command(ILI9XXX_CASET); this->data(x1 >> 8); this->data(x1 & 0xFF); From 23a9a704f349305c1785c10269a092aedb36ff7d Mon Sep 17 00:00:00 2001 From: Ruben van Dijk <15885455+RubenNL@users.noreply.github.com> Date: Sat, 27 Jan 2024 21:15:14 +0100 Subject: [PATCH 172/193] Minimum 1 for full_update_every to prevent IntegerDivideByZero. (#6150) --- esphome/components/waveshare_epaper/display.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 1645ce0b1d..b0c663770d 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -135,7 +135,7 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_MODEL): cv.one_of(*MODELS, lower=True), cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema, - cv.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t, + cv.Optional(CONF_FULL_UPDATE_EVERY): cv.int_range(min=1, max=4294967295), cv.Optional(CONF_RESET_DURATION): cv.All( cv.positive_time_period_milliseconds, cv.Range(max=core.TimePeriod(milliseconds=500)), From 92798751c2a0860dc5f85cf9f34395372cca72d6 Mon Sep 17 00:00:00 2001 From: rnauber <7414650+rnauber@users.noreply.github.com> Date: Tue, 30 Jan 2024 05:16:32 +0100 Subject: [PATCH 173/193] Support tri-color waveshare eink displays 2.7inch B and B V2 (#4238) Co-authored-by: Richard Nauber --- .../components/waveshare_epaper/display.py | 18 +- .../waveshare_epaper/waveshare_epaper.cpp | 297 +++++++++++++++++- .../waveshare_epaper/waveshare_epaper.h | 120 +++++-- tests/test4.yaml | 34 ++ 4 files changed, 423 insertions(+), 46 deletions(-) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index b0c663770d..fa7c104951 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -17,8 +17,12 @@ from esphome.const import ( DEPENDENCIES = ["spi"] waveshare_epaper_ns = cg.esphome_ns.namespace("waveshare_epaper") -WaveshareEPaper = waveshare_epaper_ns.class_( - "WaveshareEPaper", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer +WaveshareEPaperBase = waveshare_epaper_ns.class_( + "WaveshareEPaperBase", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer +) +WaveshareEPaper = waveshare_epaper_ns.class_("WaveshareEPaper", WaveshareEPaperBase) +WaveshareEPaperBWR = waveshare_epaper_ns.class_( + "WaveshareEPaperBWR", WaveshareEPaperBase ) WaveshareEPaperTypeA = waveshare_epaper_ns.class_( "WaveshareEPaperTypeA", WaveshareEPaper @@ -26,6 +30,12 @@ WaveshareEPaperTypeA = waveshare_epaper_ns.class_( WaveshareEPaper2P7In = waveshare_epaper_ns.class_( "WaveshareEPaper2P7In", WaveshareEPaper ) +WaveshareEPaper2P7InB = waveshare_epaper_ns.class_( + "WaveshareEPaper2P7InB", WaveshareEPaperBWR +) +WaveshareEPaper2P7InBV2 = waveshare_epaper_ns.class_( + "WaveshareEPaper2P7InBV2", WaveshareEPaperBWR +) WaveshareEPaper2P7InV2 = waveshare_epaper_ns.class_( "WaveshareEPaper2P7InV2", WaveshareEPaper ) @@ -92,6 +102,8 @@ MODELS = { "2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2), "gdey029t94": ("c", GDEY029T94), "2.70in": ("b", WaveshareEPaper2P7In), + "2.70in-b": ("b", WaveshareEPaper2P7InB), + "2.70in-bv2": ("b", WaveshareEPaper2P7InBV2), "2.70inv2": ("b", WaveshareEPaper2P7InV2), "2.90in-b": ("b", WaveshareEPaper2P9InB), "2.90in-bv3": ("b", WaveshareEPaper2P9InBV3), @@ -130,7 +142,7 @@ def validate_full_update_every_only_types_ac(value): CONFIG_SCHEMA = cv.All( display.FULL_DISPLAY_SCHEMA.extend( { - cv.GenerateID(): cv.declare_id(WaveshareEPaper), + cv.GenerateID(): cv.declare_id(WaveshareEPaperBase), cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_MODEL): cv.one_of(*MODELS, lower=True), cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index b0946ad9d0..9118475c36 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -83,7 +83,7 @@ static const uint8_t PARTIAL_UPDATE_LUT_TTGO_B1[LUT_SIZE_TTGO_B1] = { 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -void WaveshareEPaper::setup_pins_() { +void WaveshareEPaperBase::setup_pins_() { this->init_internal_(this->get_buffer_length_()); this->dc_pin_->setup(); // OUTPUT this->dc_pin_->digital_write(false); @@ -98,13 +98,13 @@ void WaveshareEPaper::setup_pins_() { this->reset_(); } -float WaveshareEPaper::get_setup_priority() const { return setup_priority::PROCESSOR; } -void WaveshareEPaper::command(uint8_t value) { +float WaveshareEPaperBase::get_setup_priority() const { return setup_priority::PROCESSOR; } +void WaveshareEPaperBase::command(uint8_t value) { this->start_command_(); this->write_byte(value); this->end_command_(); } -void WaveshareEPaper::data(uint8_t value) { +void WaveshareEPaperBase::data(uint8_t value) { this->start_data_(); this->write_byte(value); this->end_data_(); @@ -112,7 +112,7 @@ void WaveshareEPaper::data(uint8_t value) { // write a command followed by one or more bytes of data. // The command is the first byte, length is the total including cmd. -void WaveshareEPaper::cmd_data(const uint8_t *c_data, size_t length) { +void WaveshareEPaperBase::cmd_data(const uint8_t *c_data, size_t length) { this->dc_pin_->digital_write(false); this->enable(); this->write_byte(c_data[0]); @@ -121,7 +121,7 @@ void WaveshareEPaper::cmd_data(const uint8_t *c_data, size_t length) { this->disable(); } -bool WaveshareEPaper::wait_until_idle_() { +bool WaveshareEPaperBase::wait_until_idle_() { if (this->busy_pin_ == nullptr || !this->busy_pin_->digital_read()) { return true; } @@ -136,7 +136,7 @@ bool WaveshareEPaper::wait_until_idle_() { } return true; } -void WaveshareEPaper::update() { +void WaveshareEPaperBase::update() { this->do_update_(); this->display(); } @@ -159,20 +159,51 @@ void HOT WaveshareEPaper::draw_absolute_pixel_internal(int x, int y, Color color this->buffer_[pos] &= ~(0x80 >> subpos); } } + uint32_t WaveshareEPaper::get_buffer_length_() { return this->get_width_controller() * this->get_height_internal() / 8u; +} // just a black buffer +uint32_t WaveshareEPaperBWR::get_buffer_length_() { + return this->get_width_controller() * this->get_height_internal() / 4u; +} // black and red buffer + +void WaveshareEPaperBWR::fill(Color color) { + this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); } -void WaveshareEPaper::start_command_() { +void HOT WaveshareEPaperBWR::draw_absolute_pixel_internal(int x, int y, Color color) { + if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0) + return; + + const uint32_t buf_half_len = this->get_buffer_length_() / 2u; + + const uint32_t pos = (x + y * this->get_width_internal()) / 8u; + const uint8_t subpos = x & 0x07; + // flip logic + if (color.is_on()) { + this->buffer_[pos] |= 0x80 >> subpos; + } else { + this->buffer_[pos] &= ~(0x80 >> subpos); + } + + // draw red pixels only, if the color contains red only + if (((color.red > 0) && (color.green == 0) && (color.blue == 0))) { + this->buffer_[pos + buf_half_len] |= 0x80 >> subpos; + } else { + this->buffer_[pos + buf_half_len] &= ~(0x80 >> subpos); + } +} + +void WaveshareEPaperBase::start_command_() { this->dc_pin_->digital_write(false); this->enable(); } -void WaveshareEPaper::end_command_() { this->disable(); } -void WaveshareEPaper::start_data_() { +void WaveshareEPaperBase::end_command_() { this->disable(); } +void WaveshareEPaperBase::start_data_() { this->dc_pin_->digital_write(true); this->enable(); } -void WaveshareEPaper::end_data_() { this->disable(); } -void WaveshareEPaper::on_safe_shutdown() { this->deep_sleep(); } +void WaveshareEPaperBase::end_data_() { this->disable(); } +void WaveshareEPaperBase::on_safe_shutdown() { this->deep_sleep(); } // ======================================================== // Type A @@ -493,7 +524,7 @@ uint32_t WaveshareEPaperTypeA::idle_timeout_() { case TTGO_EPAPER_2_13_IN_B1: return 2500; default: - return WaveshareEPaper::idle_timeout_(); + return WaveshareEPaperBase::idle_timeout_(); } } @@ -699,6 +730,246 @@ void WaveshareEPaper2P7InV2::dump_config() { LOG_UPDATE_INTERVAL(this); } +// ======================================================== +// 2.7inch_e-paper_b +// ======================================================== +// Datasheet: +// - https://www.waveshare.com/w/upload/d/d8/2.7inch-e-paper-b-specification.pdf +// - https://github.com/waveshare/e-Paper/blob/master/RaspberryPi_JetsonNano/c/lib/e-Paper/EPD_2in7b.c + +static const uint8_t LUT_VCOM_DC_2_7B[44] = {0x00, 0x00, 0x00, 0x1A, 0x1A, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x0A, + 0x00, 0x00, 0x08, 0x00, 0x0E, 0x01, 0x0E, 0x01, 0x10, 0x00, 0x0A, + 0x0A, 0x00, 0x00, 0x08, 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, 0x00, + 0x03, 0x0E, 0x00, 0x00, 0x0A, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01}; + +static const uint8_t LUT_WHITE_TO_WHITE_2_7B[42] = {0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, 0x40, 0x0A, 0x0A, 0x00, 0x00, + 0x08, 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, 0x80, 0x0A, 0x0A, 0x00, + 0x00, 0x08, 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, 0x00, 0x03, 0x0E, + 0x00, 0x00, 0x0A, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01}; + +static const uint8_t LUT_BLACK_TO_WHITE_2_7B[42] = {0xA0, 0x1A, 0x1A, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x0A, 0x00, 0x00, + 0x08, 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, 0x90, 0x0A, 0x0A, 0x00, + 0x00, 0x08, 0xB0, 0x04, 0x10, 0x00, 0x00, 0x05, 0xB0, 0x03, 0x0E, + 0x00, 0x00, 0x0A, 0xC0, 0x23, 0x00, 0x00, 0x00, 0x01}; + +static const uint8_t LUT_WHITE_TO_BLACK_2_7B[] = {0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, 0x20, 0x0A, 0x0A, 0x00, 0x00, + 0x08, 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, 0x10, 0x0A, 0x0A, 0x00, + 0x00, 0x08, 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, 0x00, 0x03, 0x0E, + 0x00, 0x00, 0x0A, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01}; + +static const uint8_t LUT_BLACK_TO_BLACK_2_7B[42] = {0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, 0x40, 0x0A, 0x0A, 0x00, 0x00, + 0x08, 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, 0x80, 0x0A, 0x0A, 0x00, + 0x00, 0x08, 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, 0x00, 0x03, 0x0E, + 0x00, 0x00, 0x0A, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01}; + +void WaveshareEPaper2P7InB::initialize() { + this->reset_(); + + // command power on + this->command(0x04); + this->wait_until_idle_(); + delay(10); + + // Command panel setting + this->command(0x00); + this->data(0xAF); // KW-BF KWR-AF BWROTP 0f + // command pll control + this->command(0x30); + this->data(0x3A); // 3A 100HZ 29 150Hz 39 200HZ 31 171HZ + + // command power setting + this->command(0x01); + this->data(0x03); // VDS_EN, VDG_EN + this->data(0x00); // VCOM_HV, VGHL_LV[1], VGHL_LV[0] + this->data(0x2B); // VDH + this->data(0x2B); // VDL + this->data(0x09); // VDHR + + // command booster soft start + this->command(0x06); + this->data(0x07); + this->data(0x07); + this->data(0x17); + + // Power optimization - ??? + this->command(0xF8); + this->data(0x60); + this->data(0xA5); + this->command(0xF8); + this->data(0x89); + this->data(0xA5); + this->command(0xF8); + this->data(0x90); + this->data(0x00); + this->command(0xF8); + this->data(0x93); + this->data(0x2A); + this->command(0xF8); + this->data(0x73); + this->data(0x41); + + // COMMAND VCM DC SETTING + this->command(0x82); + this->data(0x12); + + // VCOM_AND_DATA_INTERVAL_SETTING + this->command(0x50); + this->data(0x87); // define by OTP + + delay(2); + // COMMAND LUT FOR VCOM + this->command(0x20); + for (uint8_t i : LUT_VCOM_DC_2_7B) + this->data(i); + // COMMAND LUT WHITE TO WHITE + this->command(0x21); + for (uint8_t i : LUT_WHITE_TO_WHITE_2_7B) + this->data(i); + // COMMAND LUT BLACK TO WHITE + this->command(0x22); + for (uint8_t i : LUT_BLACK_TO_WHITE_2_7B) + this->data(i); + // COMMAND LUT WHITE TO BLACK + this->command(0x23); + for (uint8_t i : LUT_WHITE_TO_BLACK_2_7B) { + this->data(i); + } + // COMMAND LUT BLACK TO BLACK + this->command(0x24); + + for (uint8_t i : LUT_BLACK_TO_BLACK_2_7B) { + this->data(i); + } + + delay(2); +} + +void HOT WaveshareEPaper2P7InB::display() { + uint32_t buf_len_half = this->get_buffer_length_() >> 1; + this->initialize(); + + // TCON_RESOLUTION + this->command(0x61); + this->data(this->get_width_internal() >> 8); + this->data(this->get_width_internal() & 0xff); // 176 + this->data(this->get_height_internal() >> 8); + this->data(this->get_height_internal() & 0xff); // 264 + + // COMMAND DATA START TRANSMISSION 1 (BLACK) + this->command(0x10); + delay(2); + for (uint32_t i = 0; i < buf_len_half; i++) { + this->data(this->buffer_[i]); + } + this->command(0x11); + delay(2); + + // COMMAND DATA START TRANSMISSION 2 (RED) + this->command(0x13); + delay(2); + for (uint32_t i = buf_len_half; i < buf_len_half * 2u; i++) { + this->data(this->buffer_[i]); + } + this->command(0x11); + + delay(2); + + // COMMAND DISPLAY REFRESH + this->command(0x12); + this->wait_until_idle_(); + + this->deep_sleep(); +} +int WaveshareEPaper2P7InB::get_width_internal() { return 176; } +int WaveshareEPaper2P7InB::get_height_internal() { return 264; } +void WaveshareEPaper2P7InB::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 2.7in B"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + +// ======================================================== +// 2.7inch_e-paper_b_v2 +// ======================================================== +// Datasheet: +// - https://www.waveshare.com/w/upload/7/7b/2.7inch-e-paper-b-v2-specification.pdf +// - https://github.com/waveshare/e-Paper/blob/master/RaspberryPi_JetsonNano/c/lib/e-Paper/EPD_2in7b_V2.c + +void WaveshareEPaper2P7InBV2::initialize() { + this->reset_(); + + this->wait_until_idle_(); + this->command(0x12); + this->wait_until_idle_(); + + this->command(0x00); + this->data(0x27); + this->data(0x01); + this->data(0x00); + + this->command(0x11); + this->data(0x03); + + // self.SetWindows(0, 0, self.width-1, self.height-1) + // SetWindows(self, Xstart, Ystart, Xend, Yend): + + uint32_t xend = this->get_width_internal() - 1; + uint32_t yend = this->get_height_internal() - 1; + this->command(0x44); + this->data(0x00); + this->data((xend >> 3) & 0xff); + + this->command(0x45); + this->data(0x00); + this->data(0x00); + this->data(yend & 0xff); + this->data((yend >> 8) & 0xff); + + // SetCursor(self, Xstart, Ystart): + this->command(0x4E); + this->data(0x00); + this->command(0x4F); + this->data(0x00); + this->data(0x00); +} + +void HOT WaveshareEPaper2P7InBV2::display() { + uint32_t buf_len = this->get_buffer_length_(); + // COMMAND DATA START TRANSMISSION 1 (BLACK) + this->command(0x24); + delay(2); + for (uint32_t i = 0; i < buf_len; i++) { + this->data(this->buffer_[i]); + } + delay(2); + + // COMMAND DATA START TRANSMISSION 2 (RED) + this->command(0x26); + delay(2); + for (uint32_t i = 0; i < buf_len; i++) { + this->data(this->buffer_[i]); + } + + delay(2); + + this->command(0x20); + + this->wait_until_idle_(); +} +int WaveshareEPaper2P7InBV2::get_width_internal() { return 176; } +int WaveshareEPaper2P7InBV2::get_height_internal() { return 264; } +void WaveshareEPaper2P7InBV2::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 2.7in B V2"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + // ======================================================== // 2.90in Type B (LUT from OTP) // Datasheet: diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 0f1144ccba..47f0cb27b6 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -7,9 +7,9 @@ namespace esphome { namespace waveshare_epaper { -class WaveshareEPaper : public display::DisplayBuffer, - public spi::SPIDevice { +class WaveshareEPaperBase : public display::DisplayBuffer, + public spi::SPIDevice { public: void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; } float get_setup_priority() const override; @@ -27,8 +27,6 @@ class WaveshareEPaper : public display::DisplayBuffer, void update() override; - void fill(Color color) override; - void setup() override { this->setup_pins_(); this->initialize(); @@ -36,11 +34,7 @@ class WaveshareEPaper : public display::DisplayBuffer, void on_safe_shutdown() override; - display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; } - protected: - void draw_absolute_pixel_internal(int x, int y, Color color) override; - bool wait_until_idle_(); void setup_pins_(); @@ -56,7 +50,7 @@ class WaveshareEPaper : public display::DisplayBuffer, virtual int get_width_controller() { return this->get_width_internal(); }; - uint32_t get_buffer_length_(); + virtual uint32_t get_buffer_length_() = 0; // NOLINT(readability-identifier-naming) uint32_t reset_duration_{200}; void start_command_(); @@ -70,6 +64,28 @@ class WaveshareEPaper : public display::DisplayBuffer, virtual uint32_t idle_timeout_() { return 1000u; } // NOLINT(readability-identifier-naming) }; +class WaveshareEPaper : public WaveshareEPaperBase { + public: + void fill(Color color) override; + + display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; } + + protected: + void draw_absolute_pixel_internal(int x, int y, Color color) override; + uint32_t get_buffer_length_() override; +}; + +class WaveshareEPaperBWR : public WaveshareEPaperBase { + public: + void fill(Color color) override; + + display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } + + protected: + void draw_absolute_pixel_internal(int x, int y, Color color) override; + uint32_t get_buffer_length_() override; +}; + enum WaveshareEPaperTypeAModel { WAVESHARE_EPAPER_1_54_IN = 0, WAVESHARE_EPAPER_1_54_IN_V2, @@ -134,6 +150,8 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { enum WaveshareEPaperTypeBModel { WAVESHARE_EPAPER_2_7_IN = 0, + WAVESHARE_EPAPER_2_7_IN_B, + WAVESHARE_EPAPER_2_7_IN_B_V2, WAVESHARE_EPAPER_4_2_IN, WAVESHARE_EPAPER_4_2_IN_B_V2, WAVESHARE_EPAPER_7_5_IN, @@ -155,6 +173,68 @@ class WaveshareEPaper2P7In : public WaveshareEPaper { this->data(0xA5); // check byte } + protected: + int get_width_internal() override; + int get_height_internal() override; +}; + +class WaveshareEPaper2P7InB : public WaveshareEPaperBWR { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + // COMMAND VCOM_AND_DATA_INTERVAL_SETTING + this->command(0x50); + // COMMAND POWER OFF + this->command(0x02); + this->wait_until_idle_(); + // COMMAND DEEP SLEEP + this->command(0x07); // deep sleep + this->data(0xA5); // check byte + } + + protected: + int get_width_internal() override; + int get_height_internal() override; +}; + +class WaveshareEPaper2P7InBV2 : public WaveshareEPaperBWR { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + // COMMAND DEEP SLEEP + this->command(0x10); + this->data(0x01); + } + + protected: + int get_width_internal() override; + int get_height_internal() override; +}; + +class GDEY029T94 : public WaveshareEPaper { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + // COMMAND DEEP SLEEP + this->command(0x07); + this->data(0xA5); // check byte + } + protected: int get_width_internal() override; @@ -177,26 +257,6 @@ class WaveshareEPaper2P7InV2 : public WaveshareEPaper { int get_height_internal() override; }; -class GDEY029T94 : public WaveshareEPaper { - public: - void initialize() override; - - void display() override; - - void dump_config() override; - - void deep_sleep() override { - // COMMAND DEEP SLEEP - this->command(0x07); - this->data(0xA5); // check byte - } - - protected: - int get_width_internal() override; - - int get_height_internal() override; -}; - class GDEW0154M09 : public WaveshareEPaper { public: void initialize() override; diff --git a/tests/test4.yaml b/tests/test4.yaml index 65068871dd..f68406298e 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -671,6 +671,40 @@ display: full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + spi_id: spi_id_1 + cs_pin: + allow_other_uses: true + number: GPIO23 + dc_pin: + allow_other_uses: true + number: GPIO23 + busy_pin: + allow_other_uses: true + number: GPIO23 + reset_pin: + allow_other_uses: true + number: GPIO23 + model: 2.70in-b + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + spi_id: spi_id_1 + cs_pin: + allow_other_uses: true + number: GPIO23 + dc_pin: + allow_other_uses: true + number: GPIO23 + busy_pin: + allow_other_uses: true + number: GPIO23 + reset_pin: + allow_other_uses: true + number: GPIO23 + model: 2.70in-bv2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: waveshare_epaper spi_id: spi_id_1 cs_pin: From 0fa0904bc53aa72e2ab291ba0840e73738ebef18 Mon Sep 17 00:00:00 2001 From: esphomebot Date: Thu, 1 Feb 2024 01:25:47 +1300 Subject: [PATCH 174/193] Synchronise Device Classes from Home Assistant (#6158) --- esphome/components/number/__init__.py | 2 ++ esphome/components/sensor/__init__.py | 2 ++ esphome/const.py | 1 + 3 files changed, 5 insertions(+) diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index 07164be5ce..ecc2ab2ee7 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -62,6 +62,7 @@ from esphome.const import ( DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLUME, + DEVICE_CLASS_VOLUME_FLOW_RATE, DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_WATER, DEVICE_CLASS_WEIGHT, @@ -117,6 +118,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLUME, + DEVICE_CLASS_VOLUME_FLOW_RATE, DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_WATER, DEVICE_CLASS_WEIGHT, diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index b3bf533695..46295fe958 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -82,6 +82,7 @@ from esphome.const import ( DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLUME, + DEVICE_CLASS_VOLUME_FLOW_RATE, DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_WATER, DEVICE_CLASS_WEIGHT, @@ -141,6 +142,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLUME, + DEVICE_CLASS_VOLUME_FLOW_RATE, DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_WATER, DEVICE_CLASS_WEIGHT, diff --git a/esphome/const.py b/esphome/const.py index a95d1d5ac3..8f9606c5fd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1082,6 +1082,7 @@ DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds" DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts" DEVICE_CLASS_VOLTAGE = "voltage" DEVICE_CLASS_VOLUME = "volume" +DEVICE_CLASS_VOLUME_FLOW_RATE = "volume_flow_rate" DEVICE_CLASS_VOLUME_STORAGE = "volume_storage" DEVICE_CLASS_WATER = "water" DEVICE_CLASS_WEIGHT = "weight" From b28821d8463db29d2298eade023a4b0a0f7e880e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roy?= Date: Sun, 4 Feb 2024 17:57:11 -0800 Subject: [PATCH 175/193] dfrobot_sen0395: Use setLatency instead of outputLatency (#5665) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../components/dfrobot_sen0395/__init__.py | 11 ++++--- .../components/dfrobot_sen0395/automation.h | 2 +- .../components/dfrobot_sen0395/commands.cpp | 30 +++++++------------ esphome/components/dfrobot_sen0395/commands.h | 4 +-- 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/esphome/components/dfrobot_sen0395/__init__.py b/esphome/components/dfrobot_sen0395/__init__.py index e772db5a15..2197ee5ef8 100644 --- a/esphome/components/dfrobot_sen0395/__init__.py +++ b/esphome/components/dfrobot_sen0395/__init__.py @@ -1,7 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome import core from esphome.automation import maybe_simple_id from esphome.const import CONF_ID from esphome.components import uart @@ -101,7 +100,7 @@ def range_segment_list(input): largest_distance = -1 for distance in input: - if isinstance(distance, core.Lambda): + if isinstance(distance, cv.Lambda): continue m = cv.distance(distance) if m > 9: @@ -128,14 +127,14 @@ MMWAVE_SETTINGS_SCHEMA = cv.Schema( cv.Optional(CONF_OUTPUT_LATENCY): { cv.Required(CONF_DELAY_AFTER_DETECT): cv.templatable( cv.All( - cv.positive_time_period, - cv.Range(max=core.TimePeriod(seconds=1638.375)), + cv.positive_time_period_milliseconds, + cv.Range(max=cv.TimePeriod(seconds=1638.375)), ) ), cv.Required(CONF_DELAY_AFTER_DISAPPEAR): cv.templatable( cv.All( - cv.positive_time_period, - cv.Range(max=core.TimePeriod(seconds=1638.375)), + cv.positive_time_period_milliseconds, + cv.Range(max=cv.TimePeriod(seconds=1638.375)), ) ), }, diff --git a/esphome/components/dfrobot_sen0395/automation.h b/esphome/components/dfrobot_sen0395/automation.h index 1f942c02e4..3f69e482b7 100644 --- a/esphome/components/dfrobot_sen0395/automation.h +++ b/esphome/components/dfrobot_sen0395/automation.h @@ -50,7 +50,7 @@ class DfrobotSen0395SettingsAction : public Action, public Parenteddelay_after_detect_.value(x...); float disappear = this->delay_after_disappear_.value(x...); if (detect >= 0 && disappear >= 0) { - this->parent_->enqueue(make_unique(detect, disappear)); + this->parent_->enqueue(make_unique(detect, disappear)); } } if (this->start_after_power_on_.has_value()) { diff --git a/esphome/components/dfrobot_sen0395/commands.cpp b/esphome/components/dfrobot_sen0395/commands.cpp index 3a89b2b71e..2c60fb3449 100644 --- a/esphome/components/dfrobot_sen0395/commands.cpp +++ b/esphome/components/dfrobot_sen0395/commands.cpp @@ -1,5 +1,7 @@ #include "commands.h" +#include + #include "esphome/core/log.h" #include "dfrobot_sen0395.h" @@ -194,32 +196,22 @@ uint8_t DetRangeCfgCommand::on_message(std::string &message) { return 0; // Command not done yet. } -OutputLatencyCommand::OutputLatencyCommand(float delay_after_detection, float delay_after_disappear) { - delay_after_detection = round(delay_after_detection / 0.025) * 0.025; - delay_after_disappear = round(delay_after_disappear / 0.025) * 0.025; - if (delay_after_detection < 0) - delay_after_detection = 0; - if (delay_after_detection > 1638.375) - delay_after_detection = 1638.375; - if (delay_after_disappear < 0) - delay_after_disappear = 0; - if (delay_after_disappear > 1638.375) - delay_after_disappear = 1638.375; - - this->delay_after_detection_ = delay_after_detection; - this->delay_after_disappear_ = delay_after_disappear; - - this->cmd_ = str_sprintf("outputLatency -1 %.0f %.0f", delay_after_detection / 0.025, delay_after_disappear / 0.025); +SetLatencyCommand::SetLatencyCommand(float delay_after_detection, float delay_after_disappear) { + delay_after_detection = std::round(delay_after_detection / 0.025f) * 0.025f; + delay_after_disappear = std::round(delay_after_disappear / 0.025f) * 0.025f; + this->delay_after_detection_ = clamp(delay_after_detection, 0.0f, 1638.375f); + this->delay_after_disappear_ = clamp(delay_after_disappear, 0.0f, 1638.375f); + this->cmd_ = str_sprintf("setLatency %.03f %.03f", this->delay_after_detection_, this->delay_after_disappear_); }; -uint8_t OutputLatencyCommand::on_message(std::string &message) { +uint8_t SetLatencyCommand::on_message(std::string &message) { if (message == "sensor is not stopped") { ESP_LOGE(TAG, "Cannot configure output latency. Sensor is not stopped!"); return 1; // Command done } else if (message == "Done") { ESP_LOGI(TAG, "Updated output latency config:"); - ESP_LOGI(TAG, "Signal that someone was detected is delayed by %.02fs.", this->delay_after_detection_); - ESP_LOGI(TAG, "Signal that nobody is detected anymore is delayed by %.02fs.", this->delay_after_disappear_); + ESP_LOGI(TAG, "Signal that someone was detected is delayed by %.03f s.", this->delay_after_detection_); + ESP_LOGI(TAG, "Signal that nobody is detected anymore is delayed by %.03f s.", this->delay_after_disappear_); ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str()); return 1; // Command done } diff --git a/esphome/components/dfrobot_sen0395/commands.h b/esphome/components/dfrobot_sen0395/commands.h index 7426d9732a..cf3ba50be0 100644 --- a/esphome/components/dfrobot_sen0395/commands.h +++ b/esphome/components/dfrobot_sen0395/commands.h @@ -62,9 +62,9 @@ class DetRangeCfgCommand : public Command { // TODO: Set min max values in component, so they can be published as sensor. }; -class OutputLatencyCommand : public Command { +class SetLatencyCommand : public Command { public: - OutputLatencyCommand(float delay_after_detection, float delay_after_disappear); + SetLatencyCommand(float delay_after_detection, float delay_after_disappear); uint8_t on_message(std::string &message) override; protected: From 5e9741f51c92482935357de359529daafeb5efad Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 4 Feb 2024 20:29:18 -0600 Subject: [PATCH 176/193] Add some components to the new testing framework (A part 1) (#6142) --- .../components/a01nyub/test.esp32-c3-idf.yaml | 13 +++++++ tests/components/a01nyub/test.esp32-c3.yaml | 13 +++++++ tests/components/a01nyub/test.esp32-idf.yaml | 13 +++++++ tests/components/a01nyub/test.esp32.yaml | 13 +++++++ tests/components/a01nyub/test.esp8266.yaml | 13 +++++++ tests/components/a01nyub/test.rp2040.yaml | 13 +++++++ .../components/a02yyuw/test.esp32-c3-idf.yaml | 13 +++++++ tests/components/a02yyuw/test.esp32-c3.yaml | 13 +++++++ tests/components/a02yyuw/test.esp32-idf.yaml | 13 +++++++ tests/components/a02yyuw/test.esp32.yaml | 13 +++++++ tests/components/a02yyuw/test.esp8266.yaml | 13 +++++++ tests/components/a02yyuw/test.rp2040.yaml | 13 +++++++ tests/components/a4988/test.esp32-c3-idf.yaml | 12 +++++++ tests/components/a4988/test.esp32-c3.yaml | 12 +++++++ tests/components/a4988/test.esp32-idf.yaml | 12 +++++++ tests/components/a4988/test.esp32.yaml | 12 +++++++ tests/components/a4988/test.esp8266.yaml | 12 +++++++ tests/components/a4988/test.rp2040.yaml | 12 +++++++ .../absolute_humidity/test.esp32-c3-idf.yaml | 21 +++++++++++ .../absolute_humidity/test.esp32-c3.yaml | 21 +++++++++++ .../absolute_humidity/test.esp32-idf.yaml | 21 +++++++++++ .../absolute_humidity/test.esp32.yaml | 21 +++++++++++ .../absolute_humidity/test.esp8266.yaml | 21 +++++++++++ .../absolute_humidity/test.rp2040.yaml | 21 +++++++++++ tests/components/ac_dimmer/test.esp32-c3.yaml | 7 ++++ tests/components/ac_dimmer/test.esp32.yaml | 7 ++++ tests/components/ac_dimmer/test.esp8266.yaml | 7 ++++ tests/components/ac_dimmer/test.rp2040.yaml | 7 ++++ .../adc128s102/test.esp32-c3-idf.yaml | 14 ++++++++ .../components/adc128s102/test.esp32-c3.yaml | 14 ++++++++ .../components/adc128s102/test.esp32-idf.yaml | 14 ++++++++ tests/components/adc128s102/test.esp32.yaml | 14 ++++++++ tests/components/adc128s102/test.esp8266.yaml | 14 ++++++++ tests/components/adc128s102/test.rp2040.yaml | 14 ++++++++ .../addressable_light/test.esp32-c3-idf.yaml | 31 ++++++++++++++++ .../addressable_light/test.esp32-c3.yaml | 31 ++++++++++++++++ .../addressable_light/test.esp32-idf.yaml | 31 ++++++++++++++++ .../addressable_light/test.esp32.yaml | 32 +++++++++++++++++ .../ade7953_i2c/test.esp32-c3-idf.yaml | 34 ++++++++++++++++++ .../components/ade7953_i2c/test.esp32-c3.yaml | 34 ++++++++++++++++++ .../ade7953_i2c/test.esp32-idf.yaml | 34 ++++++++++++++++++ tests/components/ade7953_i2c/test.esp32.yaml | 34 ++++++++++++++++++ .../components/ade7953_i2c/test.esp8266.yaml | 34 ++++++++++++++++++ tests/components/ade7953_i2c/test.rp2040.yaml | 34 ++++++++++++++++++ .../ade7953_spi/test.esp32-c3-idf.yaml | 36 +++++++++++++++++++ .../components/ade7953_spi/test.esp32-c3.yaml | 36 +++++++++++++++++++ .../ade7953_spi/test.esp32-idf.yaml | 36 +++++++++++++++++++ tests/components/ade7953_spi/test.esp32.yaml | 36 +++++++++++++++++++ .../components/ade7953_spi/test.esp8266.yaml | 36 +++++++++++++++++++ tests/components/ade7953_spi/test.rp2040.yaml | 36 +++++++++++++++++++ .../components/ads1115/test.esp32-c3-idf.yaml | 13 +++++++ tests/components/ads1115/test.esp32-c3.yaml | 13 +++++++ tests/components/ads1115/test.esp32-idf.yaml | 13 +++++++ tests/components/ads1115/test.esp32.yaml | 13 +++++++ tests/components/ads1115/test.esp8266.yaml | 13 +++++++ tests/components/ads1115/test.rp2040.yaml | 13 +++++++ tests/components/aht10/test.esp32-c3-idf.yaml | 11 ++++++ tests/components/aht10/test.esp32-c3.yaml | 11 ++++++ tests/components/aht10/test.esp32-idf.yaml | 11 ++++++ tests/components/aht10/test.esp32.yaml | 11 ++++++ tests/components/aht10/test.esp8266.yaml | 11 ++++++ tests/components/aht10/test.rp2040.yaml | 11 ++++++ .../test.esp32-c3-idf.yaml | 22 ++++++++++++ .../airthings_wave_mini/test.esp32-c3.yaml | 22 ++++++++++++ .../airthings_wave_mini/test.esp32-idf.yaml | 22 ++++++++++++ .../airthings_wave_mini/test.esp32.yaml | 22 ++++++++++++ .../test.esp32-c3-idf.yaml | 28 +++++++++++++++ .../airthings_wave_plus/test.esp32-c3.yaml | 28 +++++++++++++++ .../airthings_wave_plus/test.esp32-idf.yaml | 28 +++++++++++++++ .../airthings_wave_plus/test.esp32.yaml | 28 +++++++++++++++ 70 files changed, 1355 insertions(+) create mode 100644 tests/components/a01nyub/test.esp32-c3-idf.yaml create mode 100644 tests/components/a01nyub/test.esp32-c3.yaml create mode 100644 tests/components/a01nyub/test.esp32-idf.yaml create mode 100644 tests/components/a01nyub/test.esp32.yaml create mode 100644 tests/components/a01nyub/test.esp8266.yaml create mode 100644 tests/components/a01nyub/test.rp2040.yaml create mode 100644 tests/components/a02yyuw/test.esp32-c3-idf.yaml create mode 100644 tests/components/a02yyuw/test.esp32-c3.yaml create mode 100644 tests/components/a02yyuw/test.esp32-idf.yaml create mode 100644 tests/components/a02yyuw/test.esp32.yaml create mode 100644 tests/components/a02yyuw/test.esp8266.yaml create mode 100644 tests/components/a02yyuw/test.rp2040.yaml create mode 100644 tests/components/a4988/test.esp32-c3-idf.yaml create mode 100644 tests/components/a4988/test.esp32-c3.yaml create mode 100644 tests/components/a4988/test.esp32-idf.yaml create mode 100644 tests/components/a4988/test.esp32.yaml create mode 100644 tests/components/a4988/test.esp8266.yaml create mode 100644 tests/components/a4988/test.rp2040.yaml create mode 100644 tests/components/absolute_humidity/test.esp32-c3-idf.yaml create mode 100644 tests/components/absolute_humidity/test.esp32-c3.yaml create mode 100644 tests/components/absolute_humidity/test.esp32-idf.yaml create mode 100644 tests/components/absolute_humidity/test.esp32.yaml create mode 100644 tests/components/absolute_humidity/test.esp8266.yaml create mode 100644 tests/components/absolute_humidity/test.rp2040.yaml create mode 100644 tests/components/ac_dimmer/test.esp32-c3.yaml create mode 100644 tests/components/ac_dimmer/test.esp32.yaml create mode 100644 tests/components/ac_dimmer/test.esp8266.yaml create mode 100644 tests/components/ac_dimmer/test.rp2040.yaml create mode 100644 tests/components/adc128s102/test.esp32-c3-idf.yaml create mode 100644 tests/components/adc128s102/test.esp32-c3.yaml create mode 100644 tests/components/adc128s102/test.esp32-idf.yaml create mode 100644 tests/components/adc128s102/test.esp32.yaml create mode 100644 tests/components/adc128s102/test.esp8266.yaml create mode 100644 tests/components/adc128s102/test.rp2040.yaml create mode 100644 tests/components/addressable_light/test.esp32-c3-idf.yaml create mode 100644 tests/components/addressable_light/test.esp32-c3.yaml create mode 100644 tests/components/addressable_light/test.esp32-idf.yaml create mode 100644 tests/components/addressable_light/test.esp32.yaml create mode 100644 tests/components/ade7953_i2c/test.esp32-c3-idf.yaml create mode 100644 tests/components/ade7953_i2c/test.esp32-c3.yaml create mode 100644 tests/components/ade7953_i2c/test.esp32-idf.yaml create mode 100644 tests/components/ade7953_i2c/test.esp32.yaml create mode 100644 tests/components/ade7953_i2c/test.esp8266.yaml create mode 100644 tests/components/ade7953_i2c/test.rp2040.yaml create mode 100644 tests/components/ade7953_spi/test.esp32-c3-idf.yaml create mode 100644 tests/components/ade7953_spi/test.esp32-c3.yaml create mode 100644 tests/components/ade7953_spi/test.esp32-idf.yaml create mode 100644 tests/components/ade7953_spi/test.esp32.yaml create mode 100644 tests/components/ade7953_spi/test.esp8266.yaml create mode 100644 tests/components/ade7953_spi/test.rp2040.yaml create mode 100644 tests/components/ads1115/test.esp32-c3-idf.yaml create mode 100644 tests/components/ads1115/test.esp32-c3.yaml create mode 100644 tests/components/ads1115/test.esp32-idf.yaml create mode 100644 tests/components/ads1115/test.esp32.yaml create mode 100644 tests/components/ads1115/test.esp8266.yaml create mode 100644 tests/components/ads1115/test.rp2040.yaml create mode 100644 tests/components/aht10/test.esp32-c3-idf.yaml create mode 100644 tests/components/aht10/test.esp32-c3.yaml create mode 100644 tests/components/aht10/test.esp32-idf.yaml create mode 100644 tests/components/aht10/test.esp32.yaml create mode 100644 tests/components/aht10/test.esp8266.yaml create mode 100644 tests/components/aht10/test.rp2040.yaml create mode 100644 tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml create mode 100644 tests/components/airthings_wave_mini/test.esp32-c3.yaml create mode 100644 tests/components/airthings_wave_mini/test.esp32-idf.yaml create mode 100644 tests/components/airthings_wave_mini/test.esp32.yaml create mode 100644 tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml create mode 100644 tests/components/airthings_wave_plus/test.esp32-c3.yaml create mode 100644 tests/components/airthings_wave_plus/test.esp32-idf.yaml create mode 100644 tests/components/airthings_wave_plus/test.esp32.yaml diff --git a/tests/components/a01nyub/test.esp32-c3-idf.yaml b/tests/components/a01nyub/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..3132f77136 --- /dev/null +++ b/tests/components/a01nyub/test.esp32-c3-idf.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a01nyub + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: a01nyub + id: a01nyub_sensor + name: a01nyub Distance + uart_id: uart_a01nyub diff --git a/tests/components/a01nyub/test.esp32-c3.yaml b/tests/components/a01nyub/test.esp32-c3.yaml new file mode 100644 index 0000000000..3132f77136 --- /dev/null +++ b/tests/components/a01nyub/test.esp32-c3.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a01nyub + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: a01nyub + id: a01nyub_sensor + name: a01nyub Distance + uart_id: uart_a01nyub diff --git a/tests/components/a01nyub/test.esp32-idf.yaml b/tests/components/a01nyub/test.esp32-idf.yaml new file mode 100644 index 0000000000..79fc9c5fbf --- /dev/null +++ b/tests/components/a01nyub/test.esp32-idf.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a01nyub + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 9600 + +sensor: + - platform: a01nyub + id: a01nyub_sensor + name: a01nyub Distance + uart_id: uart_a01nyub diff --git a/tests/components/a01nyub/test.esp32.yaml b/tests/components/a01nyub/test.esp32.yaml new file mode 100644 index 0000000000..79fc9c5fbf --- /dev/null +++ b/tests/components/a01nyub/test.esp32.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a01nyub + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 9600 + +sensor: + - platform: a01nyub + id: a01nyub_sensor + name: a01nyub Distance + uart_id: uart_a01nyub diff --git a/tests/components/a01nyub/test.esp8266.yaml b/tests/components/a01nyub/test.esp8266.yaml new file mode 100644 index 0000000000..3132f77136 --- /dev/null +++ b/tests/components/a01nyub/test.esp8266.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a01nyub + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: a01nyub + id: a01nyub_sensor + name: a01nyub Distance + uart_id: uart_a01nyub diff --git a/tests/components/a01nyub/test.rp2040.yaml b/tests/components/a01nyub/test.rp2040.yaml new file mode 100644 index 0000000000..3132f77136 --- /dev/null +++ b/tests/components/a01nyub/test.rp2040.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a01nyub + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: a01nyub + id: a01nyub_sensor + name: a01nyub Distance + uart_id: uart_a01nyub diff --git a/tests/components/a02yyuw/test.esp32-c3-idf.yaml b/tests/components/a02yyuw/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..76e1ad8ee1 --- /dev/null +++ b/tests/components/a02yyuw/test.esp32-c3-idf.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a02yyuw + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: a02yyuw + id: a02yyuw_sensor + name: a02yyuw Distance + uart_id: uart_a02yyuw diff --git a/tests/components/a02yyuw/test.esp32-c3.yaml b/tests/components/a02yyuw/test.esp32-c3.yaml new file mode 100644 index 0000000000..76e1ad8ee1 --- /dev/null +++ b/tests/components/a02yyuw/test.esp32-c3.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a02yyuw + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: a02yyuw + id: a02yyuw_sensor + name: a02yyuw Distance + uart_id: uart_a02yyuw diff --git a/tests/components/a02yyuw/test.esp32-idf.yaml b/tests/components/a02yyuw/test.esp32-idf.yaml new file mode 100644 index 0000000000..98d6a266b3 --- /dev/null +++ b/tests/components/a02yyuw/test.esp32-idf.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a02yyuw + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 9600 + +sensor: + - platform: a02yyuw + id: a02yyuw_sensor + name: a02yyuw Distance + uart_id: uart_a02yyuw diff --git a/tests/components/a02yyuw/test.esp32.yaml b/tests/components/a02yyuw/test.esp32.yaml new file mode 100644 index 0000000000..98d6a266b3 --- /dev/null +++ b/tests/components/a02yyuw/test.esp32.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a02yyuw + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 9600 + +sensor: + - platform: a02yyuw + id: a02yyuw_sensor + name: a02yyuw Distance + uart_id: uart_a02yyuw diff --git a/tests/components/a02yyuw/test.esp8266.yaml b/tests/components/a02yyuw/test.esp8266.yaml new file mode 100644 index 0000000000..76e1ad8ee1 --- /dev/null +++ b/tests/components/a02yyuw/test.esp8266.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a02yyuw + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: a02yyuw + id: a02yyuw_sensor + name: a02yyuw Distance + uart_id: uart_a02yyuw diff --git a/tests/components/a02yyuw/test.rp2040.yaml b/tests/components/a02yyuw/test.rp2040.yaml new file mode 100644 index 0000000000..76e1ad8ee1 --- /dev/null +++ b/tests/components/a02yyuw/test.rp2040.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_a02yyuw + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: a02yyuw + id: a02yyuw_sensor + name: a02yyuw Distance + uart_id: uart_a02yyuw diff --git a/tests/components/a4988/test.esp32-c3-idf.yaml b/tests/components/a4988/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..af4e4fa32b --- /dev/null +++ b/tests/components/a4988/test.esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +stepper: + - platform: a4988 + id: a4988_stepper + step_pin: + number: 2 + dir_pin: + number: 3 + sleep_pin: + number: 5 + max_speed: 250 steps/s + acceleration: 100 steps/s^2 + deceleration: 200 steps/s^2 diff --git a/tests/components/a4988/test.esp32-c3.yaml b/tests/components/a4988/test.esp32-c3.yaml new file mode 100644 index 0000000000..af4e4fa32b --- /dev/null +++ b/tests/components/a4988/test.esp32-c3.yaml @@ -0,0 +1,12 @@ +stepper: + - platform: a4988 + id: a4988_stepper + step_pin: + number: 2 + dir_pin: + number: 3 + sleep_pin: + number: 5 + max_speed: 250 steps/s + acceleration: 100 steps/s^2 + deceleration: 200 steps/s^2 diff --git a/tests/components/a4988/test.esp32-idf.yaml b/tests/components/a4988/test.esp32-idf.yaml new file mode 100644 index 0000000000..0ca5e3f504 --- /dev/null +++ b/tests/components/a4988/test.esp32-idf.yaml @@ -0,0 +1,12 @@ +stepper: + - platform: a4988 + id: a4988_stepper + step_pin: + number: 22 + dir_pin: + number: 23 + sleep_pin: + number: 25 + max_speed: 250 steps/s + acceleration: 100 steps/s^2 + deceleration: 200 steps/s^2 diff --git a/tests/components/a4988/test.esp32.yaml b/tests/components/a4988/test.esp32.yaml new file mode 100644 index 0000000000..0ca5e3f504 --- /dev/null +++ b/tests/components/a4988/test.esp32.yaml @@ -0,0 +1,12 @@ +stepper: + - platform: a4988 + id: a4988_stepper + step_pin: + number: 22 + dir_pin: + number: 23 + sleep_pin: + number: 25 + max_speed: 250 steps/s + acceleration: 100 steps/s^2 + deceleration: 200 steps/s^2 diff --git a/tests/components/a4988/test.esp8266.yaml b/tests/components/a4988/test.esp8266.yaml new file mode 100644 index 0000000000..f4c1886fc5 --- /dev/null +++ b/tests/components/a4988/test.esp8266.yaml @@ -0,0 +1,12 @@ +stepper: + - platform: a4988 + id: a4988_stepper + step_pin: + number: 1 + dir_pin: + number: 2 + sleep_pin: + number: 5 + max_speed: 250 steps/s + acceleration: 100 steps/s^2 + deceleration: 200 steps/s^2 diff --git a/tests/components/a4988/test.rp2040.yaml b/tests/components/a4988/test.rp2040.yaml new file mode 100644 index 0000000000..af4e4fa32b --- /dev/null +++ b/tests/components/a4988/test.rp2040.yaml @@ -0,0 +1,12 @@ +stepper: + - platform: a4988 + id: a4988_stepper + step_pin: + number: 2 + dir_pin: + number: 3 + sleep_pin: + number: 5 + max_speed: 250 steps/s + acceleration: 100 steps/s^2 + deceleration: 200 steps/s^2 diff --git a/tests/components/absolute_humidity/test.esp32-c3-idf.yaml b/tests/components/absolute_humidity/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..87a99f5206 --- /dev/null +++ b/tests/components/absolute_humidity/test.esp32-c3-idf.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: absolute_humidity + name: Absolute Humidity + temperature: template_temperature + humidity: template_humidity + - platform: template + id: template_humidity + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } diff --git a/tests/components/absolute_humidity/test.esp32-c3.yaml b/tests/components/absolute_humidity/test.esp32-c3.yaml new file mode 100644 index 0000000000..87a99f5206 --- /dev/null +++ b/tests/components/absolute_humidity/test.esp32-c3.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: absolute_humidity + name: Absolute Humidity + temperature: template_temperature + humidity: template_humidity + - platform: template + id: template_humidity + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } diff --git a/tests/components/absolute_humidity/test.esp32-idf.yaml b/tests/components/absolute_humidity/test.esp32-idf.yaml new file mode 100644 index 0000000000..87a99f5206 --- /dev/null +++ b/tests/components/absolute_humidity/test.esp32-idf.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: absolute_humidity + name: Absolute Humidity + temperature: template_temperature + humidity: template_humidity + - platform: template + id: template_humidity + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } diff --git a/tests/components/absolute_humidity/test.esp32.yaml b/tests/components/absolute_humidity/test.esp32.yaml new file mode 100644 index 0000000000..87a99f5206 --- /dev/null +++ b/tests/components/absolute_humidity/test.esp32.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: absolute_humidity + name: Absolute Humidity + temperature: template_temperature + humidity: template_humidity + - platform: template + id: template_humidity + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } diff --git a/tests/components/absolute_humidity/test.esp8266.yaml b/tests/components/absolute_humidity/test.esp8266.yaml new file mode 100644 index 0000000000..87a99f5206 --- /dev/null +++ b/tests/components/absolute_humidity/test.esp8266.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: absolute_humidity + name: Absolute Humidity + temperature: template_temperature + humidity: template_humidity + - platform: template + id: template_humidity + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } diff --git a/tests/components/absolute_humidity/test.rp2040.yaml b/tests/components/absolute_humidity/test.rp2040.yaml new file mode 100644 index 0000000000..87a99f5206 --- /dev/null +++ b/tests/components/absolute_humidity/test.rp2040.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: absolute_humidity + name: Absolute Humidity + temperature: template_temperature + humidity: template_humidity + - platform: template + id: template_humidity + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } diff --git a/tests/components/ac_dimmer/test.esp32-c3.yaml b/tests/components/ac_dimmer/test.esp32-c3.yaml new file mode 100644 index 0000000000..f411d376be --- /dev/null +++ b/tests/components/ac_dimmer/test.esp32-c3.yaml @@ -0,0 +1,7 @@ +output: + - platform: ac_dimmer + id: ac_dimmer_1 + gate_pin: + number: 5 + zero_cross_pin: + number: 6 diff --git a/tests/components/ac_dimmer/test.esp32.yaml b/tests/components/ac_dimmer/test.esp32.yaml new file mode 100644 index 0000000000..cc17201666 --- /dev/null +++ b/tests/components/ac_dimmer/test.esp32.yaml @@ -0,0 +1,7 @@ +output: + - platform: ac_dimmer + id: ac_dimmer_1 + gate_pin: + number: 12 + zero_cross_pin: + number: 13 diff --git a/tests/components/ac_dimmer/test.esp8266.yaml b/tests/components/ac_dimmer/test.esp8266.yaml new file mode 100644 index 0000000000..af18d11c5f --- /dev/null +++ b/tests/components/ac_dimmer/test.esp8266.yaml @@ -0,0 +1,7 @@ +output: + - platform: ac_dimmer + id: ac_dimmer_1 + gate_pin: + number: 5 + zero_cross_pin: + number: 4 diff --git a/tests/components/ac_dimmer/test.rp2040.yaml b/tests/components/ac_dimmer/test.rp2040.yaml new file mode 100644 index 0000000000..f411d376be --- /dev/null +++ b/tests/components/ac_dimmer/test.rp2040.yaml @@ -0,0 +1,7 @@ +output: + - platform: ac_dimmer + id: ac_dimmer_1 + gate_pin: + number: 5 + zero_cross_pin: + number: 6 diff --git a/tests/components/adc128s102/test.esp32-c3-idf.yaml b/tests/components/adc128s102/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..8edf745e58 --- /dev/null +++ b/tests/components/adc128s102/test.esp32-c3-idf.yaml @@ -0,0 +1,14 @@ +spi: + - id: spi_adc128s102 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +adc128s102: + cs_pin: 8 + id: adc128s102_adc + +sensor: + - platform: adc128s102 + id: adc128s102_channel_0 + channel: 0 diff --git a/tests/components/adc128s102/test.esp32-c3.yaml b/tests/components/adc128s102/test.esp32-c3.yaml new file mode 100644 index 0000000000..8edf745e58 --- /dev/null +++ b/tests/components/adc128s102/test.esp32-c3.yaml @@ -0,0 +1,14 @@ +spi: + - id: spi_adc128s102 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +adc128s102: + cs_pin: 8 + id: adc128s102_adc + +sensor: + - platform: adc128s102 + id: adc128s102_channel_0 + channel: 0 diff --git a/tests/components/adc128s102/test.esp32-idf.yaml b/tests/components/adc128s102/test.esp32-idf.yaml new file mode 100644 index 0000000000..005fbccc34 --- /dev/null +++ b/tests/components/adc128s102/test.esp32-idf.yaml @@ -0,0 +1,14 @@ +spi: + - id: spi_adc128s102 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +adc128s102: + cs_pin: 12 + id: adc128s102_adc + +sensor: + - platform: adc128s102 + id: adc128s102_channel_0 + channel: 0 diff --git a/tests/components/adc128s102/test.esp32.yaml b/tests/components/adc128s102/test.esp32.yaml new file mode 100644 index 0000000000..005fbccc34 --- /dev/null +++ b/tests/components/adc128s102/test.esp32.yaml @@ -0,0 +1,14 @@ +spi: + - id: spi_adc128s102 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +adc128s102: + cs_pin: 12 + id: adc128s102_adc + +sensor: + - platform: adc128s102 + id: adc128s102_channel_0 + channel: 0 diff --git a/tests/components/adc128s102/test.esp8266.yaml b/tests/components/adc128s102/test.esp8266.yaml new file mode 100644 index 0000000000..09a51caec1 --- /dev/null +++ b/tests/components/adc128s102/test.esp8266.yaml @@ -0,0 +1,14 @@ +spi: + - id: spi_adc128s102 + clk_pin: 14 + mosi_pin: 13 + miso_pin: 12 + +adc128s102: + cs_pin: 15 + id: adc128s102_adc + +sensor: + - platform: adc128s102 + id: adc128s102_channel_0 + channel: 0 diff --git a/tests/components/adc128s102/test.rp2040.yaml b/tests/components/adc128s102/test.rp2040.yaml new file mode 100644 index 0000000000..a7d54cbfe6 --- /dev/null +++ b/tests/components/adc128s102/test.rp2040.yaml @@ -0,0 +1,14 @@ +spi: + - id: spi_adc128s102 + clk_pin: 2 + mosi_pin: 3 + miso_pin: 4 + +adc128s102: + cs_pin: 5 + id: adc128s102_adc + +sensor: + - platform: adc128s102 + id: adc128s102_channel_0 + channel: 0 diff --git a/tests/components/addressable_light/test.esp32-c3-idf.yaml b/tests/components/addressable_light/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..f587113fac --- /dev/null +++ b/tests/components/addressable_light/test.esp32-c3-idf.yaml @@ -0,0 +1,31 @@ +light: + - platform: esp32_rmt_led_strip + id: led_matrix_32x8 + default_transition_length: 500ms + chipset: ws2812 + rgb_order: GRB + num_leds: 256 + pin: 2 + rmt_channel: 0 + +display: + - platform: addressable_light + id: led_matrix_32x8_display + addressable_light_id: led_matrix_32x8 + width: 32 + height: 8 + pixel_mapper: |- + if (x % 2 == 0) { + return (x * 8) + y; + } + return (x * 8) + (7 - y); + lambda: |- + Color red = Color(0xFF0000); + Color green = Color(0x00FF00); + Color blue = Color(0x0000FF); + it.rectangle(0, 0, it.get_width(), it.get_height(), red); + it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green); + it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue); + it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red); + rotation: 0° + update_interval: 16ms diff --git a/tests/components/addressable_light/test.esp32-c3.yaml b/tests/components/addressable_light/test.esp32-c3.yaml new file mode 100644 index 0000000000..f587113fac --- /dev/null +++ b/tests/components/addressable_light/test.esp32-c3.yaml @@ -0,0 +1,31 @@ +light: + - platform: esp32_rmt_led_strip + id: led_matrix_32x8 + default_transition_length: 500ms + chipset: ws2812 + rgb_order: GRB + num_leds: 256 + pin: 2 + rmt_channel: 0 + +display: + - platform: addressable_light + id: led_matrix_32x8_display + addressable_light_id: led_matrix_32x8 + width: 32 + height: 8 + pixel_mapper: |- + if (x % 2 == 0) { + return (x * 8) + y; + } + return (x * 8) + (7 - y); + lambda: |- + Color red = Color(0xFF0000); + Color green = Color(0x00FF00); + Color blue = Color(0x0000FF); + it.rectangle(0, 0, it.get_width(), it.get_height(), red); + it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green); + it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue); + it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red); + rotation: 0° + update_interval: 16ms diff --git a/tests/components/addressable_light/test.esp32-idf.yaml b/tests/components/addressable_light/test.esp32-idf.yaml new file mode 100644 index 0000000000..f587113fac --- /dev/null +++ b/tests/components/addressable_light/test.esp32-idf.yaml @@ -0,0 +1,31 @@ +light: + - platform: esp32_rmt_led_strip + id: led_matrix_32x8 + default_transition_length: 500ms + chipset: ws2812 + rgb_order: GRB + num_leds: 256 + pin: 2 + rmt_channel: 0 + +display: + - platform: addressable_light + id: led_matrix_32x8_display + addressable_light_id: led_matrix_32x8 + width: 32 + height: 8 + pixel_mapper: |- + if (x % 2 == 0) { + return (x * 8) + y; + } + return (x * 8) + (7 - y); + lambda: |- + Color red = Color(0xFF0000); + Color green = Color(0x00FF00); + Color blue = Color(0x0000FF); + it.rectangle(0, 0, it.get_width(), it.get_height(), red); + it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green); + it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue); + it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red); + rotation: 0° + update_interval: 16ms diff --git a/tests/components/addressable_light/test.esp32.yaml b/tests/components/addressable_light/test.esp32.yaml new file mode 100644 index 0000000000..f7717be610 --- /dev/null +++ b/tests/components/addressable_light/test.esp32.yaml @@ -0,0 +1,32 @@ +light: + - platform: fastled_clockless + id: led_matrix_32x8 + name: led_matrix_32x8 + chipset: WS2812B + pin: 2 + num_leds: 256 + rgb_order: GRB + default_transition_length: 0s + color_correct: [50%, 50%, 50%] + +display: + - platform: addressable_light + id: led_matrix_32x8_display + addressable_light_id: led_matrix_32x8 + width: 32 + height: 8 + pixel_mapper: |- + if (x % 2 == 0) { + return (x * 8) + y; + } + return (x * 8) + (7 - y); + lambda: |- + Color red = Color(0xFF0000); + Color green = Color(0x00FF00); + Color blue = Color(0x0000FF); + it.rectangle(0, 0, it.get_width(), it.get_height(), red); + it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green); + it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue); + it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red); + rotation: 0° + update_interval: 16ms diff --git a/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml b/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..d7b365a7e1 --- /dev/null +++ b/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml @@ -0,0 +1,34 @@ +i2c: + - id: i2c_ade7953 + scl: 5 + sda: 4 + +sensor: + - platform: ade7953_i2c + irq_pin: 6 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_i2c/test.esp32-c3.yaml b/tests/components/ade7953_i2c/test.esp32-c3.yaml new file mode 100644 index 0000000000..d7b365a7e1 --- /dev/null +++ b/tests/components/ade7953_i2c/test.esp32-c3.yaml @@ -0,0 +1,34 @@ +i2c: + - id: i2c_ade7953 + scl: 5 + sda: 4 + +sensor: + - platform: ade7953_i2c + irq_pin: 6 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_i2c/test.esp32-idf.yaml b/tests/components/ade7953_i2c/test.esp32-idf.yaml new file mode 100644 index 0000000000..71602f20a3 --- /dev/null +++ b/tests/components/ade7953_i2c/test.esp32-idf.yaml @@ -0,0 +1,34 @@ +i2c: + - id: i2c_ade7953 + scl: 16 + sda: 17 + +sensor: + - platform: ade7953_i2c + irq_pin: 15 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_i2c/test.esp32.yaml b/tests/components/ade7953_i2c/test.esp32.yaml new file mode 100644 index 0000000000..71602f20a3 --- /dev/null +++ b/tests/components/ade7953_i2c/test.esp32.yaml @@ -0,0 +1,34 @@ +i2c: + - id: i2c_ade7953 + scl: 16 + sda: 17 + +sensor: + - platform: ade7953_i2c + irq_pin: 15 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_i2c/test.esp8266.yaml b/tests/components/ade7953_i2c/test.esp8266.yaml new file mode 100644 index 0000000000..6903cd1953 --- /dev/null +++ b/tests/components/ade7953_i2c/test.esp8266.yaml @@ -0,0 +1,34 @@ +i2c: + - id: i2c_ade7953 + scl: 5 + sda: 4 + +sensor: + - platform: ade7953_i2c + irq_pin: 15 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_i2c/test.rp2040.yaml b/tests/components/ade7953_i2c/test.rp2040.yaml new file mode 100644 index 0000000000..d7b365a7e1 --- /dev/null +++ b/tests/components/ade7953_i2c/test.rp2040.yaml @@ -0,0 +1,34 @@ +i2c: + - id: i2c_ade7953 + scl: 5 + sda: 4 + +sensor: + - platform: ade7953_i2c + irq_pin: 6 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_spi/test.esp32-c3-idf.yaml b/tests/components/ade7953_spi/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..a967f28d9c --- /dev/null +++ b/tests/components/ade7953_spi/test.esp32-c3-idf.yaml @@ -0,0 +1,36 @@ +spi: + - id: spi_ade7953 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: ade7953_spi + cs_pin: 8 + irq_pin: 9 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_spi/test.esp32-c3.yaml b/tests/components/ade7953_spi/test.esp32-c3.yaml new file mode 100644 index 0000000000..a967f28d9c --- /dev/null +++ b/tests/components/ade7953_spi/test.esp32-c3.yaml @@ -0,0 +1,36 @@ +spi: + - id: spi_ade7953 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: ade7953_spi + cs_pin: 8 + irq_pin: 9 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_spi/test.esp32-idf.yaml b/tests/components/ade7953_spi/test.esp32-idf.yaml new file mode 100644 index 0000000000..e9ef7e4116 --- /dev/null +++ b/tests/components/ade7953_spi/test.esp32-idf.yaml @@ -0,0 +1,36 @@ +spi: + - id: spi_ade7953 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: ade7953_spi + cs_pin: 5 + irq_pin: 13 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_spi/test.esp32.yaml b/tests/components/ade7953_spi/test.esp32.yaml new file mode 100644 index 0000000000..e9ef7e4116 --- /dev/null +++ b/tests/components/ade7953_spi/test.esp32.yaml @@ -0,0 +1,36 @@ +spi: + - id: spi_ade7953 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: ade7953_spi + cs_pin: 5 + irq_pin: 13 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_spi/test.esp8266.yaml b/tests/components/ade7953_spi/test.esp8266.yaml new file mode 100644 index 0000000000..b36b4445ab --- /dev/null +++ b/tests/components/ade7953_spi/test.esp8266.yaml @@ -0,0 +1,36 @@ +spi: + - id: spi_ade7953 + clk_pin: 14 + mosi_pin: 13 + miso_pin: 12 + +sensor: + - platform: ade7953_spi + cs_pin: 15 + irq_pin: 5 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_spi/test.rp2040.yaml b/tests/components/ade7953_spi/test.rp2040.yaml new file mode 100644 index 0000000000..319abd4613 --- /dev/null +++ b/tests/components/ade7953_spi/test.rp2040.yaml @@ -0,0 +1,36 @@ +spi: + - id: spi_ade7953 + clk_pin: 2 + mosi_pin: 3 + miso_pin: 4 + +sensor: + - platform: ade7953_spi + cs_pin: 5 + irq_pin: 6 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ads1115/test.esp32-c3-idf.yaml b/tests/components/ads1115/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..7ac5a09f3f --- /dev/null +++ b/tests/components/ads1115/test.esp32-c3-idf.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ads1115 + scl: 5 + sda: 4 + +ads1115: + address: 0x48 + +sensor: + - platform: ads1115 + multiplexer: A0_A1 + gain: 1.024 + id: ads1115_sensor diff --git a/tests/components/ads1115/test.esp32-c3.yaml b/tests/components/ads1115/test.esp32-c3.yaml new file mode 100644 index 0000000000..7ac5a09f3f --- /dev/null +++ b/tests/components/ads1115/test.esp32-c3.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ads1115 + scl: 5 + sda: 4 + +ads1115: + address: 0x48 + +sensor: + - platform: ads1115 + multiplexer: A0_A1 + gain: 1.024 + id: ads1115_sensor diff --git a/tests/components/ads1115/test.esp32-idf.yaml b/tests/components/ads1115/test.esp32-idf.yaml new file mode 100644 index 0000000000..a869f2379b --- /dev/null +++ b/tests/components/ads1115/test.esp32-idf.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ads1115 + scl: 16 + sda: 17 + +ads1115: + address: 0x48 + +sensor: + - platform: ads1115 + multiplexer: A0_A1 + gain: 1.024 + id: ads1115_sensor diff --git a/tests/components/ads1115/test.esp32.yaml b/tests/components/ads1115/test.esp32.yaml new file mode 100644 index 0000000000..a869f2379b --- /dev/null +++ b/tests/components/ads1115/test.esp32.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ads1115 + scl: 16 + sda: 17 + +ads1115: + address: 0x48 + +sensor: + - platform: ads1115 + multiplexer: A0_A1 + gain: 1.024 + id: ads1115_sensor diff --git a/tests/components/ads1115/test.esp8266.yaml b/tests/components/ads1115/test.esp8266.yaml new file mode 100644 index 0000000000..7ac5a09f3f --- /dev/null +++ b/tests/components/ads1115/test.esp8266.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ads1115 + scl: 5 + sda: 4 + +ads1115: + address: 0x48 + +sensor: + - platform: ads1115 + multiplexer: A0_A1 + gain: 1.024 + id: ads1115_sensor diff --git a/tests/components/ads1115/test.rp2040.yaml b/tests/components/ads1115/test.rp2040.yaml new file mode 100644 index 0000000000..7ac5a09f3f --- /dev/null +++ b/tests/components/ads1115/test.rp2040.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ads1115 + scl: 5 + sda: 4 + +ads1115: + address: 0x48 + +sensor: + - platform: ads1115 + multiplexer: A0_A1 + gain: 1.024 + id: ads1115_sensor diff --git a/tests/components/aht10/test.esp32-c3-idf.yaml b/tests/components/aht10/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..2e5f505476 --- /dev/null +++ b/tests/components/aht10/test.esp32-c3-idf.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_aht10 + scl: 5 + sda: 4 + +sensor: + - platform: aht10 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/aht10/test.esp32-c3.yaml b/tests/components/aht10/test.esp32-c3.yaml new file mode 100644 index 0000000000..2e5f505476 --- /dev/null +++ b/tests/components/aht10/test.esp32-c3.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_aht10 + scl: 5 + sda: 4 + +sensor: + - platform: aht10 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/aht10/test.esp32-idf.yaml b/tests/components/aht10/test.esp32-idf.yaml new file mode 100644 index 0000000000..499e69e5d3 --- /dev/null +++ b/tests/components/aht10/test.esp32-idf.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_aht10 + scl: 16 + sda: 17 + +sensor: + - platform: aht10 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/aht10/test.esp32.yaml b/tests/components/aht10/test.esp32.yaml new file mode 100644 index 0000000000..499e69e5d3 --- /dev/null +++ b/tests/components/aht10/test.esp32.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_aht10 + scl: 16 + sda: 17 + +sensor: + - platform: aht10 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/aht10/test.esp8266.yaml b/tests/components/aht10/test.esp8266.yaml new file mode 100644 index 0000000000..2e5f505476 --- /dev/null +++ b/tests/components/aht10/test.esp8266.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_aht10 + scl: 5 + sda: 4 + +sensor: + - platform: aht10 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/aht10/test.rp2040.yaml b/tests/components/aht10/test.rp2040.yaml new file mode 100644 index 0000000000..2e5f505476 --- /dev/null +++ b/tests/components/aht10/test.rp2040.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_aht10 + scl: 5 + sda: 4 + +sensor: + - platform: aht10 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml b/tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..87902e6c66 --- /dev/null +++ b/tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml @@ -0,0 +1,22 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: airthingsmini01 + +sensor: + - id: airthingswm + platform: airthings_wave_mini + ble_client_id: airthingsmini01 + update_interval: 5min + battery_update_interval: 12h + temperature: + name: Wave Mini Temperature + humidity: + name: Wave Mini Humidity + pressure: + name: Wave Mini Pressure + tvoc: + name: Wave Mini VOC + battery_voltage: + name: Wave Mini Battery Voltage diff --git a/tests/components/airthings_wave_mini/test.esp32-c3.yaml b/tests/components/airthings_wave_mini/test.esp32-c3.yaml new file mode 100644 index 0000000000..87902e6c66 --- /dev/null +++ b/tests/components/airthings_wave_mini/test.esp32-c3.yaml @@ -0,0 +1,22 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: airthingsmini01 + +sensor: + - id: airthingswm + platform: airthings_wave_mini + ble_client_id: airthingsmini01 + update_interval: 5min + battery_update_interval: 12h + temperature: + name: Wave Mini Temperature + humidity: + name: Wave Mini Humidity + pressure: + name: Wave Mini Pressure + tvoc: + name: Wave Mini VOC + battery_voltage: + name: Wave Mini Battery Voltage diff --git a/tests/components/airthings_wave_mini/test.esp32-idf.yaml b/tests/components/airthings_wave_mini/test.esp32-idf.yaml new file mode 100644 index 0000000000..87902e6c66 --- /dev/null +++ b/tests/components/airthings_wave_mini/test.esp32-idf.yaml @@ -0,0 +1,22 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: airthingsmini01 + +sensor: + - id: airthingswm + platform: airthings_wave_mini + ble_client_id: airthingsmini01 + update_interval: 5min + battery_update_interval: 12h + temperature: + name: Wave Mini Temperature + humidity: + name: Wave Mini Humidity + pressure: + name: Wave Mini Pressure + tvoc: + name: Wave Mini VOC + battery_voltage: + name: Wave Mini Battery Voltage diff --git a/tests/components/airthings_wave_mini/test.esp32.yaml b/tests/components/airthings_wave_mini/test.esp32.yaml new file mode 100644 index 0000000000..87902e6c66 --- /dev/null +++ b/tests/components/airthings_wave_mini/test.esp32.yaml @@ -0,0 +1,22 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: airthingsmini01 + +sensor: + - id: airthingswm + platform: airthings_wave_mini + ble_client_id: airthingsmini01 + update_interval: 5min + battery_update_interval: 12h + temperature: + name: Wave Mini Temperature + humidity: + name: Wave Mini Humidity + pressure: + name: Wave Mini Pressure + tvoc: + name: Wave Mini VOC + battery_voltage: + name: Wave Mini Battery Voltage diff --git a/tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml b/tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..2124fcdaec --- /dev/null +++ b/tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml @@ -0,0 +1,28 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: airthings01 + +sensor: + - id: airthingswp + platform: airthings_wave_plus + ble_client_id: airthings01 + update_interval: 5min + battery_update_interval: 12h + temperature: + name: Wave Plus Temperature + radon: + name: Wave Plus Radon + radon_long_term: + name: Wave Plus Radon Long Term + pressure: + name: Wave Plus Pressure + humidity: + name: Wave Plus Humidity + co2: + name: Wave Plus CO2 + tvoc: + name: Wave Plus VOC + battery_voltage: + name: Wave Plus Battery Voltage diff --git a/tests/components/airthings_wave_plus/test.esp32-c3.yaml b/tests/components/airthings_wave_plus/test.esp32-c3.yaml new file mode 100644 index 0000000000..2124fcdaec --- /dev/null +++ b/tests/components/airthings_wave_plus/test.esp32-c3.yaml @@ -0,0 +1,28 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: airthings01 + +sensor: + - id: airthingswp + platform: airthings_wave_plus + ble_client_id: airthings01 + update_interval: 5min + battery_update_interval: 12h + temperature: + name: Wave Plus Temperature + radon: + name: Wave Plus Radon + radon_long_term: + name: Wave Plus Radon Long Term + pressure: + name: Wave Plus Pressure + humidity: + name: Wave Plus Humidity + co2: + name: Wave Plus CO2 + tvoc: + name: Wave Plus VOC + battery_voltage: + name: Wave Plus Battery Voltage diff --git a/tests/components/airthings_wave_plus/test.esp32-idf.yaml b/tests/components/airthings_wave_plus/test.esp32-idf.yaml new file mode 100644 index 0000000000..2124fcdaec --- /dev/null +++ b/tests/components/airthings_wave_plus/test.esp32-idf.yaml @@ -0,0 +1,28 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: airthings01 + +sensor: + - id: airthingswp + platform: airthings_wave_plus + ble_client_id: airthings01 + update_interval: 5min + battery_update_interval: 12h + temperature: + name: Wave Plus Temperature + radon: + name: Wave Plus Radon + radon_long_term: + name: Wave Plus Radon Long Term + pressure: + name: Wave Plus Pressure + humidity: + name: Wave Plus Humidity + co2: + name: Wave Plus CO2 + tvoc: + name: Wave Plus VOC + battery_voltage: + name: Wave Plus Battery Voltage diff --git a/tests/components/airthings_wave_plus/test.esp32.yaml b/tests/components/airthings_wave_plus/test.esp32.yaml new file mode 100644 index 0000000000..2124fcdaec --- /dev/null +++ b/tests/components/airthings_wave_plus/test.esp32.yaml @@ -0,0 +1,28 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: airthings01 + +sensor: + - id: airthingswp + platform: airthings_wave_plus + ble_client_id: airthings01 + update_interval: 5min + battery_update_interval: 12h + temperature: + name: Wave Plus Temperature + radon: + name: Wave Plus Radon + radon_long_term: + name: Wave Plus Radon Long Term + pressure: + name: Wave Plus Pressure + humidity: + name: Wave Plus Humidity + co2: + name: Wave Plus CO2 + tvoc: + name: Wave Plus VOC + battery_voltage: + name: Wave Plus Battery Voltage From 164b42f5aa9847af3074ce41d06d845accf04e3a Mon Sep 17 00:00:00 2001 From: Marcel Hetzendorfer Date: Tue, 6 Feb 2024 20:03:09 +0100 Subject: [PATCH 177/193] WRGB or RGBW? WS2814 (#6164) --- esphome/components/esp32_rmt_led_strip/led_strip.cpp | 10 ++++++---- esphome/components/esp32_rmt_led_strip/led_strip.h | 4 +++- esphome/components/esp32_rmt_led_strip/light.py | 3 +++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index 3df4077c96..c5a7f7918d 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp +++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp @@ -161,10 +161,12 @@ light::ESPColorView ESP32RMTLEDStripLightOutput::get_view_internal(int32_t index break; } uint8_t multiplier = this->is_rgbw_ ? 4 : 3; - return {this->buf_ + (index * multiplier) + r, - this->buf_ + (index * multiplier) + g, - this->buf_ + (index * multiplier) + b, - this->is_rgbw_ ? this->buf_ + (index * multiplier) + 3 : nullptr, + uint8_t white = this->is_wrgb_ ? 0 : 3; + + return {this->buf_ + (index * multiplier) + r + this->is_wrgb_, + this->buf_ + (index * multiplier) + g + this->is_wrgb_, + this->buf_ + (index * multiplier) + b + this->is_wrgb_, + this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr, &this->effect_data_[index], &this->correction_}; } diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.h b/esphome/components/esp32_rmt_led_strip/led_strip.h index 11d61b07e1..51eed8e01c 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.h +++ b/esphome/components/esp32_rmt_led_strip/led_strip.h @@ -33,7 +33,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { int32_t size() const override { return this->num_leds_; } light::LightTraits get_traits() override { auto traits = light::LightTraits(); - if (this->is_rgbw_) { + if (this->is_rgbw_ || this->is_wrgb_) { traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE}); } else { traits.set_supported_color_modes({light::ColorMode::RGB}); @@ -44,6 +44,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { void set_pin(uint8_t pin) { this->pin_ = pin; } void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; } void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; } + void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; } /// Set a maximum refresh rate in µs as some lights do not like being updated too often. void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; } @@ -72,6 +73,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { uint8_t pin_; uint16_t num_leds_; bool is_rgbw_; + bool is_wrgb_; rmt_item32_t bit0_, bit1_; RGBOrder rgb_order_; diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 122ee132a7..d38c7abeb8 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -52,6 +52,7 @@ CHIPSETS = { CONF_IS_RGBW = "is_rgbw" +CONF_IS_WRGB = "is_wrgb" CONF_BIT0_HIGH = "bit0_high" CONF_BIT0_LOW = "bit0_low" CONF_BIT1_HIGH = "bit1_high" @@ -90,6 +91,7 @@ CONFIG_SCHEMA = cv.All( 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, + cv.Optional(CONF_IS_WRGB, default=False): cv.boolean, cv.Inclusive( CONF_BIT0_HIGH, "custom", @@ -145,6 +147,7 @@ async def to_code(config): cg.add(var.set_rgb_order(config[CONF_RGB_ORDER])) cg.add(var.set_is_rgbw(config[CONF_IS_RGBW])) + cg.add(var.set_is_wrgb(config[CONF_IS_WRGB])) cg.add( var.set_rmt_channel( From 9dbbc80c7409dcce581b8e9cae11edbd1a74bf78 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 6 Feb 2024 13:05:04 -0600 Subject: [PATCH 178/193] Add some components to the new testing framework (A part 2) (#6162) --- .../test.esp32-c3-idf.yaml | 64 +++++++++++++++++++ .../alarm_control_panel/test.esp32-c3.yaml | 64 +++++++++++++++++++ .../alarm_control_panel/test.esp32-idf.yaml | 64 +++++++++++++++++++ .../alarm_control_panel/test.esp32.yaml | 64 +++++++++++++++++++ .../alarm_control_panel/test.esp8266.yaml | 64 +++++++++++++++++++ .../alarm_control_panel/test.rp2040.yaml | 64 +++++++++++++++++++ .../components/alpha3/test.esp32-c3-idf.yaml | 17 +++++ tests/components/alpha3/test.esp32-c3.yaml | 17 +++++ tests/components/alpha3/test.esp32-idf.yaml | 17 +++++ tests/components/alpha3/test.esp32.yaml | 17 +++++ .../components/am2320/test.esp32-c3-idf.yaml | 11 ++++ tests/components/am2320/test.esp32-c3.yaml | 11 ++++ tests/components/am2320/test.esp32-idf.yaml | 11 ++++ tests/components/am2320/test.esp32.yaml | 11 ++++ tests/components/am2320/test.esp8266.yaml | 11 ++++ tests/components/am2320/test.rp2040.yaml | 11 ++++ tests/components/am43/test.esp32-c3-idf.yaml | 19 ++++++ tests/components/am43/test.esp32-c3.yaml | 19 ++++++ tests/components/am43/test.esp32-idf.yaml | 19 ++++++ tests/components/am43/test.esp32.yaml | 19 ++++++ .../analog_threshold/test.esp32-c3-idf.yaml | 28 ++++++++ .../analog_threshold/test.esp32-c3.yaml | 28 ++++++++ .../analog_threshold/test.esp32-idf.yaml | 28 ++++++++ .../analog_threshold/test.esp32.yaml | 28 ++++++++ .../analog_threshold/test.esp8266.yaml | 28 ++++++++ .../analog_threshold/test.rp2040.yaml | 28 ++++++++ .../animation/test.esp32-c3-idf.yaml | 23 +++++++ tests/components/animation/test.esp32-c3.yaml | 23 +++++++ .../components/animation/test.esp32-idf.yaml | 23 +++++++ tests/components/animation/test.esp32.yaml | 23 +++++++ tests/components/animation/test.esp8266.yaml | 23 +++++++ tests/components/animation/test.rp2040.yaml | 23 +++++++ tests/components/anova/test.esp32-c3-idf.yaml | 11 ++++ tests/components/anova/test.esp32-c3.yaml | 11 ++++ tests/components/anova/test.esp32-idf.yaml | 11 ++++ tests/components/anova/test.esp32.yaml | 11 ++++ .../apds9960/test.esp32-c3-idf.yaml | 48 ++++++++++++++ tests/components/apds9960/test.esp32-c3.yaml | 48 ++++++++++++++ tests/components/apds9960/test.esp32-idf.yaml | 48 ++++++++++++++ tests/components/apds9960/test.esp32.yaml | 48 ++++++++++++++ tests/components/apds9960/test.esp8266.yaml | 48 ++++++++++++++ tests/components/apds9960/test.rp2040.yaml | 48 ++++++++++++++ tests/components/api/test.esp32-c3-idf.yaml | 50 +++++++++++++++ tests/components/api/test.esp32-c3.yaml | 50 +++++++++++++++ tests/components/api/test.esp32-idf.yaml | 50 +++++++++++++++ tests/components/api/test.esp32.yaml | 50 +++++++++++++++ tests/components/api/test.esp8266.yaml | 50 +++++++++++++++ tests/components/api/test.rp2040.yaml | 50 +++++++++++++++ .../as3935_i2c/test.esp32-c3-idf.yaml | 18 ++++++ .../components/as3935_i2c/test.esp32-c3.yaml | 18 ++++++ .../components/as3935_i2c/test.esp32-idf.yaml | 18 ++++++ tests/components/as3935_i2c/test.esp32.yaml | 18 ++++++ tests/components/as3935_i2c/test.esp8266.yaml | 18 ++++++ tests/components/as3935_i2c/test.rp2040.yaml | 18 ++++++ .../as3935_spi/test.esp32-c3-idf.yaml | 20 ++++++ .../components/as3935_spi/test.esp32-c3.yaml | 20 ++++++ .../components/as3935_spi/test.esp32-idf.yaml | 20 ++++++ tests/components/as3935_spi/test.esp32.yaml | 20 ++++++ tests/components/as3935_spi/test.esp8266.yaml | 20 ++++++ tests/components/as3935_spi/test.rp2040.yaml | 20 ++++++ .../components/as5600/test.esp32-c3-idf.yaml | 27 ++++++++ tests/components/as5600/test.esp32-c3.yaml | 27 ++++++++ tests/components/as5600/test.esp32-idf.yaml | 27 ++++++++ tests/components/as5600/test.esp32.yaml | 27 ++++++++ tests/components/as5600/test.esp8266.yaml | 27 ++++++++ tests/components/as5600/test.rp2040.yaml | 27 ++++++++ .../components/as7341/test.esp32-c3-idf.yaml | 31 +++++++++ tests/components/as7341/test.esp32-c3.yaml | 31 +++++++++ tests/components/as7341/test.esp32-idf.yaml | 31 +++++++++ tests/components/as7341/test.esp32.yaml | 31 +++++++++ tests/components/as7341/test.esp8266.yaml | 31 +++++++++ tests/components/as7341/test.rp2040.yaml | 31 +++++++++ .../atc_mithermometer/test.esp32-c3-idf.yaml | 13 ++++ .../atc_mithermometer/test.esp32-c3.yaml | 13 ++++ .../atc_mithermometer/test.esp32-idf.yaml | 13 ++++ .../atc_mithermometer/test.esp32.yaml | 13 ++++ .../atm90e26/test.esp32-c3-idf.yaml | 26 ++++++++ tests/components/atm90e26/test.esp32-c3.yaml | 26 ++++++++ tests/components/atm90e26/test.esp32-idf.yaml | 26 ++++++++ tests/components/atm90e26/test.esp32.yaml | 26 ++++++++ tests/components/atm90e26/test.esp8266.yaml | 26 ++++++++ tests/components/atm90e26/test.rp2040.yaml | 26 ++++++++ .../atm90e32/test.esp32-c3-idf.yaml | 51 +++++++++++++++ tests/components/atm90e32/test.esp32-c3.yaml | 51 +++++++++++++++ tests/components/atm90e32/test.esp32-idf.yaml | 51 +++++++++++++++ tests/components/atm90e32/test.esp32.yaml | 51 +++++++++++++++ tests/components/atm90e32/test.esp8266.yaml | 51 +++++++++++++++ tests/components/atm90e32/test.rp2040.yaml | 51 +++++++++++++++ 88 files changed, 2622 insertions(+) create mode 100644 tests/components/alarm_control_panel/test.esp32-c3-idf.yaml create mode 100644 tests/components/alarm_control_panel/test.esp32-c3.yaml create mode 100644 tests/components/alarm_control_panel/test.esp32-idf.yaml create mode 100644 tests/components/alarm_control_panel/test.esp32.yaml create mode 100644 tests/components/alarm_control_panel/test.esp8266.yaml create mode 100644 tests/components/alarm_control_panel/test.rp2040.yaml create mode 100644 tests/components/alpha3/test.esp32-c3-idf.yaml create mode 100644 tests/components/alpha3/test.esp32-c3.yaml create mode 100644 tests/components/alpha3/test.esp32-idf.yaml create mode 100644 tests/components/alpha3/test.esp32.yaml create mode 100644 tests/components/am2320/test.esp32-c3-idf.yaml create mode 100644 tests/components/am2320/test.esp32-c3.yaml create mode 100644 tests/components/am2320/test.esp32-idf.yaml create mode 100644 tests/components/am2320/test.esp32.yaml create mode 100644 tests/components/am2320/test.esp8266.yaml create mode 100644 tests/components/am2320/test.rp2040.yaml create mode 100644 tests/components/am43/test.esp32-c3-idf.yaml create mode 100644 tests/components/am43/test.esp32-c3.yaml create mode 100644 tests/components/am43/test.esp32-idf.yaml create mode 100644 tests/components/am43/test.esp32.yaml create mode 100644 tests/components/analog_threshold/test.esp32-c3-idf.yaml create mode 100644 tests/components/analog_threshold/test.esp32-c3.yaml create mode 100644 tests/components/analog_threshold/test.esp32-idf.yaml create mode 100644 tests/components/analog_threshold/test.esp32.yaml create mode 100644 tests/components/analog_threshold/test.esp8266.yaml create mode 100644 tests/components/analog_threshold/test.rp2040.yaml create mode 100644 tests/components/animation/test.esp32-c3-idf.yaml create mode 100644 tests/components/animation/test.esp32-c3.yaml create mode 100644 tests/components/animation/test.esp32-idf.yaml create mode 100644 tests/components/animation/test.esp32.yaml create mode 100644 tests/components/animation/test.esp8266.yaml create mode 100644 tests/components/animation/test.rp2040.yaml create mode 100644 tests/components/anova/test.esp32-c3-idf.yaml create mode 100644 tests/components/anova/test.esp32-c3.yaml create mode 100644 tests/components/anova/test.esp32-idf.yaml create mode 100644 tests/components/anova/test.esp32.yaml create mode 100644 tests/components/apds9960/test.esp32-c3-idf.yaml create mode 100644 tests/components/apds9960/test.esp32-c3.yaml create mode 100644 tests/components/apds9960/test.esp32-idf.yaml create mode 100644 tests/components/apds9960/test.esp32.yaml create mode 100644 tests/components/apds9960/test.esp8266.yaml create mode 100644 tests/components/apds9960/test.rp2040.yaml create mode 100644 tests/components/api/test.esp32-c3-idf.yaml create mode 100644 tests/components/api/test.esp32-c3.yaml create mode 100644 tests/components/api/test.esp32-idf.yaml create mode 100644 tests/components/api/test.esp32.yaml create mode 100644 tests/components/api/test.esp8266.yaml create mode 100644 tests/components/api/test.rp2040.yaml create mode 100644 tests/components/as3935_i2c/test.esp32-c3-idf.yaml create mode 100644 tests/components/as3935_i2c/test.esp32-c3.yaml create mode 100644 tests/components/as3935_i2c/test.esp32-idf.yaml create mode 100644 tests/components/as3935_i2c/test.esp32.yaml create mode 100644 tests/components/as3935_i2c/test.esp8266.yaml create mode 100644 tests/components/as3935_i2c/test.rp2040.yaml create mode 100644 tests/components/as3935_spi/test.esp32-c3-idf.yaml create mode 100644 tests/components/as3935_spi/test.esp32-c3.yaml create mode 100644 tests/components/as3935_spi/test.esp32-idf.yaml create mode 100644 tests/components/as3935_spi/test.esp32.yaml create mode 100644 tests/components/as3935_spi/test.esp8266.yaml create mode 100644 tests/components/as3935_spi/test.rp2040.yaml create mode 100644 tests/components/as5600/test.esp32-c3-idf.yaml create mode 100644 tests/components/as5600/test.esp32-c3.yaml create mode 100644 tests/components/as5600/test.esp32-idf.yaml create mode 100644 tests/components/as5600/test.esp32.yaml create mode 100644 tests/components/as5600/test.esp8266.yaml create mode 100644 tests/components/as5600/test.rp2040.yaml create mode 100644 tests/components/as7341/test.esp32-c3-idf.yaml create mode 100644 tests/components/as7341/test.esp32-c3.yaml create mode 100644 tests/components/as7341/test.esp32-idf.yaml create mode 100644 tests/components/as7341/test.esp32.yaml create mode 100644 tests/components/as7341/test.esp8266.yaml create mode 100644 tests/components/as7341/test.rp2040.yaml create mode 100644 tests/components/atc_mithermometer/test.esp32-c3-idf.yaml create mode 100644 tests/components/atc_mithermometer/test.esp32-c3.yaml create mode 100644 tests/components/atc_mithermometer/test.esp32-idf.yaml create mode 100644 tests/components/atc_mithermometer/test.esp32.yaml create mode 100644 tests/components/atm90e26/test.esp32-c3-idf.yaml create mode 100644 tests/components/atm90e26/test.esp32-c3.yaml create mode 100644 tests/components/atm90e26/test.esp32-idf.yaml create mode 100644 tests/components/atm90e26/test.esp32.yaml create mode 100644 tests/components/atm90e26/test.esp8266.yaml create mode 100644 tests/components/atm90e26/test.rp2040.yaml create mode 100644 tests/components/atm90e32/test.esp32-c3-idf.yaml create mode 100644 tests/components/atm90e32/test.esp32-c3.yaml create mode 100644 tests/components/atm90e32/test.esp32-idf.yaml create mode 100644 tests/components/atm90e32/test.esp32.yaml create mode 100644 tests/components/atm90e32/test.esp8266.yaml create mode 100644 tests/components/atm90e32/test.rp2040.yaml diff --git a/tests/components/alarm_control_panel/test.esp32-c3-idf.yaml b/tests/components/alarm_control_panel/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..218274bad4 --- /dev/null +++ b/tests/components/alarm_control_panel/test.esp32-c3-idf.yaml @@ -0,0 +1,64 @@ +binary_sensor: + - platform: gpio + id: bin1 + pin: 1 + +alarm_control_panel: + - platform: template + id: alarmcontrolpanel1 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_state: + then: + - lambda: !lambda |- + ESP_LOGD("TEST", "State change %s", LOG_STR_ARG(alarm_control_panel_state_to_string(id(alarmcontrolpanel1)->get_state()))); + - platform: template + id: alarmcontrolpanel2 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_disarmed: + then: + - logger.log: "### DISARMED ###" + on_pending: + then: + - logger.log: "### PENDING ###" + on_arming: + then: + - logger.log: "### ARMING ###" + on_armed_home: + then: + - logger.log: "### ARMED HOME ###" + on_armed_night: + then: + - logger.log: "### ARMED NIGHT ###" + on_armed_away: + then: + - logger.log: "### ARMED AWAY ###" + on_triggered: + then: + - logger.log: "### TRIGGERED ###" + on_cleared: + then: + - logger.log: "### CLEARED ###" diff --git a/tests/components/alarm_control_panel/test.esp32-c3.yaml b/tests/components/alarm_control_panel/test.esp32-c3.yaml new file mode 100644 index 0000000000..218274bad4 --- /dev/null +++ b/tests/components/alarm_control_panel/test.esp32-c3.yaml @@ -0,0 +1,64 @@ +binary_sensor: + - platform: gpio + id: bin1 + pin: 1 + +alarm_control_panel: + - platform: template + id: alarmcontrolpanel1 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_state: + then: + - lambda: !lambda |- + ESP_LOGD("TEST", "State change %s", LOG_STR_ARG(alarm_control_panel_state_to_string(id(alarmcontrolpanel1)->get_state()))); + - platform: template + id: alarmcontrolpanel2 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_disarmed: + then: + - logger.log: "### DISARMED ###" + on_pending: + then: + - logger.log: "### PENDING ###" + on_arming: + then: + - logger.log: "### ARMING ###" + on_armed_home: + then: + - logger.log: "### ARMED HOME ###" + on_armed_night: + then: + - logger.log: "### ARMED NIGHT ###" + on_armed_away: + then: + - logger.log: "### ARMED AWAY ###" + on_triggered: + then: + - logger.log: "### TRIGGERED ###" + on_cleared: + then: + - logger.log: "### CLEARED ###" diff --git a/tests/components/alarm_control_panel/test.esp32-idf.yaml b/tests/components/alarm_control_panel/test.esp32-idf.yaml new file mode 100644 index 0000000000..218274bad4 --- /dev/null +++ b/tests/components/alarm_control_panel/test.esp32-idf.yaml @@ -0,0 +1,64 @@ +binary_sensor: + - platform: gpio + id: bin1 + pin: 1 + +alarm_control_panel: + - platform: template + id: alarmcontrolpanel1 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_state: + then: + - lambda: !lambda |- + ESP_LOGD("TEST", "State change %s", LOG_STR_ARG(alarm_control_panel_state_to_string(id(alarmcontrolpanel1)->get_state()))); + - platform: template + id: alarmcontrolpanel2 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_disarmed: + then: + - logger.log: "### DISARMED ###" + on_pending: + then: + - logger.log: "### PENDING ###" + on_arming: + then: + - logger.log: "### ARMING ###" + on_armed_home: + then: + - logger.log: "### ARMED HOME ###" + on_armed_night: + then: + - logger.log: "### ARMED NIGHT ###" + on_armed_away: + then: + - logger.log: "### ARMED AWAY ###" + on_triggered: + then: + - logger.log: "### TRIGGERED ###" + on_cleared: + then: + - logger.log: "### CLEARED ###" diff --git a/tests/components/alarm_control_panel/test.esp32.yaml b/tests/components/alarm_control_panel/test.esp32.yaml new file mode 100644 index 0000000000..218274bad4 --- /dev/null +++ b/tests/components/alarm_control_panel/test.esp32.yaml @@ -0,0 +1,64 @@ +binary_sensor: + - platform: gpio + id: bin1 + pin: 1 + +alarm_control_panel: + - platform: template + id: alarmcontrolpanel1 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_state: + then: + - lambda: !lambda |- + ESP_LOGD("TEST", "State change %s", LOG_STR_ARG(alarm_control_panel_state_to_string(id(alarmcontrolpanel1)->get_state()))); + - platform: template + id: alarmcontrolpanel2 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_disarmed: + then: + - logger.log: "### DISARMED ###" + on_pending: + then: + - logger.log: "### PENDING ###" + on_arming: + then: + - logger.log: "### ARMING ###" + on_armed_home: + then: + - logger.log: "### ARMED HOME ###" + on_armed_night: + then: + - logger.log: "### ARMED NIGHT ###" + on_armed_away: + then: + - logger.log: "### ARMED AWAY ###" + on_triggered: + then: + - logger.log: "### TRIGGERED ###" + on_cleared: + then: + - logger.log: "### CLEARED ###" diff --git a/tests/components/alarm_control_panel/test.esp8266.yaml b/tests/components/alarm_control_panel/test.esp8266.yaml new file mode 100644 index 0000000000..218274bad4 --- /dev/null +++ b/tests/components/alarm_control_panel/test.esp8266.yaml @@ -0,0 +1,64 @@ +binary_sensor: + - platform: gpio + id: bin1 + pin: 1 + +alarm_control_panel: + - platform: template + id: alarmcontrolpanel1 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_state: + then: + - lambda: !lambda |- + ESP_LOGD("TEST", "State change %s", LOG_STR_ARG(alarm_control_panel_state_to_string(id(alarmcontrolpanel1)->get_state()))); + - platform: template + id: alarmcontrolpanel2 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_disarmed: + then: + - logger.log: "### DISARMED ###" + on_pending: + then: + - logger.log: "### PENDING ###" + on_arming: + then: + - logger.log: "### ARMING ###" + on_armed_home: + then: + - logger.log: "### ARMED HOME ###" + on_armed_night: + then: + - logger.log: "### ARMED NIGHT ###" + on_armed_away: + then: + - logger.log: "### ARMED AWAY ###" + on_triggered: + then: + - logger.log: "### TRIGGERED ###" + on_cleared: + then: + - logger.log: "### CLEARED ###" diff --git a/tests/components/alarm_control_panel/test.rp2040.yaml b/tests/components/alarm_control_panel/test.rp2040.yaml new file mode 100644 index 0000000000..218274bad4 --- /dev/null +++ b/tests/components/alarm_control_panel/test.rp2040.yaml @@ -0,0 +1,64 @@ +binary_sensor: + - platform: gpio + id: bin1 + pin: 1 + +alarm_control_panel: + - platform: template + id: alarmcontrolpanel1 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_state: + then: + - lambda: !lambda |- + ESP_LOGD("TEST", "State change %s", LOG_STR_ARG(alarm_control_panel_state_to_string(id(alarmcontrolpanel1)->get_state()))); + - platform: template + id: alarmcontrolpanel2 + name: Alarm Panel + codes: + - "1234" + requires_code_to_arm: true + arming_home_time: 1s + arming_night_time: 1s + arming_away_time: 15s + pending_time: 15s + trigger_time: 30s + binary_sensors: + - input: bin1 + bypass_armed_home: true + bypass_armed_night: true + on_disarmed: + then: + - logger.log: "### DISARMED ###" + on_pending: + then: + - logger.log: "### PENDING ###" + on_arming: + then: + - logger.log: "### ARMING ###" + on_armed_home: + then: + - logger.log: "### ARMED HOME ###" + on_armed_night: + then: + - logger.log: "### ARMED NIGHT ###" + on_armed_away: + then: + - logger.log: "### ARMED AWAY ###" + on_triggered: + then: + - logger.log: "### TRIGGERED ###" + on_cleared: + then: + - logger.log: "### CLEARED ###" diff --git a/tests/components/alpha3/test.esp32-c3-idf.yaml b/tests/components/alpha3/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..913f086ac4 --- /dev/null +++ b/tests/components/alpha3/test.esp32-c3-idf.yaml @@ -0,0 +1,17 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: alpha3_blec + +sensor: + - platform: alpha3 + ble_client_id: alpha3_blec + flow: + name: "Radiator Pump Flow" + head: + name: "Radiator Pump Head" + power: + name: "Radiator Pump Power" + speed: + name: "Radiator Pump Speed" diff --git a/tests/components/alpha3/test.esp32-c3.yaml b/tests/components/alpha3/test.esp32-c3.yaml new file mode 100644 index 0000000000..913f086ac4 --- /dev/null +++ b/tests/components/alpha3/test.esp32-c3.yaml @@ -0,0 +1,17 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: alpha3_blec + +sensor: + - platform: alpha3 + ble_client_id: alpha3_blec + flow: + name: "Radiator Pump Flow" + head: + name: "Radiator Pump Head" + power: + name: "Radiator Pump Power" + speed: + name: "Radiator Pump Speed" diff --git a/tests/components/alpha3/test.esp32-idf.yaml b/tests/components/alpha3/test.esp32-idf.yaml new file mode 100644 index 0000000000..913f086ac4 --- /dev/null +++ b/tests/components/alpha3/test.esp32-idf.yaml @@ -0,0 +1,17 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: alpha3_blec + +sensor: + - platform: alpha3 + ble_client_id: alpha3_blec + flow: + name: "Radiator Pump Flow" + head: + name: "Radiator Pump Head" + power: + name: "Radiator Pump Power" + speed: + name: "Radiator Pump Speed" diff --git a/tests/components/alpha3/test.esp32.yaml b/tests/components/alpha3/test.esp32.yaml new file mode 100644 index 0000000000..913f086ac4 --- /dev/null +++ b/tests/components/alpha3/test.esp32.yaml @@ -0,0 +1,17 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: alpha3_blec + +sensor: + - platform: alpha3 + ble_client_id: alpha3_blec + flow: + name: "Radiator Pump Flow" + head: + name: "Radiator Pump Head" + power: + name: "Radiator Pump Power" + speed: + name: "Radiator Pump Speed" diff --git a/tests/components/am2320/test.esp32-c3-idf.yaml b/tests/components/am2320/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..6acfe8d4fd --- /dev/null +++ b/tests/components/am2320/test.esp32-c3-idf.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +sensor: + - platform: am2320 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/am2320/test.esp32-c3.yaml b/tests/components/am2320/test.esp32-c3.yaml new file mode 100644 index 0000000000..6acfe8d4fd --- /dev/null +++ b/tests/components/am2320/test.esp32-c3.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +sensor: + - platform: am2320 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/am2320/test.esp32-idf.yaml b/tests/components/am2320/test.esp32-idf.yaml new file mode 100644 index 0000000000..99f4173b85 --- /dev/null +++ b/tests/components/am2320/test.esp32-idf.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_bme280 + scl: 16 + sda: 17 + +sensor: + - platform: am2320 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/am2320/test.esp32.yaml b/tests/components/am2320/test.esp32.yaml new file mode 100644 index 0000000000..99f4173b85 --- /dev/null +++ b/tests/components/am2320/test.esp32.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_bme280 + scl: 16 + sda: 17 + +sensor: + - platform: am2320 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/am2320/test.esp8266.yaml b/tests/components/am2320/test.esp8266.yaml new file mode 100644 index 0000000000..6acfe8d4fd --- /dev/null +++ b/tests/components/am2320/test.esp8266.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +sensor: + - platform: am2320 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/am2320/test.rp2040.yaml b/tests/components/am2320/test.rp2040.yaml new file mode 100644 index 0000000000..6acfe8d4fd --- /dev/null +++ b/tests/components/am2320/test.rp2040.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +sensor: + - platform: am2320 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/am43/test.esp32-c3-idf.yaml b/tests/components/am43/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..60b7d81a55 --- /dev/null +++ b/tests/components/am43/test.esp32-c3-idf.yaml @@ -0,0 +1,19 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: am43_blec + +cover: + - platform: am43 + name: Test AM43 Cover + id: am43_test + ble_client_id: am43_blec + +sensor: + - platform: am43 + ble_client_id: am43_blec + battery_level: + name: Kitchen blinds battery + illuminance: + name: Kitchen blinds light diff --git a/tests/components/am43/test.esp32-c3.yaml b/tests/components/am43/test.esp32-c3.yaml new file mode 100644 index 0000000000..60b7d81a55 --- /dev/null +++ b/tests/components/am43/test.esp32-c3.yaml @@ -0,0 +1,19 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: am43_blec + +cover: + - platform: am43 + name: Test AM43 Cover + id: am43_test + ble_client_id: am43_blec + +sensor: + - platform: am43 + ble_client_id: am43_blec + battery_level: + name: Kitchen blinds battery + illuminance: + name: Kitchen blinds light diff --git a/tests/components/am43/test.esp32-idf.yaml b/tests/components/am43/test.esp32-idf.yaml new file mode 100644 index 0000000000..60b7d81a55 --- /dev/null +++ b/tests/components/am43/test.esp32-idf.yaml @@ -0,0 +1,19 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: am43_blec + +cover: + - platform: am43 + name: Test AM43 Cover + id: am43_test + ble_client_id: am43_blec + +sensor: + - platform: am43 + ble_client_id: am43_blec + battery_level: + name: Kitchen blinds battery + illuminance: + name: Kitchen blinds light diff --git a/tests/components/am43/test.esp32.yaml b/tests/components/am43/test.esp32.yaml new file mode 100644 index 0000000000..60b7d81a55 --- /dev/null +++ b/tests/components/am43/test.esp32.yaml @@ -0,0 +1,19 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: am43_blec + +cover: + - platform: am43 + name: Test AM43 Cover + id: am43_test + ble_client_id: am43_blec + +sensor: + - platform: am43 + ble_client_id: am43_blec + battery_level: + name: Kitchen blinds battery + illuminance: + name: Kitchen blinds light diff --git a/tests/components/analog_threshold/test.esp32-c3-idf.yaml b/tests/components/analog_threshold/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..b5c14dfe56 --- /dev/null +++ b/tests/components/analog_threshold/test.esp32-c3-idf.yaml @@ -0,0 +1,28 @@ +sensor: + - platform: template + id: template_sensor + name: Template Sensor + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 15s + +binary_sensor: + - platform: analog_threshold + name: Analog Threshold 1 + sensor_id: template_sensor + threshold: + upper: 110 + lower: 90 + filters: + - delayed_on: 0s + - delayed_off: 10s + - platform: analog_threshold + name: Analog Threshold 2 + sensor_id: template_sensor + threshold: 100 + filters: + - invert: diff --git a/tests/components/analog_threshold/test.esp32-c3.yaml b/tests/components/analog_threshold/test.esp32-c3.yaml new file mode 100644 index 0000000000..b5c14dfe56 --- /dev/null +++ b/tests/components/analog_threshold/test.esp32-c3.yaml @@ -0,0 +1,28 @@ +sensor: + - platform: template + id: template_sensor + name: Template Sensor + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 15s + +binary_sensor: + - platform: analog_threshold + name: Analog Threshold 1 + sensor_id: template_sensor + threshold: + upper: 110 + lower: 90 + filters: + - delayed_on: 0s + - delayed_off: 10s + - platform: analog_threshold + name: Analog Threshold 2 + sensor_id: template_sensor + threshold: 100 + filters: + - invert: diff --git a/tests/components/analog_threshold/test.esp32-idf.yaml b/tests/components/analog_threshold/test.esp32-idf.yaml new file mode 100644 index 0000000000..b5c14dfe56 --- /dev/null +++ b/tests/components/analog_threshold/test.esp32-idf.yaml @@ -0,0 +1,28 @@ +sensor: + - platform: template + id: template_sensor + name: Template Sensor + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 15s + +binary_sensor: + - platform: analog_threshold + name: Analog Threshold 1 + sensor_id: template_sensor + threshold: + upper: 110 + lower: 90 + filters: + - delayed_on: 0s + - delayed_off: 10s + - platform: analog_threshold + name: Analog Threshold 2 + sensor_id: template_sensor + threshold: 100 + filters: + - invert: diff --git a/tests/components/analog_threshold/test.esp32.yaml b/tests/components/analog_threshold/test.esp32.yaml new file mode 100644 index 0000000000..b5c14dfe56 --- /dev/null +++ b/tests/components/analog_threshold/test.esp32.yaml @@ -0,0 +1,28 @@ +sensor: + - platform: template + id: template_sensor + name: Template Sensor + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 15s + +binary_sensor: + - platform: analog_threshold + name: Analog Threshold 1 + sensor_id: template_sensor + threshold: + upper: 110 + lower: 90 + filters: + - delayed_on: 0s + - delayed_off: 10s + - platform: analog_threshold + name: Analog Threshold 2 + sensor_id: template_sensor + threshold: 100 + filters: + - invert: diff --git a/tests/components/analog_threshold/test.esp8266.yaml b/tests/components/analog_threshold/test.esp8266.yaml new file mode 100644 index 0000000000..b5c14dfe56 --- /dev/null +++ b/tests/components/analog_threshold/test.esp8266.yaml @@ -0,0 +1,28 @@ +sensor: + - platform: template + id: template_sensor + name: Template Sensor + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 15s + +binary_sensor: + - platform: analog_threshold + name: Analog Threshold 1 + sensor_id: template_sensor + threshold: + upper: 110 + lower: 90 + filters: + - delayed_on: 0s + - delayed_off: 10s + - platform: analog_threshold + name: Analog Threshold 2 + sensor_id: template_sensor + threshold: 100 + filters: + - invert: diff --git a/tests/components/analog_threshold/test.rp2040.yaml b/tests/components/analog_threshold/test.rp2040.yaml new file mode 100644 index 0000000000..b5c14dfe56 --- /dev/null +++ b/tests/components/analog_threshold/test.rp2040.yaml @@ -0,0 +1,28 @@ +sensor: + - platform: template + id: template_sensor + name: Template Sensor + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 15s + +binary_sensor: + - platform: analog_threshold + name: Analog Threshold 1 + sensor_id: template_sensor + threshold: + upper: 110 + lower: 90 + filters: + - delayed_on: 0s + - delayed_off: 10s + - platform: analog_threshold + name: Analog Threshold 2 + sensor_id: template_sensor + threshold: 100 + filters: + - invert: diff --git a/tests/components/animation/test.esp32-c3-idf.yaml b/tests/components/animation/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..9a415255ae --- /dev/null +++ b/tests/components/animation/test.esp32-c3-idf.yaml @@ -0,0 +1,23 @@ +spi: + - id: spi_main_lcd + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +display: + - platform: ili9xxx + id: main_lcd + model: ili9342 + cs_pin: 8 + dc_pin: 9 + reset_pin: 10 + +# Purposely test that `animation:` does auto-load `image:` +# Keep the `image:` undefined. +# image: + +animation: + - id: rgb565_animation + file: ../../pnglogo.png + type: RGB565 + use_transparency: no diff --git a/tests/components/animation/test.esp32-c3.yaml b/tests/components/animation/test.esp32-c3.yaml new file mode 100644 index 0000000000..9a415255ae --- /dev/null +++ b/tests/components/animation/test.esp32-c3.yaml @@ -0,0 +1,23 @@ +spi: + - id: spi_main_lcd + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +display: + - platform: ili9xxx + id: main_lcd + model: ili9342 + cs_pin: 8 + dc_pin: 9 + reset_pin: 10 + +# Purposely test that `animation:` does auto-load `image:` +# Keep the `image:` undefined. +# image: + +animation: + - id: rgb565_animation + file: ../../pnglogo.png + type: RGB565 + use_transparency: no diff --git a/tests/components/animation/test.esp32-idf.yaml b/tests/components/animation/test.esp32-idf.yaml new file mode 100644 index 0000000000..31b78eb980 --- /dev/null +++ b/tests/components/animation/test.esp32-idf.yaml @@ -0,0 +1,23 @@ +spi: + - id: spi_main_lcd + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +display: + - platform: ili9xxx + id: main_lcd + model: ili9342 + cs_pin: 12 + dc_pin: 13 + reset_pin: 21 + +# Purposely test that `animation:` does auto-load `image:` +# Keep the `image:` undefined. +# image: + +animation: + - id: rgb565_animation + file: ../../pnglogo.png + type: RGB565 + use_transparency: no diff --git a/tests/components/animation/test.esp32.yaml b/tests/components/animation/test.esp32.yaml new file mode 100644 index 0000000000..31b78eb980 --- /dev/null +++ b/tests/components/animation/test.esp32.yaml @@ -0,0 +1,23 @@ +spi: + - id: spi_main_lcd + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +display: + - platform: ili9xxx + id: main_lcd + model: ili9342 + cs_pin: 12 + dc_pin: 13 + reset_pin: 21 + +# Purposely test that `animation:` does auto-load `image:` +# Keep the `image:` undefined. +# image: + +animation: + - id: rgb565_animation + file: ../../pnglogo.png + type: RGB565 + use_transparency: no diff --git a/tests/components/animation/test.esp8266.yaml b/tests/components/animation/test.esp8266.yaml new file mode 100644 index 0000000000..2bd441de99 --- /dev/null +++ b/tests/components/animation/test.esp8266.yaml @@ -0,0 +1,23 @@ +spi: + - id: spi_main_lcd + clk_pin: 14 + mosi_pin: 13 + miso_pin: 12 + +display: + - platform: ili9xxx + id: main_lcd + model: ili9342 + cs_pin: 5 + dc_pin: 15 + reset_pin: 16 + +# Purposely test that `animation:` does auto-load `image:` +# Keep the `image:` undefined. +# image: + +animation: + - id: rgb565_animation + file: ../../pnglogo.png + type: RGB565 + use_transparency: no diff --git a/tests/components/animation/test.rp2040.yaml b/tests/components/animation/test.rp2040.yaml new file mode 100644 index 0000000000..0f42c33687 --- /dev/null +++ b/tests/components/animation/test.rp2040.yaml @@ -0,0 +1,23 @@ +spi: + - id: spi_main_lcd + clk_pin: 2 + mosi_pin: 3 + miso_pin: 4 + +display: + - platform: ili9xxx + id: main_lcd + model: ili9342 + cs_pin: 20 + dc_pin: 21 + reset_pin: 22 + +# Purposely test that `animation:` does auto-load `image:` +# Keep the `image:` undefined. +# image: + +animation: + - id: rgb565_animation + file: ../../pnglogo.png + type: RGB565 + use_transparency: no diff --git a/tests/components/anova/test.esp32-c3-idf.yaml b/tests/components/anova/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..c4162fe71e --- /dev/null +++ b/tests/components/anova/test.esp32-c3-idf.yaml @@ -0,0 +1,11 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: anova_blec + +climate: + - platform: anova + name: Anova cooker + ble_client_id: anova_blec + unit_of_measurement: c diff --git a/tests/components/anova/test.esp32-c3.yaml b/tests/components/anova/test.esp32-c3.yaml new file mode 100644 index 0000000000..c4162fe71e --- /dev/null +++ b/tests/components/anova/test.esp32-c3.yaml @@ -0,0 +1,11 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: anova_blec + +climate: + - platform: anova + name: Anova cooker + ble_client_id: anova_blec + unit_of_measurement: c diff --git a/tests/components/anova/test.esp32-idf.yaml b/tests/components/anova/test.esp32-idf.yaml new file mode 100644 index 0000000000..c4162fe71e --- /dev/null +++ b/tests/components/anova/test.esp32-idf.yaml @@ -0,0 +1,11 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: anova_blec + +climate: + - platform: anova + name: Anova cooker + ble_client_id: anova_blec + unit_of_measurement: c diff --git a/tests/components/anova/test.esp32.yaml b/tests/components/anova/test.esp32.yaml new file mode 100644 index 0000000000..c4162fe71e --- /dev/null +++ b/tests/components/anova/test.esp32.yaml @@ -0,0 +1,11 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: anova_blec + +climate: + - platform: anova + name: Anova cooker + ble_client_id: anova_blec + unit_of_measurement: c diff --git a/tests/components/apds9960/test.esp32-c3-idf.yaml b/tests/components/apds9960/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..f6b6f7bac0 --- /dev/null +++ b/tests/components/apds9960/test.esp32-c3-idf.yaml @@ -0,0 +1,48 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +apds9960: + address: 0x20 + update_interval: 60s + +binary_sensor: + - platform: apds9960 + id: apds9960_binary_sensor + direction: up + name: APDS9960 Up + device_class: motion + filters: + - invert + - delayed_on: 20ms + - delayed_off: 20ms + - lambda: "return false;" + on_state: + - logger.log: New state + - platform: apds9960 + direction: down + name: APDS9960 Down + - platform: apds9960 + direction: left + name: APDS9960 Left + - platform: apds9960 + direction: right + name: APDS9960 Right + +sensor: + - platform: apds9960 + type: proximity + name: APDS9960 Proximity + - platform: apds9960 + type: clear + name: APDS9960 Clear + - platform: apds9960 + type: red + name: APDS9960 Red + - platform: apds9960 + type: green + name: APDS9960 Green + - platform: apds9960 + type: blue + name: APDS9960 Blue diff --git a/tests/components/apds9960/test.esp32-c3.yaml b/tests/components/apds9960/test.esp32-c3.yaml new file mode 100644 index 0000000000..f6b6f7bac0 --- /dev/null +++ b/tests/components/apds9960/test.esp32-c3.yaml @@ -0,0 +1,48 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +apds9960: + address: 0x20 + update_interval: 60s + +binary_sensor: + - platform: apds9960 + id: apds9960_binary_sensor + direction: up + name: APDS9960 Up + device_class: motion + filters: + - invert + - delayed_on: 20ms + - delayed_off: 20ms + - lambda: "return false;" + on_state: + - logger.log: New state + - platform: apds9960 + direction: down + name: APDS9960 Down + - platform: apds9960 + direction: left + name: APDS9960 Left + - platform: apds9960 + direction: right + name: APDS9960 Right + +sensor: + - platform: apds9960 + type: proximity + name: APDS9960 Proximity + - platform: apds9960 + type: clear + name: APDS9960 Clear + - platform: apds9960 + type: red + name: APDS9960 Red + - platform: apds9960 + type: green + name: APDS9960 Green + - platform: apds9960 + type: blue + name: APDS9960 Blue diff --git a/tests/components/apds9960/test.esp32-idf.yaml b/tests/components/apds9960/test.esp32-idf.yaml new file mode 100644 index 0000000000..7ff70a4d47 --- /dev/null +++ b/tests/components/apds9960/test.esp32-idf.yaml @@ -0,0 +1,48 @@ +i2c: + - id: i2c_bme280 + scl: 16 + sda: 17 + +apds9960: + address: 0x20 + update_interval: 60s + +binary_sensor: + - platform: apds9960 + id: apds9960_binary_sensor + direction: up + name: APDS9960 Up + device_class: motion + filters: + - invert + - delayed_on: 20ms + - delayed_off: 20ms + - lambda: "return false;" + on_state: + - logger.log: New state + - platform: apds9960 + direction: down + name: APDS9960 Down + - platform: apds9960 + direction: left + name: APDS9960 Left + - platform: apds9960 + direction: right + name: APDS9960 Right + +sensor: + - platform: apds9960 + type: proximity + name: APDS9960 Proximity + - platform: apds9960 + type: clear + name: APDS9960 Clear + - platform: apds9960 + type: red + name: APDS9960 Red + - platform: apds9960 + type: green + name: APDS9960 Green + - platform: apds9960 + type: blue + name: APDS9960 Blue diff --git a/tests/components/apds9960/test.esp32.yaml b/tests/components/apds9960/test.esp32.yaml new file mode 100644 index 0000000000..7ff70a4d47 --- /dev/null +++ b/tests/components/apds9960/test.esp32.yaml @@ -0,0 +1,48 @@ +i2c: + - id: i2c_bme280 + scl: 16 + sda: 17 + +apds9960: + address: 0x20 + update_interval: 60s + +binary_sensor: + - platform: apds9960 + id: apds9960_binary_sensor + direction: up + name: APDS9960 Up + device_class: motion + filters: + - invert + - delayed_on: 20ms + - delayed_off: 20ms + - lambda: "return false;" + on_state: + - logger.log: New state + - platform: apds9960 + direction: down + name: APDS9960 Down + - platform: apds9960 + direction: left + name: APDS9960 Left + - platform: apds9960 + direction: right + name: APDS9960 Right + +sensor: + - platform: apds9960 + type: proximity + name: APDS9960 Proximity + - platform: apds9960 + type: clear + name: APDS9960 Clear + - platform: apds9960 + type: red + name: APDS9960 Red + - platform: apds9960 + type: green + name: APDS9960 Green + - platform: apds9960 + type: blue + name: APDS9960 Blue diff --git a/tests/components/apds9960/test.esp8266.yaml b/tests/components/apds9960/test.esp8266.yaml new file mode 100644 index 0000000000..f6b6f7bac0 --- /dev/null +++ b/tests/components/apds9960/test.esp8266.yaml @@ -0,0 +1,48 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +apds9960: + address: 0x20 + update_interval: 60s + +binary_sensor: + - platform: apds9960 + id: apds9960_binary_sensor + direction: up + name: APDS9960 Up + device_class: motion + filters: + - invert + - delayed_on: 20ms + - delayed_off: 20ms + - lambda: "return false;" + on_state: + - logger.log: New state + - platform: apds9960 + direction: down + name: APDS9960 Down + - platform: apds9960 + direction: left + name: APDS9960 Left + - platform: apds9960 + direction: right + name: APDS9960 Right + +sensor: + - platform: apds9960 + type: proximity + name: APDS9960 Proximity + - platform: apds9960 + type: clear + name: APDS9960 Clear + - platform: apds9960 + type: red + name: APDS9960 Red + - platform: apds9960 + type: green + name: APDS9960 Green + - platform: apds9960 + type: blue + name: APDS9960 Blue diff --git a/tests/components/apds9960/test.rp2040.yaml b/tests/components/apds9960/test.rp2040.yaml new file mode 100644 index 0000000000..f6b6f7bac0 --- /dev/null +++ b/tests/components/apds9960/test.rp2040.yaml @@ -0,0 +1,48 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +apds9960: + address: 0x20 + update_interval: 60s + +binary_sensor: + - platform: apds9960 + id: apds9960_binary_sensor + direction: up + name: APDS9960 Up + device_class: motion + filters: + - invert + - delayed_on: 20ms + - delayed_off: 20ms + - lambda: "return false;" + on_state: + - logger.log: New state + - platform: apds9960 + direction: down + name: APDS9960 Down + - platform: apds9960 + direction: left + name: APDS9960 Left + - platform: apds9960 + direction: right + name: APDS9960 Right + +sensor: + - platform: apds9960 + type: proximity + name: APDS9960 Proximity + - platform: apds9960 + type: clear + name: APDS9960 Clear + - platform: apds9960 + type: red + name: APDS9960 Red + - platform: apds9960 + type: green + name: APDS9960 Green + - platform: apds9960 + type: blue + name: APDS9960 Blue diff --git a/tests/components/api/test.esp32-c3-idf.yaml b/tests/components/api/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..8e7ca0fb06 --- /dev/null +++ b/tests/components/api/test.esp32-c3-idf.yaml @@ -0,0 +1,50 @@ +wifi: + ssid: MySSID + password: password1 + +api: + port: 8000 + password: pwd + reboot_timeout: 0min + encryption: + key: bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU= + services: + - service: hello_world + variables: + name: string + then: + - logger.log: + format: Hello World %s! + args: + - name.c_str() + - service: empty_service + then: + - logger.log: Service Called + - service: all_types + variables: + bool_: bool + int_: int + float_: float + string_: string + then: + - logger.log: Something happened + - service: array_types + variables: + bool_arr: bool[] + int_arr: int[] + float_arr: float[] + string_arr: string[] + then: + - logger.log: + # yamllint disable rule:line-length + format: "Bool: %s (%u), Int: %d (%u), Float: %f (%u), String: %s (%u)" + # yamllint enable rule:line-length + args: + - YESNO(bool_arr[0]) + - bool_arr.size() + - int_arr[0] + - int_arr.size() + - float_arr[0] + - float_arr.size() + - string_arr[0].c_str() + - string_arr.size() diff --git a/tests/components/api/test.esp32-c3.yaml b/tests/components/api/test.esp32-c3.yaml new file mode 100644 index 0000000000..8e7ca0fb06 --- /dev/null +++ b/tests/components/api/test.esp32-c3.yaml @@ -0,0 +1,50 @@ +wifi: + ssid: MySSID + password: password1 + +api: + port: 8000 + password: pwd + reboot_timeout: 0min + encryption: + key: bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU= + services: + - service: hello_world + variables: + name: string + then: + - logger.log: + format: Hello World %s! + args: + - name.c_str() + - service: empty_service + then: + - logger.log: Service Called + - service: all_types + variables: + bool_: bool + int_: int + float_: float + string_: string + then: + - logger.log: Something happened + - service: array_types + variables: + bool_arr: bool[] + int_arr: int[] + float_arr: float[] + string_arr: string[] + then: + - logger.log: + # yamllint disable rule:line-length + format: "Bool: %s (%u), Int: %d (%u), Float: %f (%u), String: %s (%u)" + # yamllint enable rule:line-length + args: + - YESNO(bool_arr[0]) + - bool_arr.size() + - int_arr[0] + - int_arr.size() + - float_arr[0] + - float_arr.size() + - string_arr[0].c_str() + - string_arr.size() diff --git a/tests/components/api/test.esp32-idf.yaml b/tests/components/api/test.esp32-idf.yaml new file mode 100644 index 0000000000..8e7ca0fb06 --- /dev/null +++ b/tests/components/api/test.esp32-idf.yaml @@ -0,0 +1,50 @@ +wifi: + ssid: MySSID + password: password1 + +api: + port: 8000 + password: pwd + reboot_timeout: 0min + encryption: + key: bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU= + services: + - service: hello_world + variables: + name: string + then: + - logger.log: + format: Hello World %s! + args: + - name.c_str() + - service: empty_service + then: + - logger.log: Service Called + - service: all_types + variables: + bool_: bool + int_: int + float_: float + string_: string + then: + - logger.log: Something happened + - service: array_types + variables: + bool_arr: bool[] + int_arr: int[] + float_arr: float[] + string_arr: string[] + then: + - logger.log: + # yamllint disable rule:line-length + format: "Bool: %s (%u), Int: %d (%u), Float: %f (%u), String: %s (%u)" + # yamllint enable rule:line-length + args: + - YESNO(bool_arr[0]) + - bool_arr.size() + - int_arr[0] + - int_arr.size() + - float_arr[0] + - float_arr.size() + - string_arr[0].c_str() + - string_arr.size() diff --git a/tests/components/api/test.esp32.yaml b/tests/components/api/test.esp32.yaml new file mode 100644 index 0000000000..8e7ca0fb06 --- /dev/null +++ b/tests/components/api/test.esp32.yaml @@ -0,0 +1,50 @@ +wifi: + ssid: MySSID + password: password1 + +api: + port: 8000 + password: pwd + reboot_timeout: 0min + encryption: + key: bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU= + services: + - service: hello_world + variables: + name: string + then: + - logger.log: + format: Hello World %s! + args: + - name.c_str() + - service: empty_service + then: + - logger.log: Service Called + - service: all_types + variables: + bool_: bool + int_: int + float_: float + string_: string + then: + - logger.log: Something happened + - service: array_types + variables: + bool_arr: bool[] + int_arr: int[] + float_arr: float[] + string_arr: string[] + then: + - logger.log: + # yamllint disable rule:line-length + format: "Bool: %s (%u), Int: %d (%u), Float: %f (%u), String: %s (%u)" + # yamllint enable rule:line-length + args: + - YESNO(bool_arr[0]) + - bool_arr.size() + - int_arr[0] + - int_arr.size() + - float_arr[0] + - float_arr.size() + - string_arr[0].c_str() + - string_arr.size() diff --git a/tests/components/api/test.esp8266.yaml b/tests/components/api/test.esp8266.yaml new file mode 100644 index 0000000000..8e7ca0fb06 --- /dev/null +++ b/tests/components/api/test.esp8266.yaml @@ -0,0 +1,50 @@ +wifi: + ssid: MySSID + password: password1 + +api: + port: 8000 + password: pwd + reboot_timeout: 0min + encryption: + key: bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU= + services: + - service: hello_world + variables: + name: string + then: + - logger.log: + format: Hello World %s! + args: + - name.c_str() + - service: empty_service + then: + - logger.log: Service Called + - service: all_types + variables: + bool_: bool + int_: int + float_: float + string_: string + then: + - logger.log: Something happened + - service: array_types + variables: + bool_arr: bool[] + int_arr: int[] + float_arr: float[] + string_arr: string[] + then: + - logger.log: + # yamllint disable rule:line-length + format: "Bool: %s (%u), Int: %d (%u), Float: %f (%u), String: %s (%u)" + # yamllint enable rule:line-length + args: + - YESNO(bool_arr[0]) + - bool_arr.size() + - int_arr[0] + - int_arr.size() + - float_arr[0] + - float_arr.size() + - string_arr[0].c_str() + - string_arr.size() diff --git a/tests/components/api/test.rp2040.yaml b/tests/components/api/test.rp2040.yaml new file mode 100644 index 0000000000..8e7ca0fb06 --- /dev/null +++ b/tests/components/api/test.rp2040.yaml @@ -0,0 +1,50 @@ +wifi: + ssid: MySSID + password: password1 + +api: + port: 8000 + password: pwd + reboot_timeout: 0min + encryption: + key: bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU= + services: + - service: hello_world + variables: + name: string + then: + - logger.log: + format: Hello World %s! + args: + - name.c_str() + - service: empty_service + then: + - logger.log: Service Called + - service: all_types + variables: + bool_: bool + int_: int + float_: float + string_: string + then: + - logger.log: Something happened + - service: array_types + variables: + bool_arr: bool[] + int_arr: int[] + float_arr: float[] + string_arr: string[] + then: + - logger.log: + # yamllint disable rule:line-length + format: "Bool: %s (%u), Int: %d (%u), Float: %f (%u), String: %s (%u)" + # yamllint enable rule:line-length + args: + - YESNO(bool_arr[0]) + - bool_arr.size() + - int_arr[0] + - int_arr.size() + - float_arr[0] + - float_arr.size() + - string_arr[0].c_str() + - string_arr.size() diff --git a/tests/components/as3935_i2c/test.esp32-c3-idf.yaml b/tests/components/as3935_i2c/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..c72556dbac --- /dev/null +++ b/tests/components/as3935_i2c/test.esp32-c3-idf.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_as3935 + scl: 5 + sda: 4 + +as3935_i2c: + irq_pin: 6 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_i2c/test.esp32-c3.yaml b/tests/components/as3935_i2c/test.esp32-c3.yaml new file mode 100644 index 0000000000..c72556dbac --- /dev/null +++ b/tests/components/as3935_i2c/test.esp32-c3.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_as3935 + scl: 5 + sda: 4 + +as3935_i2c: + irq_pin: 6 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_i2c/test.esp32-idf.yaml b/tests/components/as3935_i2c/test.esp32-idf.yaml new file mode 100644 index 0000000000..fad703bee5 --- /dev/null +++ b/tests/components/as3935_i2c/test.esp32-idf.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_as3935 + scl: 16 + sda: 17 + +as3935_i2c: + irq_pin: 12 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_i2c/test.esp32.yaml b/tests/components/as3935_i2c/test.esp32.yaml new file mode 100644 index 0000000000..fad703bee5 --- /dev/null +++ b/tests/components/as3935_i2c/test.esp32.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_as3935 + scl: 16 + sda: 17 + +as3935_i2c: + irq_pin: 12 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_i2c/test.esp8266.yaml b/tests/components/as3935_i2c/test.esp8266.yaml new file mode 100644 index 0000000000..adba9e440f --- /dev/null +++ b/tests/components/as3935_i2c/test.esp8266.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_as3935 + scl: 5 + sda: 4 + +as3935_i2c: + irq_pin: 15 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_i2c/test.rp2040.yaml b/tests/components/as3935_i2c/test.rp2040.yaml new file mode 100644 index 0000000000..c72556dbac --- /dev/null +++ b/tests/components/as3935_i2c/test.rp2040.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_as3935 + scl: 5 + sda: 4 + +as3935_i2c: + irq_pin: 6 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_spi/test.esp32-c3-idf.yaml b/tests/components/as3935_spi/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..7a4a01aeea --- /dev/null +++ b/tests/components/as3935_spi/test.esp32-c3-idf.yaml @@ -0,0 +1,20 @@ +spi: + - id: spi_as3935 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +as3935_spi: + cs_pin: 2 + irq_pin: 3 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_spi/test.esp32-c3.yaml b/tests/components/as3935_spi/test.esp32-c3.yaml new file mode 100644 index 0000000000..7a4a01aeea --- /dev/null +++ b/tests/components/as3935_spi/test.esp32-c3.yaml @@ -0,0 +1,20 @@ +spi: + - id: spi_as3935 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +as3935_spi: + cs_pin: 2 + irq_pin: 3 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_spi/test.esp32-idf.yaml b/tests/components/as3935_spi/test.esp32-idf.yaml new file mode 100644 index 0000000000..813a39cb23 --- /dev/null +++ b/tests/components/as3935_spi/test.esp32-idf.yaml @@ -0,0 +1,20 @@ +spi: + - id: spi_as3935 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +as3935_spi: + cs_pin: 12 + irq_pin: 13 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_spi/test.esp32.yaml b/tests/components/as3935_spi/test.esp32.yaml new file mode 100644 index 0000000000..813a39cb23 --- /dev/null +++ b/tests/components/as3935_spi/test.esp32.yaml @@ -0,0 +1,20 @@ +spi: + - id: spi_as3935 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +as3935_spi: + cs_pin: 12 + irq_pin: 13 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_spi/test.esp8266.yaml b/tests/components/as3935_spi/test.esp8266.yaml new file mode 100644 index 0000000000..38a40b0833 --- /dev/null +++ b/tests/components/as3935_spi/test.esp8266.yaml @@ -0,0 +1,20 @@ +spi: + - id: spi_as3935 + clk_pin: 14 + mosi_pin: 13 + miso_pin: 12 + +as3935_spi: + cs_pin: 15 + irq_pin: 16 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_spi/test.rp2040.yaml b/tests/components/as3935_spi/test.rp2040.yaml new file mode 100644 index 0000000000..528759d97d --- /dev/null +++ b/tests/components/as3935_spi/test.rp2040.yaml @@ -0,0 +1,20 @@ +spi: + - id: spi_as3935 + clk_pin: 2 + mosi_pin: 3 + miso_pin: 4 + +as3935_spi: + cs_pin: 6 + irq_pin: 7 + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as5600/test.esp32-c3-idf.yaml b/tests/components/as5600/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..e074fa5e0c --- /dev/null +++ b/tests/components/as5600/test.esp32-c3-idf.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_as5600 + scl: 5 + sda: 4 + +as5600: + dir_pin: 6 + direction: clockwise + start_position: 90deg + range: 180deg + watchdog: true + power_mode: low1 + hysteresis: lsb1 + slow_filter: 8x + fast_filter: lsb6 + +sensor: + - platform: as5600 + name: AS5600 Position + raw_position: + name: AS5600 Raw Position + gain: + name: AS5600 Gain + magnitude: + name: AS5600 Magnitude + status: + name: AS5600 Status diff --git a/tests/components/as5600/test.esp32-c3.yaml b/tests/components/as5600/test.esp32-c3.yaml new file mode 100644 index 0000000000..e074fa5e0c --- /dev/null +++ b/tests/components/as5600/test.esp32-c3.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_as5600 + scl: 5 + sda: 4 + +as5600: + dir_pin: 6 + direction: clockwise + start_position: 90deg + range: 180deg + watchdog: true + power_mode: low1 + hysteresis: lsb1 + slow_filter: 8x + fast_filter: lsb6 + +sensor: + - platform: as5600 + name: AS5600 Position + raw_position: + name: AS5600 Raw Position + gain: + name: AS5600 Gain + magnitude: + name: AS5600 Magnitude + status: + name: AS5600 Status diff --git a/tests/components/as5600/test.esp32-idf.yaml b/tests/components/as5600/test.esp32-idf.yaml new file mode 100644 index 0000000000..312ee9ad04 --- /dev/null +++ b/tests/components/as5600/test.esp32-idf.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_as5600 + scl: 16 + sda: 17 + +as5600: + dir_pin: 12 + direction: clockwise + start_position: 90deg + range: 180deg + watchdog: true + power_mode: low1 + hysteresis: lsb1 + slow_filter: 8x + fast_filter: lsb6 + +sensor: + - platform: as5600 + name: AS5600 Position + raw_position: + name: AS5600 Raw Position + gain: + name: AS5600 Gain + magnitude: + name: AS5600 Magnitude + status: + name: AS5600 Status diff --git a/tests/components/as5600/test.esp32.yaml b/tests/components/as5600/test.esp32.yaml new file mode 100644 index 0000000000..312ee9ad04 --- /dev/null +++ b/tests/components/as5600/test.esp32.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_as5600 + scl: 16 + sda: 17 + +as5600: + dir_pin: 12 + direction: clockwise + start_position: 90deg + range: 180deg + watchdog: true + power_mode: low1 + hysteresis: lsb1 + slow_filter: 8x + fast_filter: lsb6 + +sensor: + - platform: as5600 + name: AS5600 Position + raw_position: + name: AS5600 Raw Position + gain: + name: AS5600 Gain + magnitude: + name: AS5600 Magnitude + status: + name: AS5600 Status diff --git a/tests/components/as5600/test.esp8266.yaml b/tests/components/as5600/test.esp8266.yaml new file mode 100644 index 0000000000..a232d27305 --- /dev/null +++ b/tests/components/as5600/test.esp8266.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_as5600 + scl: 5 + sda: 4 + +as5600: + dir_pin: 15 + direction: clockwise + start_position: 90deg + range: 180deg + watchdog: true + power_mode: low1 + hysteresis: lsb1 + slow_filter: 8x + fast_filter: lsb6 + +sensor: + - platform: as5600 + name: AS5600 Position + raw_position: + name: AS5600 Raw Position + gain: + name: AS5600 Gain + magnitude: + name: AS5600 Magnitude + status: + name: AS5600 Status diff --git a/tests/components/as5600/test.rp2040.yaml b/tests/components/as5600/test.rp2040.yaml new file mode 100644 index 0000000000..e074fa5e0c --- /dev/null +++ b/tests/components/as5600/test.rp2040.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_as5600 + scl: 5 + sda: 4 + +as5600: + dir_pin: 6 + direction: clockwise + start_position: 90deg + range: 180deg + watchdog: true + power_mode: low1 + hysteresis: lsb1 + slow_filter: 8x + fast_filter: lsb6 + +sensor: + - platform: as5600 + name: AS5600 Position + raw_position: + name: AS5600 Raw Position + gain: + name: AS5600 Gain + magnitude: + name: AS5600 Magnitude + status: + name: AS5600 Status diff --git a/tests/components/as7341/test.esp32-c3-idf.yaml b/tests/components/as7341/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..19965d1715 --- /dev/null +++ b/tests/components/as7341/test.esp32-c3-idf.yaml @@ -0,0 +1,31 @@ +i2c: + - id: i2c_as5600 + scl: 5 + sda: 4 + +sensor: + - platform: as7341 + update_interval: 15s + gain: X8 + atime: 120 + astep: 99 + f1: + name: F1 + f2: + name: F2 + f3: + name: F3 + f4: + name: F4 + f5: + name: F5 + f6: + name: F6 + f7: + name: F7 + f8: + name: F8 + clear: + name: Clear + nir: + name: NIR diff --git a/tests/components/as7341/test.esp32-c3.yaml b/tests/components/as7341/test.esp32-c3.yaml new file mode 100644 index 0000000000..19965d1715 --- /dev/null +++ b/tests/components/as7341/test.esp32-c3.yaml @@ -0,0 +1,31 @@ +i2c: + - id: i2c_as5600 + scl: 5 + sda: 4 + +sensor: + - platform: as7341 + update_interval: 15s + gain: X8 + atime: 120 + astep: 99 + f1: + name: F1 + f2: + name: F2 + f3: + name: F3 + f4: + name: F4 + f5: + name: F5 + f6: + name: F6 + f7: + name: F7 + f8: + name: F8 + clear: + name: Clear + nir: + name: NIR diff --git a/tests/components/as7341/test.esp32-idf.yaml b/tests/components/as7341/test.esp32-idf.yaml new file mode 100644 index 0000000000..d582a367ac --- /dev/null +++ b/tests/components/as7341/test.esp32-idf.yaml @@ -0,0 +1,31 @@ +i2c: + - id: i2c_as5600 + scl: 16 + sda: 17 + +sensor: + - platform: as7341 + update_interval: 15s + gain: X8 + atime: 120 + astep: 99 + f1: + name: F1 + f2: + name: F2 + f3: + name: F3 + f4: + name: F4 + f5: + name: F5 + f6: + name: F6 + f7: + name: F7 + f8: + name: F8 + clear: + name: Clear + nir: + name: NIR diff --git a/tests/components/as7341/test.esp32.yaml b/tests/components/as7341/test.esp32.yaml new file mode 100644 index 0000000000..d582a367ac --- /dev/null +++ b/tests/components/as7341/test.esp32.yaml @@ -0,0 +1,31 @@ +i2c: + - id: i2c_as5600 + scl: 16 + sda: 17 + +sensor: + - platform: as7341 + update_interval: 15s + gain: X8 + atime: 120 + astep: 99 + f1: + name: F1 + f2: + name: F2 + f3: + name: F3 + f4: + name: F4 + f5: + name: F5 + f6: + name: F6 + f7: + name: F7 + f8: + name: F8 + clear: + name: Clear + nir: + name: NIR diff --git a/tests/components/as7341/test.esp8266.yaml b/tests/components/as7341/test.esp8266.yaml new file mode 100644 index 0000000000..19965d1715 --- /dev/null +++ b/tests/components/as7341/test.esp8266.yaml @@ -0,0 +1,31 @@ +i2c: + - id: i2c_as5600 + scl: 5 + sda: 4 + +sensor: + - platform: as7341 + update_interval: 15s + gain: X8 + atime: 120 + astep: 99 + f1: + name: F1 + f2: + name: F2 + f3: + name: F3 + f4: + name: F4 + f5: + name: F5 + f6: + name: F6 + f7: + name: F7 + f8: + name: F8 + clear: + name: Clear + nir: + name: NIR diff --git a/tests/components/as7341/test.rp2040.yaml b/tests/components/as7341/test.rp2040.yaml new file mode 100644 index 0000000000..19965d1715 --- /dev/null +++ b/tests/components/as7341/test.rp2040.yaml @@ -0,0 +1,31 @@ +i2c: + - id: i2c_as5600 + scl: 5 + sda: 4 + +sensor: + - platform: as7341 + update_interval: 15s + gain: X8 + atime: 120 + astep: 99 + f1: + name: F1 + f2: + name: F2 + f3: + name: F3 + f4: + name: F4 + f5: + name: F5 + f6: + name: F6 + f7: + name: F7 + f8: + name: F8 + clear: + name: Clear + nir: + name: NIR diff --git a/tests/components/atc_mithermometer/test.esp32-c3-idf.yaml b/tests/components/atc_mithermometer/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..0248090c23 --- /dev/null +++ b/tests/components/atc_mithermometer/test.esp32-c3-idf.yaml @@ -0,0 +1,13 @@ +esp32_ble_tracker: + +sensor: + - platform: atc_mithermometer + mac_address: A4:C1:38:4E:16:78 + temperature: + name: ATC Temperature + humidity: + name: ATC Humidity + battery_level: + name: ATC Battery-Level + battery_voltage: + name: ATC Battery-Voltage diff --git a/tests/components/atc_mithermometer/test.esp32-c3.yaml b/tests/components/atc_mithermometer/test.esp32-c3.yaml new file mode 100644 index 0000000000..0248090c23 --- /dev/null +++ b/tests/components/atc_mithermometer/test.esp32-c3.yaml @@ -0,0 +1,13 @@ +esp32_ble_tracker: + +sensor: + - platform: atc_mithermometer + mac_address: A4:C1:38:4E:16:78 + temperature: + name: ATC Temperature + humidity: + name: ATC Humidity + battery_level: + name: ATC Battery-Level + battery_voltage: + name: ATC Battery-Voltage diff --git a/tests/components/atc_mithermometer/test.esp32-idf.yaml b/tests/components/atc_mithermometer/test.esp32-idf.yaml new file mode 100644 index 0000000000..0248090c23 --- /dev/null +++ b/tests/components/atc_mithermometer/test.esp32-idf.yaml @@ -0,0 +1,13 @@ +esp32_ble_tracker: + +sensor: + - platform: atc_mithermometer + mac_address: A4:C1:38:4E:16:78 + temperature: + name: ATC Temperature + humidity: + name: ATC Humidity + battery_level: + name: ATC Battery-Level + battery_voltage: + name: ATC Battery-Voltage diff --git a/tests/components/atc_mithermometer/test.esp32.yaml b/tests/components/atc_mithermometer/test.esp32.yaml new file mode 100644 index 0000000000..0248090c23 --- /dev/null +++ b/tests/components/atc_mithermometer/test.esp32.yaml @@ -0,0 +1,13 @@ +esp32_ble_tracker: + +sensor: + - platform: atc_mithermometer + mac_address: A4:C1:38:4E:16:78 + temperature: + name: ATC Temperature + humidity: + name: ATC Humidity + battery_level: + name: ATC Battery-Level + battery_voltage: + name: ATC Battery-Voltage diff --git a/tests/components/atm90e26/test.esp32-c3-idf.yaml b/tests/components/atm90e26/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ce123bcf72 --- /dev/null +++ b/tests/components/atm90e26/test.esp32-c3-idf.yaml @@ -0,0 +1,26 @@ +spi: + - id: spi_atm90e26 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: atm90e26 + cs_pin: 8 + voltage: + name: Line Voltage + current: + name: CT Amps + power: + name: Active Watts + power_factor: + name: Power Factor + frequency: + name: Line Frequency + line_frequency: 50Hz + meter_constant: 1000 + pl_const: 1429876 + gain_pga: 1X + gain_metering: 7481 + gain_voltage: 26400 + gain_ct: 31251 diff --git a/tests/components/atm90e26/test.esp32-c3.yaml b/tests/components/atm90e26/test.esp32-c3.yaml new file mode 100644 index 0000000000..ce123bcf72 --- /dev/null +++ b/tests/components/atm90e26/test.esp32-c3.yaml @@ -0,0 +1,26 @@ +spi: + - id: spi_atm90e26 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: atm90e26 + cs_pin: 8 + voltage: + name: Line Voltage + current: + name: CT Amps + power: + name: Active Watts + power_factor: + name: Power Factor + frequency: + name: Line Frequency + line_frequency: 50Hz + meter_constant: 1000 + pl_const: 1429876 + gain_pga: 1X + gain_metering: 7481 + gain_voltage: 26400 + gain_ct: 31251 diff --git a/tests/components/atm90e26/test.esp32-idf.yaml b/tests/components/atm90e26/test.esp32-idf.yaml new file mode 100644 index 0000000000..72fb3e5b24 --- /dev/null +++ b/tests/components/atm90e26/test.esp32-idf.yaml @@ -0,0 +1,26 @@ +spi: + - id: spi_atm90e26 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: atm90e26 + cs_pin: 13 + voltage: + name: Line Voltage + current: + name: CT Amps + power: + name: Active Watts + power_factor: + name: Power Factor + frequency: + name: Line Frequency + line_frequency: 50Hz + meter_constant: 1000 + pl_const: 1429876 + gain_pga: 1X + gain_metering: 7481 + gain_voltage: 26400 + gain_ct: 31251 diff --git a/tests/components/atm90e26/test.esp32.yaml b/tests/components/atm90e26/test.esp32.yaml new file mode 100644 index 0000000000..72fb3e5b24 --- /dev/null +++ b/tests/components/atm90e26/test.esp32.yaml @@ -0,0 +1,26 @@ +spi: + - id: spi_atm90e26 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: atm90e26 + cs_pin: 13 + voltage: + name: Line Voltage + current: + name: CT Amps + power: + name: Active Watts + power_factor: + name: Power Factor + frequency: + name: Line Frequency + line_frequency: 50Hz + meter_constant: 1000 + pl_const: 1429876 + gain_pga: 1X + gain_metering: 7481 + gain_voltage: 26400 + gain_ct: 31251 diff --git a/tests/components/atm90e26/test.esp8266.yaml b/tests/components/atm90e26/test.esp8266.yaml new file mode 100644 index 0000000000..68d63cc278 --- /dev/null +++ b/tests/components/atm90e26/test.esp8266.yaml @@ -0,0 +1,26 @@ +spi: + - id: spi_atm90e26 + clk_pin: 14 + mosi_pin: 13 + miso_pin: 12 + +sensor: + - platform: atm90e26 + cs_pin: 5 + voltage: + name: Line Voltage + current: + name: CT Amps + power: + name: Active Watts + power_factor: + name: Power Factor + frequency: + name: Line Frequency + line_frequency: 50Hz + meter_constant: 1000 + pl_const: 1429876 + gain_pga: 1X + gain_metering: 7481 + gain_voltage: 26400 + gain_ct: 31251 diff --git a/tests/components/atm90e26/test.rp2040.yaml b/tests/components/atm90e26/test.rp2040.yaml new file mode 100644 index 0000000000..f43277dbb1 --- /dev/null +++ b/tests/components/atm90e26/test.rp2040.yaml @@ -0,0 +1,26 @@ +spi: + - id: spi_atm90e26 + clk_pin: 2 + mosi_pin: 3 + miso_pin: 4 + +sensor: + - platform: atm90e26 + cs_pin: 5 + voltage: + name: Line Voltage + current: + name: CT Amps + power: + name: Active Watts + power_factor: + name: Power Factor + frequency: + name: Line Frequency + line_frequency: 50Hz + meter_constant: 1000 + pl_const: 1429876 + gain_pga: 1X + gain_metering: 7481 + gain_voltage: 26400 + gain_ct: 31251 diff --git a/tests/components/atm90e32/test.esp32-c3-idf.yaml b/tests/components/atm90e32/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..263fb6d24e --- /dev/null +++ b/tests/components/atm90e32/test.esp32-c3-idf.yaml @@ -0,0 +1,51 @@ +spi: + - id: spi_atm90e32 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: atm90e32 + cs_pin: 8 + phase_a: + voltage: + name: EMON Line Voltage A + current: + name: EMON CT1 Current + power: + name: EMON Active Power CT1 + reactive_power: + name: EMON Reactive Power CT1 + power_factor: + name: EMON Power Factor CT1 + gain_voltage: 7305 + gain_ct: 27961 + phase_b: + current: + name: EMON CT2 Current + power: + name: EMON Active Power CT2 + reactive_power: + name: EMON Reactive Power CT2 + power_factor: + name: EMON Power Factor CT2 + gain_voltage: 7305 + gain_ct: 27961 + phase_c: + current: + name: EMON CT3 Current + power: + name: EMON Active Power CT3 + reactive_power: + name: EMON Reactive Power CT3 + power_factor: + name: EMON Power Factor CT3 + gain_voltage: 7305 + gain_ct: 27961 + frequency: + name: EMON Line Frequency + chip_temperature: + name: EMON Chip Temp A + line_frequency: 60Hz + current_phases: 3 + gain_pga: 2X diff --git a/tests/components/atm90e32/test.esp32-c3.yaml b/tests/components/atm90e32/test.esp32-c3.yaml new file mode 100644 index 0000000000..263fb6d24e --- /dev/null +++ b/tests/components/atm90e32/test.esp32-c3.yaml @@ -0,0 +1,51 @@ +spi: + - id: spi_atm90e32 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: atm90e32 + cs_pin: 8 + phase_a: + voltage: + name: EMON Line Voltage A + current: + name: EMON CT1 Current + power: + name: EMON Active Power CT1 + reactive_power: + name: EMON Reactive Power CT1 + power_factor: + name: EMON Power Factor CT1 + gain_voltage: 7305 + gain_ct: 27961 + phase_b: + current: + name: EMON CT2 Current + power: + name: EMON Active Power CT2 + reactive_power: + name: EMON Reactive Power CT2 + power_factor: + name: EMON Power Factor CT2 + gain_voltage: 7305 + gain_ct: 27961 + phase_c: + current: + name: EMON CT3 Current + power: + name: EMON Active Power CT3 + reactive_power: + name: EMON Reactive Power CT3 + power_factor: + name: EMON Power Factor CT3 + gain_voltage: 7305 + gain_ct: 27961 + frequency: + name: EMON Line Frequency + chip_temperature: + name: EMON Chip Temp A + line_frequency: 60Hz + current_phases: 3 + gain_pga: 2X diff --git a/tests/components/atm90e32/test.esp32-idf.yaml b/tests/components/atm90e32/test.esp32-idf.yaml new file mode 100644 index 0000000000..131270f8ad --- /dev/null +++ b/tests/components/atm90e32/test.esp32-idf.yaml @@ -0,0 +1,51 @@ +spi: + - id: spi_atm90e32 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: atm90e32 + cs_pin: 13 + phase_a: + voltage: + name: EMON Line Voltage A + current: + name: EMON CT1 Current + power: + name: EMON Active Power CT1 + reactive_power: + name: EMON Reactive Power CT1 + power_factor: + name: EMON Power Factor CT1 + gain_voltage: 7305 + gain_ct: 27961 + phase_b: + current: + name: EMON CT2 Current + power: + name: EMON Active Power CT2 + reactive_power: + name: EMON Reactive Power CT2 + power_factor: + name: EMON Power Factor CT2 + gain_voltage: 7305 + gain_ct: 27961 + phase_c: + current: + name: EMON CT3 Current + power: + name: EMON Active Power CT3 + reactive_power: + name: EMON Reactive Power CT3 + power_factor: + name: EMON Power Factor CT3 + gain_voltage: 7305 + gain_ct: 27961 + frequency: + name: EMON Line Frequency + chip_temperature: + name: EMON Chip Temp A + line_frequency: 60Hz + current_phases: 3 + gain_pga: 2X diff --git a/tests/components/atm90e32/test.esp32.yaml b/tests/components/atm90e32/test.esp32.yaml new file mode 100644 index 0000000000..131270f8ad --- /dev/null +++ b/tests/components/atm90e32/test.esp32.yaml @@ -0,0 +1,51 @@ +spi: + - id: spi_atm90e32 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: atm90e32 + cs_pin: 13 + phase_a: + voltage: + name: EMON Line Voltage A + current: + name: EMON CT1 Current + power: + name: EMON Active Power CT1 + reactive_power: + name: EMON Reactive Power CT1 + power_factor: + name: EMON Power Factor CT1 + gain_voltage: 7305 + gain_ct: 27961 + phase_b: + current: + name: EMON CT2 Current + power: + name: EMON Active Power CT2 + reactive_power: + name: EMON Reactive Power CT2 + power_factor: + name: EMON Power Factor CT2 + gain_voltage: 7305 + gain_ct: 27961 + phase_c: + current: + name: EMON CT3 Current + power: + name: EMON Active Power CT3 + reactive_power: + name: EMON Reactive Power CT3 + power_factor: + name: EMON Power Factor CT3 + gain_voltage: 7305 + gain_ct: 27961 + frequency: + name: EMON Line Frequency + chip_temperature: + name: EMON Chip Temp A + line_frequency: 60Hz + current_phases: 3 + gain_pga: 2X diff --git a/tests/components/atm90e32/test.esp8266.yaml b/tests/components/atm90e32/test.esp8266.yaml new file mode 100644 index 0000000000..e8e2abc1a9 --- /dev/null +++ b/tests/components/atm90e32/test.esp8266.yaml @@ -0,0 +1,51 @@ +spi: + - id: spi_atm90e32 + clk_pin: 14 + mosi_pin: 13 + miso_pin: 12 + +sensor: + - platform: atm90e32 + cs_pin: 5 + phase_a: + voltage: + name: EMON Line Voltage A + current: + name: EMON CT1 Current + power: + name: EMON Active Power CT1 + reactive_power: + name: EMON Reactive Power CT1 + power_factor: + name: EMON Power Factor CT1 + gain_voltage: 7305 + gain_ct: 27961 + phase_b: + current: + name: EMON CT2 Current + power: + name: EMON Active Power CT2 + reactive_power: + name: EMON Reactive Power CT2 + power_factor: + name: EMON Power Factor CT2 + gain_voltage: 7305 + gain_ct: 27961 + phase_c: + current: + name: EMON CT3 Current + power: + name: EMON Active Power CT3 + reactive_power: + name: EMON Reactive Power CT3 + power_factor: + name: EMON Power Factor CT3 + gain_voltage: 7305 + gain_ct: 27961 + frequency: + name: EMON Line Frequency + chip_temperature: + name: EMON Chip Temp A + line_frequency: 60Hz + current_phases: 3 + gain_pga: 2X diff --git a/tests/components/atm90e32/test.rp2040.yaml b/tests/components/atm90e32/test.rp2040.yaml new file mode 100644 index 0000000000..525e0b801a --- /dev/null +++ b/tests/components/atm90e32/test.rp2040.yaml @@ -0,0 +1,51 @@ +spi: + - id: spi_atm90e32 + clk_pin: 2 + mosi_pin: 3 + miso_pin: 4 + +sensor: + - platform: atm90e32 + cs_pin: 5 + phase_a: + voltage: + name: EMON Line Voltage A + current: + name: EMON CT1 Current + power: + name: EMON Active Power CT1 + reactive_power: + name: EMON Reactive Power CT1 + power_factor: + name: EMON Power Factor CT1 + gain_voltage: 7305 + gain_ct: 27961 + phase_b: + current: + name: EMON CT2 Current + power: + name: EMON Active Power CT2 + reactive_power: + name: EMON Reactive Power CT2 + power_factor: + name: EMON Power Factor CT2 + gain_voltage: 7305 + gain_ct: 27961 + phase_c: + current: + name: EMON CT3 Current + power: + name: EMON Active Power CT3 + reactive_power: + name: EMON Reactive Power CT3 + power_factor: + name: EMON Power Factor CT3 + gain_voltage: 7305 + gain_ct: 27961 + frequency: + name: EMON Line Frequency + chip_temperature: + name: EMON Chip Temp A + line_frequency: 60Hz + current_phases: 3 + gain_pga: 2X From cfe16c92ee368f3a1180d5cb9398b8927c5ac12b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Feb 2024 13:07:37 -0600 Subject: [PATCH 179/193] Bump aioesphomeapi to 21.0.2 (#6188) --- requirements.txt | 3 ++- requirements_optional.txt | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 18e0295fb2..878d2e8a50 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ async_timeout==4.0.3; python_version <= "3.10" +cryptography==42.0.2 voluptuous==0.14.1 PyYAML==6.0.1 paho-mqtt==1.6.1 @@ -12,7 +13,7 @@ platformio==6.1.13 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 esphome-dashboard==20231107.0 -aioesphomeapi==21.0.1 +aioesphomeapi==21.0.2 zeroconf==0.131.0 python-magic==0.4.27 diff --git a/requirements_optional.txt b/requirements_optional.txt index 54494b4585..c984d41332 100644 --- a/requirements_optional.txt +++ b/requirements_optional.txt @@ -1,3 +1,2 @@ pillow==10.2.0 cairosvg==2.7.1 -cryptography==41.0.4 From 05da0fb4cf109a8a76b5221c439e998b56081925 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 6 Feb 2024 13:32:40 -0600 Subject: [PATCH 180/193] Add some components to the new testing framework (B) (#6173) --- .../b_parasite/test.esp32-c3-idf.yaml | 15 +++++ .../components/b_parasite/test.esp32-c3.yaml | 15 +++++ .../components/b_parasite/test.esp32-idf.yaml | 15 +++++ tests/components/b_parasite/test.esp32.yaml | 15 +++++ tests/components/ballu/test.esp32.yaml | 12 ++++ tests/components/ballu/test.esp8266.yaml | 12 ++++ .../bang_bang/test.esp32-c3-idf.yaml | 35 +++++++++++ tests/components/bang_bang/test.esp32-c3.yaml | 35 +++++++++++ .../components/bang_bang/test.esp32-idf.yaml | 35 +++++++++++ tests/components/bang_bang/test.esp32.yaml | 35 +++++++++++ tests/components/bang_bang/test.esp8266.yaml | 35 +++++++++++ tests/components/bang_bang/test.rp2040.yaml | 35 +++++++++++ .../components/bedjet/test.esp32-c3-idf.yaml | 33 ++++++++++ tests/components/bedjet/test.esp32-c3.yaml | 33 ++++++++++ tests/components/bedjet/test.esp32-idf.yaml | 33 ++++++++++ tests/components/bedjet/test.esp32.yaml | 33 ++++++++++ .../components/bh1750/test.esp32-c3-idf.yaml | 10 +++ tests/components/bh1750/test.esp32-c3.yaml | 10 +++ tests/components/bh1750/test.esp32-idf.yaml | 10 +++ tests/components/bh1750/test.esp32.yaml | 10 +++ tests/components/bh1750/test.esp8266.yaml | 10 +++ tests/components/bh1750/test.rp2040.yaml | 10 +++ .../binary_sensor_map/test.esp32-c3-idf.yaml | 61 +++++++++++++++++++ .../binary_sensor_map/test.esp32-c3.yaml | 61 +++++++++++++++++++ .../binary_sensor_map/test.esp32-idf.yaml | 61 +++++++++++++++++++ .../binary_sensor_map/test.esp32.yaml | 61 +++++++++++++++++++ .../binary_sensor_map/test.esp8266.yaml | 61 +++++++++++++++++++ .../binary_sensor_map/test.rp2040.yaml | 61 +++++++++++++++++++ .../components/bl0939/test.esp32-c3-idf.yaml | 26 ++++++++ tests/components/bl0939/test.esp32-c3.yaml | 26 ++++++++ tests/components/bl0939/test.esp32-idf.yaml | 26 ++++++++ tests/components/bl0939/test.esp32.yaml | 26 ++++++++ tests/components/bl0939/test.esp8266.yaml | 26 ++++++++ tests/components/bl0939/test.rp2040.yaml | 26 ++++++++ .../components/bl0940/test.esp32-c3-idf.yaml | 22 +++++++ tests/components/bl0940/test.esp32-c3.yaml | 22 +++++++ tests/components/bl0940/test.esp32-idf.yaml | 22 +++++++ tests/components/bl0940/test.esp32.yaml | 22 +++++++ tests/components/bl0940/test.esp8266.yaml | 22 +++++++ tests/components/bl0940/test.rp2040.yaml | 22 +++++++ .../components/bl0942/test.esp32-c3-idf.yaml | 20 ++++++ tests/components/bl0942/test.esp32-c3.yaml | 20 ++++++ tests/components/bl0942/test.esp32-idf.yaml | 20 ++++++ tests/components/bl0942/test.esp32.yaml | 20 ++++++ tests/components/bl0942/test.esp8266.yaml | 20 ++++++ tests/components/bl0942/test.rp2040.yaml | 20 ++++++ .../ble_client/test.esp32-c3-idf.yaml | 5 ++ .../components/ble_client/test.esp32-c3.yaml | 5 ++ .../components/ble_client/test.esp32-idf.yaml | 5 ++ tests/components/ble_client/test.esp32.yaml | 5 ++ .../ble_presence/test.esp32-c3-idf.yaml | 20 ++++++ .../ble_presence/test.esp32-c3.yaml | 20 ++++++ .../ble_presence/test.esp32-idf.yaml | 20 ++++++ tests/components/ble_presence/test.esp32.yaml | 20 ++++++ .../ble_rssi/test.esp32-c3-idf.yaml | 18 ++++++ tests/components/ble_rssi/test.esp32-c3.yaml | 18 ++++++ tests/components/ble_rssi/test.esp32-idf.yaml | 18 ++++++ tests/components/ble_rssi/test.esp32.yaml | 18 ++++++ .../ble_scanner/test.esp32-c3-idf.yaml | 5 ++ .../components/ble_scanner/test.esp32-c3.yaml | 5 ++ .../ble_scanner/test.esp32-idf.yaml | 5 ++ tests/components/ble_scanner/test.esp32.yaml | 5 ++ .../bme280_i2c/test.esp32-c3-idf.yaml | 18 ++++++ .../components/bme280_i2c/test.esp32-c3.yaml | 18 ++++++ .../components/bme280_i2c/test.esp32-idf.yaml | 18 ++++++ tests/components/bme280_i2c/test.esp32.yaml | 18 ++++++ tests/components/bme280_i2c/test.esp8266.yaml | 18 ++++++ tests/components/bme280_i2c/test.rp2040.yaml | 18 ++++++ .../bme280_spi/test.esp32-c3-idf.yaml | 19 ++++++ .../components/bme280_spi/test.esp32-c3.yaml | 19 ++++++ .../components/bme280_spi/test.esp32-idf.yaml | 19 ++++++ tests/components/bme280_spi/test.esp32.yaml | 19 ++++++ tests/components/bme280_spi/test.esp8266.yaml | 19 ++++++ tests/components/bme280_spi/test.rp2040.yaml | 19 ++++++ .../components/bme680/test.esp32-c3-idf.yaml | 21 +++++++ tests/components/bme680/test.esp32-c3.yaml | 21 +++++++ tests/components/bme680/test.esp32-idf.yaml | 21 +++++++ tests/components/bme680/test.esp32.yaml | 21 +++++++ tests/components/bme680/test.esp8266.yaml | 21 +++++++ tests/components/bme680/test.rp2040.yaml | 21 +++++++ tests/components/bme680_bsec/test.esp32.yaml | 29 +++++++++ .../components/bme680_bsec/test.esp8266.yaml | 29 +++++++++ .../components/bmi160/test.esp32-c3-idf.yaml | 22 +++++++ tests/components/bmi160/test.esp32-c3.yaml | 22 +++++++ tests/components/bmi160/test.esp32-idf.yaml | 22 +++++++ tests/components/bmi160/test.esp32.yaml | 22 +++++++ tests/components/bmi160/test.esp8266.yaml | 22 +++++++ tests/components/bmi160/test.rp2040.yaml | 22 +++++++ .../components/bmp085/test.esp32-c3-idf.yaml | 15 +++++ tests/components/bmp085/test.esp32-c3.yaml | 15 +++++ tests/components/bmp085/test.esp32-idf.yaml | 15 +++++ tests/components/bmp085/test.esp32.yaml | 15 +++++ tests/components/bmp085/test.esp8266.yaml | 15 +++++ tests/components/bmp085/test.rp2040.yaml | 15 +++++ .../components/bmp280/test.esp32-c3-idf.yaml | 15 +++++ tests/components/bmp280/test.esp32-c3.yaml | 15 +++++ tests/components/bmp280/test.esp32-idf.yaml | 15 +++++ tests/components/bmp280/test.esp32.yaml | 15 +++++ tests/components/bmp280/test.esp8266.yaml | 15 +++++ tests/components/bmp280/test.rp2040.yaml | 15 +++++ .../components/bmp3xx/test.esp32-c3-idf.yaml | 14 +++++ tests/components/bmp3xx/test.esp32-c3.yaml | 14 +++++ tests/components/bmp3xx/test.esp32-idf.yaml | 14 +++++ tests/components/bmp3xx/test.esp32.yaml | 14 +++++ tests/components/bmp3xx/test.esp8266.yaml | 14 +++++ tests/components/bmp3xx/test.rp2040.yaml | 14 +++++ .../components/bmp581/test.esp32-c3-idf.yaml | 13 ++++ tests/components/bmp581/test.esp32-c3.yaml | 13 ++++ tests/components/bmp581/test.esp32-idf.yaml | 13 ++++ tests/components/bmp581/test.esp32.yaml | 13 ++++ tests/components/bmp581/test.esp8266.yaml | 13 ++++ tests/components/bmp581/test.rp2040.yaml | 13 ++++ .../bp1658cj/test.esp32-c3-idf.yaml | 22 +++++++ tests/components/bp1658cj/test.esp32-c3.yaml | 22 +++++++ tests/components/bp1658cj/test.esp32-idf.yaml | 22 +++++++ tests/components/bp1658cj/test.esp32.yaml | 22 +++++++ tests/components/bp1658cj/test.esp8266.yaml | 22 +++++++ tests/components/bp1658cj/test.rp2040.yaml | 22 +++++++ .../components/bp5758d/test.esp32-c3-idf.yaml | 25 ++++++++ tests/components/bp5758d/test.esp32-c3.yaml | 25 ++++++++ tests/components/bp5758d/test.esp32-idf.yaml | 25 ++++++++ tests/components/bp5758d/test.esp32.yaml | 25 ++++++++ tests/components/bp5758d/test.esp8266.yaml | 25 ++++++++ tests/components/bp5758d/test.rp2040.yaml | 25 ++++++++ .../components/button/test.esp32-c3-idf.yaml | 6 ++ tests/components/button/test.esp32-c3.yaml | 6 ++ tests/components/button/test.esp32-idf.yaml | 6 ++ tests/components/button/test.esp32.yaml | 6 ++ tests/components/button/test.esp8266.yaml | 6 ++ tests/components/button/test.rp2040.yaml | 6 ++ 130 files changed, 2650 insertions(+) create mode 100644 tests/components/b_parasite/test.esp32-c3-idf.yaml create mode 100644 tests/components/b_parasite/test.esp32-c3.yaml create mode 100644 tests/components/b_parasite/test.esp32-idf.yaml create mode 100644 tests/components/b_parasite/test.esp32.yaml create mode 100644 tests/components/ballu/test.esp32.yaml create mode 100644 tests/components/ballu/test.esp8266.yaml create mode 100644 tests/components/bang_bang/test.esp32-c3-idf.yaml create mode 100644 tests/components/bang_bang/test.esp32-c3.yaml create mode 100644 tests/components/bang_bang/test.esp32-idf.yaml create mode 100644 tests/components/bang_bang/test.esp32.yaml create mode 100644 tests/components/bang_bang/test.esp8266.yaml create mode 100644 tests/components/bang_bang/test.rp2040.yaml create mode 100644 tests/components/bedjet/test.esp32-c3-idf.yaml create mode 100644 tests/components/bedjet/test.esp32-c3.yaml create mode 100644 tests/components/bedjet/test.esp32-idf.yaml create mode 100644 tests/components/bedjet/test.esp32.yaml create mode 100644 tests/components/bh1750/test.esp32-c3-idf.yaml create mode 100644 tests/components/bh1750/test.esp32-c3.yaml create mode 100644 tests/components/bh1750/test.esp32-idf.yaml create mode 100644 tests/components/bh1750/test.esp32.yaml create mode 100644 tests/components/bh1750/test.esp8266.yaml create mode 100644 tests/components/bh1750/test.rp2040.yaml create mode 100644 tests/components/binary_sensor_map/test.esp32-c3-idf.yaml create mode 100644 tests/components/binary_sensor_map/test.esp32-c3.yaml create mode 100644 tests/components/binary_sensor_map/test.esp32-idf.yaml create mode 100644 tests/components/binary_sensor_map/test.esp32.yaml create mode 100644 tests/components/binary_sensor_map/test.esp8266.yaml create mode 100644 tests/components/binary_sensor_map/test.rp2040.yaml create mode 100644 tests/components/bl0939/test.esp32-c3-idf.yaml create mode 100644 tests/components/bl0939/test.esp32-c3.yaml create mode 100644 tests/components/bl0939/test.esp32-idf.yaml create mode 100644 tests/components/bl0939/test.esp32.yaml create mode 100644 tests/components/bl0939/test.esp8266.yaml create mode 100644 tests/components/bl0939/test.rp2040.yaml create mode 100644 tests/components/bl0940/test.esp32-c3-idf.yaml create mode 100644 tests/components/bl0940/test.esp32-c3.yaml create mode 100644 tests/components/bl0940/test.esp32-idf.yaml create mode 100644 tests/components/bl0940/test.esp32.yaml create mode 100644 tests/components/bl0940/test.esp8266.yaml create mode 100644 tests/components/bl0940/test.rp2040.yaml create mode 100644 tests/components/bl0942/test.esp32-c3-idf.yaml create mode 100644 tests/components/bl0942/test.esp32-c3.yaml create mode 100644 tests/components/bl0942/test.esp32-idf.yaml create mode 100644 tests/components/bl0942/test.esp32.yaml create mode 100644 tests/components/bl0942/test.esp8266.yaml create mode 100644 tests/components/bl0942/test.rp2040.yaml create mode 100644 tests/components/ble_client/test.esp32-c3-idf.yaml create mode 100644 tests/components/ble_client/test.esp32-c3.yaml create mode 100644 tests/components/ble_client/test.esp32-idf.yaml create mode 100644 tests/components/ble_client/test.esp32.yaml create mode 100644 tests/components/ble_presence/test.esp32-c3-idf.yaml create mode 100644 tests/components/ble_presence/test.esp32-c3.yaml create mode 100644 tests/components/ble_presence/test.esp32-idf.yaml create mode 100644 tests/components/ble_presence/test.esp32.yaml create mode 100644 tests/components/ble_rssi/test.esp32-c3-idf.yaml create mode 100644 tests/components/ble_rssi/test.esp32-c3.yaml create mode 100644 tests/components/ble_rssi/test.esp32-idf.yaml create mode 100644 tests/components/ble_rssi/test.esp32.yaml create mode 100644 tests/components/ble_scanner/test.esp32-c3-idf.yaml create mode 100644 tests/components/ble_scanner/test.esp32-c3.yaml create mode 100644 tests/components/ble_scanner/test.esp32-idf.yaml create mode 100644 tests/components/ble_scanner/test.esp32.yaml create mode 100644 tests/components/bme280_i2c/test.esp32-c3-idf.yaml create mode 100644 tests/components/bme280_i2c/test.esp32-c3.yaml create mode 100644 tests/components/bme280_i2c/test.esp32-idf.yaml create mode 100644 tests/components/bme280_i2c/test.esp32.yaml create mode 100644 tests/components/bme280_i2c/test.esp8266.yaml create mode 100644 tests/components/bme280_i2c/test.rp2040.yaml create mode 100644 tests/components/bme280_spi/test.esp32-c3-idf.yaml create mode 100644 tests/components/bme280_spi/test.esp32-c3.yaml create mode 100644 tests/components/bme280_spi/test.esp32-idf.yaml create mode 100644 tests/components/bme280_spi/test.esp32.yaml create mode 100644 tests/components/bme280_spi/test.esp8266.yaml create mode 100644 tests/components/bme280_spi/test.rp2040.yaml create mode 100644 tests/components/bme680/test.esp32-c3-idf.yaml create mode 100644 tests/components/bme680/test.esp32-c3.yaml create mode 100644 tests/components/bme680/test.esp32-idf.yaml create mode 100644 tests/components/bme680/test.esp32.yaml create mode 100644 tests/components/bme680/test.esp8266.yaml create mode 100644 tests/components/bme680/test.rp2040.yaml create mode 100644 tests/components/bme680_bsec/test.esp32.yaml create mode 100644 tests/components/bme680_bsec/test.esp8266.yaml create mode 100644 tests/components/bmi160/test.esp32-c3-idf.yaml create mode 100644 tests/components/bmi160/test.esp32-c3.yaml create mode 100644 tests/components/bmi160/test.esp32-idf.yaml create mode 100644 tests/components/bmi160/test.esp32.yaml create mode 100644 tests/components/bmi160/test.esp8266.yaml create mode 100644 tests/components/bmi160/test.rp2040.yaml create mode 100644 tests/components/bmp085/test.esp32-c3-idf.yaml create mode 100644 tests/components/bmp085/test.esp32-c3.yaml create mode 100644 tests/components/bmp085/test.esp32-idf.yaml create mode 100644 tests/components/bmp085/test.esp32.yaml create mode 100644 tests/components/bmp085/test.esp8266.yaml create mode 100644 tests/components/bmp085/test.rp2040.yaml create mode 100644 tests/components/bmp280/test.esp32-c3-idf.yaml create mode 100644 tests/components/bmp280/test.esp32-c3.yaml create mode 100644 tests/components/bmp280/test.esp32-idf.yaml create mode 100644 tests/components/bmp280/test.esp32.yaml create mode 100644 tests/components/bmp280/test.esp8266.yaml create mode 100644 tests/components/bmp280/test.rp2040.yaml create mode 100644 tests/components/bmp3xx/test.esp32-c3-idf.yaml create mode 100644 tests/components/bmp3xx/test.esp32-c3.yaml create mode 100644 tests/components/bmp3xx/test.esp32-idf.yaml create mode 100644 tests/components/bmp3xx/test.esp32.yaml create mode 100644 tests/components/bmp3xx/test.esp8266.yaml create mode 100644 tests/components/bmp3xx/test.rp2040.yaml create mode 100644 tests/components/bmp581/test.esp32-c3-idf.yaml create mode 100644 tests/components/bmp581/test.esp32-c3.yaml create mode 100644 tests/components/bmp581/test.esp32-idf.yaml create mode 100644 tests/components/bmp581/test.esp32.yaml create mode 100644 tests/components/bmp581/test.esp8266.yaml create mode 100644 tests/components/bmp581/test.rp2040.yaml create mode 100644 tests/components/bp1658cj/test.esp32-c3-idf.yaml create mode 100644 tests/components/bp1658cj/test.esp32-c3.yaml create mode 100644 tests/components/bp1658cj/test.esp32-idf.yaml create mode 100644 tests/components/bp1658cj/test.esp32.yaml create mode 100644 tests/components/bp1658cj/test.esp8266.yaml create mode 100644 tests/components/bp1658cj/test.rp2040.yaml create mode 100644 tests/components/bp5758d/test.esp32-c3-idf.yaml create mode 100644 tests/components/bp5758d/test.esp32-c3.yaml create mode 100644 tests/components/bp5758d/test.esp32-idf.yaml create mode 100644 tests/components/bp5758d/test.esp32.yaml create mode 100644 tests/components/bp5758d/test.esp8266.yaml create mode 100644 tests/components/bp5758d/test.rp2040.yaml create mode 100644 tests/components/button/test.esp32-c3-idf.yaml create mode 100644 tests/components/button/test.esp32-c3.yaml create mode 100644 tests/components/button/test.esp32-idf.yaml create mode 100644 tests/components/button/test.esp32.yaml create mode 100644 tests/components/button/test.esp8266.yaml create mode 100644 tests/components/button/test.rp2040.yaml diff --git a/tests/components/b_parasite/test.esp32-c3-idf.yaml b/tests/components/b_parasite/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..262e891bb2 --- /dev/null +++ b/tests/components/b_parasite/test.esp32-c3-idf.yaml @@ -0,0 +1,15 @@ +esp32_ble_tracker: + +sensor: + - platform: b_parasite + mac_address: F0:CA:F0:CA:01:01 + humidity: + name: b-parasite Air Humidity + temperature: + name: b-parasite Air Temperature + moisture: + name: b-parasite Soil Moisture + battery_voltage: + name: b-parasite Battery Voltage + illuminance: + name: b-parasite Illuminance diff --git a/tests/components/b_parasite/test.esp32-c3.yaml b/tests/components/b_parasite/test.esp32-c3.yaml new file mode 100644 index 0000000000..262e891bb2 --- /dev/null +++ b/tests/components/b_parasite/test.esp32-c3.yaml @@ -0,0 +1,15 @@ +esp32_ble_tracker: + +sensor: + - platform: b_parasite + mac_address: F0:CA:F0:CA:01:01 + humidity: + name: b-parasite Air Humidity + temperature: + name: b-parasite Air Temperature + moisture: + name: b-parasite Soil Moisture + battery_voltage: + name: b-parasite Battery Voltage + illuminance: + name: b-parasite Illuminance diff --git a/tests/components/b_parasite/test.esp32-idf.yaml b/tests/components/b_parasite/test.esp32-idf.yaml new file mode 100644 index 0000000000..262e891bb2 --- /dev/null +++ b/tests/components/b_parasite/test.esp32-idf.yaml @@ -0,0 +1,15 @@ +esp32_ble_tracker: + +sensor: + - platform: b_parasite + mac_address: F0:CA:F0:CA:01:01 + humidity: + name: b-parasite Air Humidity + temperature: + name: b-parasite Air Temperature + moisture: + name: b-parasite Soil Moisture + battery_voltage: + name: b-parasite Battery Voltage + illuminance: + name: b-parasite Illuminance diff --git a/tests/components/b_parasite/test.esp32.yaml b/tests/components/b_parasite/test.esp32.yaml new file mode 100644 index 0000000000..262e891bb2 --- /dev/null +++ b/tests/components/b_parasite/test.esp32.yaml @@ -0,0 +1,15 @@ +esp32_ble_tracker: + +sensor: + - platform: b_parasite + mac_address: F0:CA:F0:CA:01:01 + humidity: + name: b-parasite Air Humidity + temperature: + name: b-parasite Air Temperature + moisture: + name: b-parasite Soil Moisture + battery_voltage: + name: b-parasite Battery Voltage + illuminance: + name: b-parasite Illuminance diff --git a/tests/components/ballu/test.esp32.yaml b/tests/components/ballu/test.esp32.yaml new file mode 100644 index 0000000000..bb7b9b0435 --- /dev/null +++ b/tests/components/ballu/test.esp32.yaml @@ -0,0 +1,12 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: heatpumpir + protocol: ballu + horizontal_default: middle + vertical_default: middle + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 diff --git a/tests/components/ballu/test.esp8266.yaml b/tests/components/ballu/test.esp8266.yaml new file mode 100644 index 0000000000..05aa446739 --- /dev/null +++ b/tests/components/ballu/test.esp8266.yaml @@ -0,0 +1,12 @@ +remote_transmitter: + pin: 5 + carrier_duty_percent: 50% + +climate: + - platform: heatpumpir + protocol: ballu + horizontal_default: middle + vertical_default: middle + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 diff --git a/tests/components/bang_bang/test.esp32-c3-idf.yaml b/tests/components/bang_bang/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..5882025191 --- /dev/null +++ b/tests/components/bang_bang/test.esp32-c3-idf.yaml @@ -0,0 +1,35 @@ +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +sensor: + - platform: template + id: template_sensor1 + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 60s + +climate: + - platform: bang_bang + name: Bang Bang Climate + sensor: template_sensor1 + humidity_sensor: template_sensor1 + default_target_temperature_low: 18°C + default_target_temperature_high: 24°C + idle_action: + - switch.turn_on: template_switch1 + cool_action: + - switch.turn_on: template_switch2 + heat_action: + - switch.turn_on: template_switch1 + away_config: + default_target_temperature_low: 16°C + default_target_temperature_high: 20°C diff --git a/tests/components/bang_bang/test.esp32-c3.yaml b/tests/components/bang_bang/test.esp32-c3.yaml new file mode 100644 index 0000000000..5882025191 --- /dev/null +++ b/tests/components/bang_bang/test.esp32-c3.yaml @@ -0,0 +1,35 @@ +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +sensor: + - platform: template + id: template_sensor1 + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 60s + +climate: + - platform: bang_bang + name: Bang Bang Climate + sensor: template_sensor1 + humidity_sensor: template_sensor1 + default_target_temperature_low: 18°C + default_target_temperature_high: 24°C + idle_action: + - switch.turn_on: template_switch1 + cool_action: + - switch.turn_on: template_switch2 + heat_action: + - switch.turn_on: template_switch1 + away_config: + default_target_temperature_low: 16°C + default_target_temperature_high: 20°C diff --git a/tests/components/bang_bang/test.esp32-idf.yaml b/tests/components/bang_bang/test.esp32-idf.yaml new file mode 100644 index 0000000000..5882025191 --- /dev/null +++ b/tests/components/bang_bang/test.esp32-idf.yaml @@ -0,0 +1,35 @@ +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +sensor: + - platform: template + id: template_sensor1 + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 60s + +climate: + - platform: bang_bang + name: Bang Bang Climate + sensor: template_sensor1 + humidity_sensor: template_sensor1 + default_target_temperature_low: 18°C + default_target_temperature_high: 24°C + idle_action: + - switch.turn_on: template_switch1 + cool_action: + - switch.turn_on: template_switch2 + heat_action: + - switch.turn_on: template_switch1 + away_config: + default_target_temperature_low: 16°C + default_target_temperature_high: 20°C diff --git a/tests/components/bang_bang/test.esp32.yaml b/tests/components/bang_bang/test.esp32.yaml new file mode 100644 index 0000000000..5882025191 --- /dev/null +++ b/tests/components/bang_bang/test.esp32.yaml @@ -0,0 +1,35 @@ +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +sensor: + - platform: template + id: template_sensor1 + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 60s + +climate: + - platform: bang_bang + name: Bang Bang Climate + sensor: template_sensor1 + humidity_sensor: template_sensor1 + default_target_temperature_low: 18°C + default_target_temperature_high: 24°C + idle_action: + - switch.turn_on: template_switch1 + cool_action: + - switch.turn_on: template_switch2 + heat_action: + - switch.turn_on: template_switch1 + away_config: + default_target_temperature_low: 16°C + default_target_temperature_high: 20°C diff --git a/tests/components/bang_bang/test.esp8266.yaml b/tests/components/bang_bang/test.esp8266.yaml new file mode 100644 index 0000000000..5882025191 --- /dev/null +++ b/tests/components/bang_bang/test.esp8266.yaml @@ -0,0 +1,35 @@ +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +sensor: + - platform: template + id: template_sensor1 + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 60s + +climate: + - platform: bang_bang + name: Bang Bang Climate + sensor: template_sensor1 + humidity_sensor: template_sensor1 + default_target_temperature_low: 18°C + default_target_temperature_high: 24°C + idle_action: + - switch.turn_on: template_switch1 + cool_action: + - switch.turn_on: template_switch2 + heat_action: + - switch.turn_on: template_switch1 + away_config: + default_target_temperature_low: 16°C + default_target_temperature_high: 20°C diff --git a/tests/components/bang_bang/test.rp2040.yaml b/tests/components/bang_bang/test.rp2040.yaml new file mode 100644 index 0000000000..5882025191 --- /dev/null +++ b/tests/components/bang_bang/test.rp2040.yaml @@ -0,0 +1,35 @@ +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +sensor: + - platform: template + id: template_sensor1 + lambda: |- + if (millis() > 10000) { + return 42.0; + } else { + return 0.0; + } + update_interval: 60s + +climate: + - platform: bang_bang + name: Bang Bang Climate + sensor: template_sensor1 + humidity_sensor: template_sensor1 + default_target_temperature_low: 18°C + default_target_temperature_high: 24°C + idle_action: + - switch.turn_on: template_switch1 + cool_action: + - switch.turn_on: template_switch2 + heat_action: + - switch.turn_on: template_switch1 + away_config: + default_target_temperature_low: 16°C + default_target_temperature_high: 20°C diff --git a/tests/components/bedjet/test.esp32-c3-idf.yaml b/tests/components/bedjet/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..c2be04a49a --- /dev/null +++ b/tests/components/bedjet/test.esp32-c3-idf.yaml @@ -0,0 +1,33 @@ +wifi: + ssid: MySSID + password: password1 + +time: + - platform: sntp + id: sntp_time + servers: + - 0.pool.ntp.org + - 1.pool.ntp.org + - 192.168.178.1 + +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: bedjet_blec + +bedjet: + - id: bedjet_hub + ble_client_id: bedjet_blec + time_id: sntp_time + +climate: + - platform: bedjet + name: My Bedjet + bedjet_id: bedjet_hub + heat_mode: extended + +fan: + - platform: bedjet + name: My Bedjet fan + bedjet_id: bedjet_hub diff --git a/tests/components/bedjet/test.esp32-c3.yaml b/tests/components/bedjet/test.esp32-c3.yaml new file mode 100644 index 0000000000..c2be04a49a --- /dev/null +++ b/tests/components/bedjet/test.esp32-c3.yaml @@ -0,0 +1,33 @@ +wifi: + ssid: MySSID + password: password1 + +time: + - platform: sntp + id: sntp_time + servers: + - 0.pool.ntp.org + - 1.pool.ntp.org + - 192.168.178.1 + +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: bedjet_blec + +bedjet: + - id: bedjet_hub + ble_client_id: bedjet_blec + time_id: sntp_time + +climate: + - platform: bedjet + name: My Bedjet + bedjet_id: bedjet_hub + heat_mode: extended + +fan: + - platform: bedjet + name: My Bedjet fan + bedjet_id: bedjet_hub diff --git a/tests/components/bedjet/test.esp32-idf.yaml b/tests/components/bedjet/test.esp32-idf.yaml new file mode 100644 index 0000000000..c2be04a49a --- /dev/null +++ b/tests/components/bedjet/test.esp32-idf.yaml @@ -0,0 +1,33 @@ +wifi: + ssid: MySSID + password: password1 + +time: + - platform: sntp + id: sntp_time + servers: + - 0.pool.ntp.org + - 1.pool.ntp.org + - 192.168.178.1 + +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: bedjet_blec + +bedjet: + - id: bedjet_hub + ble_client_id: bedjet_blec + time_id: sntp_time + +climate: + - platform: bedjet + name: My Bedjet + bedjet_id: bedjet_hub + heat_mode: extended + +fan: + - platform: bedjet + name: My Bedjet fan + bedjet_id: bedjet_hub diff --git a/tests/components/bedjet/test.esp32.yaml b/tests/components/bedjet/test.esp32.yaml new file mode 100644 index 0000000000..c2be04a49a --- /dev/null +++ b/tests/components/bedjet/test.esp32.yaml @@ -0,0 +1,33 @@ +wifi: + ssid: MySSID + password: password1 + +time: + - platform: sntp + id: sntp_time + servers: + - 0.pool.ntp.org + - 1.pool.ntp.org + - 192.168.178.1 + +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: bedjet_blec + +bedjet: + - id: bedjet_hub + ble_client_id: bedjet_blec + time_id: sntp_time + +climate: + - platform: bedjet + name: My Bedjet + bedjet_id: bedjet_hub + heat_mode: extended + +fan: + - platform: bedjet + name: My Bedjet fan + bedjet_id: bedjet_hub diff --git a/tests/components/bh1750/test.esp32-c3-idf.yaml b/tests/components/bh1750/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..e367de3845 --- /dev/null +++ b/tests/components/bh1750/test.esp32-c3-idf.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_bh1750 + scl: 5 + sda: 4 + +sensor: + - platform: bh1750 + name: Living Room Brightness + address: 0x23 + update_interval: 30s diff --git a/tests/components/bh1750/test.esp32-c3.yaml b/tests/components/bh1750/test.esp32-c3.yaml new file mode 100644 index 0000000000..e367de3845 --- /dev/null +++ b/tests/components/bh1750/test.esp32-c3.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_bh1750 + scl: 5 + sda: 4 + +sensor: + - platform: bh1750 + name: Living Room Brightness + address: 0x23 + update_interval: 30s diff --git a/tests/components/bh1750/test.esp32-idf.yaml b/tests/components/bh1750/test.esp32-idf.yaml new file mode 100644 index 0000000000..b10ec231ae --- /dev/null +++ b/tests/components/bh1750/test.esp32-idf.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_bh1750 + scl: 16 + sda: 17 + +sensor: + - platform: bh1750 + name: Living Room Brightness + address: 0x23 + update_interval: 30s diff --git a/tests/components/bh1750/test.esp32.yaml b/tests/components/bh1750/test.esp32.yaml new file mode 100644 index 0000000000..b10ec231ae --- /dev/null +++ b/tests/components/bh1750/test.esp32.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_bh1750 + scl: 16 + sda: 17 + +sensor: + - platform: bh1750 + name: Living Room Brightness + address: 0x23 + update_interval: 30s diff --git a/tests/components/bh1750/test.esp8266.yaml b/tests/components/bh1750/test.esp8266.yaml new file mode 100644 index 0000000000..e367de3845 --- /dev/null +++ b/tests/components/bh1750/test.esp8266.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_bh1750 + scl: 5 + sda: 4 + +sensor: + - platform: bh1750 + name: Living Room Brightness + address: 0x23 + update_interval: 30s diff --git a/tests/components/bh1750/test.rp2040.yaml b/tests/components/bh1750/test.rp2040.yaml new file mode 100644 index 0000000000..e367de3845 --- /dev/null +++ b/tests/components/bh1750/test.rp2040.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_bh1750 + scl: 5 + sda: 4 + +sensor: + - platform: bh1750 + name: Living Room Brightness + address: 0x23 + update_interval: 30s diff --git a/tests/components/binary_sensor_map/test.esp32-c3-idf.yaml b/tests/components/binary_sensor_map/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..8ffdd1f379 --- /dev/null +++ b/tests/components/binary_sensor_map/test.esp32-c3-idf.yaml @@ -0,0 +1,61 @@ +binary_sensor: + - platform: template + id: bin1 + lambda: |- + if (millis() > 10000) { + return true; + } else { + return false; + } + - platform: template + id: bin2 + lambda: |- + if (millis() > 20000) { + return true; + } else { + return false; + } + - platform: template + id: bin3 + lambda: |- + if (millis() > 30000) { + return true; + } else { + return false; + } + +sensor: + - platform: binary_sensor_map + name: Binary Sensor Map + type: group + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: sum + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: bayesian + prior: 0.4 + observations: + - binary_sensor: bin1 + prob_given_true: 0.9 + prob_given_false: 0.4 + - binary_sensor: bin2 + prob_given_true: 0.7 + prob_given_false: 0.05 + - binary_sensor: bin3 + prob_given_true: 0.8 + prob_given_false: 0.2 diff --git a/tests/components/binary_sensor_map/test.esp32-c3.yaml b/tests/components/binary_sensor_map/test.esp32-c3.yaml new file mode 100644 index 0000000000..8ffdd1f379 --- /dev/null +++ b/tests/components/binary_sensor_map/test.esp32-c3.yaml @@ -0,0 +1,61 @@ +binary_sensor: + - platform: template + id: bin1 + lambda: |- + if (millis() > 10000) { + return true; + } else { + return false; + } + - platform: template + id: bin2 + lambda: |- + if (millis() > 20000) { + return true; + } else { + return false; + } + - platform: template + id: bin3 + lambda: |- + if (millis() > 30000) { + return true; + } else { + return false; + } + +sensor: + - platform: binary_sensor_map + name: Binary Sensor Map + type: group + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: sum + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: bayesian + prior: 0.4 + observations: + - binary_sensor: bin1 + prob_given_true: 0.9 + prob_given_false: 0.4 + - binary_sensor: bin2 + prob_given_true: 0.7 + prob_given_false: 0.05 + - binary_sensor: bin3 + prob_given_true: 0.8 + prob_given_false: 0.2 diff --git a/tests/components/binary_sensor_map/test.esp32-idf.yaml b/tests/components/binary_sensor_map/test.esp32-idf.yaml new file mode 100644 index 0000000000..8ffdd1f379 --- /dev/null +++ b/tests/components/binary_sensor_map/test.esp32-idf.yaml @@ -0,0 +1,61 @@ +binary_sensor: + - platform: template + id: bin1 + lambda: |- + if (millis() > 10000) { + return true; + } else { + return false; + } + - platform: template + id: bin2 + lambda: |- + if (millis() > 20000) { + return true; + } else { + return false; + } + - platform: template + id: bin3 + lambda: |- + if (millis() > 30000) { + return true; + } else { + return false; + } + +sensor: + - platform: binary_sensor_map + name: Binary Sensor Map + type: group + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: sum + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: bayesian + prior: 0.4 + observations: + - binary_sensor: bin1 + prob_given_true: 0.9 + prob_given_false: 0.4 + - binary_sensor: bin2 + prob_given_true: 0.7 + prob_given_false: 0.05 + - binary_sensor: bin3 + prob_given_true: 0.8 + prob_given_false: 0.2 diff --git a/tests/components/binary_sensor_map/test.esp32.yaml b/tests/components/binary_sensor_map/test.esp32.yaml new file mode 100644 index 0000000000..8ffdd1f379 --- /dev/null +++ b/tests/components/binary_sensor_map/test.esp32.yaml @@ -0,0 +1,61 @@ +binary_sensor: + - platform: template + id: bin1 + lambda: |- + if (millis() > 10000) { + return true; + } else { + return false; + } + - platform: template + id: bin2 + lambda: |- + if (millis() > 20000) { + return true; + } else { + return false; + } + - platform: template + id: bin3 + lambda: |- + if (millis() > 30000) { + return true; + } else { + return false; + } + +sensor: + - platform: binary_sensor_map + name: Binary Sensor Map + type: group + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: sum + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: bayesian + prior: 0.4 + observations: + - binary_sensor: bin1 + prob_given_true: 0.9 + prob_given_false: 0.4 + - binary_sensor: bin2 + prob_given_true: 0.7 + prob_given_false: 0.05 + - binary_sensor: bin3 + prob_given_true: 0.8 + prob_given_false: 0.2 diff --git a/tests/components/binary_sensor_map/test.esp8266.yaml b/tests/components/binary_sensor_map/test.esp8266.yaml new file mode 100644 index 0000000000..8ffdd1f379 --- /dev/null +++ b/tests/components/binary_sensor_map/test.esp8266.yaml @@ -0,0 +1,61 @@ +binary_sensor: + - platform: template + id: bin1 + lambda: |- + if (millis() > 10000) { + return true; + } else { + return false; + } + - platform: template + id: bin2 + lambda: |- + if (millis() > 20000) { + return true; + } else { + return false; + } + - platform: template + id: bin3 + lambda: |- + if (millis() > 30000) { + return true; + } else { + return false; + } + +sensor: + - platform: binary_sensor_map + name: Binary Sensor Map + type: group + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: sum + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: bayesian + prior: 0.4 + observations: + - binary_sensor: bin1 + prob_given_true: 0.9 + prob_given_false: 0.4 + - binary_sensor: bin2 + prob_given_true: 0.7 + prob_given_false: 0.05 + - binary_sensor: bin3 + prob_given_true: 0.8 + prob_given_false: 0.2 diff --git a/tests/components/binary_sensor_map/test.rp2040.yaml b/tests/components/binary_sensor_map/test.rp2040.yaml new file mode 100644 index 0000000000..8ffdd1f379 --- /dev/null +++ b/tests/components/binary_sensor_map/test.rp2040.yaml @@ -0,0 +1,61 @@ +binary_sensor: + - platform: template + id: bin1 + lambda: |- + if (millis() > 10000) { + return true; + } else { + return false; + } + - platform: template + id: bin2 + lambda: |- + if (millis() > 20000) { + return true; + } else { + return false; + } + - platform: template + id: bin3 + lambda: |- + if (millis() > 30000) { + return true; + } else { + return false; + } + +sensor: + - platform: binary_sensor_map + name: Binary Sensor Map + type: group + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: sum + channels: + - binary_sensor: bin1 + value: 10.0 + - binary_sensor: bin2 + value: 15.0 + - binary_sensor: bin3 + value: 100.0 + - platform: binary_sensor_map + name: Binary Sensor Map + type: bayesian + prior: 0.4 + observations: + - binary_sensor: bin1 + prob_given_true: 0.9 + prob_given_false: 0.4 + - binary_sensor: bin2 + prob_given_true: 0.7 + prob_given_false: 0.05 + - binary_sensor: bin3 + prob_given_true: 0.8 + prob_given_false: 0.2 diff --git a/tests/components/bl0939/test.esp32-c3-idf.yaml b/tests/components/bl0939/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..4c92ccb7dd --- /dev/null +++ b/tests/components/bl0939/test.esp32-c3-idf.yaml @@ -0,0 +1,26 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0939 + voltage: + name: BL0939 Voltage + current_1: + name: BL0939 Current 1 + current_2: + name: BL0939 Current 2 + active_power_1: + name: BL0939 Active Power 1 + active_power_2: + name: BL0939 Active Power 2 + energy_1: + name: BL0939 Energy 1 + energy_2: + name: BL0939 Energy 2 + energy_total: + name: BL0939 Total energy diff --git a/tests/components/bl0939/test.esp32-c3.yaml b/tests/components/bl0939/test.esp32-c3.yaml new file mode 100644 index 0000000000..4c92ccb7dd --- /dev/null +++ b/tests/components/bl0939/test.esp32-c3.yaml @@ -0,0 +1,26 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0939 + voltage: + name: BL0939 Voltage + current_1: + name: BL0939 Current 1 + current_2: + name: BL0939 Current 2 + active_power_1: + name: BL0939 Active Power 1 + active_power_2: + name: BL0939 Active Power 2 + energy_1: + name: BL0939 Energy 1 + energy_2: + name: BL0939 Energy 2 + energy_total: + name: BL0939 Total energy diff --git a/tests/components/bl0939/test.esp32-idf.yaml b/tests/components/bl0939/test.esp32-idf.yaml new file mode 100644 index 0000000000..df0e683b2f --- /dev/null +++ b/tests/components/bl0939/test.esp32-idf.yaml @@ -0,0 +1,26 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 9600 + +sensor: + - platform: bl0939 + voltage: + name: BL0939 Voltage + current_1: + name: BL0939 Current 1 + current_2: + name: BL0939 Current 2 + active_power_1: + name: BL0939 Active Power 1 + active_power_2: + name: BL0939 Active Power 2 + energy_1: + name: BL0939 Energy 1 + energy_2: + name: BL0939 Energy 2 + energy_total: + name: BL0939 Total energy diff --git a/tests/components/bl0939/test.esp32.yaml b/tests/components/bl0939/test.esp32.yaml new file mode 100644 index 0000000000..df0e683b2f --- /dev/null +++ b/tests/components/bl0939/test.esp32.yaml @@ -0,0 +1,26 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 9600 + +sensor: + - platform: bl0939 + voltage: + name: BL0939 Voltage + current_1: + name: BL0939 Current 1 + current_2: + name: BL0939 Current 2 + active_power_1: + name: BL0939 Active Power 1 + active_power_2: + name: BL0939 Active Power 2 + energy_1: + name: BL0939 Energy 1 + energy_2: + name: BL0939 Energy 2 + energy_total: + name: BL0939 Total energy diff --git a/tests/components/bl0939/test.esp8266.yaml b/tests/components/bl0939/test.esp8266.yaml new file mode 100644 index 0000000000..4c92ccb7dd --- /dev/null +++ b/tests/components/bl0939/test.esp8266.yaml @@ -0,0 +1,26 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0939 + voltage: + name: BL0939 Voltage + current_1: + name: BL0939 Current 1 + current_2: + name: BL0939 Current 2 + active_power_1: + name: BL0939 Active Power 1 + active_power_2: + name: BL0939 Active Power 2 + energy_1: + name: BL0939 Energy 1 + energy_2: + name: BL0939 Energy 2 + energy_total: + name: BL0939 Total energy diff --git a/tests/components/bl0939/test.rp2040.yaml b/tests/components/bl0939/test.rp2040.yaml new file mode 100644 index 0000000000..4c92ccb7dd --- /dev/null +++ b/tests/components/bl0939/test.rp2040.yaml @@ -0,0 +1,26 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0939 + voltage: + name: BL0939 Voltage + current_1: + name: BL0939 Current 1 + current_2: + name: BL0939 Current 2 + active_power_1: + name: BL0939 Active Power 1 + active_power_2: + name: BL0939 Active Power 2 + energy_1: + name: BL0939 Energy 1 + energy_2: + name: BL0939 Energy 2 + energy_total: + name: BL0939 Total energy diff --git a/tests/components/bl0940/test.esp32-c3-idf.yaml b/tests/components/bl0940/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..a20f785b02 --- /dev/null +++ b/tests/components/bl0940/test.esp32-c3-idf.yaml @@ -0,0 +1,22 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0940 + voltage: + name: BL0940 Voltage + current: + name: BL0940 Current + power: + name: BL0940 Power + energy: + name: BL0940 Energy + internal_temperature: + name: BL0940 Internal temperature + external_temperature: + name: BL0940 External temperature diff --git a/tests/components/bl0940/test.esp32-c3.yaml b/tests/components/bl0940/test.esp32-c3.yaml new file mode 100644 index 0000000000..a20f785b02 --- /dev/null +++ b/tests/components/bl0940/test.esp32-c3.yaml @@ -0,0 +1,22 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0940 + voltage: + name: BL0940 Voltage + current: + name: BL0940 Current + power: + name: BL0940 Power + energy: + name: BL0940 Energy + internal_temperature: + name: BL0940 Internal temperature + external_temperature: + name: BL0940 External temperature diff --git a/tests/components/bl0940/test.esp32-idf.yaml b/tests/components/bl0940/test.esp32-idf.yaml new file mode 100644 index 0000000000..c7d97ca3b9 --- /dev/null +++ b/tests/components/bl0940/test.esp32-idf.yaml @@ -0,0 +1,22 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 9600 + +sensor: + - platform: bl0940 + voltage: + name: BL0940 Voltage + current: + name: BL0940 Current + power: + name: BL0940 Power + energy: + name: BL0940 Energy + internal_temperature: + name: BL0940 Internal temperature + external_temperature: + name: BL0940 External temperature diff --git a/tests/components/bl0940/test.esp32.yaml b/tests/components/bl0940/test.esp32.yaml new file mode 100644 index 0000000000..c7d97ca3b9 --- /dev/null +++ b/tests/components/bl0940/test.esp32.yaml @@ -0,0 +1,22 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 9600 + +sensor: + - platform: bl0940 + voltage: + name: BL0940 Voltage + current: + name: BL0940 Current + power: + name: BL0940 Power + energy: + name: BL0940 Energy + internal_temperature: + name: BL0940 Internal temperature + external_temperature: + name: BL0940 External temperature diff --git a/tests/components/bl0940/test.esp8266.yaml b/tests/components/bl0940/test.esp8266.yaml new file mode 100644 index 0000000000..a20f785b02 --- /dev/null +++ b/tests/components/bl0940/test.esp8266.yaml @@ -0,0 +1,22 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0940 + voltage: + name: BL0940 Voltage + current: + name: BL0940 Current + power: + name: BL0940 Power + energy: + name: BL0940 Energy + internal_temperature: + name: BL0940 Internal temperature + external_temperature: + name: BL0940 External temperature diff --git a/tests/components/bl0940/test.rp2040.yaml b/tests/components/bl0940/test.rp2040.yaml new file mode 100644 index 0000000000..a20f785b02 --- /dev/null +++ b/tests/components/bl0940/test.rp2040.yaml @@ -0,0 +1,22 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0940 + voltage: + name: BL0940 Voltage + current: + name: BL0940 Current + power: + name: BL0940 Power + energy: + name: BL0940 Energy + internal_temperature: + name: BL0940 Internal temperature + external_temperature: + name: BL0940 External temperature diff --git a/tests/components/bl0942/test.esp32-c3-idf.yaml b/tests/components/bl0942/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..8d16efed4f --- /dev/null +++ b/tests/components/bl0942/test.esp32-c3-idf.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0942 + voltage: + name: BL0942 Voltage + current: + name: BL0942 Current + power: + name: BL0942 Power + energy: + name: BL0942 Energy + frequency: + name: BL0942 Frequency diff --git a/tests/components/bl0942/test.esp32-c3.yaml b/tests/components/bl0942/test.esp32-c3.yaml new file mode 100644 index 0000000000..8d16efed4f --- /dev/null +++ b/tests/components/bl0942/test.esp32-c3.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0942 + voltage: + name: BL0942 Voltage + current: + name: BL0942 Current + power: + name: BL0942 Power + energy: + name: BL0942 Energy + frequency: + name: BL0942 Frequency diff --git a/tests/components/bl0942/test.esp32-idf.yaml b/tests/components/bl0942/test.esp32-idf.yaml new file mode 100644 index 0000000000..45ac85aa2a --- /dev/null +++ b/tests/components/bl0942/test.esp32-idf.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 9600 + +sensor: + - platform: bl0942 + voltage: + name: BL0942 Voltage + current: + name: BL0942 Current + power: + name: BL0942 Power + energy: + name: BL0942 Energy + frequency: + name: BL0942 Frequency diff --git a/tests/components/bl0942/test.esp32.yaml b/tests/components/bl0942/test.esp32.yaml new file mode 100644 index 0000000000..45ac85aa2a --- /dev/null +++ b/tests/components/bl0942/test.esp32.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 9600 + +sensor: + - platform: bl0942 + voltage: + name: BL0942 Voltage + current: + name: BL0942 Current + power: + name: BL0942 Power + energy: + name: BL0942 Energy + frequency: + name: BL0942 Frequency diff --git a/tests/components/bl0942/test.esp8266.yaml b/tests/components/bl0942/test.esp8266.yaml new file mode 100644 index 0000000000..8d16efed4f --- /dev/null +++ b/tests/components/bl0942/test.esp8266.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0942 + voltage: + name: BL0942 Voltage + current: + name: BL0942 Current + power: + name: BL0942 Power + energy: + name: BL0942 Energy + frequency: + name: BL0942 Frequency diff --git a/tests/components/bl0942/test.rp2040.yaml b/tests/components/bl0942/test.rp2040.yaml new file mode 100644 index 0000000000..8d16efed4f --- /dev/null +++ b/tests/components/bl0942/test.rp2040.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_bl0939 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 9600 + +sensor: + - platform: bl0942 + voltage: + name: BL0942 Voltage + current: + name: BL0942 Current + power: + name: BL0942 Power + energy: + name: BL0942 Energy + frequency: + name: BL0942 Frequency diff --git a/tests/components/ble_client/test.esp32-c3-idf.yaml b/tests/components/ble_client/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..b5272d01f0 --- /dev/null +++ b/tests/components/ble_client/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: test_blec diff --git a/tests/components/ble_client/test.esp32-c3.yaml b/tests/components/ble_client/test.esp32-c3.yaml new file mode 100644 index 0000000000..b5272d01f0 --- /dev/null +++ b/tests/components/ble_client/test.esp32-c3.yaml @@ -0,0 +1,5 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: test_blec diff --git a/tests/components/ble_client/test.esp32-idf.yaml b/tests/components/ble_client/test.esp32-idf.yaml new file mode 100644 index 0000000000..b5272d01f0 --- /dev/null +++ b/tests/components/ble_client/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: test_blec diff --git a/tests/components/ble_client/test.esp32.yaml b/tests/components/ble_client/test.esp32.yaml new file mode 100644 index 0000000000..b5272d01f0 --- /dev/null +++ b/tests/components/ble_client/test.esp32.yaml @@ -0,0 +1,5 @@ +esp32_ble_tracker: + +ble_client: + - mac_address: 01:02:03:04:05:06 + id: test_blec diff --git a/tests/components/ble_presence/test.esp32-c3-idf.yaml b/tests/components/ble_presence/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..dde9215470 --- /dev/null +++ b/tests/components/ble_presence/test.esp32-c3-idf.yaml @@ -0,0 +1,20 @@ +esp32_ble_tracker: + +binary_sensor: + - platform: ble_presence + mac_address: AC:37:43:77:5F:4C + name: ESP32 BLE Tracker Google Home Mini + - platform: ble_presence + service_uuid: 11aa + name: BLE Test Service 16 Presence + - platform: ble_presence + service_uuid: "11223344" + name: BLE Test Service 32 Presence + - platform: ble_presence + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test Service 128 Presence + - platform: ble_presence + ibeacon_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + ibeacon_major: 100 + ibeacon_minor: 1 + name: BLE Test iBeacon Presence diff --git a/tests/components/ble_presence/test.esp32-c3.yaml b/tests/components/ble_presence/test.esp32-c3.yaml new file mode 100644 index 0000000000..dde9215470 --- /dev/null +++ b/tests/components/ble_presence/test.esp32-c3.yaml @@ -0,0 +1,20 @@ +esp32_ble_tracker: + +binary_sensor: + - platform: ble_presence + mac_address: AC:37:43:77:5F:4C + name: ESP32 BLE Tracker Google Home Mini + - platform: ble_presence + service_uuid: 11aa + name: BLE Test Service 16 Presence + - platform: ble_presence + service_uuid: "11223344" + name: BLE Test Service 32 Presence + - platform: ble_presence + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test Service 128 Presence + - platform: ble_presence + ibeacon_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + ibeacon_major: 100 + ibeacon_minor: 1 + name: BLE Test iBeacon Presence diff --git a/tests/components/ble_presence/test.esp32-idf.yaml b/tests/components/ble_presence/test.esp32-idf.yaml new file mode 100644 index 0000000000..dde9215470 --- /dev/null +++ b/tests/components/ble_presence/test.esp32-idf.yaml @@ -0,0 +1,20 @@ +esp32_ble_tracker: + +binary_sensor: + - platform: ble_presence + mac_address: AC:37:43:77:5F:4C + name: ESP32 BLE Tracker Google Home Mini + - platform: ble_presence + service_uuid: 11aa + name: BLE Test Service 16 Presence + - platform: ble_presence + service_uuid: "11223344" + name: BLE Test Service 32 Presence + - platform: ble_presence + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test Service 128 Presence + - platform: ble_presence + ibeacon_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + ibeacon_major: 100 + ibeacon_minor: 1 + name: BLE Test iBeacon Presence diff --git a/tests/components/ble_presence/test.esp32.yaml b/tests/components/ble_presence/test.esp32.yaml new file mode 100644 index 0000000000..dde9215470 --- /dev/null +++ b/tests/components/ble_presence/test.esp32.yaml @@ -0,0 +1,20 @@ +esp32_ble_tracker: + +binary_sensor: + - platform: ble_presence + mac_address: AC:37:43:77:5F:4C + name: ESP32 BLE Tracker Google Home Mini + - platform: ble_presence + service_uuid: 11aa + name: BLE Test Service 16 Presence + - platform: ble_presence + service_uuid: "11223344" + name: BLE Test Service 32 Presence + - platform: ble_presence + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test Service 128 Presence + - platform: ble_presence + ibeacon_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + ibeacon_major: 100 + ibeacon_minor: 1 + name: BLE Test iBeacon Presence diff --git a/tests/components/ble_rssi/test.esp32-c3-idf.yaml b/tests/components/ble_rssi/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..52e5b865c6 --- /dev/null +++ b/tests/components/ble_rssi/test.esp32-c3-idf.yaml @@ -0,0 +1,18 @@ +esp32_ble_tracker: + +sensor: + - platform: ble_rssi + mac_address: AC:37:43:77:5F:4C + name: BLE Google Home Mini RSSI value + - platform: ble_rssi + service_uuid: 11aa + name: BLE Test Service 16 + - platform: ble_rssi + service_uuid: "11223344" + name: BLE Test Service 32 + - platform: ble_rssi + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test Service 128 + - platform: ble_rssi + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test iBeacon UUID diff --git a/tests/components/ble_rssi/test.esp32-c3.yaml b/tests/components/ble_rssi/test.esp32-c3.yaml new file mode 100644 index 0000000000..52e5b865c6 --- /dev/null +++ b/tests/components/ble_rssi/test.esp32-c3.yaml @@ -0,0 +1,18 @@ +esp32_ble_tracker: + +sensor: + - platform: ble_rssi + mac_address: AC:37:43:77:5F:4C + name: BLE Google Home Mini RSSI value + - platform: ble_rssi + service_uuid: 11aa + name: BLE Test Service 16 + - platform: ble_rssi + service_uuid: "11223344" + name: BLE Test Service 32 + - platform: ble_rssi + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test Service 128 + - platform: ble_rssi + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test iBeacon UUID diff --git a/tests/components/ble_rssi/test.esp32-idf.yaml b/tests/components/ble_rssi/test.esp32-idf.yaml new file mode 100644 index 0000000000..52e5b865c6 --- /dev/null +++ b/tests/components/ble_rssi/test.esp32-idf.yaml @@ -0,0 +1,18 @@ +esp32_ble_tracker: + +sensor: + - platform: ble_rssi + mac_address: AC:37:43:77:5F:4C + name: BLE Google Home Mini RSSI value + - platform: ble_rssi + service_uuid: 11aa + name: BLE Test Service 16 + - platform: ble_rssi + service_uuid: "11223344" + name: BLE Test Service 32 + - platform: ble_rssi + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test Service 128 + - platform: ble_rssi + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test iBeacon UUID diff --git a/tests/components/ble_rssi/test.esp32.yaml b/tests/components/ble_rssi/test.esp32.yaml new file mode 100644 index 0000000000..52e5b865c6 --- /dev/null +++ b/tests/components/ble_rssi/test.esp32.yaml @@ -0,0 +1,18 @@ +esp32_ble_tracker: + +sensor: + - platform: ble_rssi + mac_address: AC:37:43:77:5F:4C + name: BLE Google Home Mini RSSI value + - platform: ble_rssi + service_uuid: 11aa + name: BLE Test Service 16 + - platform: ble_rssi + service_uuid: "11223344" + name: BLE Test Service 32 + - platform: ble_rssi + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test Service 128 + - platform: ble_rssi + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test iBeacon UUID diff --git a/tests/components/ble_scanner/test.esp32-c3-idf.yaml b/tests/components/ble_scanner/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..935a5a5a19 --- /dev/null +++ b/tests/components/ble_scanner/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +esp32_ble_tracker: + +text_sensor: + - platform: ble_scanner + name: Scanner diff --git a/tests/components/ble_scanner/test.esp32-c3.yaml b/tests/components/ble_scanner/test.esp32-c3.yaml new file mode 100644 index 0000000000..935a5a5a19 --- /dev/null +++ b/tests/components/ble_scanner/test.esp32-c3.yaml @@ -0,0 +1,5 @@ +esp32_ble_tracker: + +text_sensor: + - platform: ble_scanner + name: Scanner diff --git a/tests/components/ble_scanner/test.esp32-idf.yaml b/tests/components/ble_scanner/test.esp32-idf.yaml new file mode 100644 index 0000000000..935a5a5a19 --- /dev/null +++ b/tests/components/ble_scanner/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +esp32_ble_tracker: + +text_sensor: + - platform: ble_scanner + name: Scanner diff --git a/tests/components/ble_scanner/test.esp32.yaml b/tests/components/ble_scanner/test.esp32.yaml new file mode 100644 index 0000000000..935a5a5a19 --- /dev/null +++ b/tests/components/ble_scanner/test.esp32.yaml @@ -0,0 +1,5 @@ +esp32_ble_tracker: + +text_sensor: + - platform: ble_scanner + name: Scanner diff --git a/tests/components/bme280_i2c/test.esp32-c3-idf.yaml b/tests/components/bme280_i2c/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..070c2845d9 --- /dev/null +++ b/tests/components/bme280_i2c/test.esp32-c3-idf.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +sensor: + - platform: bme280_i2c + address: 0x76 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_i2c/test.esp32-c3.yaml b/tests/components/bme280_i2c/test.esp32-c3.yaml new file mode 100644 index 0000000000..070c2845d9 --- /dev/null +++ b/tests/components/bme280_i2c/test.esp32-c3.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +sensor: + - platform: bme280_i2c + address: 0x76 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_i2c/test.esp32-idf.yaml b/tests/components/bme280_i2c/test.esp32-idf.yaml new file mode 100644 index 0000000000..e379b98874 --- /dev/null +++ b/tests/components/bme280_i2c/test.esp32-idf.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_bme280 + scl: 16 + sda: 17 + +sensor: + - platform: bme280_i2c + address: 0x76 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_i2c/test.esp32.yaml b/tests/components/bme280_i2c/test.esp32.yaml new file mode 100644 index 0000000000..e379b98874 --- /dev/null +++ b/tests/components/bme280_i2c/test.esp32.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_bme280 + scl: 16 + sda: 17 + +sensor: + - platform: bme280_i2c + address: 0x76 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_i2c/test.esp8266.yaml b/tests/components/bme280_i2c/test.esp8266.yaml new file mode 100644 index 0000000000..070c2845d9 --- /dev/null +++ b/tests/components/bme280_i2c/test.esp8266.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +sensor: + - platform: bme280_i2c + address: 0x76 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_i2c/test.rp2040.yaml b/tests/components/bme280_i2c/test.rp2040.yaml new file mode 100644 index 0000000000..070c2845d9 --- /dev/null +++ b/tests/components/bme280_i2c/test.rp2040.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_bme280 + scl: 5 + sda: 4 + +sensor: + - platform: bme280_i2c + address: 0x76 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_spi/test.esp32-c3-idf.yaml b/tests/components/bme280_spi/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..4bc7c14e6c --- /dev/null +++ b/tests/components/bme280_spi/test.esp32-c3-idf.yaml @@ -0,0 +1,19 @@ +spi: + - id: spi_bme280 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: bme280_spi + cs_pin: 8 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_spi/test.esp32-c3.yaml b/tests/components/bme280_spi/test.esp32-c3.yaml new file mode 100644 index 0000000000..4bc7c14e6c --- /dev/null +++ b/tests/components/bme280_spi/test.esp32-c3.yaml @@ -0,0 +1,19 @@ +spi: + - id: spi_bme280 + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: bme280_spi + cs_pin: 8 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_spi/test.esp32-idf.yaml b/tests/components/bme280_spi/test.esp32-idf.yaml new file mode 100644 index 0000000000..ebb3d98213 --- /dev/null +++ b/tests/components/bme280_spi/test.esp32-idf.yaml @@ -0,0 +1,19 @@ +spi: + - id: spi_bme280 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: bme280_spi + cs_pin: 12 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_spi/test.esp32.yaml b/tests/components/bme280_spi/test.esp32.yaml new file mode 100644 index 0000000000..ebb3d98213 --- /dev/null +++ b/tests/components/bme280_spi/test.esp32.yaml @@ -0,0 +1,19 @@ +spi: + - id: spi_bme280 + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: bme280_spi + cs_pin: 12 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_spi/test.esp8266.yaml b/tests/components/bme280_spi/test.esp8266.yaml new file mode 100644 index 0000000000..63013abb87 --- /dev/null +++ b/tests/components/bme280_spi/test.esp8266.yaml @@ -0,0 +1,19 @@ +spi: + - id: spi_bme280 + clk_pin: 14 + mosi_pin: 13 + miso_pin: 12 + +sensor: + - platform: bme280_spi + cs_pin: 15 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme280_spi/test.rp2040.yaml b/tests/components/bme280_spi/test.rp2040.yaml new file mode 100644 index 0000000000..ba5cb483c6 --- /dev/null +++ b/tests/components/bme280_spi/test.rp2040.yaml @@ -0,0 +1,19 @@ +spi: + - id: spi_bme280 + clk_pin: 2 + mosi_pin: 3 + miso_pin: 4 + +sensor: + - platform: bme280_spi + cs_pin: 6 + temperature: + id: bme280_temperature + name: BME280 Temperature + humidity: + id: bme280_humidity + name: BME280 Humidity + pressure: + id: bme280_pressure + name: BME280 Pressure + update_interval: 15s diff --git a/tests/components/bme680/test.esp32-c3-idf.yaml b/tests/components/bme680/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..f12be09d20 --- /dev/null +++ b/tests/components/bme680/test.esp32-c3-idf.yaml @@ -0,0 +1,21 @@ +i2c: + - id: i2c_bme680 + scl: 5 + sda: 4 + +sensor: + - platform: bme680 + temperature: + name: BME680 Temperature + oversampling: 16x + pressure: + name: BME680 Pressure + humidity: + name: BME680 Humidity + gas_resistance: + name: BME680 Gas Sensor + address: 0x77 + heater: + temperature: 320 + duration: 150ms + update_interval: 15s diff --git a/tests/components/bme680/test.esp32-c3.yaml b/tests/components/bme680/test.esp32-c3.yaml new file mode 100644 index 0000000000..f12be09d20 --- /dev/null +++ b/tests/components/bme680/test.esp32-c3.yaml @@ -0,0 +1,21 @@ +i2c: + - id: i2c_bme680 + scl: 5 + sda: 4 + +sensor: + - platform: bme680 + temperature: + name: BME680 Temperature + oversampling: 16x + pressure: + name: BME680 Pressure + humidity: + name: BME680 Humidity + gas_resistance: + name: BME680 Gas Sensor + address: 0x77 + heater: + temperature: 320 + duration: 150ms + update_interval: 15s diff --git a/tests/components/bme680/test.esp32-idf.yaml b/tests/components/bme680/test.esp32-idf.yaml new file mode 100644 index 0000000000..04d0ed8fe4 --- /dev/null +++ b/tests/components/bme680/test.esp32-idf.yaml @@ -0,0 +1,21 @@ +i2c: + - id: i2c_bme680 + scl: 16 + sda: 17 + +sensor: + - platform: bme680 + temperature: + name: BME680 Temperature + oversampling: 16x + pressure: + name: BME680 Pressure + humidity: + name: BME680 Humidity + gas_resistance: + name: BME680 Gas Sensor + address: 0x77 + heater: + temperature: 320 + duration: 150ms + update_interval: 15s diff --git a/tests/components/bme680/test.esp32.yaml b/tests/components/bme680/test.esp32.yaml new file mode 100644 index 0000000000..04d0ed8fe4 --- /dev/null +++ b/tests/components/bme680/test.esp32.yaml @@ -0,0 +1,21 @@ +i2c: + - id: i2c_bme680 + scl: 16 + sda: 17 + +sensor: + - platform: bme680 + temperature: + name: BME680 Temperature + oversampling: 16x + pressure: + name: BME680 Pressure + humidity: + name: BME680 Humidity + gas_resistance: + name: BME680 Gas Sensor + address: 0x77 + heater: + temperature: 320 + duration: 150ms + update_interval: 15s diff --git a/tests/components/bme680/test.esp8266.yaml b/tests/components/bme680/test.esp8266.yaml new file mode 100644 index 0000000000..f12be09d20 --- /dev/null +++ b/tests/components/bme680/test.esp8266.yaml @@ -0,0 +1,21 @@ +i2c: + - id: i2c_bme680 + scl: 5 + sda: 4 + +sensor: + - platform: bme680 + temperature: + name: BME680 Temperature + oversampling: 16x + pressure: + name: BME680 Pressure + humidity: + name: BME680 Humidity + gas_resistance: + name: BME680 Gas Sensor + address: 0x77 + heater: + temperature: 320 + duration: 150ms + update_interval: 15s diff --git a/tests/components/bme680/test.rp2040.yaml b/tests/components/bme680/test.rp2040.yaml new file mode 100644 index 0000000000..f12be09d20 --- /dev/null +++ b/tests/components/bme680/test.rp2040.yaml @@ -0,0 +1,21 @@ +i2c: + - id: i2c_bme680 + scl: 5 + sda: 4 + +sensor: + - platform: bme680 + temperature: + name: BME680 Temperature + oversampling: 16x + pressure: + name: BME680 Pressure + humidity: + name: BME680 Humidity + gas_resistance: + name: BME680 Gas Sensor + address: 0x77 + heater: + temperature: 320 + duration: 150ms + update_interval: 15s diff --git a/tests/components/bme680_bsec/test.esp32.yaml b/tests/components/bme680_bsec/test.esp32.yaml new file mode 100644 index 0000000000..4f62f13abb --- /dev/null +++ b/tests/components/bme680_bsec/test.esp32.yaml @@ -0,0 +1,29 @@ +i2c: + - id: i2c_bme680 + scl: 16 + sda: 17 + +bme680_bsec: + address: 0x77 + +sensor: + - platform: bme680_bsec + temperature: + name: BME680 Temperature + pressure: + name: BME680 Pressure + humidity: + name: BME680 Humidity + gas_resistance: + name: BME680 Gas Sensor + iaq: + name: BME680 IAQ + co2_equivalent: + name: BME680 eCO2 + breath_voc_equivalent: + name: BME680 Breath eVOC + +text_sensor: + - platform: bme680_bsec + iaq_accuracy: + name: BME680 Accuracy diff --git a/tests/components/bme680_bsec/test.esp8266.yaml b/tests/components/bme680_bsec/test.esp8266.yaml new file mode 100644 index 0000000000..84b32d3635 --- /dev/null +++ b/tests/components/bme680_bsec/test.esp8266.yaml @@ -0,0 +1,29 @@ +i2c: + - id: i2c_bme680 + scl: 5 + sda: 4 + +bme680_bsec: + address: 0x77 + +sensor: + - platform: bme680_bsec + temperature: + name: BME680 Temperature + pressure: + name: BME680 Pressure + humidity: + name: BME680 Humidity + gas_resistance: + name: BME680 Gas Sensor + iaq: + name: BME680 IAQ + co2_equivalent: + name: BME680 eCO2 + breath_voc_equivalent: + name: BME680 Breath eVOC + +text_sensor: + - platform: bme680_bsec + iaq_accuracy: + name: BME680 Accuracy diff --git a/tests/components/bmi160/test.esp32-c3-idf.yaml b/tests/components/bmi160/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..3fd6441980 --- /dev/null +++ b/tests/components/bmi160/test.esp32-c3-idf.yaml @@ -0,0 +1,22 @@ +i2c: + - id: i2c_bmi160 + scl: 5 + sda: 4 + +sensor: + - platform: bmi160 + address: 0x68 + acceleration_x: + name: BMI160 Accel X + acceleration_y: + name: BMI160 Accel Y + acceleration_z: + name: BMI160 Accel z + gyroscope_x: + name: BMI160 Gyro X + gyroscope_y: + name: BMI160 Gyro Y + gyroscope_z: + name: BMI160 Gyro z + temperature: + name: BMI160 Temperature diff --git a/tests/components/bmi160/test.esp32-c3.yaml b/tests/components/bmi160/test.esp32-c3.yaml new file mode 100644 index 0000000000..3fd6441980 --- /dev/null +++ b/tests/components/bmi160/test.esp32-c3.yaml @@ -0,0 +1,22 @@ +i2c: + - id: i2c_bmi160 + scl: 5 + sda: 4 + +sensor: + - platform: bmi160 + address: 0x68 + acceleration_x: + name: BMI160 Accel X + acceleration_y: + name: BMI160 Accel Y + acceleration_z: + name: BMI160 Accel z + gyroscope_x: + name: BMI160 Gyro X + gyroscope_y: + name: BMI160 Gyro Y + gyroscope_z: + name: BMI160 Gyro z + temperature: + name: BMI160 Temperature diff --git a/tests/components/bmi160/test.esp32-idf.yaml b/tests/components/bmi160/test.esp32-idf.yaml new file mode 100644 index 0000000000..a8a90c8c87 --- /dev/null +++ b/tests/components/bmi160/test.esp32-idf.yaml @@ -0,0 +1,22 @@ +i2c: + - id: i2c_bmi160 + scl: 16 + sda: 17 + +sensor: + - platform: bmi160 + address: 0x68 + acceleration_x: + name: BMI160 Accel X + acceleration_y: + name: BMI160 Accel Y + acceleration_z: + name: BMI160 Accel z + gyroscope_x: + name: BMI160 Gyro X + gyroscope_y: + name: BMI160 Gyro Y + gyroscope_z: + name: BMI160 Gyro z + temperature: + name: BMI160 Temperature diff --git a/tests/components/bmi160/test.esp32.yaml b/tests/components/bmi160/test.esp32.yaml new file mode 100644 index 0000000000..a8a90c8c87 --- /dev/null +++ b/tests/components/bmi160/test.esp32.yaml @@ -0,0 +1,22 @@ +i2c: + - id: i2c_bmi160 + scl: 16 + sda: 17 + +sensor: + - platform: bmi160 + address: 0x68 + acceleration_x: + name: BMI160 Accel X + acceleration_y: + name: BMI160 Accel Y + acceleration_z: + name: BMI160 Accel z + gyroscope_x: + name: BMI160 Gyro X + gyroscope_y: + name: BMI160 Gyro Y + gyroscope_z: + name: BMI160 Gyro z + temperature: + name: BMI160 Temperature diff --git a/tests/components/bmi160/test.esp8266.yaml b/tests/components/bmi160/test.esp8266.yaml new file mode 100644 index 0000000000..3fd6441980 --- /dev/null +++ b/tests/components/bmi160/test.esp8266.yaml @@ -0,0 +1,22 @@ +i2c: + - id: i2c_bmi160 + scl: 5 + sda: 4 + +sensor: + - platform: bmi160 + address: 0x68 + acceleration_x: + name: BMI160 Accel X + acceleration_y: + name: BMI160 Accel Y + acceleration_z: + name: BMI160 Accel z + gyroscope_x: + name: BMI160 Gyro X + gyroscope_y: + name: BMI160 Gyro Y + gyroscope_z: + name: BMI160 Gyro z + temperature: + name: BMI160 Temperature diff --git a/tests/components/bmi160/test.rp2040.yaml b/tests/components/bmi160/test.rp2040.yaml new file mode 100644 index 0000000000..3fd6441980 --- /dev/null +++ b/tests/components/bmi160/test.rp2040.yaml @@ -0,0 +1,22 @@ +i2c: + - id: i2c_bmi160 + scl: 5 + sda: 4 + +sensor: + - platform: bmi160 + address: 0x68 + acceleration_x: + name: BMI160 Accel X + acceleration_y: + name: BMI160 Accel Y + acceleration_z: + name: BMI160 Accel z + gyroscope_x: + name: BMI160 Gyro X + gyroscope_y: + name: BMI160 Gyro Y + gyroscope_z: + name: BMI160 Gyro z + temperature: + name: BMI160 Temperature diff --git a/tests/components/bmp085/test.esp32-c3-idf.yaml b/tests/components/bmp085/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..76a9fd07ba --- /dev/null +++ b/tests/components/bmp085/test.esp32-c3-idf.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp085 + scl: 5 + sda: 4 + +sensor: + - platform: bmp085 + temperature: + name: Outside Temperature + pressure: + name: Outside Pressure + filters: + - lambda: >- + return x / powf(1.0 - (x / 44330.0), 5.255); + update_interval: 15s diff --git a/tests/components/bmp085/test.esp32-c3.yaml b/tests/components/bmp085/test.esp32-c3.yaml new file mode 100644 index 0000000000..76a9fd07ba --- /dev/null +++ b/tests/components/bmp085/test.esp32-c3.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp085 + scl: 5 + sda: 4 + +sensor: + - platform: bmp085 + temperature: + name: Outside Temperature + pressure: + name: Outside Pressure + filters: + - lambda: >- + return x / powf(1.0 - (x / 44330.0), 5.255); + update_interval: 15s diff --git a/tests/components/bmp085/test.esp32-idf.yaml b/tests/components/bmp085/test.esp32-idf.yaml new file mode 100644 index 0000000000..8a4f714ddd --- /dev/null +++ b/tests/components/bmp085/test.esp32-idf.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp085 + scl: 16 + sda: 17 + +sensor: + - platform: bmp085 + temperature: + name: Outside Temperature + pressure: + name: Outside Pressure + filters: + - lambda: >- + return x / powf(1.0 - (x / 44330.0), 5.255); + update_interval: 15s diff --git a/tests/components/bmp085/test.esp32.yaml b/tests/components/bmp085/test.esp32.yaml new file mode 100644 index 0000000000..8a4f714ddd --- /dev/null +++ b/tests/components/bmp085/test.esp32.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp085 + scl: 16 + sda: 17 + +sensor: + - platform: bmp085 + temperature: + name: Outside Temperature + pressure: + name: Outside Pressure + filters: + - lambda: >- + return x / powf(1.0 - (x / 44330.0), 5.255); + update_interval: 15s diff --git a/tests/components/bmp085/test.esp8266.yaml b/tests/components/bmp085/test.esp8266.yaml new file mode 100644 index 0000000000..76a9fd07ba --- /dev/null +++ b/tests/components/bmp085/test.esp8266.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp085 + scl: 5 + sda: 4 + +sensor: + - platform: bmp085 + temperature: + name: Outside Temperature + pressure: + name: Outside Pressure + filters: + - lambda: >- + return x / powf(1.0 - (x / 44330.0), 5.255); + update_interval: 15s diff --git a/tests/components/bmp085/test.rp2040.yaml b/tests/components/bmp085/test.rp2040.yaml new file mode 100644 index 0000000000..76a9fd07ba --- /dev/null +++ b/tests/components/bmp085/test.rp2040.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp085 + scl: 5 + sda: 4 + +sensor: + - platform: bmp085 + temperature: + name: Outside Temperature + pressure: + name: Outside Pressure + filters: + - lambda: >- + return x / powf(1.0 - (x / 44330.0), 5.255); + update_interval: 15s diff --git a/tests/components/bmp280/test.esp32-c3-idf.yaml b/tests/components/bmp280/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..5f7f85d3e2 --- /dev/null +++ b/tests/components/bmp280/test.esp32-c3-idf.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp280 + scl: 5 + sda: 4 + +sensor: + - platform: bmp280 + address: 0x77 + temperature: + name: Outside Temperature + oversampling: 16x + pressure: + name: Outside Pressure + iir_filter: 16x + update_interval: 15s diff --git a/tests/components/bmp280/test.esp32-c3.yaml b/tests/components/bmp280/test.esp32-c3.yaml new file mode 100644 index 0000000000..5f7f85d3e2 --- /dev/null +++ b/tests/components/bmp280/test.esp32-c3.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp280 + scl: 5 + sda: 4 + +sensor: + - platform: bmp280 + address: 0x77 + temperature: + name: Outside Temperature + oversampling: 16x + pressure: + name: Outside Pressure + iir_filter: 16x + update_interval: 15s diff --git a/tests/components/bmp280/test.esp32-idf.yaml b/tests/components/bmp280/test.esp32-idf.yaml new file mode 100644 index 0000000000..aeb1cb262b --- /dev/null +++ b/tests/components/bmp280/test.esp32-idf.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp280 + scl: 16 + sda: 17 + +sensor: + - platform: bmp280 + address: 0x77 + temperature: + name: Outside Temperature + oversampling: 16x + pressure: + name: Outside Pressure + iir_filter: 16x + update_interval: 15s diff --git a/tests/components/bmp280/test.esp32.yaml b/tests/components/bmp280/test.esp32.yaml new file mode 100644 index 0000000000..aeb1cb262b --- /dev/null +++ b/tests/components/bmp280/test.esp32.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp280 + scl: 16 + sda: 17 + +sensor: + - platform: bmp280 + address: 0x77 + temperature: + name: Outside Temperature + oversampling: 16x + pressure: + name: Outside Pressure + iir_filter: 16x + update_interval: 15s diff --git a/tests/components/bmp280/test.esp8266.yaml b/tests/components/bmp280/test.esp8266.yaml new file mode 100644 index 0000000000..5f7f85d3e2 --- /dev/null +++ b/tests/components/bmp280/test.esp8266.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp280 + scl: 5 + sda: 4 + +sensor: + - platform: bmp280 + address: 0x77 + temperature: + name: Outside Temperature + oversampling: 16x + pressure: + name: Outside Pressure + iir_filter: 16x + update_interval: 15s diff --git a/tests/components/bmp280/test.rp2040.yaml b/tests/components/bmp280/test.rp2040.yaml new file mode 100644 index 0000000000..5f7f85d3e2 --- /dev/null +++ b/tests/components/bmp280/test.rp2040.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp280 + scl: 5 + sda: 4 + +sensor: + - platform: bmp280 + address: 0x77 + temperature: + name: Outside Temperature + oversampling: 16x + pressure: + name: Outside Pressure + iir_filter: 16x + update_interval: 15s diff --git a/tests/components/bmp3xx/test.esp32-c3-idf.yaml b/tests/components/bmp3xx/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..3b244eccf9 --- /dev/null +++ b/tests/components/bmp3xx/test.esp32-c3-idf.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_bmp3xx + scl: 5 + sda: 4 + +sensor: + - platform: bmp3xx + address: 0x77 + temperature: + name: BMP Temperature + oversampling: 16x + pressure: + name: BMP Pressure + iir_filter: 2X diff --git a/tests/components/bmp3xx/test.esp32-c3.yaml b/tests/components/bmp3xx/test.esp32-c3.yaml new file mode 100644 index 0000000000..3b244eccf9 --- /dev/null +++ b/tests/components/bmp3xx/test.esp32-c3.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_bmp3xx + scl: 5 + sda: 4 + +sensor: + - platform: bmp3xx + address: 0x77 + temperature: + name: BMP Temperature + oversampling: 16x + pressure: + name: BMP Pressure + iir_filter: 2X diff --git a/tests/components/bmp3xx/test.esp32-idf.yaml b/tests/components/bmp3xx/test.esp32-idf.yaml new file mode 100644 index 0000000000..677ed8a22d --- /dev/null +++ b/tests/components/bmp3xx/test.esp32-idf.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_bmp3xx + scl: 16 + sda: 17 + +sensor: + - platform: bmp3xx + address: 0x77 + temperature: + name: BMP Temperature + oversampling: 16x + pressure: + name: BMP Pressure + iir_filter: 2X diff --git a/tests/components/bmp3xx/test.esp32.yaml b/tests/components/bmp3xx/test.esp32.yaml new file mode 100644 index 0000000000..677ed8a22d --- /dev/null +++ b/tests/components/bmp3xx/test.esp32.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_bmp3xx + scl: 16 + sda: 17 + +sensor: + - platform: bmp3xx + address: 0x77 + temperature: + name: BMP Temperature + oversampling: 16x + pressure: + name: BMP Pressure + iir_filter: 2X diff --git a/tests/components/bmp3xx/test.esp8266.yaml b/tests/components/bmp3xx/test.esp8266.yaml new file mode 100644 index 0000000000..3b244eccf9 --- /dev/null +++ b/tests/components/bmp3xx/test.esp8266.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_bmp3xx + scl: 5 + sda: 4 + +sensor: + - platform: bmp3xx + address: 0x77 + temperature: + name: BMP Temperature + oversampling: 16x + pressure: + name: BMP Pressure + iir_filter: 2X diff --git a/tests/components/bmp3xx/test.rp2040.yaml b/tests/components/bmp3xx/test.rp2040.yaml new file mode 100644 index 0000000000..3b244eccf9 --- /dev/null +++ b/tests/components/bmp3xx/test.rp2040.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_bmp3xx + scl: 5 + sda: 4 + +sensor: + - platform: bmp3xx + address: 0x77 + temperature: + name: BMP Temperature + oversampling: 16x + pressure: + name: BMP Pressure + iir_filter: 2X diff --git a/tests/components/bmp581/test.esp32-c3-idf.yaml b/tests/components/bmp581/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..29d27afb90 --- /dev/null +++ b/tests/components/bmp581/test.esp32-c3-idf.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_bmp581 + scl: 5 + sda: 4 + +sensor: + - platform: bmp581 + temperature: + name: "BMP581 Temperature" + iir_filter: 2x + pressure: + name: "BMP581 Pressure" + oversampling: 128x diff --git a/tests/components/bmp581/test.esp32-c3.yaml b/tests/components/bmp581/test.esp32-c3.yaml new file mode 100644 index 0000000000..29d27afb90 --- /dev/null +++ b/tests/components/bmp581/test.esp32-c3.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_bmp581 + scl: 5 + sda: 4 + +sensor: + - platform: bmp581 + temperature: + name: "BMP581 Temperature" + iir_filter: 2x + pressure: + name: "BMP581 Pressure" + oversampling: 128x diff --git a/tests/components/bmp581/test.esp32-idf.yaml b/tests/components/bmp581/test.esp32-idf.yaml new file mode 100644 index 0000000000..a464b8ce6a --- /dev/null +++ b/tests/components/bmp581/test.esp32-idf.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_bmp581 + scl: 16 + sda: 17 + +sensor: + - platform: bmp581 + temperature: + name: "BMP581 Temperature" + iir_filter: 2x + pressure: + name: "BMP581 Pressure" + oversampling: 128x diff --git a/tests/components/bmp581/test.esp32.yaml b/tests/components/bmp581/test.esp32.yaml new file mode 100644 index 0000000000..a464b8ce6a --- /dev/null +++ b/tests/components/bmp581/test.esp32.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_bmp581 + scl: 16 + sda: 17 + +sensor: + - platform: bmp581 + temperature: + name: "BMP581 Temperature" + iir_filter: 2x + pressure: + name: "BMP581 Pressure" + oversampling: 128x diff --git a/tests/components/bmp581/test.esp8266.yaml b/tests/components/bmp581/test.esp8266.yaml new file mode 100644 index 0000000000..29d27afb90 --- /dev/null +++ b/tests/components/bmp581/test.esp8266.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_bmp581 + scl: 5 + sda: 4 + +sensor: + - platform: bmp581 + temperature: + name: "BMP581 Temperature" + iir_filter: 2x + pressure: + name: "BMP581 Pressure" + oversampling: 128x diff --git a/tests/components/bmp581/test.rp2040.yaml b/tests/components/bmp581/test.rp2040.yaml new file mode 100644 index 0000000000..29d27afb90 --- /dev/null +++ b/tests/components/bmp581/test.rp2040.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_bmp581 + scl: 5 + sda: 4 + +sensor: + - platform: bmp581 + temperature: + name: "BMP581 Temperature" + iir_filter: 2x + pressure: + name: "BMP581 Pressure" + oversampling: 128x diff --git a/tests/components/bp1658cj/test.esp32-c3-idf.yaml b/tests/components/bp1658cj/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..74d3155371 --- /dev/null +++ b/tests/components/bp1658cj/test.esp32-c3-idf.yaml @@ -0,0 +1,22 @@ +bp1658cj: + clock_pin: 5 + data_pin: 4 + max_power_color_channels: 4 + max_power_white_channels: 6 + +output: + - platform: bp1658cj + id: bp1658cj_red + channel: 1 + - platform: bp1658cj + id: bp1658cj_green + channel: 2 + - platform: bp1658cj + id: bp1658cj_blue + channel: 0 + - platform: bp1658cj + id: bp1658cj_coldwhite + channel: 3 + - platform: bp1658cj + id: bp1658cj_warmwhite + channel: 4 diff --git a/tests/components/bp1658cj/test.esp32-c3.yaml b/tests/components/bp1658cj/test.esp32-c3.yaml new file mode 100644 index 0000000000..74d3155371 --- /dev/null +++ b/tests/components/bp1658cj/test.esp32-c3.yaml @@ -0,0 +1,22 @@ +bp1658cj: + clock_pin: 5 + data_pin: 4 + max_power_color_channels: 4 + max_power_white_channels: 6 + +output: + - platform: bp1658cj + id: bp1658cj_red + channel: 1 + - platform: bp1658cj + id: bp1658cj_green + channel: 2 + - platform: bp1658cj + id: bp1658cj_blue + channel: 0 + - platform: bp1658cj + id: bp1658cj_coldwhite + channel: 3 + - platform: bp1658cj + id: bp1658cj_warmwhite + channel: 4 diff --git a/tests/components/bp1658cj/test.esp32-idf.yaml b/tests/components/bp1658cj/test.esp32-idf.yaml new file mode 100644 index 0000000000..5f9e25d3bd --- /dev/null +++ b/tests/components/bp1658cj/test.esp32-idf.yaml @@ -0,0 +1,22 @@ +bp1658cj: + clock_pin: 16 + data_pin: 17 + max_power_color_channels: 4 + max_power_white_channels: 6 + +output: + - platform: bp1658cj + id: bp1658cj_red + channel: 1 + - platform: bp1658cj + id: bp1658cj_green + channel: 2 + - platform: bp1658cj + id: bp1658cj_blue + channel: 0 + - platform: bp1658cj + id: bp1658cj_coldwhite + channel: 3 + - platform: bp1658cj + id: bp1658cj_warmwhite + channel: 4 diff --git a/tests/components/bp1658cj/test.esp32.yaml b/tests/components/bp1658cj/test.esp32.yaml new file mode 100644 index 0000000000..5f9e25d3bd --- /dev/null +++ b/tests/components/bp1658cj/test.esp32.yaml @@ -0,0 +1,22 @@ +bp1658cj: + clock_pin: 16 + data_pin: 17 + max_power_color_channels: 4 + max_power_white_channels: 6 + +output: + - platform: bp1658cj + id: bp1658cj_red + channel: 1 + - platform: bp1658cj + id: bp1658cj_green + channel: 2 + - platform: bp1658cj + id: bp1658cj_blue + channel: 0 + - platform: bp1658cj + id: bp1658cj_coldwhite + channel: 3 + - platform: bp1658cj + id: bp1658cj_warmwhite + channel: 4 diff --git a/tests/components/bp1658cj/test.esp8266.yaml b/tests/components/bp1658cj/test.esp8266.yaml new file mode 100644 index 0000000000..74d3155371 --- /dev/null +++ b/tests/components/bp1658cj/test.esp8266.yaml @@ -0,0 +1,22 @@ +bp1658cj: + clock_pin: 5 + data_pin: 4 + max_power_color_channels: 4 + max_power_white_channels: 6 + +output: + - platform: bp1658cj + id: bp1658cj_red + channel: 1 + - platform: bp1658cj + id: bp1658cj_green + channel: 2 + - platform: bp1658cj + id: bp1658cj_blue + channel: 0 + - platform: bp1658cj + id: bp1658cj_coldwhite + channel: 3 + - platform: bp1658cj + id: bp1658cj_warmwhite + channel: 4 diff --git a/tests/components/bp1658cj/test.rp2040.yaml b/tests/components/bp1658cj/test.rp2040.yaml new file mode 100644 index 0000000000..74d3155371 --- /dev/null +++ b/tests/components/bp1658cj/test.rp2040.yaml @@ -0,0 +1,22 @@ +bp1658cj: + clock_pin: 5 + data_pin: 4 + max_power_color_channels: 4 + max_power_white_channels: 6 + +output: + - platform: bp1658cj + id: bp1658cj_red + channel: 1 + - platform: bp1658cj + id: bp1658cj_green + channel: 2 + - platform: bp1658cj + id: bp1658cj_blue + channel: 0 + - platform: bp1658cj + id: bp1658cj_coldwhite + channel: 3 + - platform: bp1658cj + id: bp1658cj_warmwhite + channel: 4 diff --git a/tests/components/bp5758d/test.esp32-c3-idf.yaml b/tests/components/bp5758d/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ec74e935cd --- /dev/null +++ b/tests/components/bp5758d/test.esp32-c3-idf.yaml @@ -0,0 +1,25 @@ +bp5758d: + clock_pin: 5 + data_pin: 4 + +output: + - platform: bp5758d + id: bp5758d_red + channel: 2 + current: 10 + - platform: bp5758d + id: bp5758d_green + channel: 3 + current: 10 + - platform: bp5758d + id: bp5758d_blue + channel: 1 + current: 10 + - platform: bp5758d + id: bp5758d_coldwhite + channel: 5 + current: 10 + - platform: bp5758d + id: bp5758d_warmwhite + channel: 4 + current: 10 diff --git a/tests/components/bp5758d/test.esp32-c3.yaml b/tests/components/bp5758d/test.esp32-c3.yaml new file mode 100644 index 0000000000..ec74e935cd --- /dev/null +++ b/tests/components/bp5758d/test.esp32-c3.yaml @@ -0,0 +1,25 @@ +bp5758d: + clock_pin: 5 + data_pin: 4 + +output: + - platform: bp5758d + id: bp5758d_red + channel: 2 + current: 10 + - platform: bp5758d + id: bp5758d_green + channel: 3 + current: 10 + - platform: bp5758d + id: bp5758d_blue + channel: 1 + current: 10 + - platform: bp5758d + id: bp5758d_coldwhite + channel: 5 + current: 10 + - platform: bp5758d + id: bp5758d_warmwhite + channel: 4 + current: 10 diff --git a/tests/components/bp5758d/test.esp32-idf.yaml b/tests/components/bp5758d/test.esp32-idf.yaml new file mode 100644 index 0000000000..b7929a0518 --- /dev/null +++ b/tests/components/bp5758d/test.esp32-idf.yaml @@ -0,0 +1,25 @@ +bp5758d: + clock_pin: 16 + data_pin: 17 + +output: + - platform: bp5758d + id: bp5758d_red + channel: 2 + current: 10 + - platform: bp5758d + id: bp5758d_green + channel: 3 + current: 10 + - platform: bp5758d + id: bp5758d_blue + channel: 1 + current: 10 + - platform: bp5758d + id: bp5758d_coldwhite + channel: 5 + current: 10 + - platform: bp5758d + id: bp5758d_warmwhite + channel: 4 + current: 10 diff --git a/tests/components/bp5758d/test.esp32.yaml b/tests/components/bp5758d/test.esp32.yaml new file mode 100644 index 0000000000..b7929a0518 --- /dev/null +++ b/tests/components/bp5758d/test.esp32.yaml @@ -0,0 +1,25 @@ +bp5758d: + clock_pin: 16 + data_pin: 17 + +output: + - platform: bp5758d + id: bp5758d_red + channel: 2 + current: 10 + - platform: bp5758d + id: bp5758d_green + channel: 3 + current: 10 + - platform: bp5758d + id: bp5758d_blue + channel: 1 + current: 10 + - platform: bp5758d + id: bp5758d_coldwhite + channel: 5 + current: 10 + - platform: bp5758d + id: bp5758d_warmwhite + channel: 4 + current: 10 diff --git a/tests/components/bp5758d/test.esp8266.yaml b/tests/components/bp5758d/test.esp8266.yaml new file mode 100644 index 0000000000..ec74e935cd --- /dev/null +++ b/tests/components/bp5758d/test.esp8266.yaml @@ -0,0 +1,25 @@ +bp5758d: + clock_pin: 5 + data_pin: 4 + +output: + - platform: bp5758d + id: bp5758d_red + channel: 2 + current: 10 + - platform: bp5758d + id: bp5758d_green + channel: 3 + current: 10 + - platform: bp5758d + id: bp5758d_blue + channel: 1 + current: 10 + - platform: bp5758d + id: bp5758d_coldwhite + channel: 5 + current: 10 + - platform: bp5758d + id: bp5758d_warmwhite + channel: 4 + current: 10 diff --git a/tests/components/bp5758d/test.rp2040.yaml b/tests/components/bp5758d/test.rp2040.yaml new file mode 100644 index 0000000000..ec74e935cd --- /dev/null +++ b/tests/components/bp5758d/test.rp2040.yaml @@ -0,0 +1,25 @@ +bp5758d: + clock_pin: 5 + data_pin: 4 + +output: + - platform: bp5758d + id: bp5758d_red + channel: 2 + current: 10 + - platform: bp5758d + id: bp5758d_green + channel: 3 + current: 10 + - platform: bp5758d + id: bp5758d_blue + channel: 1 + current: 10 + - platform: bp5758d + id: bp5758d_coldwhite + channel: 5 + current: 10 + - platform: bp5758d + id: bp5758d_warmwhite + channel: 4 + current: 10 diff --git a/tests/components/button/test.esp32-c3-idf.yaml b/tests/components/button/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..d5978601f4 --- /dev/null +++ b/tests/components/button/test.esp32-c3-idf.yaml @@ -0,0 +1,6 @@ +button: + - platform: template + name: Button + id: some_button + on_press: + - logger.log: Button pressed diff --git a/tests/components/button/test.esp32-c3.yaml b/tests/components/button/test.esp32-c3.yaml new file mode 100644 index 0000000000..d5978601f4 --- /dev/null +++ b/tests/components/button/test.esp32-c3.yaml @@ -0,0 +1,6 @@ +button: + - platform: template + name: Button + id: some_button + on_press: + - logger.log: Button pressed diff --git a/tests/components/button/test.esp32-idf.yaml b/tests/components/button/test.esp32-idf.yaml new file mode 100644 index 0000000000..d5978601f4 --- /dev/null +++ b/tests/components/button/test.esp32-idf.yaml @@ -0,0 +1,6 @@ +button: + - platform: template + name: Button + id: some_button + on_press: + - logger.log: Button pressed diff --git a/tests/components/button/test.esp32.yaml b/tests/components/button/test.esp32.yaml new file mode 100644 index 0000000000..d5978601f4 --- /dev/null +++ b/tests/components/button/test.esp32.yaml @@ -0,0 +1,6 @@ +button: + - platform: template + name: Button + id: some_button + on_press: + - logger.log: Button pressed diff --git a/tests/components/button/test.esp8266.yaml b/tests/components/button/test.esp8266.yaml new file mode 100644 index 0000000000..d5978601f4 --- /dev/null +++ b/tests/components/button/test.esp8266.yaml @@ -0,0 +1,6 @@ +button: + - platform: template + name: Button + id: some_button + on_press: + - logger.log: Button pressed diff --git a/tests/components/button/test.rp2040.yaml b/tests/components/button/test.rp2040.yaml new file mode 100644 index 0000000000..d5978601f4 --- /dev/null +++ b/tests/components/button/test.rp2040.yaml @@ -0,0 +1,6 @@ +button: + - platform: template + name: Button + id: some_button + on_press: + - logger.log: Button pressed From fe789c8beb852621c5c0b451f97ac6158cbdadd4 Mon Sep 17 00:00:00 2001 From: Bill Adams Date: Tue, 6 Feb 2024 15:13:55 -0800 Subject: [PATCH 181/193] Add "transformer_active" flag for use in effects. (#6157) --- esphome/components/light/light_state.cpp | 5 +++++ esphome/components/light/light_state.h | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 50ebd8882b..fe6538e65e 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -120,6 +120,7 @@ void LightState::loop() { // Apply transformer (if any) if (this->transformer_ != nullptr) { auto values = this->transformer_->apply(); + this->is_transformer_active_ = true; if (values.has_value()) { this->current_values = *values; this->output_->update_state(this); @@ -131,6 +132,7 @@ void LightState::loop() { this->current_values = this->transformer_->get_target_values(); this->transformer_->stop(); + this->is_transformer_active_ = false; this->transformer_ = nullptr; this->target_state_reached_callback_.call(); } @@ -214,6 +216,8 @@ void LightState::current_values_as_ct(float *color_temperature, float *white_bri this->gamma_correct_); } +bool LightState::is_transformer_active() { return this->is_transformer_active_; } + void LightState::start_effect_(uint32_t effect_index) { this->stop_effect_(); if (effect_index == 0) @@ -263,6 +267,7 @@ void LightState::start_flash_(const LightColorValues &target, uint32_t length, b } void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) { + this->is_transformer_active_ = false; this->transformer_ = nullptr; this->current_values = target; if (set_remote_values) { diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index ac4718ade5..b0aaa453b5 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -144,6 +144,17 @@ class LightState : public EntityBase, public Component { void current_values_as_ct(float *color_temperature, float *white_brightness); + /** + * Indicator if a transformer (e.g. transition) is active. This is useful + * for effects e.g. at the start of the apply() method, add a check like: + * + * if (this->state_->is_transformer_active()) { + * // Something is already running. + * return; + * } + */ + bool is_transformer_active(); + protected: friend LightOutput; friend LightCall; @@ -203,6 +214,9 @@ class LightState : public EntityBase, public Component { LightRestoreMode restore_mode_; /// List of effects for this light. std::vector effects_; + + // for effects, true if a transformer (transition) is active. + bool is_transformer_active_ = false; }; } // namespace light From 0ede4a30955d6a16da29ef9467cdca51f823e5b3 Mon Sep 17 00:00:00 2001 From: Tomek Wasilczyk Date: Tue, 6 Feb 2024 17:12:14 -0800 Subject: [PATCH 182/193] CSE7766: fix power and current measurements at low loads (#6180) --- esphome/components/cse7766/cse7766.cpp | 110 +++++++++++++++++-------- 1 file changed, 75 insertions(+), 35 deletions(-) diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index 9c5016c503..f482ba26c3 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -1,6 +1,8 @@ #include "cse7766.h" #include "esphome/core/log.h" #include +#include +#include namespace esphome { namespace cse7766 { @@ -68,20 +70,26 @@ bool CSE7766Component::check_byte_() { return true; } void CSE7766Component::parse_data_() { - ESP_LOGVV(TAG, "CSE7766 Data: "); - for (uint8_t i = 0; i < 23; i++) { - ESP_LOGVV(TAG, " %u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i + 1, BYTE_TO_BINARY(this->raw_data_[i]), - this->raw_data_[i]); +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE + { + std::stringstream ss; + ss << "Raw data:" << std::hex << std::uppercase << std::setfill('0'); + for (uint8_t i = 0; i < 23; i++) { + ss << ' ' << std::setw(2) << static_cast(this->raw_data_[i]); + } + ESP_LOGVV(TAG, "%s", ss.str().c_str()); } +#endif + // Parse header uint8_t header1 = this->raw_data_[0]; + if (header1 == 0xAA) { ESP_LOGE(TAG, "CSE7766 not calibrated!"); return; } bool power_cycle_exceeds_range = false; - if ((header1 & 0xF0) == 0xF0) { if (header1 & 0xD) { ESP_LOGE(TAG, "CSE7766 reports abnormal external circuit or chip damage: (0x%02X)", header1); @@ -94,74 +102,106 @@ void CSE7766Component::parse_data_() { if (header1 & (1 << 0)) { ESP_LOGE(TAG, " Coefficient storage area is abnormal."); } + + // Datasheet: voltage or current cycle exceeding range means invalid values return; } power_cycle_exceeds_range = header1 & (1 << 1); } - uint32_t voltage_calib = this->get_24_bit_uint_(2); + // Parse data frame + uint32_t voltage_coeff = this->get_24_bit_uint_(2); uint32_t voltage_cycle = this->get_24_bit_uint_(5); - uint32_t current_calib = this->get_24_bit_uint_(8); + uint32_t current_coeff = this->get_24_bit_uint_(8); uint32_t current_cycle = this->get_24_bit_uint_(11); - uint32_t power_calib = this->get_24_bit_uint_(14); + uint32_t power_coeff = this->get_24_bit_uint_(14); uint32_t power_cycle = this->get_24_bit_uint_(17); - uint8_t adj = this->raw_data_[20]; uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22]; + bool have_power = adj & 0x10; + bool have_current = adj & 0x20; bool have_voltage = adj & 0x40; + + float voltage = 0.0f; if (have_voltage) { - // voltage cycle of serial port outputted is a complete cycle; - float voltage = voltage_calib / float(voltage_cycle); - if (this->voltage_sensor_ != nullptr) + voltage = voltage_coeff / float(voltage_cycle); + if (this->voltage_sensor_ != nullptr) { this->voltage_sensor_->publish_state(voltage); + } } - bool have_power = adj & 0x10; float power = 0.0f; - - if (have_power) { - // power cycle of serial port outputted is a complete cycle; - // According to the user manual, power cycle exceeding range means the measured power is 0 - if (!power_cycle_exceeds_range) { - power = power_calib / float(power_cycle); + float energy = 0.0f; + if (power_cycle_exceeds_range) { + // Datasheet: power cycle exceeding range means active power is 0 + if (this->power_sensor_ != nullptr) { + this->power_sensor_->publish_state(0.0f); } - if (this->power_sensor_ != nullptr) + } else if (have_power) { + power = power_coeff / float(power_cycle); + if (this->power_sensor_ != nullptr) { this->power_sensor_->publish_state(power); + } + + // Add CF pulses to the total energy only if we have Power coefficient to multiply by - uint32_t difference; if (this->cf_pulses_last_ == 0) { this->cf_pulses_last_ = cf_pulses; } + uint32_t cf_diff; if (cf_pulses < this->cf_pulses_last_) { - difference = cf_pulses + (0x10000 - this->cf_pulses_last_); + cf_diff = cf_pulses + (0x10000 - this->cf_pulses_last_); } else { - difference = cf_pulses - this->cf_pulses_last_; + cf_diff = cf_pulses - this->cf_pulses_last_; } this->cf_pulses_last_ = cf_pulses; - this->energy_total_ += difference * float(power_calib) / 1000000.0f / 3600.0f; + + energy = cf_diff * float(power_coeff) / 1000000.0f / 3600.0f; + this->energy_total_ += energy; if (this->energy_sensor_ != nullptr) this->energy_sensor_->publish_state(this->energy_total_); } else if ((this->energy_sensor_ != nullptr) && !this->energy_sensor_->has_state()) { this->energy_sensor_->publish_state(0); } - if (adj & 0x20) { - // indicates current cycle of serial port outputted is a complete cycle; - float current = 0.0f; - if (have_voltage && !have_power) { - // Testing has shown that when we have voltage and current but not power, that means the power is 0. - // We report a power of 0, which in turn means we should report a current of 0. - if (this->power_sensor_ != nullptr) - this->power_sensor_->publish_state(0); - } else if (power != 0.0f) { - current = current_calib / float(current_cycle); + float current = 0.0f; + float calculated_current = 0.0f; + if (have_current) { + // Assumption: if we don't have power measurement, then current is likely below 50mA + if (have_power && voltage > 1.0f) { + calculated_current = power / voltage; } - if (this->current_sensor_ != nullptr) + // Datasheet: minimum measured current is 50mA + if (calculated_current > 0.05f) { + current = current_coeff / float(current_cycle); + } + if (this->current_sensor_ != nullptr) { this->current_sensor_->publish_state(current); + } } + +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE + { + std::stringstream ss; + ss << "Parsed:"; + if (have_voltage) { + ss << " V=" << voltage << "V"; + } + if (have_current) { + ss << " I=" << current * 1000.0f << "mA (~" << calculated_current * 1000.0f << "mA)"; + } + if (have_power) { + ss << " P=" << power << "W"; + } + if (energy != 0.0f) { + ss << " E=" << energy << "kWh (" << cf_pulses << ")"; + } + ESP_LOGVV(TAG, "%s", ss.str().c_str()); + } +#endif } uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) { From f3ef05f5c303111dc9c1dd6d8003baf794ff4939 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 7 Feb 2024 12:24:06 +1100 Subject: [PATCH 183/193] host platform: improvements and bugfixes (#6137) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/host/__init__.py | 8 +++++++- esphome/components/logger/logger.cpp | 8 ++++++++ esphome/components/sntp/sntp_component.cpp | 4 +++- esphome/components/text_sensor/filter.h | 2 +- esphome/core/component.h | 5 +++-- esphome/core/helpers.cpp | 15 +++++++++++++-- esphome/core/time.h | 1 + 7 files changed, 36 insertions(+), 7 deletions(-) diff --git a/esphome/components/host/__init__.py b/esphome/components/host/__init__.py index eb44bcccd6..3bd3b6b172 100644 --- a/esphome/components/host/__init__.py +++ b/esphome/components/host/__init__.py @@ -4,6 +4,7 @@ from esphome.const import ( KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, PLATFORM_HOST, + CONF_MAC_ADDRESS, ) from esphome.core import CORE from esphome.helpers import IS_MACOS @@ -28,13 +29,18 @@ def set_core_data(config): CONFIG_SCHEMA = cv.All( - cv.Schema({}), + cv.Schema( + { + cv.Optional(CONF_MAC_ADDRESS, default="98:35:69:ab:f6:79"): cv.mac_address, + } + ), set_core_data, ) async def to_code(config): cg.add_build_flag("-DUSE_HOST") + cg.add_define("USE_ESPHOME_HOST_MAC_ADDRESS", config[CONF_MAC_ADDRESS].parts) cg.add_build_flag("-std=c++17") cg.add_build_flag("-lsodium") if IS_MACOS: diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index d5f5c275eb..c8a3ba906c 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -212,6 +212,14 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) { return; #endif #ifdef USE_HOST + time_t rawtime; + struct tm *timeinfo; + char buffer[80]; + + time(&rawtime); + timeinfo = localtime(&rawtime); + strftime(buffer, sizeof buffer, "[%H:%M:%S]", timeinfo); + fputs(buffer, stdout); puts(msg); #endif diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 418eacd870..6a60e8d5c1 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -25,6 +25,7 @@ namespace sntp { static const char *const TAG = "sntp"; void SNTPComponent::setup() { +#ifndef USE_HOST ESP_LOGCONFIG(TAG, "Setting up SNTP..."); #if defined(USE_ESP32) || defined(USE_LIBRETINY) if (sntp_enabled()) { @@ -48,6 +49,7 @@ void SNTPComponent::setup() { #endif sntp_init(); +#endif } void SNTPComponent::dump_config() { ESP_LOGCONFIG(TAG, "SNTP Time:"); @@ -57,7 +59,7 @@ void SNTPComponent::dump_config() { ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); } void SNTPComponent::update() { -#ifndef USE_ESP_IDF +#if !defined(USE_ESP_IDF) && !defined(USE_HOST) // force resync if (sntp_enabled()) { sntp_stop(); diff --git a/esphome/components/text_sensor/filter.h b/esphome/components/text_sensor/filter.h index 4e36532945..2de9010b88 100644 --- a/esphome/components/text_sensor/filter.h +++ b/esphome/components/text_sensor/filter.h @@ -28,7 +28,7 @@ class Filter { * @param value The new value. * @return An optional string, the new value that should be pushed out. */ - virtual optional new_value(std::string value); + virtual optional new_value(std::string value) = 0; /// Initialize this filter, please note this can be called more than once. virtual void initialize(TextSensor *parent, Filter *next); diff --git a/esphome/core/component.h b/esphome/core/component.h index 51a6296811..594f8b65af 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -1,8 +1,9 @@ #pragma once -#include -#include #include +#include +#include +#include #include "esphome/core/optional.h" diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index c95c0470de..cec8a82d04 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -11,6 +11,12 @@ #include #include +#ifdef USE_HOST +#include +#include +#include +#include +#endif #if defined(USE_ESP8266) #include #include @@ -415,7 +421,7 @@ std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) { int8_t step_to_accuracy_decimals(float step) { // use printf %g to find number of digits based on temperature step char buf[32]; - sprintf(buf, "%.5g", step); + snprintf(buf, sizeof buf, "%.5g", step); std::string str{buf}; size_t dot_pos = str.find('.'); @@ -551,7 +557,10 @@ void HighFrequencyLoopRequester::stop() { bool HighFrequencyLoopRequester::is_high_frequency() { return num_requests > 0; } void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) -#if defined(USE_ESP32) +#if defined(USE_HOST) + static const uint8_t esphome_host_mac_address[6] = USE_ESPHOME_HOST_MAC_ADDRESS; + memcpy(mac, esphome_host_mac_address, sizeof(esphome_host_mac_address)); +#elif defined(USE_ESP32) #if defined(CONFIG_SOC_IEEE802154_SUPPORTED) || defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC) // When CONFIG_SOC_IEEE802154_SUPPORTED is defined, esp_efuse_mac_get_default // returns the 802.15.4 EUI-64 address. Read directly from eFuse instead. @@ -569,6 +578,8 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame WiFi.macAddress(mac); #elif defined(USE_LIBRETINY) WiFi.macAddress(mac); +#else +// this should be an error, but that messes with CI checks. #error No mac address method defined #endif } std::string get_mac_address() { diff --git a/esphome/core/time.h b/esphome/core/time.h index 14c36311e0..670bf0ee73 100644 --- a/esphome/core/time.h +++ b/esphome/core/time.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include From 558588ee8ae3e0b8f95d9a8f53b23dd92031a047 Mon Sep 17 00:00:00 2001 From: ChuckMash <86080247+ChuckMash@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:41:40 -0800 Subject: [PATCH 184/193] WLED Sync fix and BK72XX support (#6190) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/wled/__init__.py | 7 ++- esphome/components/wled/wled_light_effect.cpp | 43 +++++++++++++++++-- esphome/components/wled/wled_light_effect.h | 4 ++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/esphome/components/wled/__init__.py b/esphome/components/wled/__init__.py index 2795529203..396d5891d8 100644 --- a/esphome/components/wled/__init__.py +++ b/esphome/components/wled/__init__.py @@ -8,6 +8,8 @@ wled_ns = cg.esphome_ns.namespace("wled") WLEDLightEffect = wled_ns.class_("WLEDLightEffect", AddressableLightEffect) CONFIG_SCHEMA = cv.All(cv.Schema({}), cv.only_with_arduino) +CONF_SYNC_GROUP_MASK = "sync_group_mask" +CONF_BLANK_ON_START = "blank_on_start" @register_addressable_effect( @@ -16,10 +18,13 @@ CONFIG_SCHEMA = cv.All(cv.Schema({}), cv.only_with_arduino) "WLED", { cv.Optional(CONF_PORT, default=21324): cv.port, + cv.Optional(CONF_SYNC_GROUP_MASK, default=0): cv.int_range(min=0, max=255), + cv.Optional(CONF_BLANK_ON_START, default=True): cv.boolean, }, ) async def wled_light_effect_to_code(config, effect_id): effect = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(effect.set_port(config[CONF_PORT])) - + cg.add(effect.set_sync_group_mask(config[CONF_SYNC_GROUP_MASK])) + cg.add(effect.set_blank_on_start(config[CONF_BLANK_ON_START])) return effect diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index 8c68bca6e3..7a82aaeb46 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -13,6 +13,10 @@ #include #endif +#ifdef USE_BK72XX +#include +#endif + namespace esphome { namespace wled { @@ -29,7 +33,11 @@ WLEDLightEffect::WLEDLightEffect(const std::string &name) : AddressableLightEffe void WLEDLightEffect::start() { AddressableLightEffect::start(); - blank_at_ = 0; + if (this->blank_on_start_) { + this->blank_at_ = 0; + } else { + this->blank_at_ = UINT32_MAX; + } } void WLEDLightEffect::stop() { @@ -101,8 +109,11 @@ bool WLEDLightEffect::parse_frame_(light::AddressableLight &it, const uint8_t *p if (!parse_drgb_frame_(it, payload, size)) return false; } else { - if (!parse_notifier_frame_(it, payload, size)) + if (!parse_notifier_frame_(it, payload, size)) { return false; + } else { + timeout = UINT8_MAX; + } } break; @@ -143,8 +154,32 @@ bool WLEDLightEffect::parse_frame_(light::AddressableLight &it, const uint8_t *p } bool WLEDLightEffect::parse_notifier_frame_(light::AddressableLight &it, const uint8_t *payload, uint16_t size) { - // Packet needs to be empty - return size == 0; + // Receive at least RGBW and Brightness for all LEDs from WLED Sync Notification + // https://kno.wled.ge/interfaces/udp-notifier/ + // https://github.com/Aircoookie/WLED/blob/main/wled00/udp.cpp + + if (size < 34) { + return false; + } + + uint8_t payload_sync_group_mask = payload[34]; + + if ((payload_sync_group_mask & this->sync_group_mask_) != this->sync_group_mask_) { + ESP_LOGD(TAG, "sync group mask does not match"); + return false; + } + + uint8_t bri = payload[0]; + uint8_t r = esp_scale8(payload[1], bri); + uint8_t g = esp_scale8(payload[2], bri); + uint8_t b = esp_scale8(payload[3], bri); + uint8_t w = esp_scale8(payload[8], bri); + + for (auto &&led : it) { + led.set(Color(r, g, b, w)); + } + + return true; } bool WLEDLightEffect::parse_warls_frame_(light::AddressableLight &it, const uint8_t *payload, uint16_t size) { diff --git a/esphome/components/wled/wled_light_effect.h b/esphome/components/wled/wled_light_effect.h index 8f239276d7..a591e1fd1a 100644 --- a/esphome/components/wled/wled_light_effect.h +++ b/esphome/components/wled/wled_light_effect.h @@ -21,6 +21,8 @@ class WLEDLightEffect : public light::AddressableLightEffect { void stop() override; void apply(light::AddressableLight &it, const Color ¤t_color) override; void set_port(uint16_t port) { this->port_ = port; } + void set_sync_group_mask(uint8_t mask) { this->sync_group_mask_ = mask; } + void set_blank_on_start(bool blank) { this->blank_on_start_ = blank; } protected: void blank_all_leds_(light::AddressableLight &it); @@ -35,6 +37,8 @@ class WLEDLightEffect : public light::AddressableLightEffect { std::unique_ptr udp_; uint32_t blank_at_{0}; uint32_t dropped_{0}; + uint8_t sync_group_mask_{0}; + bool blank_on_start_{true}; }; } // namespace wled From a91937dca52e0e3753ae3af0d2a1a49a6bad5875 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 7 Feb 2024 15:53:44 -0600 Subject: [PATCH 185/193] Add missing vector.h for lightwaverf (#6196) --- esphome/components/lightwaverf/LwTx.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/lightwaverf/LwTx.h b/esphome/components/lightwaverf/LwTx.h index 719826640e..fe7b942a3a 100644 --- a/esphome/components/lightwaverf/LwTx.h +++ b/esphome/components/lightwaverf/LwTx.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/core/hal.h" +#include + namespace esphome { namespace lightwaverf { From 3eaf59cc5a411668ecb310cfb000c8ba85716c0b Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 7 Feb 2024 15:55:20 -0600 Subject: [PATCH 186/193] Add some components to the new testing framework (C) (#6174) --- .../components/canbus/test.esp32-c3-idf.yaml | 46 +++++++++++ tests/components/canbus/test.esp32-c3.yaml | 46 +++++++++++ tests/components/canbus/test.esp32-idf.yaml | 46 +++++++++++ tests/components/canbus/test.esp32.yaml | 46 +++++++++++ .../components/cap1188/test.esp32-c3-idf.yaml | 11 +++ tests/components/cap1188/test.esp32-c3.yaml | 11 +++ tests/components/cap1188/test.esp32-idf.yaml | 11 +++ tests/components/cap1188/test.esp32.yaml | 11 +++ tests/components/cap1188/test.esp8266.yaml | 11 +++ tests/components/cap1188/test.rp2040.yaml | 11 +++ .../captive_portal/test.esp32-c3-idf.yaml | 5 ++ .../captive_portal/test.esp32-c3.yaml | 5 ++ .../captive_portal/test.esp32-idf.yaml | 5 ++ .../components/captive_portal/test.esp32.yaml | 5 ++ .../captive_portal/test.esp8266.yaml | 5 ++ .../components/ccs811/test.esp32-c3-idf.yaml | 13 ++++ tests/components/ccs811/test.esp32-c3.yaml | 13 ++++ tests/components/ccs811/test.esp32-idf.yaml | 13 ++++ tests/components/ccs811/test.esp32.yaml | 13 ++++ tests/components/ccs811/test.esp8266.yaml | 13 ++++ tests/components/ccs811/test.rp2040.yaml | 13 ++++ .../cd74hc4067/test.esp32-c3-idf.yaml | 18 +++++ .../components/cd74hc4067/test.esp32-c3.yaml | 18 +++++ .../components/cd74hc4067/test.esp32-idf.yaml | 18 +++++ tests/components/cd74hc4067/test.esp32.yaml | 18 +++++ tests/components/cd74hc4067/test.esp8266.yaml | 18 +++++ tests/components/cd74hc4067/test.rp2040.yaml | 18 +++++ .../climate_ir_lg/test.esp32-c3-idf.yaml | 7 ++ .../climate_ir_lg/test.esp32-c3.yaml | 7 ++ .../climate_ir_lg/test.esp32-idf.yaml | 7 ++ .../components/climate_ir_lg/test.esp32.yaml | 7 ++ .../climate_ir_lg/test.esp8266.yaml | 7 ++ tests/components/color/test.esp32-c3-idf.yaml | 11 +++ tests/components/color/test.esp32-c3.yaml | 11 +++ tests/components/color/test.esp32-idf.yaml | 11 +++ tests/components/color/test.esp32.yaml | 11 +++ tests/components/color/test.esp8266.yaml | 11 +++ tests/components/color/test.rp2040.yaml | 11 +++ .../color_temperature/test.esp32-c3-idf.yaml | 15 ++++ .../color_temperature/test.esp32-c3.yaml | 15 ++++ .../color_temperature/test.esp32-idf.yaml | 15 ++++ .../color_temperature/test.esp32.yaml | 15 ++++ .../color_temperature/test.esp8266.yaml | 15 ++++ .../color_temperature/test.rp2040.yaml | 15 ++++ .../combination/test.esp32-c3-idf.yaml | 76 +++++++++++++++++++ .../components/combination/test.esp32-c3.yaml | 76 +++++++++++++++++++ .../combination/test.esp32-idf.yaml | 76 +++++++++++++++++++ tests/components/combination/test.esp32.yaml | 76 +++++++++++++++++++ .../components/combination/test.esp8266.yaml | 76 +++++++++++++++++++ tests/components/combination/test.rp2040.yaml | 76 +++++++++++++++++++ .../components/coolix/test.esp32-c3-idf.yaml | 7 ++ tests/components/coolix/test.esp32-c3.yaml | 7 ++ tests/components/coolix/test.esp32-idf.yaml | 7 ++ tests/components/coolix/test.esp32.yaml | 7 ++ tests/components/coolix/test.esp8266.yaml | 7 ++ tests/components/copy/test.esp32-c3-idf.yaml | 23 ++++++ tests/components/copy/test.esp32-c3.yaml | 23 ++++++ tests/components/copy/test.esp32-idf.yaml | 23 ++++++ tests/components/copy/test.esp32.yaml | 23 ++++++ tests/components/copy/test.esp8266.yaml | 23 ++++++ tests/components/copy/test.rp2040.yaml | 23 ++++++ .../components/cs5460a/test.esp32-c3-idf.yaml | 27 +++++++ tests/components/cs5460a/test.esp32-c3.yaml | 27 +++++++ tests/components/cs5460a/test.esp32-idf.yaml | 27 +++++++ tests/components/cs5460a/test.esp32.yaml | 27 +++++++ tests/components/cs5460a/test.esp8266.yaml | 27 +++++++ tests/components/cs5460a/test.rp2040.yaml | 27 +++++++ .../components/cse7761/test.esp32-c3-idf.yaml | 20 +++++ tests/components/cse7761/test.esp32-c3.yaml | 20 +++++ tests/components/cse7761/test.esp32-idf.yaml | 20 +++++ tests/components/cse7761/test.esp32.yaml | 20 +++++ tests/components/cse7761/test.esp8266.yaml | 20 +++++ tests/components/cse7761/test.rp2040.yaml | 20 +++++ .../components/cse7766/test.esp32-c3-idf.yaml | 16 ++++ tests/components/cse7766/test.esp32-c3.yaml | 16 ++++ tests/components/cse7766/test.esp32-idf.yaml | 16 ++++ tests/components/cse7766/test.esp32.yaml | 16 ++++ tests/components/cse7766/test.esp8266.yaml | 16 ++++ tests/components/cse7766/test.rp2040.yaml | 16 ++++ .../ct_clamp/test.esp32-c3-idf.yaml | 9 +++ tests/components/ct_clamp/test.esp32-c3.yaml | 9 +++ tests/components/ct_clamp/test.esp32-idf.yaml | 9 +++ tests/components/ct_clamp/test.esp32.yaml | 9 +++ tests/components/ct_clamp/test.esp8266.yaml | 9 +++ tests/components/ct_clamp/test.rp2040.yaml | 9 +++ .../current_based/test.esp32-c3-idf.yaml | 68 +++++++++++++++++ .../current_based/test.esp32-c3.yaml | 68 +++++++++++++++++ .../current_based/test.esp32-idf.yaml | 68 +++++++++++++++++ .../components/current_based/test.esp32.yaml | 68 +++++++++++++++++ .../current_based/test.esp8266.yaml | 68 +++++++++++++++++ .../components/current_based/test.rp2040.yaml | 68 +++++++++++++++++ tests/components/cwww/test.esp32-c3-idf.yaml | 16 ++++ tests/components/cwww/test.esp32-c3.yaml | 16 ++++ tests/components/cwww/test.esp32-idf.yaml | 16 ++++ tests/components/cwww/test.esp32.yaml | 16 ++++ tests/components/cwww/test.esp8266.yaml | 16 ++++ tests/components/cwww/test.rp2040.yaml | 16 ++++ 97 files changed, 2217 insertions(+) create mode 100644 tests/components/canbus/test.esp32-c3-idf.yaml create mode 100644 tests/components/canbus/test.esp32-c3.yaml create mode 100644 tests/components/canbus/test.esp32-idf.yaml create mode 100644 tests/components/canbus/test.esp32.yaml create mode 100644 tests/components/cap1188/test.esp32-c3-idf.yaml create mode 100644 tests/components/cap1188/test.esp32-c3.yaml create mode 100644 tests/components/cap1188/test.esp32-idf.yaml create mode 100644 tests/components/cap1188/test.esp32.yaml create mode 100644 tests/components/cap1188/test.esp8266.yaml create mode 100644 tests/components/cap1188/test.rp2040.yaml create mode 100644 tests/components/captive_portal/test.esp32-c3-idf.yaml create mode 100644 tests/components/captive_portal/test.esp32-c3.yaml create mode 100644 tests/components/captive_portal/test.esp32-idf.yaml create mode 100644 tests/components/captive_portal/test.esp32.yaml create mode 100644 tests/components/captive_portal/test.esp8266.yaml create mode 100644 tests/components/ccs811/test.esp32-c3-idf.yaml create mode 100644 tests/components/ccs811/test.esp32-c3.yaml create mode 100644 tests/components/ccs811/test.esp32-idf.yaml create mode 100644 tests/components/ccs811/test.esp32.yaml create mode 100644 tests/components/ccs811/test.esp8266.yaml create mode 100644 tests/components/ccs811/test.rp2040.yaml create mode 100644 tests/components/cd74hc4067/test.esp32-c3-idf.yaml create mode 100644 tests/components/cd74hc4067/test.esp32-c3.yaml create mode 100644 tests/components/cd74hc4067/test.esp32-idf.yaml create mode 100644 tests/components/cd74hc4067/test.esp32.yaml create mode 100644 tests/components/cd74hc4067/test.esp8266.yaml create mode 100644 tests/components/cd74hc4067/test.rp2040.yaml create mode 100644 tests/components/climate_ir_lg/test.esp32-c3-idf.yaml create mode 100644 tests/components/climate_ir_lg/test.esp32-c3.yaml create mode 100644 tests/components/climate_ir_lg/test.esp32-idf.yaml create mode 100644 tests/components/climate_ir_lg/test.esp32.yaml create mode 100644 tests/components/climate_ir_lg/test.esp8266.yaml create mode 100644 tests/components/color/test.esp32-c3-idf.yaml create mode 100644 tests/components/color/test.esp32-c3.yaml create mode 100644 tests/components/color/test.esp32-idf.yaml create mode 100644 tests/components/color/test.esp32.yaml create mode 100644 tests/components/color/test.esp8266.yaml create mode 100644 tests/components/color/test.rp2040.yaml create mode 100644 tests/components/color_temperature/test.esp32-c3-idf.yaml create mode 100644 tests/components/color_temperature/test.esp32-c3.yaml create mode 100644 tests/components/color_temperature/test.esp32-idf.yaml create mode 100644 tests/components/color_temperature/test.esp32.yaml create mode 100644 tests/components/color_temperature/test.esp8266.yaml create mode 100644 tests/components/color_temperature/test.rp2040.yaml create mode 100644 tests/components/combination/test.esp32-c3-idf.yaml create mode 100644 tests/components/combination/test.esp32-c3.yaml create mode 100644 tests/components/combination/test.esp32-idf.yaml create mode 100644 tests/components/combination/test.esp32.yaml create mode 100644 tests/components/combination/test.esp8266.yaml create mode 100644 tests/components/combination/test.rp2040.yaml create mode 100644 tests/components/coolix/test.esp32-c3-idf.yaml create mode 100644 tests/components/coolix/test.esp32-c3.yaml create mode 100644 tests/components/coolix/test.esp32-idf.yaml create mode 100644 tests/components/coolix/test.esp32.yaml create mode 100644 tests/components/coolix/test.esp8266.yaml create mode 100644 tests/components/copy/test.esp32-c3-idf.yaml create mode 100644 tests/components/copy/test.esp32-c3.yaml create mode 100644 tests/components/copy/test.esp32-idf.yaml create mode 100644 tests/components/copy/test.esp32.yaml create mode 100644 tests/components/copy/test.esp8266.yaml create mode 100644 tests/components/copy/test.rp2040.yaml create mode 100644 tests/components/cs5460a/test.esp32-c3-idf.yaml create mode 100644 tests/components/cs5460a/test.esp32-c3.yaml create mode 100644 tests/components/cs5460a/test.esp32-idf.yaml create mode 100644 tests/components/cs5460a/test.esp32.yaml create mode 100644 tests/components/cs5460a/test.esp8266.yaml create mode 100644 tests/components/cs5460a/test.rp2040.yaml create mode 100644 tests/components/cse7761/test.esp32-c3-idf.yaml create mode 100644 tests/components/cse7761/test.esp32-c3.yaml create mode 100644 tests/components/cse7761/test.esp32-idf.yaml create mode 100644 tests/components/cse7761/test.esp32.yaml create mode 100644 tests/components/cse7761/test.esp8266.yaml create mode 100644 tests/components/cse7761/test.rp2040.yaml create mode 100644 tests/components/cse7766/test.esp32-c3-idf.yaml create mode 100644 tests/components/cse7766/test.esp32-c3.yaml create mode 100644 tests/components/cse7766/test.esp32-idf.yaml create mode 100644 tests/components/cse7766/test.esp32.yaml create mode 100644 tests/components/cse7766/test.esp8266.yaml create mode 100644 tests/components/cse7766/test.rp2040.yaml create mode 100644 tests/components/ct_clamp/test.esp32-c3-idf.yaml create mode 100644 tests/components/ct_clamp/test.esp32-c3.yaml create mode 100644 tests/components/ct_clamp/test.esp32-idf.yaml create mode 100644 tests/components/ct_clamp/test.esp32.yaml create mode 100644 tests/components/ct_clamp/test.esp8266.yaml create mode 100644 tests/components/ct_clamp/test.rp2040.yaml create mode 100644 tests/components/current_based/test.esp32-c3-idf.yaml create mode 100644 tests/components/current_based/test.esp32-c3.yaml create mode 100644 tests/components/current_based/test.esp32-idf.yaml create mode 100644 tests/components/current_based/test.esp32.yaml create mode 100644 tests/components/current_based/test.esp8266.yaml create mode 100644 tests/components/current_based/test.rp2040.yaml create mode 100644 tests/components/cwww/test.esp32-c3-idf.yaml create mode 100644 tests/components/cwww/test.esp32-c3.yaml create mode 100644 tests/components/cwww/test.esp32-idf.yaml create mode 100644 tests/components/cwww/test.esp32.yaml create mode 100644 tests/components/cwww/test.esp8266.yaml create mode 100644 tests/components/cwww/test.rp2040.yaml diff --git a/tests/components/canbus/test.esp32-c3-idf.yaml b/tests/components/canbus/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..fd146cc3a3 --- /dev/null +++ b/tests/components/canbus/test.esp32-c3-idf.yaml @@ -0,0 +1,46 @@ +canbus: + - platform: esp32_can + id: esp32_internal_can + rx_pin: 4 + tx_pin: 5 + can_id: 4 + bit_rate: 50kbps + on_frame: + - can_id: 500 + then: + - lambda: |- + std::string b(x.begin(), x.end()); + ESP_LOGD("canid 500", "%s", b.c_str()); + - can_id: 23 + then: + - if: + condition: + lambda: "return x[0] == 0x11;" + then: + logger.log: Truth + - can_id: 0b00000000000000000000001000000 + can_id_mask: 0b11111000000000011111111000000 + use_extended_id: true + then: + - lambda: |- + auto pdo_id = can_id >> 14; + switch (pdo_id) + { + case 117: + ESP_LOGD("canbus", "exhaust_fan_duty"); + break; + case 118: + ESP_LOGD("canbus", "supply_fan_duty"); + break; + case 119: + ESP_LOGD("canbus", "supply_fan_flow"); + break; + } + +button: + - platform: template + name: Canbus Actions + on_press: + - canbus.send: "abc" + - canbus.send: [0, 1, 2] + - canbus.send: !lambda return {0, 1, 2}; diff --git a/tests/components/canbus/test.esp32-c3.yaml b/tests/components/canbus/test.esp32-c3.yaml new file mode 100644 index 0000000000..fd146cc3a3 --- /dev/null +++ b/tests/components/canbus/test.esp32-c3.yaml @@ -0,0 +1,46 @@ +canbus: + - platform: esp32_can + id: esp32_internal_can + rx_pin: 4 + tx_pin: 5 + can_id: 4 + bit_rate: 50kbps + on_frame: + - can_id: 500 + then: + - lambda: |- + std::string b(x.begin(), x.end()); + ESP_LOGD("canid 500", "%s", b.c_str()); + - can_id: 23 + then: + - if: + condition: + lambda: "return x[0] == 0x11;" + then: + logger.log: Truth + - can_id: 0b00000000000000000000001000000 + can_id_mask: 0b11111000000000011111111000000 + use_extended_id: true + then: + - lambda: |- + auto pdo_id = can_id >> 14; + switch (pdo_id) + { + case 117: + ESP_LOGD("canbus", "exhaust_fan_duty"); + break; + case 118: + ESP_LOGD("canbus", "supply_fan_duty"); + break; + case 119: + ESP_LOGD("canbus", "supply_fan_flow"); + break; + } + +button: + - platform: template + name: Canbus Actions + on_press: + - canbus.send: "abc" + - canbus.send: [0, 1, 2] + - canbus.send: !lambda return {0, 1, 2}; diff --git a/tests/components/canbus/test.esp32-idf.yaml b/tests/components/canbus/test.esp32-idf.yaml new file mode 100644 index 0000000000..fd146cc3a3 --- /dev/null +++ b/tests/components/canbus/test.esp32-idf.yaml @@ -0,0 +1,46 @@ +canbus: + - platform: esp32_can + id: esp32_internal_can + rx_pin: 4 + tx_pin: 5 + can_id: 4 + bit_rate: 50kbps + on_frame: + - can_id: 500 + then: + - lambda: |- + std::string b(x.begin(), x.end()); + ESP_LOGD("canid 500", "%s", b.c_str()); + - can_id: 23 + then: + - if: + condition: + lambda: "return x[0] == 0x11;" + then: + logger.log: Truth + - can_id: 0b00000000000000000000001000000 + can_id_mask: 0b11111000000000011111111000000 + use_extended_id: true + then: + - lambda: |- + auto pdo_id = can_id >> 14; + switch (pdo_id) + { + case 117: + ESP_LOGD("canbus", "exhaust_fan_duty"); + break; + case 118: + ESP_LOGD("canbus", "supply_fan_duty"); + break; + case 119: + ESP_LOGD("canbus", "supply_fan_flow"); + break; + } + +button: + - platform: template + name: Canbus Actions + on_press: + - canbus.send: "abc" + - canbus.send: [0, 1, 2] + - canbus.send: !lambda return {0, 1, 2}; diff --git a/tests/components/canbus/test.esp32.yaml b/tests/components/canbus/test.esp32.yaml new file mode 100644 index 0000000000..fd146cc3a3 --- /dev/null +++ b/tests/components/canbus/test.esp32.yaml @@ -0,0 +1,46 @@ +canbus: + - platform: esp32_can + id: esp32_internal_can + rx_pin: 4 + tx_pin: 5 + can_id: 4 + bit_rate: 50kbps + on_frame: + - can_id: 500 + then: + - lambda: |- + std::string b(x.begin(), x.end()); + ESP_LOGD("canid 500", "%s", b.c_str()); + - can_id: 23 + then: + - if: + condition: + lambda: "return x[0] == 0x11;" + then: + logger.log: Truth + - can_id: 0b00000000000000000000001000000 + can_id_mask: 0b11111000000000011111111000000 + use_extended_id: true + then: + - lambda: |- + auto pdo_id = can_id >> 14; + switch (pdo_id) + { + case 117: + ESP_LOGD("canbus", "exhaust_fan_duty"); + break; + case 118: + ESP_LOGD("canbus", "supply_fan_duty"); + break; + case 119: + ESP_LOGD("canbus", "supply_fan_flow"); + break; + } + +button: + - platform: template + name: Canbus Actions + on_press: + - canbus.send: "abc" + - canbus.send: [0, 1, 2] + - canbus.send: !lambda return {0, 1, 2}; diff --git a/tests/components/cap1188/test.esp32-c3-idf.yaml b/tests/components/cap1188/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..c6d3c95942 --- /dev/null +++ b/tests/components/cap1188/test.esp32-c3-idf.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_cap1188 + scl: 5 + sda: 4 + +cap1188: + id: cap1188_component + address: 0x29 + reset_pin: 6 + touch_threshold: 0x20 + allow_multiple_touches: true diff --git a/tests/components/cap1188/test.esp32-c3.yaml b/tests/components/cap1188/test.esp32-c3.yaml new file mode 100644 index 0000000000..c6d3c95942 --- /dev/null +++ b/tests/components/cap1188/test.esp32-c3.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_cap1188 + scl: 5 + sda: 4 + +cap1188: + id: cap1188_component + address: 0x29 + reset_pin: 6 + touch_threshold: 0x20 + allow_multiple_touches: true diff --git a/tests/components/cap1188/test.esp32-idf.yaml b/tests/components/cap1188/test.esp32-idf.yaml new file mode 100644 index 0000000000..efd1d60217 --- /dev/null +++ b/tests/components/cap1188/test.esp32-idf.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_cap1188 + scl: 16 + sda: 17 + +cap1188: + id: cap1188_component + address: 0x29 + reset_pin: 15 + touch_threshold: 0x20 + allow_multiple_touches: true diff --git a/tests/components/cap1188/test.esp32.yaml b/tests/components/cap1188/test.esp32.yaml new file mode 100644 index 0000000000..efd1d60217 --- /dev/null +++ b/tests/components/cap1188/test.esp32.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_cap1188 + scl: 16 + sda: 17 + +cap1188: + id: cap1188_component + address: 0x29 + reset_pin: 15 + touch_threshold: 0x20 + allow_multiple_touches: true diff --git a/tests/components/cap1188/test.esp8266.yaml b/tests/components/cap1188/test.esp8266.yaml new file mode 100644 index 0000000000..7573d45140 --- /dev/null +++ b/tests/components/cap1188/test.esp8266.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_cap1188 + scl: 5 + sda: 4 + +cap1188: + id: cap1188_component + address: 0x29 + reset_pin: 15 + touch_threshold: 0x20 + allow_multiple_touches: true diff --git a/tests/components/cap1188/test.rp2040.yaml b/tests/components/cap1188/test.rp2040.yaml new file mode 100644 index 0000000000..c6d3c95942 --- /dev/null +++ b/tests/components/cap1188/test.rp2040.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_cap1188 + scl: 5 + sda: 4 + +cap1188: + id: cap1188_component + address: 0x29 + reset_pin: 6 + touch_threshold: 0x20 + allow_multiple_touches: true diff --git a/tests/components/captive_portal/test.esp32-c3-idf.yaml b/tests/components/captive_portal/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..25bc4a887a --- /dev/null +++ b/tests/components/captive_portal/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +wifi: + ssid: MySSID + password: password1 + +captive_portal: diff --git a/tests/components/captive_portal/test.esp32-c3.yaml b/tests/components/captive_portal/test.esp32-c3.yaml new file mode 100644 index 0000000000..25bc4a887a --- /dev/null +++ b/tests/components/captive_portal/test.esp32-c3.yaml @@ -0,0 +1,5 @@ +wifi: + ssid: MySSID + password: password1 + +captive_portal: diff --git a/tests/components/captive_portal/test.esp32-idf.yaml b/tests/components/captive_portal/test.esp32-idf.yaml new file mode 100644 index 0000000000..25bc4a887a --- /dev/null +++ b/tests/components/captive_portal/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +wifi: + ssid: MySSID + password: password1 + +captive_portal: diff --git a/tests/components/captive_portal/test.esp32.yaml b/tests/components/captive_portal/test.esp32.yaml new file mode 100644 index 0000000000..25bc4a887a --- /dev/null +++ b/tests/components/captive_portal/test.esp32.yaml @@ -0,0 +1,5 @@ +wifi: + ssid: MySSID + password: password1 + +captive_portal: diff --git a/tests/components/captive_portal/test.esp8266.yaml b/tests/components/captive_portal/test.esp8266.yaml new file mode 100644 index 0000000000..25bc4a887a --- /dev/null +++ b/tests/components/captive_portal/test.esp8266.yaml @@ -0,0 +1,5 @@ +wifi: + ssid: MySSID + password: password1 + +captive_portal: diff --git a/tests/components/ccs811/test.esp32-c3-idf.yaml b/tests/components/ccs811/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..26ec7807e4 --- /dev/null +++ b/tests/components/ccs811/test.esp32-c3-idf.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ccs811 + scl: 5 + sda: 4 + +sensor: + - platform: ccs811 + eco2: + name: CCS811 eCO2 + tvoc: + name: CCS811 TVOC + baseline: 0x4242 + update_interval: 30s diff --git a/tests/components/ccs811/test.esp32-c3.yaml b/tests/components/ccs811/test.esp32-c3.yaml new file mode 100644 index 0000000000..26ec7807e4 --- /dev/null +++ b/tests/components/ccs811/test.esp32-c3.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ccs811 + scl: 5 + sda: 4 + +sensor: + - platform: ccs811 + eco2: + name: CCS811 eCO2 + tvoc: + name: CCS811 TVOC + baseline: 0x4242 + update_interval: 30s diff --git a/tests/components/ccs811/test.esp32-idf.yaml b/tests/components/ccs811/test.esp32-idf.yaml new file mode 100644 index 0000000000..08b3a48cc7 --- /dev/null +++ b/tests/components/ccs811/test.esp32-idf.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ccs811 + scl: 16 + sda: 17 + +sensor: + - platform: ccs811 + eco2: + name: CCS811 eCO2 + tvoc: + name: CCS811 TVOC + baseline: 0x4242 + update_interval: 30s diff --git a/tests/components/ccs811/test.esp32.yaml b/tests/components/ccs811/test.esp32.yaml new file mode 100644 index 0000000000..08b3a48cc7 --- /dev/null +++ b/tests/components/ccs811/test.esp32.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ccs811 + scl: 16 + sda: 17 + +sensor: + - platform: ccs811 + eco2: + name: CCS811 eCO2 + tvoc: + name: CCS811 TVOC + baseline: 0x4242 + update_interval: 30s diff --git a/tests/components/ccs811/test.esp8266.yaml b/tests/components/ccs811/test.esp8266.yaml new file mode 100644 index 0000000000..26ec7807e4 --- /dev/null +++ b/tests/components/ccs811/test.esp8266.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ccs811 + scl: 5 + sda: 4 + +sensor: + - platform: ccs811 + eco2: + name: CCS811 eCO2 + tvoc: + name: CCS811 TVOC + baseline: 0x4242 + update_interval: 30s diff --git a/tests/components/ccs811/test.rp2040.yaml b/tests/components/ccs811/test.rp2040.yaml new file mode 100644 index 0000000000..26ec7807e4 --- /dev/null +++ b/tests/components/ccs811/test.rp2040.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ccs811 + scl: 5 + sda: 4 + +sensor: + - platform: ccs811 + eco2: + name: CCS811 eCO2 + tvoc: + name: CCS811 TVOC + baseline: 0x4242 + update_interval: 30s diff --git a/tests/components/cd74hc4067/test.esp32-c3-idf.yaml b/tests/components/cd74hc4067/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..5aa653d26c --- /dev/null +++ b/tests/components/cd74hc4067/test.esp32-c3-idf.yaml @@ -0,0 +1,18 @@ +cd74hc4067: + pin_s0: 2 + pin_s1: 3 + pin_s2: 4 + pin_s3: 5 + +sensor: + - platform: adc + id: esp_adc_sensor + pin: 0 + - platform: cd74hc4067 + id: cd74hc4067_adc_0 + number: 0 + sensor: esp_adc_sensor + - platform: cd74hc4067 + id: cd74hc4067_adc_1 + number: 1 + sensor: esp_adc_sensor diff --git a/tests/components/cd74hc4067/test.esp32-c3.yaml b/tests/components/cd74hc4067/test.esp32-c3.yaml new file mode 100644 index 0000000000..5aa653d26c --- /dev/null +++ b/tests/components/cd74hc4067/test.esp32-c3.yaml @@ -0,0 +1,18 @@ +cd74hc4067: + pin_s0: 2 + pin_s1: 3 + pin_s2: 4 + pin_s3: 5 + +sensor: + - platform: adc + id: esp_adc_sensor + pin: 0 + - platform: cd74hc4067 + id: cd74hc4067_adc_0 + number: 0 + sensor: esp_adc_sensor + - platform: cd74hc4067 + id: cd74hc4067_adc_1 + number: 1 + sensor: esp_adc_sensor diff --git a/tests/components/cd74hc4067/test.esp32-idf.yaml b/tests/components/cd74hc4067/test.esp32-idf.yaml new file mode 100644 index 0000000000..71a1238ccc --- /dev/null +++ b/tests/components/cd74hc4067/test.esp32-idf.yaml @@ -0,0 +1,18 @@ +cd74hc4067: + pin_s0: 12 + pin_s1: 13 + pin_s2: 14 + pin_s3: 15 + +sensor: + - platform: adc + id: esp_adc_sensor + pin: 39 + - platform: cd74hc4067 + id: cd74hc4067_adc_0 + number: 0 + sensor: esp_adc_sensor + - platform: cd74hc4067 + id: cd74hc4067_adc_1 + number: 1 + sensor: esp_adc_sensor diff --git a/tests/components/cd74hc4067/test.esp32.yaml b/tests/components/cd74hc4067/test.esp32.yaml new file mode 100644 index 0000000000..71a1238ccc --- /dev/null +++ b/tests/components/cd74hc4067/test.esp32.yaml @@ -0,0 +1,18 @@ +cd74hc4067: + pin_s0: 12 + pin_s1: 13 + pin_s2: 14 + pin_s3: 15 + +sensor: + - platform: adc + id: esp_adc_sensor + pin: 39 + - platform: cd74hc4067 + id: cd74hc4067_adc_0 + number: 0 + sensor: esp_adc_sensor + - platform: cd74hc4067 + id: cd74hc4067_adc_1 + number: 1 + sensor: esp_adc_sensor diff --git a/tests/components/cd74hc4067/test.esp8266.yaml b/tests/components/cd74hc4067/test.esp8266.yaml new file mode 100644 index 0000000000..8bcce5bc17 --- /dev/null +++ b/tests/components/cd74hc4067/test.esp8266.yaml @@ -0,0 +1,18 @@ +cd74hc4067: + pin_s0: 12 + pin_s1: 13 + pin_s2: 14 + pin_s3: 15 + +sensor: + - platform: adc + id: esp_adc_sensor + pin: A0 + - platform: cd74hc4067 + id: cd74hc4067_adc_0 + number: 0 + sensor: esp_adc_sensor + - platform: cd74hc4067 + id: cd74hc4067_adc_1 + number: 1 + sensor: esp_adc_sensor diff --git a/tests/components/cd74hc4067/test.rp2040.yaml b/tests/components/cd74hc4067/test.rp2040.yaml new file mode 100644 index 0000000000..75adcce796 --- /dev/null +++ b/tests/components/cd74hc4067/test.rp2040.yaml @@ -0,0 +1,18 @@ +cd74hc4067: + pin_s0: 2 + pin_s1: 3 + pin_s2: 4 + pin_s3: 5 + +sensor: + - platform: adc + id: esp_adc_sensor + pin: 26 + - platform: cd74hc4067 + id: cd74hc4067_adc_0 + number: 0 + sensor: esp_adc_sensor + - platform: cd74hc4067 + id: cd74hc4067_adc_1 + number: 1 + sensor: esp_adc_sensor diff --git a/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml b/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..e714bf0686 --- /dev/null +++ b/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: climate_ir_lg + name: LG Climate diff --git a/tests/components/climate_ir_lg/test.esp32-c3.yaml b/tests/components/climate_ir_lg/test.esp32-c3.yaml new file mode 100644 index 0000000000..e714bf0686 --- /dev/null +++ b/tests/components/climate_ir_lg/test.esp32-c3.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: climate_ir_lg + name: LG Climate diff --git a/tests/components/climate_ir_lg/test.esp32-idf.yaml b/tests/components/climate_ir_lg/test.esp32-idf.yaml new file mode 100644 index 0000000000..e714bf0686 --- /dev/null +++ b/tests/components/climate_ir_lg/test.esp32-idf.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: climate_ir_lg + name: LG Climate diff --git a/tests/components/climate_ir_lg/test.esp32.yaml b/tests/components/climate_ir_lg/test.esp32.yaml new file mode 100644 index 0000000000..e714bf0686 --- /dev/null +++ b/tests/components/climate_ir_lg/test.esp32.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: climate_ir_lg + name: LG Climate diff --git a/tests/components/climate_ir_lg/test.esp8266.yaml b/tests/components/climate_ir_lg/test.esp8266.yaml new file mode 100644 index 0000000000..7482bf0580 --- /dev/null +++ b/tests/components/climate_ir_lg/test.esp8266.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 5 + carrier_duty_percent: 50% + +climate: + - platform: climate_ir_lg + name: LG Climate diff --git a/tests/components/color/test.esp32-c3-idf.yaml b/tests/components/color/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..7aa308bb63 --- /dev/null +++ b/tests/components/color/test.esp32-c3-idf.yaml @@ -0,0 +1,11 @@ +color: + - id: kbx_red + red: 100% + green_int: 123 + blue: 2% + - id: kbx_blue + red: 0% + green: 1% + blue: 100% + - id: kbx_green + hex: "3DEC55" diff --git a/tests/components/color/test.esp32-c3.yaml b/tests/components/color/test.esp32-c3.yaml new file mode 100644 index 0000000000..7aa308bb63 --- /dev/null +++ b/tests/components/color/test.esp32-c3.yaml @@ -0,0 +1,11 @@ +color: + - id: kbx_red + red: 100% + green_int: 123 + blue: 2% + - id: kbx_blue + red: 0% + green: 1% + blue: 100% + - id: kbx_green + hex: "3DEC55" diff --git a/tests/components/color/test.esp32-idf.yaml b/tests/components/color/test.esp32-idf.yaml new file mode 100644 index 0000000000..7aa308bb63 --- /dev/null +++ b/tests/components/color/test.esp32-idf.yaml @@ -0,0 +1,11 @@ +color: + - id: kbx_red + red: 100% + green_int: 123 + blue: 2% + - id: kbx_blue + red: 0% + green: 1% + blue: 100% + - id: kbx_green + hex: "3DEC55" diff --git a/tests/components/color/test.esp32.yaml b/tests/components/color/test.esp32.yaml new file mode 100644 index 0000000000..7aa308bb63 --- /dev/null +++ b/tests/components/color/test.esp32.yaml @@ -0,0 +1,11 @@ +color: + - id: kbx_red + red: 100% + green_int: 123 + blue: 2% + - id: kbx_blue + red: 0% + green: 1% + blue: 100% + - id: kbx_green + hex: "3DEC55" diff --git a/tests/components/color/test.esp8266.yaml b/tests/components/color/test.esp8266.yaml new file mode 100644 index 0000000000..7aa308bb63 --- /dev/null +++ b/tests/components/color/test.esp8266.yaml @@ -0,0 +1,11 @@ +color: + - id: kbx_red + red: 100% + green_int: 123 + blue: 2% + - id: kbx_blue + red: 0% + green: 1% + blue: 100% + - id: kbx_green + hex: "3DEC55" diff --git a/tests/components/color/test.rp2040.yaml b/tests/components/color/test.rp2040.yaml new file mode 100644 index 0000000000..7aa308bb63 --- /dev/null +++ b/tests/components/color/test.rp2040.yaml @@ -0,0 +1,11 @@ +color: + - id: kbx_red + red: 100% + green_int: 123 + blue: 2% + - id: kbx_blue + red: 0% + green: 1% + blue: 100% + - id: kbx_green + hex: "3DEC55" diff --git a/tests/components/color_temperature/test.esp32-c3-idf.yaml b/tests/components/color_temperature/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..8d3faa54ee --- /dev/null +++ b/tests/components/color_temperature/test.esp32-c3-idf.yaml @@ -0,0 +1,15 @@ +output: + - platform: ledc + id: light_output_1 + pin: 1 + - platform: ledc + id: light_output_2 + pin: 2 + +light: + - platform: color_temperature + name: Lights + color_temperature: light_output_1 + brightness: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds diff --git a/tests/components/color_temperature/test.esp32-c3.yaml b/tests/components/color_temperature/test.esp32-c3.yaml new file mode 100644 index 0000000000..8d3faa54ee --- /dev/null +++ b/tests/components/color_temperature/test.esp32-c3.yaml @@ -0,0 +1,15 @@ +output: + - platform: ledc + id: light_output_1 + pin: 1 + - platform: ledc + id: light_output_2 + pin: 2 + +light: + - platform: color_temperature + name: Lights + color_temperature: light_output_1 + brightness: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds diff --git a/tests/components/color_temperature/test.esp32-idf.yaml b/tests/components/color_temperature/test.esp32-idf.yaml new file mode 100644 index 0000000000..608907d2fc --- /dev/null +++ b/tests/components/color_temperature/test.esp32-idf.yaml @@ -0,0 +1,15 @@ +output: + - platform: ledc + id: light_output_1 + pin: 12 + - platform: ledc + id: light_output_2 + pin: 13 + +light: + - platform: color_temperature + name: Lights + color_temperature: light_output_1 + brightness: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds diff --git a/tests/components/color_temperature/test.esp32.yaml b/tests/components/color_temperature/test.esp32.yaml new file mode 100644 index 0000000000..608907d2fc --- /dev/null +++ b/tests/components/color_temperature/test.esp32.yaml @@ -0,0 +1,15 @@ +output: + - platform: ledc + id: light_output_1 + pin: 12 + - platform: ledc + id: light_output_2 + pin: 13 + +light: + - platform: color_temperature + name: Lights + color_temperature: light_output_1 + brightness: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds diff --git a/tests/components/color_temperature/test.esp8266.yaml b/tests/components/color_temperature/test.esp8266.yaml new file mode 100644 index 0000000000..ed0bfb6aa4 --- /dev/null +++ b/tests/components/color_temperature/test.esp8266.yaml @@ -0,0 +1,15 @@ +output: + - platform: esp8266_pwm + id: light_output_1 + pin: 12 + - platform: esp8266_pwm + id: light_output_2 + pin: 13 + +light: + - platform: color_temperature + name: Lights + color_temperature: light_output_1 + brightness: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds diff --git a/tests/components/color_temperature/test.rp2040.yaml b/tests/components/color_temperature/test.rp2040.yaml new file mode 100644 index 0000000000..887ad1c857 --- /dev/null +++ b/tests/components/color_temperature/test.rp2040.yaml @@ -0,0 +1,15 @@ +output: + - platform: rp2040_pwm + id: light_output_1 + pin: 12 + - platform: rp2040_pwm + id: light_output_2 + pin: 13 + +light: + - platform: color_temperature + name: Lights + color_temperature: light_output_1 + brightness: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds diff --git a/tests/components/combination/test.esp32-c3-idf.yaml b/tests/components/combination/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..62246190af --- /dev/null +++ b/tests/components/combination/test.esp32-c3-idf.yaml @@ -0,0 +1,76 @@ +sensor: + - platform: template + id: template_temperature1 + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature2 + lambda: |- + if (millis() > 20000) { + return 0.8; + } else { + return 0.0; + } + - platform: combination + type: kalman + name: Kalman-filtered temperature + process_std_dev: 0.00139 + sources: + - source: template_temperature1 + error: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + error: 1.5 + - platform: combination + type: linear + name: Linearly combined temperatures + sources: + - source: template_temperature1 + coeffecient: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + coeffecient: 1.5 + - platform: combination + type: max + name: Max of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: mean + name: Mean of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: median + name: Median of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: min + name: Min of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: most_recently_updated + name: Most recently updated of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: range + name: Range of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: sum + name: Sum of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 diff --git a/tests/components/combination/test.esp32-c3.yaml b/tests/components/combination/test.esp32-c3.yaml new file mode 100644 index 0000000000..62246190af --- /dev/null +++ b/tests/components/combination/test.esp32-c3.yaml @@ -0,0 +1,76 @@ +sensor: + - platform: template + id: template_temperature1 + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature2 + lambda: |- + if (millis() > 20000) { + return 0.8; + } else { + return 0.0; + } + - platform: combination + type: kalman + name: Kalman-filtered temperature + process_std_dev: 0.00139 + sources: + - source: template_temperature1 + error: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + error: 1.5 + - platform: combination + type: linear + name: Linearly combined temperatures + sources: + - source: template_temperature1 + coeffecient: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + coeffecient: 1.5 + - platform: combination + type: max + name: Max of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: mean + name: Mean of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: median + name: Median of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: min + name: Min of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: most_recently_updated + name: Most recently updated of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: range + name: Range of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: sum + name: Sum of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 diff --git a/tests/components/combination/test.esp32-idf.yaml b/tests/components/combination/test.esp32-idf.yaml new file mode 100644 index 0000000000..62246190af --- /dev/null +++ b/tests/components/combination/test.esp32-idf.yaml @@ -0,0 +1,76 @@ +sensor: + - platform: template + id: template_temperature1 + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature2 + lambda: |- + if (millis() > 20000) { + return 0.8; + } else { + return 0.0; + } + - platform: combination + type: kalman + name: Kalman-filtered temperature + process_std_dev: 0.00139 + sources: + - source: template_temperature1 + error: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + error: 1.5 + - platform: combination + type: linear + name: Linearly combined temperatures + sources: + - source: template_temperature1 + coeffecient: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + coeffecient: 1.5 + - platform: combination + type: max + name: Max of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: mean + name: Mean of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: median + name: Median of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: min + name: Min of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: most_recently_updated + name: Most recently updated of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: range + name: Range of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: sum + name: Sum of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 diff --git a/tests/components/combination/test.esp32.yaml b/tests/components/combination/test.esp32.yaml new file mode 100644 index 0000000000..62246190af --- /dev/null +++ b/tests/components/combination/test.esp32.yaml @@ -0,0 +1,76 @@ +sensor: + - platform: template + id: template_temperature1 + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature2 + lambda: |- + if (millis() > 20000) { + return 0.8; + } else { + return 0.0; + } + - platform: combination + type: kalman + name: Kalman-filtered temperature + process_std_dev: 0.00139 + sources: + - source: template_temperature1 + error: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + error: 1.5 + - platform: combination + type: linear + name: Linearly combined temperatures + sources: + - source: template_temperature1 + coeffecient: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + coeffecient: 1.5 + - platform: combination + type: max + name: Max of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: mean + name: Mean of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: median + name: Median of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: min + name: Min of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: most_recently_updated + name: Most recently updated of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: range + name: Range of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: sum + name: Sum of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 diff --git a/tests/components/combination/test.esp8266.yaml b/tests/components/combination/test.esp8266.yaml new file mode 100644 index 0000000000..62246190af --- /dev/null +++ b/tests/components/combination/test.esp8266.yaml @@ -0,0 +1,76 @@ +sensor: + - platform: template + id: template_temperature1 + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature2 + lambda: |- + if (millis() > 20000) { + return 0.8; + } else { + return 0.0; + } + - platform: combination + type: kalman + name: Kalman-filtered temperature + process_std_dev: 0.00139 + sources: + - source: template_temperature1 + error: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + error: 1.5 + - platform: combination + type: linear + name: Linearly combined temperatures + sources: + - source: template_temperature1 + coeffecient: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + coeffecient: 1.5 + - platform: combination + type: max + name: Max of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: mean + name: Mean of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: median + name: Median of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: min + name: Min of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: most_recently_updated + name: Most recently updated of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: range + name: Range of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: sum + name: Sum of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 diff --git a/tests/components/combination/test.rp2040.yaml b/tests/components/combination/test.rp2040.yaml new file mode 100644 index 0000000000..62246190af --- /dev/null +++ b/tests/components/combination/test.rp2040.yaml @@ -0,0 +1,76 @@ +sensor: + - platform: template + id: template_temperature1 + lambda: |- + if (millis() > 10000) { + return 0.6; + } else { + return 0.0; + } + - platform: template + id: template_temperature2 + lambda: |- + if (millis() > 20000) { + return 0.8; + } else { + return 0.0; + } + - platform: combination + type: kalman + name: Kalman-filtered temperature + process_std_dev: 0.00139 + sources: + - source: template_temperature1 + error: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + error: 1.5 + - platform: combination + type: linear + name: Linearly combined temperatures + sources: + - source: template_temperature1 + coeffecient: !lambda "return 0.4 + std::abs(x - 25) * 0.023;" + - source: template_temperature2 + coeffecient: 1.5 + - platform: combination + type: max + name: Max of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: mean + name: Mean of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: median + name: Median of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: min + name: Min of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: most_recently_updated + name: Most recently updated of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: range + name: Range of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 + - platform: combination + type: sum + name: Sum of combined temperatures + sources: + - source: template_temperature1 + - source: template_temperature2 diff --git a/tests/components/coolix/test.esp32-c3-idf.yaml b/tests/components/coolix/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..0f9518d2cf --- /dev/null +++ b/tests/components/coolix/test.esp32-c3-idf.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: coolix + name: Coolix Climate diff --git a/tests/components/coolix/test.esp32-c3.yaml b/tests/components/coolix/test.esp32-c3.yaml new file mode 100644 index 0000000000..0f9518d2cf --- /dev/null +++ b/tests/components/coolix/test.esp32-c3.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: coolix + name: Coolix Climate diff --git a/tests/components/coolix/test.esp32-idf.yaml b/tests/components/coolix/test.esp32-idf.yaml new file mode 100644 index 0000000000..0f9518d2cf --- /dev/null +++ b/tests/components/coolix/test.esp32-idf.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: coolix + name: Coolix Climate diff --git a/tests/components/coolix/test.esp32.yaml b/tests/components/coolix/test.esp32.yaml new file mode 100644 index 0000000000..0f9518d2cf --- /dev/null +++ b/tests/components/coolix/test.esp32.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 2 + carrier_duty_percent: 50% + +climate: + - platform: coolix + name: Coolix Climate diff --git a/tests/components/coolix/test.esp8266.yaml b/tests/components/coolix/test.esp8266.yaml new file mode 100644 index 0000000000..61de8c7558 --- /dev/null +++ b/tests/components/coolix/test.esp8266.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: 5 + carrier_duty_percent: 50% + +climate: + - platform: coolix + name: Coolix Climate diff --git a/tests/components/copy/test.esp32-c3-idf.yaml b/tests/components/copy/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..554638f462 --- /dev/null +++ b/tests/components/copy/test.esp32-c3-idf.yaml @@ -0,0 +1,23 @@ +output: + - platform: ledc + id: fan_output_1 + pin: 2 + +fan: + - platform: speed + id: fan_speed + output: fan_output_1 + - platform: copy + source_id: fan_speed + name: Fan Speed Copy + +select: + - platform: template + id: test_select + options: + - one + - two + optimistic: true + - platform: copy + source_id: test_select + name: Test Select Copy diff --git a/tests/components/copy/test.esp32-c3.yaml b/tests/components/copy/test.esp32-c3.yaml new file mode 100644 index 0000000000..554638f462 --- /dev/null +++ b/tests/components/copy/test.esp32-c3.yaml @@ -0,0 +1,23 @@ +output: + - platform: ledc + id: fan_output_1 + pin: 2 + +fan: + - platform: speed + id: fan_speed + output: fan_output_1 + - platform: copy + source_id: fan_speed + name: Fan Speed Copy + +select: + - platform: template + id: test_select + options: + - one + - two + optimistic: true + - platform: copy + source_id: test_select + name: Test Select Copy diff --git a/tests/components/copy/test.esp32-idf.yaml b/tests/components/copy/test.esp32-idf.yaml new file mode 100644 index 0000000000..806dbfe9f3 --- /dev/null +++ b/tests/components/copy/test.esp32-idf.yaml @@ -0,0 +1,23 @@ +output: + - platform: ledc + id: fan_output_1 + pin: 12 + +fan: + - platform: speed + id: fan_speed + output: fan_output_1 + - platform: copy + source_id: fan_speed + name: Fan Speed Copy + +select: + - platform: template + id: test_select + options: + - one + - two + optimistic: true + - platform: copy + source_id: test_select + name: Test Select Copy diff --git a/tests/components/copy/test.esp32.yaml b/tests/components/copy/test.esp32.yaml new file mode 100644 index 0000000000..806dbfe9f3 --- /dev/null +++ b/tests/components/copy/test.esp32.yaml @@ -0,0 +1,23 @@ +output: + - platform: ledc + id: fan_output_1 + pin: 12 + +fan: + - platform: speed + id: fan_speed + output: fan_output_1 + - platform: copy + source_id: fan_speed + name: Fan Speed Copy + +select: + - platform: template + id: test_select + options: + - one + - two + optimistic: true + - platform: copy + source_id: test_select + name: Test Select Copy diff --git a/tests/components/copy/test.esp8266.yaml b/tests/components/copy/test.esp8266.yaml new file mode 100644 index 0000000000..1521e5f279 --- /dev/null +++ b/tests/components/copy/test.esp8266.yaml @@ -0,0 +1,23 @@ +output: + - platform: esp8266_pwm + id: fan_output_1 + pin: 12 + +fan: + - platform: speed + id: fan_speed + output: fan_output_1 + - platform: copy + source_id: fan_speed + name: Fan Speed Copy + +select: + - platform: template + id: test_select + options: + - one + - two + optimistic: true + - platform: copy + source_id: test_select + name: Test Select Copy diff --git a/tests/components/copy/test.rp2040.yaml b/tests/components/copy/test.rp2040.yaml new file mode 100644 index 0000000000..42e5eb8000 --- /dev/null +++ b/tests/components/copy/test.rp2040.yaml @@ -0,0 +1,23 @@ +output: + - platform: rp2040_pwm + id: fan_output_1 + pin: 12 + +fan: + - platform: speed + id: fan_speed + output: fan_output_1 + - platform: copy + source_id: fan_speed + name: Fan Speed Copy + +select: + - platform: template + id: test_select + options: + - one + - two + optimistic: true + - platform: copy + source_id: test_select + name: Test Select Copy diff --git a/tests/components/cs5460a/test.esp32-c3-idf.yaml b/tests/components/cs5460a/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..4ce21783a3 --- /dev/null +++ b/tests/components/cs5460a/test.esp32-c3-idf.yaml @@ -0,0 +1,27 @@ +spi: + - id: spi_cs5460a + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: cs5460a + id: cs5460a1 + cs_pin: 8 + current: + name: Socket current + voltage: + name: Mains voltage + power: + name: Socket power + on_value: + then: + cs5460a.restart: cs5460a1 + samples: 1600 + pga_gain: 10X + current_gain: 0.01 + voltage_gain: 0.000573 + current_hpf: true + voltage_hpf: true + phase_offset: 20 + pulse_energy: 0.01 kWh diff --git a/tests/components/cs5460a/test.esp32-c3.yaml b/tests/components/cs5460a/test.esp32-c3.yaml new file mode 100644 index 0000000000..4ce21783a3 --- /dev/null +++ b/tests/components/cs5460a/test.esp32-c3.yaml @@ -0,0 +1,27 @@ +spi: + - id: spi_cs5460a + clk_pin: 6 + mosi_pin: 7 + miso_pin: 5 + +sensor: + - platform: cs5460a + id: cs5460a1 + cs_pin: 8 + current: + name: Socket current + voltage: + name: Mains voltage + power: + name: Socket power + on_value: + then: + cs5460a.restart: cs5460a1 + samples: 1600 + pga_gain: 10X + current_gain: 0.01 + voltage_gain: 0.000573 + current_hpf: true + voltage_hpf: true + phase_offset: 20 + pulse_energy: 0.01 kWh diff --git a/tests/components/cs5460a/test.esp32-idf.yaml b/tests/components/cs5460a/test.esp32-idf.yaml new file mode 100644 index 0000000000..e7eb1cbd73 --- /dev/null +++ b/tests/components/cs5460a/test.esp32-idf.yaml @@ -0,0 +1,27 @@ +spi: + - id: spi_cs5460a + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: cs5460a + id: cs5460a1 + cs_pin: 12 + current: + name: Socket current + voltage: + name: Mains voltage + power: + name: Socket power + on_value: + then: + cs5460a.restart: cs5460a1 + samples: 1600 + pga_gain: 10X + current_gain: 0.01 + voltage_gain: 0.000573 + current_hpf: true + voltage_hpf: true + phase_offset: 20 + pulse_energy: 0.01 kWh diff --git a/tests/components/cs5460a/test.esp32.yaml b/tests/components/cs5460a/test.esp32.yaml new file mode 100644 index 0000000000..e7eb1cbd73 --- /dev/null +++ b/tests/components/cs5460a/test.esp32.yaml @@ -0,0 +1,27 @@ +spi: + - id: spi_cs5460a + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +sensor: + - platform: cs5460a + id: cs5460a1 + cs_pin: 12 + current: + name: Socket current + voltage: + name: Mains voltage + power: + name: Socket power + on_value: + then: + cs5460a.restart: cs5460a1 + samples: 1600 + pga_gain: 10X + current_gain: 0.01 + voltage_gain: 0.000573 + current_hpf: true + voltage_hpf: true + phase_offset: 20 + pulse_energy: 0.01 kWh diff --git a/tests/components/cs5460a/test.esp8266.yaml b/tests/components/cs5460a/test.esp8266.yaml new file mode 100644 index 0000000000..c5a458d0ec --- /dev/null +++ b/tests/components/cs5460a/test.esp8266.yaml @@ -0,0 +1,27 @@ +spi: + - id: spi_cs5460a + clk_pin: 14 + mosi_pin: 13 + miso_pin: 12 + +sensor: + - platform: cs5460a + id: cs5460a1 + cs_pin: 15 + current: + name: Socket current + voltage: + name: Mains voltage + power: + name: Socket power + on_value: + then: + cs5460a.restart: cs5460a1 + samples: 1600 + pga_gain: 10X + current_gain: 0.01 + voltage_gain: 0.000573 + current_hpf: true + voltage_hpf: true + phase_offset: 20 + pulse_energy: 0.01 kWh diff --git a/tests/components/cs5460a/test.rp2040.yaml b/tests/components/cs5460a/test.rp2040.yaml new file mode 100644 index 0000000000..f3daf7d72d --- /dev/null +++ b/tests/components/cs5460a/test.rp2040.yaml @@ -0,0 +1,27 @@ +spi: + - id: spi_cs5460a + clk_pin: 2 + mosi_pin: 3 + miso_pin: 4 + +sensor: + - platform: cs5460a + id: cs5460a1 + cs_pin: 6 + current: + name: Socket current + voltage: + name: Mains voltage + power: + name: Socket power + on_value: + then: + cs5460a.restart: cs5460a1 + samples: 1600 + pga_gain: 10X + current_gain: 0.01 + voltage_gain: 0.000573 + current_hpf: true + voltage_hpf: true + phase_offset: 20 + pulse_energy: 0.01 kWh diff --git a/tests/components/cse7761/test.esp32-c3-idf.yaml b/tests/components/cse7761/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..581db24fd5 --- /dev/null +++ b/tests/components/cse7761/test.esp32-c3-idf.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_cse7761 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 38400 + +sensor: + - platform: cse7761 + voltage: + name: CSE7761 Voltage + current_1: + name: CSE7761 Current 1 + current_2: + name: CSE7761 Current 2 + active_power_1: + name: CSE7761 Active Power 1 + active_power_2: + name: CSE7761 Active Power 2 diff --git a/tests/components/cse7761/test.esp32-c3.yaml b/tests/components/cse7761/test.esp32-c3.yaml new file mode 100644 index 0000000000..581db24fd5 --- /dev/null +++ b/tests/components/cse7761/test.esp32-c3.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_cse7761 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 38400 + +sensor: + - platform: cse7761 + voltage: + name: CSE7761 Voltage + current_1: + name: CSE7761 Current 1 + current_2: + name: CSE7761 Current 2 + active_power_1: + name: CSE7761 Active Power 1 + active_power_2: + name: CSE7761 Active Power 2 diff --git a/tests/components/cse7761/test.esp32-idf.yaml b/tests/components/cse7761/test.esp32-idf.yaml new file mode 100644 index 0000000000..4174e9a92e --- /dev/null +++ b/tests/components/cse7761/test.esp32-idf.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_cse7761 + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 38400 + +sensor: + - platform: cse7761 + voltage: + name: CSE7761 Voltage + current_1: + name: CSE7761 Current 1 + current_2: + name: CSE7761 Current 2 + active_power_1: + name: CSE7761 Active Power 1 + active_power_2: + name: CSE7761 Active Power 2 diff --git a/tests/components/cse7761/test.esp32.yaml b/tests/components/cse7761/test.esp32.yaml new file mode 100644 index 0000000000..4174e9a92e --- /dev/null +++ b/tests/components/cse7761/test.esp32.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_cse7761 + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 38400 + +sensor: + - platform: cse7761 + voltage: + name: CSE7761 Voltage + current_1: + name: CSE7761 Current 1 + current_2: + name: CSE7761 Current 2 + active_power_1: + name: CSE7761 Active Power 1 + active_power_2: + name: CSE7761 Active Power 2 diff --git a/tests/components/cse7761/test.esp8266.yaml b/tests/components/cse7761/test.esp8266.yaml new file mode 100644 index 0000000000..581db24fd5 --- /dev/null +++ b/tests/components/cse7761/test.esp8266.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_cse7761 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 38400 + +sensor: + - platform: cse7761 + voltage: + name: CSE7761 Voltage + current_1: + name: CSE7761 Current 1 + current_2: + name: CSE7761 Current 2 + active_power_1: + name: CSE7761 Active Power 1 + active_power_2: + name: CSE7761 Active Power 2 diff --git a/tests/components/cse7761/test.rp2040.yaml b/tests/components/cse7761/test.rp2040.yaml new file mode 100644 index 0000000000..581db24fd5 --- /dev/null +++ b/tests/components/cse7761/test.rp2040.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_cse7761 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 38400 + +sensor: + - platform: cse7761 + voltage: + name: CSE7761 Voltage + current_1: + name: CSE7761 Current 1 + current_2: + name: CSE7761 Current 2 + active_power_1: + name: CSE7761 Active Power 1 + active_power_2: + name: CSE7761 Active Power 2 diff --git a/tests/components/cse7766/test.esp32-c3-idf.yaml b/tests/components/cse7766/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..432cc0a80e --- /dev/null +++ b/tests/components/cse7766/test.esp32-c3-idf.yaml @@ -0,0 +1,16 @@ +uart: + - id: uart_cse7766 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 4800 + +sensor: + - platform: cse7766 + voltage: + name: CSE7766 Voltage + current: + name: CSE7766 Current + power: + name: CSE776 Power diff --git a/tests/components/cse7766/test.esp32-c3.yaml b/tests/components/cse7766/test.esp32-c3.yaml new file mode 100644 index 0000000000..432cc0a80e --- /dev/null +++ b/tests/components/cse7766/test.esp32-c3.yaml @@ -0,0 +1,16 @@ +uart: + - id: uart_cse7766 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 4800 + +sensor: + - platform: cse7766 + voltage: + name: CSE7766 Voltage + current: + name: CSE7766 Current + power: + name: CSE776 Power diff --git a/tests/components/cse7766/test.esp32-idf.yaml b/tests/components/cse7766/test.esp32-idf.yaml new file mode 100644 index 0000000000..f94cd0f7d8 --- /dev/null +++ b/tests/components/cse7766/test.esp32-idf.yaml @@ -0,0 +1,16 @@ +uart: + - id: uart_cse7766 + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 4800 + +sensor: + - platform: cse7766 + voltage: + name: CSE7766 Voltage + current: + name: CSE7766 Current + power: + name: CSE776 Power diff --git a/tests/components/cse7766/test.esp32.yaml b/tests/components/cse7766/test.esp32.yaml new file mode 100644 index 0000000000..f94cd0f7d8 --- /dev/null +++ b/tests/components/cse7766/test.esp32.yaml @@ -0,0 +1,16 @@ +uart: + - id: uart_cse7766 + tx_pin: + number: 17 + rx_pin: + number: 16 + baud_rate: 4800 + +sensor: + - platform: cse7766 + voltage: + name: CSE7766 Voltage + current: + name: CSE7766 Current + power: + name: CSE776 Power diff --git a/tests/components/cse7766/test.esp8266.yaml b/tests/components/cse7766/test.esp8266.yaml new file mode 100644 index 0000000000..432cc0a80e --- /dev/null +++ b/tests/components/cse7766/test.esp8266.yaml @@ -0,0 +1,16 @@ +uart: + - id: uart_cse7766 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 4800 + +sensor: + - platform: cse7766 + voltage: + name: CSE7766 Voltage + current: + name: CSE7766 Current + power: + name: CSE776 Power diff --git a/tests/components/cse7766/test.rp2040.yaml b/tests/components/cse7766/test.rp2040.yaml new file mode 100644 index 0000000000..432cc0a80e --- /dev/null +++ b/tests/components/cse7766/test.rp2040.yaml @@ -0,0 +1,16 @@ +uart: + - id: uart_cse7766 + tx_pin: + number: 4 + rx_pin: + number: 5 + baud_rate: 4800 + +sensor: + - platform: cse7766 + voltage: + name: CSE7766 Voltage + current: + name: CSE7766 Current + power: + name: CSE776 Power diff --git a/tests/components/ct_clamp/test.esp32-c3-idf.yaml b/tests/components/ct_clamp/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..e25acc95e1 --- /dev/null +++ b/tests/components/ct_clamp/test.esp32-c3-idf.yaml @@ -0,0 +1,9 @@ +sensor: + - platform: adc + id: esp_adc_sensor + pin: 0 + - platform: ct_clamp + sensor: esp_adc_sensor + name: CT Clamp + sample_duration: 500ms + update_interval: 5s diff --git a/tests/components/ct_clamp/test.esp32-c3.yaml b/tests/components/ct_clamp/test.esp32-c3.yaml new file mode 100644 index 0000000000..e25acc95e1 --- /dev/null +++ b/tests/components/ct_clamp/test.esp32-c3.yaml @@ -0,0 +1,9 @@ +sensor: + - platform: adc + id: esp_adc_sensor + pin: 0 + - platform: ct_clamp + sensor: esp_adc_sensor + name: CT Clamp + sample_duration: 500ms + update_interval: 5s diff --git a/tests/components/ct_clamp/test.esp32-idf.yaml b/tests/components/ct_clamp/test.esp32-idf.yaml new file mode 100644 index 0000000000..1ea964fa96 --- /dev/null +++ b/tests/components/ct_clamp/test.esp32-idf.yaml @@ -0,0 +1,9 @@ +sensor: + - platform: adc + id: esp_adc_sensor + pin: 39 + - platform: ct_clamp + sensor: esp_adc_sensor + name: CT Clamp + sample_duration: 500ms + update_interval: 5s diff --git a/tests/components/ct_clamp/test.esp32.yaml b/tests/components/ct_clamp/test.esp32.yaml new file mode 100644 index 0000000000..1ea964fa96 --- /dev/null +++ b/tests/components/ct_clamp/test.esp32.yaml @@ -0,0 +1,9 @@ +sensor: + - platform: adc + id: esp_adc_sensor + pin: 39 + - platform: ct_clamp + sensor: esp_adc_sensor + name: CT Clamp + sample_duration: 500ms + update_interval: 5s diff --git a/tests/components/ct_clamp/test.esp8266.yaml b/tests/components/ct_clamp/test.esp8266.yaml new file mode 100644 index 0000000000..9c7126480d --- /dev/null +++ b/tests/components/ct_clamp/test.esp8266.yaml @@ -0,0 +1,9 @@ +sensor: + - platform: adc + id: esp_adc_sensor + pin: A0 + - platform: ct_clamp + sensor: esp_adc_sensor + name: CT Clamp + sample_duration: 500ms + update_interval: 5s diff --git a/tests/components/ct_clamp/test.rp2040.yaml b/tests/components/ct_clamp/test.rp2040.yaml new file mode 100644 index 0000000000..47077308aa --- /dev/null +++ b/tests/components/ct_clamp/test.rp2040.yaml @@ -0,0 +1,9 @@ +sensor: + - platform: adc + id: esp_adc_sensor + pin: 26 + - platform: ct_clamp + sensor: esp_adc_sensor + name: CT Clamp + sample_duration: 500ms + update_interval: 5s diff --git a/tests/components/current_based/test.esp32-c3-idf.yaml b/tests/components/current_based/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..69ab8830c3 --- /dev/null +++ b/tests/components/current_based/test.esp32-c3-idf.yaml @@ -0,0 +1,68 @@ +i2c: + - id: i2c_ade7953 + scl: 5 + sda: 4 + +sensor: + - platform: ade7953_i2c + irq_pin: 6 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s + +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +cover: + - platform: current_based + name: Current Based Cover + id: current_based_cover + open_sensor: ade7953_current_a + open_moving_current_threshold: 0.5 + open_obstacle_current_threshold: 0.8 + open_duration: 12s + open_action: + - switch.turn_on: template_switch1 + close_sensor: ade7953_current_b + close_moving_current_threshold: 0.5 + close_obstacle_current_threshold: 0.8 + close_duration: 10s + close_action: + - switch.turn_on: template_switch2 + stop_action: + - switch.turn_off: template_switch1 + - switch.turn_off: template_switch2 + obstacle_rollback: 30% + start_sensing_delay: 0.8s + malfunction_detection: true + malfunction_action: + then: + - logger.log: Malfunction Detected diff --git a/tests/components/current_based/test.esp32-c3.yaml b/tests/components/current_based/test.esp32-c3.yaml new file mode 100644 index 0000000000..69ab8830c3 --- /dev/null +++ b/tests/components/current_based/test.esp32-c3.yaml @@ -0,0 +1,68 @@ +i2c: + - id: i2c_ade7953 + scl: 5 + sda: 4 + +sensor: + - platform: ade7953_i2c + irq_pin: 6 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s + +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +cover: + - platform: current_based + name: Current Based Cover + id: current_based_cover + open_sensor: ade7953_current_a + open_moving_current_threshold: 0.5 + open_obstacle_current_threshold: 0.8 + open_duration: 12s + open_action: + - switch.turn_on: template_switch1 + close_sensor: ade7953_current_b + close_moving_current_threshold: 0.5 + close_obstacle_current_threshold: 0.8 + close_duration: 10s + close_action: + - switch.turn_on: template_switch2 + stop_action: + - switch.turn_off: template_switch1 + - switch.turn_off: template_switch2 + obstacle_rollback: 30% + start_sensing_delay: 0.8s + malfunction_detection: true + malfunction_action: + then: + - logger.log: Malfunction Detected diff --git a/tests/components/current_based/test.esp32-idf.yaml b/tests/components/current_based/test.esp32-idf.yaml new file mode 100644 index 0000000000..90781120bc --- /dev/null +++ b/tests/components/current_based/test.esp32-idf.yaml @@ -0,0 +1,68 @@ +i2c: + - id: i2c_ade7953 + scl: 16 + sda: 17 + +sensor: + - platform: ade7953_i2c + irq_pin: 15 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s + +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +cover: + - platform: current_based + name: Current Based Cover + id: current_based_cover + open_sensor: ade7953_current_a + open_moving_current_threshold: 0.5 + open_obstacle_current_threshold: 0.8 + open_duration: 12s + open_action: + - switch.turn_on: template_switch1 + close_sensor: ade7953_current_b + close_moving_current_threshold: 0.5 + close_obstacle_current_threshold: 0.8 + close_duration: 10s + close_action: + - switch.turn_on: template_switch2 + stop_action: + - switch.turn_off: template_switch1 + - switch.turn_off: template_switch2 + obstacle_rollback: 30% + start_sensing_delay: 0.8s + malfunction_detection: true + malfunction_action: + then: + - logger.log: Malfunction Detected diff --git a/tests/components/current_based/test.esp32.yaml b/tests/components/current_based/test.esp32.yaml new file mode 100644 index 0000000000..90781120bc --- /dev/null +++ b/tests/components/current_based/test.esp32.yaml @@ -0,0 +1,68 @@ +i2c: + - id: i2c_ade7953 + scl: 16 + sda: 17 + +sensor: + - platform: ade7953_i2c + irq_pin: 15 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s + +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +cover: + - platform: current_based + name: Current Based Cover + id: current_based_cover + open_sensor: ade7953_current_a + open_moving_current_threshold: 0.5 + open_obstacle_current_threshold: 0.8 + open_duration: 12s + open_action: + - switch.turn_on: template_switch1 + close_sensor: ade7953_current_b + close_moving_current_threshold: 0.5 + close_obstacle_current_threshold: 0.8 + close_duration: 10s + close_action: + - switch.turn_on: template_switch2 + stop_action: + - switch.turn_off: template_switch1 + - switch.turn_off: template_switch2 + obstacle_rollback: 30% + start_sensing_delay: 0.8s + malfunction_detection: true + malfunction_action: + then: + - logger.log: Malfunction Detected diff --git a/tests/components/current_based/test.esp8266.yaml b/tests/components/current_based/test.esp8266.yaml new file mode 100644 index 0000000000..42d6d4676b --- /dev/null +++ b/tests/components/current_based/test.esp8266.yaml @@ -0,0 +1,68 @@ +i2c: + - id: i2c_ade7953 + scl: 5 + sda: 4 + +sensor: + - platform: ade7953_i2c + irq_pin: 15 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s + +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +cover: + - platform: current_based + name: Current Based Cover + id: current_based_cover + open_sensor: ade7953_current_a + open_moving_current_threshold: 0.5 + open_obstacle_current_threshold: 0.8 + open_duration: 12s + open_action: + - switch.turn_on: template_switch1 + close_sensor: ade7953_current_b + close_moving_current_threshold: 0.5 + close_obstacle_current_threshold: 0.8 + close_duration: 10s + close_action: + - switch.turn_on: template_switch2 + stop_action: + - switch.turn_off: template_switch1 + - switch.turn_off: template_switch2 + obstacle_rollback: 30% + start_sensing_delay: 0.8s + malfunction_detection: true + malfunction_action: + then: + - logger.log: Malfunction Detected diff --git a/tests/components/current_based/test.rp2040.yaml b/tests/components/current_based/test.rp2040.yaml new file mode 100644 index 0000000000..69ab8830c3 --- /dev/null +++ b/tests/components/current_based/test.rp2040.yaml @@ -0,0 +1,68 @@ +i2c: + - id: i2c_ade7953 + scl: 5 + sda: 4 + +sensor: + - platform: ade7953_i2c + irq_pin: 6 + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s + +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +cover: + - platform: current_based + name: Current Based Cover + id: current_based_cover + open_sensor: ade7953_current_a + open_moving_current_threshold: 0.5 + open_obstacle_current_threshold: 0.8 + open_duration: 12s + open_action: + - switch.turn_on: template_switch1 + close_sensor: ade7953_current_b + close_moving_current_threshold: 0.5 + close_obstacle_current_threshold: 0.8 + close_duration: 10s + close_action: + - switch.turn_on: template_switch2 + stop_action: + - switch.turn_off: template_switch1 + - switch.turn_off: template_switch2 + obstacle_rollback: 30% + start_sensing_delay: 0.8s + malfunction_detection: true + malfunction_action: + then: + - logger.log: Malfunction Detected diff --git a/tests/components/cwww/test.esp32-c3-idf.yaml b/tests/components/cwww/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..c829ca2a2b --- /dev/null +++ b/tests/components/cwww/test.esp32-c3-idf.yaml @@ -0,0 +1,16 @@ +output: + - platform: ledc + id: light_output_1 + pin: 1 + - platform: ledc + id: light_output_2 + pin: 2 + +light: + - platform: cwww + name: CWWW Light + cold_white: light_output_1 + warm_white: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + constant_brightness: true diff --git a/tests/components/cwww/test.esp32-c3.yaml b/tests/components/cwww/test.esp32-c3.yaml new file mode 100644 index 0000000000..c829ca2a2b --- /dev/null +++ b/tests/components/cwww/test.esp32-c3.yaml @@ -0,0 +1,16 @@ +output: + - platform: ledc + id: light_output_1 + pin: 1 + - platform: ledc + id: light_output_2 + pin: 2 + +light: + - platform: cwww + name: CWWW Light + cold_white: light_output_1 + warm_white: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + constant_brightness: true diff --git a/tests/components/cwww/test.esp32-idf.yaml b/tests/components/cwww/test.esp32-idf.yaml new file mode 100644 index 0000000000..f108d96ad3 --- /dev/null +++ b/tests/components/cwww/test.esp32-idf.yaml @@ -0,0 +1,16 @@ +output: + - platform: ledc + id: light_output_1 + pin: 12 + - platform: ledc + id: light_output_2 + pin: 13 + +light: + - platform: cwww + name: CWWW Light + cold_white: light_output_1 + warm_white: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + constant_brightness: true diff --git a/tests/components/cwww/test.esp32.yaml b/tests/components/cwww/test.esp32.yaml new file mode 100644 index 0000000000..f108d96ad3 --- /dev/null +++ b/tests/components/cwww/test.esp32.yaml @@ -0,0 +1,16 @@ +output: + - platform: ledc + id: light_output_1 + pin: 12 + - platform: ledc + id: light_output_2 + pin: 13 + +light: + - platform: cwww + name: CWWW Light + cold_white: light_output_1 + warm_white: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + constant_brightness: true diff --git a/tests/components/cwww/test.esp8266.yaml b/tests/components/cwww/test.esp8266.yaml new file mode 100644 index 0000000000..50c311f616 --- /dev/null +++ b/tests/components/cwww/test.esp8266.yaml @@ -0,0 +1,16 @@ +output: + - platform: esp8266_pwm + id: light_output_1 + pin: 12 + - platform: esp8266_pwm + id: light_output_2 + pin: 13 + +light: + - platform: cwww + name: CWWW Light + cold_white: light_output_1 + warm_white: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + constant_brightness: true diff --git a/tests/components/cwww/test.rp2040.yaml b/tests/components/cwww/test.rp2040.yaml new file mode 100644 index 0000000000..505d67f862 --- /dev/null +++ b/tests/components/cwww/test.rp2040.yaml @@ -0,0 +1,16 @@ +output: + - platform: rp2040_pwm + id: light_output_1 + pin: 12 + - platform: rp2040_pwm + id: light_output_2 + pin: 13 + +light: + - platform: cwww + name: CWWW Light + cold_white: light_output_1 + warm_white: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + constant_brightness: true From 71b3a14a29c13d9e82ccfa61bc44c47e2636aa07 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Sun, 11 Feb 2024 22:08:32 +0100 Subject: [PATCH 187/193] update docstrings in cpp_generator.py (#6212) --- esphome/cpp_generator.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 909a786917..04616d97c2 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -478,7 +478,7 @@ def variable( :param type_: Manually define a type for the variable, only use this when it's not possible to do so during config validation phase (for example because of template arguments). - :returns The new variable as a MockObj. + :return: The new variable as a MockObj. """ assert isinstance(id_, ID) rhs = safe_exp(rhs) @@ -526,7 +526,7 @@ def new_variable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj :param type_: Manually define a type for the variable, only use this when it's not possible to do so during config validation phase (for example because of template arguments). - :returns The new variable as a MockObj. + :return: The new variable as a MockObj. """ assert isinstance(id_, ID) rhs = safe_exp(rhs) @@ -549,7 +549,7 @@ def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": :param type_: Manually define a type for the variable, only use this when it's not possible to do so during config validation phase (for example because of template arguments). - :returns The new variable as a MockObj. + :return: The new variable as a MockObj. """ rhs = safe_exp(rhs) obj = MockObj(id_, "->") @@ -570,7 +570,7 @@ def new_Pvariable(id_: ID, *args: SafeExpType) -> Pvariable: :param id_: The ID used to declare the variable (also specifies the type). :param args: The values to pass to the constructor. - :returns The new variable as a MockObj. + :return: The new variable as a MockObj. """ if args and isinstance(args[0], TemplateArguments): id_ = id_.copy() From 061d5b49793da2f89de94f356de2e8614b13757c Mon Sep 17 00:00:00 2001 From: ChuckMash <86080247+ChuckMash@users.noreply.github.com> Date: Sun, 11 Feb 2024 14:55:06 -0800 Subject: [PATCH 188/193] Fixed group mask logic for WLED Sync fix (#6193) --- esphome/components/wled/wled_light_effect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index 7a82aaeb46..84842dff39 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -164,7 +164,7 @@ bool WLEDLightEffect::parse_notifier_frame_(light::AddressableLight &it, const u uint8_t payload_sync_group_mask = payload[34]; - if ((payload_sync_group_mask & this->sync_group_mask_) != this->sync_group_mask_) { + if (this->sync_group_mask_ && !(payload_sync_group_mask & this->sync_group_mask_)) { ESP_LOGD(TAG, "sync group mask does not match"); return false; } From e521662342bb5cb70e67b59b08c88619ca7b5c63 Mon Sep 17 00:00:00 2001 From: kahrendt Date: Mon, 12 Feb 2024 15:38:50 -0500 Subject: [PATCH 189/193] Add micro_wake_word component (#6136) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + .../components/micro_wake_word/__init__.py | 363 +++++++++++++ .../audio_preprocessor_int8_model_data.h | 493 +++++++++++++++++ .../micro_wake_word/micro_wake_word.cpp | 503 ++++++++++++++++++ .../micro_wake_word/micro_wake_word.h | 204 +++++++ .../micro_wake_word/test.esp32-s3-idf.yaml | 15 + 6 files changed, 1579 insertions(+) create mode 100644 esphome/components/micro_wake_word/__init__.py create mode 100644 esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h create mode 100644 esphome/components/micro_wake_word/micro_wake_word.cpp create mode 100644 esphome/components/micro_wake_word/micro_wake_word.h create mode 100644 tests/components/micro_wake_word/test.esp32-s3-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index db44317776..56ec0a93cb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -199,6 +199,7 @@ esphome/components/mcp9808/* @k7hpn esphome/components/md5/* @esphome/core esphome/components/mdns/* @esphome/core esphome/components/media_player/* @jesserockz +esphome/components/micro_wake_word/* @jesserockz @kahrendt esphome/components/micronova/* @jorre05 esphome/components/microphone/* @jesserockz esphome/components/mics_4514/* @jesserockz diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py new file mode 100644 index 0000000000..2a84b7d74b --- /dev/null +++ b/esphome/components/micro_wake_word/__init__.py @@ -0,0 +1,363 @@ +import logging + +import json +import hashlib +from urllib.parse import urljoin +from pathlib import Path +import requests + +import esphome.config_validation as cv +import esphome.codegen as cg + +from esphome.core import CORE, HexInt, EsphomeError + +from esphome.components import esp32, microphone +from esphome import automation, git, external_files +from esphome.automation import register_action, register_condition + + +from esphome.const import ( + __version__, + CONF_ID, + CONF_MICROPHONE, + CONF_MODEL, + CONF_URL, + CONF_FILE, + CONF_PATH, + CONF_REF, + CONF_REFRESH, + CONF_TYPE, + CONF_USERNAME, + CONF_PASSWORD, + CONF_RAW_DATA_ID, + TYPE_GIT, + TYPE_LOCAL, +) + + +_LOGGER = logging.getLogger(__name__) + +CODEOWNERS = ["@kahrendt", "@jesserockz"] +DEPENDENCIES = ["microphone"] +DOMAIN = "micro_wake_word" + +CONF_PROBABILITY_CUTOFF = "probability_cutoff" +CONF_SLIDING_WINDOW_AVERAGE_SIZE = "sliding_window_average_size" +CONF_ON_WAKE_WORD_DETECTED = "on_wake_word_detected" + +TYPE_HTTP = "http" + +micro_wake_word_ns = cg.esphome_ns.namespace("micro_wake_word") + +MicroWakeWord = micro_wake_word_ns.class_("MicroWakeWord", cg.Component) + +StartAction = micro_wake_word_ns.class_("StartAction", automation.Action) +StopAction = micro_wake_word_ns.class_("StopAction", automation.Action) + +IsRunningCondition = micro_wake_word_ns.class_( + "IsRunningCondition", automation.Condition +) + + +def _validate_json_filename(value): + value = cv.string(value) + if not value.endswith(".json"): + raise cv.Invalid("Manifest filename must end with .json") + return value + + +def _process_git_source(config): + repo_dir, _ = git.clone_or_update( + url=config[CONF_URL], + ref=config.get(CONF_REF), + refresh=config[CONF_REFRESH], + domain=DOMAIN, + username=config.get(CONF_USERNAME), + password=config.get(CONF_PASSWORD), + ) + + if not (repo_dir / config[CONF_FILE]).exists(): + raise cv.Invalid("File does not exist in repository") + + return config + + +CV_GIT_SCHEMA = cv.GIT_SCHEMA +if isinstance(CV_GIT_SCHEMA, dict): + CV_GIT_SCHEMA = cv.Schema(CV_GIT_SCHEMA) + +GIT_SCHEMA = cv.All( + CV_GIT_SCHEMA.extend( + { + cv.Required(CONF_FILE): _validate_json_filename, + cv.Optional(CONF_REFRESH, default="1d"): cv.All( + cv.string, cv.source_refresh + ), + } + ), + _process_git_source, +) + +KEY_WAKE_WORD = "wake_word" +KEY_AUTHOR = "author" +KEY_WEBSITE = "website" +KEY_VERSION = "version" +KEY_MICRO = "micro" + +MANIFEST_SCHEMA_V1 = cv.Schema( + { + cv.Required(CONF_TYPE): "micro", + cv.Required(KEY_WAKE_WORD): cv.string, + cv.Required(KEY_AUTHOR): cv.string, + cv.Required(KEY_WEBSITE): cv.url, + cv.Required(KEY_VERSION): cv.All(cv.int_, 1), + cv.Required(CONF_MODEL): cv.string, + cv.Required(KEY_MICRO): cv.Schema( + { + cv.Required(CONF_PROBABILITY_CUTOFF): cv.float_, + cv.Required(CONF_SLIDING_WINDOW_AVERAGE_SIZE): cv.positive_int, + } + ), + } +) + + +def _compute_local_file_path(config: dict) -> Path: + url = config[CONF_URL] + h = hashlib.new("sha256") + h.update(url.encode()) + key = h.hexdigest()[:8] + base_dir = external_files.compute_local_file_dir(DOMAIN) + return base_dir / key + + +def _download_file(url: str, path: Path) -> bytes: + if not external_files.has_remote_file_changed(url, path): + _LOGGER.debug("Remote file has not changed, skipping download") + return path.read_bytes() + + try: + req = requests.get( + url, + timeout=external_files.NETWORK_TIMEOUT, + headers={"User-agent": f"ESPHome/{__version__} (https://esphome.io)"}, + ) + req.raise_for_status() + except requests.exceptions.RequestException as e: + raise cv.Invalid(f"Could not download file from {url}: {e}") from e + + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(req.content) + return req.content + + +def _process_http_source(config): + url = config[CONF_URL] + path = _compute_local_file_path(config) + + json_path = path / "manifest.json" + + json_contents = _download_file(url, json_path) + + manifest_data = json.loads(json_contents) + if not isinstance(manifest_data, dict): + raise cv.Invalid("Manifest file must contain a JSON object") + + try: + MANIFEST_SCHEMA_V1(manifest_data) + except cv.Invalid as e: + raise cv.Invalid(f"Invalid manifest file: {e}") from e + + model = manifest_data[CONF_MODEL] + model_url = urljoin(url, model) + + model_path = path / model + + _download_file(str(model_url), model_path) + + return config + + +HTTP_SCHEMA = cv.All( + { + cv.Required(CONF_URL): cv.url, + }, + _process_http_source, +) + +LOCAL_SCHEMA = cv.Schema( + { + cv.Required(CONF_PATH): cv.All(_validate_json_filename, cv.file_), + } +) + + +def _validate_source_model_name(value): + if not isinstance(value, str): + raise cv.Invalid("Model name must be a string") + + if value.endswith(".json"): + raise cv.Invalid("Model name must not end with .json") + + return MODEL_SOURCE_SCHEMA( + { + CONF_TYPE: TYPE_HTTP, + CONF_URL: f"https://github.com/esphome/micro-wake-word-models/raw/main/models/{value}.json", + } + ) + + +def _validate_source_shorthand(value): + if not isinstance(value, str): + raise cv.Invalid("Shorthand only for strings") + + try: # Test for model name + return _validate_source_model_name(value) + except cv.Invalid: + pass + + try: # Test for local path + return MODEL_SOURCE_SCHEMA({CONF_TYPE: TYPE_LOCAL, CONF_PATH: value}) + except cv.Invalid: + pass + + try: # Test for http url + return MODEL_SOURCE_SCHEMA({CONF_TYPE: TYPE_HTTP, CONF_URL: value}) + except cv.Invalid: + pass + + git_file = git.GitFile.from_shorthand(value) + + conf = { + CONF_TYPE: TYPE_GIT, + CONF_URL: git_file.git_url, + CONF_FILE: git_file.filename, + } + if git_file.ref: + conf[CONF_REF] = git_file.ref + + try: + return MODEL_SOURCE_SCHEMA(conf) + except cv.Invalid as e: + raise cv.Invalid( + f"Could not find file '{git_file.filename}' in the repository. Please make sure it exists." + ) from e + + +MODEL_SOURCE_SCHEMA = cv.Any( + _validate_source_shorthand, + cv.typed_schema( + { + TYPE_GIT: GIT_SCHEMA, + TYPE_LOCAL: LOCAL_SCHEMA, + TYPE_HTTP: HTTP_SCHEMA, + } + ), + msg="Not a valid model name, local path, http(s) url, or github shorthand", +) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MicroWakeWord), + cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone), + cv.Optional(CONF_PROBABILITY_CUTOFF): cv.float_, + cv.Optional(CONF_SLIDING_WINDOW_AVERAGE_SIZE): cv.positive_int, + cv.Optional(CONF_ON_WAKE_WORD_DETECTED): automation.validate_automation( + single=True + ), + cv.Required(CONF_MODEL): MODEL_SOURCE_SCHEMA, + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_with_esp_idf, +) + + +def _load_model_data(manifest_path: Path): + with open(manifest_path, encoding="utf-8") as f: + manifest = json.load(f) + + try: + MANIFEST_SCHEMA_V1(manifest) + except cv.Invalid as e: + raise EsphomeError(f"Invalid manifest file: {e}") from e + + model_path = urljoin(str(manifest_path), manifest[CONF_MODEL]) + + with open(model_path, "rb") as f: + model = f.read() + + return manifest, model + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + mic = await cg.get_variable(config[CONF_MICROPHONE]) + cg.add(var.set_microphone(mic)) + + if on_wake_word_detection_config := config.get(CONF_ON_WAKE_WORD_DETECTED): + await automation.build_automation( + var.get_wake_word_detected_trigger(), + [(cg.std_string, "wake_word")], + on_wake_word_detection_config, + ) + + esp32.add_idf_component( + name="esp-tflite-micro", + repo="https://github.com/espressif/esp-tflite-micro", + ) + + cg.add_build_flag("-DTF_LITE_STATIC_MEMORY") + cg.add_build_flag("-DTF_LITE_DISABLE_X86_NEON") + cg.add_build_flag("-DESP_NN") + + model_config = config.get(CONF_MODEL) + data = [] + if model_config[CONF_TYPE] == TYPE_GIT: + # compute path to model file + key = f"{model_config[CONF_URL]}@{model_config.get(CONF_REF)}" + base_dir = Path(CORE.data_dir) / DOMAIN + h = hashlib.new("sha256") + h.update(key.encode()) + file: Path = base_dir / h.hexdigest()[:8] / model_config[CONF_FILE] + + elif model_config[CONF_TYPE] == TYPE_LOCAL: + file = model_config[CONF_PATH] + + elif model_config[CONF_TYPE] == TYPE_HTTP: + file = _compute_local_file_path(model_config) / "manifest.json" + + manifest, data = _load_model_data(file) + + rhs = [HexInt(x) for x in data] + prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) + cg.add(var.set_model_start(prog_arr)) + + probability_cutoff = config.get( + CONF_PROBABILITY_CUTOFF, manifest[KEY_MICRO][CONF_PROBABILITY_CUTOFF] + ) + cg.add(var.set_probability_cutoff(probability_cutoff)) + sliding_window_average_size = config.get( + CONF_SLIDING_WINDOW_AVERAGE_SIZE, + manifest[KEY_MICRO][CONF_SLIDING_WINDOW_AVERAGE_SIZE], + ) + cg.add(var.set_sliding_window_average_size(sliding_window_average_size)) + + cg.add(var.set_wake_word(manifest[KEY_WAKE_WORD])) + + +MICRO_WAKE_WORD_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(MicroWakeWord)}) + + +@register_action("micro_wake_word.start", StartAction, MICRO_WAKE_WORD_ACTION_SCHEMA) +@register_action("micro_wake_word.stop", StopAction, MICRO_WAKE_WORD_ACTION_SCHEMA) +@register_condition( + "micro_wake_word.is_running", IsRunningCondition, MICRO_WAKE_WORD_ACTION_SCHEMA +) +async def micro_wake_word_action_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var diff --git a/esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h b/esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h new file mode 100644 index 0000000000..918e76045f --- /dev/null +++ b/esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h @@ -0,0 +1,493 @@ +#pragma once + +#ifdef USE_ESP_IDF + +// Converted audio_preprocessor_int8.tflite +// From https://github.com/tensorflow/tflite-micro/tree/main/tensorflow/lite/micro/examples/micro_speech/models accessed +// January 2024 +// +// Copyright 2023 The TensorFlow Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace esphome { +namespace micro_wake_word { + +const unsigned char G_AUDIO_PREPROCESSOR_INT8_TFLITE[] = { + 0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x14, 0x00, 0x20, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, + 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, + 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x80, 0x0e, 0x00, 0x00, 0x90, 0x0e, 0x00, 0x00, 0xcc, 0x1f, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xeb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, + 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0xff, + 0xff, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x5f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc2, 0xf5, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xdc, 0xff, 0xff, 0xff, 0x2d, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x43, 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, + 0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6d, 0x69, 0x6e, + 0x5f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x2e, 0x00, + 0x00, 0x00, 0x9c, 0x0d, 0x00, 0x00, 0x94, 0x0d, 0x00, 0x00, 0xc4, 0x09, 0x00, 0x00, 0x6c, 0x09, 0x00, 0x00, 0x48, + 0x09, 0x00, 0x00, 0x34, 0x09, 0x00, 0x00, 0x20, 0x09, 0x00, 0x00, 0x0c, 0x09, 0x00, 0x00, 0xf8, 0x08, 0x00, 0x00, + 0xec, 0x07, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x24, 0x07, 0x00, 0x00, 0xc0, 0x06, 0x00, 0x00, 0x38, 0x04, 0x00, + 0x00, 0xb0, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x60, 0x01, + 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x2c, + 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, + 0x00, 0xdc, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xbc, 0x00, + 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, + 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf2, 0xf6, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x32, 0x2e, 0x30, 0x00, + 0x00, 0x56, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x38, 0x2e, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xe1, 0xff, 0xff, 0xd8, 0xe1, 0xff, 0xff, 0xdc, + 0xe1, 0xff, 0xff, 0xe0, 0xe1, 0xff, 0xff, 0xe4, 0xe1, 0xff, 0xff, 0xe8, 0xe1, 0xff, 0xff, 0xec, 0xe1, 0xff, 0xff, + 0xf0, 0xe1, 0xff, 0xff, 0xf4, 0xe1, 0xff, 0xff, 0xf8, 0xe1, 0xff, 0xff, 0xfc, 0xe1, 0xff, 0xff, 0x00, 0xe2, 0xff, + 0xff, 0x04, 0xe2, 0xff, 0xff, 0x08, 0xe2, 0xff, 0xff, 0x0c, 0xe2, 0xff, 0xff, 0x10, 0xe2, 0xff, 0xff, 0x14, 0xe2, + 0xff, 0xff, 0x18, 0xe2, 0xff, 0xff, 0x1c, 0xe2, 0xff, 0xff, 0x20, 0xe2, 0xff, 0xff, 0x24, 0xe2, 0xff, 0xff, 0x28, + 0xe2, 0xff, 0xff, 0x2c, 0xe2, 0xff, 0xff, 0x30, 0xe2, 0xff, 0xff, 0xd2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9a, 0x02, 0x00, 0x00, 0xe2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0xf2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0xff, + 0xff, 0xff, 0x02, 0xf8, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, + 0xf8, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x22, 0xf8, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x61, 0x05, 0x00, 0x00, 0x00, 0x00, 0x23, 0x0b, 0x41, + 0x01, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x0e, 0x80, 0x05, + 0x00, 0x00, 0x00, 0x00, 0xd1, 0x0c, 0x63, 0x04, 0x00, 0x00, 0x00, 0x00, 0x34, 0x0c, 0x3f, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x81, 0x0c, 0xf7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0d, 0x77, 0x06, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x0f, + 0xa9, 0x08, 0x01, 0x02, 0x7f, 0x0b, 0x22, 0x05, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x0e, 0xd1, 0x08, 0xdb, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x0d, 0x4a, 0x07, 0xad, 0x01, 0x2c, 0x0c, 0xc6, 0x06, 0x79, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x0c, 0x29, 0x07, 0x23, 0x02, 0x34, 0x0d, 0x5b, 0x08, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x0e, 0x48, + 0x0a, 0xbd, 0x05, 0x45, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x0c, 0x88, 0x08, 0x43, 0x04, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x0b, 0xd3, 0x07, 0xcb, 0x03, 0xd2, 0x0f, 0xe7, + 0x0b, 0x09, 0x08, 0x39, 0x04, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x0c, 0x14, 0x09, + 0x75, 0x05, 0xe2, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x0e, 0xdd, 0x0a, 0x6b, 0x07, 0x03, + 0x04, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x0d, 0x09, 0x0a, 0xc9, 0x06, 0x93, 0x03, 0x65, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0d, 0x25, 0x0a, 0x12, 0x07, 0x07, 0x04, 0x05, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x0e, 0x17, 0x0b, 0x2c, 0x08, 0x49, 0x05, 0x6d, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x98, 0x0f, 0xcb, 0x0c, 0x04, 0x0a, 0x44, 0x07, 0x8b, 0x04, 0xd8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x0f, 0x87, + 0x0c, 0xe7, 0x09, 0x4e, 0x07, 0xba, 0x04, 0x2d, 0x02, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x0f, 0x23, 0x0d, 0xa7, 0x0a, + 0x30, 0x08, 0xbe, 0x05, 0x52, 0x03, 0xeb, 0x00, 0x89, 0x0e, 0x2c, 0x0c, 0xd4, 0x09, 0x81, 0x07, 0x33, 0x05, 0xe9, + 0x02, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0e, 0x29, 0x0c, 0xf1, 0x09, 0xbe, 0x07, 0x90, 0x05, 0x65, 0x03, + 0x3f, 0x01, 0x1d, 0x0f, 0xff, 0x0c, 0xe5, 0x0a, 0xcf, 0x08, 0xbc, 0x06, 0xae, 0x04, 0xa3, 0x02, 0x9c, 0x00, 0x99, + 0x0e, 0x99, 0x0c, 0x9d, 0x0a, 0xa4, 0x08, 0xaf, 0x06, 0xbd, 0x04, 0xcf, 0x02, 0xe4, 0x00, 0xfc, 0x0e, 0x17, 0x0d, + 0x36, 0x0b, 0x57, 0x09, 0x7c, 0x07, 0xa4, 0x05, 0xcf, 0x03, 0xfd, 0x01, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x62, 0x0e, 0x98, 0x0c, 0xd2, 0x0a, 0x0e, 0x09, 0x4d, 0x07, 0x8f, 0x05, 0xd4, 0x03, 0x1b, 0x02, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x0e, 0x00, 0x0d, 0x52, 0x0b, 0xa6, 0x09, 0xfd, 0x07, 0x56, 0x06, 0xb1, + 0x04, 0x0f, 0x03, 0x6f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x0f, 0x37, 0x0e, 0x9e, 0x0c, + 0x08, 0x0b, 0x73, 0x09, 0xe1, 0x07, 0x52, 0x06, 0xc4, 0x04, 0x38, 0x03, 0xaf, 0x01, 0x28, 0x00, 0xa3, 0x0e, 0x1f, + 0x0d, 0x9e, 0x0b, 0x1f, 0x0a, 0xa2, 0x08, 0x27, 0x07, 0xae, 0x05, 0x37, 0x04, 0xc2, 0x02, 0x4e, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xdd, 0x0f, 0x6d, 0x0e, 0xff, 0x0c, 0x93, 0x0b, 0x29, 0x0a, 0xc1, 0x08, 0x5a, 0x07, 0xf5, 0x05, 0x92, + 0x04, 0x30, 0x03, 0xd1, 0x01, 0x73, 0x00, 0x16, 0x0f, 0xbc, 0x0d, 0x62, 0x0c, 0x0b, 0x0b, 0xb5, 0x09, 0x61, 0x08, + 0x0e, 0x07, 0xbd, 0x05, 0x6d, 0x04, 0x1f, 0x03, 0xd3, 0x01, 0x88, 0x00, 0x3e, 0x0f, 0xf6, 0x0d, 0xaf, 0x0c, 0x6a, + 0x0b, 0x27, 0x0a, 0xe4, 0x08, 0xa3, 0x07, 0x64, 0x06, 0x26, 0x05, 0xe9, 0x03, 0xae, 0x02, 0x74, 0x01, 0x3b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0f, 0xce, 0x0d, 0x99, 0x0c, 0x66, 0x0b, 0x34, 0x0a, 0x03, + 0x09, 0xd3, 0x07, 0xa5, 0x06, 0x78, 0x05, 0x4c, 0x04, 0x22, 0x03, 0xf8, 0x01, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa9, 0x0f, 0x83, 0x0e, 0x5f, 0x0d, 0x3b, 0x0c, 0x19, 0x0b, 0xf8, 0x09, 0xd8, 0x08, 0xb9, 0x07, 0x9b, 0x06, 0x7e, + 0x05, 0x63, 0x04, 0x48, 0x03, 0x2f, 0x02, 0x17, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xfa, 0xff, 0xff, 0x04, 0x00, + 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x04, 0xbe, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x4c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x01, 0x7f, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x2e, 0x03, 0x9c, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xcb, 0x03, 0xc0, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x7e, + 0x03, 0x08, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x88, 0x09, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x56, 0x07, + 0xfe, 0x0d, 0x80, 0x04, 0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x16, 0x01, 0x2e, 0x07, 0x24, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x02, 0xb5, 0x08, 0x52, 0x0e, 0xd3, 0x03, 0x39, 0x09, 0x86, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xba, 0x03, + 0xd6, 0x08, 0xdc, 0x0d, 0xcb, 0x02, 0xa4, 0x07, 0x69, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, 0xb7, 0x05, 0x42, + 0x0a, 0xba, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x03, 0x77, 0x07, 0xbc, 0x0b, 0xf1, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x04, 0x2c, 0x08, 0x34, 0x0c, 0x2d, 0x00, 0x18, 0x04, 0xf6, + 0x07, 0xc6, 0x0b, 0x89, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0xeb, 0x06, 0x8a, 0x0a, + 0x1d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x01, 0x22, 0x05, 0x94, 0x08, 0xfc, 0x0b, 0x59, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0xac, 0x02, 0xf6, 0x05, 0x36, 0x09, 0x6c, 0x0c, 0x9a, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x02, 0xda, 0x05, 0xed, 0x08, 0xf8, 0x0b, 0xfa, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xf5, + 0x01, 0xe8, 0x04, 0xd3, 0x07, 0xb6, 0x0a, 0x92, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, + 0x34, 0x03, 0xfb, 0x05, 0xbb, 0x08, 0x74, 0x0b, 0x27, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x78, 0x03, 0x18, + 0x06, 0xb1, 0x08, 0x45, 0x0b, 0xd2, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0xdc, 0x02, 0x58, 0x05, 0xcf, 0x07, + 0x41, 0x0a, 0xad, 0x0c, 0x14, 0x0f, 0x76, 0x01, 0xd3, 0x03, 0x2b, 0x06, 0x7e, 0x08, 0xcc, 0x0a, 0x16, 0x0d, 0x5a, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x01, 0xd6, 0x03, 0x0e, 0x06, 0x41, 0x08, 0x6f, 0x0a, 0x9a, 0x0c, 0xc0, 0x0e, + 0xe2, 0x00, 0x00, 0x03, 0x1a, 0x05, 0x30, 0x07, 0x43, 0x09, 0x51, 0x0b, 0x5c, 0x0d, 0x63, 0x0f, 0x66, 0x01, 0x66, + 0x03, 0x62, 0x05, 0x5b, 0x07, 0x50, 0x09, 0x42, 0x0b, 0x30, 0x0d, 0x1b, 0x0f, 0x03, 0x01, 0xe8, 0x02, 0xc9, 0x04, + 0xa8, 0x06, 0x83, 0x08, 0x5b, 0x0a, 0x30, 0x0c, 0x02, 0x0e, 0xd1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9d, 0x01, 0x67, 0x03, 0x2d, 0x05, 0xf1, 0x06, 0xb2, 0x08, 0x70, 0x0a, 0x2b, 0x0c, 0xe4, 0x0d, 0x9a, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x4e, 0x01, 0xff, 0x02, 0xad, 0x04, 0x59, 0x06, 0x02, 0x08, 0xa9, 0x09, 0x4e, 0x0b, 0xf0, + 0x0c, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0xc8, 0x01, 0x61, 0x03, 0xf7, 0x04, + 0x8c, 0x06, 0x1e, 0x08, 0xad, 0x09, 0x3b, 0x0b, 0xc7, 0x0c, 0x50, 0x0e, 0xd7, 0x0f, 0x5c, 0x01, 0xe0, 0x02, 0x61, + 0x04, 0xe0, 0x05, 0x5d, 0x07, 0xd8, 0x08, 0x51, 0x0a, 0xc8, 0x0b, 0x3d, 0x0d, 0xb1, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x92, 0x01, 0x00, 0x03, 0x6c, 0x04, 0xd6, 0x05, 0x3e, 0x07, 0xa5, 0x08, 0x0a, 0x0a, 0x6d, 0x0b, 0xcf, + 0x0c, 0x2e, 0x0e, 0x8c, 0x0f, 0xe9, 0x00, 0x43, 0x02, 0x9d, 0x03, 0xf4, 0x04, 0x4a, 0x06, 0x9e, 0x07, 0xf1, 0x08, + 0x42, 0x0a, 0x92, 0x0b, 0xe0, 0x0c, 0x2c, 0x0e, 0x77, 0x0f, 0xc1, 0x00, 0x09, 0x02, 0x50, 0x03, 0x95, 0x04, 0xd8, + 0x05, 0x1b, 0x07, 0x5c, 0x08, 0x9b, 0x09, 0xd9, 0x0a, 0x16, 0x0c, 0x51, 0x0d, 0x8b, 0x0e, 0xc4, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x31, 0x02, 0x66, 0x03, 0x99, 0x04, 0xcb, 0x05, 0xfc, 0x06, 0x2c, + 0x08, 0x5a, 0x09, 0x87, 0x0a, 0xb3, 0x0b, 0xdd, 0x0c, 0x07, 0x0e, 0x2f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, + 0x7c, 0x01, 0xa0, 0x02, 0xc4, 0x03, 0xe6, 0x04, 0x07, 0x06, 0x27, 0x07, 0x46, 0x08, 0x64, 0x09, 0x81, 0x0a, 0x9c, + 0x0b, 0xb7, 0x0c, 0xd0, 0x0d, 0xe8, 0x0e, 0x00, 0x10, 0x00, 0x00, 0x2a, 0xfd, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, + 0x00, 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x24, 0x00, 0x26, 0x00, 0x2a, 0x00, + 0x2e, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3a, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x54, 0x00, 0x5a, + 0x00, 0x62, 0x00, 0x68, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x88, 0x00, 0x92, 0x00, 0x9a, 0x00, 0xa6, 0x00, + 0xb0, 0x00, 0xbc, 0x00, 0xc8, 0x00, 0xd4, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x8a, 0xfd, 0xff, 0xff, 0x04, 0x00, 0x00, + 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, + 0x1c, 0x00, 0x20, 0x00, 0x24, 0x00, 0x28, 0x00, 0x2c, 0x00, 0x30, 0x00, 0x34, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x44, + 0x00, 0x4c, 0x00, 0x50, 0x00, 0x58, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x88, 0x00, + 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xc4, 0x00, 0xd0, 0x00, 0xdc, 0x00, 0xe8, + 0x00, 0xf4, 0x00, 0x00, 0x01, 0x0c, 0x01, 0x1c, 0x01, 0x2c, 0x01, 0x00, 0x00, 0xea, 0xfd, 0xff, 0xff, 0x04, 0x00, + 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, + 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, + 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4a, 0xfe, 0xff, 0xff, 0x04, + 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x7c, 0x7f, 0x79, 0x7f, 0x76, 0x7f, 0xfa, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x7f, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x64, 0x7f, 0xe9, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x4b, 0x7f, 0xd0, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x7f, 0xa0, 0xff, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x7e, 0x42, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xfd, 0x7d, 0x86, 0xfe, 0x04, 0x00, 0x00, 0x00, 0x87, 0x7c, 0x1d, 0xfd, 0x12, 0x00, 0x00, 0x00, 0xb6, + 0x79, 0x7f, 0xfa, 0x3e, 0x00, 0x00, 0x00, 0x73, 0x74, 0xf9, 0xf5, 0xca, 0x00, 0x00, 0x00, 0x36, 0x6b, 0x33, 0xef, + 0x32, 0x02, 0x00, 0x00, 0x9b, 0x5c, 0x87, 0xe7, 0xce, 0x04, 0x00, 0x00, 0xf0, 0x48, 0xde, 0xe2, 0xa0, 0x07, 0x00, + 0x00, 0x6e, 0x33, 0x8a, 0xe4, 0xa4, 0x08, 0x00, 0x00, 0x9c, 0x20, 0x22, 0xeb, 0x4c, 0x07, 0x00, 0x00, 0x0a, 0x13, + 0x7d, 0xf2, 0x02, 0x05, 0x00, 0x00, 0x89, 0x0a, 0x17, 0xf8, 0x06, 0x03, 0x00, 0x00, 0xa6, 0x05, 0xa0, 0xfb, 0xb4, + 0x01, 0x00, 0x00, 0xfa, 0x02, 0xac, 0xfd, 0xe8, 0x00, 0x00, 0x00, 0x8e, 0x01, 0xc7, 0xfe, 0x7a, 0x00, 0x00, 0x00, + 0xcf, 0x00, 0x5c, 0xff, 0x40, 0x00, 0x00, 0x00, 0x6b, 0x00, 0xab, 0xff, 0x22, 0x00, 0x00, 0x00, 0x38, 0x00, 0xd3, + 0xff, 0x12, 0x00, 0x00, 0x00, 0x1d, 0x00, 0xea, 0xff, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xf3, 0xff, 0x06, 0x00, + 0x00, 0x00, 0x08, 0x00, 0xf8, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0xfd, 0xff, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfd, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x82, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4d, 0x01, 0x00, 0x00, + 0x92, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0xff, 0xff, 0xff, 0x04, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x04, 0x00, 0x05, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x13, 0x00, 0x17, 0x00, + 0x1b, 0x00, 0x20, 0x00, 0x25, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, 0x00, 0x3c, 0x00, 0x42, 0x00, 0x49, 0x00, 0x51, + 0x00, 0x58, 0x00, 0x60, 0x00, 0x68, 0x00, 0x71, 0x00, 0x7a, 0x00, 0x83, 0x00, 0x8d, 0x00, 0x97, 0x00, 0xa1, 0x00, + 0xac, 0x00, 0xb7, 0x00, 0xc2, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xe5, 0x00, 0xf2, 0x00, 0xff, 0x00, 0x0c, 0x01, 0x19, + 0x01, 0x27, 0x01, 0x35, 0x01, 0x43, 0x01, 0x52, 0x01, 0x61, 0x01, 0x70, 0x01, 0x7f, 0x01, 0x8f, 0x01, 0x9f, 0x01, + 0xaf, 0x01, 0xc0, 0x01, 0xd1, 0x01, 0xe2, 0x01, 0xf3, 0x01, 0x05, 0x02, 0x17, 0x02, 0x29, 0x02, 0x3c, 0x02, 0x4e, + 0x02, 0x61, 0x02, 0x75, 0x02, 0x88, 0x02, 0x9c, 0x02, 0xb0, 0x02, 0xc4, 0x02, 0xd8, 0x02, 0xed, 0x02, 0x02, 0x03, + 0x17, 0x03, 0x2c, 0x03, 0x41, 0x03, 0x57, 0x03, 0x6d, 0x03, 0x83, 0x03, 0x99, 0x03, 0xb0, 0x03, 0xc7, 0x03, 0xdd, + 0x03, 0xf4, 0x03, 0x0c, 0x04, 0x23, 0x04, 0x3b, 0x04, 0x52, 0x04, 0x6a, 0x04, 0x82, 0x04, 0x9a, 0x04, 0xb3, 0x04, + 0xcb, 0x04, 0xe4, 0x04, 0xfd, 0x04, 0x16, 0x05, 0x2f, 0x05, 0x48, 0x05, 0x61, 0x05, 0x7a, 0x05, 0x94, 0x05, 0xad, + 0x05, 0xc7, 0x05, 0xe1, 0x05, 0xfb, 0x05, 0x15, 0x06, 0x2f, 0x06, 0x49, 0x06, 0x63, 0x06, 0x7e, 0x06, 0x98, 0x06, + 0xb2, 0x06, 0xcd, 0x06, 0xe7, 0x06, 0x02, 0x07, 0x1d, 0x07, 0x37, 0x07, 0x52, 0x07, 0x6d, 0x07, 0x87, 0x07, 0xa2, + 0x07, 0xbd, 0x07, 0xd8, 0x07, 0xf3, 0x07, 0x0d, 0x08, 0x28, 0x08, 0x43, 0x08, 0x5e, 0x08, 0x79, 0x08, 0x93, 0x08, + 0xae, 0x08, 0xc9, 0x08, 0xe3, 0x08, 0xfe, 0x08, 0x19, 0x09, 0x33, 0x09, 0x4e, 0x09, 0x68, 0x09, 0x82, 0x09, 0x9d, + 0x09, 0xb7, 0x09, 0xd1, 0x09, 0xeb, 0x09, 0x05, 0x0a, 0x1f, 0x0a, 0x39, 0x0a, 0x53, 0x0a, 0x6c, 0x0a, 0x86, 0x0a, + 0x9f, 0x0a, 0xb8, 0x0a, 0xd1, 0x0a, 0xea, 0x0a, 0x03, 0x0b, 0x1c, 0x0b, 0x35, 0x0b, 0x4d, 0x0b, 0x66, 0x0b, 0x7e, + 0x0b, 0x96, 0x0b, 0xae, 0x0b, 0xc5, 0x0b, 0xdd, 0x0b, 0xf4, 0x0b, 0x0c, 0x0c, 0x23, 0x0c, 0x39, 0x0c, 0x50, 0x0c, + 0x67, 0x0c, 0x7d, 0x0c, 0x93, 0x0c, 0xa9, 0x0c, 0xbf, 0x0c, 0xd4, 0x0c, 0xe9, 0x0c, 0xfe, 0x0c, 0x13, 0x0d, 0x28, + 0x0d, 0x3c, 0x0d, 0x50, 0x0d, 0x64, 0x0d, 0x78, 0x0d, 0x8b, 0x0d, 0x9f, 0x0d, 0xb2, 0x0d, 0xc4, 0x0d, 0xd7, 0x0d, + 0xe9, 0x0d, 0xfb, 0x0d, 0x0d, 0x0e, 0x1e, 0x0e, 0x2f, 0x0e, 0x40, 0x0e, 0x51, 0x0e, 0x61, 0x0e, 0x71, 0x0e, 0x81, + 0x0e, 0x90, 0x0e, 0x9f, 0x0e, 0xae, 0x0e, 0xbd, 0x0e, 0xcb, 0x0e, 0xd9, 0x0e, 0xe7, 0x0e, 0xf4, 0x0e, 0x01, 0x0f, + 0x0e, 0x0f, 0x1b, 0x0f, 0x27, 0x0f, 0x33, 0x0f, 0x3e, 0x0f, 0x49, 0x0f, 0x54, 0x0f, 0x5f, 0x0f, 0x69, 0x0f, 0x73, + 0x0f, 0x7d, 0x0f, 0x86, 0x0f, 0x8f, 0x0f, 0x98, 0x0f, 0xa0, 0x0f, 0xa8, 0x0f, 0xaf, 0x0f, 0xb7, 0x0f, 0xbe, 0x0f, + 0xc4, 0x0f, 0xcb, 0x0f, 0xd0, 0x0f, 0xd6, 0x0f, 0xdb, 0x0f, 0xe0, 0x0f, 0xe5, 0x0f, 0xe9, 0x0f, 0xed, 0x0f, 0xf0, + 0x0f, 0xf3, 0x0f, 0xf6, 0x0f, 0xf9, 0x0f, 0xfb, 0x0f, 0xfc, 0x0f, 0xfe, 0x0f, 0xff, 0x0f, 0x00, 0x10, 0x00, 0x10, + 0x00, 0x10, 0x00, 0x10, 0xff, 0x0f, 0xfe, 0x0f, 0xfc, 0x0f, 0xfb, 0x0f, 0xf9, 0x0f, 0xf6, 0x0f, 0xf3, 0x0f, 0xf0, + 0x0f, 0xed, 0x0f, 0xe9, 0x0f, 0xe5, 0x0f, 0xe0, 0x0f, 0xdb, 0x0f, 0xd6, 0x0f, 0xd0, 0x0f, 0xcb, 0x0f, 0xc4, 0x0f, + 0xbe, 0x0f, 0xb7, 0x0f, 0xaf, 0x0f, 0xa8, 0x0f, 0xa0, 0x0f, 0x98, 0x0f, 0x8f, 0x0f, 0x86, 0x0f, 0x7d, 0x0f, 0x73, + 0x0f, 0x69, 0x0f, 0x5f, 0x0f, 0x54, 0x0f, 0x49, 0x0f, 0x3e, 0x0f, 0x33, 0x0f, 0x27, 0x0f, 0x1b, 0x0f, 0x0e, 0x0f, + 0x01, 0x0f, 0xf4, 0x0e, 0xe7, 0x0e, 0xd9, 0x0e, 0xcb, 0x0e, 0xbd, 0x0e, 0xae, 0x0e, 0x9f, 0x0e, 0x90, 0x0e, 0x81, + 0x0e, 0x71, 0x0e, 0x61, 0x0e, 0x51, 0x0e, 0x40, 0x0e, 0x2f, 0x0e, 0x1e, 0x0e, 0x0d, 0x0e, 0xfb, 0x0d, 0xe9, 0x0d, + 0xd7, 0x0d, 0xc4, 0x0d, 0xb2, 0x0d, 0x9f, 0x0d, 0x8b, 0x0d, 0x78, 0x0d, 0x64, 0x0d, 0x50, 0x0d, 0x3c, 0x0d, 0x28, + 0x0d, 0x13, 0x0d, 0xfe, 0x0c, 0xe9, 0x0c, 0xd4, 0x0c, 0xbf, 0x0c, 0xa9, 0x0c, 0x93, 0x0c, 0x7d, 0x0c, 0x67, 0x0c, + 0x50, 0x0c, 0x39, 0x0c, 0x23, 0x0c, 0x0c, 0x0c, 0xf4, 0x0b, 0xdd, 0x0b, 0xc5, 0x0b, 0xae, 0x0b, 0x96, 0x0b, 0x7e, + 0x0b, 0x66, 0x0b, 0x4d, 0x0b, 0x35, 0x0b, 0x1c, 0x0b, 0x03, 0x0b, 0xea, 0x0a, 0xd1, 0x0a, 0xb8, 0x0a, 0x9f, 0x0a, + 0x86, 0x0a, 0x6c, 0x0a, 0x53, 0x0a, 0x39, 0x0a, 0x1f, 0x0a, 0x05, 0x0a, 0xeb, 0x09, 0xd1, 0x09, 0xb7, 0x09, 0x9d, + 0x09, 0x82, 0x09, 0x68, 0x09, 0x4e, 0x09, 0x33, 0x09, 0x19, 0x09, 0xfe, 0x08, 0xe3, 0x08, 0xc9, 0x08, 0xae, 0x08, + 0x93, 0x08, 0x79, 0x08, 0x5e, 0x08, 0x43, 0x08, 0x28, 0x08, 0x0d, 0x08, 0xf3, 0x07, 0xd8, 0x07, 0xbd, 0x07, 0xa2, + 0x07, 0x87, 0x07, 0x6d, 0x07, 0x52, 0x07, 0x37, 0x07, 0x1d, 0x07, 0x02, 0x07, 0xe7, 0x06, 0xcd, 0x06, 0xb2, 0x06, + 0x98, 0x06, 0x7e, 0x06, 0x63, 0x06, 0x49, 0x06, 0x2f, 0x06, 0x15, 0x06, 0xfb, 0x05, 0xe1, 0x05, 0xc7, 0x05, 0xad, + 0x05, 0x94, 0x05, 0x7a, 0x05, 0x61, 0x05, 0x48, 0x05, 0x2f, 0x05, 0x16, 0x05, 0xfd, 0x04, 0xe4, 0x04, 0xcb, 0x04, + 0xb3, 0x04, 0x9a, 0x04, 0x82, 0x04, 0x6a, 0x04, 0x52, 0x04, 0x3b, 0x04, 0x23, 0x04, 0x0c, 0x04, 0xf4, 0x03, 0xdd, + 0x03, 0xc7, 0x03, 0xb0, 0x03, 0x99, 0x03, 0x83, 0x03, 0x6d, 0x03, 0x57, 0x03, 0x41, 0x03, 0x2c, 0x03, 0x17, 0x03, + 0x02, 0x03, 0xed, 0x02, 0xd8, 0x02, 0xc4, 0x02, 0xb0, 0x02, 0x9c, 0x02, 0x88, 0x02, 0x75, 0x02, 0x61, 0x02, 0x4e, + 0x02, 0x3c, 0x02, 0x29, 0x02, 0x17, 0x02, 0x05, 0x02, 0xf3, 0x01, 0xe2, 0x01, 0xd1, 0x01, 0xc0, 0x01, 0xaf, 0x01, + 0x9f, 0x01, 0x8f, 0x01, 0x7f, 0x01, 0x70, 0x01, 0x61, 0x01, 0x52, 0x01, 0x43, 0x01, 0x35, 0x01, 0x27, 0x01, 0x19, + 0x01, 0x0c, 0x01, 0xff, 0x00, 0xf2, 0x00, 0xe5, 0x00, 0xd9, 0x00, 0xcd, 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xac, 0x00, + 0xa1, 0x00, 0x97, 0x00, 0x8d, 0x00, 0x83, 0x00, 0x7a, 0x00, 0x71, 0x00, 0x68, 0x00, 0x60, 0x00, 0x58, 0x00, 0x51, + 0x00, 0x49, 0x00, 0x42, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2a, 0x00, 0x25, 0x00, 0x20, 0x00, 0x1b, 0x00, + 0x17, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xee, 0xff, 0xff, 0x38, 0xee, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x4d, 0x4c, + 0x49, 0x52, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xf4, 0x05, 0x00, 0x00, 0xf8, 0x05, 0x00, + 0x00, 0xfc, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, + 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x68, 0x05, 0x00, 0x00, 0x24, 0x05, 0x00, 0x00, 0xc8, 0x04, 0x00, 0x00, 0x70, + 0x04, 0x00, 0x00, 0x4c, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0xc8, 0x03, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x00, + 0x4c, 0x03, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0x6c, 0x01, 0x00, + 0x00, 0x48, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x78, 0x00, + 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf6, 0xfa, 0xff, 0xff, 0x0c, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x16, 0xfb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3a, 0xfb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0xa6, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x10, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x68, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xd6, 0xfc, 0xff, 0xff, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x98, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x10, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xc8, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x25, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0xfd, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0xf8, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, + 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x84, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x00, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x00, 0x02, 0x24, 0x0f, 0x02, 0x01, 0x02, 0x03, 0x40, 0x04, 0x04, 0x04, 0x24, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xdc, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x73, 0x6e, + 0x72, 0x5f, 0x73, 0x68, 0x69, 0x66, 0x74, 0x00, 0x01, 0x0b, 0x01, 0x01, 0x01, 0x06, 0x04, 0x02, 0x24, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x20, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65, 0x5f, + 0x6f, 0x6e, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, + 0x00, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, + 0x67, 0x00, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x69, 0x6e, 0x67, 0x00, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x00, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x00, 0x6f, 0x6e, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, 0x73, 0x6d, + 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x73, + 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x00, 0x73, 0x70, 0x65, 0x63, 0x74, + 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x74, + 0x73, 0x00, 0x09, 0xa5, 0x88, 0x75, 0x6d, 0x59, 0x4d, 0x3a, 0x31, 0x23, 0x09, 0x00, 0x01, 0x00, 0x09, 0x00, 0x29, + 0x3c, 0xd7, 0x03, 0x00, 0x00, 0x33, 0x03, 0x28, 0x00, 0x67, 0x3e, 0x99, 0x01, 0x0a, 0x00, 0x0e, 0x00, 0x05, 0x05, + 0x69, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1b, 0x25, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x20, 0xfe, 0xff, 0xff, 0x10, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x54, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, + 0x00, 0x2c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x00, 0x01, 0x0e, 0x01, 0x01, 0x01, 0x28, 0x04, 0x02, 0x24, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x62, 0xfe, 0xff, + 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0xca, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8c, 0xf2, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x0b, 0x00, + 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0xf2, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x00, 0xfe, 0xfe, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x64, 0xff, 0xff, 0xff, 0x10, + 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x65, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x00, 0x02, 0x17, 0x0e, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf1, 0x00, 0x05, 0x00, 0x05, 0x05, + 0x06, 0x25, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x54, 0x00, 0x66, 0x66, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x00, 0x02, 0x0e, 0x0d, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x02, 0x05, 0x05, 0x06, 0x25, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, + 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x73, + 0x68, 0x69, 0x66, 0x74, 0x00, 0x01, 0x07, 0x01, 0x01, 0x01, 0x0c, 0x04, 0x02, 0x24, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xc4, 0x0a, + 0x00, 0x00, 0x74, 0x0a, 0x00, 0x00, 0x3c, 0x0a, 0x00, 0x00, 0x04, 0x0a, 0x00, 0x00, 0xd0, 0x09, 0x00, 0x00, 0x88, + 0x09, 0x00, 0x00, 0x40, 0x09, 0x00, 0x00, 0xfc, 0x08, 0x00, 0x00, 0xb8, 0x08, 0x00, 0x00, 0x6c, 0x08, 0x00, 0x00, + 0x20, 0x08, 0x00, 0x00, 0xd4, 0x07, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x3c, 0x07, 0x00, 0x00, 0xf8, 0x06, 0x00, + 0x00, 0xb8, 0x06, 0x00, 0x00, 0x84, 0x06, 0x00, 0x00, 0x50, 0x06, 0x00, 0x00, 0x1c, 0x06, 0x00, 0x00, 0xd8, 0x05, + 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x14, 0x05, 0x00, 0x00, 0xd8, 0x04, 0x00, 0x00, 0x98, + 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x00, + 0x6c, 0x03, 0x00, 0x00, 0x1c, 0x03, 0x00, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x2c, 0x02, 0x00, + 0x00, 0xe4, 0x01, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x08, 0x01, + 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xfe, + 0xf5, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x44, 0xf5, 0xff, 0xff, 0x11, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, 0x61, 0x6c, 0x6c, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x84, 0xf5, 0xff, 0xff, + 0x0d, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x7a, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0xc0, + 0xf5, 0xff, 0xff, 0x15, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, + 0x00, 0xbe, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x04, 0xf6, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x61, + 0x64, 0x64, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf2, 0xf6, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x18, 0x00, 0x00, 0x00, 0x38, 0xf6, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, + 0x74, 0x65, 0x44, 0x69, 0x76, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2a, 0xf7, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x70, 0xf6, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x61, 0x64, 0x64, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x00, 0x5a, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0xa0, 0xf6, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x6d, 0x75, 0x6c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x8a, 0xf7, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x14, 0x00, 0x00, 0x00, 0xd0, 0xf6, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, 0x74, 0x5f, 0x32, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xbe, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x24, 0x00, 0x00, 0x00, + 0x04, 0xf7, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x6c, 0x6f, 0x67, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x02, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x18, 0x00, 0x00, 0x00, 0x48, 0xf7, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x63, 0x61, 0x6e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, + 0x00, 0x3a, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x38, 0x00, 0x00, 0x00, 0x80, 0xf7, 0xff, 0xff, 0x28, 0x00, 0x00, 0x00, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x92, 0xf8, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x34, + 0x00, 0x00, 0x00, 0xd8, 0xf7, 0xff, 0xff, 0x27, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x72, 0x61, 0x6c, + 0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0xe6, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0xf8, 0xff, 0xff, 0x1e, 0x00, 0x00, 0x00, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, + 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x32, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, 0x00, 0x78, 0xf8, 0xff, 0xff, 0x12, 0x00, 0x00, 0x00, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x72, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x14, 0x00, 0x00, 0x00, 0xb8, + 0xf8, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, 0x74, 0x5f, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0xa6, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xec, 0xf8, 0xff, 0xff, 0x06, 0x00, + 0x00, 0x00, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xda, + 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x20, 0xf9, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, + 0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xec, 0x00, + 0x00, 0x00, 0x16, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x5c, 0xf9, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x43, 0x61, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x4a, 0xfa, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x1c, 0x00, 0x00, 0x00, 0x90, 0xf9, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x6c, 0x5f, 0x65, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x86, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x00, 0x00, 0x00, 0xcc, 0xf9, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x66, 0x66, 0x74, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0xbe, + 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0x04, 0xfa, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x66, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x24, 0x00, 0x00, 0x00, 0x44, 0xfa, 0xff, 0xff, + 0x15, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x66, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f, + 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x42, 0xfb, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00, 0x88, 0xfa, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x52, 0x65, 0x73, 0x68, + 0x61, 0x70, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x76, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1c, 0x00, + 0x00, 0x00, 0xbc, 0xfa, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x77, 0x69, + 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, + 0xb6, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xfc, 0xfa, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x43, 0x6f, + 0x6e, 0x73, 0x74, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, + 0x2c, 0xfb, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x16, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x5c, 0xfb, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x43, + 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, + 0x00, 0x8c, 0xfb, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x52, 0x65, 0x73, 0x68, 0x61, 0x70, 0x65, 0x2f, 0x73, 0x68, + 0x61, 0x70, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x82, 0xfc, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x24, 0x00, 0x00, 0x00, 0xc8, 0xfb, 0xff, 0xff, 0x17, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x2f, 0x79, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc2, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x08, 0xfc, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, + 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x0a, 0xfd, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x50, 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, + 0x74, 0x5f, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x52, 0xfd, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, + 0x00, 0x00, 0x00, 0x98, 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x32, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x9a, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0xe0, + 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x33, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x29, 0x00, 0x00, 0x00, 0xe2, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x28, 0xfd, 0xff, 0xff, 0x1a, + 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, + 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x34, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, + 0x00, 0x2a, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x00, 0x70, 0xfd, 0xff, 0xff, 0x11, 0x00, 0x00, 0x00, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x63, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x6a, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0xb0, 0xfd, + 0xff, 0xff, 0x13, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, + 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xfe, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x24, 0x00, 0x00, 0x00, 0xf0, 0xfd, 0xff, 0xff, 0x15, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, + 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xee, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0x34, 0xfe, 0xff, + 0xff, 0x15, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2f, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x78, 0xfe, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, + 0x74, 0x5f, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xa8, + 0xfe, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x96, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xdc, 0xfe, 0xff, 0xff, 0x07, 0x00, + 0x00, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x5f, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xca, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, + 0x73, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x1c, 0x00, + 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x2c, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x5f, + 0x66, 0x72, 0x61, 0x6d, 0x65, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, + 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1c, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, + 0xa4, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, + 0x00, 0x10, 0x01, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x50, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x50, 0xfe, 0xff, 0xff, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x5c, 0xfe, 0xff, 0xff, + 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x68, 0xfe, 0xff, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2a, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7c, 0xfe, 0xff, 0xff, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x12, 0x70, 0xfe, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x13, + 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, + 0x4c, 0x6f, 0x67, 0x00, 0x98, 0xfe, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x0a, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x50, 0x43, 0x41, 0x4e, 0x00, 0x00, 0xb8, 0xfe, + 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x23, 0x00, 0x00, 0x00, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x70, 0x65, 0x63, + 0x74, 0x72, 0x61, 0x6c, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0xf0, 0xfe, 0xff, + 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1a, 0x00, 0x00, 0x00, 0x53, 0x69, + 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x71, 0x75, 0x61, 0x72, + 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x20, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x42, 0x61, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x6c, 0xff, 0xff, 0xff, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x0c, 0x00, 0x10, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x35, 0x7c, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x45, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x00, 0x00, + 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0a, + 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x52, 0x66, 0x66, 0x74, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, + 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x12, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, + 0x6e, 0x61, 0x6c, 0x46, 0x66, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x0c, 0x00, + 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x16, 0x0c, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, + 0x6e, 0x61, 0x6c, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x00}; + +} // namespace micro_wake_word +} // namespace esphome + +#endif // USE_ESP_IDF diff --git a/esphome/components/micro_wake_word/micro_wake_word.cpp b/esphome/components/micro_wake_word/micro_wake_word.cpp new file mode 100644 index 0000000000..8a443bc224 --- /dev/null +++ b/esphome/components/micro_wake_word/micro_wake_word.cpp @@ -0,0 +1,503 @@ +#include "micro_wake_word.h" + +/** + * This is a workaround until we can figure out a way to get + * the tflite-micro idf component code available in CI + * + * */ +// +#ifndef CLANG_TIDY + +#ifdef USE_ESP_IDF + +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#include "audio_preprocessor_int8_model_data.h" + +#include +#include +#include + +#include + +namespace esphome { +namespace micro_wake_word { + +static const char *const TAG = "micro_wake_word"; + +static const size_t SAMPLE_RATE_HZ = 16000; // 16 kHz +static const size_t BUFFER_LENGTH = 500; // 0.5 seconds +static const size_t BUFFER_SIZE = SAMPLE_RATE_HZ / 1000 * BUFFER_LENGTH; +static const size_t INPUT_BUFFER_SIZE = 32 * SAMPLE_RATE_HZ / 1000; // 32ms * 16kHz / 1000ms + +float MicroWakeWord::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } + +static const LogString *micro_wake_word_state_to_string(State state) { + switch (state) { + case State::IDLE: + return LOG_STR("IDLE"); + case State::START_MICROPHONE: + return LOG_STR("START_MICROPHONE"); + case State::STARTING_MICROPHONE: + return LOG_STR("STARTING_MICROPHONE"); + case State::DETECTING_WAKE_WORD: + return LOG_STR("DETECTING_WAKE_WORD"); + case State::STOP_MICROPHONE: + return LOG_STR("STOP_MICROPHONE"); + case State::STOPPING_MICROPHONE: + return LOG_STR("STOPPING_MICROPHONE"); + default: + return LOG_STR("UNKNOWN"); + } +} + +void MicroWakeWord::setup() { + ESP_LOGCONFIG(TAG, "Setting up Micro Wake Word..."); + + if (!this->initialize_models()) { + ESP_LOGE(TAG, "Failed to initialize models"); + this->mark_failed(); + return; + } + + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + this->input_buffer_ = allocator.allocate(NEW_SAMPLES_TO_GET); + if (this->input_buffer_ == nullptr) { + ESP_LOGW(TAG, "Could not allocate input buffer"); + this->mark_failed(); + return; + } + + this->ring_buffer_ = RingBuffer::create(BUFFER_SIZE * sizeof(int16_t)); + if (this->ring_buffer_ == nullptr) { + ESP_LOGW(TAG, "Could not allocate ring buffer"); + this->mark_failed(); + return; + } + + ESP_LOGCONFIG(TAG, "Micro Wake Word initialized"); +} + +int MicroWakeWord::read_microphone_() { + size_t bytes_read = this->microphone_->read(this->input_buffer_, NEW_SAMPLES_TO_GET * sizeof(int16_t)); + if (bytes_read == 0) { + 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); + } + return bytes_written; +} + +void MicroWakeWord::loop() { + switch (this->state_) { + case State::IDLE: + break; + case State::START_MICROPHONE: + ESP_LOGD(TAG, "Starting Microphone"); + this->microphone_->start(); + this->set_state_(State::STARTING_MICROPHONE); + this->high_freq_.start(); + break; + case State::STARTING_MICROPHONE: + if (this->microphone_->is_running()) { + this->set_state_(State::DETECTING_WAKE_WORD); + } + break; + case State::DETECTING_WAKE_WORD: + this->read_microphone_(); + if (this->detect_wake_word_()) { + ESP_LOGD(TAG, "Wake Word Detected"); + this->detected_ = true; + this->set_state_(State::STOP_MICROPHONE); + } + break; + case State::STOP_MICROPHONE: + ESP_LOGD(TAG, "Stopping Microphone"); + this->microphone_->stop(); + this->set_state_(State::STOPPING_MICROPHONE); + this->high_freq_.stop(); + break; + case State::STOPPING_MICROPHONE: + if (this->microphone_->is_stopped()) { + this->set_state_(State::IDLE); + if (this->detected_) { + this->detected_ = false; + this->wake_word_detected_trigger_->trigger(""); + } + } + break; + } +} + +void MicroWakeWord::start() { + if (this->is_failed()) { + ESP_LOGW(TAG, "Wake word component is marked as failed. Please check setup logs"); + return; + } + if (this->state_ != State::IDLE) { + ESP_LOGW(TAG, "Wake word is already running"); + return; + } + this->set_state_(State::START_MICROPHONE); +} + +void MicroWakeWord::stop() { + if (this->state_ == State::IDLE) { + ESP_LOGW(TAG, "Wake word is already stopped"); + return; + } + if (this->state_ == State::STOPPING_MICROPHONE) { + ESP_LOGW(TAG, "Wake word is already stopping"); + return; + } + this->set_state_(State::STOP_MICROPHONE); +} + +void MicroWakeWord::set_state_(State state) { + ESP_LOGD(TAG, "State changed from %s to %s", LOG_STR_ARG(micro_wake_word_state_to_string(this->state_)), + LOG_STR_ARG(micro_wake_word_state_to_string(state))); + this->state_ = state; +} + +bool MicroWakeWord::initialize_models() { + ExternalRAMAllocator arena_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + ExternalRAMAllocator features_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + ExternalRAMAllocator audio_samples_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + + this->streaming_tensor_arena_ = arena_allocator.allocate(STREAMING_MODEL_ARENA_SIZE); + if (this->streaming_tensor_arena_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate the streaming model's tensor arena."); + return false; + } + + this->streaming_var_arena_ = arena_allocator.allocate(STREAMING_MODEL_VARIABLE_ARENA_SIZE); + if (this->streaming_var_arena_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate the streaming model variable's tensor arena."); + return false; + } + + this->preprocessor_tensor_arena_ = arena_allocator.allocate(PREPROCESSOR_ARENA_SIZE); + if (this->preprocessor_tensor_arena_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate the audio preprocessor model's tensor arena."); + return false; + } + + this->new_features_data_ = features_allocator.allocate(PREPROCESSOR_FEATURE_SIZE); + if (this->new_features_data_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate the audio features buffer."); + return false; + } + + this->preprocessor_audio_buffer_ = audio_samples_allocator.allocate(SAMPLE_DURATION_COUNT); + if (this->preprocessor_audio_buffer_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate the audio preprocessor's buffer."); + 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"); + return false; + } + + this->streaming_model_ = tflite::GetModel(this->model_start_); + if (this->streaming_model_->version() != TFLITE_SCHEMA_VERSION) { + ESP_LOGE(TAG, "Wake word's streaming model's schema is not supported"); + return false; + } + + static tflite::MicroMutableOpResolver<18> preprocessor_op_resolver; + static tflite::MicroMutableOpResolver<14> streaming_op_resolver; + + if (!this->register_preprocessor_ops_(preprocessor_op_resolver)) + return false; + if (!this->register_streaming_ops_(streaming_op_resolver)) + return false; + + tflite::MicroAllocator *ma = + tflite::MicroAllocator::Create(this->streaming_var_arena_, STREAMING_MODEL_VARIABLE_ARENA_SIZE); + this->mrv_ = tflite::MicroResourceVariables::Create(ma, 15); + + static tflite::MicroInterpreter static_preprocessor_interpreter( + this->preprocessor_model_, preprocessor_op_resolver, this->preprocessor_tensor_arena_, PREPROCESSOR_ARENA_SIZE); + + static tflite::MicroInterpreter static_streaming_interpreter(this->streaming_model_, streaming_op_resolver, + this->streaming_tensor_arena_, + STREAMING_MODEL_ARENA_SIZE, this->mrv_); + + this->preprocessor_interperter_ = &static_preprocessor_interpreter; + this->streaming_interpreter_ = &static_streaming_interpreter; + + // Allocate tensors for each models. + if (this->preprocessor_interperter_->AllocateTensors() != kTfLiteOk) { + ESP_LOGE(TAG, "Failed to allocate tensors for the audio preprocessor"); + return false; + } + if (this->streaming_interpreter_->AllocateTensors() != kTfLiteOk) { + ESP_LOGE(TAG, "Failed to allocate tensors for the streaming model"); + return false; + } + + // Verify input tensor matches expected values + TfLiteTensor *input = this->streaming_interpreter_->input(0); + if ((input->dims->size != 3) || (input->dims->data[0] != 1) || (input->dims->data[0] != 1) || + (input->dims->data[1] != 1) || (input->dims->data[2] != PREPROCESSOR_FEATURE_SIZE)) { + ESP_LOGE(TAG, "Wake word detection model tensor input dimensions is not 1x1x%u", input->dims->data[2]); + return false; + } + + if (input->type != kTfLiteInt8) { + ESP_LOGE(TAG, "Wake word detection model tensor input is not int8."); + return false; + } + + // Verify output tensor matches expected values + TfLiteTensor *output = this->streaming_interpreter_->output(0); + if ((output->dims->size != 2) || (output->dims->data[0] != 1) || (output->dims->data[1] != 1)) { + ESP_LOGE(TAG, "Wake word detection model tensor output dimensions is not 1x1."); + } + + if (output->type != kTfLiteUInt8) { + ESP_LOGE(TAG, "Wake word detection model tensor input is not uint8."); + return false; + } + + this->recent_streaming_probabilities_.resize(this->sliding_window_average_size_, 0.0); + + return true; +} + +bool MicroWakeWord::update_features_() { + // Verify we have enough samples for a feature slice + if (!this->slice_available_()) { + return false; + } + + // Retrieve strided audio samples + int16_t *audio_samples = nullptr; + if (!this->stride_audio_samples_(&audio_samples)) { + return false; + } + + // Compute the features for the newest audio samples + if (!this->generate_single_feature_(audio_samples, SAMPLE_DURATION_COUNT, this->new_features_data_)) { + return false; + } + + return true; +} + +float MicroWakeWord::perform_streaming_inference_() { + TfLiteTensor *input = this->streaming_interpreter_->input(0); + + size_t bytes_to_copy = input->bytes; + + memcpy((void *) (tflite::GetTensorData(input)), (const void *) (this->new_features_data_), bytes_to_copy); + + uint32_t prior_invoke = millis(); + + TfLiteStatus invoke_status = this->streaming_interpreter_->Invoke(); + if (invoke_status != kTfLiteOk) { + ESP_LOGW(TAG, "Streaming Interpreter Invoke failed"); + return false; + } + + ESP_LOGV(TAG, "Streaming Inference Latency=%u ms", (millis() - prior_invoke)); + + TfLiteTensor *output = this->streaming_interpreter_->output(0); + + return static_cast(output->data.uint8[0]) / 255.0; +} + +bool MicroWakeWord::detect_wake_word_() { + // Preprocess the newest audio samples into features + if (!this->update_features_()) { + return false; + } + + // Perform inference + uint32_t streaming_size = micros(); + float streaming_prob = this->perform_streaming_inference_(); + + // Add the most recent probability to the sliding window + this->recent_streaming_probabilities_[this->last_n_index_] = streaming_prob; + ++this->last_n_index_; + if (this->last_n_index_ == this->sliding_window_average_size_) + this->last_n_index_ = 0; + + float sum = 0.0; + for (auto &prob : this->recent_streaming_probabilities_) { + sum += prob; + } + + float sliding_window_average = sum / static_cast(this->sliding_window_average_size_); + + // Ensure we have enough samples since the last positive detection + this->ignore_windows_ = std::min(this->ignore_windows_ + 1, 0); + if (this->ignore_windows_ < 0) { + return false; + } + + // Detect the wake word if the sliding window average is above the cutoff + if (sliding_window_average > this->probability_cutoff_) { + this->ignore_windows_ = -MIN_SLICES_BEFORE_DETECTION; + for (auto &prob : this->recent_streaming_probabilities_) { + prob = 0; + } + return true; + } + + return false; +} + +void MicroWakeWord::set_sliding_window_average_size(size_t size) { + this->sliding_window_average_size_ = size; + this->recent_streaming_probabilities_.resize(this->sliding_window_average_size_, 0.0); +} + +bool MicroWakeWord::slice_available_() { + size_t available = this->ring_buffer_->available(); + + return available > (NEW_SAMPLES_TO_GET * sizeof(int16_t)); +} + +bool MicroWakeWord::stride_audio_samples_(int16_t **audio_samples) { + // 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_), + HISTORY_SAMPLES_TO_KEEP * sizeof(int16_t)); + + if (this->ring_buffer_->available() < NEW_SAMPLES_TO_GET * sizeof(int16_t)) { + ESP_LOGD(TAG, "Audio Buffer not full enough"); + return false; + } + + // 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 + 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)); + + if (bytes_read == 0) { + ESP_LOGE(TAG, "Could not read data from Ring Buffer"); + } else if (bytes_read < NEW_SAMPLES_TO_GET * sizeof(int16_t)) { + ESP_LOGD(TAG, "Partial Read of Data by Model"); + ESP_LOGD(TAG, "Could only read %d bytes when required %d bytes ", bytes_read, + (int) (NEW_SAMPLES_TO_GET * sizeof(int16_t))); + 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; +} + +bool MicroWakeWord::generate_single_feature_(const int16_t *audio_data, const int audio_data_size, + int8_t feature_output[PREPROCESSOR_FEATURE_SIZE]) { + TfLiteTensor *input = this->preprocessor_interperter_->input(0); + TfLiteTensor *output = this->preprocessor_interperter_->output(0); + std::copy_n(audio_data, audio_data_size, tflite::GetTensorData(input)); + + if (this->preprocessor_interperter_->Invoke() != kTfLiteOk) { + ESP_LOGE(TAG, "Failed to preprocess audio for local wake word."); + return false; + } + std::memcpy(feature_output, tflite::GetTensorData(output), PREPROCESSOR_FEATURE_SIZE * sizeof(int8_t)); + + return true; +} + +bool MicroWakeWord::register_preprocessor_ops_(tflite::MicroMutableOpResolver<18> &op_resolver) { + if (op_resolver.AddReshape() != kTfLiteOk) + return false; + if (op_resolver.AddCast() != kTfLiteOk) + return false; + if (op_resolver.AddStridedSlice() != kTfLiteOk) + return false; + if (op_resolver.AddConcatenation() != kTfLiteOk) + return false; + if (op_resolver.AddMul() != kTfLiteOk) + return false; + if (op_resolver.AddAdd() != kTfLiteOk) + return false; + if (op_resolver.AddDiv() != kTfLiteOk) + return false; + if (op_resolver.AddMinimum() != kTfLiteOk) + return false; + if (op_resolver.AddMaximum() != kTfLiteOk) + return false; + if (op_resolver.AddWindow() != kTfLiteOk) + return false; + if (op_resolver.AddFftAutoScale() != kTfLiteOk) + return false; + if (op_resolver.AddRfft() != kTfLiteOk) + return false; + if (op_resolver.AddEnergy() != kTfLiteOk) + return false; + if (op_resolver.AddFilterBank() != kTfLiteOk) + return false; + if (op_resolver.AddFilterBankSquareRoot() != kTfLiteOk) + return false; + if (op_resolver.AddFilterBankSpectralSubtraction() != kTfLiteOk) + return false; + if (op_resolver.AddPCAN() != kTfLiteOk) + return false; + if (op_resolver.AddFilterBankLog() != kTfLiteOk) + return false; + + return true; +} + +bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<14> &op_resolver) { + if (op_resolver.AddCallOnce() != kTfLiteOk) + return false; + if (op_resolver.AddVarHandle() != kTfLiteOk) + return false; + if (op_resolver.AddReshape() != kTfLiteOk) + return false; + if (op_resolver.AddReadVariable() != kTfLiteOk) + return false; + if (op_resolver.AddStridedSlice() != kTfLiteOk) + return false; + if (op_resolver.AddConcatenation() != kTfLiteOk) + return false; + if (op_resolver.AddAssignVariable() != kTfLiteOk) + return false; + if (op_resolver.AddConv2D() != kTfLiteOk) + return false; + if (op_resolver.AddMul() != kTfLiteOk) + return false; + if (op_resolver.AddAdd() != kTfLiteOk) + return false; + if (op_resolver.AddMean() != kTfLiteOk) + return false; + if (op_resolver.AddFullyConnected() != kTfLiteOk) + return false; + if (op_resolver.AddLogistic() != kTfLiteOk) + return false; + if (op_resolver.AddQuantize() != kTfLiteOk) + return false; + + return true; +} + +} // namespace micro_wake_word +} // namespace esphome + +#endif // USE_ESP_IDF + +#endif // CLANG_TIDY diff --git a/esphome/components/micro_wake_word/micro_wake_word.h b/esphome/components/micro_wake_word/micro_wake_word.h new file mode 100644 index 0000000000..82f28b2ebb --- /dev/null +++ b/esphome/components/micro_wake_word/micro_wake_word.h @@ -0,0 +1,204 @@ +#pragma once + +/** + * This is a workaround until we can figure out a way to get + * the tflite-micro idf component code available in CI + * + * */ +// +#ifndef CLANG_TIDY + +#ifdef USE_ESP_IDF + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/ring_buffer.h" + +#include "esphome/components/microphone/microphone.h" + +#include +#include +#include + +namespace esphome { +namespace micro_wake_word { + +// The following are dictated by the preprocessor model +// +// The number of features the audio preprocessor generates per slice +static const uint8_t PREPROCESSOR_FEATURE_SIZE = 40; +// How frequently the preprocessor generates a new set of features +static const uint8_t FEATURE_STRIDE_MS = 20; +// Duration of each slice used as input into the preprocessor +static const uint8_t FEATURE_DURATION_MS = 30; +// Audio sample frequency in hertz +static const uint16_t AUDIO_SAMPLE_FREQUENCY = 16000; +// The number of old audio samples that are saved to be part of the next feature window +static const uint16_t HISTORY_SAMPLES_TO_KEEP = + ((FEATURE_DURATION_MS - FEATURE_STRIDE_MS) * (AUDIO_SAMPLE_FREQUENCY / 1000)); +// The number of new audio samples to receive to be included with the next feature window +static const uint16_t NEW_SAMPLES_TO_GET = (FEATURE_STRIDE_MS * (AUDIO_SAMPLE_FREQUENCY / 1000)); +// The total number of audio samples included in the feature window +static const uint16_t SAMPLE_DURATION_COUNT = FEATURE_DURATION_MS * AUDIO_SAMPLE_FREQUENCY / 1000; +// Number of bytes in memory needed for the preprocessor arena +static const uint32_t PREPROCESSOR_ARENA_SIZE = 9528; + +// The following configure the streaming wake word model +// +// The number of audio slices to process before accepting a positive detection +static const uint8_t MIN_SLICES_BEFORE_DETECTION = 74; + +// Number of bytes in memory needed for the streaming wake word model +static const uint32_t STREAMING_MODEL_ARENA_SIZE = 64000; +static const uint32_t STREAMING_MODEL_VARIABLE_ARENA_SIZE = 1024; + +enum State { + IDLE, + START_MICROPHONE, + STARTING_MICROPHONE, + DETECTING_WAKE_WORD, + STOP_MICROPHONE, + STOPPING_MICROPHONE, +}; + +class MicroWakeWord : public Component { + public: + void setup() override; + void loop() override; + float get_setup_priority() const override; + + void start(); + void stop(); + + bool is_running() const { return this->state_ != State::IDLE; } + + bool initialize_models(); + + // Increasing either of these will reduce the rate of false acceptances while increasing the false rejection rate + void set_probability_cutoff(float probability_cutoff) { this->probability_cutoff_ = probability_cutoff; } + void set_sliding_window_average_size(size_t size); + + void set_microphone(microphone::Microphone *microphone) { this->microphone_ = microphone; } + + Trigger *get_wake_word_detected_trigger() const { return this->wake_word_detected_trigger_; } + + void set_model_start(const uint8_t *model_start) { this->model_start_ = model_start; } + void set_wake_word(const std::string &wake_word) { this->wake_word_ = wake_word; } + + protected: + void set_state_(State state); + int read_microphone_(); + + const uint8_t *model_start_; + std::string wake_word_; + + microphone::Microphone *microphone_{nullptr}; + Trigger *wake_word_detected_trigger_ = new Trigger(); + State state_{State::IDLE}; + HighFrequencyLoopRequester high_freq_; + + std::unique_ptr ring_buffer_; + + int16_t *input_buffer_; + + const tflite::Model *preprocessor_model_{nullptr}; + const tflite::Model *streaming_model_{nullptr}; + tflite::MicroInterpreter *streaming_interpreter_{nullptr}; + tflite::MicroInterpreter *preprocessor_interperter_{nullptr}; + + std::vector recent_streaming_probabilities_; + size_t last_n_index_{0}; + + float probability_cutoff_{0.5}; + size_t sliding_window_average_size_{10}; + + // When the wake word detection first starts or after the word has been detected once, we ignore this many audio + // feature slices before accepting a positive detection again + int16_t ignore_windows_{-MIN_SLICES_BEFORE_DETECTION}; + + uint8_t *streaming_var_arena_{nullptr}; + uint8_t *streaming_tensor_arena_{nullptr}; + uint8_t *preprocessor_tensor_arena_{nullptr}; + int8_t *new_features_data_{nullptr}; + + tflite::MicroResourceVariables *mrv_{nullptr}; + + // Stores audio fed into feature generator preprocessor + int16_t *preprocessor_audio_buffer_; + int16_t *preprocessor_stride_buffer_; + + bool detected_{false}; + + /** Detects if wake word has been said + * + * If enough audio samples are available, it will generate one slice of new features. + * If the streaming model predicts the wake word, then the nonstreaming model confirms it. + * @param ring_Buffer Ring buffer containing raw audio samples + * @return True if the wake word is detected, false otherwise + */ + bool detect_wake_word_(); + + /// @brief Returns true if there are enough audio samples in the buffer to generate another slice of features + bool slice_available_(); + + /** Shifts previous feature slices over by one and generates a new slice of features + * + * @param ring_buffer ring buffer containing raw audio samples + * @return True if a new slice of features was generated, false otherwise + */ + bool update_features_(); + + /** Generates features from audio samples + * + * Adapted from TFLite micro speech example + * @param audio_data Pointer to array with the audio samples + * @param audio_data_size The number of samples to use as input to the preprocessor model + * @param feature_output Array that will store the features + * @return True if successful, false otherwise. + */ + bool generate_single_feature_(const int16_t *audio_data, int audio_data_size, + int8_t feature_output[PREPROCESSOR_FEATURE_SIZE]); + + /** Performs inference over the most recent feature slice with the streaming model + * + * @return Probability of the wake word between 0.0 and 1.0 + */ + float perform_streaming_inference_(); + + /** Strides the audio samples by keeping the last 10 ms of the previous slice + * + * Adapted from the TFLite micro speech example + * @param ring_buffer Ring buffer containing raw audio samples + * @param audio_samples Pointer to an array that will store the strided audio samples + * @return True if successful, false otherwise + */ + bool stride_audio_samples_(int16_t **audio_samples); + + /// @brief Returns true if successfully registered the preprocessor's TensorFlow operations + 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); +}; + +template class StartAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->start(); } +}; + +template class StopAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->stop(); } +}; + +template class IsRunningCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->is_running(); } +}; + +} // namespace micro_wake_word +} // namespace esphome + +#endif // USE_ESP_IDF + +#endif // CLANG_TIDY diff --git a/tests/components/micro_wake_word/test.esp32-s3-idf.yaml b/tests/components/micro_wake_word/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..c0f3593cc6 --- /dev/null +++ b/tests/components/micro_wake_word/test.esp32-s3-idf.yaml @@ -0,0 +1,15 @@ +i2s_audio: + i2s_lrclk_pin: GPIO18 + i2s_bclk_pin: GPIO19 + +microphone: + - platform: i2s_audio + id: echo_microphone + i2s_din_pin: GPIO17 + adc_type: external + pdm: true + +micro_wake_word: + model: hey_jarvis + on_wake_word_detected: + - logger.log: "Wake word detected" From 47d1a648941260a3260c9bddd3932e426a5ba334 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 13 Feb 2024 09:45:31 +1300 Subject: [PATCH 190/193] Bump version to 2024.3.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 8f9606c5fd..20ad564291 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.1.0-dev" +__version__ = "2024.3.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 7baf091d47cbfb426918a0a836bd4dda8c8eb93b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:29:54 +1300 Subject: [PATCH 191/193] Bump openssh-client to 1:9.2p1-2+deb12u2 (#6216) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index b28ca2ba66..5d9ece16a1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -35,7 +35,7 @@ RUN \ iputils-ping=3:20221126-1 \ git=1:2.39.2-1.1 \ curl=7.88.1-10+deb12u5 \ - openssh-client=1:9.2p1-2+deb12u1 \ + openssh-client=1:9.2p1-2+deb12u2 \ python3-cffi=1.15.1-5 \ libcairo2=1.16.0-7 \ libmagic1=1:5.44-3 \ From 27a3a081c3aa50c7591565cc9eb45b70e484e023 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Thu, 15 Feb 2024 15:47:42 -0600 Subject: [PATCH 192/193] AUTO_LOAD `sensor` for `shelly_dimmer` (#6223) --- esphome/components/shelly_dimmer/light.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/shelly_dimmer/light.py b/esphome/components/shelly_dimmer/light.py index 5bdb54baf5..625784427f 100644 --- a/esphome/components/shelly_dimmer/light.py +++ b/esphome/components/shelly_dimmer/light.py @@ -29,7 +29,8 @@ from esphome.const import ( from esphome.core import HexInt, CORE DOMAIN = "shelly_dimmer" -DEPENDENCIES = ["sensor", "uart", "esp8266"] +AUTO_LOAD = ["sensor"] +DEPENDENCIES = ["uart", "esp8266"] shelly_dimmer_ns = cg.esphome_ns.namespace("shelly_dimmer") ShellyDimmer = shelly_dimmer_ns.class_( From db9d837d296a03f83475a1ace1243914e0c37c86 Mon Sep 17 00:00:00 2001 From: kahrendt Date: Sun, 18 Feb 2024 00:50:24 -0500 Subject: [PATCH 193/193] Add more debugging logs to microWakeWord (#6238) --- .../components/micro_wake_word/__init__.py | 2 +- .../micro_wake_word/micro_wake_word.cpp | 44 +++++++++++++------ .../micro_wake_word/micro_wake_word.h | 3 ++ 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py index 2a84b7d74b..38202bdfb9 100644 --- a/esphome/components/micro_wake_word/__init__.py +++ b/esphome/components/micro_wake_word/__init__.py @@ -261,7 +261,7 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(MicroWakeWord), cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone), - cv.Optional(CONF_PROBABILITY_CUTOFF): cv.float_, + cv.Optional(CONF_PROBABILITY_CUTOFF): cv.percentage, cv.Optional(CONF_SLIDING_WINDOW_AVERAGE_SIZE): cv.positive_int, cv.Optional(CONF_ON_WAKE_WORD_DETECTED): automation.validate_automation( single=True diff --git a/esphome/components/micro_wake_word/micro_wake_word.cpp b/esphome/components/micro_wake_word/micro_wake_word.cpp index 8a443bc224..f0b3d55a9d 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.cpp +++ b/esphome/components/micro_wake_word/micro_wake_word.cpp @@ -53,8 +53,15 @@ static const LogString *micro_wake_word_state_to_string(State state) { } } +void MicroWakeWord::dump_config() { + ESP_LOGCONFIG(TAG, "microWakeWord:"); + ESP_LOGCONFIG(TAG, " Wake Word: %s", this->get_wake_word().c_str()); + ESP_LOGCONFIG(TAG, " Probability cutoff: %.3f", this->probability_cutoff_); + ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_average_size_); +} + void MicroWakeWord::setup() { - ESP_LOGCONFIG(TAG, "Setting up Micro Wake Word..."); + ESP_LOGCONFIG(TAG, "Setting up microWakeWord..."); if (!this->initialize_models()) { ESP_LOGE(TAG, "Failed to initialize models"); @@ -63,7 +70,7 @@ void MicroWakeWord::setup() { } ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); - this->input_buffer_ = allocator.allocate(NEW_SAMPLES_TO_GET); + this->input_buffer_ = allocator.allocate(INPUT_BUFFER_SIZE * sizeof(int16_t)); if (this->input_buffer_ == nullptr) { ESP_LOGW(TAG, "Could not allocate input buffer"); this->mark_failed(); @@ -81,7 +88,7 @@ void MicroWakeWord::setup() { } int MicroWakeWord::read_microphone_() { - size_t bytes_read = this->microphone_->read(this->input_buffer_, NEW_SAMPLES_TO_GET * sizeof(int16_t)); + size_t bytes_read = this->microphone_->read(this->input_buffer_, INPUT_BUFFER_SIZE * sizeof(int16_t)); if (bytes_read == 0) { return 0; } @@ -279,11 +286,6 @@ bool MicroWakeWord::initialize_models() { } bool MicroWakeWord::update_features_() { - // Verify we have enough samples for a feature slice - if (!this->slice_available_()) { - return false; - } - // Retrieve strided audio samples int16_t *audio_samples = nullptr; if (!this->stride_audio_samples_(&audio_samples)) { @@ -369,20 +371,36 @@ 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)); } bool MicroWakeWord::stride_audio_samples_(int16_t **audio_samples) { + if (!this->slice_available_()) { + 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_), HISTORY_SAMPLES_TO_KEEP * sizeof(int16_t)); - if (this->ring_buffer_->available() < NEW_SAMPLES_TO_GET * sizeof(int16_t)) { - ESP_LOGD(TAG, "Audio Buffer not full enough"); - return false; - } - // 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 size_t bytes_read = this->ring_buffer_->read((void *) (this->preprocessor_audio_buffer_ + HISTORY_SAMPLES_TO_KEEP), diff --git a/esphome/components/micro_wake_word/micro_wake_word.h b/esphome/components/micro_wake_word/micro_wake_word.h index 82f28b2ebb..27d05c3e09 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.h +++ b/esphome/components/micro_wake_word/micro_wake_word.h @@ -66,6 +66,7 @@ class MicroWakeWord : public Component { void setup() override; void loop() override; float get_setup_priority() const override; + void dump_config() override; void start(); void stop(); @@ -74,6 +75,8 @@ class MicroWakeWord : public Component { bool initialize_models(); + std::string get_wake_word() { return this->wake_word_; } + // Increasing either of these will reduce the rate of false acceptances while increasing the false rejection rate void set_probability_cutoff(float probability_cutoff) { this->probability_cutoff_ = probability_cutoff; } void set_sliding_window_average_size(size_t size);