mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +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/lcd_menu/* @numo68 | ||||
| esphome/components/ld2410/* @regevbr @sebcaps | ||||
| esphome/components/ld2412/* @Rihan9 | ||||
| esphome/components/ld2420/* @descipher | ||||
| esphome/components/ld2450/* @hareeshmu | ||||
| 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