From 484f4b3aadbcf9e97c21bbdaf35265946b7fda9c Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 6 Jan 2026 09:34:28 -0500 Subject: [PATCH 01/17] [cc1101] Add PLL lock verification and retry support (#13006) --- esphome/components/cc1101/cc1101.cpp | 67 +++++++++++++++++++------- esphome/components/cc1101/cc1101.h | 5 +- esphome/components/cc1101/cc1101defs.h | 3 ++ 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/esphome/components/cc1101/cc1101.cpp b/esphome/components/cc1101/cc1101.cpp index 5727d485f64..c4507a54e54 100644 --- a/esphome/components/cc1101/cc1101.cpp +++ b/esphome/components/cc1101/cc1101.cpp @@ -140,7 +140,10 @@ void CC1101Component::setup() { this->write_(static_cast(i)); } this->set_output_power(this->output_power_requested_); - this->strobe_(Command::RX); + if (!this->enter_rx_()) { + this->mark_failed(); + return; + } // Defer pin mode setup until after all components have completed setup() // This handles the case where remote_transmitter runs after CC1101 and changes pin mode @@ -163,8 +166,7 @@ void CC1101Component::loop() { ESP_LOGW(TAG, "RX FIFO overflow, flushing"); this->enter_idle_(); this->strobe_(Command::FRX); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return; } @@ -181,8 +183,7 @@ void CC1101Component::loop() { ESP_LOGW(TAG, "Invalid packet: rx_bytes %u, payload_length %u", rx_bytes, payload_length); this->enter_idle_(); this->strobe_(Command::FRX); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return; } this->packet_.resize(payload_length); @@ -203,8 +204,7 @@ void CC1101Component::loop() { // Return to rx this->enter_idle_(); this->strobe_(Command::FRX); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); } void CC1101Component::dump_config() { @@ -235,9 +235,8 @@ void CC1101Component::begin_tx() { if (this->gdo0_pin_ != nullptr) { this->gdo0_pin_->pin_mode(gpio::FLAG_OUTPUT); } - this->strobe_(Command::TX); - if (!this->wait_for_state_(State::TX, 50)) { - ESP_LOGW(TAG, "Timed out waiting for TX state!"); + if (!this->enter_tx_()) { + ESP_LOGW(TAG, "Failed to enter TX state!"); } } @@ -246,7 +245,9 @@ void CC1101Component::begin_rx() { if (this->gdo0_pin_ != nullptr) { this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT); } - this->strobe_(Command::RX); + if (!this->enter_rx_()) { + ESP_LOGW(TAG, "Failed to enter RX state!"); + } } void CC1101Component::reset() { @@ -272,11 +273,33 @@ bool CC1101Component::wait_for_state_(State target_state, uint32_t timeout_ms) { return false; } +bool CC1101Component::enter_calibrated_(State target_state, Command cmd) { + // The PLL must be recalibrated until PLL lock is achieved + for (uint8_t retries = PLL_LOCK_RETRIES; retries > 0; retries--) { + this->strobe_(cmd); + if (!this->wait_for_state_(target_state)) { + return false; + } + this->read_(Register::FSCAL1); + if (this->state_.FSCAL1 != FSCAL1_PLL_NOT_LOCKED) { + return true; + } + ESP_LOGW(TAG, "PLL lock failed, retrying calibration"); + this->enter_idle_(); + } + ESP_LOGE(TAG, "PLL lock failed after retries"); + return false; +} + void CC1101Component::enter_idle_() { this->strobe_(Command::IDLE); this->wait_for_state_(State::IDLE); } +bool CC1101Component::enter_rx_() { return this->enter_calibrated_(State::RX, Command::RX); } + +bool CC1101Component::enter_tx_() { return this->enter_calibrated_(State::TX, Command::TX); } + uint8_t CC1101Component::strobe_(Command cmd) { uint8_t index = static_cast(cmd); if (cmd < Command::RES || cmd > Command::NOP) { @@ -338,18 +361,26 @@ CC1101Error CC1101Component::transmit_packet(const std::vector &packet) this->write_(Register::FIFO, static_cast(packet.size())); } this->write_(Register::FIFO, packet.data(), packet.size()); + + // Calibrate PLL + if (!this->enter_calibrated_(State::FSTXON, Command::FSTXON)) { + ESP_LOGW(TAG, "PLL lock failed during TX"); + this->enter_idle_(); + this->enter_rx_(); + return CC1101Error::PLL_LOCK; + } + + // Transmit packet this->strobe_(Command::TX); if (!this->wait_for_state_(State::IDLE, 1000)) { ESP_LOGW(TAG, "TX timeout"); this->enter_idle_(); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return CC1101Error::TIMEOUT; } // Return to rx - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return CC1101Error::NONE; } @@ -406,7 +437,7 @@ void CC1101Component::set_frequency(float value) { this->write_(Register::FREQ2); this->write_(Register::FREQ1); this->write_(Register::FREQ0); - this->strobe_(Command::RX); + this->enter_rx_(); } } @@ -433,7 +464,7 @@ void CC1101Component::set_channel(uint8_t value) { if (this->initialized_) { this->enter_idle_(); this->write_(Register::CHANNR); - this->strobe_(Command::RX); + this->enter_rx_(); } } @@ -502,7 +533,7 @@ void CC1101Component::set_modulation_type(Modulation value) { this->set_output_power(this->output_power_requested_); this->write_(Register::MDMCFG2); this->write_(Register::FREND0); - this->strobe_(Command::RX); + this->enter_rx_(); } } diff --git a/esphome/components/cc1101/cc1101.h b/esphome/components/cc1101/cc1101.h index 9b8d4e56a8b..43ae5b36127 100644 --- a/esphome/components/cc1101/cc1101.h +++ b/esphome/components/cc1101/cc1101.h @@ -9,7 +9,7 @@ namespace esphome::cc1101 { -enum class CC1101Error { NONE = 0, TIMEOUT, PARAMS, CRC_ERROR, FIFO_OVERFLOW }; +enum class CC1101Error { NONE = 0, TIMEOUT, PARAMS, CRC_ERROR, FIFO_OVERFLOW, PLL_LOCK }; class CC1101Component : public Component, public spi::SPIDevice Date: Thu, 1 Jan 2026 13:44:21 +1000 Subject: [PATCH 02/17] [lvgl] Fix arc background angles (#12773) --- esphome/components/lvgl/widgets/arc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/lvgl/widgets/arc.py b/esphome/components/lvgl/widgets/arc.py index 21530441f83..34ac9c51f75 100644 --- a/esphome/components/lvgl/widgets/arc.py +++ b/esphome/components/lvgl/widgets/arc.py @@ -85,11 +85,11 @@ class ArcType(NumberType): lv.arc_set_range(w.obj, min_value, max_value) await w.set_property( - CONF_START_ANGLE, + "bg_start_angle", await lv_angle_degrees.process(config.get(CONF_START_ANGLE)), ) await w.set_property( - CONF_END_ANGLE, await lv_angle_degrees.process(config.get(CONF_END_ANGLE)) + "bg_end_angle", await lv_angle_degrees.process(config.get(CONF_END_ANGLE)) ) await w.set_property( CONF_ROTATION, await lv_angle_degrees.process(config.get(CONF_ROTATION)) From 178a61b6fd8fc298914bc6b8dd3d3b8d1e0e6f81 Mon Sep 17 00:00:00 2001 From: Artur <130101347+aanikei@users.noreply.github.com> Date: Fri, 2 Jan 2026 04:28:10 +0000 Subject: [PATCH 03/17] [sn74hc595]: fix 'Attempted read from write-only channel' when using esp-idf framework (#12801) --- esphome/components/sn74hc595/sn74hc595.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sn74hc595/sn74hc595.cpp b/esphome/components/sn74hc595/sn74hc595.cpp index fc47a6dc5e9..a9ada432e41 100644 --- a/esphome/components/sn74hc595/sn74hc595.cpp +++ b/esphome/components/sn74hc595/sn74hc595.cpp @@ -70,7 +70,7 @@ void SN74HC595GPIOComponent::write_gpio() { void SN74HC595SPIComponent::write_gpio() { for (uint8_t &output_byte : std::ranges::reverse_view(this->output_bytes_)) { this->enable(); - this->transfer_byte(output_byte); + this->write_byte(output_byte); this->disable(); } SN74HC595Component::write_gpio(); From 8b4ba8dfe66fe73957e272c9ea23ac8e1f44a315 Mon Sep 17 00:00:00 2001 From: Conrad Juhl Andersen Date: Sat, 3 Jan 2026 23:06:33 +0100 Subject: [PATCH 04/17] [wts01] Fix negative values for WTS01 sensor (#12835) --- esphome/components/wts01/wts01.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/esphome/components/wts01/wts01.cpp b/esphome/components/wts01/wts01.cpp index cb910d89cf1..a7948c805a1 100644 --- a/esphome/components/wts01/wts01.cpp +++ b/esphome/components/wts01/wts01.cpp @@ -71,17 +71,20 @@ void WTS01Sensor::process_packet_() { } // Extract temperature value - int8_t temp = this->buffer_[6]; - int32_t sign = 1; + const uint8_t raw = this->buffer_[6]; - // Handle negative temperatures - if (temp < 0) { - sign = -1; + // WTS01 encodes sign in bit 7, magnitude in bits 0-6 + const bool negative = (raw & 0x80) != 0; + const uint8_t magnitude = raw & 0x7F; + + const float decimal = static_cast(this->buffer_[7]) / 100.0f; + + float temperature = static_cast(magnitude) + decimal; + + if (negative) { + temperature = -temperature; } - // Calculate temperature (temp + decimal/100) - float temperature = static_cast(temp) + (sign * static_cast(this->buffer_[7]) / 100.0f); - ESP_LOGV(TAG, "Received new temperature: %.2f°C", temperature); this->publish_state(temperature); From 8255c02d5d4c619e0e7ab1ec6278bbde5f9b2102 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 5 Jan 2026 08:37:44 +1000 Subject: [PATCH 05/17] [esp32_ble] Remove requirement for configured network (#12891) --- esphome/components/esp32_ble/__init__.py | 1 - esphome/components/socket/__init__.py | 7 ++- esphome/core/__init__.py | 19 ++++++ .../socket/test_wake_loop_threadsafe.py | 35 +++++++++++ tests/unit_tests/test_core.py | 62 +++++++++++++++++++ 5 files changed, 122 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index ced7e3fec9b..dcc3ce71cf2 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -22,7 +22,6 @@ from esphome.core import CORE, CoroPriority, TimePeriod, coroutine_with_priority import esphome.final_validate as fv DEPENDENCIES = ["esp32"] -AUTO_LOAD = ["socket"] CODEOWNERS = ["@jesserockz", "@Rapsssito", "@bdraco"] DOMAIN = "esp32_ble" diff --git a/esphome/components/socket/__init__.py b/esphome/components/socket/__init__.py index 49e074a6ee3..e364da78f8f 100644 --- a/esphome/components/socket/__init__.py +++ b/esphome/components/socket/__init__.py @@ -47,6 +47,8 @@ def require_wake_loop_threadsafe() -> None: This enables the shared UDP loopback socket mechanism (~208 bytes RAM). The socket is shared across all components that use this feature. + This call is a no-op if networking is not enabled in the configuration. + IMPORTANT: This is for background thread context only, NOT ISR context. Socket operations are not safe to call from ISR handlers. @@ -56,8 +58,11 @@ def require_wake_loop_threadsafe() -> None: async def to_code(config): socket.require_wake_loop_threadsafe() """ + # Only set up once (idempotent - multiple components can call this) - if not CORE.data.get(KEY_WAKE_LOOP_THREADSAFE_REQUIRED, False): + if CORE.has_networking and not CORE.data.get( + KEY_WAKE_LOOP_THREADSAFE_REQUIRED, False + ): CORE.data[KEY_WAKE_LOOP_THREADSAFE_REQUIRED] = True cg.add_define("USE_WAKE_LOOP_THREADSAFE") # Consume 1 socket for the shared wake notification socket diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 721cd5787da..0e09d97fed2 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -703,6 +703,25 @@ class EsphomeCore: def config_filename(self) -> str: return self.config_path.name + def has_at_least_one_component(self, *components: str) -> bool: + """ + Are any of the given components configured? + :param components: component names + :return: true if so + """ + if self.config is None: + raise ValueError("Config has not been loaded yet") + + return any(component in self.config for component in components) + + @property + def has_networking(self) -> bool: + """ + Is a network component configured? + :return: true if so + """ + return self.has_at_least_one_component("wifi", "ethernet", "openthread") + def relative_config_path(self, *path: str | Path) -> Path: path_ = Path(*path).expanduser() return self.config_dir / path_ diff --git a/tests/components/socket/test_wake_loop_threadsafe.py b/tests/components/socket/test_wake_loop_threadsafe.py index 45e5ea22115..b4bc95176db 100644 --- a/tests/components/socket/test_wake_loop_threadsafe.py +++ b/tests/components/socket/test_wake_loop_threadsafe.py @@ -4,6 +4,7 @@ from esphome.core import CORE def test_require_wake_loop_threadsafe__first_call() -> None: """Test that first call sets up define and consumes socket.""" + CORE.config = {"wifi": True} socket.require_wake_loop_threadsafe() # Verify CORE.data was updated @@ -17,6 +18,7 @@ def test_require_wake_loop_threadsafe__idempotent() -> None: """Test that subsequent calls are idempotent.""" # Set up initial state as if already called CORE.data[socket.KEY_WAKE_LOOP_THREADSAFE_REQUIRED] = True + CORE.config = {"ethernet": True} # Call again - should not raise or fail socket.require_wake_loop_threadsafe() @@ -31,6 +33,7 @@ def test_require_wake_loop_threadsafe__idempotent() -> None: def test_require_wake_loop_threadsafe__multiple_calls() -> None: """Test that multiple calls only set up once.""" # Call three times + CORE.config = {"openthread": True} socket.require_wake_loop_threadsafe() socket.require_wake_loop_threadsafe() socket.require_wake_loop_threadsafe() @@ -40,3 +43,35 @@ def test_require_wake_loop_threadsafe__multiple_calls() -> None: # Verify the define was added (only once, but we can just check it exists) assert any(d.name == "USE_WAKE_LOOP_THREADSAFE" for d in CORE.defines) + + +def test_require_wake_loop_threadsafe__no_networking() -> None: + """Test that wake loop is NOT configured when no networking is configured.""" + # Set up config without any networking components + CORE.config = {"esphome": {"name": "test"}, "logger": {}} + + # Call require_wake_loop_threadsafe + socket.require_wake_loop_threadsafe() + + # Verify CORE.data flag was NOT set (since has_networking returns False) + assert socket.KEY_WAKE_LOOP_THREADSAFE_REQUIRED not in CORE.data + + # Verify the define was NOT added + assert not any(d.name == "USE_WAKE_LOOP_THREADSAFE" for d in CORE.defines) + + +def test_require_wake_loop_threadsafe__no_networking_does_not_consume_socket() -> None: + """Test that no socket is consumed when no networking is configured.""" + # Set up config without any networking components + CORE.config = {"logger": {}} + + # Track initial socket consumer state + initial_consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS, {}) + + # Call require_wake_loop_threadsafe + socket.require_wake_loop_threadsafe() + + # Verify no socket was consumed + consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS, {}) + assert "socket.wake_loop_threadsafe" not in consumers + assert consumers == initial_consumers diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index e52cb248319..1fc8dab358b 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -718,3 +718,65 @@ class TestEsphomeCore: # Even though "web_server" is in loaded_integrations due to the platform, # web_port must return None because the full web_server component is not configured assert target.web_port is None + + def test_has_at_least_one_component__none_configured(self, target): + """Test has_at_least_one_component returns False when none of the components are configured.""" + target.config = {const.CONF_ESPHOME: {"name": "test"}, "logger": {}} + + assert target.has_at_least_one_component("wifi", "ethernet") is False + + def test_has_at_least_one_component__one_configured(self, target): + """Test has_at_least_one_component returns True when one component is configured.""" + target.config = {const.CONF_WIFI: {}, "logger": {}} + + assert target.has_at_least_one_component("wifi", "ethernet") is True + + def test_has_at_least_one_component__multiple_configured(self, target): + """Test has_at_least_one_component returns True when multiple components are configured.""" + target.config = { + const.CONF_WIFI: {}, + const.CONF_ETHERNET: {}, + "logger": {}, + } + + assert ( + target.has_at_least_one_component("wifi", "ethernet", "bluetooth") is True + ) + + def test_has_at_least_one_component__single_component(self, target): + """Test has_at_least_one_component works with a single component.""" + target.config = {const.CONF_MQTT: {}} + + assert target.has_at_least_one_component("mqtt") is True + assert target.has_at_least_one_component("wifi") is False + + def test_has_at_least_one_component__config_not_loaded(self, target): + """Test has_at_least_one_component raises ValueError when config is not loaded.""" + target.config = None + + with pytest.raises(ValueError, match="Config has not been loaded yet"): + target.has_at_least_one_component("wifi") + + def test_has_networking__with_wifi(self, target): + """Test has_networking returns True when wifi is configured.""" + target.config = {const.CONF_WIFI: {}} + + assert target.has_networking is True + + def test_has_networking__with_ethernet(self, target): + """Test has_networking returns True when ethernet is configured.""" + target.config = {const.CONF_ETHERNET: {}} + + assert target.has_networking is True + + def test_has_networking__with_openthread(self, target): + """Test has_networking returns True when openthread is configured.""" + target.config = {const.CONF_OPENTHREAD: {}} + + assert target.has_networking is True + + def test_has_networking__without_networking(self, target): + """Test has_networking returns False when no networking component is configured.""" + target.config = {const.CONF_ESPHOME: {"name": "test"}, "logger": {}} + + assert target.has_networking is False From 47d0d3cfeb1c447c133950ceb90ef6834e9c1a27 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 6 Jan 2026 09:34:28 -0500 Subject: [PATCH 06/17] [cc1101] Add PLL lock verification and retry support (#13006) --- esphome/components/cc1101/cc1101.cpp | 67 +++++++++++++++++++------- esphome/components/cc1101/cc1101.h | 5 +- esphome/components/cc1101/cc1101defs.h | 3 ++ 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/esphome/components/cc1101/cc1101.cpp b/esphome/components/cc1101/cc1101.cpp index 7e5309e1659..fe9238b1411 100644 --- a/esphome/components/cc1101/cc1101.cpp +++ b/esphome/components/cc1101/cc1101.cpp @@ -140,7 +140,10 @@ void CC1101Component::setup() { this->write_(static_cast(i)); } this->set_output_power(this->output_power_requested_); - this->strobe_(Command::RX); + if (!this->enter_rx_()) { + this->mark_failed(); + return; + } // Defer pin mode setup until after all components have completed setup() // This handles the case where remote_transmitter runs after CC1101 and changes pin mode @@ -163,8 +166,7 @@ void CC1101Component::loop() { ESP_LOGW(TAG, "RX FIFO overflow, flushing"); this->enter_idle_(); this->strobe_(Command::FRX); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return; } @@ -181,8 +183,7 @@ void CC1101Component::loop() { ESP_LOGW(TAG, "Invalid packet: rx_bytes %u, payload_length %u", rx_bytes, payload_length); this->enter_idle_(); this->strobe_(Command::FRX); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return; } this->packet_.resize(payload_length); @@ -201,8 +202,7 @@ void CC1101Component::loop() { // Return to rx this->enter_idle_(); this->strobe_(Command::FRX); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); } void CC1101Component::dump_config() { @@ -233,9 +233,8 @@ void CC1101Component::begin_tx() { if (this->gdo0_pin_ != nullptr) { this->gdo0_pin_->pin_mode(gpio::FLAG_OUTPUT); } - this->strobe_(Command::TX); - if (!this->wait_for_state_(State::TX, 50)) { - ESP_LOGW(TAG, "Timed out waiting for TX state!"); + if (!this->enter_tx_()) { + ESP_LOGW(TAG, "Failed to enter TX state!"); } } @@ -244,7 +243,9 @@ void CC1101Component::begin_rx() { if (this->gdo0_pin_ != nullptr) { this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT); } - this->strobe_(Command::RX); + if (!this->enter_rx_()) { + ESP_LOGW(TAG, "Failed to enter RX state!"); + } } void CC1101Component::reset() { @@ -270,11 +271,33 @@ bool CC1101Component::wait_for_state_(State target_state, uint32_t timeout_ms) { return false; } +bool CC1101Component::enter_calibrated_(State target_state, Command cmd) { + // The PLL must be recalibrated until PLL lock is achieved + for (uint8_t retries = PLL_LOCK_RETRIES; retries > 0; retries--) { + this->strobe_(cmd); + if (!this->wait_for_state_(target_state)) { + return false; + } + this->read_(Register::FSCAL1); + if (this->state_.FSCAL1 != FSCAL1_PLL_NOT_LOCKED) { + return true; + } + ESP_LOGW(TAG, "PLL lock failed, retrying calibration"); + this->enter_idle_(); + } + ESP_LOGE(TAG, "PLL lock failed after retries"); + return false; +} + void CC1101Component::enter_idle_() { this->strobe_(Command::IDLE); this->wait_for_state_(State::IDLE); } +bool CC1101Component::enter_rx_() { return this->enter_calibrated_(State::RX, Command::RX); } + +bool CC1101Component::enter_tx_() { return this->enter_calibrated_(State::TX, Command::TX); } + uint8_t CC1101Component::strobe_(Command cmd) { uint8_t index = static_cast(cmd); if (cmd < Command::RES || cmd > Command::NOP) { @@ -336,18 +359,26 @@ CC1101Error CC1101Component::transmit_packet(const std::vector &packet) this->write_(Register::FIFO, static_cast(packet.size())); } this->write_(Register::FIFO, packet.data(), packet.size()); + + // Calibrate PLL + if (!this->enter_calibrated_(State::FSTXON, Command::FSTXON)) { + ESP_LOGW(TAG, "PLL lock failed during TX"); + this->enter_idle_(); + this->enter_rx_(); + return CC1101Error::PLL_LOCK; + } + + // Transmit packet this->strobe_(Command::TX); if (!this->wait_for_state_(State::IDLE, 1000)) { ESP_LOGW(TAG, "TX timeout"); this->enter_idle_(); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return CC1101Error::TIMEOUT; } // Return to rx - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return CC1101Error::NONE; } @@ -404,7 +435,7 @@ void CC1101Component::set_frequency(float value) { this->write_(Register::FREQ2); this->write_(Register::FREQ1); this->write_(Register::FREQ0); - this->strobe_(Command::RX); + this->enter_rx_(); } } @@ -431,7 +462,7 @@ void CC1101Component::set_channel(uint8_t value) { if (this->initialized_) { this->enter_idle_(); this->write_(Register::CHANNR); - this->strobe_(Command::RX); + this->enter_rx_(); } } @@ -500,7 +531,7 @@ void CC1101Component::set_modulation_type(Modulation value) { this->set_output_power(this->output_power_requested_); this->write_(Register::MDMCFG2); this->write_(Register::FREND0); - this->strobe_(Command::RX); + this->enter_rx_(); } } diff --git a/esphome/components/cc1101/cc1101.h b/esphome/components/cc1101/cc1101.h index b896f7e9742..fe4898660e7 100644 --- a/esphome/components/cc1101/cc1101.h +++ b/esphome/components/cc1101/cc1101.h @@ -9,7 +9,7 @@ namespace esphome::cc1101 { -enum class CC1101Error { NONE = 0, TIMEOUT, PARAMS, CRC_ERROR, FIFO_OVERFLOW }; +enum class CC1101Error { NONE = 0, TIMEOUT, PARAMS, CRC_ERROR, FIFO_OVERFLOW, PLL_LOCK }; class CC1101Component : public Component, public spi::SPIDevice Date: Tue, 6 Jan 2026 09:35:38 -0500 Subject: [PATCH 07/17] Bump version to 2025.12.5 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index ff747576392..079606c5012 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.12.4 +PROJECT_NUMBER = 2025.12.5 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index 3fbdb69215e..d1a7104ca4f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.12.4" +__version__ = "2025.12.5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From a8a26f4ea8428ec86bbe5b26b7f1b55925120b88 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 07:32:43 -1000 Subject: [PATCH 08/17] [opentherm][nau7802] Use direct format specifiers instead of to_string().c_str() (#13019) --- esphome/components/nau7802/nau7802.cpp | 6 +++--- esphome/components/opentherm/opentherm.cpp | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/esphome/components/nau7802/nau7802.cpp b/esphome/components/nau7802/nau7802.cpp index 11f63a9a336..5edbc798625 100644 --- a/esphome/components/nau7802/nau7802.cpp +++ b/esphome/components/nau7802/nau7802.cpp @@ -131,9 +131,9 @@ void NAU7802Sensor::dump_config() { } // Note these may differ from the values on the device if calbration has been run ESP_LOGCONFIG(TAG, - " Offset Calibration: %s\n" + " Offset Calibration: %" PRId32 "\n" " Gain Calibration: %f", - to_string(this->offset_calibration_).c_str(), this->gain_calibration_); + this->offset_calibration_, this->gain_calibration_); std::string voltage = "unknown"; switch (this->ldo_) { @@ -289,7 +289,7 @@ void NAU7802Sensor::loop() { this->status_clear_error(); int32_t ocal = this->read_value_(OCAL1_B2_REG, 3); - ESP_LOGI(TAG, "New Offset: %s", to_string(ocal).c_str()); + ESP_LOGI(TAG, "New Offset: %" PRId32, ocal); uint32_t gcal = this->read_value_(GCAL1_B3_REG, 4); float gcal_f = ((float) gcal / (float) (1 << GCAL1_FRACTIONAL)); ESP_LOGI(TAG, "New Gain: %f", gcal_f); diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index 750ef08b337..c6443f12821 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -21,7 +21,6 @@ namespace esphome { namespace opentherm { using std::string; -using std::to_string; static const char *const TAG = "opentherm"; @@ -564,10 +563,9 @@ const char *OpenTherm::message_id_to_str(MessageId id) { void OpenTherm::debug_data(OpenthermData &data) { ESP_LOGD(TAG, "%s %s %s %s", format_bin(data.type).c_str(), format_bin(data.id).c_str(), format_bin(data.valueHB).c_str(), format_bin(data.valueLB).c_str()); - ESP_LOGD(TAG, "type: %s; id: %s; HB: %s; LB: %s; uint_16: %s; float: %s", - this->message_type_to_str((MessageType) data.type), to_string(data.id).c_str(), - to_string(data.valueHB).c_str(), to_string(data.valueLB).c_str(), to_string(data.u16()).c_str(), - to_string(data.f88()).c_str()); + ESP_LOGD(TAG, "type: %s; id: %u; HB: %u; LB: %u; uint_16: %u; float: %f", + this->message_type_to_str((MessageType) data.type), data.id, data.valueHB, data.valueLB, data.u16(), + data.f88()); } void OpenTherm::debug_error(OpenThermError &error) const { ESP_LOGD(TAG, "data: 0x%08" PRIx32 "; clock: %u; capture: 0x%08" PRIx32 "; bit_pos: %u", error.data, this->clock_, From 1e56325b33a9301ceed9d6e607364d6715367762 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 07:33:32 -1000 Subject: [PATCH 09/17] [improv_base] Optimize next_url to avoid STL string operations (#13015) --- .../esp32_improv/esp32_improv_component.cpp | 9 ++- .../components/improv_base/improv_base.cpp | 56 ++++++++++++------- esphome/components/improv_base/improv_base.h | 9 +-- .../improv_serial/improv_serial_component.cpp | 8 ++- 4 files changed, 54 insertions(+), 28 deletions(-) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 4a6aec18924..1a19472c874 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -398,9 +398,12 @@ void ESP32ImprovComponent::check_wifi_connection_() { #ifdef USE_ESP32_IMPROV_NEXT_URL // Add next_url if configured (should be first per Improv BLE spec) - std::string next_url = this->get_formatted_next_url_(); - if (!next_url.empty()) { - url_strings[url_count++] = std::move(next_url); + { + char url_buffer[384]; + size_t len = this->get_formatted_next_url_(url_buffer, sizeof(url_buffer)); + if (len > 0) { + url_strings[url_count++] = std::string(url_buffer, len); + } } #endif diff --git a/esphome/components/improv_base/improv_base.cpp b/esphome/components/improv_base/improv_base.cpp index 2091390f952..d0340344a6c 100644 --- a/esphome/components/improv_base/improv_base.cpp +++ b/esphome/components/improv_base/improv_base.cpp @@ -1,5 +1,6 @@ #include "improv_base.h" +#include #include "esphome/components/network/util.h" #include "esphome/core/application.h" #include "esphome/core/defines.h" @@ -13,37 +14,54 @@ static constexpr size_t DEVICE_NAME_PLACEHOLDER_LEN = sizeof(DEVICE_NAME_PLACEHO static constexpr const char IP_ADDRESS_PLACEHOLDER[] = "{{ip_address}}"; static constexpr size_t IP_ADDRESS_PLACEHOLDER_LEN = sizeof(IP_ADDRESS_PLACEHOLDER) - 1; -static void replace_all_in_place(std::string &str, const char *placeholder, size_t placeholder_len, - const std::string &replacement) { - size_t pos = 0; - const size_t replacement_len = replacement.length(); - while ((pos = str.find(placeholder, pos)) != std::string::npos) { - str.replace(pos, placeholder_len, replacement); - pos += replacement_len; +/// Copy src to dest, returning pointer past last written char. Stops at end or if src is null. +static char *copy_to_buffer(char *dest, const char *end, const char *src) { + if (src == nullptr) { + return dest; } + while (*src != '\0' && dest < end) { + *dest++ = *src++; + } + return dest; } -std::string ImprovBase::get_formatted_next_url_() { - if (this->next_url_.empty()) { - return ""; +size_t ImprovBase::get_formatted_next_url_(char *buffer, size_t buffer_size) { + if (this->next_url_ == nullptr || buffer_size == 0) { + if (buffer_size > 0) { + buffer[0] = '\0'; + } + return 0; } - std::string formatted_url = this->next_url_; - - // Replace all occurrences of {{device_name}} - replace_all_in_place(formatted_url, DEVICE_NAME_PLACEHOLDER, DEVICE_NAME_PLACEHOLDER_LEN, App.get_name()); - - // Replace all occurrences of {{ip_address}} + // Get IP address once for replacement + const char *ip_str = nullptr; + char ip_buffer[network::IP_ADDRESS_BUFFER_SIZE]; for (auto &ip : network::get_ip_addresses()) { if (ip.is_ip4()) { - replace_all_in_place(formatted_url, IP_ADDRESS_PLACEHOLDER, IP_ADDRESS_PLACEHOLDER_LEN, ip.str()); + ip.str_to(ip_buffer); + ip_str = ip_buffer; break; } } - // Note: {{esphome_version}} is replaced at code generation time in Python + const char *device_name = App.get_name().c_str(); + char *out = buffer; + const char *end = buffer + buffer_size - 1; - return formatted_url; + // Note: {{esphome_version}} is replaced at code generation time in Python + for (const char *p = this->next_url_; *p != '\0' && out < end;) { + if (strncmp(p, DEVICE_NAME_PLACEHOLDER, DEVICE_NAME_PLACEHOLDER_LEN) == 0) { + out = copy_to_buffer(out, end, device_name); + p += DEVICE_NAME_PLACEHOLDER_LEN; + } else if (ip_str != nullptr && strncmp(p, IP_ADDRESS_PLACEHOLDER, IP_ADDRESS_PLACEHOLDER_LEN) == 0) { + out = copy_to_buffer(out, end, ip_str); + p += IP_ADDRESS_PLACEHOLDER_LEN; + } else { + *out++ = *p++; + } + } + *out = '\0'; + return out - buffer; } #endif diff --git a/esphome/components/improv_base/improv_base.h b/esphome/components/improv_base/improv_base.h index e4138479df0..ebc8f38d60b 100644 --- a/esphome/components/improv_base/improv_base.h +++ b/esphome/components/improv_base/improv_base.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "esphome/core/defines.h" namespace esphome { @@ -9,13 +9,14 @@ namespace improv_base { class ImprovBase { public: #if defined(USE_ESP32_IMPROV_NEXT_URL) || defined(USE_IMPROV_SERIAL_NEXT_URL) - void set_next_url(const std::string &next_url) { this->next_url_ = next_url; } + void set_next_url(const char *next_url) { this->next_url_ = next_url; } #endif protected: #if defined(USE_ESP32_IMPROV_NEXT_URL) || defined(USE_IMPROV_SERIAL_NEXT_URL) - std::string get_formatted_next_url_(); - std::string next_url_; + /// Format next_url_ into buffer, replacing placeholders. Returns length written. + size_t get_formatted_next_url_(char *buffer, size_t buffer_size); + const char *next_url_{nullptr}; #endif }; diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 281e95d12bd..936ff414b14 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -182,8 +182,12 @@ void ImprovSerialComponent::write_data_(const uint8_t *data, const size_t size) std::vector ImprovSerialComponent::build_rpc_settings_response_(improv::Command command) { std::vector urls; #ifdef USE_IMPROV_SERIAL_NEXT_URL - if (!this->next_url_.empty()) { - urls.push_back(this->get_formatted_next_url_()); + { + char url_buffer[384]; + size_t len = this->get_formatted_next_url_(url_buffer, sizeof(url_buffer)); + if (len > 0) { + urls.emplace_back(url_buffer, len); + } } #endif #ifdef USE_WEBSERVER From e0981323bd72dfee9043926d9badb3e9bb026355 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 07:33:56 -1000 Subject: [PATCH 10/17] [mqtt] Move Home Assistant discovery keys to PROGMEM on ESP8266 (#13011) --- .../components/mqtt/custom_mqtt_device.cpp | 6 +- esphome/components/mqtt/custom_mqtt_device.h | 6 +- .../mqtt/mqtt_alarm_control_panel.cpp | 6 +- .../mqtt/mqtt_alarm_control_panel.h | 6 +- esphome/components/mqtt/mqtt_backend.h | 6 +- .../components/mqtt/mqtt_backend_esp32.cpp | 6 +- esphome/components/mqtt/mqtt_backend_esp32.h | 6 +- .../components/mqtt/mqtt_backend_esp8266.h | 6 +- .../components/mqtt/mqtt_backend_libretiny.h | 6 +- .../components/mqtt/mqtt_binary_sensor.cpp | 6 +- esphome/components/mqtt/mqtt_binary_sensor.h | 6 +- esphome/components/mqtt/mqtt_button.cpp | 6 +- esphome/components/mqtt/mqtt_button.h | 6 +- esphome/components/mqtt/mqtt_client.cpp | 6 +- esphome/components/mqtt/mqtt_client.h | 6 +- esphome/components/mqtt/mqtt_climate.cpp | 6 +- esphome/components/mqtt/mqtt_climate.h | 6 +- esphome/components/mqtt/mqtt_component.cpp | 6 +- esphome/components/mqtt/mqtt_component.h | 6 +- esphome/components/mqtt/mqtt_const.h | 851 +++++++----------- esphome/components/mqtt/mqtt_cover.cpp | 6 +- esphome/components/mqtt/mqtt_cover.h | 6 +- esphome/components/mqtt/mqtt_date.cpp | 6 +- esphome/components/mqtt/mqtt_date.h | 6 +- esphome/components/mqtt/mqtt_datetime.cpp | 6 +- esphome/components/mqtt/mqtt_datetime.h | 6 +- esphome/components/mqtt/mqtt_event.cpp | 6 +- esphome/components/mqtt/mqtt_event.h | 6 +- esphome/components/mqtt/mqtt_fan.cpp | 6 +- esphome/components/mqtt/mqtt_fan.h | 6 +- esphome/components/mqtt/mqtt_light.cpp | 6 +- esphome/components/mqtt/mqtt_light.h | 6 +- esphome/components/mqtt/mqtt_lock.cpp | 6 +- esphome/components/mqtt/mqtt_lock.h | 6 +- esphome/components/mqtt/mqtt_number.cpp | 6 +- esphome/components/mqtt/mqtt_number.h | 6 +- esphome/components/mqtt/mqtt_select.cpp | 6 +- esphome/components/mqtt/mqtt_select.h | 6 +- esphome/components/mqtt/mqtt_sensor.cpp | 6 +- esphome/components/mqtt/mqtt_sensor.h | 6 +- esphome/components/mqtt/mqtt_switch.cpp | 6 +- esphome/components/mqtt/mqtt_switch.h | 6 +- esphome/components/mqtt/mqtt_text.cpp | 6 +- esphome/components/mqtt/mqtt_text.h | 6 +- esphome/components/mqtt/mqtt_text_sensor.cpp | 6 +- esphome/components/mqtt/mqtt_text_sensor.h | 6 +- esphome/components/mqtt/mqtt_time.cpp | 6 +- esphome/components/mqtt/mqtt_time.h | 6 +- esphome/components/mqtt/mqtt_update.cpp | 6 +- esphome/components/mqtt/mqtt_update.h | 6 +- esphome/components/mqtt/mqtt_valve.cpp | 6 +- esphome/components/mqtt/mqtt_valve.h | 6 +- 52 files changed, 420 insertions(+), 737 deletions(-) diff --git a/esphome/components/mqtt/custom_mqtt_device.cpp b/esphome/components/mqtt/custom_mqtt_device.cpp index 787cc1153f6..25a8a820663 100644 --- a/esphome/components/mqtt/custom_mqtt_device.cpp +++ b/esphome/components/mqtt/custom_mqtt_device.cpp @@ -4,8 +4,7 @@ #include "esphome/core/log.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.custom"; @@ -29,7 +28,6 @@ bool CustomMQTTDevice::publish_json(const std::string &topic, const json::json_b } bool CustomMQTTDevice::is_connected() { return global_mqtt_client != nullptr && global_mqtt_client->is_connected(); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_MQTT diff --git a/esphome/components/mqtt/custom_mqtt_device.h b/esphome/components/mqtt/custom_mqtt_device.h index 0852a17cf15..09ed7bd6d17 100644 --- a/esphome/components/mqtt/custom_mqtt_device.h +++ b/esphome/components/mqtt/custom_mqtt_device.h @@ -6,8 +6,7 @@ #include "esphome/core/component.h" #include "mqtt_client.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { /** This class is a helper class for custom components that communicate using * MQTT. It has 5 helper functions that you can use (square brackets indicate optional): @@ -214,7 +213,6 @@ void CustomMQTTDevice::subscribe_json(const std::string &topic, void (T::*callba global_mqtt_client->subscribe_json(topic, f, qos); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp index dd3df5f8aa6..8c570d1472a 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_ALARM_CONTROL_PANEL -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.alarm_control_panel"; @@ -123,8 +122,7 @@ bool MQTTAlarmControlPanelComponent::publish_state() { return this->publish(this->get_state_topic_(), state_s); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.h b/esphome/components/mqtt/mqtt_alarm_control_panel.h index 4ad37b73146..cf4fac15114 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.h +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.h @@ -8,8 +8,7 @@ #include "mqtt_component.h" #include "esphome/components/alarm_control_panel/alarm_control_panel.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTAlarmControlPanelComponent : public mqtt::MQTTComponent { public: @@ -32,8 +31,7 @@ class MQTTAlarmControlPanelComponent : public mqtt::MQTTComponent { alarm_control_panel::AlarmControlPanel *alarm_control_panel_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_backend.h b/esphome/components/mqtt/mqtt_backend.h index 0c1720ec349..a7e3f1013d8 100644 --- a/esphome/components/mqtt/mqtt_backend.h +++ b/esphome/components/mqtt/mqtt_backend.h @@ -6,8 +6,7 @@ #include "esphome/components/network/ip_address.h" #include "esphome/core/helpers.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { enum class MQTTClientDisconnectReason : int8_t { TCP_DISCONNECTED = 0, @@ -67,6 +66,5 @@ class MQTTBackend { virtual void loop() {} }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif diff --git a/esphome/components/mqtt/mqtt_backend_esp32.cpp b/esphome/components/mqtt/mqtt_backend_esp32.cpp index 3838d6df26f..c12c79499fc 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.cpp +++ b/esphome/components/mqtt/mqtt_backend_esp32.cpp @@ -8,8 +8,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.idf"; @@ -270,7 +269,6 @@ bool MQTTBackendESP32::enqueue_(MqttQueueTypeT type, const char *topic, int qos, } #endif // USE_MQTT_IDF_ENQUEUE -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_ESP32 #endif diff --git a/esphome/components/mqtt/mqtt_backend_esp32.h b/esphome/components/mqtt/mqtt_backend_esp32.h index a24e75eaf98..bd2d2a67b25 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.h +++ b/esphome/components/mqtt/mqtt_backend_esp32.h @@ -15,8 +15,7 @@ #include "esphome/core/lock_free_queue.h" #include "esphome/core/event_pool.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { struct Event { esp_mqtt_event_id_t event_id; @@ -273,8 +272,7 @@ class MQTTBackendESP32 final : public MQTTBackend { #endif }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif diff --git a/esphome/components/mqtt/mqtt_backend_esp8266.h b/esphome/components/mqtt/mqtt_backend_esp8266.h index a979634bf47..470d1e6a8be 100644 --- a/esphome/components/mqtt/mqtt_backend_esp8266.h +++ b/esphome/components/mqtt/mqtt_backend_esp8266.h @@ -6,8 +6,7 @@ #include -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTBackendESP8266 final : public MQTTBackend { public: @@ -67,8 +66,7 @@ class MQTTBackendESP8266 final : public MQTTBackend { AsyncMqttClient mqtt_client_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // defined(USE_ESP8266) #endif diff --git a/esphome/components/mqtt/mqtt_backend_libretiny.h b/esphome/components/mqtt/mqtt_backend_libretiny.h index 2578ae99415..24bf018a909 100644 --- a/esphome/components/mqtt/mqtt_backend_libretiny.h +++ b/esphome/components/mqtt/mqtt_backend_libretiny.h @@ -6,8 +6,7 @@ #include -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTBackendLibreTiny final : public MQTTBackend { public: @@ -67,8 +66,7 @@ class MQTTBackendLibreTiny final : public MQTTBackend { AsyncMqttClient mqtt_client_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // defined(USE_LIBRETINY) #endif diff --git a/esphome/components/mqtt/mqtt_binary_sensor.cpp b/esphome/components/mqtt/mqtt_binary_sensor.cpp index 479cee205ad..146ca46f680 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.cpp +++ b/esphome/components/mqtt/mqtt_binary_sensor.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_BINARY_SENSOR -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.binary_sensor"; @@ -57,8 +56,7 @@ bool MQTTBinarySensorComponent::publish_state(bool state) { return this->publish(this->get_state_topic_(), state_s); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_binary_sensor.h b/esphome/components/mqtt/mqtt_binary_sensor.h index f6579fcd19a..82176ec97ba 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.h +++ b/esphome/components/mqtt/mqtt_binary_sensor.h @@ -7,8 +7,7 @@ #include "mqtt_component.h" #include "esphome/components/binary_sensor/binary_sensor.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTBinarySensorComponent : public mqtt::MQTTComponent { public: @@ -36,8 +35,7 @@ class MQTTBinarySensorComponent : public mqtt::MQTTComponent { binary_sensor::BinarySensor *binary_sensor_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_button.cpp b/esphome/components/mqtt/mqtt_button.cpp index f8eb0eab2d7..2b700a49620 100644 --- a/esphome/components/mqtt/mqtt_button.cpp +++ b/esphome/components/mqtt/mqtt_button.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_BUTTON -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.button"; @@ -43,8 +42,7 @@ void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon std::string MQTTButtonComponent::component_type() const { return "button"; } const EntityBase *MQTTButtonComponent::get_entity() const { return this->button_; } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_button.h b/esphome/components/mqtt/mqtt_button.h index 42389caecc0..ec802664df2 100644 --- a/esphome/components/mqtt/mqtt_button.h +++ b/esphome/components/mqtt/mqtt_button.h @@ -8,8 +8,7 @@ #include "esphome/components/button/button.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTButtonComponent : public mqtt::MQTTComponent { public: @@ -33,8 +32,7 @@ class MQTTButtonComponent : public mqtt::MQTTComponent { button::Button *button_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index d26548acfc4..aecf809c8b3 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -22,8 +22,7 @@ #include "esphome/components/dashboard_import/dashboard_import.h" #endif -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt"; @@ -751,7 +750,6 @@ void MQTTMessageTrigger::dump_config() { } float MQTTMessageTrigger::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 8547fe337f0..4189e7ae77a 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -24,8 +24,7 @@ #include -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { /** Callback for MQTT events. */ @@ -462,7 +461,6 @@ template class MQTTDisableAction : public Action { MQTTClientComponent *parent_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index aee2b389421..9d9ca012a8a 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_CLIMATE -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.climate"; @@ -460,8 +459,7 @@ bool MQTTClimateComponent::publish_state_() { return success; } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_climate.h b/esphome/components/mqtt/mqtt_climate.h index 4e54230e68f..f561627ac9f 100644 --- a/esphome/components/mqtt/mqtt_climate.h +++ b/esphome/components/mqtt/mqtt_climate.h @@ -8,8 +8,7 @@ #include "esphome/components/climate/climate.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTClimateComponent : public mqtt::MQTTComponent { public: @@ -49,8 +48,7 @@ class MQTTClimateComponent : public mqtt::MQTTComponent { climate::Climate *device_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 9db1b1f7c89..ccbdb2ea915 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -9,8 +9,7 @@ #include "mqtt_const.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.component"; @@ -306,7 +305,6 @@ bool MQTTComponent::is_internal() { return this->get_entity()->is_internal(); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 2f8dfcf64ed..e5f9664f77a 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -11,8 +11,7 @@ #include "esphome/core/string_ref.h" #include "mqtt_client.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { /// Simple Helper struct used for Home Assistant MQTT send_discovery(). struct SendDiscoveryConfig { @@ -205,7 +204,6 @@ class MQTTComponent : public Component { bool resend_state_{false}; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_MQTt diff --git a/esphome/components/mqtt/mqtt_const.h b/esphome/components/mqtt/mqtt_const.h index 3ddd8fc5ccb..221af00371e 100644 --- a/esphome/components/mqtt/mqtt_const.h +++ b/esphome/components/mqtt/mqtt_const.h @@ -1,547 +1,332 @@ #pragma once #include "esphome/core/defines.h" +#include "esphome/core/progmem.h" #ifdef USE_MQTT -namespace esphome { -namespace mqtt { +// MQTT JSON Key Constants for Home Assistant Discovery +// +// This file defines string constants used as JSON keys in MQTT discovery payloads +// for Home Assistant integration. These are used exclusively with ArduinoJson as: +// root[MQTT_DEVICE_CLASS] = "temperature"; +// +// Implementation: +// - ESP8266: Stores strings in PROGMEM (flash) using __FlashStringHelper* pointers. +// ArduinoJson recognizes this type and reads from flash memory. +// - Other platforms: Uses constexpr const char* for compile-time optimization. +// - USE_MQTT_ABBREVIATIONS: When defined, uses shortened key names to reduce message size. +// +// Adding new keys: +// Add a single line to MQTT_KEYS_LIST: X(MQTT_NEW_KEY, "abbr", "full_name") +// The X-macro will generate the appropriate constants for each platform. +// +// Note: Other MQTT_* constants (e.g., MQTT_CLIENT_CONNECTED, MQTT_LEGACY_UNIQUE_ID_GENERATOR) +// are C++ enums defined in mqtt_client.h and mqtt_backend*.h - unrelated to these JSON keys. +// X-macro list: MQTT_KEYS_LIST(X) calls X(name, abbr, full) for each key +// clang-format off +#define MQTT_KEYS_LIST(X) \ + X(MQTT_ACTION_TEMPLATE, "act_tpl", "action_template") \ + X(MQTT_ACTION_TOPIC, "act_t", "action_topic") \ + X(MQTT_AUTOMATION_TYPE, "atype", "automation_type") \ + X(MQTT_AUX_COMMAND_TOPIC, "aux_cmd_t", "aux_command_topic") \ + X(MQTT_AUX_STATE_TEMPLATE, "aux_stat_tpl", "aux_state_template") \ + X(MQTT_AUX_STATE_TOPIC, "aux_stat_t", "aux_state_topic") \ + X(MQTT_AVAILABILITY, "avty", "availability") \ + X(MQTT_AVAILABILITY_MODE, "avty_mode", "availability_mode") \ + X(MQTT_AVAILABILITY_TOPIC, "avty_t", "availability_topic") \ + X(MQTT_AWAY_MODE_COMMAND_TOPIC, "away_mode_cmd_t", "away_mode_command_topic") \ + X(MQTT_AWAY_MODE_STATE_TEMPLATE, "away_mode_stat_tpl", "away_mode_state_template") \ + X(MQTT_AWAY_MODE_STATE_TOPIC, "away_mode_stat_t", "away_mode_state_topic") \ + X(MQTT_BATTERY_LEVEL_TEMPLATE, "bat_lev_tpl", "battery_level_template") \ + X(MQTT_BATTERY_LEVEL_TOPIC, "bat_lev_t", "battery_level_topic") \ + X(MQTT_BLUE_TEMPLATE, "b_tpl", "blue_template") \ + X(MQTT_BRIGHTNESS_COMMAND_TOPIC, "bri_cmd_t", "brightness_command_topic") \ + X(MQTT_BRIGHTNESS_SCALE, "bri_scl", "brightness_scale") \ + X(MQTT_BRIGHTNESS_STATE_TOPIC, "bri_stat_t", "brightness_state_topic") \ + X(MQTT_BRIGHTNESS_TEMPLATE, "bri_tpl", "brightness_template") \ + X(MQTT_BRIGHTNESS_VALUE_TEMPLATE, "bri_val_tpl", "brightness_value_template") \ + X(MQTT_CHARGING_TEMPLATE, "chrg_tpl", "charging_template") \ + X(MQTT_CHARGING_TOPIC, "chrg_t", "charging_topic") \ + X(MQTT_CLEANING_TEMPLATE, "cln_tpl", "cleaning_template") \ + X(MQTT_CLEANING_TOPIC, "cln_t", "cleaning_topic") \ + X(MQTT_CODE_ARM_REQUIRED, "cod_arm_req", "code_arm_required") \ + X(MQTT_CODE_DISARM_REQUIRED, "cod_dis_req", "code_disarm_required") \ + X(MQTT_COLOR_MODE, "clrm", "color_mode") \ + X(MQTT_COLOR_MODE_STATE_TOPIC, "clrm_stat_t", "color_mode_state_topic") \ + X(MQTT_COLOR_MODE_VALUE_TEMPLATE, "clrm_val_tpl", "color_mode_value_template") \ + X(MQTT_COLOR_TEMP_COMMAND_TEMPLATE, "clr_temp_cmd_tpl", "color_temp_command_template") \ + X(MQTT_COLOR_TEMP_COMMAND_TOPIC, "clr_temp_cmd_t", "color_temp_command_topic") \ + X(MQTT_COLOR_TEMP_STATE_TOPIC, "clr_temp_stat_t", "color_temp_state_topic") \ + X(MQTT_COLOR_TEMP_TEMPLATE, "clr_temp_tpl", "color_temp_template") \ + X(MQTT_COLOR_TEMP_VALUE_TEMPLATE, "clr_temp_val_tpl", "color_temp_value_template") \ + X(MQTT_COMMAND_OFF_TEMPLATE, "cmd_off_tpl", "command_off_template") \ + X(MQTT_COMMAND_ON_TEMPLATE, "cmd_on_tpl", "command_on_template") \ + X(MQTT_COMMAND_RETAIN, "ret", "retain") \ + X(MQTT_COMMAND_TEMPLATE, "cmd_tpl", "command_template") \ + X(MQTT_COMMAND_TOPIC, "cmd_t", "command_topic") \ + X(MQTT_CONFIGURATION_URL, "cu", "configuration_url") \ + X(MQTT_CURRENT_HUMIDITY_TEMPLATE, "curr_hum_tpl", "current_humidity_template") \ + X(MQTT_CURRENT_HUMIDITY_TOPIC, "curr_hum_t", "current_humidity_topic") \ + X(MQTT_CURRENT_TEMPERATURE_STEP, "precision", "precision") \ + X(MQTT_CURRENT_TEMPERATURE_TEMPLATE, "curr_temp_tpl", "current_temperature_template") \ + X(MQTT_CURRENT_TEMPERATURE_TOPIC, "curr_temp_t", "current_temperature_topic") \ + X(MQTT_DEVICE, "dev", "device") \ + X(MQTT_DEVICE_CLASS, "dev_cla", "device_class") \ + X(MQTT_DEVICE_CONNECTIONS, "cns", "connections") \ + X(MQTT_DEVICE_IDENTIFIERS, "ids", "identifiers") \ + X(MQTT_DEVICE_MANUFACTURER, "mf", "manufacturer") \ + X(MQTT_DEVICE_MODEL, "mdl", "model") \ + X(MQTT_DEVICE_NAME, "name", "name") \ + X(MQTT_DEVICE_SUGGESTED_AREA, "sa", "suggested_area") \ + X(MQTT_DEVICE_SW_VERSION, "sw", "sw_version") \ + X(MQTT_DEVICE_HW_VERSION, "hw", "hw_version") \ + X(MQTT_DIRECTION_COMMAND_TOPIC, "dir_cmd_t", "direction_command_topic") \ + X(MQTT_DIRECTION_STATE_TOPIC, "dir_stat_t", "direction_state_topic") \ + X(MQTT_DOCKED_TEMPLATE, "dock_tpl", "docked_template") \ + X(MQTT_DOCKED_TOPIC, "dock_t", "docked_topic") \ + X(MQTT_EFFECT_COMMAND_TOPIC, "fx_cmd_t", "effect_command_topic") \ + X(MQTT_EFFECT_LIST, "fx_list", "effect_list") \ + X(MQTT_EFFECT_STATE_TOPIC, "fx_stat_t", "effect_state_topic") \ + X(MQTT_EFFECT_TEMPLATE, "fx_tpl", "effect_template") \ + X(MQTT_EFFECT_VALUE_TEMPLATE, "fx_val_tpl", "effect_value_template") \ + X(MQTT_ENABLED_BY_DEFAULT, "en", "enabled_by_default") \ + X(MQTT_ENTITY_CATEGORY, "ent_cat", "entity_category") \ + X(MQTT_ERROR_TEMPLATE, "err_tpl", "error_template") \ + X(MQTT_ERROR_TOPIC, "err_t", "error_topic") \ + X(MQTT_EVENT_TYPE, "event_type", "event_type") \ + X(MQTT_EVENT_TYPES, "evt_typ", "event_types") \ + X(MQTT_EXPIRE_AFTER, "exp_aft", "expire_after") \ + X(MQTT_FAN_MODE_COMMAND_TEMPLATE, "fan_mode_cmd_tpl", "fan_mode_command_template") \ + X(MQTT_FAN_MODE_COMMAND_TOPIC, "fan_mode_cmd_t", "fan_mode_command_topic") \ + X(MQTT_FAN_MODE_STATE_TEMPLATE, "fan_mode_stat_tpl", "fan_mode_state_template") \ + X(MQTT_FAN_MODE_STATE_TOPIC, "fan_mode_stat_t", "fan_mode_state_topic") \ + X(MQTT_FAN_SPEED_LIST, "fanspd_lst", "fan_speed_list") \ + X(MQTT_FAN_SPEED_TEMPLATE, "fanspd_tpl", "fan_speed_template") \ + X(MQTT_FAN_SPEED_TOPIC, "fanspd_t", "fan_speed_topic") \ + X(MQTT_FLASH_TIME_LONG, "flsh_tlng", "flash_time_long") \ + X(MQTT_FLASH_TIME_SHORT, "flsh_tsht", "flash_time_short") \ + X(MQTT_FORCE_UPDATE, "frc_upd", "force_update") \ + X(MQTT_GREEN_TEMPLATE, "g_tpl", "green_template") \ + X(MQTT_HOLD_COMMAND_TEMPLATE, "hold_cmd_tpl", "hold_command_template") \ + X(MQTT_HOLD_COMMAND_TOPIC, "hold_cmd_t", "hold_command_topic") \ + X(MQTT_HOLD_STATE_TEMPLATE, "hold_stat_tpl", "hold_state_template") \ + X(MQTT_HOLD_STATE_TOPIC, "hold_stat_t", "hold_state_topic") \ + X(MQTT_HS_COMMAND_TOPIC, "hs_cmd_t", "hs_command_topic") \ + X(MQTT_HS_STATE_TOPIC, "hs_stat_t", "hs_state_topic") \ + X(MQTT_HS_VALUE_TEMPLATE, "hs_val_tpl", "hs_value_template") \ + X(MQTT_ICON, "ic", "icon") \ + X(MQTT_INITIAL, "init", "initial") \ + X(MQTT_JSON_ATTRIBUTES, "json_attr", "json_attributes") \ + X(MQTT_JSON_ATTRIBUTES_TEMPLATE, "json_attr_tpl", "json_attributes_template") \ + X(MQTT_JSON_ATTRIBUTES_TOPIC, "json_attr_t", "json_attributes_topic") \ + X(MQTT_LAST_RESET_TOPIC, "lrst_t", "last_reset_topic") \ + X(MQTT_LAST_RESET_VALUE_TEMPLATE, "lrst_val_tpl", "last_reset_value_template") \ + X(MQTT_MAX, "max", "max") \ + X(MQTT_MAX_HUMIDITY, "max_hum", "max_humidity") \ + X(MQTT_MAX_MIREDS, "max_mirs", "max_mireds") \ + X(MQTT_MAX_TEMP, "max_temp", "max_temp") \ + X(MQTT_MIN, "min", "min") \ + X(MQTT_MIN_HUMIDITY, "min_hum", "min_humidity") \ + X(MQTT_MIN_MIREDS, "min_mirs", "min_mireds") \ + X(MQTT_MIN_TEMP, "min_temp", "min_temp") \ + X(MQTT_MODE, "mode", "mode") \ + X(MQTT_MODE_COMMAND_TEMPLATE, "mode_cmd_tpl", "mode_command_template") \ + X(MQTT_MODE_COMMAND_TOPIC, "mode_cmd_t", "mode_command_topic") \ + X(MQTT_MODE_STATE_TEMPLATE, "mode_stat_tpl", "mode_state_template") \ + X(MQTT_MODE_STATE_TOPIC, "mode_stat_t", "mode_state_topic") \ + X(MQTT_MODES, "modes", "modes") \ + X(MQTT_NAME, "name", "name") \ + X(MQTT_OBJECT_ID, "obj_id", "object_id") \ + X(MQTT_OFF_DELAY, "off_dly", "off_delay") \ + X(MQTT_ON_COMMAND_TYPE, "on_cmd_type", "on_command_type") \ + X(MQTT_OPTIMISTIC, "opt", "optimistic") \ + X(MQTT_OPTIONS, "ops", "options") \ + X(MQTT_OSCILLATION_COMMAND_TEMPLATE, "osc_cmd_tpl", "oscillation_command_template") \ + X(MQTT_OSCILLATION_COMMAND_TOPIC, "osc_cmd_t", "oscillation_command_topic") \ + X(MQTT_OSCILLATION_STATE_TOPIC, "osc_stat_t", "oscillation_state_topic") \ + X(MQTT_OSCILLATION_VALUE_TEMPLATE, "osc_val_tpl", "oscillation_value_template") \ + X(MQTT_PAYLOAD, "pl", "payload") \ + X(MQTT_PAYLOAD_ARM_AWAY, "pl_arm_away", "payload_arm_away") \ + X(MQTT_PAYLOAD_ARM_CUSTOM_BYPASS, "pl_arm_custom_b", "payload_arm_custom_bypass") \ + X(MQTT_PAYLOAD_ARM_HOME, "pl_arm_home", "payload_arm_home") \ + X(MQTT_PAYLOAD_ARM_NIGHT, "pl_arm_nite", "payload_arm_night") \ + X(MQTT_PAYLOAD_ARM_VACATION, "pl_arm_vacation", "payload_arm_vacation") \ + X(MQTT_PAYLOAD_AVAILABLE, "pl_avail", "payload_available") \ + X(MQTT_PAYLOAD_CLEAN_SPOT, "pl_cln_sp", "payload_clean_spot") \ + X(MQTT_PAYLOAD_CLOSE, "pl_cls", "payload_close") \ + X(MQTT_PAYLOAD_DISARM, "pl_disarm", "payload_disarm") \ + X(MQTT_PAYLOAD_HIGH_SPEED, "pl_hi_spd", "payload_high_speed") \ + X(MQTT_PAYLOAD_HOME, "pl_home", "payload_home") \ + X(MQTT_PAYLOAD_INSTALL, "pl_inst", "payload_install") \ + X(MQTT_PAYLOAD_LOCATE, "pl_loc", "payload_locate") \ + X(MQTT_PAYLOAD_LOCK, "pl_lock", "payload_lock") \ + X(MQTT_PAYLOAD_LOW_SPEED, "pl_lo_spd", "payload_low_speed") \ + X(MQTT_PAYLOAD_MEDIUM_SPEED, "pl_med_spd", "payload_medium_speed") \ + X(MQTT_PAYLOAD_NOT_AVAILABLE, "pl_not_avail", "payload_not_available") \ + X(MQTT_PAYLOAD_NOT_HOME, "pl_not_home", "payload_not_home") \ + X(MQTT_PAYLOAD_OFF, "pl_off", "payload_off") \ + X(MQTT_PAYLOAD_OFF_SPEED, "pl_off_spd", "payload_off_speed") \ + X(MQTT_PAYLOAD_ON, "pl_on", "payload_on") \ + X(MQTT_PAYLOAD_OPEN, "pl_open", "payload_open") \ + X(MQTT_PAYLOAD_OSCILLATION_OFF, "pl_osc_off", "payload_oscillation_off") \ + X(MQTT_PAYLOAD_OSCILLATION_ON, "pl_osc_on", "payload_oscillation_on") \ + X(MQTT_PAYLOAD_PAUSE, "pl_paus", "payload_pause") \ + X(MQTT_PAYLOAD_RESET, "pl_rst", "payload_reset") \ + X(MQTT_PAYLOAD_RESET_HUMIDITY, "pl_rst_hum", "payload_reset_humidity") \ + X(MQTT_PAYLOAD_RESET_MODE, "pl_rst_mode", "payload_reset_mode") \ + X(MQTT_PAYLOAD_RESET_PERCENTAGE, "pl_rst_pct", "payload_reset_percentage") \ + X(MQTT_PAYLOAD_RESET_PRESET_MODE, "pl_rst_pr_mode", "payload_reset_preset_mode") \ + X(MQTT_PAYLOAD_RETURN_TO_BASE, "pl_ret", "payload_return_to_base") \ + X(MQTT_PAYLOAD_START, "pl_strt", "payload_start") \ + X(MQTT_PAYLOAD_START_PAUSE, "pl_stpa", "payload_start_pause") \ + X(MQTT_PAYLOAD_STOP, "pl_stop", "payload_stop") \ + X(MQTT_PAYLOAD_TURN_OFF, "pl_toff", "payload_turn_off") \ + X(MQTT_PAYLOAD_TURN_ON, "pl_ton", "payload_turn_on") \ + X(MQTT_PAYLOAD_UNLOCK, "pl_unlk", "payload_unlock") \ + X(MQTT_PERCENTAGE_COMMAND_TEMPLATE, "pct_cmd_tpl", "percentage_command_template") \ + X(MQTT_PERCENTAGE_COMMAND_TOPIC, "pct_cmd_t", "percentage_command_topic") \ + X(MQTT_PERCENTAGE_STATE_TOPIC, "pct_stat_t", "percentage_state_topic") \ + X(MQTT_PERCENTAGE_VALUE_TEMPLATE, "pct_val_tpl", "percentage_value_template") \ + X(MQTT_POSITION_CLOSED, "pos_clsd", "position_closed") \ + X(MQTT_POSITION_OPEN, "pos_open", "position_open") \ + X(MQTT_POSITION_TEMPLATE, "pos_tpl", "position_template") \ + X(MQTT_POSITION_TOPIC, "pos_t", "position_topic") \ + X(MQTT_POWER_COMMAND_TOPIC, "pow_cmd_t", "power_command_topic") \ + X(MQTT_POWER_STATE_TEMPLATE, "pow_stat_tpl", "power_state_template") \ + X(MQTT_POWER_STATE_TOPIC, "pow_stat_t", "power_state_topic") \ + X(MQTT_PRESET_MODE_COMMAND_TEMPLATE, "pr_mode_cmd_tpl", "preset_mode_command_template") \ + X(MQTT_PRESET_MODE_COMMAND_TOPIC, "pr_mode_cmd_t", "preset_mode_command_topic") \ + X(MQTT_PRESET_MODE_STATE_TOPIC, "pr_mode_stat_t", "preset_mode_state_topic") \ + X(MQTT_PRESET_MODE_VALUE_TEMPLATE, "pr_mode_val_tpl", "preset_mode_value_template") \ + X(MQTT_PRESET_MODES, "pr_modes", "preset_modes") \ + X(MQTT_QOS, "qos", "qos") \ + X(MQTT_RED_TEMPLATE, "r_tpl", "red_template") \ + X(MQTT_RETAIN, "ret", "retain") \ + X(MQTT_RGB_COMMAND_TEMPLATE, "rgb_cmd_tpl", "rgb_command_template") \ + X(MQTT_RGB_COMMAND_TOPIC, "rgb_cmd_t", "rgb_command_topic") \ + X(MQTT_RGB_STATE_TOPIC, "rgb_stat_t", "rgb_state_topic") \ + X(MQTT_RGB_VALUE_TEMPLATE, "rgb_val_tpl", "rgb_value_template") \ + X(MQTT_RGBW_COMMAND_TEMPLATE, "rgbw_cmd_tpl", "rgbw_command_template") \ + X(MQTT_RGBW_COMMAND_TOPIC, "rgbw_cmd_t", "rgbw_command_topic") \ + X(MQTT_RGBW_STATE_TOPIC, "rgbw_stat_t", "rgbw_state_topic") \ + X(MQTT_RGBW_VALUE_TEMPLATE, "rgbw_val_tpl", "rgbw_value_template") \ + X(MQTT_RGBWW_COMMAND_TEMPLATE, "rgbww_cmd_tpl", "rgbww_command_template") \ + X(MQTT_RGBWW_COMMAND_TOPIC, "rgbww_cmd_t", "rgbww_command_topic") \ + X(MQTT_RGBWW_STATE_TOPIC, "rgbww_stat_t", "rgbww_state_topic") \ + X(MQTT_RGBWW_VALUE_TEMPLATE, "rgbww_val_tpl", "rgbww_value_template") \ + X(MQTT_SEND_COMMAND_TOPIC, "send_cmd_t", "send_command_topic") \ + X(MQTT_SEND_IF_OFF, "send_if_off", "send_if_off") \ + X(MQTT_SET_FAN_SPEED_TOPIC, "set_fan_spd_t", "set_fan_speed_topic") \ + X(MQTT_SET_POSITION_TEMPLATE, "set_pos_tpl", "set_position_template") \ + X(MQTT_SET_POSITION_TOPIC, "set_pos_t", "set_position_topic") \ + X(MQTT_SOURCE_TYPE, "src_type", "source_type") \ + X(MQTT_SPEED_COMMAND_TOPIC, "spd_cmd_t", "speed_command_topic") \ + X(MQTT_SPEED_RANGE_MAX, "spd_rng_max", "speed_range_max") \ + X(MQTT_SPEED_RANGE_MIN, "spd_rng_min", "speed_range_min") \ + X(MQTT_SPEED_STATE_TOPIC, "spd_stat_t", "speed_state_topic") \ + X(MQTT_SPEED_VALUE_TEMPLATE, "spd_val_tpl", "speed_value_template") \ + X(MQTT_SPEEDS, "spds", "speeds") \ + X(MQTT_STATE_CLASS, "stat_cla", "state_class") \ + X(MQTT_STATE_CLOSED, "stat_clsd", "state_closed") \ + X(MQTT_STATE_CLOSING, "stat_closing", "state_closing") \ + X(MQTT_STATE_LOCKED, "stat_locked", "state_locked") \ + X(MQTT_STATE_OFF, "stat_off", "state_off") \ + X(MQTT_STATE_ON, "stat_on", "state_on") \ + X(MQTT_STATE_OPEN, "stat_open", "state_open") \ + X(MQTT_STATE_OPENING, "stat_opening", "state_opening") \ + X(MQTT_STATE_STOPPED, "stat_stopped", "state_stopped") \ + X(MQTT_STATE_TEMPLATE, "stat_tpl", "state_template") \ + X(MQTT_STATE_TOPIC, "stat_t", "state_topic") \ + X(MQTT_STATE_UNLOCKED, "stat_unlocked", "state_unlocked") \ + X(MQTT_STATE_VALUE_TEMPLATE, "stat_val_tpl", "state_value_template") \ + X(MQTT_STEP, "step", "step") \ + X(MQTT_SUBTYPE, "stype", "subtype") \ + X(MQTT_SUPPORTED_COLOR_MODES, "sup_clrm", "supported_color_modes") \ + X(MQTT_SUPPORTED_FEATURES, "sup_feat", "supported_features") \ + X(MQTT_SWING_MODE_COMMAND_TEMPLATE, "swing_mode_cmd_tpl", "swing_mode_command_template") \ + X(MQTT_SWING_MODE_COMMAND_TOPIC, "swing_mode_cmd_t", "swing_mode_command_topic") \ + X(MQTT_SWING_MODE_STATE_TEMPLATE, "swing_mode_stat_tpl", "swing_mode_state_template") \ + X(MQTT_SWING_MODE_STATE_TOPIC, "swing_mode_stat_t", "swing_mode_state_topic") \ + X(MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE, "hum_cmd_tpl", "target_humidity_command_template") \ + X(MQTT_TARGET_HUMIDITY_COMMAND_TOPIC, "hum_cmd_t", "target_humidity_command_topic") \ + X(MQTT_TARGET_HUMIDITY_STATE_TEMPLATE, "hum_state_tpl", "target_humidity_state_template") \ + X(MQTT_TARGET_HUMIDITY_STATE_TOPIC, "hum_stat_t", "target_humidity_state_topic") \ + X(MQTT_TARGET_TEMPERATURE_STEP, "temp_step", "temp_step") \ + X(MQTT_TEMPERATURE_COMMAND_TEMPLATE, "temp_cmd_tpl", "temperature_command_template") \ + X(MQTT_TEMPERATURE_COMMAND_TOPIC, "temp_cmd_t", "temperature_command_topic") \ + X(MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE, "temp_hi_cmd_tpl", "temperature_high_command_template") \ + X(MQTT_TEMPERATURE_HIGH_COMMAND_TOPIC, "temp_hi_cmd_t", "temperature_high_command_topic") \ + X(MQTT_TEMPERATURE_HIGH_STATE_TEMPLATE, "temp_hi_stat_tpl", "temperature_high_state_template") \ + X(MQTT_TEMPERATURE_HIGH_STATE_TOPIC, "temp_hi_stat_t", "temperature_high_state_topic") \ + X(MQTT_TEMPERATURE_LOW_COMMAND_TEMPLATE, "temp_lo_cmd_tpl", "temperature_low_command_template") \ + X(MQTT_TEMPERATURE_LOW_COMMAND_TOPIC, "temp_lo_cmd_t", "temperature_low_command_topic") \ + X(MQTT_TEMPERATURE_LOW_STATE_TEMPLATE, "temp_lo_stat_tpl", "temperature_low_state_template") \ + X(MQTT_TEMPERATURE_LOW_STATE_TOPIC, "temp_lo_stat_t", "temperature_low_state_topic") \ + X(MQTT_TEMPERATURE_STATE_TEMPLATE, "temp_stat_tpl", "temperature_state_template") \ + X(MQTT_TEMPERATURE_STATE_TOPIC, "temp_stat_t", "temperature_state_topic") \ + X(MQTT_TEMPERATURE_UNIT, "temp_unit", "temperature_unit") \ + X(MQTT_TILT_CLOSED_VALUE, "tilt_clsd_val", "tilt_closed_value") \ + X(MQTT_TILT_COMMAND_TEMPLATE, "tilt_cmd_tpl", "tilt_command_template") \ + X(MQTT_TILT_COMMAND_TOPIC, "tilt_cmd_t", "tilt_command_topic") \ + X(MQTT_TILT_INVERT_STATE, "tilt_inv_stat", "tilt_invert_state") \ + X(MQTT_TILT_MAX, "tilt_max", "tilt_max") \ + X(MQTT_TILT_MIN, "tilt_min", "tilt_min") \ + X(MQTT_TILT_OPENED_VALUE, "tilt_opnd_val", "tilt_opened_value") \ + X(MQTT_TILT_OPTIMISTIC, "tilt_opt", "tilt_optimistic") \ + X(MQTT_TILT_STATUS_TEMPLATE, "tilt_status_tpl", "tilt_status_template") \ + X(MQTT_TILT_STATUS_TOPIC, "tilt_status_t", "tilt_status_topic") \ + X(MQTT_TOPIC, "t", "topic") \ + X(MQTT_UNIQUE_ID, "uniq_id", "unique_id") \ + X(MQTT_UNIT_OF_MEASUREMENT, "unit_of_meas", "unit_of_measurement") \ + X(MQTT_VALUE_TEMPLATE, "val_tpl", "value_template") \ + X(MQTT_WHITE_COMMAND_TOPIC, "whit_cmd_t", "white_command_topic") \ + X(MQTT_WHITE_SCALE, "whit_scl", "white_scale") \ + X(MQTT_WHITE_VALUE_COMMAND_TOPIC, "whit_val_cmd_t", "white_value_command_topic") \ + X(MQTT_WHITE_VALUE_SCALE, "whit_val_scl", "white_value_scale") \ + X(MQTT_WHITE_VALUE_STATE_TOPIC, "whit_val_stat_t", "white_value_state_topic") \ + X(MQTT_WHITE_VALUE_TEMPLATE, "whit_val_tpl", "white_value_template") \ + X(MQTT_XY_COMMAND_TOPIC, "xy_cmd_t", "xy_command_topic") \ + X(MQTT_XY_STATE_TOPIC, "xy_stat_t", "xy_state_topic") \ + X(MQTT_XY_VALUE_TEMPLATE, "xy_val_tpl", "xy_value_template") +// clang-format on + +#ifdef USE_ESP8266 +// ESP8266: Store strings in PROGMEM (flash) and expose as __FlashStringHelper* pointers. +// ArduinoJson recognizes this type and reads from flash memory. +namespace esphome::mqtt { + +// Generate PROGMEM data arrays #ifdef USE_MQTT_ABBREVIATIONS - -constexpr const char *const MQTT_ACTION_TEMPLATE = "act_tpl"; -constexpr const char *const MQTT_ACTION_TOPIC = "act_t"; -constexpr const char *const MQTT_AUTOMATION_TYPE = "atype"; -constexpr const char *const MQTT_AUX_COMMAND_TOPIC = "aux_cmd_t"; -constexpr const char *const MQTT_AUX_STATE_TEMPLATE = "aux_stat_tpl"; -constexpr const char *const MQTT_AUX_STATE_TOPIC = "aux_stat_t"; -constexpr const char *const MQTT_AVAILABILITY = "avty"; -constexpr const char *const MQTT_AVAILABILITY_MODE = "avty_mode"; -constexpr const char *const MQTT_AVAILABILITY_TOPIC = "avty_t"; -constexpr const char *const MQTT_AWAY_MODE_COMMAND_TOPIC = "away_mode_cmd_t"; -constexpr const char *const MQTT_AWAY_MODE_STATE_TEMPLATE = "away_mode_stat_tpl"; -constexpr const char *const MQTT_AWAY_MODE_STATE_TOPIC = "away_mode_stat_t"; -constexpr const char *const MQTT_BATTERY_LEVEL_TEMPLATE = "bat_lev_tpl"; -constexpr const char *const MQTT_BATTERY_LEVEL_TOPIC = "bat_lev_t"; -constexpr const char *const MQTT_BLUE_TEMPLATE = "b_tpl"; -constexpr const char *const MQTT_BRIGHTNESS_COMMAND_TOPIC = "bri_cmd_t"; -constexpr const char *const MQTT_BRIGHTNESS_SCALE = "bri_scl"; -constexpr const char *const MQTT_BRIGHTNESS_STATE_TOPIC = "bri_stat_t"; -constexpr const char *const MQTT_BRIGHTNESS_TEMPLATE = "bri_tpl"; -constexpr const char *const MQTT_BRIGHTNESS_VALUE_TEMPLATE = "bri_val_tpl"; -constexpr const char *const MQTT_CHARGING_TEMPLATE = "chrg_tpl"; -constexpr const char *const MQTT_CHARGING_TOPIC = "chrg_t"; -constexpr const char *const MQTT_CLEANING_TEMPLATE = "cln_tpl"; -constexpr const char *const MQTT_CLEANING_TOPIC = "cln_t"; -constexpr const char *const MQTT_CODE_ARM_REQUIRED = "cod_arm_req"; -constexpr const char *const MQTT_CODE_DISARM_REQUIRED = "cod_dis_req"; -constexpr const char *const MQTT_COLOR_MODE = "clrm"; -constexpr const char *const MQTT_COLOR_MODE_STATE_TOPIC = "clrm_stat_t"; -constexpr const char *const MQTT_COLOR_MODE_VALUE_TEMPLATE = "clrm_val_tpl"; -constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TEMPLATE = "clr_temp_cmd_tpl"; -constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TOPIC = "clr_temp_cmd_t"; -constexpr const char *const MQTT_COLOR_TEMP_STATE_TOPIC = "clr_temp_stat_t"; -constexpr const char *const MQTT_COLOR_TEMP_TEMPLATE = "clr_temp_tpl"; -constexpr const char *const MQTT_COLOR_TEMP_VALUE_TEMPLATE = "clr_temp_val_tpl"; -constexpr const char *const MQTT_COMMAND_OFF_TEMPLATE = "cmd_off_tpl"; -constexpr const char *const MQTT_COMMAND_ON_TEMPLATE = "cmd_on_tpl"; -constexpr const char *const MQTT_COMMAND_RETAIN = "ret"; -constexpr const char *const MQTT_COMMAND_TEMPLATE = "cmd_tpl"; -constexpr const char *const MQTT_COMMAND_TOPIC = "cmd_t"; -constexpr const char *const MQTT_CONFIGURATION_URL = "cu"; -constexpr const char *const MQTT_CURRENT_HUMIDITY_TEMPLATE = "curr_hum_tpl"; -constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "curr_hum_t"; -constexpr const char *const MQTT_CURRENT_TEMPERATURE_STEP = "precision"; -constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "curr_temp_tpl"; -constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "curr_temp_t"; -constexpr const char *const MQTT_DEVICE = "dev"; -constexpr const char *const MQTT_DEVICE_CLASS = "dev_cla"; -constexpr const char *const MQTT_DEVICE_CONNECTIONS = "cns"; -constexpr const char *const MQTT_DEVICE_IDENTIFIERS = "ids"; -constexpr const char *const MQTT_DEVICE_MANUFACTURER = "mf"; -constexpr const char *const MQTT_DEVICE_MODEL = "mdl"; -constexpr const char *const MQTT_DEVICE_NAME = "name"; -constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "sa"; -constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw"; -constexpr const char *const MQTT_DEVICE_HW_VERSION = "hw"; -constexpr const char *const MQTT_DIRECTION_COMMAND_TOPIC = "dir_cmd_t"; -constexpr const char *const MQTT_DIRECTION_STATE_TOPIC = "dir_stat_t"; -constexpr const char *const MQTT_DOCKED_TEMPLATE = "dock_tpl"; -constexpr const char *const MQTT_DOCKED_TOPIC = "dock_t"; -constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "fx_cmd_t"; -constexpr const char *const MQTT_EFFECT_LIST = "fx_list"; -constexpr const char *const MQTT_EFFECT_STATE_TOPIC = "fx_stat_t"; -constexpr const char *const MQTT_EFFECT_TEMPLATE = "fx_tpl"; -constexpr const char *const MQTT_EFFECT_VALUE_TEMPLATE = "fx_val_tpl"; -constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "en"; -constexpr const char *const MQTT_ENTITY_CATEGORY = "ent_cat"; -constexpr const char *const MQTT_ERROR_TEMPLATE = "err_tpl"; -constexpr const char *const MQTT_ERROR_TOPIC = "err_t"; -constexpr const char *const MQTT_EVENT_TYPE = "event_type"; -constexpr const char *const MQTT_EVENT_TYPES = "evt_typ"; -constexpr const char *const MQTT_EXPIRE_AFTER = "exp_aft"; -constexpr const char *const MQTT_FAN_MODE_COMMAND_TEMPLATE = "fan_mode_cmd_tpl"; -constexpr const char *const MQTT_FAN_MODE_COMMAND_TOPIC = "fan_mode_cmd_t"; -constexpr const char *const MQTT_FAN_MODE_STATE_TEMPLATE = "fan_mode_stat_tpl"; -constexpr const char *const MQTT_FAN_MODE_STATE_TOPIC = "fan_mode_stat_t"; -constexpr const char *const MQTT_FAN_SPEED_LIST = "fanspd_lst"; -constexpr const char *const MQTT_FAN_SPEED_TEMPLATE = "fanspd_tpl"; -constexpr const char *const MQTT_FAN_SPEED_TOPIC = "fanspd_t"; -constexpr const char *const MQTT_FLASH_TIME_LONG = "flsh_tlng"; -constexpr const char *const MQTT_FLASH_TIME_SHORT = "flsh_tsht"; -constexpr const char *const MQTT_FORCE_UPDATE = "frc_upd"; -constexpr const char *const MQTT_GREEN_TEMPLATE = "g_tpl"; -constexpr const char *const MQTT_HOLD_COMMAND_TEMPLATE = "hold_cmd_tpl"; -constexpr const char *const MQTT_HOLD_COMMAND_TOPIC = "hold_cmd_t"; -constexpr const char *const MQTT_HOLD_STATE_TEMPLATE = "hold_stat_tpl"; -constexpr const char *const MQTT_HOLD_STATE_TOPIC = "hold_stat_t"; -constexpr const char *const MQTT_HS_COMMAND_TOPIC = "hs_cmd_t"; -constexpr const char *const MQTT_HS_STATE_TOPIC = "hs_stat_t"; -constexpr const char *const MQTT_HS_VALUE_TEMPLATE = "hs_val_tpl"; -constexpr const char *const MQTT_ICON = "ic"; -constexpr const char *const MQTT_INITIAL = "init"; -constexpr const char *const MQTT_JSON_ATTRIBUTES = "json_attr"; -constexpr const char *const MQTT_JSON_ATTRIBUTES_TEMPLATE = "json_attr_tpl"; -constexpr const char *const MQTT_JSON_ATTRIBUTES_TOPIC = "json_attr_t"; -constexpr const char *const MQTT_LAST_RESET_TOPIC = "lrst_t"; -constexpr const char *const MQTT_LAST_RESET_VALUE_TEMPLATE = "lrst_val_tpl"; -constexpr const char *const MQTT_MAX = "max"; -constexpr const char *const MQTT_MAX_HUMIDITY = "max_hum"; -constexpr const char *const MQTT_MAX_MIREDS = "max_mirs"; -constexpr const char *const MQTT_MAX_TEMP = "max_temp"; -constexpr const char *const MQTT_MIN = "min"; -constexpr const char *const MQTT_MIN_HUMIDITY = "min_hum"; -constexpr const char *const MQTT_MIN_MIREDS = "min_mirs"; -constexpr const char *const MQTT_MIN_TEMP = "min_temp"; -constexpr const char *const MQTT_MODE = "mode"; -constexpr const char *const MQTT_MODE_COMMAND_TEMPLATE = "mode_cmd_tpl"; -constexpr const char *const MQTT_MODE_COMMAND_TOPIC = "mode_cmd_t"; -constexpr const char *const MQTT_MODE_STATE_TEMPLATE = "mode_stat_tpl"; -constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_stat_t"; -constexpr const char *const MQTT_MODES = "modes"; -constexpr const char *const MQTT_NAME = "name"; -constexpr const char *const MQTT_OBJECT_ID = "obj_id"; -constexpr const char *const MQTT_OFF_DELAY = "off_dly"; -constexpr const char *const MQTT_ON_COMMAND_TYPE = "on_cmd_type"; -constexpr const char *const MQTT_OPTIMISTIC = "opt"; -constexpr const char *const MQTT_OPTIONS = "ops"; -constexpr const char *const MQTT_OSCILLATION_COMMAND_TEMPLATE = "osc_cmd_tpl"; -constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC = "osc_cmd_t"; -constexpr const char *const MQTT_OSCILLATION_STATE_TOPIC = "osc_stat_t"; -constexpr const char *const MQTT_OSCILLATION_VALUE_TEMPLATE = "osc_val_tpl"; -constexpr const char *const MQTT_PAYLOAD = "pl"; -constexpr const char *const MQTT_PAYLOAD_ARM_AWAY = "pl_arm_away"; -constexpr const char *const MQTT_PAYLOAD_ARM_CUSTOM_BYPASS = "pl_arm_custom_b"; -constexpr const char *const MQTT_PAYLOAD_ARM_HOME = "pl_arm_home"; -constexpr const char *const MQTT_PAYLOAD_ARM_NIGHT = "pl_arm_nite"; -constexpr const char *const MQTT_PAYLOAD_ARM_VACATION = "pl_arm_vacation"; -constexpr const char *const MQTT_PAYLOAD_AVAILABLE = "pl_avail"; -constexpr const char *const MQTT_PAYLOAD_CLEAN_SPOT = "pl_cln_sp"; -constexpr const char *const MQTT_PAYLOAD_CLOSE = "pl_cls"; -constexpr const char *const MQTT_PAYLOAD_DISARM = "pl_disarm"; -constexpr const char *const MQTT_PAYLOAD_HIGH_SPEED = "pl_hi_spd"; -constexpr const char *const MQTT_PAYLOAD_HOME = "pl_home"; -constexpr const char *const MQTT_PAYLOAD_INSTALL = "pl_inst"; -constexpr const char *const MQTT_PAYLOAD_LOCATE = "pl_loc"; -constexpr const char *const MQTT_PAYLOAD_LOCK = "pl_lock"; -constexpr const char *const MQTT_PAYLOAD_LOW_SPEED = "pl_lo_spd"; -constexpr const char *const MQTT_PAYLOAD_MEDIUM_SPEED = "pl_med_spd"; -constexpr const char *const MQTT_PAYLOAD_NOT_AVAILABLE = "pl_not_avail"; -constexpr const char *const MQTT_PAYLOAD_NOT_HOME = "pl_not_home"; -constexpr const char *const MQTT_PAYLOAD_OFF = "pl_off"; -constexpr const char *const MQTT_PAYLOAD_OFF_SPEED = "pl_off_spd"; -constexpr const char *const MQTT_PAYLOAD_ON = "pl_on"; -constexpr const char *const MQTT_PAYLOAD_OPEN = "pl_open"; -constexpr const char *const MQTT_PAYLOAD_OSCILLATION_OFF = "pl_osc_off"; -constexpr const char *const MQTT_PAYLOAD_OSCILLATION_ON = "pl_osc_on"; -constexpr const char *const MQTT_PAYLOAD_PAUSE = "pl_paus"; -constexpr const char *const MQTT_PAYLOAD_RESET = "pl_rst"; -constexpr const char *const MQTT_PAYLOAD_RESET_HUMIDITY = "pl_rst_hum"; -constexpr const char *const MQTT_PAYLOAD_RESET_MODE = "pl_rst_mode"; -constexpr const char *const MQTT_PAYLOAD_RESET_PERCENTAGE = "pl_rst_pct"; -constexpr const char *const MQTT_PAYLOAD_RESET_PRESET_MODE = "pl_rst_pr_mode"; -constexpr const char *const MQTT_PAYLOAD_RETURN_TO_BASE = "pl_ret"; -constexpr const char *const MQTT_PAYLOAD_START = "pl_strt"; -constexpr const char *const MQTT_PAYLOAD_START_PAUSE = "pl_stpa"; -constexpr const char *const MQTT_PAYLOAD_STOP = "pl_stop"; -constexpr const char *const MQTT_PAYLOAD_TURN_OFF = "pl_toff"; -constexpr const char *const MQTT_PAYLOAD_TURN_ON = "pl_ton"; -constexpr const char *const MQTT_PAYLOAD_UNLOCK = "pl_unlk"; -constexpr const char *const MQTT_PERCENTAGE_COMMAND_TEMPLATE = "pct_cmd_tpl"; -constexpr const char *const MQTT_PERCENTAGE_COMMAND_TOPIC = "pct_cmd_t"; -constexpr const char *const MQTT_PERCENTAGE_STATE_TOPIC = "pct_stat_t"; -constexpr const char *const MQTT_PERCENTAGE_VALUE_TEMPLATE = "pct_val_tpl"; -constexpr const char *const MQTT_POSITION_CLOSED = "pos_clsd"; -constexpr const char *const MQTT_POSITION_OPEN = "pos_open"; -constexpr const char *const MQTT_POSITION_TEMPLATE = "pos_tpl"; -constexpr const char *const MQTT_POSITION_TOPIC = "pos_t"; -constexpr const char *const MQTT_POWER_COMMAND_TOPIC = "pow_cmd_t"; -constexpr const char *const MQTT_POWER_STATE_TEMPLATE = "pow_stat_tpl"; -constexpr const char *const MQTT_POWER_STATE_TOPIC = "pow_stat_t"; -constexpr const char *const MQTT_PRESET_MODE_COMMAND_TEMPLATE = "pr_mode_cmd_tpl"; -constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "pr_mode_cmd_t"; -constexpr const char *const MQTT_PRESET_MODE_STATE_TOPIC = "pr_mode_stat_t"; -constexpr const char *const MQTT_PRESET_MODE_VALUE_TEMPLATE = "pr_mode_val_tpl"; -constexpr const char *const MQTT_PRESET_MODES = "pr_modes"; -constexpr const char *const MQTT_QOS = "qos"; -constexpr const char *const MQTT_RED_TEMPLATE = "r_tpl"; -constexpr const char *const MQTT_RETAIN = "ret"; -constexpr const char *const MQTT_RGB_COMMAND_TEMPLATE = "rgb_cmd_tpl"; -constexpr const char *const MQTT_RGB_COMMAND_TOPIC = "rgb_cmd_t"; -constexpr const char *const MQTT_RGB_STATE_TOPIC = "rgb_stat_t"; -constexpr const char *const MQTT_RGB_VALUE_TEMPLATE = "rgb_val_tpl"; -constexpr const char *const MQTT_RGBW_COMMAND_TEMPLATE = "rgbw_cmd_tpl"; -constexpr const char *const MQTT_RGBW_COMMAND_TOPIC = "rgbw_cmd_t"; -constexpr const char *const MQTT_RGBW_STATE_TOPIC = "rgbw_stat_t"; -constexpr const char *const MQTT_RGBW_VALUE_TEMPLATE = "rgbw_val_tpl"; -constexpr const char *const MQTT_RGBWW_COMMAND_TEMPLATE = "rgbww_cmd_tpl"; -constexpr const char *const MQTT_RGBWW_COMMAND_TOPIC = "rgbww_cmd_t"; -constexpr const char *const MQTT_RGBWW_STATE_TOPIC = "rgbww_stat_t"; -constexpr const char *const MQTT_RGBWW_VALUE_TEMPLATE = "rgbww_val_tpl"; -constexpr const char *const MQTT_SEND_COMMAND_TOPIC = "send_cmd_t"; -constexpr const char *const MQTT_SEND_IF_OFF = "send_if_off"; -constexpr const char *const MQTT_SET_FAN_SPEED_TOPIC = "set_fan_spd_t"; -constexpr const char *const MQTT_SET_POSITION_TEMPLATE = "set_pos_tpl"; -constexpr const char *const MQTT_SET_POSITION_TOPIC = "set_pos_t"; -constexpr const char *const MQTT_SOURCE_TYPE = "src_type"; -constexpr const char *const MQTT_SPEED_COMMAND_TOPIC = "spd_cmd_t"; -constexpr const char *const MQTT_SPEED_RANGE_MAX = "spd_rng_max"; -constexpr const char *const MQTT_SPEED_RANGE_MIN = "spd_rng_min"; -constexpr const char *const MQTT_SPEED_STATE_TOPIC = "spd_stat_t"; -constexpr const char *const MQTT_SPEED_VALUE_TEMPLATE = "spd_val_tpl"; -constexpr const char *const MQTT_SPEEDS = "spds"; -constexpr const char *const MQTT_STATE_CLASS = "stat_cla"; -constexpr const char *const MQTT_STATE_CLOSED = "stat_clsd"; -constexpr const char *const MQTT_STATE_CLOSING = "stat_closing"; -constexpr const char *const MQTT_STATE_LOCKED = "stat_locked"; -constexpr const char *const MQTT_STATE_OFF = "stat_off"; -constexpr const char *const MQTT_STATE_ON = "stat_on"; -constexpr const char *const MQTT_STATE_OPEN = "stat_open"; -constexpr const char *const MQTT_STATE_OPENING = "stat_opening"; -constexpr const char *const MQTT_STATE_STOPPED = "stat_stopped"; -constexpr const char *const MQTT_STATE_TEMPLATE = "stat_tpl"; -constexpr const char *const MQTT_STATE_TOPIC = "stat_t"; -constexpr const char *const MQTT_STATE_UNLOCKED = "stat_unlocked"; -constexpr const char *const MQTT_STATE_VALUE_TEMPLATE = "stat_val_tpl"; -constexpr const char *const MQTT_STEP = "step"; -constexpr const char *const MQTT_SUBTYPE = "stype"; -constexpr const char *const MQTT_SUPPORTED_COLOR_MODES = "sup_clrm"; -constexpr const char *const MQTT_SUPPORTED_FEATURES = "sup_feat"; -constexpr const char *const MQTT_SWING_MODE_COMMAND_TEMPLATE = "swing_mode_cmd_tpl"; -constexpr const char *const MQTT_SWING_MODE_COMMAND_TOPIC = "swing_mode_cmd_t"; -constexpr const char *const MQTT_SWING_MODE_STATE_TEMPLATE = "swing_mode_stat_tpl"; -constexpr const char *const MQTT_SWING_MODE_STATE_TOPIC = "swing_mode_stat_t"; -constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "hum_cmd_tpl"; -constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "hum_cmd_t"; -constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "hum_state_tpl"; -constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "hum_stat_t"; -constexpr const char *const MQTT_TARGET_TEMPERATURE_STEP = "temp_step"; -constexpr const char *const MQTT_TEMPERATURE_COMMAND_TEMPLATE = "temp_cmd_tpl"; -constexpr const char *const MQTT_TEMPERATURE_COMMAND_TOPIC = "temp_cmd_t"; -constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE = "temp_hi_cmd_tpl"; -constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TOPIC = "temp_hi_cmd_t"; -constexpr const char *const MQTT_TEMPERATURE_HIGH_STATE_TEMPLATE = "temp_hi_stat_tpl"; -constexpr const char *const MQTT_TEMPERATURE_HIGH_STATE_TOPIC = "temp_hi_stat_t"; -constexpr const char *const MQTT_TEMPERATURE_LOW_COMMAND_TEMPLATE = "temp_lo_cmd_tpl"; -constexpr const char *const MQTT_TEMPERATURE_LOW_COMMAND_TOPIC = "temp_lo_cmd_t"; -constexpr const char *const MQTT_TEMPERATURE_LOW_STATE_TEMPLATE = "temp_lo_stat_tpl"; -constexpr const char *const MQTT_TEMPERATURE_LOW_STATE_TOPIC = "temp_lo_stat_t"; -constexpr const char *const MQTT_TEMPERATURE_STATE_TEMPLATE = "temp_stat_tpl"; -constexpr const char *const MQTT_TEMPERATURE_STATE_TOPIC = "temp_stat_t"; -constexpr const char *const MQTT_TEMPERATURE_UNIT = "temp_unit"; -constexpr const char *const MQTT_TILT_CLOSED_VALUE = "tilt_clsd_val"; -constexpr const char *const MQTT_TILT_COMMAND_TEMPLATE = "tilt_cmd_tpl"; -constexpr const char *const MQTT_TILT_COMMAND_TOPIC = "tilt_cmd_t"; -constexpr const char *const MQTT_TILT_INVERT_STATE = "tilt_inv_stat"; -constexpr const char *const MQTT_TILT_MAX = "tilt_max"; -constexpr const char *const MQTT_TILT_MIN = "tilt_min"; -constexpr const char *const MQTT_TILT_OPENED_VALUE = "tilt_opnd_val"; -constexpr const char *const MQTT_TILT_OPTIMISTIC = "tilt_opt"; -constexpr const char *const MQTT_TILT_STATUS_TEMPLATE = "tilt_status_tpl"; -constexpr const char *const MQTT_TILT_STATUS_TOPIC = "tilt_status_t"; -constexpr const char *const MQTT_TOPIC = "t"; -constexpr const char *const MQTT_UNIQUE_ID = "uniq_id"; -constexpr const char *const MQTT_UNIT_OF_MEASUREMENT = "unit_of_meas"; -constexpr const char *const MQTT_VALUE_TEMPLATE = "val_tpl"; -constexpr const char *const MQTT_WHITE_COMMAND_TOPIC = "whit_cmd_t"; -constexpr const char *const MQTT_WHITE_SCALE = "whit_scl"; -constexpr const char *const MQTT_WHITE_VALUE_COMMAND_TOPIC = "whit_val_cmd_t"; -constexpr const char *const MQTT_WHITE_VALUE_SCALE = "whit_val_scl"; -constexpr const char *const MQTT_WHITE_VALUE_STATE_TOPIC = "whit_val_stat_t"; -constexpr const char *const MQTT_WHITE_VALUE_TEMPLATE = "whit_val_tpl"; -constexpr const char *const MQTT_XY_COMMAND_TOPIC = "xy_cmd_t"; -constexpr const char *const MQTT_XY_STATE_TOPIC = "xy_stat_t"; -constexpr const char *const MQTT_XY_VALUE_TEMPLATE = "xy_val_tpl"; - +#define MQTT_DATA(name, abbr, full) static const char name##_data[] PROGMEM = abbr; #else +#define MQTT_DATA(name, abbr, full) static const char name##_data[] PROGMEM = full; +#endif +MQTT_KEYS_LIST(MQTT_DATA) +#undef MQTT_DATA -constexpr const char *const MQTT_ACTION_TEMPLATE = "action_template"; -constexpr const char *const MQTT_ACTION_TOPIC = "action_topic"; -constexpr const char *const MQTT_AUTOMATION_TYPE = "automation_type"; -constexpr const char *const MQTT_AUX_COMMAND_TOPIC = "aux_command_topic"; -constexpr const char *const MQTT_AUX_STATE_TEMPLATE = "aux_state_template"; -constexpr const char *const MQTT_AUX_STATE_TOPIC = "aux_state_topic"; -constexpr const char *const MQTT_AVAILABILITY = "availability"; -constexpr const char *const MQTT_AVAILABILITY_MODE = "availability_mode"; -constexpr const char *const MQTT_AVAILABILITY_TOPIC = "availability_topic"; -constexpr const char *const MQTT_AWAY_MODE_COMMAND_TOPIC = "away_mode_command_topic"; -constexpr const char *const MQTT_AWAY_MODE_STATE_TEMPLATE = "away_mode_state_template"; -constexpr const char *const MQTT_AWAY_MODE_STATE_TOPIC = "away_mode_state_topic"; -constexpr const char *const MQTT_BATTERY_LEVEL_TEMPLATE = "battery_level_template"; -constexpr const char *const MQTT_BATTERY_LEVEL_TOPIC = "battery_level_topic"; -constexpr const char *const MQTT_BLUE_TEMPLATE = "blue_template"; -constexpr const char *const MQTT_BRIGHTNESS_COMMAND_TOPIC = "brightness_command_topic"; -constexpr const char *const MQTT_BRIGHTNESS_SCALE = "brightness_scale"; -constexpr const char *const MQTT_BRIGHTNESS_STATE_TOPIC = "brightness_state_topic"; -constexpr const char *const MQTT_BRIGHTNESS_TEMPLATE = "brightness_template"; -constexpr const char *const MQTT_BRIGHTNESS_VALUE_TEMPLATE = "brightness_value_template"; -constexpr const char *const MQTT_CHARGING_TEMPLATE = "charging_template"; -constexpr const char *const MQTT_CHARGING_TOPIC = "charging_topic"; -constexpr const char *const MQTT_CLEANING_TEMPLATE = "cleaning_template"; -constexpr const char *const MQTT_CLEANING_TOPIC = "cleaning_topic"; -constexpr const char *const MQTT_CODE_ARM_REQUIRED = "code_arm_required"; -constexpr const char *const MQTT_CODE_DISARM_REQUIRED = "code_disarm_required"; -constexpr const char *const MQTT_COLOR_MODE = "color_mode"; -constexpr const char *const MQTT_COLOR_MODE_STATE_TOPIC = "color_mode_state_topic"; -constexpr const char *const MQTT_COLOR_MODE_VALUE_TEMPLATE = "color_mode_value_template"; -constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TEMPLATE = "color_temp_command_template"; -constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TOPIC = "color_temp_command_topic"; -constexpr const char *const MQTT_COLOR_TEMP_STATE_TOPIC = "color_temp_state_topic"; -constexpr const char *const MQTT_COLOR_TEMP_TEMPLATE = "color_temp_template"; -constexpr const char *const MQTT_COLOR_TEMP_VALUE_TEMPLATE = "color_temp_value_template"; -constexpr const char *const MQTT_COMMAND_OFF_TEMPLATE = "command_off_template"; -constexpr const char *const MQTT_COMMAND_ON_TEMPLATE = "command_on_template"; -constexpr const char *const MQTT_COMMAND_RETAIN = "retain"; -constexpr const char *const MQTT_COMMAND_TEMPLATE = "command_template"; -constexpr const char *const MQTT_COMMAND_TOPIC = "command_topic"; -constexpr const char *const MQTT_CONFIGURATION_URL = "configuration_url"; -constexpr const char *const MQTT_CURRENT_HUMIDITY_TEMPLATE = "current_humidity_template"; -constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "current_humidity_topic"; -constexpr const char *const MQTT_CURRENT_TEMPERATURE_STEP = "precision"; -constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "current_temperature_template"; -constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "current_temperature_topic"; -constexpr const char *const MQTT_DEVICE = "device"; -constexpr const char *const MQTT_DEVICE_CLASS = "device_class"; -constexpr const char *const MQTT_DEVICE_CONNECTIONS = "connections"; -constexpr const char *const MQTT_DEVICE_IDENTIFIERS = "identifiers"; -constexpr const char *const MQTT_DEVICE_MANUFACTURER = "manufacturer"; -constexpr const char *const MQTT_DEVICE_MODEL = "model"; -constexpr const char *const MQTT_DEVICE_NAME = "name"; -constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "suggested_area"; -constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw_version"; -constexpr const char *const MQTT_DEVICE_HW_VERSION = "hw_version"; -constexpr const char *const MQTT_DIRECTION_COMMAND_TOPIC = "direction_command_topic"; -constexpr const char *const MQTT_DIRECTION_STATE_TOPIC = "direction_state_topic"; -constexpr const char *const MQTT_DOCKED_TEMPLATE = "docked_template"; -constexpr const char *const MQTT_DOCKED_TOPIC = "docked_topic"; -constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "effect_command_topic"; -constexpr const char *const MQTT_EFFECT_LIST = "effect_list"; -constexpr const char *const MQTT_EFFECT_STATE_TOPIC = "effect_state_topic"; -constexpr const char *const MQTT_EFFECT_TEMPLATE = "effect_template"; -constexpr const char *const MQTT_EFFECT_VALUE_TEMPLATE = "effect_value_template"; -constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "enabled_by_default"; -constexpr const char *const MQTT_ENTITY_CATEGORY = "entity_category"; -constexpr const char *const MQTT_ERROR_TEMPLATE = "error_template"; -constexpr const char *const MQTT_ERROR_TOPIC = "error_topic"; -constexpr const char *const MQTT_EVENT_TYPE = "event_type"; -constexpr const char *const MQTT_EVENT_TYPES = "event_types"; -constexpr const char *const MQTT_EXPIRE_AFTER = "expire_after"; -constexpr const char *const MQTT_FAN_MODE_COMMAND_TEMPLATE = "fan_mode_command_template"; -constexpr const char *const MQTT_FAN_MODE_COMMAND_TOPIC = "fan_mode_command_topic"; -constexpr const char *const MQTT_FAN_MODE_STATE_TEMPLATE = "fan_mode_state_template"; -constexpr const char *const MQTT_FAN_MODE_STATE_TOPIC = "fan_mode_state_topic"; -constexpr const char *const MQTT_FAN_SPEED_LIST = "fan_speed_list"; -constexpr const char *const MQTT_FAN_SPEED_TEMPLATE = "fan_speed_template"; -constexpr const char *const MQTT_FAN_SPEED_TOPIC = "fan_speed_topic"; -constexpr const char *const MQTT_FLASH_TIME_LONG = "flash_time_long"; -constexpr const char *const MQTT_FLASH_TIME_SHORT = "flash_time_short"; -constexpr const char *const MQTT_FORCE_UPDATE = "force_update"; -constexpr const char *const MQTT_GREEN_TEMPLATE = "green_template"; -constexpr const char *const MQTT_HOLD_COMMAND_TEMPLATE = "hold_command_template"; -constexpr const char *const MQTT_HOLD_COMMAND_TOPIC = "hold_command_topic"; -constexpr const char *const MQTT_HOLD_STATE_TEMPLATE = "hold_state_template"; -constexpr const char *const MQTT_HOLD_STATE_TOPIC = "hold_state_topic"; -constexpr const char *const MQTT_HS_COMMAND_TOPIC = "hs_command_topic"; -constexpr const char *const MQTT_HS_STATE_TOPIC = "hs_state_topic"; -constexpr const char *const MQTT_HS_VALUE_TEMPLATE = "hs_value_template"; -constexpr const char *const MQTT_ICON = "icon"; -constexpr const char *const MQTT_INITIAL = "initial"; -constexpr const char *const MQTT_JSON_ATTRIBUTES = "json_attributes"; -constexpr const char *const MQTT_JSON_ATTRIBUTES_TEMPLATE = "json_attributes_template"; -constexpr const char *const MQTT_JSON_ATTRIBUTES_TOPIC = "json_attributes_topic"; -constexpr const char *const MQTT_LAST_RESET_TOPIC = "last_reset_topic"; -constexpr const char *const MQTT_LAST_RESET_VALUE_TEMPLATE = "last_reset_value_template"; -constexpr const char *const MQTT_MAX = "max"; -constexpr const char *const MQTT_MAX_HUMIDITY = "max_humidity"; -constexpr const char *const MQTT_MAX_MIREDS = "max_mireds"; -constexpr const char *const MQTT_MAX_TEMP = "max_temp"; -constexpr const char *const MQTT_MIN = "min"; -constexpr const char *const MQTT_MIN_HUMIDITY = "min_humidity"; -constexpr const char *const MQTT_MIN_MIREDS = "min_mireds"; -constexpr const char *const MQTT_MIN_TEMP = "min_temp"; -constexpr const char *const MQTT_MODE = "mode"; -constexpr const char *const MQTT_MODE_COMMAND_TEMPLATE = "mode_command_template"; -constexpr const char *const MQTT_MODE_COMMAND_TOPIC = "mode_command_topic"; -constexpr const char *const MQTT_MODE_STATE_TEMPLATE = "mode_state_template"; -constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_state_topic"; -constexpr const char *const MQTT_MODES = "modes"; -constexpr const char *const MQTT_NAME = "name"; -constexpr const char *const MQTT_OBJECT_ID = "object_id"; -constexpr const char *const MQTT_OFF_DELAY = "off_delay"; -constexpr const char *const MQTT_ON_COMMAND_TYPE = "on_command_type"; -constexpr const char *const MQTT_OPTIMISTIC = "optimistic"; -constexpr const char *const MQTT_OPTIONS = "options"; -constexpr const char *const MQTT_OSCILLATION_COMMAND_TEMPLATE = "oscillation_command_template"; -constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC = "oscillation_command_topic"; -constexpr const char *const MQTT_OSCILLATION_STATE_TOPIC = "oscillation_state_topic"; -constexpr const char *const MQTT_OSCILLATION_VALUE_TEMPLATE = "oscillation_value_template"; -constexpr const char *const MQTT_PAYLOAD = "payload"; -constexpr const char *const MQTT_PAYLOAD_ARM_AWAY = "payload_arm_away"; -constexpr const char *const MQTT_PAYLOAD_ARM_CUSTOM_BYPASS = "payload_arm_custom_bypass"; -constexpr const char *const MQTT_PAYLOAD_ARM_HOME = "payload_arm_home"; -constexpr const char *const MQTT_PAYLOAD_ARM_NIGHT = "payload_arm_night"; -constexpr const char *const MQTT_PAYLOAD_ARM_VACATION = "payload_arm_vacation"; -constexpr const char *const MQTT_PAYLOAD_AVAILABLE = "payload_available"; -constexpr const char *const MQTT_PAYLOAD_CLEAN_SPOT = "payload_clean_spot"; -constexpr const char *const MQTT_PAYLOAD_CLOSE = "payload_close"; -constexpr const char *const MQTT_PAYLOAD_DISARM = "payload_disarm"; -constexpr const char *const MQTT_PAYLOAD_HIGH_SPEED = "payload_high_speed"; -constexpr const char *const MQTT_PAYLOAD_HOME = "payload_home"; -constexpr const char *const MQTT_PAYLOAD_INSTALL = "payload_install"; -constexpr const char *const MQTT_PAYLOAD_LOCATE = "payload_locate"; -constexpr const char *const MQTT_PAYLOAD_LOCK = "payload_lock"; -constexpr const char *const MQTT_PAYLOAD_LOW_SPEED = "payload_low_speed"; -constexpr const char *const MQTT_PAYLOAD_MEDIUM_SPEED = "payload_medium_speed"; -constexpr const char *const MQTT_PAYLOAD_NOT_AVAILABLE = "payload_not_available"; -constexpr const char *const MQTT_PAYLOAD_NOT_HOME = "payload_not_home"; -constexpr const char *const MQTT_PAYLOAD_OFF = "payload_off"; -constexpr const char *const MQTT_PAYLOAD_OFF_SPEED = "payload_off_speed"; -constexpr const char *const MQTT_PAYLOAD_ON = "payload_on"; -constexpr const char *const MQTT_PAYLOAD_OPEN = "payload_open"; -constexpr const char *const MQTT_PAYLOAD_OSCILLATION_OFF = "payload_oscillation_off"; -constexpr const char *const MQTT_PAYLOAD_OSCILLATION_ON = "payload_oscillation_on"; -constexpr const char *const MQTT_PAYLOAD_PAUSE = "payload_pause"; -constexpr const char *const MQTT_PAYLOAD_RESET = "payload_reset"; -constexpr const char *const MQTT_PAYLOAD_RESET_HUMIDITY = "payload_reset_humidity"; -constexpr const char *const MQTT_PAYLOAD_RESET_MODE = "payload_reset_mode"; -constexpr const char *const MQTT_PAYLOAD_RESET_PERCENTAGE = "payload_reset_percentage"; -constexpr const char *const MQTT_PAYLOAD_RESET_PRESET_MODE = "payload_reset_preset_mode"; -constexpr const char *const MQTT_PAYLOAD_RETURN_TO_BASE = "payload_return_to_base"; -constexpr const char *const MQTT_PAYLOAD_START = "payload_start"; -constexpr const char *const MQTT_PAYLOAD_START_PAUSE = "payload_start_pause"; -constexpr const char *const MQTT_PAYLOAD_STOP = "payload_stop"; -constexpr const char *const MQTT_PAYLOAD_TURN_OFF = "payload_turn_off"; -constexpr const char *const MQTT_PAYLOAD_TURN_ON = "payload_turn_on"; -constexpr const char *const MQTT_PAYLOAD_UNLOCK = "payload_unlock"; -constexpr const char *const MQTT_PERCENTAGE_COMMAND_TEMPLATE = "percentage_command_template"; -constexpr const char *const MQTT_PERCENTAGE_COMMAND_TOPIC = "percentage_command_topic"; -constexpr const char *const MQTT_PERCENTAGE_STATE_TOPIC = "percentage_state_topic"; -constexpr const char *const MQTT_PERCENTAGE_VALUE_TEMPLATE = "percentage_value_template"; -constexpr const char *const MQTT_POSITION_CLOSED = "position_closed"; -constexpr const char *const MQTT_POSITION_OPEN = "position_open"; -constexpr const char *const MQTT_POSITION_TEMPLATE = "position_template"; -constexpr const char *const MQTT_POSITION_TOPIC = "position_topic"; -constexpr const char *const MQTT_POWER_COMMAND_TOPIC = "power_command_topic"; -constexpr const char *const MQTT_POWER_STATE_TEMPLATE = "power_state_template"; -constexpr const char *const MQTT_POWER_STATE_TOPIC = "power_state_topic"; -constexpr const char *const MQTT_PRESET_MODE_COMMAND_TEMPLATE = "preset_mode_command_template"; -constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "preset_mode_command_topic"; -constexpr const char *const MQTT_PRESET_MODE_STATE_TOPIC = "preset_mode_state_topic"; -constexpr const char *const MQTT_PRESET_MODE_VALUE_TEMPLATE = "preset_mode_value_template"; -constexpr const char *const MQTT_PRESET_MODES = "preset_modes"; -constexpr const char *const MQTT_QOS = "qos"; -constexpr const char *const MQTT_RED_TEMPLATE = "red_template"; -constexpr const char *const MQTT_RETAIN = "retain"; -constexpr const char *const MQTT_RGB_COMMAND_TEMPLATE = "rgb_command_template"; -constexpr const char *const MQTT_RGB_COMMAND_TOPIC = "rgb_command_topic"; -constexpr const char *const MQTT_RGB_STATE_TOPIC = "rgb_state_topic"; -constexpr const char *const MQTT_RGB_VALUE_TEMPLATE = "rgb_value_template"; -constexpr const char *const MQTT_RGBW_COMMAND_TEMPLATE = "rgbw_command_template"; -constexpr const char *const MQTT_RGBW_COMMAND_TOPIC = "rgbw_command_topic"; -constexpr const char *const MQTT_RGBW_STATE_TOPIC = "rgbw_state_topic"; -constexpr const char *const MQTT_RGBW_VALUE_TEMPLATE = "rgbw_value_template"; -constexpr const char *const MQTT_RGBWW_COMMAND_TEMPLATE = "rgbww_command_template"; -constexpr const char *const MQTT_RGBWW_COMMAND_TOPIC = "rgbww_command_topic"; -constexpr const char *const MQTT_RGBWW_STATE_TOPIC = "rgbww_state_topic"; -constexpr const char *const MQTT_RGBWW_VALUE_TEMPLATE = "rgbww_value_template"; -constexpr const char *const MQTT_SEND_COMMAND_TOPIC = "send_command_topic"; -constexpr const char *const MQTT_SEND_IF_OFF = "send_if_off"; -constexpr const char *const MQTT_SET_FAN_SPEED_TOPIC = "set_fan_speed_topic"; -constexpr const char *const MQTT_SET_POSITION_TEMPLATE = "set_position_template"; -constexpr const char *const MQTT_SET_POSITION_TOPIC = "set_position_topic"; -constexpr const char *const MQTT_SOURCE_TYPE = "source_type"; -constexpr const char *const MQTT_SPEED_COMMAND_TOPIC = "speed_command_topic"; -constexpr const char *const MQTT_SPEED_RANGE_MAX = "speed_range_max"; -constexpr const char *const MQTT_SPEED_RANGE_MIN = "speed_range_min"; -constexpr const char *const MQTT_SPEED_STATE_TOPIC = "speed_state_topic"; -constexpr const char *const MQTT_SPEED_VALUE_TEMPLATE = "speed_value_template"; -constexpr const char *const MQTT_SPEEDS = "speeds"; -constexpr const char *const MQTT_STATE_CLASS = "state_class"; -constexpr const char *const MQTT_STATE_CLOSED = "state_closed"; -constexpr const char *const MQTT_STATE_CLOSING = "state_closing"; -constexpr const char *const MQTT_STATE_LOCKED = "state_locked"; -constexpr const char *const MQTT_STATE_OFF = "state_off"; -constexpr const char *const MQTT_STATE_ON = "state_on"; -constexpr const char *const MQTT_STATE_OPEN = "state_open"; -constexpr const char *const MQTT_STATE_OPENING = "state_opening"; -constexpr const char *const MQTT_STATE_STOPPED = "state_stopped"; -constexpr const char *const MQTT_STATE_TEMPLATE = "state_template"; -constexpr const char *const MQTT_STATE_TOPIC = "state_topic"; -constexpr const char *const MQTT_STATE_UNLOCKED = "state_unlocked"; -constexpr const char *const MQTT_STATE_VALUE_TEMPLATE = "state_value_template"; -constexpr const char *const MQTT_STEP = "step"; -constexpr const char *const MQTT_SUBTYPE = "subtype"; -constexpr const char *const MQTT_SUPPORTED_COLOR_MODES = "supported_color_modes"; -constexpr const char *const MQTT_SUPPORTED_FEATURES = "supported_features"; -constexpr const char *const MQTT_SWING_MODE_COMMAND_TEMPLATE = "swing_mode_command_template"; -constexpr const char *const MQTT_SWING_MODE_COMMAND_TOPIC = "swing_mode_command_topic"; -constexpr const char *const MQTT_SWING_MODE_STATE_TEMPLATE = "swing_mode_state_template"; -constexpr const char *const MQTT_SWING_MODE_STATE_TOPIC = "swing_mode_state_topic"; -constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "target_humidity_command_template"; -constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "target_humidity_command_topic"; -constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "target_humidity_state_template"; -constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "target_humidity_state_topic"; -constexpr const char *const MQTT_TARGET_TEMPERATURE_STEP = "temp_step"; -constexpr const char *const MQTT_TEMPERATURE_COMMAND_TEMPLATE = "temperature_command_template"; -constexpr const char *const MQTT_TEMPERATURE_COMMAND_TOPIC = "temperature_command_topic"; -constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE = "temperature_high_command_template"; -constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TOPIC = "temperature_high_command_topic"; -constexpr const char *const MQTT_TEMPERATURE_HIGH_STATE_TEMPLATE = "temperature_high_state_template"; -constexpr const char *const MQTT_TEMPERATURE_HIGH_STATE_TOPIC = "temperature_high_state_topic"; -constexpr const char *const MQTT_TEMPERATURE_LOW_COMMAND_TEMPLATE = "temperature_low_command_template"; -constexpr const char *const MQTT_TEMPERATURE_LOW_COMMAND_TOPIC = "temperature_low_command_topic"; -constexpr const char *const MQTT_TEMPERATURE_LOW_STATE_TEMPLATE = "temperature_low_state_template"; -constexpr const char *const MQTT_TEMPERATURE_LOW_STATE_TOPIC = "temperature_low_state_topic"; -constexpr const char *const MQTT_TEMPERATURE_STATE_TEMPLATE = "temperature_state_template"; -constexpr const char *const MQTT_TEMPERATURE_STATE_TOPIC = "temperature_state_topic"; -constexpr const char *const MQTT_TEMPERATURE_UNIT = "temperature_unit"; -constexpr const char *const MQTT_TILT_CLOSED_VALUE = "tilt_closed_value"; -constexpr const char *const MQTT_TILT_COMMAND_TEMPLATE = "tilt_command_template"; -constexpr const char *const MQTT_TILT_COMMAND_TOPIC = "tilt_command_topic"; -constexpr const char *const MQTT_TILT_INVERT_STATE = "tilt_invert_state"; -constexpr const char *const MQTT_TILT_MAX = "tilt_max"; -constexpr const char *const MQTT_TILT_MIN = "tilt_min"; -constexpr const char *const MQTT_TILT_OPENED_VALUE = "tilt_opened_value"; -constexpr const char *const MQTT_TILT_OPTIMISTIC = "tilt_optimistic"; -constexpr const char *const MQTT_TILT_STATUS_TEMPLATE = "tilt_status_template"; -constexpr const char *const MQTT_TILT_STATUS_TOPIC = "tilt_status_topic"; -constexpr const char *const MQTT_TOPIC = "topic"; -constexpr const char *const MQTT_UNIQUE_ID = "unique_id"; -constexpr const char *const MQTT_UNIT_OF_MEASUREMENT = "unit_of_measurement"; -constexpr const char *const MQTT_VALUE_TEMPLATE = "value_template"; -constexpr const char *const MQTT_WHITE_COMMAND_TOPIC = "white_command_topic"; -constexpr const char *const MQTT_WHITE_SCALE = "white_scale"; -constexpr const char *const MQTT_WHITE_VALUE_COMMAND_TOPIC = "white_value_command_topic"; -constexpr const char *const MQTT_WHITE_VALUE_SCALE = "white_value_scale"; -constexpr const char *const MQTT_WHITE_VALUE_STATE_TOPIC = "white_value_state_topic"; -constexpr const char *const MQTT_WHITE_VALUE_TEMPLATE = "white_value_template"; -constexpr const char *const MQTT_XY_COMMAND_TOPIC = "xy_command_topic"; -constexpr const char *const MQTT_XY_STATE_TOPIC = "xy_state_topic"; -constexpr const char *const MQTT_XY_VALUE_TEMPLATE = "xy_value_template"; +// Generate flash string pointers from the PROGMEM data +// NOLINTNEXTLINE(bugprone-macro-parentheses) +#define MQTT_PTR(name, abbr, full) \ + static const __FlashStringHelper *const name = reinterpret_cast(name##_data); +MQTT_KEYS_LIST(MQTT_PTR) +#undef MQTT_PTR +} // namespace esphome::mqtt +#else +// Other platforms: constexpr in namespace +namespace esphome::mqtt { +#ifdef USE_MQTT_ABBREVIATIONS +// NOLINTNEXTLINE(bugprone-macro-parentheses) +#define MQTT_CONST(name, abbr, full) constexpr const char *name = abbr; +#else +// NOLINTNEXTLINE(bugprone-macro-parentheses) +#define MQTT_CONST(name, abbr, full) constexpr const char *name = full; +#endif +MQTT_KEYS_LIST(MQTT_CONST) +#undef MQTT_CONST +} // namespace esphome::mqtt #endif -} // namespace mqtt -} // namespace esphome - #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index b63aa66d295..e628ac37a90 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_COVER -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.cover"; @@ -119,8 +118,7 @@ bool MQTTCoverComponent::publish_state() { return success; } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_cover.h b/esphome/components/mqtt/mqtt_cover.h index f3e6053d0b1..6b874af16ad 100644 --- a/esphome/components/mqtt/mqtt_cover.h +++ b/esphome/components/mqtt/mqtt_cover.h @@ -8,8 +8,7 @@ #include "esphome/components/cover/cover.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTCoverComponent : public mqtt::MQTTComponent { public: @@ -36,8 +35,7 @@ class MQTTCoverComponent : public mqtt::MQTTComponent { cover::Cover *cover_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_date.cpp b/esphome/components/mqtt/mqtt_date.cpp index 0f0a334ae7e..c5a17abdfda 100644 --- a/esphome/components/mqtt/mqtt_date.cpp +++ b/esphome/components/mqtt/mqtt_date.cpp @@ -8,8 +8,7 @@ #ifdef USE_MQTT #ifdef USE_DATETIME_DATE -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.datetime"; @@ -62,8 +61,7 @@ bool MQTTDateComponent::publish_state(uint16_t year, uint8_t month, uint8_t day) }); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_DATETIME_DATE #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_date.h b/esphome/components/mqtt/mqtt_date.h index 5147afe7e7b..380bb69e0ec 100644 --- a/esphome/components/mqtt/mqtt_date.h +++ b/esphome/components/mqtt/mqtt_date.h @@ -8,8 +8,7 @@ #include "esphome/components/datetime/date_entity.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTDateComponent : public mqtt::MQTTComponent { public: @@ -38,8 +37,7 @@ class MQTTDateComponent : public mqtt::MQTTComponent { datetime::DateEntity *date_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_DATETIME_DATE #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_datetime.cpp b/esphome/components/mqtt/mqtt_datetime.cpp index 5c56baabe01..d2feddcb007 100644 --- a/esphome/components/mqtt/mqtt_datetime.cpp +++ b/esphome/components/mqtt/mqtt_datetime.cpp @@ -8,8 +8,7 @@ #ifdef USE_MQTT #ifdef USE_DATETIME_DATETIME -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.datetime.datetime"; @@ -78,8 +77,7 @@ bool MQTTDateTimeComponent::publish_state(uint16_t year, uint8_t month, uint8_t }); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_DATETIME_DATETIME #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_datetime.h b/esphome/components/mqtt/mqtt_datetime.h index ba81c06cb36..8706bfcf754 100644 --- a/esphome/components/mqtt/mqtt_datetime.h +++ b/esphome/components/mqtt/mqtt_datetime.h @@ -8,8 +8,7 @@ #include "esphome/components/datetime/datetime_entity.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTDateTimeComponent : public mqtt::MQTTComponent { public: @@ -38,8 +37,7 @@ class MQTTDateTimeComponent : public mqtt::MQTTComponent { datetime::DateTimeEntity *datetime_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_DATETIME_DATETIME #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_event.cpp b/esphome/components/mqtt/mqtt_event.cpp index fd095ea041b..67a7aab5bd6 100644 --- a/esphome/components/mqtt/mqtt_event.cpp +++ b/esphome/components/mqtt/mqtt_event.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_EVENT -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.event"; @@ -54,8 +53,7 @@ bool MQTTEventComponent::publish_event_(const std::string &event_type) { std::string MQTTEventComponent::component_type() const { return "event"; } const EntityBase *MQTTEventComponent::get_entity() const { return this->event_; } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_event.h b/esphome/components/mqtt/mqtt_event.h index 4335820e537..fc6e778d44d 100644 --- a/esphome/components/mqtt/mqtt_event.h +++ b/esphome/components/mqtt/mqtt_event.h @@ -8,8 +8,7 @@ #include "esphome/components/event/event.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTEventComponent : public mqtt::MQTTComponent { public: @@ -32,8 +31,7 @@ class MQTTEventComponent : public mqtt::MQTTComponent { event::Event *event_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 2aefc3a4db5..ffecd9c663f 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_FAN -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.fan"; @@ -182,8 +181,7 @@ bool MQTTFanComponent::publish_state() { return !failed; } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_fan.h b/esphome/components/mqtt/mqtt_fan.h index 78641d224ff..16ce2468534 100644 --- a/esphome/components/mqtt/mqtt_fan.h +++ b/esphome/components/mqtt/mqtt_fan.h @@ -8,8 +8,7 @@ #include "esphome/components/fan/fan.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTFanComponent : public mqtt::MQTTComponent { public: @@ -47,8 +46,7 @@ class MQTTFanComponent : public mqtt::MQTTComponent { fan::Fan *state_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_light.cpp b/esphome/components/mqtt/mqtt_light.cpp index fe911bfba22..6a040e4b1ca 100644 --- a/esphome/components/mqtt/mqtt_light.cpp +++ b/esphome/components/mqtt/mqtt_light.cpp @@ -8,8 +8,7 @@ #ifdef USE_LIGHT #include "esphome/components/light/light_json_schema.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.light"; @@ -92,8 +91,7 @@ void MQTTJSONLightComponent::dump_config() { LOG_MQTT_COMPONENT(true, true) } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_light.h b/esphome/components/mqtt/mqtt_light.h index a105f3d7b88..2cc631c9013 100644 --- a/esphome/components/mqtt/mqtt_light.h +++ b/esphome/components/mqtt/mqtt_light.h @@ -8,8 +8,7 @@ #include "mqtt_component.h" #include "esphome/components/light/light_state.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTJSONLightComponent : public mqtt::MQTTComponent, public light::LightRemoteValuesListener { public: @@ -37,8 +36,7 @@ class MQTTJSONLightComponent : public mqtt::MQTTComponent, public light::LightRe light::LightState *state_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_lock.cpp b/esphome/components/mqtt/mqtt_lock.cpp index 95efbf60e1f..58fa675eb7a 100644 --- a/esphome/components/mqtt/mqtt_lock.cpp +++ b/esphome/components/mqtt/mqtt_lock.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_LOCK -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.lock"; @@ -58,8 +57,7 @@ bool MQTTLockComponent::publish_state() { #endif } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_lock.h b/esphome/components/mqtt/mqtt_lock.h index 789f74c7955..6fb4998b254 100644 --- a/esphome/components/mqtt/mqtt_lock.h +++ b/esphome/components/mqtt/mqtt_lock.h @@ -8,8 +8,7 @@ #include "esphome/components/lock/lock.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTLockComponent : public mqtt::MQTTComponent { public: @@ -34,8 +33,7 @@ class MQTTLockComponent : public mqtt::MQTTComponent { lock::Lock *lock_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index f419eac130f..381574ae565 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_NUMBER -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.number"; @@ -80,8 +79,7 @@ bool MQTTNumberComponent::publish_state(float value) { return this->publish(this->get_state_topic_(), buffer); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_number.h b/esphome/components/mqtt/mqtt_number.h index 10500c83331..b89e78a4546 100644 --- a/esphome/components/mqtt/mqtt_number.h +++ b/esphome/components/mqtt/mqtt_number.h @@ -8,8 +8,7 @@ #include "esphome/components/number/number.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTNumberComponent : public mqtt::MQTTComponent { public: @@ -39,8 +38,7 @@ class MQTTNumberComponent : public mqtt::MQTTComponent { number::Number *number_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_select.cpp b/esphome/components/mqtt/mqtt_select.cpp index e48af980c8f..5edc5c50dc6 100644 --- a/esphome/components/mqtt/mqtt_select.cpp +++ b/esphome/components/mqtt/mqtt_select.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_SELECT -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.select"; @@ -53,8 +52,7 @@ bool MQTTSelectComponent::publish_state(const std::string &value) { return this->publish(this->get_state_topic_(), value); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_select.h b/esphome/components/mqtt/mqtt_select.h index e0d8ac2417c..19aad662e5b 100644 --- a/esphome/components/mqtt/mqtt_select.h +++ b/esphome/components/mqtt/mqtt_select.h @@ -8,8 +8,7 @@ #include "esphome/components/select/select.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTSelectComponent : public mqtt::MQTTComponent { public: @@ -39,8 +38,7 @@ class MQTTSelectComponent : public mqtt::MQTTComponent { select::Select *select_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index 010ac3013ef..bd79ae40fe0 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -11,8 +11,7 @@ #include "esphome/components/deep_sleep/deep_sleep_component.h" #endif -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.sensor"; @@ -86,8 +85,7 @@ bool MQTTSensorComponent::publish_state(float value) { return this->publish(this->get_state_topic_(), value_accuracy_to_string(value, accuracy)); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_sensor.h b/esphome/components/mqtt/mqtt_sensor.h index 15ea703ad4a..8c60199e1b2 100644 --- a/esphome/components/mqtt/mqtt_sensor.h +++ b/esphome/components/mqtt/mqtt_sensor.h @@ -8,8 +8,7 @@ #include "esphome/components/sensor/sensor.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTSensorComponent : public mqtt::MQTTComponent { public: @@ -51,8 +50,7 @@ class MQTTSensorComponent : public mqtt::MQTTComponent { optional expire_after_; // Override the expire after advertised to Home Assistant }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_switch.cpp b/esphome/components/mqtt/mqtt_switch.cpp index b3a35420b92..a35ae8f9b68 100644 --- a/esphome/components/mqtt/mqtt_switch.cpp +++ b/esphome/components/mqtt/mqtt_switch.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_SWITCH -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.switch"; @@ -57,8 +56,7 @@ bool MQTTSwitchComponent::publish_state(bool state) { return this->publish(this->get_state_topic_(), state_s); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_switch.h b/esphome/components/mqtt/mqtt_switch.h index c4d3f7164cc..fb6a13f1726 100644 --- a/esphome/components/mqtt/mqtt_switch.h +++ b/esphome/components/mqtt/mqtt_switch.h @@ -8,8 +8,7 @@ #include "esphome/components/switch/switch.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTSwitchComponent : public mqtt::MQTTComponent { public: @@ -34,8 +33,7 @@ class MQTTSwitchComponent : public mqtt::MQTTComponent { switch_::Switch *switch_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_text.cpp b/esphome/components/mqtt/mqtt_text.cpp index 5ab0ca96886..3cb851fd38e 100644 --- a/esphome/components/mqtt/mqtt_text.cpp +++ b/esphome/components/mqtt/mqtt_text.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_TEXT -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.text"; @@ -57,8 +56,7 @@ bool MQTTTextComponent::publish_state(const std::string &value) { return this->publish(this->get_state_topic_(), value); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_text.h b/esphome/components/mqtt/mqtt_text.h index d9486fcbf8e..0480b89395e 100644 --- a/esphome/components/mqtt/mqtt_text.h +++ b/esphome/components/mqtt/mqtt_text.h @@ -8,8 +8,7 @@ #include "esphome/components/text/text.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTTextComponent : public mqtt::MQTTComponent { public: @@ -39,8 +38,7 @@ class MQTTTextComponent : public mqtt::MQTTComponent { text::Text *text_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_text_sensor.cpp b/esphome/components/mqtt/mqtt_text_sensor.cpp index e6e7cf04e8a..c87f22fb8e0 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.cpp +++ b/esphome/components/mqtt/mqtt_text_sensor.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_TEXT_SENSOR -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.text_sensor"; @@ -43,8 +42,7 @@ bool MQTTTextSensor::send_initial_state() { std::string MQTTTextSensor::component_type() const { return "sensor"; } const EntityBase *MQTTTextSensor::get_entity() const { return this->sensor_; } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_text_sensor.h b/esphome/components/mqtt/mqtt_text_sensor.h index 9a14efdd165..d4d38d7eb23 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.h +++ b/esphome/components/mqtt/mqtt_text_sensor.h @@ -8,8 +8,7 @@ #include "esphome/components/text_sensor/text_sensor.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTTextSensor : public mqtt::MQTTComponent { public: @@ -32,8 +31,7 @@ class MQTTTextSensor : public mqtt::MQTTComponent { text_sensor::TextSensor *sensor_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_time.cpp b/esphome/components/mqtt/mqtt_time.cpp index 0c95bd8147a..c97a463858a 100644 --- a/esphome/components/mqtt/mqtt_time.cpp +++ b/esphome/components/mqtt/mqtt_time.cpp @@ -8,8 +8,7 @@ #ifdef USE_MQTT #ifdef USE_DATETIME_TIME -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.datetime.time"; @@ -62,8 +61,7 @@ bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t seco }); } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_DATETIME_TIME #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_time.h b/esphome/components/mqtt/mqtt_time.h index b9dd822a73b..60345c37ae8 100644 --- a/esphome/components/mqtt/mqtt_time.h +++ b/esphome/components/mqtt/mqtt_time.h @@ -8,8 +8,7 @@ #include "esphome/components/datetime/time_entity.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTTimeComponent : public mqtt::MQTTComponent { public: @@ -38,8 +37,7 @@ class MQTTTimeComponent : public mqtt::MQTTComponent { datetime::TimeEntity *time_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_DATETIME_DATE #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_update.cpp b/esphome/components/mqtt/mqtt_update.cpp index 20f3a69a9e0..150ddbf745f 100644 --- a/esphome/components/mqtt/mqtt_update.cpp +++ b/esphome/components/mqtt/mqtt_update.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_UPDATE -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.update"; @@ -56,8 +55,7 @@ void MQTTUpdateComponent::dump_config() { std::string MQTTUpdateComponent::component_type() const { return "update"; } const EntityBase *MQTTUpdateComponent::get_entity() const { return this->update_; } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_UPDATE #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_update.h b/esphome/components/mqtt/mqtt_update.h index 6fe04c4ea7a..d04d22d25fd 100644 --- a/esphome/components/mqtt/mqtt_update.h +++ b/esphome/components/mqtt/mqtt_update.h @@ -8,8 +8,7 @@ #include "esphome/components/update/update_entity.h" #include "mqtt_component.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTUpdateComponent : public mqtt::MQTTComponent { public: @@ -34,8 +33,7 @@ class MQTTUpdateComponent : public mqtt::MQTTComponent { update::UpdateEntity *update_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif // USE_UPDATE #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_valve.cpp b/esphome/components/mqtt/mqtt_valve.cpp index ae606707482..8ee693121bf 100644 --- a/esphome/components/mqtt/mqtt_valve.cpp +++ b/esphome/components/mqtt/mqtt_valve.cpp @@ -6,8 +6,7 @@ #ifdef USE_MQTT #ifdef USE_VALVE -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { static const char *const TAG = "mqtt.valve"; @@ -89,8 +88,7 @@ bool MQTTValveComponent::publish_state() { return success; } -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_valve.h b/esphome/components/mqtt/mqtt_valve.h index 63a04621930..9e5221e4952 100644 --- a/esphome/components/mqtt/mqtt_valve.h +++ b/esphome/components/mqtt/mqtt_valve.h @@ -8,8 +8,7 @@ #include "esphome/components/valve/valve.h" -namespace esphome { -namespace mqtt { +namespace esphome::mqtt { class MQTTValveComponent : public mqtt::MQTTComponent { public: @@ -34,8 +33,7 @@ class MQTTValveComponent : public mqtt::MQTTComponent { valve::Valve *valve_; }; -} // namespace mqtt -} // namespace esphome +} // namespace esphome::mqtt #endif #endif // USE_MQTT From 11aed601b85d6f4e1605fa4cfc783af2e4593f41 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 07:34:38 -1000 Subject: [PATCH 11/17] [ble_scanner] Use stack-based string formatting to reduce heap allocations (#13013) --- esphome/components/ble_scanner/ble_scanner.h | 21 +++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/esphome/components/ble_scanner/ble_scanner.h b/esphome/components/ble_scanner/ble_scanner.h index 8bb51fcff2b..7061b6d3365 100644 --- a/esphome/components/ble_scanner/ble_scanner.h +++ b/esphome/components/ble_scanner/ble_scanner.h @@ -1,7 +1,8 @@ #pragma once +#include +#include #include -#include #include "esphome/core/component.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" @@ -15,17 +16,13 @@ namespace ble_scanner { class BLEScanner : public text_sensor::TextSensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { public: bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { - this->publish_state("{\"timestamp\":" + to_string(::time(nullptr)) + - "," - "\"address\":\"" + - device.address_str() + - "\"," - "\"rssi\":" + - to_string(device.get_rssi()) + - "," - "\"name\":\"" + - device.get_name() + "\"}"); - + // Format JSON using stack buffer to avoid heap allocations from string concatenation + char buf[128]; + char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; + snprintf(buf, sizeof(buf), "{\"timestamp\":%" PRId64 ",\"address\":\"%s\",\"rssi\":%d,\"name\":\"%s\"}", + static_cast(::time(nullptr)), device.address_str_to(addr_buf), device.get_rssi(), + device.get_name().c_str()); + this->publish_state(buf); return true; } void dump_config() override; From d3e193cd7102c6f48fc6c9bf270aeafb0b4df98c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 07:34:58 -1000 Subject: [PATCH 12/17] [ota] Fix ESP32-S3 OTA crash with hardware SHA acceleration on IDF 5.5.x (#13021) --- esphome/components/esphome/ota/ota_esphome.cpp | 8 ++++++-- esphome/components/sha256/sha256.cpp | 15 +++++++++------ esphome/components/sha256/sha256.h | 18 ++++++++++++++---- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index ba25c69faea..f71163f79e0 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -560,7 +560,9 @@ bool ESPHomeOTAComponent::handle_auth_send_() { // CRITICAL ESP32-S3 HARDWARE SHA ACCELERATION: Hash object must stay in same stack frame // (no passing to other functions). All hash operations must happen in this function. - sha256::SHA256 hasher; + // NOTE: On ESP32-S3 with IDF 5.5.x, the SHA256 context must be properly aligned for + // hardware SHA acceleration DMA operations. + alignas(32) sha256::SHA256 hasher; const size_t hex_size = hasher.get_size() * 2; const size_t nonce_len = hasher.get_size() / 4; @@ -634,7 +636,9 @@ bool ESPHomeOTAComponent::handle_auth_read_() { // CRITICAL ESP32-S3 HARDWARE SHA ACCELERATION: Hash object must stay in same stack frame // (no passing to other functions). All hash operations must happen in this function. - sha256::SHA256 hasher; + // NOTE: On ESP32-S3 with IDF 5.5.x, the SHA256 context must be properly aligned for + // hardware SHA acceleration DMA operations. + alignas(32) sha256::SHA256 hasher; hasher.init(); hasher.add(this->password_.c_str(), this->password_.length()); diff --git a/esphome/components/sha256/sha256.cpp b/esphome/components/sha256/sha256.cpp index 32abbd739db..48559d7c73d 100644 --- a/esphome/components/sha256/sha256.cpp +++ b/esphome/components/sha256/sha256.cpp @@ -10,23 +10,26 @@ namespace esphome::sha256 { #if defined(USE_ESP32) || defined(USE_LIBRETINY) -// CRITICAL ESP32-S3 HARDWARE SHA ACCELERATION REQUIREMENTS: +// CRITICAL ESP32-S3 HARDWARE SHA ACCELERATION REQUIREMENTS (IDF 5.5.x): // // The ESP32-S3 uses hardware DMA for SHA acceleration. The mbedtls_sha256_context structure contains -// internal state that the DMA engine references. This imposes two critical constraints: +// internal state that the DMA engine references. This imposes three critical constraints: // -// 1. NO VARIABLE LENGTH ARRAYS (VLAs): VLAs corrupt the stack layout, causing the DMA engine to +// 1. ALIGNMENT: The SHA256 object MUST be declared with `alignas(32)` for proper DMA alignment. +// Without this, the DMA engine may crash with an abort in sha_hal_read_digest(). +// +// 2. NO VARIABLE LENGTH ARRAYS (VLAs): VLAs corrupt the stack layout, causing the DMA engine to // write to incorrect memory locations. This results in null pointer dereferences and crashes. // ALWAYS use fixed-size arrays (e.g., char buf[65], not char buf[size+1]). // -// 2. SAME STACK FRAME ONLY: The SHA256 object must be created and used entirely within the same +// 3. SAME STACK FRAME ONLY: The SHA256 object must be created and used entirely within the same // function. NEVER pass the SHA256 object or HashBase pointer to another function. When the stack // frame changes (function call/return), the DMA references become invalid and will produce // truncated hash output (20 bytes instead of 32) or corrupt memory. // // CORRECT USAGE: // void my_function() { -// sha256::SHA256 hasher; // Created locally +// alignas(32) sha256::SHA256 hasher; // Created locally with proper alignment // hasher.init(); // hasher.add(data, len); // Any size, no chunking needed // hasher.calculate(); @@ -36,7 +39,7 @@ namespace esphome::sha256 { // // INCORRECT USAGE (WILL FAIL ON ESP32-S3): // void my_function() { -// sha256::SHA256 hasher; +// sha256::SHA256 hasher; // WRONG: Missing alignas(32) // helper(&hasher); // WRONG: Passed to different stack frame // } // void helper(HashBase *h) { diff --git a/esphome/components/sha256/sha256.h b/esphome/components/sha256/sha256.h index a2b62799e1b..17d80636f13 100644 --- a/esphome/components/sha256/sha256.h +++ b/esphome/components/sha256/sha256.h @@ -22,6 +22,18 @@ namespace esphome::sha256 { +/// SHA256 hash implementation. +/// +/// CRITICAL for ESP32-S3 with IDF 5.5.x hardware SHA acceleration: +/// 1. SHA256 objects MUST be declared with `alignas(32)` for proper DMA alignment +/// 2. The object MUST stay in the same stack frame (no passing to other functions) +/// 3. NO Variable Length Arrays (VLAs) in the same function +/// +/// Example usage: +/// alignas(32) sha256::SHA256 hasher; +/// hasher.init(); +/// hasher.add(data, len); +/// hasher.calculate(); class SHA256 : public esphome::HashBase { public: SHA256() = default; @@ -39,10 +51,8 @@ class SHA256 : public esphome::HashBase { protected: #if defined(USE_ESP32) || defined(USE_LIBRETINY) - // CRITICAL: The mbedtls context MUST be stack-allocated (not a pointer) for ESP32-S3 hardware SHA acceleration. - // The ESP32-S3 DMA engine references this structure's memory addresses. If the context is passed to another - // function (crossing stack frames) or if VLAs are present, the DMA operations will corrupt memory and produce - // truncated/incorrect hash results. + // The mbedtls context for ESP32-S3 hardware SHA requires proper alignment and stack frame constraints. + // See class documentation above for critical requirements. mbedtls_sha256_context ctx_{}; #elif defined(USE_ESP8266) || defined(USE_RP2040) br_sha256_context ctx_{}; From c1ad39a0724baf685de035ffa833375372fa1e98 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 07:35:16 -1000 Subject: [PATCH 13/17] [wifi] Clean up duplicate and empty logging output (#13018) --- esphome/components/wifi/wifi_component.cpp | 39 ++++++++-------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index ba25bc9f762..e1dc2d17d6f 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -317,7 +317,6 @@ void WiFiComponent::start_initial_connection_() { WiFiAP params = this->build_params_for_current_phase_(); this->start_connecting(params); } else { - ESP_LOGI(TAG, "Starting scan"); this->start_scanning(); } } @@ -369,11 +368,7 @@ void WiFiComponent::setup() { } void WiFiComponent::start() { - char mac_s[18]; - ESP_LOGCONFIG(TAG, - "Starting\n" - " Local MAC: %s", - get_mac_address_pretty_into_buffer(mac_s)); + ESP_LOGCONFIG(TAG, "Starting"); this->last_connected_ = millis(); uint32_t hash = this->has_sta() ? App.get_config_version_hash() : 88491487UL; @@ -857,14 +852,6 @@ void WiFiComponent::start_connecting(const WiFiAP &ap) { } const LogString *get_signal_bars(int8_t rssi) { - // Check for disconnected sentinel value first - if (rssi == WIFI_RSSI_DISCONNECTED) { - // MULTIPLICATION SIGN - // Unicode: U+00D7, UTF-8: C3 97 - return LOG_STR("\033[0;31m" // red - "\xc3\x97\xc3\x97\xc3\x97\xc3\x97" - "\033[0m"); - } // LOWER ONE QUARTER BLOCK // Unicode: U+2582, UTF-8: E2 96 82 // LOWER HALF BLOCK @@ -909,15 +896,8 @@ const LogString *get_signal_bars(int8_t rssi) { void WiFiComponent::print_connect_params_() { bssid_t bssid = wifi_bssid(); - char bssid_s[18]; + char bssid_s[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; format_mac_addr_upper(bssid.data(), bssid_s); - - char mac_s[18]; - ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty_into_buffer(mac_s)); - if (this->is_disabled()) { - ESP_LOGCONFIG(TAG, " Disabled"); - return; - } // Use stack buffers for IP address formatting to avoid heap allocations char ip_buf[network::IP_ADDRESS_BUFFER_SIZE]; for (auto &ip : wifi_sta_ip_addresses()) { @@ -1189,11 +1169,19 @@ void WiFiComponent::check_scanning_finished() { } void WiFiComponent::dump_config() { + char mac_s[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; ESP_LOGCONFIG(TAG, "WiFi:\n" + " Local MAC: %s\n" " Connected: %s", - YESNO(this->is_connected())); - this->print_connect_params_(); + get_mac_address_pretty_into_buffer(mac_s), YESNO(this->is_connected())); + if (this->is_disabled()) { + ESP_LOGCONFIG(TAG, " Disabled"); + return; + } + if (this->is_connected()) { + this->print_connect_params_(); + } } void WiFiComponent::check_connecting_finished() { @@ -1223,8 +1211,6 @@ void WiFiComponent::check_connecting_finished() { // the first connection as a failure. this->error_from_callback_ = false; - this->print_connect_params_(); - if (this->has_ap()) { #ifdef USE_CAPTIVE_PORTAL if (this->is_captive_portal_active_()) { @@ -1242,6 +1228,7 @@ void WiFiComponent::check_connecting_finished() { this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTED; this->num_retried_ = 0; + this->print_connect_params_(); // Clear priority tracking if all priorities are at minimum this->clear_priorities_if_all_min_(); From 2c6584baf50a65b26e6d1b072810bf7a4f5ad2bb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 07:35:40 -1000 Subject: [PATCH 14/17] [xiaomi_ble] Simplify set_bindkey using parse_hex and const char* (#13014) --- esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp | 12 +----------- esphome/components/xiaomi_cgd1/xiaomi_cgd1.h | 2 +- esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp | 12 +----------- esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h | 2 +- esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp | 12 +----------- esphome/components/xiaomi_cgg1/xiaomi_cgg1.h | 2 +- esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp | 13 ++----------- esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h | 2 +- .../xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp | 12 +----------- .../xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h | 2 +- .../xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp | 12 +----------- .../xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h | 2 +- .../components/xiaomi_mhoc401/xiaomi_mhoc401.cpp | 12 +----------- esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h | 2 +- .../xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp | 13 ++----------- .../components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h | 2 +- .../xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.cpp | 12 +----------- .../components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h | 2 +- .../xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp | 12 +----------- .../xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h | 2 +- 20 files changed, 22 insertions(+), 120 deletions(-) diff --git a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp index 1aa542633ac..82a04f0d6e6 100644 --- a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp +++ b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp @@ -63,17 +63,7 @@ bool XiaomiCGD1::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { return success; } -void XiaomiCGD1::set_bindkey(const std::string &bindkey) { - memset(bindkey_, 0, 16); - if (bindkey.size() != 32) { - return; - } - char temp[3] = {0}; - for (int i = 0; i < 16; i++) { - strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, nullptr, 16); - } -} +void XiaomiCGD1::set_bindkey(const char *bindkey) { parse_hex(bindkey, this->bindkey_, sizeof(this->bindkey_)); } } // namespace xiaomi_cgd1 } // namespace esphome diff --git a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h index 393795439b8..4a34eea32ad 100644 --- a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h +++ b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h @@ -13,7 +13,7 @@ namespace xiaomi_cgd1 { class XiaomiCGD1 : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { address_ = address; }; - void set_bindkey(const std::string &bindkey); + void set_bindkey(const char *bindkey); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; void dump_config() override; diff --git a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp index a0498549356..39ece3e0916 100644 --- a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp +++ b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp @@ -63,17 +63,7 @@ bool XiaomiCGDK2::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { return success; } -void XiaomiCGDK2::set_bindkey(const std::string &bindkey) { - memset(bindkey_, 0, 16); - if (bindkey.size() != 32) { - return; - } - char temp[3] = {0}; - for (int i = 0; i < 16; i++) { - strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, nullptr, 16); - } -} +void XiaomiCGDK2::set_bindkey(const char *bindkey) { parse_hex(bindkey, this->bindkey_, sizeof(this->bindkey_)); } } // namespace xiaomi_cgdk2 } // namespace esphome diff --git a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h index 1f5ef898693..ed917e2bbd8 100644 --- a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h +++ b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h @@ -13,7 +13,7 @@ namespace xiaomi_cgdk2 { class XiaomiCGDK2 : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { address_ = address; }; - void set_bindkey(const std::string &bindkey); + void set_bindkey(const char *bindkey); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; void dump_config() override; diff --git a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp index da4bab66234..448592db16f 100644 --- a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp +++ b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp @@ -63,17 +63,7 @@ bool XiaomiCGG1::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { return success; } -void XiaomiCGG1::set_bindkey(const std::string &bindkey) { - memset(bindkey_, 0, 16); - if (bindkey.size() != 32) { - return; - } - char temp[3] = {0}; - for (int i = 0; i < 16; i++) { - strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, nullptr, 16); - } -} +void XiaomiCGG1::set_bindkey(const char *bindkey) { parse_hex(bindkey, this->bindkey_, sizeof(this->bindkey_)); } } // namespace xiaomi_cgg1 } // namespace esphome diff --git a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h index 52904fd75ed..c560bddd695 100644 --- a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h +++ b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h @@ -13,7 +13,7 @@ namespace xiaomi_cgg1 { class XiaomiCGG1 : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { address_ = address; } - void set_bindkey(const std::string &bindkey); + void set_bindkey(const char *bindkey); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; diff --git a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp index 2048c786d35..8813f6479b7 100644 --- a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp +++ b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp @@ -1,4 +1,5 @@ #include "xiaomi_cgpr1.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #ifdef USE_ESP32 @@ -59,17 +60,7 @@ bool XiaomiCGPR1::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { return success; } -void XiaomiCGPR1::set_bindkey(const std::string &bindkey) { - memset(bindkey_, 0, 16); - if (bindkey.size() != 32) { - return; - } - char temp[3] = {0}; - for (int i = 0; i < 16; i++) { - strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, nullptr, 16); - } -} +void XiaomiCGPR1::set_bindkey(const char *bindkey) { parse_hex(bindkey, this->bindkey_, sizeof(this->bindkey_)); } } // namespace xiaomi_cgpr1 } // namespace esphome diff --git a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h index 124f9411a19..82bbbfa58d9 100644 --- a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h +++ b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h @@ -16,7 +16,7 @@ class XiaomiCGPR1 : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { address_ = address; } - void set_bindkey(const std::string &bindkey); + void set_bindkey(const char *bindkey); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; diff --git a/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp index edd9f67f567..2dd60d4ecbc 100644 --- a/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp +++ b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp @@ -63,17 +63,7 @@ bool XiaomiLYWSD02MMC::parse_device(const esp32_ble_tracker::ESPBTDevice &device return success; } -void XiaomiLYWSD02MMC::set_bindkey(const std::string &bindkey) { - memset(this->bindkey_, 0, 16); - if (bindkey.size() != 32) { - return; - } - char temp[3] = {0}; - for (int i = 0; i < 16; i++) { - strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - this->bindkey_[i] = std::strtoul(temp, nullptr, 16); - } -} +void XiaomiLYWSD02MMC::set_bindkey(const char *bindkey) { parse_hex(bindkey, this->bindkey_, sizeof(this->bindkey_)); } } // namespace xiaomi_lywsd02mmc } // namespace esphome diff --git a/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h index e1e0fcae402..968604fee69 100644 --- a/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h +++ b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h @@ -13,7 +13,7 @@ namespace xiaomi_lywsd02mmc { class XiaomiLYWSD02MMC : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { this->address_ = address; } - void set_bindkey(const std::string &bindkey); + void set_bindkey(const char *bindkey); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; diff --git a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp index 2b4b67c92f0..b11bbdc40cd 100644 --- a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp +++ b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp @@ -67,17 +67,7 @@ bool XiaomiLYWSD03MMC::parse_device(const esp32_ble_tracker::ESPBTDevice &device return success; } -void XiaomiLYWSD03MMC::set_bindkey(const std::string &bindkey) { - memset(bindkey_, 0, 16); - if (bindkey.size() != 32) { - return; - } - char temp[3] = {0}; - for (int i = 0; i < 16; i++) { - strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, nullptr, 16); - } -} +void XiaomiLYWSD03MMC::set_bindkey(const char *bindkey) { parse_hex(bindkey, this->bindkey_, sizeof(this->bindkey_)); } } // namespace xiaomi_lywsd03mmc } // namespace esphome diff --git a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h index 3c7907479ac..d890e5ed120 100644 --- a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h +++ b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h @@ -13,7 +13,7 @@ namespace xiaomi_lywsd03mmc { class XiaomiLYWSD03MMC : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { address_ = address; }; - void set_bindkey(const std::string &bindkey); + void set_bindkey(const char *bindkey); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; void dump_config() override; diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp index e1b808c54ea..10cd15ddbdd 100644 --- a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp @@ -67,17 +67,7 @@ bool XiaomiMHOC401::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { return success; } -void XiaomiMHOC401::set_bindkey(const std::string &bindkey) { - memset(bindkey_, 0, 16); - if (bindkey.size() != 32) { - return; - } - char temp[3] = {0}; - for (int i = 0; i < 16; i++) { - strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, nullptr, 16); - } -} +void XiaomiMHOC401::set_bindkey(const char *bindkey) { parse_hex(bindkey, this->bindkey_, sizeof(this->bindkey_)); } } // namespace xiaomi_mhoc401 } // namespace esphome diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h index 1acdaa88afa..13547e45d9f 100644 --- a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h @@ -13,7 +13,7 @@ namespace xiaomi_mhoc401 { class XiaomiMHOC401 : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { address_ = address; }; - void set_bindkey(const std::string &bindkey); + void set_bindkey(const char *bindkey); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; void dump_config() override; diff --git a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp index eb4862a7e92..ec03c851cd4 100644 --- a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp +++ b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp @@ -1,4 +1,5 @@ #include "xiaomi_mjyd02yla.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #ifdef USE_ESP32 @@ -62,17 +63,7 @@ bool XiaomiMJYD02YLA::parse_device(const esp32_ble_tracker::ESPBTDevice &device) return success; } -void XiaomiMJYD02YLA::set_bindkey(const std::string &bindkey) { - memset(bindkey_, 0, 16); - if (bindkey.size() != 32) { - return; - } - char temp[3] = {0}; - for (int i = 0; i < 16; i++) { - strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, nullptr, 16); - } -} +void XiaomiMJYD02YLA::set_bindkey(const char *bindkey) { parse_hex(bindkey, this->bindkey_, sizeof(this->bindkey_)); } } // namespace xiaomi_mjyd02yla } // namespace esphome diff --git a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h index e1b4055696e..bf9dcaf8446 100644 --- a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h +++ b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h @@ -16,7 +16,7 @@ class XiaomiMJYD02YLA : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { address_ = address; } - void set_bindkey(const std::string &bindkey); + void set_bindkey(const char *bindkey); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; diff --git a/esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.cpp b/esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.cpp index d5b89507fe7..ee3ad316e1b 100644 --- a/esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.cpp +++ b/esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.cpp @@ -79,17 +79,7 @@ bool XiaomiRTCGQ02LM::parse_device(const esp32_ble_tracker::ESPBTDevice &device) return success; } -void XiaomiRTCGQ02LM::set_bindkey(const std::string &bindkey) { - memset(bindkey_, 0, 16); - if (bindkey.size() != 32) { - return; - } - char temp[3] = {0}; - for (int i = 0; i < 16; i++) { - strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, nullptr, 16); - } -} +void XiaomiRTCGQ02LM::set_bindkey(const char *bindkey) { parse_hex(bindkey, this->bindkey_, sizeof(this->bindkey_)); } } // namespace xiaomi_rtcgq02lm } // namespace esphome diff --git a/esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h b/esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h index ae00a28ac90..87dfc0b62bc 100644 --- a/esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h +++ b/esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h @@ -19,7 +19,7 @@ namespace xiaomi_rtcgq02lm { class XiaomiRTCGQ02LM : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { address_ = address; }; - void set_bindkey(const std::string &bindkey); + void set_bindkey(const char *bindkey); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; void dump_config() override; diff --git a/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp b/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp index f126e8bdfd9..50cf5f2d767 100644 --- a/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp +++ b/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp @@ -67,17 +67,7 @@ bool XiaomiXMWSDJ04MMC::parse_device(const esp32_ble_tracker::ESPBTDevice &devic return success; } -void XiaomiXMWSDJ04MMC::set_bindkey(const std::string &bindkey) { - memset(this->bindkey_, 0, 16); - if (bindkey.size() != 32) { - return; - } - char temp[3] = {0}; - for (int i = 0; i < 16; i++) { - strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - this->bindkey_[i] = std::strtoul(temp, nullptr, 16); - } -} +void XiaomiXMWSDJ04MMC::set_bindkey(const char *bindkey) { parse_hex(bindkey, this->bindkey_, sizeof(this->bindkey_)); } } // namespace xiaomi_xmwsdj04mmc } // namespace esphome diff --git a/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h b/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h index ed0458ce490..22cac630595 100644 --- a/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h +++ b/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h @@ -13,7 +13,7 @@ namespace xiaomi_xmwsdj04mmc { class XiaomiXMWSDJ04MMC : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { this->address_ = address; } - void set_bindkey(const std::string &bindkey); + void set_bindkey(const char *bindkey); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; From ac42102320a722fe144bfa57ca6dc4d050dd2e80 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 07:36:01 -1000 Subject: [PATCH 15/17] [core] Auto-replace / in entity names with Unicode fraction slash during deprecation period (#13016) --- esphome/config_validation.py | 21 ++++++++++++--- tests/unit_tests/test_config_validation.py | 31 +++++++++++++++++----- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index b0da88c50d6..81a30cb0b78 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1981,16 +1981,31 @@ MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend( ) +# Unicode FRACTION SLASH (U+2044) - visually similar to '/' but URL-safe +FRACTION_SLASH = "\u2044" + + def _validate_no_slash(value): """Validate that a name does not contain '/' characters. The '/' character is used as a path separator in web server URLs, so it cannot be used in entity or device names. + + During the deprecation period, '/' is automatically replaced with + the visually similar Unicode FRACTION SLASH (U+2044) character. """ if "/" in value: - raise Invalid( - f"Name cannot contain '/' character (used as URL path separator): {value}" + # Remove before 2026.7.0 + new_value = value.replace("/", FRACTION_SLASH) + _LOGGER.warning( + "'%s' contains '/' which is reserved as a URL path separator. " + "Automatically replacing with '%s' (Unicode FRACTION SLASH). " + "Please update your configuration. " + "This will become an error in ESPHome 2026.7.0.", + value, + new_value, ) + return new_value return value @@ -2019,7 +2034,7 @@ def _validate_entity_name(value): f"Maximum length is {NAME_MAX_LENGTH} characters." ) # Validate no '/' in name for web server URL compatibility - _validate_no_slash(value) + value = _validate_no_slash(value) return value diff --git a/tests/unit_tests/test_config_validation.py b/tests/unit_tests/test_config_validation.py index 94224f23645..9602010ad30 100644 --- a/tests/unit_tests/test_config_validation.py +++ b/tests/unit_tests/test_config_validation.py @@ -510,10 +510,23 @@ def test_string_no_slash__valid(value: str) -> None: assert actual == value -@pytest.mark.parametrize("value", ("has/slash", "a/b/c", "/leading", "trailing/")) -def test_string_no_slash__slash_rejected(value: str) -> None: - with pytest.raises(Invalid, match="cannot contain '/' character"): - config_validation.string_no_slash(value) +@pytest.mark.parametrize( + ("value", "expected"), + ( + ("has/slash", "has⁄slash"), + ("a/b/c", "a⁄b⁄c"), + ("/leading", "⁄leading"), + ("trailing/", "trailing⁄"), + ), +) +def test_string_no_slash__slash_replaced_with_warning( + value: str, expected: str, caplog: pytest.LogCaptureFixture +) -> None: + """Test that '/' is auto-replaced with fraction slash and warning is logged.""" + actual = config_validation.string_no_slash(value) + assert actual == expected + assert "reserved as a URL path separator" in caplog.text + assert "will become an error in ESPHome 2026.7.0" in caplog.text def test_string_no_slash__long_string_allowed() -> None: @@ -532,9 +545,13 @@ def test_validate_entity_name__valid(value: str) -> None: assert actual == value -def test_validate_entity_name__slash_rejected() -> None: - with pytest.raises(Invalid, match="cannot contain '/' character"): - config_validation._validate_entity_name("has/slash") +def test_validate_entity_name__slash_replaced_with_warning( + caplog: pytest.LogCaptureFixture, +) -> None: + """Test that '/' in entity names is auto-replaced with fraction slash.""" + actual = config_validation._validate_entity_name("has/slash") + assert actual == "has⁄slash" + assert "reserved as a URL path separator" in caplog.text def test_validate_entity_name__max_length() -> None: From c4d3a56cc955d050862c88d1787a5e8d491c1e13 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 08:13:35 -1000 Subject: [PATCH 16/17] [api] Coalesce log packets to reduce buffer pressure and prevent dropped state updates --- esphome/components/api/api_connection.cpp | 9 ++++++++- esphome/components/api/api_frame_helper.h | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 27344a53ec9..b624cace2c4 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1820,10 +1820,17 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { return false; } bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) { - if (!this->try_to_clear_buffer(message_type != SubscribeLogsResponse::MESSAGE_TYPE)) { // SubscribeLogsResponse + const bool is_log_message = (message_type == SubscribeLogsResponse::MESSAGE_TYPE); + + if (!this->try_to_clear_buffer(!is_log_message)) { return false; } + // Toggle NODELAY based on message type: + // - Log messages: Enable Nagle (NODELAY=false) so they coalesce into fewer packets + // - All other messages: Disable Nagle (NODELAY=true) for immediate delivery + this->helper_->set_nodelay(!is_log_message); + APIError err = this->helper_->write_protobuf_packet(message_type, buffer); if (err == APIError::WOULD_BLOCK) return false; diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 383e763e6dc..6fdda64c3eb 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -111,6 +111,19 @@ class APIFrameHelper { } return APIError::OK; } + /// Set TCP_NODELAY option. Only calls setsockopt when state changes. + /// @param enable true to enable NODELAY (disable Nagle), false to enable Nagle + /// @return true if successful or already in desired state + bool set_nodelay(bool enable) { + if (this->nodelay_enabled_ == enable) + return true; + int val = enable ? 1 : 0; + int err = this->socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int)); + if (err == 0) { + this->nodelay_enabled_ = enable; + } + return err == 0; + } virtual APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) = 0; // Write multiple protobuf messages in a single operation // messages contains (message_type, offset, length) for each message in the buffer @@ -198,7 +211,7 @@ class APIFrameHelper { uint8_t tx_buf_head_{0}; uint8_t tx_buf_tail_{0}; uint8_t tx_buf_count_{0}; - // 8 bytes total, 0 bytes padding + bool nodelay_enabled_{true}; // Tracks current TCP_NODELAY state // Common initialization for both plaintext and noise protocols APIError init_common_(); From 195b606259f5fc226b44d240c35b1ad00ceab396 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Jan 2026 08:30:13 -1000 Subject: [PATCH 17/17] explain --- esphome/components/api/api_connection.cpp | 15 ++++++++++++--- esphome/components/api/api_frame_helper.h | 15 +++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index b624cace2c4..12766ffe658 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1826,9 +1826,18 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) { return false; } - // Toggle NODELAY based on message type: - // - Log messages: Enable Nagle (NODELAY=false) so they coalesce into fewer packets - // - All other messages: Disable Nagle (NODELAY=true) for immediate delivery + // Toggle Nagle's algorithm based on message type to prevent log messages from + // filling the TCP send buffer and crowding out important state updates. + // + // - Log messages: Enable Nagle (NODELAY=false) so small log packets coalesce + // into fewer, larger packets. They flush naturally via TCP delayed ACK timer + // (~200ms), buffer filling, or when a state update triggers a flush. + // + // - All other messages (state updates, responses): Disable Nagle (NODELAY=true) + // for immediate delivery. These are time-sensitive and should not be delayed. + // + // This must be done proactively BEFORE the buffer fills up - checking buffer + // state here would be too late since we'd already be in a degraded state. this->helper_->set_nodelay(!is_log_message); APIError err = this->helper_->write_protobuf_packet(message_type, buffer); diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 6fdda64c3eb..1c4c045a866 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -111,7 +111,15 @@ class APIFrameHelper { } return APIError::OK; } - /// Set TCP_NODELAY option. Only calls setsockopt when state changes. + /// Toggle TCP_NODELAY socket option to control Nagle's algorithm. + /// + /// This is used to allow log messages to coalesce (Nagle enabled) while keeping + /// state updates low-latency (NODELAY enabled). Without this, many small log + /// packets fill the TCP send buffer, crowding out important state updates. + /// + /// State is tracked to minimize setsockopt() overhead - on lwip_raw (ESP8266/RP2040) + /// this is just a boolean assignment; on other platforms it's a lightweight syscall. + /// /// @param enable true to enable NODELAY (disable Nagle), false to enable Nagle /// @return true if successful or already in desired state bool set_nodelay(bool enable) { @@ -211,7 +219,10 @@ class APIFrameHelper { uint8_t tx_buf_head_{0}; uint8_t tx_buf_tail_{0}; uint8_t tx_buf_count_{0}; - bool nodelay_enabled_{true}; // Tracks current TCP_NODELAY state + // Tracks TCP_NODELAY state to minimize setsockopt() calls. Initialized to true + // since init_common_() enables NODELAY. Used by set_nodelay() to allow log + // messages to coalesce while keeping state updates low-latency. + bool nodelay_enabled_{true}; // Common initialization for both plaintext and noise protocols APIError init_common_();