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,274 @@
1
+ import esphome.codegen as cg
2
+ import esphome.config_validation as cv
3
+ from esphome import automation
4
+ from esphome.components import i2c, sensor
5
+ from esphome.const import (
6
+ CONF_ACTUAL_GAIN,
7
+ CONF_ACTUAL_INTEGRATION_TIME,
8
+ CONF_AMBIENT_LIGHT,
9
+ CONF_AUTO_MODE,
10
+ CONF_FULL_SPECTRUM_COUNTS,
11
+ CONF_GAIN,
12
+ CONF_GLASS_ATTENUATION_FACTOR,
13
+ CONF_ID,
14
+ CONF_INTEGRATION_TIME,
15
+ CONF_NAME,
16
+ CONF_REPEAT,
17
+ CONF_TRIGGER_ID,
18
+ CONF_TYPE,
19
+ DEVICE_CLASS_DISTANCE,
20
+ DEVICE_CLASS_ILLUMINANCE,
21
+ ICON_BRIGHTNESS_5,
22
+ ICON_BRIGHTNESS_6,
23
+ ICON_TIMER,
24
+ STATE_CLASS_MEASUREMENT,
25
+ UNIT_LUX,
26
+ UNIT_MILLISECOND,
27
+ )
28
+
29
+ CODEOWNERS = ["@latonita"]
30
+ DEPENDENCIES = ["i2c"]
31
+
32
+ CONF_INFRARED_COUNTS = "infrared_counts"
33
+ CONF_ON_PS_HIGH_THRESHOLD = "on_ps_high_threshold"
34
+ CONF_ON_PS_LOW_THRESHOLD = "on_ps_low_threshold"
35
+ CONF_PS_COOLDOWN = "ps_cooldown"
36
+ CONF_PS_COUNTS = "ps_counts"
37
+ CONF_PS_GAIN = "ps_gain"
38
+ CONF_PS_HIGH_THRESHOLD = "ps_high_threshold"
39
+ CONF_PS_LOW_THRESHOLD = "ps_low_threshold"
40
+ ICON_BRIGHTNESS_7 = "mdi:brightness-7"
41
+ ICON_GAIN = "mdi:multiplication"
42
+ ICON_PROXIMITY = "mdi:hand-wave-outline"
43
+ UNIT_COUNTS = "#"
44
+
45
+ ltr501_ns = cg.esphome_ns.namespace("ltr501")
46
+
47
+ LTRAlsPsComponent = ltr501_ns.class_(
48
+ "LTRAlsPs501Component", cg.PollingComponent, i2c.I2CDevice
49
+ )
50
+
51
+ LtrType = ltr501_ns.enum("LtrType")
52
+ LTR_TYPES = {
53
+ "ALS": LtrType.LTR_TYPE_ALS_ONLY,
54
+ "PS": LtrType.LTR_TYPE_PS_ONLY,
55
+ "ALS_PS": LtrType.LTR_TYPE_ALS_AND_PS,
56
+ }
57
+
58
+ AlsGain = ltr501_ns.enum("AlsGain501")
59
+ ALS_GAINS = {
60
+ "1X": AlsGain.GAIN_1,
61
+ "150X": AlsGain.GAIN_150,
62
+ }
63
+
64
+ IntegrationTime = ltr501_ns.enum("IntegrationTime501")
65
+ INTEGRATION_TIMES = {
66
+ 50: IntegrationTime.INTEGRATION_TIME_50MS,
67
+ 100: IntegrationTime.INTEGRATION_TIME_100MS,
68
+ 200: IntegrationTime.INTEGRATION_TIME_200MS,
69
+ 400: IntegrationTime.INTEGRATION_TIME_400MS,
70
+ }
71
+
72
+ MeasurementRepeatRate = ltr501_ns.enum("MeasurementRepeatRate")
73
+ MEASUREMENT_REPEAT_RATES = {
74
+ 50: MeasurementRepeatRate.REPEAT_RATE_50MS,
75
+ 100: MeasurementRepeatRate.REPEAT_RATE_100MS,
76
+ 200: MeasurementRepeatRate.REPEAT_RATE_200MS,
77
+ 500: MeasurementRepeatRate.REPEAT_RATE_500MS,
78
+ 1000: MeasurementRepeatRate.REPEAT_RATE_1000MS,
79
+ 2000: MeasurementRepeatRate.REPEAT_RATE_2000MS,
80
+ }
81
+
82
+ PsGain = ltr501_ns.enum("PsGain501")
83
+ PS_GAINS = {
84
+ "1X": PsGain.PS_GAIN_1,
85
+ "4X": PsGain.PS_GAIN_4,
86
+ "8X": PsGain.PS_GAIN_8,
87
+ "16X": PsGain.PS_GAIN_16,
88
+ }
89
+
90
+ LTRPsHighTrigger = ltr501_ns.class_("LTRPsHighTrigger", automation.Trigger.template())
91
+ LTRPsLowTrigger = ltr501_ns.class_("LTRPsLowTrigger", automation.Trigger.template())
92
+
93
+
94
+ def validate_integration_time(value):
95
+ value = cv.positive_time_period_milliseconds(value).total_milliseconds
96
+ return cv.enum(INTEGRATION_TIMES, int=True)(value)
97
+
98
+
99
+ def validate_repeat_rate(value):
100
+ value = cv.positive_time_period_milliseconds(value).total_milliseconds
101
+ return cv.enum(MEASUREMENT_REPEAT_RATES, int=True)(value)
102
+
103
+
104
+ def validate_time_and_repeat_rate(config):
105
+ integraton_time = config[CONF_INTEGRATION_TIME]
106
+ repeat_rate = config[CONF_REPEAT]
107
+ if integraton_time > repeat_rate:
108
+ raise cv.Invalid(
109
+ f"Measurement repeat rate ({repeat_rate}ms) shall be greater or equal to integration time ({integraton_time}ms)"
110
+ )
111
+ return config
112
+
113
+
114
+ def validate_als_gain_and_integration_time(config):
115
+ integraton_time = config[CONF_INTEGRATION_TIME]
116
+ if config[CONF_GAIN] == "1X" and integraton_time > 100:
117
+ raise cv.Invalid(
118
+ "ALS gain 1X can only be used with integration time 50ms or 100ms"
119
+ )
120
+ if config[CONF_GAIN] == "200X" and integraton_time == 50:
121
+ raise cv.Invalid("ALS gain 200X can not be used with integration time 50ms")
122
+ return config
123
+
124
+
125
+ CONFIG_SCHEMA = cv.All(
126
+ cv.Schema(
127
+ {
128
+ cv.GenerateID(): cv.declare_id(LTRAlsPsComponent),
129
+ cv.Optional(CONF_TYPE, default="ALS_PS"): cv.enum(LTR_TYPES, upper=True),
130
+ cv.Optional(CONF_AUTO_MODE, default=True): cv.boolean,
131
+ cv.Optional(CONF_GAIN, default="1X"): cv.enum(ALS_GAINS, upper=True),
132
+ cv.Optional(
133
+ CONF_INTEGRATION_TIME, default="100ms"
134
+ ): validate_integration_time,
135
+ cv.Optional(CONF_REPEAT, default="500ms"): validate_repeat_rate,
136
+ cv.Optional(CONF_GLASS_ATTENUATION_FACTOR, default=1.0): cv.float_range(
137
+ min=1.0
138
+ ),
139
+ cv.Optional(
140
+ CONF_PS_COOLDOWN, default="5s"
141
+ ): cv.positive_time_period_seconds,
142
+ cv.Optional(CONF_PS_GAIN, default="1X"): cv.enum(PS_GAINS, upper=True),
143
+ cv.Optional(CONF_PS_HIGH_THRESHOLD, default=65535): cv.int_range(
144
+ min=0, max=65535
145
+ ),
146
+ cv.Optional(CONF_PS_LOW_THRESHOLD, default=0): cv.int_range(
147
+ min=0, max=65535
148
+ ),
149
+ cv.Optional(CONF_ON_PS_HIGH_THRESHOLD): automation.validate_automation(
150
+ {
151
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LTRPsHighTrigger),
152
+ }
153
+ ),
154
+ cv.Optional(CONF_ON_PS_LOW_THRESHOLD): automation.validate_automation(
155
+ {
156
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LTRPsLowTrigger),
157
+ }
158
+ ),
159
+ cv.Optional(CONF_AMBIENT_LIGHT): cv.maybe_simple_value(
160
+ sensor.sensor_schema(
161
+ unit_of_measurement=UNIT_LUX,
162
+ icon=ICON_BRIGHTNESS_6,
163
+ accuracy_decimals=1,
164
+ device_class=DEVICE_CLASS_ILLUMINANCE,
165
+ state_class=STATE_CLASS_MEASUREMENT,
166
+ ),
167
+ key=CONF_NAME,
168
+ ),
169
+ cv.Optional(CONF_INFRARED_COUNTS): cv.maybe_simple_value(
170
+ sensor.sensor_schema(
171
+ unit_of_measurement=UNIT_COUNTS,
172
+ icon=ICON_BRIGHTNESS_5,
173
+ accuracy_decimals=0,
174
+ device_class=DEVICE_CLASS_ILLUMINANCE,
175
+ state_class=STATE_CLASS_MEASUREMENT,
176
+ ),
177
+ key=CONF_NAME,
178
+ ),
179
+ cv.Optional(CONF_FULL_SPECTRUM_COUNTS): cv.maybe_simple_value(
180
+ sensor.sensor_schema(
181
+ unit_of_measurement=UNIT_COUNTS,
182
+ icon=ICON_BRIGHTNESS_7,
183
+ accuracy_decimals=0,
184
+ device_class=DEVICE_CLASS_ILLUMINANCE,
185
+ state_class=STATE_CLASS_MEASUREMENT,
186
+ ),
187
+ key=CONF_NAME,
188
+ ),
189
+ cv.Optional(CONF_PS_COUNTS): cv.maybe_simple_value(
190
+ sensor.sensor_schema(
191
+ unit_of_measurement=UNIT_COUNTS,
192
+ icon=ICON_PROXIMITY,
193
+ accuracy_decimals=0,
194
+ device_class=DEVICE_CLASS_DISTANCE,
195
+ state_class=STATE_CLASS_MEASUREMENT,
196
+ ),
197
+ key=CONF_NAME,
198
+ ),
199
+ cv.Optional(CONF_ACTUAL_GAIN): cv.maybe_simple_value(
200
+ sensor.sensor_schema(
201
+ icon=ICON_GAIN,
202
+ accuracy_decimals=0,
203
+ device_class=DEVICE_CLASS_ILLUMINANCE,
204
+ state_class=STATE_CLASS_MEASUREMENT,
205
+ ),
206
+ key=CONF_NAME,
207
+ ),
208
+ cv.Optional(CONF_ACTUAL_INTEGRATION_TIME): cv.maybe_simple_value(
209
+ sensor.sensor_schema(
210
+ unit_of_measurement=UNIT_MILLISECOND,
211
+ icon=ICON_TIMER,
212
+ accuracy_decimals=0,
213
+ state_class=STATE_CLASS_MEASUREMENT,
214
+ ),
215
+ key=CONF_NAME,
216
+ ),
217
+ }
218
+ )
219
+ .extend(cv.polling_component_schema("60s"))
220
+ .extend(i2c.i2c_device_schema(0x23)),
221
+ validate_time_and_repeat_rate,
222
+ validate_als_gain_and_integration_time,
223
+ )
224
+
225
+
226
+ async def to_code(config):
227
+ var = cg.new_Pvariable(config[CONF_ID])
228
+ await cg.register_component(var, config)
229
+ await i2c.register_i2c_device(var, config)
230
+
231
+ if als_config := config.get(CONF_AMBIENT_LIGHT):
232
+ sens = await sensor.new_sensor(als_config)
233
+ cg.add(var.set_ambient_light_sensor(sens))
234
+
235
+ if infrared_cnt_config := config.get(CONF_INFRARED_COUNTS):
236
+ sens = await sensor.new_sensor(infrared_cnt_config)
237
+ cg.add(var.set_infrared_counts_sensor(sens))
238
+
239
+ if full_spect_cnt_config := config.get(CONF_FULL_SPECTRUM_COUNTS):
240
+ sens = await sensor.new_sensor(full_spect_cnt_config)
241
+ cg.add(var.set_full_spectrum_counts_sensor(sens))
242
+
243
+ if act_gain_config := config.get(CONF_ACTUAL_GAIN):
244
+ sens = await sensor.new_sensor(act_gain_config)
245
+ cg.add(var.set_actual_gain_sensor(sens))
246
+
247
+ if act_itime_config := config.get(CONF_ACTUAL_INTEGRATION_TIME):
248
+ sens = await sensor.new_sensor(act_itime_config)
249
+ cg.add(var.set_actual_integration_time_sensor(sens))
250
+
251
+ if prox_cnt_config := config.get(CONF_PS_COUNTS):
252
+ sens = await sensor.new_sensor(prox_cnt_config)
253
+ cg.add(var.set_proximity_counts_sensor(sens))
254
+
255
+ for prox_high_tr in config.get(CONF_ON_PS_HIGH_THRESHOLD, []):
256
+ trigger = cg.new_Pvariable(prox_high_tr[CONF_TRIGGER_ID], var)
257
+ await automation.build_automation(trigger, [], prox_high_tr)
258
+
259
+ for prox_low_tr in config.get(CONF_ON_PS_LOW_THRESHOLD, []):
260
+ trigger = cg.new_Pvariable(prox_low_tr[CONF_TRIGGER_ID], var)
261
+ await automation.build_automation(trigger, [], prox_low_tr)
262
+
263
+ cg.add(var.set_ltr_type(config[CONF_TYPE]))
264
+
265
+ cg.add(var.set_als_auto_mode(config[CONF_AUTO_MODE]))
266
+ cg.add(var.set_als_gain(config[CONF_GAIN]))
267
+ cg.add(var.set_als_integration_time(config[CONF_INTEGRATION_TIME]))
268
+ cg.add(var.set_als_meas_repeat_rate(config[CONF_REPEAT]))
269
+ cg.add(var.set_als_glass_attenuation_factor(config[CONF_GLASS_ATTENUATION_FACTOR]))
270
+
271
+ cg.add(var.set_ps_cooldown_time_s(config[CONF_PS_COOLDOWN]))
272
+ cg.add(var.set_ps_gain(config[CONF_PS_GAIN]))
273
+ cg.add(var.set_ps_high_threshold(config[CONF_PS_HIGH_THRESHOLD]))
274
+ cg.add(var.set_ps_low_threshold(config[CONF_PS_LOW_THRESHOLD]))
@@ -4,8 +4,10 @@ from esphome import automation
4
4
  from esphome.components import i2c, sensor
5
5
  from esphome.const import (
6
6
  CONF_ACTUAL_GAIN,
7
+ CONF_ACTUAL_INTEGRATION_TIME,
7
8
  CONF_AMBIENT_LIGHT,
8
9
  CONF_AUTO_MODE,
10
+ CONF_FULL_SPECTRUM_COUNTS,
9
11
  CONF_GAIN,
10
12
  CONF_GLASS_ATTENUATION_FACTOR,
11
13
  CONF_ID,
@@ -27,8 +29,6 @@ from esphome.const import (
27
29
  CODEOWNERS = ["@latonita"]
28
30
  DEPENDENCIES = ["i2c"]
29
31
 
30
- CONF_ACTUAL_INTEGRATION_TIME = "actual_integration_time"
31
- CONF_FULL_SPECTRUM_COUNTS = "full_spectrum_counts"
32
32
  CONF_INFRARED_COUNTS = "infrared_counts"
33
33
  CONF_ON_PS_HIGH_THRESHOLD = "on_ps_high_threshold"
34
34
  CONF_ON_PS_LOW_THRESHOLD = "on_ps_low_threshold"
@@ -21,9 +21,10 @@ from esphome.final_validate import full_config
21
21
  from esphome.helpers import write_file_if_changed
22
22
 
23
23
  from . import defines as df, helpers, lv_validation as lvalid
24
- from .automation import disp_update, update_to_code
25
- from .defines import CONF_SKIP
24
+ from .automation import disp_update, focused_widgets, update_to_code
25
+ from .defines import add_define
26
26
  from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code
27
+ from .gradient import GRADIENT_SCHEMA, gradients_to_code
27
28
  from .lv_validation import lv_bool, lv_images_used
28
29
  from .lvcode import LvContext, LvglComponent
29
30
  from .schemas import (
@@ -67,7 +68,7 @@ from .widgets.lv_bar import bar_spec
67
68
  from .widgets.meter import meter_spec
68
69
  from .widgets.msgbox import MSGBOX_SCHEMA, msgboxes_to_code
69
70
  from .widgets.obj import obj_spec
70
- from .widgets.page import add_pages, page_spec
71
+ from .widgets.page import add_pages, generate_page_triggers, page_spec
71
72
  from .widgets.roller import roller_spec
72
73
  from .widgets.slider import slider_spec
73
74
  from .widgets.spinbox import spinbox_spec
@@ -128,17 +129,6 @@ for w_type in WIDGET_TYPES.values():
128
129
  )(update_to_code)
129
130
 
130
131
 
131
- lv_defines = {} # Dict of #defines to provide as build flags
132
-
133
-
134
- def add_define(macro, value="1"):
135
- if macro in lv_defines and lv_defines[macro] != value:
136
- LOGGER.error(
137
- "Redefinition of %s - was %s now %s", macro, lv_defines[macro], value
138
- )
139
- lv_defines[macro] = value
140
-
141
-
142
132
  def as_macro(macro, value):
143
133
  if value is None:
144
134
  return f"#define {macro}"
@@ -153,14 +143,14 @@ LV_CONF_H_FORMAT = """\
153
143
 
154
144
 
155
145
  def generate_lv_conf_h():
156
- definitions = [as_macro(m, v) for m, v in lv_defines.items()]
146
+ definitions = [as_macro(m, v) for m, v in df.lv_defines.items()]
157
147
  definitions.sort()
158
148
  return LV_CONF_H_FORMAT.format("\n".join(definitions))
159
149
 
160
150
 
161
151
  def final_validation(config):
162
152
  if pages := config.get(CONF_PAGES):
163
- if all(p[CONF_SKIP] for p in pages):
153
+ if all(p[df.CONF_SKIP] for p in pages):
164
154
  raise cv.Invalid("At least one page must not be skipped")
165
155
  global_config = full_config.get()
166
156
  for display_id in config[df.CONF_DISPLAYS]:
@@ -182,6 +172,14 @@ def final_validation(config):
182
172
  raise cv.Invalid(
183
173
  "Using RGBA or RGB24 in image config not compatible with LVGL", path
184
174
  )
175
+ for w in focused_widgets:
176
+ path = global_config.get_path_for_id(w)
177
+ widget_conf = global_config.get_config_for_path(path[:-1])
178
+ if df.CONF_ADJUSTABLE in widget_conf and not widget_conf[df.CONF_ADJUSTABLE]:
179
+ raise cv.Invalid(
180
+ "A non adjustable arc may not be focused",
181
+ path,
182
+ )
185
183
 
186
184
 
187
185
  async def to_code(config):
@@ -260,6 +258,7 @@ async def to_code(config):
260
258
  await encoders_to_code(lv_component, config)
261
259
  await theme_to_code(config)
262
260
  await styles_to_code(config)
261
+ await gradients_to_code(config)
263
262
  await set_obj_properties(lv_scr_act, config)
264
263
  await add_widgets(lv_scr_act, config)
265
264
  await add_pages(lv_component, config)
@@ -271,6 +270,7 @@ async def to_code(config):
271
270
  Widget.set_completed()
272
271
  async with LvContext(lv_component):
273
272
  await generate_triggers(lv_component)
273
+ await generate_page_triggers(lv_component, config)
274
274
  for conf in config.get(CONF_ON_IDLE, ()):
275
275
  templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)
276
276
  idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ)
@@ -318,6 +318,8 @@ CONFIG_SCHEMA = (
318
318
  {
319
319
  cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments,
320
320
  cv.Optional(df.CONF_GRID_CELL_Y_ALIGN): grid_alignments,
321
+ cv.Optional(df.CONF_PAD_ROW): lvalid.pixels,
322
+ cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels,
321
323
  }
322
324
  )
323
325
  ),
@@ -340,6 +342,7 @@ CONFIG_SCHEMA = (
340
342
  cv.Optional(df.CONF_THEME): cv.Schema(
341
343
  {cv.Optional(name): obj_schema(w) for name, w in WIDGET_TYPES.items()}
342
344
  ),
345
+ cv.Optional(df.CONF_GRADIENTS): GRADIENT_SCHEMA,
343
346
  cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema,
344
347
  cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG,
345
348
  cv.GenerateID(df.CONF_DEFAULT_GROUP): cv.declare_id(lv_group_t),
@@ -4,13 +4,15 @@ from typing import Callable
4
4
  from esphome import automation
5
5
  import esphome.codegen as cg
6
6
  import esphome.config_validation as cv
7
- from esphome.const import CONF_ID, CONF_TIMEOUT
8
- from esphome.cpp_generator import RawExpression
7
+ from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT
8
+ from esphome.cpp_generator import RawExpression, get_variable
9
9
  from esphome.cpp_types import nullptr
10
10
 
11
11
  from .defines import (
12
12
  CONF_DISP_BG_COLOR,
13
13
  CONF_DISP_BG_IMAGE,
14
+ CONF_EDITING,
15
+ CONF_FREEZE,
14
16
  CONF_LVGL_ID,
15
17
  CONF_SHOW_SNOW,
16
18
  literal,
@@ -30,6 +32,7 @@ from .lvcode import (
30
32
  lv_expr,
31
33
  lv_obj,
32
34
  lvgl_comp,
35
+ static_cast,
33
36
  )
34
37
  from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA
35
38
  from .types import (
@@ -38,7 +41,9 @@ from .types import (
38
41
  LvglCondition,
39
42
  ObjUpdateAction,
40
43
  lv_disp_t,
44
+ lv_group_t,
41
45
  lv_obj_t,
46
+ lv_pseudo_button_t,
42
47
  )
43
48
  from .widgets import (
44
49
  Widget,
@@ -48,6 +53,9 @@ from .widgets import (
48
53
  wait_for_widgets,
49
54
  )
50
55
 
56
+ # Record widgets that are used in a focused action here
57
+ focused_widgets = set()
58
+
51
59
 
52
60
  async def action_to_code(
53
61
  widgets: list[Widget],
@@ -157,7 +165,7 @@ async def lvgl_update_to_code(config, action_id, template_arg, args):
157
165
  widgets = await get_widgets(config)
158
166
  w = widgets[0]
159
167
  disp = f"{w.obj}->get_disp()"
160
- async with LambdaContext(parameters=args, where=action_id) as context:
168
+ async with LambdaContext(LVGL_COMP_ARG, where=action_id) as context:
161
169
  await disp_update(disp, config)
162
170
  var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
163
171
  await cg.register_parented(var, w.var)
@@ -221,16 +229,89 @@ async def obj_hide_to_code(config, action_id, template_arg, args):
221
229
  async def do_hide(widget: Widget):
222
230
  widget.add_flag("LV_OBJ_FLAG_HIDDEN")
223
231
 
224
- return await action_to_code(
225
- await get_widgets(config), do_hide, action_id, template_arg, args
226
- )
232
+ widgets = [
233
+ widget.outer if widget.outer else widget for widget in await get_widgets(config)
234
+ ]
235
+ return await action_to_code(widgets, do_hide, action_id, template_arg, args)
227
236
 
228
237
 
229
238
  @automation.register_action("lvgl.widget.show", ObjUpdateAction, LIST_ACTION_SCHEMA)
230
239
  async def obj_show_to_code(config, action_id, template_arg, args):
231
240
  async def do_show(widget: Widget):
232
241
  widget.clear_flag("LV_OBJ_FLAG_HIDDEN")
242
+ if widget.move_to_foreground:
243
+ lv_obj.move_foreground(widget.obj)
233
244
 
234
- return await action_to_code(
235
- await get_widgets(config), do_show, action_id, template_arg, args
236
- )
245
+ widgets = [
246
+ widget.outer if widget.outer else widget for widget in await get_widgets(config)
247
+ ]
248
+ return await action_to_code(widgets, do_show, action_id, template_arg, args)
249
+
250
+
251
+ def focused_id(value):
252
+ value = cv.use_id(lv_pseudo_button_t)(value)
253
+ focused_widgets.add(value)
254
+ return value
255
+
256
+
257
+ @automation.register_action(
258
+ "lvgl.widget.focus",
259
+ ObjUpdateAction,
260
+ cv.Any(
261
+ cv.maybe_simple_value(
262
+ {
263
+ cv.Optional(CONF_GROUP): cv.use_id(lv_group_t),
264
+ cv.Required(CONF_ACTION): cv.one_of(
265
+ "MARK", "RESTORE", "NEXT", "PREVIOUS", upper=True
266
+ ),
267
+ cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent),
268
+ cv.Optional(CONF_FREEZE, default=False): cv.boolean,
269
+ },
270
+ key=CONF_ACTION,
271
+ ),
272
+ cv.maybe_simple_value(
273
+ {
274
+ cv.Required(CONF_ID): focused_id,
275
+ cv.Optional(CONF_FREEZE, default=False): cv.boolean,
276
+ cv.Optional(CONF_EDITING, default=False): cv.boolean,
277
+ },
278
+ key=CONF_ID,
279
+ ),
280
+ ),
281
+ )
282
+ async def widget_focus(config, action_id, template_arg, args):
283
+ widget = await get_widgets(config)
284
+ if widget:
285
+ widget = widget[0]
286
+ group = static_cast(
287
+ lv_group_t.operator("ptr"), lv_expr.obj_get_group(widget.obj)
288
+ )
289
+ elif group := config.get(CONF_GROUP):
290
+ group = await get_variable(group)
291
+ else:
292
+ group = lv_expr.group_get_default()
293
+
294
+ async with LambdaContext(parameters=args, where=action_id) as context:
295
+ if widget:
296
+ lv.group_focus_freeze(group, False)
297
+ lv.group_focus_obj(widget.obj)
298
+ if config[CONF_EDITING]:
299
+ lv.group_set_editing(group, True)
300
+ else:
301
+ action = config[CONF_ACTION]
302
+ lv_comp = await get_variable(config[CONF_LVGL_ID])
303
+ if action == "MARK":
304
+ context.add(lv_comp.set_focus_mark(group))
305
+ else:
306
+ lv.group_focus_freeze(group, False)
307
+ if action == "RESTORE":
308
+ context.add(lv_comp.restore_focus_mark(group))
309
+ elif action == "NEXT":
310
+ lv.group_focus_next(group)
311
+ else:
312
+ lv.group_focus_prev(group)
313
+
314
+ if config[CONF_FREEZE]:
315
+ lv.group_focus_freeze(group, True)
316
+ var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
317
+ return var
@@ -4,6 +4,8 @@ Constants already defined in esphome.const are not duplicated here and must be i
4
4
 
5
5
  """
6
6
 
7
+ import logging
8
+
7
9
  from esphome import codegen as cg, config_validation as cv
8
10
  from esphome.const import CONF_ITEMS
9
11
  from esphome.core import Lambda
@@ -13,8 +15,19 @@ from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
13
15
 
14
16
  from .helpers import requires_component
15
17
 
18
+ LOGGER = logging.getLogger(__name__)
16
19
  lvgl_ns = cg.esphome_ns.namespace("lvgl")
17
20
 
21
+ lv_defines = {} # Dict of #defines to provide as build flags
22
+
23
+
24
+ def add_define(macro, value="1"):
25
+ if macro in lv_defines and lv_defines[macro] != value:
26
+ LOGGER.error(
27
+ "Redefinition of %s - was %s now %s", macro, lv_defines[macro], value
28
+ )
29
+ lv_defines[macro] = value
30
+
18
31
 
19
32
  def literal(arg):
20
33
  if isinstance(arg, str):
@@ -148,6 +161,7 @@ LV_EVENT_MAP = {
148
161
  "DEFOCUS": "DEFOCUSED",
149
162
  "READY": "READY",
150
163
  "CANCEL": "CANCEL",
164
+ "ALL_EVENTS": "ALL",
151
165
  }
152
166
 
153
167
  LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT_MAP)
@@ -172,6 +186,9 @@ LV_ANIM = LvConstant(
172
186
  "OUT_BOTTOM",
173
187
  )
174
188
 
189
+ LV_GRAD_DIR = LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER")
190
+ LV_DITHER = LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF")
191
+
175
192
  LOG_LEVELS = (
176
193
  "TRACE",
177
194
  "INFO",
@@ -373,6 +390,7 @@ CONF_ANTIALIAS = "antialias"
373
390
  CONF_ARC_LENGTH = "arc_length"
374
391
  CONF_AUTO_START = "auto_start"
375
392
  CONF_BACKGROUND_STYLE = "background_style"
393
+ CONF_BUTTON_STYLE = "button_style"
376
394
  CONF_DECIMAL_PLACES = "decimal_places"
377
395
  CONF_COLUMN = "column"
378
396
  CONF_DIGITS = "digits"
@@ -390,6 +408,7 @@ CONF_DEFAULT_FONT = "default_font"
390
408
  CONF_DEFAULT_GROUP = "default_group"
391
409
  CONF_DIR = "dir"
392
410
  CONF_DISPLAYS = "displays"
411
+ CONF_EDITING = "editing"
393
412
  CONF_ENCODERS = "encoders"
394
413
  CONF_END_ANGLE = "end_angle"
395
414
  CONF_END_VALUE = "end_value"
@@ -401,7 +420,9 @@ CONF_FLEX_ALIGN_MAIN = "flex_align_main"
401
420
  CONF_FLEX_ALIGN_CROSS = "flex_align_cross"
402
421
  CONF_FLEX_ALIGN_TRACK = "flex_align_track"
403
422
  CONF_FLEX_GROW = "flex_grow"
423
+ CONF_FREEZE = "freeze"
404
424
  CONF_FULL_REFRESH = "full_refresh"
425
+ CONF_GRADIENTS = "gradients"
405
426
  CONF_GRID_CELL_ROW_POS = "grid_cell_row_pos"
406
427
  CONF_GRID_CELL_COLUMN_POS = "grid_cell_column_pos"
407
428
  CONF_GRID_CELL_ROW_SPAN = "grid_cell_row_span"
@@ -428,9 +449,9 @@ CONF_MSGBOXES = "msgboxes"
428
449
  CONF_OBJ = "obj"
429
450
  CONF_OFFSET_X = "offset_x"
430
451
  CONF_OFFSET_Y = "offset_y"
452
+ CONF_ONE_CHECKED = "one_checked"
431
453
  CONF_ONE_LINE = "one_line"
432
454
  CONF_ON_SELECT = "on_select"
433
- CONF_ONE_CHECKED = "one_checked"
434
455
  CONF_NEXT = "next"
435
456
  CONF_PAD_ROW = "pad_row"
436
457
  CONF_PAD_COLUMN = "pad_column"
@@ -505,4 +526,10 @@ DEFAULT_ESPHOME_FONT = "esphome_lv_default_font"
505
526
 
506
527
 
507
528
  def join_enums(enums, prefix=""):
508
- return literal("|".join(f"(int){prefix}{e.upper()}" for e in enums))
529
+ enums = list(enums)
530
+ enums.sort()
531
+ # If a prefix is provided, prepend each constant with the prefix, and assume that all the constants are within the
532
+ # same namespace, otherwise cast to int to avoid triggering warnings about mixing enum types.
533
+ if prefix:
534
+ return literal("|".join(f"{prefix}{e.upper()}" for e in enums))
535
+ return literal("|".join(f"(int){e.upper()}" for e in enums))