esphome 2024.9.2__py3-none-any.whl → 2024.10.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 (192) 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 +12 -1
  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/event/__init__.py +20 -12
  53. esphome/components/fan/__init__.py +3 -4
  54. esphome/components/gp2y1010au0f/__init__.py +0 -0
  55. esphome/components/gp2y1010au0f/gp2y1010au0f.cpp +67 -0
  56. esphome/components/gp2y1010au0f/gp2y1010au0f.h +52 -0
  57. esphome/components/gp2y1010au0f/sensor.py +61 -0
  58. esphome/components/gpio_expander/__init__.py +0 -0
  59. esphome/components/gpio_expander/cached_gpio.h +38 -0
  60. esphome/components/grove_gas_mc_v2/__init__.py +0 -0
  61. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +88 -0
  62. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +39 -0
  63. esphome/components/grove_gas_mc_v2/sensor.py +77 -0
  64. esphome/components/haier/climate.py +4 -3
  65. esphome/components/haier/haier_base.cpp +63 -8
  66. esphome/components/haier/haier_base.h +29 -3
  67. esphome/components/haier/hon_climate.cpp +122 -65
  68. esphome/components/haier/hon_climate.h +18 -2
  69. esphome/components/haier/smartair2_climate.cpp +21 -21
  70. esphome/components/haier/switch/__init__.py +91 -0
  71. esphome/components/haier/switch/beeper.cpp +14 -0
  72. esphome/components/haier/switch/beeper.h +18 -0
  73. esphome/components/haier/switch/display.cpp +14 -0
  74. esphome/components/haier/switch/display.h +18 -0
  75. esphome/components/haier/switch/health_mode.cpp +14 -0
  76. esphome/components/haier/switch/health_mode.h +18 -0
  77. esphome/components/haier/switch/quiet_mode.cpp +14 -0
  78. esphome/components/haier/switch/quiet_mode.h +18 -0
  79. esphome/components/hmac_md5/hmac_md5.cpp +2 -0
  80. esphome/components/hmac_md5/hmac_md5.h +2 -1
  81. esphome/components/i2s_audio/speaker/__init__.py +19 -0
  82. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  83. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +2 -0
  84. esphome/components/ili9xxx/ili9xxx_display.h +1 -0
  85. esphome/components/image/__init__.py +12 -12
  86. esphome/components/image/image.cpp +44 -0
  87. esphome/components/image/image.h +17 -2
  88. esphome/components/inkplate6/display.py +2 -0
  89. esphome/components/inkplate6/inkplate.h +30 -2
  90. esphome/components/light/__init__.py +3 -4
  91. esphome/components/lock/__init__.py +3 -4
  92. esphome/components/lvgl/__init__.py +16 -5
  93. esphome/components/lvgl/defines.py +1 -0
  94. esphome/components/lvgl/hello_world.py +64 -0
  95. esphome/components/lvgl/lv_validation.py +159 -3
  96. esphome/components/lvgl/lvgl_esphome.cpp +0 -43
  97. esphome/components/lvgl/lvgl_esphome.h +0 -4
  98. esphome/components/lvgl/styles.py +3 -2
  99. esphome/components/lvgl/text/__init__.py +3 -3
  100. esphome/components/lvgl/widgets/__init__.py +2 -0
  101. esphome/components/lvgl/widgets/animimg.py +3 -4
  102. esphome/components/lvgl/widgets/dropdown.py +5 -1
  103. esphome/components/lvgl/widgets/meter.py +16 -11
  104. esphome/components/md5/__init__.py +6 -0
  105. esphome/components/md5/md5.cpp +2 -0
  106. esphome/components/md5/md5.h +2 -0
  107. esphome/components/micro_wake_word/__init__.py +7 -0
  108. esphome/components/mics_4514/sensor.py +11 -26
  109. esphome/components/modbus_controller/__init__.py +7 -5
  110. esphome/components/modbus_controller/binary_sensor/__init__.py +6 -6
  111. esphome/components/modbus_controller/number/__init__.py +5 -6
  112. esphome/components/modbus_controller/output/__init__.py +10 -14
  113. esphome/components/modbus_controller/select/__init__.py +1 -1
  114. esphome/components/modbus_controller/sensor/__init__.py +7 -7
  115. esphome/components/modbus_controller/switch/__init__.py +6 -7
  116. esphome/components/modbus_controller/text_sensor/__init__.py +8 -9
  117. esphome/components/mqtt/__init__.py +3 -0
  118. esphome/components/mqtt/mqtt_client.cpp +2 -0
  119. esphome/components/mqtt/mqtt_client.h +2 -0
  120. esphome/components/nau7802/__init__.py +0 -0
  121. esphome/components/nau7802/nau7802.cpp +323 -0
  122. esphome/components/nau7802/nau7802.h +121 -0
  123. esphome/components/nau7802/sensor.py +134 -0
  124. esphome/components/nextion/base_component.py +1 -0
  125. esphome/components/nextion/display.py +4 -0
  126. esphome/components/nextion/nextion.cpp +19 -4
  127. esphome/components/nextion/nextion.h +16 -0
  128. esphome/components/npi19/__init__.py +0 -0
  129. esphome/components/npi19/npi19.cpp +111 -0
  130. esphome/components/npi19/npi19.h +30 -0
  131. esphome/components/npi19/sensor.py +52 -0
  132. esphome/components/number/__init__.py +3 -5
  133. esphome/components/online_image/__init__.py +1 -1
  134. esphome/components/online_image/online_image.h +1 -2
  135. esphome/components/opentherm/__init__.py +57 -0
  136. esphome/components/opentherm/hub.cpp +277 -0
  137. esphome/components/opentherm/hub.h +110 -0
  138. esphome/components/opentherm/opentherm.cpp +568 -0
  139. esphome/components/opentherm/opentherm.h +347 -0
  140. esphome/components/pulse_counter/pulse_counter_sensor.cpp +8 -1
  141. esphome/components/pulse_counter/pulse_counter_sensor.h +1 -0
  142. esphome/components/radon_eye_ble/radon_eye_listener.cpp +10 -3
  143. esphome/components/remote_transmitter/__init__.py +18 -2
  144. esphome/components/remote_transmitter/remote_transmitter.h +6 -0
  145. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +2 -0
  146. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
  147. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
  148. esphome/components/select/__init__.py +3 -4
  149. esphome/components/sensor/__init__.py +3 -4
  150. esphome/components/shelly_dimmer/shelly_dimmer.cpp +32 -32
  151. esphome/components/shelly_dimmer/shelly_dimmer.h +2 -0
  152. esphome/components/st7701s/st7701s.cpp +21 -8
  153. esphome/components/st7701s/st7701s.h +2 -0
  154. esphome/components/switch/__init__.py +3 -4
  155. esphome/components/tca9555/__init__.py +72 -0
  156. esphome/components/tca9555/tca9555.cpp +140 -0
  157. esphome/components/tca9555/tca9555.h +64 -0
  158. esphome/components/tcs34725/tcs34725.cpp +62 -64
  159. esphome/components/tem3200/__init__.py +0 -0
  160. esphome/components/tem3200/sensor.py +55 -0
  161. esphome/components/tem3200/tem3200.cpp +151 -0
  162. esphome/components/tem3200/tem3200.h +30 -0
  163. esphome/components/template/binary_sensor/__init__.py +19 -6
  164. esphome/components/text/__init__.py +3 -4
  165. esphome/components/text_sensor/__init__.py +3 -4
  166. esphome/components/thermostat/climate.py +11 -9
  167. esphome/components/thermostat/thermostat_climate.cpp +21 -15
  168. esphome/components/tm1638/binary_sensor/__init__.py +3 -2
  169. esphome/components/tm1638/display.py +5 -5
  170. esphome/components/tm1638/output/__init__.py +3 -2
  171. esphome/components/tm1638/switch/__init__.py +3 -2
  172. esphome/components/touchscreen/touchscreen.cpp +2 -2
  173. esphome/components/update/__init__.py +3 -4
  174. esphome/components/valve/__init__.py +3 -4
  175. esphome/components/web_server/__init__.py +78 -22
  176. esphome/components/web_server/server_index_v3.h +3989 -3979
  177. esphome/components/web_server/web_server.cpp +219 -34
  178. esphome/components/web_server/web_server.h +10 -1
  179. esphome/components/wifi/wifi_component_esp_idf.cpp +4 -5
  180. esphome/config_validation.py +1 -0
  181. esphome/const.py +12 -2
  182. esphome/core/defines.h +4 -2
  183. esphome/core/helpers.cpp +46 -10
  184. esphome/core/helpers.h +8 -0
  185. esphome/core/ring_buffer.cpp +12 -2
  186. esphome/core/ring_buffer.h +3 -0
  187. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/METADATA +5 -3
  188. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/RECORD +192 -143
  189. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/LICENSE +0 -0
  190. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/WHEEL +0 -0
  191. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/entry_points.txt +0 -0
  192. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/top_level.txt +0 -0
@@ -9,6 +9,10 @@
9
9
  #include "esphome/components/esp32_ble_server/ble_server.h"
10
10
  #include "esphome/components/wifi/wifi_component.h"
11
11
 
12
+ #ifdef USE_ESP32_IMPROV_STATE_CALLBACK
13
+ #include "esphome/core/automation.h"
14
+ #endif
15
+
12
16
  #ifdef USE_BINARY_SENSOR
13
17
  #include "esphome/components/binary_sensor/binary_sensor.h"
14
18
  #endif
@@ -42,6 +46,11 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
42
46
  void stop() override;
43
47
  bool is_active() const { return this->state_ != improv::STATE_STOPPED; }
44
48
 
49
+ #ifdef USE_ESP32_IMPROV_STATE_CALLBACK
50
+ void add_on_state_callback(std::function<void(improv::State, improv::Error)> &&callback) {
51
+ this->state_callback_.add(std::move(callback));
52
+ }
53
+ #endif
45
54
  #ifdef USE_BINARY_SENSOR
46
55
  void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; }
47
56
  #endif
@@ -54,6 +63,9 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
54
63
  void set_wifi_timeout(uint32_t wifi_timeout) { this->wifi_timeout_ = wifi_timeout; }
55
64
  uint32_t get_wifi_timeout() const { return this->wifi_timeout_; }
56
65
 
66
+ improv::State get_improv_state() const { return this->state_; }
67
+ improv::Error get_improv_error_state() const { return this->error_state_; }
68
+
57
69
  protected:
58
70
  bool should_start_{false};
59
71
  bool setup_complete_{false};
@@ -84,6 +96,9 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
84
96
 
85
97
  improv::State state_{improv::STATE_STOPPED};
86
98
  improv::Error error_state_{improv::ERROR_NONE};
99
+ #ifdef USE_ESP32_IMPROV_STATE_CALLBACK
100
+ CallbackManager<void(improv::State, improv::Error)> state_callback_{};
101
+ #endif
87
102
 
88
103
  bool status_indicator_state_{false};
89
104
  void set_status_indicator_state_(bool state);
@@ -59,6 +59,7 @@ ETHERNET_TYPES = {
59
59
  "KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081,
60
60
  "KSZ8081RNA": EthernetType.ETHERNET_TYPE_KSZ8081RNA,
61
61
  "W5500": EthernetType.ETHERNET_TYPE_W5500,
62
+ "OPENETH": EthernetType.ETHERNET_TYPE_OPENETH,
62
63
  }
63
64
 
64
65
  SPI_ETHERNET_TYPES = ["W5500"]
@@ -171,6 +172,7 @@ CONFIG_SCHEMA = cv.All(
171
172
  "KSZ8081": RMII_SCHEMA,
172
173
  "KSZ8081RNA": RMII_SCHEMA,
173
174
  "W5500": SPI_SCHEMA,
175
+ "OPENETH": BASE_SCHEMA,
174
176
  },
175
177
  upper=True,
176
178
  ),
@@ -240,6 +242,9 @@ async def to_code(config):
240
242
  if CORE.using_esp_idf:
241
243
  add_idf_sdkconfig_option("CONFIG_ETH_USE_SPI_ETHERNET", True)
242
244
  add_idf_sdkconfig_option("CONFIG_ETH_SPI_ETHERNET_W5500", True)
245
+ elif config[CONF_TYPE] == "OPENETH":
246
+ cg.add_define("USE_ETHERNET_OPENETH")
247
+ add_idf_sdkconfig_option("CONFIG_ETH_USE_OPENETH", True)
243
248
  else:
244
249
  cg.add(var.set_phy_addr(config[CONF_PHY_ADDR]))
245
250
  cg.add(var.set_mdc_pin(config[CONF_MDC_PIN]))
@@ -120,6 +120,8 @@ void EthernetComponent::setup() {
120
120
  phy_config.reset_gpio_num = this->reset_pin_;
121
121
 
122
122
  esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
123
+ #elif defined(USE_ETHERNET_OPENETH)
124
+ esp_eth_mac_t *mac = esp_eth_mac_new_openeth(&mac_config);
123
125
  #else
124
126
  phy_config.phy_addr = this->phy_addr_;
125
127
  phy_config.reset_gpio_num = this->power_pin_;
@@ -143,6 +145,13 @@ void EthernetComponent::setup() {
143
145
  #endif
144
146
 
145
147
  switch (this->type_) {
148
+ #ifdef USE_ETHERNET_OPENETH
149
+ case ETHERNET_TYPE_OPENETH: {
150
+ phy_config.autonego_timeout_ms = 1000;
151
+ this->phy_ = esp_eth_phy_new_dp83848(&phy_config);
152
+ break;
153
+ }
154
+ #endif
146
155
  #if CONFIG_ETH_USE_ESP32_EMAC
147
156
  case ETHERNET_TYPE_LAN8720: {
148
157
  this->phy_ = esp_eth_phy_new_lan87xx(&phy_config);
@@ -302,6 +311,10 @@ void EthernetComponent::dump_config() {
302
311
  eth_type = "W5500";
303
312
  break;
304
313
 
314
+ case ETHERNET_TYPE_OPENETH:
315
+ eth_type = "OPENETH";
316
+ break;
317
+
305
318
  default:
306
319
  eth_type = "Unknown";
307
320
  break;
@@ -25,6 +25,7 @@ enum EthernetType {
25
25
  ETHERNET_TYPE_KSZ8081,
26
26
  ETHERNET_TYPE_KSZ8081RNA,
27
27
  ETHERNET_TYPE_W5500,
28
+ ETHERNET_TYPE_OPENETH,
28
29
  };
29
30
 
30
31
  struct ManualIP {
@@ -1,6 +1,6 @@
1
1
  from esphome import automation
2
2
  import esphome.codegen as cg
3
- from esphome.components import mqtt
3
+ from esphome.components import mqtt, web_server
4
4
  import esphome.config_validation as cv
5
5
  from esphome.const import (
6
6
  CONF_DEVICE_CLASS,
@@ -11,6 +11,7 @@ from esphome.const import (
11
11
  CONF_MQTT_ID,
12
12
  CONF_ON_EVENT,
13
13
  CONF_TRIGGER_ID,
14
+ CONF_WEB_SERVER,
14
15
  DEVICE_CLASS_BUTTON,
15
16
  DEVICE_CLASS_DOORBELL,
16
17
  DEVICE_CLASS_EMPTY,
@@ -40,17 +41,21 @@ EventTrigger = event_ns.class_("EventTrigger", automation.Trigger.template())
40
41
 
41
42
  validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
42
43
 
43
- EVENT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
44
- {
45
- cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent),
46
- cv.GenerateID(): cv.declare_id(Event),
47
- cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
48
- cv.Optional(CONF_ON_EVENT): automation.validate_automation(
49
- {
50
- cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger),
51
- }
52
- ),
53
- }
44
+ EVENT_SCHEMA = (
45
+ cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
46
+ .extend(cv.MQTT_COMPONENT_SCHEMA)
47
+ .extend(
48
+ {
49
+ cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent),
50
+ cv.GenerateID(): cv.declare_id(Event),
51
+ cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
52
+ cv.Optional(CONF_ON_EVENT): automation.validate_automation(
53
+ {
54
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger),
55
+ }
56
+ ),
57
+ }
58
+ )
54
59
  )
55
60
 
56
61
  _UNDEF = object()
@@ -97,6 +102,9 @@ async def setup_event_core_(var, config, *, event_types: list[str]):
97
102
  mqtt_ = cg.new_Pvariable(mqtt_id, var)
98
103
  await mqtt.register_mqtt_component(mqtt_, config)
99
104
 
105
+ if web_server_config := config.get(CONF_WEB_SERVER):
106
+ await web_server.add_entity_config(var, web_server_config)
107
+
100
108
 
101
109
  async def register_event(var, config, *, event_types: list[str]):
102
110
  if not CORE.has_id(config[CONF_ID]):
@@ -25,7 +25,7 @@ from esphome.const import (
25
25
  CONF_SPEED_LEVEL_STATE_TOPIC,
26
26
  CONF_SPEED_STATE_TOPIC,
27
27
  CONF_TRIGGER_ID,
28
- CONF_WEB_SERVER_ID,
28
+ CONF_WEB_SERVER,
29
29
  )
30
30
  from esphome.core import CORE, coroutine_with_priority
31
31
  from esphome.cpp_helpers import setup_entity
@@ -218,9 +218,8 @@ async def setup_fan_core_(var, config):
218
218
  if (speed_command_topic := config.get(CONF_SPEED_COMMAND_TOPIC)) is not None:
219
219
  cg.add(mqtt_.set_custom_speed_command_topic(speed_command_topic))
220
220
 
221
- if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
222
- web_server_ = await cg.get_variable(webserver_id)
223
- web_server.add_entity_to_sorting_list(web_server_, var, config)
221
+ if web_server_config := config.get(CONF_WEB_SERVER):
222
+ await web_server.add_entity_config(var, web_server_config)
224
223
 
225
224
  for conf in config.get(CONF_ON_STATE, []):
226
225
  trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
File without changes
@@ -0,0 +1,67 @@
1
+ #include "gp2y1010au0f.h"
2
+ #include "esphome/core/hal.h"
3
+ #include "esphome/core/log.h"
4
+
5
+ #include <cinttypes>
6
+
7
+ namespace esphome {
8
+ namespace gp2y1010au0f {
9
+
10
+ static const char *const TAG = "gp2y1010au0f";
11
+ static const float MIN_VOLTAGE = 0.0f;
12
+ static const float MAX_VOLTAGE = 4.0f;
13
+
14
+ void GP2Y1010AU0FSensor::dump_config() {
15
+ LOG_SENSOR("", "Sharp GP2Y1010AU0F PM2.5 Sensor", this);
16
+ ESP_LOGCONFIG(TAG, " Sampling duration: %" PRId32 " ms", this->sample_duration_);
17
+ ESP_LOGCONFIG(TAG, " ADC voltage multiplier: %.3f", this->voltage_multiplier_);
18
+ LOG_UPDATE_INTERVAL(this);
19
+ }
20
+
21
+ void GP2Y1010AU0FSensor::update() {
22
+ is_sampling_ = true;
23
+
24
+ this->set_timeout("read", this->sample_duration_, [this]() {
25
+ this->is_sampling_ = false;
26
+ if (this->num_samples_ == 0)
27
+ return;
28
+
29
+ float mean = this->sample_sum_ / float(this->num_samples_);
30
+ ESP_LOGD(TAG, "ADC read voltage: %.3f V (mean from %" PRId32 " samples)", mean, this->num_samples_);
31
+
32
+ // PM2.5 calculation
33
+ // ref: https://www.howmuchsnow.com/arduino/airquality/
34
+ int16_t pm_2_5_value = 170 * mean;
35
+ this->publish_state(pm_2_5_value);
36
+ });
37
+
38
+ // reset readings
39
+ this->num_samples_ = 0;
40
+ this->sample_sum_ = 0.0f;
41
+ }
42
+
43
+ void GP2Y1010AU0FSensor::loop() {
44
+ if (!this->is_sampling_)
45
+ return;
46
+
47
+ // enable the internal IR LED
48
+ this->led_output_->turn_on();
49
+ // wait for the sensor to stabilize
50
+ delayMicroseconds(this->sample_wait_before_);
51
+ // perform a single sample
52
+ float read_voltage = this->source_->sample();
53
+ // disable the internal IR LED
54
+ this->led_output_->turn_off();
55
+
56
+ if (std::isnan(read_voltage))
57
+ return;
58
+ read_voltage = read_voltage * this->voltage_multiplier_ - this->voltage_offset_;
59
+ if (read_voltage < MIN_VOLTAGE || read_voltage > MAX_VOLTAGE)
60
+ return;
61
+
62
+ this->num_samples_++;
63
+ this->sample_sum_ += read_voltage;
64
+ }
65
+
66
+ } // namespace gp2y1010au0f
67
+ } // namespace esphome
@@ -0,0 +1,52 @@
1
+ #pragma once
2
+
3
+ #include "esphome/core/component.h"
4
+ #include "esphome/components/sensor/sensor.h"
5
+ #include "esphome/components/voltage_sampler/voltage_sampler.h"
6
+ #include "esphome/components/output/binary_output.h"
7
+
8
+ namespace esphome {
9
+ namespace gp2y1010au0f {
10
+
11
+ class GP2Y1010AU0FSensor : public sensor::Sensor, public PollingComponent {
12
+ public:
13
+ void update() override;
14
+ void loop() override;
15
+ void dump_config() override;
16
+ float get_setup_priority() const override {
17
+ // after the base sensor has been initialized
18
+ return setup_priority::DATA - 1.0f;
19
+ }
20
+
21
+ void set_adc_source(voltage_sampler::VoltageSampler *source) { source_ = source; }
22
+ void set_voltage_refs(float offset, float multiplier) {
23
+ this->voltage_offset_ = offset;
24
+ this->voltage_multiplier_ = multiplier;
25
+ }
26
+ void set_led_output(output::BinaryOutput *output) { led_output_ = output; }
27
+
28
+ protected:
29
+ // duration in ms of the sampling phase
30
+ uint32_t sample_duration_ = 100;
31
+ // duration in us of the wait before sampling
32
+ // ref: https://global.sharp/products/device/lineup/data/pdf/datasheet/gp2y1010au_appl_e.pdf
33
+ uint32_t sample_wait_before_ = 280;
34
+ // duration in us of the wait after sampling
35
+ // it seems no need to delay on purpose since one ADC sampling takes longer than that (300-400 us on ESP8266)
36
+ // uint32_t sample_wait_after_ = 40;
37
+ // the sampling source to read voltage from
38
+ voltage_sampler::VoltageSampler *source_;
39
+ // ADC voltage reading offset
40
+ float voltage_offset_ = 0.0f;
41
+ // ADC voltage reading multiplier
42
+ float voltage_multiplier_ = 1.0f;
43
+ // the binary output to control the sampling LED
44
+ output::BinaryOutput *led_output_;
45
+
46
+ float sample_sum_ = 0.0f;
47
+ uint32_t num_samples_ = 0;
48
+ bool is_sampling_ = false;
49
+ };
50
+
51
+ } // namespace gp2y1010au0f
52
+ } // namespace esphome
@@ -0,0 +1,61 @@
1
+ import esphome.codegen as cg
2
+ import esphome.config_validation as cv
3
+ from esphome.components import sensor, voltage_sampler, output
4
+ from esphome.const import (
5
+ CONF_SENSOR,
6
+ CONF_OUTPUT,
7
+ DEVICE_CLASS_PM25,
8
+ STATE_CLASS_MEASUREMENT,
9
+ UNIT_MICROGRAMS_PER_CUBIC_METER,
10
+ ICON_CHEMICAL_WEAPON,
11
+ )
12
+
13
+ DEPENDENCIES = ["output"]
14
+ AUTO_LOAD = ["voltage_sampler"]
15
+ CODEOWNERS = ["@zry98"]
16
+
17
+ CONF_ADC_VOLTAGE_OFFSET = "adc_voltage_offset"
18
+ CONF_ADC_VOLTAGE_MULTIPLIER = "adc_voltage_multiplier"
19
+
20
+ gp2y1010au0f_ns = cg.esphome_ns.namespace("gp2y1010au0f")
21
+ GP2Y1010AU0FSensor = gp2y1010au0f_ns.class_(
22
+ "GP2Y1010AU0FSensor", sensor.Sensor, cg.PollingComponent
23
+ )
24
+
25
+ CONFIG_SCHEMA = (
26
+ sensor.sensor_schema(
27
+ GP2Y1010AU0FSensor,
28
+ unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
29
+ accuracy_decimals=0,
30
+ device_class=DEVICE_CLASS_PM25,
31
+ state_class=STATE_CLASS_MEASUREMENT,
32
+ icon=ICON_CHEMICAL_WEAPON,
33
+ )
34
+ .extend(
35
+ {
36
+ cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler),
37
+ cv.Optional(CONF_ADC_VOLTAGE_OFFSET, default=0.0): cv.float_,
38
+ cv.Optional(CONF_ADC_VOLTAGE_MULTIPLIER, default=1.0): cv.float_,
39
+ cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
40
+ }
41
+ )
42
+ .extend(cv.polling_component_schema("60s"))
43
+ )
44
+
45
+
46
+ async def to_code(config):
47
+ var = await sensor.new_sensor(config)
48
+ await cg.register_component(var, config)
49
+
50
+ # the ADC sensor to read voltage from
51
+ adc_sensor = await cg.get_variable(config[CONF_SENSOR])
52
+ cg.add(var.set_adc_source(adc_sensor))
53
+ cg.add(
54
+ var.set_voltage_refs(
55
+ config[CONF_ADC_VOLTAGE_OFFSET], config[CONF_ADC_VOLTAGE_MULTIPLIER]
56
+ )
57
+ )
58
+
59
+ # the binary output to control the module's internal IR LED
60
+ led_output = await cg.get_variable(config[CONF_OUTPUT])
61
+ cg.add(var.set_led_output(led_output))
File without changes
@@ -0,0 +1,38 @@
1
+ #pragma once
2
+
3
+ #include <array>
4
+ #include <cstdint>
5
+ #include "esphome/core/hal.h"
6
+
7
+ namespace esphome {
8
+ namespace gpio_expander {
9
+
10
+ /// @brief A class to cache the read state of a GPIO expander.
11
+ template<typename T, T N> class CachedGpioExpander {
12
+ public:
13
+ bool digital_read(T pin) {
14
+ if (!this->read_cache_invalidated_[pin]) {
15
+ this->read_cache_invalidated_[pin] = true;
16
+ return this->digital_read_cache(pin);
17
+ }
18
+ return this->digital_read_hw(pin);
19
+ }
20
+
21
+ void digital_write(T pin, bool value) { this->digital_write_hw(pin, value); }
22
+
23
+ protected:
24
+ virtual bool digital_read_hw(T pin) = 0;
25
+ virtual bool digital_read_cache(T pin) = 0;
26
+ virtual void digital_write_hw(T pin, bool value) = 0;
27
+
28
+ void reset_pin_cache_() {
29
+ for (T i = 0; i < N; i++) {
30
+ this->read_cache_invalidated_[i] = false;
31
+ }
32
+ }
33
+
34
+ std::array<bool, N> read_cache_invalidated_{};
35
+ };
36
+
37
+ } // namespace gpio_expander
38
+ } // namespace esphome
File without changes
@@ -0,0 +1,88 @@
1
+ #include "grove_gas_mc_v2.h"
2
+ #include "esphome/core/hal.h"
3
+ #include "esphome/core/log.h"
4
+
5
+ namespace esphome {
6
+ namespace grove_gas_mc_v2 {
7
+
8
+ static const char *const TAG = "grove_gas_mc_v2";
9
+
10
+ // I2C Commands for Grove Gas Multichannel V2 Sensor
11
+ // Taken from:
12
+ // https://github.com/Seeed-Studio/Seeed_Arduino_MultiGas/blob/master/src/Multichannel_Gas_GroveGasMultichannelV2.h
13
+ static const uint8_t GROVE_GAS_MC_V2_HEAT_ON = 0xFE;
14
+ static const uint8_t GROVE_GAS_MC_V2_HEAT_OFF = 0xFF;
15
+ static const uint8_t GROVE_GAS_MC_V2_READ_GM102B = 0x01;
16
+ static const uint8_t GROVE_GAS_MC_V2_READ_GM302B = 0x03;
17
+ static const uint8_t GROVE_GAS_MC_V2_READ_GM502B = 0x05;
18
+ static const uint8_t GROVE_GAS_MC_V2_READ_GM702B = 0x07;
19
+
20
+ bool GroveGasMultichannelV2Component::read_sensor_(uint8_t address, sensor::Sensor *sensor) {
21
+ if (sensor == nullptr) {
22
+ return true;
23
+ }
24
+ uint32_t value = 0;
25
+ if (!this->read_bytes(address, (uint8_t *) &value, 4)) {
26
+ ESP_LOGW(TAG, "Reading Grove Gas Sensor data failed!");
27
+ this->error_code_ = COMMUNICATION_FAILED;
28
+ this->status_set_warning();
29
+ return false;
30
+ }
31
+ sensor->publish_state(value);
32
+ return true;
33
+ }
34
+
35
+ void GroveGasMultichannelV2Component::setup() {
36
+ ESP_LOGCONFIG(TAG, "Setting up Grove Multichannel Gas Sensor V2...");
37
+
38
+ // Before reading sensor values, must preheat sensor
39
+ if (!(this->write_bytes(GROVE_GAS_MC_V2_HEAT_ON, {}))) {
40
+ this->mark_failed();
41
+ this->error_code_ = APP_START_FAILED;
42
+ }
43
+ }
44
+
45
+ void GroveGasMultichannelV2Component::update() {
46
+ // Read from each of the gas sensors
47
+ if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM102B, this->nitrogen_dioxide_sensor_))
48
+ return;
49
+ if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM302B, this->ethanol_sensor_))
50
+ return;
51
+ if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM502B, this->tvoc_sensor_))
52
+ return;
53
+ if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM702B, this->carbon_monoxide_sensor_))
54
+ return;
55
+
56
+ this->status_clear_warning();
57
+ }
58
+
59
+ void GroveGasMultichannelV2Component::dump_config() {
60
+ ESP_LOGCONFIG(TAG, "Grove Multichannel Gas Sensor V2");
61
+ LOG_I2C_DEVICE(this)
62
+ LOG_UPDATE_INTERVAL(this)
63
+ LOG_SENSOR(" ", "Nitrogen Dioxide", this->nitrogen_dioxide_sensor_)
64
+ LOG_SENSOR(" ", "Ethanol", this->ethanol_sensor_)
65
+ LOG_SENSOR(" ", "Carbon Monoxide", this->carbon_monoxide_sensor_)
66
+ LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_)
67
+
68
+ if (this->is_failed()) {
69
+ switch (this->error_code_) {
70
+ case COMMUNICATION_FAILED:
71
+ ESP_LOGW(TAG, "Communication failed! Is the sensor connected?");
72
+ break;
73
+ case APP_INVALID:
74
+ ESP_LOGW(TAG, "Sensor reported invalid APP installed.");
75
+ break;
76
+ case APP_START_FAILED:
77
+ ESP_LOGW(TAG, "Sensor reported APP start failed.");
78
+ break;
79
+ case UNKNOWN:
80
+ default:
81
+ ESP_LOGW(TAG, "Unknown setup error!");
82
+ break;
83
+ }
84
+ }
85
+ }
86
+
87
+ } // namespace grove_gas_mc_v2
88
+ } // namespace esphome
@@ -0,0 +1,39 @@
1
+ #pragma once
2
+
3
+ #include "esphome/components/i2c/i2c.h"
4
+ #include "esphome/components/sensor/sensor.h"
5
+ #include "esphome/core/component.h"
6
+ #include "esphome/core/preferences.h"
7
+
8
+ namespace esphome {
9
+ namespace grove_gas_mc_v2 {
10
+
11
+ class GroveGasMultichannelV2Component : public PollingComponent, public i2c::I2CDevice {
12
+ SUB_SENSOR(tvoc)
13
+ SUB_SENSOR(carbon_monoxide)
14
+ SUB_SENSOR(nitrogen_dioxide)
15
+ SUB_SENSOR(ethanol)
16
+
17
+ public:
18
+ /// Setup the sensor and test for a connection.
19
+ void setup() override;
20
+ /// Schedule temperature+pressure readings.
21
+ void update() override;
22
+
23
+ void dump_config() override;
24
+
25
+ float get_setup_priority() const override { return setup_priority::DATA; }
26
+
27
+ protected:
28
+ enum ErrorCode {
29
+ UNKNOWN,
30
+ COMMUNICATION_FAILED,
31
+ APP_INVALID,
32
+ APP_START_FAILED,
33
+ } error_code_{UNKNOWN};
34
+
35
+ bool read_sensor_(uint8_t address, sensor::Sensor *sensor);
36
+ };
37
+
38
+ } // namespace grove_gas_mc_v2
39
+ } // namespace esphome
@@ -0,0 +1,77 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components import i2c, sensor
3
+ import esphome.config_validation as cv
4
+ from esphome.const import (
5
+ CONF_CARBON_MONOXIDE,
6
+ CONF_ETHANOL,
7
+ CONF_ID,
8
+ CONF_NITROGEN_DIOXIDE,
9
+ CONF_TVOC,
10
+ DEVICE_CLASS_CARBON_MONOXIDE,
11
+ DEVICE_CLASS_NITROGEN_DIOXIDE,
12
+ DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
13
+ ICON_AIR_FILTER,
14
+ ICON_FLASK_ROUND_BOTTOM,
15
+ ICON_GAS_CYLINDER,
16
+ ICON_MOLECULE_CO,
17
+ STATE_CLASS_MEASUREMENT,
18
+ UNIT_MICROGRAMS_PER_CUBIC_METER,
19
+ UNIT_PARTS_PER_MILLION,
20
+ )
21
+
22
+ CODEOWNERS = ["@YorkshireIoT"]
23
+ DEPENDENCIES = ["i2c"]
24
+
25
+ grove_gas_mc_v2_ns = cg.esphome_ns.namespace("grove_gas_mc_v2")
26
+
27
+ GroveGasMultichannelV2Component = grove_gas_mc_v2_ns.class_(
28
+ "GroveGasMultichannelV2Component", cg.PollingComponent, i2c.I2CDevice
29
+ )
30
+
31
+ CONFIG_SCHEMA = (
32
+ cv.Schema(
33
+ {
34
+ cv.GenerateID(): cv.declare_id(GroveGasMultichannelV2Component),
35
+ cv.Optional(CONF_TVOC): sensor.sensor_schema(
36
+ unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
37
+ icon=ICON_AIR_FILTER,
38
+ accuracy_decimals=0,
39
+ device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
40
+ state_class=STATE_CLASS_MEASUREMENT,
41
+ ),
42
+ cv.Optional(CONF_CARBON_MONOXIDE): sensor.sensor_schema(
43
+ unit_of_measurement=UNIT_PARTS_PER_MILLION,
44
+ icon=ICON_MOLECULE_CO,
45
+ accuracy_decimals=0,
46
+ device_class=DEVICE_CLASS_CARBON_MONOXIDE,
47
+ state_class=STATE_CLASS_MEASUREMENT,
48
+ ),
49
+ cv.Optional(CONF_NITROGEN_DIOXIDE): sensor.sensor_schema(
50
+ unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
51
+ icon=ICON_GAS_CYLINDER,
52
+ accuracy_decimals=0,
53
+ device_class=DEVICE_CLASS_NITROGEN_DIOXIDE,
54
+ state_class=STATE_CLASS_MEASUREMENT,
55
+ ),
56
+ cv.Optional(CONF_ETHANOL): sensor.sensor_schema(
57
+ unit_of_measurement=UNIT_PARTS_PER_MILLION,
58
+ icon=ICON_FLASK_ROUND_BOTTOM,
59
+ accuracy_decimals=0,
60
+ state_class=STATE_CLASS_MEASUREMENT,
61
+ ),
62
+ }
63
+ )
64
+ .extend(cv.polling_component_schema("60s"))
65
+ .extend(i2c.i2c_device_schema(0x08))
66
+ )
67
+
68
+
69
+ async def to_code(config):
70
+ var = cg.new_Pvariable(config[CONF_ID])
71
+ await cg.register_component(var, config)
72
+ await i2c.register_i2c_device(var, config)
73
+
74
+ for key in [CONF_TVOC, CONF_CARBON_MONOXIDE, CONF_NITROGEN_DIOXIDE, CONF_ETHANOL]:
75
+ if sensor_config := config.get(key):
76
+ sensor_ = await sensor.new_sensor(sensor_config)
77
+ cg.add(getattr(var, f"set_{key}_sensor")(sensor_))
@@ -114,7 +114,6 @@ SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = {
114
114
  SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = {
115
115
  "AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
116
116
  "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
117
- "ECO": ClimatePreset.CLIMATE_PRESET_ECO,
118
117
  "SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
119
118
  }
120
119
 
@@ -240,7 +239,9 @@ CONFIG_SCHEMA = cv.All(
240
239
  ): cv.ensure_list(
241
240
  cv.enum(SUPPORTED_HON_CONTROL_METHODS, upper=True)
242
241
  ),
243
- cv.Optional(CONF_BEEPER, default=True): cv.boolean,
242
+ cv.Optional(CONF_BEEPER): cv.invalid(
243
+ f"The {CONF_BEEPER} option is deprecated, use beeper_on/beeper_off actions or beeper switch for a haier platform instead"
244
+ ),
244
245
  cv.Optional(
245
246
  CONF_CONTROL_PACKET_SIZE, default=PROTOCOL_CONTROL_PACKET_SIZE
246
247
  ): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50),
@@ -254,7 +255,7 @@ CONFIG_SCHEMA = cv.All(
254
255
  ): cv.int_range(min=PROTOCOL_STATUS_MESSAGE_HEADER_SIZE),
255
256
  cv.Optional(
256
257
  CONF_SUPPORTED_PRESETS,
257
- default=["BOOST", "ECO", "SLEEP"], # No AWAY by default
258
+ default=["BOOST", "SLEEP"], # No AWAY by default
258
259
  ): cv.ensure_list(
259
260
  cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True)
260
261
  ),