Merge branch 'dev' into deprecate-sensor-raw-state

This commit is contained in:
J. Nick Koston
2026-03-22 18:26:43 -10:00
committed by GitHub
15 changed files with 194 additions and 48 deletions
@@ -18,12 +18,6 @@ void EthernetComponent::set_type(EthernetType type) { this->type_ = type; }
void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; }
#endif
// set_use_address() is guaranteed to be called during component setup by Python code generation,
// so use_address_ will always be valid when get_use_address() is called - no fallback needed.
const char *EthernetComponent::get_use_address() const { return this->use_address_; }
void EthernetComponent::set_use_address(const char *use_address) { this->use_address_ = use_address; }
#ifdef USE_ETHERNET_IP_STATE_LISTENERS
void EthernetComponent::notify_ip_state_listeners_() {
auto ips = this->get_ip_addresses();
@@ -103,8 +103,8 @@ class EthernetComponent final : public Component {
network::IPAddresses get_ip_addresses();
network::IPAddress get_dns_address(uint8_t num);
const char *get_use_address() const;
void set_use_address(const char *use_address);
const char *get_use_address() const { return this->use_address_; }
void set_use_address(const char *use_address) { this->use_address_ = use_address; }
void get_eth_mac_address_raw(uint8_t *mac);
// Remove before 2026.9.0
ESPDEPRECATED("Use get_eth_mac_address_pretty_into_buffer() instead. Removed in 2026.9.0", "2026.3.0")
-24
View File
@@ -42,29 +42,5 @@ network::IPAddresses get_ip_addresses() {
return {};
}
const char *get_use_address() {
// Global component pointers are guaranteed to be set by component constructors when USE_* is defined
#ifdef USE_ETHERNET
return ethernet::global_eth_component->get_use_address();
#endif
#ifdef USE_MODEM
return modem::global_modem_component->get_use_address();
#endif
#ifdef USE_WIFI
return wifi::global_wifi_component->get_use_address();
#endif
#ifdef USE_OPENTHREAD
return openthread::global_openthread_component->get_use_address();
#endif
#if !defined(USE_ETHERNET) && !defined(USE_MODEM) && !defined(USE_WIFI) && !defined(USE_OPENTHREAD)
// Fallback when no network component is defined (e.g., host platform)
return "";
#endif
}
} // namespace esphome::network
#endif
+23 -1
View File
@@ -54,7 +54,29 @@ ESPHOME_ALWAYS_INLINE inline bool is_connected() {
/// Return whether the network is disabled (only wifi for now)
bool is_disabled();
/// Get the active network hostname
const char *get_use_address();
ESPHOME_ALWAYS_INLINE inline const char *get_use_address() {
// Global component pointers are guaranteed to be set by component constructors when USE_* is defined
#ifdef USE_ETHERNET
return ethernet::global_eth_component->get_use_address();
#endif
#ifdef USE_MODEM
return modem::global_modem_component->get_use_address();
#endif
#ifdef USE_WIFI
return wifi::global_wifi_component->get_use_address();
#endif
#ifdef USE_OPENTHREAD
return openthread::global_openthread_component->get_use_address();
#endif
#if !defined(USE_ETHERNET) && !defined(USE_MODEM) && !defined(USE_WIFI) && !defined(USE_OPENTHREAD)
// Fallback when no network component is defined (e.g., host platform)
return "";
#endif
}
IPAddresses get_ip_addresses();
} // namespace esphome::network
@@ -257,11 +257,5 @@ void OpenThreadComponent::on_factory_reset(std::function<void()> callback) {
ESP_LOGD(TAG, "Waiting on Confirmation Removal SRP Host and Services");
}
// set_use_address() is guaranteed to be called during component setup by Python code generation,
// so use_address_ will always be valid when get_use_address() is called - no fallback needed.
const char *OpenThreadComponent::get_use_address() const { return this->use_address_; }
void OpenThreadComponent::set_use_address(const char *use_address) { this->use_address_ = use_address; }
} // namespace esphome::openthread
#endif
+2 -2
View File
@@ -37,8 +37,8 @@ class OpenThreadComponent : public Component {
void on_factory_reset(std::function<void()> callback);
void defer_factory_reset_external_callback();
const char *get_use_address() const;
void set_use_address(const char *use_address);
const char *get_use_address() const { return this->use_address_; }
void set_use_address(const char *use_address) { this->use_address_ = use_address; }
#if CONFIG_OPENTHREAD_MTD
void set_poll_period(uint32_t poll_period) { this->poll_period_ = poll_period; }
#endif
@@ -891,10 +891,6 @@ network::IPAddress WiFiComponent::get_dns_address(int num) {
return this->wifi_dns_ip_(num);
return {};
}
// set_use_address() is guaranteed to be called during component setup by Python code generation,
// so use_address_ will always be valid when get_use_address() is called - no fallback needed.
const char *WiFiComponent::get_use_address() const { return this->use_address_; }
void WiFiComponent::set_use_address(const char *use_address) { this->use_address_ = use_address; }
#ifdef USE_WIFI_AP
void WiFiComponent::setup_ap_config_() {
+2 -2
View File
@@ -481,8 +481,8 @@ class WiFiComponent final : public Component {
network::IPAddress get_dns_address(int num);
network::IPAddresses get_ip_addresses();
const char *get_use_address() const;
void set_use_address(const char *use_address);
const char *get_use_address() const { return this->use_address_; }
void set_use_address(const char *use_address) { this->use_address_ = use_address; }
const wifi_scan_vector_t<WiFiScanResult> &get_scan_result() const { return scan_result_; }
+7 -1
View File
@@ -166,7 +166,13 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
item->component = component;
item->set_name(name_type, static_name, hash_or_id);
item->type = type;
item->callback = std::move(func);
// Use destroy + placement-new instead of move-assignment.
// GCC's std::function::operator=(function&&) does a full swap dance even when the
// target is empty. Since recycled/new items always have an empty callback, we can
// destroy the empty one (no-op) and move-construct directly, saving ~40 bytes of
// swap/destructor code on Xtensa.
item->callback.~function();
new (&item->callback) std::function<void()>(std::move(func));
// Reset remove flag - recycled items may have been cancelled (remove=true) in previous use
this->set_item_removed_(item, false);
item->is_retry = is_retry;
@@ -0,0 +1,5 @@
from tests.testing_helpers import ComponentManifestOverride
def override_manifest(manifest: ComponentManifestOverride) -> None:
manifest.enable_codegen()
@@ -0,0 +1,61 @@
#include <benchmark/benchmark.h>
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome::binary_sensor::benchmarks {
static constexpr int kInnerIterations = 2000;
// Benchmark: publish_state with alternating values (forces state change every time)
static void BinarySensorPublish_Alternating(benchmark::State &state) {
BinarySensor sensor;
// First publish to establish initial state
sensor.publish_initial_state(false);
for (auto _ : state) {
for (int i = 0; i < kInnerIterations; i++) {
sensor.publish_state(i % 2 == 0);
}
benchmark::DoNotOptimize(sensor.state);
}
state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(BinarySensorPublish_Alternating);
// Benchmark: publish_state with same value (tests dedup fast path)
static void BinarySensorPublish_NoChange(benchmark::State &state) {
BinarySensor sensor;
sensor.publish_initial_state(true);
for (auto _ : state) {
for (int i = 0; i < kInnerIterations; i++) {
sensor.publish_state(true);
}
benchmark::DoNotOptimize(sensor.state);
}
state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(BinarySensorPublish_NoChange);
// Benchmark: publish_state with a callback registered
static void BinarySensorPublish_WithCallback(benchmark::State &state) {
BinarySensor sensor;
int callback_count = 0;
sensor.add_on_state_callback([&callback_count](bool) { callback_count++; });
sensor.publish_initial_state(false);
for (auto _ : state) {
for (int i = 0; i < kInnerIterations; i++) {
sensor.publish_state(i % 2 == 0);
}
benchmark::DoNotOptimize(callback_count);
}
state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(BinarySensorPublish_WithCallback);
} // namespace esphome::binary_sensor::benchmarks
@@ -0,0 +1 @@
binary_sensor:
@@ -0,0 +1,12 @@
import esphome.codegen as cg
from tests.testing_helpers import ComponentManifestOverride
def override_manifest(manifest: ComponentManifestOverride) -> None:
# Sensor filter benchmarks need USE_SENSOR_FILTER defined.
# We use a custom to_code instead of enable_codegen() to avoid
# pulling in the full sensor component setup.
async def to_code(config):
cg.add_define("USE_SENSOR_FILTER")
manifest.to_code = to_code
@@ -0,0 +1,78 @@
#include <benchmark/benchmark.h>
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/sensor/filter.h"
namespace esphome::sensor::benchmarks {
static constexpr int kInnerIterations = 2000;
// Benchmark: sensor publish through a SlidingWindowMovingAverageFilter (window=5, send_every=1)
static void SensorFilter_SlidingWindowAvg(benchmark::State &state) {
Sensor sensor;
// Create filter: window_size=5, send_every=1, send_first_at=1
auto *filter = new SlidingWindowMovingAverageFilter(5, 1, 1);
sensor.add_filter(filter);
float value = 0.0f;
for (auto _ : state) {
for (int i = 0; i < kInnerIterations; i++) {
sensor.publish_state(value);
value += 0.1f;
if (value > 1000.0f)
value = 0.0f;
}
benchmark::DoNotOptimize(sensor.state);
}
state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(SensorFilter_SlidingWindowAvg);
// Benchmark: sensor publish through ExponentialMovingAverageFilter
static void SensorFilter_ExponentialMovingAvg(benchmark::State &state) {
Sensor sensor;
// alpha=0.1, send_every=1, send_first_at=1
auto *filter = new ExponentialMovingAverageFilter(0.1f, 1, 1);
sensor.add_filter(filter);
float value = 0.0f;
for (auto _ : state) {
for (int i = 0; i < kInnerIterations; i++) {
sensor.publish_state(value);
value += 0.1f;
if (value > 1000.0f)
value = 0.0f;
}
benchmark::DoNotOptimize(sensor.state);
}
state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(SensorFilter_ExponentialMovingAvg);
// Benchmark: sensor publish through a chain of 3 filters (offset + multiply + sliding window)
static void SensorFilter_Chain3(benchmark::State &state) {
Sensor sensor;
sensor.add_filters({
new OffsetFilter(1.0f),
new MultiplyFilter(2.0f),
new SlidingWindowMovingAverageFilter(5, 1, 1),
});
float value = 0.0f;
for (auto _ : state) {
for (int i = 0; i < kInnerIterations; i++) {
sensor.publish_state(value);
value += 0.1f;
if (value > 1000.0f)
value = 0.0f;
}
benchmark::DoNotOptimize(sensor.state);
}
state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(SensorFilter_Chain3);
} // namespace esphome::sensor::benchmarks
@@ -0,0 +1 @@
sensor: