esphome 2024.11.3__py3-none-any.whl → 2024.12.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 (183) hide show
  1. esphome/components/adc/adc_sensor.h +7 -8
  2. esphome/components/adc/adc_sensor_common.cpp +24 -0
  3. esphome/components/adc/{adc_sensor.cpp → adc_sensor_esp32.cpp} +10 -179
  4. esphome/components/adc/adc_sensor_esp8266.cpp +58 -0
  5. esphome/components/adc/adc_sensor_libretiny.cpp +48 -0
  6. esphome/components/adc/adc_sensor_rp2040.cpp +93 -0
  7. esphome/components/alarm_control_panel/alarm_control_panel_call.cpp +3 -4
  8. esphome/components/animation/__init__.py +1 -2
  9. esphome/components/apds9306/apds9306.cpp +2 -1
  10. esphome/components/audio/audio.h +1 -1
  11. esphome/components/bk72xx/__init__.py +1 -1
  12. esphome/components/cse7766/cse7766.cpp +1 -1
  13. esphome/components/deep_sleep/deep_sleep_esp32.cpp +2 -2
  14. esphome/components/dht/dht.cpp +2 -1
  15. esphome/components/display/display.cpp +10 -6
  16. esphome/components/display/display.h +14 -0
  17. esphome/components/display_menu_base/__init__.py +0 -2
  18. esphome/components/display_menu_base/display_menu_base.cpp +1 -1
  19. esphome/components/dsmr/dsmr.cpp +1 -1
  20. esphome/components/esp32/__init__.py +94 -12
  21. esphome/components/esp32/boards.py +222 -14
  22. esphome/components/esp32_ble/__init__.py +22 -2
  23. esphome/components/esp32_ble/ble.cpp +39 -12
  24. esphome/components/esp32_ble/ble.h +2 -0
  25. esphome/components/esp32_ble/ble_advertising.cpp +1 -1
  26. esphome/components/esp32_ble/ble_uuid.cpp +9 -10
  27. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +4 -1
  28. esphome/components/esp32_camera_web_server/camera_web_server.h +1 -1
  29. esphome/components/esp32_rmt_led_strip/light.py +3 -3
  30. esphome/components/esp8266/__init__.py +5 -7
  31. esphome/components/ezo/ezo.cpp +14 -26
  32. esphome/components/font/__init__.py +10 -25
  33. esphome/components/font/font.cpp +5 -3
  34. esphome/components/graphical_display_menu/__init__.py +2 -0
  35. esphome/components/haier/hon_climate.cpp +79 -80
  36. esphome/components/hbridge/switch/__init__.py +44 -0
  37. esphome/components/hbridge/switch/hbridge_switch.cpp +95 -0
  38. esphome/components/hbridge/switch/hbridge_switch.h +50 -0
  39. esphome/components/hitachi_ac344/hitachi_ac344.cpp +4 -2
  40. esphome/components/hitachi_ac424/hitachi_ac424.cpp +4 -2
  41. esphome/components/homeassistant/number/homeassistant_number.cpp +3 -0
  42. esphome/components/hx711/hx711.cpp +1 -1
  43. esphome/components/hx711/hx711.h +1 -1
  44. esphome/components/i2c/i2c_bus_esp_idf.cpp +2 -2
  45. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +61 -59
  46. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +8 -17
  47. esphome/components/ili9xxx/display.py +1 -2
  48. esphome/components/ili9xxx/ili9xxx_display.cpp +3 -2
  49. esphome/components/image/__init__.py +1 -2
  50. esphome/components/logger/logger.cpp +1 -1
  51. esphome/components/ltr501/ltr501.cpp +1 -1
  52. esphome/components/lvgl/defines.py +8 -1
  53. esphome/components/lvgl/lv_validation.py +8 -3
  54. esphome/components/lvgl/lvgl_esphome.cpp +1 -1
  55. esphome/components/lvgl/lvgl_esphome.h +16 -0
  56. esphome/components/lvgl/widgets/animimg.py +12 -17
  57. esphome/components/lvgl/widgets/img.py +1 -3
  58. esphome/components/matrix_keypad/__init__.py +15 -3
  59. esphome/components/matrix_keypad/matrix_keypad.cpp +4 -0
  60. esphome/components/matrix_keypad/matrix_keypad.h +5 -0
  61. esphome/components/max31865/max31865.cpp +4 -2
  62. esphome/components/modbus_controller/modbus_controller.cpp +24 -24
  63. esphome/components/modbus_controller/modbus_controller.h +22 -22
  64. esphome/components/modbus_controller/number/modbus_number.cpp +8 -8
  65. esphome/components/modbus_controller/number/modbus_number.h +4 -4
  66. esphome/components/modbus_controller/output/modbus_output.cpp +7 -6
  67. esphome/components/modbus_controller/output/modbus_output.h +5 -5
  68. esphome/components/modbus_controller/select/modbus_select.cpp +4 -3
  69. esphome/components/modbus_controller/select/modbus_select.h +4 -4
  70. esphome/components/modbus_controller/switch/modbus_switch.cpp +5 -5
  71. esphome/components/modbus_controller/switch/modbus_switch.h +2 -2
  72. esphome/components/mqtt/__init__.py +4 -0
  73. esphome/components/mqtt/mqtt_alarm_control_panel.cpp +2 -5
  74. esphome/components/mqtt/mqtt_backend_esp32.cpp +3 -3
  75. esphome/components/mqtt/mqtt_client.cpp +4 -0
  76. esphome/components/mqtt/mqtt_client.h +6 -0
  77. esphome/components/mqtt/mqtt_climate.cpp +13 -3
  78. esphome/components/mqtt/mqtt_sensor.cpp +2 -0
  79. esphome/components/network/ip_address.h +1 -1
  80. esphome/components/nextion/__init__.py +2 -0
  81. esphome/components/nextion/automation.h +76 -0
  82. esphome/components/nextion/base_component.py +1 -0
  83. esphome/components/nextion/binary_sensor/__init__.py +43 -2
  84. esphome/components/nextion/display.py +15 -0
  85. esphome/components/nextion/nextion.cpp +8 -5
  86. esphome/components/nextion/nextion.h +7 -0
  87. esphome/components/nextion/nextion_upload_idf.cpp +2 -2
  88. esphome/components/nextion/sensor/__init__.py +38 -5
  89. esphome/components/nextion/switch/__init__.py +38 -2
  90. esphome/components/nextion/text_sensor/__init__.py +37 -2
  91. esphome/components/nfc/ndef_record.cpp +3 -3
  92. esphome/components/online_image/__init__.py +1 -0
  93. esphome/components/opentherm/opentherm.cpp +3 -3
  94. esphome/components/opentherm/opentherm.h +1 -1
  95. esphome/components/ota/automation.h +1 -1
  96. esphome/components/output/float_output.cpp +1 -1
  97. esphome/components/pca6416a/pca6416a.cpp +5 -3
  98. esphome/components/pca9554/pca9554.cpp +4 -4
  99. esphome/components/pipsolar/pipsolar.cpp +2 -2
  100. esphome/components/pipsolar/switch/pipsolar_switch.cpp +2 -2
  101. esphome/components/pn532/pn532_mifare_ultralight.cpp +2 -2
  102. esphome/components/pn7150/pn7150_mifare_ultralight.cpp +2 -2
  103. esphome/components/pn7160/pn7160_mifare_ultralight.cpp +2 -2
  104. esphome/components/qmc5883l/qmc5883l.cpp +45 -19
  105. esphome/components/qmc5883l/qmc5883l.h +1 -1
  106. esphome/components/qspi_dbi/qspi_dbi.cpp +2 -1
  107. esphome/components/remote_base/raw_protocol.cpp +1 -1
  108. esphome/components/remote_receiver/__init__.py +5 -6
  109. esphome/components/rotary_encoder/rotary_encoder.cpp +3 -1
  110. esphome/components/rp2040/__init__.py +1 -1
  111. esphome/components/rtl87xx/__init__.py +1 -1
  112. esphome/components/safe_mode/automation.h +1 -1
  113. esphome/components/seeed_mr60bha2/__init__.py +41 -0
  114. esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +173 -0
  115. esphome/components/seeed_mr60bha2/seeed_mr60bha2.h +61 -0
  116. esphome/components/seeed_mr60bha2/sensor.py +57 -0
  117. esphome/components/seeed_mr60fda2/__init__.py +41 -0
  118. esphome/components/seeed_mr60fda2/binary_sensor.py +33 -0
  119. esphome/components/seeed_mr60fda2/button/__init__.py +45 -0
  120. esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.cpp +9 -0
  121. esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.h +18 -0
  122. esphome/components/seeed_mr60fda2/button/reset_radar_button.cpp +9 -0
  123. esphome/components/seeed_mr60fda2/button/reset_radar_button.h +18 -0
  124. esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +368 -0
  125. esphome/components/seeed_mr60fda2/seeed_mr60fda2.h +101 -0
  126. esphome/components/seeed_mr60fda2/select/__init__.py +59 -0
  127. esphome/components/seeed_mr60fda2/select/height_threshold_select.cpp +15 -0
  128. esphome/components/seeed_mr60fda2/select/height_threshold_select.h +18 -0
  129. esphome/components/seeed_mr60fda2/select/install_height_select.cpp +15 -0
  130. esphome/components/seeed_mr60fda2/select/install_height_select.h +18 -0
  131. esphome/components/seeed_mr60fda2/select/sensitivity_select.cpp +15 -0
  132. esphome/components/seeed_mr60fda2/select/sensitivity_select.h +18 -0
  133. esphome/components/sen5x/sensor.py +5 -6
  134. esphome/components/sgp30/sensor.py +8 -9
  135. esphome/components/sgp30/sgp30.cpp +2 -6
  136. esphome/components/shelly_dimmer/shelly_dimmer.cpp +1 -1
  137. esphome/components/sim800l/sim800l.cpp +1 -1
  138. esphome/components/sntp/sntp_component.cpp +14 -20
  139. esphome/components/sntp/sntp_component.h +6 -9
  140. esphome/components/sntp/time.py +4 -7
  141. esphome/components/sprinkler/sprinkler.cpp +2 -2
  142. esphome/components/st7735/st7735.cpp +1 -1
  143. esphome/components/st7789v/st7789v.cpp +1 -1
  144. esphome/components/stepper/stepper.h +0 -1
  145. esphome/components/sun_gtil2/sun_gtil2.cpp +1 -1
  146. esphome/components/switch/binary_sensor/__init__.py +31 -0
  147. esphome/components/switch/binary_sensor/switch_binary_sensor.cpp +17 -0
  148. esphome/components/switch/binary_sensor/switch_binary_sensor.h +22 -0
  149. esphome/components/sx1509/sx1509_gpio_pin.cpp +2 -1
  150. esphome/components/sx1509/sx1509_gpio_pin.h +5 -5
  151. esphome/components/uart/uart.h +1 -1
  152. esphome/components/udp/udp_component.cpp +32 -16
  153. esphome/components/ufire_ec/sensor.py +4 -4
  154. esphome/components/uln2003/uln2003.cpp +4 -1
  155. esphome/components/waveshare_epaper/display.py +8 -0
  156. esphome/components/waveshare_epaper/waveshare_epaper.cpp +191 -0
  157. esphome/components/waveshare_epaper/waveshare_epaper.h +56 -0
  158. esphome/components/wiegand/__init__.py +3 -4
  159. esphome/components/wifi/__init__.py +42 -0
  160. esphome/components/wifi/wifi_component.cpp +2 -2
  161. esphome/components/wifi/wifi_component.h +82 -1
  162. esphome/components/wifi/wifi_component_esp32_arduino.cpp +1 -1
  163. esphome/components/wifi/wifi_component_esp8266.cpp +1 -1
  164. esphome/components/wifi/wifi_component_esp_idf.cpp +1 -1
  165. esphome/components/wifi/wifi_component_libretiny.cpp +1 -1
  166. esphome/components/wifi/wifi_component_pico_w.cpp +1 -1
  167. esphome/components/wireguard/wireguard.cpp +2 -2
  168. esphome/components/xiaomi_ble/xiaomi_ble.cpp +1 -1
  169. esphome/config_validation.py +15 -11
  170. esphome/const.py +11 -1
  171. esphome/core/component.cpp +1 -1
  172. esphome/core/config.py +1 -2
  173. esphome/core/defines.h +3 -1
  174. esphome/core/helpers.cpp +8 -2
  175. esphome/core/helpers.h +2 -1
  176. esphome/core/optional.h +2 -2
  177. esphome/dashboard/web_server.py +6 -0
  178. {esphome-2024.11.3.dist-info → esphome-2024.12.0.dist-info}/METADATA +4 -4
  179. {esphome-2024.11.3.dist-info → esphome-2024.12.0.dist-info}/RECORD +183 -153
  180. {esphome-2024.11.3.dist-info → esphome-2024.12.0.dist-info}/LICENSE +0 -0
  181. {esphome-2024.11.3.dist-info → esphome-2024.12.0.dist-info}/WHEEL +0 -0
  182. {esphome-2024.11.3.dist-info → esphome-2024.12.0.dist-info}/entry_points.txt +0 -0
  183. {esphome-2024.11.3.dist-info → esphome-2024.12.0.dist-info}/top_level.txt +0 -0
@@ -81,8 +81,8 @@ bool PN7160::is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_
81
81
  const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector
82
82
 
83
83
  return (page_3_to_6.size() > p4_offset + 3) &&
84
- !((page_3_to_6[p4_offset + 0] == 0xFF) && (page_3_to_6[p4_offset + 1] == 0xFF) &&
85
- (page_3_to_6[p4_offset + 2] == 0xFF) && (page_3_to_6[p4_offset + 3] == 0xFF));
84
+ ((page_3_to_6[p4_offset + 0] != 0xFF) || (page_3_to_6[p4_offset + 1] != 0xFF) ||
85
+ (page_3_to_6[p4_offset + 2] != 0xFF) || (page_3_to_6[p4_offset + 3] != 0xFF));
86
86
  }
87
87
 
88
88
  uint16_t PN7160::read_mifare_ultralight_capacity_() {
@@ -81,16 +81,39 @@ void QMC5883LComponent::dump_config() {
81
81
  }
82
82
  float QMC5883LComponent::get_setup_priority() const { return setup_priority::DATA; }
83
83
  void QMC5883LComponent::update() {
84
+ i2c::ErrorCode err;
84
85
  uint8_t status = false;
85
- this->read_byte(QMC5883L_REGISTER_STATUS, &status);
86
-
87
- // Always request X,Y,Z regardless if there are sensors for them
88
- // to avoid https://github.com/esphome/issues/issues/5731
89
- uint16_t raw_x, raw_y, raw_z;
90
- if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_X_LSB, &raw_x) ||
91
- !this->read_byte_16_(QMC5883L_REGISTER_DATA_Y_LSB, &raw_y) ||
92
- !this->read_byte_16_(QMC5883L_REGISTER_DATA_Z_LSB, &raw_z)) {
93
- this->status_set_warning();
86
+ // Status byte gets cleared when data is read, so we have to read this first.
87
+ // If status and two axes are desired, it's possible to save one byte of traffic by enabling
88
+ // ROL_PNT in setup and reading 7 bytes starting at the status register.
89
+ // If status and all three axes are desired, using ROL_PNT saves you 3 bytes.
90
+ // But simply not reading status saves you 4 bytes always and is much simpler.
91
+ if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG) {
92
+ err = this->read_register(QMC5883L_REGISTER_STATUS, &status, 1);
93
+ if (err != i2c::ERROR_OK) {
94
+ this->status_set_warning(str_sprintf("status read failed (%d)", err).c_str());
95
+ return;
96
+ }
97
+ }
98
+
99
+ uint16_t raw[3] = {0};
100
+ // Z must always be requested, otherwise the data registers will remain locked against updates.
101
+ // Skipping the Y axis if X and Z are needed actually requires an additional byte of comms.
102
+ // Starting partway through the axes does save you traffic.
103
+ uint8_t start, dest;
104
+ if (this->heading_sensor_ != nullptr || this->x_sensor_ != nullptr) {
105
+ start = QMC5883L_REGISTER_DATA_X_LSB;
106
+ dest = 0;
107
+ } else if (this->y_sensor_ != nullptr) {
108
+ start = QMC5883L_REGISTER_DATA_Y_LSB;
109
+ dest = 1;
110
+ } else {
111
+ start = QMC5883L_REGISTER_DATA_Z_LSB;
112
+ dest = 2;
113
+ }
114
+ err = this->read_bytes_16_le_(start, &raw[dest], 3 - dest);
115
+ if (err != i2c::ERROR_OK) {
116
+ this->status_set_warning(str_sprintf("mag read failed (%d)", err).c_str());
94
117
  return;
95
118
  }
96
119
 
@@ -107,17 +130,18 @@ void QMC5883LComponent::update() {
107
130
  }
108
131
 
109
132
  // in µT
110
- const float x = int16_t(raw_x) * mg_per_bit * 0.1f;
111
- const float y = int16_t(raw_y) * mg_per_bit * 0.1f;
112
- const float z = int16_t(raw_z) * mg_per_bit * 0.1f;
133
+ const float x = int16_t(raw[0]) * mg_per_bit * 0.1f;
134
+ const float y = int16_t(raw[1]) * mg_per_bit * 0.1f;
135
+ const float z = int16_t(raw[2]) * mg_per_bit * 0.1f;
113
136
 
114
137
  float heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
115
138
 
116
139
  float temp = NAN;
117
140
  if (this->temperature_sensor_ != nullptr) {
118
141
  uint16_t raw_temp;
119
- if (!this->read_byte_16_(QMC5883L_REGISTER_TEMPERATURE_LSB, &raw_temp)) {
120
- this->status_set_warning();
142
+ err = this->read_bytes_16_le_(QMC5883L_REGISTER_TEMPERATURE_LSB, &raw_temp);
143
+ if (err != i2c::ERROR_OK) {
144
+ this->status_set_warning(str_sprintf("temp read failed (%d)", err).c_str());
121
145
  return;
122
146
  }
123
147
  temp = int16_t(raw_temp) * 0.01f;
@@ -138,11 +162,13 @@ void QMC5883LComponent::update() {
138
162
  this->temperature_sensor_->publish_state(temp);
139
163
  }
140
164
 
141
- bool QMC5883LComponent::read_byte_16_(uint8_t a_register, uint16_t *data) {
142
- if (!this->read_byte_16(a_register, data))
143
- return false;
144
- *data = (*data & 0x00FF) << 8 | (*data & 0xFF00) >> 8; // Flip Byte order, LSB first;
145
- return true;
165
+ i2c::ErrorCode QMC5883LComponent::read_bytes_16_le_(uint8_t a_register, uint16_t *data, uint8_t len) {
166
+ i2c::ErrorCode err = this->read_register(a_register, reinterpret_cast<uint8_t *>(data), len * 2);
167
+ if (err != i2c::ERROR_OK)
168
+ return err;
169
+ for (size_t i = 0; i < len; i++)
170
+ data[i] = convert_little_endian(data[i]);
171
+ return err;
146
172
  }
147
173
 
148
174
  } // namespace qmc5883l
@@ -55,7 +55,7 @@ class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
55
55
  NONE = 0,
56
56
  COMMUNICATION_FAILED,
57
57
  } error_code_;
58
- bool read_byte_16_(uint8_t a_register, uint16_t *data);
58
+ i2c::ErrorCode read_bytes_16_le_(uint8_t a_register, uint16_t *data, uint8_t len = 1);
59
59
  HighFrequencyLoopRequester high_freq_;
60
60
  };
61
61
 
@@ -146,7 +146,8 @@ void QspiDbi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8
146
146
  return;
147
147
  if (bitness != display::COLOR_BITNESS_565 || order != this->color_mode_ ||
148
148
  big_endian != (this->bit_order_ == spi::BIT_ORDER_MSB_FIRST)) {
149
- return Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad);
149
+ Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad);
150
+ return;
150
151
  } else if (this->draw_from_origin_) {
151
152
  auto stride = x_offset + w + x_pad;
152
153
  for (int y = 0; y != h; y++) {
@@ -28,7 +28,7 @@ bool RawDumper::dump(RemoteReceiveData src) {
28
28
  ESP_LOGI(TAG, "%s", buffer);
29
29
  buffer_offset = 0;
30
30
  written = sprintf(buffer, " ");
31
- if (i + 1 < src.size()) {
31
+ if (i + 1 < src.size() - 1) {
32
32
  written += sprintf(buffer + written, "%" PRId32 ", ", value);
33
33
  } else {
34
34
  written += sprintf(buffer + written, "%" PRId32, value);
@@ -1,24 +1,23 @@
1
+ from esphome import pins
1
2
  import esphome.codegen as cg
3
+ from esphome.components import esp32_rmt, remote_base
2
4
  import esphome.config_validation as cv
3
- from esphome import pins
4
- from esphome.components import remote_base, esp32_rmt
5
5
  from esphome.const import (
6
6
  CONF_BUFFER_SIZE,
7
+ CONF_CLOCK_DIVIDER,
7
8
  CONF_DUMP,
8
9
  CONF_FILTER,
9
10
  CONF_ID,
10
11
  CONF_IDLE,
12
+ CONF_MEMORY_BLOCKS,
11
13
  CONF_PIN,
14
+ CONF_RMT_CHANNEL,
12
15
  CONF_TOLERANCE,
13
16
  CONF_TYPE,
14
- CONF_MEMORY_BLOCKS,
15
- CONF_RMT_CHANNEL,
16
17
  CONF_VALUE,
17
18
  )
18
19
  from esphome.core import CORE, TimePeriod
19
20
 
20
- CONF_CLOCK_DIVIDER = "clock_divider"
21
-
22
21
  AUTO_LOAD = ["remote_base"]
23
22
  remote_receiver_ns = cg.esphome_ns.namespace("remote_receiver")
24
23
  remote_base_ns = cg.esphome_ns.namespace("remote_base")
@@ -162,7 +162,7 @@ void RotaryEncoderSensor::dump_config() {
162
162
  LOG_PIN(" Pin B: ", this->pin_b_);
163
163
  LOG_PIN(" Pin I: ", this->pin_i_);
164
164
 
165
- const LogString *restore_mode = LOG_STR("");
165
+ const LogString *restore_mode;
166
166
  switch (this->restore_mode_) {
167
167
  case ROTARY_ENCODER_RESTORE_DEFAULT_ZERO:
168
168
  restore_mode = LOG_STR("Restore (Defaults to zero)");
@@ -170,6 +170,8 @@ void RotaryEncoderSensor::dump_config() {
170
170
  case ROTARY_ENCODER_ALWAYS_ZERO:
171
171
  restore_mode = LOG_STR("Always zero");
172
172
  break;
173
+ default:
174
+ restore_mode = LOG_STR("");
173
175
  }
174
176
  ESP_LOGCONFIG(TAG, " Restore Mode: %s", LOG_STR_ARG(restore_mode));
175
177
 
@@ -17,7 +17,7 @@ from esphome.const import (
17
17
  PLATFORM_RP2040,
18
18
  )
19
19
  from esphome.core import CORE, EsphomeError, coroutine_with_priority
20
- from esphome.helpers import copy_file_if_changed, mkdir_p, write_file, read_file
20
+ from esphome.helpers import copy_file_if_changed, mkdir_p, read_file, write_file
21
21
 
22
22
  from .const import KEY_BOARD, KEY_PIO_FILES, KEY_RP2040, rp2040_ns
23
23
 
@@ -15,7 +15,7 @@ from esphome.components.libretiny.const import (
15
15
  )
16
16
  from esphome.core import CORE
17
17
 
18
- from .boards import RTL87XX_BOARDS, RTL87XX_BOARD_PINS
18
+ from .boards import RTL87XX_BOARD_PINS, RTL87XX_BOARDS
19
19
 
20
20
  CODEOWNERS = ["@kuba2k2"]
21
21
  AUTO_LOAD = ["libretiny"]
@@ -9,7 +9,7 @@ namespace safe_mode {
9
9
  class SafeModeTrigger : public Trigger<> {
10
10
  public:
11
11
  explicit SafeModeTrigger(SafeModeComponent *parent) {
12
- parent->add_on_safe_mode_callback([this, parent]() { trigger(); });
12
+ parent->add_on_safe_mode_callback([this]() { trigger(); });
13
13
  }
14
14
  };
15
15
 
@@ -0,0 +1,41 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components import uart
3
+ import esphome.config_validation as cv
4
+ from esphome.const import CONF_ID
5
+
6
+ CODEOWNERS = ["@limengdu"]
7
+ DEPENDENCIES = ["uart"]
8
+ MULTI_CONF = True
9
+
10
+ mr60bha2_ns = cg.esphome_ns.namespace("seeed_mr60bha2")
11
+
12
+ MR60BHA2Component = mr60bha2_ns.class_(
13
+ "MR60BHA2Component", cg.Component, uart.UARTDevice
14
+ )
15
+
16
+ CONF_MR60BHA2_ID = "mr60bha2_id"
17
+
18
+ CONFIG_SCHEMA = (
19
+ cv.Schema(
20
+ {
21
+ cv.GenerateID(): cv.declare_id(MR60BHA2Component),
22
+ }
23
+ )
24
+ .extend(uart.UART_DEVICE_SCHEMA)
25
+ .extend(cv.COMPONENT_SCHEMA)
26
+ )
27
+
28
+ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
29
+ "seeed_mr60bha2",
30
+ require_tx=True,
31
+ require_rx=True,
32
+ baud_rate=115200,
33
+ parity="NONE",
34
+ stop_bits=1,
35
+ )
36
+
37
+
38
+ async def to_code(config):
39
+ var = cg.new_Pvariable(config[CONF_ID])
40
+ await cg.register_component(var, config)
41
+ await uart.register_uart_device(var, config)
@@ -0,0 +1,173 @@
1
+ #include "seeed_mr60bha2.h"
2
+ #include "esphome/core/log.h"
3
+
4
+ #include <utility>
5
+
6
+ namespace esphome {
7
+ namespace seeed_mr60bha2 {
8
+
9
+ static const char *const TAG = "seeed_mr60bha2";
10
+
11
+ // Prints the component's configuration data. dump_config() prints all of the component's configuration
12
+ // items in an easy-to-read format, including the configuration key-value pairs.
13
+ void MR60BHA2Component::dump_config() {
14
+ ESP_LOGCONFIG(TAG, "MR60BHA2:");
15
+ #ifdef USE_SENSOR
16
+ LOG_SENSOR(" ", "Breath Rate Sensor", this->breath_rate_sensor_);
17
+ LOG_SENSOR(" ", "Heart Rate Sensor", this->heart_rate_sensor_);
18
+ LOG_SENSOR(" ", "Distance Sensor", this->distance_sensor_);
19
+ #endif
20
+ }
21
+
22
+ // main loop
23
+ void MR60BHA2Component::loop() {
24
+ uint8_t byte;
25
+
26
+ // Is there data on the serial port
27
+ while (this->available()) {
28
+ this->read_byte(&byte);
29
+ this->rx_message_.push_back(byte);
30
+ if (!this->validate_message_()) {
31
+ this->rx_message_.clear();
32
+ }
33
+ }
34
+ }
35
+
36
+ /**
37
+ * @brief Calculate the checksum for a byte array.
38
+ *
39
+ * This function calculates the checksum for the provided byte array using an
40
+ * XOR-based checksum algorithm.
41
+ *
42
+ * @param data The byte array to calculate the checksum for.
43
+ * @param len The length of the byte array.
44
+ * @return The calculated checksum.
45
+ */
46
+ static uint8_t calculate_checksum(const uint8_t *data, size_t len) {
47
+ uint8_t checksum = 0;
48
+ for (size_t i = 0; i < len; i++) {
49
+ checksum ^= data[i];
50
+ }
51
+ checksum = ~checksum;
52
+ return checksum;
53
+ }
54
+
55
+ /**
56
+ * @brief Validate the checksum of a byte array.
57
+ *
58
+ * This function validates the checksum of the provided byte array by comparing
59
+ * it to the expected checksum.
60
+ *
61
+ * @param data The byte array to validate.
62
+ * @param len The length of the byte array.
63
+ * @param expected_checksum The expected checksum.
64
+ * @return True if the checksum is valid, false otherwise.
65
+ */
66
+ static bool validate_checksum(const uint8_t *data, size_t len, uint8_t expected_checksum) {
67
+ return calculate_checksum(data, len) == expected_checksum;
68
+ }
69
+
70
+ bool MR60BHA2Component::validate_message_() {
71
+ size_t at = this->rx_message_.size() - 1;
72
+ auto *data = &this->rx_message_[0];
73
+ uint8_t new_byte = data[at];
74
+
75
+ if (at == 0) {
76
+ return new_byte == FRAME_HEADER_BUFFER;
77
+ }
78
+
79
+ if (at <= 2) {
80
+ return true;
81
+ }
82
+ uint16_t frame_id = encode_uint16(data[1], data[2]);
83
+
84
+ if (at <= 4) {
85
+ return true;
86
+ }
87
+
88
+ uint16_t length = encode_uint16(data[3], data[4]);
89
+
90
+ if (at <= 6) {
91
+ return true;
92
+ }
93
+
94
+ uint16_t frame_type = encode_uint16(data[5], data[6]);
95
+
96
+ if (frame_type != BREATH_RATE_TYPE_BUFFER && frame_type != HEART_RATE_TYPE_BUFFER &&
97
+ frame_type != DISTANCE_TYPE_BUFFER) {
98
+ return false;
99
+ }
100
+
101
+ uint8_t header_checksum = new_byte;
102
+
103
+ if (at == 7) {
104
+ if (!validate_checksum(data, 7, header_checksum)) {
105
+ ESP_LOGE(TAG, "HEAD_CKSUM_FRAME ERROR: 0x%02x", header_checksum);
106
+ ESP_LOGV(TAG, "GET FRAME: %s", format_hex_pretty(data, 8).c_str());
107
+ return false;
108
+ }
109
+ return true;
110
+ }
111
+
112
+ // Wait until all data is read
113
+ if (at - 8 < length) {
114
+ return true;
115
+ }
116
+
117
+ uint8_t data_checksum = new_byte;
118
+ if (at == 8 + length) {
119
+ if (!validate_checksum(data + 8, length, data_checksum)) {
120
+ ESP_LOGE(TAG, "DATA_CKSUM_FRAME ERROR: 0x%02x", data_checksum);
121
+ ESP_LOGV(TAG, "GET FRAME: %s", format_hex_pretty(data, 8 + length).c_str());
122
+ return false;
123
+ }
124
+ }
125
+
126
+ const uint8_t *frame_data = data + 8;
127
+ ESP_LOGV(TAG, "Received Frame: ID: 0x%04x, Type: 0x%04x, Data: [%s] Raw Data: [%s]", frame_id, frame_type,
128
+ format_hex_pretty(frame_data, length).c_str(), format_hex_pretty(this->rx_message_).c_str());
129
+ this->process_frame_(frame_id, frame_type, data + 8, length);
130
+
131
+ // Return false to reset rx buffer
132
+ return false;
133
+ }
134
+
135
+ void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, const uint8_t *data, size_t length) {
136
+ switch (frame_type) {
137
+ case BREATH_RATE_TYPE_BUFFER:
138
+ if (this->breath_rate_sensor_ != nullptr && length >= 4) {
139
+ uint32_t current_breath_rate_int = encode_uint32(data[3], data[2], data[1], data[0]);
140
+ if (current_breath_rate_int != 0) {
141
+ float breath_rate_float;
142
+ memcpy(&breath_rate_float, &current_breath_rate_int, sizeof(float));
143
+ this->breath_rate_sensor_->publish_state(breath_rate_float);
144
+ }
145
+ }
146
+ break;
147
+ case HEART_RATE_TYPE_BUFFER:
148
+ if (this->heart_rate_sensor_ != nullptr && length >= 4) {
149
+ uint32_t current_heart_rate_int = encode_uint32(data[3], data[2], data[1], data[0]);
150
+ if (current_heart_rate_int != 0) {
151
+ float heart_rate_float;
152
+ memcpy(&heart_rate_float, &current_heart_rate_int, sizeof(float));
153
+ this->heart_rate_sensor_->publish_state(heart_rate_float);
154
+ }
155
+ }
156
+ break;
157
+ case DISTANCE_TYPE_BUFFER:
158
+ if (!data[0]) {
159
+ if (this->distance_sensor_ != nullptr && length >= 8) {
160
+ uint32_t current_distance_int = encode_uint32(data[7], data[6], data[5], data[4]);
161
+ float distance_float;
162
+ memcpy(&distance_float, &current_distance_int, sizeof(float));
163
+ this->distance_sensor_->publish_state(distance_float);
164
+ }
165
+ }
166
+ break;
167
+ default:
168
+ break;
169
+ }
170
+ }
171
+
172
+ } // namespace seeed_mr60bha2
173
+ } // namespace esphome
@@ -0,0 +1,61 @@
1
+ #pragma once
2
+ #include "esphome/core/component.h"
3
+ #include "esphome/core/defines.h"
4
+ #ifdef USE_SENSOR
5
+ #include "esphome/components/sensor/sensor.h"
6
+ #endif
7
+ #include "esphome/components/uart/uart.h"
8
+ #include "esphome/core/automation.h"
9
+ #include "esphome/core/helpers.h"
10
+
11
+ #include <map>
12
+
13
+ namespace esphome {
14
+ namespace seeed_mr60bha2 {
15
+
16
+ static const uint8_t DATA_BUF_MAX_SIZE = 12;
17
+ static const uint8_t FRAME_BUF_MAX_SIZE = 21;
18
+ static const uint8_t LEN_TO_HEAD_CKSUM = 8;
19
+ static const uint8_t LEN_TO_DATA_FRAME = 9;
20
+
21
+ static const uint8_t FRAME_HEADER_BUFFER = 0x01;
22
+ static const uint16_t BREATH_RATE_TYPE_BUFFER = 0x0A14;
23
+ static const uint16_t HEART_RATE_TYPE_BUFFER = 0x0A15;
24
+ static const uint16_t DISTANCE_TYPE_BUFFER = 0x0A16;
25
+
26
+ enum FrameLocation {
27
+ LOCATE_FRAME_HEADER,
28
+ LOCATE_ID_FRAME1,
29
+ LOCATE_ID_FRAME2,
30
+ LOCATE_LENGTH_FRAME_H,
31
+ LOCATE_LENGTH_FRAME_L,
32
+ LOCATE_TYPE_FRAME1,
33
+ LOCATE_TYPE_FRAME2,
34
+ LOCATE_HEAD_CKSUM_FRAME, // Header checksum: [from the first byte to the previous byte of the HEAD_CKSUM bit]
35
+ LOCATE_DATA_FRAME,
36
+ LOCATE_DATA_CKSUM_FRAME, // Data checksum: [from the first to the previous byte of the DATA_CKSUM bit]
37
+ LOCATE_PROCESS_FRAME,
38
+ };
39
+
40
+ class MR60BHA2Component : public Component,
41
+ public uart::UARTDevice { // The class name must be the name defined by text_sensor.py
42
+ #ifdef USE_SENSOR
43
+ SUB_SENSOR(breath_rate);
44
+ SUB_SENSOR(heart_rate);
45
+ SUB_SENSOR(distance);
46
+ #endif
47
+
48
+ public:
49
+ float get_setup_priority() const override { return esphome::setup_priority::LATE; }
50
+ void dump_config() override;
51
+ void loop() override;
52
+
53
+ protected:
54
+ bool validate_message_();
55
+ void process_frame_(uint16_t frame_id, uint16_t frame_type, const uint8_t *data, size_t length);
56
+
57
+ std::vector<uint8_t> rx_message_;
58
+ };
59
+
60
+ } // namespace seeed_mr60bha2
61
+ } // namespace esphome
@@ -0,0 +1,57 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components import sensor
3
+ import esphome.config_validation as cv
4
+ from esphome.const import (
5
+ CONF_DISTANCE,
6
+ DEVICE_CLASS_DISTANCE,
7
+ ICON_HEART_PULSE,
8
+ ICON_PULSE,
9
+ ICON_SIGNAL,
10
+ STATE_CLASS_MEASUREMENT,
11
+ UNIT_BEATS_PER_MINUTE,
12
+ UNIT_CENTIMETER,
13
+ )
14
+
15
+ from . import CONF_MR60BHA2_ID, MR60BHA2Component
16
+
17
+ DEPENDENCIES = ["seeed_mr60bha2"]
18
+
19
+ CONF_BREATH_RATE = "breath_rate"
20
+ CONF_HEART_RATE = "heart_rate"
21
+
22
+ CONFIG_SCHEMA = cv.Schema(
23
+ {
24
+ cv.GenerateID(CONF_MR60BHA2_ID): cv.use_id(MR60BHA2Component),
25
+ cv.Optional(CONF_BREATH_RATE): sensor.sensor_schema(
26
+ accuracy_decimals=2,
27
+ state_class=STATE_CLASS_MEASUREMENT,
28
+ icon=ICON_PULSE,
29
+ ),
30
+ cv.Optional(CONF_HEART_RATE): sensor.sensor_schema(
31
+ accuracy_decimals=0,
32
+ icon=ICON_HEART_PULSE,
33
+ state_class=STATE_CLASS_MEASUREMENT,
34
+ unit_of_measurement=UNIT_BEATS_PER_MINUTE,
35
+ ),
36
+ cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
37
+ device_class=DEVICE_CLASS_DISTANCE,
38
+ state_class=STATE_CLASS_MEASUREMENT,
39
+ unit_of_measurement=UNIT_CENTIMETER,
40
+ accuracy_decimals=2,
41
+ icon=ICON_SIGNAL,
42
+ ),
43
+ }
44
+ )
45
+
46
+
47
+ async def to_code(config):
48
+ mr60bha2_component = await cg.get_variable(config[CONF_MR60BHA2_ID])
49
+ if breath_rate_config := config.get(CONF_BREATH_RATE):
50
+ sens = await sensor.new_sensor(breath_rate_config)
51
+ cg.add(mr60bha2_component.set_breath_rate_sensor(sens))
52
+ if heart_rate_config := config.get(CONF_HEART_RATE):
53
+ sens = await sensor.new_sensor(heart_rate_config)
54
+ cg.add(mr60bha2_component.set_heart_rate_sensor(sens))
55
+ if distance_config := config.get(CONF_DISTANCE):
56
+ sens = await sensor.new_sensor(distance_config)
57
+ cg.add(mr60bha2_component.set_distance_sensor(sens))
@@ -0,0 +1,41 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components import uart
3
+ import esphome.config_validation as cv
4
+ from esphome.const import CONF_ID
5
+
6
+ CODEOWNERS = ["@limengdu"]
7
+ DEPENDENCIES = ["uart"]
8
+ MULTI_CONF = True
9
+
10
+ mr60fda2_ns = cg.esphome_ns.namespace("seeed_mr60fda2")
11
+
12
+ MR60FDA2Component = mr60fda2_ns.class_(
13
+ "MR60FDA2Component", cg.Component, uart.UARTDevice
14
+ )
15
+
16
+ CONF_MR60FDA2_ID = "mr60fda2_id"
17
+
18
+ CONFIG_SCHEMA = (
19
+ cv.Schema(
20
+ {
21
+ cv.GenerateID(): cv.declare_id(MR60FDA2Component),
22
+ }
23
+ )
24
+ .extend(uart.UART_DEVICE_SCHEMA)
25
+ .extend(cv.COMPONENT_SCHEMA)
26
+ )
27
+
28
+ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
29
+ "seeed_mr60fda2",
30
+ require_tx=True,
31
+ require_rx=True,
32
+ baud_rate=115200,
33
+ parity="NONE",
34
+ stop_bits=1,
35
+ )
36
+
37
+
38
+ async def to_code(config):
39
+ var = cg.new_Pvariable(config[CONF_ID])
40
+ await cg.register_component(var, config)
41
+ await uart.register_uart_device(var, config)
@@ -0,0 +1,33 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components import binary_sensor
3
+ import esphome.config_validation as cv
4
+ from esphome.const import DEVICE_CLASS_OCCUPANCY, DEVICE_CLASS_SAFETY
5
+
6
+ from . import CONF_MR60FDA2_ID, MR60FDA2Component
7
+
8
+ DEPENDENCIES = ["seeed_mr60fda2"]
9
+
10
+ CONF_PEOPLE_EXIST = "people_exist"
11
+ CONF_FALL_DETECTED = "fall_detected"
12
+
13
+ CONFIG_SCHEMA = {
14
+ cv.GenerateID(CONF_MR60FDA2_ID): cv.use_id(MR60FDA2Component),
15
+ cv.Optional(CONF_PEOPLE_EXIST): binary_sensor.binary_sensor_schema(
16
+ device_class=DEVICE_CLASS_OCCUPANCY, icon="mdi:motion-sensor"
17
+ ),
18
+ cv.Optional(CONF_FALL_DETECTED): binary_sensor.binary_sensor_schema(
19
+ device_class=DEVICE_CLASS_SAFETY, icon="mdi:emergency"
20
+ ),
21
+ }
22
+
23
+
24
+ async def to_code(config):
25
+ mr60fda2_component = await cg.get_variable(config[CONF_MR60FDA2_ID])
26
+
27
+ if people_exist_config := config.get(CONF_PEOPLE_EXIST):
28
+ sens = await binary_sensor.new_binary_sensor(people_exist_config)
29
+ cg.add(mr60fda2_component.set_people_exist_binary_sensor(sens))
30
+
31
+ if is_fall_config := config.get(CONF_FALL_DETECTED):
32
+ sens = await binary_sensor.new_binary_sensor(is_fall_config)
33
+ cg.add(mr60fda2_component.set_fall_detected_binary_sensor(sens))
@@ -0,0 +1,45 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components import button
3
+ import esphome.config_validation as cv
4
+ from esphome.const import (
5
+ DEVICE_CLASS_RESTART,
6
+ DEVICE_CLASS_UPDATE,
7
+ ENTITY_CATEGORY_DIAGNOSTIC,
8
+ ENTITY_CATEGORY_NONE,
9
+ CONF_FACTORY_RESET,
10
+ )
11
+
12
+ from .. import CONF_MR60FDA2_ID, MR60FDA2Component, mr60fda2_ns
13
+
14
+ DEPENDENCIES = ["seeed_mr60fda2"]
15
+
16
+ GetRadarParametersButton = mr60fda2_ns.class_("GetRadarParametersButton", button.Button)
17
+ ResetRadarButton = mr60fda2_ns.class_("ResetRadarButton", button.Button)
18
+
19
+ CONF_GET_RADAR_PARAMETERS = "get_radar_parameters"
20
+
21
+ CONFIG_SCHEMA = {
22
+ cv.GenerateID(CONF_MR60FDA2_ID): cv.use_id(MR60FDA2Component),
23
+ cv.Optional(CONF_GET_RADAR_PARAMETERS): button.button_schema(
24
+ GetRadarParametersButton,
25
+ device_class=DEVICE_CLASS_UPDATE,
26
+ entity_category=ENTITY_CATEGORY_NONE,
27
+ ),
28
+ cv.Optional(CONF_FACTORY_RESET): button.button_schema(
29
+ ResetRadarButton,
30
+ device_class=DEVICE_CLASS_RESTART,
31
+ entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
32
+ ),
33
+ }
34
+
35
+
36
+ async def to_code(config):
37
+ mr60fda2_component = await cg.get_variable(config[CONF_MR60FDA2_ID])
38
+ if get_radar_parameters_config := config.get(CONF_GET_RADAR_PARAMETERS):
39
+ b = await button.new_button(get_radar_parameters_config)
40
+ await cg.register_parented(b, config[CONF_MR60FDA2_ID])
41
+ cg.add(mr60fda2_component.set_get_radar_parameters_button(b))
42
+ if factory_reset_config := config.get(CONF_FACTORY_RESET):
43
+ b = await button.new_button(factory_reset_config)
44
+ await cg.register_parented(b, config[CONF_MR60FDA2_ID])
45
+ cg.add(mr60fda2_component.set_factory_reset_button(b))
@@ -0,0 +1,9 @@
1
+ #include "get_radar_parameters_button.h"
2
+
3
+ namespace esphome {
4
+ namespace seeed_mr60fda2 {
5
+
6
+ void GetRadarParametersButton::press_action() { this->parent_->get_radar_parameters(); }
7
+
8
+ } // namespace seeed_mr60fda2
9
+ } // namespace esphome