esphome 2024.7.2__py3-none-any.whl → 2024.8.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 (353) hide show
  1. esphome/__main__.py +15 -81
  2. esphome/automation.py +1 -1
  3. esphome/codegen.py +53 -53
  4. esphome/components/ade7953/sensor.py +1 -1
  5. esphome/components/ade7953_spi/ade7953_spi.cpp +1 -1
  6. esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +5 -2
  7. esphome/components/airthings_wave_plus/airthings_wave_plus.h +2 -0
  8. esphome/components/airthings_wave_plus/sensor.py +12 -0
  9. esphome/components/alarm_control_panel/__init__.py +75 -66
  10. esphome/components/apds9306/__init__.py +4 -0
  11. esphome/components/apds9306/apds9306.cpp +151 -0
  12. esphome/components/apds9306/apds9306.h +66 -0
  13. esphome/components/apds9306/sensor.py +95 -0
  14. esphome/components/api/__init__.py +80 -52
  15. esphome/components/api/api_connection.cpp +14 -1
  16. esphome/components/api/api_pb2.cpp +33 -4
  17. esphome/components/api/api_pb2.h +8 -1
  18. esphome/components/api/api_server.cpp +10 -0
  19. esphome/components/api/api_server.h +3 -0
  20. esphome/components/atm90e32/__init__.py +7 -0
  21. esphome/components/atm90e32/atm90e32.cpp +68 -16
  22. esphome/components/atm90e32/atm90e32.h +20 -7
  23. esphome/components/atm90e32/atm90e32_reg.h +2 -0
  24. esphome/components/atm90e32/button/__init__.py +43 -0
  25. esphome/components/atm90e32/button/atm90e32_button.cpp +20 -0
  26. esphome/components/atm90e32/button/atm90e32_button.h +27 -0
  27. esphome/components/atm90e32/sensor.py +15 -11
  28. esphome/components/bedjet/bedjet_codec.h +1 -1
  29. esphome/components/binary/light/binary_light_output.h +3 -2
  30. esphome/components/binary_sensor/__init__.py +5 -5
  31. esphome/components/ble_client/__init__.py +3 -3
  32. esphome/components/ble_client/output/__init__.py +1 -1
  33. esphome/components/ble_client/sensor/__init__.py +4 -3
  34. esphome/components/ble_client/switch/__init__.py +2 -1
  35. esphome/components/ble_client/text_sensor/__init__.py +4 -3
  36. esphome/components/ble_presence/binary_sensor.py +3 -3
  37. esphome/components/ble_rssi/sensor.py +2 -2
  38. esphome/components/ble_scanner/text_sensor.py +1 -1
  39. esphome/components/bluetooth_proxy/__init__.py +3 -3
  40. esphome/components/bme68x_bsec2/__init__.py +196 -0
  41. esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +523 -0
  42. esphome/components/bme68x_bsec2/bme68x_bsec2.h +163 -0
  43. esphome/components/bme68x_bsec2/sensor.py +130 -0
  44. esphome/components/bme68x_bsec2/text_sensor.py +33 -0
  45. esphome/components/bme68x_bsec2_i2c/__init__.py +28 -0
  46. esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp +53 -0
  47. esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.h +28 -0
  48. esphome/components/bmp3xx/sensor.py +1 -1
  49. esphome/components/button/__init__.py +4 -4
  50. esphome/components/climate/__init__.py +5 -5
  51. esphome/components/climate/climate.h +1 -1
  52. esphome/components/cover/__init__.py +8 -8
  53. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +11 -7
  54. esphome/components/cst226/touchscreen/cst226_touchscreen.h +1 -1
  55. esphome/components/datetime/__init__.py +11 -13
  56. esphome/components/demo/demo_sensor.h +3 -2
  57. esphome/components/display/display.cpp +31 -0
  58. esphome/components/display/display.h +3 -0
  59. esphome/components/display_menu_base/__init__.py +14 -13
  60. esphome/components/ens160/sensor.py +1 -1
  61. esphome/components/esp32/__init__.py +22 -10
  62. esphome/components/esp32/boards.py +1 -1
  63. esphome/components/esp32/gpio.py +12 -13
  64. esphome/components/esp32/gpio_esp32.py +1 -2
  65. esphome/components/esp32/gpio_esp32_c2.py +1 -2
  66. esphome/components/esp32/gpio_esp32_c3.py +1 -5
  67. esphome/components/esp32/gpio_esp32_c6.py +1 -2
  68. esphome/components/esp32/gpio_esp32_h2.py +1 -2
  69. esphome/components/esp32/gpio_esp32_s2.py +1 -2
  70. esphome/components/esp32/gpio_esp32_s3.py +1 -6
  71. esphome/components/esp32_ble/__init__.py +20 -3
  72. esphome/components/esp32_ble/ble.cpp +9 -1
  73. esphome/components/esp32_ble/ble.h +9 -0
  74. esphome/components/esp32_ble/ble_advertising.cpp +42 -9
  75. esphome/components/esp32_ble/ble_advertising.h +21 -1
  76. esphome/components/esp32_ble_beacon/__init__.py +17 -7
  77. esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +45 -113
  78. esphome/components/esp32_ble_beacon/esp32_ble_beacon.h +17 -19
  79. esphome/components/esp32_ble_client/__init__.py +0 -1
  80. esphome/components/esp32_ble_server/__init__.py +2 -3
  81. esphome/components/esp32_ble_tracker/__init__.py +2 -2
  82. esphome/components/esp32_improv/__init__.py +2 -4
  83. esphome/components/ethernet/__init__.py +17 -17
  84. esphome/components/ethernet_info/text_sensor.py +2 -2
  85. esphome/components/event/__init__.py +5 -5
  86. esphome/components/fan/__init__.py +14 -14
  87. esphome/components/fan/fan.cpp +2 -2
  88. esphome/components/fingerprint_grow/fingerprint_grow.cpp +1 -1
  89. esphome/components/fingerprint_grow/fingerprint_grow.h +1 -1
  90. esphome/components/graphical_display_menu/__init__.py +11 -8
  91. esphome/components/haier/haier_base.h +2 -2
  92. esphome/components/homeassistant/__init__.py +8 -1
  93. esphome/components/homeassistant/number/__init__.py +33 -0
  94. esphome/components/homeassistant/number/homeassistant_number.cpp +100 -0
  95. esphome/components/homeassistant/number/homeassistant_number.h +31 -0
  96. esphome/components/homeassistant/switch/__init__.py +30 -0
  97. esphome/components/homeassistant/switch/homeassistant_switch.cpp +59 -0
  98. esphome/components/homeassistant/switch/homeassistant_switch.h +22 -0
  99. esphome/components/host/__init__.py +3 -7
  100. esphome/components/http_request/__init__.py +12 -1
  101. esphome/components/http_request/http_request_arduino.cpp +2 -2
  102. esphome/components/http_request/http_request_idf.cpp +11 -2
  103. esphome/components/http_request/http_request_idf.h +10 -0
  104. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  105. esphome/components/http_request/update/http_request_update.cpp +2 -2
  106. esphome/components/http_request/update/http_request_update.h +2 -1
  107. esphome/components/hx711/hx711.cpp +10 -1
  108. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +1 -1
  109. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +11 -2
  110. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +2 -0
  111. esphome/components/ili9xxx/ili9xxx_defines.h +3 -1
  112. esphome/components/ili9xxx/ili9xxx_display.cpp +9 -22
  113. esphome/components/ili9xxx/ili9xxx_display.h +5 -7
  114. esphome/components/ili9xxx/ili9xxx_init.h +4 -4
  115. esphome/components/improv_base/__init__.py +2 -3
  116. esphome/components/improv_serial/__init__.py +4 -10
  117. esphome/components/improv_serial/improv_serial_component.cpp +4 -0
  118. esphome/components/jsn_sr04t/jsn_sr04t.cpp +18 -1
  119. esphome/components/jsn_sr04t/jsn_sr04t.h +7 -1
  120. esphome/components/jsn_sr04t/sensor.py +13 -0
  121. esphome/components/kalman_combinator/sensor.py +1 -1
  122. esphome/components/light/__init__.py +16 -15
  123. esphome/components/light/addressable_light_effect.h +12 -8
  124. esphome/components/light/automation.h +16 -1
  125. esphome/components/light/automation.py +21 -0
  126. esphome/components/light/base_light_effects.h +5 -5
  127. esphome/components/light/esp_color_correction.h +8 -8
  128. esphome/components/light/types.py +7 -0
  129. esphome/components/lock/__init__.py +3 -3
  130. esphome/components/logger/__init__.py +15 -18
  131. esphome/components/lvgl/__init__.py +346 -0
  132. esphome/components/lvgl/automation.py +226 -0
  133. esphome/components/lvgl/binary_sensor/__init__.py +43 -0
  134. esphome/components/lvgl/defines.py +508 -0
  135. esphome/components/lvgl/encoders.py +77 -0
  136. esphome/components/lvgl/font.cpp +76 -0
  137. esphome/components/lvgl/helpers.py +49 -0
  138. esphome/components/lvgl/light/__init__.py +32 -0
  139. esphome/components/lvgl/light/lvgl_light.h +48 -0
  140. esphome/components/lvgl/lv_validation.py +303 -0
  141. esphome/components/lvgl/lvcode.py +349 -0
  142. esphome/components/lvgl/lvgl_esphome.cpp +407 -0
  143. esphome/components/lvgl/lvgl_esphome.h +274 -0
  144. esphome/components/lvgl/lvgl_hal.h +21 -0
  145. esphome/components/lvgl/number/__init__.py +66 -0
  146. esphome/components/lvgl/number/lvgl_number.h +34 -0
  147. esphome/components/lvgl/schemas.py +436 -0
  148. esphome/components/lvgl/select/__init__.py +55 -0
  149. esphome/components/lvgl/select/lvgl_select.h +62 -0
  150. esphome/components/lvgl/sensor/__init__.py +47 -0
  151. esphome/components/lvgl/styles.py +58 -0
  152. esphome/components/lvgl/switch/__init__.py +56 -0
  153. esphome/components/lvgl/switch/lvgl_switch.h +34 -0
  154. esphome/components/lvgl/text/__init__.py +50 -0
  155. esphome/components/lvgl/text/lvgl_text.h +34 -0
  156. esphome/components/lvgl/text_sensor/__init__.py +42 -0
  157. esphome/components/lvgl/touchscreens.py +45 -0
  158. esphome/components/lvgl/trigger.py +74 -0
  159. esphome/components/lvgl/types.py +191 -0
  160. esphome/components/lvgl/widgets/__init__.py +419 -0
  161. esphome/components/lvgl/widgets/animimg.py +117 -0
  162. esphome/components/lvgl/widgets/arc.py +78 -0
  163. esphome/components/lvgl/widgets/button.py +20 -0
  164. esphome/components/lvgl/widgets/buttonmatrix.py +275 -0
  165. esphome/components/lvgl/widgets/checkbox.py +27 -0
  166. esphome/components/lvgl/widgets/dropdown.py +76 -0
  167. esphome/components/lvgl/widgets/img.py +85 -0
  168. esphome/components/lvgl/widgets/keyboard.py +49 -0
  169. esphome/components/lvgl/widgets/label.py +42 -0
  170. esphome/components/lvgl/widgets/led.py +29 -0
  171. esphome/components/lvgl/widgets/line.py +50 -0
  172. esphome/components/lvgl/widgets/lv_bar.py +55 -0
  173. esphome/components/lvgl/widgets/meter.py +302 -0
  174. esphome/components/lvgl/widgets/msgbox.py +134 -0
  175. esphome/components/lvgl/widgets/obj.py +28 -0
  176. esphome/components/lvgl/widgets/page.py +113 -0
  177. esphome/components/lvgl/widgets/roller.py +77 -0
  178. esphome/components/lvgl/widgets/slider.py +63 -0
  179. esphome/components/lvgl/widgets/spinbox.py +178 -0
  180. esphome/components/lvgl/widgets/spinner.py +43 -0
  181. esphome/components/lvgl/widgets/switch.py +20 -0
  182. esphome/components/lvgl/widgets/tabview.py +114 -0
  183. esphome/components/lvgl/widgets/textarea.py +66 -0
  184. esphome/components/lvgl/widgets/tileview.py +128 -0
  185. esphome/components/m5stack_8angle/__init__.py +33 -0
  186. esphome/components/m5stack_8angle/binary_sensor/__init__.py +30 -0
  187. esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.cpp +17 -0
  188. esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.h +19 -0
  189. esphome/components/m5stack_8angle/light/__init__.py +31 -0
  190. esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp +45 -0
  191. esphome/components/m5stack_8angle/light/m5stack_8angle_light.h +37 -0
  192. esphome/components/m5stack_8angle/m5stack_8angle.cpp +74 -0
  193. esphome/components/m5stack_8angle/m5stack_8angle.h +34 -0
  194. esphome/components/m5stack_8angle/sensor/__init__.py +66 -0
  195. esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.cpp +24 -0
  196. esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.h +27 -0
  197. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -0
  198. esphome/components/max31856/sensor.py +5 -5
  199. esphome/components/media_player/__init__.py +3 -5
  200. esphome/components/media_player/automation.h +31 -27
  201. esphome/components/micro_wake_word/__init__.py +20 -25
  202. esphome/components/micro_wake_word/streaming_model.cpp +6 -4
  203. esphome/components/microphone/microphone.h +4 -1
  204. esphome/components/mitsubishi/mitsubishi.cpp +7 -1
  205. esphome/components/modbus_controller/__init__.py +26 -2
  206. esphome/components/modbus_controller/automation.h +19 -0
  207. esphome/components/modbus_controller/const.py +1 -0
  208. esphome/components/modbus_controller/modbus_controller.cpp +8 -0
  209. esphome/components/modbus_controller/modbus_controller.h +3 -0
  210. esphome/components/mqtt/__init__.py +20 -9
  211. esphome/components/mqtt/mqtt_alarm_control_panel.cpp +128 -0
  212. esphome/components/mqtt/mqtt_alarm_control_panel.h +39 -0
  213. esphome/components/mqtt/mqtt_backend.h +3 -1
  214. esphome/components/mqtt/mqtt_backend_esp32.cpp +4 -1
  215. esphome/components/mqtt/mqtt_backend_esp32.h +3 -1
  216. esphome/components/mqtt/mqtt_backend_esp8266.h +3 -1
  217. esphome/components/mqtt/mqtt_backend_libretiny.h +3 -1
  218. esphome/components/mqtt/mqtt_client.cpp +16 -3
  219. esphome/components/mqtt/mqtt_client.h +5 -1
  220. esphome/components/mqtt/mqtt_component.cpp +32 -4
  221. esphome/components/mqtt/mqtt_const.h +2 -0
  222. esphome/components/network/__init__.py +15 -12
  223. esphome/components/network/ip_address.h +3 -0
  224. esphome/components/network/util.cpp +2 -1
  225. esphome/components/network/util.h +3 -1
  226. esphome/components/nextion/base_component.py +5 -8
  227. esphome/components/number/__init__.py +7 -8
  228. esphome/components/online_image/__init__.py +167 -0
  229. esphome/components/online_image/image_decoder.cpp +44 -0
  230. esphome/components/online_image/image_decoder.h +112 -0
  231. esphome/components/online_image/online_image.cpp +283 -0
  232. esphome/components/online_image/online_image.h +195 -0
  233. esphome/components/online_image/png_image.cpp +68 -0
  234. esphome/components/online_image/png_image.h +33 -0
  235. esphome/components/ota/__init__.py +8 -4
  236. esphome/components/pid/pid_climate.h +2 -0
  237. esphome/components/pmwcs3/pmwcs3.cpp +31 -30
  238. esphome/components/remote_base/pronto_protocol.cpp +0 -3
  239. esphome/components/remote_transmitter/remote_transmitter.h +1 -1
  240. esphome/components/rgbct/rgbct_light_output.h +3 -2
  241. esphome/components/rgbw/rgbw_light_output.h +3 -2
  242. esphome/components/rgbww/rgbww_light_output.h +3 -2
  243. esphome/components/rp2040_pio_led_strip/led_strip.cpp +31 -5
  244. esphome/components/rp2040_pio_led_strip/led_strip.h +5 -0
  245. esphome/components/rtttl/rtttl.cpp +108 -21
  246. esphome/components/rtttl/rtttl.h +15 -6
  247. esphome/components/select/__init__.py +7 -7
  248. esphome/components/sensor/__init__.py +29 -10
  249. esphome/components/sensor/filter.cpp +8 -0
  250. esphome/components/sensor/filter.h +9 -0
  251. esphome/components/sml/sml_parser.cpp +48 -22
  252. esphome/components/socket/socket.cpp +11 -14
  253. esphome/components/speaker/__init__.py +14 -5
  254. esphome/components/speaker/automation.h +10 -0
  255. esphome/components/speaker/speaker.h +9 -0
  256. esphome/components/spi/spi.cpp +0 -6
  257. esphome/components/spi/spi.h +2 -19
  258. esphome/components/spi_led_strip/spi_led_strip.h +5 -4
  259. esphome/components/sprinkler/sprinkler.cpp +2 -2
  260. esphome/components/sprinkler/sprinkler.h +1 -1
  261. esphome/components/switch/__init__.py +3 -3
  262. esphome/components/text/__init__.py +5 -5
  263. esphome/components/text_sensor/__init__.py +7 -7
  264. esphome/components/time/__init__.py +8 -8
  265. esphome/components/touchscreen/binary_sensor/__init__.py +24 -10
  266. esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.cpp +3 -2
  267. esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h +4 -2
  268. esphome/components/uart/uart_component_host.cpp +6 -2
  269. esphome/components/update/__init__.py +33 -15
  270. esphome/components/update/automation.h +23 -0
  271. esphome/components/update/update_entity.h +3 -1
  272. esphome/components/valve/__init__.py +3 -3
  273. esphome/components/voice_assistant/__init__.py +7 -8
  274. esphome/components/wake_on_lan/wake_on_lan.cpp +2 -0
  275. esphome/components/wake_on_lan/wake_on_lan.h +3 -1
  276. esphome/components/watchdog/__init__.py +1 -0
  277. esphome/components/{http_request → watchdog}/watchdog.cpp +0 -2
  278. esphome/components/{http_request → watchdog}/watchdog.h +0 -2
  279. esphome/components/waveshare_epaper/waveshare_epaper.cpp +5 -5
  280. esphome/components/web_server/server_index_v3.h +3615 -3603
  281. esphome/components/web_server/web_server.cpp +0 -209
  282. esphome/components/web_server/web_server.h +1 -1
  283. esphome/components/web_server/web_server_v1.cpp +217 -0
  284. esphome/components/web_server_base/web_server_base.h +1 -0
  285. esphome/components/wifi/__init__.py +15 -14
  286. esphome/components/wifi/wifi_component.cpp +2 -0
  287. esphome/components/wifi/wifi_component.h +7 -1
  288. esphome/components/wifi/wifi_component_esp32_arduino.cpp +5 -2
  289. esphome/components/wifi/wifi_component_esp8266.cpp +2 -0
  290. esphome/components/wifi/wifi_component_esp_idf.cpp +43 -7
  291. esphome/components/wifi/wifi_component_libretiny.cpp +2 -0
  292. esphome/components/wifi/wifi_component_pico_w.cpp +2 -0
  293. esphome/components/wifi/wpa2_eap.py +6 -7
  294. esphome/components/wifi_info/text_sensor.py +3 -3
  295. esphome/components/wifi_info/wifi_info_text_sensor.cpp +2 -0
  296. esphome/components/wifi_info/wifi_info_text_sensor.h +2 -0
  297. esphome/components/wifi_signal/sensor.py +1 -1
  298. esphome/components/wifi_signal/wifi_signal_sensor.cpp +2 -0
  299. esphome/components/wifi_signal/wifi_signal_sensor.h +2 -1
  300. esphome/components/xiaomi_ble/xiaomi_ble.cpp +20 -3
  301. esphome/components/xiaomi_ble/xiaomi_ble.h +1 -0
  302. esphome/components/xiaomi_lywsd02mmc/__init__.py +0 -0
  303. esphome/components/xiaomi_lywsd02mmc/sensor.py +77 -0
  304. esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp +73 -0
  305. esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h +37 -0
  306. esphome/config.py +17 -19
  307. esphome/config_validation.py +55 -23
  308. esphome/const.py +25 -9
  309. esphome/core/__init__.py +17 -14
  310. esphome/core/application.h +42 -21
  311. esphome/core/automation.h +5 -3
  312. esphome/core/base_automation.h +3 -2
  313. esphome/core/bytebuffer.cpp +134 -0
  314. esphome/core/bytebuffer.h +96 -0
  315. esphome/core/color.h +24 -16
  316. esphome/core/config.py +3 -3
  317. esphome/core/defines.h +14 -1
  318. esphome/core/entity_base.h +2 -2
  319. esphome/core/entity_helpers.py +1 -2
  320. esphome/core/gpio.h +0 -18
  321. esphome/core/helpers.h +1 -1
  322. esphome/core/optional.h +15 -16
  323. esphome/coroutine.py +1 -1
  324. esphome/cpp_generator.py +1 -1
  325. esphome/cpp_helpers.py +3 -5
  326. esphome/dashboard/core.py +3 -3
  327. esphome/dashboard/dashboard.py +3 -3
  328. esphome/dashboard/entries.py +1 -1
  329. esphome/dashboard/util/file.py +1 -1
  330. esphome/dashboard/web_server.py +3 -3
  331. esphome/external_files.py +5 -3
  332. esphome/final_validate.py +2 -2
  333. esphome/git.py +4 -4
  334. esphome/helpers.py +5 -5
  335. esphome/loader.py +15 -10
  336. esphome/mqtt.py +4 -8
  337. esphome/pins.py +6 -6
  338. esphome/platformio_api.py +5 -5
  339. esphome/storage_json.py +2 -1
  340. esphome/types.py +1 -1
  341. esphome/util.py +2 -3
  342. esphome/voluptuous_schema.py +1 -0
  343. esphome/vscode.py +5 -4
  344. esphome/wizard.py +1 -1
  345. esphome/writer.py +7 -7
  346. esphome/yaml_util.py +3 -3
  347. esphome/zeroconf.py +1 -1
  348. {esphome-2024.7.2.dist-info → esphome-2024.8.0.dist-info}/METADATA +3 -3
  349. {esphome-2024.7.2.dist-info → esphome-2024.8.0.dist-info}/RECORD +353 -247
  350. {esphome-2024.7.2.dist-info → esphome-2024.8.0.dist-info}/LICENSE +0 -0
  351. {esphome-2024.7.2.dist-info → esphome-2024.8.0.dist-info}/WHEEL +0 -0
  352. {esphome-2024.7.2.dist-info → esphome-2024.8.0.dist-info}/entry_points.txt +0 -0
  353. {esphome-2024.7.2.dist-info → esphome-2024.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,66 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components import number
3
+ import esphome.config_validation as cv
4
+ from esphome.cpp_generator import MockObj
5
+
6
+ from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_UPDATE_ON_RELEASE, CONF_WIDGET
7
+ from ..lv_validation import animated
8
+ from ..lvcode import (
9
+ API_EVENT,
10
+ EVENT_ARG,
11
+ UPDATE_EVENT,
12
+ LambdaContext,
13
+ LvContext,
14
+ lv,
15
+ lv_add,
16
+ )
17
+ from ..schemas import LVGL_SCHEMA
18
+ from ..types import LV_EVENT, LvNumber, lvgl_ns
19
+ from ..widgets import get_widgets
20
+
21
+ LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number)
22
+
23
+ CONFIG_SCHEMA = (
24
+ number.number_schema(LVGLNumber)
25
+ .extend(LVGL_SCHEMA)
26
+ .extend(
27
+ {
28
+ cv.Required(CONF_WIDGET): cv.use_id(LvNumber),
29
+ cv.Optional(CONF_ANIMATED, default=True): animated,
30
+ cv.Optional(CONF_UPDATE_ON_RELEASE, default=False): cv.boolean,
31
+ }
32
+ )
33
+ )
34
+
35
+
36
+ async def to_code(config):
37
+ paren = await cg.get_variable(config[CONF_LVGL_ID])
38
+ widget = await get_widgets(config, CONF_WIDGET)
39
+ widget = widget[0]
40
+ var = await number.new_number(
41
+ config,
42
+ max_value=widget.get_max(),
43
+ min_value=widget.get_min(),
44
+ step=widget.get_step(),
45
+ )
46
+
47
+ async with LambdaContext([(cg.float_, "v")]) as control:
48
+ await widget.set_property(
49
+ "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED]
50
+ )
51
+ lv.event_send(widget.obj, API_EVENT, cg.nullptr)
52
+ async with LambdaContext(EVENT_ARG) as event:
53
+ event.add(var.publish_state(widget.get_value()))
54
+ event_code = (
55
+ LV_EVENT.VALUE_CHANGED
56
+ if not config[CONF_UPDATE_ON_RELEASE]
57
+ else LV_EVENT.RELEASED
58
+ )
59
+ async with LvContext(paren):
60
+ lv_add(var.set_control_lambda(await control.get_lambda()))
61
+ lv_add(
62
+ paren.add_event_cb(
63
+ widget.obj, await event.get_lambda(), UPDATE_EVENT, event_code
64
+ )
65
+ )
66
+ lv_add(var.publish_state(widget.get_value()))
@@ -0,0 +1,34 @@
1
+ #pragma once
2
+
3
+ #include "esphome/components/number/number.h"
4
+ #include "esphome/core/automation.h"
5
+ #include "esphome/core/component.h"
6
+ #include "esphome/core/preferences.h"
7
+
8
+ namespace esphome {
9
+ namespace lvgl {
10
+
11
+ class LVGLNumber : public number::Number {
12
+ public:
13
+ void set_control_lambda(std::function<void(float)> control_lambda) {
14
+ this->control_lambda_ = control_lambda;
15
+ if (this->initial_state_.has_value()) {
16
+ this->control_lambda_(this->initial_state_.value());
17
+ this->initial_state_.reset();
18
+ }
19
+ }
20
+
21
+ protected:
22
+ void control(float value) override {
23
+ if (this->control_lambda_ != nullptr) {
24
+ this->control_lambda_(value);
25
+ } else {
26
+ this->initial_state_ = value;
27
+ }
28
+ }
29
+ std::function<void(float)> control_lambda_{};
30
+ optional<float> initial_state_{};
31
+ };
32
+
33
+ } // namespace lvgl
34
+ } // namespace esphome
@@ -0,0 +1,436 @@
1
+ from esphome import config_validation as cv
2
+ from esphome.automation import Trigger, validate_automation
3
+ from esphome.components.time import RealTimeClock
4
+ from esphome.const import (
5
+ CONF_ARGS,
6
+ CONF_FORMAT,
7
+ CONF_GROUP,
8
+ CONF_ID,
9
+ CONF_ON_VALUE,
10
+ CONF_STATE,
11
+ CONF_TEXT,
12
+ CONF_TIME,
13
+ CONF_TRIGGER_ID,
14
+ CONF_TYPE,
15
+ )
16
+ from esphome.core import TimePeriod
17
+ from esphome.schema_extractors import SCHEMA_EXTRACT
18
+
19
+ from . import defines as df, lv_validation as lvalid
20
+ from .defines import CONF_TIME_FORMAT
21
+ from .helpers import add_lv_use, requires_component, validate_printf
22
+ from .lv_validation import lv_color, lv_font, lv_image
23
+ from .lvcode import LvglComponent
24
+ from .types import (
25
+ LVEncoderListener,
26
+ LvType,
27
+ WidgetType,
28
+ lv_group_t,
29
+ lv_obj_t,
30
+ lv_pseudo_button_t,
31
+ lv_style_t,
32
+ )
33
+
34
+ # this will be populated later, in __init__.py to avoid circular imports.
35
+ WIDGET_TYPES: dict = {}
36
+
37
+ # A schema for text properties
38
+ TEXT_SCHEMA = cv.Schema(
39
+ {
40
+ cv.Optional(CONF_TEXT): cv.Any(
41
+ cv.All(
42
+ cv.Schema(
43
+ {
44
+ cv.Required(CONF_FORMAT): cv.string,
45
+ cv.Optional(CONF_ARGS, default=list): cv.ensure_list(
46
+ cv.lambda_
47
+ ),
48
+ },
49
+ ),
50
+ validate_printf,
51
+ ),
52
+ cv.Schema(
53
+ {
54
+ cv.Required(CONF_TIME_FORMAT): cv.string,
55
+ cv.GenerateID(CONF_TIME): cv.templatable(cv.use_id(RealTimeClock)),
56
+ }
57
+ ),
58
+ cv.templatable(cv.string),
59
+ )
60
+ }
61
+ )
62
+
63
+ LIST_ACTION_SCHEMA = cv.ensure_list(
64
+ cv.maybe_simple_value(
65
+ {
66
+ cv.Required(CONF_ID): cv.use_id(lv_pseudo_button_t),
67
+ },
68
+ key=CONF_ID,
69
+ )
70
+ )
71
+
72
+ PRESS_TIME = cv.All(
73
+ lvalid.lv_milliseconds, cv.Range(max=TimePeriod(milliseconds=65535))
74
+ )
75
+
76
+ ENCODER_SCHEMA = cv.Schema(
77
+ {
78
+ cv.GenerateID(): cv.All(
79
+ cv.declare_id(LVEncoderListener), requires_component("binary_sensor")
80
+ ),
81
+ cv.Optional(CONF_GROUP): cv.declare_id(lv_group_t),
82
+ cv.Optional(df.CONF_INITIAL_FOCUS): cv.use_id(lv_obj_t),
83
+ cv.Optional(df.CONF_LONG_PRESS_TIME, default="400ms"): PRESS_TIME,
84
+ cv.Optional(df.CONF_LONG_PRESS_REPEAT_TIME, default="100ms"): PRESS_TIME,
85
+ }
86
+ )
87
+
88
+ # All LVGL styles and their validators
89
+ STYLE_PROPS = {
90
+ "align": df.CHILD_ALIGNMENTS.one_of,
91
+ "arc_opa": lvalid.opacity,
92
+ "arc_color": lvalid.lv_color,
93
+ "arc_rounded": lvalid.lv_bool,
94
+ "arc_width": cv.positive_int,
95
+ "anim_time": lvalid.lv_milliseconds,
96
+ "bg_color": lvalid.lv_color,
97
+ "bg_grad_color": lvalid.lv_color,
98
+ "bg_dither_mode": df.LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF").one_of,
99
+ "bg_grad_dir": df.LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER").one_of,
100
+ "bg_grad_stop": lvalid.stop_value,
101
+ "bg_image_opa": lvalid.opacity,
102
+ "bg_image_recolor": lvalid.lv_color,
103
+ "bg_image_recolor_opa": lvalid.opacity,
104
+ "bg_image_src": lvalid.lv_image,
105
+ "bg_main_stop": lvalid.stop_value,
106
+ "bg_opa": lvalid.opacity,
107
+ "border_color": lvalid.lv_color,
108
+ "border_opa": lvalid.opacity,
109
+ "border_post": lvalid.lv_bool,
110
+ "border_side": df.LvConstant(
111
+ "LV_BORDER_SIDE_", "NONE", "TOP", "BOTTOM", "LEFT", "RIGHT", "INTERNAL"
112
+ ).several_of,
113
+ "border_width": cv.positive_int,
114
+ "clip_corner": lvalid.lv_bool,
115
+ "color_filter_opa": lvalid.opacity,
116
+ "height": lvalid.size,
117
+ "image_recolor": lvalid.lv_color,
118
+ "image_recolor_opa": lvalid.opacity,
119
+ "line_width": cv.positive_int,
120
+ "line_dash_width": cv.positive_int,
121
+ "line_dash_gap": cv.positive_int,
122
+ "line_rounded": lvalid.lv_bool,
123
+ "line_color": lvalid.lv_color,
124
+ "opa": lvalid.opacity,
125
+ "opa_layered": lvalid.opacity,
126
+ "outline_color": lvalid.lv_color,
127
+ "outline_opa": lvalid.opacity,
128
+ "outline_pad": lvalid.pixels,
129
+ "outline_width": lvalid.pixels,
130
+ "pad_all": lvalid.pixels,
131
+ "pad_bottom": lvalid.pixels,
132
+ "pad_left": lvalid.pixels,
133
+ "pad_right": lvalid.pixels,
134
+ "pad_top": lvalid.pixels,
135
+ "shadow_color": lvalid.lv_color,
136
+ "shadow_ofs_x": cv.int_,
137
+ "shadow_ofs_y": cv.int_,
138
+ "shadow_opa": lvalid.opacity,
139
+ "shadow_spread": cv.int_,
140
+ "shadow_width": cv.positive_int,
141
+ "text_align": df.LvConstant(
142
+ "LV_TEXT_ALIGN_", "LEFT", "CENTER", "RIGHT", "AUTO"
143
+ ).one_of,
144
+ "text_color": lvalid.lv_color,
145
+ "text_decor": df.LvConstant(
146
+ "LV_TEXT_DECOR_", "NONE", "UNDERLINE", "STRIKETHROUGH"
147
+ ).several_of,
148
+ "text_font": lv_font,
149
+ "text_letter_space": cv.positive_int,
150
+ "text_line_space": cv.positive_int,
151
+ "text_opa": lvalid.opacity,
152
+ "transform_angle": lvalid.angle,
153
+ "transform_height": lvalid.pixels_or_percent,
154
+ "transform_pivot_x": lvalid.pixels_or_percent,
155
+ "transform_pivot_y": lvalid.pixels_or_percent,
156
+ "transform_zoom": lvalid.zoom,
157
+ "translate_x": lvalid.pixels_or_percent,
158
+ "translate_y": lvalid.pixels_or_percent,
159
+ "max_height": lvalid.pixels_or_percent,
160
+ "max_width": lvalid.pixels_or_percent,
161
+ "min_height": lvalid.pixels_or_percent,
162
+ "min_width": lvalid.pixels_or_percent,
163
+ "radius": lvalid.radius,
164
+ "width": lvalid.size,
165
+ "x": lvalid.pixels_or_percent,
166
+ "y": lvalid.pixels_or_percent,
167
+ }
168
+
169
+ STYLE_REMAP = {
170
+ "bg_image_opa": "bg_img_opa",
171
+ "bg_image_recolor": "bg_img_recolor",
172
+ "bg_image_recolor_opa": "bg_img_recolor_opa",
173
+ "bg_image_src": "bg_img_src",
174
+ "image_recolor": "img_recolor",
175
+ "image_recolor_opa": "img_recolor_opa",
176
+ }
177
+
178
+ # Complete object style schema
179
+ STYLE_SCHEMA = cv.Schema({cv.Optional(k): v for k, v in STYLE_PROPS.items()}).extend(
180
+ {
181
+ cv.Optional(df.CONF_STYLES): cv.ensure_list(cv.use_id(lv_style_t)),
182
+ cv.Optional(df.CONF_SCROLLBAR_MODE): df.LvConstant(
183
+ "LV_SCROLLBAR_MODE_", "OFF", "ON", "ACTIVE", "AUTO"
184
+ ).one_of,
185
+ }
186
+ )
187
+
188
+ # Object states. Top level properties apply to MAIN
189
+ STATE_SCHEMA = cv.Schema(
190
+ {cv.Optional(state): STYLE_SCHEMA for state in df.STATES}
191
+ ).extend(STYLE_SCHEMA)
192
+ # Setting object states
193
+ SET_STATE_SCHEMA = cv.Schema(
194
+ {cv.Optional(state): lvalid.lv_bool for state in df.STATES}
195
+ )
196
+ # Setting object flags
197
+ FLAG_SCHEMA = cv.Schema({cv.Optional(flag): lvalid.lv_bool for flag in df.OBJ_FLAGS})
198
+ FLAG_LIST = cv.ensure_list(df.LvConstant("LV_OBJ_FLAG_", *df.OBJ_FLAGS).one_of)
199
+
200
+
201
+ def part_schema(widget_type: WidgetType):
202
+ """
203
+ Generate a schema for the various parts (e.g. main:, indicator:) of a widget type
204
+ :param widget_type: The type of widget to generate for
205
+ :return:
206
+ """
207
+ parts = widget_type.parts
208
+ return cv.Schema({cv.Optional(part): STATE_SCHEMA for part in parts}).extend(
209
+ STATE_SCHEMA
210
+ )
211
+
212
+
213
+ def automation_schema(typ: LvType):
214
+ if typ.has_on_value:
215
+ events = df.LV_EVENT_TRIGGERS + (CONF_ON_VALUE,)
216
+ else:
217
+ events = df.LV_EVENT_TRIGGERS
218
+ if isinstance(typ, LvType):
219
+ template = Trigger.template(typ.get_arg_type())
220
+ else:
221
+ template = Trigger.template()
222
+ return {
223
+ cv.Optional(event): validate_automation(
224
+ {
225
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(template),
226
+ }
227
+ )
228
+ for event in events
229
+ }
230
+
231
+
232
+ def create_modify_schema(widget_type):
233
+ return (
234
+ part_schema(widget_type)
235
+ .extend(
236
+ {
237
+ cv.Required(CONF_ID): cv.ensure_list(
238
+ cv.maybe_simple_value(
239
+ {
240
+ cv.Required(CONF_ID): cv.use_id(widget_type),
241
+ },
242
+ key=CONF_ID,
243
+ )
244
+ ),
245
+ cv.Optional(CONF_STATE): SET_STATE_SCHEMA,
246
+ }
247
+ )
248
+ .extend(FLAG_SCHEMA)
249
+ .extend(widget_type.modify_schema)
250
+ )
251
+
252
+
253
+ def obj_schema(widget_type: WidgetType):
254
+ """
255
+ Create a schema for a widget type itself i.e. no allowance for children
256
+ :param widget_type:
257
+ :return:
258
+ """
259
+ return (
260
+ part_schema(widget_type)
261
+ .extend(FLAG_SCHEMA)
262
+ .extend(LAYOUT_SCHEMA)
263
+ .extend(ALIGN_TO_SCHEMA)
264
+ .extend(automation_schema(widget_type.w_type))
265
+ .extend(
266
+ cv.Schema(
267
+ {
268
+ cv.Optional(CONF_STATE): SET_STATE_SCHEMA,
269
+ cv.Optional(CONF_GROUP): cv.use_id(lv_group_t),
270
+ }
271
+ )
272
+ )
273
+ )
274
+
275
+
276
+ LAYOUT_SCHEMAS = {}
277
+
278
+ ALIGN_TO_SCHEMA = {
279
+ cv.Optional(df.CONF_ALIGN_TO): cv.Schema(
280
+ {
281
+ cv.Required(CONF_ID): cv.use_id(lv_obj_t),
282
+ cv.Required(df.CONF_ALIGN): df.ALIGN_ALIGNMENTS.one_of,
283
+ cv.Optional(df.CONF_X, default=0): lvalid.pixels_or_percent,
284
+ cv.Optional(df.CONF_Y, default=0): lvalid.pixels_or_percent,
285
+ }
286
+ )
287
+ }
288
+
289
+
290
+ def grid_free_space(value):
291
+ value = cv.Upper(value)
292
+ if value.startswith("FR(") and value.endswith(")"):
293
+ value = value.removesuffix(")").removeprefix("FR(")
294
+ return f"LV_GRID_FR({cv.positive_int(value)})"
295
+ raise cv.Invalid("must be a size in pixels, CONTENT or FR(nn)")
296
+
297
+
298
+ grid_spec = cv.Any(
299
+ lvalid.size, df.LvConstant("LV_GRID_", "CONTENT").one_of, grid_free_space
300
+ )
301
+
302
+ cell_alignments = df.LV_CELL_ALIGNMENTS.one_of
303
+ grid_alignments = df.LV_GRID_ALIGNMENTS.one_of
304
+ flex_alignments = df.LV_FLEX_ALIGNMENTS.one_of
305
+
306
+ LAYOUT_SCHEMA = {
307
+ cv.Optional(df.CONF_LAYOUT): cv.typed_schema(
308
+ {
309
+ df.TYPE_GRID: {
310
+ cv.Required(df.CONF_GRID_ROWS): [grid_spec],
311
+ cv.Required(df.CONF_GRID_COLUMNS): [grid_spec],
312
+ cv.Optional(df.CONF_GRID_COLUMN_ALIGN): grid_alignments,
313
+ cv.Optional(df.CONF_GRID_ROW_ALIGN): grid_alignments,
314
+ cv.Optional(df.CONF_PAD_ROW): lvalid.pixels,
315
+ cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels,
316
+ },
317
+ df.TYPE_FLEX: {
318
+ cv.Optional(
319
+ df.CONF_FLEX_FLOW, default="row_wrap"
320
+ ): df.FLEX_FLOWS.one_of,
321
+ cv.Optional(df.CONF_FLEX_ALIGN_MAIN, default="start"): flex_alignments,
322
+ cv.Optional(df.CONF_FLEX_ALIGN_CROSS, default="start"): flex_alignments,
323
+ cv.Optional(df.CONF_FLEX_ALIGN_TRACK, default="start"): flex_alignments,
324
+ cv.Optional(df.CONF_PAD_ROW): lvalid.pixels,
325
+ cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels,
326
+ },
327
+ },
328
+ lower=True,
329
+ )
330
+ }
331
+
332
+ GRID_CELL_SCHEMA = {
333
+ cv.Required(df.CONF_GRID_CELL_ROW_POS): cv.positive_int,
334
+ cv.Required(df.CONF_GRID_CELL_COLUMN_POS): cv.positive_int,
335
+ cv.Optional(df.CONF_GRID_CELL_ROW_SPAN, default=1): cv.positive_int,
336
+ cv.Optional(df.CONF_GRID_CELL_COLUMN_SPAN, default=1): cv.positive_int,
337
+ cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments,
338
+ cv.Optional(df.CONF_GRID_CELL_Y_ALIGN): grid_alignments,
339
+ }
340
+
341
+ FLEX_OBJ_SCHEMA = {
342
+ cv.Optional(df.CONF_FLEX_GROW): cv.int_,
343
+ }
344
+
345
+ DISP_BG_SCHEMA = cv.Schema(
346
+ {
347
+ cv.Optional(df.CONF_DISP_BG_IMAGE): lv_image,
348
+ cv.Optional(df.CONF_DISP_BG_COLOR): lv_color,
349
+ }
350
+ )
351
+
352
+ # A style schema that can include text
353
+ STYLED_TEXT_SCHEMA = cv.maybe_simple_value(
354
+ STYLE_SCHEMA.extend(TEXT_SCHEMA), key=CONF_TEXT
355
+ )
356
+
357
+ # For use by platform components
358
+ LVGL_SCHEMA = cv.Schema(
359
+ {
360
+ cv.GenerateID(df.CONF_LVGL_ID): cv.use_id(LvglComponent),
361
+ }
362
+ )
363
+
364
+ ALL_STYLES = {**STYLE_PROPS, **GRID_CELL_SCHEMA, **FLEX_OBJ_SCHEMA}
365
+
366
+
367
+ def container_validator(schema, widget_type: WidgetType):
368
+ """
369
+ Create a validator for a container given the widget type
370
+ :param schema: Base schema to extend
371
+ :param widget_type:
372
+ :return:
373
+ """
374
+
375
+ def validator(value):
376
+ result = schema
377
+ if w_sch := widget_type.schema:
378
+ result = result.extend(w_sch)
379
+ ltype = df.TYPE_NONE
380
+ if value and (layout := value.get(df.CONF_LAYOUT)):
381
+ if not isinstance(layout, dict):
382
+ raise cv.Invalid("Layout value must be a dict")
383
+ ltype = layout.get(CONF_TYPE)
384
+ if not ltype:
385
+ raise (cv.Invalid("Layout schema requires type:"))
386
+ add_lv_use(ltype)
387
+ if value == SCHEMA_EXTRACT:
388
+ return result
389
+ result = result.extend(LAYOUT_SCHEMAS[ltype.lower()])
390
+ return result(value)
391
+
392
+ return validator
393
+
394
+
395
+ def container_schema(widget_type: WidgetType, extras=None):
396
+ """
397
+ Create a schema for a container widget of a given type. All obj properties are available, plus
398
+ the extras passed in, plus any defined for the specific widget being specified.
399
+ :param widget_type: The widget type, e.g. "img"
400
+ :param extras: Additional options to be made available, e.g. layout properties for children
401
+ :return: The schema for this type of widget.
402
+ """
403
+ schema = obj_schema(widget_type).extend(
404
+ {cv.GenerateID(): cv.declare_id(widget_type.w_type)}
405
+ )
406
+ if extras:
407
+ schema = schema.extend(extras)
408
+ # Delayed evaluation for recursion
409
+ return container_validator(schema, widget_type)
410
+
411
+
412
+ def widget_schema(widget_type: WidgetType, extras=None):
413
+ """
414
+ Create a schema for a given widget type
415
+ :param widget_type: The name of the widget
416
+ :param extras:
417
+ :return:
418
+ """
419
+ validator = container_schema(widget_type, extras=extras)
420
+ if required := widget_type.required_component:
421
+ validator = cv.All(validator, requires_component(required))
422
+ return cv.Exclusive(widget_type.name, df.CONF_WIDGETS), validator
423
+
424
+
425
+ # All widget schemas must be defined before this is called.
426
+
427
+
428
+ def any_widget_schema(extras=None):
429
+ """
430
+ Generate schemas for all possible LVGL widgets. This is what implements the ability to have a list of any kind of
431
+ widget under the widgets: key.
432
+
433
+ :param extras: Additional schema to be applied to each generated one
434
+ :return:
435
+ """
436
+ return cv.Any(dict(widget_schema(wt, extras) for wt in WIDGET_TYPES.values()))
@@ -0,0 +1,55 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components import select
3
+ import esphome.config_validation as cv
4
+ from esphome.const import CONF_OPTIONS
5
+
6
+ from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET
7
+ from ..lvcode import (
8
+ API_EVENT,
9
+ EVENT_ARG,
10
+ UPDATE_EVENT,
11
+ LambdaContext,
12
+ LvContext,
13
+ lv,
14
+ lv_add,
15
+ )
16
+ from ..schemas import LVGL_SCHEMA
17
+ from ..types import LV_EVENT, LvSelect, lvgl_ns
18
+ from ..widgets import get_widgets
19
+
20
+ LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select)
21
+
22
+ CONFIG_SCHEMA = (
23
+ select.select_schema(LVGLSelect)
24
+ .extend(LVGL_SCHEMA)
25
+ .extend(
26
+ {
27
+ cv.Required(CONF_WIDGET): cv.use_id(LvSelect),
28
+ cv.Optional(CONF_ANIMATED, default=False): cv.boolean,
29
+ }
30
+ )
31
+ )
32
+
33
+
34
+ async def to_code(config):
35
+ widget = await get_widgets(config, CONF_WIDGET)
36
+ widget = widget[0]
37
+ options = widget.config.get(CONF_OPTIONS, [])
38
+ selector = await select.new_select(config, options=options)
39
+ paren = await cg.get_variable(config[CONF_LVGL_ID])
40
+ async with LambdaContext(EVENT_ARG) as pub_ctx:
41
+ pub_ctx.add(selector.publish_index(widget.get_value()))
42
+ async with LambdaContext([(cg.uint16, "v")]) as control:
43
+ await widget.set_property("selected", "v", animated=config[CONF_ANIMATED])
44
+ lv.event_send(widget.obj, API_EVENT, cg.nullptr)
45
+ async with LvContext(paren) as ctx:
46
+ lv_add(selector.set_control_lambda(await control.get_lambda()))
47
+ ctx.add(
48
+ paren.add_event_cb(
49
+ widget.obj,
50
+ await pub_ctx.get_lambda(),
51
+ LV_EVENT.VALUE_CHANGED,
52
+ UPDATE_EVENT,
53
+ )
54
+ )
55
+ lv_add(selector.publish_index(widget.get_value()))
@@ -0,0 +1,62 @@
1
+ #pragma once
2
+
3
+ #include "esphome/components/select/select.h"
4
+ #include "esphome/core/automation.h"
5
+ #include "esphome/core/component.h"
6
+ #include "esphome/core/preferences.h"
7
+
8
+ namespace esphome {
9
+ namespace lvgl {
10
+
11
+ static std::vector<std::string> split_string(const std::string &str) {
12
+ std::vector<std::string> strings;
13
+ auto delimiter = std::string("\n");
14
+
15
+ std::string::size_type pos;
16
+ std::string::size_type prev = 0;
17
+ while ((pos = str.find(delimiter, prev)) != std::string::npos) {
18
+ strings.push_back(str.substr(prev, pos - prev));
19
+ prev = pos + delimiter.size();
20
+ }
21
+
22
+ // To get the last substring (or only, if delimiter is not found)
23
+ strings.push_back(str.substr(prev));
24
+
25
+ return strings;
26
+ }
27
+
28
+ class LVGLSelect : public select::Select {
29
+ public:
30
+ void set_control_lambda(std::function<void(size_t)> lambda) {
31
+ this->control_lambda_ = lambda;
32
+ if (this->initial_state_.has_value()) {
33
+ this->control(this->initial_state_.value());
34
+ this->initial_state_.reset();
35
+ }
36
+ }
37
+
38
+ void publish_index(size_t index) {
39
+ auto value = this->at(index);
40
+ if (value)
41
+ this->publish_state(value.value());
42
+ }
43
+
44
+ void set_options(const char *str) { this->traits.set_options(split_string(str)); }
45
+
46
+ protected:
47
+ void control(const std::string &value) override {
48
+ if (this->control_lambda_ != nullptr) {
49
+ auto index = index_of(value);
50
+ if (index)
51
+ this->control_lambda_(index.value());
52
+ } else {
53
+ this->initial_state_ = value.c_str();
54
+ }
55
+ }
56
+
57
+ std::function<void(size_t)> control_lambda_{};
58
+ optional<const char *> initial_state_{};
59
+ };
60
+
61
+ } // namespace lvgl
62
+ } // namespace esphome
@@ -0,0 +1,47 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components.sensor import Sensor, new_sensor, sensor_schema
3
+ import esphome.config_validation as cv
4
+
5
+ from ..defines import CONF_LVGL_ID, CONF_WIDGET
6
+ from ..lvcode import (
7
+ API_EVENT,
8
+ EVENT_ARG,
9
+ LVGL_COMP_ARG,
10
+ UPDATE_EVENT,
11
+ LambdaContext,
12
+ LvContext,
13
+ lv_add,
14
+ )
15
+ from ..schemas import LVGL_SCHEMA
16
+ from ..types import LV_EVENT, LvNumber
17
+ from ..widgets import Widget, get_widgets
18
+
19
+ CONFIG_SCHEMA = (
20
+ sensor_schema(Sensor)
21
+ .extend(LVGL_SCHEMA)
22
+ .extend(
23
+ {
24
+ cv.Required(CONF_WIDGET): cv.use_id(LvNumber),
25
+ }
26
+ )
27
+ )
28
+
29
+
30
+ async def to_code(config):
31
+ sensor = await new_sensor(config)
32
+ paren = await cg.get_variable(config[CONF_LVGL_ID])
33
+ widget = await get_widgets(config, CONF_WIDGET)
34
+ widget = widget[0]
35
+ assert isinstance(widget, Widget)
36
+ async with LambdaContext(EVENT_ARG) as lamb:
37
+ lv_add(sensor.publish_state(widget.get_value()))
38
+ async with LvContext(paren, LVGL_COMP_ARG):
39
+ lv_add(
40
+ paren.add_event_cb(
41
+ widget.obj,
42
+ await lamb.get_lambda(),
43
+ LV_EVENT.VALUE_CHANGED,
44
+ API_EVENT,
45
+ UPDATE_EVENT,
46
+ )
47
+ )