esphome 2025.5.0b2__py3-none-any.whl → 2025.5.0b4__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 (104) hide show
  1. esphome/__main__.py +16 -14
  2. esphome/components/api/api_connection.cpp +339 -652
  3. esphome/components/api/api_connection.h +251 -57
  4. esphome/components/api/api_frame_helper.cpp +136 -49
  5. esphome/components/api/api_frame_helper.h +49 -6
  6. esphome/components/api/proto.h +48 -8
  7. esphome/components/ballu/climate.py +1 -1
  8. esphome/components/bedjet/bedjet_hub.cpp +1 -0
  9. esphome/components/binary_sensor/binary_sensor.cpp +10 -6
  10. esphome/components/binary_sensor/binary_sensor.h +1 -1
  11. esphome/components/binary_sensor/filter.cpp +21 -21
  12. esphome/components/binary_sensor/filter.h +10 -10
  13. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -1
  14. esphome/components/ccs811/sensor.py +9 -6
  15. esphome/components/climate_ir/__init__.py +3 -3
  16. esphome/components/climate_ir_lg/climate.py +1 -1
  17. esphome/components/coolix/climate.py +1 -1
  18. esphome/components/cse7766/cse7766.cpp +2 -1
  19. esphome/components/current_based/current_based_cover.cpp +2 -1
  20. esphome/components/daikin/climate.py +1 -1
  21. esphome/components/daikin_arc/climate.py +1 -1
  22. esphome/components/daikin_brc/climate.py +1 -1
  23. esphome/components/daly_bms/daly_bms.cpp +2 -1
  24. esphome/components/debug/debug_component.cpp +1 -1
  25. esphome/components/delonghi/climate.py +1 -1
  26. esphome/components/dps310/sensor.py +6 -6
  27. esphome/components/ee895/sensor.py +9 -9
  28. esphome/components/emmeti/climate.py +1 -1
  29. esphome/components/endstop/endstop_cover.cpp +2 -1
  30. esphome/components/ens160_base/__init__.py +12 -9
  31. esphome/components/esp32_ble/ble_advertising.cpp +2 -1
  32. esphome/components/esp32_camera/__init__.py +1 -1
  33. esphome/components/esp32_camera/esp32_camera.cpp +2 -10
  34. esphome/components/esp32_camera/esp32_camera.h +1 -1
  35. esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
  36. esphome/components/esp32_touch/esp32_touch.cpp +1 -1
  37. esphome/components/ethernet/ethernet_component.cpp +1 -1
  38. esphome/components/feedback/feedback_cover.cpp +2 -1
  39. esphome/components/fujitsu_general/climate.py +1 -1
  40. esphome/components/gcja5/gcja5.cpp +2 -1
  41. esphome/components/gps/__init__.py +37 -16
  42. esphome/components/gps/gps.cpp +33 -17
  43. esphome/components/gps/gps.h +16 -15
  44. esphome/components/gree/climate.py +1 -1
  45. esphome/components/growatt_solar/growatt_solar.cpp +2 -1
  46. esphome/components/heatpumpir/climate.py +1 -1
  47. esphome/components/hitachi_ac344/climate.py +1 -1
  48. esphome/components/hitachi_ac424/climate.py +1 -1
  49. esphome/components/hte501/sensor.py +6 -6
  50. esphome/components/hyt271/sensor.py +6 -6
  51. esphome/components/kuntze/kuntze.cpp +2 -1
  52. esphome/components/logger/__init__.py +1 -0
  53. esphome/components/logger/logger.cpp +53 -32
  54. esphome/components/logger/logger.h +55 -5
  55. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
  56. esphome/components/max7219digit/max7219digit.cpp +2 -1
  57. esphome/components/mhz19/sensor.py +11 -7
  58. esphome/components/midea_ir/climate.py +1 -1
  59. esphome/components/mitsubishi/climate.py +1 -1
  60. esphome/components/modbus/modbus.cpp +2 -1
  61. esphome/components/mqtt/mqtt_client.cpp +1 -1
  62. esphome/components/ms5611/sensor.py +6 -6
  63. esphome/components/ms8607/sensor.py +3 -3
  64. esphome/components/noblex/climate.py +1 -1
  65. esphome/components/pmsx003/pmsx003.cpp +2 -1
  66. esphome/components/pzem004t/pzem004t.cpp +2 -1
  67. esphome/components/rf_bridge/rf_bridge.cpp +2 -1
  68. esphome/components/sds011/sds011.cpp +2 -1
  69. esphome/components/sen5x/sen5x.cpp +55 -36
  70. esphome/components/senseair/sensor.py +3 -3
  71. esphome/components/sgp30/sensor.py +14 -16
  72. esphome/components/shtcx/sensor.py +6 -6
  73. esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
  74. esphome/components/sprinkler/sprinkler.cpp +6 -5
  75. esphome/components/t6615/sensor.py +3 -3
  76. esphome/components/t6615/t6615.cpp +2 -1
  77. esphome/components/tcl112/climate.py +1 -1
  78. esphome/components/time_based/time_based_cover.cpp +2 -1
  79. esphome/components/toshiba/climate.py +1 -1
  80. esphome/components/uart/switch/uart_switch.cpp +2 -1
  81. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
  82. esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
  83. esphome/components/weikai/weikai.cpp +0 -52
  84. esphome/components/whirlpool/climate.py +1 -1
  85. esphome/components/whynter/climate.py +1 -1
  86. esphome/components/zhlt01/climate.py +1 -1
  87. esphome/config.py +13 -13
  88. esphome/const.py +1 -1
  89. esphome/core/application.cpp +26 -10
  90. esphome/core/application.h +5 -1
  91. esphome/core/component.cpp +10 -5
  92. esphome/core/component.h +5 -1
  93. esphome/core/doxygen.h +13 -0
  94. esphome/core/scheduler.cpp +4 -1
  95. esphome/log.py +15 -19
  96. esphome/mqtt.py +2 -2
  97. esphome/voluptuous_schema.py +3 -1
  98. esphome/wizard.py +45 -35
  99. {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/METADATA +1 -1
  100. {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/RECORD +104 -103
  101. {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/WHEEL +0 -0
  102. {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/entry_points.txt +0 -0
  103. {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/licenses/LICENSE +0 -0
  104. {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  #include "matrix_keypad.h"
2
2
  #include "esphome/core/log.h"
3
+ #include "esphome/core/application.h"
3
4
 
4
5
  namespace esphome {
5
6
  namespace matrix_keypad {
@@ -28,7 +29,7 @@ void MatrixKeypad::setup() {
28
29
  void MatrixKeypad::loop() {
29
30
  static uint32_t active_start = 0;
30
31
  static int active_key = -1;
31
- uint32_t now = millis();
32
+ uint32_t now = App.get_loop_component_start_time();
32
33
  int key = -1;
33
34
  bool error = false;
34
35
  int pos = 0, row, col;
@@ -2,6 +2,7 @@
2
2
  #include "esphome/core/log.h"
3
3
  #include "esphome/core/helpers.h"
4
4
  #include "esphome/core/hal.h"
5
+ #include "esphome/core/application.h"
5
6
  #include "max7219font.h"
6
7
 
7
8
  #include <algorithm>
@@ -63,7 +64,7 @@ void MAX7219Component::dump_config() {
63
64
  }
64
65
 
65
66
  void MAX7219Component::loop() {
66
- const uint32_t now = millis();
67
+ const uint32_t now = App.get_loop_component_start_time();
67
68
  const uint32_t millis_since_last_scroll = now - this->last_scroll_;
68
69
  const size_t first_line_size = this->max_displaybuffer_[0].size();
69
70
  // check if the buffer has shrunk past the current position since last update
@@ -32,7 +32,7 @@ CONFIG_SCHEMA = (
32
32
  cv.Schema(
33
33
  {
34
34
  cv.GenerateID(): cv.declare_id(MHZ19Component),
35
- cv.Required(CONF_CO2): sensor.sensor_schema(
35
+ cv.Optional(CONF_CO2): sensor.sensor_schema(
36
36
  unit_of_measurement=UNIT_PARTS_PER_MILLION,
37
37
  icon=ICON_MOLECULE_CO2,
38
38
  accuracy_decimals=0,
@@ -61,16 +61,20 @@ async def to_code(config):
61
61
  await cg.register_component(var, config)
62
62
  await uart.register_uart_device(var, config)
63
63
 
64
- if CONF_CO2 in config:
65
- sens = await sensor.new_sensor(config[CONF_CO2])
64
+ if co2 := config.get(CONF_CO2):
65
+ sens = await sensor.new_sensor(co2)
66
66
  cg.add(var.set_co2_sensor(sens))
67
67
 
68
- if CONF_TEMPERATURE in config:
69
- sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
68
+ if temperature := config.get(CONF_TEMPERATURE):
69
+ sens = await sensor.new_sensor(temperature)
70
70
  cg.add(var.set_temperature_sensor(sens))
71
71
 
72
- if CONF_AUTOMATIC_BASELINE_CALIBRATION in config:
73
- cg.add(var.set_abc_enabled(config[CONF_AUTOMATIC_BASELINE_CALIBRATION]))
72
+ if (
73
+ automatic_baseline_calibration := config.get(
74
+ CONF_AUTOMATIC_BASELINE_CALIBRATION
75
+ )
76
+ ) is not None:
77
+ cg.add(var.set_abc_enabled(automatic_baseline_calibration))
74
78
 
75
79
  cg.add(var.set_warmup_seconds(config[CONF_WARMUP_TIME]))
76
80
 
@@ -10,7 +10,7 @@ midea_ir_ns = cg.esphome_ns.namespace("midea_ir")
10
10
  MideaIR = midea_ir_ns.class_("MideaIR", climate_ir.ClimateIR)
11
11
 
12
12
 
13
- CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(MideaIR).extend(
13
+ CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(MideaIR).extend(
14
14
  {
15
15
  cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.boolean,
16
16
  }
@@ -43,7 +43,7 @@ VERTICAL_DIRECTIONS = {
43
43
  }
44
44
 
45
45
 
46
- CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(MitsubishiClimate).extend(
46
+ CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(MitsubishiClimate).extend(
47
47
  {
48
48
  cv.Optional(CONF_SET_FAN_MODE, default="3levels"): cv.enum(SETFANMODE),
49
49
  cv.Optional(CONF_SUPPORTS_DRY, default=False): cv.boolean,
@@ -1,6 +1,7 @@
1
1
  #include "modbus.h"
2
2
  #include "esphome/core/log.h"
3
3
  #include "esphome/core/helpers.h"
4
+ #include "esphome/core/application.h"
4
5
 
5
6
  namespace esphome {
6
7
  namespace modbus {
@@ -13,7 +14,7 @@ void Modbus::setup() {
13
14
  }
14
15
  }
15
16
  void Modbus::loop() {
16
- const uint32_t now = millis();
17
+ const uint32_t now = App.get_loop_component_start_time();
17
18
 
18
19
  while (this->available()) {
19
20
  uint8_t byte;
@@ -345,7 +345,7 @@ void MQTTClientComponent::loop() {
345
345
  this->disconnect_reason_.reset();
346
346
  }
347
347
 
348
- const uint32_t now = millis();
348
+ const uint32_t now = App.get_loop_component_start_time();
349
349
 
350
350
  switch (this->state_) {
351
351
  case MQTT_CLIENT_DISABLED:
@@ -24,13 +24,13 @@ CONFIG_SCHEMA = (
24
24
  cv.Schema(
25
25
  {
26
26
  cv.GenerateID(): cv.declare_id(MS5611Component),
27
- cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(
27
+ cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
28
28
  unit_of_measurement=UNIT_CELSIUS,
29
29
  accuracy_decimals=1,
30
30
  device_class=DEVICE_CLASS_TEMPERATURE,
31
31
  state_class=STATE_CLASS_MEASUREMENT,
32
32
  ),
33
- cv.Required(CONF_PRESSURE): sensor.sensor_schema(
33
+ cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
34
34
  unit_of_measurement=UNIT_HECTOPASCAL,
35
35
  icon=ICON_GAUGE,
36
36
  accuracy_decimals=1,
@@ -49,10 +49,10 @@ async def to_code(config):
49
49
  await cg.register_component(var, config)
50
50
  await i2c.register_i2c_device(var, config)
51
51
 
52
- if CONF_TEMPERATURE in config:
53
- sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
52
+ if temperature := config.get(CONF_TEMPERATURE):
53
+ sens = await sensor.new_sensor(temperature)
54
54
  cg.add(var.set_temperature_sensor(sens))
55
55
 
56
- if CONF_PRESSURE in config:
57
- sens = await sensor.new_sensor(config[CONF_PRESSURE])
56
+ if pressure := config.get(CONF_PRESSURE):
57
+ sens = await sensor.new_sensor(pressure)
58
58
  cg.add(var.set_pressure_sensor(sens))
@@ -29,19 +29,19 @@ CONFIG_SCHEMA = (
29
29
  cv.Schema(
30
30
  {
31
31
  cv.GenerateID(): cv.declare_id(MS8607Component),
32
- cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(
32
+ cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
33
33
  unit_of_measurement=UNIT_CELSIUS,
34
34
  accuracy_decimals=2, # Resolution: 0.01
35
35
  device_class=DEVICE_CLASS_TEMPERATURE,
36
36
  state_class=STATE_CLASS_MEASUREMENT,
37
37
  ),
38
- cv.Required(CONF_PRESSURE): sensor.sensor_schema(
38
+ cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
39
39
  unit_of_measurement=UNIT_HECTOPASCAL,
40
40
  accuracy_decimals=2, # Resolution: 0.016
41
41
  device_class=DEVICE_CLASS_PRESSURE,
42
42
  state_class=STATE_CLASS_MEASUREMENT,
43
43
  ),
44
- cv.Required(CONF_HUMIDITY): sensor.sensor_schema(
44
+ cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
45
45
  unit_of_measurement=UNIT_PERCENT,
46
46
  accuracy_decimals=2, # Resolution: 0.04
47
47
  device_class=DEVICE_CLASS_HUMIDITY,
@@ -6,7 +6,7 @@ AUTO_LOAD = ["climate_ir"]
6
6
  noblex_ns = cg.esphome_ns.namespace("noblex")
7
7
  NoblexClimate = noblex_ns.class_("NoblexClimate", climate_ir.ClimateIR)
8
8
 
9
- CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(NoblexClimate)
9
+ CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(NoblexClimate)
10
10
 
11
11
 
12
12
  async def to_code(config):
@@ -1,5 +1,6 @@
1
1
  #include "pmsx003.h"
2
2
  #include "esphome/core/log.h"
3
+ #include "esphome/core/application.h"
3
4
 
4
5
  namespace esphome {
5
6
  namespace pmsx003 {
@@ -42,7 +43,7 @@ void PMSX003Component::dump_config() {
42
43
  }
43
44
 
44
45
  void PMSX003Component::loop() {
45
- const uint32_t now = millis();
46
+ const uint32_t now = App.get_loop_component_start_time();
46
47
 
47
48
  // If we update less often than it takes the device to stabilise, spin the fan down
48
49
  // rather than running it constantly. It does take some time to stabilise, so we
@@ -1,5 +1,6 @@
1
1
  #include "pzem004t.h"
2
2
  #include "esphome/core/log.h"
3
+ #include "esphome/core/application.h"
3
4
  #include <cinttypes>
4
5
 
5
6
  namespace esphome {
@@ -16,7 +17,7 @@ void PZEM004T::setup() {
16
17
  }
17
18
 
18
19
  void PZEM004T::loop() {
19
- const uint32_t now = millis();
20
+ const uint32_t now = App.get_loop_component_start_time();
20
21
  if (now - this->last_read_ > 500 && this->available() < 7) {
21
22
  while (this->available())
22
23
  this->read();
@@ -1,5 +1,6 @@
1
1
  #include "rf_bridge.h"
2
2
  #include "esphome/core/log.h"
3
+ #include "esphome/core/application.h"
3
4
  #include <cinttypes>
4
5
  #include <cstring>
5
6
 
@@ -128,7 +129,7 @@ void RFBridgeComponent::write_byte_str_(const std::string &codes) {
128
129
  }
129
130
 
130
131
  void RFBridgeComponent::loop() {
131
- const uint32_t now = millis();
132
+ const uint32_t now = App.get_loop_component_start_time();
132
133
  if (now - this->last_bridge_byte_ > 50) {
133
134
  this->rx_buffer_.clear();
134
135
  this->last_bridge_byte_ = now;
@@ -1,5 +1,6 @@
1
1
  #include "sds011.h"
2
2
  #include "esphome/core/log.h"
3
+ #include "esphome/core/application.h"
3
4
 
4
5
  namespace esphome {
5
6
  namespace sds011 {
@@ -75,7 +76,7 @@ void SDS011Component::dump_config() {
75
76
  }
76
77
 
77
78
  void SDS011Component::loop() {
78
- const uint32_t now = millis();
79
+ const uint32_t now = App.get_loop_component_start_time();
79
80
  if ((now - this->last_transmission_ >= 500) && this->data_index_) {
80
81
  // last transmission too long ago. Reset RX index.
81
82
  ESP_LOGV(TAG, "Last transmission too long ago. Reset RX index.");
@@ -25,6 +25,10 @@ static const uint16_t SEN5X_CMD_TEMPERATURE_COMPENSATION = 0x60B2;
25
25
  static const uint16_t SEN5X_CMD_VOC_ALGORITHM_STATE = 0x6181;
26
26
  static const uint16_t SEN5X_CMD_VOC_ALGORITHM_TUNING = 0x60D0;
27
27
 
28
+ static const int8_t SEN5X_INDEX_SCALE_FACTOR = 10; // used for VOC and NOx index values
29
+ static const int8_t SEN5X_MIN_INDEX_VALUE = 1 * SEN5X_INDEX_SCALE_FACTOR; // must be adjusted by the scale factor
30
+ static const int16_t SEN5X_MAX_INDEX_VALUE = 500 * SEN5X_INDEX_SCALE_FACTOR; // must be adjusted by the scale factor
31
+
28
32
  void SEN5XComponent::setup() {
29
33
  ESP_LOGCONFIG(TAG, "Setting up sen5x...");
30
34
 
@@ -88,8 +92,9 @@ void SEN5XComponent::setup() {
88
92
  product_name_.push_back(current_char);
89
93
  // second char
90
94
  current_char = *current_int & 0xFF;
91
- if (current_char)
95
+ if (current_char) {
92
96
  product_name_.push_back(current_char);
97
+ }
93
98
  }
94
99
  current_int++;
95
100
  } while (current_char && --max);
@@ -271,10 +276,10 @@ void SEN5XComponent::dump_config() {
271
276
  ESP_LOGCONFIG(TAG, " Low RH/T acceleration mode");
272
277
  break;
273
278
  case MEDIUM_ACCELERATION:
274
- ESP_LOGCONFIG(TAG, " Medium RH/T accelertion mode");
279
+ ESP_LOGCONFIG(TAG, " Medium RH/T acceleration mode");
275
280
  break;
276
281
  case HIGH_ACCELERATION:
277
- ESP_LOGCONFIG(TAG, " High RH/T accelertion mode");
282
+ ESP_LOGCONFIG(TAG, " High RH/T acceleration mode");
278
283
  break;
279
284
  }
280
285
  }
@@ -337,47 +342,61 @@ void SEN5XComponent::update() {
337
342
  ESP_LOGD(TAG, "read data error (%d)", this->last_error_);
338
343
  return;
339
344
  }
340
- float pm_1_0 = measurements[0] / 10.0;
341
- if (measurements[0] == 0xFFFF)
342
- pm_1_0 = NAN;
343
- float pm_2_5 = measurements[1] / 10.0;
344
- if (measurements[1] == 0xFFFF)
345
- pm_2_5 = NAN;
346
- float pm_4_0 = measurements[2] / 10.0;
347
- if (measurements[2] == 0xFFFF)
348
- pm_4_0 = NAN;
349
- float pm_10_0 = measurements[3] / 10.0;
350
- if (measurements[3] == 0xFFFF)
351
- pm_10_0 = NAN;
352
- float humidity = measurements[4] / 100.0;
353
- if (measurements[4] == 0xFFFF)
354
- humidity = NAN;
355
- float temperature = (int16_t) measurements[5] / 200.0;
356
- if (measurements[5] == 0xFFFF)
357
- temperature = NAN;
358
- float voc = measurements[6] / 10.0;
359
- if (measurements[6] == 0xFFFF)
360
- voc = NAN;
361
- float nox = measurements[7] / 10.0;
362
- if (measurements[7] == 0xFFFF)
363
- nox = NAN;
364
-
365
- if (this->pm_1_0_sensor_ != nullptr)
345
+
346
+ ESP_LOGVV(TAG, "pm_1_0 = 0x%.4x", measurements[0]);
347
+ float pm_1_0 = measurements[0] == UINT16_MAX ? NAN : measurements[0] / 10.0f;
348
+
349
+ ESP_LOGVV(TAG, "pm_2_5 = 0x%.4x", measurements[1]);
350
+ float pm_2_5 = measurements[1] == UINT16_MAX ? NAN : measurements[1] / 10.0f;
351
+
352
+ ESP_LOGVV(TAG, "pm_4_0 = 0x%.4x", measurements[2]);
353
+ float pm_4_0 = measurements[2] == UINT16_MAX ? NAN : measurements[2] / 10.0f;
354
+
355
+ ESP_LOGVV(TAG, "pm_10_0 = 0x%.4x", measurements[3]);
356
+ float pm_10_0 = measurements[3] == UINT16_MAX ? NAN : measurements[3] / 10.0f;
357
+
358
+ ESP_LOGVV(TAG, "humidity = 0x%.4x", measurements[4]);
359
+ float humidity = measurements[4] == INT16_MAX ? NAN : static_cast<int16_t>(measurements[4]) / 100.0f;
360
+
361
+ ESP_LOGVV(TAG, "temperature = 0x%.4x", measurements[5]);
362
+ float temperature = measurements[5] == INT16_MAX ? NAN : static_cast<int16_t>(measurements[5]) / 200.0f;
363
+
364
+ ESP_LOGVV(TAG, "voc = 0x%.4x", measurements[6]);
365
+ int16_t voc_idx = static_cast<int16_t>(measurements[6]);
366
+ float voc = (voc_idx < SEN5X_MIN_INDEX_VALUE || voc_idx > SEN5X_MAX_INDEX_VALUE)
367
+ ? NAN
368
+ : static_cast<float>(voc_idx) / 10.0f;
369
+
370
+ ESP_LOGVV(TAG, "nox = 0x%.4x", measurements[7]);
371
+ int16_t nox_idx = static_cast<int16_t>(measurements[7]);
372
+ float nox = (nox_idx < SEN5X_MIN_INDEX_VALUE || nox_idx > SEN5X_MAX_INDEX_VALUE)
373
+ ? NAN
374
+ : static_cast<float>(nox_idx) / 10.0f;
375
+
376
+ if (this->pm_1_0_sensor_ != nullptr) {
366
377
  this->pm_1_0_sensor_->publish_state(pm_1_0);
367
- if (this->pm_2_5_sensor_ != nullptr)
378
+ }
379
+ if (this->pm_2_5_sensor_ != nullptr) {
368
380
  this->pm_2_5_sensor_->publish_state(pm_2_5);
369
- if (this->pm_4_0_sensor_ != nullptr)
381
+ }
382
+ if (this->pm_4_0_sensor_ != nullptr) {
370
383
  this->pm_4_0_sensor_->publish_state(pm_4_0);
371
- if (this->pm_10_0_sensor_ != nullptr)
384
+ }
385
+ if (this->pm_10_0_sensor_ != nullptr) {
372
386
  this->pm_10_0_sensor_->publish_state(pm_10_0);
373
- if (this->temperature_sensor_ != nullptr)
387
+ }
388
+ if (this->temperature_sensor_ != nullptr) {
374
389
  this->temperature_sensor_->publish_state(temperature);
375
- if (this->humidity_sensor_ != nullptr)
390
+ }
391
+ if (this->humidity_sensor_ != nullptr) {
376
392
  this->humidity_sensor_->publish_state(humidity);
377
- if (this->voc_sensor_ != nullptr)
393
+ }
394
+ if (this->voc_sensor_ != nullptr) {
378
395
  this->voc_sensor_->publish_state(voc);
379
- if (this->nox_sensor_ != nullptr)
396
+ }
397
+ if (this->nox_sensor_ != nullptr) {
380
398
  this->nox_sensor_->publish_state(nox);
399
+ }
381
400
  this->status_clear_warning();
382
401
  });
383
402
  }
@@ -38,7 +38,7 @@ CONFIG_SCHEMA = (
38
38
  cv.Schema(
39
39
  {
40
40
  cv.GenerateID(): cv.declare_id(SenseAirComponent),
41
- cv.Required(CONF_CO2): sensor.sensor_schema(
41
+ cv.Optional(CONF_CO2): sensor.sensor_schema(
42
42
  unit_of_measurement=UNIT_PARTS_PER_MILLION,
43
43
  icon=ICON_MOLECULE_CO2,
44
44
  accuracy_decimals=0,
@@ -57,8 +57,8 @@ async def to_code(config):
57
57
  await cg.register_component(var, config)
58
58
  await uart.register_uart_device(var, config)
59
59
 
60
- if CONF_CO2 in config:
61
- sens = await sensor.new_sensor(config[CONF_CO2])
60
+ if co2 := config.get(CONF_CO2):
61
+ sens = await sensor.new_sensor(co2)
62
62
  cg.add(var.set_co2_sensor(sens))
63
63
 
64
64
 
@@ -37,14 +37,14 @@ CONFIG_SCHEMA = (
37
37
  cv.Schema(
38
38
  {
39
39
  cv.GenerateID(): cv.declare_id(SGP30Component),
40
- cv.Required(CONF_ECO2): sensor.sensor_schema(
40
+ cv.Optional(CONF_ECO2): sensor.sensor_schema(
41
41
  unit_of_measurement=UNIT_PARTS_PER_MILLION,
42
42
  icon=ICON_MOLECULE_CO2,
43
43
  accuracy_decimals=0,
44
44
  device_class=DEVICE_CLASS_CARBON_DIOXIDE,
45
45
  state_class=STATE_CLASS_MEASUREMENT,
46
46
  ),
47
- cv.Required(CONF_TVOC): sensor.sensor_schema(
47
+ cv.Optional(CONF_TVOC): sensor.sensor_schema(
48
48
  unit_of_measurement=UNIT_PARTS_PER_BILLION,
49
49
  icon=ICON_RADIATOR,
50
50
  accuracy_decimals=0,
@@ -86,32 +86,30 @@ async def to_code(config):
86
86
  await cg.register_component(var, config)
87
87
  await i2c.register_i2c_device(var, config)
88
88
 
89
- if CONF_ECO2 in config:
90
- sens = await sensor.new_sensor(config[CONF_ECO2])
89
+ if eco2_config := config.get(CONF_ECO2):
90
+ sens = await sensor.new_sensor(eco2_config)
91
91
  cg.add(var.set_eco2_sensor(sens))
92
92
 
93
- if CONF_TVOC in config:
94
- sens = await sensor.new_sensor(config[CONF_TVOC])
93
+ if tvoc_config := config.get(CONF_TVOC):
94
+ sens = await sensor.new_sensor(tvoc_config)
95
95
  cg.add(var.set_tvoc_sensor(sens))
96
96
 
97
- if CONF_ECO2_BASELINE in config:
98
- sens = await sensor.new_sensor(config[CONF_ECO2_BASELINE])
97
+ if eco2_baseline_config := config.get(CONF_ECO2_BASELINE):
98
+ sens = await sensor.new_sensor(eco2_baseline_config)
99
99
  cg.add(var.set_eco2_baseline_sensor(sens))
100
100
 
101
- if CONF_TVOC_BASELINE in config:
102
- sens = await sensor.new_sensor(config[CONF_TVOC_BASELINE])
101
+ if tvoc_baseline_config := config.get(CONF_TVOC_BASELINE):
102
+ sens = await sensor.new_sensor(tvoc_baseline_config)
103
103
  cg.add(var.set_tvoc_baseline_sensor(sens))
104
104
 
105
- if CONF_STORE_BASELINE in config:
106
- cg.add(var.set_store_baseline(config[CONF_STORE_BASELINE]))
105
+ if (store_baseline := config.get(CONF_STORE_BASELINE)) is not None:
106
+ cg.add(var.set_store_baseline(store_baseline))
107
107
 
108
- if CONF_BASELINE in config:
109
- baseline_config = config[CONF_BASELINE]
108
+ if baseline_config := config.get(CONF_BASELINE):
110
109
  cg.add(var.set_eco2_baseline(baseline_config[CONF_ECO2_BASELINE]))
111
110
  cg.add(var.set_tvoc_baseline(baseline_config[CONF_TVOC_BASELINE]))
112
111
 
113
- if CONF_COMPENSATION in config:
114
- compensation_config = config[CONF_COMPENSATION]
112
+ if compensation_config := config.get(CONF_COMPENSATION):
115
113
  sens = await cg.get_variable(compensation_config[CONF_HUMIDITY_SOURCE])
116
114
  cg.add(var.set_humidity_sensor(sens))
117
115
  sens = await cg.get_variable(compensation_config[CONF_TEMPERATURE_SOURCE])
@@ -26,13 +26,13 @@ CONFIG_SCHEMA = (
26
26
  cv.Schema(
27
27
  {
28
28
  cv.GenerateID(): cv.declare_id(SHTCXComponent),
29
- cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(
29
+ cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
30
30
  unit_of_measurement=UNIT_CELSIUS,
31
31
  accuracy_decimals=1,
32
32
  device_class=DEVICE_CLASS_TEMPERATURE,
33
33
  state_class=STATE_CLASS_MEASUREMENT,
34
34
  ),
35
- cv.Required(CONF_HUMIDITY): sensor.sensor_schema(
35
+ cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
36
36
  unit_of_measurement=UNIT_PERCENT,
37
37
  accuracy_decimals=1,
38
38
  device_class=DEVICE_CLASS_HUMIDITY,
@@ -50,10 +50,10 @@ async def to_code(config):
50
50
  await cg.register_component(var, config)
51
51
  await i2c.register_i2c_device(var, config)
52
52
 
53
- if CONF_TEMPERATURE in config:
54
- sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
53
+ if temperature := config.get(CONF_TEMPERATURE):
54
+ sens = await sensor.new_sensor(temperature)
55
55
  cg.add(var.set_temperature_sensor(sens))
56
56
 
57
- if CONF_HUMIDITY in config:
58
- sens = await sensor.new_sensor(config[CONF_HUMIDITY])
57
+ if humidity := config.get(CONF_HUMIDITY):
58
+ sens = await sensor.new_sensor(humidity)
59
59
  cg.add(var.set_humidity_sensor(sens))
@@ -1,5 +1,6 @@
1
1
  #include "slow_pwm_output.h"
2
2
  #include "esphome/core/log.h"
3
+ #include "esphome/core/application.h"
3
4
 
4
5
  namespace esphome {
5
6
  namespace slow_pwm {
@@ -39,7 +40,7 @@ void SlowPWMOutput::set_output_state_(bool new_state) {
39
40
  }
40
41
 
41
42
  void SlowPWMOutput::loop() {
42
- uint32_t now = millis();
43
+ uint32_t now = App.get_loop_component_start_time();
43
44
  float scaled_state = this->state_ * this->period_;
44
45
 
45
46
  if (now - this->period_start_time_ >= this->period_) {
@@ -20,7 +20,7 @@ SprinklerSwitch::SprinklerSwitch(switch_::Switch *off_switch, switch_::Switch *o
20
20
  bool SprinklerSwitch::is_latching_valve() { return (this->off_switch_ != nullptr) && (this->on_switch_ != nullptr); }
21
21
 
22
22
  void SprinklerSwitch::loop() {
23
- if ((this->pinned_millis_) && (millis() > this->pinned_millis_ + this->pulse_duration_)) {
23
+ if ((this->pinned_millis_) && (App.get_loop_component_start_time() > this->pinned_millis_ + this->pulse_duration_)) {
24
24
  this->pinned_millis_ = 0; // reset tracker
25
25
  if (this->off_switch_->state) {
26
26
  this->off_switch_->turn_off();
@@ -148,22 +148,23 @@ SprinklerValveOperator::SprinklerValveOperator(SprinklerValve *valve, Sprinkler
148
148
  : controller_(controller), valve_(valve) {}
149
149
 
150
150
  void SprinklerValveOperator::loop() {
151
- if (millis() >= this->start_millis_) { // dummy check
151
+ uint32_t now = App.get_loop_component_start_time();
152
+ if (now >= this->start_millis_) { // dummy check
152
153
  switch (this->state_) {
153
154
  case STARTING:
154
- if (millis() > (this->start_millis_ + this->start_delay_)) {
155
+ if (now > (this->start_millis_ + this->start_delay_)) {
155
156
  this->run_(); // start_delay_ has been exceeded, so ensure both valves are on and update the state
156
157
  }
157
158
  break;
158
159
 
159
160
  case ACTIVE:
160
- if (millis() > (this->start_millis_ + this->start_delay_ + this->run_duration_)) {
161
+ if (now > (this->start_millis_ + this->start_delay_ + this->run_duration_)) {
161
162
  this->stop(); // start_delay_ + run_duration_ has been exceeded, start shutting down
162
163
  }
163
164
  break;
164
165
 
165
166
  case STOPPING:
166
- if (millis() > (this->stop_millis_ + this->stop_delay_)) {
167
+ if (now > (this->stop_millis_ + this->stop_delay_)) {
167
168
  this->kill_(); // stop_delay_has been exceeded, ensure all valves are off
168
169
  }
169
170
  break;
@@ -19,7 +19,7 @@ CONFIG_SCHEMA = (
19
19
  cv.Schema(
20
20
  {
21
21
  cv.GenerateID(): cv.declare_id(T6615Component),
22
- cv.Required(CONF_CO2): sensor.sensor_schema(
22
+ cv.Optional(CONF_CO2): sensor.sensor_schema(
23
23
  unit_of_measurement=UNIT_PARTS_PER_MILLION,
24
24
  accuracy_decimals=0,
25
25
  device_class=DEVICE_CLASS_CARBON_DIOXIDE,
@@ -41,6 +41,6 @@ async def to_code(config):
41
41
  await cg.register_component(var, config)
42
42
  await uart.register_uart_device(var, config)
43
43
 
44
- if CONF_CO2 in config:
45
- sens = await sensor.new_sensor(config[CONF_CO2])
44
+ if co2 := config.get(CONF_CO2):
45
+ sens = await sensor.new_sensor(co2)
46
46
  cg.add(var.set_co2_sensor(sens))
@@ -63,7 +63,8 @@ void T6615Component::loop() {
63
63
  case T6615Command::GET_PPM: {
64
64
  const uint16_t ppm = encode_uint16(response_buffer[3], response_buffer[4]);
65
65
  ESP_LOGD(TAG, "T6615 Received CO₂=%uppm", ppm);
66
- this->co2_sensor_->publish_state(ppm);
66
+ if (this->co2_sensor_ != nullptr)
67
+ this->co2_sensor_->publish_state(ppm);
67
68
  break;
68
69
  }
69
70
  default:
@@ -7,7 +7,7 @@ CODEOWNERS = ["@glmnet"]
7
7
  tcl112_ns = cg.esphome_ns.namespace("tcl112")
8
8
  Tcl112Climate = tcl112_ns.class_("Tcl112Climate", climate_ir.ClimateIR)
9
9
 
10
- CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(Tcl112Climate)
10
+ CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(Tcl112Climate)
11
11
 
12
12
 
13
13
  async def to_code(config):
@@ -1,6 +1,7 @@
1
1
  #include "time_based_cover.h"
2
2
  #include "esphome/core/log.h"
3
3
  #include "esphome/core/hal.h"
4
+ #include "esphome/core/application.h"
4
5
 
5
6
  namespace esphome {
6
7
  namespace time_based {
@@ -26,7 +27,7 @@ void TimeBasedCover::loop() {
26
27
  if (this->current_operation == COVER_OPERATION_IDLE)
27
28
  return;
28
29
 
29
- const uint32_t now = millis();
30
+ const uint32_t now = App.get_loop_component_start_time();
30
31
 
31
32
  // Recompute position every loop cycle
32
33
  this->recompute_position_();
@@ -16,7 +16,7 @@ MODELS = {
16
16
  "RAC-PT1411HWRU-F": Model.MODEL_RAC_PT1411HWRU_F,
17
17
  }
18
18
 
19
- CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(ToshibaClimate).extend(
19
+ CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(ToshibaClimate).extend(
20
20
  {
21
21
  cv.Optional(CONF_MODEL, default="generic"): cv.enum(MODELS, upper=True),
22
22
  }
@@ -1,5 +1,6 @@
1
1
  #include "uart_switch.h"
2
2
  #include "esphome/core/log.h"
3
+ #include "esphome/core/application.h"
3
4
 
4
5
  namespace esphome {
5
6
  namespace uart {
@@ -8,7 +9,7 @@ static const char *const TAG = "uart.switch";
8
9
 
9
10
  void UARTSwitch::loop() {
10
11
  if (this->send_every_) {
11
- const uint32_t now = millis();
12
+ const uint32_t now = App.get_loop_component_start_time();
12
13
  if (now - this->last_transmission_ > this->send_every_) {
13
14
  this->write_command_(this->state);
14
15
  this->last_transmission_ = now;
@@ -1,6 +1,7 @@
1
1
  #include "uponor_smatrix_climate.h"
2
2
  #include "esphome/core/helpers.h"
3
3
  #include "esphome/core/log.h"
4
+ #include "esphome/core/application.h"
4
5
 
5
6
  namespace esphome {
6
7
  namespace uponor_smatrix {
@@ -13,7 +14,7 @@ void UponorSmatrixClimate::dump_config() {
13
14
  }
14
15
 
15
16
  void UponorSmatrixClimate::loop() {
16
- const uint32_t now = millis();
17
+ const uint32_t now = App.get_loop_component_start_time();
17
18
 
18
19
  // Publish state after all update packets are processed
19
20
  if (this->last_data_ != 0 && (now - this->last_data_ > 100) && this->target_temperature_raw_ != 0) {