esphome 2024.12.4__py3-none-any.whl → 2025.2.0b2__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 (358) hide show
  1. esphome/__main__.py +16 -3
  2. esphome/components/adc/__init__.py +17 -11
  3. esphome/components/adc/adc_sensor.h +17 -0
  4. esphome/components/adc/adc_sensor_common.cpp +55 -0
  5. esphome/components/adc/adc_sensor_esp32.cpp +8 -5
  6. esphome/components/adc/adc_sensor_esp8266.cpp +10 -6
  7. esphome/components/adc/adc_sensor_libretiny.cpp +11 -6
  8. esphome/components/adc/adc_sensor_rp2040.cpp +13 -10
  9. esphome/components/adc/sensor.py +9 -3
  10. esphome/components/ads1115/ads1115.cpp +56 -7
  11. esphome/components/ads1115/ads1115.h +13 -1
  12. esphome/components/ads1115/sensor/__init__.py +16 -0
  13. esphome/components/ads1115/sensor/ads1115_sensor.cpp +2 -1
  14. esphome/components/ads1115/sensor/ads1115_sensor.h +2 -0
  15. esphome/components/animation/__init__.py +23 -261
  16. esphome/components/animation/animation.cpp +2 -2
  17. esphome/components/animation/animation.h +2 -1
  18. esphome/components/api/api_pb2.cpp +14 -0
  19. esphome/components/api/api_pb2.h +1 -0
  20. esphome/components/audio/__init__.py +112 -0
  21. esphome/components/audio/audio.cpp +67 -0
  22. esphome/components/audio/audio.h +125 -7
  23. esphome/components/audio/audio_decoder.cpp +361 -0
  24. esphome/components/audio/audio_decoder.h +135 -0
  25. esphome/components/audio/audio_reader.cpp +308 -0
  26. esphome/components/audio/audio_reader.h +85 -0
  27. esphome/components/audio/audio_resampler.cpp +159 -0
  28. esphome/components/audio/audio_resampler.h +101 -0
  29. esphome/components/audio/audio_transfer_buffer.cpp +165 -0
  30. esphome/components/audio/audio_transfer_buffer.h +139 -0
  31. esphome/components/audio_adc/__init__.py +41 -0
  32. esphome/components/audio_adc/audio_adc.h +17 -0
  33. esphome/components/audio_adc/automation.h +23 -0
  34. esphome/components/bk72xx/__init__.py +1 -0
  35. esphome/components/ble_client/ble_client.cpp +1 -2
  36. esphome/components/ble_client/sensor/__init__.py +1 -1
  37. esphome/components/ble_client/text_sensor/__init__.py +1 -1
  38. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +5 -0
  39. esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -0
  40. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +5 -0
  41. esphome/components/ch422g/ch422g.h +2 -0
  42. esphome/components/climate/__init__.py +1 -1
  43. esphome/components/climate_ir/climate_ir.cpp +2 -1
  44. esphome/components/coolix/coolix.cpp +2 -1
  45. esphome/components/cse7766/cse7766.cpp +8 -16
  46. esphome/components/custom/__init__.py +0 -3
  47. esphome/components/custom/binary_sensor/__init__.py +2 -28
  48. esphome/components/custom/climate/__init__.py +2 -27
  49. esphome/components/custom/cover/__init__.py +2 -27
  50. esphome/components/custom/light/__init__.py +2 -27
  51. esphome/components/custom/output/__init__.py +2 -58
  52. esphome/components/custom/sensor/__init__.py +2 -24
  53. esphome/components/custom/switch/__init__.py +2 -24
  54. esphome/components/custom/text_sensor/__init__.py +2 -29
  55. esphome/components/custom_component/__init__.py +3 -27
  56. esphome/components/daly_bms/daly_bms.cpp +6 -0
  57. esphome/components/daly_bms/daly_bms.h +2 -0
  58. esphome/components/daly_bms/sensor.py +6 -0
  59. esphome/components/debug/debug_component.cpp +4 -0
  60. esphome/components/debug/debug_component.h +14 -0
  61. esphome/components/debug/debug_esp32.cpp +154 -74
  62. esphome/components/dfplayer/dfplayer.cpp +15 -2
  63. esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp +2 -1
  64. esphome/components/dht/dht.cpp +2 -1
  65. esphome/components/display/__init__.py +18 -5
  66. esphome/components/display/display.cpp +2 -1
  67. esphome/components/display/rect.cpp +2 -1
  68. esphome/components/es7210/__init__.py +0 -0
  69. esphome/components/es7210/audio_adc.py +51 -0
  70. esphome/components/es7210/es7210.cpp +228 -0
  71. esphome/components/es7210/es7210.h +62 -0
  72. esphome/components/es7210/es7210_const.h +129 -0
  73. esphome/components/es7243e/__init__.py +0 -0
  74. esphome/components/es7243e/audio_adc.py +34 -0
  75. esphome/components/es7243e/es7243e.cpp +125 -0
  76. esphome/components/es7243e/es7243e.h +37 -0
  77. esphome/components/es7243e/es7243e_const.h +54 -0
  78. esphome/components/es8156/__init__.py +0 -0
  79. esphome/components/es8156/audio_dac.py +27 -0
  80. esphome/components/es8156/es8156.cpp +87 -0
  81. esphome/components/es8156/es8156.h +51 -0
  82. esphome/components/es8156/es8156_const.h +68 -0
  83. esphome/components/es8311/audio_dac.py +1 -2
  84. esphome/components/esp32/__init__.py +1 -0
  85. esphome/components/esp32/core.cpp +5 -1
  86. esphome/components/esp32/gpio.h +2 -0
  87. esphome/components/esp32_ble/__init__.py +39 -0
  88. esphome/components/esp32_ble/queue.h +4 -4
  89. esphome/components/esp32_ble_client/ble_client_base.cpp +46 -0
  90. esphome/components/esp32_ble_client/ble_client_base.h +2 -0
  91. esphome/components/esp32_ble_server/__init__.py +582 -12
  92. esphome/components/esp32_ble_server/ble_characteristic.cpp +48 -60
  93. esphome/components/esp32_ble_server/ble_characteristic.h +24 -17
  94. esphome/components/esp32_ble_server/ble_descriptor.cpp +21 -9
  95. esphome/components/esp32_ble_server/ble_descriptor.h +17 -6
  96. esphome/components/esp32_ble_server/ble_server.cpp +62 -67
  97. esphome/components/esp32_ble_server/ble_server.h +28 -32
  98. esphome/components/esp32_ble_server/ble_server_automations.cpp +77 -0
  99. esphome/components/esp32_ble_server/ble_server_automations.h +115 -0
  100. esphome/components/esp32_ble_server/ble_service.cpp +17 -15
  101. esphome/components/esp32_ble_server/ble_service.h +10 -14
  102. esphome/components/esp32_ble_tracker/__init__.py +6 -39
  103. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +33 -10
  104. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +8 -4
  105. esphome/components/esp32_improv/__init__.py +2 -8
  106. esphome/components/esp32_improv/esp32_improv_component.cpp +21 -20
  107. esphome/components/esp32_improv/esp32_improv_component.h +3 -4
  108. esphome/components/esp32_rmt/__init__.py +28 -3
  109. esphome/components/esp32_rmt_led_strip/led_strip.cpp +73 -6
  110. esphome/components/esp32_rmt_led_strip/led_strip.h +21 -3
  111. esphome/components/esp32_rmt_led_strip/light.py +72 -7
  112. esphome/components/esp32_touch/esp32_touch.cpp +5 -0
  113. esphome/components/esp8266/__init__.py +1 -0
  114. esphome/components/esp8266/gpio.h +1 -0
  115. esphome/components/ethernet/__init__.py +10 -10
  116. esphome/components/event/event.cpp +4 -2
  117. esphome/components/event/event.h +2 -0
  118. esphome/components/event_emitter/__init__.py +5 -0
  119. esphome/components/event_emitter/event_emitter.cpp +14 -0
  120. esphome/components/event_emitter/event_emitter.h +63 -0
  121. esphome/components/gcja5/gcja5.cpp +2 -1
  122. esphome/components/graph/graph.cpp +4 -9
  123. esphome/components/haier/haier_base.cpp +2 -1
  124. esphome/components/haier/hon_climate.cpp +2 -1
  125. esphome/components/heatpumpir/heatpumpir.cpp +2 -1
  126. esphome/components/host/__init__.py +1 -0
  127. esphome/components/host/gpio.h +1 -0
  128. esphome/components/http_request/http_request.h +2 -2
  129. esphome/components/http_request/http_request_arduino.cpp +1 -1
  130. esphome/components/http_request/http_request_idf.cpp +1 -1
  131. esphome/components/i2c/i2c_bus_esp_idf.cpp +4 -0
  132. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +7 -5
  133. esphome/components/i2s_audio/speaker/__init__.py +53 -6
  134. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +92 -46
  135. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +8 -0
  136. esphome/components/ili9xxx/display.py +29 -11
  137. esphome/components/ili9xxx/ili9xxx_display.cpp +2 -5
  138. esphome/components/ili9xxx/ili9xxx_display.h +2 -1
  139. esphome/components/image/__init__.py +443 -255
  140. esphome/components/image/image.cpp +115 -61
  141. esphome/components/image/image.h +15 -24
  142. esphome/components/json/json_util.cpp +8 -34
  143. esphome/components/libretiny/__init__.py +1 -0
  144. esphome/components/libretiny/gpio_arduino.h +1 -0
  145. esphome/components/light/light_color_values.h +1 -1
  146. esphome/components/logger/__init__.py +43 -7
  147. esphome/components/logger/logger.cpp +16 -11
  148. esphome/components/logger/logger.h +11 -7
  149. esphome/components/logger/select/__init__.py +29 -0
  150. esphome/components/logger/select/logger_level_select.cpp +27 -0
  151. esphome/components/logger/select/logger_level_select.h +15 -0
  152. esphome/components/lvgl/__init__.py +96 -73
  153. esphome/components/lvgl/automation.py +39 -7
  154. esphome/components/lvgl/defines.py +8 -2
  155. esphome/components/lvgl/lvgl_esphome.cpp +8 -15
  156. esphome/components/lvgl/lvgl_esphome.h +20 -5
  157. esphome/components/lvgl/schemas.py +25 -14
  158. esphome/components/lvgl/trigger.py +27 -3
  159. esphome/components/lvgl/widgets/dropdown.py +1 -1
  160. esphome/components/lvgl/widgets/keyboard.py +8 -1
  161. esphome/components/lvgl/widgets/meter.py +2 -1
  162. esphome/components/lvgl/widgets/msgbox.py +1 -1
  163. esphome/components/lvgl/widgets/obj.py +1 -12
  164. esphome/components/lvgl/widgets/page.py +37 -2
  165. esphome/components/lvgl/widgets/tabview.py +1 -1
  166. esphome/components/max6956/max6956.h +2 -0
  167. esphome/components/mcp23016/mcp23016.h +2 -0
  168. esphome/components/mcp23xxx_base/mcp23xxx_base.h +2 -0
  169. esphome/components/mdns/__init__.py +1 -1
  170. esphome/components/media_player/__init__.py +37 -8
  171. esphome/components/media_player/automation.h +11 -2
  172. esphome/components/media_player/media_player.cpp +8 -0
  173. esphome/components/media_player/media_player.h +8 -4
  174. esphome/components/micronova/switch/micronova_switch.cpp +4 -2
  175. esphome/components/midea/ac_automations.h +3 -1
  176. esphome/components/midea/air_conditioner.cpp +7 -5
  177. esphome/components/midea/air_conditioner.h +1 -1
  178. esphome/components/midea/climate.py +4 -2
  179. esphome/components/midea/ir_transmitter.h +36 -5
  180. esphome/components/mixer/__init__.py +0 -0
  181. esphome/components/mixer/speaker/__init__.py +172 -0
  182. esphome/components/mixer/speaker/automation.h +19 -0
  183. esphome/components/mixer/speaker/mixer_speaker.cpp +624 -0
  184. esphome/components/mixer/speaker/mixer_speaker.h +207 -0
  185. esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp +7 -13
  186. esphome/components/mpr121/mpr121.h +2 -0
  187. esphome/components/mqtt/__init__.py +1 -1
  188. esphome/components/mqtt/mqtt_client.cpp +7 -1
  189. esphome/components/mqtt/mqtt_client.h +1 -1
  190. esphome/components/mqtt/mqtt_climate.cpp +2 -2
  191. esphome/components/network/ip_address.h +2 -0
  192. esphome/components/nextion/automation.h +17 -0
  193. esphome/components/nextion/display.py +42 -17
  194. esphome/components/nextion/nextion.cpp +4 -10
  195. esphome/components/nextion/nextion.h +89 -82
  196. esphome/components/nextion/nextion_commands.cpp +10 -10
  197. esphome/components/ntc/sensor.py +2 -4
  198. esphome/components/online_image/__init__.py +98 -46
  199. esphome/components/online_image/bmp_image.cpp +101 -0
  200. esphome/components/online_image/bmp_image.h +40 -0
  201. esphome/components/online_image/image_decoder.cpp +28 -2
  202. esphome/components/online_image/image_decoder.h +24 -15
  203. esphome/components/online_image/jpeg_image.cpp +90 -0
  204. esphome/components/online_image/jpeg_image.h +34 -0
  205. esphome/components/online_image/online_image.cpp +112 -53
  206. esphome/components/online_image/online_image.h +24 -7
  207. esphome/components/online_image/png_image.cpp +7 -3
  208. esphome/components/online_image/png_image.h +2 -1
  209. esphome/components/opentherm/__init__.py +73 -7
  210. esphome/components/opentherm/automation.h +25 -0
  211. esphome/components/opentherm/const.py +1 -0
  212. esphome/components/opentherm/generate.py +39 -6
  213. esphome/components/opentherm/hub.cpp +117 -79
  214. esphome/components/opentherm/hub.h +31 -15
  215. esphome/components/opentherm/opentherm.cpp +47 -23
  216. esphome/components/opentherm/opentherm.h +27 -6
  217. esphome/components/opentherm/opentherm_macros.h +11 -0
  218. esphome/components/opentherm/schema.py +78 -1
  219. esphome/components/opentherm/validate.py +7 -2
  220. esphome/components/pca6416a/pca6416a.h +2 -0
  221. esphome/components/pca9554/pca9554.h +2 -0
  222. esphome/components/pcf8574/pcf8574.h +2 -0
  223. esphome/components/preferences/__init__.py +2 -4
  224. esphome/components/preferences/syncer.h +10 -3
  225. esphome/components/prometheus/prometheus_handler.cpp +313 -0
  226. esphome/components/prometheus/prometheus_handler.h +48 -7
  227. esphome/components/psram/psram.cpp +8 -1
  228. esphome/components/pulse_counter/pulse_counter_sensor.cpp +14 -9
  229. esphome/components/pulse_counter/pulse_counter_sensor.h +4 -4
  230. esphome/components/pulse_meter/pulse_meter_sensor.cpp +2 -0
  231. esphome/components/qspi_dbi/__init__.py +3 -0
  232. esphome/components/qspi_dbi/display.py +74 -47
  233. esphome/components/qspi_dbi/models.py +245 -2
  234. esphome/components/qspi_dbi/qspi_dbi.cpp +9 -16
  235. esphome/components/qspi_dbi/qspi_dbi.h +2 -2
  236. esphome/components/remote_base/__init__.py +77 -25
  237. esphome/components/remote_base/remote_base.cpp +1 -1
  238. esphome/components/remote_base/remote_base.h +20 -2
  239. esphome/components/remote_base/toto_protocol.cpp +100 -0
  240. esphome/components/remote_base/toto_protocol.h +45 -0
  241. esphome/components/remote_receiver/__init__.py +55 -10
  242. esphome/components/remote_receiver/remote_receiver.h +36 -3
  243. esphome/components/remote_receiver/remote_receiver_esp32.cpp +145 -6
  244. esphome/components/remote_transmitter/__init__.py +62 -4
  245. esphome/components/remote_transmitter/remote_transmitter.h +21 -2
  246. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +140 -4
  247. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +3 -3
  248. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +3 -3
  249. esphome/components/resampler/__init__.py +0 -0
  250. esphome/components/resampler/speaker/__init__.py +103 -0
  251. esphome/components/resampler/speaker/resampler_speaker.cpp +318 -0
  252. esphome/components/resampler/speaker/resampler_speaker.h +107 -0
  253. esphome/components/resistance/resistance_sensor.h +2 -3
  254. esphome/components/resistance/sensor.py +2 -9
  255. esphome/components/rotary_encoder/rotary_encoder.cpp +8 -4
  256. esphome/components/rp2040/__init__.py +1 -0
  257. esphome/components/rp2040/gpio.h +1 -0
  258. esphome/components/rtl87xx/__init__.py +2 -0
  259. esphome/components/sdl/binary_sensor.py +270 -0
  260. esphome/components/sdl/sdl_esphome.cpp +16 -0
  261. esphome/components/sdl/sdl_esphome.h +9 -0
  262. esphome/components/seeed_mr60bha2/binary_sensor.py +25 -0
  263. esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +26 -2
  264. esphome/components/seeed_mr60bha2/seeed_mr60bha2.h +9 -20
  265. esphome/components/seeed_mr60bha2/sensor.py +9 -1
  266. esphome/components/sn74hc165/sn74hc165.h +3 -0
  267. esphome/components/sn74hc595/sn74hc595.h +3 -0
  268. esphome/components/speaker/__init__.py +5 -4
  269. esphome/components/speaker/media_player/__init__.py +458 -0
  270. esphome/components/speaker/media_player/audio_pipeline.cpp +568 -0
  271. esphome/components/speaker/media_player/audio_pipeline.h +159 -0
  272. esphome/components/speaker/media_player/automation.h +26 -0
  273. esphome/components/speaker/media_player/speaker_media_player.cpp +577 -0
  274. esphome/components/speaker/media_player/speaker_media_player.h +160 -0
  275. esphome/components/speaker/speaker.h +20 -0
  276. esphome/components/spi/__init__.py +1 -5
  277. esphome/components/spi/spi.cpp +7 -1
  278. esphome/components/spi/spi.h +21 -2
  279. esphome/components/spi_led_strip/light.py +3 -5
  280. esphome/components/spi_led_strip/spi_led_strip.cpp +67 -0
  281. esphome/components/spi_led_strip/spi_led_strip.h +8 -60
  282. esphome/components/sprinkler/sprinkler.cpp +3 -1
  283. esphome/components/sx1509/sx1509_gpio_pin.h +2 -0
  284. esphome/components/tca9555/tca9555.h +2 -0
  285. esphome/components/toshiba/toshiba.cpp +2 -1
  286. esphome/components/tuya/light/tuya_light.cpp +4 -2
  287. esphome/components/uart/uart_component_esp32_arduino.cpp +2 -2
  288. esphome/components/uart/uart_component_esp_idf.cpp +2 -2
  289. esphome/components/udp/__init__.py +8 -2
  290. esphome/components/udp/udp_component.cpp +25 -56
  291. esphome/components/udp/udp_component.h +3 -0
  292. esphome/components/uponor_smatrix/sensor/__init__.py +14 -4
  293. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +5 -0
  294. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h +1 -0
  295. esphome/components/uptime/text_sensor/__init__.py +19 -0
  296. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +63 -0
  297. esphome/components/uptime/text_sensor/uptime_text_sensor.h +25 -0
  298. esphome/components/voice_assistant/voice_assistant.cpp +24 -14
  299. esphome/components/voice_assistant/voice_assistant.h +8 -0
  300. esphome/components/waveshare_epaper/display.py +22 -1
  301. esphome/components/waveshare_epaper/waveshare_213v3.cpp +9 -3
  302. esphome/components/waveshare_epaper/waveshare_epaper.cpp +1155 -44
  303. esphome/components/waveshare_epaper/waveshare_epaper.h +208 -7
  304. esphome/components/web_server/web_server.cpp +28 -6
  305. esphome/components/weikai/weikai.h +2 -0
  306. esphome/components/wifi/__init__.py +6 -6
  307. esphome/components/wifi/wifi_component.cpp +1 -1
  308. esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -1
  309. esphome/components/wireguard/__init__.py +2 -2
  310. esphome/components/xl9535/xl9535.h +2 -0
  311. esphome/components/xxtea/__init__.py +3 -0
  312. esphome/components/xxtea/xxtea.cpp +46 -0
  313. esphome/components/xxtea/xxtea.h +26 -0
  314. esphome/components/yashima/yashima.cpp +2 -1
  315. esphome/config.py +9 -5
  316. esphome/config_validation.py +55 -17
  317. esphome/const.py +7 -10
  318. esphome/core/__init__.py +6 -13
  319. esphome/core/base_automation.h +1 -0
  320. esphome/core/config.py +59 -72
  321. esphome/core/defines.h +9 -1
  322. esphome/core/gpio.h +7 -0
  323. esphome/core/helpers.cpp +19 -15
  324. esphome/core/helpers.h +57 -8
  325. esphome/core/log.h +9 -7
  326. esphome/cpp_generator.py +2 -2
  327. esphome/espota2.py +3 -2
  328. esphome/loader.py +12 -4
  329. esphome/log.py +5 -7
  330. esphome/yaml_util.py +2 -2
  331. {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/METADATA +12 -7
  332. {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/RECORD +341 -292
  333. esphome/components/custom/binary_sensor/custom_binary_sensor.cpp +0 -16
  334. esphome/components/custom/binary_sensor/custom_binary_sensor.h +0 -26
  335. esphome/components/custom/climate/custom_climate.h +0 -22
  336. esphome/components/custom/cover/custom_cover.h +0 -21
  337. esphome/components/custom/light/custom_light_output.h +0 -24
  338. esphome/components/custom/output/custom_output.h +0 -37
  339. esphome/components/custom/sensor/custom_sensor.cpp +0 -16
  340. esphome/components/custom/sensor/custom_sensor.h +0 -24
  341. esphome/components/custom/switch/custom_switch.cpp +0 -16
  342. esphome/components/custom/switch/custom_switch.h +0 -24
  343. esphome/components/custom/text_sensor/custom_text_sensor.cpp +0 -16
  344. esphome/components/custom/text_sensor/custom_text_sensor.h +0 -26
  345. esphome/components/custom_component/custom_component.h +0 -28
  346. esphome/components/esp32_ble_server/ble_2901.cpp +0 -18
  347. esphome/components/esp32_ble_server/ble_2901.h +0 -19
  348. esphome/components/resistance_sampler/__init__.py +0 -6
  349. esphome/components/resistance_sampler/resistance_sampler.h +0 -10
  350. esphome/components/uptime/{sensor.py → sensor/__init__.py} +3 -3
  351. /esphome/components/uptime/{uptime_seconds_sensor.cpp → sensor/uptime_seconds_sensor.cpp} +0 -0
  352. /esphome/components/uptime/{uptime_seconds_sensor.h → sensor/uptime_seconds_sensor.h} +0 -0
  353. /esphome/components/uptime/{uptime_timestamp_sensor.cpp → sensor/uptime_timestamp_sensor.cpp} +0 -0
  354. /esphome/components/uptime/{uptime_timestamp_sensor.h → sensor/uptime_timestamp_sensor.h} +0 -0
  355. {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/LICENSE +0 -0
  356. {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/WHEEL +0 -0
  357. {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/entry_points.txt +0 -0
  358. {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/top_level.txt +0 -0
@@ -1,37 +1,526 @@
1
+ import encodings
2
+
3
+ from esphome import automation
1
4
  import esphome.codegen as cg
2
5
  from esphome.components import esp32_ble
3
6
  from esphome.components.esp32 import add_idf_sdkconfig_option
7
+ from esphome.components.esp32_ble import bt_uuid
4
8
  import esphome.config_validation as cv
5
- from esphome.const import CONF_ID, CONF_MODEL
9
+ from esphome.config_validation import UNDEFINED
10
+ from esphome.const import (
11
+ CONF_DATA,
12
+ CONF_ESPHOME,
13
+ CONF_ID,
14
+ CONF_MAX_LENGTH,
15
+ CONF_MODEL,
16
+ CONF_NOTIFY,
17
+ CONF_ON_CONNECT,
18
+ CONF_ON_DISCONNECT,
19
+ CONF_PROJECT,
20
+ CONF_SERVICES,
21
+ CONF_TYPE,
22
+ CONF_UUID,
23
+ CONF_VALUE,
24
+ __version__ as ESPHOME_VERSION,
25
+ )
6
26
  from esphome.core import CORE
27
+ from esphome.schema_extractors import SCHEMA_EXTRACT
7
28
 
8
- AUTO_LOAD = ["esp32_ble"]
29
+ AUTO_LOAD = ["esp32_ble", "bytebuffer", "event_emitter"]
9
30
  CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"]
10
31
  DEPENDENCIES = ["esp32"]
32
+ DOMAIN = "esp32_ble_server"
11
33
 
34
+ CONF_ADVERTISE = "advertise"
35
+ CONF_BROADCAST = "broadcast"
36
+ CONF_CHARACTERISTICS = "characteristics"
37
+ CONF_DESCRIPTION = "description"
38
+ CONF_DESCRIPTORS = "descriptors"
39
+ CONF_ENDIANNESS = "endianness"
40
+ CONF_FIRMWARE_VERSION = "firmware_version"
41
+ CONF_INDICATE = "indicate"
12
42
  CONF_MANUFACTURER = "manufacturer"
13
43
  CONF_MANUFACTURER_DATA = "manufacturer_data"
44
+ CONF_ON_WRITE = "on_write"
45
+ CONF_READ = "read"
46
+ CONF_STRING = "string"
47
+ CONF_STRING_ENCODING = "string_encoding"
48
+ CONF_WRITE = "write"
49
+ CONF_WRITE_NO_RESPONSE = "write_no_response"
50
+
51
+ # Internal configuration keys
52
+ CONF_CHAR_VALUE_ACTION_ID_ = "char_value_action_id_"
53
+
54
+ # BLE reserverd UUIDs
55
+ CCCD_DESCRIPTOR_UUID = 0x2902
56
+ CUD_DESCRIPTOR_UUID = 0x2901
57
+ DEVICE_INFORMATION_SERVICE_UUID = 0x180A
58
+ MANUFACTURER_NAME_CHARACTERISTIC_UUID = 0x2A29
59
+ MODEL_CHARACTERISTIC_UUID = 0x2A24
60
+ FIRMWARE_VERSION_CHARACTERISTIC_UUID = 0x2A26
61
+
62
+ # Core key to store the global configuration
63
+ KEY_NOTIFY_REQUIRED = "notify_required"
64
+ KEY_SET_VALUE = "set_value"
14
65
 
15
66
  esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server")
67
+ ESPBTUUID_ns = cg.esphome_ns.namespace("esp32_ble").namespace("ESPBTUUID")
68
+ BLECharacteristic_ns = esp32_ble_server_ns.namespace("BLECharacteristic")
16
69
  BLEServer = esp32_ble_server_ns.class_(
17
70
  "BLEServer",
18
71
  cg.Component,
19
72
  esp32_ble.GATTsEventHandler,
20
73
  cg.Parented.template(esp32_ble.ESP32BLE),
21
74
  )
22
- BLEServiceComponent = esp32_ble_server_ns.class_("BLEServiceComponent")
75
+ esp32_ble_server_automations_ns = esp32_ble_server_ns.namespace(
76
+ "esp32_ble_server_automations"
77
+ )
78
+ BLETriggers_ns = esp32_ble_server_automations_ns.namespace("BLETriggers")
79
+ BLEDescriptor = esp32_ble_server_ns.class_("BLEDescriptor")
80
+ BLECharacteristic = esp32_ble_server_ns.class_("BLECharacteristic")
81
+ BLEService = esp32_ble_server_ns.class_("BLEService")
82
+ BLECharacteristicSetValueAction = esp32_ble_server_automations_ns.class_(
83
+ "BLECharacteristicSetValueAction", automation.Action
84
+ )
85
+ BLEDescriptorSetValueAction = esp32_ble_server_automations_ns.class_(
86
+ "BLEDescriptorSetValueAction", automation.Action
87
+ )
88
+ BLECharacteristicNotifyAction = esp32_ble_server_automations_ns.class_(
89
+ "BLECharacteristicNotifyAction", automation.Action
90
+ )
91
+ bytebuffer_ns = cg.esphome_ns.namespace("bytebuffer")
92
+ Endianness_ns = bytebuffer_ns.namespace("Endian")
93
+ ByteBuffer_ns = bytebuffer_ns.namespace("ByteBuffer")
94
+ ByteBuffer = bytebuffer_ns.class_("ByteBuffer")
95
+
96
+
97
+ PROPERTY_MAP = {
98
+ CONF_READ: BLECharacteristic_ns.PROPERTY_READ,
99
+ CONF_WRITE: BLECharacteristic_ns.PROPERTY_WRITE,
100
+ CONF_NOTIFY: BLECharacteristic_ns.PROPERTY_NOTIFY,
101
+ CONF_BROADCAST: BLECharacteristic_ns.PROPERTY_BROADCAST,
102
+ CONF_INDICATE: BLECharacteristic_ns.PROPERTY_INDICATE,
103
+ CONF_WRITE_NO_RESPONSE: BLECharacteristic_ns.PROPERTY_WRITE_NR,
104
+ }
105
+
106
+
107
+ class ValueType:
108
+ def __init__(self, type_, validator, length):
109
+ self.type_ = type_
110
+ self.validator = validator
111
+ self.length = length
112
+
113
+ def validate(self, value, encoding):
114
+ value = self.validator(value)
115
+ if self.type_ == "string":
116
+ try:
117
+ value.encode(encoding)
118
+ except UnicodeEncodeError as e:
119
+ raise cv.Invalid(str(e)) from e
120
+ return value
121
+
122
+
123
+ VALUE_TYPES = {
124
+ type_name: ValueType(type_name, validator, length)
125
+ for type_name, validator, length in (
126
+ ("uint8_t", cv.uint8_t, 1),
127
+ ("uint16_t", cv.uint16_t, 2),
128
+ ("uint32_t", cv.uint32_t, 4),
129
+ ("uint64_t", cv.uint64_t, 8),
130
+ ("int8_t", cv.int_range(-128, 127), 1),
131
+ ("int16_t", cv.int_range(-32768, 32767), 2),
132
+ ("int32_t", cv.int_range(-2147483648, 2147483647), 4),
133
+ ("int64_t", cv.int_range(-9223372036854775808, 9223372036854775807), 8),
134
+ ("float", cv.float_, 4),
135
+ ("double", cv.float_, 8),
136
+ ("string", cv.string_strict, None), # Length is variable
137
+ )
138
+ }
139
+
140
+
141
+ def validate_char_on_write(char_config):
142
+ if CONF_ON_WRITE in char_config:
143
+ if not char_config[CONF_WRITE] and not char_config[CONF_WRITE_NO_RESPONSE]:
144
+ raise cv.Invalid(
145
+ f"{CONF_ON_WRITE} requires the {CONF_WRITE} or {CONF_WRITE_NO_RESPONSE} property to be set"
146
+ )
147
+ return char_config
148
+
149
+
150
+ def validate_descriptor(desc_config):
151
+ if CONF_ON_WRITE in desc_config:
152
+ if not desc_config[CONF_WRITE]:
153
+ raise cv.Invalid(
154
+ f"{CONF_ON_WRITE} requires the {CONF_WRITE} property to be set"
155
+ )
156
+ if CONF_MAX_LENGTH not in desc_config:
157
+ value = desc_config[CONF_VALUE][CONF_DATA]
158
+ if cg.is_template(value):
159
+ raise cv.Invalid(
160
+ f"Descriptor {desc_config[CONF_UUID]} has a templatable value and the {CONF_MAX_LENGTH} property is not set"
161
+ )
162
+ if isinstance(value, list):
163
+ desc_config[CONF_MAX_LENGTH] = len(value)
164
+ elif isinstance(value, str):
165
+ desc_config[CONF_MAX_LENGTH] = len(
166
+ value.encode(desc_config[CONF_VALUE][CONF_STRING_ENCODING])
167
+ )
168
+ else:
169
+ desc_config[CONF_MAX_LENGTH] = VALUE_TYPES[
170
+ desc_config[CONF_VALUE][CONF_TYPE]
171
+ ].length
172
+ return desc_config
173
+
174
+
175
+ def validate_notify_action(config):
176
+ # Store the characteristic ID in the global data for the final validation
177
+ data = CORE.data.setdefault(DOMAIN, {}).setdefault(KEY_NOTIFY_REQUIRED, set())
178
+ data.add(config[CONF_ID])
179
+ return config
180
+
181
+
182
+ def validate_set_value_action(config):
183
+ # Store the characteristic ID in the global data for the final validation
184
+ data = CORE.data.setdefault(DOMAIN, {}).setdefault(KEY_SET_VALUE, set())
185
+ data.add(config[CONF_ID])
186
+ return config
187
+
188
+
189
+ def create_description_cud(char_config):
190
+ if CONF_DESCRIPTION not in char_config:
191
+ return char_config
192
+ # If the config displays a description, there cannot be a descriptor with the CUD UUID
193
+ for desc in char_config[CONF_DESCRIPTORS]:
194
+ if desc[CONF_UUID] == CUD_DESCRIPTOR_UUID:
195
+ raise cv.Invalid(
196
+ f"Characteristic {char_config[CONF_UUID]} has a description, but a CUD descriptor is already present"
197
+ )
198
+ # Manually add the CUD descriptor
199
+ char_config[CONF_DESCRIPTORS].append(
200
+ DESCRIPTOR_SCHEMA(
201
+ {
202
+ CONF_UUID: CUD_DESCRIPTOR_UUID,
203
+ CONF_READ: True,
204
+ CONF_WRITE: False,
205
+ CONF_VALUE: char_config[CONF_DESCRIPTION],
206
+ }
207
+ )
208
+ )
209
+ return char_config
210
+
211
+
212
+ def create_notify_cccd(char_config):
213
+ if not char_config[CONF_NOTIFY] and not char_config[CONF_INDICATE]:
214
+ return char_config
215
+ # If the CCCD descriptor is already present, return the config
216
+ for desc in char_config[CONF_DESCRIPTORS]:
217
+ if desc[CONF_UUID] == CCCD_DESCRIPTOR_UUID:
218
+ # Check if the WRITE property is set
219
+ if not desc[CONF_WRITE]:
220
+ raise cv.Invalid(
221
+ f"Characteristic {char_config[CONF_UUID]} has notify actions, but the CCCD descriptor does not have the {CONF_WRITE} property set"
222
+ )
223
+ return char_config
224
+ # Manually add the CCCD descriptor
225
+ char_config[CONF_DESCRIPTORS].append(
226
+ DESCRIPTOR_SCHEMA(
227
+ {
228
+ CONF_UUID: CCCD_DESCRIPTOR_UUID,
229
+ CONF_READ: True,
230
+ CONF_WRITE: True,
231
+ CONF_MAX_LENGTH: 2,
232
+ CONF_VALUE: [0, 0],
233
+ }
234
+ )
235
+ )
236
+ return char_config
237
+
238
+
239
+ def create_device_information_service(config):
240
+ # If there is already a device information service,
241
+ # there cannot be CONF_MODEL, CONF_MANUFACTURER or CONF_FIRMWARE_VERSION properties
242
+ for service in config[CONF_SERVICES]:
243
+ if service[CONF_UUID] == DEVICE_INFORMATION_SERVICE_UUID:
244
+ if (
245
+ CONF_MODEL in config
246
+ or CONF_MANUFACTURER in config
247
+ or CONF_FIRMWARE_VERSION in config
248
+ ):
249
+ raise cv.Invalid(
250
+ "Device information service already present, cannot add manufacturer, model or firmware version"
251
+ )
252
+ return config
253
+ project = CORE.raw_config[CONF_ESPHOME].get(CONF_PROJECT, {})
254
+ model = config.get(CONF_MODEL, project.get("name", CORE.data["esp32"]["board"]))
255
+ version = config.get(
256
+ CONF_FIRMWARE_VERSION, project.get("version", "ESPHome " + ESPHOME_VERSION)
257
+ )
258
+ # Manually add the device information service
259
+ config[CONF_SERVICES].append(
260
+ SERVICE_SCHEMA(
261
+ {
262
+ CONF_UUID: DEVICE_INFORMATION_SERVICE_UUID,
263
+ CONF_CHARACTERISTICS: [
264
+ {
265
+ CONF_UUID: MANUFACTURER_NAME_CHARACTERISTIC_UUID,
266
+ CONF_READ: True,
267
+ CONF_VALUE: config.get(CONF_MANUFACTURER, "ESPHome"),
268
+ },
269
+ {
270
+ CONF_UUID: MODEL_CHARACTERISTIC_UUID,
271
+ CONF_READ: True,
272
+ CONF_VALUE: model,
273
+ },
274
+ {
275
+ CONF_UUID: FIRMWARE_VERSION_CHARACTERISTIC_UUID,
276
+ CONF_READ: True,
277
+ CONF_VALUE: version,
278
+ },
279
+ ],
280
+ }
281
+ )
282
+ )
283
+ return config
284
+
285
+
286
+ def final_validate_config(config):
287
+ # Check if all characteristics that require notifications have the notify property set
288
+ for char_id in CORE.data.get(DOMAIN, {}).get(KEY_NOTIFY_REQUIRED, set()):
289
+ # Look for the characteristic in the configuration
290
+ char_config = [
291
+ char_conf
292
+ for service_conf in config[CONF_SERVICES]
293
+ for char_conf in service_conf[CONF_CHARACTERISTICS]
294
+ if char_conf[CONF_ID] == char_id
295
+ ][0]
296
+ if not char_config[CONF_NOTIFY]:
297
+ raise cv.Invalid(
298
+ f"Characteristic {char_config[CONF_UUID]} has notify actions and the {CONF_NOTIFY} property is not set"
299
+ )
300
+ for char_id in CORE.data.get(DOMAIN, {}).get(KEY_SET_VALUE, set()):
301
+ # Look for the characteristic in the configuration
302
+ char_config = [
303
+ char_conf
304
+ for service_conf in config[CONF_SERVICES]
305
+ for char_conf in service_conf[CONF_CHARACTERISTICS]
306
+ if char_conf[CONF_ID] == char_id
307
+ ][0]
308
+ if isinstance(char_config.get(CONF_VALUE, {}).get(CONF_DATA), cv.Lambda):
309
+ raise cv.Invalid(
310
+ f"Characteristic {char_config[CONF_UUID]} has both a set_value action and a templated value"
311
+ )
312
+ return config
313
+
314
+
315
+ def validate_value_type(value_config):
316
+ # If the value is a not a templatable, the type must be set
317
+ value = value_config[CONF_DATA]
318
+
319
+ if type_ := value_config.get(CONF_TYPE):
320
+ if cg.is_template(value):
321
+ raise cv.Invalid(
322
+ f'The "{CONF_TYPE}" property is not allowed for templatable values'
323
+ )
324
+ value_config[CONF_DATA] = VALUE_TYPES[type_].validate(
325
+ value, value_config[CONF_STRING_ENCODING]
326
+ )
327
+ elif isinstance(value, (float, int)):
328
+ raise cv.Invalid(
329
+ f'The "{CONF_TYPE}" property is required for the value "{value}"'
330
+ )
331
+ return value_config
332
+
333
+
334
+ def validate_encoding(value):
335
+ if value == SCHEMA_EXTRACT:
336
+ return cv.one_of("utf-8", "latin-1", "ascii", "utf-16", "utf-32")
337
+ value = encodings.normalize_encoding(value)
338
+ if not value:
339
+ raise cv.Invalid("Invalid encoding")
340
+ return value
341
+
23
342
 
343
+ def value_schema(default_type=UNDEFINED, templatable=True):
344
+ data_validators = [
345
+ cv.string_strict,
346
+ cv.int_,
347
+ cv.float_,
348
+ cv.All([cv.uint8_t], cv.Length(min=1)),
349
+ ]
350
+ if templatable:
351
+ data_validators.append(cv.returning_lambda)
352
+
353
+ return cv.maybe_simple_value(
354
+ cv.All(
355
+ {
356
+ cv.Required(CONF_DATA): cv.Any(*data_validators),
357
+ cv.Optional(CONF_TYPE, default=default_type): cv.one_of(
358
+ *VALUE_TYPES, lower=True
359
+ ),
360
+ cv.Optional(CONF_STRING_ENCODING, default="utf_8"): validate_encoding,
361
+ cv.Optional(CONF_ENDIANNESS, default="LITTLE"): cv.enum(
362
+ {
363
+ "LITTLE": Endianness_ns.LITTLE,
364
+ "BIG": Endianness_ns.BIG,
365
+ }
366
+ ),
367
+ },
368
+ validate_value_type,
369
+ ),
370
+ key=CONF_DATA,
371
+ )
372
+
373
+
374
+ DESCRIPTOR_SCHEMA = cv.All(
375
+ {
376
+ cv.GenerateID(): cv.declare_id(BLEDescriptor),
377
+ cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t),
378
+ cv.Optional(CONF_READ, default=True): cv.boolean,
379
+ cv.Optional(CONF_WRITE, default=True): cv.boolean,
380
+ cv.Optional(CONF_ON_WRITE): automation.validate_automation(single=True),
381
+ cv.Required(CONF_VALUE): value_schema(templatable=False),
382
+ cv.Optional(CONF_MAX_LENGTH): cv.uint16_t,
383
+ },
384
+ validate_descriptor,
385
+ )
386
+
387
+ CHARACTERISTIC_SCHEMA = cv.Schema(
388
+ {
389
+ cv.GenerateID(): cv.declare_id(BLECharacteristic),
390
+ cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t),
391
+ cv.Optional(CONF_VALUE): value_schema(templatable=True),
392
+ cv.GenerateID(CONF_CHAR_VALUE_ACTION_ID_): cv.declare_id(
393
+ BLECharacteristicSetValueAction
394
+ ),
395
+ cv.Optional(CONF_DESCRIPTORS, default=[]): cv.ensure_list(DESCRIPTOR_SCHEMA),
396
+ cv.Optional(CONF_ON_WRITE): automation.validate_automation(single=True),
397
+ cv.Optional(CONF_DESCRIPTION): value_schema(
398
+ default_type="string", templatable=False
399
+ ),
400
+ },
401
+ extra_schemas=[
402
+ validate_char_on_write,
403
+ create_description_cud,
404
+ create_notify_cccd,
405
+ ],
406
+ ).extend({cv.Optional(k, default=False): cv.boolean for k in PROPERTY_MAP})
407
+
408
+ SERVICE_SCHEMA = cv.Schema(
409
+ {
410
+ cv.GenerateID(): cv.declare_id(BLEService),
411
+ cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t),
412
+ cv.Optional(CONF_ADVERTISE, default=False): cv.boolean,
413
+ cv.Optional(CONF_CHARACTERISTICS, default=[]): cv.ensure_list(
414
+ CHARACTERISTIC_SCHEMA
415
+ ),
416
+ }
417
+ )
24
418
 
25
419
  CONFIG_SCHEMA = cv.Schema(
26
420
  {
27
421
  cv.GenerateID(): cv.declare_id(BLEServer),
28
422
  cv.GenerateID(esp32_ble.CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE),
29
- cv.Optional(CONF_MANUFACTURER, default="ESPHome"): cv.string,
30
- cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.hex_uint8_t]),
31
- cv.Optional(CONF_MODEL): cv.string,
32
- }
423
+ cv.Optional(CONF_MANUFACTURER): value_schema("string", templatable=False),
424
+ cv.Optional(CONF_MODEL): value_schema("string", templatable=False),
425
+ cv.Optional(CONF_FIRMWARE_VERSION): value_schema("string", templatable=False),
426
+ cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.uint8_t]),
427
+ cv.Optional(CONF_SERVICES, default=[]): cv.ensure_list(SERVICE_SCHEMA),
428
+ cv.Optional(CONF_ON_CONNECT): automation.validate_automation(single=True),
429
+ cv.Optional(CONF_ON_DISCONNECT): automation.validate_automation(single=True),
430
+ },
431
+ extra_schemas=[create_device_information_service],
33
432
  ).extend(cv.COMPONENT_SCHEMA)
34
433
 
434
+ FINAL_VALIDATE_SCHEMA = final_validate_config
435
+
436
+
437
+ def parse_properties(char_conf):
438
+ return sum(
439
+ (PROPERTY_MAP[k] for k in char_conf if k in PROPERTY_MAP and char_conf[k]),
440
+ start=0,
441
+ )
442
+
443
+
444
+ def parse_uuid(uuid):
445
+ # If the UUID is a int, use from_uint32
446
+ if isinstance(uuid, int):
447
+ return ESPBTUUID_ns.from_uint32(uuid)
448
+ # Otherwise, use ESPBTUUID_ns.from_raw
449
+ return ESPBTUUID_ns.from_raw(uuid)
450
+
451
+
452
+ async def parse_value(value_config, args):
453
+ value = value_config[CONF_DATA]
454
+ if isinstance(value, cv.Lambda):
455
+ return await cg.templatable(value, args, cg.std_vector.template(cg.uint8))
456
+
457
+ if isinstance(value, str):
458
+ value = list(value.encode(value_config[CONF_STRING_ENCODING]))
459
+ if isinstance(value, list):
460
+ return cg.std_vector.template(cg.uint8)(value)
461
+ val = cg.RawExpression(f"{value_config[CONF_TYPE]}({cg.safe_exp(value)})")
462
+ return ByteBuffer_ns.wrap(val, value_config[CONF_ENDIANNESS])
463
+
464
+
465
+ def calculate_num_handles(service_config):
466
+ total = 1 + len(service_config[CONF_CHARACTERISTICS]) * 2
467
+ total += sum(
468
+ len(char_conf[CONF_DESCRIPTORS])
469
+ for char_conf in service_config[CONF_CHARACTERISTICS]
470
+ )
471
+ return total
472
+
473
+
474
+ async def to_code_descriptor(descriptor_conf, char_var):
475
+ value = await parse_value(descriptor_conf[CONF_VALUE], {})
476
+ desc_var = cg.new_Pvariable(
477
+ descriptor_conf[CONF_ID],
478
+ parse_uuid(descriptor_conf[CONF_UUID]),
479
+ descriptor_conf[CONF_MAX_LENGTH],
480
+ descriptor_conf[CONF_READ],
481
+ descriptor_conf[CONF_WRITE],
482
+ )
483
+ cg.add(char_var.add_descriptor(desc_var))
484
+ cg.add(desc_var.set_value(value))
485
+ if CONF_ON_WRITE in descriptor_conf:
486
+ on_write_conf = descriptor_conf[CONF_ON_WRITE]
487
+ await automation.build_automation(
488
+ BLETriggers_ns.create_descriptor_on_write_trigger(desc_var),
489
+ [(cg.std_vector.template(cg.uint8), "x"), (cg.uint16, "id")],
490
+ on_write_conf,
491
+ )
492
+
493
+
494
+ async def to_code_characteristic(service_var, char_conf):
495
+ char_var = cg.Pvariable(
496
+ char_conf[CONF_ID],
497
+ service_var.create_characteristic(
498
+ parse_uuid(char_conf[CONF_UUID]),
499
+ parse_properties(char_conf),
500
+ ),
501
+ )
502
+ if CONF_ON_WRITE in char_conf:
503
+ on_write_conf = char_conf[CONF_ON_WRITE]
504
+ await automation.build_automation(
505
+ BLETriggers_ns.create_characteristic_on_write_trigger(char_var),
506
+ [(cg.std_vector.template(cg.uint8), "x"), (cg.uint16, "id")],
507
+ on_write_conf,
508
+ )
509
+ if CONF_VALUE in char_conf:
510
+ action_conf = {
511
+ CONF_ID: char_conf[CONF_ID],
512
+ CONF_VALUE: char_conf[CONF_VALUE],
513
+ }
514
+ value_action = await ble_server_characteristic_set_value(
515
+ action_conf,
516
+ char_conf[CONF_CHAR_VALUE_ACTION_ID_],
517
+ cg.TemplateArguments(),
518
+ {},
519
+ )
520
+ cg.add(value_action.play())
521
+ for descriptor_conf in char_conf[CONF_DESCRIPTORS]:
522
+ await to_code_descriptor(descriptor_conf, char_var)
523
+
35
524
 
36
525
  async def to_code(config):
37
526
  var = cg.new_Pvariable(config[CONF_ID])
@@ -42,13 +531,94 @@ async def to_code(config):
42
531
  cg.add(parent.register_gatts_event_handler(var))
43
532
  cg.add(parent.register_ble_status_event_handler(var))
44
533
  cg.add(var.set_parent(parent))
45
-
46
- cg.add(var.set_manufacturer(config[CONF_MANUFACTURER]))
47
534
  if CONF_MANUFACTURER_DATA in config:
48
535
  cg.add(var.set_manufacturer_data(config[CONF_MANUFACTURER_DATA]))
49
- if CONF_MODEL in config:
50
- cg.add(var.set_model(config[CONF_MODEL]))
536
+ for service_config in config[CONF_SERVICES]:
537
+ # Calculate the optimal number of handles based on the number of characteristics and descriptors
538
+ num_handles = calculate_num_handles(service_config)
539
+ service_var = cg.Pvariable(
540
+ service_config[CONF_ID],
541
+ var.create_service(
542
+ parse_uuid(service_config[CONF_UUID]),
543
+ service_config[CONF_ADVERTISE],
544
+ num_handles,
545
+ ),
546
+ )
547
+ for char_conf in service_config[CONF_CHARACTERISTICS]:
548
+ await to_code_characteristic(service_var, char_conf)
549
+ if service_config[CONF_UUID] == DEVICE_INFORMATION_SERVICE_UUID:
550
+ cg.add(var.set_device_information_service(service_var))
551
+ else:
552
+ cg.add(var.enqueue_start_service(service_var))
553
+ if CONF_ON_CONNECT in config:
554
+ await automation.build_automation(
555
+ BLETriggers_ns.create_server_on_connect_trigger(var),
556
+ [(cg.uint16, "id")],
557
+ config[CONF_ON_CONNECT],
558
+ )
559
+ if CONF_ON_DISCONNECT in config:
560
+ await automation.build_automation(
561
+ BLETriggers_ns.create_server_on_disconnect_trigger(var),
562
+ [(cg.uint16, "id")],
563
+ config[CONF_ON_DISCONNECT],
564
+ )
51
565
  cg.add_define("USE_ESP32_BLE_SERVER")
52
-
53
566
  if CORE.using_esp_idf:
54
567
  add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
568
+
569
+
570
+ @automation.register_action(
571
+ "ble_server.characteristic.set_value",
572
+ BLECharacteristicSetValueAction,
573
+ cv.All(
574
+ cv.Schema(
575
+ {
576
+ cv.Required(CONF_ID): cv.use_id(BLECharacteristic),
577
+ cv.Required(CONF_VALUE): value_schema(),
578
+ }
579
+ ),
580
+ validate_set_value_action,
581
+ ),
582
+ )
583
+ async def ble_server_characteristic_set_value(config, action_id, template_arg, args):
584
+ paren = await cg.get_variable(config[CONF_ID])
585
+ var = cg.new_Pvariable(action_id, template_arg, paren)
586
+ value = await parse_value(config[CONF_VALUE], args)
587
+ cg.add(var.set_buffer(value))
588
+ return var
589
+
590
+
591
+ @automation.register_action(
592
+ "ble_server.descriptor.set_value",
593
+ BLEDescriptorSetValueAction,
594
+ cv.Schema(
595
+ {
596
+ cv.Required(CONF_ID): cv.use_id(BLEDescriptor),
597
+ cv.Required(CONF_VALUE): value_schema(),
598
+ }
599
+ ),
600
+ )
601
+ async def ble_server_descriptor_set_value(config, action_id, template_arg, args):
602
+ paren = await cg.get_variable(config[CONF_ID])
603
+ var = cg.new_Pvariable(action_id, template_arg, paren)
604
+ value = await parse_value(config[CONF_VALUE], args)
605
+ cg.add(var.set_buffer(value))
606
+ return var
607
+
608
+
609
+ @automation.register_action(
610
+ "ble_server.characteristic.notify",
611
+ BLECharacteristicNotifyAction,
612
+ cv.All(
613
+ cv.Schema(
614
+ {
615
+ cv.Required(CONF_ID): cv.use_id(BLECharacteristic),
616
+ }
617
+ ),
618
+ validate_notify_action,
619
+ ),
620
+ )
621
+ async def ble_server_characteristic_notify(config, action_id, template_arg, args):
622
+ paren = await cg.get_variable(config[CONF_ID])
623
+ var = cg.new_Pvariable(action_id, template_arg, paren)
624
+ return var