esphome 2024.9.2__py3-none-any.whl → 2024.10.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 (188) hide show
  1. esphome/components/aic3204/__init__.py +0 -0
  2. esphome/components/aic3204/aic3204.cpp +173 -0
  3. esphome/components/aic3204/aic3204.h +88 -0
  4. esphome/components/aic3204/audio_dac.py +52 -0
  5. esphome/components/aic3204/automation.h +23 -0
  6. esphome/components/alarm_control_panel/__init__.py +3 -4
  7. esphome/components/animation/__init__.py +16 -12
  8. esphome/components/api/api_connection.cpp +2 -0
  9. esphome/components/api/api_connection.h +3 -1
  10. esphome/components/api/api_frame_helper.cpp +2 -1
  11. esphome/components/api/api_frame_helper.h +2 -1
  12. esphome/components/api/api_server.cpp +2 -0
  13. esphome/components/api/api_server.h +3 -1
  14. esphome/components/api/custom_api_device.h +3 -2
  15. esphome/components/api/homeassistant_service.h +4 -3
  16. esphome/components/api/list_entities.cpp +2 -0
  17. esphome/components/api/list_entities.h +3 -2
  18. esphome/components/api/subscribe_state.cpp +2 -0
  19. esphome/components/api/subscribe_state.h +3 -2
  20. esphome/components/audio_dac/__init__.py +57 -0
  21. esphome/components/audio_dac/audio_dac.h +23 -0
  22. esphome/components/audio_dac/automation.h +43 -0
  23. esphome/components/bang_bang/bang_bang_climate.cpp +5 -2
  24. esphome/components/bedjet/bedjet_codec.cpp +4 -2
  25. esphome/components/binary_sensor/__init__.py +3 -4
  26. esphome/components/button/__init__.py +3 -4
  27. esphome/components/ch422g/__init__.py +26 -17
  28. esphome/components/ch422g/ch422g.cpp +66 -49
  29. esphome/components/ch422g/ch422g.h +17 -19
  30. esphome/components/climate/__init__.py +3 -4
  31. esphome/components/cover/__init__.py +4 -5
  32. esphome/components/cse7766/cse7766.cpp +11 -0
  33. esphome/components/cse7766/cse7766.h +4 -0
  34. esphome/components/cse7766/sensor.py +13 -1
  35. esphome/components/cst816/touchscreen/__init__.py +7 -4
  36. esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +20 -19
  37. esphome/components/cst816/touchscreen/cst816_touchscreen.h +2 -0
  38. esphome/components/datetime/__init__.py +21 -14
  39. esphome/components/datetime/datetime_base.h +8 -1
  40. esphome/components/datetime/datetime_entity.cpp +2 -0
  41. esphome/components/datetime/datetime_entity.h +2 -0
  42. esphome/components/datetime/time_entity.cpp +2 -0
  43. esphome/components/datetime/time_entity.h +2 -0
  44. esphome/components/esp32/__init__.py +20 -4
  45. esphome/components/esp32_improv/__init__.py +82 -1
  46. esphome/components/esp32_improv/automation.h +72 -0
  47. esphome/components/esp32_improv/esp32_improv_component.cpp +13 -5
  48. esphome/components/esp32_improv/esp32_improv_component.h +15 -0
  49. esphome/components/ethernet/__init__.py +5 -0
  50. esphome/components/ethernet/ethernet_component.cpp +13 -0
  51. esphome/components/ethernet/ethernet_component.h +1 -0
  52. esphome/components/fan/__init__.py +3 -4
  53. esphome/components/gp2y1010au0f/__init__.py +0 -0
  54. esphome/components/gp2y1010au0f/gp2y1010au0f.cpp +67 -0
  55. esphome/components/gp2y1010au0f/gp2y1010au0f.h +52 -0
  56. esphome/components/gp2y1010au0f/sensor.py +61 -0
  57. esphome/components/gpio_expander/__init__.py +0 -0
  58. esphome/components/gpio_expander/cached_gpio.h +38 -0
  59. esphome/components/grove_gas_mc_v2/__init__.py +0 -0
  60. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +88 -0
  61. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +39 -0
  62. esphome/components/grove_gas_mc_v2/sensor.py +77 -0
  63. esphome/components/haier/climate.py +4 -3
  64. esphome/components/haier/haier_base.cpp +63 -8
  65. esphome/components/haier/haier_base.h +29 -3
  66. esphome/components/haier/hon_climate.cpp +122 -65
  67. esphome/components/haier/hon_climate.h +18 -2
  68. esphome/components/haier/smartair2_climate.cpp +21 -21
  69. esphome/components/haier/switch/__init__.py +91 -0
  70. esphome/components/haier/switch/beeper.cpp +14 -0
  71. esphome/components/haier/switch/beeper.h +18 -0
  72. esphome/components/haier/switch/display.cpp +14 -0
  73. esphome/components/haier/switch/display.h +18 -0
  74. esphome/components/haier/switch/health_mode.cpp +14 -0
  75. esphome/components/haier/switch/health_mode.h +18 -0
  76. esphome/components/haier/switch/quiet_mode.cpp +14 -0
  77. esphome/components/haier/switch/quiet_mode.h +18 -0
  78. esphome/components/hmac_md5/hmac_md5.cpp +2 -0
  79. esphome/components/hmac_md5/hmac_md5.h +2 -1
  80. esphome/components/i2s_audio/speaker/__init__.py +19 -0
  81. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  82. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +2 -0
  83. esphome/components/ili9xxx/ili9xxx_display.h +1 -0
  84. esphome/components/image/__init__.py +12 -12
  85. esphome/components/image/image.cpp +44 -0
  86. esphome/components/image/image.h +17 -2
  87. esphome/components/inkplate6/display.py +2 -0
  88. esphome/components/inkplate6/inkplate.h +30 -2
  89. esphome/components/light/__init__.py +3 -4
  90. esphome/components/lock/__init__.py +3 -4
  91. esphome/components/lvgl/__init__.py +16 -5
  92. esphome/components/lvgl/defines.py +1 -0
  93. esphome/components/lvgl/hello_world.py +64 -0
  94. esphome/components/lvgl/lv_validation.py +159 -3
  95. esphome/components/lvgl/lvgl_esphome.cpp +0 -43
  96. esphome/components/lvgl/lvgl_esphome.h +0 -4
  97. esphome/components/lvgl/styles.py +3 -2
  98. esphome/components/lvgl/text/__init__.py +3 -3
  99. esphome/components/lvgl/widgets/__init__.py +2 -0
  100. esphome/components/lvgl/widgets/animimg.py +3 -4
  101. esphome/components/lvgl/widgets/dropdown.py +5 -1
  102. esphome/components/lvgl/widgets/meter.py +16 -11
  103. esphome/components/md5/__init__.py +6 -0
  104. esphome/components/md5/md5.cpp +2 -0
  105. esphome/components/md5/md5.h +2 -0
  106. esphome/components/micro_wake_word/__init__.py +7 -0
  107. esphome/components/mics_4514/sensor.py +11 -26
  108. esphome/components/modbus_controller/__init__.py +7 -5
  109. esphome/components/modbus_controller/binary_sensor/__init__.py +6 -6
  110. esphome/components/modbus_controller/number/__init__.py +5 -6
  111. esphome/components/modbus_controller/output/__init__.py +10 -14
  112. esphome/components/modbus_controller/select/__init__.py +1 -1
  113. esphome/components/modbus_controller/sensor/__init__.py +7 -7
  114. esphome/components/modbus_controller/switch/__init__.py +6 -7
  115. esphome/components/modbus_controller/text_sensor/__init__.py +8 -9
  116. esphome/components/mqtt/__init__.py +3 -0
  117. esphome/components/mqtt/mqtt_client.cpp +2 -0
  118. esphome/components/mqtt/mqtt_client.h +2 -0
  119. esphome/components/nau7802/__init__.py +0 -0
  120. esphome/components/nau7802/nau7802.cpp +323 -0
  121. esphome/components/nau7802/nau7802.h +121 -0
  122. esphome/components/nau7802/sensor.py +134 -0
  123. esphome/components/nextion/base_component.py +1 -0
  124. esphome/components/nextion/display.py +4 -0
  125. esphome/components/nextion/nextion.cpp +19 -4
  126. esphome/components/nextion/nextion.h +16 -0
  127. esphome/components/npi19/__init__.py +0 -0
  128. esphome/components/npi19/npi19.cpp +111 -0
  129. esphome/components/npi19/npi19.h +30 -0
  130. esphome/components/npi19/sensor.py +52 -0
  131. esphome/components/number/__init__.py +3 -5
  132. esphome/components/online_image/__init__.py +1 -1
  133. esphome/components/online_image/online_image.h +1 -2
  134. esphome/components/opentherm/__init__.py +57 -0
  135. esphome/components/opentherm/hub.cpp +277 -0
  136. esphome/components/opentherm/hub.h +110 -0
  137. esphome/components/opentherm/opentherm.cpp +568 -0
  138. esphome/components/opentherm/opentherm.h +347 -0
  139. esphome/components/pulse_counter/pulse_counter_sensor.cpp +8 -1
  140. esphome/components/pulse_counter/pulse_counter_sensor.h +1 -0
  141. esphome/components/radon_eye_ble/radon_eye_listener.cpp +10 -3
  142. esphome/components/remote_transmitter/__init__.py +18 -2
  143. esphome/components/remote_transmitter/remote_transmitter.h +6 -0
  144. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +2 -0
  145. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
  146. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
  147. esphome/components/select/__init__.py +3 -4
  148. esphome/components/sensor/__init__.py +3 -4
  149. esphome/components/st7701s/st7701s.cpp +21 -8
  150. esphome/components/st7701s/st7701s.h +2 -0
  151. esphome/components/switch/__init__.py +3 -4
  152. esphome/components/tca9555/__init__.py +72 -0
  153. esphome/components/tca9555/tca9555.cpp +140 -0
  154. esphome/components/tca9555/tca9555.h +64 -0
  155. esphome/components/tcs34725/tcs34725.cpp +62 -64
  156. esphome/components/tem3200/__init__.py +0 -0
  157. esphome/components/tem3200/sensor.py +55 -0
  158. esphome/components/tem3200/tem3200.cpp +151 -0
  159. esphome/components/tem3200/tem3200.h +30 -0
  160. esphome/components/template/binary_sensor/__init__.py +19 -6
  161. esphome/components/text/__init__.py +3 -4
  162. esphome/components/text_sensor/__init__.py +3 -4
  163. esphome/components/thermostat/climate.py +11 -9
  164. esphome/components/thermostat/thermostat_climate.cpp +21 -15
  165. esphome/components/tm1638/binary_sensor/__init__.py +3 -2
  166. esphome/components/tm1638/display.py +5 -5
  167. esphome/components/tm1638/output/__init__.py +3 -2
  168. esphome/components/tm1638/switch/__init__.py +3 -2
  169. esphome/components/update/__init__.py +3 -4
  170. esphome/components/valve/__init__.py +3 -4
  171. esphome/components/web_server/__init__.py +78 -22
  172. esphome/components/web_server/server_index_v3.h +3989 -3979
  173. esphome/components/web_server/web_server.cpp +212 -33
  174. esphome/components/web_server/web_server.h +10 -1
  175. esphome/components/wifi/wifi_component_esp_idf.cpp +4 -5
  176. esphome/config_validation.py +1 -0
  177. esphome/const.py +12 -2
  178. esphome/core/defines.h +4 -2
  179. esphome/core/helpers.cpp +46 -10
  180. esphome/core/helpers.h +8 -0
  181. esphome/core/ring_buffer.cpp +12 -2
  182. esphome/core/ring_buffer.h +3 -0
  183. {esphome-2024.9.2.dist-info → esphome-2024.10.0b1.dist-info}/METADATA +5 -3
  184. {esphome-2024.9.2.dist-info → esphome-2024.10.0b1.dist-info}/RECORD +188 -139
  185. {esphome-2024.9.2.dist-info → esphome-2024.10.0b1.dist-info}/LICENSE +0 -0
  186. {esphome-2024.9.2.dist-info → esphome-2024.10.0b1.dist-info}/WHEEL +0 -0
  187. {esphome-2024.9.2.dist-info → esphome-2024.10.0b1.dist-info}/entry_points.txt +0 -0
  188. {esphome-2024.9.2.dist-info → esphome-2024.10.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,30 @@
1
+ #pragma once
2
+
3
+ #include "esphome/core/component.h"
4
+ #include "esphome/components/sensor/sensor.h"
5
+ #include "esphome/components/i2c/i2c.h"
6
+
7
+ namespace esphome {
8
+ namespace tem3200 {
9
+
10
+ /// This class implements support for the tem3200 pressure and temperature i2c sensors.
11
+ class TEM3200Component : public PollingComponent, public i2c::I2CDevice {
12
+ public:
13
+ void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
14
+ void set_raw_pressure_sensor(sensor::Sensor *raw_pressure_sensor) {
15
+ this->raw_pressure_sensor_ = raw_pressure_sensor;
16
+ }
17
+
18
+ float get_setup_priority() const override;
19
+ void setup() override;
20
+ void dump_config() override;
21
+ void update() override;
22
+
23
+ protected:
24
+ i2c::ErrorCode read_(uint8_t &status, uint16_t &raw_temperature, uint16_t &raw_pressure);
25
+ sensor::Sensor *temperature_sensor_{nullptr};
26
+ sensor::Sensor *raw_pressure_sensor_{nullptr};
27
+ };
28
+
29
+ } // namespace tem3200
30
+ } // namespace esphome
@@ -1,8 +1,10 @@
1
- import esphome.codegen as cg
2
- import esphome.config_validation as cv
3
1
  from esphome import automation
2
+ import esphome.codegen as cg
4
3
  from esphome.components import binary_sensor
5
- from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE
4
+ import esphome.config_validation as cv
5
+ from esphome.const import CONF_CONDITION, CONF_ID, CONF_LAMBDA, CONF_STATE
6
+ from esphome.cpp_generator import LambdaExpression
7
+
6
8
  from .. import template_ns
7
9
 
8
10
  TemplateBinarySensor = template_ns.class_(
@@ -13,7 +15,10 @@ CONFIG_SCHEMA = (
13
15
  binary_sensor.binary_sensor_schema(TemplateBinarySensor)
14
16
  .extend(
15
17
  {
16
- cv.Optional(CONF_LAMBDA): cv.returning_lambda,
18
+ cv.Exclusive(CONF_LAMBDA, CONF_CONDITION): cv.returning_lambda,
19
+ cv.Exclusive(
20
+ CONF_CONDITION, CONF_CONDITION
21
+ ): automation.validate_potentially_and_condition,
17
22
  }
18
23
  )
19
24
  .extend(cv.COMPONENT_SCHEMA)
@@ -24,9 +29,17 @@ async def to_code(config):
24
29
  var = await binary_sensor.new_binary_sensor(config)
25
30
  await cg.register_component(var, config)
26
31
 
27
- if CONF_LAMBDA in config:
32
+ if lamb := config.get(CONF_LAMBDA):
28
33
  template_ = await cg.process_lambda(
29
- config[CONF_LAMBDA], [], return_type=cg.optional.template(bool)
34
+ lamb, [], return_type=cg.optional.template(bool)
35
+ )
36
+ cg.add(var.set_template(template_))
37
+ if condition := config.get(CONF_CONDITION):
38
+ condition = await automation.build_condition(
39
+ condition, cg.TemplateArguments(), []
40
+ )
41
+ template_ = LambdaExpression(
42
+ f"return {condition.check()};", [], return_type=cg.optional.template(bool)
30
43
  )
31
44
  cg.add(var.set_template(template_))
32
45
 
@@ -11,7 +11,7 @@ from esphome.const import (
11
11
  CONF_ON_VALUE,
12
12
  CONF_TRIGGER_ID,
13
13
  CONF_VALUE,
14
- CONF_WEB_SERVER_ID,
14
+ CONF_WEB_SERVER,
15
15
  )
16
16
  from esphome.core import CORE, coroutine_with_priority
17
17
  from esphome.cpp_helpers import setup_entity
@@ -82,9 +82,8 @@ async def setup_text_core_(
82
82
  mqtt_ = cg.new_Pvariable(mqtt_id, var)
83
83
  await mqtt.register_mqtt_component(mqtt_, config)
84
84
 
85
- if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
86
- web_server_ = await cg.get_variable(webserver_id)
87
- web_server.add_entity_to_sorting_list(web_server_, var, config)
85
+ if web_server_config := config.get(CONF_WEB_SERVER):
86
+ await web_server.add_entity_config(var, web_server_config)
88
87
 
89
88
 
90
89
  async def register_text(
@@ -15,7 +15,7 @@ from esphome.const import (
15
15
  CONF_STATE,
16
16
  CONF_TO,
17
17
  CONF_TRIGGER_ID,
18
- CONF_WEB_SERVER_ID,
18
+ CONF_WEB_SERVER,
19
19
  DEVICE_CLASS_DATE,
20
20
  DEVICE_CLASS_EMPTY,
21
21
  DEVICE_CLASS_TIMESTAMP,
@@ -212,9 +212,8 @@ async def setup_text_sensor_core_(var, config):
212
212
  mqtt_ = cg.new_Pvariable(mqtt_id, var)
213
213
  await mqtt.register_mqtt_component(mqtt_, config)
214
214
 
215
- if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
216
- web_server_ = await cg.get_variable(webserver_id)
217
- web_server.add_entity_to_sorting_list(web_server_, var, config)
215
+ if web_server_config := config.get(CONF_WEB_SERVER):
216
+ await web_server.add_entity_config(var, web_server_config)
218
217
 
219
218
 
220
219
  async def register_text_sensor(var, config):
@@ -1,7 +1,7 @@
1
- import esphome.codegen as cg
2
- import esphome.config_validation as cv
3
1
  from esphome import automation
2
+ import esphome.codegen as cg
4
3
  from esphome.components import climate, sensor
4
+ import esphome.config_validation as cv
5
5
  from esphome.const import (
6
6
  CONF_AUTO_MODE,
7
7
  CONF_AWAY_CONFIG,
@@ -15,15 +15,15 @@ from esphome.const import (
15
15
  CONF_DRY_ACTION,
16
16
  CONF_DRY_MODE,
17
17
  CONF_FAN_MODE,
18
- CONF_FAN_MODE_ON_ACTION,
19
- CONF_FAN_MODE_OFF_ACTION,
20
18
  CONF_FAN_MODE_AUTO_ACTION,
19
+ CONF_FAN_MODE_DIFFUSE_ACTION,
20
+ CONF_FAN_MODE_FOCUS_ACTION,
21
+ CONF_FAN_MODE_HIGH_ACTION,
21
22
  CONF_FAN_MODE_LOW_ACTION,
22
23
  CONF_FAN_MODE_MEDIUM_ACTION,
23
- CONF_FAN_MODE_HIGH_ACTION,
24
24
  CONF_FAN_MODE_MIDDLE_ACTION,
25
- CONF_FAN_MODE_FOCUS_ACTION,
26
- CONF_FAN_MODE_DIFFUSE_ACTION,
25
+ CONF_FAN_MODE_OFF_ACTION,
26
+ CONF_FAN_MODE_ON_ACTION,
27
27
  CONF_FAN_MODE_QUIET_ACTION,
28
28
  CONF_FAN_ONLY_ACTION,
29
29
  CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER,
@@ -50,8 +50,8 @@ from esphome.const import (
50
50
  CONF_MIN_HEATING_RUN_TIME,
51
51
  CONF_MIN_IDLE_TIME,
52
52
  CONF_MIN_TEMPERATURE,
53
- CONF_NAME,
54
53
  CONF_MODE,
54
+ CONF_NAME,
55
55
  CONF_OFF_MODE,
56
56
  CONF_PRESET,
57
57
  CONF_SENSOR,
@@ -892,7 +892,7 @@ async def to_code(config):
892
892
  if name.upper() in climate.CLIMATE_PRESETS:
893
893
  standard_preset = climate.CLIMATE_PRESETS[name.upper()]
894
894
 
895
- if two_points_available is True:
895
+ if two_points_available:
896
896
  preset_target_config = ThermostatClimateTargetTempConfig(
897
897
  preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
898
898
  preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH],
@@ -905,6 +905,8 @@ async def to_code(config):
905
905
  preset_target_config = ThermostatClimateTargetTempConfig(
906
906
  preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW]
907
907
  )
908
+ else:
909
+ preset_target_config = None
908
910
 
909
911
  preset_target_variable = cg.new_variable(
910
912
  preset_config[CONF_ID], preset_target_config
@@ -502,8 +502,9 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
502
502
  }
503
503
  this->action = action;
504
504
  this->prev_action_trigger_ = trig;
505
- assert(trig != nullptr);
506
- trig->trigger();
505
+ if (trig != nullptr) {
506
+ trig->trigger();
507
+ }
507
508
  // if enabled, call the fan_only action with cooling/heating actions
508
509
  if (trig_fan != nullptr) {
509
510
  ESP_LOGVV(TAG, "Calling FAN_ONLY action with HEATING/COOLING action");
@@ -564,7 +565,6 @@ void ThermostatClimate::trigger_supplemental_action_() {
564
565
  }
565
566
 
566
567
  if (trig != nullptr) {
567
- assert(trig != nullptr);
568
568
  trig->trigger();
569
569
  }
570
570
  }
@@ -634,8 +634,9 @@ void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode, bo
634
634
  this->prev_fan_mode_trigger_ = nullptr;
635
635
  }
636
636
  this->start_timer_(thermostat::TIMER_FAN_MODE);
637
- assert(trig != nullptr);
638
- trig->trigger();
637
+ if (trig != nullptr) {
638
+ trig->trigger();
639
+ }
639
640
  this->prev_fan_mode_ = fan_mode;
640
641
  this->prev_fan_mode_trigger_ = trig;
641
642
  }
@@ -678,8 +679,9 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_
678
679
  mode = climate::CLIMATE_MODE_HEAT_COOL;
679
680
  // trig = this->auto_mode_trigger_;
680
681
  }
681
- assert(trig != nullptr);
682
- trig->trigger();
682
+ if (trig != nullptr) {
683
+ trig->trigger();
684
+ }
683
685
  this->mode = mode;
684
686
  this->prev_mode_ = mode;
685
687
  this->prev_mode_trigger_ = trig;
@@ -718,8 +720,9 @@ void ThermostatClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mo
718
720
  swing_mode = climate::CLIMATE_SWING_OFF;
719
721
  // trig = this->swing_mode_off_trigger_;
720
722
  }
721
- assert(trig != nullptr);
722
- trig->trigger();
723
+ if (trig != nullptr) {
724
+ trig->trigger();
725
+ }
723
726
  this->swing_mode = swing_mode;
724
727
  this->prev_swing_mode_ = swing_mode;
725
728
  this->prev_swing_mode_trigger_ = trig;
@@ -867,8 +870,9 @@ void ThermostatClimate::check_temperature_change_trigger_() {
867
870
  }
868
871
  // trigger the action
869
872
  Trigger<> *trig = this->temperature_change_trigger_;
870
- assert(trig != nullptr);
871
- trig->trigger();
873
+ if (trig != nullptr) {
874
+ trig->trigger();
875
+ }
872
876
  }
873
877
 
874
878
  bool ThermostatClimate::cooling_required_() {
@@ -998,9 +1002,10 @@ void ThermostatClimate::change_preset_(climate::ClimatePreset preset) {
998
1002
  this->preset.value() != preset) {
999
1003
  // Fire any preset changed trigger if defined
1000
1004
  Trigger<> *trig = this->preset_change_trigger_;
1001
- assert(trig != nullptr);
1002
1005
  this->preset = preset;
1003
- trig->trigger();
1006
+ if (trig != nullptr) {
1007
+ trig->trigger();
1008
+ }
1004
1009
 
1005
1010
  this->refresh();
1006
1011
  ESP_LOGI(TAG, "Preset %s applied", LOG_STR_ARG(climate::climate_preset_to_string(preset)));
@@ -1023,9 +1028,10 @@ void ThermostatClimate::change_custom_preset_(const std::string &custom_preset)
1023
1028
  this->custom_preset.value() != custom_preset) {
1024
1029
  // Fire any preset changed trigger if defined
1025
1030
  Trigger<> *trig = this->preset_change_trigger_;
1026
- assert(trig != nullptr);
1027
1031
  this->custom_preset = custom_preset;
1028
- trig->trigger();
1032
+ if (trig != nullptr) {
1033
+ trig->trigger();
1034
+ }
1029
1035
 
1030
1036
  this->refresh();
1031
1037
  ESP_LOGI(TAG, "Custom preset %s applied", custom_preset.c_str());
@@ -1,8 +1,9 @@
1
1
  import esphome.codegen as cg
2
- import esphome.config_validation as cv
3
2
  from esphome.components import binary_sensor
3
+ import esphome.config_validation as cv
4
4
  from esphome.const import CONF_KEY
5
- from ..display import tm1638_ns, TM1638Component, CONF_TM1638_ID
5
+
6
+ from ..display import CONF_TM1638_ID, TM1638Component, tm1638_ns
6
7
 
7
8
  TM1638Key = tm1638_ns.class_("TM1638Key", binary_sensor.BinarySensor)
8
9
 
@@ -1,13 +1,13 @@
1
- import esphome.codegen as cg
2
- import esphome.config_validation as cv
3
1
  from esphome import pins
2
+ import esphome.codegen as cg
4
3
  from esphome.components import display
4
+ import esphome.config_validation as cv
5
5
  from esphome.const import (
6
+ CONF_CLK_PIN,
7
+ CONF_DIO_PIN,
6
8
  CONF_ID,
7
9
  CONF_INTENSITY,
8
10
  CONF_LAMBDA,
9
- CONF_CLK_PIN,
10
- CONF_DIO_PIN,
11
11
  CONF_STB_PIN,
12
12
  )
13
13
 
@@ -51,4 +51,4 @@ async def to_code(config):
51
51
  config[CONF_LAMBDA], [(TM1638ComponentRef, "it")], return_type=cg.void
52
52
  )
53
53
 
54
- cg.add(var.set_writer(lambda_))
54
+ cg.add(var.set_writer(lambda_))
@@ -1,8 +1,9 @@
1
1
  import esphome.codegen as cg
2
- import esphome.config_validation as cv
3
2
  from esphome.components import output
3
+ import esphome.config_validation as cv
4
4
  from esphome.const import CONF_ID, CONF_LED
5
- from ..display import tm1638_ns, TM1638Component, CONF_TM1638_ID
5
+
6
+ from ..display import CONF_TM1638_ID, TM1638Component, tm1638_ns
6
7
 
7
8
  TM1638OutputLed = tm1638_ns.class_("TM1638OutputLed", output.BinaryOutput, cg.Component)
8
9
 
@@ -1,8 +1,9 @@
1
1
  import esphome.codegen as cg
2
- import esphome.config_validation as cv
3
2
  from esphome.components import switch
3
+ import esphome.config_validation as cv
4
4
  from esphome.const import CONF_LED
5
- from ..display import tm1638_ns, TM1638Component, CONF_TM1638_ID
5
+
6
+ from ..display import CONF_TM1638_ID, TM1638Component, tm1638_ns
6
7
 
7
8
  TM1638SwitchLed = tm1638_ns.class_("TM1638SwitchLed", switch.Switch, cg.Component)
8
9
 
@@ -8,7 +8,7 @@ from esphome.const import (
8
8
  CONF_FORCE_UPDATE,
9
9
  CONF_ID,
10
10
  CONF_MQTT_ID,
11
- CONF_WEB_SERVER_ID,
11
+ CONF_WEB_SERVER,
12
12
  DEVICE_CLASS_EMPTY,
13
13
  DEVICE_CLASS_FIRMWARE,
14
14
  ENTITY_CATEGORY_CONFIG,
@@ -73,9 +73,8 @@ async def setup_update_core_(var, config):
73
73
  mqtt_ = cg.new_Pvariable(mqtt_id_config, var)
74
74
  await mqtt.register_mqtt_component(mqtt_, config)
75
75
 
76
- if web_server_id_config := config.get(CONF_WEB_SERVER_ID):
77
- web_server_ = await cg.get_variable(web_server_id_config)
78
- web_server.add_entity_to_sorting_list(web_server_, var, config)
76
+ if web_server_config := config.get(CONF_WEB_SERVER):
77
+ await web_server.add_entity_config(var, web_server_config)
79
78
 
80
79
 
81
80
  async def register_update(var, config):
@@ -14,7 +14,7 @@ from esphome.const import (
14
14
  CONF_STATE,
15
15
  CONF_STOP,
16
16
  CONF_TRIGGER_ID,
17
- CONF_WEB_SERVER_ID,
17
+ CONF_WEB_SERVER,
18
18
  DEVICE_CLASS_EMPTY,
19
19
  DEVICE_CLASS_GAS,
20
20
  DEVICE_CLASS_WATER,
@@ -124,9 +124,8 @@ async def setup_valve_core_(var, config):
124
124
  mqtt_.set_custom_position_command_topic(position_command_topic_config)
125
125
  )
126
126
 
127
- if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
128
- web_server_ = await cg.get_variable(webserver_id)
129
- web_server.add_entity_to_sorting_list(web_server_, var, config)
127
+ if web_server_config := config.get(CONF_WEB_SERVER):
128
+ await web_server.add_entity_config(var, web_server_config)
130
129
 
131
130
 
132
131
  async def register_valve(var, config):
@@ -17,13 +17,14 @@ from esphome.const import (
17
17
  CONF_JS_URL,
18
18
  CONF_LOCAL,
19
19
  CONF_LOG,
20
+ CONF_NAME,
20
21
  CONF_OTA,
21
22
  CONF_PASSWORD,
22
23
  CONF_PORT,
23
24
  CONF_USERNAME,
24
25
  CONF_VERSION,
26
+ CONF_WEB_SERVER,
25
27
  CONF_WEB_SERVER_ID,
26
- CONF_WEB_SERVER_SORTING_WEIGHT,
27
28
  PLATFORM_BK72XX,
28
29
  PLATFORM_ESP32,
29
30
  PLATFORM_ESP8266,
@@ -34,9 +35,15 @@ import esphome.final_validate as fv
34
35
 
35
36
  AUTO_LOAD = ["json", "web_server_base"]
36
37
 
38
+ CONF_SORTING_GROUP_ID = "sorting_group_id"
39
+ CONF_SORTING_GROUPS = "sorting_groups"
40
+ CONF_SORTING_WEIGHT = "sorting_weight"
41
+
37
42
  web_server_ns = cg.esphome_ns.namespace("web_server")
38
43
  WebServer = web_server_ns.class_("WebServer", cg.Component, cg.Controller)
39
44
 
45
+ sorting_groups = {}
46
+
40
47
 
41
48
  def default_url(config):
42
49
  config = config.copy()
@@ -70,42 +77,74 @@ def validate_ota(config):
70
77
  return config
71
78
 
72
79
 
73
- def _validate_no_sorting_weight(
74
- webserver_version: int, config: dict, path: list[str] | None = None
80
+ def validate_sorting_groups(config):
81
+ if CONF_SORTING_GROUPS in config and config[CONF_VERSION] != 3:
82
+ raise cv.Invalid(
83
+ f"'{CONF_SORTING_GROUPS}' is only supported in 'web_server' version 3"
84
+ )
85
+ return config
86
+
87
+
88
+ def _validate_no_sorting_component(
89
+ sorting_component: str,
90
+ webserver_version: int,
91
+ config: dict,
92
+ path: list[str] | None = None,
75
93
  ) -> None:
76
94
  if path is None:
77
95
  path = []
78
- if CONF_WEB_SERVER_SORTING_WEIGHT in config:
96
+ if CONF_WEB_SERVER in config and sorting_component in config[CONF_WEB_SERVER]:
79
97
  raise cv.FinalExternalInvalid(
80
- f"Sorting weight on entities is not supported in web_server version {webserver_version}",
81
- path=path + [CONF_WEB_SERVER_SORTING_WEIGHT],
98
+ f"{sorting_component} on entities is not supported in web_server version {webserver_version}",
99
+ path=path + [sorting_component],
82
100
  )
83
101
  for p, value in config.items():
84
102
  if isinstance(value, dict):
85
- _validate_no_sorting_weight(webserver_version, value, path + [p])
103
+ _validate_no_sorting_component(
104
+ sorting_component, webserver_version, value, path + [p]
105
+ )
86
106
  elif isinstance(value, list):
87
107
  for i, item in enumerate(value):
88
108
  if isinstance(item, dict):
89
- _validate_no_sorting_weight(webserver_version, item, path + [p, i])
109
+ _validate_no_sorting_component(
110
+ sorting_component, webserver_version, item, path + [p, i]
111
+ )
90
112
 
91
113
 
92
- def _final_validate_sorting_weight(config):
114
+ def _final_validate_sorting(config):
93
115
  if (webserver_version := config.get(CONF_VERSION)) != 3:
94
- _validate_no_sorting_weight(webserver_version, fv.full_config.get())
95
-
116
+ _validate_no_sorting_component(
117
+ CONF_SORTING_WEIGHT, webserver_version, fv.full_config.get()
118
+ )
119
+ _validate_no_sorting_component(
120
+ CONF_SORTING_GROUP_ID, webserver_version, fv.full_config.get()
121
+ )
96
122
  return config
97
123
 
98
124
 
99
- FINAL_VALIDATE_SCHEMA = _final_validate_sorting_weight
125
+ FINAL_VALIDATE_SCHEMA = _final_validate_sorting
100
126
 
127
+ sorting_group = {
128
+ cv.Required(CONF_ID): cv.declare_id(cg.int_),
129
+ cv.Required(CONF_NAME): cv.string,
130
+ cv.Optional(CONF_SORTING_WEIGHT): cv.float_,
131
+ }
101
132
 
102
133
  WEBSERVER_SORTING_SCHEMA = cv.Schema(
103
134
  {
104
- cv.OnlyWith(CONF_WEB_SERVER_ID, "web_server"): cv.use_id(WebServer),
105
- cv.Optional(CONF_WEB_SERVER_SORTING_WEIGHT): cv.All(
106
- cv.requires_component("web_server"),
107
- cv.float_,
108
- ),
135
+ cv.Optional(CONF_WEB_SERVER): cv.Schema(
136
+ {
137
+ cv.OnlyWith(CONF_WEB_SERVER_ID, "web_server"): cv.use_id(WebServer),
138
+ cv.Optional(CONF_SORTING_WEIGHT): cv.All(
139
+ cv.requires_component("web_server"),
140
+ cv.float_,
141
+ ),
142
+ cv.Optional(CONF_SORTING_GROUP_ID): cv.All(
143
+ cv.requires_component("web_server"),
144
+ cv.use_id(cg.int_),
145
+ ),
146
+ }
147
+ )
109
148
  }
110
149
  )
111
150
 
@@ -145,24 +184,38 @@ CONFIG_SCHEMA = cv.All(
145
184
  ): cv.boolean,
146
185
  cv.Optional(CONF_LOG, default=True): cv.boolean,
147
186
  cv.Optional(CONF_LOCAL): cv.boolean,
187
+ cv.Optional(CONF_SORTING_GROUPS): cv.ensure_list(sorting_group),
148
188
  }
149
189
  ).extend(cv.COMPONENT_SCHEMA),
150
190
  cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]),
151
191
  default_url,
152
192
  validate_local,
153
193
  validate_ota,
194
+ validate_sorting_groups,
154
195
  )
155
196
 
156
197
 
157
- def add_entity_to_sorting_list(web_server, entity, config):
158
- sorting_weight = 50
159
- if CONF_WEB_SERVER_SORTING_WEIGHT in config:
160
- sorting_weight = config[CONF_WEB_SERVER_SORTING_WEIGHT]
198
+ def add_sorting_groups(web_server_var, config):
199
+ for group in config:
200
+ sorting_groups[group[CONF_ID]] = group[CONF_NAME]
201
+ group_sorting_weight = group.get(CONF_SORTING_WEIGHT, 50)
202
+ cg.add(
203
+ web_server_var.add_sorting_group(
204
+ hash(group[CONF_ID]), group[CONF_NAME], group_sorting_weight
205
+ )
206
+ )
207
+
208
+
209
+ async def add_entity_config(entity, config):
210
+ web_server = await cg.get_variable(config[CONF_WEB_SERVER_ID])
211
+ sorting_weight = config.get(CONF_SORTING_WEIGHT, 50)
212
+ sorting_group_hash = hash(config.get(CONF_SORTING_GROUP_ID))
161
213
 
162
214
  cg.add(
163
- web_server.add_entity_to_sorting_list(
215
+ web_server.add_entity_config(
164
216
  entity,
165
217
  sorting_weight,
218
+ sorting_group_hash,
166
219
  )
167
220
  )
168
221
 
@@ -241,3 +294,6 @@ async def to_code(config):
241
294
  cg.add(var.set_include_internal(config[CONF_INCLUDE_INTERNAL]))
242
295
  if CONF_LOCAL in config and config[CONF_LOCAL]:
243
296
  cg.add_define("USE_WEBSERVER_LOCAL")
297
+
298
+ if (sorting_group_config := config.get(CONF_SORTING_GROUPS)) is not None:
299
+ add_sorting_groups(var, sorting_group_config)