mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Merge branch 'integration' into memory_api
This commit is contained in:
		| @@ -246,6 +246,7 @@ esphome/components/lcd_menu/* @numo68 | ||||
| esphome/components/ld2410/* @regevbr @sebcaps | ||||
| esphome/components/ld2420/* @descipher | ||||
| esphome/components/ld2450/* @hareeshmu | ||||
| esphome/components/ld24xx/* @kbx81 | ||||
| esphome/components/ledc/* @OttoWinter | ||||
| esphome/components/libretiny/* @kuba2k2 | ||||
| esphome/components/libretiny_pwm/* @kuba2k2 | ||||
|   | ||||
| @@ -31,7 +31,7 @@ from esphome.const import ( | ||||
|     KEY_TARGET_FRAMEWORK, | ||||
|     KEY_TARGET_PLATFORM, | ||||
|     PLATFORM_ESP32, | ||||
|     CoreModel, | ||||
|     ThreadModel, | ||||
|     __version__, | ||||
| ) | ||||
| from esphome.core import CORE, HexInt, TimePeriod | ||||
| @@ -98,16 +98,6 @@ ARDUINO_ALLOWED_VARIANTS = [ | ||||
|     VARIANT_ESP32S3, | ||||
| ] | ||||
|  | ||||
| # Single-core ESP32 variants | ||||
| SINGLE_CORE_VARIANTS = frozenset( | ||||
|     [ | ||||
|         VARIANT_ESP32S2, | ||||
|         VARIANT_ESP32C3, | ||||
|         VARIANT_ESP32C6, | ||||
|         VARIANT_ESP32H2, | ||||
|     ] | ||||
| ) | ||||
|  | ||||
|  | ||||
| def get_cpu_frequencies(*frequencies): | ||||
|     return [str(x) + "MHZ" for x in frequencies] | ||||
| @@ -732,11 +722,7 @@ async def to_code(config): | ||||
|     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) | ||||
|     cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}") | ||||
|     cg.add_define("ESPHOME_VARIANT", VARIANT_FRIENDLY[config[CONF_VARIANT]]) | ||||
|     # Set threading model based on core count | ||||
|     if config[CONF_VARIANT] in SINGLE_CORE_VARIANTS: | ||||
|         cg.add_define(CoreModel.SINGLE) | ||||
|     else: | ||||
|         cg.add_define(CoreModel.MULTI_ATOMICS) | ||||
|     cg.add_define(ThreadModel.MULTI_ATOMICS) | ||||
|  | ||||
|     cg.add_platformio_option("lib_ldf_mode", "off") | ||||
|     cg.add_platformio_option("lib_compat_mode", "strict") | ||||
|   | ||||
| @@ -15,7 +15,7 @@ from esphome.const import ( | ||||
|     KEY_TARGET_FRAMEWORK, | ||||
|     KEY_TARGET_PLATFORM, | ||||
|     PLATFORM_ESP8266, | ||||
|     CoreModel, | ||||
|     ThreadModel, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.helpers import copy_file_if_changed | ||||
| @@ -188,7 +188,7 @@ async def to_code(config): | ||||
|     cg.set_cpp_standard("gnu++20") | ||||
|     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) | ||||
|     cg.add_define("ESPHOME_VARIANT", "ESP8266") | ||||
|     cg.add_define(CoreModel.SINGLE) | ||||
|     cg.add_define(ThreadModel.SINGLE) | ||||
|  | ||||
|     cg.add_platformio_option("extra_scripts", ["post:post_build.py"]) | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ from esphome.const import ( | ||||
|     KEY_TARGET_FRAMEWORK, | ||||
|     KEY_TARGET_PLATFORM, | ||||
|     PLATFORM_HOST, | ||||
|     CoreModel, | ||||
|     ThreadModel, | ||||
| ) | ||||
| from esphome.core import CORE | ||||
|  | ||||
| @@ -44,7 +44,7 @@ async def to_code(config): | ||||
|     cg.add_define("USE_ESPHOME_HOST_MAC_ADDRESS", config[CONF_MAC_ADDRESS].parts) | ||||
|     cg.add_build_flag("-std=gnu++20") | ||||
|     cg.add_define("ESPHOME_BOARD", "host") | ||||
|     cg.add_define(CoreModel.MULTI_ATOMICS) | ||||
|     cg.add_define(ThreadModel.MULTI_ATOMICS) | ||||
|     cg.add_platformio_option("platform", "platformio/native") | ||||
|     cg.add_platformio_option("lib_ldf_mode", "off") | ||||
|     cg.add_platformio_option("lib_compat_mode", "strict") | ||||
|   | ||||
| @@ -5,6 +5,7 @@ from esphome.components import uart | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import CONF_ID, CONF_PASSWORD, CONF_THROTTLE, CONF_TIMEOUT | ||||
|  | ||||
| AUTO_LOAD = ["ld24xx"] | ||||
| DEPENDENCIES = ["uart"] | ||||
| CODEOWNERS = ["@sebcaps", "@regevbr"] | ||||
| MULTI_CONF = True | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #include "ld2410.h" | ||||
|  | ||||
| #include <utility> | ||||
| #ifdef USE_NUMBER | ||||
| #include "esphome/components/number/number.h" | ||||
| #endif | ||||
| @@ -10,10 +9,6 @@ | ||||
|  | ||||
| #include "esphome/core/application.h" | ||||
|  | ||||
| #define CHECK_BIT(var, pos) (((var) >> (pos)) & 1) | ||||
| #define highbyte(val) (uint8_t)((val) >> 8) | ||||
| #define lowbyte(val) (uint8_t)((val) &0xff) | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| @@ -165,6 +160,9 @@ static constexpr uint8_t CMD_BLUETOOTH = 0xA4; | ||||
| static constexpr uint8_t CMD_MAX_MOVE_VALUE = 0x00; | ||||
| static constexpr uint8_t CMD_MAX_STILL_VALUE = 0x01; | ||||
| static constexpr uint8_t CMD_DURATION_VALUE = 0x02; | ||||
| // Bitmasks for target states | ||||
| static constexpr uint8_t MOVE_BITMASK = 0x01; | ||||
| static constexpr uint8_t STILL_BITMASK = 0x02; | ||||
| // Header & Footer size | ||||
| static constexpr uint8_t HEADER_FOOTER_SIZE = 4; | ||||
| // Command Header & Footer | ||||
| @@ -202,17 +200,17 @@ void LD2410Component::dump_config() { | ||||
| #endif | ||||
| #ifdef USE_SENSOR | ||||
|   ESP_LOGCONFIG(TAG, "Sensors:"); | ||||
|   LOG_SENSOR("  ", "Light", this->light_sensor_); | ||||
|   LOG_SENSOR("  ", "DetectionDistance", this->detection_distance_sensor_); | ||||
|   LOG_SENSOR("  ", "MovingTargetDistance", this->moving_target_distance_sensor_); | ||||
|   LOG_SENSOR("  ", "MovingTargetEnergy", this->moving_target_energy_sensor_); | ||||
|   LOG_SENSOR("  ", "StillTargetDistance", this->still_target_distance_sensor_); | ||||
|   LOG_SENSOR("  ", "StillTargetEnergy", this->still_target_energy_sensor_); | ||||
|   for (sensor::Sensor *s : this->gate_move_sensors_) { | ||||
|     LOG_SENSOR("  ", "GateMove", s); | ||||
|   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "Light", this->light_sensor_); | ||||
|   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "DetectionDistance", this->detection_distance_sensor_); | ||||
|   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "MovingTargetDistance", this->moving_target_distance_sensor_); | ||||
|   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "MovingTargetEnergy", this->moving_target_energy_sensor_); | ||||
|   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "StillTargetDistance", this->still_target_distance_sensor_); | ||||
|   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "StillTargetEnergy", this->still_target_energy_sensor_); | ||||
|   for (auto &s : this->gate_move_sensors_) { | ||||
|     LOG_SENSOR_WITH_DEDUP_SAFE("  ", "GateMove", s); | ||||
|   } | ||||
|   for (sensor::Sensor *s : this->gate_still_sensors_) { | ||||
|     LOG_SENSOR("  ", "GateStill", s); | ||||
|   for (auto &s : this->gate_still_sensors_) { | ||||
|     LOG_SENSOR_WITH_DEDUP_SAFE("  ", "GateStill", s); | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_TEXT_SENSOR | ||||
| @@ -304,9 +302,11 @@ void LD2410Component::send_command_(uint8_t command, const uint8_t *command_valu | ||||
|   } | ||||
|   // frame footer bytes | ||||
|   this->write_array(CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER)); | ||||
|   // FIXME to remove | ||||
|  | ||||
|   if (command != CMD_ENABLE_CONF && command != CMD_DISABLE_CONF) { | ||||
|     delay(50);  // NOLINT | ||||
|   } | ||||
| } | ||||
|  | ||||
| void LD2410Component::handle_periodic_data_() { | ||||
|   // Reduce data update rate to reduce home assistant database growth | ||||
| @@ -348,10 +348,10 @@ void LD2410Component::handle_periodic_data_() { | ||||
|     this->target_binary_sensor_->publish_state(target_state != 0x00); | ||||
|   } | ||||
|   if (this->moving_target_binary_sensor_ != nullptr) { | ||||
|     this->moving_target_binary_sensor_->publish_state(CHECK_BIT(target_state, 0)); | ||||
|     this->moving_target_binary_sensor_->publish_state(target_state & MOVE_BITMASK); | ||||
|   } | ||||
|   if (this->still_target_binary_sensor_ != nullptr) { | ||||
|     this->still_target_binary_sensor_->publish_state(CHECK_BIT(target_state, 1)); | ||||
|     this->still_target_binary_sensor_->publish_state(target_state & STILL_BITMASK); | ||||
|   } | ||||
| #endif | ||||
|   /* | ||||
| @@ -362,89 +362,51 @@ void LD2410Component::handle_periodic_data_() { | ||||
|     Detect distance: 16~17th bytes | ||||
|   */ | ||||
| #ifdef USE_SENSOR | ||||
|   if (this->moving_target_distance_sensor_ != nullptr) { | ||||
|     int new_moving_target_distance = | ||||
|         ld2410::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH]); | ||||
|     if (this->moving_target_distance_sensor_->get_state() != new_moving_target_distance) | ||||
|       this->moving_target_distance_sensor_->publish_state(new_moving_target_distance); | ||||
|   } | ||||
|   if (this->moving_target_energy_sensor_ != nullptr) { | ||||
|     int new_moving_target_energy = this->buffer_data_[MOVING_ENERGY]; | ||||
|     if (this->moving_target_energy_sensor_->get_state() != new_moving_target_energy) | ||||
|       this->moving_target_energy_sensor_->publish_state(new_moving_target_energy); | ||||
|   } | ||||
|   if (this->still_target_distance_sensor_ != nullptr) { | ||||
|     int new_still_target_distance = | ||||
|         ld2410::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH]); | ||||
|     if (this->still_target_distance_sensor_->get_state() != new_still_target_distance) | ||||
|       this->still_target_distance_sensor_->publish_state(new_still_target_distance); | ||||
|   } | ||||
|   if (this->still_target_energy_sensor_ != nullptr) { | ||||
|     int new_still_target_energy = this->buffer_data_[STILL_ENERGY]; | ||||
|     if (this->still_target_energy_sensor_->get_state() != new_still_target_energy) | ||||
|       this->still_target_energy_sensor_->publish_state(new_still_target_energy); | ||||
|   } | ||||
|   if (this->detection_distance_sensor_ != nullptr) { | ||||
|     int new_detect_distance = | ||||
|         ld2410::two_byte_to_int(this->buffer_data_[DETECT_DISTANCE_LOW], this->buffer_data_[DETECT_DISTANCE_HIGH]); | ||||
|     if (this->detection_distance_sensor_->get_state() != new_detect_distance) | ||||
|       this->detection_distance_sensor_->publish_state(new_detect_distance); | ||||
|   } | ||||
|   SAFE_PUBLISH_SENSOR( | ||||
|       this->moving_target_distance_sensor_, | ||||
|       ld2410::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH])) | ||||
|   SAFE_PUBLISH_SENSOR(this->moving_target_energy_sensor_, this->buffer_data_[MOVING_ENERGY]) | ||||
|   SAFE_PUBLISH_SENSOR( | ||||
|       this->still_target_distance_sensor_, | ||||
|       ld2410::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH])); | ||||
|   SAFE_PUBLISH_SENSOR(this->still_target_energy_sensor_, this->buffer_data_[STILL_ENERGY]); | ||||
|   SAFE_PUBLISH_SENSOR( | ||||
|       this->detection_distance_sensor_, | ||||
|       ld2410::two_byte_to_int(this->buffer_data_[DETECT_DISTANCE_LOW], this->buffer_data_[DETECT_DISTANCE_HIGH])); | ||||
|  | ||||
|   if (engineering_mode) { | ||||
|     /* | ||||
|       Moving distance range: 18th byte | ||||
|       Still distance range: 19th byte | ||||
|       Moving energy: 20~28th bytes | ||||
|     */ | ||||
|     for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_move_sensors_.size(); i++) { | ||||
|       sensor::Sensor *s = this->gate_move_sensors_[i]; | ||||
|       if (s != nullptr) { | ||||
|         s->publish_state(this->buffer_data_[MOVING_SENSOR_START + i]); | ||||
|       } | ||||
|     for (uint8_t i = 0; i < TOTAL_GATES; i++) { | ||||
|       SAFE_PUBLISH_SENSOR(this->gate_move_sensors_[i], this->buffer_data_[MOVING_SENSOR_START + i]) | ||||
|     } | ||||
|     /* | ||||
|       Still energy: 29~37th bytes | ||||
|     */ | ||||
|     for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_still_sensors_.size(); i++) { | ||||
|       sensor::Sensor *s = this->gate_still_sensors_[i]; | ||||
|       if (s != nullptr) { | ||||
|         s->publish_state(this->buffer_data_[STILL_SENSOR_START + i]); | ||||
|       } | ||||
|     for (uint8_t i = 0; i < TOTAL_GATES; i++) { | ||||
|       SAFE_PUBLISH_SENSOR(this->gate_still_sensors_[i], this->buffer_data_[STILL_SENSOR_START + i]) | ||||
|     } | ||||
|     /* | ||||
|       Light sensor: 38th bytes | ||||
|     */ | ||||
|     if (this->light_sensor_ != nullptr) { | ||||
|       int new_light_sensor = this->buffer_data_[LIGHT_SENSOR]; | ||||
|       if (this->light_sensor_->get_state() != new_light_sensor) { | ||||
|         this->light_sensor_->publish_state(new_light_sensor); | ||||
|       } | ||||
|     } | ||||
|     SAFE_PUBLISH_SENSOR(this->light_sensor_, this->buffer_data_[LIGHT_SENSOR]) | ||||
|   } else { | ||||
|     for (auto *s : this->gate_move_sensors_) { | ||||
|       if (s != nullptr && !std::isnan(s->get_state())) { | ||||
|         s->publish_state(NAN); | ||||
|     for (auto &gate_move_sensor : this->gate_move_sensors_) { | ||||
|       SAFE_PUBLISH_SENSOR_UNKNOWN(gate_move_sensor) | ||||
|     } | ||||
|     for (auto &gate_still_sensor : this->gate_still_sensors_) { | ||||
|       SAFE_PUBLISH_SENSOR_UNKNOWN(gate_still_sensor) | ||||
|     } | ||||
|     for (auto *s : this->gate_still_sensors_) { | ||||
|       if (s != nullptr && !std::isnan(s->get_state())) { | ||||
|         s->publish_state(NAN); | ||||
|       } | ||||
|     } | ||||
|     if (this->light_sensor_ != nullptr && !std::isnan(this->light_sensor_->get_state())) { | ||||
|       this->light_sensor_->publish_state(NAN); | ||||
|     } | ||||
|     SAFE_PUBLISH_SENSOR_UNKNOWN(this->light_sensor_) | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   if (engineering_mode) { | ||||
|   if (this->out_pin_presence_status_binary_sensor_ != nullptr) { | ||||
|       this->out_pin_presence_status_binary_sensor_->publish_state(this->buffer_data_[OUT_PIN_SENSOR] == 0x01); | ||||
|     } | ||||
|   } else { | ||||
|     if (this->out_pin_presence_status_binary_sensor_ != nullptr) { | ||||
|       this->out_pin_presence_status_binary_sensor_->publish_state(false); | ||||
|     } | ||||
|     this->out_pin_presence_status_binary_sensor_->publish_state( | ||||
|         engineering_mode ? this->buffer_data_[OUT_PIN_SENSOR] == 0x01 : false); | ||||
|   } | ||||
| #endif | ||||
| } | ||||
| @@ -824,8 +786,14 @@ void LD2410Component::set_light_out_control() { | ||||
| } | ||||
|  | ||||
| #ifdef USE_SENSOR | ||||
| void LD2410Component::set_gate_move_sensor(uint8_t gate, sensor::Sensor *s) { this->gate_move_sensors_[gate] = s; } | ||||
| void LD2410Component::set_gate_still_sensor(uint8_t gate, sensor::Sensor *s) { this->gate_still_sensors_[gate] = s; } | ||||
| // These could leak memory, but they are only set once prior to 'setup()' and should never be used again. | ||||
| void LD2410Component::set_gate_move_sensor(uint8_t gate, sensor::Sensor *s) { | ||||
|   this->gate_move_sensors_[gate] = new SensorWithDedup<uint8_t>(s); | ||||
| } | ||||
|  | ||||
| void LD2410Component::set_gate_still_sensor(uint8_t gate, sensor::Sensor *s) { | ||||
|   this->gate_still_sensors_[gate] = new SensorWithDedup<uint8_t>(s); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| }  // namespace ld2410 | ||||
|   | ||||
| @@ -22,15 +22,20 @@ | ||||
| #ifdef USE_TEXT_SENSOR | ||||
| #include "esphome/components/text_sensor/text_sensor.h" | ||||
| #endif | ||||
| #include "esphome/components/ld24xx/ld24xx.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| #include <array> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| static const uint8_t MAX_LINE_LENGTH = 46;  // Max characters for serial buffer | ||||
| static const uint8_t TOTAL_GATES = 9;       // Total number of gates supported by the LD2410 | ||||
| using namespace ld24xx; | ||||
|  | ||||
| static constexpr uint8_t MAX_LINE_LENGTH = 46;  // Max characters for serial buffer | ||||
| static constexpr uint8_t TOTAL_GATES = 9;       // Total number of gates supported by the LD2410 | ||||
|  | ||||
| class LD2410Component : public Component, public uart::UARTDevice { | ||||
| #ifdef USE_BINARY_SENSOR | ||||
| @@ -40,12 +45,12 @@ class LD2410Component : public Component, public uart::UARTDevice { | ||||
|   SUB_BINARY_SENSOR(target) | ||||
| #endif | ||||
| #ifdef USE_SENSOR | ||||
|   SUB_SENSOR(light) | ||||
|   SUB_SENSOR(detection_distance) | ||||
|   SUB_SENSOR(moving_target_distance) | ||||
|   SUB_SENSOR(moving_target_energy) | ||||
|   SUB_SENSOR(still_target_distance) | ||||
|   SUB_SENSOR(still_target_energy) | ||||
|   SUB_SENSOR_WITH_DEDUP(light, uint8_t) | ||||
|   SUB_SENSOR_WITH_DEDUP(detection_distance, int) | ||||
|   SUB_SENSOR_WITH_DEDUP(moving_target_distance, int) | ||||
|   SUB_SENSOR_WITH_DEDUP(moving_target_energy, uint8_t) | ||||
|   SUB_SENSOR_WITH_DEDUP(still_target_distance, int) | ||||
|   SUB_SENSOR_WITH_DEDUP(still_target_energy, uint8_t) | ||||
| #endif | ||||
| #ifdef USE_TEXT_SENSOR | ||||
|   SUB_TEXT_SENSOR(version) | ||||
| @@ -122,12 +127,12 @@ class LD2410Component : public Component, public uart::UARTDevice { | ||||
|   uint8_t version_[6] = {0, 0, 0, 0, 0, 0}; | ||||
|   bool bluetooth_on_{false}; | ||||
| #ifdef USE_NUMBER | ||||
|   std::vector<number::Number *> gate_move_threshold_numbers_ = std::vector<number::Number *>(TOTAL_GATES); | ||||
|   std::vector<number::Number *> gate_still_threshold_numbers_ = std::vector<number::Number *>(TOTAL_GATES); | ||||
|   std::array<number::Number *, TOTAL_GATES> gate_move_threshold_numbers_{}; | ||||
|   std::array<number::Number *, TOTAL_GATES> gate_still_threshold_numbers_{}; | ||||
| #endif | ||||
| #ifdef USE_SENSOR | ||||
|   std::vector<sensor::Sensor *> gate_move_sensors_ = std::vector<sensor::Sensor *>(TOTAL_GATES); | ||||
|   std::vector<sensor::Sensor *> gate_still_sensors_ = std::vector<sensor::Sensor *>(TOTAL_GATES); | ||||
|   std::array<SensorWithDedup<uint8_t> *, TOTAL_GATES> gate_move_sensors_{}; | ||||
|   std::array<SensorWithDedup<uint8_t> *, TOTAL_GATES> gate_still_sensors_{}; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										1
									
								
								esphome/components/ld24xx/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/ld24xx/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| CODEOWNERS = ["@kbx81"] | ||||
							
								
								
									
										65
									
								
								esphome/components/ld24xx/ld24xx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								esphome/components/ld24xx/ld24xx.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/defines.h" | ||||
|  | ||||
| #include <memory> | ||||
|  | ||||
| #ifdef USE_SENSOR | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
|  | ||||
| #define SUB_SENSOR_WITH_DEDUP(name, dedup_type) \ | ||||
|  protected: \ | ||||
|   ld24xx::SensorWithDedup<dedup_type> *name##_sensor_{nullptr}; \ | ||||
| \ | ||||
|  public: \ | ||||
|   void set_##name##_sensor(sensor::Sensor *sensor) { \ | ||||
|     this->name##_sensor_ = new ld24xx::SensorWithDedup<dedup_type>(sensor); \ | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #define LOG_SENSOR_WITH_DEDUP_SAFE(tag, name, sensor) \ | ||||
|   if ((sensor) != nullptr) { \ | ||||
|     LOG_SENSOR(tag, name, (sensor)->sens); \ | ||||
|   } | ||||
|  | ||||
| #define SAFE_PUBLISH_SENSOR(sensor, value) \ | ||||
|   if ((sensor) != nullptr) { \ | ||||
|     (sensor)->publish_state_if_not_dup(value); \ | ||||
|   } | ||||
|  | ||||
| #define SAFE_PUBLISH_SENSOR_UNKNOWN(sensor) \ | ||||
|   if ((sensor) != nullptr) { \ | ||||
|     (sensor)->publish_state_unknown(); \ | ||||
|   } | ||||
|  | ||||
| #define highbyte(val) (uint8_t)((val) >> 8) | ||||
| #define lowbyte(val) (uint8_t)((val) &0xff) | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld24xx { | ||||
|  | ||||
| #ifdef USE_SENSOR | ||||
| // Helper class to store a sensor with a deduplicator & publish state only when the value changes | ||||
| template<typename T> class SensorWithDedup { | ||||
|  public: | ||||
|   SensorWithDedup(sensor::Sensor *sens) : sens(sens) {} | ||||
|  | ||||
|   void publish_state_if_not_dup(T state) { | ||||
|     if (this->publish_dedup.next(state)) { | ||||
|       this->sens->publish_state(static_cast<float>(state)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void publish_state_unknown() { | ||||
|     if (this->publish_dedup.next_unknown()) { | ||||
|       this->sens->publish_state(NAN); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   sensor::Sensor *sens; | ||||
|   Deduplicator<T> publish_dedup; | ||||
| }; | ||||
| #endif | ||||
| }  // namespace ld24xx | ||||
| }  // namespace esphome | ||||
| @@ -20,7 +20,7 @@ from esphome.const import ( | ||||
|     KEY_FRAMEWORK_VERSION, | ||||
|     KEY_TARGET_FRAMEWORK, | ||||
|     KEY_TARGET_PLATFORM, | ||||
|     CoreModel, | ||||
|     ThreadModel, | ||||
|     __version__, | ||||
| ) | ||||
| from esphome.core import CORE | ||||
| @@ -261,7 +261,7 @@ async def component_to_code(config): | ||||
|     cg.add_build_flag(f"-DUSE_LIBRETINY_VARIANT_{config[CONF_FAMILY]}") | ||||
|     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) | ||||
|     cg.add_define("ESPHOME_VARIANT", FAMILY_FRIENDLY[config[CONF_FAMILY]]) | ||||
|     cg.add_define(CoreModel.MULTI_NO_ATOMICS) | ||||
|     cg.add_define(ThreadModel.MULTI_NO_ATOMICS) | ||||
|  | ||||
|     # force using arduino framework | ||||
|     cg.add_platformio_option("framework", "arduino") | ||||
|   | ||||
| @@ -23,7 +23,7 @@ from esphome.const import ( | ||||
|     KEY_TARGET_FRAMEWORK, | ||||
|     KEY_TARGET_PLATFORM, | ||||
|     PLATFORM_NRF52, | ||||
|     CoreModel, | ||||
|     ThreadModel, | ||||
| ) | ||||
| from esphome.core import CORE, EsphomeError, coroutine_with_priority | ||||
| from esphome.storage_json import StorageJSON | ||||
| @@ -110,7 +110,7 @@ async def to_code(config: ConfigType) -> None: | ||||
|     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) | ||||
|     cg.add_define("ESPHOME_VARIANT", "NRF52") | ||||
|     # nRF52 processors are single-core | ||||
|     cg.add_define(CoreModel.SINGLE) | ||||
|     cg.add_define(ThreadModel.SINGLE) | ||||
|     cg.add_platformio_option(CONF_FRAMEWORK, CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK]) | ||||
|     cg.add_platformio_option( | ||||
|         "platform", | ||||
|   | ||||
| @@ -16,7 +16,7 @@ from esphome.const import ( | ||||
|     KEY_TARGET_FRAMEWORK, | ||||
|     KEY_TARGET_PLATFORM, | ||||
|     PLATFORM_RP2040, | ||||
|     CoreModel, | ||||
|     ThreadModel, | ||||
| ) | ||||
| from esphome.core import CORE, EsphomeError, coroutine_with_priority | ||||
| from esphome.helpers import copy_file_if_changed, mkdir_p, read_file, write_file | ||||
| @@ -172,7 +172,7 @@ async def to_code(config): | ||||
|     cg.set_cpp_standard("gnu++20") | ||||
|     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) | ||||
|     cg.add_define("ESPHOME_VARIANT", "RP2040") | ||||
|     cg.add_define(CoreModel.SINGLE) | ||||
|     cg.add_define(ThreadModel.SINGLE) | ||||
|  | ||||
|     cg.add_platformio_option("extra_scripts", ["post:post_build.py"]) | ||||
|  | ||||
|   | ||||
| @@ -35,12 +35,12 @@ class Framework(StrEnum): | ||||
|     ZEPHYR = "zephyr" | ||||
|  | ||||
|  | ||||
| class CoreModel(StrEnum): | ||||
|     """Core model identifiers for ESPHome scheduler.""" | ||||
| class ThreadModel(StrEnum): | ||||
|     """Threading model identifiers for ESPHome scheduler.""" | ||||
|  | ||||
|     SINGLE = "ESPHOME_CORES_SINGLE" | ||||
|     MULTI_NO_ATOMICS = "ESPHOME_CORES_MULTI_NO_ATOMICS" | ||||
|     MULTI_ATOMICS = "ESPHOME_CORES_MULTI_ATOMICS" | ||||
|     SINGLE = "ESPHOME_THREAD_SINGLE" | ||||
|     MULTI_NO_ATOMICS = "ESPHOME_THREAD_MULTI_NO_ATOMICS" | ||||
|     MULTI_ATOMICS = "ESPHOME_THREAD_MULTI_ATOMICS" | ||||
|  | ||||
|  | ||||
| class PlatformFramework(Enum): | ||||
|   | ||||
| @@ -15,8 +15,8 @@ | ||||
| #define ESPHOME_VARIANT "ESP32" | ||||
| #define ESPHOME_DEBUG_SCHEDULER | ||||
|  | ||||
| // Default threading model for static analysis (ESP32 is multi-core with atomics) | ||||
| #define ESPHOME_CORES_MULTI_ATOMICS | ||||
| // Default threading model for static analysis (ESP32 is multi-threaded with atomics) | ||||
| #define ESPHOME_THREAD_MULTI_ATOMICS | ||||
|  | ||||
| // logger | ||||
| #define ESPHOME_LOG_LEVEL ESPHOME_LOG_LEVEL_VERY_VERBOSE | ||||
|   | ||||
| @@ -84,7 +84,7 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type | ||||
|   item->callback = std::move(func); | ||||
|   item->remove = false; | ||||
|  | ||||
| #ifndef ESPHOME_CORES_SINGLE | ||||
| #ifndef ESPHOME_THREAD_SINGLE | ||||
|   // Special handling for defer() (delay = 0, type = TIMEOUT) | ||||
|   // Single-core platforms don't need thread-safe defer handling | ||||
|   if (delay == 0 && type == SchedulerItem::TIMEOUT) { | ||||
| @@ -94,7 +94,7 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type | ||||
|     this->defer_queue_.push_back(std::move(item)); | ||||
|     return; | ||||
|   } | ||||
| #endif /* not ESPHOME_CORES_SINGLE */ | ||||
| #endif /* not ESPHOME_THREAD_SINGLE */ | ||||
|  | ||||
|   // Get fresh timestamp for new timer/interval - ensures accurate scheduling | ||||
|   const auto now = this->millis_64_(millis());  // Fresh millis() call | ||||
| @@ -238,7 +238,7 @@ optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) { | ||||
|   return item->next_execution_ - now_64; | ||||
| } | ||||
| void HOT Scheduler::call(uint32_t now) { | ||||
| #ifndef ESPHOME_CORES_SINGLE | ||||
| #ifndef ESPHOME_THREAD_SINGLE | ||||
|   // Process defer queue first to guarantee FIFO execution order for deferred items. | ||||
|   // Previously, defer() used the heap which gave undefined order for equal timestamps, | ||||
|   // causing race conditions on multi-core systems (ESP32, BK7200). | ||||
| @@ -268,7 +268,7 @@ void HOT Scheduler::call(uint32_t now) { | ||||
|       this->execute_item_(item.get(), now); | ||||
|     } | ||||
|   } | ||||
| #endif /* not ESPHOME_CORES_SINGLE */ | ||||
| #endif /* not ESPHOME_THREAD_SINGLE */ | ||||
|  | ||||
|   // Convert the fresh timestamp from main loop to 64-bit for scheduler operations | ||||
|   const auto now_64 = this->millis_64_(now);  // 'now' from parameter - fresh from Application::loop() | ||||
| @@ -280,15 +280,15 @@ void HOT Scheduler::call(uint32_t now) { | ||||
|   if (now_64 - last_print > 2000) { | ||||
|     last_print = now_64; | ||||
|     std::vector<std::unique_ptr<SchedulerItem>> old_items; | ||||
| #ifdef ESPHOME_CORES_MULTI_ATOMICS | ||||
| #ifdef ESPHOME_THREAD_MULTI_ATOMICS | ||||
|     const auto last_dbg = this->last_millis_.load(std::memory_order_relaxed); | ||||
|     const auto major_dbg = this->millis_major_.load(std::memory_order_relaxed); | ||||
|     ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(), now_64, | ||||
|              major_dbg, last_dbg); | ||||
| #else  /* not ESPHOME_CORES_MULTI_ATOMICS */ | ||||
| #else  /* not ESPHOME_THREAD_MULTI_ATOMICS */ | ||||
|     ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(), now_64, | ||||
|              this->millis_major_, this->last_millis_); | ||||
| #endif /* else ESPHOME_CORES_MULTI_ATOMICS */ | ||||
| #endif /* else ESPHOME_THREAD_MULTI_ATOMICS */ | ||||
|     // Cleanup before debug output | ||||
|     this->cleanup_(); | ||||
|     while (!this->items_.empty()) { | ||||
| @@ -473,7 +473,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c | ||||
|   size_t total_cancelled = 0; | ||||
|  | ||||
|   // Check all containers for matching items | ||||
| #ifndef ESPHOME_CORES_SINGLE | ||||
| #ifndef ESPHOME_THREAD_SINGLE | ||||
|   // Only check defer queue for timeouts (intervals never go there) | ||||
|   if (type == SchedulerItem::TIMEOUT) { | ||||
|     for (auto &item : this->defer_queue_) { | ||||
| @@ -483,7 +483,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| #endif /* not ESPHOME_CORES_SINGLE */ | ||||
| #endif /* not ESPHOME_THREAD_SINGLE */ | ||||
|  | ||||
|   // Cancel items in the main heap | ||||
|   for (auto &item : this->items_) { | ||||
| @@ -509,9 +509,9 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c | ||||
| uint64_t Scheduler::millis_64_(uint32_t now) { | ||||
|   // THREAD SAFETY NOTE: | ||||
|   // This function has three implementations, based on the precompiler flags | ||||
|   // - ESPHOME_CORES_SINGLE - Runs on single-core platforms (ESP8266, RP2040, etc.) | ||||
|   // - ESPHOME_CORES_MULTI_NO_ATOMICS - Runs on multi-core platforms without atomics (LibreTiny) | ||||
|   // - ESPHOME_CORES_MULTI_ATOMICS - Runs on multi-core platforms with atomics (ESP32, HOST, etc.) | ||||
|   // - ESPHOME_THREAD_SINGLE - Runs on single-threaded platforms (ESP8266, RP2040, etc.) | ||||
|   // - ESPHOME_THREAD_MULTI_NO_ATOMICS - Runs on multi-threaded platforms without atomics (LibreTiny) | ||||
|   // - ESPHOME_THREAD_MULTI_ATOMICS - Runs on multi-threaded platforms with atomics (ESP32, HOST, etc.) | ||||
|   // | ||||
|   // Make sure all changes are synchronized if you edit this function. | ||||
|   // | ||||
| @@ -520,7 +520,7 @@ uint64_t Scheduler::millis_64_(uint32_t now) { | ||||
|   // helps maintain accuracy. | ||||
|   // | ||||
|  | ||||
| #ifdef ESPHOME_CORES_SINGLE | ||||
| #ifdef ESPHOME_THREAD_SINGLE | ||||
|   // This is the single core implementation. | ||||
|   // | ||||
|   // Single-core platforms have no concurrency, so this is a simple implementation | ||||
| @@ -546,7 +546,7 @@ uint64_t Scheduler::millis_64_(uint32_t now) { | ||||
|   // Combine major (high 32 bits) and now (low 32 bits) into 64-bit time | ||||
|   return now + (static_cast<uint64_t>(major) << 32); | ||||
|  | ||||
| #elif defined(ESPHOME_CORES_MULTI_NO_ATOMICS) | ||||
| #elif defined(ESPHOME_THREAD_MULTI_NO_ATOMICS) | ||||
|   // This is the multi core no atomics implementation. | ||||
|   // | ||||
|   // Without atomics, this implementation uses locks more aggressively: | ||||
| @@ -595,7 +595,7 @@ uint64_t Scheduler::millis_64_(uint32_t now) { | ||||
|   // Combine major (high 32 bits) and now (low 32 bits) into 64-bit time | ||||
|   return now + (static_cast<uint64_t>(major) << 32); | ||||
|  | ||||
| #elif defined(ESPHOME_CORES_MULTI_ATOMICS) | ||||
| #elif defined(ESPHOME_THREAD_MULTI_ATOMICS) | ||||
|   // This is the multi core with atomics implementation. | ||||
|   // | ||||
|   // Uses atomic operations with acquire/release semantics to ensure coherent | ||||
| @@ -660,7 +660,7 @@ uint64_t Scheduler::millis_64_(uint32_t now) { | ||||
|  | ||||
| #else | ||||
| #error \ | ||||
|     "No platform threading model defined. One of ESPHOME_CORES_SINGLE, ESPHOME_CORES_MULTI_NO_ATOMICS, or ESPHOME_CORES_MULTI_ATOMICS must be defined." | ||||
|     "No platform threading model defined. One of ESPHOME_THREAD_SINGLE, ESPHOME_THREAD_MULTI_NO_ATOMICS, or ESPHOME_THREAD_MULTI_ATOMICS must be defined." | ||||
| #endif | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include <memory> | ||||
| #include <cstring> | ||||
| #include <deque> | ||||
| #ifdef ESPHOME_CORES_MULTI_ATOMICS | ||||
| #ifdef ESPHOME_THREAD_MULTI_ATOMICS | ||||
| #include <atomic> | ||||
| #endif | ||||
|  | ||||
| @@ -200,13 +200,13 @@ class Scheduler { | ||||
|   Mutex lock_; | ||||
|   std::vector<std::unique_ptr<SchedulerItem>> items_; | ||||
|   std::vector<std::unique_ptr<SchedulerItem>> to_add_; | ||||
| #ifndef ESPHOME_CORES_SINGLE | ||||
| #ifndef ESPHOME_THREAD_SINGLE | ||||
|   // Single-core platforms don't need the defer queue and save 40 bytes of RAM | ||||
|   std::deque<std::unique_ptr<SchedulerItem>> defer_queue_;  // FIFO queue for defer() calls | ||||
| #endif                                                      /* ESPHOME_CORES_SINGLE */ | ||||
| #endif                                                      /* ESPHOME_THREAD_SINGLE */ | ||||
|   uint32_t to_remove_{0}; | ||||
|  | ||||
| #ifdef ESPHOME_CORES_MULTI_ATOMICS | ||||
| #ifdef ESPHOME_THREAD_MULTI_ATOMICS | ||||
|   /* | ||||
|    * Multi-threaded platforms with atomic support: last_millis_ needs atomic for lock-free updates | ||||
|    * | ||||
| @@ -218,10 +218,10 @@ class Scheduler { | ||||
|    * it also observes the corresponding increment of `millis_major_`. | ||||
|    */ | ||||
|   std::atomic<uint32_t> last_millis_{0}; | ||||
| #else  /* not ESPHOME_CORES_MULTI_ATOMICS */ | ||||
| #else  /* not ESPHOME_THREAD_MULTI_ATOMICS */ | ||||
|   // Platforms without atomic support or single-threaded platforms | ||||
|   uint32_t last_millis_{0}; | ||||
| #endif /* else ESPHOME_CORES_MULTI_ATOMICS */ | ||||
| #endif /* else ESPHOME_THREAD_MULTI_ATOMICS */ | ||||
|  | ||||
|   /* | ||||
|    * Upper 16 bits of the 64-bit millis counter. Incremented only while holding | ||||
| @@ -229,11 +229,11 @@ class Scheduler { | ||||
|    * Ordering relative to `last_millis_` is provided by its release store and the | ||||
|    * corresponding acquire loads. | ||||
|    */ | ||||
| #ifdef ESPHOME_CORES_MULTI_ATOMICS | ||||
| #ifdef ESPHOME_THREAD_MULTI_ATOMICS | ||||
|   std::atomic<uint16_t> millis_major_{0}; | ||||
| #else  /* not ESPHOME_CORES_MULTI_ATOMICS */ | ||||
| #else  /* not ESPHOME_THREAD_MULTI_ATOMICS */ | ||||
|   uint16_t millis_major_{0}; | ||||
| #endif /* else ESPHOME_CORES_MULTI_ATOMICS */ | ||||
| #endif /* else ESPHOME_THREAD_MULTI_ATOMICS */ | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome | ||||
|   | ||||
		Reference in New Issue
	
	Block a user