Merge branch 'duplicate_webserver_code' into integration

This commit is contained in:
J. Nick Koston
2025-06-27 00:09:07 +02:00
9 changed files with 136 additions and 159 deletions

View File

@@ -491,7 +491,7 @@ esphome/components/vbus/* @ssieb
esphome/components/veml3235/* @kbx81 esphome/components/veml3235/* @kbx81
esphome/components/veml7700/* @latonita esphome/components/veml7700/* @latonita
esphome/components/version/* @esphome/core esphome/components/version/* @esphome/core
esphome/components/voice_assistant/* @jesserockz esphome/components/voice_assistant/* @jesserockz @kahrendt
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54 esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
esphome/components/watchdog/* @oarcher esphome/components/watchdog/* @oarcher
esphome/components/waveshare_epaper/* @clydebarrow esphome/components/waveshare_epaper/* @clydebarrow

View File

@@ -5,6 +5,7 @@
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE #if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
#include "esp_crt_bundle.h" #include "esp_crt_bundle.h"
@@ -16,13 +17,13 @@ namespace audio {
static const uint32_t READ_WRITE_TIMEOUT_MS = 20; static const uint32_t READ_WRITE_TIMEOUT_MS = 20;
static const uint32_t CONNECTION_TIMEOUT_MS = 5000; static const uint32_t CONNECTION_TIMEOUT_MS = 5000;
static const uint8_t MAX_FETCHING_HEADER_ATTEMPTS = 6;
// The number of times the http read times out with no data before throwing an error
static const uint32_t ERROR_COUNT_NO_DATA_READ_TIMEOUT = 100;
static const size_t HTTP_STREAM_BUFFER_SIZE = 2048; static const size_t HTTP_STREAM_BUFFER_SIZE = 2048;
static const uint8_t MAX_REDIRECTION = 5; static const uint8_t MAX_REDIRECTIONS = 5;
static const char *const TAG = "audio_reader";
// Some common HTTP status codes - borrowed from http_request component accessed 20241224 // Some common HTTP status codes - borrowed from http_request component accessed 20241224
enum HttpStatus { enum HttpStatus {
@@ -94,7 +95,7 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
client_config.url = uri.c_str(); client_config.url = uri.c_str();
client_config.cert_pem = nullptr; client_config.cert_pem = nullptr;
client_config.disable_auto_redirect = false; client_config.disable_auto_redirect = false;
client_config.max_redirection_count = 10; client_config.max_redirection_count = MAX_REDIRECTIONS;
client_config.event_handler = http_event_handler; client_config.event_handler = http_event_handler;
client_config.user_data = this; client_config.user_data = this;
client_config.buffer_size = HTTP_STREAM_BUFFER_SIZE; client_config.buffer_size = HTTP_STREAM_BUFFER_SIZE;
@@ -116,12 +117,29 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
esp_err_t err = esp_http_client_open(this->client_, 0); esp_err_t err = esp_http_client_open(this->client_, 0);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open URL");
this->cleanup_connection_(); this->cleanup_connection_();
return err; return err;
} }
int64_t header_length = esp_http_client_fetch_headers(this->client_); int64_t header_length = esp_http_client_fetch_headers(this->client_);
uint8_t reattempt_count = 0;
while ((header_length < 0) && (reattempt_count < MAX_FETCHING_HEADER_ATTEMPTS)) {
this->cleanup_connection_();
if (header_length != -ESP_ERR_HTTP_EAGAIN) {
// Serious error, no recovery
return ESP_FAIL;
} else {
// Reconnect from a fresh state to avoid a bug where it never reads the headers even if made available
this->client_ = esp_http_client_init(&client_config);
esp_http_client_open(this->client_, 0);
header_length = esp_http_client_fetch_headers(this->client_);
++reattempt_count;
}
}
if (header_length < 0) { if (header_length < 0) {
ESP_LOGE(TAG, "Failed to fetch headers");
this->cleanup_connection_(); this->cleanup_connection_();
return ESP_FAIL; return ESP_FAIL;
} }
@@ -135,7 +153,7 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
ssize_t redirect_count = 0; ssize_t redirect_count = 0;
while ((esp_http_client_set_redirection(this->client_) == ESP_OK) && (redirect_count < MAX_REDIRECTION)) { while ((esp_http_client_set_redirection(this->client_) == ESP_OK) && (redirect_count < MAX_REDIRECTIONS)) {
err = esp_http_client_open(this->client_, 0); err = esp_http_client_open(this->client_, 0);
if (err != ESP_OK) { if (err != ESP_OK) {
this->cleanup_connection_(); this->cleanup_connection_();
@@ -267,27 +285,29 @@ AudioReaderState AudioReader::http_read_() {
return AudioReaderState::FINISHED; return AudioReaderState::FINISHED;
} }
} else if (this->output_transfer_buffer_->free() > 0) { } else if (this->output_transfer_buffer_->free() > 0) {
size_t bytes_to_read = this->output_transfer_buffer_->free(); int received_len = esp_http_client_read(this->client_, (char *) this->output_transfer_buffer_->get_buffer_end(),
int received_len = this->output_transfer_buffer_->free());
esp_http_client_read(this->client_, (char *) this->output_transfer_buffer_->get_buffer_end(), bytes_to_read);
if (received_len > 0) { if (received_len > 0) {
this->output_transfer_buffer_->increase_buffer_length(received_len); this->output_transfer_buffer_->increase_buffer_length(received_len);
this->last_data_read_ms_ = millis(); this->last_data_read_ms_ = millis();
} else if (received_len < 0) { return AudioReaderState::READING;
} else if (received_len <= 0) {
// HTTP read error // HTTP read error
this->cleanup_connection_(); if (received_len == -1) {
return AudioReaderState::FAILED; // A true connection error occured, no chance at recovery
} else { this->cleanup_connection_();
if (bytes_to_read > 0) { return AudioReaderState::FAILED;
// Read timed out
if ((millis() - this->last_data_read_ms_) > CONNECTION_TIMEOUT_MS) {
this->cleanup_connection_();
return AudioReaderState::FAILED;
}
delay(READ_WRITE_TIMEOUT_MS);
} }
// Read timed out, manually verify if it has been too long since the last successful read
if ((millis() - this->last_data_read_ms_) > MAX_FETCHING_HEADER_ATTEMPTS * CONNECTION_TIMEOUT_MS) {
ESP_LOGE(TAG, "Timed out");
this->cleanup_connection_();
return AudioReaderState::FAILED;
}
delay(READ_WRITE_TIMEOUT_MS);
} }
} }

View File

@@ -343,13 +343,12 @@ void AudioPipeline::read_task(void *params) {
xEventGroupSetBits(this_pipeline->event_group_, EventGroupBits::READER_MESSAGE_FINISHED); xEventGroupSetBits(this_pipeline->event_group_, EventGroupBits::READER_MESSAGE_FINISHED);
// Wait until the pipeline notifies us the source of the media file // Wait until the pipeline notifies us the source of the media file
EventBits_t event_bits = EventBits_t event_bits = xEventGroupWaitBits(
xEventGroupWaitBits(this_pipeline->event_group_, this_pipeline->event_group_,
EventGroupBits::READER_COMMAND_INIT_FILE | EventGroupBits::READER_COMMAND_INIT_HTTP | EventGroupBits::READER_COMMAND_INIT_FILE | EventGroupBits::READER_COMMAND_INIT_HTTP, // Bit message to read
EventGroupBits::PIPELINE_COMMAND_STOP, // Bit message to read pdFALSE, // Clear the bit on exit
pdFALSE, // Clear the bit on exit pdFALSE, // Wait for all the bits,
pdFALSE, // Wait for all the bits, portMAX_DELAY); // Block indefinitely until bit is set
portMAX_DELAY); // Block indefinitely until bit is set
if (!(event_bits & EventGroupBits::PIPELINE_COMMAND_STOP)) { if (!(event_bits & EventGroupBits::PIPELINE_COMMAND_STOP)) {
xEventGroupClearBits(this_pipeline->event_group_, EventGroupBits::READER_MESSAGE_FINISHED | xEventGroupClearBits(this_pipeline->event_group_, EventGroupBits::READER_MESSAGE_FINISHED |
@@ -434,12 +433,12 @@ void AudioPipeline::decode_task(void *params) {
xEventGroupSetBits(this_pipeline->event_group_, EventGroupBits::DECODER_MESSAGE_FINISHED); xEventGroupSetBits(this_pipeline->event_group_, EventGroupBits::DECODER_MESSAGE_FINISHED);
// Wait until the reader notifies us that the media type is available // Wait until the reader notifies us that the media type is available
EventBits_t event_bits = xEventGroupWaitBits(this_pipeline->event_group_, EventBits_t event_bits =
EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE | xEventGroupWaitBits(this_pipeline->event_group_,
EventGroupBits::PIPELINE_COMMAND_STOP, // Bit message to read EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE, // Bit message to read
pdFALSE, // Clear the bit on exit pdFALSE, // Clear the bit on exit
pdFALSE, // Wait for all the bits, pdFALSE, // Wait for all the bits,
portMAX_DELAY); // Block indefinitely until bit is set portMAX_DELAY); // Block indefinitely until bit is set
xEventGroupClearBits(this_pipeline->event_group_, xEventGroupClearBits(this_pipeline->event_group_,
EventGroupBits::DECODER_MESSAGE_FINISHED | EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE); EventGroupBits::DECODER_MESSAGE_FINISHED | EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE);

View File

@@ -17,10 +17,11 @@ from esphome.const import (
AUTO_LOAD = ["socket"] AUTO_LOAD = ["socket"]
DEPENDENCIES = ["api", "microphone"] DEPENDENCIES = ["api", "microphone"]
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz", "@kahrendt"]
CONF_ON_END = "on_end" CONF_ON_END = "on_end"
CONF_ON_INTENT_END = "on_intent_end" CONF_ON_INTENT_END = "on_intent_end"
CONF_ON_INTENT_PROGRESS = "on_intent_progress"
CONF_ON_INTENT_START = "on_intent_start" CONF_ON_INTENT_START = "on_intent_start"
CONF_ON_LISTENING = "on_listening" CONF_ON_LISTENING = "on_listening"
CONF_ON_START = "on_start" CONF_ON_START = "on_start"
@@ -136,6 +137,9 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_ON_INTENT_START): automation.validate_automation( cv.Optional(CONF_ON_INTENT_START): automation.validate_automation(
single=True single=True
), ),
cv.Optional(CONF_ON_INTENT_PROGRESS): automation.validate_automation(
single=True
),
cv.Optional(CONF_ON_INTENT_END): automation.validate_automation( cv.Optional(CONF_ON_INTENT_END): automation.validate_automation(
single=True single=True
), ),
@@ -282,6 +286,13 @@ async def to_code(config):
config[CONF_ON_INTENT_START], config[CONF_ON_INTENT_START],
) )
if CONF_ON_INTENT_PROGRESS in config:
await automation.build_automation(
var.get_intent_progress_trigger(),
[(cg.std_string, "x")],
config[CONF_ON_INTENT_PROGRESS],
)
if CONF_ON_INTENT_END in config: if CONF_ON_INTENT_END in config:
await automation.build_automation( await automation.build_automation(
var.get_intent_end_trigger(), var.get_intent_end_trigger(),

View File

@@ -555,7 +555,7 @@ void VoiceAssistant::request_stop() {
break; break;
case State::AWAITING_RESPONSE: case State::AWAITING_RESPONSE:
this->signal_stop_(); this->signal_stop_();
break; // Fallthrough intended to stop a streaming TTS announcement that has potentially started
case State::STREAMING_RESPONSE: case State::STREAMING_RESPONSE:
#ifdef USE_MEDIA_PLAYER #ifdef USE_MEDIA_PLAYER
// Stop any ongoing media player announcement // Stop any ongoing media player announcement
@@ -599,6 +599,14 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
switch (msg.event_type) { switch (msg.event_type) {
case api::enums::VOICE_ASSISTANT_RUN_START: case api::enums::VOICE_ASSISTANT_RUN_START:
ESP_LOGD(TAG, "Assist Pipeline running"); ESP_LOGD(TAG, "Assist Pipeline running");
#ifdef USE_MEDIA_PLAYER
this->started_streaming_tts_ = false;
for (auto arg : msg.data) {
if (arg.name == "url") {
this->tts_response_url_ = std::move(arg.value);
}
}
#endif
this->defer([this]() { this->start_trigger_->trigger(); }); this->defer([this]() { this->start_trigger_->trigger(); });
break; break;
case api::enums::VOICE_ASSISTANT_WAKE_WORD_START: case api::enums::VOICE_ASSISTANT_WAKE_WORD_START:
@@ -622,6 +630,8 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
if (text.empty()) { if (text.empty()) {
ESP_LOGW(TAG, "No text in STT_END event"); ESP_LOGW(TAG, "No text in STT_END event");
return; return;
} else if (text.length() > 500) {
text = text.substr(0, 497) + "...";
} }
ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str()); ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str());
this->defer([this, text]() { this->stt_end_trigger_->trigger(text); }); this->defer([this, text]() { this->stt_end_trigger_->trigger(text); });
@@ -631,6 +641,27 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
ESP_LOGD(TAG, "Intent started"); ESP_LOGD(TAG, "Intent started");
this->defer([this]() { this->intent_start_trigger_->trigger(); }); this->defer([this]() { this->intent_start_trigger_->trigger(); });
break; break;
case api::enums::VOICE_ASSISTANT_INTENT_PROGRESS: {
ESP_LOGD(TAG, "Intent progress");
std::string tts_url_for_trigger = "";
#ifdef USE_MEDIA_PLAYER
if (this->media_player_ != nullptr) {
for (const auto &arg : msg.data) {
if ((arg.name == "tts_start_streaming") && (arg.value == "1") && !this->tts_response_url_.empty()) {
this->media_player_->make_call().set_media_url(this->tts_response_url_).set_announcement(true).perform();
this->media_player_wait_for_announcement_start_ = true;
this->media_player_wait_for_announcement_end_ = false;
this->started_streaming_tts_ = true;
tts_url_for_trigger = this->tts_response_url_;
this->tts_response_url_.clear(); // Reset streaming URL
}
}
}
#endif
this->defer([this, tts_url_for_trigger]() { this->intent_progress_trigger_->trigger(tts_url_for_trigger); });
break;
}
case api::enums::VOICE_ASSISTANT_INTENT_END: { case api::enums::VOICE_ASSISTANT_INTENT_END: {
for (auto arg : msg.data) { for (auto arg : msg.data) {
if (arg.name == "conversation_id") { if (arg.name == "conversation_id") {
@@ -653,6 +684,9 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
ESP_LOGW(TAG, "No text in TTS_START event"); ESP_LOGW(TAG, "No text in TTS_START event");
return; return;
} }
if (text.length() > 500) {
text = text.substr(0, 497) + "...";
}
ESP_LOGD(TAG, "Response: \"%s\"", text.c_str()); ESP_LOGD(TAG, "Response: \"%s\"", text.c_str());
this->defer([this, text]() { this->defer([this, text]() {
this->tts_start_trigger_->trigger(text); this->tts_start_trigger_->trigger(text);
@@ -678,7 +712,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
ESP_LOGD(TAG, "Response URL: \"%s\"", url.c_str()); ESP_LOGD(TAG, "Response URL: \"%s\"", url.c_str());
this->defer([this, url]() { this->defer([this, url]() {
#ifdef USE_MEDIA_PLAYER #ifdef USE_MEDIA_PLAYER
if (this->media_player_ != nullptr) { if ((this->media_player_ != nullptr) && (!this->started_streaming_tts_)) {
this->media_player_->make_call().set_media_url(url).set_announcement(true).perform(); this->media_player_->make_call().set_media_url(url).set_announcement(true).perform();
this->media_player_wait_for_announcement_start_ = true; this->media_player_wait_for_announcement_start_ = true;

View File

@@ -177,6 +177,7 @@ class VoiceAssistant : public Component {
Trigger<> *get_intent_end_trigger() const { return this->intent_end_trigger_; } Trigger<> *get_intent_end_trigger() const { return this->intent_end_trigger_; }
Trigger<> *get_intent_start_trigger() const { return this->intent_start_trigger_; } Trigger<> *get_intent_start_trigger() const { return this->intent_start_trigger_; }
Trigger<std::string> *get_intent_progress_trigger() const { return this->intent_progress_trigger_; }
Trigger<> *get_listening_trigger() const { return this->listening_trigger_; } Trigger<> *get_listening_trigger() const { return this->listening_trigger_; }
Trigger<> *get_end_trigger() const { return this->end_trigger_; } Trigger<> *get_end_trigger() const { return this->end_trigger_; }
Trigger<> *get_start_trigger() const { return this->start_trigger_; } Trigger<> *get_start_trigger() const { return this->start_trigger_; }
@@ -233,6 +234,7 @@ class VoiceAssistant : public Component {
Trigger<> *tts_stream_start_trigger_ = new Trigger<>(); Trigger<> *tts_stream_start_trigger_ = new Trigger<>();
Trigger<> *tts_stream_end_trigger_ = new Trigger<>(); Trigger<> *tts_stream_end_trigger_ = new Trigger<>();
#endif #endif
Trigger<std::string> *intent_progress_trigger_ = new Trigger<std::string>();
Trigger<> *wake_word_detected_trigger_ = new Trigger<>(); Trigger<> *wake_word_detected_trigger_ = new Trigger<>();
Trigger<std::string> *stt_end_trigger_ = new Trigger<std::string>(); Trigger<std::string> *stt_end_trigger_ = new Trigger<std::string>();
Trigger<std::string> *tts_end_trigger_ = new Trigger<std::string>(); Trigger<std::string> *tts_end_trigger_ = new Trigger<std::string>();
@@ -268,6 +270,8 @@ class VoiceAssistant : public Component {
#endif #endif
#ifdef USE_MEDIA_PLAYER #ifdef USE_MEDIA_PLAYER
media_player::MediaPlayer *media_player_{nullptr}; media_player::MediaPlayer *media_player_{nullptr};
std::string tts_response_url_{""};
bool started_streaming_tts_{false};
bool media_player_wait_for_announcement_start_{false}; bool media_player_wait_for_announcement_start_{false};
bool media_player_wait_for_announcement_end_{false}; bool media_player_wait_for_announcement_end_{false};
#endif #endif

View File

@@ -411,12 +411,7 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail
} }
set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config); set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
if (!obj->get_unit_of_measurement().empty()) if (!obj->get_unit_of_measurement().empty())
root["uom"] = obj->get_unit_of_measurement(); root["uom"] = obj->get_unit_of_measurement();
} }
@@ -460,12 +455,7 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std:
return json::build_json([this, obj, value, start_config](JsonObject root) { return json::build_json([this, obj, value, start_config](JsonObject root) {
set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config); set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -517,12 +507,7 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail
set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config); set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
root["assumed_state"] = obj->assumed_state(); root["assumed_state"] = obj->assumed_state();
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -562,12 +547,7 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config)
return json::build_json([this, obj, start_config](JsonObject root) { return json::build_json([this, obj, start_config](JsonObject root) {
set_json_id(root, obj, "button-" + obj->get_object_id(), start_config); set_json_id(root, obj, "button-" + obj->get_object_id(), start_config);
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -609,12 +589,7 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool
set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value, set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
start_config); start_config);
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -699,12 +674,7 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
if (obj->get_traits().supports_oscillation()) if (obj->get_traits().supports_oscillation())
root["oscillation"] = obj->oscillating; root["oscillation"] = obj->oscillating;
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -824,12 +794,7 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi
for (auto const &option : obj->get_effects()) { for (auto const &option : obj->get_effects()) {
opt.add(option->get_name()); opt.add(option->get_name());
} }
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -914,12 +879,7 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
if (obj->get_traits().get_supports_tilt()) if (obj->get_traits().get_supports_tilt())
root["tilt"] = obj->tilt; root["tilt"] = obj->tilt;
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -984,12 +944,7 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
root["mode"] = (int) obj->traits.get_mode(); root["mode"] = (int) obj->traits.get_mode();
if (!obj->traits.get_unit_of_measurement().empty()) if (!obj->traits.get_unit_of_measurement().empty())
root["uom"] = obj->traits.get_unit_of_measurement(); root["uom"] = obj->traits.get_unit_of_measurement();
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
if (std::isnan(value)) { if (std::isnan(value)) {
root["value"] = "\"NaN\""; root["value"] = "\"NaN\"";
@@ -1062,12 +1017,7 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con
root["value"] = value; root["value"] = value;
root["state"] = value; root["state"] = value;
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -1129,12 +1079,7 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con
root["value"] = value; root["value"] = value;
root["state"] = value; root["state"] = value;
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -1197,12 +1142,7 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s
root["value"] = value; root["value"] = value;
root["state"] = value; root["state"] = value;
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -1267,12 +1207,7 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json
root["value"] = value; root["value"] = value;
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
root["mode"] = (int) obj->traits.get_mode(); root["mode"] = (int) obj->traits.get_mode();
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -1332,12 +1267,7 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value
for (auto &option : obj->traits.get_options()) { for (auto &option : obj->traits.get_options()) {
opt.add(option); opt.add(option);
} }
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -1458,12 +1388,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
for (auto const &custom_preset : traits.get_supported_custom_presets()) for (auto const &custom_preset : traits.get_supported_custom_presets())
opt.add(custom_preset); opt.add(custom_preset);
} }
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
bool has_state = false; bool has_state = false;
@@ -1560,12 +1485,7 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet
set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value, set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
start_config); start_config);
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -1641,12 +1561,7 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
if (obj->get_traits().get_supports_position()) if (obj->get_traits().get_supports_position())
root["position"] = obj->position; root["position"] = obj->position;
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -1718,12 +1633,7 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro
set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(), set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config); PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -1772,12 +1682,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty
event_types.add(event_type); event_types.add(event_type);
} }
root["device_class"] = obj->get_device_class(); root["device_class"] = obj->get_device_class();
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -1845,12 +1750,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c
root["title"] = obj->update_info.title; root["title"] = obj->update_info.title;
root["summary"] = obj->update_info.summary; root["summary"] = obj->update_info.summary;
root["release_url"] = obj->update_info.release_url; root["release_url"] = obj->update_info.release_url;
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { this->add_sorting_info_(root, obj);
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
}
}
} }
}); });
} }
@@ -2168,6 +2068,15 @@ void WebServer::add_sorting_group(uint64_t group_id, const std::string &group_na
this->sorting_groups_[group_id] = SortingGroup{group_name, weight}; this->sorting_groups_[group_id] = SortingGroup{group_name, weight};
} }
void WebServer::add_sorting_info_(JsonObject &root, EntityBase *entity) {
if (this->sorting_entitys_.find(entity) != this->sorting_entitys_.end()) {
root["sorting_weight"] = this->sorting_entitys_[entity].weight;
if (this->sorting_groups_.find(this->sorting_entitys_[entity].group_id) != this->sorting_groups_.end()) {
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[entity].group_id].name;
}
}
}
void WebServer::schedule_(std::function<void()> &&f) { void WebServer::schedule_(std::function<void()> &&f) {
#ifdef USE_ESP32 #ifdef USE_ESP32
xSemaphoreTake(this->to_schedule_lock_, portMAX_DELAY); xSemaphoreTake(this->to_schedule_lock_, portMAX_DELAY);

View File

@@ -482,6 +482,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
bool include_internal_{false}; bool include_internal_{false};
protected: protected:
void add_sorting_info_(JsonObject &root, EntityBase *entity);
void schedule_(std::function<void()> &&f); void schedule_(std::function<void()> &&f);
web_server_base::WebServerBase *base_; web_server_base::WebServerBase *base_;
#ifdef USE_ARDUINO #ifdef USE_ARDUINO

View File

@@ -15,4 +15,3 @@ packages:
file: $component_test_file file: $component_test_file
vars: vars:
component_test_file: $component_test_file component_test_file: $component_test_file