mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[ld2450] Add new component (#5674)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Marcus Better <marcus@better.se> Co-authored-by: Trevor Schirmer <24777085+TrevorSchirmer@users.noreply.github.com> Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
		| @@ -234,6 +234,7 @@ esphome/components/kuntze/* @ssieb | |||||||
| esphome/components/lcd_menu/* @numo68 | esphome/components/lcd_menu/* @numo68 | ||||||
| esphome/components/ld2410/* @regevbr @sebcaps | esphome/components/ld2410/* @regevbr @sebcaps | ||||||
| esphome/components/ld2420/* @descipher | esphome/components/ld2420/* @descipher | ||||||
|  | esphome/components/ld2450/* @hareeshmu | ||||||
| esphome/components/ledc/* @OttoWinter | esphome/components/ledc/* @OttoWinter | ||||||
| esphome/components/libretiny/* @kuba2k2 | esphome/components/libretiny/* @kuba2k2 | ||||||
| esphome/components/libretiny_pwm/* @kuba2k2 | esphome/components/libretiny_pwm/* @kuba2k2 | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								esphome/components/ld2450/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								esphome/components/ld2450/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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])) | ||||||
							
								
								
									
										47
									
								
								esphome/components/ld2450/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								esphome/components/ld2450/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import binary_sensor | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_HAS_MOVING_TARGET, | ||||||
|  |     CONF_HAS_STILL_TARGET, | ||||||
|  |     CONF_HAS_TARGET, | ||||||
|  |     DEVICE_CLASS_MOTION, | ||||||
|  |     DEVICE_CLASS_OCCUPANCY, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from . import CONF_LD2450_ID, LD2450Component | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["ld2450"] | ||||||
|  |  | ||||||
|  | ICON_MEDITATION = "mdi:meditation" | ||||||
|  | ICON_SHIELD_ACCOUNT = "mdi:shield-account" | ||||||
|  | ICON_TARGET_ACCOUNT = "mdi:target-account" | ||||||
|  |  | ||||||
|  | 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=ICON_SHIELD_ACCOUNT, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_HAS_MOVING_TARGET): binary_sensor.binary_sensor_schema( | ||||||
|  |         device_class=DEVICE_CLASS_MOTION, | ||||||
|  |         icon=ICON_TARGET_ACCOUNT, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_HAS_STILL_TARGET): binary_sensor.binary_sensor_schema( | ||||||
|  |         device_class=DEVICE_CLASS_OCCUPANCY, | ||||||
|  |         icon=ICON_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)) | ||||||
							
								
								
									
										45
									
								
								esphome/components/ld2450/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								esphome/components/ld2450/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import button | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_FACTORY_RESET, | ||||||
|  |     CONF_RESTART, | ||||||
|  |     DEVICE_CLASS_RESTART, | ||||||
|  |     ENTITY_CATEGORY_CONFIG, | ||||||
|  |     ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |     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) | ||||||
|  |  | ||||||
|  | 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)) | ||||||
							
								
								
									
										9
									
								
								esphome/components/ld2450/button/reset_button.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								esphome/components/ld2450/button/reset_button.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | #include "reset_button.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2450 { | ||||||
|  |  | ||||||
|  | void ResetButton::press_action() { this->parent_->factory_reset(); } | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2450/button/reset_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2450/button/reset_button.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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<LD2450Component> { | ||||||
|  |  public: | ||||||
|  |   ResetButton() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void press_action() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										9
									
								
								esphome/components/ld2450/button/restart_button.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								esphome/components/ld2450/button/restart_button.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2450/button/restart_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2450/button/restart_button.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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<LD2450Component> { | ||||||
|  |  public: | ||||||
|  |   RestartButton() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void press_action() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										867
									
								
								esphome/components/ld2450/ld2450.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										867
									
								
								esphome/components/ld2450/ld2450.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,867 @@ | |||||||
|  | #include "ld2450.h" | ||||||
|  | #include <utility> | ||||||
|  | #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"; | ||||||
|  | static const char *const UNKNOWN_MAC("unknown"); | ||||||
|  |  | ||||||
|  | // 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; | ||||||
|  | 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_MODE = 0x0080; | ||||||
|  | static const uint8_t CMD_MULTI_TARGET_MODE = 0x0090; | ||||||
|  | static const uint8_t CMD_QUERY_TARGET_MODE = 0x0091; | ||||||
|  | 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; | ||||||
|  |  | ||||||
|  | static inline uint16_t convert_seconds_to_ms(uint16_t value) { return value * 1000; }; | ||||||
|  |  | ||||||
|  | static inline std::string convert_signed_int_to_hex(int value) { | ||||||
|  |   auto value_as_str = str_snprintf("%04x", 4, value & 0xFFFF); | ||||||
|  |   return value_as_str; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline void 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 | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline 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 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline int16_t decode_speed(uint8_t low_byte, uint8_t high_byte) { | ||||||
|  |   int16_t speed = (high_byte & 0x7F) << 8 | low_byte; | ||||||
|  |   if ((high_byte & 0x80) == 0) { | ||||||
|  |     speed = -speed; | ||||||
|  |   } | ||||||
|  |   return speed * 10;  // mm/s | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline 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<int16_t>(hex_val); | ||||||
|  |   if (dec_val & 0x8000) { | ||||||
|  |     dec_val -= 65536; | ||||||
|  |   } | ||||||
|  |   return dec_val; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline 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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline std::string get_direction(int16_t speed) { | ||||||
|  |   static const char *const APPROACHING = "Approaching"; | ||||||
|  |   static const char *const MOVING_AWAY = "Moving away"; | ||||||
|  |   static const char *const STATIONARY = "Stationary"; | ||||||
|  |  | ||||||
|  |   if (speed > 0) { | ||||||
|  |     return MOVING_AWAY; | ||||||
|  |   } | ||||||
|  |   if (speed < 0) { | ||||||
|  |     return APPROACHING; | ||||||
|  |   } | ||||||
|  |   return STATIONARY; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline std::string format_mac(uint8_t *buffer) { | ||||||
|  |   return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], | ||||||
|  |                       buffer[15]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline std::string format_version(uint8_t *buffer) { | ||||||
|  |   return str_sprintf("%u.%02X.%02X%02X%02X%02X", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15], | ||||||
|  |                      buffer[14]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | LD2450Component::LD2450Component() {} | ||||||
|  |  | ||||||
|  | void LD2450Component::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up HLK-LD2450..."); | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |   this->pref_ = global_preferences->make_preference<float>(this->presence_timeout_number_->get_object_id_hash()); | ||||||
|  |   this->set_presence_timeout(); | ||||||
|  | #endif | ||||||
|  |   this->read_all_info(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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); | ||||||
|  |   } | ||||||
|  |   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_); | ||||||
|  |   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 | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Throttle : %ums", this->throttle_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  MAC Address : %s", const_cast<char *>(this->mac_.c_str())); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Firmware version : %s", const_cast<char *>(this->version_.c_str())); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2450Component::loop() { | ||||||
|  |   while (this->available()) { | ||||||
|  |     this->readline_(read(), this->buffer_data_, MAX_LINE_LENGTH); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Count targets in 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 && | ||||||
|  |         index.is_moving == is_moving) { | ||||||
|  |       count++; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return count; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Service reset_radar_zone | ||||||
|  | void LD2450Component::reset_radar_zone() { | ||||||
|  |   this->zone_type_ = 0; | ||||||
|  |   for (auto &i : this->zone_config_) { | ||||||
|  |     i.x1 = 0; | ||||||
|  |     i.y1 = 0; | ||||||
|  |     i.x2 = 0; | ||||||
|  |     i.y2 = 0; | ||||||
|  |   } | ||||||
|  |   this->send_set_zone_command_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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}; | ||||||
|  |   for (int i = 0; i < MAX_ZONES; i++) { | ||||||
|  |     this->zone_config_[i].x1 = zone_parameters[i * 4]; | ||||||
|  |     this->zone_config_[i].y1 = zone_parameters[i * 4 + 1]; | ||||||
|  |     this->zone_config_[i].x2 = zone_parameters[i * 4 + 2]; | ||||||
|  |     this->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<uint8_t>(this->zone_type_), 0x00}; | ||||||
|  |   uint8_t area_config[24] = {}; | ||||||
|  |   for (int i = 0; i < MAX_ZONES; i++) { | ||||||
|  |     int values[4] = {this->zone_config_[i].x1, this->zone_config_[i].y1, this->zone_config_[i].x2, | ||||||
|  |                      this->zone_config_[i].y2}; | ||||||
|  |     ld2450::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); | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   this->send_command_(CMD_SET_ZONE, cmd_value, 26); | ||||||
|  |   this->set_config_mode_(false); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Check presense timeout to reset presence status | ||||||
|  | bool LD2450Component::get_timeout_status_(uint32_t check_millis) { | ||||||
|  |   if (check_millis == 0) { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   if (this->timeout_ == 0) { | ||||||
|  |     this->timeout_ = ld2450::convert_seconds_to_ms(DEFAULT_PRESENCE_TIMEOUT); | ||||||
|  |   } | ||||||
|  |   auto current_millis = millis(); | ||||||
|  |   return current_millis - check_millis >= this->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; | ||||||
|  |     this->zone_config_[index].x1 = ld2450::hex_to_signed_int(buffer, start); | ||||||
|  |     this->zone_config_[index].y1 = ld2450::hex_to_signed_int(buffer, start + 2); | ||||||
|  |     this->zone_config_[index].x2 = ld2450::hex_to_signed_int(buffer, start + 4); | ||||||
|  |     this->zone_config_[index].y2 = ld2450::hex_to_signed_int(buffer, start + 6); | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |     this->zone_x1_numbers_[index]->publish_state(this->zone_config_[index].x1); | ||||||
|  |     this->zone_y1_numbers_[index]->publish_state(this->zone_config_[index].y1); | ||||||
|  |     this->zone_x2_numbers_[index]->publish_state(this->zone_config_[index].x2); | ||||||
|  |     this->zone_y2_numbers_[index]->publish_state(this->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_target_tracking_mode_(); | ||||||
|  |   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, uint8_t command_value_len) { | ||||||
|  |   ESP_LOGV(TAG, "Sending command %02X", command); | ||||||
|  |   // frame header | ||||||
|  |   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]); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   // footer | ||||||
|  |   this->write_array(CMD_FRAME_END, 4); | ||||||
|  |   // FIXME to remove | ||||||
|  |   delay(50);  // NOLINT | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LD2450 Radar data message: | ||||||
|  | //  [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, uint8_t len) { | ||||||
|  |   if (len < 29) {  // header (4 bytes) + 8 x 3 target data + footer (2 bytes) | ||||||
|  |     ESP_LOGE(TAG, "Periodic data: invalid message length"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   if (buffer[0] != 0xAA || buffer[1] != 0xFF || buffer[2] != 0x03 || buffer[3] != 0x00) {  // header | ||||||
|  |     ESP_LOGE(TAG, "Periodic data: invalid message header"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   if (buffer[len - 2] != 0x55 || buffer[len - 1] != 0xCC) {  // footer | ||||||
|  |     ESP_LOGE(TAG, "Periodic data: invalid message footer"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   auto current_millis = millis(); | ||||||
|  |   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 = 0; | ||||||
|  |   int16_t val = 0; | ||||||
|  |   uint8_t index = 0; | ||||||
|  |   int16_t tx = 0; | ||||||
|  |   int16_t ty = 0; | ||||||
|  |   int16_t td = 0; | ||||||
|  |   int16_t ts = 0; | ||||||
|  |   int16_t angle = 0; | ||||||
|  |   std::string direction{}; | ||||||
|  |   bool is_moving = false; | ||||||
|  |  | ||||||
|  | #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 = ld2450::decode_coordinate(buffer[start], buffer[start + 1]); | ||||||
|  |       tx = val; | ||||||
|  |       sx->publish_state(val); | ||||||
|  |     } | ||||||
|  |     // Y | ||||||
|  |     start = TARGET_Y + index * 8; | ||||||
|  |     sensor::Sensor *sy = this->move_y_sensors_[index]; | ||||||
|  |     if (sy != nullptr) { | ||||||
|  |       val = ld2450::decode_coordinate(buffer[start], buffer[start + 1]); | ||||||
|  |       ty = val; | ||||||
|  |       sy->publish_state(val); | ||||||
|  |     } | ||||||
|  |     // SPEED | ||||||
|  |     start = TARGET_SPEED + index * 8; | ||||||
|  |     sensor::Sensor *ss = this->move_speed_sensors_[index]; | ||||||
|  |     if (ss != nullptr) { | ||||||
|  |       val = ld2450::decode_speed(buffer[start], buffer[start + 1]); | ||||||
|  |       ts = val; | ||||||
|  |       if (val) { | ||||||
|  |         is_moving = true; | ||||||
|  |         moving_target_count++; | ||||||
|  |       } | ||||||
|  |       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]; | ||||||
|  |       sr->publish_state(val); | ||||||
|  |     } | ||||||
|  |     // DISTANCE | ||||||
|  |     sensor::Sensor *sd = this->move_distance_sensors_[index]; | ||||||
|  |     if (sd != nullptr) { | ||||||
|  |       val = (uint16_t) sqrt( | ||||||
|  |           pow(ld2450::decode_coordinate(buffer[TARGET_X + index * 8], buffer[(TARGET_X + index * 8) + 1]), 2) + | ||||||
|  |           pow(ld2450::decode_coordinate(buffer[TARGET_Y + index * 8], buffer[(TARGET_Y + index * 8) + 1]), 2)); | ||||||
|  |       td = val; | ||||||
|  |       if (val > 0) { | ||||||
|  |         target_count++; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       sd->publish_state(val); | ||||||
|  |     } | ||||||
|  |     // ANGLE | ||||||
|  |     angle = calculate_angle(static_cast<float>(ty), static_cast<float>(td)); | ||||||
|  |     if (tx > 0) { | ||||||
|  |       angle = angle * -1; | ||||||
|  |     } | ||||||
|  |     sensor::Sensor *sa = this->move_angle_sensors_[index]; | ||||||
|  |     if (sa != nullptr) { | ||||||
|  |       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) { | ||||||
|  |       tsd->publish_state(direction); | ||||||
|  |     } | ||||||
|  | #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; | ||||||
|  |  | ||||||
|  |   }  // 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 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); | ||||||
|  |       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); | ||||||
|  |       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) { | ||||||
|  |       sztc->publish_state(zone_all_targets); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   }  // End loop thru zones | ||||||
|  |  | ||||||
|  |   still_target_count = target_count - moving_target_count; | ||||||
|  |   // Target Count | ||||||
|  |   if (this->target_count_sensor_ != nullptr) { | ||||||
|  |     this->target_count_sensor_->publish_state(target_count); | ||||||
|  |   } | ||||||
|  |   // Still Target Count | ||||||
|  |   if (this->still_target_count_sensor_ != nullptr) { | ||||||
|  |     this->still_target_count_sensor_->publish_state(still_target_count); | ||||||
|  |   } | ||||||
|  |   // Moving Target Count | ||||||
|  |   if (this->moving_target_count_sensor_ != nullptr) { | ||||||
|  |     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 | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  |   // 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(); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | ||||||
|  |   ESP_LOGV(TAG, "Handling ack data for command %02X", buffer[COMMAND]); | ||||||
|  |   if (len < 10) { | ||||||
|  |     ESP_LOGE(TAG, "Ack data: invalid length"); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) {  // frame header | ||||||
|  |     ESP_LOGE(TAG, "Ack data: invalid header (command %02X)", buffer[COMMAND]); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   if (buffer[COMMAND_STATUS] != 0x01) { | ||||||
|  |     ESP_LOGE(TAG, "Ack data: invalid status"); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   if (buffer[8] || buffer[9]) { | ||||||
|  |     ESP_LOGE(TAG, "Ack data: last buffer was %u, %u", buffer[8], buffer[9]); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   switch (buffer[COMMAND]) { | ||||||
|  |     case lowbyte(CMD_ENABLE_CONF): | ||||||
|  |       ESP_LOGV(TAG, "Got enable conf command"); | ||||||
|  |       break; | ||||||
|  |     case lowbyte(CMD_DISABLE_CONF): | ||||||
|  |       ESP_LOGV(TAG, "Got disable conf command"); | ||||||
|  |       break; | ||||||
|  |     case lowbyte(CMD_SET_BAUD_RATE): | ||||||
|  |       ESP_LOGV(TAG, "Got baud rate change command"); | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |       if (this->baud_rate_select_ != nullptr) { | ||||||
|  |         ESP_LOGV(TAG, "Change baud rate to %s", this->baud_rate_select_->state.c_str()); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       break; | ||||||
|  |     case lowbyte(CMD_VERSION): | ||||||
|  |       this->version_ = ld2450::format_version(buffer); | ||||||
|  |       ESP_LOGV(TAG, "Firmware version: %s", 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_ = ld2450::format_mac(buffer); | ||||||
|  |       ESP_LOGV(TAG, "MAC address: %s", 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, "Got Bluetooth command"); | ||||||
|  |       break; | ||||||
|  |     case lowbyte(CMD_SINGLE_TARGET_MODE): | ||||||
|  |       ESP_LOGV(TAG, "Got 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_MODE): | ||||||
|  |       ESP_LOGV(TAG, "Got 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_TARGET_MODE): | ||||||
|  |       ESP_LOGV(TAG, "Got query target tracking mode command"); | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |       if (this->multi_target_switch_ != nullptr) { | ||||||
|  |         this->multi_target_switch_->publish_state(buffer[10] == 0x02); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       break; | ||||||
|  |     case lowbyte(CMD_QUERY_ZONE): | ||||||
|  |       ESP_LOGV(TAG, "Got 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 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, "Got 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, uint8_t len) { | ||||||
|  |   if (readch < 0) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   if (this->buffer_pos_ < len - 1) { | ||||||
|  |     buffer[this->buffer_pos_++] = readch; | ||||||
|  |     buffer[this->buffer_pos_] = 0; | ||||||
|  |   } else { | ||||||
|  |     this->buffer_pos_ = 0; | ||||||
|  |   } | ||||||
|  |   if (this->buffer_pos_ < 4) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   if (buffer[this->buffer_pos_ - 2] == 0x55 && buffer[this->buffer_pos_ - 1] == 0xCC) { | ||||||
|  |     ESP_LOGV(TAG, "Handle periodic radar data"); | ||||||
|  |     this->handle_periodic_data_(buffer, this->buffer_pos_); | ||||||
|  |     this->buffer_pos_ = 0;  // Reset position index for next frame | ||||||
|  |   } else if (buffer[this->buffer_pos_ - 4] == 0x04 && buffer[this->buffer_pos_ - 3] == 0x03 && | ||||||
|  |              buffer[this->buffer_pos_ - 2] == 0x02 && buffer[this->buffer_pos_ - 1] == 0x01) { | ||||||
|  |     ESP_LOGV(TAG, "Handle command ack data"); | ||||||
|  |     if (this->handle_ack_data_(buffer, this->buffer_pos_)) { | ||||||
|  |       this->buffer_pos_ = 0;  // Reset position index for next frame | ||||||
|  |     } else { | ||||||
|  |       ESP_LOGV(TAG, "Command ack data invalid"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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() { | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |   std::string zone_type = ZONE_TYPE_INT_TO_ENUM.at(static_cast<ZoneTypeStructure>(this->zone_type_)); | ||||||
|  |   if (this->zone_type_select_ != nullptr) { | ||||||
|  |     this->zone_type_select_->publish_state(zone_type); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set Single/Multiplayer target detection | ||||||
|  | void LD2450Component::set_multi_target(bool enable) { | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   uint8_t cmd = enable ? CMD_MULTI_TARGET_MODE : CMD_SINGLE_TARGET_MODE; | ||||||
|  |   this->send_command_(cmd, nullptr, 0); | ||||||
|  |   this->set_config_mode_(false); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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 for target tracking mode | ||||||
|  | void LD2450Component::query_target_tracking_mode_() { this->send_command_(CMD_QUERY_TARGET_MODE, nullptr, 0); } | ||||||
|  |  | ||||||
|  | // Query for zone info | ||||||
|  | void LD2450Component::query_zone_() { this->send_command_(CMD_QUERY_ZONE, nullptr, 0); } | ||||||
|  |  | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  | void LD2450Component::set_move_x_sensor(uint8_t target, sensor::Sensor *s) { this->move_x_sensors_[target] = s; } | ||||||
|  | void LD2450Component::set_move_y_sensor(uint8_t target, sensor::Sensor *s) { this->move_y_sensors_[target] = s; } | ||||||
|  | void LD2450Component::set_move_speed_sensor(uint8_t target, sensor::Sensor *s) { | ||||||
|  |   this->move_speed_sensors_[target] = s; | ||||||
|  | } | ||||||
|  | void LD2450Component::set_move_angle_sensor(uint8_t target, sensor::Sensor *s) { | ||||||
|  |   this->move_angle_sensors_[target] = s; | ||||||
|  | } | ||||||
|  | void LD2450Component::set_move_distance_sensor(uint8_t target, sensor::Sensor *s) { | ||||||
|  |   this->move_distance_sensors_[target] = s; | ||||||
|  | } | ||||||
|  | void LD2450Component::set_move_resolution_sensor(uint8_t target, sensor::Sensor *s) { | ||||||
|  |   this->move_resolution_sensors_[target] = s; | ||||||
|  | } | ||||||
|  | void LD2450Component::set_zone_target_count_sensor(uint8_t zone, sensor::Sensor *s) { | ||||||
|  |   this->zone_target_count_sensors_[zone] = s; | ||||||
|  | } | ||||||
|  | void LD2450Component::set_zone_still_target_count_sensor(uint8_t zone, sensor::Sensor *s) { | ||||||
|  |   this->zone_still_target_count_sensors_[zone] = s; | ||||||
|  | } | ||||||
|  | void LD2450Component::set_zone_moving_target_count_sensor(uint8_t zone, sensor::Sensor *s) { | ||||||
|  |   this->zone_moving_target_count_sensors_[zone] = s; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | #ifdef USE_TEXT_SENSOR | ||||||
|  | void LD2450Component::set_direction_text_sensor(uint8_t 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; | ||||||
|  |   } | ||||||
|  |   this->zone_config_[zone].x1 = static_cast<int>(x1sens->state); | ||||||
|  |   this->zone_config_[zone].y1 = static_cast<int>(y1sens->state); | ||||||
|  |   this->zone_config_[zone].x2 = static_cast<int>(x2sens->state); | ||||||
|  |   this->zone_config_[zone].y2 = static_cast<int>(y2sens->state); | ||||||
|  |   this->send_set_zone_command_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2450Component::set_zone_x1_number(uint8_t zone, number::Number *n) { this->zone_x1_numbers_[zone] = n; } | ||||||
|  | void LD2450Component::set_zone_y1_number(uint8_t zone, number::Number *n) { this->zone_y1_numbers_[zone] = n; } | ||||||
|  | void LD2450Component::set_zone_x2_number(uint8_t zone, number::Number *n) { this->zone_x2_numbers_[zone] = n; } | ||||||
|  | void LD2450Component::set_zone_y2_number(uint8_t 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_ = ld2450::convert_seconds_to_ms(timeout); | ||||||
|  |     } | ||||||
|  |     if (this->presence_timeout_number_->has_state()) { | ||||||
|  |       this->save_to_flash_(this->presence_timeout_number_->state); | ||||||
|  |       this->timeout_ = ld2450::convert_seconds_to_ms(this->presence_timeout_number_->state); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										231
									
								
								esphome/components/ld2450/ld2450.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								esphome/components/ld2450/ld2450.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,231 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <iomanip> | ||||||
|  | #include <map> | ||||||
|  | #include "esphome/components/uart/uart.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/defines.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/preferences.h" | ||||||
|  | #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 | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  | #include "esphome/components/binary_sensor/binary_sensor.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifndef M_PI | ||||||
|  | #define M_PI 3.14 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2450 { | ||||||
|  |  | ||||||
|  | // Constants | ||||||
|  | static const uint8_t DEFAULT_PRESENCE_TIMEOUT = 5;  // Timeout to reset presense status 5 sec. | ||||||
|  | static const uint8_t MAX_LINE_LENGTH = 60;          // Max characters for serial buffer | ||||||
|  | static const uint8_t MAX_TARGETS = 3;               // Max 3 Targets in LD2450 | ||||||
|  | static const uint8_t MAX_ZONES = 3;                 // Max 3 Zones in LD2450 | ||||||
|  |  | ||||||
|  | // Target coordinate struct | ||||||
|  | struct Target { | ||||||
|  |   int16_t x; | ||||||
|  |   int16_t y; | ||||||
|  |   bool is_moving; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Zone coordinate struct | ||||||
|  | struct Zone { | ||||||
|  |   int16_t x1 = 0; | ||||||
|  |   int16_t y1 = 0; | ||||||
|  |   int16_t x2 = 0; | ||||||
|  |   int16_t y2 = 0; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Convert baud rate enum to int | ||||||
|  | static const std::map<std::string, uint8_t> 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<ZoneTypeStructure, std::string> ZONE_TYPE_INT_TO_ENUM{ | ||||||
|  |     {ZONE_DISABLED, "Disabled"}, {ZONE_DETECTION, "Detection"}, {ZONE_FILTER, "Filter"}}; | ||||||
|  |  | ||||||
|  | // Convert zone type enum to int | ||||||
|  | static const std::map<std::string, uint8_t> ZONE_TYPE_ENUM_TO_INT{ | ||||||
|  |     {"Disabled", ZONE_DISABLED}, {"Detection", ZONE_DETECTION}, {"Filter", ZONE_FILTER}}; | ||||||
|  |  | ||||||
|  | // LD2450 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}; | ||||||
|  |  | ||||||
|  | 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 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(); | ||||||
|  | #ifdef USE_TEXT_SENSOR | ||||||
|  |   void set_direction_text_sensor(uint8_t target, text_sensor::TextSensor *s); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |   void set_zone_coordinate(uint8_t zone); | ||||||
|  |   void set_zone_x1_number(uint8_t zone, number::Number *n); | ||||||
|  |   void set_zone_y1_number(uint8_t zone, number::Number *n); | ||||||
|  |   void set_zone_x2_number(uint8_t zone, number::Number *n); | ||||||
|  |   void set_zone_y2_number(uint8_t zone, number::Number *n); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  |   void set_move_x_sensor(uint8_t target, sensor::Sensor *s); | ||||||
|  |   void set_move_y_sensor(uint8_t target, sensor::Sensor *s); | ||||||
|  |   void set_move_speed_sensor(uint8_t target, sensor::Sensor *s); | ||||||
|  |   void set_move_angle_sensor(uint8_t target, sensor::Sensor *s); | ||||||
|  |   void set_move_distance_sensor(uint8_t target, sensor::Sensor *s); | ||||||
|  |   void set_move_resolution_sensor(uint8_t target, sensor::Sensor *s); | ||||||
|  |   void set_zone_target_count_sensor(uint8_t zone, sensor::Sensor *s); | ||||||
|  |   void set_zone_still_target_count_sensor(uint8_t zone, sensor::Sensor *s); | ||||||
|  |   void set_zone_moving_target_count_sensor(uint8_t 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: | ||||||
|  |   void send_command_(uint8_t command_str, const uint8_t *command_value, uint8_t command_value_len); | ||||||
|  |   void set_config_mode_(bool enable); | ||||||
|  |   void handle_periodic_data_(uint8_t *buffer, uint8_t len); | ||||||
|  |   bool handle_ack_data_(uint8_t *buffer, uint8_t len); | ||||||
|  |   void process_zone_(uint8_t *buffer); | ||||||
|  |   void readline_(int readch, uint8_t *buffer, uint8_t len); | ||||||
|  |   void get_version_(); | ||||||
|  |   void get_mac_(); | ||||||
|  |   void query_target_tracking_mode_(); | ||||||
|  |   void query_zone_(); | ||||||
|  |   void restart_(); | ||||||
|  |   void send_set_zone_command_(); | ||||||
|  |   void save_to_flash_(float value); | ||||||
|  |   float restore_from_flash_(); | ||||||
|  |   bool get_timeout_status_(uint32_t check_millis); | ||||||
|  |   uint8_t count_targets_in_zone_(const Zone &zone, bool is_moving); | ||||||
|  |  | ||||||
|  |   Target target_info_[MAX_TARGETS]; | ||||||
|  |   Zone zone_config_[MAX_ZONES]; | ||||||
|  |   uint8_t buffer_pos_ = 0;  // where to resume processing/populating buffer | ||||||
|  |   uint8_t buffer_data_[MAX_LINE_LENGTH]; | ||||||
|  |   uint32_t last_periodic_millis_ = 0; | ||||||
|  |   uint32_t presence_millis_ = 0; | ||||||
|  |   uint32_t still_presence_millis_ = 0; | ||||||
|  |   uint32_t moving_presence_millis_ = 0; | ||||||
|  |   uint16_t throttle_ = 0; | ||||||
|  |   uint16_t timeout_ = 5; | ||||||
|  |   uint8_t zone_type_ = 0; | ||||||
|  |   std::string version_{}; | ||||||
|  |   std::string mac_{}; | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |   ESPPreferenceObject pref_;  // only used when numbers are in use | ||||||
|  |   std::vector<number::Number *> zone_x1_numbers_ = std::vector<number::Number *>(MAX_ZONES); | ||||||
|  |   std::vector<number::Number *> zone_y1_numbers_ = std::vector<number::Number *>(MAX_ZONES); | ||||||
|  |   std::vector<number::Number *> zone_x2_numbers_ = std::vector<number::Number *>(MAX_ZONES); | ||||||
|  |   std::vector<number::Number *> zone_y2_numbers_ = std::vector<number::Number *>(MAX_ZONES); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  |   std::vector<sensor::Sensor *> move_x_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS); | ||||||
|  |   std::vector<sensor::Sensor *> move_y_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS); | ||||||
|  |   std::vector<sensor::Sensor *> move_speed_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS); | ||||||
|  |   std::vector<sensor::Sensor *> move_angle_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS); | ||||||
|  |   std::vector<sensor::Sensor *> move_distance_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS); | ||||||
|  |   std::vector<sensor::Sensor *> move_resolution_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS); | ||||||
|  |   std::vector<sensor::Sensor *> zone_target_count_sensors_ = std::vector<sensor::Sensor *>(MAX_ZONES); | ||||||
|  |   std::vector<sensor::Sensor *> zone_still_target_count_sensors_ = std::vector<sensor::Sensor *>(MAX_ZONES); | ||||||
|  |   std::vector<sensor::Sensor *> zone_moving_target_count_sensors_ = std::vector<sensor::Sensor *>(MAX_ZONES); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_TEXT_SENSOR | ||||||
|  |   std::vector<text_sensor::TextSensor *> direction_text_sensors_ = std::vector<text_sensor::TextSensor *>(3); | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										120
									
								
								esphome/components/ld2450/number/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								esphome/components/ld2450/number/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import number | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ID, | ||||||
|  |     DEVICE_CLASS_DISTANCE, | ||||||
|  |     ENTITY_CATEGORY_CONFIG, | ||||||
|  |     ICON_TIMELAPSE, | ||||||
|  |     UNIT_MILLIMETER, | ||||||
|  |     UNIT_SECOND, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns | ||||||
|  |  | ||||||
|  | CONF_PRESENCE_TIMEOUT = "presence_timeout" | ||||||
|  | CONF_X1 = "x1" | ||||||
|  | CONF_X2 = "x2" | ||||||
|  | CONF_Y1 = "y1" | ||||||
|  | CONF_Y2 = "y2" | ||||||
|  | ICON_ARROW_BOTTOM_RIGHT = "mdi:arrow-bottom-right" | ||||||
|  | ICON_ARROW_BOTTOM_RIGHT_BOLD_BOX_OUTLINE = "mdi:arrow-bottom-right-bold-box-outline" | ||||||
|  | ICON_ARROW_TOP_LEFT = "mdi:arrow-top-left" | ||||||
|  | ICON_ARROW_TOP_LEFT_BOLD_BOX_OUTLINE = "mdi:arrow-top-left-bold-box-outline" | ||||||
|  | MAX_ZONES = 3 | ||||||
|  |  | ||||||
|  | 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.Required(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=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=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=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=ICON_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)) | ||||||
							
								
								
									
										12
									
								
								esphome/components/ld2450/number/presence_timeout_number.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2450/number/presence_timeout_number.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2450/number/presence_timeout_number.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2450/number/presence_timeout_number.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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<LD2450Component> { | ||||||
|  |  public: | ||||||
|  |   PresenceTimeoutNumber() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void control(float value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										14
									
								
								esphome/components/ld2450/number/zone_coordinate_number.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								esphome/components/ld2450/number/zone_coordinate_number.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										19
									
								
								esphome/components/ld2450/number/zone_coordinate_number.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								esphome/components/ld2450/number/zone_coordinate_number.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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<LD2450Component> { | ||||||
|  |  public: | ||||||
|  |   ZoneCoordinateNumber(uint8_t zone); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint8_t zone_; | ||||||
|  |   void control(float value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										56
									
								
								esphome/components/ld2450/select/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								esphome/components/ld2450/select/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import select | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import CONF_BAUD_RATE, ENTITY_CATEGORY_CONFIG, 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)) | ||||||
							
								
								
									
										12
									
								
								esphome/components/ld2450/select/baud_rate_select.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2450/select/baud_rate_select.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2450/select/baud_rate_select.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2450/select/baud_rate_select.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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<LD2450Component> { | ||||||
|  |  public: | ||||||
|  |   BaudRateSelect() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void control(const std::string &value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										12
									
								
								esphome/components/ld2450/select/zone_type_select.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2450/select/zone_type_select.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2450/select/zone_type_select.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2450/select/zone_type_select.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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<LD2450Component> { | ||||||
|  |  public: | ||||||
|  |   ZoneTypeSelect() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void control(const std::string &value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										156
									
								
								esphome/components/ld2450/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								esphome/components/ld2450/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import sensor | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ANGLE, | ||||||
|  |     CONF_DISTANCE, | ||||||
|  |     CONF_RESOLUTION, | ||||||
|  |     CONF_SPEED, | ||||||
|  |     DEVICE_CLASS_DISTANCE, | ||||||
|  |     DEVICE_CLASS_SPEED, | ||||||
|  |     UNIT_DEGREES, | ||||||
|  |     UNIT_MILLIMETER, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from . import CONF_LD2450_ID, LD2450Component | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["ld2450"] | ||||||
|  |  | ||||||
|  | CONF_MOVING_TARGET_COUNT = "moving_target_count" | ||||||
|  | CONF_STILL_TARGET_COUNT = "still_target_count" | ||||||
|  | CONF_TARGET_COUNT = "target_count" | ||||||
|  | CONF_X = "x" | ||||||
|  | CONF_Y = "y" | ||||||
|  |  | ||||||
|  | ICON_ACCOUNT_GROUP = "mdi:account-group" | ||||||
|  | ICON_ACCOUNT_SWITCH = "mdi:account-switch" | ||||||
|  | ICON_ALPHA_X_BOX_OUTLINE = "mdi:alpha-x-box-outline" | ||||||
|  | ICON_ALPHA_Y_BOX_OUTLINE = "mdi:alpha-y-box-outline" | ||||||
|  | ICON_FORMAT_TEXT_ROTATION_ANGLE_UP = "mdi:format-text-rotation-angle-up" | ||||||
|  | ICON_HUMAN_GREETING_PROXIMITY = "mdi:human-greeting-proximity" | ||||||
|  | ICON_MAP_MARKER_ACCOUNT = "mdi:map-marker-account" | ||||||
|  | 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_SPEEDOMETER_SLOW = "mdi:speedometer-slow" | ||||||
|  |  | ||||||
|  | MAX_TARGETS = 3 | ||||||
|  | MAX_ZONES = 3 | ||||||
|  |  | ||||||
|  | UNIT_MILLIMETER_PER_SECOND = "mm/s" | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), | ||||||
|  |         cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema( | ||||||
|  |             icon=ICON_ACCOUNT_GROUP, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_STILL_TARGET_COUNT): sensor.sensor_schema( | ||||||
|  |             icon=ICON_HUMAN_GREETING_PROXIMITY, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_MOVING_TARGET_COUNT): sensor.sensor_schema( | ||||||
|  |             icon=ICON_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=ICON_ALPHA_X_BOX_OUTLINE, | ||||||
|  |                 ), | ||||||
|  |                 cv.Optional(CONF_Y): sensor.sensor_schema( | ||||||
|  |                     device_class=DEVICE_CLASS_DISTANCE, | ||||||
|  |                     unit_of_measurement=UNIT_MILLIMETER, | ||||||
|  |                     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=ICON_SPEEDOMETER_SLOW, | ||||||
|  |                 ), | ||||||
|  |                 cv.Optional(CONF_ANGLE): sensor.sensor_schema( | ||||||
|  |                     unit_of_measurement=UNIT_DEGREES, | ||||||
|  |                     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=ICON_MAP_MARKER_DISTANCE, | ||||||
|  |                 ), | ||||||
|  |                 cv.Optional(CONF_RESOLUTION): sensor.sensor_schema( | ||||||
|  |                     device_class=DEVICE_CLASS_DISTANCE, | ||||||
|  |                     unit_of_measurement=UNIT_MILLIMETER, | ||||||
|  |                     icon=ICON_RELATION_ZERO_OR_ONE_TO_ZERO_OR_ONE, | ||||||
|  |                 ), | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |         for n in range(MAX_TARGETS) | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         cv.Optional(f"zone_{n + 1}"): cv.Schema( | ||||||
|  |             { | ||||||
|  |                 cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema( | ||||||
|  |                     icon=ICON_MAP_MARKER_ACCOUNT, | ||||||
|  |                 ), | ||||||
|  |                 cv.Optional(CONF_STILL_TARGET_COUNT): sensor.sensor_schema( | ||||||
|  |                     icon=ICON_MAP_MARKER_ACCOUNT, | ||||||
|  |                 ), | ||||||
|  |                 cv.Optional(CONF_MOVING_TARGET_COUNT): sensor.sensor_schema( | ||||||
|  |                     icon=ICON_MAP_MARKER_ACCOUNT, | ||||||
|  |                 ), | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |         for n in range(MAX_ZONES) | ||||||
|  |     }, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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)) | ||||||
|  |     for n in range(MAX_ZONES): | ||||||
|  |         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)) | ||||||
							
								
								
									
										45
									
								
								esphome/components/ld2450/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								esphome/components/ld2450/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import switch | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     DEVICE_CLASS_SWITCH, | ||||||
|  |     ENTITY_CATEGORY_CONFIG, | ||||||
|  |     ICON_BLUETOOTH, | ||||||
|  |     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)) | ||||||
							
								
								
									
										12
									
								
								esphome/components/ld2450/switch/bluetooth_switch.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2450/switch/bluetooth_switch.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2450/switch/bluetooth_switch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2450/switch/bluetooth_switch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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<LD2450Component> { | ||||||
|  |  public: | ||||||
|  |   BluetoothSwitch() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void write_state(bool state) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										12
									
								
								esphome/components/ld2450/switch/multi_target_switch.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2450/switch/multi_target_switch.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2450/switch/multi_target_switch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2450/switch/multi_target_switch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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<LD2450Component> { | ||||||
|  |  public: | ||||||
|  |   MultiTargetSwitch() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void write_state(bool state) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2450 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										62
									
								
								esphome/components/ld2450/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								esphome/components/ld2450/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import text_sensor | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_DIRECTION, | ||||||
|  |     CONF_MAC_ADDRESS, | ||||||
|  |     CONF_VERSION, | ||||||
|  |     ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |     ENTITY_CATEGORY_NONE, | ||||||
|  |     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)) | ||||||
							
								
								
									
										168
									
								
								tests/components/ld2450/common.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								tests/components/ld2450/common.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | |||||||
|  | uart: | ||||||
|  |   - id: ld2450_uart | ||||||
|  |     tx_pin: ${tx_pin} | ||||||
|  |     rx_pin: ${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 | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2450/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2450/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO17 | ||||||
|  |   rx_pin: GPIO16 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2450/test.esp32-c3-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2450/test.esp32-c3-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO4 | ||||||
|  |   rx_pin: GPIO5 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2450/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2450/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO4 | ||||||
|  |   rx_pin: GPIO5 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2450/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2450/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO17 | ||||||
|  |   rx_pin: GPIO16 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2450/test.esp8266-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2450/test.esp8266-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO4 | ||||||
|  |   rx_pin: GPIO5 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2450/test.rp2040-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2450/test.rp2040-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO4 | ||||||
|  |   rx_pin: GPIO5 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
		Reference in New Issue
	
	Block a user