esphome 2025.2.1__py3-none-any.whl → 2025.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. esphome/__main__.py +9 -1
  2. esphome/components/api/api_connection.cpp +426 -70
  3. esphome/components/api/api_connection.h +117 -25
  4. esphome/components/api/api_pb2.cpp +33 -0
  5. esphome/components/api/api_pb2.h +4 -0
  6. esphome/components/api/api_server.cpp +2 -2
  7. esphome/components/api/list_entities.cpp +76 -22
  8. esphome/components/api/list_entities.h +1 -0
  9. esphome/components/api/subscribe_state.h +2 -0
  10. esphome/components/audio/__init__.py +1 -1
  11. esphome/components/audio/audio_decoder.cpp +43 -11
  12. esphome/components/audio/audio_reader.cpp +9 -9
  13. esphome/components/audio/audio_reader.h +1 -1
  14. esphome/components/audio/audio_resampler.cpp +4 -2
  15. esphome/components/audio/audio_transfer_buffer.cpp +19 -9
  16. esphome/components/audio/audio_transfer_buffer.h +7 -2
  17. esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -0
  18. esphome/components/bmp085/bmp085.cpp +1 -1
  19. esphome/components/chsc6x/__init__.py +2 -0
  20. esphome/components/chsc6x/chsc6x_touchscreen.cpp +47 -0
  21. esphome/components/chsc6x/chsc6x_touchscreen.h +34 -0
  22. esphome/components/chsc6x/touchscreen.py +33 -0
  23. esphome/components/climate/__init__.py +0 -1
  24. esphome/components/cst816/binary_sensor/__init__.py +2 -25
  25. esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +3 -14
  26. esphome/components/cst816/touchscreen/cst816_touchscreen.h +0 -4
  27. esphome/components/esp32_ble_beacon/__init__.py +3 -1
  28. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +2 -2
  29. esphome/components/esp8266/gpio.py +1 -2
  30. esphome/components/font/__init__.py +198 -215
  31. esphome/components/font/font.cpp +4 -4
  32. esphome/components/font/font.h +1 -0
  33. esphome/components/graph/graph.cpp +4 -0
  34. esphome/components/graph/graph.h +4 -0
  35. esphome/components/haier/climate.py +11 -10
  36. esphome/components/hbridge/switch/hbridge_switch.cpp +2 -2
  37. esphome/components/heatpumpir/climate.py +2 -1
  38. esphome/components/heatpumpir/heatpumpir.cpp +1 -0
  39. esphome/components/heatpumpir/heatpumpir.h +1 -0
  40. esphome/components/i2c/__init__.py +6 -6
  41. esphome/components/i2c/i2c_bus_esp_idf.cpp +6 -2
  42. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  43. esphome/components/ili9xxx/display.py +1 -0
  44. esphome/components/ili9xxx/ili9xxx_display.h +5 -0
  45. esphome/components/ili9xxx/ili9xxx_init.h +59 -0
  46. esphome/components/ld2450/__init__.py +51 -0
  47. esphome/components/ld2450/binary_sensor.py +47 -0
  48. esphome/components/ld2450/button/__init__.py +45 -0
  49. esphome/components/ld2450/button/reset_button.cpp +9 -0
  50. esphome/components/ld2450/button/reset_button.h +18 -0
  51. esphome/components/ld2450/button/restart_button.cpp +9 -0
  52. esphome/components/ld2450/button/restart_button.h +18 -0
  53. esphome/components/ld2450/ld2450.cpp +876 -0
  54. esphome/components/ld2450/ld2450.h +234 -0
  55. esphome/components/ld2450/number/__init__.py +121 -0
  56. esphome/components/ld2450/number/presence_timeout_number.cpp +12 -0
  57. esphome/components/ld2450/number/presence_timeout_number.h +18 -0
  58. esphome/components/ld2450/number/zone_coordinate_number.cpp +14 -0
  59. esphome/components/ld2450/number/zone_coordinate_number.h +19 -0
  60. esphome/components/ld2450/select/__init__.py +56 -0
  61. esphome/components/ld2450/select/baud_rate_select.cpp +12 -0
  62. esphome/components/ld2450/select/baud_rate_select.h +18 -0
  63. esphome/components/ld2450/select/zone_type_select.cpp +12 -0
  64. esphome/components/ld2450/select/zone_type_select.h +18 -0
  65. esphome/components/ld2450/sensor.py +156 -0
  66. esphome/components/ld2450/switch/__init__.py +45 -0
  67. esphome/components/ld2450/switch/bluetooth_switch.cpp +12 -0
  68. esphome/components/ld2450/switch/bluetooth_switch.h +18 -0
  69. esphome/components/ld2450/switch/multi_target_switch.cpp +12 -0
  70. esphome/components/ld2450/switch/multi_target_switch.h +18 -0
  71. esphome/components/ld2450/text_sensor.py +62 -0
  72. esphome/components/ltr390/ltr390.cpp +7 -7
  73. esphome/components/ltr390/ltr390.h +0 -1
  74. esphome/components/lvgl/defines.py +0 -2
  75. esphome/components/lvgl/font.cpp +1 -1
  76. esphome/components/lvgl/lvgl_esphome.cpp +27 -19
  77. esphome/components/lvgl/widgets/img.py +1 -3
  78. esphome/components/mcp2515/mcp2515.cpp +1 -0
  79. esphome/components/mdns/__init__.py +1 -1
  80. esphome/components/mixer/speaker/mixer_speaker.cpp +6 -1
  81. esphome/components/mixer/speaker/mixer_speaker.h +2 -0
  82. esphome/components/mlx90393/sensor.py +53 -33
  83. esphome/components/mlx90393/sensor_mlx90393.cpp +4 -0
  84. esphome/components/mlx90393/sensor_mlx90393.h +8 -3
  85. esphome/components/mqtt/__init__.py +2 -2
  86. esphome/components/msa3xx/__init__.py +189 -0
  87. esphome/components/msa3xx/binary_sensor.py +40 -0
  88. esphome/components/msa3xx/msa3xx.cpp +417 -0
  89. esphome/components/msa3xx/msa3xx.h +311 -0
  90. esphome/components/msa3xx/sensor.py +42 -0
  91. esphome/components/msa3xx/text_sensor.py +38 -0
  92. esphome/components/nfc/binary_sensor/__init__.py +4 -4
  93. esphome/components/opentherm/binary_sensor/__init__.py +4 -4
  94. esphome/components/opentherm/generate.py +6 -6
  95. esphome/components/opentherm/sensor/__init__.py +5 -6
  96. esphome/components/packages/__init__.py +35 -11
  97. esphome/components/pn532/binary_sensor.py +4 -4
  98. esphome/components/rc522/binary_sensor.py +4 -4
  99. esphome/components/resampler/speaker/resampler_speaker.h +2 -0
  100. esphome/components/socket/bsd_sockets_impl.cpp +1 -0
  101. esphome/components/socket/lwip_sockets_impl.cpp +1 -0
  102. esphome/components/socket/socket.h +3 -1
  103. esphome/components/speaker/speaker.h +2 -2
  104. esphome/components/ssd1306_base/__init__.py +7 -7
  105. esphome/components/thermostat/climate.py +1 -1
  106. esphome/components/tmp1075/tmp1075.cpp +7 -11
  107. esphome/components/tmp1075/tmp1075.h +1 -2
  108. esphome/components/tormatic/__init__.py +1 -0
  109. esphome/components/tormatic/cover.py +47 -0
  110. esphome/components/tormatic/tormatic_cover.cpp +355 -0
  111. esphome/components/tormatic/tormatic_cover.h +60 -0
  112. esphome/components/tormatic/tormatic_protocol.h +211 -0
  113. esphome/components/touchscreen/binary_sensor/__init__.py +3 -0
  114. esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.cpp +7 -1
  115. esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h +3 -1
  116. esphome/components/touchscreen/touchscreen.cpp +3 -4
  117. esphome/components/udp/udp_component.h +4 -1
  118. esphome/components/web_server/list_entities.cpp +70 -66
  119. esphome/components/web_server/list_entities.h +43 -22
  120. esphome/components/web_server/web_server.cpp +345 -68
  121. esphome/components/web_server/web_server.h +138 -6
  122. esphome/components/web_server_base/__init__.py +1 -1
  123. esphome/components/web_server_idf/__init__.py +2 -0
  124. esphome/components/web_server_idf/web_server_idf.cpp +177 -30
  125. esphome/components/web_server_idf/web_server_idf.h +53 -4
  126. esphome/config_validation.py +23 -125
  127. esphome/const.py +5 -1
  128. esphome/core/config.py +15 -6
  129. esphome/core/defines.h +1 -1
  130. esphome/core/helpers.h +24 -3
  131. esphome/core/time.cpp +1 -0
  132. esphome/cpp_generator.py +3 -3
  133. esphome/dashboard/core.py +30 -21
  134. esphome/dashboard/dns.py +7 -1
  135. esphome/dashboard/entries.py +83 -16
  136. esphome/dashboard/settings.py +0 -4
  137. esphome/dashboard/status/mdns.py +43 -14
  138. esphome/dashboard/status/mqtt.py +22 -9
  139. esphome/dashboard/status/ping.py +54 -10
  140. esphome/dashboard/web_server.py +56 -24
  141. esphome/storage_json.py +4 -0
  142. esphome/wizard.py +13 -17
  143. esphome/writer.py +1 -3
  144. esphome/yaml_util.py +36 -33
  145. esphome/zeroconf.py +9 -21
  146. {esphome-2025.2.1.dist-info → esphome-2025.3.0.dist-info}/METADATA +7 -7
  147. {esphome-2025.2.1.dist-info → esphome-2025.3.0.dist-info}/RECORD +151 -111
  148. esphome/components/cst816/binary_sensor/cst816_button.h +0 -27
  149. {esphome-2025.2.1.dist-info → esphome-2025.3.0.dist-info}/LICENSE +0 -0
  150. {esphome-2025.2.1.dist-info → esphome-2025.3.0.dist-info}/WHEEL +0 -0
  151. {esphome-2025.2.1.dist-info → esphome-2025.3.0.dist-info}/entry_points.txt +0 -0
  152. {esphome-2025.2.1.dist-info → esphome-2025.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,876 @@
1
+ #include "ld2450.h"
2
+ #include <utility>
3
+ #ifdef USE_NUMBER
4
+ #include "esphome/components/number/number.h"
5
+ #endif
6
+ #ifdef USE_SENSOR
7
+ #include "esphome/components/sensor/sensor.h"
8
+ #endif
9
+ #include "esphome/core/component.h"
10
+
11
+ #define highbyte(val) (uint8_t)((val) >> 8)
12
+ #define lowbyte(val) (uint8_t)((val) &0xff)
13
+
14
+ namespace esphome {
15
+ namespace ld2450 {
16
+
17
+ static const char *const TAG = "ld2450";
18
+ static const char *const UNKNOWN_MAC("unknown");
19
+
20
+ // LD2450 UART Serial Commands
21
+ static const uint8_t CMD_ENABLE_CONF = 0x00FF;
22
+ static const uint8_t CMD_DISABLE_CONF = 0x00FE;
23
+ static const uint8_t CMD_VERSION = 0x00A0;
24
+ static const uint8_t CMD_MAC = 0x00A5;
25
+ static const uint8_t CMD_RESET = 0x00A2;
26
+ static const uint8_t CMD_RESTART = 0x00A3;
27
+ static const uint8_t CMD_BLUETOOTH = 0x00A4;
28
+ static const uint8_t CMD_SINGLE_TARGET_MODE = 0x0080;
29
+ static const uint8_t CMD_MULTI_TARGET_MODE = 0x0090;
30
+ static const uint8_t CMD_QUERY_TARGET_MODE = 0x0091;
31
+ static const uint8_t CMD_SET_BAUD_RATE = 0x00A1;
32
+ static const uint8_t CMD_QUERY_ZONE = 0x00C1;
33
+ static const uint8_t CMD_SET_ZONE = 0x00C2;
34
+
35
+ static inline uint16_t convert_seconds_to_ms(uint16_t value) { return value * 1000; };
36
+
37
+ static inline std::string convert_signed_int_to_hex(int value) {
38
+ auto value_as_str = str_snprintf("%04x", 4, value & 0xFFFF);
39
+ return value_as_str;
40
+ }
41
+
42
+ static inline void convert_int_values_to_hex(const int *values, uint8_t *bytes) {
43
+ for (int i = 0; i < 4; i++) {
44
+ std::string temp_hex = convert_signed_int_to_hex(values[i]);
45
+ bytes[i * 2] = std::stoi(temp_hex.substr(2, 2), nullptr, 16); // Store high byte
46
+ bytes[i * 2 + 1] = std::stoi(temp_hex.substr(0, 2), nullptr, 16); // Store low byte
47
+ }
48
+ }
49
+
50
+ static inline int16_t decode_coordinate(uint8_t low_byte, uint8_t high_byte) {
51
+ int16_t coordinate = (high_byte & 0x7F) << 8 | low_byte;
52
+ if ((high_byte & 0x80) == 0) {
53
+ coordinate = -coordinate;
54
+ }
55
+ return coordinate; // mm
56
+ }
57
+
58
+ static inline int16_t decode_speed(uint8_t low_byte, uint8_t high_byte) {
59
+ int16_t speed = (high_byte & 0x7F) << 8 | low_byte;
60
+ if ((high_byte & 0x80) == 0) {
61
+ speed = -speed;
62
+ }
63
+ return speed * 10; // mm/s
64
+ }
65
+
66
+ static inline int16_t hex_to_signed_int(const uint8_t *buffer, uint8_t offset) {
67
+ uint16_t hex_val = (buffer[offset + 1] << 8) | buffer[offset];
68
+ int16_t dec_val = static_cast<int16_t>(hex_val);
69
+ if (dec_val & 0x8000) {
70
+ dec_val -= 65536;
71
+ }
72
+ return dec_val;
73
+ }
74
+
75
+ static inline float calculate_angle(float base, float hypotenuse) {
76
+ if (base < 0.0 || hypotenuse <= 0.0) {
77
+ return 0.0;
78
+ }
79
+ float angle_radians = std::acos(base / hypotenuse);
80
+ float angle_degrees = angle_radians * (180.0 / M_PI);
81
+ return angle_degrees;
82
+ }
83
+
84
+ static inline std::string get_direction(int16_t speed) {
85
+ static const char *const APPROACHING = "Approaching";
86
+ static const char *const MOVING_AWAY = "Moving away";
87
+ static const char *const STATIONARY = "Stationary";
88
+
89
+ if (speed > 0) {
90
+ return MOVING_AWAY;
91
+ }
92
+ if (speed < 0) {
93
+ return APPROACHING;
94
+ }
95
+ return STATIONARY;
96
+ }
97
+
98
+ static inline std::string format_mac(uint8_t *buffer) {
99
+ return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14],
100
+ buffer[15]);
101
+ }
102
+
103
+ static inline std::string format_version(uint8_t *buffer) {
104
+ return str_sprintf("%u.%02X.%02X%02X%02X%02X", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15],
105
+ buffer[14]);
106
+ }
107
+
108
+ LD2450Component::LD2450Component() {}
109
+
110
+ void LD2450Component::setup() {
111
+ ESP_LOGCONFIG(TAG, "Setting up HLK-LD2450...");
112
+ #ifdef USE_NUMBER
113
+ if (this->presence_timeout_number_ != nullptr) {
114
+ this->pref_ = global_preferences->make_preference<float>(this->presence_timeout_number_->get_object_id_hash());
115
+ this->set_presence_timeout();
116
+ }
117
+ #endif
118
+ this->restart_and_read_all_info();
119
+ }
120
+
121
+ void LD2450Component::dump_config() {
122
+ ESP_LOGCONFIG(TAG, "HLK-LD2450 Human motion tracking radar module:");
123
+ #ifdef USE_BINARY_SENSOR
124
+ LOG_BINARY_SENSOR(" ", "TargetBinarySensor", this->target_binary_sensor_);
125
+ LOG_BINARY_SENSOR(" ", "MovingTargetBinarySensor", this->moving_target_binary_sensor_);
126
+ LOG_BINARY_SENSOR(" ", "StillTargetBinarySensor", this->still_target_binary_sensor_);
127
+ #endif
128
+ #ifdef USE_SWITCH
129
+ LOG_SWITCH(" ", "BluetoothSwitch", this->bluetooth_switch_);
130
+ LOG_SWITCH(" ", "MultiTargetSwitch", this->multi_target_switch_);
131
+ #endif
132
+ #ifdef USE_BUTTON
133
+ LOG_BUTTON(" ", "ResetButton", this->reset_button_);
134
+ LOG_BUTTON(" ", "RestartButton", this->restart_button_);
135
+ #endif
136
+ #ifdef USE_SENSOR
137
+ LOG_SENSOR(" ", "TargetCountSensor", this->target_count_sensor_);
138
+ LOG_SENSOR(" ", "StillTargetCountSensor", this->still_target_count_sensor_);
139
+ LOG_SENSOR(" ", "MovingTargetCountSensor", this->moving_target_count_sensor_);
140
+ for (sensor::Sensor *s : this->move_x_sensors_) {
141
+ LOG_SENSOR(" ", "NthTargetXSensor", s);
142
+ }
143
+ for (sensor::Sensor *s : this->move_y_sensors_) {
144
+ LOG_SENSOR(" ", "NthTargetYSensor", s);
145
+ }
146
+ for (sensor::Sensor *s : this->move_speed_sensors_) {
147
+ LOG_SENSOR(" ", "NthTargetSpeedSensor", s);
148
+ }
149
+ for (sensor::Sensor *s : this->move_angle_sensors_) {
150
+ LOG_SENSOR(" ", "NthTargetAngleSensor", s);
151
+ }
152
+ for (sensor::Sensor *s : this->move_distance_sensors_) {
153
+ LOG_SENSOR(" ", "NthTargetDistanceSensor", s);
154
+ }
155
+ for (sensor::Sensor *s : this->move_resolution_sensors_) {
156
+ LOG_SENSOR(" ", "NthTargetResolutionSensor", s);
157
+ }
158
+ for (sensor::Sensor *s : this->zone_target_count_sensors_) {
159
+ LOG_SENSOR(" ", "NthZoneTargetCountSensor", s);
160
+ }
161
+ for (sensor::Sensor *s : this->zone_still_target_count_sensors_) {
162
+ LOG_SENSOR(" ", "NthZoneStillTargetCountSensor", s);
163
+ }
164
+ for (sensor::Sensor *s : this->zone_moving_target_count_sensors_) {
165
+ LOG_SENSOR(" ", "NthZoneMovingTargetCountSensor", s);
166
+ }
167
+ #endif
168
+ #ifdef USE_TEXT_SENSOR
169
+ LOG_TEXT_SENSOR(" ", "VersionTextSensor", this->version_text_sensor_);
170
+ LOG_TEXT_SENSOR(" ", "MacTextSensor", this->mac_text_sensor_);
171
+ for (text_sensor::TextSensor *s : this->direction_text_sensors_) {
172
+ LOG_TEXT_SENSOR(" ", "NthDirectionTextSensor", s);
173
+ }
174
+ #endif
175
+ #ifdef USE_NUMBER
176
+ for (auto n : this->zone_numbers_) {
177
+ LOG_NUMBER(" ", "ZoneX1Number", n.x1);
178
+ LOG_NUMBER(" ", "ZoneY1Number", n.y1);
179
+ LOG_NUMBER(" ", "ZoneX2Number", n.x2);
180
+ LOG_NUMBER(" ", "ZoneY2Number", n.y2);
181
+ }
182
+ #endif
183
+ #ifdef USE_SELECT
184
+ LOG_SELECT(" ", "BaudRateSelect", this->baud_rate_select_);
185
+ LOG_SELECT(" ", "ZoneTypeSelect", this->zone_type_select_);
186
+ #endif
187
+ #ifdef USE_NUMBER
188
+ LOG_NUMBER(" ", "PresenceTimeoutNumber", this->presence_timeout_number_);
189
+ #endif
190
+ ESP_LOGCONFIG(TAG, " Throttle : %ums", this->throttle_);
191
+ ESP_LOGCONFIG(TAG, " MAC Address : %s", const_cast<char *>(this->mac_.c_str()));
192
+ ESP_LOGCONFIG(TAG, " Firmware version : %s", const_cast<char *>(this->version_.c_str()));
193
+ }
194
+
195
+ void LD2450Component::loop() {
196
+ while (this->available()) {
197
+ this->readline_(read(), this->buffer_data_, MAX_LINE_LENGTH);
198
+ }
199
+ }
200
+
201
+ // Count targets in zone
202
+ uint8_t LD2450Component::count_targets_in_zone_(const Zone &zone, bool is_moving) {
203
+ uint8_t count = 0;
204
+ for (auto &index : this->target_info_) {
205
+ if (index.x > zone.x1 && index.x < zone.x2 && index.y > zone.y1 && index.y < zone.y2 &&
206
+ index.is_moving == is_moving) {
207
+ count++;
208
+ }
209
+ }
210
+ return count;
211
+ }
212
+
213
+ // Service reset_radar_zone
214
+ void LD2450Component::reset_radar_zone() {
215
+ this->zone_type_ = 0;
216
+ for (auto &i : this->zone_config_) {
217
+ i.x1 = 0;
218
+ i.y1 = 0;
219
+ i.x2 = 0;
220
+ i.y2 = 0;
221
+ }
222
+ this->send_set_zone_command_();
223
+ }
224
+
225
+ void LD2450Component::set_radar_zone(int32_t zone_type, int32_t zone1_x1, int32_t zone1_y1, int32_t zone1_x2,
226
+ int32_t zone1_y2, int32_t zone2_x1, int32_t zone2_y1, int32_t zone2_x2,
227
+ int32_t zone2_y2, int32_t zone3_x1, int32_t zone3_y1, int32_t zone3_x2,
228
+ int32_t zone3_y2) {
229
+ this->zone_type_ = zone_type;
230
+ int zone_parameters[12] = {zone1_x1, zone1_y1, zone1_x2, zone1_y2, zone2_x1, zone2_y1,
231
+ zone2_x2, zone2_y2, zone3_x1, zone3_y1, zone3_x2, zone3_y2};
232
+ for (int i = 0; i < MAX_ZONES; i++) {
233
+ this->zone_config_[i].x1 = zone_parameters[i * 4];
234
+ this->zone_config_[i].y1 = zone_parameters[i * 4 + 1];
235
+ this->zone_config_[i].x2 = zone_parameters[i * 4 + 2];
236
+ this->zone_config_[i].y2 = zone_parameters[i * 4 + 3];
237
+ }
238
+ this->send_set_zone_command_();
239
+ }
240
+
241
+ // Set Zone on LD2450 Sensor
242
+ void LD2450Component::send_set_zone_command_() {
243
+ uint8_t cmd_value[26] = {};
244
+ uint8_t zone_type_bytes[2] = {static_cast<uint8_t>(this->zone_type_), 0x00};
245
+ uint8_t area_config[24] = {};
246
+ for (int i = 0; i < MAX_ZONES; i++) {
247
+ int values[4] = {this->zone_config_[i].x1, this->zone_config_[i].y1, this->zone_config_[i].x2,
248
+ this->zone_config_[i].y2};
249
+ ld2450::convert_int_values_to_hex(values, area_config + (i * 8));
250
+ }
251
+ std::memcpy(cmd_value, zone_type_bytes, 2);
252
+ std::memcpy(cmd_value + 2, area_config, 24);
253
+ this->set_config_mode_(true);
254
+ this->send_command_(CMD_SET_ZONE, cmd_value, 26);
255
+ this->set_config_mode_(false);
256
+ }
257
+
258
+ // Check presense timeout to reset presence status
259
+ bool LD2450Component::get_timeout_status_(uint32_t check_millis) {
260
+ if (check_millis == 0) {
261
+ return true;
262
+ }
263
+ if (this->timeout_ == 0) {
264
+ this->timeout_ = ld2450::convert_seconds_to_ms(DEFAULT_PRESENCE_TIMEOUT);
265
+ }
266
+ auto current_millis = millis();
267
+ return current_millis - check_millis >= this->timeout_;
268
+ }
269
+
270
+ // Extract, store and publish zone details LD2450 buffer
271
+ void LD2450Component::process_zone_(uint8_t *buffer) {
272
+ uint8_t index, start;
273
+ for (index = 0; index < MAX_ZONES; index++) {
274
+ start = 12 + index * 8;
275
+ this->zone_config_[index].x1 = ld2450::hex_to_signed_int(buffer, start);
276
+ this->zone_config_[index].y1 = ld2450::hex_to_signed_int(buffer, start + 2);
277
+ this->zone_config_[index].x2 = ld2450::hex_to_signed_int(buffer, start + 4);
278
+ this->zone_config_[index].y2 = ld2450::hex_to_signed_int(buffer, start + 6);
279
+ #ifdef USE_NUMBER
280
+ // only one null check as all coordinates are required for a single zone
281
+ if (this->zone_numbers_[index].x1 != nullptr) {
282
+ this->zone_numbers_[index].x1->publish_state(this->zone_config_[index].x1);
283
+ this->zone_numbers_[index].y1->publish_state(this->zone_config_[index].y1);
284
+ this->zone_numbers_[index].x2->publish_state(this->zone_config_[index].x2);
285
+ this->zone_numbers_[index].y2->publish_state(this->zone_config_[index].y2);
286
+ }
287
+ #endif
288
+ }
289
+ }
290
+
291
+ // Read all info from LD2450 buffer
292
+ void LD2450Component::read_all_info() {
293
+ this->set_config_mode_(true);
294
+ this->get_version_();
295
+ this->get_mac_();
296
+ this->query_target_tracking_mode_();
297
+ this->query_zone_();
298
+ this->set_config_mode_(false);
299
+ #ifdef USE_SELECT
300
+ const auto baud_rate = std::to_string(this->parent_->get_baud_rate());
301
+ if (this->baud_rate_select_ != nullptr && this->baud_rate_select_->state != baud_rate) {
302
+ this->baud_rate_select_->publish_state(baud_rate);
303
+ }
304
+ this->publish_zone_type();
305
+ #endif
306
+ }
307
+
308
+ // Read zone info from LD2450 buffer
309
+ void LD2450Component::query_zone_info() {
310
+ this->set_config_mode_(true);
311
+ this->query_zone_();
312
+ this->set_config_mode_(false);
313
+ }
314
+
315
+ // Restart LD2450 and read all info from buffer
316
+ void LD2450Component::restart_and_read_all_info() {
317
+ this->set_config_mode_(true);
318
+ this->restart_();
319
+ this->set_timeout(1500, [this]() { this->read_all_info(); });
320
+ }
321
+
322
+ // Send command with values to LD2450
323
+ void LD2450Component::send_command_(uint8_t command, const uint8_t *command_value, uint8_t command_value_len) {
324
+ ESP_LOGV(TAG, "Sending command %02X", command);
325
+ // frame header
326
+ this->write_array(CMD_FRAME_HEADER, 4);
327
+ // length bytes
328
+ int len = 2;
329
+ if (command_value != nullptr) {
330
+ len += command_value_len;
331
+ }
332
+ this->write_byte(lowbyte(len));
333
+ this->write_byte(highbyte(len));
334
+ // command
335
+ this->write_byte(lowbyte(command));
336
+ this->write_byte(highbyte(command));
337
+ // command value bytes
338
+ if (command_value != nullptr) {
339
+ for (int i = 0; i < command_value_len; i++) {
340
+ this->write_byte(command_value[i]);
341
+ }
342
+ }
343
+ // footer
344
+ this->write_array(CMD_FRAME_END, 4);
345
+ // FIXME to remove
346
+ delay(50); // NOLINT
347
+ }
348
+
349
+ // LD2450 Radar data message:
350
+ // [AA FF 03 00] [0E 03 B1 86 10 00 40 01] [00 00 00 00 00 00 00 00] [00 00 00 00 00 00 00 00] [55 CC]
351
+ // Header Target 1 Target 2 Target 3 End
352
+ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
353
+ if (len < 29) { // header (4 bytes) + 8 x 3 target data + footer (2 bytes)
354
+ ESP_LOGE(TAG, "Periodic data: invalid message length");
355
+ return;
356
+ }
357
+ if (buffer[0] != 0xAA || buffer[1] != 0xFF || buffer[2] != 0x03 || buffer[3] != 0x00) { // header
358
+ ESP_LOGE(TAG, "Periodic data: invalid message header");
359
+ return;
360
+ }
361
+ if (buffer[len - 2] != 0x55 || buffer[len - 1] != 0xCC) { // footer
362
+ ESP_LOGE(TAG, "Periodic data: invalid message footer");
363
+ return;
364
+ }
365
+
366
+ auto current_millis = millis();
367
+ if (current_millis - this->last_periodic_millis_ < this->throttle_) {
368
+ ESP_LOGV(TAG, "Throttling: %d", this->throttle_);
369
+ return;
370
+ }
371
+
372
+ this->last_periodic_millis_ = current_millis;
373
+
374
+ int16_t target_count = 0;
375
+ int16_t still_target_count = 0;
376
+ int16_t moving_target_count = 0;
377
+ int16_t start = 0;
378
+ int16_t val = 0;
379
+ uint8_t index = 0;
380
+ int16_t tx = 0;
381
+ int16_t ty = 0;
382
+ int16_t td = 0;
383
+ int16_t ts = 0;
384
+ int16_t angle = 0;
385
+ std::string direction{};
386
+ bool is_moving = false;
387
+
388
+ #if defined(USE_BINARY_SENSOR) || defined(USE_SENSOR) || defined(USE_TEXT_SENSOR)
389
+ // Loop thru targets
390
+ for (index = 0; index < MAX_TARGETS; index++) {
391
+ #ifdef USE_SENSOR
392
+ // X
393
+ start = TARGET_X + index * 8;
394
+ is_moving = false;
395
+ sensor::Sensor *sx = this->move_x_sensors_[index];
396
+ if (sx != nullptr) {
397
+ val = ld2450::decode_coordinate(buffer[start], buffer[start + 1]);
398
+ tx = val;
399
+ sx->publish_state(val);
400
+ }
401
+ // Y
402
+ start = TARGET_Y + index * 8;
403
+ sensor::Sensor *sy = this->move_y_sensors_[index];
404
+ if (sy != nullptr) {
405
+ val = ld2450::decode_coordinate(buffer[start], buffer[start + 1]);
406
+ ty = val;
407
+ sy->publish_state(val);
408
+ }
409
+ // RESOLUTION
410
+ start = TARGET_RESOLUTION + index * 8;
411
+ sensor::Sensor *sr = this->move_resolution_sensors_[index];
412
+ if (sr != nullptr) {
413
+ val = (buffer[start + 1] << 8) | buffer[start];
414
+ sr->publish_state(val);
415
+ }
416
+ #endif
417
+ // SPEED
418
+ start = TARGET_SPEED + index * 8;
419
+ val = ld2450::decode_speed(buffer[start], buffer[start + 1]);
420
+ ts = val;
421
+ if (val) {
422
+ is_moving = true;
423
+ moving_target_count++;
424
+ }
425
+ #ifdef USE_SENSOR
426
+ sensor::Sensor *ss = this->move_speed_sensors_[index];
427
+ if (ss != nullptr) {
428
+ ss->publish_state(val);
429
+ }
430
+ #endif
431
+ // DISTANCE
432
+ val = (uint16_t) sqrt(
433
+ pow(ld2450::decode_coordinate(buffer[TARGET_X + index * 8], buffer[(TARGET_X + index * 8) + 1]), 2) +
434
+ pow(ld2450::decode_coordinate(buffer[TARGET_Y + index * 8], buffer[(TARGET_Y + index * 8) + 1]), 2));
435
+ td = val;
436
+ if (val > 0) {
437
+ target_count++;
438
+ }
439
+ #ifdef USE_SENSOR
440
+ sensor::Sensor *sd = this->move_distance_sensors_[index];
441
+ if (sd != nullptr) {
442
+ sd->publish_state(val);
443
+ }
444
+ // ANGLE
445
+ angle = calculate_angle(static_cast<float>(ty), static_cast<float>(td));
446
+ if (tx > 0) {
447
+ angle = angle * -1;
448
+ }
449
+ sensor::Sensor *sa = this->move_angle_sensors_[index];
450
+ if (sa != nullptr) {
451
+ sa->publish_state(angle);
452
+ }
453
+ #endif
454
+ #ifdef USE_TEXT_SENSOR
455
+ // DIRECTION
456
+ direction = get_direction(ts);
457
+ if (td == 0) {
458
+ direction = "NA";
459
+ }
460
+ text_sensor::TextSensor *tsd = this->direction_text_sensors_[index];
461
+ if (tsd != nullptr) {
462
+ tsd->publish_state(direction);
463
+ }
464
+ #endif
465
+
466
+ // Store target info for zone target count
467
+ this->target_info_[index].x = tx;
468
+ this->target_info_[index].y = ty;
469
+ this->target_info_[index].is_moving = is_moving;
470
+
471
+ } // End loop thru targets
472
+
473
+ still_target_count = target_count - moving_target_count;
474
+ #endif
475
+
476
+ #ifdef USE_SENSOR
477
+ // Loop thru zones
478
+ uint8_t zone_still_targets = 0;
479
+ uint8_t zone_moving_targets = 0;
480
+ uint8_t zone_all_targets = 0;
481
+ for (index = 0; index < MAX_ZONES; index++) {
482
+ zone_still_targets = this->count_targets_in_zone_(this->zone_config_[index], false);
483
+ zone_moving_targets = this->count_targets_in_zone_(this->zone_config_[index], true);
484
+ zone_all_targets = zone_still_targets + zone_moving_targets;
485
+
486
+ // Publish Still Target Count in Zones
487
+ sensor::Sensor *szstc = this->zone_still_target_count_sensors_[index];
488
+ if (szstc != nullptr) {
489
+ szstc->publish_state(zone_still_targets);
490
+ }
491
+ // Publish Moving Target Count in Zones
492
+ sensor::Sensor *szmtc = this->zone_moving_target_count_sensors_[index];
493
+ if (szmtc != nullptr) {
494
+ szmtc->publish_state(zone_moving_targets);
495
+ }
496
+ // Publish All Target Count in Zones
497
+ sensor::Sensor *sztc = this->zone_target_count_sensors_[index];
498
+ if (sztc != nullptr) {
499
+ sztc->publish_state(zone_all_targets);
500
+ }
501
+
502
+ } // End loop thru zones
503
+
504
+ // Target Count
505
+ if (this->target_count_sensor_ != nullptr) {
506
+ this->target_count_sensor_->publish_state(target_count);
507
+ }
508
+ // Still Target Count
509
+ if (this->still_target_count_sensor_ != nullptr) {
510
+ this->still_target_count_sensor_->publish_state(still_target_count);
511
+ }
512
+ // Moving Target Count
513
+ if (this->moving_target_count_sensor_ != nullptr) {
514
+ this->moving_target_count_sensor_->publish_state(moving_target_count);
515
+ }
516
+ #endif
517
+
518
+ #ifdef USE_BINARY_SENSOR
519
+ // Target Presence
520
+ if (this->target_binary_sensor_ != nullptr) {
521
+ if (target_count > 0) {
522
+ this->target_binary_sensor_->publish_state(true);
523
+ } else {
524
+ if (this->get_timeout_status_(this->presence_millis_)) {
525
+ this->target_binary_sensor_->publish_state(false);
526
+ } else {
527
+ ESP_LOGV(TAG, "Clear presence waiting timeout: %d", this->timeout_);
528
+ }
529
+ }
530
+ }
531
+ // Moving Target Presence
532
+ if (this->moving_target_binary_sensor_ != nullptr) {
533
+ if (moving_target_count > 0) {
534
+ this->moving_target_binary_sensor_->publish_state(true);
535
+ } else {
536
+ if (this->get_timeout_status_(this->moving_presence_millis_)) {
537
+ this->moving_target_binary_sensor_->publish_state(false);
538
+ }
539
+ }
540
+ }
541
+ // Still Target Presence
542
+ if (this->still_target_binary_sensor_ != nullptr) {
543
+ if (still_target_count > 0) {
544
+ this->still_target_binary_sensor_->publish_state(true);
545
+ } else {
546
+ if (this->get_timeout_status_(this->still_presence_millis_)) {
547
+ this->still_target_binary_sensor_->publish_state(false);
548
+ }
549
+ }
550
+ }
551
+ #endif
552
+ #ifdef USE_SENSOR
553
+ // For presence timeout check
554
+ if (target_count > 0) {
555
+ this->presence_millis_ = millis();
556
+ }
557
+ if (moving_target_count > 0) {
558
+ this->moving_presence_millis_ = millis();
559
+ }
560
+ if (still_target_count > 0) {
561
+ this->still_presence_millis_ = millis();
562
+ }
563
+ #endif
564
+ }
565
+
566
+ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
567
+ ESP_LOGV(TAG, "Handling ack data for command %02X", buffer[COMMAND]);
568
+ if (len < 10) {
569
+ ESP_LOGE(TAG, "Ack data: invalid length");
570
+ return true;
571
+ }
572
+ if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // frame header
573
+ ESP_LOGE(TAG, "Ack data: invalid header (command %02X)", buffer[COMMAND]);
574
+ return true;
575
+ }
576
+ if (buffer[COMMAND_STATUS] != 0x01) {
577
+ ESP_LOGE(TAG, "Ack data: invalid status");
578
+ return true;
579
+ }
580
+ if (buffer[8] || buffer[9]) {
581
+ ESP_LOGE(TAG, "Ack data: last buffer was %u, %u", buffer[8], buffer[9]);
582
+ return true;
583
+ }
584
+
585
+ switch (buffer[COMMAND]) {
586
+ case lowbyte(CMD_ENABLE_CONF):
587
+ ESP_LOGV(TAG, "Got enable conf command");
588
+ break;
589
+ case lowbyte(CMD_DISABLE_CONF):
590
+ ESP_LOGV(TAG, "Got disable conf command");
591
+ break;
592
+ case lowbyte(CMD_SET_BAUD_RATE):
593
+ ESP_LOGV(TAG, "Got baud rate change command");
594
+ #ifdef USE_SELECT
595
+ if (this->baud_rate_select_ != nullptr) {
596
+ ESP_LOGV(TAG, "Change baud rate to %s", this->baud_rate_select_->state.c_str());
597
+ }
598
+ #endif
599
+ break;
600
+ case lowbyte(CMD_VERSION):
601
+ this->version_ = ld2450::format_version(buffer);
602
+ ESP_LOGV(TAG, "Firmware version: %s", this->version_.c_str());
603
+ #ifdef USE_TEXT_SENSOR
604
+ if (this->version_text_sensor_ != nullptr) {
605
+ this->version_text_sensor_->publish_state(this->version_);
606
+ }
607
+ #endif
608
+ break;
609
+ case lowbyte(CMD_MAC):
610
+ if (len < 20) {
611
+ return false;
612
+ }
613
+ this->mac_ = ld2450::format_mac(buffer);
614
+ ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str());
615
+ #ifdef USE_TEXT_SENSOR
616
+ if (this->mac_text_sensor_ != nullptr) {
617
+ this->mac_text_sensor_->publish_state(this->mac_);
618
+ }
619
+ #endif
620
+ #ifdef USE_SWITCH
621
+ if (this->bluetooth_switch_ != nullptr) {
622
+ this->bluetooth_switch_->publish_state(this->mac_ != UNKNOWN_MAC);
623
+ }
624
+ #endif
625
+ break;
626
+ case lowbyte(CMD_BLUETOOTH):
627
+ ESP_LOGV(TAG, "Got Bluetooth command");
628
+ break;
629
+ case lowbyte(CMD_SINGLE_TARGET_MODE):
630
+ ESP_LOGV(TAG, "Got single target conf command");
631
+ #ifdef USE_SWITCH
632
+ if (this->multi_target_switch_ != nullptr) {
633
+ this->multi_target_switch_->publish_state(false);
634
+ }
635
+ #endif
636
+ break;
637
+ case lowbyte(CMD_MULTI_TARGET_MODE):
638
+ ESP_LOGV(TAG, "Got multi target conf command");
639
+ #ifdef USE_SWITCH
640
+ if (this->multi_target_switch_ != nullptr) {
641
+ this->multi_target_switch_->publish_state(true);
642
+ }
643
+ #endif
644
+ break;
645
+ case lowbyte(CMD_QUERY_TARGET_MODE):
646
+ ESP_LOGV(TAG, "Got query target tracking mode command");
647
+ #ifdef USE_SWITCH
648
+ if (this->multi_target_switch_ != nullptr) {
649
+ this->multi_target_switch_->publish_state(buffer[10] == 0x02);
650
+ }
651
+ #endif
652
+ break;
653
+ case lowbyte(CMD_QUERY_ZONE):
654
+ ESP_LOGV(TAG, "Got query zone conf command");
655
+ this->zone_type_ = std::stoi(std::to_string(buffer[10]), nullptr, 16);
656
+ this->publish_zone_type();
657
+ #ifdef USE_SELECT
658
+ if (this->zone_type_select_ != nullptr) {
659
+ ESP_LOGV(TAG, "Change zone type to: %s", this->zone_type_select_->state.c_str());
660
+ }
661
+ #endif
662
+ if (buffer[10] == 0x00) {
663
+ ESP_LOGV(TAG, "Zone: Disabled");
664
+ }
665
+ if (buffer[10] == 0x01) {
666
+ ESP_LOGV(TAG, "Zone: Area detection");
667
+ }
668
+ if (buffer[10] == 0x02) {
669
+ ESP_LOGV(TAG, "Zone: Area filter");
670
+ }
671
+ this->process_zone_(buffer);
672
+ break;
673
+ case lowbyte(CMD_SET_ZONE):
674
+ ESP_LOGV(TAG, "Got set zone conf command");
675
+ this->query_zone_info();
676
+ break;
677
+ default:
678
+ break;
679
+ }
680
+ return true;
681
+ }
682
+
683
+ // Read LD2450 buffer data
684
+ void LD2450Component::readline_(int readch, uint8_t *buffer, uint8_t len) {
685
+ if (readch < 0) {
686
+ return;
687
+ }
688
+ if (this->buffer_pos_ < len - 1) {
689
+ buffer[this->buffer_pos_++] = readch;
690
+ buffer[this->buffer_pos_] = 0;
691
+ } else {
692
+ this->buffer_pos_ = 0;
693
+ }
694
+ if (this->buffer_pos_ < 4) {
695
+ return;
696
+ }
697
+ if (buffer[this->buffer_pos_ - 2] == 0x55 && buffer[this->buffer_pos_ - 1] == 0xCC) {
698
+ ESP_LOGV(TAG, "Handle periodic radar data");
699
+ this->handle_periodic_data_(buffer, this->buffer_pos_);
700
+ this->buffer_pos_ = 0; // Reset position index for next frame
701
+ } else if (buffer[this->buffer_pos_ - 4] == 0x04 && buffer[this->buffer_pos_ - 3] == 0x03 &&
702
+ buffer[this->buffer_pos_ - 2] == 0x02 && buffer[this->buffer_pos_ - 1] == 0x01) {
703
+ ESP_LOGV(TAG, "Handle command ack data");
704
+ if (this->handle_ack_data_(buffer, this->buffer_pos_)) {
705
+ this->buffer_pos_ = 0; // Reset position index for next frame
706
+ } else {
707
+ ESP_LOGV(TAG, "Command ack data invalid");
708
+ }
709
+ }
710
+ }
711
+
712
+ // Set Config Mode - Pre-requisite sending commands
713
+ void LD2450Component::set_config_mode_(bool enable) {
714
+ uint8_t cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
715
+ uint8_t cmd_value[2] = {0x01, 0x00};
716
+ this->send_command_(cmd, enable ? cmd_value : nullptr, 2);
717
+ }
718
+
719
+ // Set Bluetooth Enable/Disable
720
+ void LD2450Component::set_bluetooth(bool enable) {
721
+ this->set_config_mode_(true);
722
+ uint8_t enable_cmd_value[2] = {0x01, 0x00};
723
+ uint8_t disable_cmd_value[2] = {0x00, 0x00};
724
+ this->send_command_(CMD_BLUETOOTH, enable ? enable_cmd_value : disable_cmd_value, 2);
725
+ this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
726
+ }
727
+
728
+ // Set Baud rate
729
+ void LD2450Component::set_baud_rate(const std::string &state) {
730
+ this->set_config_mode_(true);
731
+ uint8_t cmd_value[2] = {BAUD_RATE_ENUM_TO_INT.at(state), 0x00};
732
+ this->send_command_(CMD_SET_BAUD_RATE, cmd_value, 2);
733
+ this->set_timeout(200, [this]() { this->restart_(); });
734
+ }
735
+
736
+ // Set Zone Type - one of: Disabled, Detection, Filter
737
+ void LD2450Component::set_zone_type(const std::string &state) {
738
+ ESP_LOGV(TAG, "Set zone type: %s", state.c_str());
739
+ uint8_t zone_type = ZONE_TYPE_ENUM_TO_INT.at(state);
740
+ this->zone_type_ = zone_type;
741
+ this->send_set_zone_command_();
742
+ }
743
+
744
+ // Publish Zone Type to Select component
745
+ void LD2450Component::publish_zone_type() {
746
+ #ifdef USE_SELECT
747
+ std::string zone_type = ZONE_TYPE_INT_TO_ENUM.at(static_cast<ZoneTypeStructure>(this->zone_type_));
748
+ if (this->zone_type_select_ != nullptr) {
749
+ this->zone_type_select_->publish_state(zone_type);
750
+ }
751
+ #endif
752
+ }
753
+
754
+ // Set Single/Multiplayer target detection
755
+ void LD2450Component::set_multi_target(bool enable) {
756
+ this->set_config_mode_(true);
757
+ uint8_t cmd = enable ? CMD_MULTI_TARGET_MODE : CMD_SINGLE_TARGET_MODE;
758
+ this->send_command_(cmd, nullptr, 0);
759
+ this->set_config_mode_(false);
760
+ }
761
+
762
+ // LD2450 factory reset
763
+ void LD2450Component::factory_reset() {
764
+ this->set_config_mode_(true);
765
+ this->send_command_(CMD_RESET, nullptr, 0);
766
+ this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
767
+ }
768
+
769
+ // Restart LD2450 module
770
+ void LD2450Component::restart_() { this->send_command_(CMD_RESTART, nullptr, 0); }
771
+
772
+ // Get LD2450 firmware version
773
+ void LD2450Component::get_version_() { this->send_command_(CMD_VERSION, nullptr, 0); }
774
+
775
+ // Get LD2450 mac address
776
+ void LD2450Component::get_mac_() {
777
+ uint8_t cmd_value[2] = {0x01, 0x00};
778
+ this->send_command_(CMD_MAC, cmd_value, 2);
779
+ }
780
+
781
+ // Query for target tracking mode
782
+ void LD2450Component::query_target_tracking_mode_() { this->send_command_(CMD_QUERY_TARGET_MODE, nullptr, 0); }
783
+
784
+ // Query for zone info
785
+ void LD2450Component::query_zone_() { this->send_command_(CMD_QUERY_ZONE, nullptr, 0); }
786
+
787
+ #ifdef USE_SENSOR
788
+ void LD2450Component::set_move_x_sensor(uint8_t target, sensor::Sensor *s) { this->move_x_sensors_[target] = s; }
789
+ void LD2450Component::set_move_y_sensor(uint8_t target, sensor::Sensor *s) { this->move_y_sensors_[target] = s; }
790
+ void LD2450Component::set_move_speed_sensor(uint8_t target, sensor::Sensor *s) {
791
+ this->move_speed_sensors_[target] = s;
792
+ }
793
+ void LD2450Component::set_move_angle_sensor(uint8_t target, sensor::Sensor *s) {
794
+ this->move_angle_sensors_[target] = s;
795
+ }
796
+ void LD2450Component::set_move_distance_sensor(uint8_t target, sensor::Sensor *s) {
797
+ this->move_distance_sensors_[target] = s;
798
+ }
799
+ void LD2450Component::set_move_resolution_sensor(uint8_t target, sensor::Sensor *s) {
800
+ this->move_resolution_sensors_[target] = s;
801
+ }
802
+ void LD2450Component::set_zone_target_count_sensor(uint8_t zone, sensor::Sensor *s) {
803
+ this->zone_target_count_sensors_[zone] = s;
804
+ }
805
+ void LD2450Component::set_zone_still_target_count_sensor(uint8_t zone, sensor::Sensor *s) {
806
+ this->zone_still_target_count_sensors_[zone] = s;
807
+ }
808
+ void LD2450Component::set_zone_moving_target_count_sensor(uint8_t zone, sensor::Sensor *s) {
809
+ this->zone_moving_target_count_sensors_[zone] = s;
810
+ }
811
+ #endif
812
+ #ifdef USE_TEXT_SENSOR
813
+ void LD2450Component::set_direction_text_sensor(uint8_t target, text_sensor::TextSensor *s) {
814
+ this->direction_text_sensors_[target] = s;
815
+ }
816
+ #endif
817
+
818
+ // Send Zone coordinates data to LD2450
819
+ #ifdef USE_NUMBER
820
+ void LD2450Component::set_zone_coordinate(uint8_t zone) {
821
+ number::Number *x1sens = this->zone_numbers_[zone].x1;
822
+ number::Number *y1sens = this->zone_numbers_[zone].y1;
823
+ number::Number *x2sens = this->zone_numbers_[zone].x2;
824
+ number::Number *y2sens = this->zone_numbers_[zone].y2;
825
+ if (!x1sens->has_state() || !y1sens->has_state() || !x2sens->has_state() || !y2sens->has_state()) {
826
+ return;
827
+ }
828
+ this->zone_config_[zone].x1 = static_cast<int>(x1sens->state);
829
+ this->zone_config_[zone].y1 = static_cast<int>(y1sens->state);
830
+ this->zone_config_[zone].x2 = static_cast<int>(x2sens->state);
831
+ this->zone_config_[zone].y2 = static_cast<int>(y2sens->state);
832
+ this->send_set_zone_command_();
833
+ }
834
+
835
+ void LD2450Component::set_zone_numbers(uint8_t zone, number::Number *x1, number::Number *y1, number::Number *x2,
836
+ number::Number *y2) {
837
+ if (zone < MAX_ZONES) {
838
+ this->zone_numbers_[zone].x1 = x1;
839
+ this->zone_numbers_[zone].y1 = y1;
840
+ this->zone_numbers_[zone].x2 = x2;
841
+ this->zone_numbers_[zone].y2 = y2;
842
+ }
843
+ }
844
+ #endif
845
+
846
+ // Set Presence Timeout load and save from flash
847
+ #ifdef USE_NUMBER
848
+ void LD2450Component::set_presence_timeout() {
849
+ if (this->presence_timeout_number_ != nullptr) {
850
+ if (this->presence_timeout_number_->state == 0) {
851
+ float timeout = this->restore_from_flash_();
852
+ this->presence_timeout_number_->publish_state(timeout);
853
+ this->timeout_ = ld2450::convert_seconds_to_ms(timeout);
854
+ }
855
+ if (this->presence_timeout_number_->has_state()) {
856
+ this->save_to_flash_(this->presence_timeout_number_->state);
857
+ this->timeout_ = ld2450::convert_seconds_to_ms(this->presence_timeout_number_->state);
858
+ }
859
+ }
860
+ }
861
+
862
+ // Save Presence Timeout to flash
863
+ void LD2450Component::save_to_flash_(float value) { this->pref_.save(&value); }
864
+
865
+ // Load Presence Timeout from flash
866
+ float LD2450Component::restore_from_flash_() {
867
+ float value;
868
+ if (!this->pref_.load(&value)) {
869
+ value = DEFAULT_PRESENCE_TIMEOUT;
870
+ }
871
+ return value;
872
+ }
873
+ #endif
874
+
875
+ } // namespace ld2450
876
+ } // namespace esphome