diff --git a/CODEOWNERS b/CODEOWNERS index cd11c7796f..f85993bd87 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/esphome/components/ld2410/__init__.py b/esphome/components/ld2410/__init__.py index c58b9a4017..4918190179 100644 --- a/esphome/components/ld2410/__init__.py +++ b/esphome/components/ld2410/__init__.py @@ -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 diff --git a/esphome/components/ld2410/ld2410.cpp b/esphome/components/ld2410/ld2410.cpp index 8f1fb0ee9d..bb6d63a963 100644 --- a/esphome/components/ld2410/ld2410.cpp +++ b/esphome/components/ld2410/ld2410.cpp @@ -1,6 +1,5 @@ #include "ld2410.h" -#include #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,8 +302,10 @@ 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 - delay(50); // NOLINT + + if (command != CMD_ENABLE_CONF && command != CMD_DISABLE_CONF) { + delay(50); // NOLINT + } } void LD2410Component::handle_periodic_data_() { @@ -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::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::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 *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); + for (auto &gate_still_sensor : this->gate_still_sensors_) { + SAFE_PUBLISH_SENSOR_UNKNOWN(gate_still_sensor) } + 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); - } + if (this->out_pin_presence_status_binary_sensor_ != nullptr) { + 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(s); +} + +void LD2410Component::set_gate_still_sensor(uint8_t gate, sensor::Sensor *s) { + this->gate_still_sensors_[gate] = new SensorWithDedup(s); +} #endif } // namespace ld2410 diff --git a/esphome/components/ld2410/ld2410.h b/esphome/components/ld2410/ld2410.h index 8bd1dbcb5a..e9225ccfe4 100644 --- a/esphome/components/ld2410/ld2410.h +++ b/esphome/components/ld2410/ld2410.h @@ -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 + 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 gate_move_threshold_numbers_ = std::vector(TOTAL_GATES); - std::vector gate_still_threshold_numbers_ = std::vector(TOTAL_GATES); + std::array gate_move_threshold_numbers_{}; + std::array gate_still_threshold_numbers_{}; #endif #ifdef USE_SENSOR - std::vector gate_move_sensors_ = std::vector(TOTAL_GATES); - std::vector gate_still_sensors_ = std::vector(TOTAL_GATES); + std::array *, TOTAL_GATES> gate_move_sensors_{}; + std::array *, TOTAL_GATES> gate_still_sensors_{}; #endif }; diff --git a/esphome/components/ld24xx/__init__.py b/esphome/components/ld24xx/__init__.py new file mode 100644 index 0000000000..516af84856 --- /dev/null +++ b/esphome/components/ld24xx/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@kbx81"] diff --git a/esphome/components/ld24xx/ld24xx.h b/esphome/components/ld24xx/ld24xx.h new file mode 100644 index 0000000000..1cd5e01163 --- /dev/null +++ b/esphome/components/ld24xx/ld24xx.h @@ -0,0 +1,65 @@ +#pragma once + +#include "esphome/core/defines.h" + +#include + +#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 *name##_sensor_{nullptr}; \ +\ + public: \ + void set_##name##_sensor(sensor::Sensor *sensor) { \ + this->name##_sensor_ = new ld24xx::SensorWithDedup(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 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(state)); + } + } + + void publish_state_unknown() { + if (this->publish_dedup.next_unknown()) { + this->sens->publish_state(NAN); + } + } + + sensor::Sensor *sens; + Deduplicator publish_dedup; +}; +#endif +} // namespace ld24xx +} // namespace esphome