Merge remote-tracking branch 'upstream/dev' into 20260218-zigbee-proxy

This commit is contained in:
kbx81
2026-02-24 16:51:04 -06:00
51 changed files with 248 additions and 195 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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]:

View File

@@ -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)

View File

@@ -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_{};

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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_; }

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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_,

View File

@@ -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};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]))

View File

@@ -1,40 +1,54 @@
#include "version_text_sensor.h"
#include "esphome/core/application.h"
#include "esphome/core/build_info_data.h"
#include "esphome/core/log.h"
#include "esphome/core/version.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

View File

@@ -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

View File

@@ -463,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"

View File

@@ -496,9 +496,9 @@ 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<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) {
size_t count = 0;
for (auto &item : container) {
// Skip nullptr items (can happen in defer_queue_ when items are being processed)

View File

@@ -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

View File

@@ -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

View File

@@ -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