esphome 2024.8.2__py3-none-any.whl → 2024.9.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 (204) hide show
  1. esphome/__main__.py +6 -2
  2. esphome/components/api/api_connection.cpp +53 -0
  3. esphome/components/api/api_connection.h +4 -0
  4. esphome/components/api/api_pb2.cpp +280 -0
  5. esphome/components/api/api_pb2.h +91 -0
  6. esphome/components/api/api_pb2_service.cpp +85 -0
  7. esphome/components/api/api_pb2_service.h +28 -0
  8. esphome/components/async_tcp/__init__.py +3 -3
  9. esphome/components/atm90e26/sensor.py +10 -10
  10. esphome/components/atm90e32/sensor.py +1 -1
  11. esphome/components/bl0906/__init__.py +1 -0
  12. esphome/components/bl0906/bl0906.cpp +238 -0
  13. esphome/components/bl0906/bl0906.h +96 -0
  14. esphome/components/bl0906/const.py +4 -0
  15. esphome/components/bl0906/constants.h +122 -0
  16. esphome/components/bl0906/sensor.py +184 -0
  17. esphome/components/bl0942/__init__.py +1 -1
  18. esphome/components/bl0942/bl0942.cpp +127 -34
  19. esphome/components/bl0942/bl0942.h +87 -3
  20. esphome/components/bl0942/sensor.py +46 -8
  21. esphome/components/ble_client/__init__.py +1 -3
  22. esphome/components/ble_presence/binary_sensor.py +2 -2
  23. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +5 -0
  24. esphome/components/bmp280/sensor.py +2 -93
  25. esphome/components/bmp280_base/__init__.py +88 -0
  26. esphome/components/{bmp280/bmp280.cpp → bmp280_base/bmp280_base.cpp} +11 -4
  27. esphome/components/{bmp280/bmp280.h → bmp280_base/bmp280_base.h} +9 -5
  28. esphome/components/bmp280_i2c/__init__.py +0 -0
  29. esphome/components/bmp280_i2c/bmp280_i2c.cpp +27 -0
  30. esphome/components/bmp280_i2c/bmp280_i2c.h +22 -0
  31. esphome/components/bmp280_i2c/sensor.py +22 -0
  32. esphome/components/bmp280_spi/__init__.py +0 -0
  33. esphome/components/bmp280_spi/bmp280_spi.cpp +65 -0
  34. esphome/components/bmp280_spi/bmp280_spi.h +20 -0
  35. esphome/components/bmp280_spi/sensor.py +22 -0
  36. esphome/components/captive_portal/captive_portal.cpp +2 -0
  37. esphome/components/captive_portal/captive_portal.h +3 -1
  38. esphome/components/ch422g/__init__.py +67 -0
  39. esphome/components/ch422g/ch422g.cpp +122 -0
  40. esphome/components/ch422g/ch422g.h +70 -0
  41. esphome/components/debug/debug_esp32.cpp +3 -1
  42. esphome/components/display/__init__.py +5 -4
  43. esphome/components/dsmr/dsmr.cpp +6 -0
  44. esphome/components/dsmr/dsmr.h +6 -0
  45. esphome/components/dsmr/text_sensor.py +7 -2
  46. esphome/components/e131/e131.cpp +2 -0
  47. esphome/components/e131/e131.h +3 -1
  48. esphome/components/e131/e131_addressable_light_effect.cpp +2 -0
  49. esphome/components/e131/e131_addressable_light_effect.h +2 -1
  50. esphome/components/e131/e131_packet.cpp +2 -0
  51. esphome/components/esp32_ble/ble_uuid.cpp +7 -0
  52. esphome/components/esp32_ble/ble_uuid.h +1 -0
  53. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +11 -9
  54. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +3 -3
  55. esphome/components/esp32_camera/__init__.py +4 -0
  56. esphome/components/esp32_camera/esp32_camera.cpp +9 -1
  57. esphome/components/esp32_camera/esp32_camera.h +3 -0
  58. esphome/components/esp32_can/canbus.py +18 -7
  59. esphome/components/esp32_can/esp32_can.cpp +8 -0
  60. esphome/components/esp32_can/esp32_can.h +4 -0
  61. esphome/components/esp32_rmt_led_strip/led_strip.cpp +14 -2
  62. esphome/components/esp32_rmt_led_strip/led_strip.h +3 -2
  63. esphome/components/esp32_rmt_led_strip/light.py +21 -4
  64. esphome/components/esphome/ota/ota_esphome.cpp +2 -1
  65. esphome/components/esphome/ota/ota_esphome.h +2 -0
  66. esphome/components/font/__init__.py +11 -22
  67. esphome/components/font/font.cpp +3 -2
  68. esphome/components/font/font.h +12 -3
  69. esphome/components/gree/climate.py +2 -1
  70. esphome/components/gree/gree.cpp +54 -3
  71. esphome/components/gree/gree.h +10 -2
  72. esphome/components/gt911/touchscreen/__init__.py +6 -4
  73. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +17 -0
  74. esphome/components/gt911/touchscreen/gt911_touchscreen.h +2 -0
  75. esphome/components/hmac_md5/__init__.py +2 -0
  76. esphome/components/hmac_md5/hmac_md5.cpp +56 -0
  77. esphome/components/hmac_md5/hmac_md5.h +48 -0
  78. esphome/components/homeassistant/__init__.py +13 -0
  79. esphome/components/homeassistant/switch/__init__.py +15 -2
  80. esphome/components/homeassistant/switch/homeassistant_switch.cpp +2 -2
  81. esphome/components/i2s_audio/__init__.py +88 -9
  82. esphome/components/i2s_audio/i2s_audio.h +20 -2
  83. esphome/components/i2s_audio/media_player/__init__.py +8 -4
  84. esphome/components/i2s_audio/media_player/i2s_audio_media_player.h +1 -1
  85. esphome/components/i2s_audio/microphone/__init__.py +19 -51
  86. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +18 -15
  87. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +0 -12
  88. esphome/components/i2s_audio/speaker/__init__.py +39 -27
  89. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +49 -37
  90. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +3 -4
  91. esphome/components/ili9xxx/display.py +16 -17
  92. esphome/components/ili9xxx/ili9xxx_display.cpp +1 -1
  93. esphome/components/ili9xxx/ili9xxx_display.h +18 -18
  94. esphome/components/ili9xxx/ili9xxx_init.h +0 -3
  95. esphome/components/improv_serial/improv_serial_component.cpp +2 -1
  96. esphome/components/improv_serial/improv_serial_component.h +2 -1
  97. esphome/components/ledc/ledc_output.cpp +11 -7
  98. esphome/components/libretiny/__init__.py +8 -13
  99. esphome/components/ltr501/__init__.py +1 -0
  100. esphome/components/ltr501/ltr501.cpp +542 -0
  101. esphome/components/ltr501/ltr501.h +184 -0
  102. esphome/components/ltr501/ltr_definitions_501.h +260 -0
  103. esphome/components/ltr501/sensor.py +274 -0
  104. esphome/components/ltr_als_ps/sensor.py +2 -2
  105. esphome/components/lvgl/__init__.py +19 -16
  106. esphome/components/lvgl/automation.py +90 -9
  107. esphome/components/lvgl/defines.py +29 -2
  108. esphome/components/lvgl/gradient.py +61 -0
  109. esphome/components/lvgl/lv_validation.py +45 -27
  110. esphome/components/lvgl/lvcode.py +8 -3
  111. esphome/components/lvgl/lvgl_esphome.cpp +54 -0
  112. esphome/components/lvgl/lvgl_esphome.h +9 -3
  113. esphome/components/lvgl/number/__init__.py +1 -0
  114. esphome/components/lvgl/number/lvgl_number.h +3 -1
  115. esphome/components/lvgl/schemas.py +16 -11
  116. esphome/components/lvgl/select/__init__.py +1 -0
  117. esphome/components/lvgl/select/lvgl_select.h +3 -1
  118. esphome/components/lvgl/switch/__init__.py +2 -1
  119. esphome/components/lvgl/switch/lvgl_switch.h +3 -1
  120. esphome/components/lvgl/text/__init__.py +1 -0
  121. esphome/components/lvgl/text/lvgl_text.h +3 -1
  122. esphome/components/lvgl/trigger.py +3 -2
  123. esphome/components/lvgl/types.py +2 -1
  124. esphome/components/lvgl/widgets/__init__.py +23 -8
  125. esphome/components/lvgl/widgets/arc.py +5 -1
  126. esphome/components/lvgl/widgets/buttonmatrix.py +5 -1
  127. esphome/components/lvgl/widgets/checkbox.py +8 -3
  128. esphome/components/lvgl/widgets/meter.py +8 -1
  129. esphome/components/lvgl/widgets/msgbox.py +26 -15
  130. esphome/components/lvgl/widgets/page.py +51 -7
  131. esphome/components/lvgl/widgets/tileview.py +2 -8
  132. esphome/components/max31856/max31856.cpp +12 -1
  133. esphome/components/max31856/max31856.h +5 -2
  134. esphome/components/max31856/sensor.py +20 -0
  135. esphome/components/mcp9600/sensor.py +2 -2
  136. esphome/components/mdns/__init__.py +6 -6
  137. esphome/components/media_player/media_player.h +16 -0
  138. esphome/components/micro_wake_word/__init__.py +2 -25
  139. esphome/components/microphone/microphone.h +1 -1
  140. esphome/components/mics_4514/mics_4514.cpp +26 -36
  141. esphome/components/modbus_controller/__init__.py +6 -0
  142. esphome/components/modbus_controller/const.py +2 -0
  143. esphome/components/modbus_controller/modbus_controller.cpp +30 -27
  144. esphome/components/modbus_controller/modbus_controller.h +22 -4
  145. esphome/components/network/__init__.py +11 -8
  146. esphome/components/pipsolar/pipsolar.cpp +3 -0
  147. esphome/components/pipsolar/pipsolar.h +1 -0
  148. esphome/components/pipsolar/switch/__init__.py +2 -0
  149. esphome/components/prometheus/prometheus_handler.cpp +2 -0
  150. esphome/components/prometheus/prometheus_handler.h +3 -1
  151. esphome/components/rp2040/__init__.py +7 -8
  152. esphome/components/rpi_dpi_rgb/display.py +20 -17
  153. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +36 -6
  154. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h +4 -0
  155. esphome/components/socket/socket.cpp +2 -0
  156. esphome/components/socket/socket.h +2 -0
  157. esphome/components/speaker/speaker.h +1 -1
  158. esphome/components/st7701s/display.py +35 -37
  159. esphome/components/st7701s/st7701s.cpp +11 -6
  160. esphome/components/st7701s/st7701s.h +1 -0
  161. esphome/components/statsd/__init__.py +65 -0
  162. esphome/components/statsd/statsd.cpp +156 -0
  163. esphome/components/statsd/statsd.h +86 -0
  164. esphome/components/tuya/__init__.py +1 -0
  165. esphome/components/tuya/number/__init__.py +39 -2
  166. esphome/components/tuya/number/tuya_number.cpp +58 -2
  167. esphome/components/tuya/number/tuya_number.h +12 -3
  168. esphome/components/udp/__init__.py +158 -0
  169. esphome/components/udp/binary_sensor.py +27 -0
  170. esphome/components/udp/sensor.py +27 -0
  171. esphome/components/udp/udp_component.cpp +616 -0
  172. esphome/components/udp/udp_component.h +158 -0
  173. esphome/components/uponor_smatrix/uponor_smatrix.cpp +4 -6
  174. esphome/components/uponor_smatrix/uponor_smatrix.h +0 -1
  175. esphome/components/veml7700/sensor.py +2 -2
  176. esphome/components/voice_assistant/__init__.py +6 -0
  177. esphome/components/voice_assistant/voice_assistant.cpp +24 -2
  178. esphome/components/voice_assistant/voice_assistant.h +20 -0
  179. esphome/components/web_server/__init__.py +11 -11
  180. esphome/components/web_server/list_entities.cpp +2 -0
  181. esphome/components/web_server/list_entities.h +3 -1
  182. esphome/components/web_server/web_server.cpp +2 -1
  183. esphome/components/web_server/web_server.h +2 -0
  184. esphome/components/web_server_base/web_server_base.cpp +2 -0
  185. esphome/components/web_server_base/web_server_base.h +3 -1
  186. esphome/components/wifi/wifi_component_libretiny.cpp +15 -1
  187. esphome/components/wireguard/__init__.py +9 -6
  188. esphome/components/wireguard/wireguard.cpp +2 -1
  189. esphome/components/wireguard/wireguard.h +3 -1
  190. esphome/config_validation.py +8 -0
  191. esphome/const.py +8 -1
  192. esphome/core/bytebuffer.cpp +117 -84
  193. esphome/core/bytebuffer.h +69 -21
  194. esphome/core/config.py +0 -3
  195. esphome/core/defines.h +2 -0
  196. esphome/core/ring_buffer.cpp +13 -2
  197. esphome/core/ring_buffer.h +56 -0
  198. esphome/external_files.py +5 -3
  199. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/METADATA +1 -1
  200. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/RECORD +204 -169
  201. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/LICENSE +0 -0
  202. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/WHEEL +0 -0
  203. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/entry_points.txt +0 -0
  204. {esphome-2024.8.2.dist-info → esphome-2024.9.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,61 @@
1
+ from esphome import config_validation as cv
2
+ import esphome.codegen as cg
3
+ from esphome.const import (
4
+ CONF_COLOR,
5
+ CONF_DIRECTION,
6
+ CONF_DITHER,
7
+ CONF_ID,
8
+ CONF_POSITION,
9
+ )
10
+ from esphome.cpp_generator import MockObj
11
+
12
+ from .defines import CONF_GRADIENTS, LV_DITHER, LV_GRAD_DIR, add_define
13
+ from .lv_validation import lv_color, lv_fraction
14
+ from .lvcode import lv_assign
15
+ from .types import lv_gradient_t
16
+
17
+ CONF_STOPS = "stops"
18
+
19
+
20
+ def min_stops(value):
21
+ if len(value) < 2:
22
+ raise cv.Invalid("Must have at least 2 stops")
23
+ return value
24
+
25
+
26
+ GRADIENT_SCHEMA = cv.ensure_list(
27
+ cv.Schema(
28
+ {
29
+ cv.GenerateID(CONF_ID): cv.declare_id(lv_gradient_t),
30
+ cv.Optional(CONF_DIRECTION, default="NONE"): LV_GRAD_DIR.one_of,
31
+ cv.Optional(CONF_DITHER, default="NONE"): LV_DITHER.one_of,
32
+ cv.Required(CONF_STOPS): cv.All(
33
+ [
34
+ cv.Schema(
35
+ {
36
+ cv.Required(CONF_COLOR): lv_color,
37
+ cv.Required(CONF_POSITION): lv_fraction,
38
+ }
39
+ )
40
+ ],
41
+ min_stops,
42
+ ),
43
+ }
44
+ )
45
+ )
46
+
47
+
48
+ async def gradients_to_code(config):
49
+ max_stops = 2
50
+ for gradient in config.get(CONF_GRADIENTS, ()):
51
+ var = MockObj(cg.new_Pvariable(gradient[CONF_ID]), "->")
52
+ max_stops = max(max_stops, len(gradient[CONF_STOPS]))
53
+ lv_assign(var.dir, await LV_GRAD_DIR.process(gradient[CONF_DIRECTION]))
54
+ lv_assign(var.dither, await LV_DITHER.process(gradient[CONF_DITHER]))
55
+ lv_assign(var.stops_count, len(gradient[CONF_STOPS]))
56
+ for index, stop in enumerate(gradient[CONF_STOPS]):
57
+ lv_assign(var.stops[index].color, await lv_color.process(stop[CONF_COLOR]))
58
+ lv_assign(
59
+ var.stops[index].frac, await lv_fraction.process(stop[CONF_POSITION])
60
+ )
61
+ add_define("LV_GRADIENT_MAX_STOPS", max_stops)
@@ -1,12 +1,19 @@
1
1
  from typing import Union
2
2
 
3
3
  import esphome.codegen as cg
4
- from esphome.components.color import ColorStruct
4
+ from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw
5
5
  from esphome.components.font import Font
6
6
  from esphome.components.image import Image_
7
7
  import esphome.config_validation as cv
8
- from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT, CONF_TIME, CONF_VALUE
9
- from esphome.core import HexInt, Lambda
8
+ from esphome.const import (
9
+ CONF_ARGS,
10
+ CONF_COLOR,
11
+ CONF_FORMAT,
12
+ CONF_ID,
13
+ CONF_TIME,
14
+ CONF_VALUE,
15
+ )
16
+ from esphome.core import CORE, ID, Lambda
10
17
  from esphome.cpp_generator import MockObj
11
18
  from esphome.cpp_types import ESPTime, uint32
12
19
  from esphome.helpers import cpp_string_escape
@@ -23,14 +30,9 @@ from .defines import (
23
30
  call_lambda,
24
31
  literal,
25
32
  )
26
- from .helpers import (
27
- esphome_fonts_used,
28
- lv_fonts_used,
29
- lvgl_components_required,
30
- requires_component,
31
- )
33
+ from .helpers import esphome_fonts_used, lv_fonts_used, requires_component
32
34
  from .lvcode import lv_expr
33
- from .types import lv_font_t, lv_img_t
35
+ from .types import lv_font_t, lv_gradient_t, lv_img_t
34
36
 
35
37
  opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER")
36
38
 
@@ -52,20 +54,24 @@ opacity = LValidator(opacity_validator, uint32, retmapper=literal)
52
54
  def color(value):
53
55
  if value == SCHEMA_EXTRACT:
54
56
  return ["hex color value", "color ID"]
55
- if isinstance(value, int):
56
- return value
57
- return cv.use_id(ColorStruct)(value)
57
+ return cv.Any(cv.int_, cv.use_id(ColorStruct))(value)
58
58
 
59
59
 
60
60
  def color_retmapper(value):
61
61
  if isinstance(value, cv.Lambda):
62
62
  return cv.returning_lambda(value)
63
63
  if isinstance(value, int):
64
- hexval = HexInt(value)
65
- return lv_expr.color_hex(hexval)
66
- # Must be an id
67
- lvgl_components_required.add(CONF_COLOR)
68
- return lv_expr.color_from(MockObj(value))
64
+ return literal(
65
+ f"lv_color_make({(value >> 16) & 0xFF}, {(value >> 8) & 0xFF}, {value & 0xFF})"
66
+ )
67
+ if isinstance(value, ID):
68
+ cval = [x for x in CORE.config[CONF_COLOR] if x[CONF_ID] == value][0]
69
+ if CONF_HEX in cval:
70
+ r, g, b = cval[CONF_HEX]
71
+ else:
72
+ r, g, b, _ = from_rgbw(cval)
73
+ return literal(f"lv_color_make({r}, {g}, {b})")
74
+ assert False
69
75
 
70
76
 
71
77
  def option_string(value):
@@ -82,10 +88,10 @@ def pixels_or_percent_validator(value):
82
88
  """A length in one axis - either a number (pixels) or a percentage"""
83
89
  if value == SCHEMA_EXTRACT:
84
90
  return ["pixels", "..%"]
91
+ value = cv.Any(cv.int_, cv.percentage)(value)
85
92
  if isinstance(value, int):
86
- return cv.int_(value)
87
- # Will throw an exception if not a percentage.
88
- return f"lv_pct({int(cv.percentage(value) * 100)})"
93
+ return value
94
+ return f"lv_pct({int(value * 100)})"
89
95
 
90
96
 
91
97
  pixels_or_percent = LValidator(pixels_or_percent_validator, uint32, retmapper=literal)
@@ -116,10 +122,7 @@ def size_validator(value):
116
122
  if value.upper() == "SIZE_CONTENT":
117
123
  return "LV_SIZE_CONTENT"
118
124
  raise cv.Invalid("must be 'size_content', a percentage or an integer (pixels)")
119
- if isinstance(value, int):
120
- return cv.int_(value)
121
- # Will throw an exception if not a percentage.
122
- return f"lv_pct({int(cv.percentage(value) * 100)})"
125
+ return pixels_or_percent_validator(value)
123
126
 
124
127
 
125
128
  size = LValidator(size_validator, uint32, retmapper=literal)
@@ -137,7 +140,7 @@ radius_consts = LvConstant("LV_RADIUS_", "CIRCLE")
137
140
 
138
141
 
139
142
  @schema_extractor("one_of")
140
- def radius_validator(value):
143
+ def fraction_validator(value):
141
144
  if value == SCHEMA_EXTRACT:
142
145
  return radius_consts.choices
143
146
  value = cv.Any(size, cv.percentage, radius_consts.one_of)(value)
@@ -146,7 +149,7 @@ def radius_validator(value):
146
149
  return value
147
150
 
148
151
 
149
- radius = LValidator(radius_validator, uint32, retmapper=literal)
152
+ lv_fraction = LValidator(fraction_validator, uint32, retmapper=literal)
150
153
 
151
154
 
152
155
  def id_name(value):
@@ -247,6 +250,21 @@ lv_int = LValidator(cv.int_, cg.int_)
247
250
  lv_brightness = LValidator(cv.percentage, cg.float_, retmapper=lambda x: int(x * 255))
248
251
 
249
252
 
253
+ def gradient_mapper(value):
254
+ return MockObj(value)
255
+
256
+
257
+ def gradient_validator(value):
258
+ return cv.use_id(lv_gradient_t)(value)
259
+
260
+
261
+ lv_gradient = LValidator(
262
+ validator=gradient_validator,
263
+ rtype=lv_gradient_t,
264
+ retmapper=gradient_mapper,
265
+ )
266
+
267
+
250
268
  def is_lv_font(font):
251
269
  return isinstance(font, str) and font.lower() in LV_FONTS
252
270
 
@@ -28,7 +28,7 @@ LVGL_COMP = "lv_component" # used as a lambda argument in lvgl_comp()
28
28
  LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent)
29
29
  LVGL_COMP_ARG = [(LvglComponent.operator("ptr"), LVGL_COMP)]
30
30
  lv_event_t_ptr = cg.global_ns.namespace("lv_event_t").operator("ptr")
31
- EVENT_ARG = [(lv_event_t_ptr, "ev")]
31
+ EVENT_ARG = [(lv_event_t_ptr, "event")]
32
32
  # Two custom events; API_EVENT is fired when an entity is updated remotely by an API interaction;
33
33
  # UPDATE_EVENT is fired when an entity is programmatically updated locally.
34
34
  # VALUE_CHANGED is the event generated by LVGL when an entity's value changes through user interaction.
@@ -184,8 +184,9 @@ class LvContext(LambdaContext):
184
184
  self.lv_component = lv_component
185
185
 
186
186
  async def add_init_lambda(self):
187
- cg.add(self.lv_component.add_init_lambda(await self.get_lambda()))
188
- LvContext.added_lambda_count += 1
187
+ if self.code_list:
188
+ cg.add(self.lv_component.add_init_lambda(await self.get_lambda()))
189
+ LvContext.added_lambda_count += 1
189
190
 
190
191
  async def __aexit__(self, exc_type, exc_val, exc_tb):
191
192
  await super().__aexit__(exc_type, exc_val, exc_tb)
@@ -291,6 +292,10 @@ class LvExpr(MockLv):
291
292
  pass
292
293
 
293
294
 
295
+ def static_cast(type, value):
296
+ return literal(f"static_cast<{type}>({value})")
297
+
298
+
294
299
  # Top level mock for generic lv_ calls to be recorded
295
300
  lv = MockLv("lv_")
296
301
  # Just generate an expression
@@ -15,6 +15,60 @@ static void log_cb(const char *buf) {
15
15
  }
16
16
  #endif // LV_USE_LOG
17
17
 
18
+ static const char *const EVENT_NAMES[] = {
19
+ "NONE",
20
+ "PRESSED",
21
+ "PRESSING",
22
+ "PRESS_LOST",
23
+ "SHORT_CLICKED",
24
+ "LONG_PRESSED",
25
+ "LONG_PRESSED_REPEAT",
26
+ "CLICKED",
27
+ "RELEASED",
28
+ "SCROLL_BEGIN",
29
+ "SCROLL_END",
30
+ "SCROLL",
31
+ "GESTURE",
32
+ "KEY",
33
+ "FOCUSED",
34
+ "DEFOCUSED",
35
+ "LEAVE",
36
+ "HIT_TEST",
37
+ "COVER_CHECK",
38
+ "REFR_EXT_DRAW_SIZE",
39
+ "DRAW_MAIN_BEGIN",
40
+ "DRAW_MAIN",
41
+ "DRAW_MAIN_END",
42
+ "DRAW_POST_BEGIN",
43
+ "DRAW_POST",
44
+ "DRAW_POST_END",
45
+ "DRAW_PART_BEGIN",
46
+ "DRAW_PART_END",
47
+ "VALUE_CHANGED",
48
+ "INSERT",
49
+ "REFRESH",
50
+ "READY",
51
+ "CANCEL",
52
+ "DELETE",
53
+ "CHILD_CHANGED",
54
+ "CHILD_CREATED",
55
+ "CHILD_DELETED",
56
+ "SCREEN_UNLOAD_START",
57
+ "SCREEN_LOAD_START",
58
+ "SCREEN_LOADED",
59
+ "SCREEN_UNLOADED",
60
+ "SIZE_CHANGED",
61
+ "STYLE_CHANGED",
62
+ "LAYOUT_CHANGED",
63
+ "GET_SELF_SIZE",
64
+ };
65
+
66
+ std::string lv_event_code_name_for(uint8_t event_code) {
67
+ if (event_code < sizeof(EVENT_NAMES) / sizeof(EVENT_NAMES[0])) {
68
+ return EVENT_NAMES[event_code];
69
+ }
70
+ return str_sprintf("%2d", event_code);
71
+ }
18
72
  static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) {
19
73
  // make sure all coordinates are even
20
74
  if (area->x1 & 1)
@@ -40,10 +40,8 @@ namespace lvgl {
40
40
 
41
41
  extern lv_event_code_t lv_api_event; // NOLINT
42
42
  extern lv_event_code_t lv_update_event; // NOLINT
43
+ extern std::string lv_event_code_name_for(uint8_t event_code);
43
44
  extern bool lv_is_pre_initialise();
44
- #ifdef USE_LVGL_COLOR
45
- inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); }
46
- #endif // USE_LVGL_COLOR
47
45
  #if LV_COLOR_DEPTH == 16
48
46
  static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_565;
49
47
  #elif LV_COLOR_DEPTH == 32
@@ -143,6 +141,13 @@ class LvglComponent : public PollingComponent {
143
141
  void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
144
142
  void show_prev_page(lv_scr_load_anim_t anim, uint32_t time);
145
143
  void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; }
144
+ void set_focus_mark(lv_group_t *group) { this->focus_marks_[group] = lv_group_get_focused(group); }
145
+ void restore_focus_mark(lv_group_t *group) {
146
+ auto *mark = this->focus_marks_[group];
147
+ if (mark != nullptr) {
148
+ lv_group_focus_obj(mark);
149
+ }
150
+ }
146
151
 
147
152
  protected:
148
153
  void write_random_();
@@ -158,6 +163,7 @@ class LvglComponent : public PollingComponent {
158
163
  bool show_snow_{};
159
164
  lv_coord_t snow_line_{};
160
165
  bool page_wrap_{true};
166
+ std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
161
167
 
162
168
  std::vector<std::function<void(LvglComponent *lv_component)>> init_lambdas_;
163
169
  CallbackManager<void(uint32_t)> idle_callbacks_{};
@@ -50,6 +50,7 @@ async def to_code(config):
50
50
  "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED]
51
51
  )
52
52
  lv.event_send(widget.obj, API_EVENT, cg.nullptr)
53
+ control.add(var.publish_state(widget.get_value()))
53
54
  async with LambdaContext(EVENT_ARG) as event:
54
55
  event.add(var.publish_state(widget.get_value()))
55
56
  event_code = (
@@ -1,5 +1,7 @@
1
1
  #pragma once
2
2
 
3
+ #include <utility>
4
+
3
5
  #include "esphome/components/number/number.h"
4
6
  #include "esphome/core/automation.h"
5
7
  #include "esphome/core/component.h"
@@ -11,7 +13,7 @@ namespace lvgl {
11
13
  class LVGLNumber : public number::Number {
12
14
  public:
13
15
  void set_control_lambda(std::function<void(float)> control_lambda) {
14
- this->control_lambda_ = control_lambda;
16
+ this->control_lambda_ = std::move(control_lambda);
15
17
  if (this->initial_state_.has_value()) {
16
18
  this->control_lambda_(this->initial_state_.value());
17
19
  this->initial_state_.reset();
@@ -17,10 +17,10 @@ from esphome.core import TimePeriod
17
17
  from esphome.schema_extractors import SCHEMA_EXTRACT
18
18
 
19
19
  from . import defines as df, lv_validation as lvalid
20
- from .defines import CONF_TIME_FORMAT
20
+ from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR
21
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
22
+ from .lv_validation import lv_color, lv_font, lv_gradient, lv_image
23
+ from .lvcode import LvglComponent, lv_event_t_ptr
24
24
  from .types import (
25
25
  LVEncoderListener,
26
26
  LvType,
@@ -94,9 +94,10 @@ STYLE_PROPS = {
94
94
  "arc_width": cv.positive_int,
95
95
  "anim_time": lvalid.lv_milliseconds,
96
96
  "bg_color": lvalid.lv_color,
97
+ "bg_grad": lv_gradient,
97
98
  "bg_grad_color": lvalid.lv_color,
98
99
  "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_dir": LV_GRAD_DIR.one_of,
100
101
  "bg_grad_stop": lvalid.stop_value,
101
102
  "bg_image_opa": lvalid.opacity,
102
103
  "bg_image_recolor": lvalid.lv_color,
@@ -160,7 +161,7 @@ STYLE_PROPS = {
160
161
  "max_width": lvalid.pixels_or_percent,
161
162
  "min_height": lvalid.pixels_or_percent,
162
163
  "min_width": lvalid.pixels_or_percent,
163
- "radius": lvalid.radius,
164
+ "radius": lvalid.lv_fraction,
164
165
  "width": lvalid.size,
165
166
  "x": lvalid.pixels_or_percent,
166
167
  "y": lvalid.pixels_or_percent,
@@ -215,14 +216,12 @@ def automation_schema(typ: LvType):
215
216
  events = df.LV_EVENT_TRIGGERS + (CONF_ON_VALUE,)
216
217
  else:
217
218
  events = df.LV_EVENT_TRIGGERS
218
- if isinstance(typ, LvType):
219
- template = Trigger.template(typ.get_arg_type())
220
- else:
221
- template = Trigger.template()
219
+ args = [typ.get_arg_type()] if isinstance(typ, LvType) else []
220
+ args.append(lv_event_t_ptr)
222
221
  return {
223
222
  cv.Optional(event): validate_automation(
224
223
  {
225
- cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(template),
224
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger.template(*args)),
226
225
  }
227
226
  )
228
227
  for event in events
@@ -361,7 +360,13 @@ LVGL_SCHEMA = cv.Schema(
361
360
  }
362
361
  )
363
362
 
364
- ALL_STYLES = {**STYLE_PROPS, **GRID_CELL_SCHEMA, **FLEX_OBJ_SCHEMA}
363
+ ALL_STYLES = {
364
+ **STYLE_PROPS,
365
+ **GRID_CELL_SCHEMA,
366
+ **FLEX_OBJ_SCHEMA,
367
+ cv.Optional(df.CONF_PAD_ROW): lvalid.pixels,
368
+ cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels,
369
+ }
365
370
 
366
371
 
367
372
  def container_validator(schema, widget_type: WidgetType):
@@ -43,6 +43,7 @@ async def to_code(config):
43
43
  async with LambdaContext([(cg.uint16, "v")]) as control:
44
44
  await widget.set_property("selected", "v", animated=config[CONF_ANIMATED])
45
45
  lv.event_send(widget.obj, API_EVENT, cg.nullptr)
46
+ control.add(selector.publish_index(widget.get_value()))
46
47
  async with LvContext(paren) as ctx:
47
48
  lv_add(selector.set_control_lambda(await control.get_lambda()))
48
49
  ctx.add(
@@ -1,5 +1,7 @@
1
1
  #pragma once
2
2
 
3
+ #include <utility>
4
+
3
5
  #include "esphome/components/select/select.h"
4
6
  #include "esphome/core/automation.h"
5
7
  #include "esphome/core/component.h"
@@ -28,7 +30,7 @@ static std::vector<std::string> split_string(const std::string &str) {
28
30
  class LVGLSelect : public select::Select {
29
31
  public:
30
32
  void set_control_lambda(std::function<void(size_t)> lambda) {
31
- this->control_lambda_ = lambda;
33
+ this->control_lambda_ = std::move(lambda);
32
34
  if (this->initial_state_.has_value()) {
33
35
  this->control(this->initial_state_.value());
34
36
  this->initial_state_.reset();
@@ -3,7 +3,7 @@ from esphome.components.switch import Switch, new_switch, switch_schema
3
3
  import esphome.config_validation as cv
4
4
  from esphome.cpp_generator import MockObj
5
5
 
6
- from ..defines import CONF_LVGL_ID, CONF_WIDGET
6
+ from ..defines import CONF_LVGL_ID, CONF_WIDGET, literal
7
7
  from ..lvcode import (
8
8
  API_EVENT,
9
9
  EVENT_ARG,
@@ -44,6 +44,7 @@ async def to_code(config):
44
44
  cond.else_()
45
45
  widget.clear_state(LV_STATE.CHECKED)
46
46
  lv.event_send(widget.obj, API_EVENT, cg.nullptr)
47
+ control.add(switch.publish_state(literal("v")))
47
48
  async with LvContext(paren) as ctx:
48
49
  lv_add(switch.set_control_lambda(await control.get_lambda()))
49
50
  ctx.add(
@@ -1,5 +1,7 @@
1
1
  #pragma once
2
2
 
3
+ #include <utility>
4
+
3
5
  #include "esphome/components/switch/switch.h"
4
6
  #include "esphome/core/automation.h"
5
7
  #include "esphome/core/component.h"
@@ -11,7 +13,7 @@ namespace lvgl {
11
13
  class LVGLSwitch : public switch_::Switch {
12
14
  public:
13
15
  void set_control_lambda(std::function<void(bool)> state_lambda) {
14
- this->state_lambda_ = state_lambda;
16
+ this->state_lambda_ = std::move(state_lambda);
15
17
  if (this->initial_state_.has_value()) {
16
18
  this->state_lambda_(this->initial_state_.value());
17
19
  this->initial_state_.reset();
@@ -36,6 +36,7 @@ async def to_code(config):
36
36
  async with LambdaContext([(cg.std_string, "text_value")]) as control:
37
37
  await widget.set_property("text", "text_value.c_str())")
38
38
  lv.event_send(widget.obj, API_EVENT, None)
39
+ control.add(textvar.publish_state(widget.get_value()))
39
40
  async with LambdaContext(EVENT_ARG) as lamb:
40
41
  lv_add(textvar.publish_state(widget.get_value()))
41
42
  async with LvContext(paren):
@@ -1,5 +1,7 @@
1
1
  #pragma once
2
2
 
3
+ #include <utility>
4
+
3
5
  #include "esphome/components/text/text.h"
4
6
  #include "esphome/core/automation.h"
5
7
  #include "esphome/core/component.h"
@@ -11,7 +13,7 @@ namespace lvgl {
11
13
  class LVGLText : public text::Text {
12
14
  public:
13
15
  void set_control_lambda(std::function<void(const std::string)> control_lambda) {
14
- this->control_lambda_ = control_lambda;
16
+ this->control_lambda_ = std::move(control_lambda);
15
17
  if (this->initial_state_.has_value()) {
16
18
  this->control_lambda_(this->initial_state_.value());
17
19
  this->initial_state_.reset();
@@ -19,6 +19,7 @@ from .lvcode import (
19
19
  LvConditional,
20
20
  lv,
21
21
  lv_add,
22
+ lv_event_t_ptr,
22
23
  )
23
24
  from .types import LV_EVENT
24
25
  from .widgets import widget_map
@@ -65,10 +66,10 @@ async def generate_triggers(lv_component):
65
66
  async def add_trigger(conf, lv_component, w, *events):
66
67
  tid = conf[CONF_TRIGGER_ID]
67
68
  trigger = cg.new_Pvariable(tid)
68
- args = w.get_args()
69
+ args = w.get_args() + [(lv_event_t_ptr, "event")]
69
70
  value = w.get_value()
70
71
  await automation.build_automation(trigger, args, conf)
71
72
  async with LambdaContext(EVENT_ARG, where=tid) as context:
72
73
  with LvConditional(w.is_selected()):
73
- lv_add(trigger.trigger(value))
74
+ lv_add(trigger.trigger(value, literal("event")))
74
75
  lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), *events))
@@ -57,8 +57,9 @@ lv_group_t = cg.global_ns.struct("lv_group_t")
57
57
  LVTouchListener = lvgl_ns.class_("LVTouchListener")
58
58
  LVEncoderListener = lvgl_ns.class_("LVEncoderListener")
59
59
  lv_obj_t = LvType("lv_obj_t")
60
- lv_page_t = cg.global_ns.class_("LvPageType", LvCompound)
60
+ lv_page_t = LvType("LvPageType", parents=(LvCompound,))
61
61
  lv_img_t = LvType("lv_img_t")
62
+ lv_gradient_t = LvType("lv_grad_dsc_t")
62
63
 
63
64
  LV_EVENT = MockObj(base="LV_EVENT_", op="")
64
65
  LV_STATE = MockObj(base="LV_STATE_", op="")
@@ -89,6 +89,8 @@ class Widget:
89
89
  self.obj = MockObj(f"{self.var}->obj")
90
90
  else:
91
91
  self.obj = var
92
+ self.outer = None
93
+ self.move_to_foreground = False
92
94
 
93
95
  @staticmethod
94
96
  def create(name, var, wtype: WidgetType, config: dict = None):
@@ -118,7 +120,14 @@ class Widget:
118
120
  def clear_flag(self, flag):
119
121
  return lv_obj.clear_flag(self.obj, literal(flag))
120
122
 
121
- async def set_property(self, prop, value, animated: bool = None):
123
+ async def set_property(self, prop, value, animated: bool = None, lv_name=None):
124
+ """
125
+ Set a property of the widget.
126
+ :param prop: The property name
127
+ :param value: The value
128
+ :param animated: If the change should be animated
129
+ :param lv_name: The base type of the widget e.g. "obj"
130
+ """
122
131
  if isinstance(value, dict):
123
132
  value = value.get(prop)
124
133
  if isinstance(ALL_STYLES.get(prop), LValidator):
@@ -131,11 +140,12 @@ class Widget:
131
140
  value = value.total_milliseconds
132
141
  if isinstance(value, str):
133
142
  value = literal(value)
143
+ lv_name = lv_name or self.type.lv_name
134
144
  if animated is None or self.type.animated is not True:
135
- lv.call(f"{self.type.lv_name}_set_{prop}", self.obj, value)
145
+ lv.call(f"{lv_name}_set_{prop}", self.obj, value)
136
146
  else:
137
147
  lv.call(
138
- f"{self.type.lv_name}_set_{prop}",
148
+ f"{lv_name}_set_{prop}",
139
149
  self.obj,
140
150
  value,
141
151
  literal("LV_ANIM_ON" if animated else "LV_ANIM_OFF"),
@@ -217,7 +227,7 @@ def get_widget_generator(wid):
217
227
  yield
218
228
 
219
229
 
220
- async def get_widget_(wid: Widget):
230
+ async def get_widget_(wid):
221
231
  if obj := widget_map.get(wid):
222
232
  return obj
223
233
  return await FakeAwaitable(get_widget_generator(wid))
@@ -319,8 +329,15 @@ async def set_obj_properties(w: Widget, config):
319
329
  lv_obj.set_flex_align(w.obj, main, cross, track)
320
330
  parts = collect_parts(config)
321
331
  for part, states in parts.items():
332
+ part = "LV_PART_" + part.upper()
322
333
  for state, props in states.items():
323
- lv_state = join_enums((f"LV_STATE_{state}", f"LV_PART_{part}"))
334
+ state = "LV_STATE_" + state.upper()
335
+ if state == "LV_STATE_DEFAULT":
336
+ lv_state = literal(part)
337
+ elif part == "LV_PART_MAIN":
338
+ lv_state = literal(state)
339
+ else:
340
+ lv_state = join_enums((state, part))
324
341
  for style_id in props.get(CONF_STYLES, ()):
325
342
  lv_obj.add_style(w.obj, MockObj(style_id), lv_state)
326
343
  for prop, value in {
@@ -333,8 +350,6 @@ async def set_obj_properties(w: Widget, config):
333
350
  if group := config.get(CONF_GROUP):
334
351
  group = await cg.get_variable(group)
335
352
  lv.group_add_obj(group, w.obj)
336
- flag_clr = set()
337
- flag_set = set()
338
353
  props = parts[CONF_MAIN][CONF_DEFAULT]
339
354
  lambs = {}
340
355
  flag_set = set()
@@ -384,7 +399,7 @@ async def set_obj_properties(w: Widget, config):
384
399
  w.add_state(state)
385
400
  cond.else_()
386
401
  w.clear_state(state)
387
- await w.set_property(CONF_SCROLLBAR_MODE, config)
402
+ await w.set_property(CONF_SCROLLBAR_MODE, config, lv_name="obj")
388
403
 
389
404
 
390
405
  async def add_widgets(parent: Widget, config: dict):
@@ -1,5 +1,6 @@
1
1
  import esphome.config_validation as cv
2
2
  from esphome.const import (
3
+ CONF_GROUP,
3
4
  CONF_MAX_VALUE,
4
5
  CONF_MIN_VALUE,
5
6
  CONF_MODE,
@@ -20,7 +21,7 @@ from ..defines import (
20
21
  literal,
21
22
  )
22
23
  from ..lv_validation import angle, get_start_value, lv_float
23
- from ..lvcode import lv, lv_obj
24
+ from ..lvcode import lv, lv_expr, lv_obj
24
25
  from ..types import LvNumber, NumberType
25
26
  from . import Widget
26
27
 
@@ -69,6 +70,9 @@ class ArcType(NumberType):
69
70
  if config.get(CONF_ADJUSTABLE) is False:
70
71
  lv_obj.remove_style(w.obj, nullptr, literal("LV_PART_KNOB"))
71
72
  w.clear_flag("LV_OBJ_FLAG_CLICKABLE")
73
+ elif CONF_GROUP not in config:
74
+ # For some reason arc does not get automatically added to the default group
75
+ lv.group_add_obj(lv_expr.group_get_default(), w.obj)
72
76
 
73
77
  value = await get_start_value(config)
74
78
  if value is not None:
@@ -13,11 +13,13 @@ from ..defines import (
13
13
  CONF_KEY_CODE,
14
14
  CONF_MAIN,
15
15
  CONF_ONE_CHECKED,
16
+ CONF_PAD_COLUMN,
17
+ CONF_PAD_ROW,
16
18
  CONF_ROWS,
17
19
  CONF_SELECTED,
18
20
  )
19
21
  from ..helpers import lvgl_components_required
20
- from ..lv_validation import key_code, lv_bool
22
+ from ..lv_validation import key_code, lv_bool, pixels
21
23
  from ..lvcode import lv, lv_add, lv_expr
22
24
  from ..schemas import automation_schema
23
25
  from ..types import (
@@ -57,6 +59,8 @@ BUTTONMATRIX_BUTTON_SCHEMA = cv.Schema(
57
59
  BUTTONMATRIX_SCHEMA = cv.Schema(
58
60
  {
59
61
  cv.Optional(CONF_ONE_CHECKED, default=False): lv_bool,
62
+ cv.Optional(CONF_PAD_ROW): pixels,
63
+ cv.Optional(CONF_PAD_COLUMN): pixels,
60
64
  cv.GenerateID(CONF_BUTTON_TEXT_LIST_ID): cv.declare_id(char_ptr),
61
65
  cv.Required(CONF_ROWS): cv.ensure_list(
62
66
  cv.Schema(