esphome 2025.6.2__py3-none-any.whl → 2025.7.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 (648) hide show
  1. esphome/__main__.py +1 -3
  2. esphome/codegen.py +2 -0
  3. esphome/components/ac_dimmer/ac_dimmer.cpp +6 -6
  4. esphome/components/adc/__init__.py +25 -1
  5. esphome/components/adc/adc_sensor.h +11 -11
  6. esphome/components/adc/adc_sensor_common.cpp +1 -1
  7. esphome/components/adc/adc_sensor_esp32.cpp +16 -8
  8. esphome/components/ade7880/ade7880.h +0 -2
  9. esphome/components/ads1115/ads1115.h +0 -1
  10. esphome/components/ads1118/ads1118.h +0 -1
  11. esphome/components/ags10/ags10.h +0 -2
  12. esphome/components/aic3204/aic3204.h +0 -1
  13. esphome/components/alarm_control_panel/__init__.py +5 -2
  14. esphome/components/alpha3/alpha3.h +0 -1
  15. esphome/components/am43/cover/am43_cover.h +0 -1
  16. esphome/components/am43/sensor/am43_sensor.h +0 -1
  17. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +0 -2
  18. esphome/components/anova/anova.cpp +5 -1
  19. esphome/components/anova/anova.h +0 -1
  20. esphome/components/apds9960/apds9960.cpp +1 -1
  21. esphome/components/api/__init__.py +57 -21
  22. esphome/components/api/api_connection.cpp +344 -539
  23. esphome/components/api/api_connection.h +224 -141
  24. esphome/components/api/api_frame_helper.cpp +91 -127
  25. esphome/components/api/api_frame_helper.h +64 -54
  26. esphome/components/api/api_pb2.cpp +1837 -9044
  27. esphome/components/api/api_pb2.h +532 -685
  28. esphome/components/api/api_pb2_dump.cpp +4432 -0
  29. esphome/components/api/api_pb2_service.cpp +184 -425
  30. esphome/components/api/api_pb2_service.h +13 -6
  31. esphome/components/api/api_server.cpp +131 -167
  32. esphome/components/api/api_server.h +38 -10
  33. esphome/components/api/client.py +10 -4
  34. esphome/components/api/custom_api_device.h +8 -0
  35. esphome/components/api/list_entities.cpp +37 -104
  36. esphome/components/api/list_entities.h +33 -23
  37. esphome/components/api/proto.h +532 -26
  38. esphome/components/api/subscribe_state.cpp +23 -29
  39. esphome/components/api/subscribe_state.h +26 -19
  40. esphome/components/api/user_services.h +2 -0
  41. esphome/components/as3935_spi/as3935_spi.h +0 -2
  42. esphome/components/as5600/as5600.h +0 -1
  43. esphome/components/async_tcp/__init__.py +14 -5
  44. esphome/components/atc_mithermometer/atc_mithermometer.h +0 -1
  45. esphome/components/atm90e32/atm90e32.cpp +2 -1
  46. esphome/components/audio/audio_decoder.cpp +1 -1
  47. esphome/components/audio/audio_transfer_buffer.cpp +2 -2
  48. esphome/components/b_parasite/b_parasite.h +0 -1
  49. esphome/components/bedjet/bedjet_hub.cpp +5 -1
  50. esphome/components/bedjet/climate/bedjet_climate.cpp +5 -1
  51. esphome/components/beken_spi_led_strip/led_strip.cpp +4 -2
  52. esphome/components/bh1750/bh1750.cpp +5 -5
  53. esphome/components/binary_sensor/__init__.py +82 -5
  54. esphome/components/binary_sensor/automation.h +19 -1
  55. esphome/components/binary_sensor/binary_sensor.cpp +12 -30
  56. esphome/components/binary_sensor/binary_sensor.h +11 -25
  57. esphome/components/binary_sensor/filter.cpp +29 -24
  58. esphome/components/binary_sensor/filter.h +20 -10
  59. esphome/components/ble_client/output/ble_binary_output.h +0 -1
  60. esphome/components/ble_client/sensor/ble_rssi_sensor.cpp +5 -1
  61. esphome/components/ble_client/sensor/ble_rssi_sensor.h +0 -1
  62. esphome/components/ble_client/sensor/ble_sensor.cpp +5 -1
  63. esphome/components/ble_client/sensor/ble_sensor.h +0 -1
  64. esphome/components/ble_client/switch/ble_switch.h +0 -1
  65. esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +5 -1
  66. esphome/components/ble_client/text_sensor/ble_text_sensor.h +0 -1
  67. esphome/components/ble_presence/ble_presence_device.h +0 -1
  68. esphome/components/ble_rssi/ble_rssi_sensor.h +0 -1
  69. esphome/components/ble_scanner/ble_scanner.h +0 -1
  70. esphome/components/bluetooth_proxy/bluetooth_connection.h +9 -2
  71. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +16 -6
  72. esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -2
  73. esphome/components/bme680/sensor.py +1 -1
  74. esphome/components/bmp581/bmp581.h +0 -2
  75. esphome/components/button/__init__.py +5 -2
  76. esphome/components/camera/__init__.py +1 -0
  77. esphome/components/camera/camera.cpp +22 -0
  78. esphome/components/camera/camera.h +80 -0
  79. esphome/components/canbus/__init__.py +1 -0
  80. esphome/components/cap1188/cap1188.h +0 -1
  81. esphome/components/captive_portal/__init__.py +12 -2
  82. esphome/components/captive_portal/captive_portal.cpp +12 -2
  83. esphome/components/captive_portal/captive_portal.h +5 -2
  84. esphome/components/ccs811/ccs811.h +0 -2
  85. esphome/components/climate/__init__.py +5 -2
  86. esphome/components/cm1106/sensor.py +2 -2
  87. esphome/components/const/__init__.py +2 -0
  88. esphome/components/copy/binary_sensor/copy_binary_sensor.h +0 -1
  89. esphome/components/copy/button/copy_button.h +0 -1
  90. esphome/components/copy/cover/copy_cover.h +0 -1
  91. esphome/components/copy/fan/copy_fan.h +0 -1
  92. esphome/components/copy/lock/copy_lock.h +0 -1
  93. esphome/components/copy/number/copy_number.h +0 -1
  94. esphome/components/copy/select/copy_select.h +0 -1
  95. esphome/components/copy/sensor/copy_sensor.h +0 -1
  96. esphome/components/copy/switch/copy_switch.h +0 -1
  97. esphome/components/copy/text/copy_text.h +0 -1
  98. esphome/components/copy/text_sensor/copy_text_sensor.h +0 -1
  99. esphome/components/cover/__init__.py +5 -2
  100. esphome/components/cs5460a/cs5460a.h +0 -1
  101. esphome/components/datetime/__init__.py +4 -2
  102. esphome/components/debug/__init__.py +20 -0
  103. esphome/components/debug/debug_esp32.cpp +2 -0
  104. esphome/components/deep_sleep/__init__.py +43 -9
  105. esphome/components/demo/__init__.py +2 -2
  106. esphome/components/display/display.cpp +4 -3
  107. esphome/components/display/display.h +0 -2
  108. esphome/components/display/display_buffer.cpp +1 -1
  109. esphome/components/ds2484/__init__.py +1 -0
  110. esphome/components/ds2484/ds2484.cpp +209 -0
  111. esphome/components/ds2484/ds2484.h +43 -0
  112. esphome/components/ds2484/one_wire.py +37 -0
  113. esphome/components/duty_time/duty_time_sensor.h +0 -1
  114. esphome/components/ens160_base/ens160_base.h +0 -1
  115. esphome/components/es7210/es7210.h +0 -1
  116. esphome/components/es7243e/es7243e.h +0 -1
  117. esphome/components/es8156/es8156.h +0 -1
  118. esphome/components/es8311/es8311.h +0 -1
  119. esphome/components/es8388/es8388.h +0 -1
  120. esphome/components/esp32/__init__.py +103 -135
  121. esphome/components/esp32/core.cpp +0 -4
  122. esphome/components/esp32/gpio.h +1 -1
  123. esphome/components/esp32/helpers.cpp +69 -0
  124. esphome/components/esp32_ble/ble.cpp +5 -6
  125. esphome/components/esp32_ble/ble.h +29 -14
  126. esphome/components/esp32_ble/ble_event.h +6 -6
  127. esphome/components/esp32_ble_client/ble_client_base.cpp +21 -6
  128. esphome/components/esp32_ble_client/ble_client_base.h +24 -9
  129. esphome/components/esp32_ble_tracker/__init__.py +2 -8
  130. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +5 -5
  131. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +11 -7
  132. esphome/components/esp32_camera/__init__.py +112 -98
  133. esphome/components/esp32_camera/esp32_camera.cpp +41 -31
  134. esphome/components/esp32_camera/esp32_camera.h +35 -30
  135. esphome/components/esp32_camera_web_server/__init__.py +2 -1
  136. esphome/components/esp32_camera_web_server/camera_web_server.cpp +8 -8
  137. esphome/components/esp32_camera_web_server/camera_web_server.h +3 -3
  138. esphome/components/esp32_hall/sensor.py +2 -21
  139. esphome/components/esp32_hosted/__init__.py +101 -0
  140. esphome/components/esp32_hosted/esp32_hosted.py.script +12 -0
  141. esphome/components/esp32_improv/esp32_improv_component.cpp +3 -0
  142. esphome/components/esp32_rmt/__init__.py +0 -58
  143. esphome/components/esp32_rmt_led_strip/led_strip.cpp +77 -63
  144. esphome/components/esp32_rmt_led_strip/led_strip.h +11 -17
  145. esphome/components/esp32_rmt_led_strip/light.py +14 -76
  146. esphome/components/esp32_touch/esp32_touch.h +174 -28
  147. esphome/components/esp32_touch/esp32_touch_common.cpp +162 -0
  148. esphome/components/esp32_touch/esp32_touch_v1.cpp +240 -0
  149. esphome/components/esp32_touch/esp32_touch_v2.cpp +397 -0
  150. esphome/components/esp8266/__init__.py +2 -0
  151. esphome/components/esp8266/gpio.cpp +10 -10
  152. esphome/components/esp8266/helpers.cpp +31 -0
  153. esphome/components/esp_ldo/__init__.py +10 -8
  154. esphome/components/esp_ldo/esp_ldo.h +3 -0
  155. esphome/components/esphome/ota/__init__.py +1 -0
  156. esphome/components/esphome/ota/ota_esphome.cpp +24 -19
  157. esphome/components/ethernet/__init__.py +42 -23
  158. esphome/components/ethernet/esp_eth_phy_jl1101.c +0 -16
  159. esphome/components/ethernet/ethernet_component.cpp +69 -29
  160. esphome/components/ethernet/ethernet_component.h +18 -10
  161. esphome/components/event/__init__.py +5 -2
  162. esphome/components/ezo/ezo.h +0 -1
  163. esphome/components/ezo_pmp/ezo_pmp.h +0 -1
  164. esphome/components/fan/__init__.py +5 -2
  165. esphome/components/fan/fan.cpp +4 -0
  166. esphome/components/feedback/feedback_cover.h +0 -1
  167. esphome/components/font/__init__.py +92 -82
  168. esphome/components/font/font.cpp +9 -2
  169. esphome/components/font/font.h +20 -5
  170. esphome/components/fs3000/fs3000.h +0 -1
  171. esphome/components/gcja5/gcja5.h +0 -1
  172. esphome/components/gl_r01_i2c/__init__.py +0 -0
  173. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +68 -0
  174. esphome/components/gl_r01_i2c/gl_r01_i2c.h +22 -0
  175. esphome/components/gl_r01_i2c/sensor.py +36 -0
  176. esphome/components/gp8403/gp8403.h +0 -1
  177. esphome/components/gpio/binary_sensor/__init__.py +39 -1
  178. esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +77 -3
  179. esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +40 -0
  180. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +0 -2
  181. esphome/components/he60r/he60r.h +0 -1
  182. esphome/components/heatpumpir/climate.py +2 -1
  183. esphome/components/heatpumpir/heatpumpir.cpp +1 -0
  184. esphome/components/heatpumpir/heatpumpir.h +1 -0
  185. esphome/components/honeywellabp2_i2c/honeywellabp2.h +0 -1
  186. esphome/components/host/__init__.py +3 -1
  187. esphome/components/host/helpers.cpp +57 -0
  188. esphome/components/http_request/__init__.py +19 -1
  189. esphome/components/http_request/http_request.h +1 -1
  190. esphome/components/http_request/http_request_arduino.cpp +0 -1
  191. esphome/components/http_request/http_request_arduino.h +1 -0
  192. esphome/components/http_request/http_request_idf.cpp +0 -1
  193. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  194. esphome/components/http_request/update/http_request_update.cpp +35 -16
  195. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +3 -9
  196. esphome/components/hydreon_rgxx/sensor.py +1 -1
  197. esphome/components/i2c/__init__.py +23 -11
  198. esphome/components/i2c/i2c_bus.h +8 -1
  199. esphome/components/i2c/i2c_bus_arduino.cpp +4 -3
  200. esphome/components/i2c/i2c_bus_arduino.h +6 -3
  201. esphome/components/i2c/i2c_bus_esp_idf.h +5 -3
  202. esphome/components/i2c_device/i2c_device.h +0 -1
  203. esphome/components/i2s_audio/__init__.py +2 -10
  204. esphome/components/i2s_audio/i2s_audio.cpp +1 -5
  205. esphome/components/i2s_audio/media_player/__init__.py +2 -2
  206. esphome/components/i2s_audio/speaker/__init__.py +1 -1
  207. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +2 -2
  208. esphome/components/iaqcore/iaqcore.h +0 -2
  209. esphome/components/image/__init__.py +123 -24
  210. esphome/components/improv_serial/improv_serial_component.cpp +0 -4
  211. esphome/components/ina219/ina219.cpp +7 -0
  212. esphome/components/ina219/ina219.h +1 -0
  213. esphome/components/ina260/ina260.h +0 -2
  214. esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h +0 -1
  215. esphome/components/inkplate6/display.py +15 -0
  216. esphome/components/inkplate6/inkplate.cpp +2 -2
  217. esphome/components/integration/integration_sensor.h +0 -1
  218. esphome/components/internal_temperature/internal_temperature.cpp +8 -27
  219. esphome/components/internal_temperature/sensor.py +0 -26
  220. esphome/components/interval/interval.h +0 -2
  221. esphome/components/json/__init__.py +1 -1
  222. esphome/components/json/json_util.cpp +56 -63
  223. esphome/components/ld2410/button/__init__.py +3 -3
  224. esphome/components/ld2410/button/factory_reset_button.cpp +9 -0
  225. esphome/components/ld2410/button/{reset_button.h → factory_reset_button.h} +2 -2
  226. esphome/components/ld2410/ld2410.cpp +421 -268
  227. esphome/components/ld2410/ld2410.h +44 -146
  228. esphome/components/ld2410/number/__init__.py +2 -2
  229. esphome/components/ld2410/sensor.py +1 -1
  230. esphome/components/ld2410/switch/__init__.py +1 -1
  231. esphome/components/ld2420/binary_sensor/ld2420_binary_sensor.cpp +2 -2
  232. esphome/components/ld2420/button/reconfig_buttons.cpp +1 -1
  233. esphome/components/ld2420/ld2420.cpp +252 -147
  234. esphome/components/ld2420/ld2420.h +52 -126
  235. esphome/components/ld2420/number/__init__.py +2 -2
  236. esphome/components/ld2420/number/gate_config_number.cpp +1 -1
  237. esphome/components/ld2420/select/operating_mode_select.cpp +1 -1
  238. esphome/components/ld2420/sensor/__init__.py +6 -2
  239. esphome/components/ld2420/sensor/ld2420_sensor.cpp +2 -2
  240. esphome/components/ld2420/sensor/ld2420_sensor.h +1 -1
  241. esphome/components/ld2420/text_sensor/text_sensor.cpp +2 -2
  242. esphome/components/ld2450/button/__init__.py +3 -3
  243. esphome/components/ld2450/button/factory_reset_button.cpp +9 -0
  244. esphome/components/ld2450/button/{reset_button.h → factory_reset_button.h} +2 -2
  245. esphome/components/ld2450/ld2450.cpp +384 -232
  246. esphome/components/ld2450/ld2450.h +60 -69
  247. esphome/components/ld2450/switch/__init__.py +1 -1
  248. esphome/components/ledc/ledc_output.cpp +1 -63
  249. esphome/components/libretiny/__init__.py +5 -3
  250. esphome/components/libretiny/const.py +5 -0
  251. esphome/components/libretiny/generate_components.py +1 -0
  252. esphome/components/libretiny/helpers.cpp +35 -0
  253. esphome/components/libretiny/lt_component.cpp +5 -3
  254. esphome/components/light/__init__.py +4 -2
  255. esphome/components/light/addressable_light.h +3 -3
  256. esphome/components/light/light_call.cpp +180 -243
  257. esphome/components/light/light_call.h +72 -20
  258. esphome/components/light/light_color_values.h +14 -14
  259. esphome/components/light/light_json_schema.cpp +17 -16
  260. esphome/components/light/light_state.h +15 -13
  261. esphome/components/light/transformers.h +2 -2
  262. esphome/components/ln882x/__init__.py +52 -0
  263. esphome/components/ln882x/boards.py +285 -0
  264. esphome/components/lock/__init__.py +5 -2
  265. esphome/components/logger/__init__.py +40 -3
  266. esphome/components/logger/logger.cpp +47 -12
  267. esphome/components/logger/logger.h +80 -49
  268. esphome/components/logger/logger_esp32.cpp +3 -3
  269. esphome/components/lps22/__init__.py +0 -0
  270. esphome/components/lps22/lps22.cpp +75 -0
  271. esphome/components/lps22/lps22.h +27 -0
  272. esphome/components/lps22/sensor.py +58 -0
  273. esphome/components/ltr390/ltr390.h +0 -1
  274. esphome/components/ltr501/ltr501.h +0 -1
  275. esphome/components/ltr_als_ps/ltr_als_ps.h +0 -1
  276. esphome/components/lvgl/__init__.py +1 -1
  277. esphome/components/lvgl/schemas.py +66 -6
  278. esphome/components/lvgl/styles.py +24 -16
  279. esphome/components/lvgl/widgets/__init__.py +12 -2
  280. esphome/components/lvgl/widgets/lv_bar.py +40 -19
  281. esphome/components/lvgl/widgets/meter.py +20 -13
  282. esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp +1 -1
  283. esphome/components/max9611/max9611.h +0 -1
  284. esphome/components/mcp23016/__init__.py +1 -1
  285. esphome/components/mcp23xxx_base/__init__.py +1 -1
  286. esphome/components/mcp4461/__init__.py +1 -1
  287. esphome/components/mcp4461/output/__init__.py +3 -2
  288. esphome/components/mcp9600/mcp9600.h +0 -2
  289. esphome/components/md5/md5.cpp +3 -3
  290. esphome/components/md5/md5.h +1 -6
  291. esphome/components/mdns/__init__.py +22 -11
  292. esphome/components/media_player/__init__.py +4 -3
  293. esphome/components/micro_wake_word/__init__.py +1 -5
  294. esphome/components/micro_wake_word/streaming_model.cpp +2 -2
  295. esphome/components/microphone/microphone.cpp +7 -9
  296. esphome/components/microphone/microphone.h +0 -2
  297. esphome/components/mipi_spi/display.py +1 -0
  298. esphome/components/mmc5603/mmc5603.cpp +1 -1
  299. esphome/components/modbus/modbus.cpp +33 -15
  300. esphome/components/modbus/modbus.h +9 -0
  301. esphome/components/modbus_controller/__init__.py +42 -10
  302. esphome/components/modbus_controller/modbus_controller.cpp +92 -11
  303. esphome/components/modbus_controller/modbus_controller.h +61 -7
  304. esphome/components/mopeka_pro_check/mopeka_pro_check.h +0 -1
  305. esphome/components/mopeka_std_check/mopeka_std_check.h +0 -1
  306. esphome/components/mpl3115a2/mpl3115a2.h +0 -2
  307. esphome/components/mqtt/__init__.py +16 -0
  308. esphome/components/mqtt/mqtt_alarm_control_panel.cpp +2 -1
  309. esphome/components/mqtt/mqtt_backend.h +2 -1
  310. esphome/components/mqtt/mqtt_backend_esp32.cpp +132 -47
  311. esphome/components/mqtt/mqtt_backend_esp32.h +106 -4
  312. esphome/components/mqtt/mqtt_binary_sensor.cpp +1 -0
  313. esphome/components/mqtt/mqtt_button.cpp +4 -1
  314. esphome/components/mqtt/mqtt_client.cpp +17 -9
  315. esphome/components/mqtt/mqtt_client.h +8 -3
  316. esphome/components/mqtt/mqtt_climate.cpp +6 -4
  317. esphome/components/mqtt/mqtt_component.cpp +3 -1
  318. esphome/components/mqtt/mqtt_cover.cpp +1 -0
  319. esphome/components/mqtt/mqtt_date.cpp +4 -3
  320. esphome/components/mqtt/mqtt_datetime.cpp +7 -6
  321. esphome/components/mqtt/mqtt_event.cpp +6 -3
  322. esphome/components/mqtt/mqtt_fan.cpp +1 -0
  323. esphome/components/mqtt/mqtt_light.cpp +8 -4
  324. esphome/components/mqtt/mqtt_lock.cpp +3 -1
  325. esphome/components/mqtt/mqtt_number.cpp +1 -0
  326. esphome/components/mqtt/mqtt_select.cpp +2 -1
  327. esphome/components/mqtt/mqtt_sensor.cpp +3 -1
  328. esphome/components/mqtt/mqtt_switch.cpp +3 -1
  329. esphome/components/mqtt/mqtt_text.cpp +1 -0
  330. esphome/components/mqtt/mqtt_text_sensor.cpp +3 -1
  331. esphome/components/mqtt/mqtt_time.cpp +4 -3
  332. esphome/components/mqtt/mqtt_update.cpp +1 -0
  333. esphome/components/mqtt/mqtt_valve.cpp +3 -1
  334. esphome/components/ms8607/ms8607.cpp +1 -1
  335. esphome/components/ms8607/ms8607.h +0 -1
  336. esphome/components/neopixelbus/light.py +4 -1
  337. esphome/components/neopixelbus/neopixelbus_light.h +1 -1
  338. esphome/components/network/__init__.py +4 -1
  339. esphome/components/network/ip_address.h +1 -0
  340. esphome/components/nextion/__init__.py +16 -0
  341. esphome/components/nextion/base_component.py +1 -0
  342. esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +1 -1
  343. esphome/components/nextion/display.py +14 -4
  344. esphome/components/nextion/nextion.cpp +166 -101
  345. esphome/components/nextion/nextion.h +84 -53
  346. esphome/components/nextion/nextion_commands.cpp +11 -10
  347. esphome/components/nextion/nextion_component.cpp +28 -28
  348. esphome/components/nextion/nextion_component.h +53 -18
  349. esphome/components/nextion/nextion_component_base.h +3 -0
  350. esphome/components/nextion/nextion_upload.cpp +36 -0
  351. esphome/components/nextion/nextion_upload_arduino.cpp +10 -35
  352. esphome/components/nextion/nextion_upload_idf.cpp +9 -33
  353. esphome/components/nextion/sensor/nextion_sensor.cpp +1 -1
  354. esphome/components/nextion/switch/nextion_switch.cpp +1 -1
  355. esphome/components/nextion/text_sensor/nextion_textsensor.cpp +1 -1
  356. esphome/components/nfc/nfc.cpp +3 -22
  357. esphome/components/nfc/nfc.h +3 -3
  358. esphome/components/number/__init__.py +5 -2
  359. esphome/components/online_image/__init__.py +9 -1
  360. esphome/components/online_image/online_image.cpp +17 -7
  361. esphome/components/online_image/online_image.h +10 -2
  362. esphome/components/opentherm/opentherm.cpp +7 -12
  363. esphome/components/opentherm/output/output.cpp +1 -1
  364. esphome/components/openthread/__init__.py +47 -40
  365. esphome/components/openthread/const.py +1 -0
  366. esphome/components/openthread/openthread_esp.cpp +27 -5
  367. esphome/components/opt3001/__init__.py +0 -0
  368. esphome/components/opt3001/opt3001.cpp +122 -0
  369. esphome/components/opt3001/opt3001.h +27 -0
  370. esphome/components/opt3001/sensor.py +35 -0
  371. esphome/components/ota/__init__.py +17 -0
  372. esphome/components/ota/ota_backend.h +27 -1
  373. esphome/components/ota/ota_backend_arduino_esp32.cpp +12 -2
  374. esphome/components/ota/ota_backend_arduino_esp32.h +3 -0
  375. esphome/components/ota/ota_backend_arduino_esp8266.cpp +18 -4
  376. esphome/components/ota/ota_backend_arduino_esp8266.h +3 -0
  377. esphome/components/ota/ota_backend_arduino_libretiny.cpp +12 -2
  378. esphome/components/ota/ota_backend_arduino_libretiny.h +3 -0
  379. esphome/components/ota/ota_backend_arduino_rp2040.cpp +9 -2
  380. esphome/components/ota/ota_backend_arduino_rp2040.h +3 -0
  381. esphome/components/ota/ota_backend_esp_idf.cpp +10 -16
  382. esphome/components/ota/ota_backend_esp_idf.h +1 -0
  383. esphome/components/packages/__init__.py +5 -2
  384. esphome/components/packet_transport/binary_sensor.py +61 -4
  385. esphome/components/packet_transport/packet_transport.cpp +34 -1
  386. esphome/components/packet_transport/packet_transport.h +11 -5
  387. esphome/components/pcf8574/__init__.py +1 -1
  388. esphome/components/pi4ioe5v6408/__init__.py +84 -0
  389. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +171 -0
  390. esphome/components/pi4ioe5v6408/pi4ioe5v6408.h +70 -0
  391. esphome/components/pmsa003i/pmsa003i.h +0 -1
  392. esphome/components/pmsx003/pmsx003.h +0 -1
  393. esphome/components/pn7150/pn7150.cpp +7 -7
  394. esphome/components/pn7150/pn7150.h +0 -1
  395. esphome/components/pn7160/pn7160.cpp +7 -7
  396. esphome/components/pn7160/pn7160.h +0 -1
  397. esphome/components/preferences/syncer.h +2 -0
  398. esphome/components/prometheus/prometheus_handler.h +1 -1
  399. esphome/components/psram/psram.cpp +0 -20
  400. esphome/components/pulse_counter/pulse_counter_sensor.h +0 -1
  401. esphome/components/pulse_meter/pulse_meter_sensor.cpp +8 -4
  402. esphome/components/pulse_width/pulse_width.h +0 -1
  403. esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +0 -4
  404. esphome/components/pvvx_mithermometer/display/pvvx_display.h +0 -2
  405. esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +0 -1
  406. esphome/components/qr_code/__init__.py +13 -10
  407. esphome/components/qwiic_pir/qwiic_pir.h +0 -1
  408. esphome/components/radon_eye_ble/radon_eye_listener.cpp +1 -1
  409. esphome/components/rc522/rc522.h +0 -1
  410. esphome/components/rdm6300/rdm6300.h +0 -2
  411. esphome/components/remote_base/__init__.py +7 -5
  412. esphome/components/remote_base/remote_base.cpp +24 -21
  413. esphome/components/remote_base/remote_base.h +3 -26
  414. esphome/components/remote_receiver/__init__.py +40 -46
  415. esphome/components/remote_receiver/remote_receiver.h +4 -18
  416. esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -87
  417. esphome/components/remote_receiver/remote_receiver_esp8266.cpp +1 -1
  418. esphome/components/remote_transmitter/__init__.py +42 -43
  419. esphome/components/remote_transmitter/remote_transmitter.h +2 -14
  420. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -77
  421. esphome/components/resistance/resistance_sensor.h +0 -1
  422. esphome/components/rp2040/__init__.py +2 -0
  423. esphome/components/rp2040/helpers.cpp +55 -0
  424. esphome/components/rp2040_pio_led_strip/led_strip.cpp +2 -2
  425. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -4
  426. esphome/components/rtttl/__init__.py +4 -4
  427. esphome/components/rtttl/rtttl.cpp +10 -1
  428. esphome/components/ruuvitag/ruuvitag.h +0 -1
  429. esphome/components/safe_mode/safe_mode.cpp +2 -0
  430. esphome/components/safe_mode/safe_mode.h +4 -1
  431. esphome/components/scd30/scd30.h +0 -1
  432. esphome/components/scd30/sensor.py +2 -2
  433. esphome/components/scd4x/scd4x.cpp +61 -54
  434. esphome/components/scd4x/scd4x.h +17 -15
  435. esphome/components/scd4x/sensor.py +4 -4
  436. esphome/components/script/script.h +0 -2
  437. esphome/components/sdp3x/sensor.py +1 -1
  438. esphome/components/select/__init__.py +5 -2
  439. esphome/components/sen5x/sen5x.h +0 -1
  440. esphome/components/senseair/senseair.h +0 -1
  441. esphome/components/sensor/__init__.py +4 -2
  442. esphome/components/sensor/filter.cpp +1 -1
  443. esphome/components/sensor/sensor.cpp +12 -6
  444. esphome/components/sensor/sensor.h +13 -5
  445. esphome/components/servo/servo.cpp +2 -2
  446. esphome/components/servo/servo.h +0 -1
  447. esphome/components/sfa30/sfa30.h +0 -1
  448. esphome/components/sgp30/sgp30.h +0 -1
  449. esphome/components/sgp4x/sgp4x.h +0 -1
  450. esphome/components/shelly_dimmer/stm32flash.cpp +1 -2
  451. esphome/components/sht4x/sht4x.h +0 -1
  452. esphome/components/sm300d2/sm300d2.h +0 -2
  453. esphome/components/smt100/sensor.py +8 -4
  454. esphome/components/smt100/smt100.cpp +5 -5
  455. esphome/components/smt100/smt100.h +3 -3
  456. esphome/components/sn74hc595/__init__.py +1 -1
  457. esphome/components/sn74hc595/sn74hc595.cpp +5 -4
  458. esphome/components/sntp/sntp_component.cpp +9 -3
  459. esphome/components/sntp/time.py +2 -0
  460. esphome/components/socket/__init__.py +17 -0
  461. esphome/components/spi/__init__.py +27 -6
  462. esphome/components/spi/spi.cpp +3 -2
  463. esphome/components/spi/spi.h +9 -3
  464. esphome/components/spi/spi_arduino.cpp +3 -5
  465. esphome/components/spi/spi_esp_idf.cpp +40 -21
  466. esphome/components/spi_led_strip/spi_led_strip.cpp +1 -1
  467. esphome/components/sps30/sps30.h +0 -1
  468. esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
  469. esphome/components/st7701s/st7701s.cpp +0 -4
  470. esphome/components/status/status_binary_sensor.h +0 -2
  471. esphome/components/substitutions/__init__.py +81 -21
  472. esphome/components/substitutions/jinja.py +99 -0
  473. esphome/components/sun/sun.cpp +3 -4
  474. esphome/components/switch/__init__.py +5 -2
  475. esphome/components/switch/binary_sensor/switch_binary_sensor.h +0 -1
  476. esphome/components/sx126x/__init__.py +317 -0
  477. esphome/components/sx126x/automation.h +62 -0
  478. esphome/components/sx126x/packet_transport/__init__.py +26 -0
  479. esphome/components/sx126x/packet_transport/sx126x_transport.cpp +26 -0
  480. esphome/components/sx126x/packet_transport/sx126x_transport.h +25 -0
  481. esphome/components/sx126x/sx126x.cpp +523 -0
  482. esphome/components/sx126x/sx126x.h +140 -0
  483. esphome/components/sx126x/sx126x_reg.h +163 -0
  484. esphome/components/sx127x/__init__.py +325 -0
  485. esphome/components/sx127x/automation.h +62 -0
  486. esphome/components/sx127x/packet_transport/__init__.py +26 -0
  487. esphome/components/sx127x/packet_transport/sx127x_transport.cpp +26 -0
  488. esphome/components/sx127x/packet_transport/sx127x_transport.h +25 -0
  489. esphome/components/sx127x/sx127x.cpp +498 -0
  490. esphome/components/sx127x/sx127x.h +128 -0
  491. esphome/components/sx127x/sx127x_reg.h +295 -0
  492. esphome/components/syslog/esphome_syslog.cpp +5 -3
  493. esphome/components/syslog/esphome_syslog.h +1 -1
  494. esphome/components/tca9555/__init__.py +1 -1
  495. esphome/components/template/binary_sensor/template_binary_sensor.cpp +1 -9
  496. esphome/components/text/__init__.py +5 -2
  497. esphome/components/text_sensor/__init__.py +5 -2
  498. esphome/components/thermostat/thermostat_climate.cpp +34 -31
  499. esphome/components/thermostat/thermostat_climate.h +43 -39
  500. esphome/components/time/__init__.py +16 -2
  501. esphome/components/time/real_time_clock.cpp +4 -0
  502. esphome/components/time/real_time_clock.h +5 -1
  503. esphome/components/tlc5971/tlc5971.cpp +4 -1
  504. esphome/components/tmp1075/tmp1075.h +0 -2
  505. esphome/components/tof10120/tof10120_sensor.h +0 -1
  506. esphome/components/tormatic/tormatic_cover.h +0 -1
  507. esphome/components/total_daily_energy/total_daily_energy.h +0 -1
  508. esphome/components/tsl2591/tsl2591.cpp +1 -1
  509. esphome/components/ttp229_bsf/ttp229_bsf.h +0 -1
  510. esphome/components/ttp229_lsf/ttp229_lsf.h +0 -1
  511. esphome/components/tx20/tx20.cpp +2 -2
  512. esphome/components/uart/__init__.py +18 -0
  513. esphome/components/uart/uart_component_esp_idf.cpp +1 -5
  514. esphome/components/update/__init__.py +5 -2
  515. esphome/components/update/update_entity.h +8 -0
  516. esphome/components/usb_host/__init__.py +5 -2
  517. esphome/components/usb_host/usb_host_client.cpp +10 -10
  518. esphome/components/usb_uart/cp210x.cpp +1 -1
  519. esphome/components/usb_uart/usb_uart.cpp +41 -44
  520. esphome/components/usb_uart/usb_uart.h +4 -3
  521. esphome/components/valve/__init__.py +5 -2
  522. esphome/components/vbus/vbus.h +0 -1
  523. esphome/components/veml3235/veml3235.h +0 -1
  524. esphome/components/veml7700/veml7700.h +0 -1
  525. esphome/components/vl53l0x/vl53l0x_sensor.h +0 -1
  526. esphome/components/voice_assistant/voice_assistant.cpp +4 -4
  527. esphome/components/watchdog/watchdog.cpp +0 -4
  528. esphome/components/waveshare_epaper/waveshare_epaper.cpp +6 -6
  529. esphome/components/web_server/__init__.py +34 -19
  530. esphome/components/web_server/ota/__init__.py +32 -0
  531. esphome/components/web_server/ota/ota_web_server.cpp +210 -0
  532. esphome/components/web_server/ota/ota_web_server.h +26 -0
  533. esphome/components/web_server/web_server.cpp +324 -439
  534. esphome/components/web_server/web_server.h +33 -23
  535. esphome/components/web_server/web_server_v1.cpp +4 -5
  536. esphome/components/web_server_base/__init__.py +5 -2
  537. esphome/components/web_server_base/web_server_base.cpp +2 -94
  538. esphome/components/web_server_base/web_server_base.h +5 -25
  539. esphome/components/web_server_idf/multipart.cpp +254 -0
  540. esphome/components/web_server_idf/multipart.h +86 -0
  541. esphome/components/web_server_idf/utils.cpp +32 -0
  542. esphome/components/web_server_idf/utils.h +10 -0
  543. esphome/components/web_server_idf/web_server_idf.cpp +164 -16
  544. esphome/components/web_server_idf/web_server_idf.h +11 -10
  545. esphome/components/wiegand/wiegand.cpp +2 -2
  546. esphome/components/wifi/__init__.py +18 -0
  547. esphome/components/wifi/wifi_component.cpp +17 -22
  548. esphome/components/wifi/wifi_component.h +27 -23
  549. esphome/components/wifi/wifi_component_esp32_arduino.cpp +52 -59
  550. esphome/components/wifi/wifi_component_esp8266.cpp +46 -46
  551. esphome/components/wifi/wifi_component_esp_idf.cpp +35 -36
  552. esphome/components/wifi/wifi_component_libretiny.cpp +26 -27
  553. esphome/components/wifi/wifi_component_pico_w.cpp +3 -3
  554. esphome/components/wifi_info/wifi_info_text_sensor.cpp +6 -6
  555. esphome/components/wireguard/__init__.py +2 -11
  556. esphome/components/xiaomi_ble/xiaomi_ble.cpp +13 -1
  557. esphome/components/xiaomi_ble/xiaomi_ble.h +1 -0
  558. esphome/components/xiaomi_cgd1/xiaomi_cgd1.h +0 -1
  559. esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h +0 -1
  560. esphome/components/xiaomi_cgg1/xiaomi_cgg1.h +0 -1
  561. esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h +0 -1
  562. esphome/components/xiaomi_gcls002/xiaomi_gcls002.h +0 -1
  563. esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h +0 -1
  564. esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h +0 -1
  565. esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h +0 -1
  566. esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h +0 -1
  567. esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h +0 -1
  568. esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h +0 -1
  569. esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h +0 -1
  570. esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h +0 -1
  571. esphome/components/xiaomi_mhoc303/xiaomi_mhoc303.h +0 -1
  572. esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h +0 -1
  573. esphome/components/xiaomi_miscale/xiaomi_miscale.h +0 -1
  574. esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h +0 -1
  575. esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h +0 -1
  576. esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h +0 -1
  577. esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h +0 -1
  578. esphome/components/xiaomi_xmwsdj04mmc/__init__.py +0 -0
  579. esphome/components/xiaomi_xmwsdj04mmc/sensor.py +77 -0
  580. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp +77 -0
  581. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h +36 -0
  582. esphome/components/zio_ultrasonic/zio_ultrasonic.h +0 -2
  583. esphome/components/zyaura/zyaura.h +0 -1
  584. esphome/config.py +88 -22
  585. esphome/config_helpers.py +74 -1
  586. esphome/config_validation.py +12 -1
  587. esphome/const.py +65 -10
  588. esphome/core/__init__.py +18 -2
  589. esphome/core/application.cpp +169 -10
  590. esphome/core/application.h +145 -165
  591. esphome/core/area.h +19 -0
  592. esphome/core/automation.h +58 -9
  593. esphome/core/color.cpp +3 -5
  594. esphome/core/color.h +16 -16
  595. esphome/core/component.cpp +156 -22
  596. esphome/core/component.h +98 -4
  597. esphome/core/component_iterator.cpp +11 -9
  598. esphome/core/component_iterator.h +12 -10
  599. esphome/core/config.py +155 -6
  600. esphome/core/controller.cpp +4 -2
  601. esphome/core/controller.h +1 -1
  602. esphome/core/datatypes.h +2 -2
  603. esphome/core/defines.h +17 -2
  604. esphome/core/device.h +20 -0
  605. esphome/core/entity_base.cpp +20 -15
  606. esphome/core/entity_base.h +76 -0
  607. esphome/core/entity_helpers.py +168 -1
  608. esphome/core/event_pool.h +81 -0
  609. esphome/core/helpers.cpp +75 -230
  610. esphome/core/helpers.h +165 -105
  611. esphome/core/lock_free_queue.h +151 -0
  612. esphome/core/log.cpp +2 -2
  613. esphome/core/log.h +2 -0
  614. esphome/core/optional.h +5 -0
  615. esphome/core/ring_buffer.cpp +2 -2
  616. esphome/core/scheduler.cpp +275 -103
  617. esphome/core/scheduler.h +154 -17
  618. esphome/core/time.cpp +5 -5
  619. esphome/core/time.h +5 -5
  620. esphome/cpp_generator.py +17 -0
  621. esphome/cpp_helpers.py +0 -22
  622. esphome/cpp_types.py +3 -1
  623. esphome/dashboard/entries.py +1 -1
  624. esphome/dashboard/util/text.py +5 -21
  625. esphome/dashboard/web_server.py +9 -1
  626. esphome/helpers.py +47 -0
  627. esphome/loader.py +15 -1
  628. esphome/pins.py +14 -8
  629. esphome/platformio_api.py +2 -0
  630. esphome/wizard.py +17 -4
  631. esphome/writer.py +44 -3
  632. esphome/yaml_util.py +0 -2
  633. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/METADATA +10 -9
  634. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/RECORD +639 -580
  635. esphome/components/api/api_pb2_size.h +0 -361
  636. esphome/components/esp32_ble/ble_event_pool.h +0 -72
  637. esphome/components/esp32_ble/queue.h +0 -85
  638. esphome/components/esp32_hall/esp32_hall.cpp +0 -25
  639. esphome/components/esp32_hall/esp32_hall.h +0 -23
  640. esphome/components/esp32_touch/esp32_touch.cpp +0 -355
  641. esphome/components/ld2410/button/reset_button.cpp +0 -9
  642. esphome/components/ld2450/button/reset_button.cpp +0 -9
  643. esphome/components/openthread/tlv.py +0 -65
  644. /esphome/{dashboard/enum.py → enum.py} +0 -0
  645. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/WHEEL +0 -0
  646. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/entry_points.txt +0 -0
  647. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/licenses/LICENSE +0 -0
  648. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/top_level.txt +0 -0
@@ -39,6 +39,7 @@ CODEOWNERS = ["@martgras"]
39
39
  AUTO_LOAD = ["modbus"]
40
40
 
41
41
  CONF_READ_LAMBDA = "read_lambda"
42
+ CONF_WRITE_LAMBDA = "write_lambda"
42
43
  CONF_SERVER_REGISTERS = "server_registers"
43
44
  MULTI_CONF = True
44
45
 
@@ -112,6 +113,22 @@ TYPE_REGISTER_MAP = {
112
113
  "FP32_R": 2,
113
114
  }
114
115
 
116
+ CPP_TYPE_REGISTER_MAP = {
117
+ "RAW": cg.uint16,
118
+ "U_WORD": cg.uint16,
119
+ "S_WORD": cg.int16,
120
+ "U_DWORD": cg.uint32,
121
+ "U_DWORD_R": cg.uint32,
122
+ "S_DWORD": cg.int32,
123
+ "S_DWORD_R": cg.int32,
124
+ "U_QWORD": cg.uint64,
125
+ "U_QWORD_R": cg.uint64,
126
+ "S_QWORD": cg.int64,
127
+ "S_QWORD_R": cg.int64,
128
+ "FP32": cg.float_,
129
+ "FP32_R": cg.float_,
130
+ }
131
+
115
132
  ModbusCommandSentTrigger = modbus_controller_ns.class_(
116
133
  "ModbusCommandSentTrigger", automation.Trigger.template(cg.int_, cg.int_)
117
134
  )
@@ -132,6 +149,7 @@ ModbusServerRegisterSchema = cv.Schema(
132
149
  cv.Required(CONF_ADDRESS): cv.positive_int,
133
150
  cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(SENSOR_VALUE_TYPE),
134
151
  cv.Required(CONF_READ_LAMBDA): cv.returning_lambda,
152
+ cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda,
135
153
  }
136
154
  )
137
155
 
@@ -285,21 +303,35 @@ async def to_code(config):
285
303
  cg.add(var.set_offline_skip_updates(config[CONF_OFFLINE_SKIP_UPDATES]))
286
304
  if CONF_SERVER_REGISTERS in config:
287
305
  for server_register in config[CONF_SERVER_REGISTERS]:
306
+ server_register_var = cg.new_Pvariable(
307
+ server_register[CONF_ID],
308
+ server_register[CONF_ADDRESS],
309
+ server_register[CONF_VALUE_TYPE],
310
+ TYPE_REGISTER_MAP[server_register[CONF_VALUE_TYPE]],
311
+ )
312
+ cpp_type = CPP_TYPE_REGISTER_MAP[server_register[CONF_VALUE_TYPE]]
288
313
  cg.add(
289
- var.add_server_register(
290
- cg.new_Pvariable(
291
- server_register[CONF_ID],
292
- server_register[CONF_ADDRESS],
293
- server_register[CONF_VALUE_TYPE],
294
- TYPE_REGISTER_MAP[server_register[CONF_VALUE_TYPE]],
314
+ server_register_var.set_read_lambda(
315
+ cg.TemplateArguments(cpp_type),
316
+ await cg.process_lambda(
317
+ server_register[CONF_READ_LAMBDA],
318
+ [(cg.uint16, "address")],
319
+ return_type=cpp_type,
320
+ ),
321
+ )
322
+ )
323
+ if CONF_WRITE_LAMBDA in server_register:
324
+ cg.add(
325
+ server_register_var.set_write_lambda(
326
+ cg.TemplateArguments(cpp_type),
295
327
  await cg.process_lambda(
296
- server_register[CONF_READ_LAMBDA],
297
- [],
298
- return_type=cg.float_,
328
+ server_register[CONF_WRITE_LAMBDA],
329
+ parameters=[(cg.uint16, "address"), (cpp_type, "x")],
330
+ return_type=cg.bool_,
299
331
  ),
300
332
  )
301
333
  )
302
- )
334
+ cg.add(var.add_server_register(server_register_var))
303
335
  await register_modbus_device(var, config)
304
336
  for conf in config.get(CONF_ON_COMMAND_SENT, []):
305
337
  trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
@@ -117,12 +117,17 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t
117
117
  bool found = false;
118
118
  for (auto *server_register : this->server_registers_) {
119
119
  if (server_register->address == current_address) {
120
- float value = server_register->read_lambda();
121
-
122
- ESP_LOGD(TAG, "Matched register. Address: 0x%02X. Value type: %zu. Register count: %u. Value: %0.1f.",
123
- server_register->address, static_cast<uint8_t>(server_register->value_type),
124
- server_register->register_count, value);
125
- std::vector<uint16_t> payload = float_to_payload(value, server_register->value_type);
120
+ if (!server_register->read_lambda) {
121
+ break;
122
+ }
123
+ int64_t value = server_register->read_lambda();
124
+ ESP_LOGD(TAG, "Matched register. Address: 0x%02X. Value type: %zu. Register count: %u. Value: %s.",
125
+ server_register->address, static_cast<size_t>(server_register->value_type),
126
+ server_register->register_count, server_register->format_value(value).c_str());
127
+
128
+ std::vector<uint16_t> payload;
129
+ payload.reserve(server_register->register_count * 2);
130
+ number_to_payload(payload, value, server_register->value_type);
126
131
  sixteen_bit_response.insert(sixteen_bit_response.end(), payload.cbegin(), payload.cend());
127
132
  current_address += server_register->register_count;
128
133
  found = true;
@@ -132,11 +137,7 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t
132
137
 
133
138
  if (!found) {
134
139
  ESP_LOGW(TAG, "Could not match any register to address %02X. Sending exception response.", current_address);
135
- std::vector<uint8_t> error_response;
136
- error_response.push_back(this->address_);
137
- error_response.push_back(0x81);
138
- error_response.push_back(0x02);
139
- this->send_raw(error_response);
140
+ send_error(function_code, 0x02);
140
141
  return;
141
142
  }
142
143
  }
@@ -151,6 +152,86 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t
151
152
  this->send(function_code, start_address, number_of_registers, response.size(), response.data());
152
153
  }
153
154
 
155
+ void ModbusController::on_modbus_write_registers(uint8_t function_code, const std::vector<uint8_t> &data) {
156
+ uint16_t number_of_registers;
157
+ uint16_t payload_offset;
158
+
159
+ if (function_code == 0x10) {
160
+ number_of_registers = uint16_t(data[3]) | (uint16_t(data[2]) << 8);
161
+ if (number_of_registers == 0 || number_of_registers > 0x7B) {
162
+ ESP_LOGW(TAG, "Invalid number of registers %d. Sending exception response.", number_of_registers);
163
+ send_error(function_code, 3);
164
+ return;
165
+ }
166
+ uint16_t payload_size = data[4];
167
+ if (payload_size != number_of_registers * 2) {
168
+ ESP_LOGW(TAG, "Payload size of %d bytes is not 2 times the number of registers (%d). Sending exception response.",
169
+ payload_size, number_of_registers);
170
+ send_error(function_code, 3);
171
+ return;
172
+ }
173
+ payload_offset = 5;
174
+ } else if (function_code == 0x06) {
175
+ number_of_registers = 1;
176
+ payload_offset = 2;
177
+ } else {
178
+ ESP_LOGW(TAG, "Invalid function code 0x%X. Sending exception response.", function_code);
179
+ send_error(function_code, 1);
180
+ return;
181
+ }
182
+
183
+ uint16_t start_address = uint16_t(data[1]) | (uint16_t(data[0]) << 8);
184
+ ESP_LOGD(TAG,
185
+ "Received write holding registers for device 0x%X. FC: 0x%X. Start address: 0x%X. Number of registers: "
186
+ "0x%X.",
187
+ this->address_, function_code, start_address, number_of_registers);
188
+
189
+ auto for_each_register = [this, start_address, number_of_registers, payload_offset](
190
+ const std::function<bool(ServerRegister *, uint16_t offset)> &callback) -> bool {
191
+ uint16_t offset = payload_offset;
192
+ for (uint16_t current_address = start_address; current_address < start_address + number_of_registers;) {
193
+ bool ok = false;
194
+ for (auto *server_register : this->server_registers_) {
195
+ if (server_register->address == current_address) {
196
+ ok = callback(server_register, offset);
197
+ current_address += server_register->register_count;
198
+ offset += server_register->register_count * sizeof(uint16_t);
199
+ break;
200
+ }
201
+ }
202
+
203
+ if (!ok) {
204
+ return false;
205
+ }
206
+ }
207
+ return true;
208
+ };
209
+
210
+ // check all registers are writable before writing to any of them:
211
+ if (!for_each_register([](ServerRegister *server_register, uint16_t offset) -> bool {
212
+ return server_register->write_lambda != nullptr;
213
+ })) {
214
+ send_error(function_code, 1);
215
+ return;
216
+ }
217
+
218
+ // Actually write to the registers:
219
+ if (!for_each_register([&data](ServerRegister *server_register, uint16_t offset) {
220
+ int64_t number = payload_to_number(data, server_register->value_type, offset, 0xFFFFFFFF);
221
+ return server_register->write_lambda(number);
222
+ })) {
223
+ send_error(function_code, 4);
224
+ return;
225
+ }
226
+
227
+ std::vector<uint8_t> response;
228
+ response.reserve(6);
229
+ response.push_back(this->address_);
230
+ response.push_back(function_code);
231
+ response.insert(response.end(), data.begin(), data.begin() + 4);
232
+ this->send_raw(response);
233
+ }
234
+
154
235
  SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const {
155
236
  auto reg_it = std::find_if(
156
237
  std::begin(this->register_ranges_), std::end(this->register_ranges_),
@@ -63,6 +63,10 @@ enum class SensorValueType : uint8_t {
63
63
  FP32_R = 0xD
64
64
  };
65
65
 
66
+ inline bool value_type_is_float(SensorValueType v) {
67
+ return v == SensorValueType::FP32 || v == SensorValueType::FP32_R;
68
+ }
69
+
66
70
  inline ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type) {
67
71
  switch (reg_type) {
68
72
  case ModbusRegisterType::COIL:
@@ -253,18 +257,66 @@ class SensorItem {
253
257
  };
254
258
 
255
259
  class ServerRegister {
260
+ using ReadLambda = std::function<int64_t()>;
261
+ using WriteLambda = std::function<bool(int64_t value)>;
262
+
256
263
  public:
257
- ServerRegister(uint16_t address, SensorValueType value_type, uint8_t register_count,
258
- std::function<float()> read_lambda) {
264
+ ServerRegister(uint16_t address, SensorValueType value_type, uint8_t register_count) {
259
265
  this->address = address;
260
266
  this->value_type = value_type;
261
267
  this->register_count = register_count;
262
- this->read_lambda = std::move(read_lambda);
263
268
  }
269
+
270
+ template<typename T> void set_read_lambda(const std::function<T(uint16_t address)> &&user_read_lambda) {
271
+ this->read_lambda = [this, user_read_lambda]() -> int64_t {
272
+ T user_value = user_read_lambda(this->address);
273
+ if constexpr (std::is_same_v<T, float>) {
274
+ return bit_cast<uint32_t>(user_value);
275
+ } else {
276
+ return static_cast<int64_t>(user_value);
277
+ }
278
+ };
279
+ }
280
+
281
+ template<typename T>
282
+ void set_write_lambda(const std::function<bool(uint16_t address, const T v)> &&user_write_lambda) {
283
+ this->write_lambda = [this, user_write_lambda](int64_t number) {
284
+ if constexpr (std::is_same_v<T, float>) {
285
+ float float_value = bit_cast<float>(static_cast<uint32_t>(number));
286
+ return user_write_lambda(this->address, float_value);
287
+ }
288
+ return user_write_lambda(this->address, static_cast<T>(number));
289
+ };
290
+ }
291
+
292
+ // Formats a raw value into a string representation based on the value type for debugging
293
+ std::string format_value(int64_t value) const {
294
+ switch (this->value_type) {
295
+ case SensorValueType::U_WORD:
296
+ case SensorValueType::U_DWORD:
297
+ case SensorValueType::U_DWORD_R:
298
+ case SensorValueType::U_QWORD:
299
+ case SensorValueType::U_QWORD_R:
300
+ return std::to_string(static_cast<uint64_t>(value));
301
+ case SensorValueType::S_WORD:
302
+ case SensorValueType::S_DWORD:
303
+ case SensorValueType::S_DWORD_R:
304
+ case SensorValueType::S_QWORD:
305
+ case SensorValueType::S_QWORD_R:
306
+ return std::to_string(value);
307
+ case SensorValueType::FP32_R:
308
+ case SensorValueType::FP32:
309
+ return str_sprintf("%.1f", bit_cast<float>(static_cast<uint32_t>(value)));
310
+ default:
311
+ return std::to_string(value);
312
+ }
313
+ }
314
+
264
315
  uint16_t address{0};
265
316
  SensorValueType value_type{SensorValueType::RAW};
266
317
  uint8_t register_count{0};
267
- std::function<float()> read_lambda;
318
+ ReadLambda read_lambda;
319
+ WriteLambda write_lambda;
268
320
  };
269
321
 
270
322
  // ModbusController::create_register_ranges_ tries to optimize register range
@@ -444,8 +496,10 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice {
444
496
  void on_modbus_data(const std::vector<uint8_t> &data) override;
445
497
  /// called when a modbus error response was received
446
498
  void on_modbus_error(uint8_t function_code, uint8_t exception_code) override;
447
- /// called when a modbus request (function code 3 or 4) was parsed without errors
499
+ /// called when a modbus request (function code 0x03 or 0x04) was parsed without errors
448
500
  void on_modbus_read_registers(uint8_t function_code, uint16_t start_address, uint16_t number_of_registers) final;
501
+ /// called when a modbus request (function code 0x06 or 0x10) was parsed without errors
502
+ void on_modbus_write_registers(uint8_t function_code, const std::vector<uint8_t> &data) final;
449
503
  /// default delegate called by process_modbus_data when a response has retrieved from the incoming queue
450
504
  void on_register_data(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data);
451
505
  /// default delegate called by process_modbus_data when a response for a write response has retrieved from the
@@ -529,7 +583,7 @@ inline float payload_to_float(const std::vector<uint8_t> &data, const SensorItem
529
583
  int64_t number = payload_to_number(data, item.sensor_value_type, item.offset, item.bitmask);
530
584
 
531
585
  float float_value;
532
- if (item.sensor_value_type == SensorValueType::FP32 || item.sensor_value_type == SensorValueType::FP32_R) {
586
+ if (value_type_is_float(item.sensor_value_type)) {
533
587
  float_value = bit_cast<float>(static_cast<uint32_t>(number));
534
588
  } else {
535
589
  float_value = static_cast<float>(number);
@@ -541,7 +595,7 @@ inline float payload_to_float(const std::vector<uint8_t> &data, const SensorItem
541
595
  inline std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type) {
542
596
  int64_t val;
543
597
 
544
- if (value_type == SensorValueType::FP32 || value_type == SensorValueType::FP32_R) {
598
+ if (value_type_is_float(value_type)) {
545
599
  val = bit_cast<uint32_t>(value);
546
600
  } else {
547
601
  val = llroundf(value);
@@ -34,7 +34,6 @@ class MopekaProCheck : public Component, public esp32_ble_tracker::ESPBTDeviceLi
34
34
 
35
35
  bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
36
36
  void dump_config() override;
37
- float get_setup_priority() const override { return setup_priority::DATA; }
38
37
  void set_min_signal_quality(SensorReadQuality min) { this->min_signal_quality_ = min; };
39
38
 
40
39
  void set_level(sensor::Sensor *level) { level_ = level; };
@@ -48,7 +48,6 @@ class MopekaStdCheck : public Component, public esp32_ble_tracker::ESPBTDeviceLi
48
48
 
49
49
  bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
50
50
  void dump_config() override;
51
- float get_setup_priority() const override { return setup_priority::DATA; }
52
51
 
53
52
  void set_level(sensor::Sensor *level) { this->level_ = level; };
54
53
  void set_temperature(sensor::Sensor *temperature) { this->temperature_ = temperature; };
@@ -91,8 +91,6 @@ class MPL3115A2Component : public PollingComponent, public i2c::I2CDevice {
91
91
  void dump_config() override;
92
92
  void update() override;
93
93
 
94
- float get_setup_priority() const override { return setup_priority::DATA; }
95
-
96
94
  protected:
97
95
  sensor::Sensor *temperature_{nullptr};
98
96
  sensor::Sensor *altitude_{nullptr};
@@ -5,6 +5,7 @@ from esphome.automation import Condition
5
5
  import esphome.codegen as cg
6
6
  from esphome.components import logger
7
7
  from esphome.components.esp32 import add_idf_sdkconfig_option
8
+ from esphome.config_helpers import filter_source_files_from_platform
8
9
  import esphome.config_validation as cv
9
10
  from esphome.const import (
10
11
  CONF_AVAILABILITY,
@@ -54,6 +55,7 @@ from esphome.const import (
54
55
  PLATFORM_BK72XX,
55
56
  PLATFORM_ESP32,
56
57
  PLATFORM_ESP8266,
58
+ PlatformFramework,
57
59
  )
58
60
  from esphome.core import CORE, coroutine_with_priority
59
61
 
@@ -68,6 +70,7 @@ def AUTO_LOAD():
68
70
 
69
71
  CONF_DISCOVER_IP = "discover_ip"
70
72
  CONF_IDF_SEND_ASYNC = "idf_send_async"
73
+ CONF_WAIT_FOR_CONNECTION = "wait_for_connection"
71
74
 
72
75
 
73
76
  def validate_message_just_topic(value):
@@ -298,6 +301,7 @@ CONFIG_SCHEMA = cv.All(
298
301
  }
299
302
  ),
300
303
  cv.Optional(CONF_PUBLISH_NAN_AS_NONE, default=False): cv.boolean,
304
+ cv.Optional(CONF_WAIT_FOR_CONNECTION, default=False): cv.boolean,
301
305
  }
302
306
  ),
303
307
  validate_config,
@@ -453,6 +457,8 @@ async def to_code(config):
453
457
 
454
458
  cg.add(var.set_publish_nan_as_none(config[CONF_PUBLISH_NAN_AS_NONE]))
455
459
 
460
+ cg.add(var.set_wait_for_connection(config[CONF_WAIT_FOR_CONNECTION]))
461
+
456
462
 
457
463
  MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema(
458
464
  {
@@ -592,3 +598,13 @@ async def mqtt_enable_to_code(config, action_id, template_arg, args):
592
598
  async def mqtt_disable_to_code(config, action_id, template_arg, args):
593
599
  paren = await cg.get_variable(config[CONF_ID])
594
600
  return cg.new_Pvariable(action_id, template_arg, paren)
601
+
602
+
603
+ FILTER_SOURCE_FILES = filter_source_files_from_platform(
604
+ {
605
+ "mqtt_backend_esp32.cpp": {
606
+ PlatformFramework.ESP32_ARDUINO,
607
+ PlatformFramework.ESP32_IDF,
608
+ },
609
+ }
610
+ )
@@ -55,7 +55,8 @@ void MQTTAlarmControlPanelComponent::dump_config() {
55
55
  }
56
56
 
57
57
  void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
58
- JsonArray supported_features = root.createNestedArray(MQTT_SUPPORTED_FEATURES);
58
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
59
+ JsonArray supported_features = root[MQTT_SUPPORTED_FEATURES].to<JsonArray>();
59
60
  const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features();
60
61
  if (acp_supported_features & ACP_FEAT_ARM_AWAY) {
61
62
  supported_features.add("arm_away");
@@ -17,7 +17,8 @@ enum class MQTTClientDisconnectReason : int8_t {
17
17
  MQTT_MALFORMED_CREDENTIALS = 4,
18
18
  MQTT_NOT_AUTHORIZED = 5,
19
19
  ESP8266_NOT_ENOUGH_SPACE = 6,
20
- TLS_BAD_FINGERPRINT = 7
20
+ TLS_BAD_FINGERPRINT = 7,
21
+ DNS_RESOLVE_ERROR = 8
21
22
  };
22
23
 
23
24
  /// internal struct for MQTT messages.
@@ -6,6 +6,7 @@
6
6
  #include <string>
7
7
  #include "esphome/core/helpers.h"
8
8
  #include "esphome/core/log.h"
9
+ #include "esphome/core/application.h"
9
10
 
10
11
  namespace esphome {
11
12
  namespace mqtt {
@@ -13,49 +14,6 @@ namespace mqtt {
13
14
  static const char *const TAG = "mqtt.idf";
14
15
 
15
16
  bool MQTTBackendESP32::initialize_() {
16
- #if ESP_IDF_VERSION_MAJOR < 5
17
- mqtt_cfg_.user_context = (void *) this;
18
- mqtt_cfg_.buffer_size = MQTT_BUFFER_SIZE;
19
-
20
- mqtt_cfg_.host = this->host_.c_str();
21
- mqtt_cfg_.port = this->port_;
22
- mqtt_cfg_.keepalive = this->keep_alive_;
23
- mqtt_cfg_.disable_clean_session = !this->clean_session_;
24
-
25
- if (!this->username_.empty()) {
26
- mqtt_cfg_.username = this->username_.c_str();
27
- if (!this->password_.empty()) {
28
- mqtt_cfg_.password = this->password_.c_str();
29
- }
30
- }
31
-
32
- if (!this->lwt_topic_.empty()) {
33
- mqtt_cfg_.lwt_topic = this->lwt_topic_.c_str();
34
- this->mqtt_cfg_.lwt_qos = this->lwt_qos_;
35
- this->mqtt_cfg_.lwt_retain = this->lwt_retain_;
36
-
37
- if (!this->lwt_message_.empty()) {
38
- mqtt_cfg_.lwt_msg = this->lwt_message_.c_str();
39
- mqtt_cfg_.lwt_msg_len = this->lwt_message_.size();
40
- }
41
- }
42
-
43
- if (!this->client_id_.empty()) {
44
- mqtt_cfg_.client_id = this->client_id_.c_str();
45
- }
46
- if (ca_certificate_.has_value()) {
47
- mqtt_cfg_.cert_pem = ca_certificate_.value().c_str();
48
- mqtt_cfg_.skip_cert_common_name_check = skip_cert_cn_check_;
49
- mqtt_cfg_.transport = MQTT_TRANSPORT_OVER_SSL;
50
-
51
- if (this->cl_certificate_.has_value() && this->cl_key_.has_value()) {
52
- mqtt_cfg_.client_cert_pem = this->cl_certificate_.value().c_str();
53
- mqtt_cfg_.client_key_pem = this->cl_key_.value().c_str();
54
- }
55
- } else {
56
- mqtt_cfg_.transport = MQTT_TRANSPORT_OVER_TCP;
57
- }
58
- #else
59
17
  mqtt_cfg_.broker.address.hostname = this->host_.c_str();
60
18
  mqtt_cfg_.broker.address.port = this->port_;
61
19
  mqtt_cfg_.session.keepalive = this->keep_alive_;
@@ -94,15 +52,30 @@ bool MQTTBackendESP32::initialize_() {
94
52
  } else {
95
53
  mqtt_cfg_.broker.address.transport = MQTT_TRANSPORT_OVER_TCP;
96
54
  }
97
- #endif
55
+
98
56
  auto *mqtt_client = esp_mqtt_client_init(&mqtt_cfg_);
99
57
  if (mqtt_client) {
100
58
  handler_.reset(mqtt_client);
101
59
  is_initalized_ = true;
102
60
  esp_mqtt_client_register_event(mqtt_client, MQTT_EVENT_ANY, mqtt_event_handler, this);
61
+ #if defined(USE_MQTT_IDF_ENQUEUE)
62
+ // Create the task only after MQTT client is initialized successfully
63
+ // Use larger stack size when TLS is enabled
64
+ size_t stack_size = this->ca_certificate_.has_value() ? TASK_STACK_SIZE_TLS : TASK_STACK_SIZE;
65
+ xTaskCreate(esphome_mqtt_task, "esphome_mqtt", stack_size, (void *) this, TASK_PRIORITY, &this->task_handle_);
66
+ if (this->task_handle_ == nullptr) {
67
+ ESP_LOGE(TAG, "Failed to create MQTT task");
68
+ // Clean up MQTT client since we can't start the async task
69
+ handler_.reset();
70
+ is_initalized_ = false;
71
+ return false;
72
+ }
73
+ // Set the task handle so the queue can notify it
74
+ this->mqtt_queue_.set_task_to_notify(this->task_handle_);
75
+ #endif
103
76
  return true;
104
77
  } else {
105
- ESP_LOGE(TAG, "Failed to initialize IDF-MQTT");
78
+ ESP_LOGE(TAG, "Failed to init client");
106
79
  return false;
107
80
  }
108
81
  }
@@ -115,6 +88,26 @@ void MQTTBackendESP32::loop() {
115
88
  mqtt_event_handler_(event);
116
89
  mqtt_events_.pop();
117
90
  }
91
+
92
+ #if defined(USE_MQTT_IDF_ENQUEUE)
93
+ // Periodically log dropped messages to avoid blocking during spikes.
94
+ // During high load, many messages can be dropped in quick succession.
95
+ // Logging each drop immediately would flood the logs and potentially
96
+ // cause more drops if MQTT logging is enabled (cascade effect).
97
+ // Instead, we accumulate the count and log a summary periodically.
98
+ // IMPORTANT: Don't move this to the scheduler - if drops are due to memory
99
+ // pressure, the scheduler's heap allocations would make things worse.
100
+ uint32_t now = App.get_loop_component_start_time();
101
+ // Handle rollover: (now - last_time) works correctly with unsigned arithmetic
102
+ // even when now < last_time due to rollover
103
+ if ((now - this->last_dropped_log_time_) >= DROP_LOG_INTERVAL_MS) {
104
+ uint16_t dropped = this->mqtt_queue_.get_and_reset_dropped_count();
105
+ if (dropped > 0) {
106
+ ESP_LOGW(TAG, "Dropped %u messages (%us)", dropped, DROP_LOG_INTERVAL_MS / 1000);
107
+ }
108
+ this->last_dropped_log_time_ = now;
109
+ }
110
+ #endif
118
111
  }
119
112
 
120
113
  void MQTTBackendESP32::mqtt_event_handler_(const Event &event) {
@@ -127,12 +120,20 @@ void MQTTBackendESP32::mqtt_event_handler_(const Event &event) {
127
120
  case MQTT_EVENT_CONNECTED:
128
121
  ESP_LOGV(TAG, "MQTT_EVENT_CONNECTED");
129
122
  this->is_connected_ = true;
123
+ #if defined(USE_MQTT_IDF_ENQUEUE)
124
+ this->last_dropped_log_time_ = 0;
125
+ xTaskNotifyGive(this->task_handle_);
126
+ #endif
130
127
  this->on_connect_.call(event.session_present);
131
128
  break;
132
129
  case MQTT_EVENT_DISCONNECTED:
133
130
  ESP_LOGV(TAG, "MQTT_EVENT_DISCONNECTED");
134
131
  // TODO is there a way to get the disconnect reason?
135
132
  this->is_connected_ = false;
133
+ #if defined(USE_MQTT_IDF_ENQUEUE)
134
+ this->last_dropped_log_time_ = 0;
135
+ xTaskNotifyGive(this->task_handle_);
136
+ #endif
136
137
  this->on_disconnect_.call(MQTTClientDisconnectReason::TCP_DISCONNECTED);
137
138
  break;
138
139
 
@@ -152,11 +153,15 @@ void MQTTBackendESP32::mqtt_event_handler_(const Event &event) {
152
153
  case MQTT_EVENT_DATA: {
153
154
  static std::string topic;
154
155
  if (!event.topic.empty()) {
156
+ // When a single message arrives as multiple chunks, the topic will be empty
157
+ // on any but the first message, leading to event.topic being an empty string.
158
+ // To ensure handlers get the correct topic, cache the last seen topic to
159
+ // simulate always receiving the topic from underlying library
155
160
  topic = event.topic;
156
161
  }
157
162
  ESP_LOGV(TAG, "MQTT_EVENT_DATA %s", topic.c_str());
158
- this->on_message_.call(!event.topic.empty() ? topic.c_str() : nullptr, event.data.data(), event.data.size(),
159
- event.current_data_offset, event.total_data_len);
163
+ this->on_message_.call(topic.c_str(), event.data.data(), event.data.size(), event.current_data_offset,
164
+ event.total_data_len);
160
165
  } break;
161
166
  case MQTT_EVENT_ERROR:
162
167
  ESP_LOGE(TAG, "MQTT_EVENT_ERROR");
@@ -188,6 +193,86 @@ void MQTTBackendESP32::mqtt_event_handler(void *handler_args, esp_event_base_t b
188
193
  }
189
194
  }
190
195
 
196
+ #if defined(USE_MQTT_IDF_ENQUEUE)
197
+ void MQTTBackendESP32::esphome_mqtt_task(void *params) {
198
+ MQTTBackendESP32 *this_mqtt = (MQTTBackendESP32 *) params;
199
+
200
+ while (true) {
201
+ // Wait for notification indefinitely
202
+ ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
203
+
204
+ // Process all queued items
205
+ struct QueueElement *elem;
206
+ while ((elem = this_mqtt->mqtt_queue_.pop()) != nullptr) {
207
+ if (this_mqtt->is_connected_) {
208
+ switch (elem->type) {
209
+ case MQTT_QUEUE_TYPE_SUBSCRIBE:
210
+ esp_mqtt_client_subscribe(this_mqtt->handler_.get(), elem->topic, elem->qos);
211
+ break;
212
+
213
+ case MQTT_QUEUE_TYPE_UNSUBSCRIBE:
214
+ esp_mqtt_client_unsubscribe(this_mqtt->handler_.get(), elem->topic);
215
+ break;
216
+
217
+ case MQTT_QUEUE_TYPE_PUBLISH:
218
+ esp_mqtt_client_publish(this_mqtt->handler_.get(), elem->topic, elem->payload, elem->payload_len, elem->qos,
219
+ elem->retain);
220
+ break;
221
+
222
+ default:
223
+ ESP_LOGE(TAG, "Invalid operation type from MQTT queue");
224
+ break;
225
+ }
226
+ }
227
+ this_mqtt->mqtt_event_pool_.release(elem);
228
+ }
229
+ }
230
+
231
+ // Clean up any remaining items in the queue
232
+ struct QueueElement *elem;
233
+ while ((elem = this_mqtt->mqtt_queue_.pop()) != nullptr) {
234
+ this_mqtt->mqtt_event_pool_.release(elem);
235
+ }
236
+
237
+ // Note: EventPool destructor will clean up the pool itself
238
+ // Task will delete itself
239
+ vTaskDelete(nullptr);
240
+ }
241
+
242
+ bool MQTTBackendESP32::enqueue_(MqttQueueTypeT type, const char *topic, int qos, bool retain, const char *payload,
243
+ size_t len) {
244
+ auto *elem = this->mqtt_event_pool_.allocate();
245
+
246
+ if (!elem) {
247
+ // Queue is full - increment counter but don't log immediately.
248
+ // Logging here can cause a cascade effect: if MQTT logging is enabled,
249
+ // each dropped message would generate a log message, which could itself
250
+ // be sent via MQTT, causing more drops and more logs in a feedback loop
251
+ // that eventually triggers a watchdog reset. Instead, we log periodically
252
+ // in loop() to prevent blocking the event loop during spikes.
253
+ this->mqtt_queue_.increment_dropped_count();
254
+ return false;
255
+ }
256
+
257
+ elem->type = type;
258
+ elem->qos = qos;
259
+ elem->retain = retain;
260
+
261
+ // Use the helper to allocate and copy data
262
+ if (!elem->set_data(topic, payload, len)) {
263
+ // Allocation failed, return elem to pool
264
+ this->mqtt_event_pool_.release(elem);
265
+ // Increment counter without logging to avoid cascade effect during memory pressure
266
+ this->mqtt_queue_.increment_dropped_count();
267
+ return false;
268
+ }
269
+
270
+ // Push to queue - always succeeds since we allocated from the pool
271
+ this->mqtt_queue_.push(elem);
272
+ return true;
273
+ }
274
+ #endif // USE_MQTT_IDF_ENQUEUE
275
+
191
276
  } // namespace mqtt
192
277
  } // namespace esphome
193
278
  #endif // USE_ESP32