mirror of
https://github.com/esphome/esphome.git
synced 2026-03-01 02:14:19 -07:00
Merge branch 'dev' into esp32_touch_new_driver
This commit is contained in:
@@ -433,8 +433,8 @@ void APIServer::handle_action_response(uint32_t call_id, bool success, StringRef
|
||||
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
// Helper to add subscription (reduces duplication)
|
||||
void APIServer::add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(StringRef)> f,
|
||||
bool once) {
|
||||
void APIServer::add_state_subscription_(const char *entity_id, const char *attribute,
|
||||
std::function<void(StringRef)> &&f, bool once) {
|
||||
this->state_subs_.push_back(HomeAssistantStateSubscription{
|
||||
.entity_id = entity_id, .attribute = attribute, .callback = std::move(f), .once = once,
|
||||
// entity_id_dynamic_storage and attribute_dynamic_storage remain nullptr (no heap allocation)
|
||||
@@ -443,7 +443,7 @@ void APIServer::add_state_subscription_(const char *entity_id, const char *attri
|
||||
|
||||
// Helper to add subscription with heap-allocated strings (reduces duplication)
|
||||
void APIServer::add_state_subscription_(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f, bool once) {
|
||||
std::function<void(StringRef)> &&f, bool once) {
|
||||
HomeAssistantStateSubscription sub;
|
||||
// Allocate heap storage for the strings
|
||||
sub.entity_id_dynamic_storage = std::make_unique<std::string>(std::move(entity_id));
|
||||
@@ -463,29 +463,29 @@ void APIServer::add_state_subscription_(std::string entity_id, optional<std::str
|
||||
|
||||
// New const char* overload (for internal components - zero allocation)
|
||||
void APIServer::subscribe_home_assistant_state(const char *entity_id, const char *attribute,
|
||||
std::function<void(StringRef)> f) {
|
||||
std::function<void(StringRef)> &&f) {
|
||||
this->add_state_subscription_(entity_id, attribute, std::move(f), false);
|
||||
}
|
||||
|
||||
void APIServer::get_home_assistant_state(const char *entity_id, const char *attribute,
|
||||
std::function<void(StringRef)> f) {
|
||||
std::function<void(StringRef)> &&f) {
|
||||
this->add_state_subscription_(entity_id, attribute, std::move(f), true);
|
||||
}
|
||||
|
||||
// std::string overload with StringRef callback (zero-allocation callback)
|
||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f) {
|
||||
std::function<void(StringRef)> &&f) {
|
||||
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), false);
|
||||
}
|
||||
|
||||
void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f) {
|
||||
std::function<void(StringRef)> &&f) {
|
||||
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), true);
|
||||
}
|
||||
|
||||
// Legacy helper: wraps std::string callback and delegates to StringRef version
|
||||
void APIServer::add_state_subscription_(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f, bool once) {
|
||||
std::function<void(const std::string &)> &&f, bool once) {
|
||||
// Wrap callback to convert StringRef -> std::string, then delegate
|
||||
this->add_state_subscription_(std::move(entity_id), std::move(attribute),
|
||||
std::function<void(StringRef)>([f = std::move(f)](StringRef state) { f(state.str()); }),
|
||||
@@ -494,12 +494,12 @@ void APIServer::add_state_subscription_(std::string entity_id, optional<std::str
|
||||
|
||||
// Legacy std::string overload (for custom_api_device.h - converts StringRef to std::string)
|
||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f) {
|
||||
std::function<void(const std::string &)> &&f) {
|
||||
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), false);
|
||||
}
|
||||
|
||||
void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f) {
|
||||
std::function<void(const std::string &)> &&f) {
|
||||
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), true);
|
||||
}
|
||||
|
||||
|
||||
@@ -201,20 +201,20 @@ class APIServer : public Component,
|
||||
};
|
||||
|
||||
// New const char* overload (for internal components - zero allocation)
|
||||
void subscribe_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(StringRef)> f);
|
||||
void get_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(StringRef)> f);
|
||||
void subscribe_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(StringRef)> &&f);
|
||||
void get_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(StringRef)> &&f);
|
||||
|
||||
// std::string overload with StringRef callback (for custom_api_device.h with zero-allocation callback)
|
||||
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f);
|
||||
std::function<void(StringRef)> &&f);
|
||||
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f);
|
||||
std::function<void(StringRef)> &&f);
|
||||
|
||||
// Legacy std::string overload (for custom_api_device.h - converts StringRef to std::string for callback)
|
||||
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f);
|
||||
std::function<void(const std::string &)> &&f);
|
||||
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f);
|
||||
std::function<void(const std::string &)> &&f);
|
||||
|
||||
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
|
||||
#endif
|
||||
@@ -241,13 +241,13 @@ class APIServer : public Component,
|
||||
#endif // USE_API_NOISE
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
// Helper methods to reduce code duplication
|
||||
void add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(StringRef)> f,
|
||||
bool once);
|
||||
void add_state_subscription_(std::string entity_id, optional<std::string> attribute, std::function<void(StringRef)> f,
|
||||
void add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(StringRef)> &&f,
|
||||
bool once);
|
||||
void add_state_subscription_(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> &&f, bool once);
|
||||
// Legacy helper: wraps std::string callback and delegates to StringRef version
|
||||
void add_state_subscription_(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f, bool once);
|
||||
std::function<void(const std::string &)> &&f, bool once);
|
||||
#endif // USE_API_HOMEASSISTANT_STATES
|
||||
// No explicit close() needed — listen sockets have no active connections on
|
||||
// failure/shutdown. Destructor handles fd cleanup (close or abort per platform).
|
||||
|
||||
@@ -230,7 +230,7 @@ template<typename... Ts> class APIRespondAction : public Action<Ts...> {
|
||||
void set_is_optional_mode(bool is_optional) { this->is_optional_mode_ = is_optional; }
|
||||
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
void set_data(std::function<void(Ts..., JsonObject)> func) {
|
||||
void set_data(std::function<void(Ts..., JsonObject)> &&func) {
|
||||
this->json_builder_ = std::move(func);
|
||||
this->has_data_ = true;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace cse7766 {
|
||||
namespace esphome::cse7766 {
|
||||
|
||||
static const char *const TAG = "cse7766";
|
||||
|
||||
@@ -258,5 +257,4 @@ void CSE7766Component::dump_config() {
|
||||
this->check_uart_settings(4800, 1, uart::UART_CONFIG_PARITY_EVEN);
|
||||
}
|
||||
|
||||
} // namespace cse7766
|
||||
} // namespace esphome
|
||||
} // namespace esphome::cse7766
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace cse7766 {
|
||||
namespace esphome::cse7766 {
|
||||
|
||||
static constexpr size_t CSE7766_RAW_DATA_SIZE = 24;
|
||||
|
||||
@@ -49,5 +48,4 @@ class CSE7766Component : public Component, public uart::UARTDevice {
|
||||
uint16_t cf_pulses_last_{0};
|
||||
};
|
||||
|
||||
} // namespace cse7766
|
||||
} // namespace esphome
|
||||
} // namespace esphome::cse7766
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "dlms_meter.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#if defined(USE_ESP8266_FRAMEWORK_ARDUINO)
|
||||
#include <bearssl/bearssl.h>
|
||||
#elif defined(USE_ESP32)
|
||||
@@ -410,7 +408,7 @@ void DlmsMeterComponent::decode_obis_(uint8_t *plaintext, uint16_t message_lengt
|
||||
if (current_position + 1 < message_length) {
|
||||
int8_t scaler = static_cast<int8_t>(plaintext[current_position + 1]);
|
||||
if (scaler != 0) {
|
||||
value *= powf(10.0f, scaler);
|
||||
value *= pow10_int(scaler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ from esphome.const import (
|
||||
CONF_BOARD,
|
||||
CONF_COMPONENTS,
|
||||
CONF_DISABLED,
|
||||
CONF_ENABLE_OTA_ROLLBACK,
|
||||
CONF_ESPHOME,
|
||||
CONF_FRAMEWORK,
|
||||
CONF_IGNORE_EFUSE_CUSTOM_MAC,
|
||||
@@ -90,7 +91,6 @@ CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES = "enable_idf_experimental_features"
|
||||
CONF_ENGINEERING_SAMPLE = "engineering_sample"
|
||||
CONF_INCLUDE_BUILTIN_IDF_COMPONENTS = "include_builtin_idf_components"
|
||||
CONF_ENABLE_LWIP_ASSERT = "enable_lwip_assert"
|
||||
CONF_ENABLE_OTA_ROLLBACK = "enable_ota_rollback"
|
||||
CONF_EXECUTE_FROM_PSRAM = "execute_from_psram"
|
||||
CONF_MINIMUM_CHIP_REVISION = "minimum_chip_revision"
|
||||
CONF_RELEASE = "release"
|
||||
@@ -790,8 +790,15 @@ def _detect_variant(value):
|
||||
engineering_sample = value.get(CONF_ENGINEERING_SAMPLE)
|
||||
if engineering_sample is None:
|
||||
_LOGGER.warning(
|
||||
"No board specified for ESP32-P4. Defaulting to production silicon (rev3). "
|
||||
"If you have an early engineering sample (pre-rev3), set 'engineering_sample: true'."
|
||||
"No board specified for ESP32-P4. Defaulting to production silicon (rev3).\n"
|
||||
"If you have an early engineering sample (pre-rev3), add this to your config:\n"
|
||||
"\n"
|
||||
" esp32:\n"
|
||||
" engineering_sample: true\n"
|
||||
"\n"
|
||||
"To check your chip revision, look for 'chip revision: vX.Y' in the boot log.\n"
|
||||
"Engineering samples will show a revision below v3.0.\n"
|
||||
"The 'debug:' component also reports the revision (e.g. Revision: 100 = v1.0, 300 = v3.0)."
|
||||
)
|
||||
elif engineering_sample:
|
||||
value[CONF_BOARD] = "esp32-p4-evboard"
|
||||
|
||||
@@ -24,6 +24,7 @@ from esphome.const import (
|
||||
__version__ as ESPHOME_VERSION,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
import esphome.final_validate as fv
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT
|
||||
|
||||
AUTO_LOAD = ["esp32_ble", "bytebuffer"]
|
||||
@@ -42,6 +43,7 @@ CONF_FIRMWARE_VERSION = "firmware_version"
|
||||
CONF_INDICATE = "indicate"
|
||||
CONF_MANUFACTURER = "manufacturer"
|
||||
CONF_MANUFACTURER_DATA = "manufacturer_data"
|
||||
CONF_MAX_CLIENTS = "max_clients"
|
||||
CONF_ON_WRITE = "on_write"
|
||||
CONF_READ = "read"
|
||||
CONF_STRING = "string"
|
||||
@@ -287,6 +289,22 @@ def create_device_information_service(config):
|
||||
|
||||
|
||||
def final_validate_config(config):
|
||||
# Validate max_clients does not exceed esp32_ble max_connections
|
||||
max_clients = config[CONF_MAX_CLIENTS]
|
||||
if max_clients > 1:
|
||||
full_config = fv.full_config.get()
|
||||
ble_config = full_config.get("esp32_ble", {})
|
||||
max_connections = ble_config.get(
|
||||
"max_connections", esp32_ble.DEFAULT_MAX_CONNECTIONS
|
||||
)
|
||||
if max_clients > max_connections:
|
||||
raise cv.Invalid(
|
||||
f"'max_clients' ({max_clients}) cannot exceed esp32_ble "
|
||||
f"'max_connections' ({max_connections}). "
|
||||
f"Please set 'max_connections: {max_clients}' in the "
|
||||
f"'esp32_ble' component."
|
||||
)
|
||||
|
||||
# Check if all characteristics that require notifications have the notify property set
|
||||
for char_id in CORE.data.get(DOMAIN, {}).get(KEY_NOTIFY_REQUIRED, set()):
|
||||
# Look for the characteristic in the configuration
|
||||
@@ -428,6 +446,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_MODEL): value_schema("string", templatable=False),
|
||||
cv.Optional(CONF_FIRMWARE_VERSION): value_schema("string", templatable=False),
|
||||
cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.uint8_t]),
|
||||
cv.Optional(CONF_MAX_CLIENTS, default=1): cv.int_range(min=1, max=9),
|
||||
cv.Optional(CONF_SERVICES, default=[]): cv.ensure_list(SERVICE_SCHEMA),
|
||||
cv.Optional(CONF_ON_CONNECT): automation.validate_automation(single=True),
|
||||
cv.Optional(CONF_ON_DISCONNECT): automation.validate_automation(single=True),
|
||||
@@ -552,6 +571,7 @@ async def to_code(config):
|
||||
esp32_ble.register_ble_status_event_handler(parent, var)
|
||||
cg.add(var.set_parent(parent))
|
||||
cg.add(parent.advertising_set_appearance(config[CONF_APPEARANCE]))
|
||||
cg.add(var.set_max_clients(config[CONF_MAX_CLIENTS]))
|
||||
if CONF_MANUFACTURER_DATA in config:
|
||||
cg.add(var.set_manufacturer_data(config[CONF_MANUFACTURER_DATA]))
|
||||
for service_config in config[CONF_SERVICES]:
|
||||
|
||||
@@ -175,6 +175,10 @@ void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t ga
|
||||
case ESP_GATTS_CONNECT_EVT: {
|
||||
ESP_LOGD(TAG, "BLE Client connected");
|
||||
this->add_client_(param->connect.conn_id);
|
||||
// Resume advertising so additional clients can discover and connect
|
||||
if (this->client_count_ < this->max_clients_) {
|
||||
this->parent_->advertising_start();
|
||||
}
|
||||
this->dispatch_callbacks_(CallbackType::ON_CONNECT, param->connect.conn_id);
|
||||
break;
|
||||
}
|
||||
@@ -241,7 +245,12 @@ void BLEServer::ble_before_disabled_event_handler() {
|
||||
|
||||
float BLEServer::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH + 10; }
|
||||
|
||||
void BLEServer::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 BLE Server:"); }
|
||||
void BLEServer::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"ESP32 BLE Server:\n"
|
||||
" Max clients: %u",
|
||||
this->max_clients_);
|
||||
}
|
||||
|
||||
BLEServer *global_ble_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
|
||||
this->restart_advertising_();
|
||||
}
|
||||
|
||||
void set_max_clients(uint8_t max_clients) { this->max_clients_ = max_clients; }
|
||||
uint8_t get_max_clients() const { return this->max_clients_; }
|
||||
|
||||
BLEService *create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15);
|
||||
void remove_service(ESPBTUUID uuid, uint8_t inst_id = 0);
|
||||
BLEService *get_service(ESPBTUUID uuid, uint8_t inst_id = 0);
|
||||
@@ -95,6 +98,7 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
|
||||
|
||||
uint16_t clients_[USE_ESP32_BLE_MAX_CONNECTIONS]{};
|
||||
uint8_t client_count_{0};
|
||||
uint8_t max_clients_{1};
|
||||
std::vector<ServiceEntry> services_{};
|
||||
std::vector<BLEService *> services_to_start_{};
|
||||
BLEService *device_information_service_{};
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
#include <driver/spi_master.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace ethernet {
|
||||
namespace esphome::ethernet {
|
||||
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2)
|
||||
// work around IDF compile issue on P4 https://github.com/espressif/esp-idf/pull/15637
|
||||
@@ -881,7 +880,6 @@ void EthernetComponent::write_phy_register_(esp_eth_mac_t *mac, PHYRegister regi
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace ethernet
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ethernet
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
#include "esp_mac.h"
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ethernet {
|
||||
namespace esphome::ethernet {
|
||||
|
||||
#ifdef USE_ETHERNET_IP_STATE_LISTENERS
|
||||
/** Listener interface for Ethernet IP state changes.
|
||||
@@ -218,7 +217,6 @@ extern EthernetComponent *global_eth_component;
|
||||
extern "C" esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config);
|
||||
#endif
|
||||
|
||||
} // namespace ethernet
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ethernet
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "ota_http_request.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -209,6 +211,26 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
||||
return ota::OTA_RESPONSE_OK;
|
||||
}
|
||||
|
||||
// URL-encode characters that are not unreserved per RFC 3986 section 2.3.
|
||||
// This is needed for embedding userinfo (username/password) in URLs safely.
|
||||
static std::string url_encode(const std::string &str) {
|
||||
std::string result;
|
||||
result.reserve(str.size());
|
||||
for (char c : str) {
|
||||
if (std::isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_' || c == '.' || c == '~') {
|
||||
result += c;
|
||||
} else {
|
||||
result += '%';
|
||||
result += format_hex_pretty_char((static_cast<uint8_t>(c) >> 4) & 0x0F);
|
||||
result += format_hex_pretty_char(static_cast<uint8_t>(c) & 0x0F);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void OtaHttpRequestComponent::set_password(const std::string &password) { this->password_ = url_encode(password); }
|
||||
void OtaHttpRequestComponent::set_username(const std::string &username) { this->username_ = url_encode(username); }
|
||||
|
||||
std::string OtaHttpRequestComponent::get_url_with_auth_(const std::string &url) {
|
||||
if (this->username_.empty() || this->password_.empty()) {
|
||||
return url;
|
||||
|
||||
@@ -29,9 +29,9 @@ class OtaHttpRequestComponent : public ota::OTAComponent, public Parented<HttpRe
|
||||
|
||||
void set_md5_url(const std::string &md5_url);
|
||||
void set_md5(const std::string &md5) { this->md5_expected_ = md5; }
|
||||
void set_password(const std::string &password) { this->password_ = password; }
|
||||
void set_password(const std::string &password);
|
||||
void set_url(const std::string &url);
|
||||
void set_username(const std::string &username) { this->username_ = username; }
|
||||
void set_username(const std::string &username);
|
||||
|
||||
std::string md5_computed() { return this->md5_computed_; }
|
||||
std::string md5_expected() { return this->md5_expected_; }
|
||||
|
||||
@@ -24,8 +24,29 @@ namespace http_request {
|
||||
static const char *const TAG = "http_request.update";
|
||||
|
||||
static const size_t MAX_READ_SIZE = 256;
|
||||
static constexpr uint32_t INITIAL_CHECK_INTERVAL_ID = 0;
|
||||
static constexpr uint32_t INITIAL_CHECK_INTERVAL_MS = 10000;
|
||||
static constexpr uint8_t INITIAL_CHECK_MAX_ATTEMPTS = 6;
|
||||
|
||||
void HttpRequestUpdate::setup() { this->ota_parent_->add_state_listener(this); }
|
||||
void HttpRequestUpdate::setup() {
|
||||
this->ota_parent_->add_state_listener(this);
|
||||
|
||||
// Check periodically until network is ready
|
||||
// Only if update interval is > total retry window to avoid redundant checks
|
||||
if (this->get_update_interval() != SCHEDULER_DONT_RUN &&
|
||||
this->get_update_interval() > INITIAL_CHECK_INTERVAL_MS * INITIAL_CHECK_MAX_ATTEMPTS) {
|
||||
this->initial_check_remaining_ = INITIAL_CHECK_MAX_ATTEMPTS;
|
||||
this->set_interval(INITIAL_CHECK_INTERVAL_ID, INITIAL_CHECK_INTERVAL_MS, [this]() {
|
||||
bool connected = network::is_connected();
|
||||
if (--this->initial_check_remaining_ == 0 || connected) {
|
||||
this->cancel_interval(INITIAL_CHECK_INTERVAL_ID);
|
||||
if (connected) {
|
||||
this->update();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void HttpRequestUpdate::on_ota_state(ota::OTAState state, float progress, uint8_t error) {
|
||||
if (state == ota::OTAState::OTA_IN_PROGRESS) {
|
||||
@@ -45,6 +66,7 @@ void HttpRequestUpdate::update() {
|
||||
ESP_LOGD(TAG, "Network not connected, skipping update check");
|
||||
return;
|
||||
}
|
||||
this->cancel_interval(INITIAL_CHECK_INTERVAL_ID);
|
||||
#ifdef USE_ESP32
|
||||
xTaskCreate(HttpRequestUpdate::update_task, "update_task", 8192, (void *) this, 1, &this->update_task_handle_);
|
||||
#else
|
||||
|
||||
@@ -40,6 +40,7 @@ class HttpRequestUpdate final : public update::UpdateEntity, public PollingCompo
|
||||
#ifdef USE_ESP32
|
||||
TaskHandle_t update_task_handle_{nullptr};
|
||||
#endif
|
||||
uint8_t initial_check_remaining_{0};
|
||||
};
|
||||
|
||||
} // namespace http_request
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include <memory>
|
||||
|
||||
namespace esphome {
|
||||
namespace i2c {
|
||||
namespace esphome::i2c {
|
||||
|
||||
static const char *const TAG = "i2c";
|
||||
|
||||
@@ -109,5 +108,4 @@ uint8_t I2CRegister16::get() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace i2c
|
||||
} // namespace esphome
|
||||
} // namespace esphome::i2c
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/core/optional.h"
|
||||
#include "i2c_bus.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace i2c {
|
||||
namespace esphome::i2c {
|
||||
|
||||
#define LOG_I2C_DEVICE(this) ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
|
||||
|
||||
@@ -272,5 +271,4 @@ class I2CDevice {
|
||||
I2CBus *bus_{nullptr}; ///< pointer to I2CBus instance
|
||||
};
|
||||
|
||||
} // namespace i2c
|
||||
} // namespace esphome
|
||||
} // namespace esphome::i2c
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace i2c {
|
||||
namespace esphome::i2c {
|
||||
|
||||
/// @brief Error codes returned by I2CBus and I2CDevice methods
|
||||
enum ErrorCode {
|
||||
@@ -69,5 +68,4 @@ class InternalI2CBus : public I2CBus {
|
||||
virtual int get_port() const = 0;
|
||||
};
|
||||
|
||||
} // namespace i2c
|
||||
} // namespace esphome
|
||||
} // namespace esphome::i2c
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace i2c {
|
||||
namespace esphome::i2c {
|
||||
|
||||
static const char *const TAG = "i2c.arduino";
|
||||
|
||||
@@ -262,7 +261,6 @@ void ArduinoI2CBus::recover_() {
|
||||
|
||||
recovery_result_ = RECOVERY_COMPLETED;
|
||||
}
|
||||
} // namespace i2c
|
||||
} // namespace esphome
|
||||
} // namespace esphome::i2c
|
||||
|
||||
#endif // defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "i2c_bus.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace i2c {
|
||||
namespace esphome::i2c {
|
||||
|
||||
enum RecoveryCode {
|
||||
RECOVERY_FAILED_SCL_LOW,
|
||||
@@ -45,7 +44,6 @@ class ArduinoI2CBus : public InternalI2CBus, public Component {
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace i2c
|
||||
} // namespace esphome
|
||||
} // namespace esphome::i2c
|
||||
|
||||
#endif // defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace i2c {
|
||||
namespace esphome::i2c {
|
||||
|
||||
static const char *const TAG = "i2c.idf";
|
||||
|
||||
@@ -312,6 +311,5 @@ void IDFI2CBus::recover_() {
|
||||
recovery_result_ = RECOVERY_COMPLETED;
|
||||
}
|
||||
|
||||
} // namespace i2c
|
||||
} // namespace esphome
|
||||
} // namespace esphome::i2c
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "i2c_bus.h"
|
||||
#include <driver/i2c_master.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace i2c {
|
||||
namespace esphome::i2c {
|
||||
|
||||
enum RecoveryCode {
|
||||
RECOVERY_FAILED_SCL_LOW,
|
||||
@@ -56,7 +55,6 @@ class IDFI2CBus : public InternalI2CBus, public Component {
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace i2c
|
||||
} // namespace esphome
|
||||
} // namespace esphome::i2c
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -222,11 +222,11 @@ void KamstrupKMPComponent::parse_command_message_(uint16_t command, const uint8_
|
||||
}
|
||||
|
||||
// Calculate exponent
|
||||
float exponent = msg[6] & 0x3F;
|
||||
int8_t exp_val = msg[6] & 0x3F;
|
||||
if (msg[6] & 0x40) {
|
||||
exponent = -exponent;
|
||||
exp_val = -exp_val;
|
||||
}
|
||||
exponent = powf(10, exponent);
|
||||
float exponent = pow10_int(exp_val);
|
||||
if (msg[6] & 0x80) {
|
||||
exponent = -exponent;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ __attribute__((weak)) void print_coredump() {}
|
||||
|
||||
namespace esphome::logger {
|
||||
|
||||
static const uint32_t CRASH_MAGIC = 0xDEADBEEF;
|
||||
|
||||
__attribute__((section(".noinit"))) struct {
|
||||
uint32_t magic;
|
||||
uint32_t reason;
|
||||
@@ -152,7 +150,7 @@ static const char *reason_to_str(unsigned int reason, char *buf) {
|
||||
|
||||
void Logger::dump_crash_() {
|
||||
ESP_LOGD(TAG, "Crash buffer address %p", &crash_buf);
|
||||
if (crash_buf.magic == CRASH_MAGIC) {
|
||||
if (crash_buf.magic == App.get_config_hash()) {
|
||||
char reason_buf[REASON_BUF_SIZE];
|
||||
ESP_LOGE(TAG, "Last crash:");
|
||||
ESP_LOGE(TAG, "Reason=%s PC=0x%08x LR=0x%08x", reason_to_str(crash_buf.reason, reason_buf), crash_buf.pc,
|
||||
@@ -164,7 +162,7 @@ void Logger::dump_crash_() {
|
||||
}
|
||||
|
||||
void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) {
|
||||
crash_buf.magic = CRASH_MAGIC;
|
||||
crash_buf.magic = App.get_config_hash();
|
||||
crash_buf.reason = reason;
|
||||
if (esf) {
|
||||
crash_buf.pc = esf->basic.pc;
|
||||
|
||||
@@ -39,6 +39,7 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_COLOR_ORDER,
|
||||
CONF_DIMENSIONS,
|
||||
CONF_DISABLED,
|
||||
CONF_ENABLE_PIN,
|
||||
CONF_ID,
|
||||
CONF_INIT_SEQUENCE,
|
||||
@@ -88,14 +89,17 @@ COLOR_DEPTHS = {
|
||||
def model_schema(config):
|
||||
model = MODELS[config[CONF_MODEL].upper()]
|
||||
model.defaults[CONF_SWAP_XY] = cv.UNDEFINED
|
||||
transform = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_MIRROR_X): cv.boolean,
|
||||
cv.Required(CONF_MIRROR_Y): cv.boolean,
|
||||
cv.Optional(CONF_SWAP_XY): cv.invalid(
|
||||
"Axis swapping not supported by DSI displays"
|
||||
),
|
||||
}
|
||||
transform = cv.Any(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_MIRROR_X): cv.boolean,
|
||||
cv.Required(CONF_MIRROR_Y): cv.boolean,
|
||||
cv.Optional(CONF_SWAP_XY): cv.invalid(
|
||||
"Axis swapping not supported by DSI displays"
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.one_of(CONF_DISABLED, lower=True),
|
||||
)
|
||||
# CUSTOM model will need to provide a custom init sequence
|
||||
iseqconf = (
|
||||
@@ -199,9 +203,9 @@ async def to_code(config):
|
||||
cg.add(var.set_vsync_pulse_width(config[CONF_VSYNC_PULSE_WIDTH]))
|
||||
cg.add(var.set_vsync_back_porch(config[CONF_VSYNC_BACK_PORCH]))
|
||||
cg.add(var.set_vsync_front_porch(config[CONF_VSYNC_FRONT_PORCH]))
|
||||
cg.add(var.set_pclk_frequency(int(config[CONF_PCLK_FREQUENCY] / 1e6)))
|
||||
cg.add(var.set_pclk_frequency(config[CONF_PCLK_FREQUENCY] / 1.0e6))
|
||||
cg.add(var.set_lanes(int(config[CONF_LANES])))
|
||||
cg.add(var.set_lane_bit_rate(int(config[CONF_LANE_BIT_RATE] / 1e6)))
|
||||
cg.add(var.set_lane_bit_rate(config[CONF_LANE_BIT_RATE] / 1.0e6))
|
||||
if reset_pin := config.get(CONF_RESET_PIN):
|
||||
reset = await cg.gpio_pin_expression(reset_pin)
|
||||
cg.add(var.set_reset_pin(reset))
|
||||
|
||||
@@ -374,7 +374,7 @@ void MIPI_DSI::dump_config() {
|
||||
"\n Swap X/Y: %s"
|
||||
"\n Rotation: %d degrees"
|
||||
"\n DSI Lanes: %u"
|
||||
"\n Lane Bit Rate: %uMbps"
|
||||
"\n Lane Bit Rate: %.0fMbps"
|
||||
"\n HSync Pulse Width: %u"
|
||||
"\n HSync Back Porch: %u"
|
||||
"\n HSync Front Porch: %u"
|
||||
@@ -385,7 +385,7 @@ void MIPI_DSI::dump_config() {
|
||||
"\n Display Pixel Mode: %d bit"
|
||||
"\n Color Order: %s"
|
||||
"\n Invert Colors: %s"
|
||||
"\n Pixel Clock: %dMHz",
|
||||
"\n Pixel Clock: %.1fMHz",
|
||||
this->model_, this->width_, this->height_, YESNO(this->madctl_ & (MADCTL_XFLIP | MADCTL_MX)),
|
||||
YESNO(this->madctl_ & (MADCTL_YFLIP | MADCTL_MY)), YESNO(this->madctl_ & MADCTL_MV), this->rotation_,
|
||||
this->lanes_, this->lane_bit_rate_, this->hsync_pulse_width_, this->hsync_back_porch_,
|
||||
|
||||
@@ -47,7 +47,7 @@ class MIPI_DSI : public display::Display {
|
||||
|
||||
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
|
||||
void set_enable_pins(std::vector<GPIOPin *> enable_pins) { this->enable_pins_ = std::move(enable_pins); }
|
||||
void set_pclk_frequency(uint32_t pclk_frequency) { this->pclk_frequency_ = pclk_frequency; }
|
||||
void set_pclk_frequency(float pclk_frequency) { this->pclk_frequency_ = pclk_frequency; }
|
||||
int get_width_internal() override { return this->width_; }
|
||||
int get_height_internal() override { return this->height_; }
|
||||
void set_hsync_back_porch(uint16_t hsync_back_porch) { this->hsync_back_porch_ = hsync_back_porch; }
|
||||
@@ -58,7 +58,7 @@ class MIPI_DSI : public display::Display {
|
||||
void set_vsync_front_porch(uint16_t vsync_front_porch) { this->vsync_front_porch_ = vsync_front_porch; }
|
||||
void set_init_sequence(const std::vector<uint8_t> &init_sequence) { this->init_sequence_ = init_sequence; }
|
||||
void set_model(const char *model) { this->model_ = model; }
|
||||
void set_lane_bit_rate(uint16_t lane_bit_rate) { this->lane_bit_rate_ = lane_bit_rate; }
|
||||
void set_lane_bit_rate(float lane_bit_rate) { this->lane_bit_rate_ = lane_bit_rate; }
|
||||
void set_lanes(uint8_t lanes) { this->lanes_ = lanes; }
|
||||
void set_madctl(uint8_t madctl) { this->madctl_ = madctl; }
|
||||
|
||||
@@ -95,9 +95,9 @@ class MIPI_DSI : public display::Display {
|
||||
uint16_t vsync_front_porch_ = 10;
|
||||
const char *model_{"Unknown"};
|
||||
std::vector<uint8_t> init_sequence_{};
|
||||
uint16_t pclk_frequency_ = 16; // in MHz
|
||||
uint16_t lane_bit_rate_{1500}; // in Mbps
|
||||
uint8_t lanes_{2}; // 1, 2, 3 or 4 lanes
|
||||
float pclk_frequency_ = 16; // in MHz
|
||||
float lane_bit_rate_{1500}; // in Mbps
|
||||
uint8_t lanes_{2}; // 1, 2, 3 or 4 lanes
|
||||
|
||||
bool invert_colors_{};
|
||||
display::ColorOrder color_mode_{display::COLOR_ORDER_BGR};
|
||||
|
||||
@@ -37,8 +37,7 @@ using ip4_addr_t = in_addr;
|
||||
#include <esp_netif.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace network {
|
||||
namespace esphome::network {
|
||||
|
||||
/// Buffer size for IP address string (IPv6 max: 39 chars + null)
|
||||
static constexpr size_t IP_ADDRESS_BUFFER_SIZE = 40;
|
||||
@@ -187,6 +186,5 @@ struct IPAddress {
|
||||
|
||||
using IPAddresses = std::array<IPAddress, 5>;
|
||||
|
||||
} // namespace network
|
||||
} // namespace esphome
|
||||
} // namespace esphome::network
|
||||
#endif
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
#include "esphome/components/modem/modem_component.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace network {
|
||||
namespace esphome::network {
|
||||
|
||||
// The order of the components is important: WiFi should come after any possible main interfaces (it may be used as
|
||||
// an AP that use a previous interface for NAT).
|
||||
@@ -109,6 +108,5 @@ const char *get_use_address() {
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace network
|
||||
} // namespace esphome
|
||||
} // namespace esphome::network
|
||||
#endif
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include <string>
|
||||
#include "ip_address.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace network {
|
||||
namespace esphome::network {
|
||||
|
||||
/// Return whether the node is connected to the network (through wifi, eth, ...)
|
||||
bool is_connected();
|
||||
@@ -15,6 +14,5 @@ bool is_disabled();
|
||||
const char *get_use_address();
|
||||
IPAddresses get_ip_addresses();
|
||||
|
||||
} // namespace network
|
||||
} // namespace esphome
|
||||
} // namespace esphome::network
|
||||
#endif
|
||||
|
||||
@@ -39,7 +39,7 @@ std::string get_random_ha_tag_ndef() {
|
||||
for (int i = 0; i < 12; i++) {
|
||||
uri += ALPHANUM[random_uint32() % (sizeof(ALPHANUM) - 1)];
|
||||
}
|
||||
ESP_LOGD("pn7160", "Payload to be written: %s", uri.c_str());
|
||||
ESP_LOGD(TAG, "Payload to be written: %s", uri.c_str());
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ async def to_code(config):
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"remote_receiver_esp32.cpp": {
|
||||
"remote_receiver_rmt.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
|
||||
@@ -171,7 +171,7 @@ async def to_code(config):
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"remote_transmitter_esp32.cpp": {
|
||||
"remote_transmitter_rmt.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <cmath>
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "sensor.h"
|
||||
|
||||
@@ -240,7 +241,7 @@ ValueListFilter::ValueListFilter(std::initializer_list<TemplatableValue<float>>
|
||||
|
||||
bool ValueListFilter::value_matches_any_(float sensor_value) {
|
||||
int8_t accuracy = this->parent_->get_accuracy_decimals();
|
||||
float accuracy_mult = powf(10.0f, accuracy);
|
||||
float accuracy_mult = pow10_int(accuracy);
|
||||
float rounded_sensor = roundf(accuracy_mult * sensor_value);
|
||||
|
||||
for (auto &filter_value : this->values_) {
|
||||
@@ -472,7 +473,7 @@ optional<float> ClampFilter::new_value(float value) {
|
||||
RoundFilter::RoundFilter(uint8_t precision) : precision_(precision) {}
|
||||
optional<float> RoundFilter::new_value(float value) {
|
||||
if (std::isfinite(value)) {
|
||||
float accuracy_mult = powf(10.0f, this->precision_);
|
||||
float accuracy_mult = pow10_int(this->precision_);
|
||||
return roundf(accuracy_mult * value) / accuracy_mult;
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#include "automation.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace switch_ {
|
||||
namespace esphome::switch_ {
|
||||
|
||||
static const char *const TAG = "switch.automation";
|
||||
|
||||
} // namespace switch_
|
||||
} // namespace esphome
|
||||
} // namespace esphome::switch_
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace switch_ {
|
||||
namespace esphome::switch_ {
|
||||
|
||||
template<typename... Ts> class TurnOnAction : public Action<Ts...> {
|
||||
public:
|
||||
@@ -104,5 +103,4 @@ template<typename... Ts> class SwitchPublishAction : public Action<Ts...> {
|
||||
Switch *switch_;
|
||||
};
|
||||
|
||||
} // namespace switch_
|
||||
} // namespace esphome
|
||||
} // namespace esphome::switch_
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "switch_binary_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace switch_ {
|
||||
namespace esphome::switch_ {
|
||||
|
||||
static const char *const TAG = "switch.binary_sensor";
|
||||
|
||||
@@ -13,5 +12,4 @@ void SwitchBinarySensor::setup() {
|
||||
|
||||
void SwitchBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Switch Binary Sensor", this); }
|
||||
|
||||
} // namespace switch_
|
||||
} // namespace esphome
|
||||
} // namespace esphome::switch_
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace switch_ {
|
||||
namespace esphome::switch_ {
|
||||
|
||||
class SwitchBinarySensor : public binary_sensor::BinarySensor, public Component {
|
||||
public:
|
||||
@@ -17,5 +16,4 @@ class SwitchBinarySensor : public binary_sensor::BinarySensor, public Component
|
||||
Switch *source_;
|
||||
};
|
||||
|
||||
} // namespace switch_
|
||||
} // namespace esphome
|
||||
} // namespace esphome::switch_
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace switch_ {
|
||||
namespace esphome::switch_ {
|
||||
|
||||
static const char *const TAG = "switch";
|
||||
|
||||
@@ -107,5 +106,4 @@ void log_switch(const char *tag, const char *prefix, const char *type, Switch *o
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace switch_
|
||||
} // namespace esphome
|
||||
} // namespace esphome::switch_
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace switch_ {
|
||||
namespace esphome::switch_ {
|
||||
|
||||
#define SUB_SWITCH(name) \
|
||||
protected: \
|
||||
@@ -16,10 +15,10 @@ namespace switch_ {
|
||||
void set_##name##_switch(switch_::Switch *s) { this->name##_switch_ = s; }
|
||||
|
||||
// bit0: on/off. bit1: persistent. bit2: inverted. bit3: disabled
|
||||
const int RESTORE_MODE_ON_MASK = 0x01;
|
||||
const int RESTORE_MODE_PERSISTENT_MASK = 0x02;
|
||||
const int RESTORE_MODE_INVERTED_MASK = 0x04;
|
||||
const int RESTORE_MODE_DISABLED_MASK = 0x08;
|
||||
constexpr int RESTORE_MODE_ON_MASK = 0x01;
|
||||
constexpr int RESTORE_MODE_PERSISTENT_MASK = 0x02;
|
||||
constexpr int RESTORE_MODE_INVERTED_MASK = 0x04;
|
||||
constexpr int RESTORE_MODE_DISABLED_MASK = 0x08;
|
||||
|
||||
enum SwitchRestoreMode : uint8_t {
|
||||
SWITCH_ALWAYS_OFF = !RESTORE_MODE_ON_MASK,
|
||||
@@ -146,5 +145,4 @@ class Switch : public EntityBase, public EntityBase_DeviceClass {
|
||||
#define LOG_SWITCH(prefix, type, obj) log_switch((TAG), (prefix), LOG_STR_LITERAL(type), (obj))
|
||||
void log_switch(const char *tag, const char *prefix, const char *type, Switch *obj);
|
||||
|
||||
} // namespace switch_
|
||||
} // namespace esphome
|
||||
} // namespace esphome::switch_
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "text.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace text {
|
||||
namespace esphome::text {
|
||||
|
||||
class TextStateTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
@@ -29,5 +28,4 @@ template<typename... Ts> class TextSetAction : public Action<Ts...> {
|
||||
Text *text_;
|
||||
};
|
||||
|
||||
} // namespace text
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace esphome {
|
||||
namespace text {
|
||||
namespace esphome::text {
|
||||
|
||||
static const char *const TAG = "text";
|
||||
|
||||
@@ -34,5 +33,4 @@ void Text::add_on_state_callback(std::function<void(const std::string &)> &&call
|
||||
this->state_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
} // namespace text
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "text_call.h"
|
||||
#include "text_traits.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace text {
|
||||
namespace esphome::text {
|
||||
|
||||
#define LOG_TEXT(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
@@ -47,5 +46,4 @@ class Text : public EntityBase {
|
||||
LazyCallbackManager<void(const std::string &)> state_callback_;
|
||||
};
|
||||
|
||||
} // namespace text
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "text.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace text {
|
||||
namespace esphome::text {
|
||||
|
||||
static const char *const TAG = "text";
|
||||
|
||||
@@ -52,5 +51,4 @@ void TextCall::perform() {
|
||||
this->parent_->control(target_value);
|
||||
}
|
||||
|
||||
} // namespace text
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "text_traits.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace text {
|
||||
namespace esphome::text {
|
||||
|
||||
class Text;
|
||||
|
||||
@@ -21,5 +20,4 @@ class TextCall {
|
||||
void validate_();
|
||||
};
|
||||
|
||||
} // namespace text
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
#include "esphome/core/string_ref.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace text {
|
||||
namespace esphome::text {
|
||||
|
||||
enum TextMode : uint8_t {
|
||||
TEXT_MODE_TEXT = 0,
|
||||
@@ -37,5 +36,4 @@ class TextTraits {
|
||||
TextMode mode_{TEXT_MODE_TEXT};
|
||||
};
|
||||
|
||||
} // namespace text
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace text_sensor {
|
||||
namespace esphome::text_sensor {
|
||||
|
||||
class TextSensorStateTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
@@ -46,5 +45,4 @@ template<typename... Ts> class TextSensorPublishAction : public Action<Ts...> {
|
||||
TextSensor *sensor_;
|
||||
};
|
||||
|
||||
} // namespace text_sensor
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text_sensor
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace text_sensor {
|
||||
namespace esphome::text_sensor {
|
||||
|
||||
static const char *const TAG = "text_sensor.filter";
|
||||
|
||||
@@ -107,7 +106,6 @@ bool MapFilter::new_value(std::string &value) {
|
||||
return true; // Pass through if no match
|
||||
}
|
||||
|
||||
} // namespace text_sensor
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text_sensor
|
||||
|
||||
#endif // USE_TEXT_SENSOR_FILTER
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace text_sensor {
|
||||
namespace esphome::text_sensor {
|
||||
|
||||
class TextSensor;
|
||||
|
||||
@@ -165,7 +164,6 @@ class MapFilter : public Filter {
|
||||
FixedVector<Substitution> mappings_;
|
||||
};
|
||||
|
||||
} // namespace text_sensor
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text_sensor
|
||||
|
||||
#endif // USE_TEXT_SENSOR_FILTER
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace esphome {
|
||||
namespace text_sensor {
|
||||
namespace esphome::text_sensor {
|
||||
|
||||
static const char *const TAG = "text_sensor";
|
||||
|
||||
@@ -125,5 +124,4 @@ void TextSensor::notify_frontend_() {
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace text_sensor
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text_sensor
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
|
||||
namespace esphome {
|
||||
namespace text_sensor {
|
||||
namespace esphome::text_sensor {
|
||||
|
||||
class TextSensor;
|
||||
|
||||
@@ -84,5 +83,4 @@ class TextSensor : public EntityBase, public EntityBase_DeviceClass {
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace text_sensor
|
||||
} // namespace esphome
|
||||
} // namespace esphome::text_sensor
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include "esphome/core/event_pool.h"
|
||||
#include <atomic>
|
||||
|
||||
namespace esphome {
|
||||
namespace usb_host {
|
||||
namespace esphome::usb_host {
|
||||
|
||||
// THREADING MODEL:
|
||||
// This component uses a dedicated USB task for event processing to prevent data loss.
|
||||
@@ -44,16 +43,16 @@ struct TransferRequest;
|
||||
class USBClient;
|
||||
|
||||
// constants for setup packet type
|
||||
static const uint8_t USB_RECIP_DEVICE = 0;
|
||||
static const uint8_t USB_RECIP_INTERFACE = 1;
|
||||
static const uint8_t USB_RECIP_ENDPOINT = 2;
|
||||
static const uint8_t USB_TYPE_STANDARD = 0 << 5;
|
||||
static const uint8_t USB_TYPE_CLASS = 1 << 5;
|
||||
static const uint8_t USB_TYPE_VENDOR = 2 << 5;
|
||||
static const uint8_t USB_DIR_MASK = 1 << 7;
|
||||
static const uint8_t USB_DIR_IN = 1 << 7;
|
||||
static const uint8_t USB_DIR_OUT = 0;
|
||||
static const size_t SETUP_PACKET_SIZE = 8;
|
||||
static constexpr uint8_t USB_RECIP_DEVICE = 0;
|
||||
static constexpr uint8_t USB_RECIP_INTERFACE = 1;
|
||||
static constexpr uint8_t USB_RECIP_ENDPOINT = 2;
|
||||
static constexpr uint8_t USB_TYPE_STANDARD = 0 << 5;
|
||||
static constexpr uint8_t USB_TYPE_CLASS = 1 << 5;
|
||||
static constexpr uint8_t USB_TYPE_VENDOR = 2 << 5;
|
||||
static constexpr uint8_t USB_DIR_MASK = 1 << 7;
|
||||
static constexpr uint8_t USB_DIR_IN = 1 << 7;
|
||||
static constexpr uint8_t USB_DIR_OUT = 0;
|
||||
static constexpr size_t SETUP_PACKET_SIZE = 8;
|
||||
|
||||
static constexpr size_t MAX_REQUESTS = USB_HOST_MAX_REQUESTS; // maximum number of outstanding requests possible.
|
||||
static_assert(MAX_REQUESTS >= 1 && MAX_REQUESTS <= 32, "MAX_REQUESTS must be between 1 and 32");
|
||||
@@ -189,7 +188,6 @@ class USBHost : public Component {
|
||||
std::vector<USBClient *> clients_{};
|
||||
};
|
||||
|
||||
} // namespace usb_host
|
||||
} // namespace esphome
|
||||
} // namespace esphome::usb_host
|
||||
|
||||
#endif // USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include <cstring>
|
||||
#include <atomic>
|
||||
#include <span>
|
||||
namespace esphome {
|
||||
namespace usb_host {
|
||||
namespace esphome::usb_host {
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wparentheses"
|
||||
|
||||
@@ -568,6 +567,5 @@ void USBClient::release_trq(TransferRequest *trq) {
|
||||
this->trq_in_use_.fetch_and(mask, std::memory_order_release);
|
||||
}
|
||||
|
||||
} // namespace usb_host
|
||||
} // namespace esphome
|
||||
} // namespace esphome::usb_host
|
||||
#endif // USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include <cinttypes>
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace usb_host {
|
||||
namespace esphome::usb_host {
|
||||
|
||||
void USBHost::setup() {
|
||||
usb_host_config_t config{};
|
||||
@@ -28,7 +27,6 @@ void USBHost::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace usb_host
|
||||
} // namespace esphome
|
||||
} // namespace esphome::usb_host
|
||||
|
||||
#endif // USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_HIDE_TIMESTAMP, ENTITY_CATEGORY_DIAGNOSTIC, ICON_NEW_BOX
|
||||
from esphome.const import (
|
||||
CONF_HIDE_HASH,
|
||||
CONF_HIDE_TIMESTAMP,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_NEW_BOX,
|
||||
)
|
||||
|
||||
version_ns = cg.esphome_ns.namespace("version")
|
||||
VersionTextSensor = version_ns.class_(
|
||||
@@ -16,6 +21,9 @@ CONFIG_SCHEMA = (
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(VersionTextSensor),
|
||||
# Hide the config hash suffix and restore the pre-2026.1
|
||||
# version text format when set to true.
|
||||
cv.Optional(CONF_HIDE_HASH, default=False): cv.boolean,
|
||||
cv.Optional(CONF_HIDE_TIMESTAMP, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
@@ -26,4 +34,5 @@ CONFIG_SCHEMA = (
|
||||
async def to_code(config):
|
||||
var = await text_sensor.new_text_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
cg.add(var.set_hide_hash(config[CONF_HIDE_HASH]))
|
||||
cg.add(var.set_hide_timestamp(config[CONF_HIDE_TIMESTAMP]))
|
||||
|
||||
@@ -1,39 +1,54 @@
|
||||
#include "version_text_sensor.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/version.h"
|
||||
#include "esphome/core/build_info_data.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
#include "esphome/core/version.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace version {
|
||||
namespace esphome::version {
|
||||
|
||||
static const char *const TAG = "version.text_sensor";
|
||||
|
||||
void VersionTextSensor::setup() {
|
||||
static const char PREFIX[] PROGMEM = ESPHOME_VERSION " (config hash 0x";
|
||||
static const char HASH_PREFIX[] PROGMEM = ESPHOME_VERSION " (config hash 0x";
|
||||
static const char VERSION_PREFIX[] PROGMEM = ESPHOME_VERSION;
|
||||
static const char BUILT_STR[] PROGMEM = ", built ";
|
||||
// Buffer size: PREFIX + 8 hex chars + BUILT_STR + BUILD_TIME_STR_SIZE + ")" + null
|
||||
constexpr size_t buf_size = sizeof(PREFIX) + 8 + sizeof(BUILT_STR) + esphome::Application::BUILD_TIME_STR_SIZE + 2;
|
||||
|
||||
// Buffer size: HASH_PREFIX + 8 hex chars + BUILT_STR + BUILD_TIME_STR_SIZE + ")" + null
|
||||
constexpr size_t buf_size =
|
||||
sizeof(HASH_PREFIX) + 8 + sizeof(BUILT_STR) + esphome::Application::BUILD_TIME_STR_SIZE + 2;
|
||||
char version_str[buf_size];
|
||||
|
||||
ESPHOME_strncpy_P(version_str, PREFIX, sizeof(version_str));
|
||||
// hide_hash restores the pre-2026.1 base format by omitting
|
||||
// the " (config hash 0x...)" suffix entirely.
|
||||
if (this->hide_hash_) {
|
||||
ESPHOME_strncpy_P(version_str, VERSION_PREFIX, sizeof(version_str));
|
||||
} else {
|
||||
ESPHOME_strncpy_P(version_str, HASH_PREFIX, sizeof(version_str));
|
||||
|
||||
size_t len = strlen(version_str);
|
||||
snprintf(version_str + len, sizeof(version_str) - len, "%08" PRIx32, App.get_config_hash());
|
||||
size_t len = strlen(version_str);
|
||||
snprintf(version_str + len, sizeof(version_str) - len, "%08" PRIx32, App.get_config_hash());
|
||||
}
|
||||
|
||||
// Keep hide_timestamp behavior independent from hide_hash so all
|
||||
// combinations remain available to users.
|
||||
if (!this->hide_timestamp_) {
|
||||
size_t len = strlen(version_str);
|
||||
ESPHOME_strncat_P(version_str, BUILT_STR, sizeof(version_str) - len - 1);
|
||||
ESPHOME_strncat_P(version_str, ESPHOME_BUILD_TIME_STR, sizeof(version_str) - strlen(version_str) - 1);
|
||||
}
|
||||
|
||||
strncat(version_str, ")", sizeof(version_str) - strlen(version_str) - 1);
|
||||
// The closing parenthesis is part of the config-hash suffix and must
|
||||
// only be appended when that suffix is present.
|
||||
if (!this->hide_hash_) {
|
||||
strncat(version_str, ")", sizeof(version_str) - strlen(version_str) - 1);
|
||||
}
|
||||
version_str[sizeof(version_str) - 1] = '\0';
|
||||
this->publish_state(version_str);
|
||||
}
|
||||
void VersionTextSensor::set_hide_hash(bool hide_hash) { this->hide_hash_ = hide_hash; }
|
||||
void VersionTextSensor::set_hide_timestamp(bool hide_timestamp) { this->hide_timestamp_ = hide_timestamp; }
|
||||
void VersionTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Version Text Sensor", this); }
|
||||
|
||||
} // namespace version
|
||||
} // namespace esphome
|
||||
} // namespace esphome::version
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace version {
|
||||
namespace esphome::version {
|
||||
|
||||
class VersionTextSensor : public text_sensor::TextSensor, public Component {
|
||||
public:
|
||||
void set_hide_hash(bool hide_hash);
|
||||
void set_hide_timestamp(bool hide_timestamp);
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
bool hide_hash_{false};
|
||||
bool hide_timestamp_{false};
|
||||
};
|
||||
|
||||
} // namespace version
|
||||
} // namespace esphome
|
||||
} // namespace esphome::version
|
||||
|
||||
@@ -375,7 +375,7 @@ json::SerializationBuffer<> WebServer::get_config_json() {
|
||||
JsonObject root = builder.root();
|
||||
|
||||
root[ESPHOME_F("title")] = App.get_friendly_name().empty() ? App.get_name().c_str() : App.get_friendly_name().c_str();
|
||||
char comment_buffer[ESPHOME_COMMENT_SIZE];
|
||||
char comment_buffer[Application::ESPHOME_COMMENT_SIZE_MAX];
|
||||
App.get_comment_string(comment_buffer);
|
||||
root[ESPHOME_F("comment")] = comment_buffer;
|
||||
#if defined(USE_WEBSERVER_OTA_DISABLED) || !defined(USE_WEBSERVER_OTA)
|
||||
|
||||
@@ -33,8 +33,8 @@ class MultipartReader {
|
||||
~MultipartReader();
|
||||
|
||||
// Set callbacks for handling data
|
||||
void set_data_callback(DataCallback callback) { data_callback_ = std::move(callback); }
|
||||
void set_part_complete_callback(PartCompleteCallback callback) { part_complete_callback_ = std::move(callback); }
|
||||
void set_data_callback(DataCallback &&callback) { data_callback_ = std::move(callback); }
|
||||
void set_part_complete_callback(PartCompleteCallback &&callback) { part_complete_callback_ = std::move(callback); }
|
||||
|
||||
// Parse incoming data
|
||||
size_t parse(const char *data, size_t len);
|
||||
|
||||
@@ -204,7 +204,7 @@ class AsyncWebServer {
|
||||
~AsyncWebServer() { this->end(); }
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
void onNotFound(std::function<void(AsyncWebServerRequest *request)> fn) { on_not_found_ = std::move(fn); }
|
||||
void onNotFound(std::function<void(AsyncWebServerRequest *request)> &&fn) { on_not_found_ = std::move(fn); }
|
||||
|
||||
void begin();
|
||||
void end();
|
||||
@@ -327,7 +327,7 @@ class AsyncEventSource : public AsyncWebHandler {
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
void handleRequest(AsyncWebServerRequest *request) override;
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
void onConnect(connect_handler_t cb) { this->on_connect_ = std::move(cb); }
|
||||
void onConnect(connect_handler_t &&cb) { this->on_connect_ = std::move(cb); }
|
||||
|
||||
void try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
|
||||
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
|
||||
|
||||
@@ -354,6 +354,7 @@ CONF_ELSE = "else"
|
||||
CONF_ENABLE_BTM = "enable_btm"
|
||||
CONF_ENABLE_IPV6 = "enable_ipv6"
|
||||
CONF_ENABLE_ON_BOOT = "enable_on_boot"
|
||||
CONF_ENABLE_OTA_ROLLBACK = "enable_ota_rollback"
|
||||
CONF_ENABLE_PIN = "enable_pin"
|
||||
CONF_ENABLE_PRIVATE_NETWORK_ACCESS = "enable_private_network_access"
|
||||
CONF_ENABLE_RRM = "enable_rrm"
|
||||
@@ -462,6 +463,7 @@ CONF_HEAT_OVERRUN = "heat_overrun"
|
||||
CONF_HEATER = "heater"
|
||||
CONF_HEIGHT = "height"
|
||||
CONF_HIDDEN = "hidden"
|
||||
CONF_HIDE_HASH = "hide_hash"
|
||||
CONF_HIDE_TIMESTAMP = "hide_timestamp"
|
||||
CONF_HIGH = "high"
|
||||
CONF_HIGH_VOLTAGE_REFERENCE = "high_voltage_reference"
|
||||
|
||||
@@ -134,7 +134,7 @@ void Application::setup() {
|
||||
this->after_loop_tasks_();
|
||||
this->app_state_ = new_app_state;
|
||||
yield();
|
||||
} while (!component->can_proceed());
|
||||
} while (!component->can_proceed() && !component->is_failed());
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "setup() finished successfully!");
|
||||
@@ -749,4 +749,15 @@ void Application::get_build_time_string(std::span<char, BUILD_TIME_STR_SIZE> buf
|
||||
buffer[buffer.size() - 1] = '\0';
|
||||
}
|
||||
|
||||
void Application::get_comment_string(std::span<char, ESPHOME_COMMENT_SIZE_MAX> buffer) {
|
||||
ESPHOME_strncpy_P(buffer.data(), ESPHOME_COMMENT_STR, ESPHOME_COMMENT_SIZE);
|
||||
buffer[ESPHOME_COMMENT_SIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
uint32_t Application::get_config_hash() { return ESPHOME_CONFIG_HASH; }
|
||||
|
||||
uint32_t Application::get_config_version_hash() { return fnv1a_hash_extend(ESPHOME_CONFIG_HASH, ESPHOME_VERSION); }
|
||||
|
||||
time_t Application::get_build_time() { return ESPHOME_BUILD_TIME; }
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "esphome/core/build_info_data.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
@@ -274,16 +273,15 @@ class Application {
|
||||
return "";
|
||||
}
|
||||
|
||||
/// Maximum size of the comment buffer (including null terminator)
|
||||
static constexpr size_t ESPHOME_COMMENT_SIZE_MAX = 256;
|
||||
|
||||
/// Copy the comment string into the provided buffer
|
||||
/// Buffer must be ESPHOME_COMMENT_SIZE bytes (compile-time enforced)
|
||||
void get_comment_string(std::span<char, ESPHOME_COMMENT_SIZE> buffer) {
|
||||
ESPHOME_strncpy_P(buffer.data(), ESPHOME_COMMENT_STR, buffer.size());
|
||||
buffer[buffer.size() - 1] = '\0';
|
||||
}
|
||||
void get_comment_string(std::span<char, ESPHOME_COMMENT_SIZE_MAX> buffer);
|
||||
|
||||
/// Get the comment of this Application as a string
|
||||
std::string get_comment() {
|
||||
char buffer[ESPHOME_COMMENT_SIZE];
|
||||
char buffer[ESPHOME_COMMENT_SIZE_MAX];
|
||||
this->get_comment_string(buffer);
|
||||
return std::string(buffer);
|
||||
}
|
||||
@@ -294,13 +292,13 @@ class Application {
|
||||
static constexpr size_t BUILD_TIME_STR_SIZE = 26;
|
||||
|
||||
/// Get the config hash as a 32-bit integer
|
||||
constexpr uint32_t get_config_hash() { return ESPHOME_CONFIG_HASH; }
|
||||
uint32_t get_config_hash();
|
||||
|
||||
/// Get the config hash extended with ESPHome version
|
||||
constexpr uint32_t get_config_version_hash() { return fnv1a_hash_extend(ESPHOME_CONFIG_HASH, ESPHOME_VERSION); }
|
||||
uint32_t get_config_version_hash();
|
||||
|
||||
/// Get the build time as a Unix timestamp
|
||||
constexpr time_t get_build_time() { return ESPHOME_BUILD_TIME; }
|
||||
time_t get_build_time();
|
||||
|
||||
/// Copy the build time string into the provided buffer
|
||||
/// Buffer must be BUILD_TIME_STR_SIZE bytes (compile-time enforced)
|
||||
|
||||
@@ -468,8 +468,15 @@ ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) {
|
||||
|
||||
static inline void normalize_accuracy_decimals(float &value, int8_t &accuracy_decimals) {
|
||||
if (accuracy_decimals < 0) {
|
||||
auto multiplier = powf(10.0f, accuracy_decimals);
|
||||
value = roundf(value * multiplier) / multiplier;
|
||||
float divisor;
|
||||
if (accuracy_decimals == -1) {
|
||||
divisor = 10.0f;
|
||||
} else if (accuracy_decimals == -2) {
|
||||
divisor = 100.0f;
|
||||
} else {
|
||||
divisor = pow10_int(-accuracy_decimals);
|
||||
}
|
||||
value = roundf(value / divisor) * divisor;
|
||||
accuracy_decimals = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,6 +439,21 @@ template<size_t STACK_SIZE, typename T = uint8_t> class SmallBufferWithHeapFallb
|
||||
/// @name Mathematics
|
||||
///@{
|
||||
|
||||
/// Compute 10^exp using iterative multiplication/division.
|
||||
/// Avoids pulling in powf/__ieee754_powf (~2.3KB flash) for small integer exponents.
|
||||
/// Matches powf(10, exp) for the int8_t exponent range used by sensor accuracy_decimals.
|
||||
inline float pow10_int(int8_t exp) {
|
||||
float result = 1.0f;
|
||||
if (exp >= 0) {
|
||||
for (int8_t i = 0; i < exp; i++)
|
||||
result *= 10.0f;
|
||||
} else {
|
||||
for (int8_t i = exp; i < 0; i++)
|
||||
result /= 10.0f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Remap \p value from the range (\p min, \p max) to (\p min_out, \p max_out).
|
||||
template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max_out) {
|
||||
return (value - min) * (max_out - min_out) / (max - min) + min_out;
|
||||
|
||||
@@ -38,13 +38,27 @@ template<class T, uint8_t SIZE> class LockFreeQueue {
|
||||
}
|
||||
|
||||
protected:
|
||||
// Advance ring buffer index by one, wrapping at SIZE.
|
||||
// Power-of-2 sizes use modulo (compiler emits single mask instruction).
|
||||
// Non-power-of-2 sizes use comparison to avoid expensive multiply-shift sequences.
|
||||
static constexpr uint8_t next_index(uint8_t index) {
|
||||
if constexpr ((SIZE & (SIZE - 1)) == 0) {
|
||||
return (index + 1) % SIZE;
|
||||
} else {
|
||||
uint8_t next = index + 1;
|
||||
if (next >= SIZE) [[unlikely]]
|
||||
next = 0;
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
// Internal push that reports queue state - for use by derived classes
|
||||
bool push_internal_(T *element, bool &was_empty, uint8_t &old_tail) {
|
||||
if (element == nullptr)
|
||||
return false;
|
||||
|
||||
uint8_t current_tail = tail_.load(std::memory_order_relaxed);
|
||||
uint8_t next_tail = (current_tail + 1) % SIZE;
|
||||
uint8_t next_tail = next_index(current_tail);
|
||||
|
||||
// Read head before incrementing tail
|
||||
uint8_t head_before = head_.load(std::memory_order_acquire);
|
||||
@@ -73,14 +87,21 @@ template<class T, uint8_t SIZE> class LockFreeQueue {
|
||||
}
|
||||
|
||||
T *element = buffer_[current_head];
|
||||
head_.store((current_head + 1) % SIZE, std::memory_order_release);
|
||||
head_.store(next_index(current_head), std::memory_order_release);
|
||||
return element;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
uint8_t tail = tail_.load(std::memory_order_acquire);
|
||||
uint8_t head = head_.load(std::memory_order_acquire);
|
||||
return (tail - head + SIZE) % SIZE;
|
||||
if constexpr ((SIZE & (SIZE - 1)) == 0) {
|
||||
return (tail - head + SIZE) % SIZE;
|
||||
} else {
|
||||
int diff = static_cast<int>(tail) - static_cast<int>(head);
|
||||
if (diff < 0)
|
||||
diff += SIZE;
|
||||
return static_cast<size_t>(diff);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t get_and_reset_dropped_count() { return dropped_count_.exchange(0, std::memory_order_relaxed); }
|
||||
@@ -90,7 +111,7 @@ template<class T, uint8_t SIZE> class LockFreeQueue {
|
||||
bool empty() const { return head_.load(std::memory_order_acquire) == tail_.load(std::memory_order_acquire); }
|
||||
|
||||
bool full() const {
|
||||
uint8_t next_tail = (tail_.load(std::memory_order_relaxed) + 1) % SIZE;
|
||||
uint8_t next_tail = next_index(tail_.load(std::memory_order_relaxed));
|
||||
return next_tail == head_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,11 @@ static constexpr uint32_t HALF_MAX_UINT32 = std::numeric_limits<uint32_t>::max()
|
||||
// max delay to start an interval sequence
|
||||
static constexpr uint32_t MAX_INTERVAL_DELAY = 5000;
|
||||
|
||||
// Prevent inlining of SchedulerItem deletion. On BK7231N (Thumb-1), GCC inlines
|
||||
// ~unique_ptr<SchedulerItem> (~30 bytes each) at every destruction site. Defining
|
||||
// the deleter in the .cpp file ensures a single copy of the destructor + operator delete.
|
||||
void Scheduler::SchedulerItemDeleter::operator()(SchedulerItem *ptr) const noexcept { delete ptr; }
|
||||
|
||||
#if defined(ESPHOME_LOG_HAS_VERBOSE) || defined(ESPHOME_DEBUG_SCHEDULER)
|
||||
// Helper struct for formatting scheduler item names consistently in logs
|
||||
// Uses a stack buffer to avoid heap allocation
|
||||
@@ -135,7 +140,7 @@ bool Scheduler::is_retry_cancelled_locked_(Component *component, NameType name_t
|
||||
// name_type determines storage type: STATIC_STRING uses static_name, others use hash_or_id
|
||||
void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type type, NameType name_type,
|
||||
const char *static_name, uint32_t hash_or_id, uint32_t delay,
|
||||
std::function<void()> func, bool is_retry, bool skip_cancel) {
|
||||
std::function<void()> &&func, bool is_retry, bool skip_cancel) {
|
||||
if (delay == SCHEDULER_DONT_RUN) {
|
||||
// Still need to cancel existing timer if we have a name/id
|
||||
if (!skip_cancel) {
|
||||
@@ -216,17 +221,18 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
||||
target->push_back(std::move(item));
|
||||
}
|
||||
|
||||
void HOT Scheduler::set_timeout(Component *component, const char *name, uint32_t timeout, std::function<void()> func) {
|
||||
void HOT Scheduler::set_timeout(Component *component, const char *name, uint32_t timeout,
|
||||
std::function<void()> &&func) {
|
||||
this->set_timer_common_(component, SchedulerItem::TIMEOUT, NameType::STATIC_STRING, name, 0, timeout,
|
||||
std::move(func));
|
||||
}
|
||||
|
||||
void HOT Scheduler::set_timeout(Component *component, const std::string &name, uint32_t timeout,
|
||||
std::function<void()> func) {
|
||||
std::function<void()> &&func) {
|
||||
this->set_timer_common_(component, SchedulerItem::TIMEOUT, NameType::HASHED_STRING, nullptr, fnv1a_hash(name),
|
||||
timeout, std::move(func));
|
||||
}
|
||||
void HOT Scheduler::set_timeout(Component *component, uint32_t id, uint32_t timeout, std::function<void()> func) {
|
||||
void HOT Scheduler::set_timeout(Component *component, uint32_t id, uint32_t timeout, std::function<void()> &&func) {
|
||||
this->set_timer_common_(component, SchedulerItem::TIMEOUT, NameType::NUMERIC_ID, nullptr, id, timeout,
|
||||
std::move(func));
|
||||
}
|
||||
@@ -240,17 +246,17 @@ bool HOT Scheduler::cancel_timeout(Component *component, uint32_t id) {
|
||||
return this->cancel_item_(component, NameType::NUMERIC_ID, nullptr, id, SchedulerItem::TIMEOUT);
|
||||
}
|
||||
void HOT Scheduler::set_interval(Component *component, const std::string &name, uint32_t interval,
|
||||
std::function<void()> func) {
|
||||
std::function<void()> &&func) {
|
||||
this->set_timer_common_(component, SchedulerItem::INTERVAL, NameType::HASHED_STRING, nullptr, fnv1a_hash(name),
|
||||
interval, std::move(func));
|
||||
}
|
||||
|
||||
void HOT Scheduler::set_interval(Component *component, const char *name, uint32_t interval,
|
||||
std::function<void()> func) {
|
||||
std::function<void()> &&func) {
|
||||
this->set_timer_common_(component, SchedulerItem::INTERVAL, NameType::STATIC_STRING, name, 0, interval,
|
||||
std::move(func));
|
||||
}
|
||||
void HOT Scheduler::set_interval(Component *component, uint32_t id, uint32_t interval, std::function<void()> func) {
|
||||
void HOT Scheduler::set_interval(Component *component, uint32_t id, uint32_t interval, std::function<void()> &&func) {
|
||||
this->set_timer_common_(component, SchedulerItem::INTERVAL, NameType::NUMERIC_ID, nullptr, id, interval,
|
||||
std::move(func));
|
||||
}
|
||||
@@ -467,7 +473,7 @@ void HOT Scheduler::call(uint32_t now) {
|
||||
|
||||
if (now_64 - last_print > 2000) {
|
||||
last_print = now_64;
|
||||
std::vector<std::unique_ptr<SchedulerItem>> old_items;
|
||||
std::vector<SchedulerItemPtr> old_items;
|
||||
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
|
||||
const auto last_dbg = this->last_millis_.load(std::memory_order_relaxed);
|
||||
const auto major_dbg = this->millis_major_.load(std::memory_order_relaxed);
|
||||
@@ -480,7 +486,7 @@ void HOT Scheduler::call(uint32_t now) {
|
||||
// Cleanup before debug output
|
||||
this->cleanup_();
|
||||
while (!this->items_.empty()) {
|
||||
std::unique_ptr<SchedulerItem> item;
|
||||
SchedulerItemPtr item;
|
||||
{
|
||||
LockGuard guard{this->lock_};
|
||||
item = this->pop_raw_locked_();
|
||||
@@ -641,7 +647,7 @@ size_t HOT Scheduler::cleanup_() {
|
||||
}
|
||||
return this->items_.size();
|
||||
}
|
||||
std::unique_ptr<Scheduler::SchedulerItem> HOT Scheduler::pop_raw_locked_() {
|
||||
Scheduler::SchedulerItemPtr HOT Scheduler::pop_raw_locked_() {
|
||||
std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
|
||||
|
||||
// Move the item out before popping - this is the item that was at the front of the heap
|
||||
@@ -864,8 +870,7 @@ uint64_t Scheduler::millis_64_(uint32_t now) {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool HOT Scheduler::SchedulerItem::cmp(const std::unique_ptr<SchedulerItem> &a,
|
||||
const std::unique_ptr<SchedulerItem> &b) {
|
||||
bool HOT Scheduler::SchedulerItem::cmp(const SchedulerItemPtr &a, const SchedulerItemPtr &b) {
|
||||
// High bits are almost always equal (change only on 32-bit rollover ~49 days)
|
||||
// Optimize for common case: check low bits first when high bits are equal
|
||||
return (a->next_execution_high_ == b->next_execution_high_) ? (a->next_execution_low_ > b->next_execution_low_)
|
||||
@@ -876,7 +881,7 @@ bool HOT Scheduler::SchedulerItem::cmp(const std::unique_ptr<SchedulerItem> &a,
|
||||
// IMPORTANT: Caller must hold the scheduler lock before calling this function.
|
||||
// This protects scheduler_item_pool_ from concurrent access by other threads
|
||||
// that may be acquiring items from the pool in set_timer_common_().
|
||||
void Scheduler::recycle_item_main_loop_(std::unique_ptr<SchedulerItem> item) {
|
||||
void Scheduler::recycle_item_main_loop_(SchedulerItemPtr item) {
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
@@ -919,8 +924,8 @@ void Scheduler::debug_log_timer_(const SchedulerItem *item, NameType name_type,
|
||||
|
||||
// Helper to get or create a scheduler item from the pool
|
||||
// IMPORTANT: Caller must hold the scheduler lock before calling this function.
|
||||
std::unique_ptr<Scheduler::SchedulerItem> Scheduler::get_item_from_pool_locked_() {
|
||||
std::unique_ptr<SchedulerItem> item;
|
||||
Scheduler::SchedulerItemPtr Scheduler::get_item_from_pool_locked_() {
|
||||
SchedulerItemPtr item;
|
||||
if (!this->scheduler_item_pool_.empty()) {
|
||||
item = std::move(this->scheduler_item_pool_.back());
|
||||
this->scheduler_item_pool_.pop_back();
|
||||
@@ -928,7 +933,7 @@ std::unique_ptr<Scheduler::SchedulerItem> Scheduler::get_item_from_pool_locked_(
|
||||
ESP_LOGD(TAG, "Reused item from pool (pool size now: %zu)", this->scheduler_item_pool_.size());
|
||||
#endif
|
||||
} else {
|
||||
item = make_unique<SchedulerItem>();
|
||||
item = SchedulerItemPtr(new SchedulerItem());
|
||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||
ESP_LOGD(TAG, "Allocated new item (pool empty)");
|
||||
#endif
|
||||
|
||||
@@ -33,7 +33,7 @@ class Scheduler {
|
||||
// std::string overload - deprecated, use const char* or uint32_t instead
|
||||
// Remove before 2026.7.0
|
||||
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0")
|
||||
void set_timeout(Component *component, const std::string &name, uint32_t timeout, std::function<void()> func);
|
||||
void set_timeout(Component *component, const std::string &name, uint32_t timeout, std::function<void()> &&func);
|
||||
|
||||
/** Set a timeout with a const char* name.
|
||||
*
|
||||
@@ -43,11 +43,11 @@ class Scheduler {
|
||||
* - A static const char* variable
|
||||
* - A pointer with lifetime >= the scheduled task
|
||||
*/
|
||||
void set_timeout(Component *component, const char *name, uint32_t timeout, std::function<void()> func);
|
||||
void set_timeout(Component *component, const char *name, uint32_t timeout, std::function<void()> &&func);
|
||||
/// Set a timeout with a numeric ID (zero heap allocation)
|
||||
void set_timeout(Component *component, uint32_t id, uint32_t timeout, std::function<void()> func);
|
||||
void set_timeout(Component *component, uint32_t id, uint32_t timeout, std::function<void()> &&func);
|
||||
/// Set a timeout with an internal scheduler ID (separate namespace from component NUMERIC_ID)
|
||||
void set_timeout(Component *component, InternalSchedulerID id, uint32_t timeout, std::function<void()> func) {
|
||||
void set_timeout(Component *component, InternalSchedulerID id, uint32_t timeout, std::function<void()> &&func) {
|
||||
this->set_timer_common_(component, SchedulerItem::TIMEOUT, NameType::NUMERIC_ID_INTERNAL, nullptr,
|
||||
static_cast<uint32_t>(id), timeout, std::move(func));
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class Scheduler {
|
||||
}
|
||||
|
||||
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0")
|
||||
void set_interval(Component *component, const std::string &name, uint32_t interval, std::function<void()> func);
|
||||
void set_interval(Component *component, const std::string &name, uint32_t interval, std::function<void()> &&func);
|
||||
|
||||
/** Set an interval with a const char* name.
|
||||
*
|
||||
@@ -72,11 +72,11 @@ class Scheduler {
|
||||
* - A static const char* variable
|
||||
* - A pointer with lifetime >= the scheduled task
|
||||
*/
|
||||
void set_interval(Component *component, const char *name, uint32_t interval, std::function<void()> func);
|
||||
void set_interval(Component *component, const char *name, uint32_t interval, std::function<void()> &&func);
|
||||
/// Set an interval with a numeric ID (zero heap allocation)
|
||||
void set_interval(Component *component, uint32_t id, uint32_t interval, std::function<void()> func);
|
||||
void set_interval(Component *component, uint32_t id, uint32_t interval, std::function<void()> &&func);
|
||||
/// Set an interval with an internal scheduler ID (separate namespace from component NUMERIC_ID)
|
||||
void set_interval(Component *component, InternalSchedulerID id, uint32_t interval, std::function<void()> func) {
|
||||
void set_interval(Component *component, InternalSchedulerID id, uint32_t interval, std::function<void()> &&func) {
|
||||
this->set_timer_common_(component, SchedulerItem::INTERVAL, NameType::NUMERIC_ID_INTERNAL, nullptr,
|
||||
static_cast<uint32_t>(id), interval, std::move(func));
|
||||
}
|
||||
@@ -142,6 +142,19 @@ class Scheduler {
|
||||
};
|
||||
|
||||
protected:
|
||||
struct SchedulerItem;
|
||||
|
||||
// Custom deleter for SchedulerItem unique_ptr that prevents the compiler from
|
||||
// inlining the destructor at every destruction site. On BK7231N (Thumb-1), GCC
|
||||
// inlines ~unique_ptr<SchedulerItem> (~30 bytes: null check + ~std::function +
|
||||
// operator delete) at every destruction site, while ESP32/ESP8266/RTL8720CF outline
|
||||
// it into a single helper. This noinline deleter ensures only one copy exists.
|
||||
// operator() is defined in scheduler.cpp to prevent inlining.
|
||||
struct SchedulerItemDeleter {
|
||||
void operator()(SchedulerItem *ptr) const noexcept;
|
||||
};
|
||||
using SchedulerItemPtr = std::unique_ptr<SchedulerItem, SchedulerItemDeleter>;
|
||||
|
||||
struct SchedulerItem {
|
||||
// Ordered by size to minimize padding
|
||||
Component *component;
|
||||
@@ -233,7 +246,7 @@ class Scheduler {
|
||||
name_type_ = type;
|
||||
}
|
||||
|
||||
static bool cmp(const std::unique_ptr<SchedulerItem> &a, const std::unique_ptr<SchedulerItem> &b);
|
||||
static bool cmp(const SchedulerItemPtr &a, const SchedulerItemPtr &b);
|
||||
|
||||
// Note: We use 48 bits total (32 + 16), stored in a 64-bit value for API compatibility.
|
||||
// The upper 16 bits of the 64-bit value are always zero, which is fine since
|
||||
@@ -255,7 +268,7 @@ class Scheduler {
|
||||
// Common implementation for both timeout and interval
|
||||
// name_type determines storage type: STATIC_STRING uses static_name, others use hash_or_id
|
||||
void set_timer_common_(Component *component, SchedulerItem::Type type, NameType name_type, const char *static_name,
|
||||
uint32_t hash_or_id, uint32_t delay, std::function<void()> func, bool is_retry = false,
|
||||
uint32_t hash_or_id, uint32_t delay, std::function<void()> &&func, bool is_retry = false,
|
||||
bool skip_cancel = false);
|
||||
|
||||
// Common implementation for retry - Remove before 2026.8.0
|
||||
@@ -276,10 +289,10 @@ class Scheduler {
|
||||
size_t cleanup_();
|
||||
// Remove and return the front item from the heap
|
||||
// IMPORTANT: Caller must hold the scheduler lock before calling this function.
|
||||
std::unique_ptr<SchedulerItem> pop_raw_locked_();
|
||||
SchedulerItemPtr pop_raw_locked_();
|
||||
// Get or create a scheduler item from the pool
|
||||
// IMPORTANT: Caller must hold the scheduler lock before calling this function.
|
||||
std::unique_ptr<SchedulerItem> get_item_from_pool_locked_();
|
||||
SchedulerItemPtr get_item_from_pool_locked_();
|
||||
|
||||
private:
|
||||
// Helper to cancel items - must be called with lock held
|
||||
@@ -303,9 +316,9 @@ class Scheduler {
|
||||
// Helper function to check if item matches criteria for cancellation
|
||||
// name_type determines matching: STATIC_STRING uses static_name, others use hash_or_id
|
||||
// IMPORTANT: Must be called with scheduler lock held
|
||||
inline bool HOT matches_item_locked_(const std::unique_ptr<SchedulerItem> &item, Component *component,
|
||||
NameType name_type, const char *static_name, uint32_t hash_or_id,
|
||||
SchedulerItem::Type type, bool match_retry, bool skip_removed = true) const {
|
||||
inline bool HOT matches_item_locked_(const SchedulerItemPtr &item, Component *component, NameType name_type,
|
||||
const char *static_name, uint32_t hash_or_id, SchedulerItem::Type type,
|
||||
bool match_retry, bool skip_removed = true) const {
|
||||
// THREAD SAFETY: Check for nullptr first to prevent LoadProhibited crashes. On multi-threaded
|
||||
// platforms, items can be moved out of defer_queue_ during processing, leaving nullptr entries.
|
||||
// PR #11305 added nullptr checks in callers (mark_matching_items_removed_locked_()), but this check
|
||||
@@ -340,7 +353,7 @@ class Scheduler {
|
||||
// IMPORTANT: Only call from main loop context! Recycling clears the callback,
|
||||
// so calling from another thread while the callback is executing causes use-after-free.
|
||||
// IMPORTANT: Caller must hold the scheduler lock before calling this function.
|
||||
void recycle_item_main_loop_(std::unique_ptr<SchedulerItem> item);
|
||||
void recycle_item_main_loop_(SchedulerItemPtr item);
|
||||
|
||||
// Helper to perform full cleanup when too many items are cancelled
|
||||
void full_cleanup_removed_items_();
|
||||
@@ -396,7 +409,7 @@ class Scheduler {
|
||||
// Merge lock acquisitions: instead of separate locks for move-out and recycle (2N+1 total),
|
||||
// recycle each item after re-acquiring the lock for the next iteration (N+1 total).
|
||||
// The lock is held across: recycle → loop condition → move-out, then released for execution.
|
||||
std::unique_ptr<SchedulerItem> item;
|
||||
SchedulerItemPtr item;
|
||||
|
||||
this->lock_.lock();
|
||||
while (this->defer_queue_front_ < defer_queue_end) {
|
||||
@@ -496,9 +509,10 @@ class Scheduler {
|
||||
// name_type determines matching: STATIC_STRING uses static_name, others use hash_or_id
|
||||
// Returns the number of items marked for removal
|
||||
// IMPORTANT: Must be called with scheduler lock held
|
||||
size_t mark_matching_items_removed_locked_(std::vector<std::unique_ptr<SchedulerItem>> &container,
|
||||
Component *component, NameType name_type, const char *static_name,
|
||||
uint32_t hash_or_id, SchedulerItem::Type type, bool match_retry) {
|
||||
__attribute__((noinline)) size_t mark_matching_items_removed_locked_(std::vector<SchedulerItemPtr> &container,
|
||||
Component *component, NameType name_type,
|
||||
const char *static_name, uint32_t hash_or_id,
|
||||
SchedulerItem::Type type, bool match_retry) {
|
||||
size_t count = 0;
|
||||
for (auto &item : container) {
|
||||
// Skip nullptr items (can happen in defer_queue_ when items are being processed)
|
||||
@@ -514,15 +528,15 @@ class Scheduler {
|
||||
}
|
||||
|
||||
Mutex lock_;
|
||||
std::vector<std::unique_ptr<SchedulerItem>> items_;
|
||||
std::vector<std::unique_ptr<SchedulerItem>> to_add_;
|
||||
std::vector<SchedulerItemPtr> items_;
|
||||
std::vector<SchedulerItemPtr> to_add_;
|
||||
#ifndef ESPHOME_THREAD_SINGLE
|
||||
// Single-core platforms don't need the defer queue and save ~32 bytes of RAM
|
||||
// Using std::vector instead of std::deque avoids 512-byte chunked allocations
|
||||
// Index tracking avoids O(n) erase() calls when draining the queue each loop
|
||||
std::vector<std::unique_ptr<SchedulerItem>> defer_queue_; // FIFO queue for defer() calls
|
||||
size_t defer_queue_front_{0}; // Index of first valid item in defer_queue_ (tracks consumed items)
|
||||
#endif /* ESPHOME_THREAD_SINGLE */
|
||||
std::vector<SchedulerItemPtr> defer_queue_; // FIFO queue for defer() calls
|
||||
size_t defer_queue_front_{0}; // Index of first valid item in defer_queue_ (tracks consumed items)
|
||||
#endif /* ESPHOME_THREAD_SINGLE */
|
||||
uint32_t to_remove_{0};
|
||||
|
||||
// Memory pool for recycling SchedulerItem objects to reduce heap churn.
|
||||
@@ -533,7 +547,7 @@ class Scheduler {
|
||||
// - The pool significantly reduces heap fragmentation which is critical because heap allocation/deallocation
|
||||
// can stall the entire system, causing timing issues and dropped events for any components that need
|
||||
// to synchronize between tasks (see https://github.com/esphome/backlog/issues/52)
|
||||
std::vector<std::unique_ptr<SchedulerItem>> scheduler_item_pool_;
|
||||
std::vector<SchedulerItemPtr> scheduler_item_pool_;
|
||||
|
||||
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
|
||||
/*
|
||||
|
||||
@@ -12,7 +12,7 @@ platformio==6.1.19
|
||||
esptool==5.2.0
|
||||
click==8.1.7
|
||||
esphome-dashboard==20260210.0
|
||||
aioesphomeapi==44.0.0
|
||||
aioesphomeapi==44.1.0
|
||||
zeroconf==0.148.0
|
||||
puremagic==1.30
|
||||
ruamel.yaml==0.19.1 # dashboard_import
|
||||
|
||||
@@ -22,6 +22,23 @@ display:
|
||||
id: p4_86
|
||||
model: "WAVESHARE-P4-86-PANEL"
|
||||
rotation: 180
|
||||
- platform: mipi_dsi
|
||||
model: custom
|
||||
id: custom_id
|
||||
dimensions:
|
||||
width: 400
|
||||
height: 1280
|
||||
hsync_back_porch: 40
|
||||
hsync_pulse_width: 30
|
||||
hsync_front_porch: 40
|
||||
vsync_back_porch: 20
|
||||
vsync_pulse_width: 10
|
||||
vsync_front_porch: 20
|
||||
pclk_frequency: 48Mhz
|
||||
lane_bit_rate: 1.2Gbps
|
||||
rotation: 180
|
||||
transform: disabled
|
||||
init_sequence:
|
||||
i2c:
|
||||
sda: GPIO7
|
||||
scl: GPIO8
|
||||
|
||||
@@ -123,7 +123,8 @@ def test_code_generation(
|
||||
in main_cpp
|
||||
)
|
||||
assert "set_init_sequence({224, 1, 0, 225, 1, 147, 226, 1," in main_cpp
|
||||
assert "p4_nano->set_lane_bit_rate(1500);" in main_cpp
|
||||
assert "p4_nano->set_lane_bit_rate(1500.0f);" in main_cpp
|
||||
assert "p4_nano->set_rotation(display::DISPLAY_ROTATION_90_DEGREES);" in main_cpp
|
||||
assert "p4_86->set_rotation(display::DISPLAY_ROTATION_0_DEGREES);" in main_cpp
|
||||
assert "custom_id->set_rotation(display::DISPLAY_ROTATION_180_DEGREES);" in main_cpp
|
||||
# assert "backlight_id = new light::LightState(mipi_dsi_dsibacklight_id);" in main_cpp
|
||||
|
||||
@@ -71,6 +71,32 @@ esphome:
|
||||
- light.control:
|
||||
id: test_monochromatic_light
|
||||
state: on
|
||||
# Test static effect name resolution at codegen time
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: Strobe
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: none
|
||||
# Test resolving a different effect on the same light
|
||||
- light.control:
|
||||
id: test_monochromatic_light
|
||||
effect: My Flicker
|
||||
# Test effect: None (capitalized)
|
||||
- light.control:
|
||||
id: test_monochromatic_light
|
||||
effect: None
|
||||
# Test effect lambda with no args (on_boot has empty Ts...)
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: !lambda 'return "Strobe";'
|
||||
# Test effect lambda with non-empty args (repeat passes uint32_t iteration)
|
||||
- repeat:
|
||||
count: 3
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: !lambda 'return iteration > 1 ? "Strobe" : "none";'
|
||||
- light.dim_relative:
|
||||
id: test_monochromatic_light
|
||||
relative_brightness: 5%
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
text_sensor:
|
||||
- platform: version
|
||||
name: "ESPHome Version"
|
||||
name: "ESPHome Version Full"
|
||||
|
||||
- platform: version
|
||||
name: "ESPHome Version No Timestamp"
|
||||
hide_timestamp: true
|
||||
|
||||
- platform: version
|
||||
name: "ESPHome Version No Hash"
|
||||
hide_hash: true
|
||||
|
||||
- platform: version
|
||||
name: "ESPHome Version Shortest"
|
||||
hide_timestamp: true
|
||||
hide_hash: true
|
||||
|
||||
Reference in New Issue
Block a user