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