esphome 2024.10.2__py3-none-any.whl → 2024.11.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 (239) hide show
  1. esphome/__main__.py +22 -4
  2. esphome/automation.py +29 -2
  3. esphome/components/animation/__init__.py +5 -8
  4. esphome/components/animation/animation.cpp +1 -1
  5. esphome/components/audio/__init__.py +9 -0
  6. esphome/components/audio/audio.h +21 -0
  7. esphome/components/axs15231/__init__.py +6 -0
  8. esphome/components/axs15231/touchscreen/__init__.py +36 -0
  9. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +64 -0
  10. esphome/components/axs15231/touchscreen/axs15231_touchscreen.h +27 -0
  11. esphome/components/bme68x_bsec2/__init__.py +1 -1
  12. esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +50 -47
  13. esphome/components/bme68x_bsec2/bme68x_bsec2.h +0 -2
  14. esphome/components/bytebuffer/__init__.py +5 -0
  15. esphome/components/bytebuffer/bytebuffer.h +421 -0
  16. esphome/components/climate/__init__.py +14 -13
  17. esphome/components/datetime/__init__.py +3 -3
  18. esphome/components/debug/debug_esp32.cpp +16 -8
  19. esphome/components/dfplayer/dfplayer.cpp +132 -6
  20. esphome/components/dfplayer/dfplayer.h +19 -53
  21. esphome/components/display/display.cpp +142 -0
  22. esphome/components/display/display.h +7 -0
  23. esphome/components/es8311/__init__.py +0 -0
  24. esphome/components/es8311/audio_dac.py +70 -0
  25. esphome/components/es8311/es8311.cpp +227 -0
  26. esphome/components/es8311/es8311.h +135 -0
  27. esphome/components/es8311/es8311_const.h +195 -0
  28. esphome/components/esp32/boards.py +199 -1
  29. esphome/components/esp32/gpio.py +3 -1
  30. esphome/components/esp32_ble/const_esp32c6.h +7 -0
  31. esphome/components/esp32_ble_client/ble_client_base.h +1 -1
  32. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +3 -0
  33. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -1
  34. esphome/components/esp32_rmt_led_strip/led_strip.cpp +2 -2
  35. esphome/components/esp32_rmt_led_strip/led_strip.h +2 -0
  36. esphome/components/esp32_rmt_led_strip/light.py +3 -1
  37. esphome/components/esp8266/gpio.py +7 -5
  38. esphome/components/ethernet/__init__.py +55 -1
  39. esphome/components/ethernet/ethernet_component.cpp +14 -1
  40. esphome/components/ethernet/ethernet_component.h +7 -1
  41. esphome/components/font/__init__.py +213 -108
  42. esphome/components/gp8403/output/__init__.py +1 -1
  43. esphome/components/host/gpio.py +6 -4
  44. esphome/components/http_request/__init__.py +12 -0
  45. esphome/components/http_request/http_request.h +65 -3
  46. esphome/components/http_request/http_request_arduino.cpp +4 -3
  47. esphome/components/http_request/http_request_idf.cpp +12 -14
  48. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  49. esphome/components/http_request/update/http_request_update.cpp +1 -1
  50. esphome/components/i2c_device/__init__.py +26 -0
  51. esphome/components/i2c_device/i2c_device.cpp +17 -0
  52. esphome/components/i2c_device/i2c_device.h +18 -0
  53. esphome/components/i2s_audio/__init__.py +1 -3
  54. esphome/components/i2s_audio/speaker/__init__.py +12 -4
  55. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +426 -200
  56. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +92 -33
  57. esphome/components/ili9xxx/display.py +5 -1
  58. esphome/components/image/__init__.py +5 -8
  59. esphome/components/image/image.cpp +14 -14
  60. esphome/components/image/image.h +20 -24
  61. esphome/components/internal_temperature/internal_temperature.cpp +51 -2
  62. esphome/components/internal_temperature/internal_temperature.h +1 -0
  63. esphome/components/ld2420/ld2420.cpp +1 -1
  64. esphome/components/libretiny/gpio.py +4 -2
  65. esphome/components/light/__init__.py +32 -1
  66. esphome/components/light/automation.py +39 -32
  67. esphome/components/light/effects.py +36 -36
  68. esphome/components/light/light_state.cpp +6 -16
  69. esphome/components/light/light_state.h +34 -0
  70. esphome/components/light/types.py +3 -1
  71. esphome/components/logger/logger_esp32.cpp +15 -0
  72. esphome/components/lvgl/__init__.py +202 -95
  73. esphome/components/lvgl/automation.py +42 -40
  74. esphome/components/lvgl/binary_sensor/__init__.py +8 -15
  75. esphome/components/lvgl/defines.py +14 -8
  76. esphome/components/lvgl/encoders.py +11 -8
  77. esphome/components/lvgl/keypads.py +77 -0
  78. esphome/components/lvgl/light/__init__.py +6 -8
  79. esphome/components/lvgl/lv_validation.py +2 -4
  80. esphome/components/lvgl/lvcode.py +3 -9
  81. esphome/components/lvgl/lvgl_esphome.cpp +210 -89
  82. esphome/components/lvgl/lvgl_esphome.h +113 -30
  83. esphome/components/lvgl/lvgl_proxy.h +17 -0
  84. esphome/components/lvgl/number/__init__.py +10 -15
  85. esphome/components/lvgl/schemas.py +4 -2
  86. esphome/components/lvgl/select/__init__.py +12 -37
  87. esphome/components/lvgl/select/lvgl_select.h +27 -33
  88. esphome/components/lvgl/sensor/__init__.py +8 -14
  89. esphome/components/lvgl/styles.py +3 -4
  90. esphome/components/lvgl/switch/__init__.py +8 -13
  91. esphome/components/lvgl/text/__init__.py +5 -6
  92. esphome/components/lvgl/text_sensor/__init__.py +15 -15
  93. esphome/components/lvgl/touchscreens.py +2 -3
  94. esphome/components/lvgl/trigger.py +7 -9
  95. esphome/components/lvgl/types.py +9 -3
  96. esphome/components/lvgl/widgets/__init__.py +32 -21
  97. esphome/components/lvgl/widgets/animimg.py +4 -3
  98. esphome/components/lvgl/widgets/dropdown.py +22 -10
  99. esphome/components/lvgl/widgets/img.py +2 -0
  100. esphome/components/lvgl/widgets/msgbox.py +6 -5
  101. esphome/components/lvgl/widgets/obj.py +4 -2
  102. esphome/components/lvgl/widgets/page.py +3 -2
  103. esphome/components/lvgl/widgets/qrcode.py +54 -0
  104. esphome/components/lvgl/widgets/roller.py +21 -14
  105. esphome/components/lvgl/widgets/tileview.py +2 -1
  106. esphome/components/max17043/__init__.py +1 -0
  107. esphome/components/max17043/automation.h +20 -0
  108. esphome/components/max17043/max17043.cpp +98 -0
  109. esphome/components/max17043/max17043.h +29 -0
  110. esphome/components/max17043/sensor.py +77 -0
  111. esphome/components/media_player/__init__.py +11 -0
  112. esphome/components/media_player/automation.h +10 -0
  113. esphome/components/media_player/media_player.cpp +4 -0
  114. esphome/components/midea/air_conditioner.cpp +17 -1
  115. esphome/components/mlx90393/sensor.py +1 -1
  116. esphome/components/modbus/modbus.cpp +24 -12
  117. esphome/components/modbus_controller/__init__.py +31 -1
  118. esphome/components/modbus_controller/automation.h +16 -0
  119. esphome/components/modbus_controller/const.py +2 -0
  120. esphome/components/modbus_controller/modbus_controller.cpp +14 -2
  121. esphome/components/modbus_controller/modbus_controller.h +9 -0
  122. esphome/components/mopeka_pro_check/mopeka_pro_check.cpp +40 -21
  123. esphome/components/mopeka_pro_check/mopeka_pro_check.h +9 -2
  124. esphome/components/mopeka_pro_check/sensor.py +41 -0
  125. esphome/components/mqtt/__init__.py +36 -0
  126. esphome/components/mqtt/mqtt_client.cpp +27 -3
  127. esphome/components/mqtt/mqtt_client.h +27 -2
  128. esphome/components/mqtt/mqtt_climate.cpp +4 -2
  129. esphome/components/mqtt/mqtt_component.cpp +6 -0
  130. esphome/components/mqtt/mqtt_component.h +4 -0
  131. esphome/components/mqtt/mqtt_const.h +6 -0
  132. esphome/components/online_image/online_image.cpp +2 -8
  133. esphome/components/online_image/online_image.h +2 -6
  134. esphome/components/opentherm/__init__.py +35 -9
  135. esphome/components/opentherm/binary_sensor/__init__.py +33 -0
  136. esphome/components/opentherm/const.py +11 -0
  137. esphome/components/opentherm/generate.py +142 -0
  138. esphome/components/opentherm/hub.cpp +130 -24
  139. esphome/components/opentherm/hub.h +62 -9
  140. esphome/components/opentherm/input.h +18 -0
  141. esphome/components/opentherm/input.py +51 -0
  142. esphome/components/opentherm/number/__init__.py +74 -0
  143. esphome/components/opentherm/number/number.cpp +40 -0
  144. esphome/components/opentherm/number/number.h +31 -0
  145. esphome/components/opentherm/opentherm.cpp +30 -0
  146. esphome/components/opentherm/opentherm.h +34 -2
  147. esphome/components/opentherm/opentherm_macros.h +151 -0
  148. esphome/components/opentherm/output/__init__.py +47 -0
  149. esphome/components/opentherm/output/output.cpp +18 -0
  150. esphome/components/opentherm/output/output.h +33 -0
  151. esphome/components/opentherm/schema.py +814 -0
  152. esphome/components/opentherm/sensor/__init__.py +51 -0
  153. esphome/components/opentherm/switch/__init__.py +43 -0
  154. esphome/components/opentherm/switch/switch.cpp +28 -0
  155. esphome/components/opentherm/switch/switch.h +20 -0
  156. esphome/components/opentherm/validate.py +31 -0
  157. esphome/components/pcd8544/display.py +8 -4
  158. esphome/components/prometheus/prometheus_handler.cpp +176 -14
  159. esphome/components/prometheus/prometheus_handler.h +25 -7
  160. esphome/components/qspi_amoled/display.py +1 -141
  161. esphome/components/qspi_dbi/display.py +185 -0
  162. esphome/components/qspi_dbi/models.py +64 -0
  163. esphome/components/{qspi_amoled/qspi_amoled.cpp → qspi_dbi/qspi_dbi.cpp} +95 -46
  164. esphome/components/{qspi_amoled/qspi_amoled.h → qspi_dbi/qspi_dbi.h} +26 -15
  165. esphome/components/rp2040/__init__.py +6 -3
  166. esphome/components/rp2040/gpio.py +5 -3
  167. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +20 -0
  168. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h +3 -2
  169. esphome/components/rtttl/rtttl.cpp +4 -1
  170. esphome/components/rtttl/rtttl.h +1 -0
  171. esphome/components/sdl/sdl_esphome.cpp +22 -5
  172. esphome/components/sdl/sdl_esphome.h +1 -0
  173. esphome/components/sdm_meter/sdm_meter.cpp +1 -1
  174. esphome/components/sensor/__init__.py +18 -8
  175. esphome/components/sensor/filter.cpp +19 -18
  176. esphome/components/sensor/filter.h +9 -10
  177. esphome/components/sgp4x/sgp4x.cpp +40 -74
  178. esphome/components/sgp4x/sgp4x.h +5 -3
  179. esphome/components/speaker/__init__.py +51 -5
  180. esphome/components/speaker/automation.h +25 -0
  181. esphome/components/speaker/speaker.h +72 -1
  182. esphome/components/spi/__init__.py +15 -14
  183. esphome/components/spi_device/__init__.py +4 -15
  184. esphome/components/ssd1306_spi/display.py +6 -2
  185. esphome/components/ssd1322_spi/display.py +6 -2
  186. esphome/components/ssd1325_spi/display.py +6 -2
  187. esphome/components/ssd1327_spi/display.py +6 -2
  188. esphome/components/ssd1331_spi/display.py +6 -2
  189. esphome/components/ssd1351_spi/display.py +6 -2
  190. esphome/components/st7567_spi/display.py +6 -2
  191. esphome/components/st7701s/display.py +5 -1
  192. esphome/components/st7735/display.py +10 -5
  193. esphome/components/st7789v/display.py +12 -7
  194. esphome/components/statsd/statsd.cpp +2 -0
  195. esphome/components/statsd/statsd.h +2 -0
  196. esphome/components/sun/sun.h +3 -0
  197. esphome/components/tc74/__init__.py +1 -0
  198. esphome/components/tc74/sensor.py +32 -0
  199. esphome/components/tc74/tc74.cpp +68 -0
  200. esphome/components/tc74/tc74.h +28 -0
  201. esphome/components/touchscreen/__init__.py +41 -50
  202. esphome/components/touchscreen/touchscreen.h +4 -8
  203. esphome/components/tuya/fan/tuya_fan.cpp +1 -1
  204. esphome/components/udp/udp_component.cpp +6 -3
  205. esphome/components/udp/udp_component.h +4 -2
  206. esphome/components/waveshare_epaper/display.py +6 -2
  207. esphome/components/web_server/web_server.cpp +22 -0
  208. esphome/components/web_server/web_server.h +3 -0
  209. esphome/components/weikai/weikai.h +2 -2
  210. esphome/components/wifi/wifi_component.cpp +2 -2
  211. esphome/components/wifi/wifi_component_esp32_arduino.cpp +4 -4
  212. esphome/components/wifi/wifi_component_esp8266.cpp +4 -4
  213. esphome/components/wifi/wifi_component_esp_idf.cpp +2 -2
  214. esphome/components/xpt2046/touchscreen/__init__.py +7 -32
  215. esphome/config_validation.py +3 -1
  216. esphome/const.py +9 -2
  217. esphome/core/defines.h +8 -2
  218. esphome/core/helpers.cpp +32 -17
  219. esphome/core/helpers.h +32 -16
  220. esphome/core/ring_buffer.cpp +2 -2
  221. esphome/core/ring_buffer.h +2 -2
  222. esphome/dashboard/core.py +25 -0
  223. esphome/dashboard/status/mdns.py +3 -4
  224. esphome/dashboard/web_server.py +54 -19
  225. esphome/espota2.py +36 -35
  226. esphome/helpers.py +68 -16
  227. esphome/mqtt.py +9 -2
  228. esphome/storage_json.py +4 -0
  229. esphome/writer.py +7 -18
  230. esphome/zeroconf.py +8 -6
  231. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/METADATA +7 -5
  232. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/RECORD +237 -191
  233. esphome/core/bytebuffer.cpp +0 -167
  234. esphome/core/bytebuffer.h +0 -144
  235. /esphome/components/{qspi_amoled → qspi_dbi}/__init__.py +0 -0
  236. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/LICENSE +0 -0
  237. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/WHEEL +0 -0
  238. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/entry_points.txt +0 -0
  239. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,3 @@
1
- import esphome.codegen as cg
2
1
  import esphome.config_validation as cv
3
2
  from esphome.const import CONF_OPTIONS
4
3
 
@@ -9,21 +8,24 @@ from ..defines import (
9
8
  CONF_SCROLLBAR,
10
9
  CONF_SELECTED,
11
10
  CONF_SELECTED_INDEX,
11
+ CONF_SELECTED_TEXT,
12
12
  CONF_SYMBOL,
13
13
  DIRECTIONS,
14
14
  literal,
15
15
  )
16
+ from ..helpers import lvgl_components_required
16
17
  from ..lv_validation import lv_int, lv_text, option_string
17
- from ..lvcode import LocalVariable, lv, lv_expr
18
+ from ..lvcode import LocalVariable, lv, lv_add, lv_expr
18
19
  from ..schemas import part_schema
19
- from ..types import LvSelect, LvType, lv_obj_t
20
+ from ..types import LvCompound, LvSelect, LvType, lv_obj_t
20
21
  from . import Widget, WidgetType, set_obj_properties
21
22
  from .label import CONF_LABEL
22
23
 
23
24
  CONF_DROPDOWN = "dropdown"
24
25
  CONF_DROPDOWN_LIST = "dropdown_list"
25
26
 
26
- lv_dropdown_t = LvSelect("lv_dropdown_t")
27
+ lv_dropdown_t = LvSelect("LvDropdownType", parents=(LvCompound,))
28
+
27
29
  lv_dropdown_list_t = LvType("lv_dropdown_list_t")
28
30
  dropdown_list_spec = WidgetType(
29
31
  CONF_DROPDOWN_LIST, lv_dropdown_list_t, (CONF_MAIN, CONF_SELECTED, CONF_SCROLLBAR)
@@ -32,7 +34,8 @@ dropdown_list_spec = WidgetType(
32
34
  DROPDOWN_BASE_SCHEMA = cv.Schema(
33
35
  {
34
36
  cv.Optional(CONF_SYMBOL): lv_text,
35
- cv.Optional(CONF_SELECTED_INDEX): cv.templatable(cv.int_),
37
+ cv.Exclusive(CONF_SELECTED_INDEX, CONF_SELECTED_TEXT): lv_int,
38
+ cv.Exclusive(CONF_SELECTED_TEXT, CONF_SELECTED_TEXT): lv_text,
36
39
  cv.Optional(CONF_DIR, default="BOTTOM"): DIRECTIONS.one_of,
37
40
  cv.Optional(CONF_DROPDOWN_LIST): part_schema(dropdown_list_spec),
38
41
  }
@@ -44,6 +47,12 @@ DROPDOWN_SCHEMA = DROPDOWN_BASE_SCHEMA.extend(
44
47
  }
45
48
  )
46
49
 
50
+ DROPDOWN_UPDATE_SCHEMA = DROPDOWN_BASE_SCHEMA.extend(
51
+ {
52
+ cv.Optional(CONF_OPTIONS): cv.ensure_list(option_string),
53
+ }
54
+ )
55
+
47
56
 
48
57
  class DropdownType(WidgetType):
49
58
  def __init__(self):
@@ -52,18 +61,21 @@ class DropdownType(WidgetType):
52
61
  lv_dropdown_t,
53
62
  (CONF_MAIN, CONF_INDICATOR),
54
63
  DROPDOWN_SCHEMA,
55
- DROPDOWN_BASE_SCHEMA,
64
+ modify_schema=DROPDOWN_UPDATE_SCHEMA,
56
65
  )
57
66
 
58
67
  async def to_code(self, w: Widget, config):
68
+ lvgl_components_required.add(CONF_DROPDOWN)
59
69
  if options := config.get(CONF_OPTIONS):
60
- text = cg.safe_exp("\n".join(options))
61
- lv.dropdown_set_options(w.obj, text)
70
+ lv_add(w.var.set_options(options))
62
71
  if symbol := config.get(CONF_SYMBOL):
63
- lv.dropdown_set_symbol(w.obj, await lv_text.process(symbol))
72
+ lv.dropdown_set_symbol(w.var.obj, await lv_text.process(symbol))
64
73
  if (selected := config.get(CONF_SELECTED_INDEX)) is not None:
65
74
  value = await lv_int.process(selected)
66
- lv.dropdown_set_selected(w.obj, value)
75
+ lv_add(w.var.set_selected_index(value, literal("LV_ANIM_OFF")))
76
+ if (selected := config.get(CONF_SELECTED_TEXT)) is not None:
77
+ value = await lv_text.process(selected)
78
+ lv_add(w.var.set_selected_text(value, literal("LV_ANIM_OFF")))
67
79
  if dirn := config.get(CONF_DIR):
68
80
  lv.dropdown_set_dir(w.obj, literal(dirn))
69
81
  if dlist := config.get(CONF_DROPDOWN_LIST):
@@ -1,3 +1,4 @@
1
+ import esphome.codegen as cg
1
2
  import esphome.config_validation as cv
2
3
  from esphome.const import CONF_ANGLE, CONF_MODE
3
4
 
@@ -64,6 +65,7 @@ class ImgType(WidgetType):
64
65
 
65
66
  async def to_code(self, w: Widget, config):
66
67
  if src := config.get(CONF_SRC):
68
+ src = await cg.get_variable(src)
67
69
  lv.img_set_src(w.obj, await lv_image.process(src))
68
70
  if (cf_angle := config.get(CONF_ANGLE)) is not None:
69
71
  pivot_x = config[CONF_PIVOT_X]
@@ -20,6 +20,7 @@ from ..lvcode import (
20
20
  EVENT_ARG,
21
21
  LambdaContext,
22
22
  LocalVariable,
23
+ lv,
23
24
  lv_add,
24
25
  lv_assign,
25
26
  lv_expr,
@@ -27,7 +28,6 @@ from ..lvcode import (
27
28
  lv_Pvariable,
28
29
  )
29
30
  from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema
30
- from ..styles import TOP_LAYER
31
31
  from ..types import LV_EVENT, char_ptr, lv_obj_t
32
32
  from . import Widget, set_obj_properties
33
33
  from .button import button_spec
@@ -59,7 +59,7 @@ MSGBOX_SCHEMA = container_schema(
59
59
  )
60
60
 
61
61
 
62
- async def msgbox_to_code(conf):
62
+ async def msgbox_to_code(top_layer, conf):
63
63
  """
64
64
  Construct a message box. This consists of a full-screen translucent background enclosing a centered container
65
65
  with an optional title, body, close button and a button matrix. And any other widgets the user cares to add
@@ -101,7 +101,7 @@ async def msgbox_to_code(conf):
101
101
  text = await lv_text.process(conf[CONF_BODY].get(CONF_TEXT, ""))
102
102
  title = await lv_text.process(conf[CONF_TITLE].get(CONF_TEXT, ""))
103
103
  close_button = conf[CONF_CLOSE_BUTTON]
104
- lv_assign(outer, lv_expr.obj_create(TOP_LAYER))
104
+ lv_assign(outer, lv_expr.obj_create(top_layer))
105
105
  lv_obj.set_width(outer, lv_pct(100))
106
106
  lv_obj.set_height(outer, lv_pct(100))
107
107
  lv_obj.set_style_bg_opa(outer, 128, 0)
@@ -141,6 +141,7 @@ async def msgbox_to_code(conf):
141
141
  set_btn_data(buttonmatrix.obj, ctrl_list, width_list)
142
142
 
143
143
 
144
- async def msgboxes_to_code(config):
144
+ async def msgboxes_to_code(lv_component, config):
145
+ top_layer = lv.disp_get_layer_top(lv_component.get_disp())
145
146
  for conf in config.get(CONF_MSGBOXES, ()):
146
- await msgbox_to_code(conf)
147
+ await msgbox_to_code(top_layer, conf)
@@ -1,7 +1,7 @@
1
1
  from esphome import automation
2
2
 
3
3
  from ..automation import update_to_code
4
- from ..defines import CONF_MAIN, CONF_OBJ
4
+ from ..defines import CONF_MAIN, CONF_OBJ, CONF_SCROLLBAR
5
5
  from ..schemas import create_modify_schema
6
6
  from ..types import ObjUpdateAction, WidgetType, lv_obj_t
7
7
 
@@ -12,7 +12,9 @@ class ObjType(WidgetType):
12
12
  """
13
13
 
14
14
  def __init__(self):
15
- super().__init__(CONF_OBJ, lv_obj_t, (CONF_MAIN,), schema={}, modify_schema={})
15
+ super().__init__(
16
+ CONF_OBJ, lv_obj_t, (CONF_MAIN, CONF_SCROLLBAR), schema={}, modify_schema={}
17
+ )
16
18
 
17
19
  async def to_code(self, w, config):
18
20
  return []
@@ -20,6 +20,7 @@ from ..lvcode import (
20
20
  add_line_marks,
21
21
  lv_add,
22
22
  lvgl_comp,
23
+ lvgl_static,
23
24
  )
24
25
  from ..schemas import LVGL_SCHEMA
25
26
  from ..types import LvglAction, lv_page_t
@@ -139,7 +140,7 @@ async def add_pages(lv_component, config):
139
140
  await add_widgets(page, pconf)
140
141
 
141
142
 
142
- async def generate_page_triggers(lv_component, config):
143
+ async def generate_page_triggers(config):
143
144
  for pconf in config.get(CONF_PAGES, ()):
144
145
  page = (await get_widgets(pconf))[0]
145
146
  for ev in (CONF_ON_LOAD, CONF_ON_UNLOAD):
@@ -149,7 +150,7 @@ async def generate_page_triggers(lv_component, config):
149
150
  async with LambdaContext(EVENT_ARG, where=id) as context:
150
151
  lv_add(trigger.trigger())
151
152
  lv_add(
152
- lv_component.add_event_cb(
153
+ lvgl_static.add_event_cb(
153
154
  page.obj,
154
155
  await context.get_lambda(),
155
156
  literal(f"LV_EVENT_SCREEN_{ev[3:].upper()}_START"),
@@ -0,0 +1,54 @@
1
+ import esphome.codegen as cg
2
+ import esphome.config_validation as cv
3
+ from esphome.const import CONF_SIZE, CONF_TEXT
4
+ from esphome.cpp_generator import MockObjClass
5
+
6
+ from ..defines import CONF_MAIN, literal
7
+ from ..lv_validation import color, color_retmapper, lv_text
8
+ from ..lvcode import LocalVariable, lv, lv_expr
9
+ from ..schemas import TEXT_SCHEMA
10
+ from ..types import WidgetType, lv_obj_t
11
+ from . import Widget
12
+
13
+ CONF_QRCODE = "qrcode"
14
+ CONF_DARK_COLOR = "dark_color"
15
+ CONF_LIGHT_COLOR = "light_color"
16
+
17
+ QRCODE_SCHEMA = TEXT_SCHEMA.extend(
18
+ {
19
+ cv.Optional(CONF_DARK_COLOR, default="black"): color,
20
+ cv.Optional(CONF_LIGHT_COLOR, default="white"): color,
21
+ cv.Required(CONF_SIZE): cv.int_,
22
+ }
23
+ )
24
+
25
+
26
+ class QrCodeType(WidgetType):
27
+ def __init__(self):
28
+ super().__init__(
29
+ CONF_QRCODE,
30
+ lv_obj_t,
31
+ (CONF_MAIN,),
32
+ QRCODE_SCHEMA,
33
+ modify_schema=TEXT_SCHEMA,
34
+ )
35
+
36
+ def get_uses(self):
37
+ return ("canvas", "img")
38
+
39
+ def obj_creator(self, parent: MockObjClass, config: dict):
40
+ dark_color = color_retmapper(config[CONF_DARK_COLOR])
41
+ light_color = color_retmapper(config[CONF_LIGHT_COLOR])
42
+ size = config[CONF_SIZE]
43
+ return lv_expr.call("qrcode_create", parent, size, dark_color, light_color)
44
+
45
+ async def to_code(self, w: Widget, config):
46
+ if (value := config.get(CONF_TEXT)) is not None:
47
+ value = await lv_text.process(value)
48
+ with LocalVariable(
49
+ "qr_text", cg.const_char_ptr, value, modifier=""
50
+ ) as str_obj:
51
+ lv.qrcode_update(w.obj, str_obj, literal(f"strlen({str_obj})"))
52
+
53
+
54
+ qr_code_spec = QrCodeType()
@@ -1,4 +1,3 @@
1
- import esphome.codegen as cg
2
1
  import esphome.config_validation as cv
3
2
  from esphome.const import CONF_MODE, CONF_OPTIONS
4
3
 
@@ -7,36 +6,40 @@ from ..defines import (
7
6
  CONF_MAIN,
8
7
  CONF_SELECTED,
9
8
  CONF_SELECTED_INDEX,
9
+ CONF_SELECTED_TEXT,
10
10
  CONF_VISIBLE_ROW_COUNT,
11
11
  ROLLER_MODES,
12
12
  literal,
13
13
  )
14
- from ..lv_validation import animated, lv_int, option_string
15
- from ..lvcode import lv
14
+ from ..helpers import lvgl_components_required
15
+ from ..lv_validation import animated, lv_int, lv_text, option_string
16
+ from ..lvcode import lv_add
16
17
  from ..types import LvSelect
17
18
  from . import WidgetType
18
19
  from .label import CONF_LABEL
19
20
 
20
21
  CONF_ROLLER = "roller"
21
- lv_roller_t = LvSelect("lv_roller_t")
22
+ lv_roller_t = LvSelect("LvRollerType")
22
23
 
23
24
  ROLLER_BASE_SCHEMA = cv.Schema(
24
25
  {
25
- cv.Optional(CONF_SELECTED_INDEX): cv.templatable(cv.int_),
26
+ cv.Exclusive(CONF_SELECTED_INDEX, CONF_SELECTED_TEXT): lv_int,
27
+ cv.Exclusive(CONF_SELECTED_TEXT, CONF_SELECTED_TEXT): lv_text,
26
28
  cv.Optional(CONF_VISIBLE_ROW_COUNT): lv_int,
29
+ cv.Optional(CONF_MODE): ROLLER_MODES.one_of,
27
30
  }
28
31
  )
29
32
 
30
33
  ROLLER_SCHEMA = ROLLER_BASE_SCHEMA.extend(
31
34
  {
32
35
  cv.Required(CONF_OPTIONS): cv.ensure_list(option_string),
33
- cv.Optional(CONF_MODE, default="NORMAL"): ROLLER_MODES.one_of,
34
36
  }
35
37
  )
36
38
 
37
39
  ROLLER_MODIFY_SCHEMA = ROLLER_BASE_SCHEMA.extend(
38
40
  {
39
41
  cv.Optional(CONF_ANIMATED, default=True): animated,
42
+ cv.Optional(CONF_OPTIONS): cv.ensure_list(option_string),
40
43
  }
41
44
  )
42
45
 
@@ -52,15 +55,19 @@ class RollerType(WidgetType):
52
55
  )
53
56
 
54
57
  async def to_code(self, w, config):
58
+ lvgl_components_required.add(CONF_ROLLER)
59
+ if mode := config.get(CONF_MODE):
60
+ mode = await ROLLER_MODES.process(mode)
61
+ lv_add(w.var.set_mode(mode))
55
62
  if options := config.get(CONF_OPTIONS):
56
- mode = await ROLLER_MODES.process(config[CONF_MODE])
57
- text = cg.safe_exp("\n".join(options))
58
- lv.roller_set_options(w.obj, text, mode)
59
- animopt = literal(config.get(CONF_ANIMATED) or "LV_ANIM_OFF")
60
- if CONF_SELECTED_INDEX in config:
61
- if selected := config[CONF_SELECTED_INDEX]:
62
- value = await lv_int.process(selected)
63
- lv.roller_set_selected(w.obj, value, animopt)
63
+ lv_add(w.var.set_options(options))
64
+ animopt = literal(config.get(CONF_ANIMATED, "LV_ANIM_OFF"))
65
+ if (selected := config.get(CONF_SELECTED_INDEX)) is not None:
66
+ value = await lv_int.process(selected)
67
+ lv_add(w.var.set_selected_index(value, animopt))
68
+ if (selected := config.get(CONF_SELECTED_TEXT)) is not None:
69
+ value = await lv_text.process(selected)
70
+ lv_add(w.var.set_selected_text(value, animopt))
64
71
  await w.set_property(
65
72
  CONF_VISIBLE_ROW_COUNT,
66
73
  await lv_int.process(config.get(CONF_VISIBLE_ROW_COUNT)),
@@ -9,6 +9,7 @@ from ..defines import (
9
9
  CONF_COLUMN,
10
10
  CONF_DIR,
11
11
  CONF_MAIN,
12
+ CONF_SCROLLBAR,
12
13
  CONF_TILE_ID,
13
14
  CONF_TILES,
14
15
  TILE_DIRECTIONS,
@@ -56,7 +57,7 @@ class TileviewType(WidgetType):
56
57
  super().__init__(
57
58
  CONF_TILEVIEW,
58
59
  lv_tileview_t,
59
- (CONF_MAIN,),
60
+ (CONF_MAIN, CONF_SCROLLBAR),
60
61
  schema=TILEVIEW_SCHEMA,
61
62
  modify_schema={},
62
63
  )
@@ -0,0 +1 @@
1
+ CODEOWNERS = ["@blacknell"]
@@ -0,0 +1,20 @@
1
+
2
+ #pragma once
3
+ #include "esphome/core/automation.h"
4
+ #include "max17043.h"
5
+
6
+ namespace esphome {
7
+ namespace max17043 {
8
+
9
+ template<typename... Ts> class SleepAction : public Action<Ts...> {
10
+ public:
11
+ explicit SleepAction(MAX17043Component *max17043) : max17043_(max17043) {}
12
+
13
+ void play(Ts... x) override { this->max17043_->sleep_mode(); }
14
+
15
+ protected:
16
+ MAX17043Component *max17043_;
17
+ };
18
+
19
+ } // namespace max17043
20
+ } // namespace esphome
@@ -0,0 +1,98 @@
1
+ #include "max17043.h"
2
+ #include "esphome/core/log.h"
3
+
4
+ namespace esphome {
5
+ namespace max17043 {
6
+
7
+ // MAX174043 is a 1-Cell Fuel Gauge with ModelGauge and Low-Battery Alert
8
+ // Consult the datasheet at https://www.analog.com/en/products/max17043.html
9
+
10
+ static const char *const TAG = "max17043";
11
+
12
+ static const uint8_t MAX17043_VCELL = 0x02;
13
+ static const uint8_t MAX17043_SOC = 0x04;
14
+ static const uint8_t MAX17043_CONFIG = 0x0c;
15
+
16
+ static const uint16_t MAX17043_CONFIG_POWER_UP_DEFAULT = 0x971C;
17
+ static const uint16_t MAX17043_CONFIG_SAFE_MASK = 0xFF1F; // mask out sleep bit (7), unused bit (6) and alert bit (4)
18
+ static const uint16_t MAX17043_CONFIG_SLEEP_MASK = 0x0080;
19
+
20
+ void MAX17043Component::update() {
21
+ uint16_t raw_voltage, raw_percent;
22
+
23
+ if (this->voltage_sensor_ != nullptr) {
24
+ if (!this->read_byte_16(MAX17043_VCELL, &raw_voltage)) {
25
+ this->status_set_warning("Unable to read MAX17043_VCELL");
26
+ } else {
27
+ float voltage = (1.25 * (float) (raw_voltage >> 4)) / 1000.0;
28
+ this->voltage_sensor_->publish_state(voltage);
29
+ this->status_clear_warning();
30
+ }
31
+ }
32
+ if (this->battery_remaining_sensor_ != nullptr) {
33
+ if (!this->read_byte_16(MAX17043_SOC, &raw_percent)) {
34
+ this->status_set_warning("Unable to read MAX17043_SOC");
35
+ } else {
36
+ float percent = (float) ((raw_percent >> 8) + 0.003906f * (raw_percent & 0x00ff));
37
+ this->battery_remaining_sensor_->publish_state(percent);
38
+ this->status_clear_warning();
39
+ }
40
+ }
41
+ }
42
+
43
+ void MAX17043Component::setup() {
44
+ ESP_LOGCONFIG(TAG, "Setting up MAX17043...");
45
+
46
+ uint16_t config_reg;
47
+ if (this->write(&MAX17043_CONFIG, 1) != i2c::ERROR_OK) {
48
+ this->status_set_warning();
49
+ return;
50
+ }
51
+
52
+ if (this->read(reinterpret_cast<uint8_t *>(&config_reg), 2) != i2c::ERROR_OK) {
53
+ this->status_set_warning();
54
+ return;
55
+ }
56
+
57
+ config_reg = i2c::i2ctohs(config_reg) & MAX17043_CONFIG_SAFE_MASK;
58
+ ESP_LOGV(TAG, "MAX17043 CONFIG register reads 0x%X", config_reg);
59
+
60
+ if (config_reg != MAX17043_CONFIG_POWER_UP_DEFAULT) {
61
+ ESP_LOGE(TAG, "Device does not appear to be a MAX17043");
62
+ this->status_set_error("unrecognised");
63
+ this->mark_failed();
64
+ return;
65
+ }
66
+
67
+ // need to write back to config register to reset the sleep bit
68
+ if (!this->write_byte_16(MAX17043_CONFIG, MAX17043_CONFIG_POWER_UP_DEFAULT)) {
69
+ this->status_set_error("sleep reset failed");
70
+ this->mark_failed();
71
+ return;
72
+ }
73
+ }
74
+
75
+ void MAX17043Component::dump_config() {
76
+ ESP_LOGCONFIG(TAG, "MAX17043:");
77
+ LOG_I2C_DEVICE(this);
78
+ if (this->is_failed()) {
79
+ ESP_LOGE(TAG, "Communication with MAX17043 failed");
80
+ }
81
+ LOG_UPDATE_INTERVAL(this);
82
+ LOG_SENSOR(" ", "Battery Voltage", this->voltage_sensor_);
83
+ LOG_SENSOR(" ", "Battery Level", this->battery_remaining_sensor_);
84
+ }
85
+
86
+ float MAX17043Component::get_setup_priority() const { return setup_priority::DATA; }
87
+
88
+ void MAX17043Component::sleep_mode() {
89
+ if (!this->is_failed()) {
90
+ if (!this->write_byte_16(MAX17043_CONFIG, MAX17043_CONFIG_POWER_UP_DEFAULT | MAX17043_CONFIG_SLEEP_MASK)) {
91
+ ESP_LOGW(TAG, "Unable to write the sleep bit to config register");
92
+ this->status_set_warning();
93
+ }
94
+ }
95
+ }
96
+
97
+ } // namespace max17043
98
+ } // namespace esphome
@@ -0,0 +1,29 @@
1
+ #pragma once
2
+
3
+ #include "esphome/core/component.h"
4
+ #include "esphome/components/sensor/sensor.h"
5
+ #include "esphome/components/i2c/i2c.h"
6
+
7
+ namespace esphome {
8
+ namespace max17043 {
9
+
10
+ class MAX17043Component : public PollingComponent, public i2c::I2CDevice {
11
+ public:
12
+ void setup() override;
13
+ void dump_config() override;
14
+ float get_setup_priority() const override;
15
+ void update() override;
16
+ void sleep_mode();
17
+
18
+ void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
19
+ void set_battery_remaining_sensor(sensor::Sensor *battery_remaining_sensor) {
20
+ battery_remaining_sensor_ = battery_remaining_sensor;
21
+ }
22
+
23
+ protected:
24
+ sensor::Sensor *voltage_sensor_{nullptr};
25
+ sensor::Sensor *battery_remaining_sensor_{nullptr};
26
+ };
27
+
28
+ } // namespace max17043
29
+ } // namespace esphome
@@ -0,0 +1,77 @@
1
+ from esphome import automation
2
+ from esphome.automation import maybe_simple_id
3
+ import esphome.codegen as cg
4
+ from esphome.components import i2c, sensor
5
+ import esphome.config_validation as cv
6
+ from esphome.const import (
7
+ CONF_BATTERY_LEVEL,
8
+ CONF_BATTERY_VOLTAGE,
9
+ CONF_ID,
10
+ DEVICE_CLASS_BATTERY,
11
+ DEVICE_CLASS_VOLTAGE,
12
+ ENTITY_CATEGORY_DIAGNOSTIC,
13
+ STATE_CLASS_MEASUREMENT,
14
+ UNIT_PERCENT,
15
+ UNIT_VOLT,
16
+ )
17
+
18
+ DEPENDENCIES = ["i2c"]
19
+
20
+ max17043_ns = cg.esphome_ns.namespace("max17043")
21
+ MAX17043Component = max17043_ns.class_(
22
+ "MAX17043Component", cg.PollingComponent, i2c.I2CDevice
23
+ )
24
+
25
+ # Actions
26
+ SleepAction = max17043_ns.class_("SleepAction", automation.Action)
27
+
28
+ CONFIG_SCHEMA = (
29
+ cv.Schema(
30
+ {
31
+ cv.GenerateID(): cv.declare_id(MAX17043Component),
32
+ cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
33
+ unit_of_measurement=UNIT_VOLT,
34
+ accuracy_decimals=3,
35
+ device_class=DEVICE_CLASS_VOLTAGE,
36
+ state_class=STATE_CLASS_MEASUREMENT,
37
+ entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
38
+ ),
39
+ cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
40
+ unit_of_measurement=UNIT_PERCENT,
41
+ accuracy_decimals=3,
42
+ device_class=DEVICE_CLASS_BATTERY,
43
+ state_class=STATE_CLASS_MEASUREMENT,
44
+ entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
45
+ ),
46
+ }
47
+ )
48
+ .extend(cv.polling_component_schema("60s"))
49
+ .extend(i2c.i2c_device_schema(0x36))
50
+ )
51
+
52
+
53
+ async def to_code(config):
54
+ var = cg.new_Pvariable(config[CONF_ID])
55
+ await cg.register_component(var, config)
56
+ await i2c.register_i2c_device(var, config)
57
+
58
+ if voltage_config := config.get(CONF_BATTERY_VOLTAGE):
59
+ sens = await sensor.new_sensor(voltage_config)
60
+ cg.add(var.set_voltage_sensor(sens))
61
+
62
+ if CONF_BATTERY_LEVEL in config:
63
+ sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
64
+ cg.add(var.set_battery_remaining_sensor(sens))
65
+
66
+
67
+ MAX17043_ACTION_SCHEMA = maybe_simple_id(
68
+ {
69
+ cv.Required(CONF_ID): cv.use_id(MAX17043Component),
70
+ }
71
+ )
72
+
73
+
74
+ @automation.register_action("max17043.sleep_mode", SleepAction, MAX17043_ACTION_SCHEMA)
75
+ async def max17043_sleep_mode_to_code(config, action_id, template_arg, args):
76
+ paren = await cg.get_variable(config[CONF_ID])
77
+ return cg.new_Pvariable(action_id, template_arg, paren)
@@ -21,6 +21,7 @@ media_player_ns = cg.esphome_ns.namespace("media_player")
21
21
 
22
22
  MediaPlayer = media_player_ns.class_("MediaPlayer")
23
23
 
24
+
24
25
  PlayAction = media_player_ns.class_(
25
26
  "PlayAction", automation.Action, cg.Parented.template(MediaPlayer)
26
27
  )
@@ -60,7 +61,11 @@ AnnoucementTrigger = media_player_ns.class_(
60
61
  "AnnouncementTrigger", automation.Trigger.template()
61
62
  )
62
63
  IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition)
64
+ IsPausedCondition = media_player_ns.class_("IsPausedCondition", automation.Condition)
63
65
  IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition)
66
+ IsAnnouncingCondition = media_player_ns.class_(
67
+ "IsAnnouncingCondition", automation.Condition
68
+ )
64
69
 
65
70
 
66
71
  async def setup_media_player_core_(var, config):
@@ -159,9 +164,15 @@ async def media_player_play_media_action(config, action_id, template_arg, args):
159
164
  @automation.register_condition(
160
165
  "media_player.is_idle", IsIdleCondition, MEDIA_PLAYER_ACTION_SCHEMA
161
166
  )
167
+ @automation.register_condition(
168
+ "media_player.is_paused", IsPausedCondition, MEDIA_PLAYER_ACTION_SCHEMA
169
+ )
162
170
  @automation.register_condition(
163
171
  "media_player.is_playing", IsPlayingCondition, MEDIA_PLAYER_ACTION_SCHEMA
164
172
  )
173
+ @automation.register_condition(
174
+ "media_player.is_announcing", IsAnnouncingCondition, MEDIA_PLAYER_ACTION_SCHEMA
175
+ )
165
176
  async def media_player_action(config, action_id, template_arg, args):
166
177
  var = cg.new_Pvariable(action_id, template_arg)
167
178
  await cg.register_parented(var, config[CONF_ID])
@@ -68,5 +68,15 @@ template<typename... Ts> class IsPlayingCondition : public Condition<Ts...>, pub
68
68
  bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING; }
69
69
  };
70
70
 
71
+ template<typename... Ts> class IsPausedCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
72
+ public:
73
+ bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED; }
74
+ };
75
+
76
+ template<typename... Ts> class IsAnnouncingCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
77
+ public:
78
+ bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING; }
79
+ };
80
+
71
81
  } // namespace media_player
72
82
  } // namespace esphome
@@ -37,6 +37,10 @@ const char *media_player_command_to_string(MediaPlayerCommand command) {
37
37
  return "UNMUTE";
38
38
  case MEDIA_PLAYER_COMMAND_TOGGLE:
39
39
  return "TOGGLE";
40
+ case MEDIA_PLAYER_COMMAND_VOLUME_UP:
41
+ return "VOLUME_UP";
42
+ case MEDIA_PLAYER_COMMAND_VOLUME_DOWN:
43
+ return "VOLUME_DOWN";
40
44
  default:
41
45
  return "UNKNOWN";
42
46
  }
@@ -3,6 +3,8 @@
3
3
  #include "esphome/core/log.h"
4
4
  #include "air_conditioner.h"
5
5
  #include "ac_adapter.h"
6
+ #include <cmath>
7
+ #include <cstdint>
6
8
 
7
9
  namespace esphome {
8
10
  namespace midea {
@@ -121,7 +123,21 @@ void AirConditioner::dump_config() {
121
123
 
122
124
  void AirConditioner::do_follow_me(float temperature, bool beeper) {
123
125
  #ifdef USE_REMOTE_TRANSMITTER
124
- IrFollowMeData data(static_cast<uint8_t>(lroundf(temperature)), beeper);
126
+ // Check if temperature is finite (not NaN or infinite)
127
+ if (!std::isfinite(temperature)) {
128
+ ESP_LOGW(Constants::TAG, "Follow me action requires a finite temperature, got: %f", temperature);
129
+ return;
130
+ }
131
+
132
+ // Round and convert temperature to long, then clamp and convert it to uint8_t
133
+ uint8_t temp_uint8 =
134
+ static_cast<uint8_t>(std::max(0L, std::min(static_cast<long>(UINT8_MAX), std::lroundf(temperature))));
135
+
136
+ ESP_LOGD(Constants::TAG, "Follow me action called with temperature: %f °C, rounded to: %u °C", temperature,
137
+ temp_uint8);
138
+
139
+ // Create and transmit the data
140
+ IrFollowMeData data(temp_uint8, beeper);
125
141
  this->transmitter_.transmit(data);
126
142
  #else
127
143
  ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component");
@@ -132,4 +132,4 @@ async def to_code(config):
132
132
  pin = await cg.gpio_pin_expression(config[CONF_DRDY_PIN])
133
133
  cg.add(var.set_drdy_gpio(pin))
134
134
 
135
- cg.add_library("functionpointer/arduino-MLX90393", "1.0.0")
135
+ cg.add_library("functionpointer/arduino-MLX90393", "1.0.2")