mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	[ld2412] New component (#9075)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
		| @@ -246,6 +246,7 @@ esphome/components/kuntze/* @ssieb | |||||||
| esphome/components/lc709203f/* @ilikecake | esphome/components/lc709203f/* @ilikecake | ||||||
| esphome/components/lcd_menu/* @numo68 | esphome/components/lcd_menu/* @numo68 | ||||||
| esphome/components/ld2410/* @regevbr @sebcaps | esphome/components/ld2410/* @regevbr @sebcaps | ||||||
|  | esphome/components/ld2412/* @Rihan9 | ||||||
| esphome/components/ld2420/* @descipher | esphome/components/ld2420/* @descipher | ||||||
| esphome/components/ld2450/* @hareeshmu | esphome/components/ld2450/* @hareeshmu | ||||||
| esphome/components/ld24xx/* @kbx81 | esphome/components/ld24xx/* @kbx81 | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								esphome/components/ld2412/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								esphome/components/ld2412/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import uart | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import CONF_ID, CONF_THROTTLE | ||||||
|  |  | ||||||
|  | AUTO_LOAD = ["ld24xx"] | ||||||
|  | CODEOWNERS = ["@Rihan9"] | ||||||
|  | DEPENDENCIES = ["uart"] | ||||||
|  | MULTI_CONF = True | ||||||
|  |  | ||||||
|  | LD2412_ns = cg.esphome_ns.namespace("ld2412") | ||||||
|  | LD2412Component = LD2412_ns.class_("LD2412Component", cg.Component, uart.UARTDevice) | ||||||
|  |  | ||||||
|  | CONF_LD2412_ID = "ld2412_id" | ||||||
|  |  | ||||||
|  | CONF_MAX_MOVE_DISTANCE = "max_move_distance" | ||||||
|  | CONF_MAX_STILL_DISTANCE = "max_still_distance" | ||||||
|  | CONF_MOVE_THRESHOLDS = [f"g{x}_move_threshold" for x in range(9)] | ||||||
|  | CONF_STILL_THRESHOLDS = [f"g{x}_still_threshold" for x in range(9)] | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(LD2412Component), | ||||||
|  |             cv.Optional(CONF_THROTTLE): cv.invalid( | ||||||
|  |                 f"{CONF_THROTTLE} has been removed; use per-sensor filters, instead" | ||||||
|  |             ), | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     .extend(uart.UART_DEVICE_SCHEMA) | ||||||
|  |     .extend(cv.COMPONENT_SCHEMA) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( | ||||||
|  |     "ld2412", | ||||||
|  |     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) | ||||||
							
								
								
									
										70
									
								
								esphome/components/ld2412/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								esphome/components/ld2412/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | |||||||
|  | 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, | ||||||
|  |     DEVICE_CLASS_RUNNING, | ||||||
|  |     ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |     ICON_ACCOUNT, | ||||||
|  |     ICON_MOTION_SENSOR, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from . import CONF_LD2412_ID, LD2412Component | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["ld2412"] | ||||||
|  |  | ||||||
|  | CONF_DYNAMIC_BACKGROUND_CORRECTION_STATUS = "dynamic_background_correction_status" | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = { | ||||||
|  |     cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component), | ||||||
|  |     cv.Optional( | ||||||
|  |         CONF_DYNAMIC_BACKGROUND_CORRECTION_STATUS | ||||||
|  |     ): binary_sensor.binary_sensor_schema( | ||||||
|  |         device_class=DEVICE_CLASS_RUNNING, | ||||||
|  |         entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |         icon=ICON_ACCOUNT, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema( | ||||||
|  |         device_class=DEVICE_CLASS_OCCUPANCY, | ||||||
|  |         filters=[{"settle": cv.TimePeriod(milliseconds=1000)}], | ||||||
|  |         icon=ICON_ACCOUNT, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_HAS_MOVING_TARGET): binary_sensor.binary_sensor_schema( | ||||||
|  |         device_class=DEVICE_CLASS_MOTION, | ||||||
|  |         filters=[{"settle": cv.TimePeriod(milliseconds=1000)}], | ||||||
|  |         icon=ICON_MOTION_SENSOR, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_HAS_STILL_TARGET): binary_sensor.binary_sensor_schema( | ||||||
|  |         device_class=DEVICE_CLASS_OCCUPANCY, | ||||||
|  |         filters=[{"settle": cv.TimePeriod(milliseconds=1000)}], | ||||||
|  |         icon=ICON_MOTION_SENSOR, | ||||||
|  |     ), | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     LD2412_component = await cg.get_variable(config[CONF_LD2412_ID]) | ||||||
|  |     if dynamic_background_correction_status_config := config.get( | ||||||
|  |         CONF_DYNAMIC_BACKGROUND_CORRECTION_STATUS | ||||||
|  |     ): | ||||||
|  |         sens = await binary_sensor.new_binary_sensor( | ||||||
|  |             dynamic_background_correction_status_config | ||||||
|  |         ) | ||||||
|  |         cg.add( | ||||||
|  |             LD2412_component.set_dynamic_background_correction_status_binary_sensor( | ||||||
|  |                 sens | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     if has_target_config := config.get(CONF_HAS_TARGET): | ||||||
|  |         sens = await binary_sensor.new_binary_sensor(has_target_config) | ||||||
|  |         cg.add(LD2412_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(LD2412_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(LD2412_component.set_still_target_binary_sensor(sens)) | ||||||
							
								
								
									
										74
									
								
								esphome/components/ld2412/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								esphome/components/ld2412/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | 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_DATABASE, | ||||||
|  |     ICON_PULSE, | ||||||
|  |     ICON_RESTART, | ||||||
|  |     ICON_RESTART_ALERT, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from .. import CONF_LD2412_ID, LD2412_ns, LD2412Component | ||||||
|  |  | ||||||
|  | FactoryResetButton = LD2412_ns.class_("FactoryResetButton", button.Button) | ||||||
|  | QueryButton = LD2412_ns.class_("QueryButton", button.Button) | ||||||
|  | RestartButton = LD2412_ns.class_("RestartButton", button.Button) | ||||||
|  | StartDynamicBackgroundCorrectionButton = LD2412_ns.class_( | ||||||
|  |     "StartDynamicBackgroundCorrectionButton", button.Button | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONF_QUERY_PARAMS = "query_params" | ||||||
|  | CONF_START_DYNAMIC_BACKGROUND_CORRECTION = "start_dynamic_background_correction" | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = { | ||||||
|  |     cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component), | ||||||
|  |     cv.Optional(CONF_FACTORY_RESET): button.button_schema( | ||||||
|  |         FactoryResetButton, | ||||||
|  |         device_class=DEVICE_CLASS_RESTART, | ||||||
|  |         entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |         icon=ICON_RESTART_ALERT, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_QUERY_PARAMS): button.button_schema( | ||||||
|  |         QueryButton, | ||||||
|  |         entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |         icon=ICON_DATABASE, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_RESTART): button.button_schema( | ||||||
|  |         RestartButton, | ||||||
|  |         device_class=DEVICE_CLASS_RESTART, | ||||||
|  |         entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |         icon=ICON_RESTART, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_START_DYNAMIC_BACKGROUND_CORRECTION): button.button_schema( | ||||||
|  |         StartDynamicBackgroundCorrectionButton, | ||||||
|  |         entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |         icon=ICON_PULSE, | ||||||
|  |     ), | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     LD2412_component = await cg.get_variable(config[CONF_LD2412_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_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_factory_reset_button(b)) | ||||||
|  |     if query_params_config := config.get(CONF_QUERY_PARAMS): | ||||||
|  |         b = await button.new_button(query_params_config) | ||||||
|  |         await cg.register_parented(b, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_query_button(b)) | ||||||
|  |     if restart_config := config.get(CONF_RESTART): | ||||||
|  |         b = await button.new_button(restart_config) | ||||||
|  |         await cg.register_parented(b, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_restart_button(b)) | ||||||
|  |     if start_dynamic_background_correction_config := config.get( | ||||||
|  |         CONF_START_DYNAMIC_BACKGROUND_CORRECTION | ||||||
|  |     ): | ||||||
|  |         b = await button.new_button(start_dynamic_background_correction_config) | ||||||
|  |         await cg.register_parented(b, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_start_dynamic_background_correction_button(b)) | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | #include "factory_reset_button.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void FactoryResetButton::press_action() { this->parent_->factory_reset(); } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2412/button/factory_reset_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2412/button/factory_reset_button.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/button/button.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class FactoryResetButton : public button::Button, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   FactoryResetButton() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void press_action() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										9
									
								
								esphome/components/ld2412/button/query_button.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								esphome/components/ld2412/button/query_button.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | #include "query_button.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void QueryButton::press_action() { this->parent_->read_all_info(); } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2412/button/query_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2412/button/query_button.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/button/button.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class QueryButton : public button::Button, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   QueryButton() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void press_action() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										9
									
								
								esphome/components/ld2412/button/restart_button.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								esphome/components/ld2412/button/restart_button.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | #include "restart_button.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void RestartButton::press_action() { this->parent_->restart_and_read_all_info(); } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2412/button/restart_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2412/button/restart_button.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/button/button.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class RestartButton : public button::Button, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   RestartButton() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void press_action() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | #include "start_dynamic_background_correction_button.h" | ||||||
|  |  | ||||||
|  | #include "restart_button.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void StartDynamicBackgroundCorrectionButton::press_action() { this->parent_->start_dynamic_background_correction(); } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/button/button.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class StartDynamicBackgroundCorrectionButton : public button::Button, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   StartDynamicBackgroundCorrectionButton() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void press_action() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										861
									
								
								esphome/components/ld2412/ld2412.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										861
									
								
								esphome/components/ld2412/ld2412.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,861 @@ | |||||||
|  | #include "ld2412.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  | #include "esphome/components/number/number.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include "esphome/core/application.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "ld2412"; | ||||||
|  | static const char *const UNKNOWN_MAC = "unknown"; | ||||||
|  | static const char *const VERSION_FMT = "%u.%02X.%02X%02X%02X%02X"; | ||||||
|  |  | ||||||
|  | enum BaudRate : 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, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum DistanceResolution : uint8_t { | ||||||
|  |   DISTANCE_RESOLUTION_0_2 = 0x03, | ||||||
|  |   DISTANCE_RESOLUTION_0_5 = 0x01, | ||||||
|  |   DISTANCE_RESOLUTION_0_75 = 0x00, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum LightFunction : uint8_t { | ||||||
|  |   LIGHT_FUNCTION_OFF = 0x00, | ||||||
|  |   LIGHT_FUNCTION_BELOW = 0x01, | ||||||
|  |   LIGHT_FUNCTION_ABOVE = 0x02, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum OutPinLevel : uint8_t { | ||||||
|  |   OUT_PIN_LEVEL_LOW = 0x01, | ||||||
|  |   OUT_PIN_LEVEL_HIGH = 0x00, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Data Type: 6th byte | ||||||
|  | Target states: 9th byte | ||||||
|  |     Moving target distance: 10~11th bytes | ||||||
|  |     Moving target energy: 12th byte | ||||||
|  |     Still target distance: 13~14th bytes | ||||||
|  |     Still target energy: 15th byte | ||||||
|  |     Detect distance: 16~17th bytes | ||||||
|  | */ | ||||||
|  | enum PeriodicData : uint8_t { | ||||||
|  |   DATA_TYPES = 6, | ||||||
|  |   TARGET_STATES = 8, | ||||||
|  |   MOVING_TARGET_LOW = 9, | ||||||
|  |   MOVING_TARGET_HIGH = 10, | ||||||
|  |   MOVING_ENERGY = 11, | ||||||
|  |   STILL_TARGET_LOW = 12, | ||||||
|  |   STILL_TARGET_HIGH = 13, | ||||||
|  |   STILL_ENERGY = 14, | ||||||
|  |   MOVING_SENSOR_START = 17, | ||||||
|  |   STILL_SENSOR_START = 31, | ||||||
|  |   LIGHT_SENSOR = 45, | ||||||
|  |   OUT_PIN_SENSOR = 38, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum PeriodicDataValue : uint8_t { | ||||||
|  |   HEADER = 0XAA, | ||||||
|  |   FOOTER = 0x55, | ||||||
|  |   CHECK = 0x00, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum AckData : uint8_t { | ||||||
|  |   COMMAND = 6, | ||||||
|  |   COMMAND_STATUS = 7, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Memory-efficient lookup tables | ||||||
|  | struct StringToUint8 { | ||||||
|  |   const char *str; | ||||||
|  |   const uint8_t value; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct Uint8ToString { | ||||||
|  |   const uint8_t value; | ||||||
|  |   const char *str; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr StringToUint8 BAUD_RATES_BY_STR[] = { | ||||||
|  |     {"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}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr StringToUint8 DISTANCE_RESOLUTIONS_BY_STR[] = { | ||||||
|  |     {"0.2m", DISTANCE_RESOLUTION_0_2}, | ||||||
|  |     {"0.5m", DISTANCE_RESOLUTION_0_5}, | ||||||
|  |     {"0.75m", DISTANCE_RESOLUTION_0_75}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr Uint8ToString DISTANCE_RESOLUTIONS_BY_UINT[] = { | ||||||
|  |     {DISTANCE_RESOLUTION_0_2, "0.2m"}, | ||||||
|  |     {DISTANCE_RESOLUTION_0_5, "0.5m"}, | ||||||
|  |     {DISTANCE_RESOLUTION_0_75, "0.75m"}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr StringToUint8 LIGHT_FUNCTIONS_BY_STR[] = { | ||||||
|  |     {"off", LIGHT_FUNCTION_OFF}, | ||||||
|  |     {"below", LIGHT_FUNCTION_BELOW}, | ||||||
|  |     {"above", LIGHT_FUNCTION_ABOVE}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr Uint8ToString LIGHT_FUNCTIONS_BY_UINT[] = { | ||||||
|  |     {LIGHT_FUNCTION_OFF, "off"}, | ||||||
|  |     {LIGHT_FUNCTION_BELOW, "below"}, | ||||||
|  |     {LIGHT_FUNCTION_ABOVE, "above"}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr StringToUint8 OUT_PIN_LEVELS_BY_STR[] = { | ||||||
|  |     {"low", OUT_PIN_LEVEL_LOW}, | ||||||
|  |     {"high", OUT_PIN_LEVEL_HIGH}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr Uint8ToString OUT_PIN_LEVELS_BY_UINT[] = { | ||||||
|  |     {OUT_PIN_LEVEL_LOW, "low"}, | ||||||
|  |     {OUT_PIN_LEVEL_HIGH, "high"}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Helper functions for lookups | ||||||
|  | template<size_t N> uint8_t find_uint8(const StringToUint8 (&arr)[N], const std::string &str) { | ||||||
|  |   for (const auto &entry : arr) { | ||||||
|  |     if (str == entry.str) { | ||||||
|  |       return entry.value; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 0xFF;  // Not found | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template<size_t N> const char *find_str(const Uint8ToString (&arr)[N], uint8_t value) { | ||||||
|  |   for (const auto &entry : arr) { | ||||||
|  |     if (value == entry.value) { | ||||||
|  |       return entry.str; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return "";  // Not found | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static constexpr uint8_t DEFAULT_PRESENCE_TIMEOUT = 5;  // Default used when number component is not defined | ||||||
|  | // Commands | ||||||
|  | static constexpr uint8_t CMD_ENABLE_CONF = 0xFF; | ||||||
|  | static constexpr uint8_t CMD_DISABLE_CONF = 0xFE; | ||||||
|  | static constexpr uint8_t CMD_ENABLE_ENG = 0x62; | ||||||
|  | static constexpr uint8_t CMD_DISABLE_ENG = 0x63; | ||||||
|  | static constexpr uint8_t CMD_QUERY_BASIC_CONF = 0x12; | ||||||
|  | static constexpr uint8_t CMD_BASIC_CONF = 0x02; | ||||||
|  | static constexpr uint8_t CMD_QUERY_VERSION = 0xA0; | ||||||
|  | static constexpr uint8_t CMD_QUERY_DISTANCE_RESOLUTION = 0x11; | ||||||
|  | static constexpr uint8_t CMD_SET_DISTANCE_RESOLUTION = 0x01; | ||||||
|  | static constexpr uint8_t CMD_QUERY_LIGHT_CONTROL = 0x1C; | ||||||
|  | static constexpr uint8_t CMD_SET_LIGHT_CONTROL = 0x0C; | ||||||
|  | static constexpr uint8_t CMD_SET_BAUD_RATE = 0xA1; | ||||||
|  | static constexpr uint8_t CMD_QUERY_MAC_ADDRESS = 0xA5; | ||||||
|  | static constexpr uint8_t CMD_FACTORY_RESET = 0xA2; | ||||||
|  | static constexpr uint8_t CMD_RESTART = 0xA3; | ||||||
|  | static constexpr uint8_t CMD_BLUETOOTH = 0xA4; | ||||||
|  | static constexpr uint8_t CMD_DYNAMIC_BACKGROUND_CORRECTION = 0x0B; | ||||||
|  | static constexpr uint8_t CMD_QUERY_DYNAMIC_BACKGROUND_CORRECTION = 0x1B; | ||||||
|  | static constexpr uint8_t CMD_MOTION_GATE_SENS = 0x03; | ||||||
|  | static constexpr uint8_t CMD_QUERY_MOTION_GATE_SENS = 0x13; | ||||||
|  | static constexpr uint8_t CMD_STATIC_GATE_SENS = 0x04; | ||||||
|  | static constexpr uint8_t CMD_QUERY_STATIC_GATE_SENS = 0x14; | ||||||
|  | static constexpr uint8_t CMD_NONE = 0x00; | ||||||
|  | // Commands values | ||||||
|  | static constexpr uint8_t CMD_MAX_MOVE_VALUE = 0x00; | ||||||
|  | static constexpr uint8_t CMD_MAX_STILL_VALUE = 0x01; | ||||||
|  | static constexpr uint8_t CMD_DURATION_VALUE = 0x02; | ||||||
|  | // Bitmasks for target states | ||||||
|  | static constexpr uint8_t MOVE_BITMASK = 0x01; | ||||||
|  | static constexpr uint8_t STILL_BITMASK = 0x02; | ||||||
|  | // Header & Footer size | ||||||
|  | static constexpr uint8_t HEADER_FOOTER_SIZE = 4; | ||||||
|  | // Command Header & Footer | ||||||
|  | static constexpr uint8_t CMD_FRAME_HEADER[HEADER_FOOTER_SIZE] = {0xFD, 0xFC, 0xFB, 0xFA}; | ||||||
|  | static constexpr uint8_t CMD_FRAME_FOOTER[HEADER_FOOTER_SIZE] = {0x04, 0x03, 0x02, 0x01}; | ||||||
|  | // Data Header & Footer | ||||||
|  | static constexpr uint8_t DATA_FRAME_HEADER[HEADER_FOOTER_SIZE] = {0xF4, 0xF3, 0xF2, 0xF1}; | ||||||
|  | static constexpr uint8_t DATA_FRAME_FOOTER[HEADER_FOOTER_SIZE] = {0xF8, 0xF7, 0xF6, 0xF5}; | ||||||
|  | // MAC address the module uses when Bluetooth is disabled | ||||||
|  | static constexpr uint8_t NO_MAC[] = {0x08, 0x05, 0x04, 0x03, 0x02, 0x01}; | ||||||
|  |  | ||||||
|  | static inline int two_byte_to_int(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; } | ||||||
|  |  | ||||||
|  | static inline bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) { | ||||||
|  |   return std::memcmp(header_footer, buffer, HEADER_FOOTER_SIZE) == 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::dump_config() { | ||||||
|  |   std::string mac_str = | ||||||
|  |       mac_address_is_valid(this->mac_address_) ? format_mac_address_pretty(this->mac_address_) : UNKNOWN_MAC; | ||||||
|  |   std::string version = str_sprintf(VERSION_FMT, this->version_[1], this->version_[0], this->version_[5], | ||||||
|  |                                     this->version_[4], this->version_[3], this->version_[2]); | ||||||
|  |   ESP_LOGCONFIG(TAG, | ||||||
|  |                 "LD2412:\n" | ||||||
|  |                 "  Firmware version: %s\n" | ||||||
|  |                 "  MAC address: %s", | ||||||
|  |                 version.c_str(), mac_str.c_str()); | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  |   ESP_LOGCONFIG(TAG, "Binary Sensors:"); | ||||||
|  |   LOG_BINARY_SENSOR("  ", "DynamicBackgroundCorrectionStatus", | ||||||
|  |                     this->dynamic_background_correction_status_binary_sensor_); | ||||||
|  |   LOG_BINARY_SENSOR("  ", "MovingTarget", this->moving_target_binary_sensor_); | ||||||
|  |   LOG_BINARY_SENSOR("  ", "StillTarget", this->still_target_binary_sensor_); | ||||||
|  |   LOG_BINARY_SENSOR("  ", "Target", this->target_binary_sensor_); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  |   ESP_LOGCONFIG(TAG, "Sensors:"); | ||||||
|  |   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "Light", this->light_sensor_); | ||||||
|  |   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "DetectionDistance", this->detection_distance_sensor_); | ||||||
|  |   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "MovingTargetDistance", this->moving_target_distance_sensor_); | ||||||
|  |   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "MovingTargetEnergy", this->moving_target_energy_sensor_); | ||||||
|  |   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "StillTargetDistance", this->still_target_distance_sensor_); | ||||||
|  |   LOG_SENSOR_WITH_DEDUP_SAFE("  ", "StillTargetEnergy", this->still_target_energy_sensor_); | ||||||
|  |   for (auto &s : this->gate_still_sensors_) { | ||||||
|  |     LOG_SENSOR_WITH_DEDUP_SAFE("  ", "GateStill", s); | ||||||
|  |   } | ||||||
|  |   for (auto &s : this->gate_move_sensors_) { | ||||||
|  |     LOG_SENSOR_WITH_DEDUP_SAFE("  ", "GateMove", s); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | #ifdef USE_TEXT_SENSOR | ||||||
|  |   ESP_LOGCONFIG(TAG, "Text Sensors:"); | ||||||
|  |   LOG_TEXT_SENSOR("  ", "MAC address", this->mac_text_sensor_); | ||||||
|  |   LOG_TEXT_SENSOR("  ", "Version", this->version_text_sensor_); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |   ESP_LOGCONFIG(TAG, "Numbers:"); | ||||||
|  |   LOG_NUMBER("  ", "LightThreshold", this->light_threshold_number_); | ||||||
|  |   LOG_NUMBER("  ", "MaxDistanceGate", this->max_distance_gate_number_); | ||||||
|  |   LOG_NUMBER("  ", "MinDistanceGate", this->min_distance_gate_number_); | ||||||
|  |   LOG_NUMBER("  ", "Timeout", this->timeout_number_); | ||||||
|  |   for (number::Number *n : this->gate_move_threshold_numbers_) { | ||||||
|  |     LOG_NUMBER("  ", "Move Thresholds", n); | ||||||
|  |   } | ||||||
|  |   for (number::Number *n : this->gate_still_threshold_numbers_) { | ||||||
|  |     LOG_NUMBER("  ", "Still Thresholds", n); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |   ESP_LOGCONFIG(TAG, "Selects:"); | ||||||
|  |   LOG_SELECT("  ", "BaudRate", this->baud_rate_select_); | ||||||
|  |   LOG_SELECT("  ", "DistanceResolution", this->distance_resolution_select_); | ||||||
|  |   LOG_SELECT("  ", "LightFunction", this->light_function_select_); | ||||||
|  |   LOG_SELECT("  ", "OutPinLevel", this->out_pin_level_select_); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |   ESP_LOGCONFIG(TAG, "Switches:"); | ||||||
|  |   LOG_SWITCH("  ", "Bluetooth", this->bluetooth_switch_); | ||||||
|  |   LOG_SWITCH("  ", "EngineeringMode", this->engineering_mode_switch_); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_BUTTON | ||||||
|  |   ESP_LOGCONFIG(TAG, "Buttons:"); | ||||||
|  |   LOG_BUTTON("  ", "FactoryReset", this->factory_reset_button_); | ||||||
|  |   LOG_BUTTON("  ", "Query", this->query_button_); | ||||||
|  |   LOG_BUTTON("  ", "Restart", this->restart_button_); | ||||||
|  |   LOG_BUTTON("  ", "StartDynamicBackgroundCorrection", this->start_dynamic_background_correction_button_); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Running setup"); | ||||||
|  |   this->read_all_info(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::read_all_info() { | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   this->get_version_(); | ||||||
|  |   delay(10);  // NOLINT | ||||||
|  |   this->get_mac_(); | ||||||
|  |   delay(10);  // NOLINT | ||||||
|  |   this->get_distance_resolution_(); | ||||||
|  |   delay(10);  // NOLINT | ||||||
|  |   this->query_parameters_(); | ||||||
|  |   delay(10);  // NOLINT | ||||||
|  |   this->query_dynamic_background_correction_(); | ||||||
|  |   delay(10);  // NOLINT | ||||||
|  |   this->query_light_control_(); | ||||||
|  |   delay(10);  // NOLINT | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |   this->get_gate_threshold(); | ||||||
|  |   delay(10);  // NOLINT | ||||||
|  | #endif | ||||||
|  |   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_->publish_state(baud_rate); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::restart_and_read_all_info() { | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   this->restart_(); | ||||||
|  |   this->set_timeout(1000, [this]() { this->read_all_info(); }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::loop() { | ||||||
|  |   while (this->available()) { | ||||||
|  |     this->readline_(this->read()); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::send_command_(uint8_t command, const uint8_t *command_value, uint8_t command_value_len) { | ||||||
|  |   ESP_LOGV(TAG, "Sending COMMAND %02X", command); | ||||||
|  |   // frame header bytes | ||||||
|  |   this->write_array(CMD_FRAME_HEADER, HEADER_FOOTER_SIZE); | ||||||
|  |   // length bytes | ||||||
|  |   uint8_t len = 2; | ||||||
|  |   if (command_value != nullptr) { | ||||||
|  |     len += command_value_len; | ||||||
|  |   } | ||||||
|  |   // 2 length bytes (low, high) + 2 command bytes (low, high) | ||||||
|  |   uint8_t len_cmd[] = {len, 0x00, command, 0x00}; | ||||||
|  |   this->write_array(len_cmd, sizeof(len_cmd)); | ||||||
|  |  | ||||||
|  |   // command value bytes | ||||||
|  |   if (command_value != nullptr) { | ||||||
|  |     this->write_array(command_value, command_value_len); | ||||||
|  |   } | ||||||
|  |   // frame footer bytes | ||||||
|  |   this->write_array(CMD_FRAME_FOOTER, HEADER_FOOTER_SIZE); | ||||||
|  |  | ||||||
|  |   if (command != CMD_ENABLE_CONF && command != CMD_DISABLE_CONF) { | ||||||
|  |     delay(30);  // NOLINT | ||||||
|  |   } | ||||||
|  |   delay(20);  // NOLINT | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::handle_periodic_data_() { | ||||||
|  |   // 4 frame header bytes + 2 length bytes + 1 data end byte + 1 crc byte + 4 frame footer bytes | ||||||
|  |   // data header=0xAA, data footer=0x55, crc=0x00 | ||||||
|  |   if (this->buffer_pos_ < 12 || !ld2412::validate_header_footer(DATA_FRAME_HEADER, this->buffer_data_) || | ||||||
|  |       this->buffer_data_[7] != HEADER || this->buffer_data_[this->buffer_pos_ - 6] != FOOTER) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   /* | ||||||
|  |     Data Type: 7th | ||||||
|  |     0x01: Engineering mode | ||||||
|  |     0x02: Normal mode | ||||||
|  |   */ | ||||||
|  |   bool engineering_mode = this->buffer_data_[DATA_TYPES] == 0x01; | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |   if (this->engineering_mode_switch_ != nullptr) { | ||||||
|  |     this->engineering_mode_switch_->publish_state(engineering_mode); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  |   /* | ||||||
|  |     Target states: 9th | ||||||
|  |     0x00 = No target | ||||||
|  |     0x01 = Moving targets | ||||||
|  |     0x02 = Still targets | ||||||
|  |     0x03 = Moving+Still targets | ||||||
|  |   */ | ||||||
|  |   char target_state = this->buffer_data_[TARGET_STATES]; | ||||||
|  |   if (this->target_binary_sensor_ != nullptr) { | ||||||
|  |     this->target_binary_sensor_->publish_state(target_state != 0x00); | ||||||
|  |   } | ||||||
|  |   if (this->moving_target_binary_sensor_ != nullptr) { | ||||||
|  |     this->moving_target_binary_sensor_->publish_state(target_state & MOVE_BITMASK); | ||||||
|  |   } | ||||||
|  |   if (this->still_target_binary_sensor_ != nullptr) { | ||||||
|  |     this->still_target_binary_sensor_->publish_state(target_state & STILL_BITMASK); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |   /* | ||||||
|  |     Moving target distance: 10~11th bytes | ||||||
|  |     Moving target energy: 12th byte | ||||||
|  |     Still target distance: 13~14th bytes | ||||||
|  |     Still target energy: 15th byte | ||||||
|  |     Detect distance: 16~17th bytes | ||||||
|  |   */ | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  |   SAFE_PUBLISH_SENSOR( | ||||||
|  |       this->moving_target_distance_sensor_, | ||||||
|  |       ld2412::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH])) | ||||||
|  |   SAFE_PUBLISH_SENSOR(this->moving_target_energy_sensor_, this->buffer_data_[MOVING_ENERGY]) | ||||||
|  |   SAFE_PUBLISH_SENSOR( | ||||||
|  |       this->still_target_distance_sensor_, | ||||||
|  |       ld2412::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH])) | ||||||
|  |   SAFE_PUBLISH_SENSOR(this->still_target_energy_sensor_, this->buffer_data_[STILL_ENERGY]) | ||||||
|  |   if (this->detection_distance_sensor_ != nullptr) { | ||||||
|  |     int new_detect_distance = 0; | ||||||
|  |     if (target_state != 0x00 && (target_state & MOVE_BITMASK)) { | ||||||
|  |       new_detect_distance = | ||||||
|  |           ld2412::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH]); | ||||||
|  |     } else if (target_state != 0x00) { | ||||||
|  |       new_detect_distance = | ||||||
|  |           ld2412::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH]); | ||||||
|  |     } | ||||||
|  |     this->detection_distance_sensor_->publish_state_if_not_dup(new_detect_distance); | ||||||
|  |   } | ||||||
|  |   if (engineering_mode) { | ||||||
|  |     /* | ||||||
|  |       Moving distance range: 18th byte | ||||||
|  |       Still distance range: 19th byte | ||||||
|  |       Moving energy: 20~28th bytes | ||||||
|  |     */ | ||||||
|  |     for (uint8_t i = 0; i < TOTAL_GATES; i++) { | ||||||
|  |       SAFE_PUBLISH_SENSOR(this->gate_move_sensors_[i], this->buffer_data_[MOVING_SENSOR_START + i]) | ||||||
|  |     } | ||||||
|  |     /* | ||||||
|  |       Still energy: 29~37th bytes | ||||||
|  |     */ | ||||||
|  |     for (uint8_t i = 0; i < TOTAL_GATES; i++) { | ||||||
|  |       SAFE_PUBLISH_SENSOR(this->gate_still_sensors_[i], this->buffer_data_[STILL_SENSOR_START + i]) | ||||||
|  |     } | ||||||
|  |     /* | ||||||
|  |       Light sensor: 38th bytes | ||||||
|  |     */ | ||||||
|  |     SAFE_PUBLISH_SENSOR(this->light_sensor_, this->buffer_data_[LIGHT_SENSOR]) | ||||||
|  |   } else { | ||||||
|  |     for (auto &gate_move_sensor : this->gate_move_sensors_) { | ||||||
|  |       SAFE_PUBLISH_SENSOR_UNKNOWN(gate_move_sensor) | ||||||
|  |     } | ||||||
|  |     for (auto &gate_still_sensor : this->gate_still_sensors_) { | ||||||
|  |       SAFE_PUBLISH_SENSOR_UNKNOWN(gate_still_sensor) | ||||||
|  |     } | ||||||
|  |     SAFE_PUBLISH_SENSOR_UNKNOWN(this->light_sensor_) | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |   // the radar module won't tell us when it's done, so we just have to keep polling... | ||||||
|  |   if (this->dynamic_background_correction_active_) { | ||||||
|  |     this->set_config_mode_(true); | ||||||
|  |     this->query_dynamic_background_correction_(); | ||||||
|  |     this->set_config_mode_(false); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  | std::function<void(void)> set_number_value(number::Number *n, float value) { | ||||||
|  |   if (n != nullptr && (!n->has_state() || n->state != value)) { | ||||||
|  |     n->state = value; | ||||||
|  |     return [n, value]() { n->publish_state(value); }; | ||||||
|  |   } | ||||||
|  |   return []() {}; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | bool LD2412Component::handle_ack_data_() { | ||||||
|  |   ESP_LOGV(TAG, "Handling ACK DATA for COMMAND %02X", this->buffer_data_[COMMAND]); | ||||||
|  |   if (this->buffer_pos_ < 10) { | ||||||
|  |     ESP_LOGW(TAG, "Invalid length"); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   if (!ld2412::validate_header_footer(CMD_FRAME_HEADER, this->buffer_data_)) { | ||||||
|  |     ESP_LOGW(TAG, "Invalid header: %s", format_hex_pretty(this->buffer_data_, HEADER_FOOTER_SIZE).c_str()); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   if (this->buffer_data_[COMMAND_STATUS] != 0x01) { | ||||||
|  |     ESP_LOGW(TAG, "Invalid status"); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   if (this->buffer_data_[8] || this->buffer_data_[9]) { | ||||||
|  |     ESP_LOGW(TAG, "Invalid command: %02X, %02X", this->buffer_data_[8], this->buffer_data_[9]); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   switch (this->buffer_data_[COMMAND]) { | ||||||
|  |     case CMD_ENABLE_CONF: | ||||||
|  |       ESP_LOGV(TAG, "Enable conf"); | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case CMD_DISABLE_CONF: | ||||||
|  |       ESP_LOGV(TAG, "Disabled conf"); | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case CMD_SET_BAUD_RATE: | ||||||
|  |       ESP_LOGV(TAG, "Baud rate change"); | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |       if (this->baud_rate_select_ != nullptr) { | ||||||
|  |         ESP_LOGW(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->state.c_str()); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case CMD_QUERY_VERSION: { | ||||||
|  |       std::memcpy(this->version_, &this->buffer_data_[12], sizeof(this->version_)); | ||||||
|  |       std::string version = str_sprintf(VERSION_FMT, this->version_[1], this->version_[0], this->version_[5], | ||||||
|  |                                         this->version_[4], this->version_[3], this->version_[2]); | ||||||
|  |       ESP_LOGV(TAG, "Firmware version: %s", version.c_str()); | ||||||
|  | #ifdef USE_TEXT_SENSOR | ||||||
|  |       if (this->version_text_sensor_ != nullptr) { | ||||||
|  |         this->version_text_sensor_->publish_state(version); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case CMD_QUERY_DISTANCE_RESOLUTION: { | ||||||
|  |       const auto *distance_resolution = find_str(DISTANCE_RESOLUTIONS_BY_UINT, this->buffer_data_[10]); | ||||||
|  |       ESP_LOGV(TAG, "Distance resolution: %s", distance_resolution); | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |       if (this->distance_resolution_select_ != nullptr) { | ||||||
|  |         this->distance_resolution_select_->publish_state(distance_resolution); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     case CMD_QUERY_LIGHT_CONTROL: { | ||||||
|  |       this->light_function_ = this->buffer_data_[10]; | ||||||
|  |       this->light_threshold_ = this->buffer_data_[11]; | ||||||
|  |       const auto *light_function_str = find_str(LIGHT_FUNCTIONS_BY_UINT, this->light_function_); | ||||||
|  |       ESP_LOGV(TAG, | ||||||
|  |                "Light function: %s\n" | ||||||
|  |                "Light threshold: %u", | ||||||
|  |                light_function_str, this->light_threshold_); | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |       if (this->light_function_select_ != nullptr) { | ||||||
|  |         this->light_function_select_->publish_state(light_function_str); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |       if (this->light_threshold_number_ != nullptr) { | ||||||
|  |         this->light_threshold_number_->publish_state(static_cast<float>(this->light_threshold_)); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     case CMD_QUERY_MAC_ADDRESS: { | ||||||
|  |       if (this->buffer_pos_ < 20) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       this->bluetooth_on_ = std::memcmp(&this->buffer_data_[10], NO_MAC, sizeof(NO_MAC)) != 0; | ||||||
|  |       if (this->bluetooth_on_) { | ||||||
|  |         std::memcpy(this->mac_address_, &this->buffer_data_[10], sizeof(this->mac_address_)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       std::string mac_str = | ||||||
|  |           mac_address_is_valid(this->mac_address_) ? format_mac_address_pretty(this->mac_address_) : UNKNOWN_MAC; | ||||||
|  |       ESP_LOGV(TAG, "MAC address: %s", mac_str.c_str()); | ||||||
|  | #ifdef USE_TEXT_SENSOR | ||||||
|  |       if (this->mac_text_sensor_ != nullptr) { | ||||||
|  |         this->mac_text_sensor_->publish_state(mac_str); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |       if (this->bluetooth_switch_ != nullptr) { | ||||||
|  |         this->bluetooth_switch_->publish_state(this->bluetooth_on_); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     case CMD_SET_DISTANCE_RESOLUTION: | ||||||
|  |       ESP_LOGV(TAG, "Handled set distance resolution command"); | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case CMD_QUERY_DYNAMIC_BACKGROUND_CORRECTION: { | ||||||
|  |       ESP_LOGV(TAG, "Handled query dynamic background correction"); | ||||||
|  |       bool dynamic_background_correction_active = (this->buffer_data_[10] != 0x00); | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  |       if (this->dynamic_background_correction_status_binary_sensor_ != nullptr) { | ||||||
|  |         this->dynamic_background_correction_status_binary_sensor_->publish_state(dynamic_background_correction_active); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       this->dynamic_background_correction_active_ = dynamic_background_correction_active; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     case CMD_BLUETOOTH: | ||||||
|  |       ESP_LOGV(TAG, "Handled bluetooth command"); | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case CMD_SET_LIGHT_CONTROL: | ||||||
|  |       ESP_LOGV(TAG, "Handled set light control command"); | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case CMD_QUERY_MOTION_GATE_SENS: { | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |       std::vector<std::function<void(void)>> updates; | ||||||
|  |       updates.reserve(this->gate_still_threshold_numbers_.size()); | ||||||
|  |       for (size_t i = 0; i < this->gate_still_threshold_numbers_.size(); i++) { | ||||||
|  |         updates.push_back(set_number_value(this->gate_move_threshold_numbers_[i], this->buffer_data_[10 + i])); | ||||||
|  |       } | ||||||
|  |       for (auto &update : updates) { | ||||||
|  |         update(); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     case CMD_QUERY_STATIC_GATE_SENS: { | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |       std::vector<std::function<void(void)>> updates; | ||||||
|  |       updates.reserve(this->gate_still_threshold_numbers_.size()); | ||||||
|  |       for (size_t i = 0; i < this->gate_still_threshold_numbers_.size(); i++) { | ||||||
|  |         updates.push_back(set_number_value(this->gate_still_threshold_numbers_[i], this->buffer_data_[10 + i])); | ||||||
|  |       } | ||||||
|  |       for (auto &update : updates) { | ||||||
|  |         update(); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     case CMD_QUERY_BASIC_CONF:  // Query parameters response | ||||||
|  |     { | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |       /* | ||||||
|  |         Moving distance range: 9th byte | ||||||
|  |         Still distance range: 10th byte | ||||||
|  |       */ | ||||||
|  |       std::vector<std::function<void(void)>> updates; | ||||||
|  |       updates.push_back(set_number_value(this->min_distance_gate_number_, this->buffer_data_[10])); | ||||||
|  |       updates.push_back(set_number_value(this->max_distance_gate_number_, this->buffer_data_[11] - 1)); | ||||||
|  |       ESP_LOGV(TAG, "min_distance_gate_number_: %u, max_distance_gate_number_ %u", this->buffer_data_[10], | ||||||
|  |                this->buffer_data_[11]); | ||||||
|  |       /* | ||||||
|  |         None Duration: 11~12th bytes | ||||||
|  |       */ | ||||||
|  |       updates.push_back(set_number_value(this->timeout_number_, | ||||||
|  |                                          ld2412::two_byte_to_int(this->buffer_data_[12], this->buffer_data_[13]))); | ||||||
|  |       ESP_LOGV(TAG, "timeout_number_: %u", ld2412::two_byte_to_int(this->buffer_data_[12], this->buffer_data_[13])); | ||||||
|  |       /* | ||||||
|  |         Output pin configuration: 13th bytes | ||||||
|  |       */ | ||||||
|  |       this->out_pin_level_ = this->buffer_data_[14]; | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |       const auto *out_pin_level_str = find_str(OUT_PIN_LEVELS_BY_UINT, this->out_pin_level_); | ||||||
|  |       if (this->out_pin_level_select_ != nullptr) { | ||||||
|  |         this->out_pin_level_select_->publish_state(out_pin_level_str); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |       for (auto &update : updates) { | ||||||
|  |         update(); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |     } break; | ||||||
|  |     default: | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::readline_(int readch) { | ||||||
|  |   if (readch < 0) { | ||||||
|  |     return;  // No data available | ||||||
|  |   } | ||||||
|  |   if (this->buffer_pos_ < HEADER_FOOTER_SIZE && readch != DATA_FRAME_HEADER[this->buffer_pos_] && | ||||||
|  |       readch != CMD_FRAME_HEADER[this->buffer_pos_]) { | ||||||
|  |     this->buffer_pos_ = 0; | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   if (this->buffer_pos_ < MAX_LINE_LENGTH - 1) { | ||||||
|  |     this->buffer_data_[this->buffer_pos_++] = readch; | ||||||
|  |     this->buffer_data_[this->buffer_pos_] = 0; | ||||||
|  |   } else { | ||||||
|  |     // We should never get here, but just in case... | ||||||
|  |     ESP_LOGW(TAG, "Max command length exceeded; ignoring"); | ||||||
|  |     this->buffer_pos_ = 0; | ||||||
|  |   } | ||||||
|  |   if (this->buffer_pos_ < 4) { | ||||||
|  |     return;  // Not enough data to process yet | ||||||
|  |   } | ||||||
|  |   if (ld2412::validate_header_footer(DATA_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) { | ||||||
|  |     ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str()); | ||||||
|  |     this->handle_periodic_data_(); | ||||||
|  |     this->buffer_pos_ = 0;  // Reset position index for next message | ||||||
|  |   } else if (ld2412::validate_header_footer(CMD_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) { | ||||||
|  |     ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str()); | ||||||
|  |     if (this->handle_ack_data_()) { | ||||||
|  |       this->buffer_pos_ = 0;  // Reset position index for next message | ||||||
|  |     } else { | ||||||
|  |       ESP_LOGV(TAG, "Ack Data incomplete"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::set_config_mode_(bool enable) { | ||||||
|  |   const uint8_t cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF; | ||||||
|  |   const uint8_t cmd_value[2] = {0x01, 0x00}; | ||||||
|  |   this->send_command_(cmd, enable ? cmd_value : nullptr, sizeof(cmd_value)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::set_bluetooth(bool enable) { | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   const uint8_t cmd_value[2] = {enable ? (uint8_t) 0x01 : (uint8_t) 0x00, 0x00}; | ||||||
|  |   this->send_command_(CMD_BLUETOOTH, cmd_value, sizeof(cmd_value)); | ||||||
|  |   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::set_distance_resolution(const std::string &state) { | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   const uint8_t cmd_value[6] = {find_uint8(DISTANCE_RESOLUTIONS_BY_STR, state), 0x00, 0x00, 0x00, 0x00, 0x00}; | ||||||
|  |   this->send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, sizeof(cmd_value)); | ||||||
|  |   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::set_baud_rate(const std::string &state) { | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   const uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00}; | ||||||
|  |   this->send_command_(CMD_SET_BAUD_RATE, cmd_value, sizeof(cmd_value)); | ||||||
|  |   this->set_timeout(200, [this]() { this->restart_(); }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::query_dynamic_background_correction_() { | ||||||
|  |   this->send_command_(CMD_QUERY_DYNAMIC_BACKGROUND_CORRECTION, nullptr, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::start_dynamic_background_correction() { | ||||||
|  |   if (this->dynamic_background_correction_active_) { | ||||||
|  |     return;  // Already in progress | ||||||
|  |   } | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  |   if (this->dynamic_background_correction_status_binary_sensor_ != nullptr) { | ||||||
|  |     this->dynamic_background_correction_status_binary_sensor_->publish_state(true); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |   this->dynamic_background_correction_active_ = true; | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   this->send_command_(CMD_DYNAMIC_BACKGROUND_CORRECTION, nullptr, 0); | ||||||
|  |   this->set_config_mode_(false); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::set_engineering_mode(bool enable) { | ||||||
|  |   const uint8_t cmd = enable ? CMD_ENABLE_ENG : CMD_DISABLE_ENG; | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   this->send_command_(cmd, nullptr, 0); | ||||||
|  |   this->set_config_mode_(false); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::factory_reset() { | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   this->send_command_(CMD_FACTORY_RESET, nullptr, 0); | ||||||
|  |   this->set_timeout(2000, [this]() { this->restart_and_read_all_info(); }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::restart_() { this->send_command_(CMD_RESTART, nullptr, 0); } | ||||||
|  |  | ||||||
|  | void LD2412Component::query_parameters_() { this->send_command_(CMD_QUERY_BASIC_CONF, nullptr, 0); } | ||||||
|  |  | ||||||
|  | void LD2412Component::get_version_() { this->send_command_(CMD_QUERY_VERSION, nullptr, 0); } | ||||||
|  |  | ||||||
|  | void LD2412Component::get_mac_() { | ||||||
|  |   const uint8_t cmd_value[2] = {0x01, 0x00}; | ||||||
|  |   this->send_command_(CMD_QUERY_MAC_ADDRESS, cmd_value, sizeof(cmd_value)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::get_distance_resolution_() { this->send_command_(CMD_QUERY_DISTANCE_RESOLUTION, nullptr, 0); } | ||||||
|  |  | ||||||
|  | void LD2412Component::query_light_control_() { this->send_command_(CMD_QUERY_LIGHT_CONTROL, nullptr, 0); } | ||||||
|  |  | ||||||
|  | void LD2412Component::set_basic_config() { | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |   if (!this->min_distance_gate_number_->has_state() || !this->max_distance_gate_number_->has_state() || | ||||||
|  |       !this->timeout_number_->has_state()) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |   if (!this->out_pin_level_select_->has_state()) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |   uint8_t value[5] = { | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |       lowbyte(static_cast<int>(this->min_distance_gate_number_->state)), | ||||||
|  |       lowbyte(static_cast<int>(this->max_distance_gate_number_->state) + 1), | ||||||
|  |       lowbyte(static_cast<int>(this->timeout_number_->state)), | ||||||
|  |       highbyte(static_cast<int>(this->timeout_number_->state)), | ||||||
|  | #else | ||||||
|  |       1,    TOTAL_GATES, DEFAULT_PRESENCE_TIMEOUT, 0, | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |       find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->state), | ||||||
|  | #else | ||||||
|  |       0x01,  // Default value if not using select | ||||||
|  | #endif | ||||||
|  |   }; | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   this->send_command_(CMD_BASIC_CONF, value, sizeof(value)); | ||||||
|  |   this->set_config_mode_(false); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  | void LD2412Component::set_gate_threshold() { | ||||||
|  |   if (this->gate_move_threshold_numbers_.empty() && this->gate_still_threshold_numbers_.empty()) { | ||||||
|  |     return;  // No gate threshold numbers set; nothing to do here | ||||||
|  |   } | ||||||
|  |   uint8_t value[TOTAL_GATES] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   if (!this->gate_move_threshold_numbers_.empty()) { | ||||||
|  |     for (size_t i = 0; i < this->gate_move_threshold_numbers_.size(); i++) { | ||||||
|  |       value[i] = lowbyte(static_cast<int>(this->gate_move_threshold_numbers_[i]->state)); | ||||||
|  |     } | ||||||
|  |     this->send_command_(CMD_MOTION_GATE_SENS, value, sizeof(value)); | ||||||
|  |   } | ||||||
|  |   if (!this->gate_still_threshold_numbers_.empty()) { | ||||||
|  |     for (size_t i = 0; i < this->gate_still_threshold_numbers_.size(); i++) { | ||||||
|  |       value[i] = lowbyte(static_cast<int>(this->gate_still_threshold_numbers_[i]->state)); | ||||||
|  |     } | ||||||
|  |     this->send_command_(CMD_STATIC_GATE_SENS, value, sizeof(value)); | ||||||
|  |   } | ||||||
|  |   this->set_config_mode_(false); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::get_gate_threshold() { | ||||||
|  |   this->send_command_(CMD_QUERY_MOTION_GATE_SENS, nullptr, 0); | ||||||
|  |   this->send_command_(CMD_QUERY_STATIC_GATE_SENS, nullptr, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::set_gate_still_threshold_number(uint8_t gate, number::Number *n) { | ||||||
|  |   this->gate_still_threshold_numbers_[gate] = n; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void LD2412Component::set_gate_move_threshold_number(uint8_t gate, number::Number *n) { | ||||||
|  |   this->gate_move_threshold_numbers_[gate] = n; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | void LD2412Component::set_light_out_control() { | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |   if (this->light_threshold_number_ != nullptr && this->light_threshold_number_->has_state()) { | ||||||
|  |     this->light_threshold_ = static_cast<uint8_t>(this->light_threshold_number_->state); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |   if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) { | ||||||
|  |     this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->state); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |   uint8_t value[2] = {this->light_function_, this->light_threshold_}; | ||||||
|  |   this->set_config_mode_(true); | ||||||
|  |   this->send_command_(CMD_SET_LIGHT_CONTROL, value, sizeof(value)); | ||||||
|  |   this->query_light_control_(); | ||||||
|  |   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  | // These could leak memory, but they are only set once prior to 'setup()' and should never be used again. | ||||||
|  | void LD2412Component::set_gate_move_sensor(uint8_t gate, sensor::Sensor *s) { | ||||||
|  |   this->gate_move_sensors_[gate] = new SensorWithDedup<uint8_t>(s); | ||||||
|  | } | ||||||
|  | void LD2412Component::set_gate_still_sensor(uint8_t gate, sensor::Sensor *s) { | ||||||
|  |   this->gate_still_sensors_[gate] = new SensorWithDedup<uint8_t>(s); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										141
									
								
								esphome/components/ld2412/ld2412.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								esphome/components/ld2412/ld2412.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "esphome/core/defines.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  | #include "esphome/components/binary_sensor/binary_sensor.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  | #include "esphome/components/number/number.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  | #include "esphome/components/switch/switch.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_BUTTON | ||||||
|  | #include "esphome/components/button/button.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SELECT | ||||||
|  | #include "esphome/components/select/select.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_TEXT_SENSOR | ||||||
|  | #include "esphome/components/text_sensor/text_sensor.h" | ||||||
|  | #endif | ||||||
|  | #include "esphome/components/ld24xx/ld24xx.h" | ||||||
|  | #include "esphome/components/uart/uart.h" | ||||||
|  | #include "esphome/core/automation.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | using namespace ld24xx; | ||||||
|  |  | ||||||
|  | static constexpr uint8_t MAX_LINE_LENGTH = 54;  // Max characters for serial buffer | ||||||
|  | static constexpr uint8_t TOTAL_GATES = 14;      // Total number of gates supported by the LD2412 | ||||||
|  |  | ||||||
|  | class LD2412Component : public Component, public uart::UARTDevice { | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  |   SUB_BINARY_SENSOR(dynamic_background_correction_status) | ||||||
|  |   SUB_BINARY_SENSOR(moving_target) | ||||||
|  |   SUB_BINARY_SENSOR(still_target) | ||||||
|  |   SUB_BINARY_SENSOR(target) | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  |   SUB_SENSOR_WITH_DEDUP(light, uint8_t) | ||||||
|  |   SUB_SENSOR_WITH_DEDUP(detection_distance, int) | ||||||
|  |   SUB_SENSOR_WITH_DEDUP(moving_target_distance, int) | ||||||
|  |   SUB_SENSOR_WITH_DEDUP(moving_target_energy, uint8_t) | ||||||
|  |   SUB_SENSOR_WITH_DEDUP(still_target_distance, int) | ||||||
|  |   SUB_SENSOR_WITH_DEDUP(still_target_energy, uint8_t) | ||||||
|  | #endif | ||||||
|  | #ifdef USE_TEXT_SENSOR | ||||||
|  |   SUB_TEXT_SENSOR(mac) | ||||||
|  |   SUB_TEXT_SENSOR(version) | ||||||
|  | #endif | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |   SUB_NUMBER(light_threshold) | ||||||
|  |   SUB_NUMBER(max_distance_gate) | ||||||
|  |   SUB_NUMBER(min_distance_gate) | ||||||
|  |   SUB_NUMBER(timeout) | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SELECT | ||||||
|  |   SUB_SELECT(baud_rate) | ||||||
|  |   SUB_SELECT(distance_resolution) | ||||||
|  |   SUB_SELECT(light_function) | ||||||
|  |   SUB_SELECT(out_pin_level) | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |   SUB_SWITCH(bluetooth) | ||||||
|  |   SUB_SWITCH(engineering_mode) | ||||||
|  | #endif | ||||||
|  | #ifdef USE_BUTTON | ||||||
|  |   SUB_BUTTON(factory_reset) | ||||||
|  |   SUB_BUTTON(query) | ||||||
|  |   SUB_BUTTON(restart) | ||||||
|  |   SUB_BUTTON(start_dynamic_background_correction) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   void setup() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   void loop() override; | ||||||
|  |   void set_light_out_control(); | ||||||
|  |   void set_basic_config(); | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |   void set_gate_move_threshold_number(uint8_t gate, number::Number *n); | ||||||
|  |   void set_gate_still_threshold_number(uint8_t gate, number::Number *n); | ||||||
|  |   void set_gate_threshold(); | ||||||
|  |   void get_gate_threshold(); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  |   void set_gate_move_sensor(uint8_t gate, sensor::Sensor *s); | ||||||
|  |   void set_gate_still_sensor(uint8_t gate, sensor::Sensor *s); | ||||||
|  | #endif | ||||||
|  |   void set_engineering_mode(bool enable); | ||||||
|  |   void read_all_info(); | ||||||
|  |   void restart_and_read_all_info(); | ||||||
|  |   void set_bluetooth(bool enable); | ||||||
|  |   void set_distance_resolution(const std::string &state); | ||||||
|  |   void set_baud_rate(const std::string &state); | ||||||
|  |   void factory_reset(); | ||||||
|  |   void start_dynamic_background_correction(); | ||||||
|  |  | ||||||
|  |  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_(); | ||||||
|  |   bool handle_ack_data_(); | ||||||
|  |   void readline_(int readch); | ||||||
|  |   void query_parameters_(); | ||||||
|  |   void get_version_(); | ||||||
|  |   void get_mac_(); | ||||||
|  |   void get_distance_resolution_(); | ||||||
|  |   void query_light_control_(); | ||||||
|  |   void restart_(); | ||||||
|  |   void query_dynamic_background_correction_(); | ||||||
|  |  | ||||||
|  |   uint8_t light_function_ = 0; | ||||||
|  |   uint8_t light_threshold_ = 0; | ||||||
|  |   uint8_t out_pin_level_ = 0; | ||||||
|  |   uint8_t buffer_pos_ = 0;  // where to resume processing/populating buffer | ||||||
|  |   uint8_t buffer_data_[MAX_LINE_LENGTH]; | ||||||
|  |   uint8_t mac_address_[6] = {0, 0, 0, 0, 0, 0}; | ||||||
|  |   uint8_t version_[6] = {0, 0, 0, 0, 0, 0}; | ||||||
|  |   bool bluetooth_on_{false}; | ||||||
|  |   bool dynamic_background_correction_active_{false}; | ||||||
|  | #ifdef USE_NUMBER | ||||||
|  |   std::array<number::Number *, TOTAL_GATES> gate_move_threshold_numbers_{}; | ||||||
|  |   std::array<number::Number *, TOTAL_GATES> gate_still_threshold_numbers_{}; | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SENSOR | ||||||
|  |   std::array<SensorWithDedup<uint8_t> *, TOTAL_GATES> gate_move_sensors_{}; | ||||||
|  |   std::array<SensorWithDedup<uint8_t> *, TOTAL_GATES> gate_still_sensors_{}; | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										126
									
								
								esphome/components/ld2412/number/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								esphome/components/ld2412/number/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import number | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ID, | ||||||
|  |     CONF_MOVE_THRESHOLD, | ||||||
|  |     CONF_STILL_THRESHOLD, | ||||||
|  |     CONF_TIMEOUT, | ||||||
|  |     DEVICE_CLASS_DISTANCE, | ||||||
|  |     DEVICE_CLASS_ILLUMINANCE, | ||||||
|  |     DEVICE_CLASS_SIGNAL_STRENGTH, | ||||||
|  |     ENTITY_CATEGORY_CONFIG, | ||||||
|  |     ICON_LIGHTBULB, | ||||||
|  |     ICON_MOTION_SENSOR, | ||||||
|  |     ICON_TIMELAPSE, | ||||||
|  |     UNIT_PERCENT, | ||||||
|  |     UNIT_SECOND, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from .. import CONF_LD2412_ID, LD2412_ns, LD2412Component | ||||||
|  |  | ||||||
|  | GateThresholdNumber = LD2412_ns.class_("GateThresholdNumber", number.Number) | ||||||
|  | LightThresholdNumber = LD2412_ns.class_("LightThresholdNumber", number.Number) | ||||||
|  | MaxDistanceTimeoutNumber = LD2412_ns.class_("MaxDistanceTimeoutNumber", number.Number) | ||||||
|  |  | ||||||
|  | CONF_LIGHT_THRESHOLD = "light_threshold" | ||||||
|  | CONF_MAX_DISTANCE_GATE = "max_distance_gate" | ||||||
|  | CONF_MIN_DISTANCE_GATE = "min_distance_gate" | ||||||
|  |  | ||||||
|  | TIMEOUT_GROUP = "timeout" | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component), | ||||||
|  |         cv.Optional(CONF_LIGHT_THRESHOLD): number.number_schema( | ||||||
|  |             LightThresholdNumber, | ||||||
|  |             device_class=DEVICE_CLASS_ILLUMINANCE, | ||||||
|  |             entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |             icon=ICON_LIGHTBULB, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_MAX_DISTANCE_GATE): number.number_schema( | ||||||
|  |             MaxDistanceTimeoutNumber, | ||||||
|  |             device_class=DEVICE_CLASS_DISTANCE, | ||||||
|  |             entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |             icon=ICON_MOTION_SENSOR, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_MIN_DISTANCE_GATE): number.number_schema( | ||||||
|  |             MaxDistanceTimeoutNumber, | ||||||
|  |             device_class=DEVICE_CLASS_DISTANCE, | ||||||
|  |             entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |             icon=ICON_MOTION_SENSOR, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_TIMEOUT): number.number_schema( | ||||||
|  |             MaxDistanceTimeoutNumber, | ||||||
|  |             entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |             icon=ICON_TIMELAPSE, | ||||||
|  |             unit_of_measurement=UNIT_SECOND, | ||||||
|  |         ), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = CONFIG_SCHEMA.extend( | ||||||
|  |     { | ||||||
|  |         cv.Optional(f"gate_{x}"): ( | ||||||
|  |             { | ||||||
|  |                 cv.Required(CONF_MOVE_THRESHOLD): number.number_schema( | ||||||
|  |                     GateThresholdNumber, | ||||||
|  |                     device_class=DEVICE_CLASS_SIGNAL_STRENGTH, | ||||||
|  |                     entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |                     icon=ICON_MOTION_SENSOR, | ||||||
|  |                     unit_of_measurement=UNIT_PERCENT, | ||||||
|  |                 ), | ||||||
|  |                 cv.Required(CONF_STILL_THRESHOLD): number.number_schema( | ||||||
|  |                     GateThresholdNumber, | ||||||
|  |                     device_class=DEVICE_CLASS_SIGNAL_STRENGTH, | ||||||
|  |                     entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |                     icon=ICON_MOTION_SENSOR, | ||||||
|  |                     unit_of_measurement=UNIT_PERCENT, | ||||||
|  |                 ), | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |         for x in range(14) | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     LD2412_component = await cg.get_variable(config[CONF_LD2412_ID]) | ||||||
|  |     if light_threshold_config := config.get(CONF_LIGHT_THRESHOLD): | ||||||
|  |         n = await number.new_number( | ||||||
|  |             light_threshold_config, min_value=0, max_value=255, step=1 | ||||||
|  |         ) | ||||||
|  |         await cg.register_parented(n, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_light_threshold_number(n)) | ||||||
|  |     if max_distance_gate_config := config.get(CONF_MAX_DISTANCE_GATE): | ||||||
|  |         n = await number.new_number( | ||||||
|  |             max_distance_gate_config, min_value=2, max_value=13, step=1 | ||||||
|  |         ) | ||||||
|  |         await cg.register_parented(n, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_max_distance_gate_number(n)) | ||||||
|  |     if min_distance_gate_config := config.get(CONF_MIN_DISTANCE_GATE): | ||||||
|  |         n = await number.new_number( | ||||||
|  |             min_distance_gate_config, min_value=1, max_value=12, step=1 | ||||||
|  |         ) | ||||||
|  |         await cg.register_parented(n, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_min_distance_gate_number(n)) | ||||||
|  |     for x in range(14): | ||||||
|  |         if gate_conf := config.get(f"gate_{x}"): | ||||||
|  |             move_config = gate_conf[CONF_MOVE_THRESHOLD] | ||||||
|  |             n = cg.new_Pvariable(move_config[CONF_ID], x) | ||||||
|  |             await number.register_number( | ||||||
|  |                 n, move_config, min_value=0, max_value=100, step=1 | ||||||
|  |             ) | ||||||
|  |             await cg.register_parented(n, config[CONF_LD2412_ID]) | ||||||
|  |             cg.add(LD2412_component.set_gate_move_threshold_number(x, n)) | ||||||
|  |             still_config = gate_conf[CONF_STILL_THRESHOLD] | ||||||
|  |             n = cg.new_Pvariable(still_config[CONF_ID], x) | ||||||
|  |             await number.register_number( | ||||||
|  |                 n, still_config, min_value=0, max_value=100, step=1 | ||||||
|  |             ) | ||||||
|  |             await cg.register_parented(n, config[CONF_LD2412_ID]) | ||||||
|  |             cg.add(LD2412_component.set_gate_still_threshold_number(x, n)) | ||||||
|  |     if timeout_config := config.get(CONF_TIMEOUT): | ||||||
|  |         n = await number.new_number(timeout_config, min_value=0, max_value=900, step=1) | ||||||
|  |         await cg.register_parented(n, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_timeout_number(n)) | ||||||
							
								
								
									
										14
									
								
								esphome/components/ld2412/number/gate_threshold_number.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								esphome/components/ld2412/number/gate_threshold_number.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | #include "gate_threshold_number.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | GateThresholdNumber::GateThresholdNumber(uint8_t gate) : gate_(gate) {} | ||||||
|  |  | ||||||
|  | void GateThresholdNumber::control(float value) { | ||||||
|  |   this->publish_state(value); | ||||||
|  |   this->parent_->set_gate_threshold(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										19
									
								
								esphome/components/ld2412/number/gate_threshold_number.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								esphome/components/ld2412/number/gate_threshold_number.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/number/number.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class GateThresholdNumber : public number::Number, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   GateThresholdNumber(uint8_t gate); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint8_t gate_; | ||||||
|  |   void control(float value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										12
									
								
								esphome/components/ld2412/number/light_threshold_number.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2412/number/light_threshold_number.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | #include "light_threshold_number.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void LightThresholdNumber::control(float value) { | ||||||
|  |   this->publish_state(value); | ||||||
|  |   this->parent_->set_light_out_control(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2412/number/light_threshold_number.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2412/number/light_threshold_number.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/number/number.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class LightThresholdNumber : public number::Number, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   LightThresholdNumber() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void control(float value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | #include "max_distance_timeout_number.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void MaxDistanceTimeoutNumber::control(float value) { | ||||||
|  |   this->publish_state(value); | ||||||
|  |   this->parent_->set_basic_config(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/number/number.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class MaxDistanceTimeoutNumber : public number::Number, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   MaxDistanceTimeoutNumber() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void control(float value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										82
									
								
								esphome/components/ld2412/select/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								esphome/components/ld2412/select/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | 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_LIGHTBULB, | ||||||
|  |     ICON_RULER, | ||||||
|  |     ICON_SCALE, | ||||||
|  |     ICON_THERMOMETER, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from .. import CONF_LD2412_ID, LD2412_ns, LD2412Component | ||||||
|  |  | ||||||
|  | BaudRateSelect = LD2412_ns.class_("BaudRateSelect", select.Select) | ||||||
|  | DistanceResolutionSelect = LD2412_ns.class_("DistanceResolutionSelect", select.Select) | ||||||
|  | LightOutControlSelect = LD2412_ns.class_("LightOutControlSelect", select.Select) | ||||||
|  |  | ||||||
|  | CONF_DISTANCE_RESOLUTION = "distance_resolution" | ||||||
|  | CONF_LIGHT_FUNCTION = "light_function" | ||||||
|  | CONF_OUT_PIN_LEVEL = "out_pin_level" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = { | ||||||
|  |     cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component), | ||||||
|  |     cv.Optional(CONF_BAUD_RATE): select.select_schema( | ||||||
|  |         BaudRateSelect, | ||||||
|  |         entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |         icon=ICON_THERMOMETER, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_DISTANCE_RESOLUTION): select.select_schema( | ||||||
|  |         DistanceResolutionSelect, | ||||||
|  |         entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |         icon=ICON_RULER, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_LIGHT_FUNCTION): select.select_schema( | ||||||
|  |         LightOutControlSelect, | ||||||
|  |         entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |         icon=ICON_LIGHTBULB, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_OUT_PIN_LEVEL): select.select_schema( | ||||||
|  |         LightOutControlSelect, | ||||||
|  |         entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |         icon=ICON_SCALE, | ||||||
|  |     ), | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     LD2412_component = await cg.get_variable(config[CONF_LD2412_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_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_baud_rate_select(s)) | ||||||
|  |     if distance_resolution_config := config.get(CONF_DISTANCE_RESOLUTION): | ||||||
|  |         s = await select.new_select( | ||||||
|  |             distance_resolution_config, options=["0.2m", "0.5m", "0.75m"] | ||||||
|  |         ) | ||||||
|  |         await cg.register_parented(s, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_distance_resolution_select(s)) | ||||||
|  |     if light_function_config := config.get(CONF_LIGHT_FUNCTION): | ||||||
|  |         s = await select.new_select( | ||||||
|  |             light_function_config, options=["off", "below", "above"] | ||||||
|  |         ) | ||||||
|  |         await cg.register_parented(s, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_light_function_select(s)) | ||||||
|  |     if out_pin_level_config := config.get(CONF_OUT_PIN_LEVEL): | ||||||
|  |         s = await select.new_select(out_pin_level_config, options=["low", "high"]) | ||||||
|  |         await cg.register_parented(s, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_out_pin_level_select(s)) | ||||||
							
								
								
									
										12
									
								
								esphome/components/ld2412/select/baud_rate_select.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2412/select/baud_rate_select.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | #include "baud_rate_select.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void BaudRateSelect::control(const std::string &value) { | ||||||
|  |   this->publish_state(value); | ||||||
|  |   this->parent_->set_baud_rate(state); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2412/select/baud_rate_select.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2412/select/baud_rate_select.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/select/select.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class BaudRateSelect : public select::Select, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   BaudRateSelect() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void control(const std::string &value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | #include "distance_resolution_select.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void DistanceResolutionSelect::control(const std::string &value) { | ||||||
|  |   this->publish_state(value); | ||||||
|  |   this->parent_->set_distance_resolution(state); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/select/select.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class DistanceResolutionSelect : public select::Select, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   DistanceResolutionSelect() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void control(const std::string &value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | #include "light_out_control_select.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void LightOutControlSelect::control(const std::string &value) { | ||||||
|  |   this->publish_state(value); | ||||||
|  |   this->parent_->set_light_out_control(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2412/select/light_out_control_select.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2412/select/light_out_control_select.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/select/select.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class LightOutControlSelect : public select::Select, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   LightOutControlSelect() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void control(const std::string &value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										124
									
								
								esphome/components/ld2412/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								esphome/components/ld2412/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import sensor | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_LIGHT, | ||||||
|  |     CONF_MOVING_DISTANCE, | ||||||
|  |     DEVICE_CLASS_DISTANCE, | ||||||
|  |     DEVICE_CLASS_ILLUMINANCE, | ||||||
|  |     ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |     ICON_FLASH, | ||||||
|  |     ICON_LIGHTBULB, | ||||||
|  |     ICON_MOTION_SENSOR, | ||||||
|  |     ICON_SIGNAL, | ||||||
|  |     UNIT_CENTIMETER, | ||||||
|  |     UNIT_EMPTY, | ||||||
|  |     UNIT_PERCENT, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from . import CONF_LD2412_ID, LD2412Component | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["ld2412"] | ||||||
|  |  | ||||||
|  | CONF_DETECTION_DISTANCE = "detection_distance" | ||||||
|  | CONF_MOVE_ENERGY = "move_energy" | ||||||
|  | CONF_MOVING_ENERGY = "moving_energy" | ||||||
|  | CONF_STILL_DISTANCE = "still_distance" | ||||||
|  | CONF_STILL_ENERGY = "still_energy" | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component), | ||||||
|  |         cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema( | ||||||
|  |             device_class=DEVICE_CLASS_DISTANCE, | ||||||
|  |             filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], | ||||||
|  |             icon=ICON_SIGNAL, | ||||||
|  |             unit_of_measurement=UNIT_CENTIMETER, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_LIGHT): sensor.sensor_schema( | ||||||
|  |             device_class=DEVICE_CLASS_ILLUMINANCE, | ||||||
|  |             entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |             filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], | ||||||
|  |             icon=ICON_LIGHTBULB, | ||||||
|  |             unit_of_measurement=UNIT_EMPTY,  # No standard unit for this light sensor | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema( | ||||||
|  |             device_class=DEVICE_CLASS_DISTANCE, | ||||||
|  |             filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], | ||||||
|  |             icon=ICON_SIGNAL, | ||||||
|  |             unit_of_measurement=UNIT_CENTIMETER, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema( | ||||||
|  |             filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], | ||||||
|  |             icon=ICON_MOTION_SENSOR, | ||||||
|  |             unit_of_measurement=UNIT_PERCENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema( | ||||||
|  |             device_class=DEVICE_CLASS_DISTANCE, | ||||||
|  |             filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], | ||||||
|  |             icon=ICON_SIGNAL, | ||||||
|  |             unit_of_measurement=UNIT_CENTIMETER, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( | ||||||
|  |             filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], | ||||||
|  |             icon=ICON_FLASH, | ||||||
|  |             unit_of_measurement=UNIT_PERCENT, | ||||||
|  |         ), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = CONFIG_SCHEMA.extend( | ||||||
|  |     { | ||||||
|  |         cv.Optional(f"gate_{x}"): ( | ||||||
|  |             { | ||||||
|  |                 cv.Optional(CONF_MOVE_ENERGY): sensor.sensor_schema( | ||||||
|  |                     entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |                     filters=[ | ||||||
|  |                         {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)} | ||||||
|  |                     ], | ||||||
|  |                     icon=ICON_MOTION_SENSOR, | ||||||
|  |                     unit_of_measurement=UNIT_PERCENT, | ||||||
|  |                 ), | ||||||
|  |                 cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( | ||||||
|  |                     entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |                     filters=[ | ||||||
|  |                         {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)} | ||||||
|  |                     ], | ||||||
|  |                     icon=ICON_FLASH, | ||||||
|  |                     unit_of_measurement=UNIT_PERCENT, | ||||||
|  |                 ), | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |         for x in range(14) | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     LD2412_component = await cg.get_variable(config[CONF_LD2412_ID]) | ||||||
|  |     if detection_distance_config := config.get(CONF_DETECTION_DISTANCE): | ||||||
|  |         sens = await sensor.new_sensor(detection_distance_config) | ||||||
|  |         cg.add(LD2412_component.set_detection_distance_sensor(sens)) | ||||||
|  |     if light_config := config.get(CONF_LIGHT): | ||||||
|  |         sens = await sensor.new_sensor(light_config) | ||||||
|  |         cg.add(LD2412_component.set_light_sensor(sens)) | ||||||
|  |     if moving_distance_config := config.get(CONF_MOVING_DISTANCE): | ||||||
|  |         sens = await sensor.new_sensor(moving_distance_config) | ||||||
|  |         cg.add(LD2412_component.set_moving_target_distance_sensor(sens)) | ||||||
|  |     if moving_energy_config := config.get(CONF_MOVING_ENERGY): | ||||||
|  |         sens = await sensor.new_sensor(moving_energy_config) | ||||||
|  |         cg.add(LD2412_component.set_moving_target_energy_sensor(sens)) | ||||||
|  |     if still_distance_config := config.get(CONF_STILL_DISTANCE): | ||||||
|  |         sens = await sensor.new_sensor(still_distance_config) | ||||||
|  |         cg.add(LD2412_component.set_still_target_distance_sensor(sens)) | ||||||
|  |     if still_energy_config := config.get(CONF_STILL_ENERGY): | ||||||
|  |         sens = await sensor.new_sensor(still_energy_config) | ||||||
|  |         cg.add(LD2412_component.set_still_target_energy_sensor(sens)) | ||||||
|  |     for x in range(14): | ||||||
|  |         if gate_conf := config.get(f"gate_{x}"): | ||||||
|  |             if move_config := gate_conf.get(CONF_MOVE_ENERGY): | ||||||
|  |                 sens = await sensor.new_sensor(move_config) | ||||||
|  |                 cg.add(LD2412_component.set_gate_move_sensor(x, sens)) | ||||||
|  |             if still_config := gate_conf.get(CONF_STILL_ENERGY): | ||||||
|  |                 sens = await sensor.new_sensor(still_config) | ||||||
|  |                 cg.add(LD2412_component.set_gate_still_sensor(x, sens)) | ||||||
							
								
								
									
										45
									
								
								esphome/components/ld2412/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								esphome/components/ld2412/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 ( | ||||||
|  |     CONF_BLUETOOTH, | ||||||
|  |     DEVICE_CLASS_SWITCH, | ||||||
|  |     ENTITY_CATEGORY_CONFIG, | ||||||
|  |     ICON_BLUETOOTH, | ||||||
|  |     ICON_PULSE, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from .. import CONF_LD2412_ID, LD2412_ns, LD2412Component | ||||||
|  |  | ||||||
|  | BluetoothSwitch = LD2412_ns.class_("BluetoothSwitch", switch.Switch) | ||||||
|  | EngineeringModeSwitch = LD2412_ns.class_("EngineeringModeSwitch", switch.Switch) | ||||||
|  |  | ||||||
|  | CONF_ENGINEERING_MODE = "engineering_mode" | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = { | ||||||
|  |     cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component), | ||||||
|  |     cv.Optional(CONF_BLUETOOTH): switch.switch_schema( | ||||||
|  |         BluetoothSwitch, | ||||||
|  |         device_class=DEVICE_CLASS_SWITCH, | ||||||
|  |         entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |         icon=ICON_BLUETOOTH, | ||||||
|  |     ), | ||||||
|  |     cv.Optional(CONF_ENGINEERING_MODE): switch.switch_schema( | ||||||
|  |         EngineeringModeSwitch, | ||||||
|  |         device_class=DEVICE_CLASS_SWITCH, | ||||||
|  |         entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |         icon=ICON_PULSE, | ||||||
|  |     ), | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     LD2412_component = await cg.get_variable(config[CONF_LD2412_ID]) | ||||||
|  |     if bluetooth_config := config.get(CONF_BLUETOOTH): | ||||||
|  |         s = await switch.new_switch(bluetooth_config) | ||||||
|  |         await cg.register_parented(s, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_bluetooth_switch(s)) | ||||||
|  |     if engineering_mode_config := config.get(CONF_ENGINEERING_MODE): | ||||||
|  |         s = await switch.new_switch(engineering_mode_config) | ||||||
|  |         await cg.register_parented(s, config[CONF_LD2412_ID]) | ||||||
|  |         cg.add(LD2412_component.set_engineering_mode_switch(s)) | ||||||
							
								
								
									
										12
									
								
								esphome/components/ld2412/switch/bluetooth_switch.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2412/switch/bluetooth_switch.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | #include "bluetooth_switch.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void BluetoothSwitch::write_state(bool state) { | ||||||
|  |   this->publish_state(state); | ||||||
|  |   this->parent_->set_bluetooth(state); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2412/switch/bluetooth_switch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2412/switch/bluetooth_switch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/switch/switch.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class BluetoothSwitch : public switch_::Switch, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   BluetoothSwitch() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void write_state(bool state) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										12
									
								
								esphome/components/ld2412/switch/engineering_mode_switch.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2412/switch/engineering_mode_switch.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | #include "engineering_mode_switch.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | void EngineeringModeSwitch::write_state(bool state) { | ||||||
|  |   this->publish_state(state); | ||||||
|  |   this->parent_->set_engineering_mode(state); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										18
									
								
								esphome/components/ld2412/switch/engineering_mode_switch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2412/switch/engineering_mode_switch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/switch/switch.h" | ||||||
|  | #include "../ld2412.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ld2412 { | ||||||
|  |  | ||||||
|  | class EngineeringModeSwitch : public switch_::Switch, public Parented<LD2412Component> { | ||||||
|  |  public: | ||||||
|  |   EngineeringModeSwitch() = default; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void write_state(bool state) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ld2412 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										34
									
								
								esphome/components/ld2412/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								esphome/components/ld2412/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome.components import text_sensor | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_MAC_ADDRESS, | ||||||
|  |     CONF_VERSION, | ||||||
|  |     ENTITY_CATEGORY_DIAGNOSTIC, | ||||||
|  |     ICON_BLUETOOTH, | ||||||
|  |     ICON_CHIP, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from . import CONF_LD2412_ID, LD2412Component | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["ld2412"] | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = { | ||||||
|  |     cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component), | ||||||
|  |     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 | ||||||
|  |     ), | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     LD2412_component = await cg.get_variable(config[CONF_LD2412_ID]) | ||||||
|  |     if version_config := config.get(CONF_VERSION): | ||||||
|  |         sens = await text_sensor.new_text_sensor(version_config) | ||||||
|  |         cg.add(LD2412_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(LD2412_component.set_mac_text_sensor(sens)) | ||||||
							
								
								
									
										233
									
								
								tests/components/ld2412/common.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								tests/components/ld2412/common.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,233 @@ | |||||||
|  | uart: | ||||||
|  |   - id: uart_ld2412 | ||||||
|  |     tx_pin: ${tx_pin} | ||||||
|  |     rx_pin: ${rx_pin} | ||||||
|  |     baud_rate: 9600 | ||||||
|  |  | ||||||
|  | ld2412: | ||||||
|  |   id: my_ld2412 | ||||||
|  |  | ||||||
|  | binary_sensor: | ||||||
|  |   - platform: ld2412 | ||||||
|  |     dynamic_background_correction_status: | ||||||
|  |       name: Dynamic Background Correction Status | ||||||
|  |     has_target: | ||||||
|  |       name: Presence | ||||||
|  |     has_moving_target: | ||||||
|  |       name: Moving Target | ||||||
|  |     has_still_target: | ||||||
|  |       name: Still Target | ||||||
|  |  | ||||||
|  | button: | ||||||
|  |   - platform: ld2412 | ||||||
|  |     factory_reset: | ||||||
|  |       name: Factory reset | ||||||
|  |     restart: | ||||||
|  |       name: Restart | ||||||
|  |     query_params: | ||||||
|  |       name: Query params | ||||||
|  |     start_dynamic_background_correction: | ||||||
|  |       name: Start Dynamic Background Correction | ||||||
|  |  | ||||||
|  | number: | ||||||
|  |   - platform: ld2412 | ||||||
|  |     light_threshold: | ||||||
|  |       name: Light Threshold | ||||||
|  |     timeout: | ||||||
|  |       name: Presence timeout | ||||||
|  |     min_distance_gate: | ||||||
|  |       name: Minimum distance gate | ||||||
|  |     max_distance_gate: | ||||||
|  |       name: Maximum distance gate | ||||||
|  |     gate_0: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 0 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 0 Still Threshold | ||||||
|  |     gate_1: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 1 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 1 Still Threshold | ||||||
|  |     gate_2: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 2 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 2 Still Threshold | ||||||
|  |     gate_3: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 3 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 3 Still Threshold | ||||||
|  |     gate_4: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 4 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 4 Still Threshold | ||||||
|  |     gate_5: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 5 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 5 Still Threshold | ||||||
|  |     gate_6: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 6 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 6 Still Threshold | ||||||
|  |     gate_7: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 7 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 7 Still Threshold | ||||||
|  |     gate_8: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 8 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 8 Still Threshold | ||||||
|  |     gate_9: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 9 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 9 Still Threshold | ||||||
|  |     gate_10: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 10 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 10 Still Threshold | ||||||
|  |     gate_11: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 11 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 11 Still Threshold | ||||||
|  |     gate_12: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 12 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 12 Still Threshold | ||||||
|  |     gate_13: | ||||||
|  |       move_threshold: | ||||||
|  |         name: Gate 13 Move Threshold | ||||||
|  |       still_threshold: | ||||||
|  |         name: Gate 13 Still Threshold | ||||||
|  |  | ||||||
|  | select: | ||||||
|  |   - platform: ld2412 | ||||||
|  |     light_function: | ||||||
|  |       name: Light Function | ||||||
|  |     out_pin_level: | ||||||
|  |       name: Hardware output pin level | ||||||
|  |     distance_resolution: | ||||||
|  |       name: Distance resolution | ||||||
|  |     baud_rate: | ||||||
|  |       name: Baud rate | ||||||
|  |       on_value: | ||||||
|  |         - delay: 3s | ||||||
|  |         - lambda: |- | ||||||
|  |             id(uart_ld2412).flush(); | ||||||
|  |             uint32_t new_baud_rate = stoi(x); | ||||||
|  |             ESP_LOGD("change_baud_rate", "Changing baud rate from %i to %i",id(uart_ld2412).get_baud_rate(), new_baud_rate); | ||||||
|  |             if (id(uart_ld2412).get_baud_rate() != new_baud_rate) { | ||||||
|  |               id(uart_ld2412).set_baud_rate(new_baud_rate); | ||||||
|  |             #if defined(USE_ESP8266) || defined(USE_ESP32) | ||||||
|  |               id(uart_ld2412).load_settings(); | ||||||
|  |             #endif | ||||||
|  |             } | ||||||
|  |  | ||||||
|  | sensor: | ||||||
|  |   - platform: ld2412 | ||||||
|  |     light: | ||||||
|  |       name: Light | ||||||
|  |     moving_distance: | ||||||
|  |       name: Moving Distance | ||||||
|  |     still_distance: | ||||||
|  |       name: Still Distance | ||||||
|  |     moving_energy: | ||||||
|  |       name: Move Energy | ||||||
|  |     still_energy: | ||||||
|  |       name: Still Energy | ||||||
|  |     detection_distance: | ||||||
|  |       name: Detection Distance | ||||||
|  |     gate_0: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 0 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 0 Still Energy | ||||||
|  |     gate_1: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 1 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 1 Still Energy | ||||||
|  |     gate_2: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 2 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 2 Still Energy | ||||||
|  |     gate_3: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 3 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 3 Still Energy | ||||||
|  |     gate_4: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 4 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 4 Still Energy | ||||||
|  |     gate_5: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 5 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 5 Still Energy | ||||||
|  |     gate_6: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 6 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 6 Still Energy | ||||||
|  |     gate_7: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 7 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 7 Still Energy | ||||||
|  |     gate_8: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 8 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 8 Still Energy | ||||||
|  |     gate_9: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 9 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 9 Still Energy | ||||||
|  |     gate_10: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 10 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 10 Still Energy | ||||||
|  |     gate_11: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 11 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 11 Still Energy | ||||||
|  |     gate_12: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 12 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 12 Still Energy | ||||||
|  |     gate_13: | ||||||
|  |       move_energy: | ||||||
|  |         name: Gate 13 Move Energy | ||||||
|  |       still_energy: | ||||||
|  |         name: Gate 13 Still Energy | ||||||
|  |  | ||||||
|  | switch: | ||||||
|  |   - platform: ld2412 | ||||||
|  |     bluetooth: | ||||||
|  |       name: Bluetooth | ||||||
|  |     engineering_mode: | ||||||
|  |       name: Engineering Mode | ||||||
|  |  | ||||||
|  | text_sensor: | ||||||
|  |   - platform: ld2412 | ||||||
|  |     version: | ||||||
|  |       name: Firmware version | ||||||
|  |     mac_address: | ||||||
|  |       name: MAC address | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2412/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2412/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO17 | ||||||
|  |   rx_pin: GPIO16 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2412/test.esp32-c3-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2412/test.esp32-c3-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO4 | ||||||
|  |   rx_pin: GPIO5 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2412/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2412/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO4 | ||||||
|  |   rx_pin: GPIO5 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2412/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2412/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO17 | ||||||
|  |   rx_pin: GPIO16 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2412/test.esp8266-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2412/test.esp8266-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | substitutions: | ||||||
|  |   tx_pin: GPIO4 | ||||||
|  |   rx_pin: GPIO5 | ||||||
|  |  | ||||||
|  | <<: !include common.yaml | ||||||
							
								
								
									
										5
									
								
								tests/components/ld2412/test.rp2040-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/ld2412/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