mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	[ld2410] Use `Deduplicator` for sensors (#9584)
				
					
				
			Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
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 | ||||
|   | ||||
| @@ -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 | ||||
		Reference in New Issue
	
	Block a user