From a0eafd362990c94e691ccd6beff050d7af8a4a34 Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sat, 4 Nov 2023 09:39:51 +0000 Subject: [PATCH 01/18] Add initial LD2450 component --- esphome/components/ld2450/__init__.py | 51 ++ esphome/components/ld2450/binary_sensor.py | 42 + esphome/components/ld2450/button/__init__.py | 45 + .../components/ld2450/button/reset_button.cpp | 9 + .../components/ld2450/button/reset_button.h | 18 + .../ld2450/button/restart_button.cpp | 9 + .../components/ld2450/button/restart_button.h | 18 + esphome/components/ld2450/ld2450.cpp | 767 ++++++++++++++++++ esphome/components/ld2450/ld2450.h | 269 ++++++ esphome/components/ld2450/number/__init__.py | 117 +++ .../ld2450/number/presence_timeout_number.cpp | 12 + .../ld2450/number/presence_timeout_number.h | 18 + .../ld2450/number/zone_coordinate_number.cpp | 14 + .../ld2450/number/zone_coordinate_number.h | 19 + esphome/components/ld2450/select/__init__.py | 59 ++ .../ld2450/select/baud_rate_select.cpp | 12 + .../ld2450/select/baud_rate_select.h | 18 + .../ld2450/select/zone_type_select.cpp | 12 + .../ld2450/select/zone_type_select.h | 18 + esphome/components/ld2450/sensor.py | 118 +++ esphome/components/ld2450/switch/__init__.py | 44 + .../ld2450/switch/bluetooth_switch.cpp | 12 + .../ld2450/switch/bluetooth_switch.h | 18 + .../ld2450/switch/multi_target_switch.cpp | 12 + .../ld2450/switch/multi_target_switch.h | 18 + esphome/components/ld2450/text_sensor.py | 59 ++ 26 files changed, 1808 insertions(+) create mode 100644 esphome/components/ld2450/__init__.py create mode 100644 esphome/components/ld2450/binary_sensor.py create mode 100644 esphome/components/ld2450/button/__init__.py create mode 100644 esphome/components/ld2450/button/reset_button.cpp create mode 100644 esphome/components/ld2450/button/reset_button.h create mode 100644 esphome/components/ld2450/button/restart_button.cpp create mode 100644 esphome/components/ld2450/button/restart_button.h create mode 100644 esphome/components/ld2450/ld2450.cpp create mode 100644 esphome/components/ld2450/ld2450.h create mode 100644 esphome/components/ld2450/number/__init__.py create mode 100644 esphome/components/ld2450/number/presence_timeout_number.cpp create mode 100644 esphome/components/ld2450/number/presence_timeout_number.h create mode 100644 esphome/components/ld2450/number/zone_coordinate_number.cpp create mode 100644 esphome/components/ld2450/number/zone_coordinate_number.h create mode 100644 esphome/components/ld2450/select/__init__.py create mode 100644 esphome/components/ld2450/select/baud_rate_select.cpp create mode 100644 esphome/components/ld2450/select/baud_rate_select.h create mode 100644 esphome/components/ld2450/select/zone_type_select.cpp create mode 100644 esphome/components/ld2450/select/zone_type_select.h create mode 100644 esphome/components/ld2450/sensor.py create mode 100644 esphome/components/ld2450/switch/__init__.py create mode 100644 esphome/components/ld2450/switch/bluetooth_switch.cpp create mode 100644 esphome/components/ld2450/switch/bluetooth_switch.h create mode 100644 esphome/components/ld2450/switch/multi_target_switch.cpp create mode 100644 esphome/components/ld2450/switch/multi_target_switch.h create mode 100644 esphome/components/ld2450/text_sensor.py diff --git a/esphome/components/ld2450/__init__.py b/esphome/components/ld2450/__init__.py new file mode 100644 index 0000000000..37f68a8f3e --- /dev/null +++ b/esphome/components/ld2450/__init__.py @@ -0,0 +1,51 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import uart +from esphome.const import ( + CONF_ID, + CONF_THROTTLE, +) + +DEPENDENCIES = ["uart"] +CODEOWNERS = ["@hareeshmu"] +MULTI_CONF = True + +ld2450_ns = cg.esphome_ns.namespace("ld2450") +LD2450Component = ld2450_ns.class_("LD2450Component", cg.Component, uart.UARTDevice) + +CONF_LD2450_ID = "ld2450_id" + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(LD2450Component), + cv.Optional(CONF_THROTTLE, default="1000ms"): cv.All( + cv.positive_time_period_milliseconds, + cv.Range(min=cv.TimePeriod(milliseconds=1)), + ), + } + ) + .extend(uart.UART_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) + +LD2450BaseSchema = cv.Schema( + { + cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), + }, +) + +FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( + "ld2450", + require_tx=True, + require_rx=True, + parity="NONE", + stop_bits=1, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) + cg.add(var.set_throttle(config[CONF_THROTTLE])) diff --git a/esphome/components/ld2450/binary_sensor.py b/esphome/components/ld2450/binary_sensor.py new file mode 100644 index 0000000000..d16d1f79cb --- /dev/null +++ b/esphome/components/ld2450/binary_sensor.py @@ -0,0 +1,42 @@ +import esphome.codegen as cg +from esphome.components import binary_sensor +import esphome.config_validation as cv +from esphome.const import ( + DEVICE_CLASS_MOTION, + DEVICE_CLASS_OCCUPANCY, +) +from . import CONF_LD2450_ID, LD2450Component + +DEPENDENCIES = ["ld2450"] +CONF_HAS_TARGET = "has_target" +CONF_HAS_MOVING_TARGET = "has_moving_target" +CONF_HAS_STILL_TARGET = "has_still_target" + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), + cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_OCCUPANCY, + icon="mdi:shield-account", + ), + cv.Optional(CONF_HAS_MOVING_TARGET): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_MOTION, + icon="mdi:target-account", + ), + cv.Optional(CONF_HAS_STILL_TARGET): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_OCCUPANCY, + icon="mdi:meditation", + ), +} + + +async def to_code(config): + ld2450_component = await cg.get_variable(config[CONF_LD2450_ID]) + if has_target_config := config.get(CONF_HAS_TARGET): + sens = await binary_sensor.new_binary_sensor(has_target_config) + cg.add(ld2450_component.set_target_binary_sensor(sens)) + if has_moving_target_config := config.get(CONF_HAS_MOVING_TARGET): + sens = await binary_sensor.new_binary_sensor(has_moving_target_config) + cg.add(ld2450_component.set_moving_target_binary_sensor(sens)) + if has_still_target_config := config.get(CONF_HAS_STILL_TARGET): + sens = await binary_sensor.new_binary_sensor(has_still_target_config) + cg.add(ld2450_component.set_still_target_binary_sensor(sens)) diff --git a/esphome/components/ld2450/button/__init__.py b/esphome/components/ld2450/button/__init__.py new file mode 100644 index 0000000000..973a9694c2 --- /dev/null +++ b/esphome/components/ld2450/button/__init__.py @@ -0,0 +1,45 @@ +import esphome.codegen as cg +from esphome.components import button +import esphome.config_validation as cv +from esphome.const import ( + DEVICE_CLASS_RESTART, + ENTITY_CATEGORY_DIAGNOSTIC, + ENTITY_CATEGORY_CONFIG, + ICON_RESTART, + ICON_RESTART_ALERT, +) +from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns + +ResetButton = ld2450_ns.class_("ResetButton", button.Button) +RestartButton = ld2450_ns.class_("RestartButton", button.Button) + +CONF_FACTORY_RESET = "factory_reset" +CONF_RESTART = "restart" + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), + cv.Optional(CONF_FACTORY_RESET): button.button_schema( + ResetButton, + device_class=DEVICE_CLASS_RESTART, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_RESTART_ALERT, + ), + cv.Optional(CONF_RESTART): button.button_schema( + RestartButton, + device_class=DEVICE_CLASS_RESTART, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + icon=ICON_RESTART, + ), +} + + +async def to_code(config): + ld2450_component = await cg.get_variable(config[CONF_LD2450_ID]) + if factory_reset_config := config.get(CONF_FACTORY_RESET): + b = await button.new_button(factory_reset_config) + await cg.register_parented(b, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_reset_button(b)) + if restart_config := config.get(CONF_RESTART): + b = await button.new_button(restart_config) + await cg.register_parented(b, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_restart_button(b)) diff --git a/esphome/components/ld2450/button/reset_button.cpp b/esphome/components/ld2450/button/reset_button.cpp new file mode 100644 index 0000000000..e96ec99cc5 --- /dev/null +++ b/esphome/components/ld2450/button/reset_button.cpp @@ -0,0 +1,9 @@ +#include "reset_button.h" + +namespace esphome { +namespace ld2450 { + +void ResetButton::press_action() { this->parent_->factory_reset(); } + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/button/reset_button.h b/esphome/components/ld2450/button/reset_button.h new file mode 100644 index 0000000000..73804fa6d6 --- /dev/null +++ b/esphome/components/ld2450/button/reset_button.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/button/button.h" +#include "../ld2450.h" + +namespace esphome { +namespace ld2450 { + +class ResetButton : public button::Button, public Parented { + public: + ResetButton() = default; + + protected: + void press_action() override; +}; + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/button/restart_button.cpp b/esphome/components/ld2450/button/restart_button.cpp new file mode 100644 index 0000000000..ee2f5ac12f --- /dev/null +++ b/esphome/components/ld2450/button/restart_button.cpp @@ -0,0 +1,9 @@ +#include "restart_button.h" + +namespace esphome { +namespace ld2450 { + +void RestartButton::press_action() { this->parent_->restart_and_read_all_info(); } + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/button/restart_button.h b/esphome/components/ld2450/button/restart_button.h new file mode 100644 index 0000000000..a44ae5a4d2 --- /dev/null +++ b/esphome/components/ld2450/button/restart_button.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/button/button.h" +#include "../ld2450.h" + +namespace esphome { +namespace ld2450 { + +class RestartButton : public button::Button, public Parented { + public: + RestartButton() = default; + + protected: + void press_action() override; +}; + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp new file mode 100644 index 0000000000..572f1b99ea --- /dev/null +++ b/esphome/components/ld2450/ld2450.cpp @@ -0,0 +1,767 @@ +#include "ld2450.h" +#include +#ifdef USE_NUMBER +#include "esphome/components/number/number.h" +#endif +#ifdef USE_SENSOR +#include "esphome/components/sensor/sensor.h" +#endif +#include "esphome/core/component.h" + +#define highbyte(val) (uint8_t)((val) >> 8) +#define lowbyte(val) (uint8_t)((val) &0xff) + +namespace esphome { +namespace ld2450 { + +static const char *const TAG = "ld2450"; + +LD2450Component::LD2450Component() {} + +void LD2450Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up HLK-LD2450"); + this->pref_ = global_preferences->make_preference(this->presence_timeout_number_->get_object_id_hash()); + this->set_presence_timeout(); + this->read_all_info(); + ESP_LOGCONFIG(TAG, "Mac Address : %s", const_cast(this->mac_.c_str())); + ESP_LOGCONFIG(TAG, "Firmware Version : %s", const_cast(this->version_.c_str())); + ESP_LOGCONFIG(TAG, "HLK-LD2450 setup complete"); + ESP_LOGCONFIG(TAG, "Registering services"); + register_service(&::esphome::ld2450::LD2450Component::on_set_radar_zone_, "set_radar_zone", + { + "zone_type", + "zone1_x1", + "zone1_y1", + "zone1_x2", + "zone1_y2", + "zone2_x1", + "zone2_y1", + "zone2_x2", + "zone2_y2", + "zone3_x1", + "zone3_y1", + "zone3_x2", + "zone3_y2", + }); + register_service(&::esphome::ld2450::LD2450Component::on_reset_radar_zone_, "reset_radar_zone"); + ESP_LOGCONFIG(TAG, "Services registration complete"); +} + +void LD2450Component::dump_config() { + ESP_LOGCONFIG(TAG, "HLK-LD2450 Human motion tracking radar module:"); +#ifdef USE_BINARY_SENSOR + LOG_BINARY_SENSOR(" ", "TargetBinarySensor", this->target_binary_sensor_); + LOG_BINARY_SENSOR(" ", "MovingTargetBinarySensor", this->moving_target_binary_sensor_); + LOG_BINARY_SENSOR(" ", "StillTargetBinarySensor", this->still_target_binary_sensor_); +#endif +#ifdef USE_SWITCH + LOG_SWITCH(" ", "BluetoothSwitch", this->bluetooth_switch_); + LOG_SWITCH(" ", "MultiTargetSwitch", this->multi_target_switch_); +#endif +#ifdef USE_BUTTON + LOG_BUTTON(" ", "ResetButton", this->reset_button_); + LOG_BUTTON(" ", "RestartButton", this->restart_button_); +#endif +#ifdef USE_SENSOR + LOG_SENSOR(" ", "TargetCountSensor", this->target_count_sensor_); + LOG_SENSOR(" ", "StillTargetCountSensor", this->still_target_count_sensor_); + LOG_SENSOR(" ", "MovingTargetCountSensor", this->moving_target_count_sensor_); + for (sensor::Sensor *s : this->move_x_sensors_) { + LOG_SENSOR(" ", "NthTargetXSensor", s); + } + for (sensor::Sensor *s : this->move_y_sensors_) { + LOG_SENSOR(" ", "NthTargetYSensor", s); + } + for (sensor::Sensor *s : this->move_speed_sensors_) { + LOG_SENSOR(" ", "NthTargetSpeedSensor", s); + } + for (sensor::Sensor *s : this->move_angle_sensors_) { + LOG_SENSOR(" ", "NthTargetAngleSensor", s); + } + for (sensor::Sensor *s : this->move_distance_sensors_) { + LOG_SENSOR(" ", "NthTargetDistanceSensor", s); + } + for (sensor::Sensor *s : this->move_resolution_sensors_) { + LOG_SENSOR(" ", "NthTargetResolutionSensor", s); + } +#endif +#ifdef USE_TEXT_SENSOR + LOG_TEXT_SENSOR(" ", "VersionTextSensor", this->version_text_sensor_); + LOG_TEXT_SENSOR(" ", "MacTextSensor", this->mac_text_sensor_); + for (text_sensor::TextSensor *s : this->direction_text_sensors_) { + LOG_TEXT_SENSOR(" ", "NthDirectionTextSensor", s); + } +#endif +#ifdef USE_NUMBER + for (number::Number *n : this->zone_x1_numbers_) { + LOG_NUMBER(" ", "ZoneX1Number", n); + } + for (number::Number *n : this->zone_y1_numbers_) { + LOG_NUMBER(" ", "ZoneY1Number", n); + } + for (number::Number *n : this->zone_x2_numbers_) { + LOG_NUMBER(" ", "ZoneX2Number", n); + } + for (number::Number *n : this->zone_y2_numbers_) { + LOG_NUMBER(" ", "ZoneY2Number", n); + } +#endif +#ifdef USE_SELECT + LOG_SELECT(" ", "BaudRateSelect", this->baud_rate_select_); + LOG_SELECT(" ", "ZoneTypeSelect", this->zone_type_select_); +#endif +#ifdef USE_NUMBER + LOG_NUMBER(" ", "PresenceTimeoutNumber", this->presence_timeout_number_); +#endif + this->read_all_info(); + ESP_LOGCONFIG(TAG, " Throttle_ : %ums", this->throttle_); + ESP_LOGCONFIG(TAG, " MAC Address : %s", const_cast(this->mac_.c_str())); + ESP_LOGCONFIG(TAG, " Firmware Version : %s", const_cast(this->version_.c_str())); +} + +void LD2450Component::loop() { + const int max_line_length = 80; + static uint8_t buffer[max_line_length]; + + while (available()) { + this->readline_(read(), buffer, max_line_length); + } +} + +// Service reset_radar_zone +void LD2450Component::on_reset_radar_zone_() { + this->zone_type_ = 0; + for (auto &i : zone_config_) { + i.x1 = 0; + i.y1 = 0; + i.x2 = 0; + i.y2 = 0; + } + this->send_set_zone_command_(); +} + +// Service set_radar_zone +void LD2450Component::on_set_radar_zone_(int zone_type, int zone1_x1, int zone1_y1, int zone1_x2, int zone1_y2, + int zone2_x1, int zone2_y1, int zone2_x2, int zone2_y2, int zone3_x1, + int zone3_y1, int zone3_x2, int zone3_y2) { + this->zone_type_ = zone_type; + int zone_parameters[12] = {zone1_x1, zone1_y1, zone1_x2, zone1_y2, zone2_x1, zone2_y1, + zone2_x2, zone2_y2, zone3_x1, zone3_y1, zone3_x2, zone3_y2}; + for (int i = 0; i < MAX_ZONES; i++) { + zone_config_[i].x1 = zone_parameters[i * 4]; + zone_config_[i].y1 = zone_parameters[i * 4 + 1]; + zone_config_[i].x2 = zone_parameters[i * 4 + 2]; + zone_config_[i].y2 = zone_parameters[i * 4 + 3]; + } + this->send_set_zone_command_(); +} + +// Set Zone on LD2450 Sensor +void LD2450Component::send_set_zone_command_() { + uint8_t cmd_value[26] = {}; + uint8_t zone_type_bytes[2] = {static_cast(this->zone_type_), 0x00}; + uint8_t area_config[24] = {}; + for (int i = 0; i < MAX_ZONES; i++) { + int values[4] = {zone_config_[i].x1, zone_config_[i].y1, zone_config_[i].x2, zone_config_[i].y2}; + this->convert_int_values_to_hex_(values, area_config + (i * 8)); + } + std::memcpy(cmd_value, zone_type_bytes, 2); + std::memcpy(cmd_value + 2, area_config, 24); + set_config_mode_(true); + send_command_(CMD_SET_ZONE, cmd_value, 26); + set_config_mode_(false); +} + +// Convert signed int to HEX high and low bytes +void LD2450Component::convert_int_values_to_hex_(const int *values, uint8_t *bytes) { + for (int i = 0; i < 4; i++) { + std::string temp_hex = convert_signed_int_to_hex_(values[i]); + bytes[i * 2] = std::stoi(temp_hex.substr(2, 2), nullptr, 16); // Store high byte + bytes[i * 2 + 1] = std::stoi(temp_hex.substr(0, 2), nullptr, 16); // Store low byte + } +} + +// Check presense timeout to reset presence status +bool LD2450Component::get_timeout_status_(int32_t check_millis) { + if (check_millis == 0) + return true; + if (this->timeout_ == 0) + this->timeout_ = this->convert_seconds_to_ms(DEFAULT_PRESENCE_TIMEOUT); + int32_t current_millis = millis(); + int32_t timeout = this->timeout_; + return current_millis - check_millis >= timeout; +} + +// Extract, store and publish zone details LD2450 buffer +void LD2450Component::process_zone_(uint8_t *buffer) { + uint8_t index, start; + for (index = 0; index < MAX_ZONES; index++) { + start = 12 + index * 8; + zone_config_[index].x1 = this->hex_to_signed_int_(buffer, start); + zone_config_[index].y1 = this->hex_to_signed_int_(buffer, start + 2); + zone_config_[index].x2 = this->hex_to_signed_int_(buffer, start + 4); + zone_config_[index].y2 = this->hex_to_signed_int_(buffer, start + 6); +#ifdef USE_NUMBER + this->zone_x1_numbers_[index]->publish_state(zone_config_[index].x1); + this->zone_y1_numbers_[index]->publish_state(zone_config_[index].y1); + this->zone_x2_numbers_[index]->publish_state(zone_config_[index].x2); + this->zone_y2_numbers_[index]->publish_state(zone_config_[index].y2); +#endif + } +} + +// Read all info from LD2450 buffer +void LD2450Component::read_all_info() { + this->set_config_mode_(true); + this->get_version_(); + this->get_mac_(); + this->query_zone_(); + this->set_config_mode_(false); +#ifdef USE_SELECT + const auto baud_rate = std::to_string(this->parent_->get_baud_rate()); + if (this->baud_rate_select_ != nullptr && this->baud_rate_select_->state != baud_rate) { + this->baud_rate_select_->publish_state(baud_rate); + } + this->publish_zone_type(); +#endif +} + +// Read zone info from LD2450 buffer +void LD2450Component::query_zone_info() { + this->set_config_mode_(true); + this->query_zone_(); + this->set_config_mode_(false); +} + +// Restart LD2450 and read all info from buffer +void LD2450Component::restart_and_read_all_info() { + this->set_config_mode_(true); + this->restart_(); + this->set_timeout(1000, [this]() { this->read_all_info(); }); +} + +// Send command with values to LD2450 +void LD2450Component::send_command_(uint8_t command, const uint8_t *command_value, int command_value_len) { + ESP_LOGV(TAG, "Sending COMMAND %02X", command); + // frame start bytes + this->write_array(CMD_FRAME_HEADER, 4); + // length bytes + int len = 2; + if (command_value != nullptr) + len += command_value_len; + this->write_byte(lowbyte(len)); + this->write_byte(highbyte(len)); + // command + this->write_byte(lowbyte(command)); + this->write_byte(highbyte(command)); + // command value bytes + if (command_value != nullptr) { + for (int i = 0; i < command_value_len; i++) { + this->write_byte(command_value[i]); + } + } + // frame end bytes + this->write_array(CMD_FRAME_END, 4); + // FIXME to remove + delay(50); // NOLINT +} + +// LD2450 Radar data output protocol +// Eg: [AA FF 03 00] [0E 03 B1 86 10 00 40 01] [00 00 00 00 00 00 00 00] [00 00 00 00 00 00 00 00] [55 CC] +// Header Target 1 Target 2 Target 3 End +void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { + if (len < 29) + return; // 4 frame start bytes + 8 x 3 Target Data + 2 frame end bytes + if (buffer[0] != 0xAA || buffer[1] != 0xFF || buffer[2] != 0x03 || buffer[3] != 0x00) // check 4 frame start bytes + return; + if (buffer[len - 2] != 0x55 || buffer[len - 1] != 0xCC) // Check 2 end frame bytes + return; // frame end=0x55 0xCC + int32_t current_millis = millis(); + if (current_millis - uptime_millis_ < START_DELAY) { + ESP_LOGV(TAG, "Waiting for Delayed Start: %d", START_DELAY); + return; + } + if (current_millis - this->last_periodic_millis_ < this->throttle_) { + ESP_LOGV(TAG, "Throttling: %d", this->throttle_); + return; + } + + this->last_periodic_millis_ = current_millis; + + int16_t target_count = 0; + int16_t still_target_count = 0; + int16_t moving_target_count = 0; + int16_t start; + int16_t val; + uint8_t index; + int16_t tx; + int16_t ty; + int16_t td; + int16_t ts; + int16_t angle; + std::string direction; + +#ifdef USE_SENSOR + // Loop thru targets + // X + for (index = 0; index < MAX_TARGETS; index++) { + start = TARGET_X + index * 8; + sensor::Sensor *sx = this->move_x_sensors_[index]; + if (sx != nullptr) { + val = this->decode_coordinate_(buffer[start], buffer[start + 1]); + tx = val; + if (sx->get_state() != val) { + sx->publish_state(val); + } + } + // Y + start = TARGET_Y + index * 8; + sensor::Sensor *sy = this->move_y_sensors_[index]; + if (sy != nullptr) { + val = this->decode_coordinate_(buffer[start], buffer[start + 1]); + ty = val; + if (sy->get_state() != val) { + sy->publish_state(val); + } + } + // SPEED + start = TARGET_SPEED + index * 8; + sensor::Sensor *ss = this->move_speed_sensors_[index]; + if (ss != nullptr) { + val = this->decode_speed_(buffer[start], buffer[start + 1]); + ts = val; + if (val > 0) + moving_target_count++; + if (ss->get_state() != val) { + ss->publish_state(val); + } + } + // RESOLUTION + start = TARGET_RESOLUTION + index * 8; + sensor::Sensor *sr = this->move_resolution_sensors_[index]; + if (sr != nullptr) { + val = (buffer[start + 1] << 8) | buffer[start]; + if (sr->get_state() != val) { + sr->publish_state(val); + } + } + // DISTANCE + sensor::Sensor *sd = this->move_distance_sensors_[index]; + if (sd != nullptr) { + val = (uint16_t) sqrt( + pow(this->decode_coordinate_(buffer[TARGET_X + index * 8], buffer[(TARGET_X + index * 8) + 1]), 2) + + pow(this->decode_coordinate_(buffer[TARGET_Y + index * 8], buffer[(TARGET_Y + index * 8) + 1]), 2)); + td = val; + if (val > 0) + target_count++; + + if (sd->get_state() != val) { + sd->publish_state(val); + } + } + // ANGLE + angle = calculate_angle_(static_cast(ty), static_cast(td)); + if (tx > 0) { + angle = angle * -1; + } + sensor::Sensor *sa = this->move_angle_sensors_[index]; + if (sa != nullptr) { + if (sa->get_state() != angle) { + sa->publish_state(angle); + } + } +#endif + // DIRECTION +#ifdef USE_TEXT_SENSOR + direction = get_direction_(ts); + if (td == 0) + direction = "NA"; + text_sensor::TextSensor *tsd = this->direction_text_sensors_[index]; + if (tsd != nullptr) { + if (tsd->get_state() != direction) { + tsd->publish_state(direction); + } + } +#endif + } // End loop thru targets + + still_target_count = target_count - moving_target_count; + +#ifdef USE_SENSOR + // Target Count + if (this->target_count_sensor_ != nullptr) { + if (this->target_count_sensor_->get_state() != target_count) { + this->target_count_sensor_->publish_state(target_count); + } + } + // Still Target Count + if (this->still_target_count_sensor_ != nullptr) { + if (this->still_target_count_sensor_->get_state() != still_target_count) { + this->still_target_count_sensor_->publish_state(still_target_count); + } + } + // Moving Target Count + if (this->moving_target_count_sensor_ != nullptr) { + if (this->moving_target_count_sensor_->get_state() != moving_target_count) { + this->moving_target_count_sensor_->publish_state(moving_target_count); + } + } +#endif + +#ifdef USE_BINARY_SENSOR + // Target Presence + if (this->target_binary_sensor_ != nullptr) { + if (target_count > 0) { + this->target_binary_sensor_->publish_state(true); + } else { + if (this->get_timeout_status_(this->presence_millis_)) { + this->target_binary_sensor_->publish_state(false); + } else { + ESP_LOGV(TAG, "Clear Presence Waiting Timeout: %d", this->timeout_); + } + } + } + // Moving Target Presence + if (this->moving_target_binary_sensor_ != nullptr) { + if (moving_target_count > 0) { + this->moving_target_binary_sensor_->publish_state(true); + } else { + if (this->get_timeout_status_(this->moving_presence_millis_)) { + this->moving_target_binary_sensor_->publish_state(false); + } + } + } + // Still Target Presence + if (this->still_target_binary_sensor_ != nullptr) { + if (still_target_count > 0) { + this->still_target_binary_sensor_->publish_state(true); + } else { + if (this->get_timeout_status_(this->still_presence_millis_)) { + this->still_target_binary_sensor_->publish_state(false); + } + } + } +#endif + // For presence timeout check + if (target_count > 0) { + this->presence_millis_ = millis(); + } + if (moving_target_count > 0) { + this->moving_presence_millis_ = millis(); + } + if (still_target_count > 0) { + this->still_presence_millis_ = millis(); + } +} + +const char VERSION_FMT[] = "%u.%02X.%02X%02X%02X%02X"; + +std::string format_version(uint8_t *buffer) { + std::string::size_type version_size = 256; + std::string version; + do { + version.resize(version_size + 1); + version_size = std::snprintf(&version[0], version.size(), VERSION_FMT, buffer[13], buffer[12], buffer[17], + buffer[16], buffer[15], buffer[14]); + } while (version_size + 1 > version.size()); + version.resize(version_size); + return version; +} + +const char MAC_FMT[] = "%02X:%02X:%02X:%02X:%02X:%02X"; + +const std::string UNKNOWN_MAC("unknown"); +const std::string NO_MAC("08:05:04:03:02:01"); + +std::string format_mac(uint8_t *buffer) { + std::string::size_type mac_size = 256; + std::string mac; + do { + mac.resize(mac_size + 1); + mac_size = std::snprintf(&mac[0], mac.size(), MAC_FMT, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], + buffer[15]); + } while (mac_size + 1 > mac.size()); + mac.resize(mac_size); + if (mac == NO_MAC) + return UNKNOWN_MAC; + return mac; +} + +bool LD2450Component::handle_ack_data_(uint8_t *buffer, int len) { + ESP_LOGD(TAG, "Handling ACK DATA for COMMAND %02X", buffer[COMMAND]); + if (len < 10) { + ESP_LOGE(TAG, "Error with last command: Incorrect length"); + return true; + } + if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // check 4 frame start bytes + ESP_LOGE(TAG, "Error with last command: Incorrect Header. COMMAND: %02X", buffer[COMMAND]); + return true; + } + if (buffer[COMMAND_STATUS] != 0x01) { + ESP_LOGE(TAG, "Error with last command: Status != 0x01"); + return true; + } + if (this->two_byte_to_int_(buffer[8], buffer[9]) != 0x00) { + ESP_LOGE(TAG, "Error with last command, last buffer was: %u , %u", buffer[8], buffer[9]); + return true; + } + switch (buffer[COMMAND]) { + case lowbyte(CMD_ENABLE_CONF): + ESP_LOGV(TAG, "Handled Enable conf command"); + break; + case lowbyte(CMD_DISABLE_CONF): + ESP_LOGV(TAG, "Handled Disabled conf command"); + break; + case lowbyte(CMD_SET_BAUD_RATE): + ESP_LOGV(TAG, "Handled baud rate change command"); +#ifdef USE_SELECT + if (this->baud_rate_select_ != nullptr) { + ESP_LOGV(TAG, "Change baud rate component config to %s and reinstall", this->baud_rate_select_->state.c_str()); + } +#endif + break; + case lowbyte(CMD_VERSION): + this->version_ = format_version(buffer); + ESP_LOGV(TAG, "LD2450 Firmware Version: %s", const_cast(this->version_.c_str())); +#ifdef USE_TEXT_SENSOR + if (this->version_text_sensor_ != nullptr) { + this->version_text_sensor_->publish_state(this->version_); + } +#endif + break; + case lowbyte(CMD_MAC): + if (len < 20) + return false; + this->mac_ = format_mac(buffer); + ESP_LOGV(TAG, "LD2450 MAC Address: %s", const_cast(this->mac_.c_str())); +#ifdef USE_TEXT_SENSOR + if (this->mac_text_sensor_ != nullptr) { + this->mac_text_sensor_->publish_state(this->mac_); + } +#endif +#ifdef USE_SWITCH + if (this->bluetooth_switch_ != nullptr) { + this->bluetooth_switch_->publish_state(this->mac_ != UNKNOWN_MAC); + } +#endif + break; + case lowbyte(CMD_BLUETOOTH): + ESP_LOGV(TAG, "Handled Bluetooth command"); + break; + case lowbyte(CMD_SINGLE_TARGET): + ESP_LOGV(TAG, "Handled Single Target conf command"); +#ifdef USE_SWITCH + if (this->multi_target_switch_ != nullptr) { + this->multi_target_switch_->publish_state(false); + } +#endif + break; + case lowbyte(CMD_MULTI_TARGET): + ESP_LOGV(TAG, "Handled Multi Target conf command"); +#ifdef USE_SWITCH + if (this->multi_target_switch_ != nullptr) { + this->multi_target_switch_->publish_state(true); + } +#endif + break; + case lowbyte(CMD_QUERY_ZONE): + ESP_LOGV(TAG, "Handled Query Zone conf command"); + this->zone_type_ = std::stoi(std::to_string(buffer[10]), nullptr, 16); + this->publish_zone_type(); +#ifdef USE_SELECT + if (this->zone_type_select_ != nullptr) { + ESP_LOGV(TAG, "Change Zone Type component config to: %s", this->zone_type_select_->state.c_str()); + } +#endif + if (buffer[10] == 0x00) { + ESP_LOGV(TAG, "Zone: Disabled"); + } + if (buffer[10] == 0x01) { + ESP_LOGV(TAG, "Zone: Area Detection"); + } + if (buffer[10] == 0x02) { + ESP_LOGV(TAG, "Zone: Area Filter"); + } + this->process_zone_(buffer); + break; + case lowbyte(CMD_SET_ZONE): + ESP_LOGV(TAG, "Handled SET Zone conf command"); + this->query_zone_info(); + break; + default: + break; + } + return true; +} + +// Read LD2450 buffer data +void LD2450Component::readline_(int readch, uint8_t *buffer, int len) { + static int pos = 0; + if (readch >= 0) { + if (pos < len - 1) { + buffer[pos++] = readch; + buffer[pos] = 0; + } else { + pos = 0; + } + if (pos >= 4) { + if (buffer[pos - 2] == 0x55 && buffer[pos - 1] == 0xCC) { + ESP_LOGV(TAG, "Handle Periodic Radar Data"); + this->handle_periodic_data_(buffer, pos); + pos = 0; // Reset position index ready for next time + } else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 && + buffer[pos - 1] == 0x01) { + ESP_LOGV(TAG, "Handle Commad ACK Data"); + if (this->handle_ack_data_(buffer, pos)) { + pos = 0; // Reset position index ready for next time + } else { + ESP_LOGV(TAG, "Command ACK Data incomplete"); + } + } + } + } +} + +// Set Config Mode - Pre-requisite sending commands +void LD2450Component::set_config_mode_(bool enable) { + uint8_t cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF; + uint8_t cmd_value[2] = {0x01, 0x00}; + this->send_command_(cmd, enable ? cmd_value : nullptr, 2); +} + +// Set Bluetooth Enable/Disable +void LD2450Component::set_bluetooth(bool enable) { + this->set_config_mode_(true); + uint8_t enable_cmd_value[2] = {0x01, 0x00}; + uint8_t disable_cmd_value[2] = {0x00, 0x00}; + this->send_command_(CMD_BLUETOOTH, enable ? enable_cmd_value : disable_cmd_value, 2); + this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); +} + +// Set Baud rate +void LD2450Component::set_baud_rate(const std::string &state) { + this->set_config_mode_(true); + uint8_t cmd_value[2] = {BAUD_RATE_ENUM_TO_INT.at(state), 0x00}; + this->send_command_(CMD_SET_BAUD_RATE, cmd_value, 2); + this->set_timeout(200, [this]() { this->restart_(); }); +} + +// Set Zone Type - one of: Disabled, Detection, Filter +void LD2450Component::set_zone_type(const std::string &state) { + ESP_LOGV(TAG, "Set zone type: %s", state.c_str()); + uint8_t zone_type = ZONE_TYPE_ENUM_TO_INT.at(state); + this->zone_type_ = zone_type; + this->send_set_zone_command_(); +} + +// Publish Zone Type to Select component +void LD2450Component::publish_zone_type() { + std::string zone_type = ZONE_TYPE_INT_TO_ENUM.at(static_cast(this->zone_type_)); +#ifdef USE_SELECT + if (this->zone_type_select_ != nullptr && this->zone_type_select_->state != zone_type) { + this->zone_type_select_->publish_state(zone_type); + } +#endif +} + +// Set Single/Multiplayer +void LD2450Component::set_multi_target(bool enable) { + this->set_config_mode_(true); + uint8_t cmd = enable ? CMD_MULTI_TARGET : CMD_SINGLE_TARGET; + this->send_command_(cmd, nullptr, 0); + this->set_config_mode_(false); + // this->set_timeout(200, [this]() { this->read_all_info(); }); +} + +// LD2450 factory reset +void LD2450Component::factory_reset() { + this->set_config_mode_(true); + this->send_command_(CMD_RESET, nullptr, 0); + this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); +} + +// Restart LD2450 module +void LD2450Component::restart_() { this->send_command_(CMD_RESTART, nullptr, 0); } + +// Get LD2450 firmware version +void LD2450Component::get_version_() { this->send_command_(CMD_VERSION, nullptr, 0); } + +// Get LD2450 mac address +void LD2450Component::get_mac_() { + uint8_t cmd_value[2] = {0x01, 0x00}; + this->send_command_(CMD_MAC, cmd_value, 2); +} + +// Query Zone info from LD2450 +void LD2450Component::query_zone_() { this->send_command_(CMD_QUERY_ZONE, nullptr, 0); } + +#ifdef USE_SENSOR +void LD2450Component::set_move_x_sensor(int target, sensor::Sensor *s) { this->move_x_sensors_[target] = s; } +void LD2450Component::set_move_y_sensor(int target, sensor::Sensor *s) { this->move_y_sensors_[target] = s; } +void LD2450Component::set_move_speed_sensor(int target, sensor::Sensor *s) { this->move_speed_sensors_[target] = s; } +void LD2450Component::set_move_angle_sensor(int target, sensor::Sensor *s) { this->move_angle_sensors_[target] = s; } +void LD2450Component::set_move_distance_sensor(int target, sensor::Sensor *s) { + this->move_distance_sensors_[target] = s; +} +void LD2450Component::set_move_resolution_sensor(int target, sensor::Sensor *s) { + this->move_resolution_sensors_[target] = s; +} +void LD2450Component::set_direction_text_sensor(int target, text_sensor::TextSensor *s) { + this->direction_text_sensors_[target] = s; +} +#endif + +// Send Zone coordinates data to LD2450 +#ifdef USE_NUMBER +void LD2450Component::set_zone_coordinate(uint8_t zone) { + number::Number *x1sens = this->zone_x1_numbers_[zone]; + number::Number *y1sens = this->zone_y1_numbers_[zone]; + number::Number *x2sens = this->zone_x2_numbers_[zone]; + number::Number *y2sens = this->zone_y2_numbers_[zone]; + if (!x1sens->has_state() || !y1sens->has_state() || !x2sens->has_state() || !y2sens->has_state()) { + return; + } + zone_config_[zone].x1 = static_cast(x1sens->state); + zone_config_[zone].y1 = static_cast(y1sens->state); + zone_config_[zone].x2 = static_cast(x2sens->state); + zone_config_[zone].y2 = static_cast(y2sens->state); + this->send_set_zone_command_(); +} + +void LD2450Component::set_zone_x1_number(int zone, number::Number *n) { this->zone_x1_numbers_[zone] = n; } +void LD2450Component::set_zone_y1_number(int zone, number::Number *n) { this->zone_y1_numbers_[zone] = n; } +void LD2450Component::set_zone_x2_number(int zone, number::Number *n) { this->zone_x2_numbers_[zone] = n; } +void LD2450Component::set_zone_y2_number(int zone, number::Number *n) { this->zone_y2_numbers_[zone] = n; } +#endif + +// Set Presence Timeout load and save from flash +#ifdef USE_NUMBER +void LD2450Component::set_presence_timeout() { + if (this->presence_timeout_number_ != nullptr) { + if (this->presence_timeout_number_->state == 0) { + float timeout = this->restore_from_flash_(); + this->presence_timeout_number_->publish_state(timeout); + this->timeout_ = this->convert_seconds_to_ms(timeout); + } + if (this->presence_timeout_number_->has_state()) { + this->save_to_flash_(this->presence_timeout_number_->state); + this->timeout_ = this->convert_seconds_to_ms(this->presence_timeout_number_->state); + } + } +} +#endif + +// Save Presence Timeout to flash +void LD2450Component::save_to_flash_(float value) { this->pref_.save(&value); } + +// Load Presence Timeout from flash +float LD2450Component::restore_from_flash_() { + float value; + if (!this->pref_.load(&value)) { + value = DEFAULT_PRESENCE_TIMEOUT; + } + return value; +} + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h new file mode 100644 index 0000000000..c909eb543b --- /dev/null +++ b/esphome/components/ld2450/ld2450.h @@ -0,0 +1,269 @@ +#pragma once +#include "esphome/components/api/custom_api_device.h" +#include "esphome/core/defines.h" +#include "esphome/core/component.h" +#include "esphome/core/preferences.h" +#ifdef USE_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif +#ifdef USE_SENSOR +#include "esphome/components/sensor/sensor.h" +#endif +#ifdef USE_NUMBER +#include "esphome/components/number/number.h" +#endif +#ifdef USE_SWITCH +#include "esphome/components/switch/switch.h" +#endif +#ifdef USE_BUTTON +#include "esphome/components/button/button.h" +#endif +#ifdef USE_SELECT +#include "esphome/components/select/select.h" +#endif +#ifdef USE_TEXT_SENSOR +#include "esphome/components/text_sensor/text_sensor.h" +#endif +#include "esphome/components/uart/uart.h" +#include "esphome/core/helpers.h" +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14 +#endif + +namespace esphome { +namespace ld2450 { + +#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1) + +// Constants +static const uint16_t START_DELAY = 5000; // Sensor startup delay 5 sec. +static const uint8_t DEFAULT_PRESENCE_TIMEOUT = 5; // Timeout to reset presense status 5 sec. +static const uint8_t MAX_TARGETS = 3; // Max 3 Targets in LD2450 +static const uint8_t MAX_ZONES = 3; // Max 3 Zones in LD2450 + +// Zone coordinate config +struct Zone { + int16_t x1 = 0; + int16_t y1 = 0; + int16_t x2 = 0; + int16_t y2 = 0; +}; + +// Commands +static const uint8_t CMD_ENABLE_CONF = 0x00FF; +static const uint8_t CMD_DISABLE_CONF = 0x00FE; +static const uint8_t CMD_VERSION = 0x00A0; +static const uint8_t CMD_MAC = 0x00A5; +static const uint8_t CMD_RESET = 0x00A2; +static const uint8_t CMD_RESTART = 0x00A3; +static const uint8_t CMD_BLUETOOTH = 0x00A4; +static const uint8_t CMD_SINGLE_TARGET = 0x0080; +static const uint8_t CMD_MULTI_TARGET = 0x0090; +static const uint8_t CMD_SET_BAUD_RATE = 0x00A1; +static const uint8_t CMD_QUERY_ZONE = 0x00C1; +static const uint8_t CMD_SET_ZONE = 0x00C2; + +enum BaudRateStructure : uint8_t { + BAUD_RATE_9600 = 1, + BAUD_RATE_19200 = 2, + BAUD_RATE_38400 = 3, + BAUD_RATE_57600 = 4, + BAUD_RATE_115200 = 5, + BAUD_RATE_230400 = 6, + BAUD_RATE_256000 = 7, + BAUD_RATE_460800 = 8 +}; + +static const std::map BAUD_RATE_ENUM_TO_INT{ + {"9600", BAUD_RATE_9600}, {"19200", BAUD_RATE_19200}, {"38400", BAUD_RATE_38400}, + {"57600", BAUD_RATE_57600}, {"115200", BAUD_RATE_115200}, {"230400", BAUD_RATE_230400}, + {"256000", BAUD_RATE_256000}, {"460800", BAUD_RATE_460800}}; + +enum ZoneTypeStructure : uint8_t { ZONE_DISABLED = 0, ZONE_DETECTION = 1, ZONE_FILTER = 2 }; + +static const std::map ZONE_TYPE_INT_TO_ENUM{ + {ZONE_DISABLED, "Disabled"}, {ZONE_DETECTION, "Detection"}, {ZONE_FILTER, "Filter"}}; + +static const std::map ZONE_TYPE_ENUM_TO_INT{ + {"Disabled", ZONE_DISABLED}, {"Detection", ZONE_DETECTION}, {"Filter", ZONE_FILTER}}; + +// Command Header & Footer +static const uint8_t CMD_FRAME_HEADER[4] = {0xFD, 0xFC, 0xFB, 0xFA}; +static const uint8_t CMD_FRAME_END[4] = {0x04, 0x03, 0x02, 0x01}; + +// Data Header & Footer +static const uint8_t DATA_FRAME_HEADER[4] = {0xAA, 0xFF, 0x03, 0x00}; +static const uint8_t DATA_FRAME_END[2] = {0x55, 0xCC}; + +enum PeriodicDataStructure : uint8_t { + TARGET_X = 4, + TARGET_Y = 6, + TARGET_SPEED = 8, + TARGET_RESOLUTION = 10, +}; + +enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 }; + +enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 }; + +class LD2450Component : public Component, public api::CustomAPIDevice, public uart::UARTDevice { +#ifdef USE_SENSOR + SUB_SENSOR(target_count) + SUB_SENSOR(still_target_count) + SUB_SENSOR(moving_target_count) +#endif +#ifdef USE_BINARY_SENSOR + SUB_BINARY_SENSOR(target) + SUB_BINARY_SENSOR(moving_target) + SUB_BINARY_SENSOR(still_target) +#endif +#ifdef USE_TEXT_SENSOR + SUB_TEXT_SENSOR(version) + SUB_TEXT_SENSOR(mac) +#endif +#ifdef USE_SELECT + SUB_SELECT(baud_rate) + SUB_SELECT(zone_type) +#endif +#ifdef USE_SWITCH + SUB_SWITCH(bluetooth) + SUB_SWITCH(multi_target) +#endif +#ifdef USE_BUTTON + SUB_BUTTON(reset) + SUB_BUTTON(restart) +#endif +#ifdef USE_NUMBER + SUB_NUMBER(presence_timeout) +#endif + + public: + LD2450Component(); + void setup() override; + void dump_config() override; + void loop() override; + void set_presence_timeout(); + void set_throttle(uint16_t value) { this->throttle_ = value; }; + void read_all_info(); + void query_zone_info(); + void restart_and_read_all_info(); + void set_bluetooth(bool enable); + void set_multi_target(bool enable); + void set_baud_rate(const std::string &state); + void set_zone_type(const std::string &state); + void publish_zone_type(); + void factory_reset(); + uint16_t convert_seconds_to_ms(uint16_t value) { return value * 1000; }; +#ifdef USE_TEXT_SENSOR + void set_direction_text_sensor(int target, text_sensor::TextSensor *s); +#endif +#ifdef USE_NUMBER + void set_zone_coordinate(uint8_t zone); + void set_zone_x1_number(int zone, number::Number *n); + void set_zone_y1_number(int zone, number::Number *n); + void set_zone_x2_number(int zone, number::Number *n); + void set_zone_y2_number(int zone, number::Number *n); +#endif +#ifdef USE_SENSOR + void set_move_x_sensor(int target, sensor::Sensor *s); + void set_move_y_sensor(int target, sensor::Sensor *s); + void set_move_speed_sensor(int target, sensor::Sensor *s); + void set_move_angle_sensor(int target, sensor::Sensor *s); + void set_move_distance_sensor(int target, sensor::Sensor *s); + void set_move_resolution_sensor(int target, sensor::Sensor *s); +#endif + + protected: + ESPPreferenceObject pref_; + int two_byte_to_int_(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; } + void send_command_(uint8_t command_str, const uint8_t *command_value, int command_value_len); + void set_config_mode_(bool enable); + void handle_periodic_data_(uint8_t *buffer, int len); + bool handle_ack_data_(uint8_t *buffer, int len); + void process_zone_(uint8_t *buffer); + void readline_(int readch, uint8_t *buffer, int len); + void get_version_(); + void get_mac_(); + void query_zone_(); + void restart_(); + void send_set_zone_command_(); + void convert_int_values_to_hex_(const int *values, uint8_t *bytes); + void on_reset_radar_zone_(); + void save_to_flash_(float value); + float restore_from_flash_(); + Zone zone_config_[MAX_ZONES]; + void on_set_radar_zone_(int zone_type, int zone1_x1, int zone1_y1, int zone1_x2, int zone1_y2, int zone2_x1, + int zone2_y1, int zone2_x2, int zone2_y2, int zone3_x1, int zone3_y1, int zone3_x2, + int zone3_y2); + int16_t decode_coordinate_(uint8_t low_byte, uint8_t high_byte) { + int16_t coordinate = (high_byte & 0x7F) << 8 | low_byte; + if ((high_byte & 0x80) == 0) + coordinate = -coordinate; + return coordinate; // mm + } + int16_t decode_speed_(uint8_t low_byte, uint8_t high_byte) { + int16_t speed = (high_byte & 0x7F) << 8 | low_byte; + return speed * 10; // mm/s + } + std::string convert_signed_int_to_hex_(int value) { + std::stringstream ss; + ss << std::hex << std::setw(4) << std::setfill('0') << (value & 0xFFFF); + return ss.str(); + } + int16_t hex_to_signed_int_(const uint8_t *buffer, uint8_t offset) { + uint16_t hex_val = (buffer[offset + 1] << 8) | buffer[offset]; + int16_t dec_val = static_cast(hex_val); + if (dec_val & 0x8000) + dec_val -= 65536; + return dec_val; + } + float calculate_angle_(float base, float hypotenuse) { + if (base < 0.0 || hypotenuse <= 0.0) + return 0.0; + float angle_radians = std::acos(base / hypotenuse); + float angle_degrees = angle_radians * (180.0 / M_PI); + return angle_degrees; + } + std::string get_direction_(int16_t speed) { + if (speed > 0) + return "Moving away"; + if (speed < 0) + return "Coming closer"; + return "Stationary"; + } + int32_t uptime_millis_ = millis(); + int32_t last_periodic_millis_ = millis(); + int32_t presence_millis_ = 0; + int32_t still_presence_millis_ = 0; + int32_t moving_presence_millis_ = 0; + uint16_t throttle_; + uint16_t timeout_; + uint8_t zone_type_ = 0; + std::string version_; + std::string mac_; + bool get_timeout_status_(int32_t check_millis); +#ifdef USE_TEXT_SENSOR + std::vector direction_text_sensors_ = std::vector(3); +#endif +#ifdef USE_NUMBER + std::vector zone_x1_numbers_ = std::vector(3); + std::vector zone_y1_numbers_ = std::vector(3); + std::vector zone_x2_numbers_ = std::vector(3); + std::vector zone_y2_numbers_ = std::vector(3); +#endif +#ifdef USE_SENSOR + std::vector move_x_sensors_ = std::vector(3); + std::vector move_y_sensors_ = std::vector(3); + std::vector move_speed_sensors_ = std::vector(3); + std::vector move_angle_sensors_ = std::vector(3); + std::vector move_distance_sensors_ = std::vector(3); + std::vector move_resolution_sensors_ = std::vector(3); +#endif +}; + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/number/__init__.py b/esphome/components/ld2450/number/__init__.py new file mode 100644 index 0000000000..65cb514e03 --- /dev/null +++ b/esphome/components/ld2450/number/__init__.py @@ -0,0 +1,117 @@ +import esphome.codegen as cg +from esphome.components import number +import esphome.config_validation as cv +from esphome.const import ( + CONF_ID, + UNIT_SECOND, + ENTITY_CATEGORY_CONFIG, + ICON_TIMELAPSE, + DEVICE_CLASS_DISTANCE, +) +from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns + +CONF_PRESENCE_TIMEOUT = "presence_timeout" +UNIT_MILLIMETER = "mm" + +MAX_ZONES = 3 + +CONF_X1 = "x1" +CONF_Y1 = "y1" +CONF_X2 = "x2" +CONF_Y2 = "y2" + +PresenceTimeoutNumber = ld2450_ns.class_("PresenceTimeoutNumber", number.Number) +ZoneCoordinateNumber = ld2450_ns.class_("ZoneCoordinateNumber", number.Number) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), + cv.Optional(CONF_PRESENCE_TIMEOUT): number.number_schema( + PresenceTimeoutNumber, + unit_of_measurement=UNIT_SECOND, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_TIMELAPSE, + ), + } +) + +CONFIG_SCHEMA = CONFIG_SCHEMA.extend( + { + cv.Optional(f"zone_{n+1}"): cv.Schema( + { + cv.Required(CONF_X1): number.number_schema( + ZoneCoordinateNumber, + device_class=DEVICE_CLASS_DISTANCE, + unit_of_measurement=UNIT_MILLIMETER, + entity_category=ENTITY_CATEGORY_CONFIG, + icon="mdi:arrow-top-left-bold-box-outline", + ), + cv.Required(CONF_Y1): number.number_schema( + ZoneCoordinateNumber, + device_class=DEVICE_CLASS_DISTANCE, + unit_of_measurement=UNIT_MILLIMETER, + entity_category=ENTITY_CATEGORY_CONFIG, + icon="mdi:arrow-top-left", + ), + cv.Required(CONF_X2): number.number_schema( + ZoneCoordinateNumber, + device_class=DEVICE_CLASS_DISTANCE, + unit_of_measurement=UNIT_MILLIMETER, + entity_category=ENTITY_CATEGORY_CONFIG, + icon="mdi:arrow-bottom-right-bold-box-outline", + ), + cv.Required(CONF_Y2): number.number_schema( + ZoneCoordinateNumber, + device_class=DEVICE_CLASS_DISTANCE, + unit_of_measurement=UNIT_MILLIMETER, + entity_category=ENTITY_CATEGORY_CONFIG, + icon="mdi:arrow-bottom-right", + ), + } + ) + for n in range(MAX_ZONES) + } +) + + +async def to_code(config): + ld2450_component = await cg.get_variable(config[CONF_LD2450_ID]) + if presence_timeout_config := config.get(CONF_PRESENCE_TIMEOUT): + n = await number.new_number( + presence_timeout_config, + min_value=0, + max_value=3600, + step=1, + ) + await cg.register_parented(n, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_presence_timeout_number(n)) + for x in range(MAX_ZONES): + if zone_conf := config.get(f"zone_{x+1}"): + if zone_x1_config := zone_conf.get(CONF_X1): + n = cg.new_Pvariable(zone_x1_config[CONF_ID], x) + await number.register_number( + n, zone_x1_config, min_value=-4860, max_value=4860, step=1 + ) + await cg.register_parented(n, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_zone_x1_number(x, n)) + if zone_y1_config := zone_conf.get(CONF_Y1): + n = cg.new_Pvariable(zone_y1_config[CONF_ID], x) + await number.register_number( + n, zone_y1_config, min_value=0, max_value=7560, step=1 + ) + await cg.register_parented(n, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_zone_y1_number(x, n)) + if zone_x2_config := zone_conf.get(CONF_X2): + n = cg.new_Pvariable(zone_x2_config[CONF_ID], x) + await number.register_number( + n, zone_x2_config, min_value=-4860, max_value=4860, step=1 + ) + await cg.register_parented(n, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_zone_x2_number(x, n)) + if zone_y2_config := zone_conf.get(CONF_Y2): + n = cg.new_Pvariable(zone_y2_config[CONF_ID], x) + await number.register_number( + n, zone_y2_config, min_value=0, max_value=7560, step=1 + ) + await cg.register_parented(n, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_zone_y2_number(x, n)) diff --git a/esphome/components/ld2450/number/presence_timeout_number.cpp b/esphome/components/ld2450/number/presence_timeout_number.cpp new file mode 100644 index 0000000000..ecfe71f484 --- /dev/null +++ b/esphome/components/ld2450/number/presence_timeout_number.cpp @@ -0,0 +1,12 @@ +#include "presence_timeout_number.h" + +namespace esphome { +namespace ld2450 { + +void PresenceTimeoutNumber::control(float value) { + this->publish_state(value); + this->parent_->set_presence_timeout(); +} + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/number/presence_timeout_number.h b/esphome/components/ld2450/number/presence_timeout_number.h new file mode 100644 index 0000000000..b18699792f --- /dev/null +++ b/esphome/components/ld2450/number/presence_timeout_number.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/number/number.h" +#include "../ld2450.h" + +namespace esphome { +namespace ld2450 { + +class PresenceTimeoutNumber : public number::Number, public Parented { + public: + PresenceTimeoutNumber() = default; + + protected: + void control(float value) override; +}; + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/number/zone_coordinate_number.cpp b/esphome/components/ld2450/number/zone_coordinate_number.cpp new file mode 100644 index 0000000000..5338d7e5ee --- /dev/null +++ b/esphome/components/ld2450/number/zone_coordinate_number.cpp @@ -0,0 +1,14 @@ +#include "zone_coordinate_number.h" + +namespace esphome { +namespace ld2450 { + +ZoneCoordinateNumber::ZoneCoordinateNumber(uint8_t zone) : zone_(zone) {} + +void ZoneCoordinateNumber::control(float value) { + this->publish_state(value); + this->parent_->set_zone_coordinate(this->zone_); +} + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/number/zone_coordinate_number.h b/esphome/components/ld2450/number/zone_coordinate_number.h new file mode 100644 index 0000000000..72b83889c4 --- /dev/null +++ b/esphome/components/ld2450/number/zone_coordinate_number.h @@ -0,0 +1,19 @@ +#pragma once + +#include "esphome/components/number/number.h" +#include "../ld2450.h" + +namespace esphome { +namespace ld2450 { + +class ZoneCoordinateNumber : public number::Number, public Parented { + public: + ZoneCoordinateNumber(uint8_t zone); + + protected: + uint8_t zone_; + void control(float value) override; +}; + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/select/__init__.py b/esphome/components/ld2450/select/__init__.py new file mode 100644 index 0000000000..19d09bdcce --- /dev/null +++ b/esphome/components/ld2450/select/__init__.py @@ -0,0 +1,59 @@ +import esphome.codegen as cg +from esphome.components import select +import esphome.config_validation as cv +from esphome.const import ( + ENTITY_CATEGORY_CONFIG, + CONF_BAUD_RATE, + ICON_THERMOMETER, +) +from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns + +CONF_ZONE_TYPE = "zone_type" + +BaudRateSelect = ld2450_ns.class_("BaudRateSelect", select.Select) +ZoneTypeSelect = ld2450_ns.class_("ZoneTypeSelect", select.Select) + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), + cv.Optional(CONF_BAUD_RATE): select.select_schema( + BaudRateSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_THERMOMETER, + ), + cv.Optional(CONF_ZONE_TYPE): select.select_schema( + ZoneTypeSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_THERMOMETER, + ), +} + + +async def to_code(config): + ld2450_component = await cg.get_variable(config[CONF_LD2450_ID]) + if baud_rate_config := config.get(CONF_BAUD_RATE): + s = await select.new_select( + baud_rate_config, + options=[ + "9600", + "19200", + "38400", + "57600", + "115200", + "230400", + "256000", + "460800", + ], + ) + await cg.register_parented(s, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_baud_rate_select(s)) + if zone_type_config := config.get(CONF_ZONE_TYPE): + s = await select.new_select( + zone_type_config, + options=[ + "Disabled", + "Detection", + "Filter", + ], + ) + await cg.register_parented(s, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_zone_type_select(s)) diff --git a/esphome/components/ld2450/select/baud_rate_select.cpp b/esphome/components/ld2450/select/baud_rate_select.cpp new file mode 100644 index 0000000000..06439aaa75 --- /dev/null +++ b/esphome/components/ld2450/select/baud_rate_select.cpp @@ -0,0 +1,12 @@ +#include "baud_rate_select.h" + +namespace esphome { +namespace ld2450 { + +void BaudRateSelect::control(const std::string &value) { + this->publish_state(value); + this->parent_->set_baud_rate(state); +} + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/select/baud_rate_select.h b/esphome/components/ld2450/select/baud_rate_select.h new file mode 100644 index 0000000000..04fe65b4fd --- /dev/null +++ b/esphome/components/ld2450/select/baud_rate_select.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/select/select.h" +#include "../ld2450.h" + +namespace esphome { +namespace ld2450 { + +class BaudRateSelect : public select::Select, public Parented { + public: + BaudRateSelect() = default; + + protected: + void control(const std::string &value) override; +}; + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/select/zone_type_select.cpp b/esphome/components/ld2450/select/zone_type_select.cpp new file mode 100644 index 0000000000..a9f6155142 --- /dev/null +++ b/esphome/components/ld2450/select/zone_type_select.cpp @@ -0,0 +1,12 @@ +#include "zone_type_select.h" + +namespace esphome { +namespace ld2450 { + +void ZoneTypeSelect::control(const std::string &value) { + this->publish_state(value); + this->parent_->set_zone_type(state); +} + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/select/zone_type_select.h b/esphome/components/ld2450/select/zone_type_select.h new file mode 100644 index 0000000000..8aafeb6beb --- /dev/null +++ b/esphome/components/ld2450/select/zone_type_select.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/select/select.h" +#include "../ld2450.h" + +namespace esphome { +namespace ld2450 { + +class ZoneTypeSelect : public select::Select, public Parented { + public: + ZoneTypeSelect() = default; + + protected: + void control(const std::string &value) override; +}; + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/sensor.py b/esphome/components/ld2450/sensor.py new file mode 100644 index 0000000000..6ad77708d0 --- /dev/null +++ b/esphome/components/ld2450/sensor.py @@ -0,0 +1,118 @@ +import esphome.codegen as cg +from esphome.components import sensor +import esphome.config_validation as cv +from esphome.const import ( + DEVICE_CLASS_DISTANCE, + DEVICE_CLASS_SPEED, + UNIT_DEGREES, + CONF_SPEED, + CONF_DISTANCE, + CONF_RESOLUTION, +) +from . import CONF_LD2450_ID, LD2450Component + +DEPENDENCIES = ["ld2450"] + +UNIT_MILLIMETER = "mm" +UNIT_MILLIMETER_PER_SECOND = "mm/s" + +CONF_TARGET_COUNT = "target_count" +CONF_STILL_TARGET_COUNT = "still_target_count" +CONF_MOVING_TARGET_COUNT = "moving_target_count" + +MAX_TARGETS = 3 + +CONF_X = "x" +CONF_Y = "y" +CONF_ANGLE = "angle" + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), + cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema( + icon="mdi:account-group", + ), + cv.Optional(CONF_STILL_TARGET_COUNT): sensor.sensor_schema( + icon="mdi:human-greeting-proximity", + ), + cv.Optional(CONF_MOVING_TARGET_COUNT): sensor.sensor_schema( + icon="mdi:account-switch", + ), + } +) + +CONFIG_SCHEMA = CONFIG_SCHEMA.extend( + { + cv.Optional(f"target_{n+1}"): cv.Schema( + { + cv.Optional(CONF_X): sensor.sensor_schema( + device_class=DEVICE_CLASS_DISTANCE, + unit_of_measurement=UNIT_MILLIMETER, + icon="mdi:alpha-x-box-outline", + ), + cv.Optional(CONF_Y): sensor.sensor_schema( + device_class=DEVICE_CLASS_DISTANCE, + unit_of_measurement=UNIT_MILLIMETER, + icon="mdi:alpha-y-box-outline", + ), + cv.Optional(CONF_SPEED): sensor.sensor_schema( + device_class=DEVICE_CLASS_SPEED, + unit_of_measurement=UNIT_MILLIMETER_PER_SECOND, + icon="mdi:speedometer-slow", + ), + cv.Optional(CONF_ANGLE): sensor.sensor_schema( + unit_of_measurement=UNIT_DEGREES, + icon="mdi:format-text-rotation-angle-up", + ), + cv.Optional(CONF_DISTANCE): sensor.sensor_schema( + device_class=DEVICE_CLASS_DISTANCE, + unit_of_measurement=UNIT_MILLIMETER, + icon="mdi:map-marker-distance", + ), + cv.Optional(CONF_RESOLUTION): sensor.sensor_schema( + device_class=DEVICE_CLASS_DISTANCE, + unit_of_measurement=UNIT_MILLIMETER, + icon="mdi:relation-zero-or-one-to-zero-or-one", + ), + } + ) + for n in range(MAX_TARGETS) + }, +) + + +async def to_code(config): + ld2450_component = await cg.get_variable(config[CONF_LD2450_ID]) + + if target_count_config := config.get(CONF_TARGET_COUNT): + sens = await sensor.new_sensor(target_count_config) + cg.add(ld2450_component.set_target_count_sensor(sens)) + + if still_target_count_config := config.get(CONF_STILL_TARGET_COUNT): + sens = await sensor.new_sensor(still_target_count_config) + cg.add(ld2450_component.set_still_target_count_sensor(sens)) + + if moving_target_count_config := config.get(CONF_MOVING_TARGET_COUNT): + sens = await sensor.new_sensor(moving_target_count_config) + cg.add(ld2450_component.set_moving_target_count_sensor(sens)) + + for n in range(MAX_TARGETS): + if target_conf := config.get(f"target_{n+1}"): + if x_config := target_conf.get(CONF_X): + sens = await sensor.new_sensor(x_config) + cg.add(ld2450_component.set_move_x_sensor(n, sens)) + if y_config := target_conf.get(CONF_Y): + sens = await sensor.new_sensor(y_config) + cg.add(ld2450_component.set_move_y_sensor(n, sens)) + if speed_config := target_conf.get(CONF_SPEED): + sens = await sensor.new_sensor(speed_config) + cg.add(ld2450_component.set_move_speed_sensor(n, sens)) + if angle_config := target_conf.get(CONF_ANGLE): + sens = await sensor.new_sensor(angle_config) + cg.add(ld2450_component.set_move_angle_sensor(n, sens)) + if distance_config := target_conf.get(CONF_DISTANCE): + sens = await sensor.new_sensor(distance_config) + cg.add(ld2450_component.set_move_distance_sensor(n, sens)) + if resolution_config := target_conf.get(CONF_RESOLUTION): + sens = await sensor.new_sensor(resolution_config) + cg.add(ld2450_component.set_move_resolution_sensor(n, sens)) diff --git a/esphome/components/ld2450/switch/__init__.py b/esphome/components/ld2450/switch/__init__.py new file mode 100644 index 0000000000..319be50d13 --- /dev/null +++ b/esphome/components/ld2450/switch/__init__.py @@ -0,0 +1,44 @@ +import esphome.codegen as cg +from esphome.components import switch +import esphome.config_validation as cv +from esphome.const import ( + DEVICE_CLASS_SWITCH, + ICON_BLUETOOTH, + ENTITY_CATEGORY_CONFIG, + ICON_PULSE, +) +from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns + +BluetoothSwitch = ld2450_ns.class_("BluetoothSwitch", switch.Switch) +MultiTargetSwitch = ld2450_ns.class_("MultiTargetSwitch", switch.Switch) + +CONF_BLUETOOTH = "bluetooth" +CONF_MULTI_TARGET = "multi_target" + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), + cv.Optional(CONF_BLUETOOTH): switch.switch_schema( + BluetoothSwitch, + device_class=DEVICE_CLASS_SWITCH, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_BLUETOOTH, + ), + cv.Optional(CONF_MULTI_TARGET): switch.switch_schema( + MultiTargetSwitch, + device_class=DEVICE_CLASS_SWITCH, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_PULSE, + ), +} + + +async def to_code(config): + ld2450_component = await cg.get_variable(config[CONF_LD2450_ID]) + if bluetooth_config := config.get(CONF_BLUETOOTH): + s = await switch.new_switch(bluetooth_config) + await cg.register_parented(s, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_bluetooth_switch(s)) + if multi_target_config := config.get(CONF_MULTI_TARGET): + s = await switch.new_switch(multi_target_config) + await cg.register_parented(s, config[CONF_LD2450_ID]) + cg.add(ld2450_component.set_multi_target_switch(s)) diff --git a/esphome/components/ld2450/switch/bluetooth_switch.cpp b/esphome/components/ld2450/switch/bluetooth_switch.cpp new file mode 100644 index 0000000000..fa0d4fb06a --- /dev/null +++ b/esphome/components/ld2450/switch/bluetooth_switch.cpp @@ -0,0 +1,12 @@ +#include "bluetooth_switch.h" + +namespace esphome { +namespace ld2450 { + +void BluetoothSwitch::write_state(bool state) { + this->publish_state(state); + this->parent_->set_bluetooth(state); +} + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/switch/bluetooth_switch.h b/esphome/components/ld2450/switch/bluetooth_switch.h new file mode 100644 index 0000000000..3c1c4f755c --- /dev/null +++ b/esphome/components/ld2450/switch/bluetooth_switch.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/switch/switch.h" +#include "../ld2450.h" + +namespace esphome { +namespace ld2450 { + +class BluetoothSwitch : public switch_::Switch, public Parented { + public: + BluetoothSwitch() = default; + + protected: + void write_state(bool state) override; +}; + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/switch/multi_target_switch.cpp b/esphome/components/ld2450/switch/multi_target_switch.cpp new file mode 100644 index 0000000000..a163e29fc5 --- /dev/null +++ b/esphome/components/ld2450/switch/multi_target_switch.cpp @@ -0,0 +1,12 @@ +#include "multi_target_switch.h" + +namespace esphome { +namespace ld2450 { + +void MultiTargetSwitch::write_state(bool state) { + this->publish_state(state); + this->parent_->set_multi_target(state); +} + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/switch/multi_target_switch.h b/esphome/components/ld2450/switch/multi_target_switch.h new file mode 100644 index 0000000000..ca6253588d --- /dev/null +++ b/esphome/components/ld2450/switch/multi_target_switch.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/switch/switch.h" +#include "../ld2450.h" + +namespace esphome { +namespace ld2450 { + +class MultiTargetSwitch : public switch_::Switch, public Parented { + public: + MultiTargetSwitch() = default; + + protected: + void write_state(bool state) override; +}; + +} // namespace ld2450 +} // namespace esphome diff --git a/esphome/components/ld2450/text_sensor.py b/esphome/components/ld2450/text_sensor.py new file mode 100644 index 0000000000..fffd286f50 --- /dev/null +++ b/esphome/components/ld2450/text_sensor.py @@ -0,0 +1,59 @@ +import esphome.codegen as cg +from esphome.components import text_sensor +import esphome.config_validation as cv +from esphome.const import ( + ENTITY_CATEGORY_DIAGNOSTIC, + ENTITY_CATEGORY_NONE, + CONF_VERSION, + CONF_MAC_ADDRESS, + CONF_DIRECTION, + ICON_BLUETOOTH, + ICON_CHIP, + ICON_SIGN_DIRECTION, +) + +from . import CONF_LD2450_ID, LD2450Component + +DEPENDENCIES = ["ld2450"] + +MAX_TARGETS = 3 + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), + cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, icon=ICON_CHIP + ), + cv.Optional(CONF_MAC_ADDRESS): text_sensor.text_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, icon=ICON_BLUETOOTH + ), + } +) + +CONFIG_SCHEMA = CONFIG_SCHEMA.extend( + { + cv.Optional(f"target_{n+1}"): cv.Schema( + { + cv.Optional(CONF_DIRECTION): text_sensor.text_sensor_schema( + entity_category=ENTITY_CATEGORY_NONE, icon=ICON_SIGN_DIRECTION + ), + } + ) + for n in range(MAX_TARGETS) + } +) + + +async def to_code(config): + ld2450_component = await cg.get_variable(config[CONF_LD2450_ID]) + if version_config := config.get(CONF_VERSION): + sens = await text_sensor.new_text_sensor(version_config) + cg.add(ld2450_component.set_version_text_sensor(sens)) + if mac_address_config := config.get(CONF_MAC_ADDRESS): + sens = await text_sensor.new_text_sensor(mac_address_config) + cg.add(ld2450_component.set_mac_text_sensor(sens)) + for n in range(MAX_TARGETS): + if direction_conf := config.get(f"target_{n+1}"): + if direction_config := direction_conf.get(CONF_DIRECTION): + sens = await text_sensor.new_text_sensor(direction_config) + cg.add(ld2450_component.set_direction_text_sensor(n, sens)) From 58510069298724e97dad1d1a14c07f466dc8694d Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sat, 4 Nov 2023 10:11:09 +0000 Subject: [PATCH 02/18] Add ld2450 tests --- esphome/components/ld2450/button/__init__.py | 2 +- esphome/const.py | 1 + tests/test1.yaml | 132 +++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) diff --git a/esphome/components/ld2450/button/__init__.py b/esphome/components/ld2450/button/__init__.py index 973a9694c2..77fc3c6e59 100644 --- a/esphome/components/ld2450/button/__init__.py +++ b/esphome/components/ld2450/button/__init__.py @@ -7,13 +7,13 @@ from esphome.const import ( ENTITY_CATEGORY_CONFIG, ICON_RESTART, ICON_RESTART_ALERT, + CONF_FACTORY_RESET, ) from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns ResetButton = ld2450_ns.class_("ResetButton", button.Button) RestartButton = ld2450_ns.class_("RestartButton", button.Button) -CONF_FACTORY_RESET = "factory_reset" CONF_RESTART = "restart" CONFIG_SCHEMA = { diff --git a/esphome/const.py b/esphome/const.py index 5de34b86cd..bff972539a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -248,6 +248,7 @@ CONF_EXPORT_REACTIVE_ENERGY = "export_reactive_energy" CONF_EXTERNAL_CLOCK_INPUT = "external_clock_input" CONF_EXTERNAL_COMPONENTS = "external_components" CONF_EXTERNAL_VCC = "external_vcc" +CONF_FACTORY_RESET = "factory_reset" CONF_FALLING_EDGE = "falling_edge" CONF_FAMILY = "family" CONF_FAN_MODE = "fan_mode" diff --git a/tests/test1.yaml b/tests/test1.yaml index f7b433cce2..31d1112eb4 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -263,6 +263,12 @@ uart: rx_pin: GPIO10 parity: EVEN baud_rate: 9600 + - id: uart_ld2450 + tx_pin: 18 + rx_pin: 23 + baud_rate: 256000 + parity: NONE + stop_bits: 1 ota: safe_mode: true @@ -1648,6 +1654,53 @@ sensor: memory_location: 0x20 memory_address: 0x7d name: Adres sensor + - platform: ld2450 + ld2450_id: ld2450_radar + target_count: + name: Presence Target Count + still_target_count: + name: Still Target Count + moving_target_count: + name: Moving Target Count + target_1: + x: + name: Target-1 X + y: + name: Target-1 Y + speed: + name: Target-1 Speed + angle: + name: Target-1 Angle + distance: + name: Target-1 Distance + resolution: + name: Target-1 Resolution + target_2: + x: + name: Target-2 X + y: + name: Target-2 Y + speed: + name: Target-2 Speed + angle: + name: Target-2 Angle + distance: + name: Target-2 Distance + resolution: + name: Target-2 Resolution + target_3: + x: + name: Target-3 X + y: + name: Target-3 Y + speed: + name: Target-3 Speed + angle: + name: Target-3 Angle + distance: + name: Target-3 Distance + resolution: + name: Target-3 Resolution psram: @@ -1986,6 +2039,14 @@ binary_sensor: - platform: dfrobot_sen0395 id: mmwave_detected_uart dfrobot_sen0395_id: mmwave + - platform: ld2450 + ld2450_id: ld2450_radar + has_target: + name: Presence + has_moving_target: + name: Moving Target + has_still_target: + name: Still Target pca9685: frequency: 500 @@ -2920,6 +2981,12 @@ switch: - platform: micronova stove: name: Stove on/off + - platform: ld2450 + ld2450_id: ld2450_radar + bluetooth: + name: "Bluetooth" + multi_target: + name: "Multi Target Tracking" fan: - platform: binary @@ -3746,6 +3813,21 @@ text_sensor: name: "presenece sensor version" mac_address: name: "presenece sensor mac address" + - platform: ld2450 + ld2450_id: ld2450_radar + version: + name: "LD2450 Firmware" + mac_address: + name: "LD2450 BT MAC" + target_1: + direction: + name: "Target-1 Direction" + target_2: + direction: + name: "Target-2 Direction" + target_3: + direction: + name: "Target-3 Direction" sn74hc595: - id: sn74hc595_hub @@ -3936,6 +4018,37 @@ number: step: 1 power_level: name: Micronova Power level + - platform: ld2450 + ld2450_id: ld2450_radar + presence_timeout: + name: "Timeout" + zone_1: + x1: + name: Zone-1 X1 + y1: + name: Zone-1 Y1 + x2: + name: Zone-1 X2 + y2: + name: Zone-1 Y2 + zone_2: + x1: + name: Zone-2 X1 + y1: + name: Zone-2 Y1 + x2: + name: Zone-2 X2 + y2: + name: Zone-2 Y2 + zone_3: + x1: + name: Zone-3 X1 + y1: + name: Zone-3 Y1 + x2: + name: Zone-3 X2 + y2: + name: Zone-3 Y2 select: - platform: template @@ -3958,6 +4071,12 @@ select: name: light function out_pin_level: name: out ping level + - platform: ld2450 + ld2450_id: ld2450_radar + baud_rate: + name: "Baud rate" + zone_type: + name: "Zone Type" qr_code: - id: homepage_qr @@ -4072,6 +4191,14 @@ button: memory_location: 0xA0 memory_address: 0x7D memory_data: 0x0F + - platform: ld2450 + ld2450_id: ld2450_radar + factory_reset: + name: "LD2450 Factory Reset" + entity_category: "config" + restart: + name: "LD2450 Restart" + entity_category: "config" ld2410: id: my_ld2410 @@ -4081,6 +4208,11 @@ ld2420: id: my_ld2420 uart_id: ld2420_uart +ld2450: + id: ld2450_radar + uart_id: uart_ld2450 + throttle: 1000ms + lcd_menu: id: test_lcd_menu display_id: my_lcd_gpio From 042b3331d12f26be4fa760418848f9b511ea206b Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sat, 4 Nov 2023 10:32:39 +0000 Subject: [PATCH 03/18] Add LD2450 codeowner --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 320a23ffaa..b50d830fcf 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -163,6 +163,7 @@ esphome/components/kuntze/* @ssieb esphome/components/lcd_menu/* @numo68 esphome/components/ld2410/* @regevbr @sebcaps esphome/components/ld2420/* @descipher +esphome/components/ld2450/* @hareeshmu esphome/components/ledc/* @OttoWinter esphome/components/libretiny/* @kuba2k2 esphome/components/libretiny_pwm/* @kuba2k2 From bdc6ae354f5577ea63dae38269c4b3795fbe612d Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sat, 4 Nov 2023 10:59:48 +0000 Subject: [PATCH 04/18] Add CONF_FACTORY_RESET in to const.py and updated dfrobot_sen0395, ld2410, ld2420 --- esphome/components/dfrobot_sen0395/__init__.py | 3 +-- esphome/components/ld2410/button/__init__.py | 2 +- esphome/components/ld2420/button/__init__.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/esphome/components/dfrobot_sen0395/__init__.py b/esphome/components/dfrobot_sen0395/__init__.py index e772db5a15..6dba6192ff 100644 --- a/esphome/components/dfrobot_sen0395/__init__.py +++ b/esphome/components/dfrobot_sen0395/__init__.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import automation from esphome import core from esphome.automation import maybe_simple_id -from esphome.const import CONF_ID +from esphome.const import CONF_ID, CONF_FACTORY_RESET from esphome.components import uart CODEOWNERS = ["@niklasweber"] @@ -29,7 +29,6 @@ CONF_DELAY_AFTER_DETECT = "delay_after_detect" CONF_DELAY_AFTER_DISAPPEAR = "delay_after_disappear" CONF_DETECTION_SEGMENTS = "detection_segments" CONF_OUTPUT_LATENCY = "output_latency" -CONF_FACTORY_RESET = "factory_reset" CONF_SENSITIVITY = "sensitivity" CONFIG_SCHEMA = cv.All( diff --git a/esphome/components/ld2410/button/__init__.py b/esphome/components/ld2410/button/__init__.py index 3567114c2c..dec219ebb3 100644 --- a/esphome/components/ld2410/button/__init__.py +++ b/esphome/components/ld2410/button/__init__.py @@ -8,6 +8,7 @@ from esphome.const import ( ICON_RESTART, ICON_RESTART_ALERT, ICON_DATABASE, + CONF_FACTORY_RESET, ) from .. import CONF_LD2410_ID, LD2410Component, ld2410_ns @@ -15,7 +16,6 @@ QueryButton = ld2410_ns.class_("QueryButton", button.Button) ResetButton = ld2410_ns.class_("ResetButton", button.Button) RestartButton = ld2410_ns.class_("RestartButton", button.Button) -CONF_FACTORY_RESET = "factory_reset" CONF_RESTART = "restart" CONF_QUERY_PARAMS = "query_params" diff --git a/esphome/components/ld2420/button/__init__.py b/esphome/components/ld2420/button/__init__.py index 675e041dd4..07dce316d2 100644 --- a/esphome/components/ld2420/button/__init__.py +++ b/esphome/components/ld2420/button/__init__.py @@ -8,6 +8,7 @@ from esphome.const import ( ICON_RESTART, ICON_RESTART_ALERT, ICON_DATABASE, + CONF_FACTORY_RESET, ) from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns @@ -19,7 +20,6 @@ LD2420FactoryResetButton = ld2420_ns.class_("LD2420FactoryResetButton", button.B CONF_APPLY_CONFIG = "apply_config" CONF_REVERT_CONFIG = "revert_config" CONF_RESTART_MODULE = "restart_module" -CONF_FACTORY_RESET = "factory_reset" CONFIG_SCHEMA = { From 6f4fb305c8010ec26689bfa571eff43f339ec6c0 Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sat, 4 Nov 2023 11:43:50 +0000 Subject: [PATCH 05/18] Add USE_API for custom_api_device.h --- esphome/components/ld2450/ld2450.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index c909eb543b..d3c30e9d07 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -1,11 +1,13 @@ #pragma once -#include "esphome/components/api/custom_api_device.h" + +#include +#include +#include #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" #include "esphome/core/component.h" #include "esphome/core/preferences.h" -#ifdef USE_BINARY_SENSOR -#include "esphome/components/binary_sensor/binary_sensor.h" -#endif +#include "esphome/components/uart/uart.h" #ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" #endif @@ -21,14 +23,15 @@ #ifdef USE_SELECT #include "esphome/components/select/select.h" #endif +#ifdef USE_API +#include "esphome/components/api/custom_api_device.h" +#endif #ifdef USE_TEXT_SENSOR #include "esphome/components/text_sensor/text_sensor.h" #endif -#include "esphome/components/uart/uart.h" -#include "esphome/core/helpers.h" -#include -#include -#include +#ifdef USE_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif #ifndef M_PI #define M_PI 3.14 From 59499d19703c068ad58326b70533342f0c4c626f Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sat, 4 Nov 2023 13:04:42 +0000 Subject: [PATCH 06/18] Trying to fix test error for CustomAPIDevice --- esphome/components/ld2450/ld2450.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index d3c30e9d07..baa5ec9997 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -113,7 +113,7 @@ enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 }; enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 }; -class LD2450Component : public Component, public api::CustomAPIDevice, public uart::UARTDevice { +class LD2450Component : public Component, public uart::UARTDevice, public esphome::api::CustomAPIDevice { #ifdef USE_SENSOR SUB_SENSOR(target_count) SUB_SENSOR(still_target_count) From cd8201ac6baed3402e12987d03f4a0747313cd8f Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sat, 4 Nov 2023 16:18:45 +0000 Subject: [PATCH 07/18] Remove Service- Fix Tests --- esphome/components/ld2450/ld2450.cpp | 47 ---------------------------- esphome/components/ld2450/ld2450.h | 5 +-- 2 files changed, 1 insertion(+), 51 deletions(-) diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index 572f1b99ea..4bd7d280ec 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -26,25 +26,6 @@ void LD2450Component::setup() { ESP_LOGCONFIG(TAG, "Mac Address : %s", const_cast(this->mac_.c_str())); ESP_LOGCONFIG(TAG, "Firmware Version : %s", const_cast(this->version_.c_str())); ESP_LOGCONFIG(TAG, "HLK-LD2450 setup complete"); - ESP_LOGCONFIG(TAG, "Registering services"); - register_service(&::esphome::ld2450::LD2450Component::on_set_radar_zone_, "set_radar_zone", - { - "zone_type", - "zone1_x1", - "zone1_y1", - "zone1_x2", - "zone1_y2", - "zone2_x1", - "zone2_y1", - "zone2_x2", - "zone2_y2", - "zone3_x1", - "zone3_y1", - "zone3_x2", - "zone3_y2", - }); - register_service(&::esphome::ld2450::LD2450Component::on_reset_radar_zone_, "reset_radar_zone"); - ESP_LOGCONFIG(TAG, "Services registration complete"); } void LD2450Component::dump_config() { @@ -128,34 +109,6 @@ void LD2450Component::loop() { } } -// Service reset_radar_zone -void LD2450Component::on_reset_radar_zone_() { - this->zone_type_ = 0; - for (auto &i : zone_config_) { - i.x1 = 0; - i.y1 = 0; - i.x2 = 0; - i.y2 = 0; - } - this->send_set_zone_command_(); -} - -// Service set_radar_zone -void LD2450Component::on_set_radar_zone_(int zone_type, int zone1_x1, int zone1_y1, int zone1_x2, int zone1_y2, - int zone2_x1, int zone2_y1, int zone2_x2, int zone2_y2, int zone3_x1, - int zone3_y1, int zone3_x2, int zone3_y2) { - this->zone_type_ = zone_type; - int zone_parameters[12] = {zone1_x1, zone1_y1, zone1_x2, zone1_y2, zone2_x1, zone2_y1, - zone2_x2, zone2_y2, zone3_x1, zone3_y1, zone3_x2, zone3_y2}; - for (int i = 0; i < MAX_ZONES; i++) { - zone_config_[i].x1 = zone_parameters[i * 4]; - zone_config_[i].y1 = zone_parameters[i * 4 + 1]; - zone_config_[i].x2 = zone_parameters[i * 4 + 2]; - zone_config_[i].y2 = zone_parameters[i * 4 + 3]; - } - this->send_set_zone_command_(); -} - // Set Zone on LD2450 Sensor void LD2450Component::send_set_zone_command_() { uint8_t cmd_value[26] = {}; diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index baa5ec9997..15d02f0865 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -23,9 +23,6 @@ #ifdef USE_SELECT #include "esphome/components/select/select.h" #endif -#ifdef USE_API -#include "esphome/components/api/custom_api_device.h" -#endif #ifdef USE_TEXT_SENSOR #include "esphome/components/text_sensor/text_sensor.h" #endif @@ -113,7 +110,7 @@ enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 }; enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 }; -class LD2450Component : public Component, public uart::UARTDevice, public esphome::api::CustomAPIDevice { +class LD2450Component : public Component, public uart::UARTDevice { #ifdef USE_SENSOR SUB_SENSOR(target_count) SUB_SENSOR(still_target_count) From ec4531a790fe67525701d603788eefb683e3a953 Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sat, 4 Nov 2023 16:39:11 +0000 Subject: [PATCH 08/18] Change test file --- tests/test1.1.yaml | 144 +++++++++++++++++++++++++++++++++++++++++++++ tests/test1.yaml | 132 ----------------------------------------- 2 files changed, 144 insertions(+), 132 deletions(-) diff --git a/tests/test1.1.yaml b/tests/test1.1.yaml index c71aa6e0ef..fa97da8eed 100644 --- a/tests/test1.1.yaml +++ b/tests/test1.1.yaml @@ -36,6 +36,17 @@ uart: rx_pin: GPIO26 baud_rate: 115200 rx_buffer_size: 1024 + - id: uart_ld2450 + tx_pin: 18 + rx_pin: 23 + baud_rate: 256000 + parity: NONE + stop_bits: 1 + +ld2450: + id: ld2450_radar + uart_id: uart_ld2450 + throttle: 1000ms adalight: @@ -230,3 +241,136 @@ button: - canbus.send: "abc" - canbus.send: [0, 1, 2] - canbus.send: !lambda return {0, 1, 2}; + - platform: ld2450 + ld2450_id: ld2450_radar + factory_reset: + name: "LD2450 Factory Reset" + entity_category: "config" + restart: + name: "LD2450 Restart" + entity_category: "config" + +sensor: + - platform: ld2450 + ld2450_id: ld2450_radar + target_count: + name: Presence Target Count + still_target_count: + name: Still Target Count + moving_target_count: + name: Moving Target Count + target_1: + x: + name: Target-1 X + y: + name: Target-1 Y + speed: + name: Target-1 Speed + angle: + name: Target-1 Angle + distance: + name: Target-1 Distance + resolution: + name: Target-1 Resolution + target_2: + x: + name: Target-2 X + y: + name: Target-2 Y + speed: + name: Target-2 Speed + angle: + name: Target-2 Angle + distance: + name: Target-2 Distance + resolution: + name: Target-2 Resolution + target_3: + x: + name: Target-3 X + y: + name: Target-3 Y + speed: + name: Target-3 Speed + angle: + name: Target-3 Angle + distance: + name: Target-3 Distance + resolution: + name: Target-3 Resolution + +binary_sensor: + - platform: ld2450 + ld2450_id: ld2450_radar + has_target: + name: Presence + has_moving_target: + name: Moving Target + has_still_target: + name: Still Target + +switch: + - platform: ld2450 + ld2450_id: ld2450_radar + bluetooth: + name: "Bluetooth" + multi_target: + name: "Multi Target Tracking" + +text_sensor: + - platform: ld2450 + ld2450_id: ld2450_radar + version: + name: "LD2450 Firmware" + mac_address: + name: "LD2450 BT MAC" + target_1: + direction: + name: "Target-1 Direction" + target_2: + direction: + name: "Target-2 Direction" + target_3: + direction: + name: "Target-3 Direction" + +number: + - platform: ld2450 + ld2450_id: ld2450_radar + presence_timeout: + name: "Timeout" + zone_1: + x1: + name: Zone-1 X1 + y1: + name: Zone-1 Y1 + x2: + name: Zone-1 X2 + y2: + name: Zone-1 Y2 + zone_2: + x1: + name: Zone-2 X1 + y1: + name: Zone-2 Y1 + x2: + name: Zone-2 X2 + y2: + name: Zone-2 Y2 + zone_3: + x1: + name: Zone-3 X1 + y1: + name: Zone-3 Y1 + x2: + name: Zone-3 X2 + y2: + name: Zone-3 Y2 + +select: + - platform: ld2450 + ld2450_id: ld2450_radar + baud_rate: + name: "Baud rate" + zone_type: + name: "Zone Type" diff --git a/tests/test1.yaml b/tests/test1.yaml index 31d1112eb4..f7b433cce2 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -263,12 +263,6 @@ uart: rx_pin: GPIO10 parity: EVEN baud_rate: 9600 - - id: uart_ld2450 - tx_pin: 18 - rx_pin: 23 - baud_rate: 256000 - parity: NONE - stop_bits: 1 ota: safe_mode: true @@ -1654,53 +1648,6 @@ sensor: memory_location: 0x20 memory_address: 0x7d name: Adres sensor - - platform: ld2450 - ld2450_id: ld2450_radar - target_count: - name: Presence Target Count - still_target_count: - name: Still Target Count - moving_target_count: - name: Moving Target Count - target_1: - x: - name: Target-1 X - y: - name: Target-1 Y - speed: - name: Target-1 Speed - angle: - name: Target-1 Angle - distance: - name: Target-1 Distance - resolution: - name: Target-1 Resolution - target_2: - x: - name: Target-2 X - y: - name: Target-2 Y - speed: - name: Target-2 Speed - angle: - name: Target-2 Angle - distance: - name: Target-2 Distance - resolution: - name: Target-2 Resolution - target_3: - x: - name: Target-3 X - y: - name: Target-3 Y - speed: - name: Target-3 Speed - angle: - name: Target-3 Angle - distance: - name: Target-3 Distance - resolution: - name: Target-3 Resolution psram: @@ -2039,14 +1986,6 @@ binary_sensor: - platform: dfrobot_sen0395 id: mmwave_detected_uart dfrobot_sen0395_id: mmwave - - platform: ld2450 - ld2450_id: ld2450_radar - has_target: - name: Presence - has_moving_target: - name: Moving Target - has_still_target: - name: Still Target pca9685: frequency: 500 @@ -2981,12 +2920,6 @@ switch: - platform: micronova stove: name: Stove on/off - - platform: ld2450 - ld2450_id: ld2450_radar - bluetooth: - name: "Bluetooth" - multi_target: - name: "Multi Target Tracking" fan: - platform: binary @@ -3813,21 +3746,6 @@ text_sensor: name: "presenece sensor version" mac_address: name: "presenece sensor mac address" - - platform: ld2450 - ld2450_id: ld2450_radar - version: - name: "LD2450 Firmware" - mac_address: - name: "LD2450 BT MAC" - target_1: - direction: - name: "Target-1 Direction" - target_2: - direction: - name: "Target-2 Direction" - target_3: - direction: - name: "Target-3 Direction" sn74hc595: - id: sn74hc595_hub @@ -4018,37 +3936,6 @@ number: step: 1 power_level: name: Micronova Power level - - platform: ld2450 - ld2450_id: ld2450_radar - presence_timeout: - name: "Timeout" - zone_1: - x1: - name: Zone-1 X1 - y1: - name: Zone-1 Y1 - x2: - name: Zone-1 X2 - y2: - name: Zone-1 Y2 - zone_2: - x1: - name: Zone-2 X1 - y1: - name: Zone-2 Y1 - x2: - name: Zone-2 X2 - y2: - name: Zone-2 Y2 - zone_3: - x1: - name: Zone-3 X1 - y1: - name: Zone-3 Y1 - x2: - name: Zone-3 X2 - y2: - name: Zone-3 Y2 select: - platform: template @@ -4071,12 +3958,6 @@ select: name: light function out_pin_level: name: out ping level - - platform: ld2450 - ld2450_id: ld2450_radar - baud_rate: - name: "Baud rate" - zone_type: - name: "Zone Type" qr_code: - id: homepage_qr @@ -4191,14 +4072,6 @@ button: memory_location: 0xA0 memory_address: 0x7D memory_data: 0x0F - - platform: ld2450 - ld2450_id: ld2450_radar - factory_reset: - name: "LD2450 Factory Reset" - entity_category: "config" - restart: - name: "LD2450 Restart" - entity_category: "config" ld2410: id: my_ld2410 @@ -4208,11 +4081,6 @@ ld2420: id: my_ld2420 uart_id: ld2420_uart -ld2450: - id: ld2450_radar - uart_id: uart_ld2450 - throttle: 1000ms - lcd_menu: id: test_lcd_menu display_id: my_lcd_gpio From 89f9d2bd1b06c79bbce5293af29e1aad2905eccc Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sun, 5 Nov 2023 19:52:04 +0000 Subject: [PATCH 09/18] Add ld2450 zone set reset services --- esphome/components/ld2450/ld2450.cpp | 49 ++++++++++++++++++++++++++++ esphome/components/ld2450/ld2450.h | 7 ++++ tests/test1.1.yaml | 2 ++ 3 files changed, 58 insertions(+) diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index 4bd7d280ec..baa091a925 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -26,6 +26,27 @@ void LD2450Component::setup() { ESP_LOGCONFIG(TAG, "Mac Address : %s", const_cast(this->mac_.c_str())); ESP_LOGCONFIG(TAG, "Firmware Version : %s", const_cast(this->version_.c_str())); ESP_LOGCONFIG(TAG, "HLK-LD2450 setup complete"); +#ifdef USE_API + ESP_LOGCONFIG(TAG, "Registering services"); + CustomAPIDevice::register_service(&::esphome::ld2450::LD2450Component::on_set_radar_zone_, "set_radar_zone", + { + "zone_type", + "zone1_x1", + "zone1_y1", + "zone1_x2", + "zone1_y2", + "zone2_x1", + "zone2_y1", + "zone2_x2", + "zone2_y2", + "zone3_x1", + "zone3_y1", + "zone3_x2", + "zone3_y2", + }); + CustomAPIDevice::register_service(&::esphome::ld2450::LD2450Component::on_reset_radar_zone_, "reset_radar_zone"); + ESP_LOGCONFIG(TAG, "Services registration complete"); +#endif } void LD2450Component::dump_config() { @@ -109,6 +130,34 @@ void LD2450Component::loop() { } } +// Service reset_radar_zone +void LD2450Component::on_reset_radar_zone_() { + this->zone_type_ = 0; + for (auto &i : zone_config_) { + i.x1 = 0; + i.y1 = 0; + i.x2 = 0; + i.y2 = 0; + } + this->send_set_zone_command_(); +} + +// Service set_radar_zone +void LD2450Component::on_set_radar_zone_(int zone_type, int zone1_x1, int zone1_y1, int zone1_x2, int zone1_y2, + int zone2_x1, int zone2_y1, int zone2_x2, int zone2_y2, int zone3_x1, + int zone3_y1, int zone3_x2, int zone3_y2) { + this->zone_type_ = zone_type; + int zone_parameters[12] = {zone1_x1, zone1_y1, zone1_x2, zone1_y2, zone2_x1, zone2_y1, + zone2_x2, zone2_y2, zone3_x1, zone3_y1, zone3_x2, zone3_y2}; + for (int i = 0; i < MAX_ZONES; i++) { + zone_config_[i].x1 = zone_parameters[i * 4]; + zone_config_[i].y1 = zone_parameters[i * 4 + 1]; + zone_config_[i].x2 = zone_parameters[i * 4 + 2]; + zone_config_[i].y2 = zone_parameters[i * 4 + 3]; + } + this->send_set_zone_command_(); +} + // Set Zone on LD2450 Sensor void LD2450Component::send_set_zone_command_() { uint8_t cmd_value[26] = {}; diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index 15d02f0865..3a1bc725d2 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -23,6 +23,9 @@ #ifdef USE_SELECT #include "esphome/components/select/select.h" #endif +#ifdef USE_API +#include "esphome/components/api/custom_api_device.h" +#endif #ifdef USE_TEXT_SENSOR #include "esphome/components/text_sensor/text_sensor.h" #endif @@ -110,7 +113,11 @@ enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 }; enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 }; +#ifdef USE_API +class LD2450Component : public Component, public uart::UARTDevice, public esphome::api::CustomAPIDevice { +#else class LD2450Component : public Component, public uart::UARTDevice { +#endif #ifdef USE_SENSOR SUB_SENSOR(target_count) SUB_SENSOR(still_target_count) diff --git a/tests/test1.1.yaml b/tests/test1.1.yaml index fa97da8eed..252713021c 100644 --- a/tests/test1.1.yaml +++ b/tests/test1.1.yaml @@ -43,6 +43,8 @@ uart: parity: NONE stop_bits: 1 +api: + ld2450: id: ld2450_radar uart_id: uart_ld2450 From fed899b486e320f1a86dffcdfaec479b30195ab7 Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sun, 5 Nov 2023 20:47:01 +0000 Subject: [PATCH 10/18] Add USE checks --- esphome/components/ld2450/ld2450.cpp | 11 ++++++++--- esphome/components/ld2450/ld2450.h | 2 +- esphome/components/ld2450/number/__init__.py | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index baa091a925..ee20607b05 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -20,8 +20,10 @@ LD2450Component::LD2450Component() {} void LD2450Component::setup() { ESP_LOGCONFIG(TAG, "Setting up HLK-LD2450"); +#ifdef USE_NUMBER this->pref_ = global_preferences->make_preference(this->presence_timeout_number_->get_object_id_hash()); this->set_presence_timeout(); +#endif this->read_all_info(); ESP_LOGCONFIG(TAG, "Mac Address : %s", const_cast(this->mac_.c_str())); ESP_LOGCONFIG(TAG, "Firmware Version : %s", const_cast(this->version_.c_str())); @@ -387,9 +389,8 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { #endif } // End loop thru targets - still_target_count = target_count - moving_target_count; - #ifdef USE_SENSOR + still_target_count = target_count - moving_target_count; // Target Count if (this->target_count_sensor_ != nullptr) { if (this->target_count_sensor_->get_state() != target_count) { @@ -444,6 +445,7 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { } } #endif +#ifdef USE_SENSOR // For presence timeout check if (target_count > 0) { this->presence_millis_ = millis(); @@ -454,6 +456,7 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { if (still_target_count > 0) { this->still_presence_millis_ = millis(); } +#endif } const char VERSION_FMT[] = "%u.%02X.%02X%02X%02X%02X"; @@ -708,6 +711,8 @@ void LD2450Component::set_move_distance_sensor(int target, sensor::Sensor *s) { void LD2450Component::set_move_resolution_sensor(int target, sensor::Sensor *s) { this->move_resolution_sensors_[target] = s; } +#endif +#ifdef USE_TEXT_SENSOR void LD2450Component::set_direction_text_sensor(int target, text_sensor::TextSensor *s) { this->direction_text_sensors_[target] = s; } @@ -751,7 +756,6 @@ void LD2450Component::set_presence_timeout() { } } } -#endif // Save Presence Timeout to flash void LD2450Component::save_to_flash_(float value) { this->pref_.save(&value); } @@ -764,6 +768,7 @@ float LD2450Component::restore_from_flash_() { } return value; } +#endif } // namespace ld2450 } // namespace esphome diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index 3a1bc725d2..4c67c20380 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -248,7 +248,7 @@ class LD2450Component : public Component, public uart::UARTDevice { int32_t still_presence_millis_ = 0; int32_t moving_presence_millis_ = 0; uint16_t throttle_; - uint16_t timeout_; + uint16_t timeout_ = 5; uint8_t zone_type_ = 0; std::string version_; std::string mac_; diff --git a/esphome/components/ld2450/number/__init__.py b/esphome/components/ld2450/number/__init__.py index 65cb514e03..e5c0331696 100644 --- a/esphome/components/ld2450/number/__init__.py +++ b/esphome/components/ld2450/number/__init__.py @@ -26,7 +26,7 @@ ZoneCoordinateNumber = ld2450_ns.class_("ZoneCoordinateNumber", number.Number) CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), - cv.Optional(CONF_PRESENCE_TIMEOUT): number.number_schema( + cv.Required(CONF_PRESENCE_TIMEOUT): number.number_schema( PresenceTimeoutNumber, unit_of_measurement=UNIT_SECOND, entity_category=ENTITY_CATEGORY_CONFIG, From 50afb3411aa17d6905368ecf246258f2e9763ee5 Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Wed, 8 Nov 2023 14:12:49 +0000 Subject: [PATCH 11/18] Initialize tx,ty,td,ts vars --- esphome/components/ld2450/ld2450.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index ee20607b05..e6b4e7c719 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -298,10 +298,10 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { int16_t start; int16_t val; uint8_t index; - int16_t tx; - int16_t ty; - int16_t td; - int16_t ts; + int16_t tx = 0; + int16_t ty = 0; + int16_t td = 0; + int16_t ts = 0; int16_t angle; std::string direction; From fab4fe8f28e26ef86807312bb934921427f9cfd8 Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Thu, 9 Nov 2023 06:30:56 +0000 Subject: [PATCH 12/18] Add zone target count --- esphome/components/ld2450/ld2450.cpp | 33 ++++++++++++++++++++++++++++ esphome/components/ld2450/ld2450.h | 32 +++++++++++++++++---------- esphome/components/ld2450/sensor.py | 16 +++++++++++++- tests/test1.1.yaml | 9 ++++++++ 4 files changed, 78 insertions(+), 12 deletions(-) diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index e6b4e7c719..21cf622f43 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -88,6 +88,9 @@ void LD2450Component::dump_config() { for (sensor::Sensor *s : this->move_resolution_sensors_) { LOG_SENSOR(" ", "NthTargetResolutionSensor", s); } + for (sensor::Sensor *s : this->zone_target_count_sensors_) { + LOG_SENSOR(" ", "NthZoneTargetCountSensor", s); + } #endif #ifdef USE_TEXT_SENSOR LOG_TEXT_SENSOR(" ", "VersionTextSensor", this->version_text_sensor_); @@ -132,6 +135,17 @@ void LD2450Component::loop() { } } +// Count targets in zone +uint8_t LD2450Component::count_targets_in_zone_(const Zone &zone) { + uint8_t count = 0; + for (auto &index : this->target_info_) { + if (index.x >= zone.x1 && index.x <= zone.x2 && index.y >= zone.y1 && index.y <= zone.y2) { + count++; + } + } + return count; +} + // Service reset_radar_zone void LD2450Component::on_reset_radar_zone_() { this->zone_type_ = 0; @@ -387,9 +401,25 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { } } #endif + + this->target_info_[index].x = tx; + this->target_info_[index].y = ty; + } // End loop thru targets #ifdef USE_SENSOR + // Loop thru zones + for (index = 0; index < MAX_ZONES; index++) { + // Publish Target Count in Zones + sensor::Sensor *sztc = this->zone_target_count_sensors_[index]; + if (sztc != nullptr) { + val = this->count_targets_in_zone_(this->zone_config_[index]); + if (sztc->get_state() != val) { + sztc->publish_state(val); + } + } + } // End loop thru zones + still_target_count = target_count - moving_target_count; // Target Count if (this->target_count_sensor_ != nullptr) { @@ -711,6 +741,9 @@ void LD2450Component::set_move_distance_sensor(int target, sensor::Sensor *s) { void LD2450Component::set_move_resolution_sensor(int target, sensor::Sensor *s) { this->move_resolution_sensors_[target] = s; } +void LD2450Component::set_zone_target_count_sensor(int zone, sensor::Sensor *s) { + this->zone_target_count_sensors_[zone] = s; +} #endif #ifdef USE_TEXT_SENSOR void LD2450Component::set_direction_text_sensor(int target, text_sensor::TextSensor *s) { diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index 4c67c20380..f5b4a356e4 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -48,7 +48,13 @@ static const uint8_t DEFAULT_PRESENCE_TIMEOUT = 5; // Timeout to reset presense static const uint8_t MAX_TARGETS = 3; // Max 3 Targets in LD2450 static const uint8_t MAX_ZONES = 3; // Max 3 Zones in LD2450 -// Zone coordinate config +// Target coordinate struct +struct Target { + int16_t x; + int16_t y; +}; + +// Zone coordinate struct struct Zone { int16_t x1 = 0; int16_t y1 = 0; @@ -182,6 +188,7 @@ class LD2450Component : public Component, public uart::UARTDevice { void set_move_angle_sensor(int target, sensor::Sensor *s); void set_move_distance_sensor(int target, sensor::Sensor *s); void set_move_resolution_sensor(int target, sensor::Sensor *s); + void set_zone_target_count_sensor(int zone, sensor::Sensor *s); #endif protected: @@ -202,6 +209,7 @@ class LD2450Component : public Component, public uart::UARTDevice { void on_reset_radar_zone_(); void save_to_flash_(float value); float restore_from_flash_(); + Target target_info_[MAX_TARGETS]; Zone zone_config_[MAX_ZONES]; void on_set_radar_zone_(int zone_type, int zone1_x1, int zone1_y1, int zone1_x2, int zone1_y2, int zone2_x1, int zone2_y1, int zone2_x2, int zone2_y2, int zone3_x1, int zone3_y1, int zone3_x2, @@ -253,22 +261,24 @@ class LD2450Component : public Component, public uart::UARTDevice { std::string version_; std::string mac_; bool get_timeout_status_(int32_t check_millis); + uint8_t count_targets_in_zone_(const Zone &zone); #ifdef USE_TEXT_SENSOR std::vector direction_text_sensors_ = std::vector(3); #endif #ifdef USE_NUMBER - std::vector zone_x1_numbers_ = std::vector(3); - std::vector zone_y1_numbers_ = std::vector(3); - std::vector zone_x2_numbers_ = std::vector(3); - std::vector zone_y2_numbers_ = std::vector(3); + std::vector zone_x1_numbers_ = std::vector(MAX_ZONES); + std::vector zone_y1_numbers_ = std::vector(MAX_ZONES); + std::vector zone_x2_numbers_ = std::vector(MAX_ZONES); + std::vector zone_y2_numbers_ = std::vector(MAX_ZONES); #endif #ifdef USE_SENSOR - std::vector move_x_sensors_ = std::vector(3); - std::vector move_y_sensors_ = std::vector(3); - std::vector move_speed_sensors_ = std::vector(3); - std::vector move_angle_sensors_ = std::vector(3); - std::vector move_distance_sensors_ = std::vector(3); - std::vector move_resolution_sensors_ = std::vector(3); + std::vector move_x_sensors_ = std::vector(MAX_TARGETS); + std::vector move_y_sensors_ = std::vector(MAX_TARGETS); + std::vector move_speed_sensors_ = std::vector(MAX_TARGETS); + std::vector move_angle_sensors_ = std::vector(MAX_TARGETS); + std::vector move_distance_sensors_ = std::vector(MAX_TARGETS); + std::vector move_resolution_sensors_ = std::vector(MAX_TARGETS); + std::vector zone_target_count_sensors_ = std::vector(MAX_ZONES); #endif }; diff --git a/esphome/components/ld2450/sensor.py b/esphome/components/ld2450/sensor.py index 6ad77708d0..f0c51503ff 100644 --- a/esphome/components/ld2450/sensor.py +++ b/esphome/components/ld2450/sensor.py @@ -21,6 +21,7 @@ CONF_STILL_TARGET_COUNT = "still_target_count" CONF_MOVING_TARGET_COUNT = "moving_target_count" MAX_TARGETS = 3 +MAX_ZONES = 3 CONF_X = "x" CONF_Y = "y" @@ -78,6 +79,16 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend( ) for n in range(MAX_TARGETS) }, + { + cv.Optional(f"zone_{n+1}"): cv.Schema( + { + cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema( + icon="mdi:map-marker-account", + ), + } + ) + for n in range(MAX_ZONES) + }, ) @@ -95,7 +106,6 @@ async def to_code(config): if moving_target_count_config := config.get(CONF_MOVING_TARGET_COUNT): sens = await sensor.new_sensor(moving_target_count_config) cg.add(ld2450_component.set_moving_target_count_sensor(sens)) - for n in range(MAX_TARGETS): if target_conf := config.get(f"target_{n+1}"): if x_config := target_conf.get(CONF_X): @@ -116,3 +126,7 @@ async def to_code(config): if resolution_config := target_conf.get(CONF_RESOLUTION): sens = await sensor.new_sensor(resolution_config) cg.add(ld2450_component.set_move_resolution_sensor(n, sens)) + for n in range(MAX_ZONES): + if zone_target_count_config := config.get(f"zone_{n+1}_target_count"): + sens = await sensor.new_sensor(zone_target_count_config) + cg.add(ld2450_component.set_zone_target_count_sensor(n, sens)) diff --git a/tests/test1.1.yaml b/tests/test1.1.yaml index 252713021c..b1523edeb9 100644 --- a/tests/test1.1.yaml +++ b/tests/test1.1.yaml @@ -300,6 +300,15 @@ sensor: name: Target-3 Distance resolution: name: Target-3 Resolution + zone_1: + target_count: + name: "Zone-1 Target Count" + zone_2: + target_count: + name: "Zone-2 Target Count" + zone_3: + target_count: + name: "Zone-3 Target Count" binary_sensor: - platform: ld2450 From 15d3f171793ef8f18e7727b65d01fe98ca9c1966 Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Thu, 9 Nov 2023 08:25:23 +0000 Subject: [PATCH 13/18] Add Zone still and moving target sensors --- esphome/components/ld2450/ld2450.cpp | 59 +++++++++++++++++++++++----- esphome/components/ld2450/ld2450.h | 7 +++- esphome/components/ld2450/sensor.py | 19 +++++++-- tests/test1.1.yaml | 18 +++++++-- 4 files changed, 87 insertions(+), 16 deletions(-) diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index 21cf622f43..b2961cfa97 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -91,6 +91,12 @@ void LD2450Component::dump_config() { for (sensor::Sensor *s : this->zone_target_count_sensors_) { LOG_SENSOR(" ", "NthZoneTargetCountSensor", s); } + for (sensor::Sensor *s : this->zone_still_target_count_sensors_) { + LOG_SENSOR(" ", "NthZoneStillTargetCountSensor", s); + } + for (sensor::Sensor *s : this->zone_moving_target_count_sensors_) { + LOG_SENSOR(" ", "NthZoneMovingTargetCountSensor", s); + } #endif #ifdef USE_TEXT_SENSOR LOG_TEXT_SENSOR(" ", "VersionTextSensor", this->version_text_sensor_); @@ -136,10 +142,11 @@ void LD2450Component::loop() { } // Count targets in zone -uint8_t LD2450Component::count_targets_in_zone_(const Zone &zone) { +uint8_t LD2450Component::count_targets_in_zone_(const Zone &zone, bool is_moving) { uint8_t count = 0; for (auto &index : this->target_info_) { - if (index.x >= zone.x1 && index.x <= zone.x2 && index.y >= zone.y1 && index.y <= zone.y2) { + if (index.x > zone.x1 && index.x < zone.x2 && index.y > zone.y1 && index.y < zone.y2 && + index.is_moving == is_moving) { count++; } } @@ -318,12 +325,14 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { int16_t ts = 0; int16_t angle; std::string direction; + bool is_moving; #ifdef USE_SENSOR // Loop thru targets // X for (index = 0; index < MAX_TARGETS; index++) { start = TARGET_X + index * 8; + is_moving = false; sensor::Sensor *sx = this->move_x_sensors_[index]; if (sx != nullptr) { val = this->decode_coordinate_(buffer[start], buffer[start + 1]); @@ -348,8 +357,10 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { if (ss != nullptr) { val = this->decode_speed_(buffer[start], buffer[start + 1]); ts = val; - if (val > 0) + if (val > 0) { + is_moving = true; moving_target_count++; + } if (ss->get_state() != val) { ss->publish_state(val); } @@ -404,20 +415,44 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { this->target_info_[index].x = tx; this->target_info_[index].y = ty; + this->target_info_[index].is_moving = is_moving; } // End loop thru targets #ifdef USE_SENSOR // Loop thru zones + uint8_t zone_still_targets = 0; + uint8_t zone_moving_targets = 0; + uint8_t zone_all_targets = 0; for (index = 0; index < MAX_ZONES; index++) { - // Publish Target Count in Zones - sensor::Sensor *sztc = this->zone_target_count_sensors_[index]; - if (sztc != nullptr) { - val = this->count_targets_in_zone_(this->zone_config_[index]); - if (sztc->get_state() != val) { - sztc->publish_state(val); + // Publish Still Target Count in Zones + sensor::Sensor *szstc = this->zone_still_target_count_sensors_[index]; + if (szstc != nullptr) { + zone_still_targets = this->count_targets_in_zone_(this->zone_config_[index], false); + if (szstc->get_state() != zone_still_targets) { + szstc->publish_state(zone_still_targets); } } + + // Publish Moving Target Count in Zones + sensor::Sensor *szmtc = this->zone_moving_target_count_sensors_[index]; + if (szmtc != nullptr) { + zone_moving_targets = this->count_targets_in_zone_(this->zone_config_[index], true); + if (szmtc->get_state() != zone_moving_targets) { + szmtc->publish_state(zone_moving_targets); + } + } + + zone_all_targets = zone_still_targets + zone_moving_targets; + + // Publish All Target Count in Zones + sensor::Sensor *sztc = this->zone_target_count_sensors_[index]; + if (sztc != nullptr) { + if (sztc->get_state() != zone_all_targets) { + sztc->publish_state(zone_all_targets); + } + } + } // End loop thru zones still_target_count = target_count - moving_target_count; @@ -744,6 +779,12 @@ void LD2450Component::set_move_resolution_sensor(int target, sensor::Sensor *s) void LD2450Component::set_zone_target_count_sensor(int zone, sensor::Sensor *s) { this->zone_target_count_sensors_[zone] = s; } +void LD2450Component::set_zone_still_target_count_sensor(int zone, sensor::Sensor *s) { + this->zone_still_target_count_sensors_[zone] = s; +} +void LD2450Component::set_zone_moving_target_count_sensor(int zone, sensor::Sensor *s) { + this->zone_moving_target_count_sensors_[zone] = s; +} #endif #ifdef USE_TEXT_SENSOR void LD2450Component::set_direction_text_sensor(int target, text_sensor::TextSensor *s) { diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index f5b4a356e4..06da2e8cb7 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -52,6 +52,7 @@ static const uint8_t MAX_ZONES = 3; // Max 3 Zones in LD2450 struct Target { int16_t x; int16_t y; + bool is_moving; }; // Zone coordinate struct @@ -189,6 +190,8 @@ class LD2450Component : public Component, public uart::UARTDevice { void set_move_distance_sensor(int target, sensor::Sensor *s); void set_move_resolution_sensor(int target, sensor::Sensor *s); void set_zone_target_count_sensor(int zone, sensor::Sensor *s); + void set_zone_still_target_count_sensor(int zone, sensor::Sensor *s); + void set_zone_moving_target_count_sensor(int zone, sensor::Sensor *s); #endif protected: @@ -261,7 +264,7 @@ class LD2450Component : public Component, public uart::UARTDevice { std::string version_; std::string mac_; bool get_timeout_status_(int32_t check_millis); - uint8_t count_targets_in_zone_(const Zone &zone); + uint8_t count_targets_in_zone_(const Zone &zone, bool is_moving); #ifdef USE_TEXT_SENSOR std::vector direction_text_sensors_ = std::vector(3); #endif @@ -279,6 +282,8 @@ class LD2450Component : public Component, public uart::UARTDevice { std::vector move_distance_sensors_ = std::vector(MAX_TARGETS); std::vector move_resolution_sensors_ = std::vector(MAX_TARGETS); std::vector zone_target_count_sensors_ = std::vector(MAX_ZONES); + std::vector zone_still_target_count_sensors_ = std::vector(MAX_ZONES); + std::vector zone_moving_target_count_sensors_ = std::vector(MAX_ZONES); #endif }; diff --git a/esphome/components/ld2450/sensor.py b/esphome/components/ld2450/sensor.py index f0c51503ff..3f58f5b6ce 100644 --- a/esphome/components/ld2450/sensor.py +++ b/esphome/components/ld2450/sensor.py @@ -85,6 +85,12 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend( cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema( icon="mdi:map-marker-account", ), + cv.Optional(CONF_STILL_TARGET_COUNT): sensor.sensor_schema( + icon="mdi:map-marker-account", + ), + cv.Optional(CONF_MOVING_TARGET_COUNT): sensor.sensor_schema( + icon="mdi:map-marker-account", + ), } ) for n in range(MAX_ZONES) @@ -127,6 +133,13 @@ async def to_code(config): sens = await sensor.new_sensor(resolution_config) cg.add(ld2450_component.set_move_resolution_sensor(n, sens)) for n in range(MAX_ZONES): - if zone_target_count_config := config.get(f"zone_{n+1}_target_count"): - sens = await sensor.new_sensor(zone_target_count_config) - cg.add(ld2450_component.set_zone_target_count_sensor(n, sens)) + if zone_config := config.get(f"zone_{n+1}"): + if target_count_config := zone_config.get(CONF_TARGET_COUNT): + sens = await sensor.new_sensor(target_count_config) + cg.add(ld2450_component.set_zone_target_count_sensor(n, sens)) + if still_target_count_config := zone_config.get(CONF_STILL_TARGET_COUNT): + sens = await sensor.new_sensor(still_target_count_config) + cg.add(ld2450_component.set_zone_still_target_count_sensor(n, sens)) + if moving_target_count_config := zone_config.get(CONF_MOVING_TARGET_COUNT): + sens = await sensor.new_sensor(moving_target_count_config) + cg.add(ld2450_component.set_zone_moving_target_count_sensor(n, sens)) diff --git a/tests/test1.1.yaml b/tests/test1.1.yaml index b1523edeb9..49b114e01b 100644 --- a/tests/test1.1.yaml +++ b/tests/test1.1.yaml @@ -302,13 +302,25 @@ sensor: name: Target-3 Resolution zone_1: target_count: - name: "Zone-1 Target Count" + name: Zone-1 All Target Count + still_target_count: + name: Zone-1 Still Target Count + moving_target_count: + name: Zone-1 Moving Target Count zone_2: target_count: - name: "Zone-2 Target Count" + name: Zone-2 All Target Count + still_target_count: + name: Zone-2 Still Target Count + moving_target_count: + name: Zone-2 Moving Target Count zone_3: target_count: - name: "Zone-3 Target Count" + name: Zone-3 All Target Count + still_target_count: + name: Zone-3 Still Target Count + moving_target_count: + name: Zone-3 Moving Target Count binary_sensor: - platform: ld2450 From 21af3f4eee4880b1b121bbdf2fdbbd249c8ae801 Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Thu, 9 Nov 2023 12:44:45 +0000 Subject: [PATCH 14/18] Refactor and cleanup --- esphome/components/ld2450/binary_sensor.py | 10 ++++-- esphome/components/ld2450/ld2450.cpp | 28 ++++++++-------- esphome/components/ld2450/ld2450.h | 16 ++++----- esphome/components/ld2450/number/__init__.py | 13 +++++--- esphome/components/ld2450/sensor.py | 35 +++++++++++++------- 5 files changed, 60 insertions(+), 42 deletions(-) diff --git a/esphome/components/ld2450/binary_sensor.py b/esphome/components/ld2450/binary_sensor.py index d16d1f79cb..e5ea7a6a85 100644 --- a/esphome/components/ld2450/binary_sensor.py +++ b/esphome/components/ld2450/binary_sensor.py @@ -12,19 +12,23 @@ CONF_HAS_TARGET = "has_target" CONF_HAS_MOVING_TARGET = "has_moving_target" CONF_HAS_STILL_TARGET = "has_still_target" +ICON_SHIELD_ACCOUNT = "mdi:shield-account" +ICON_TARGET_ACCOUNT = "mdi:target-account" +ICON_MEDITATION = "mdi:meditation" + CONFIG_SCHEMA = { cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema( device_class=DEVICE_CLASS_OCCUPANCY, - icon="mdi:shield-account", + icon=ICON_SHIELD_ACCOUNT, ), cv.Optional(CONF_HAS_MOVING_TARGET): binary_sensor.binary_sensor_schema( device_class=DEVICE_CLASS_MOTION, - icon="mdi:target-account", + icon=ICON_TARGET_ACCOUNT, ), cv.Optional(CONF_HAS_STILL_TARGET): binary_sensor.binary_sensor_schema( device_class=DEVICE_CLASS_OCCUPANCY, - icon="mdi:meditation", + icon=ICON_MEDITATION, ), } diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index b2961cfa97..2bdaddfd1b 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -25,8 +25,8 @@ void LD2450Component::setup() { this->set_presence_timeout(); #endif this->read_all_info(); - ESP_LOGCONFIG(TAG, "Mac Address : %s", const_cast(this->mac_.c_str())); - ESP_LOGCONFIG(TAG, "Firmware Version : %s", const_cast(this->version_.c_str())); + ESP_LOGCONFIG(TAG, "Mac Address: %s", const_cast(this->mac_.c_str())); + ESP_LOGCONFIG(TAG, "Firmware Version: %s", const_cast(this->version_.c_str())); ESP_LOGCONFIG(TAG, "HLK-LD2450 setup complete"); #ifdef USE_API ESP_LOGCONFIG(TAG, "Registering services"); @@ -413,6 +413,7 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { } #endif + // Store target info for zone target count this->target_info_[index].x = tx; this->target_info_[index].y = ty; this->target_info_[index].is_moving = is_moving; @@ -433,7 +434,6 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, int len) { szstc->publish_state(zone_still_targets); } } - // Publish Moving Target Count in Zones sensor::Sensor *szmtc = this->zone_moving_target_count_sensors_[index]; if (szmtc != nullptr) { @@ -557,8 +557,9 @@ std::string format_mac(uint8_t *buffer) { return mac; } +// Handle the UART serial ack data bool LD2450Component::handle_ack_data_(uint8_t *buffer, int len) { - ESP_LOGD(TAG, "Handling ACK DATA for COMMAND %02X", buffer[COMMAND]); + ESP_LOGV(TAG, "Handling ACK DATA for COMMAND %02X", buffer[COMMAND]); if (len < 10) { ESP_LOGE(TAG, "Error with last command: Incorrect length"); return true; @@ -577,16 +578,16 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, int len) { } switch (buffer[COMMAND]) { case lowbyte(CMD_ENABLE_CONF): - ESP_LOGV(TAG, "Handled Enable conf command"); + ESP_LOGV(TAG, "Handled Enable Conf command"); break; case lowbyte(CMD_DISABLE_CONF): - ESP_LOGV(TAG, "Handled Disabled conf command"); + ESP_LOGV(TAG, "Handled Disabled Conf command"); break; case lowbyte(CMD_SET_BAUD_RATE): - ESP_LOGV(TAG, "Handled baud rate change command"); + ESP_LOGV(TAG, "Handled Baud rate change command"); #ifdef USE_SELECT if (this->baud_rate_select_ != nullptr) { - ESP_LOGV(TAG, "Change baud rate component config to %s and reinstall", this->baud_rate_select_->state.c_str()); + ESP_LOGV(TAG, "Change Baud rate to %s", this->baud_rate_select_->state.c_str()); } #endif break; @@ -640,7 +641,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, int len) { this->publish_zone_type(); #ifdef USE_SELECT if (this->zone_type_select_ != nullptr) { - ESP_LOGV(TAG, "Change Zone Type component config to: %s", this->zone_type_select_->state.c_str()); + ESP_LOGV(TAG, "Change Zone Type to: %s", this->zone_type_select_->state.c_str()); } #endif if (buffer[10] == 0x00) { @@ -676,16 +677,16 @@ void LD2450Component::readline_(int readch, uint8_t *buffer, int len) { } if (pos >= 4) { if (buffer[pos - 2] == 0x55 && buffer[pos - 1] == 0xCC) { - ESP_LOGV(TAG, "Handle Periodic Radar Data"); + ESP_LOGV(TAG, "Handle LD2450 Periodic Radar Data"); this->handle_periodic_data_(buffer, pos); pos = 0; // Reset position index ready for next time } else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 && buffer[pos - 1] == 0x01) { - ESP_LOGV(TAG, "Handle Commad ACK Data"); + ESP_LOGV(TAG, "Handle LD2450 Commad ACK Data"); if (this->handle_ack_data_(buffer, pos)) { pos = 0; // Reset position index ready for next time } else { - ESP_LOGV(TAG, "Command ACK Data incomplete"); + ESP_LOGV(TAG, "LD2450 Command ACK Data incomplete"); } } } @@ -734,13 +735,12 @@ void LD2450Component::publish_zone_type() { #endif } -// Set Single/Multiplayer +// Set Single/Multiplayer target detection void LD2450Component::set_multi_target(bool enable) { this->set_config_mode_(true); uint8_t cmd = enable ? CMD_MULTI_TARGET : CMD_SINGLE_TARGET; this->send_command_(cmd, nullptr, 0); this->set_config_mode_(false); - // this->set_timeout(200, [this]() { this->read_all_info(); }); } // LD2450 factory reset diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index 06da2e8cb7..9c9a9b6898 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -40,8 +40,6 @@ namespace esphome { namespace ld2450 { -#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1) - // Constants static const uint16_t START_DELAY = 5000; // Sensor startup delay 5 sec. static const uint8_t DEFAULT_PRESENCE_TIMEOUT = 5; // Timeout to reset presense status 5 sec. @@ -63,7 +61,7 @@ struct Zone { int16_t y2 = 0; }; -// Commands +// LD2450 UART Serial Commands static const uint8_t CMD_ENABLE_CONF = 0x00FF; static const uint8_t CMD_DISABLE_CONF = 0x00FE; static const uint8_t CMD_VERSION = 0x00A0; @@ -88,27 +86,27 @@ enum BaudRateStructure : uint8_t { BAUD_RATE_460800 = 8 }; +// Convert Baud Rate enum to int static const std::map BAUD_RATE_ENUM_TO_INT{ {"9600", BAUD_RATE_9600}, {"19200", BAUD_RATE_19200}, {"38400", BAUD_RATE_38400}, {"57600", BAUD_RATE_57600}, {"115200", BAUD_RATE_115200}, {"230400", BAUD_RATE_230400}, {"256000", BAUD_RATE_256000}, {"460800", BAUD_RATE_460800}}; +// Zone Type struct enum ZoneTypeStructure : uint8_t { ZONE_DISABLED = 0, ZONE_DETECTION = 1, ZONE_FILTER = 2 }; +// Convert Zone Type int to Enum static const std::map ZONE_TYPE_INT_TO_ENUM{ {ZONE_DISABLED, "Disabled"}, {ZONE_DETECTION, "Detection"}, {ZONE_FILTER, "Filter"}}; +// Convert Zone Type enum to int static const std::map ZONE_TYPE_ENUM_TO_INT{ {"Disabled", ZONE_DISABLED}, {"Detection", ZONE_DETECTION}, {"Filter", ZONE_FILTER}}; -// Command Header & Footer +// LD2450 UART serial Command Header & Footer static const uint8_t CMD_FRAME_HEADER[4] = {0xFD, 0xFC, 0xFB, 0xFA}; static const uint8_t CMD_FRAME_END[4] = {0x04, 0x03, 0x02, 0x01}; -// Data Header & Footer -static const uint8_t DATA_FRAME_HEADER[4] = {0xAA, 0xFF, 0x03, 0x00}; -static const uint8_t DATA_FRAME_END[2] = {0x55, 0xCC}; - enum PeriodicDataStructure : uint8_t { TARGET_X = 4, TARGET_Y = 6, @@ -119,7 +117,7 @@ enum PeriodicDataStructure : uint8_t { enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 }; enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 }; - +// Use CustomAPIDevice if API is defined in YAML - used for service calls to set/reset zone #ifdef USE_API class LD2450Component : public Component, public uart::UARTDevice, public esphome::api::CustomAPIDevice { #else diff --git a/esphome/components/ld2450/number/__init__.py b/esphome/components/ld2450/number/__init__.py index e5c0331696..6371ccd43a 100644 --- a/esphome/components/ld2450/number/__init__.py +++ b/esphome/components/ld2450/number/__init__.py @@ -20,6 +20,11 @@ CONF_Y1 = "y1" CONF_X2 = "x2" CONF_Y2 = "y2" +ICON_ARROW_TOP_LEFT_BOLD_BOX_OUTLINE = "mdi:arrow-top-left-bold-box-outline" +ICON_ARROW_TOP_LEFT = "mdi:arrow-top-left" +ICON_ARROW_BOTTOM_RIGHT_BOLD_BOX_OUTLINE = "mdi:arrow-bottom-right-bold-box-outline" +ICON_ARROW_BOTTOM_RIGHT = "mdi:arrow-bottom-right" + PresenceTimeoutNumber = ld2450_ns.class_("PresenceTimeoutNumber", number.Number) ZoneCoordinateNumber = ld2450_ns.class_("ZoneCoordinateNumber", number.Number) @@ -44,28 +49,28 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend( device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_MILLIMETER, entity_category=ENTITY_CATEGORY_CONFIG, - icon="mdi:arrow-top-left-bold-box-outline", + icon=ICON_ARROW_TOP_LEFT_BOLD_BOX_OUTLINE, ), cv.Required(CONF_Y1): number.number_schema( ZoneCoordinateNumber, device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_MILLIMETER, entity_category=ENTITY_CATEGORY_CONFIG, - icon="mdi:arrow-top-left", + icon=ICON_ARROW_TOP_LEFT, ), cv.Required(CONF_X2): number.number_schema( ZoneCoordinateNumber, device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_MILLIMETER, entity_category=ENTITY_CATEGORY_CONFIG, - icon="mdi:arrow-bottom-right-bold-box-outline", + icon=ICON_ARROW_BOTTOM_RIGHT_BOLD_BOX_OUTLINE, ), cv.Required(CONF_Y2): number.number_schema( ZoneCoordinateNumber, device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_MILLIMETER, entity_category=ENTITY_CATEGORY_CONFIG, - icon="mdi:arrow-bottom-right", + icon=ICON_ARROW_BOTTOM_RIGHT, ), } ) diff --git a/esphome/components/ld2450/sensor.py b/esphome/components/ld2450/sensor.py index 3f58f5b6ce..08511615ac 100644 --- a/esphome/components/ld2450/sensor.py +++ b/esphome/components/ld2450/sensor.py @@ -27,17 +27,28 @@ CONF_X = "x" CONF_Y = "y" CONF_ANGLE = "angle" +ICON_ACCOUNT_GROUP = "mdi:account-group" +ICON_ACCOUNT_SWITCH = "mdi:account-switch" +ICON_HUMAN_GREETING_PROXIMITY = "mdi:human-greeting-proximity" +ICON_ALPHA_X_BOX_OUTLINE = "mdi:alpha-x-box-outline" +ICON_ALPHA_Y_BOX_OUTLINE = "mdi:alpha-y-box-outline" +ICON_SPEEDOMETER_SLOW = "mdi:speedometer-slow" +ICON_FORMAT_TEXT_ROTATION_ANGLE_UP = "mdi:format-text-rotation-angle-up" +ICON_MAP_MARKER_DISTANCE = "mdi:map-marker-distance" +ICON_RELATION_ZERO_OR_ONE_TO_ZERO_OR_ONE = "mdi:relation-zero-or-one-to-zero-or-one" +ICON_MAP_MARKER_ACCOUNT = "mdi:map-marker-account" + CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema( - icon="mdi:account-group", + icon=ICON_ACCOUNT_GROUP, ), cv.Optional(CONF_STILL_TARGET_COUNT): sensor.sensor_schema( - icon="mdi:human-greeting-proximity", + icon=ICON_HUMAN_GREETING_PROXIMITY, ), cv.Optional(CONF_MOVING_TARGET_COUNT): sensor.sensor_schema( - icon="mdi:account-switch", + icon=ICON_ACCOUNT_SWITCH, ), } ) @@ -49,31 +60,31 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend( cv.Optional(CONF_X): sensor.sensor_schema( device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_MILLIMETER, - icon="mdi:alpha-x-box-outline", + icon=ICON_ALPHA_X_BOX_OUTLINE, ), cv.Optional(CONF_Y): sensor.sensor_schema( device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_MILLIMETER, - icon="mdi:alpha-y-box-outline", + icon=ICON_ALPHA_Y_BOX_OUTLINE, ), cv.Optional(CONF_SPEED): sensor.sensor_schema( device_class=DEVICE_CLASS_SPEED, unit_of_measurement=UNIT_MILLIMETER_PER_SECOND, - icon="mdi:speedometer-slow", + icon=ICON_SPEEDOMETER_SLOW, ), cv.Optional(CONF_ANGLE): sensor.sensor_schema( unit_of_measurement=UNIT_DEGREES, - icon="mdi:format-text-rotation-angle-up", + icon=ICON_FORMAT_TEXT_ROTATION_ANGLE_UP, ), cv.Optional(CONF_DISTANCE): sensor.sensor_schema( device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_MILLIMETER, - icon="mdi:map-marker-distance", + icon=ICON_MAP_MARKER_DISTANCE, ), cv.Optional(CONF_RESOLUTION): sensor.sensor_schema( device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_MILLIMETER, - icon="mdi:relation-zero-or-one-to-zero-or-one", + icon=ICON_RELATION_ZERO_OR_ONE_TO_ZERO_OR_ONE, ), } ) @@ -83,13 +94,13 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend( cv.Optional(f"zone_{n+1}"): cv.Schema( { cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema( - icon="mdi:map-marker-account", + icon=ICON_MAP_MARKER_ACCOUNT, ), cv.Optional(CONF_STILL_TARGET_COUNT): sensor.sensor_schema( - icon="mdi:map-marker-account", + icon=ICON_MAP_MARKER_ACCOUNT, ), cv.Optional(CONF_MOVING_TARGET_COUNT): sensor.sensor_schema( - icon="mdi:map-marker-account", + icon=ICON_MAP_MARKER_ACCOUNT, ), } ) From 378b46f81134775222bcd1fa2086cbea17b9de82 Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Sat, 18 Nov 2023 03:40:10 +0000 Subject: [PATCH 15/18] Fix int service variables for ESP32-C3 --- esphome/components/ld2450/ld2450.cpp | 7 ++++--- esphome/components/ld2450/ld2450.h | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index 2bdaddfd1b..97f6c75e43 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -166,9 +166,10 @@ void LD2450Component::on_reset_radar_zone_() { } // Service set_radar_zone -void LD2450Component::on_set_radar_zone_(int zone_type, int zone1_x1, int zone1_y1, int zone1_x2, int zone1_y2, - int zone2_x1, int zone2_y1, int zone2_x2, int zone2_y2, int zone3_x1, - int zone3_y1, int zone3_x2, int zone3_y2) { +void LD2450Component::on_set_radar_zone_(int32_t zone_type, int32_t zone1_x1, int32_t zone1_y1, int32_t zone1_x2, + int32_t zone1_y2, int32_t zone2_x1, int32_t zone2_y1, int32_t zone2_x2, + int32_t zone2_y2, int32_t zone3_x1, int32_t zone3_y1, int32_t zone3_x2, + int32_t zone3_y2) { this->zone_type_ = zone_type; int zone_parameters[12] = {zone1_x1, zone1_y1, zone1_x2, zone1_y2, zone2_x1, zone2_y1, zone2_x2, zone2_y2, zone3_x1, zone3_y1, zone3_x2, zone3_y2}; diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index 9c9a9b6898..f4e0c1feeb 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -212,9 +212,9 @@ class LD2450Component : public Component, public uart::UARTDevice { float restore_from_flash_(); Target target_info_[MAX_TARGETS]; Zone zone_config_[MAX_ZONES]; - void on_set_radar_zone_(int zone_type, int zone1_x1, int zone1_y1, int zone1_x2, int zone1_y2, int zone2_x1, - int zone2_y1, int zone2_x2, int zone2_y2, int zone3_x1, int zone3_y1, int zone3_x2, - int zone3_y2); + void on_set_radar_zone_(int32_t zone_type, int32_t zone1_x1, int32_t zone1_y1, int32_t zone1_x2, int32_t zone1_y2, + int32_t zone2_x1, int32_t zone2_y1, int32_t zone2_x2, int32_t zone2_y2, int32_t zone3_x1, + int32_t zone3_y1, int32_t zone3_x2, int32_t zone3_y2); int16_t decode_coordinate_(uint8_t low_byte, uint8_t high_byte) { int16_t coordinate = (high_byte & 0x7F) << 8 | low_byte; if ((high_byte & 0x80) == 0) From d92660185f0edd4d98a71456a6bd5ea98da24d9a Mon Sep 17 00:00:00 2001 From: Hareesh M U Date: Thu, 14 Dec 2023 06:35:16 +0000 Subject: [PATCH 16/18] Update test1.1 uart_ld2450 pin --- tests/test1.1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test1.1.yaml b/tests/test1.1.yaml index 49b114e01b..79f1e9345c 100644 --- a/tests/test1.1.yaml +++ b/tests/test1.1.yaml @@ -38,7 +38,7 @@ uart: rx_buffer_size: 1024 - id: uart_ld2450 tx_pin: 18 - rx_pin: 23 + rx_pin: 19 baud_rate: 256000 parity: NONE stop_bits: 1 From 3b19b581e8ce1eebb420bfc953b6f900906ecfc6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 2 May 2024 15:55:21 +1200 Subject: [PATCH 17/18] Move tests --- tests/components/ld2450/common.yaml | 168 ++++++++++++++++++++ tests/components/ld2450/test.esp32-idf.yaml | 5 + tests/components/ld2450/test.esp32.yaml | 5 + tests/components/ld2450/test.esp8266.yaml | 5 + tests/test1.1.yaml | 167 ------------------- 5 files changed, 183 insertions(+), 167 deletions(-) create mode 100644 tests/components/ld2450/common.yaml create mode 100644 tests/components/ld2450/test.esp32-idf.yaml create mode 100644 tests/components/ld2450/test.esp32.yaml create mode 100644 tests/components/ld2450/test.esp8266.yaml diff --git a/tests/components/ld2450/common.yaml b/tests/components/ld2450/common.yaml new file mode 100644 index 0000000000..aac9eeefe7 --- /dev/null +++ b/tests/components/ld2450/common.yaml @@ -0,0 +1,168 @@ +uart: + id: ld2450_uart + tx_pin: ${uart_tx_pin} + rx_pin: ${uart_rx_pin} + baud_rate: 256000 + parity: NONE + stop_bits: 1 + +ld2450: + id: ld2450_radar + uart_id: ld2450_uart + throttle: 1000ms + +button: + - platform: ld2450 + ld2450_id: ld2450_radar + factory_reset: + name: "LD2450 Factory Reset" + entity_category: "config" + restart: + name: "LD2450 Restart" + entity_category: "config" + +sensor: + - platform: ld2450 + ld2450_id: ld2450_radar + target_count: + name: Presence Target Count + still_target_count: + name: Still Target Count + moving_target_count: + name: Moving Target Count + target_1: + x: + name: Target-1 X + y: + name: Target-1 Y + speed: + name: Target-1 Speed + angle: + name: Target-1 Angle + distance: + name: Target-1 Distance + resolution: + name: Target-1 Resolution + target_2: + x: + name: Target-2 X + y: + name: Target-2 Y + speed: + name: Target-2 Speed + angle: + name: Target-2 Angle + distance: + name: Target-2 Distance + resolution: + name: Target-2 Resolution + target_3: + x: + name: Target-3 X + y: + name: Target-3 Y + speed: + name: Target-3 Speed + angle: + name: Target-3 Angle + distance: + name: Target-3 Distance + resolution: + name: Target-3 Resolution + zone_1: + target_count: + name: Zone-1 All Target Count + still_target_count: + name: Zone-1 Still Target Count + moving_target_count: + name: Zone-1 Moving Target Count + zone_2: + target_count: + name: Zone-2 All Target Count + still_target_count: + name: Zone-2 Still Target Count + moving_target_count: + name: Zone-2 Moving Target Count + zone_3: + target_count: + name: Zone-3 All Target Count + still_target_count: + name: Zone-3 Still Target Count + moving_target_count: + name: Zone-3 Moving Target Count + +binary_sensor: + - platform: ld2450 + ld2450_id: ld2450_radar + has_target: + name: Presence + has_moving_target: + name: Moving Target + has_still_target: + name: Still Target + +switch: + - platform: ld2450 + ld2450_id: ld2450_radar + bluetooth: + name: "Bluetooth" + multi_target: + name: "Multi Target Tracking" + +text_sensor: + - platform: ld2450 + ld2450_id: ld2450_radar + version: + name: "LD2450 Firmware" + mac_address: + name: "LD2450 BT MAC" + target_1: + direction: + name: "Target-1 Direction" + target_2: + direction: + name: "Target-2 Direction" + target_3: + direction: + name: "Target-3 Direction" + +number: + - platform: ld2450 + ld2450_id: ld2450_radar + presence_timeout: + name: "Timeout" + zone_1: + x1: + name: Zone-1 X1 + y1: + name: Zone-1 Y1 + x2: + name: Zone-1 X2 + y2: + name: Zone-1 Y2 + zone_2: + x1: + name: Zone-2 X1 + y1: + name: Zone-2 Y1 + x2: + name: Zone-2 X2 + y2: + name: Zone-2 Y2 + zone_3: + x1: + name: Zone-3 X1 + y1: + name: Zone-3 Y1 + x2: + name: Zone-3 X2 + y2: + name: Zone-3 Y2 + +select: + - platform: ld2450 + ld2450_id: ld2450_radar + baud_rate: + name: "Baud rate" + zone_type: + name: "Zone Type" diff --git a/tests/components/ld2450/test.esp32-idf.yaml b/tests/components/ld2450/test.esp32-idf.yaml new file mode 100644 index 0000000000..adc2c4d24a --- /dev/null +++ b/tests/components/ld2450/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + uart_tx_pin: GPIO1 + uart_rx_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/ld2450/test.esp32.yaml b/tests/components/ld2450/test.esp32.yaml new file mode 100644 index 0000000000..adc2c4d24a --- /dev/null +++ b/tests/components/ld2450/test.esp32.yaml @@ -0,0 +1,5 @@ +substitutions: + uart_tx_pin: GPIO1 + uart_rx_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/ld2450/test.esp8266.yaml b/tests/components/ld2450/test.esp8266.yaml new file mode 100644 index 0000000000..adc2c4d24a --- /dev/null +++ b/tests/components/ld2450/test.esp8266.yaml @@ -0,0 +1,5 @@ +substitutions: + uart_tx_pin: GPIO1 + uart_rx_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/test1.1.yaml b/tests/test1.1.yaml index 79f1e9345c..c71aa6e0ef 100644 --- a/tests/test1.1.yaml +++ b/tests/test1.1.yaml @@ -36,19 +36,6 @@ uart: rx_pin: GPIO26 baud_rate: 115200 rx_buffer_size: 1024 - - id: uart_ld2450 - tx_pin: 18 - rx_pin: 19 - baud_rate: 256000 - parity: NONE - stop_bits: 1 - -api: - -ld2450: - id: ld2450_radar - uart_id: uart_ld2450 - throttle: 1000ms adalight: @@ -243,157 +230,3 @@ button: - canbus.send: "abc" - canbus.send: [0, 1, 2] - canbus.send: !lambda return {0, 1, 2}; - - platform: ld2450 - ld2450_id: ld2450_radar - factory_reset: - name: "LD2450 Factory Reset" - entity_category: "config" - restart: - name: "LD2450 Restart" - entity_category: "config" - -sensor: - - platform: ld2450 - ld2450_id: ld2450_radar - target_count: - name: Presence Target Count - still_target_count: - name: Still Target Count - moving_target_count: - name: Moving Target Count - target_1: - x: - name: Target-1 X - y: - name: Target-1 Y - speed: - name: Target-1 Speed - angle: - name: Target-1 Angle - distance: - name: Target-1 Distance - resolution: - name: Target-1 Resolution - target_2: - x: - name: Target-2 X - y: - name: Target-2 Y - speed: - name: Target-2 Speed - angle: - name: Target-2 Angle - distance: - name: Target-2 Distance - resolution: - name: Target-2 Resolution - target_3: - x: - name: Target-3 X - y: - name: Target-3 Y - speed: - name: Target-3 Speed - angle: - name: Target-3 Angle - distance: - name: Target-3 Distance - resolution: - name: Target-3 Resolution - zone_1: - target_count: - name: Zone-1 All Target Count - still_target_count: - name: Zone-1 Still Target Count - moving_target_count: - name: Zone-1 Moving Target Count - zone_2: - target_count: - name: Zone-2 All Target Count - still_target_count: - name: Zone-2 Still Target Count - moving_target_count: - name: Zone-2 Moving Target Count - zone_3: - target_count: - name: Zone-3 All Target Count - still_target_count: - name: Zone-3 Still Target Count - moving_target_count: - name: Zone-3 Moving Target Count - -binary_sensor: - - platform: ld2450 - ld2450_id: ld2450_radar - has_target: - name: Presence - has_moving_target: - name: Moving Target - has_still_target: - name: Still Target - -switch: - - platform: ld2450 - ld2450_id: ld2450_radar - bluetooth: - name: "Bluetooth" - multi_target: - name: "Multi Target Tracking" - -text_sensor: - - platform: ld2450 - ld2450_id: ld2450_radar - version: - name: "LD2450 Firmware" - mac_address: - name: "LD2450 BT MAC" - target_1: - direction: - name: "Target-1 Direction" - target_2: - direction: - name: "Target-2 Direction" - target_3: - direction: - name: "Target-3 Direction" - -number: - - platform: ld2450 - ld2450_id: ld2450_radar - presence_timeout: - name: "Timeout" - zone_1: - x1: - name: Zone-1 X1 - y1: - name: Zone-1 Y1 - x2: - name: Zone-1 X2 - y2: - name: Zone-1 Y2 - zone_2: - x1: - name: Zone-2 X1 - y1: - name: Zone-2 Y1 - x2: - name: Zone-2 X2 - y2: - name: Zone-2 Y2 - zone_3: - x1: - name: Zone-3 X1 - y1: - name: Zone-3 Y1 - x2: - name: Zone-3 X2 - y2: - name: Zone-3 Y2 - -select: - - platform: ld2450 - ld2450_id: ld2450_radar - baud_rate: - name: "Baud rate" - zone_type: - name: "Zone Type" From 76c9f5530e7f41f5a82f98a31acaf01f53e086ec Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 2 May 2024 16:04:36 +1200 Subject: [PATCH 18/18] Remove custom api code --- esphome/components/ld2450/ld2450.cpp | 32 +++++----------------------- esphome/components/ld2450/ld2450.h | 22 ++++++------------- 2 files changed, 12 insertions(+), 42 deletions(-) diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index 97f6c75e43..b3e6c2be6f 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -28,27 +28,6 @@ void LD2450Component::setup() { ESP_LOGCONFIG(TAG, "Mac Address: %s", const_cast(this->mac_.c_str())); ESP_LOGCONFIG(TAG, "Firmware Version: %s", const_cast(this->version_.c_str())); ESP_LOGCONFIG(TAG, "HLK-LD2450 setup complete"); -#ifdef USE_API - ESP_LOGCONFIG(TAG, "Registering services"); - CustomAPIDevice::register_service(&::esphome::ld2450::LD2450Component::on_set_radar_zone_, "set_radar_zone", - { - "zone_type", - "zone1_x1", - "zone1_y1", - "zone1_x2", - "zone1_y2", - "zone2_x1", - "zone2_y1", - "zone2_x2", - "zone2_y2", - "zone3_x1", - "zone3_y1", - "zone3_x2", - "zone3_y2", - }); - CustomAPIDevice::register_service(&::esphome::ld2450::LD2450Component::on_reset_radar_zone_, "reset_radar_zone"); - ESP_LOGCONFIG(TAG, "Services registration complete"); -#endif } void LD2450Component::dump_config() { @@ -154,7 +133,7 @@ uint8_t LD2450Component::count_targets_in_zone_(const Zone &zone, bool is_moving } // Service reset_radar_zone -void LD2450Component::on_reset_radar_zone_() { +void LD2450Component::reset_radar_zone() { this->zone_type_ = 0; for (auto &i : zone_config_) { i.x1 = 0; @@ -165,11 +144,10 @@ void LD2450Component::on_reset_radar_zone_() { this->send_set_zone_command_(); } -// Service set_radar_zone -void LD2450Component::on_set_radar_zone_(int32_t zone_type, int32_t zone1_x1, int32_t zone1_y1, int32_t zone1_x2, - int32_t zone1_y2, int32_t zone2_x1, int32_t zone2_y1, int32_t zone2_x2, - int32_t zone2_y2, int32_t zone3_x1, int32_t zone3_y1, int32_t zone3_x2, - int32_t zone3_y2) { +void LD2450Component::set_radar_zone(int32_t zone_type, int32_t zone1_x1, int32_t zone1_y1, int32_t zone1_x2, + int32_t zone1_y2, int32_t zone2_x1, int32_t zone2_y1, int32_t zone2_x2, + int32_t zone2_y2, int32_t zone3_x1, int32_t zone3_y1, int32_t zone3_x2, + int32_t zone3_y2) { this->zone_type_ = zone_type; int zone_parameters[12] = {zone1_x1, zone1_y1, zone1_x2, zone1_y2, zone2_x1, zone2_y1, zone2_x2, zone2_y2, zone3_x1, zone3_y1, zone3_x2, zone3_y2}; diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index f4e0c1feeb..2dc4abb5a7 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -1,13 +1,13 @@ #pragma once +#include #include #include -#include +#include "esphome/components/uart/uart.h" +#include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" -#include "esphome/core/component.h" #include "esphome/core/preferences.h" -#include "esphome/components/uart/uart.h" #ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" #endif @@ -23,9 +23,6 @@ #ifdef USE_SELECT #include "esphome/components/select/select.h" #endif -#ifdef USE_API -#include "esphome/components/api/custom_api_device.h" -#endif #ifdef USE_TEXT_SENSOR #include "esphome/components/text_sensor/text_sensor.h" #endif @@ -117,12 +114,7 @@ enum PeriodicDataStructure : uint8_t { enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 }; enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 }; -// Use CustomAPIDevice if API is defined in YAML - used for service calls to set/reset zone -#ifdef USE_API -class LD2450Component : public Component, public uart::UARTDevice, public esphome::api::CustomAPIDevice { -#else class LD2450Component : public Component, public uart::UARTDevice { -#endif #ifdef USE_SENSOR SUB_SENSOR(target_count) SUB_SENSOR(still_target_count) @@ -191,6 +183,10 @@ class LD2450Component : public Component, public uart::UARTDevice { void set_zone_still_target_count_sensor(int zone, sensor::Sensor *s); void set_zone_moving_target_count_sensor(int zone, sensor::Sensor *s); #endif + void reset_radar_zone(); + void set_radar_zone(int32_t zone_type, int32_t zone1_x1, int32_t zone1_y1, int32_t zone1_x2, int32_t zone1_y2, + int32_t zone2_x1, int32_t zone2_y1, int32_t zone2_x2, int32_t zone2_y2, int32_t zone3_x1, + int32_t zone3_y1, int32_t zone3_x2, int32_t zone3_y2); protected: ESPPreferenceObject pref_; @@ -207,14 +203,10 @@ class LD2450Component : public Component, public uart::UARTDevice { void restart_(); void send_set_zone_command_(); void convert_int_values_to_hex_(const int *values, uint8_t *bytes); - void on_reset_radar_zone_(); void save_to_flash_(float value); float restore_from_flash_(); Target target_info_[MAX_TARGETS]; Zone zone_config_[MAX_ZONES]; - void on_set_radar_zone_(int32_t zone_type, int32_t zone1_x1, int32_t zone1_y1, int32_t zone1_x2, int32_t zone1_y2, - int32_t zone2_x1, int32_t zone2_y1, int32_t zone2_x2, int32_t zone2_y2, int32_t zone3_x1, - int32_t zone3_y1, int32_t zone3_x2, int32_t zone3_y2); int16_t decode_coordinate_(uint8_t low_byte, uint8_t high_byte) { int16_t coordinate = (high_byte & 0x7F) << 8 | low_byte; if ((high_byte & 0x80) == 0)