esphome 2024.10.3__py3-none-any.whl → 2024.11.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 (228) 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/bytebuffer/__init__.py +5 -0
  13. esphome/components/bytebuffer/bytebuffer.h +421 -0
  14. esphome/components/climate/__init__.py +14 -13
  15. esphome/components/datetime/__init__.py +3 -3
  16. esphome/components/debug/debug_esp32.cpp +16 -8
  17. esphome/components/dfplayer/dfplayer.cpp +132 -6
  18. esphome/components/dfplayer/dfplayer.h +19 -53
  19. esphome/components/display/display.cpp +142 -0
  20. esphome/components/display/display.h +7 -0
  21. esphome/components/es8311/__init__.py +0 -0
  22. esphome/components/es8311/audio_dac.py +70 -0
  23. esphome/components/es8311/es8311.cpp +227 -0
  24. esphome/components/es8311/es8311.h +135 -0
  25. esphome/components/es8311/es8311_const.h +195 -0
  26. esphome/components/esp32/boards.py +199 -1
  27. esphome/components/esp32/gpio.py +3 -1
  28. esphome/components/esp32_ble/const_esp32c6.h +7 -0
  29. esphome/components/esp32_ble_client/ble_client_base.h +1 -1
  30. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -1
  31. esphome/components/esp32_rmt_led_strip/led_strip.cpp +2 -2
  32. esphome/components/esp32_rmt_led_strip/led_strip.h +2 -0
  33. esphome/components/esp32_rmt_led_strip/light.py +3 -1
  34. esphome/components/esp8266/gpio.py +7 -5
  35. esphome/components/ethernet/__init__.py +55 -1
  36. esphome/components/ethernet/ethernet_component.cpp +14 -1
  37. esphome/components/ethernet/ethernet_component.h +7 -1
  38. esphome/components/font/__init__.py +213 -108
  39. esphome/components/gp8403/output/__init__.py +1 -1
  40. esphome/components/host/gpio.py +6 -4
  41. esphome/components/http_request/__init__.py +12 -0
  42. esphome/components/http_request/http_request.h +65 -3
  43. esphome/components/http_request/http_request_arduino.cpp +2 -3
  44. esphome/components/http_request/http_request_idf.cpp +6 -14
  45. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  46. esphome/components/http_request/update/http_request_update.cpp +1 -1
  47. esphome/components/i2c_device/__init__.py +26 -0
  48. esphome/components/i2c_device/i2c_device.cpp +17 -0
  49. esphome/components/i2c_device/i2c_device.h +18 -0
  50. esphome/components/i2s_audio/__init__.py +1 -3
  51. esphome/components/i2s_audio/speaker/__init__.py +12 -4
  52. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +432 -197
  53. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +91 -32
  54. esphome/components/ili9xxx/display.py +5 -1
  55. esphome/components/image/__init__.py +5 -8
  56. esphome/components/image/image.cpp +14 -14
  57. esphome/components/image/image.h +20 -24
  58. esphome/components/internal_temperature/internal_temperature.cpp +51 -2
  59. esphome/components/internal_temperature/internal_temperature.h +1 -0
  60. esphome/components/libretiny/gpio.py +4 -2
  61. esphome/components/light/__init__.py +32 -1
  62. esphome/components/light/automation.py +39 -32
  63. esphome/components/light/effects.py +36 -36
  64. esphome/components/light/light_state.cpp +6 -16
  65. esphome/components/light/light_state.h +34 -0
  66. esphome/components/light/types.py +3 -1
  67. esphome/components/logger/logger_esp32.cpp +15 -0
  68. esphome/components/lvgl/__init__.py +202 -95
  69. esphome/components/lvgl/automation.py +42 -40
  70. esphome/components/lvgl/binary_sensor/__init__.py +8 -15
  71. esphome/components/lvgl/defines.py +14 -8
  72. esphome/components/lvgl/encoders.py +11 -8
  73. esphome/components/lvgl/keypads.py +77 -0
  74. esphome/components/lvgl/light/__init__.py +6 -8
  75. esphome/components/lvgl/lv_validation.py +2 -4
  76. esphome/components/lvgl/lvcode.py +3 -9
  77. esphome/components/lvgl/lvgl_esphome.cpp +210 -89
  78. esphome/components/lvgl/lvgl_esphome.h +113 -30
  79. esphome/components/lvgl/lvgl_proxy.h +17 -0
  80. esphome/components/lvgl/number/__init__.py +10 -15
  81. esphome/components/lvgl/schemas.py +4 -2
  82. esphome/components/lvgl/select/__init__.py +12 -37
  83. esphome/components/lvgl/select/lvgl_select.h +27 -33
  84. esphome/components/lvgl/sensor/__init__.py +8 -14
  85. esphome/components/lvgl/styles.py +3 -4
  86. esphome/components/lvgl/switch/__init__.py +8 -13
  87. esphome/components/lvgl/text/__init__.py +5 -6
  88. esphome/components/lvgl/text_sensor/__init__.py +15 -15
  89. esphome/components/lvgl/touchscreens.py +2 -3
  90. esphome/components/lvgl/trigger.py +7 -9
  91. esphome/components/lvgl/types.py +9 -3
  92. esphome/components/lvgl/widgets/__init__.py +32 -21
  93. esphome/components/lvgl/widgets/dropdown.py +22 -10
  94. esphome/components/lvgl/widgets/msgbox.py +6 -5
  95. esphome/components/lvgl/widgets/obj.py +4 -2
  96. esphome/components/lvgl/widgets/page.py +3 -2
  97. esphome/components/lvgl/widgets/qrcode.py +54 -0
  98. esphome/components/lvgl/widgets/roller.py +21 -14
  99. esphome/components/lvgl/widgets/tileview.py +2 -1
  100. esphome/components/max17043/__init__.py +1 -0
  101. esphome/components/max17043/automation.h +20 -0
  102. esphome/components/max17043/max17043.cpp +98 -0
  103. esphome/components/max17043/max17043.h +29 -0
  104. esphome/components/max17043/sensor.py +77 -0
  105. esphome/components/media_player/__init__.py +11 -0
  106. esphome/components/media_player/automation.h +10 -0
  107. esphome/components/media_player/media_player.cpp +4 -0
  108. esphome/components/midea/air_conditioner.cpp +17 -1
  109. esphome/components/mlx90393/sensor.py +1 -1
  110. esphome/components/modbus_controller/__init__.py +31 -1
  111. esphome/components/modbus_controller/automation.h +16 -0
  112. esphome/components/modbus_controller/const.py +2 -0
  113. esphome/components/modbus_controller/modbus_controller.cpp +14 -2
  114. esphome/components/modbus_controller/modbus_controller.h +9 -0
  115. esphome/components/mopeka_pro_check/mopeka_pro_check.cpp +40 -21
  116. esphome/components/mopeka_pro_check/mopeka_pro_check.h +9 -2
  117. esphome/components/mopeka_pro_check/sensor.py +41 -0
  118. esphome/components/mqtt/__init__.py +36 -0
  119. esphome/components/mqtt/mqtt_client.cpp +27 -3
  120. esphome/components/mqtt/mqtt_client.h +27 -2
  121. esphome/components/mqtt/mqtt_climate.cpp +4 -2
  122. esphome/components/mqtt/mqtt_component.cpp +6 -0
  123. esphome/components/mqtt/mqtt_component.h +4 -0
  124. esphome/components/mqtt/mqtt_const.h +6 -0
  125. esphome/components/online_image/online_image.cpp +2 -8
  126. esphome/components/online_image/online_image.h +2 -6
  127. esphome/components/opentherm/__init__.py +35 -9
  128. esphome/components/opentherm/binary_sensor/__init__.py +33 -0
  129. esphome/components/opentherm/const.py +11 -0
  130. esphome/components/opentherm/generate.py +142 -0
  131. esphome/components/opentherm/hub.cpp +130 -24
  132. esphome/components/opentherm/hub.h +62 -9
  133. esphome/components/opentherm/input.h +18 -0
  134. esphome/components/opentherm/input.py +51 -0
  135. esphome/components/opentherm/number/__init__.py +74 -0
  136. esphome/components/opentherm/number/number.cpp +40 -0
  137. esphome/components/opentherm/number/number.h +31 -0
  138. esphome/components/opentherm/opentherm.cpp +30 -0
  139. esphome/components/opentherm/opentherm.h +34 -2
  140. esphome/components/opentherm/opentherm_macros.h +151 -0
  141. esphome/components/opentherm/output/__init__.py +47 -0
  142. esphome/components/opentherm/output/output.cpp +18 -0
  143. esphome/components/opentherm/output/output.h +33 -0
  144. esphome/components/opentherm/schema.py +814 -0
  145. esphome/components/opentherm/sensor/__init__.py +51 -0
  146. esphome/components/opentherm/switch/__init__.py +43 -0
  147. esphome/components/opentherm/switch/switch.cpp +28 -0
  148. esphome/components/opentherm/switch/switch.h +20 -0
  149. esphome/components/opentherm/validate.py +31 -0
  150. esphome/components/pcd8544/display.py +8 -4
  151. esphome/components/prometheus/prometheus_handler.cpp +176 -14
  152. esphome/components/prometheus/prometheus_handler.h +25 -7
  153. esphome/components/qspi_amoled/display.py +1 -141
  154. esphome/components/qspi_dbi/display.py +185 -0
  155. esphome/components/qspi_dbi/models.py +64 -0
  156. esphome/components/{qspi_amoled/qspi_amoled.cpp → qspi_dbi/qspi_dbi.cpp} +95 -46
  157. esphome/components/{qspi_amoled/qspi_amoled.h → qspi_dbi/qspi_dbi.h} +26 -15
  158. esphome/components/rp2040/__init__.py +6 -3
  159. esphome/components/rp2040/gpio.py +5 -3
  160. esphome/components/rtttl/rtttl.cpp +4 -1
  161. esphome/components/rtttl/rtttl.h +1 -0
  162. esphome/components/sdl/sdl_esphome.cpp +22 -5
  163. esphome/components/sdl/sdl_esphome.h +1 -0
  164. esphome/components/sensor/__init__.py +18 -8
  165. esphome/components/sensor/filter.cpp +19 -18
  166. esphome/components/sensor/filter.h +9 -10
  167. esphome/components/sgp4x/sgp4x.cpp +40 -74
  168. esphome/components/sgp4x/sgp4x.h +5 -3
  169. esphome/components/speaker/__init__.py +51 -5
  170. esphome/components/speaker/automation.h +25 -0
  171. esphome/components/speaker/speaker.h +72 -1
  172. esphome/components/spi/__init__.py +15 -14
  173. esphome/components/spi_device/__init__.py +4 -15
  174. esphome/components/ssd1306_spi/display.py +6 -2
  175. esphome/components/ssd1322_spi/display.py +6 -2
  176. esphome/components/ssd1325_spi/display.py +6 -2
  177. esphome/components/ssd1327_spi/display.py +6 -2
  178. esphome/components/ssd1331_spi/display.py +6 -2
  179. esphome/components/ssd1351_spi/display.py +6 -2
  180. esphome/components/st7567_spi/display.py +6 -2
  181. esphome/components/st7701s/display.py +5 -1
  182. esphome/components/st7735/display.py +10 -5
  183. esphome/components/st7789v/display.py +12 -7
  184. esphome/components/statsd/statsd.cpp +2 -0
  185. esphome/components/statsd/statsd.h +2 -0
  186. esphome/components/sun/sun.h +3 -0
  187. esphome/components/tc74/__init__.py +1 -0
  188. esphome/components/tc74/sensor.py +32 -0
  189. esphome/components/tc74/tc74.cpp +68 -0
  190. esphome/components/tc74/tc74.h +28 -0
  191. esphome/components/touchscreen/__init__.py +41 -50
  192. esphome/components/touchscreen/touchscreen.h +4 -8
  193. esphome/components/udp/udp_component.cpp +6 -3
  194. esphome/components/udp/udp_component.h +4 -2
  195. esphome/components/waveshare_epaper/display.py +6 -2
  196. esphome/components/web_server/web_server.cpp +22 -0
  197. esphome/components/web_server/web_server.h +3 -0
  198. esphome/components/weikai/weikai.h +2 -2
  199. esphome/components/wifi/wifi_component.cpp +2 -2
  200. esphome/components/wifi/wifi_component_esp32_arduino.cpp +4 -4
  201. esphome/components/wifi/wifi_component_esp8266.cpp +4 -4
  202. esphome/components/wifi/wifi_component_esp_idf.cpp +2 -2
  203. esphome/components/xpt2046/touchscreen/__init__.py +7 -32
  204. esphome/config_validation.py +3 -1
  205. esphome/const.py +8 -1
  206. esphome/core/defines.h +8 -2
  207. esphome/core/helpers.cpp +32 -17
  208. esphome/core/helpers.h +32 -16
  209. esphome/core/ring_buffer.cpp +2 -2
  210. esphome/core/ring_buffer.h +2 -2
  211. esphome/dashboard/core.py +25 -0
  212. esphome/dashboard/status/mdns.py +3 -4
  213. esphome/dashboard/web_server.py +54 -19
  214. esphome/espota2.py +36 -35
  215. esphome/helpers.py +68 -16
  216. esphome/mqtt.py +9 -2
  217. esphome/storage_json.py +4 -0
  218. esphome/writer.py +7 -18
  219. esphome/zeroconf.py +8 -6
  220. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/METADATA +7 -5
  221. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/RECORD +226 -180
  222. esphome/core/bytebuffer.cpp +0 -167
  223. esphome/core/bytebuffer.h +0 -144
  224. /esphome/components/{qspi_amoled → qspi_dbi}/__init__.py +0 -0
  225. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/LICENSE +0 -0
  226. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/WHEEL +0 -0
  227. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/entry_points.txt +0 -0
  228. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/top_level.txt +0 -0
@@ -1,59 +1,59 @@
1
- from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
1
+ from esphome import automation
2
2
  import esphome.codegen as cg
3
3
  import esphome.config_validation as cv
4
- from esphome import automation
5
-
6
4
  from esphome.const import (
7
- CONF_NAME,
8
- CONF_LAMBDA,
9
- CONF_UPDATE_INTERVAL,
10
- CONF_TRANSITION_LENGTH,
11
- CONF_COLORS,
12
- CONF_STATE,
13
- CONF_DURATION,
5
+ CONF_ALPHA,
6
+ CONF_BLUE,
14
7
  CONF_BRIGHTNESS,
15
- CONF_COLOR_MODE,
8
+ CONF_COLD_WHITE,
16
9
  CONF_COLOR_BRIGHTNESS,
17
- CONF_RED,
18
- CONF_GREEN,
19
- CONF_BLUE,
20
- CONF_WHITE,
10
+ CONF_COLOR_MODE,
21
11
  CONF_COLOR_TEMPERATURE,
22
- CONF_COLD_WHITE,
23
- CONF_WARM_WHITE,
24
- CONF_ALPHA,
12
+ CONF_COLORS,
13
+ CONF_DURATION,
14
+ CONF_GREEN,
25
15
  CONF_INTENSITY,
26
- CONF_SPEED,
27
- CONF_WIDTH,
16
+ CONF_LAMBDA,
17
+ CONF_MAX_BRIGHTNESS,
18
+ CONF_MIN_BRIGHTNESS,
19
+ CONF_NAME,
28
20
  CONF_NUM_LEDS,
29
21
  CONF_RANDOM,
22
+ CONF_RED,
30
23
  CONF_SEQUENCE,
31
- CONF_MAX_BRIGHTNESS,
32
- CONF_MIN_BRIGHTNESS,
24
+ CONF_SPEED,
25
+ CONF_STATE,
26
+ CONF_TRANSITION_LENGTH,
27
+ CONF_UPDATE_INTERVAL,
28
+ CONF_WARM_WHITE,
29
+ CONF_WHITE,
30
+ CONF_WIDTH,
33
31
  )
32
+ from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
34
33
  from esphome.util import Registry
34
+
35
35
  from .types import (
36
- ColorMode,
37
36
  COLOR_MODES,
38
- LambdaLightEffect,
39
- PulseLightEffect,
40
- RandomLightEffect,
41
- StrobeLightEffect,
42
- StrobeLightEffectColor,
43
- LightColorValues,
44
- AddressableLightRef,
45
- AddressableLambdaLightEffect,
46
- FlickerLightEffect,
47
- AddressableRainbowLightEffect,
48
37
  AddressableColorWipeEffect,
49
38
  AddressableColorWipeEffectColor,
50
- AddressableScanEffect,
51
- AddressableTwinkleEffect,
52
- AddressableRandomTwinkleEffect,
53
39
  AddressableFireworksEffect,
54
40
  AddressableFlickerEffect,
41
+ AddressableLambdaLightEffect,
42
+ AddressableLightRef,
43
+ AddressableRainbowLightEffect,
44
+ AddressableRandomTwinkleEffect,
45
+ AddressableScanEffect,
46
+ AddressableTwinkleEffect,
55
47
  AutomationLightEffect,
56
48
  Color,
49
+ ColorMode,
50
+ FlickerLightEffect,
51
+ LambdaLightEffect,
52
+ LightColorValues,
53
+ PulseLightEffect,
54
+ RandomLightEffect,
55
+ StrobeLightEffect,
56
+ StrobeLightEffectColor,
57
57
  )
58
58
 
59
59
  CONF_ADD_LED_INTERVAL = "add_led_interval"
@@ -1,6 +1,7 @@
1
1
  #include "esphome/core/log.h"
2
- #include "light_state.h"
2
+
3
3
  #include "light_output.h"
4
+ #include "light_state.h"
4
5
  #include "transformers.h"
5
6
 
6
7
  namespace esphome {
@@ -16,21 +17,6 @@ LightCall LightState::turn_off() { return this->make_call().set_state(false); }
16
17
  LightCall LightState::toggle() { return this->make_call().set_state(!this->remote_values.is_on()); }
17
18
  LightCall LightState::make_call() { return LightCall(this); }
18
19
 
19
- struct LightStateRTCState {
20
- ColorMode color_mode{ColorMode::UNKNOWN};
21
- bool state{false};
22
- float brightness{1.0f};
23
- float color_brightness{1.0f};
24
- float red{1.0f};
25
- float green{1.0f};
26
- float blue{1.0f};
27
- float white{1.0f};
28
- float color_temp{1.0f};
29
- float cold_white{1.0f};
30
- float warm_white{1.0f};
31
- uint32_t effect{0};
32
- };
33
-
34
20
  void LightState::setup() {
35
21
  ESP_LOGCONFIG(TAG, "Setting up light '%s'...", this->get_name().c_str());
36
22
 
@@ -48,6 +34,9 @@ void LightState::setup() {
48
34
 
49
35
  auto call = this->make_call();
50
36
  LightStateRTCState recovered{};
37
+ if (this->initial_state_.has_value()) {
38
+ recovered = *this->initial_state_;
39
+ }
51
40
  switch (this->restore_mode_) {
52
41
  case LIGHT_RESTORE_DEFAULT_OFF:
53
42
  case LIGHT_RESTORE_DEFAULT_ON:
@@ -175,6 +164,7 @@ void LightState::set_flash_transition_length(uint32_t flash_transition_length) {
175
164
  uint32_t LightState::get_flash_transition_length() const { return this->flash_transition_length_; }
176
165
  void LightState::set_gamma_correct(float gamma_correct) { this->gamma_correct_ = gamma_correct; }
177
166
  void LightState::set_restore_mode(LightRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
167
+ void LightState::set_initial_state(const LightStateRTCState &initial_state) { this->initial_state_ = initial_state; }
178
168
  bool LightState::supports_effects() { return !this->effects_.empty(); }
179
169
  const std::vector<LightEffect *> &LightState::get_effects() const { return this->effects_; }
180
170
  void LightState::add_effects(const std::vector<LightEffect *> &effects) {
@@ -28,6 +28,35 @@ enum LightRestoreMode {
28
28
  LIGHT_RESTORE_AND_ON,
29
29
  };
30
30
 
31
+ struct LightStateRTCState {
32
+ LightStateRTCState(ColorMode color_mode, bool state, float brightness, float color_brightness, float red, float green,
33
+ float blue, float white, float color_temp, float cold_white, float warm_white)
34
+ : color_mode(color_mode),
35
+ state(state),
36
+ brightness(brightness),
37
+ color_brightness(color_brightness),
38
+ red(red),
39
+ green(green),
40
+ blue(blue),
41
+ white(white),
42
+ color_temp(color_temp),
43
+ cold_white(cold_white),
44
+ warm_white(warm_white) {}
45
+ LightStateRTCState() = default;
46
+ ColorMode color_mode{ColorMode::UNKNOWN};
47
+ bool state{false};
48
+ float brightness{1.0f};
49
+ float color_brightness{1.0f};
50
+ float red{1.0f};
51
+ float green{1.0f};
52
+ float blue{1.0f};
53
+ float white{1.0f};
54
+ float color_temp{1.0f};
55
+ float cold_white{1.0f};
56
+ float warm_white{1.0f};
57
+ uint32_t effect{0};
58
+ };
59
+
31
60
  /** This class represents the communication layer between the front-end MQTT layer and the
32
61
  * hardware output layer.
33
62
  */
@@ -116,6 +145,9 @@ class LightState : public EntityBase, public Component {
116
145
  /// Set the restore mode of this light
117
146
  void set_restore_mode(LightRestoreMode restore_mode);
118
147
 
148
+ /// Set the initial state of this light
149
+ void set_initial_state(const LightStateRTCState &initial_state);
150
+
119
151
  /// Return whether the light has any effects that meet the trait requirements.
120
152
  bool supports_effects();
121
153
 
@@ -212,6 +244,8 @@ class LightState : public EntityBase, public Component {
212
244
  float gamma_correct_{};
213
245
  /// Restore mode of the light.
214
246
  LightRestoreMode restore_mode_;
247
+ /// Initial state of the light.
248
+ optional<LightStateRTCState> initial_state_{};
215
249
  /// List of effects for this light.
216
250
  std::vector<LightEffect *> effects_;
217
251
 
@@ -1,5 +1,5 @@
1
- import esphome.codegen as cg
2
1
  from esphome import automation
2
+ import esphome.codegen as cg
3
3
 
4
4
  # Base
5
5
  light_ns = cg.esphome_ns.namespace("light")
@@ -12,6 +12,8 @@ AddressableLightRef = AddressableLight.operator("ref")
12
12
  Color = cg.esphome_ns.class_("Color")
13
13
  LightColorValues = light_ns.class_("LightColorValues")
14
14
 
15
+ LightStateRTCState = light_ns.struct("LightStateRTCState")
16
+
15
17
  # Color modes
16
18
  ColorMode = light_ns.enum("ColorMode", is_class=True)
17
19
  COLOR_MODES = {
@@ -10,8 +10,12 @@
10
10
 
11
11
  #ifdef USE_LOGGER_USB_SERIAL_JTAG
12
12
  #include <driver/usb_serial_jtag.h>
13
+ #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)
13
14
  #include <esp_vfs_dev.h>
14
15
  #include <esp_vfs_usb_serial_jtag.h>
16
+ #else
17
+ #include <driver/usb_serial_jtag_vfs.h>
18
+ #endif
15
19
  #endif
16
20
 
17
21
  #include "freertos/FreeRTOS.h"
@@ -36,10 +40,17 @@ static const char *const TAG = "logger";
36
40
  static void init_usb_serial_jtag_() {
37
41
  setvbuf(stdin, NULL, _IONBF, 0); // Disable buffering on stdin
38
42
 
43
+ #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)
39
44
  // Minicom, screen, idf_monitor send CR when ENTER key is pressed
40
45
  esp_vfs_dev_usb_serial_jtag_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
41
46
  // Move the caret to the beginning of the next line on '\n'
42
47
  esp_vfs_dev_usb_serial_jtag_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
48
+ #else
49
+ // Minicom, screen, idf_monitor send CR when ENTER key is pressed
50
+ usb_serial_jtag_vfs_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
51
+ // Move the caret to the beginning of the next line on '\n'
52
+ usb_serial_jtag_vfs_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
53
+ #endif
43
54
 
44
55
  // Enable non-blocking mode on stdin and stdout
45
56
  fcntl(fileno(stdout), F_SETFL, 0);
@@ -57,7 +68,11 @@ static void init_usb_serial_jtag_() {
57
68
  }
58
69
 
59
70
  // Tell vfs to use usb-serial-jtag driver
71
+ #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)
60
72
  esp_vfs_usb_serial_jtag_use_driver();
73
+ #else
74
+ usb_serial_jtag_vfs_use_driver();
75
+ #endif
61
76
  }
62
77
  #endif
63
78
 
@@ -7,6 +7,7 @@ import esphome.config_validation as cv
7
7
  from esphome.const import (
8
8
  CONF_AUTO_CLEAR_ENABLED,
9
9
  CONF_BUFFER_SIZE,
10
+ CONF_GROUP,
10
11
  CONF_ID,
11
12
  CONF_LAMBDA,
12
13
  CONF_ON_IDLE,
@@ -22,12 +23,18 @@ from esphome.helpers import write_file_if_changed
22
23
 
23
24
  from . import defines as df, helpers, lv_validation as lvalid
24
25
  from .automation import disp_update, focused_widgets, update_to_code
25
- from .defines import CONF_WIDGETS, add_define
26
- from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code
26
+ from .defines import add_define
27
+ from .encoders import (
28
+ ENCODERS_CONFIG,
29
+ encoders_to_code,
30
+ get_default_group,
31
+ initial_focus_to_code,
32
+ )
27
33
  from .gradient import GRADIENT_SCHEMA, gradients_to_code
28
34
  from .hello_world import get_hello_world
35
+ from .keypads import KEYPADS_CONFIG, keypads_to_code
29
36
  from .lv_validation import lv_bool, lv_images_used
30
- from .lvcode import LvContext, LvglComponent
37
+ from .lvcode import LvContext, LvglComponent, lvgl_static
31
38
  from .schemas import (
32
39
  DISP_BG_SCHEMA,
33
40
  FLEX_OBJ_SCHEMA,
@@ -48,12 +55,13 @@ from .types import (
48
55
  FontEngine,
49
56
  IdleTrigger,
50
57
  ObjUpdateAction,
58
+ PauseTrigger,
51
59
  lv_font_t,
52
60
  lv_group_t,
53
61
  lv_style_t,
54
62
  lvgl_ns,
55
63
  )
56
- from .widgets import Widget, add_widgets, lv_scr_act, set_obj_properties, styles_used
64
+ from .widgets import Widget, add_widgets, get_scr_act, set_obj_properties, styles_used
57
65
  from .widgets.animimg import animimg_spec
58
66
  from .widgets.arc import arc_spec
59
67
  from .widgets.button import button_spec
@@ -70,6 +78,7 @@ from .widgets.meter import meter_spec
70
78
  from .widgets.msgbox import MSGBOX_SCHEMA, msgboxes_to_code
71
79
  from .widgets.obj import obj_spec
72
80
  from .widgets.page import add_pages, generate_page_triggers, page_spec
81
+ from .widgets.qrcode import qr_code_spec
73
82
  from .widgets.roller import roller_spec
74
83
  from .widgets.slider import slider_spec
75
84
  from .widgets.spinbox import spinbox_spec
@@ -108,6 +117,7 @@ for w_type in (
108
117
  spinbox_spec,
109
118
  keyboard_spec,
110
119
  tileview_spec,
120
+ qr_code_spec,
111
121
  ):
112
122
  WIDGET_TYPES[w_type.name] = w_type
113
123
 
@@ -149,43 +159,80 @@ def generate_lv_conf_h():
149
159
  return LV_CONF_H_FORMAT.format("\n".join(definitions))
150
160
 
151
161
 
152
- def final_validation(config):
153
- if pages := config.get(CONF_PAGES):
154
- if all(p[df.CONF_SKIP] for p in pages):
155
- raise cv.Invalid("At least one page must not be skipped")
162
+ def multi_conf_validate(configs: list[dict]):
163
+ displays = [config[df.CONF_DISPLAYS] for config in configs]
164
+ # flatten the display list
165
+ display_list = [disp for disps in displays for disp in disps]
166
+ if len(display_list) != len(set(display_list)):
167
+ raise cv.Invalid("A display ID may be used in only one LVGL instance")
168
+ for config in configs:
169
+ for item in (df.CONF_ENCODERS, df.CONF_KEYPADS):
170
+ for enc in config.get(item, ()):
171
+ if CONF_GROUP not in enc:
172
+ raise cv.Invalid(
173
+ f"'{item}' must have an explicit group set when using multiple LVGL instances"
174
+ )
175
+ base_config = configs[0]
176
+ for config in configs[1:]:
177
+ for item in (
178
+ df.CONF_LOG_LEVEL,
179
+ df.CONF_COLOR_DEPTH,
180
+ df.CONF_BYTE_ORDER,
181
+ df.CONF_TRANSPARENCY_KEY,
182
+ ):
183
+ if base_config[item] != config[item]:
184
+ raise cv.Invalid(
185
+ f"Config item '{item}' must be the same for all LVGL instances"
186
+ )
187
+
188
+
189
+ def final_validation(configs):
190
+ if len(configs) != 1:
191
+ multi_conf_validate(configs)
156
192
  global_config = full_config.get()
157
- for display_id in config[df.CONF_DISPLAYS]:
158
- path = global_config.get_path_for_id(display_id)[:-1]
159
- display = global_config.get_config_for_path(path)
160
- if CONF_LAMBDA in display:
161
- raise cv.Invalid("Using lambda: in display config not compatible with LVGL")
162
- if display[CONF_AUTO_CLEAR_ENABLED]:
163
- raise cv.Invalid(
164
- "Using auto_clear_enabled: true in display config not compatible with LVGL"
165
- )
166
- buffer_frac = config[CONF_BUFFER_SIZE]
167
- if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config:
168
- LOGGER.warning("buffer_size: may need to be reduced without PSRAM")
169
- for image_id in lv_images_used:
170
- path = global_config.get_path_for_id(image_id)[:-1]
171
- image_conf = global_config.get_config_for_path(path)
172
- if image_conf[CONF_TYPE] in ("RGBA", "RGB24"):
173
- raise cv.Invalid(
174
- "Using RGBA or RGB24 in image config not compatible with LVGL", path
175
- )
176
- for w in focused_widgets:
177
- path = global_config.get_path_for_id(w)
178
- widget_conf = global_config.get_config_for_path(path[:-1])
179
- if df.CONF_ADJUSTABLE in widget_conf and not widget_conf[df.CONF_ADJUSTABLE]:
180
- raise cv.Invalid(
181
- "A non adjustable arc may not be focused",
182
- path,
183
- )
184
-
185
-
186
- async def to_code(config):
193
+ for config in configs:
194
+ if pages := config.get(CONF_PAGES):
195
+ if all(p[df.CONF_SKIP] for p in pages):
196
+ raise cv.Invalid("At least one page must not be skipped")
197
+ for display_id in config[df.CONF_DISPLAYS]:
198
+ path = global_config.get_path_for_id(display_id)[:-1]
199
+ display = global_config.get_config_for_path(path)
200
+ if CONF_LAMBDA in display:
201
+ raise cv.Invalid(
202
+ "Using lambda: in display config not compatible with LVGL"
203
+ )
204
+ if display[CONF_AUTO_CLEAR_ENABLED]:
205
+ raise cv.Invalid(
206
+ "Using auto_clear_enabled: true in display config not compatible with LVGL"
207
+ )
208
+ buffer_frac = config[CONF_BUFFER_SIZE]
209
+ if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config:
210
+ LOGGER.warning("buffer_size: may need to be reduced without PSRAM")
211
+ for image_id in lv_images_used:
212
+ path = global_config.get_path_for_id(image_id)[:-1]
213
+ image_conf = global_config.get_config_for_path(path)
214
+ if image_conf[CONF_TYPE] in ("RGBA", "RGB24"):
215
+ raise cv.Invalid(
216
+ "Using RGBA or RGB24 in image config not compatible with LVGL", path
217
+ )
218
+ for w in focused_widgets:
219
+ path = global_config.get_path_for_id(w)
220
+ widget_conf = global_config.get_config_for_path(path[:-1])
221
+ if (
222
+ df.CONF_ADJUSTABLE in widget_conf
223
+ and not widget_conf[df.CONF_ADJUSTABLE]
224
+ ):
225
+ raise cv.Invalid(
226
+ "A non adjustable arc may not be focused",
227
+ path,
228
+ )
229
+
230
+
231
+ async def to_code(configs):
232
+ config_0 = configs[0]
233
+ # Global configuration
187
234
  cg.add_library("lvgl/lvgl", "8.4.0")
188
- CORE.add_define("USE_LVGL")
235
+ cg.add_define("USE_LVGL")
189
236
  # suppress default enabling of extra widgets
190
237
  add_define("_LV_KCONFIG_PRESENT")
191
238
  # Always enable - lots of things use it.
@@ -199,45 +246,34 @@ async def to_code(config):
199
246
  add_define("LV_MEM_CUSTOM_REALLOC", "lv_custom_mem_realloc")
200
247
  add_define("LV_MEM_CUSTOM_INCLUDE", '"esphome/components/lvgl/lvgl_hal.h"')
201
248
 
202
- add_define("LV_LOG_LEVEL", f"LV_LOG_LEVEL_{config[df.CONF_LOG_LEVEL]}")
203
- add_define("LV_COLOR_DEPTH", config[df.CONF_COLOR_DEPTH])
249
+ add_define(
250
+ "LV_LOG_LEVEL",
251
+ f"LV_LOG_LEVEL_{df.LV_LOG_LEVELS[config_0[df.CONF_LOG_LEVEL]]}",
252
+ )
253
+ cg.add_define(
254
+ "LVGL_LOG_LEVEL",
255
+ cg.RawExpression(f"ESPHOME_LOG_LEVEL_{config_0[df.CONF_LOG_LEVEL]}"),
256
+ )
257
+ add_define("LV_COLOR_DEPTH", config_0[df.CONF_COLOR_DEPTH])
204
258
  for font in helpers.lv_fonts_used:
205
259
  add_define(f"LV_FONT_{font.upper()}")
206
260
 
207
- if config[df.CONF_COLOR_DEPTH] == 16:
261
+ if config_0[df.CONF_COLOR_DEPTH] == 16:
208
262
  add_define(
209
263
  "LV_COLOR_16_SWAP",
210
- "1" if config[df.CONF_BYTE_ORDER] == "big_endian" else "0",
264
+ "1" if config_0[df.CONF_BYTE_ORDER] == "big_endian" else "0",
211
265
  )
212
266
  add_define(
213
267
  "LV_COLOR_CHROMA_KEY",
214
- await lvalid.lv_color.process(config[df.CONF_TRANSPARENCY_KEY]),
268
+ await lvalid.lv_color.process(config_0[df.CONF_TRANSPARENCY_KEY]),
215
269
  )
216
- CORE.add_build_flag("-Isrc")
270
+ cg.add_build_flag("-Isrc")
217
271
 
218
272
  cg.add_global(lvgl_ns.using)
219
- lv_component = cg.new_Pvariable(config[CONF_ID])
220
- await cg.register_component(lv_component, config)
221
- Widget.create(config[CONF_ID], lv_component, obj_spec, config)
222
- for display in config[df.CONF_DISPLAYS]:
223
- cg.add(lv_component.add_display(await cg.get_variable(display)))
224
-
225
- frac = config[CONF_BUFFER_SIZE]
226
- if frac >= 0.75:
227
- frac = 1
228
- elif frac >= 0.375:
229
- frac = 2
230
- elif frac > 0.19:
231
- frac = 4
232
- else:
233
- frac = 8
234
- cg.add(lv_component.set_buffer_frac(int(frac)))
235
- cg.add(lv_component.set_full_refresh(config[df.CONF_FULL_REFRESH]))
236
-
237
273
  for font in helpers.esphome_fonts_used:
238
274
  await cg.get_variable(font)
239
275
  cg.new_Pvariable(ID(f"{font}_engine", True, type=FontEngine), MockObj(font))
240
- default_font = config[df.CONF_DEFAULT_FONT]
276
+ default_font = config_0[df.CONF_DEFAULT_FONT]
241
277
  if not lvalid.is_lv_font(default_font):
242
278
  add_define(
243
279
  "LV_FONT_CUSTOM_DECLARE", f"LV_FONT_DECLARE(*{df.DEFAULT_ESPHOME_FONT})"
@@ -253,58 +289,103 @@ async def to_code(config):
253
289
  add_define("LV_FONT_DEFAULT", df.DEFAULT_ESPHOME_FONT)
254
290
  else:
255
291
  add_define("LV_FONT_DEFAULT", await lvalid.lv_font.process(default_font))
292
+ cg.add(lvgl_static.esphome_lvgl_init())
293
+ default_group = get_default_group(config_0)
256
294
 
257
- async with LvContext(lv_component):
258
- await touchscreens_to_code(lv_component, config)
259
- await encoders_to_code(lv_component, config)
260
- await theme_to_code(config)
261
- await styles_to_code(config)
262
- await gradients_to_code(config)
263
- await set_obj_properties(lv_scr_act, config)
264
- await add_widgets(lv_scr_act, config)
265
- await add_pages(lv_component, config)
266
- await add_top_layer(config)
267
- await msgboxes_to_code(config)
268
- await disp_update(f"{lv_component}->get_disp()", config)
269
- # At this point only the setup code should be generated
270
- assert LvContext.added_lambda_count == 1
271
- Widget.set_completed()
272
- async with LvContext(lv_component):
273
- await generate_triggers(lv_component)
274
- await generate_page_triggers(lv_component, config)
275
- for conf in config.get(CONF_ON_IDLE, ()):
276
- templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)
277
- idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ)
278
- await build_automation(idle_trigger, [], conf)
279
- await initial_focus_to_code(config)
295
+ for config in configs:
296
+ frac = config[CONF_BUFFER_SIZE]
297
+ if frac >= 0.75:
298
+ frac = 1
299
+ elif frac >= 0.375:
300
+ frac = 2
301
+ elif frac > 0.19:
302
+ frac = 4
303
+ else:
304
+ frac = 8
305
+ displays = [
306
+ await cg.get_variable(display) for display in config[df.CONF_DISPLAYS]
307
+ ]
308
+ lv_component = cg.new_Pvariable(
309
+ config[CONF_ID],
310
+ displays,
311
+ frac,
312
+ config[df.CONF_FULL_REFRESH],
313
+ config[df.CONF_DRAW_ROUNDING],
314
+ config[df.CONF_RESUME_ON_INPUT],
315
+ )
316
+ await cg.register_component(lv_component, config)
317
+ Widget.create(config[CONF_ID], lv_component, obj_spec, config)
318
+
319
+ lv_scr_act = get_scr_act(lv_component)
320
+ async with LvContext():
321
+ await touchscreens_to_code(lv_component, config)
322
+ await encoders_to_code(lv_component, config, default_group)
323
+ await keypads_to_code(lv_component, config, default_group)
324
+ await theme_to_code(config)
325
+ await styles_to_code(config)
326
+ await gradients_to_code(config)
327
+ await set_obj_properties(lv_scr_act, config)
328
+ await add_widgets(lv_scr_act, config)
329
+ await add_pages(lv_component, config)
330
+ await add_top_layer(lv_component, config)
331
+ await msgboxes_to_code(lv_component, config)
332
+ await disp_update(lv_component.get_disp(), config)
333
+ # Set this directly since we are limited in how many methods can be added to the Widget class.
334
+ Widget.widgets_completed = True
335
+ async with LvContext():
336
+ await generate_triggers()
337
+ for config in configs:
338
+ lv_component = await cg.get_variable(config[CONF_ID])
339
+ await generate_page_triggers(config)
340
+ await initial_focus_to_code(config)
341
+ for conf in config.get(CONF_ON_IDLE, ()):
342
+ templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)
343
+ idle_trigger = cg.new_Pvariable(
344
+ conf[CONF_TRIGGER_ID], lv_component, templ
345
+ )
346
+ await build_automation(idle_trigger, [], conf)
347
+ for conf in config.get(df.CONF_ON_PAUSE, ()):
348
+ pause_trigger = cg.new_Pvariable(
349
+ conf[CONF_TRIGGER_ID], lv_component, True
350
+ )
351
+ await build_automation(pause_trigger, [], conf)
352
+ for conf in config.get(df.CONF_ON_RESUME, ()):
353
+ resume_trigger = cg.new_Pvariable(
354
+ conf[CONF_TRIGGER_ID], lv_component, False
355
+ )
356
+ await build_automation(resume_trigger, [], conf)
280
357
 
358
+ # This must be done after all widgets are created
281
359
  for comp in helpers.lvgl_components_required:
282
- CORE.add_define(f"USE_LVGL_{comp.upper()}")
360
+ cg.add_define(f"USE_LVGL_{comp.upper()}")
283
361
  if "transform_angle" in styles_used:
284
362
  add_define("LV_COLOR_SCREEN_TRANSP", "1")
285
363
  for use in helpers.lv_uses:
286
364
  add_define(f"LV_USE_{use.upper()}")
287
365
  lv_conf_h_file = CORE.relative_src_path(LV_CONF_FILENAME)
288
366
  write_file_if_changed(lv_conf_h_file, generate_lv_conf_h())
289
- CORE.add_build_flag("-DLV_CONF_H=1")
290
- CORE.add_build_flag(f'-DLV_CONF_PATH="{LV_CONF_FILENAME}"')
367
+ cg.add_build_flag("-DLV_CONF_H=1")
368
+ cg.add_build_flag(f'-DLV_CONF_PATH="{LV_CONF_FILENAME}"')
291
369
 
292
370
 
293
371
  def display_schema(config):
294
372
  value = cv.ensure_list(cv.use_id(Display))(config)
295
- return value or [cv.use_id(Display)(config)]
373
+ value = value or [cv.use_id(Display)(config)]
374
+ if len(set(value)) != len(value):
375
+ raise cv.Invalid("Display IDs must be unique")
376
+ return value
296
377
 
297
378
 
298
379
  def add_hello_world(config):
299
- if CONF_WIDGETS not in config and CONF_PAGES not in config:
380
+ if df.CONF_WIDGETS not in config and CONF_PAGES not in config:
300
381
  LOGGER.info("No pages or widgets configured, creating default hello_world page")
301
- config[CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world())
382
+ config[df.CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world())
302
383
  return config
303
384
 
304
385
 
305
386
  FINAL_VALIDATE_SCHEMA = final_validation
306
387
 
307
- CONFIG_SCHEMA = (
388
+ LVGL_SCHEMA = (
308
389
  cv.polling_component_schema("1s")
309
390
  .extend(obj_schema(obj_spec))
310
391
  .extend(
@@ -314,9 +395,10 @@ CONFIG_SCHEMA = (
314
395
  cv.Optional(df.CONF_COLOR_DEPTH, default=16): cv.one_of(16),
315
396
  cv.Optional(df.CONF_DEFAULT_FONT, default="montserrat_14"): lvalid.lv_font,
316
397
  cv.Optional(df.CONF_FULL_REFRESH, default=False): cv.boolean,
398
+ cv.Optional(df.CONF_DRAW_ROUNDING, default=2): cv.positive_int,
317
399
  cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage,
318
400
  cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of(
319
- *df.LOG_LEVELS, upper=True
401
+ *df.LV_LOG_LEVELS, upper=True
320
402
  ),
321
403
  cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of(
322
404
  "big_endian", "little_endian"
@@ -341,6 +423,16 @@ CONFIG_SCHEMA = (
341
423
  ),
342
424
  }
343
425
  ),
426
+ cv.Optional(df.CONF_ON_PAUSE): validate_automation(
427
+ {
428
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger),
429
+ }
430
+ ),
431
+ cv.Optional(df.CONF_ON_RESUME): validate_automation(
432
+ {
433
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger),
434
+ }
435
+ ),
344
436
  cv.Exclusive(df.CONF_WIDGETS, CONF_PAGES): cv.ensure_list(WIDGET_SCHEMA),
345
437
  cv.Exclusive(CONF_PAGES, CONF_PAGES): cv.ensure_list(
346
438
  container_schema(page_spec)
@@ -355,9 +447,24 @@ CONFIG_SCHEMA = (
355
447
  cv.Optional(df.CONF_GRADIENTS): GRADIENT_SCHEMA,
356
448
  cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema,
357
449
  cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG,
450
+ cv.Optional(df.CONF_KEYPADS, default=None): KEYPADS_CONFIG,
358
451
  cv.GenerateID(df.CONF_DEFAULT_GROUP): cv.declare_id(lv_group_t),
452
+ cv.Optional(df.CONF_RESUME_ON_INPUT, default=True): cv.boolean,
359
453
  }
360
454
  )
361
455
  .extend(DISP_BG_SCHEMA)
362
456
  .add_extra(add_hello_world)
363
457
  )
458
+
459
+
460
+ def lvgl_config_schema(config):
461
+ """
462
+ Can't use cv.ensure_list here because it converts an empty config to an empty list,
463
+ rather than a default config.
464
+ """
465
+ if not config or isinstance(config, dict):
466
+ return [LVGL_SCHEMA(config)]
467
+ return cv.Schema([LVGL_SCHEMA])(config)
468
+
469
+
470
+ CONFIG_SCHEMA = lvgl_config_schema