esphome 2025.6.0b1__py3-none-any.whl → 2025.6.0b2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. esphome/components/api/api_connection.cpp +53 -33
  2. esphome/components/api/api_connection.h +24 -25
  3. esphome/components/api/api_pb2.cpp +1 -0
  4. esphome/components/api/api_pb2.h +65 -226
  5. esphome/components/api/client.py +1 -3
  6. esphome/components/api/proto.h +1 -1
  7. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -2
  8. esphome/components/bluetooth_proxy/bluetooth_proxy.h +1 -1
  9. esphome/components/bme280_base/bme280_base.cpp +2 -3
  10. esphome/components/datetime/date_entity.cpp +5 -5
  11. esphome/components/datetime/datetime_base.h +0 -5
  12. esphome/components/datetime/datetime_entity.cpp +8 -8
  13. esphome/components/datetime/time_entity.cpp +4 -4
  14. esphome/components/esp32/__init__.py +30 -3
  15. esphome/components/esp32_ble/ble.cpp +90 -45
  16. esphome/components/esp32_ble/ble.h +24 -5
  17. esphome/components/esp32_ble/ble_event.h +172 -32
  18. esphome/components/esp32_ble/ble_scan_result.h +24 -0
  19. esphome/components/esp32_ble/queue.h +53 -27
  20. esphome/components/esp32_ble_tracker/__init__.py +1 -0
  21. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +95 -66
  22. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +16 -16
  23. esphome/components/esp32_camera/esp32_camera.cpp +1 -1
  24. esphome/components/fan/fan.cpp +26 -17
  25. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +12 -9
  26. esphome/components/kmeteriso/kmeteriso.cpp +2 -3
  27. esphome/components/logger/logger.cpp +2 -15
  28. esphome/components/logger/logger.h +1 -2
  29. esphome/components/mqtt/mqtt_component.cpp +1 -1
  30. esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +1 -1
  31. esphome/components/nextion/nextion_upload_arduino.cpp +12 -9
  32. esphome/components/nextion/nextion_upload_idf.cpp +11 -9
  33. esphome/components/nextion/sensor/nextion_sensor.cpp +1 -1
  34. esphome/components/nextion/text_sensor/nextion_textsensor.cpp +1 -1
  35. esphome/components/number/number.cpp +1 -1
  36. esphome/components/number/number.h +0 -4
  37. esphome/components/prometheus/__init__.py +0 -1
  38. esphome/components/select/select.cpp +1 -1
  39. esphome/components/select/select.h +0 -4
  40. esphome/components/sensor/sensor.cpp +8 -4
  41. esphome/components/sensor/sensor.h +3 -6
  42. esphome/components/status_led/light/status_led_light.cpp +2 -2
  43. esphome/components/status_led/light/status_led_light.h +1 -1
  44. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +14 -9
  45. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +1 -0
  46. esphome/components/text/text.cpp +1 -1
  47. esphome/components/text/text.h +0 -4
  48. esphome/components/text_sensor/text_sensor.cpp +8 -4
  49. esphome/components/text_sensor/text_sensor.h +6 -6
  50. esphome/components/update/update_entity.cpp +1 -1
  51. esphome/components/update/update_entity.h +0 -3
  52. esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp +1 -1
  53. esphome/components/web_server_idf/__init__.py +0 -2
  54. esphome/components/web_server_idf/web_server_idf.cpp +6 -2
  55. esphome/components/web_server_idf/web_server_idf.h +7 -0
  56. esphome/components/weikai/weikai.cpp +1 -1
  57. esphome/const.py +1 -1
  58. esphome/core/application.cpp +14 -8
  59. esphome/core/application.h +7 -7
  60. esphome/core/component.cpp +36 -15
  61. esphome/core/component.h +30 -13
  62. esphome/core/entity_base.cpp +4 -16
  63. esphome/core/entity_base.h +27 -13
  64. esphome/core/helpers.h +1 -1
  65. esphome/wizard.py +0 -16
  66. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b2.dist-info}/METADATA +2 -2
  67. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b2.dist-info}/RECORD +71 -70
  68. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b2.dist-info}/WHEEL +0 -0
  69. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b2.dist-info}/entry_points.txt +0 -0
  70. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b2.dist-info}/licenses/LICENSE +0 -0
  71. {esphome-2025.6.0b1.dist-info → esphome-2025.6.0b2.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@
6
6
  #include "esphome/core/helpers.h"
7
7
 
8
8
  #include <array>
9
+ #include <atomic>
9
10
  #include <string>
10
11
  #include <vector>
11
12
 
@@ -62,7 +63,7 @@ class ESPBLEiBeacon {
62
63
 
63
64
  class ESPBTDevice {
64
65
  public:
65
- void parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param);
66
+ void parse_scan_rst(const BLEScanResult &scan_result);
66
67
 
67
68
  std::string address_str() const;
68
69
 
@@ -84,8 +85,6 @@ class ESPBTDevice {
84
85
 
85
86
  const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
86
87
 
87
- const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &get_scan_result() const { return scan_result_; }
88
-
89
88
  bool resolve_irk(const uint8_t *irk) const;
90
89
 
91
90
  optional<ESPBLEiBeacon> get_ibeacon() const {
@@ -98,7 +97,7 @@ class ESPBTDevice {
98
97
  }
99
98
 
100
99
  protected:
101
- void parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param);
100
+ void parse_adv_(const uint8_t *payload, uint8_t len);
102
101
 
103
102
  esp_bd_addr_t address_{
104
103
  0,
@@ -112,7 +111,6 @@ class ESPBTDevice {
112
111
  std::vector<ESPBTUUID> service_uuids_{};
113
112
  std::vector<ServiceData> manufacturer_datas_{};
114
113
  std::vector<ServiceData> service_datas_{};
115
- esp_ble_gap_cb_param_t::ble_scan_result_evt_param scan_result_{};
116
114
  };
117
115
 
118
116
  class ESP32BLETracker;
@@ -121,9 +119,7 @@ class ESPBTDeviceListener {
121
119
  public:
122
120
  virtual void on_scan_end() {}
123
121
  virtual bool parse_device(const ESPBTDevice &device) = 0;
124
- virtual bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
125
- return false;
126
- };
122
+ virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; };
127
123
  virtual AdvertisementParserType get_advertisement_parser_type() {
128
124
  return AdvertisementParserType::PARSED_ADVERTISEMENTS;
129
125
  };
@@ -210,6 +206,7 @@ class ESPBTClient : public ESPBTDeviceListener {
210
206
 
211
207
  class ESP32BLETracker : public Component,
212
208
  public GAPEventHandler,
209
+ public GAPScanEventHandler,
213
210
  public GATTcEventHandler,
214
211
  public BLEStatusEventHandler,
215
212
  public Parented<ESP32BLE> {
@@ -240,6 +237,7 @@ class ESP32BLETracker : public Component,
240
237
  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
241
238
  esp_ble_gattc_cb_param_t *param) override;
242
239
  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
240
+ void gap_scan_event_handler(const BLEScanResult &scan_result) override;
243
241
  void ble_before_disabled_event_handler() override;
244
242
 
245
243
  void add_scanner_state_callback(std::function<void(ScannerState)> &&callback) {
@@ -285,14 +283,16 @@ class ESP32BLETracker : public Component,
285
283
  bool ble_was_disabled_{true};
286
284
  bool raw_advertisements_{false};
287
285
  bool parse_advertisements_{false};
288
- SemaphoreHandle_t scan_result_lock_;
289
- size_t scan_result_index_{0};
290
- #ifdef USE_PSRAM
291
- const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32;
292
- #else
293
- const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 20;
294
- #endif // USE_PSRAM
295
- esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_;
286
+
287
+ // Lock-free Single-Producer Single-Consumer (SPSC) ring buffer for scan results
288
+ // Producer: ESP-IDF Bluetooth stack callback (gap_scan_event_handler)
289
+ // Consumer: ESPHome main loop (loop() method)
290
+ // This design ensures zero blocking in the BT callback and prevents scan result loss
291
+ BLEScanResult *scan_ring_buffer_;
292
+ std::atomic<size_t> ring_write_index_{0}; // Written only by BT callback (producer)
293
+ std::atomic<size_t> ring_read_index_{0}; // Written only by main loop (consumer)
294
+ std::atomic<size_t> scan_results_dropped_{0}; // Tracks buffer overflow events
295
+
296
296
  esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
297
297
  esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS};
298
298
  int connecting_{0};
@@ -57,7 +57,7 @@ void ESP32Camera::dump_config() {
57
57
  " External Clock: Pin:%d Frequency:%u\n"
58
58
  " I2C Pins: SDA:%d SCL:%d\n"
59
59
  " Reset Pin: %d",
60
- this->name_.c_str(), YESNO(this->internal_), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3,
60
+ this->name_.c_str(), YESNO(this->is_internal()), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3,
61
61
  conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7, conf.pin_vsync, conf.pin_href, conf.pin_pclk,
62
62
  conf.pin_xclk, conf.xclk_freq_hz, conf.pin_sccb_sda, conf.pin_sccb_scl, conf.pin_reset);
63
63
  switch (this->config_.frame_size) {
@@ -41,39 +41,48 @@ void FanCall::perform() {
41
41
  void FanCall::validate_() {
42
42
  auto traits = this->parent_.get_traits();
43
43
 
44
- if (this->speed_.has_value())
44
+ if (this->speed_.has_value()) {
45
45
  this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count());
46
46
 
47
- if (this->binary_state_.has_value() && *this->binary_state_) {
48
- // when turning on, if neither current nor new speed available, set speed to 100%
49
- if (traits.supports_speed() && !this->parent_.state && this->parent_.speed == 0 && !this->speed_.has_value()) {
50
- this->speed_ = traits.supported_speed_count();
47
+ // https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes
48
+ // "Manually setting a speed must disable any set preset mode"
49
+ this->preset_mode_.clear();
50
+ }
51
+
52
+ if (!this->preset_mode_.empty()) {
53
+ const auto &preset_modes = traits.supported_preset_modes();
54
+ if (preset_modes.find(this->preset_mode_) == preset_modes.end()) {
55
+ ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), this->preset_mode_.c_str());
56
+ this->preset_mode_.clear();
51
57
  }
52
58
  }
53
59
 
60
+ // when turning on...
61
+ if (!this->parent_.state && this->binary_state_.has_value() &&
62
+ *this->binary_state_
63
+ // ..,and no preset mode will be active...
64
+ && this->preset_mode_.empty() &&
65
+ this->parent_.preset_mode.empty()
66
+ // ...and neither current nor new speed is available...
67
+ && traits.supports_speed() && this->parent_.speed == 0 && !this->speed_.has_value()) {
68
+ // ...set speed to 100%
69
+ this->speed_ = traits.supported_speed_count();
70
+ }
71
+
54
72
  if (this->oscillating_.has_value() && !traits.supports_oscillation()) {
55
- ESP_LOGW(TAG, "'%s' - This fan does not support oscillation!", this->parent_.get_name().c_str());
73
+ ESP_LOGW(TAG, "%s: Oscillation not supported", this->parent_.get_name().c_str());
56
74
  this->oscillating_.reset();
57
75
  }
58
76
 
59
77
  if (this->speed_.has_value() && !traits.supports_speed()) {
60
- ESP_LOGW(TAG, "'%s' - This fan does not support speeds!", this->parent_.get_name().c_str());
78
+ ESP_LOGW(TAG, "%s: Speed control not supported", this->parent_.get_name().c_str());
61
79
  this->speed_.reset();
62
80
  }
63
81
 
64
82
  if (this->direction_.has_value() && !traits.supports_direction()) {
65
- ESP_LOGW(TAG, "'%s' - This fan does not support directions!", this->parent_.get_name().c_str());
83
+ ESP_LOGW(TAG, "%s: Direction control not supported", this->parent_.get_name().c_str());
66
84
  this->direction_.reset();
67
85
  }
68
-
69
- if (!this->preset_mode_.empty()) {
70
- const auto &preset_modes = traits.supported_preset_modes();
71
- if (preset_modes.find(this->preset_mode_) == preset_modes.end()) {
72
- ESP_LOGW(TAG, "'%s' - This fan does not support preset mode '%s'!", this->parent_.get_name().c_str(),
73
- this->preset_mode_.c_str());
74
- this->preset_mode_.clear();
75
- }
76
- }
77
86
  }
78
87
 
79
88
  FanCall FanRestoreState::to_call(Fan &fan) {
@@ -317,15 +317,18 @@ void I2SAudioMicrophone::stop_driver_() {
317
317
  ESP_LOGW(TAG, "Error uninstalling I2S driver - it may not have started: %s", esp_err_to_name(err));
318
318
  }
319
319
  #else
320
- /* Have to stop the channel before deleting it */
321
- err = i2s_channel_disable(this->rx_handle_);
322
- if (err != ESP_OK) {
323
- ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err));
324
- }
325
- /* If the handle is not needed any more, delete it to release the channel resources */
326
- err = i2s_del_channel(this->rx_handle_);
327
- if (err != ESP_OK) {
328
- ESP_LOGW(TAG, "Error deleting I2S channel - it may not have started: %s", esp_err_to_name(err));
320
+ if (this->rx_handle_ != nullptr) {
321
+ /* Have to stop the channel before deleting it */
322
+ err = i2s_channel_disable(this->rx_handle_);
323
+ if (err != ESP_OK) {
324
+ ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err));
325
+ }
326
+ /* If the handle is not needed any more, delete it to release the channel resources */
327
+ err = i2s_del_channel(this->rx_handle_);
328
+ if (err != ESP_OK) {
329
+ ESP_LOGW(TAG, "Error deleting I2S channel - it may not have started: %s", esp_err_to_name(err));
330
+ }
331
+ this->rx_handle_ = nullptr;
329
332
  }
330
333
  #endif
331
334
  this->parent_->unlock();
@@ -19,9 +19,8 @@ void KMeterISOComponent::setup() {
19
19
 
20
20
  // Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
21
21
  // and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
22
- if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
23
- this->component_state_ &= ~COMPONENT_STATE_MASK;
24
- this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
22
+ if (this->is_failed()) {
23
+ this->reset_to_construction_state();
25
24
  }
26
25
 
27
26
  auto err = this->bus_->writev(this->address_, nullptr, 0);
@@ -116,7 +116,7 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr
116
116
  if (this->baud_rate_ > 0) {
117
117
  this->write_msg_(this->tx_buffer_ + msg_start);
118
118
  }
119
- this->call_log_callbacks_(level, tag, this->tx_buffer_ + msg_start);
119
+ this->log_callback_.call(level, tag, this->tx_buffer_ + msg_start);
120
120
 
121
121
  global_recursion_guard_ = false;
122
122
  }
@@ -129,19 +129,6 @@ inline int Logger::level_for(const char *tag) {
129
129
  return this->current_level_;
130
130
  }
131
131
 
132
- void HOT Logger::call_log_callbacks_(int level, const char *tag, const char *msg) {
133
- #ifdef USE_ESP32
134
- // Suppress network-logging if memory constrained
135
- // In some configurations (eg BLE enabled) there may be some transient
136
- // memory exhaustion, and trying to log when OOM can lead to a crash. Skipping
137
- // here usually allows the stack to recover instead.
138
- // See issue #1234 for analysis.
139
- if (xPortGetFreeHeapSize() < 2048)
140
- return;
141
- #endif
142
- this->log_callback_.call(level, tag, msg);
143
- }
144
-
145
132
  Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) {
146
133
  // add 1 to buffer size for null terminator
147
134
  this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
@@ -189,7 +176,7 @@ void Logger::loop() {
189
176
  this->tx_buffer_size_);
190
177
  this->write_footer_to_buffer_(this->tx_buffer_, &this->tx_buffer_at_, this->tx_buffer_size_);
191
178
  this->tx_buffer_[this->tx_buffer_at_] = '\0';
192
- this->call_log_callbacks_(message->level, message->tag, this->tx_buffer_);
179
+ this->log_callback_.call(message->level, message->tag, this->tx_buffer_);
193
180
  // At this point all the data we need from message has been transferred to the tx_buffer
194
181
  // so we can release the message to allow other tasks to use it as soon as possible.
195
182
  this->log_buffer_->release_message_main_loop(received_token);
@@ -156,7 +156,6 @@ class Logger : public Component {
156
156
  #endif
157
157
 
158
158
  protected:
159
- void call_log_callbacks_(int level, const char *tag, const char *msg);
160
159
  void write_msg_(const char *msg);
161
160
 
162
161
  // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
@@ -191,7 +190,7 @@ class Logger : public Component {
191
190
  if (this->baud_rate_ > 0) {
192
191
  this->write_msg_(this->tx_buffer_); // If logging is enabled, write to console
193
192
  }
194
- this->call_log_callbacks_(level, tag, this->tx_buffer_);
193
+ this->log_callback_.call(level, tag, this->tx_buffer_);
195
194
  }
196
195
 
197
196
  // Write the body of the log message to the buffer
@@ -153,7 +153,7 @@ bool MQTTComponent::send_discovery_() {
153
153
  if (node_friendly_name.empty()) {
154
154
  node_friendly_name = node_name;
155
155
  }
156
- const std::string &node_area = App.get_area();
156
+ std::string node_area = App.get_area();
157
157
 
158
158
  JsonObject device_info = root.createNestedObject(MQTT_DEVICE);
159
159
  const auto mac = get_mac_address();
@@ -56,7 +56,7 @@ void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nexti
56
56
  this->publish_state(state);
57
57
  } else {
58
58
  this->state = state;
59
- this->has_state_ = true;
59
+ this->set_has_state(true);
60
60
  }
61
61
 
62
62
  this->update_component_settings();
@@ -337,23 +337,26 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
337
337
 
338
338
  bool Nextion::upload_end_(bool successful) {
339
339
  ESP_LOGD(TAG, "TFT upload done: %s", YESNO(successful));
340
- this->is_updating_ = false;
341
- this->ignore_is_setup_ = false;
342
-
343
- uint32_t baud_rate = this->parent_->get_baud_rate();
344
- if (baud_rate != this->original_baud_rate_) {
345
- ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
346
- this->parent_->set_baud_rate(this->original_baud_rate_);
347
- this->parent_->load_settings();
348
- }
349
340
 
350
341
  if (successful) {
351
342
  ESP_LOGD(TAG, "Restart");
352
343
  delay(1500); // NOLINT
353
344
  App.safe_reboot();
345
+ delay(1500); // NOLINT
354
346
  } else {
355
347
  ESP_LOGE(TAG, "TFT upload failed");
348
+
349
+ this->is_updating_ = false;
350
+ this->ignore_is_setup_ = false;
351
+
352
+ uint32_t baud_rate = this->parent_->get_baud_rate();
353
+ if (baud_rate != this->original_baud_rate_) {
354
+ ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
355
+ this->parent_->set_baud_rate(this->original_baud_rate_);
356
+ this->parent_->load_settings();
357
+ }
356
358
  }
359
+
357
360
  return successful;
358
361
  }
359
362
 
@@ -337,15 +337,6 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
337
337
 
338
338
  bool Nextion::upload_end_(bool successful) {
339
339
  ESP_LOGD(TAG, "TFT upload done: %s", YESNO(successful));
340
- this->is_updating_ = false;
341
- this->ignore_is_setup_ = false;
342
-
343
- uint32_t baud_rate = this->parent_->get_baud_rate();
344
- if (baud_rate != this->original_baud_rate_) {
345
- ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
346
- this->parent_->set_baud_rate(this->original_baud_rate_);
347
- this->parent_->load_settings();
348
- }
349
340
 
350
341
  if (successful) {
351
342
  ESP_LOGD(TAG, "Restart");
@@ -353,7 +344,18 @@ bool Nextion::upload_end_(bool successful) {
353
344
  App.safe_reboot();
354
345
  } else {
355
346
  ESP_LOGE(TAG, "TFT upload failed");
347
+
348
+ this->is_updating_ = false;
349
+ this->ignore_is_setup_ = false;
350
+
351
+ uint32_t baud_rate = this->parent_->get_baud_rate();
352
+ if (baud_rate != this->original_baud_rate_) {
353
+ ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
354
+ this->parent_->set_baud_rate(this->original_baud_rate_);
355
+ this->parent_->load_settings();
356
+ }
356
357
  }
358
+
357
359
  return successful;
358
360
  }
359
361
 
@@ -88,7 +88,7 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) {
88
88
  } else {
89
89
  this->raw_state = state;
90
90
  this->state = state;
91
- this->has_state_ = true;
91
+ this->set_has_state(true);
92
92
  }
93
93
  }
94
94
  this->update_component_settings();
@@ -37,7 +37,7 @@ void NextionTextSensor::set_state(const std::string &state, bool publish, bool s
37
37
  this->publish_state(state);
38
38
  } else {
39
39
  this->state = state;
40
- this->has_state_ = true;
40
+ this->set_has_state(true);
41
41
  }
42
42
 
43
43
  this->update_component_settings();
@@ -7,7 +7,7 @@ namespace number {
7
7
  static const char *const TAG = "number";
8
8
 
9
9
  void Number::publish_state(float state) {
10
- this->has_state_ = true;
10
+ this->set_has_state(true);
11
11
  this->state = state;
12
12
  ESP_LOGD(TAG, "'%s': Sending state %f", this->get_name().c_str(), state);
13
13
  this->state_callback_.call(state);
@@ -48,9 +48,6 @@ class Number : public EntityBase {
48
48
 
49
49
  NumberTraits traits;
50
50
 
51
- /// Return whether this number has gotten a full state yet.
52
- bool has_state() const { return has_state_; }
53
-
54
51
  protected:
55
52
  friend class NumberCall;
56
53
 
@@ -63,7 +60,6 @@ class Number : public EntityBase {
63
60
  virtual void control(float value) = 0;
64
61
 
65
62
  CallbackManager<void(float)> state_callback_;
66
- bool has_state_{false};
67
63
  };
68
64
 
69
65
  } // namespace number
@@ -31,7 +31,6 @@ CONFIG_SCHEMA = cv.Schema(
31
31
  }
32
32
  ),
33
33
  },
34
- cv.only_with_arduino,
35
34
  ).extend(cv.COMPONENT_SCHEMA)
36
35
 
37
36
 
@@ -10,7 +10,7 @@ void Select::publish_state(const std::string &state) {
10
10
  auto index = this->index_of(state);
11
11
  const auto *name = this->get_name().c_str();
12
12
  if (index.has_value()) {
13
- this->has_state_ = true;
13
+ this->set_has_state(true);
14
14
  this->state = state;
15
15
  ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", name, state.c_str(), index.value());
16
16
  this->state_callback_.call(state, index.value());
@@ -35,9 +35,6 @@ class Select : public EntityBase {
35
35
 
36
36
  void publish_state(const std::string &state);
37
37
 
38
- /// Return whether this select component has gotten a full state yet.
39
- bool has_state() const { return has_state_; }
40
-
41
38
  /// Instantiate a SelectCall object to modify this select component's state.
42
39
  SelectCall make_call() { return SelectCall(this); }
43
40
 
@@ -73,7 +70,6 @@ class Select : public EntityBase {
73
70
  virtual void control(const std::string &value) = 0;
74
71
 
75
72
  CallbackManager<void(std::string, size_t)> state_callback_;
76
- bool has_state_{false};
77
73
  };
78
74
 
79
75
  } // namespace select
@@ -38,7 +38,9 @@ StateClass Sensor::get_state_class() {
38
38
 
39
39
  void Sensor::publish_state(float state) {
40
40
  this->raw_state = state;
41
- this->raw_callback_.call(state);
41
+ if (this->raw_callback_) {
42
+ this->raw_callback_->call(state);
43
+ }
42
44
 
43
45
  ESP_LOGV(TAG, "'%s': Received new state %f", this->name_.c_str(), state);
44
46
 
@@ -51,7 +53,10 @@ void Sensor::publish_state(float state) {
51
53
 
52
54
  void Sensor::add_on_state_callback(std::function<void(float)> &&callback) { this->callback_.add(std::move(callback)); }
53
55
  void Sensor::add_on_raw_state_callback(std::function<void(float)> &&callback) {
54
- this->raw_callback_.add(std::move(callback));
56
+ if (!this->raw_callback_) {
57
+ this->raw_callback_ = make_unique<CallbackManager<void(float)>>();
58
+ }
59
+ this->raw_callback_->add(std::move(callback));
55
60
  }
56
61
 
57
62
  void Sensor::add_filter(Filter *filter) {
@@ -88,13 +93,12 @@ float Sensor::get_raw_state() const { return this->raw_state; }
88
93
  std::string Sensor::unique_id() { return ""; }
89
94
 
90
95
  void Sensor::internal_send_state_to_frontend(float state) {
91
- this->has_state_ = true;
96
+ this->set_has_state(true);
92
97
  this->state = state;
93
98
  ESP_LOGD(TAG, "'%s': Sending state %.5f %s with %d decimals of accuracy", this->get_name().c_str(), state,
94
99
  this->get_unit_of_measurement().c_str(), this->get_accuracy_decimals());
95
100
  this->callback_.call(state);
96
101
  }
97
- bool Sensor::has_state() const { return this->has_state_; }
98
102
 
99
103
  } // namespace sensor
100
104
  } // namespace esphome
@@ -7,6 +7,7 @@
7
7
  #include "esphome/components/sensor/filter.h"
8
8
 
9
9
  #include <vector>
10
+ #include <memory>
10
11
 
11
12
  namespace esphome {
12
13
  namespace sensor {
@@ -140,9 +141,6 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa
140
141
  */
141
142
  float raw_state;
142
143
 
143
- /// Return whether this sensor has gotten a full state (that passed through all filters) yet.
144
- bool has_state() const;
145
-
146
144
  /** Override this method to set the unique ID of this sensor.
147
145
  *
148
146
  * @deprecated Do not use for new sensors, a suitable unique ID is automatically generated (2023.4).
@@ -152,15 +150,14 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa
152
150
  void internal_send_state_to_frontend(float state);
153
151
 
154
152
  protected:
155
- CallbackManager<void(float)> raw_callback_; ///< Storage for raw state callbacks.
156
- CallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.
153
+ std::unique_ptr<CallbackManager<void(float)>> raw_callback_; ///< Storage for raw state callbacks (lazy allocated).
154
+ CallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.
157
155
 
158
156
  Filter *filter_list_{nullptr}; ///< Store all active filters.
159
157
 
160
158
  optional<int8_t> accuracy_decimals_; ///< Accuracy in decimals override
161
159
  optional<StateClass> state_class_{STATE_CLASS_NONE}; ///< State class override
162
160
  bool force_update_{false}; ///< Force update mode
163
- bool has_state_{false};
164
161
  };
165
162
 
166
163
  } // namespace sensor
@@ -9,10 +9,10 @@ namespace status_led {
9
9
  static const char *const TAG = "status_led";
10
10
 
11
11
  void StatusLEDLightOutput::loop() {
12
- uint32_t new_state = App.get_app_state() & STATUS_LED_MASK;
12
+ uint8_t new_state = App.get_app_state() & STATUS_LED_MASK;
13
13
 
14
14
  if (new_state != this->last_app_state_) {
15
- ESP_LOGV(TAG, "New app state 0x%08" PRIX32, new_state);
15
+ ESP_LOGV(TAG, "New app state 0x%02X", new_state);
16
16
  }
17
17
 
18
18
  if ((new_state & STATUS_LED_ERROR) != 0u) {
@@ -36,7 +36,7 @@ class StatusLEDLightOutput : public light::LightOutput, public Component {
36
36
  GPIOPin *pin_{nullptr};
37
37
  output::BinaryOutput *output_{nullptr};
38
38
  light::LightState *lightstate_{};
39
- uint32_t last_app_state_{0xFFFF};
39
+ uint8_t last_app_state_{0xFF};
40
40
  void output_state_(bool state);
41
41
  };
42
42
 
@@ -110,15 +110,7 @@ void TemplateAlarmControlPanel::loop() {
110
110
  delay = this->arming_night_time_;
111
111
  }
112
112
  if ((millis() - this->last_update_) > delay) {
113
- #ifdef USE_BINARY_SENSOR
114
- for (auto sensor_info : this->sensor_map_) {
115
- // Check for sensors left on and set to bypass automatically and remove them from monitoring
116
- if ((sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor_info.first->state)) {
117
- ESP_LOGW(TAG, "%s is left on and will be automatically bypassed", sensor_info.first->get_name().c_str());
118
- this->bypassed_sensor_indicies_.push_back(sensor_info.second.store_index);
119
- }
120
- }
121
- #endif
113
+ this->bypass_before_arming();
122
114
  this->publish_state(this->desired_state_);
123
115
  }
124
116
  return;
@@ -259,10 +251,23 @@ void TemplateAlarmControlPanel::arm_(optional<std::string> code, alarm_control_p
259
251
  if (delay > 0) {
260
252
  this->publish_state(ACP_STATE_ARMING);
261
253
  } else {
254
+ this->bypass_before_arming();
262
255
  this->publish_state(state);
263
256
  }
264
257
  }
265
258
 
259
+ void TemplateAlarmControlPanel::bypass_before_arming() {
260
+ #ifdef USE_BINARY_SENSOR
261
+ for (auto sensor_info : this->sensor_map_) {
262
+ // Check for sensors left on and set to bypass automatically and remove them from monitoring
263
+ if ((sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor_info.first->state)) {
264
+ ESP_LOGW(TAG, "'%s' is left on and will be automatically bypassed", sensor_info.first->get_name().c_str());
265
+ this->bypassed_sensor_indicies_.push_back(sensor_info.second.store_index);
266
+ }
267
+ }
268
+ #endif
269
+ }
270
+
266
271
  void TemplateAlarmControlPanel::control(const AlarmControlPanelCall &call) {
267
272
  if (call.get_state()) {
268
273
  if (call.get_state() == ACP_STATE_ARMED_AWAY) {
@@ -60,6 +60,7 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel,
60
60
  bool get_requires_code_to_arm() const override { return this->requires_code_to_arm_; }
61
61
  bool get_all_sensors_ready() { return this->sensors_ready_; };
62
62
  void set_restore_mode(TemplateAlarmControlPanelRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
63
+ void bypass_before_arming();
63
64
 
64
65
  #ifdef USE_BINARY_SENSOR
65
66
  /** Add a binary_sensor to the alarm_panel.
@@ -7,7 +7,7 @@ namespace text {
7
7
  static const char *const TAG = "text";
8
8
 
9
9
  void Text::publish_state(const std::string &state) {
10
- this->has_state_ = true;
10
+ this->set_has_state(true);
11
11
  this->state = state;
12
12
  if (this->traits.get_mode() == TEXT_MODE_PASSWORD) {
13
13
  ESP_LOGD(TAG, "'%s': Sending state " LOG_SECRET("'%s'"), this->get_name().c_str(), state.c_str());
@@ -28,9 +28,6 @@ class Text : public EntityBase {
28
28
 
29
29
  void publish_state(const std::string &state);
30
30
 
31
- /// Return whether this text input has gotten a full state yet.
32
- bool has_state() const { return has_state_; }
33
-
34
31
  /// Instantiate a TextCall object to modify this text component's state.
35
32
  TextCall make_call() { return TextCall(this); }
36
33
 
@@ -48,7 +45,6 @@ class Text : public EntityBase {
48
45
  virtual void control(const std::string &value) = 0;
49
46
 
50
47
  CallbackManager<void(std::string)> state_callback_;
51
- bool has_state_{false};
52
48
  };
53
49
 
54
50
  } // namespace text