esphome 2025.8.4__py3-none-any.whl → 2025.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (369) hide show
  1. esphome/__main__.py +177 -105
  2. esphome/components/absolute_humidity/absolute_humidity.cpp +3 -5
  3. esphome/components/adc/__init__.py +1 -26
  4. esphome/components/adc/adc_sensor_esp32.cpp +29 -6
  5. esphome/components/adc/sensor.py +20 -0
  6. esphome/components/ade7880/ade7880.cpp +1 -1
  7. esphome/components/ags10/ags10.cpp +3 -18
  8. esphome/components/ags10/ags10.h +2 -12
  9. esphome/components/aht10/aht10.cpp +3 -3
  10. esphome/components/airthings_ble/__init__.py +2 -2
  11. esphome/components/alarm_control_panel/__init__.py +2 -2
  12. esphome/components/am2315c/am2315c.cpp +1 -17
  13. esphome/components/am2315c/am2315c.h +2 -3
  14. esphome/components/api/__init__.py +2 -2
  15. esphome/components/api/api_connection.cpp +38 -34
  16. esphome/components/api/api_connection.h +20 -40
  17. esphome/components/api/api_frame_helper.cpp +25 -25
  18. esphome/components/api/api_frame_helper.h +3 -3
  19. esphome/components/api/api_frame_helper_noise.cpp +75 -40
  20. esphome/components/api/api_frame_helper_noise.h +3 -7
  21. esphome/components/api/api_frame_helper_plaintext.cpp +17 -4
  22. esphome/components/api/api_frame_helper_plaintext.h +1 -4
  23. esphome/components/api/api_pb2.cpp +12 -2
  24. esphome/components/api/api_pb2.h +144 -143
  25. esphome/components/api/api_pb2_dump.cpp +6 -1
  26. esphome/components/api/api_pb2_service.cpp +0 -14
  27. esphome/components/api/api_pb2_service.h +1 -3
  28. esphome/components/api/client.py +5 -3
  29. esphome/components/api/proto.cpp +33 -37
  30. esphome/components/async_tcp/__init__.py +2 -2
  31. esphome/components/atm90e26/sensor.py +2 -0
  32. esphome/components/atm90e32/sensor.py +4 -2
  33. esphome/components/audio_adc/__init__.py +2 -2
  34. esphome/components/audio_dac/__init__.py +2 -2
  35. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +1 -1
  36. esphome/components/bedjet/bedjet_hub.cpp +1 -1
  37. esphome/components/binary_sensor/__init__.py +2 -2
  38. esphome/components/binary_sensor/binary_sensor.cpp +13 -0
  39. esphome/components/binary_sensor/binary_sensor.h +4 -7
  40. esphome/components/bl0940/__init__.py +6 -1
  41. esphome/components/bl0940/bl0940.cpp +178 -41
  42. esphome/components/bl0940/bl0940.h +121 -76
  43. esphome/components/bl0940/button/__init__.py +27 -0
  44. esphome/components/bl0940/button/calibration_reset_button.cpp +20 -0
  45. esphome/components/bl0940/button/calibration_reset_button.h +19 -0
  46. esphome/components/bl0940/number/__init__.py +94 -0
  47. esphome/components/bl0940/number/calibration_number.cpp +29 -0
  48. esphome/components/bl0940/number/calibration_number.h +26 -0
  49. esphome/components/bl0940/sensor.py +151 -2
  50. esphome/components/bl0942/bl0942.cpp +1 -1
  51. esphome/components/ble_client/output/__init__.py +4 -4
  52. esphome/components/bluetooth_proxy/__init__.py +1 -1
  53. esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -1
  54. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +15 -7
  55. esphome/components/bluetooth_proxy/bluetooth_proxy.h +6 -3
  56. esphome/components/button/__init__.py +2 -2
  57. esphome/components/button/button.cpp +13 -0
  58. esphome/components/button/button.h +4 -7
  59. esphome/components/camera/buffer.h +18 -0
  60. esphome/components/camera/buffer_impl.cpp +20 -0
  61. esphome/components/camera/buffer_impl.h +26 -0
  62. esphome/components/camera/camera.h +43 -0
  63. esphome/components/camera/encoder.h +69 -0
  64. esphome/components/camera_encoder/__init__.py +62 -0
  65. esphome/components/camera_encoder/encoder_buffer_impl.cpp +23 -0
  66. esphome/components/camera_encoder/encoder_buffer_impl.h +25 -0
  67. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +82 -0
  68. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +39 -0
  69. esphome/components/captive_portal/__init__.py +2 -2
  70. esphome/components/captive_portal/captive_index.h +77 -97
  71. esphome/components/captive_portal/captive_portal.cpp +35 -12
  72. esphome/components/captive_portal/captive_portal.h +3 -3
  73. esphome/components/ccs811/ccs811.cpp +3 -3
  74. esphome/components/climate/__init__.py +2 -2
  75. esphome/components/climate/climate.cpp +1 -1
  76. esphome/components/cover/__init__.py +5 -5
  77. esphome/components/cover/cover.cpp +1 -1
  78. esphome/components/cover/cover.h +2 -2
  79. esphome/components/dallas_temp/dallas_temp.cpp +2 -2
  80. esphome/components/datetime/__init__.py +2 -2
  81. esphome/components/datetime/date_entity.h +2 -2
  82. esphome/components/datetime/datetime_entity.h +2 -2
  83. esphome/components/datetime/time_entity.h +2 -2
  84. esphome/components/debug/debug_esp32.cpp +1 -1
  85. esphome/components/display/__init__.py +4 -4
  86. esphome/components/duty_time/duty_time_sensor.cpp +1 -1
  87. esphome/components/esp32/__init__.py +0 -5
  88. esphome/components/esp32/gpio.cpp +27 -23
  89. esphome/components/esp32/gpio.h +26 -11
  90. esphome/components/esp32/preferences.cpp +8 -4
  91. esphome/components/esp32_ble/__init__.py +7 -2
  92. esphome/components/esp32_ble/ble_uuid.cpp +30 -9
  93. esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +4 -3
  94. esphome/components/esp32_ble_client/ble_client_base.cpp +7 -3
  95. esphome/components/esp32_ble_client/ble_client_base.h +8 -5
  96. esphome/components/esp32_ble_tracker/__init__.py +2 -2
  97. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +11 -47
  98. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -14
  99. esphome/components/esp8266/__init__.py +2 -2
  100. esphome/components/esp8266/core.cpp +2 -2
  101. esphome/components/esp8266/gpio.py +4 -4
  102. esphome/components/esp8266/preferences.cpp +30 -28
  103. esphome/components/esphome/ota/__init__.py +2 -2
  104. esphome/components/esphome/ota/ota_esphome.cpp +21 -19
  105. esphome/components/esphome/ota/ota_esphome.h +6 -5
  106. esphome/components/ethernet/__init__.py +18 -2
  107. esphome/components/ethernet/ethernet_component.cpp +53 -3
  108. esphome/components/ethernet/ethernet_component.h +4 -0
  109. esphome/components/event/__init__.py +2 -2
  110. esphome/components/event/event.h +4 -4
  111. esphome/components/factory_reset/button/factory_reset_button.cpp +18 -1
  112. esphome/components/factory_reset/button/factory_reset_button.h +6 -1
  113. esphome/components/factory_reset/switch/factory_reset_switch.cpp +18 -1
  114. esphome/components/factory_reset/switch/factory_reset_switch.h +5 -1
  115. esphome/components/fan/__init__.py +2 -2
  116. esphome/components/fan/fan.cpp +2 -1
  117. esphome/components/gdk101/gdk101.cpp +4 -4
  118. esphome/components/globals/__init__.py +2 -2
  119. esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +19 -18
  120. esphome/components/gpio_expander/cached_gpio.h +36 -16
  121. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +5 -5
  122. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +1 -1
  123. esphome/components/haier/haier_base.cpp +1 -1
  124. esphome/components/haier/hon_climate.cpp +1 -1
  125. esphome/components/hlw8012/hlw8012.cpp +5 -5
  126. esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +4 -4
  127. esphome/components/host/preferences.h +3 -2
  128. esphome/components/hte501/hte501.cpp +3 -21
  129. esphome/components/hte501/hte501.h +2 -3
  130. esphome/components/http_request/ota/__init__.py +2 -2
  131. esphome/components/i2c/__init__.py +2 -2
  132. esphome/components/i2c/i2c.cpp +13 -9
  133. esphome/components/i2c/i2c_bus.h +36 -6
  134. esphome/components/i2s_audio/__init__.py +8 -2
  135. esphome/components/i2s_audio/media_player/__init__.py +1 -1
  136. esphome/components/i2s_audio/microphone/__init__.py +1 -1
  137. esphome/components/i2s_audio/speaker/__init__.py +1 -1
  138. esphome/components/ina2xx_base/__init__.py +4 -2
  139. esphome/components/inkplate/__init__.py +1 -0
  140. esphome/components/inkplate/const.py +105 -0
  141. esphome/components/inkplate/display.py +238 -0
  142. esphome/components/{inkplate6 → inkplate}/inkplate.cpp +156 -74
  143. esphome/components/{inkplate6 → inkplate}/inkplate.h +28 -68
  144. esphome/components/inkplate6/__init__.py +0 -1
  145. esphome/components/inkplate6/display.py +2 -211
  146. esphome/components/integration/integration_sensor.cpp +1 -1
  147. esphome/components/json/__init__.py +2 -2
  148. esphome/components/lc709203f/lc709203f.cpp +4 -17
  149. esphome/components/lc709203f/lc709203f.h +2 -3
  150. esphome/components/ld2420/text_sensor/{text_sensor.cpp → ld2420_text_sensor.cpp} +1 -1
  151. esphome/components/ld2450/ld2450.cpp +1 -1
  152. esphome/components/libretiny/preferences.cpp +13 -5
  153. esphome/components/light/__init__.py +2 -2
  154. esphome/components/light/addressable_light_effect.h +7 -0
  155. esphome/components/light/base_light_effects.h +8 -0
  156. esphome/components/light/light_call.cpp +22 -20
  157. esphome/components/light/light_effect.cpp +36 -0
  158. esphome/components/light/light_effect.h +14 -0
  159. esphome/components/light/light_json_schema.cpp +9 -1
  160. esphome/components/light/light_state.cpp +2 -2
  161. esphome/components/light/light_state.h +38 -0
  162. esphome/components/lock/__init__.py +2 -2
  163. esphome/components/lock/lock.h +2 -2
  164. esphome/components/logger/__init__.py +2 -2
  165. esphome/components/logger/logger.cpp +25 -4
  166. esphome/components/logger/logger.h +1 -1
  167. esphome/components/logger/logger_esp32.cpp +16 -8
  168. esphome/components/logger/logger_esp8266.cpp +11 -3
  169. esphome/components/logger/logger_libretiny.cpp +13 -3
  170. esphome/components/logger/logger_rp2040.cpp +14 -3
  171. esphome/components/logger/logger_zephyr.cpp +15 -4
  172. esphome/components/lvgl/defines.py +1 -0
  173. esphome/components/lvgl/hello_world.py +96 -33
  174. esphome/components/lvgl/number/lvgl_number.h +1 -1
  175. esphome/components/lvgl/select/lvgl_select.h +1 -1
  176. esphome/components/lvgl/widgets/__init__.py +0 -1
  177. esphome/components/lvgl/widgets/spinbox.py +20 -11
  178. esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.cpp +1 -1
  179. esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.cpp +1 -1
  180. esphome/components/mapping/__init__.py +13 -5
  181. esphome/components/mapping/mapping.h +69 -0
  182. esphome/components/max17043/max17043.cpp +2 -2
  183. esphome/components/mcp23016/__init__.py +1 -0
  184. esphome/components/mcp23016/mcp23016.cpp +20 -5
  185. esphome/components/mcp23016/mcp23016.h +10 -4
  186. esphome/components/mcp23x08_base/mcp23x08_base.cpp +1 -1
  187. esphome/components/mcp23x17_base/mcp23x17_base.cpp +2 -2
  188. esphome/components/md5/md5.cpp +3 -2
  189. esphome/components/mdns/__init__.py +2 -2
  190. esphome/components/mdns/mdns_component.cpp +145 -54
  191. esphome/components/media_player/__init__.py +2 -2
  192. esphome/components/micro_wake_word/__init__.py +2 -2
  193. esphome/components/microphone/__init__.py +2 -2
  194. esphome/components/mipi/__init__.py +77 -33
  195. esphome/components/mipi_rgb/__init__.py +2 -0
  196. esphome/components/mipi_rgb/display.py +321 -0
  197. esphome/components/mipi_rgb/mipi_rgb.cpp +388 -0
  198. esphome/components/mipi_rgb/mipi_rgb.h +127 -0
  199. esphome/components/mipi_rgb/models/guition.py +24 -0
  200. esphome/components/mipi_rgb/models/lilygo.py +228 -0
  201. esphome/components/mipi_rgb/models/rpi.py +9 -0
  202. esphome/components/mipi_rgb/models/st7701s.py +214 -0
  203. esphome/components/mipi_rgb/models/waveshare.py +64 -0
  204. esphome/components/mipi_spi/models/jc.py +229 -0
  205. esphome/components/mlx90614/mlx90614.cpp +1 -16
  206. esphome/components/mlx90614/mlx90614.h +0 -1
  207. esphome/components/mqtt/__init__.py +2 -2
  208. esphome/components/mqtt/mqtt_client.cpp +1 -1
  209. esphome/components/mqtt/mqtt_sensor.cpp +7 -2
  210. esphome/components/ms5611/ms5611.cpp +7 -6
  211. esphome/components/network/__init__.py +2 -2
  212. esphome/components/nextion/nextion_upload.cpp +4 -1
  213. esphome/components/nrf52/__init__.py +49 -6
  214. esphome/components/nrf52/const.py +1 -0
  215. esphome/components/nrf52/dfu.cpp +51 -0
  216. esphome/components/nrf52/dfu.h +24 -0
  217. esphome/components/ntc/ntc.cpp +1 -1
  218. esphome/components/number/__init__.py +2 -2
  219. esphome/components/number/automation.cpp +1 -1
  220. esphome/components/number/number.cpp +21 -0
  221. esphome/components/number/number.h +4 -13
  222. esphome/components/opentherm/hub.h +6 -6
  223. esphome/components/opentherm/number/{number.cpp → opentherm_number.cpp} +2 -2
  224. esphome/components/opentherm/output/{output.cpp → opentherm_output.cpp} +1 -1
  225. esphome/components/opentherm/switch/{switch.cpp → opentherm_switch.cpp} +1 -1
  226. esphome/components/openthread/openthread.cpp +41 -7
  227. esphome/components/openthread/openthread.h +11 -0
  228. esphome/components/ota/__init__.py +2 -2
  229. esphome/components/pca6416a/__init__.py +1 -0
  230. esphome/components/pca6416a/pca6416a.cpp +20 -5
  231. esphome/components/pca6416a/pca6416a.h +12 -5
  232. esphome/components/pca9554/__init__.py +2 -1
  233. esphome/components/pca9554/pca9554.cpp +12 -18
  234. esphome/components/pca9554/pca9554.h +10 -9
  235. esphome/components/pcf8574/__init__.py +1 -0
  236. esphome/components/pcf8574/pcf8574.cpp +14 -5
  237. esphome/components/pcf8574/pcf8574.h +13 -6
  238. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +7 -7
  239. esphome/components/pipsolar/__init__.py +3 -3
  240. esphome/components/pipsolar/output/__init__.py +4 -4
  241. esphome/components/pulse_width/pulse_width.cpp +2 -2
  242. esphome/components/qmp6988/qmp6988.cpp +81 -126
  243. esphome/components/qmp6988/qmp6988.h +31 -37
  244. esphome/components/radon_eye_ble/__init__.py +2 -2
  245. esphome/components/remote_base/__init__.py +6 -8
  246. esphome/components/rotary_encoder/rotary_encoder.cpp +1 -1
  247. esphome/components/rp2040/__init__.py +2 -2
  248. esphome/components/runtime_stats/runtime_stats.cpp +10 -23
  249. esphome/components/runtime_stats/runtime_stats.h +4 -10
  250. esphome/components/safe_mode/__init__.py +2 -2
  251. esphome/components/safe_mode/safe_mode.cpp +33 -31
  252. esphome/components/script/script.cpp +6 -0
  253. esphome/components/script/script.h +19 -5
  254. esphome/components/sdm_meter/sensor.py +3 -1
  255. esphome/components/select/__init__.py +2 -2
  256. esphome/components/select/select.cpp +3 -3
  257. esphome/components/select/select.h +2 -2
  258. esphome/components/select/select_call.cpp +1 -1
  259. esphome/components/sen5x/sen5x.cpp +57 -55
  260. esphome/components/sen5x/sen5x.h +21 -15
  261. esphome/components/sen5x/sensor.py +67 -44
  262. esphome/components/sensirion_common/i2c_sensirion.cpp +18 -47
  263. esphome/components/sensirion_common/i2c_sensirion.h +39 -55
  264. esphome/components/sensor/__init__.py +2 -2
  265. esphome/components/sensor/automation.h +1 -1
  266. esphome/components/sensor/sensor.cpp +34 -6
  267. esphome/components/sensor/sensor.h +4 -21
  268. esphome/components/sgp30/sgp30.cpp +34 -35
  269. esphome/components/sgp30/sgp30.h +11 -10
  270. esphome/components/sgp4x/sgp4x.cpp +2 -2
  271. esphome/components/shelly_dimmer/light.py +7 -7
  272. esphome/components/sht4x/sht4x.cpp +1 -1
  273. esphome/components/sntp/sntp_component.cpp +36 -9
  274. esphome/components/sntp/sntp_component.h +7 -0
  275. esphome/components/sound_level/sound_level.cpp +1 -1
  276. esphome/components/speaker/__init__.py +2 -2
  277. esphome/components/speaker/media_player/__init__.py +2 -2
  278. esphome/components/speaker/media_player/speaker_media_player.cpp +1 -1
  279. esphome/components/spi/__init__.py +2 -2
  280. esphome/components/sprinkler/sprinkler.cpp +1 -1
  281. esphome/components/sps30/sps30.cpp +18 -23
  282. esphome/components/sps30/sps30.h +3 -3
  283. esphome/components/status_led/__init__.py +2 -2
  284. esphome/components/stepper/__init__.py +2 -2
  285. esphome/components/switch/__init__.py +2 -2
  286. esphome/components/switch/switch.cpp +5 -5
  287. esphome/components/sx1509/__init__.py +1 -1
  288. esphome/components/sx1509/sx1509.cpp +12 -7
  289. esphome/components/sx1509/sx1509.h +11 -4
  290. esphome/components/tca9555/tca9555.cpp +5 -5
  291. esphome/components/tee501/tee501.cpp +2 -21
  292. esphome/components/tee501/tee501.h +2 -4
  293. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +1 -1
  294. esphome/components/template/datetime/template_date.cpp +1 -1
  295. esphome/components/template/datetime/template_datetime.cpp +2 -2
  296. esphome/components/template/datetime/template_time.cpp +1 -1
  297. esphome/components/template/number/template_number.cpp +1 -1
  298. esphome/components/template/select/template_select.cpp +1 -1
  299. esphome/components/template/text/template_text.cpp +1 -1
  300. esphome/components/text/__init__.py +2 -2
  301. esphome/components/text/text.h +2 -2
  302. esphome/components/text_sensor/__init__.py +2 -2
  303. esphome/components/text_sensor/text_sensor.h +4 -4
  304. esphome/components/thermostat/climate.py +11 -7
  305. esphome/components/thermostat/thermostat_climate.cpp +237 -206
  306. esphome/components/thermostat/thermostat_climate.h +52 -41
  307. esphome/components/time/__init__.py +2 -2
  308. esphome/components/tmp1075/tmp1075.cpp +1 -1
  309. esphome/components/total_daily_energy/total_daily_energy.cpp +1 -1
  310. esphome/components/touchscreen/__init__.py +2 -2
  311. esphome/components/tuya/number/tuya_number.cpp +1 -1
  312. esphome/components/udp/udp_component.cpp +3 -3
  313. esphome/components/ufire_ec/ufire_ec.cpp +4 -4
  314. esphome/components/ufire_ise/ufire_ise.cpp +4 -4
  315. esphome/components/update/__init__.py +2 -2
  316. esphome/components/usb_uart/usb_uart.cpp +1 -1
  317. esphome/components/valve/__init__.py +5 -5
  318. esphome/components/valve/valve.cpp +1 -1
  319. esphome/components/valve/valve.h +2 -2
  320. esphome/components/wake_on_lan/wake_on_lan.cpp +2 -2
  321. esphome/components/waveshare_epaper/waveshare_213v3.cpp +1 -1
  322. esphome/components/web_server/__init__.py +2 -2
  323. esphome/components/web_server/ota/__init__.py +2 -2
  324. esphome/components/web_server/ota/ota_web_server.cpp +11 -0
  325. esphome/components/web_server/server_index_v2.h +149 -149
  326. esphome/components/web_server/web_server.cpp +58 -12
  327. esphome/components/web_server_base/__init__.py +2 -2
  328. esphome/components/wifi/__init__.py +5 -5
  329. esphome/components/wifi/wifi_component.cpp +4 -4
  330. esphome/components/wifi/wifi_component_esp_idf.cpp +2 -0
  331. esphome/components/wifi_info/wifi_info_text_sensor.h +3 -2
  332. esphome/config_validation.py +2 -2
  333. esphome/const.py +3 -1
  334. esphome/core/__init__.py +1 -0
  335. esphome/core/application.cpp +89 -51
  336. esphome/core/application.h +1 -0
  337. esphome/core/component.cpp +41 -19
  338. esphome/core/component.h +17 -13
  339. esphome/core/config.py +7 -7
  340. esphome/core/defines.h +5 -0
  341. esphome/core/entity_base.cpp +22 -8
  342. esphome/core/entity_base.h +43 -0
  343. esphome/core/helpers.cpp +34 -20
  344. esphome/core/helpers.h +33 -3
  345. esphome/core/ring_buffer.cpp +6 -2
  346. esphome/core/ring_buffer.h +2 -1
  347. esphome/core/scheduler.cpp +178 -97
  348. esphome/core/scheduler.h +67 -36
  349. esphome/core/time.cpp +6 -20
  350. esphome/coroutine.py +80 -3
  351. esphome/cpp_generator.py +13 -0
  352. esphome/cpp_helpers.py +2 -2
  353. esphome/dashboard/web_server.py +69 -15
  354. esphome/espota2.py +13 -6
  355. esphome/helpers.py +68 -83
  356. esphome/resolver.py +67 -0
  357. esphome/util.py +9 -6
  358. esphome/wizard.py +81 -34
  359. esphome/writer.py +13 -0
  360. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/METADATA +9 -9
  361. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/RECORD +369 -338
  362. /esphome/components/ld2420/text_sensor/{text_sensor.h → ld2420_text_sensor.h} +0 -0
  363. /esphome/components/opentherm/number/{number.h → opentherm_number.h} +0 -0
  364. /esphome/components/opentherm/output/{output.h → opentherm_output.h} +0 -0
  365. /esphome/components/opentherm/switch/{switch.h → opentherm_switch.h} +0 -0
  366. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/WHEEL +0 -0
  367. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/entry_points.txt +0 -0
  368. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/licenses/LICENSE +0 -0
  369. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,6 @@
1
1
  #include "thermostat_climate.h"
2
+ #include "esphome/core/application.h"
3
+ #include "esphome/core/helpers.h"
2
4
  #include "esphome/core/log.h"
3
5
 
4
6
  namespace esphome {
@@ -9,11 +11,11 @@ static const char *const TAG = "thermostat.climate";
9
11
  void ThermostatClimate::setup() {
10
12
  if (this->use_startup_delay_) {
11
13
  // start timers so that no actions are called for a moment
12
- this->start_timer_(thermostat::TIMER_COOLING_OFF);
13
- this->start_timer_(thermostat::TIMER_FANNING_OFF);
14
- this->start_timer_(thermostat::TIMER_HEATING_OFF);
14
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_COOLING_OFF);
15
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_FANNING_OFF);
16
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_HEATING_OFF);
15
17
  if (this->supports_fan_only_action_uses_fan_mode_timer_)
16
- this->start_timer_(thermostat::TIMER_FAN_MODE);
18
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_FAN_MODE);
17
19
  }
18
20
  // add a callback so that whenever the sensor state changes we can take action
19
21
  this->sensor_->add_on_state_callback([this](float state) {
@@ -64,7 +66,7 @@ void ThermostatClimate::setup() {
64
66
 
65
67
  void ThermostatClimate::loop() {
66
68
  for (auto &timer : this->timer_) {
67
- if (timer.active && (timer.started + timer.time < millis())) {
69
+ if (timer.active && (timer.started + timer.time < App.get_loop_component_start_time())) {
68
70
  timer.active = false;
69
71
  timer.func();
70
72
  }
@@ -127,26 +129,35 @@ bool ThermostatClimate::hysteresis_valid() {
127
129
  return true;
128
130
  }
129
131
 
132
+ bool ThermostatClimate::limit_setpoints_for_heat_cool() {
133
+ return this->mode == climate::CLIMATE_MODE_HEAT_COOL ||
134
+ (this->mode == climate::CLIMATE_MODE_AUTO && this->supports_heat_cool_);
135
+ }
136
+
130
137
  void ThermostatClimate::validate_target_temperature() {
131
138
  if (std::isnan(this->target_temperature)) {
139
+ // default to the midpoint between visual min and max
132
140
  this->target_temperature =
133
141
  ((this->get_traits().get_visual_max_temperature() - this->get_traits().get_visual_min_temperature()) / 2) +
134
142
  this->get_traits().get_visual_min_temperature();
135
143
  } else {
136
144
  // target_temperature must be between the visual minimum and the visual maximum
137
- if (this->target_temperature < this->get_traits().get_visual_min_temperature())
138
- this->target_temperature = this->get_traits().get_visual_min_temperature();
139
- if (this->target_temperature > this->get_traits().get_visual_max_temperature())
140
- this->target_temperature = this->get_traits().get_visual_max_temperature();
145
+ this->target_temperature = clamp(this->target_temperature, this->get_traits().get_visual_min_temperature(),
146
+ this->get_traits().get_visual_max_temperature());
141
147
  }
142
148
  }
143
149
 
144
- void ThermostatClimate::validate_target_temperatures() {
145
- if (this->supports_two_points_) {
150
+ void ThermostatClimate::validate_target_temperatures(const bool pin_target_temperature_high) {
151
+ if (!this->supports_two_points_) {
152
+ this->validate_target_temperature();
153
+ } else if (pin_target_temperature_high) {
154
+ // if target_temperature_high is set less than target_temperature_low, move down target_temperature_low
146
155
  this->validate_target_temperature_low();
147
156
  this->validate_target_temperature_high();
148
157
  } else {
149
- this->validate_target_temperature();
158
+ // if target_temperature_low is set greater than target_temperature_high, move up target_temperature_high
159
+ this->validate_target_temperature_high();
160
+ this->validate_target_temperature_low();
150
161
  }
151
162
  }
152
163
 
@@ -154,18 +165,13 @@ void ThermostatClimate::validate_target_temperature_low() {
154
165
  if (std::isnan(this->target_temperature_low)) {
155
166
  this->target_temperature_low = this->get_traits().get_visual_min_temperature();
156
167
  } else {
157
- // target_temperature_low must not be lower than the visual minimum
158
- if (this->target_temperature_low < this->get_traits().get_visual_min_temperature())
159
- this->target_temperature_low = this->get_traits().get_visual_min_temperature();
160
- // target_temperature_low must not be greater than the visual maximum minus set_point_minimum_differential_
161
- if (this->target_temperature_low >
162
- this->get_traits().get_visual_max_temperature() - this->set_point_minimum_differential_) {
163
- this->target_temperature_low =
164
- this->get_traits().get_visual_max_temperature() - this->set_point_minimum_differential_;
165
- }
166
- // if target_temperature_low is set greater than target_temperature_high, move up target_temperature_high
167
- if (this->target_temperature_low > this->target_temperature_high - this->set_point_minimum_differential_)
168
- this->target_temperature_high = this->target_temperature_low + this->set_point_minimum_differential_;
168
+ float target_temperature_low_upper_limit =
169
+ this->limit_setpoints_for_heat_cool()
170
+ ? clamp(this->target_temperature_high - this->set_point_minimum_differential_,
171
+ this->get_traits().get_visual_min_temperature(), this->get_traits().get_visual_max_temperature())
172
+ : this->get_traits().get_visual_max_temperature();
173
+ this->target_temperature_low = clamp(this->target_temperature_low, this->get_traits().get_visual_min_temperature(),
174
+ target_temperature_low_upper_limit);
169
175
  }
170
176
  }
171
177
 
@@ -173,62 +179,64 @@ void ThermostatClimate::validate_target_temperature_high() {
173
179
  if (std::isnan(this->target_temperature_high)) {
174
180
  this->target_temperature_high = this->get_traits().get_visual_max_temperature();
175
181
  } else {
176
- // target_temperature_high must not be lower than the visual maximum
177
- if (this->target_temperature_high > this->get_traits().get_visual_max_temperature())
178
- this->target_temperature_high = this->get_traits().get_visual_max_temperature();
179
- // target_temperature_high must not be lower than the visual minimum plus set_point_minimum_differential_
180
- if (this->target_temperature_high <
181
- this->get_traits().get_visual_min_temperature() + this->set_point_minimum_differential_) {
182
- this->target_temperature_high =
183
- this->get_traits().get_visual_min_temperature() + this->set_point_minimum_differential_;
184
- }
185
- // if target_temperature_high is set less than target_temperature_low, move down target_temperature_low
186
- if (this->target_temperature_high < this->target_temperature_low + this->set_point_minimum_differential_)
187
- this->target_temperature_low = this->target_temperature_high - this->set_point_minimum_differential_;
182
+ float target_temperature_high_lower_limit =
183
+ this->limit_setpoints_for_heat_cool()
184
+ ? clamp(this->target_temperature_low + this->set_point_minimum_differential_,
185
+ this->get_traits().get_visual_min_temperature(), this->get_traits().get_visual_max_temperature())
186
+ : this->get_traits().get_visual_min_temperature();
187
+ this->target_temperature_high = clamp(this->target_temperature_high, target_temperature_high_lower_limit,
188
+ this->get_traits().get_visual_max_temperature());
188
189
  }
189
190
  }
190
191
 
191
192
  void ThermostatClimate::control(const climate::ClimateCall &call) {
193
+ bool target_temperature_high_changed = false;
194
+
192
195
  if (call.get_preset().has_value()) {
193
196
  // setup_complete_ blocks modifying/resetting the temps immediately after boot
194
197
  if (this->setup_complete_) {
195
- this->change_preset_(*call.get_preset());
198
+ this->change_preset_(call.get_preset().value());
196
199
  } else {
197
- this->preset = *call.get_preset();
200
+ this->preset = call.get_preset().value();
198
201
  }
199
202
  }
200
203
  if (call.get_custom_preset().has_value()) {
201
204
  // setup_complete_ blocks modifying/resetting the temps immediately after boot
202
205
  if (this->setup_complete_) {
203
- this->change_custom_preset_(*call.get_custom_preset());
206
+ this->change_custom_preset_(call.get_custom_preset().value());
204
207
  } else {
205
- this->custom_preset = *call.get_custom_preset();
208
+ this->custom_preset = call.get_custom_preset().value();
206
209
  }
207
210
  }
208
211
 
209
- if (call.get_mode().has_value())
210
- this->mode = *call.get_mode();
211
- if (call.get_fan_mode().has_value())
212
- this->fan_mode = *call.get_fan_mode();
213
- if (call.get_swing_mode().has_value())
214
- this->swing_mode = *call.get_swing_mode();
212
+ if (call.get_mode().has_value()) {
213
+ this->mode = call.get_mode().value();
214
+ }
215
+ if (call.get_fan_mode().has_value()) {
216
+ this->fan_mode = call.get_fan_mode().value();
217
+ }
218
+ if (call.get_swing_mode().has_value()) {
219
+ this->swing_mode = call.get_swing_mode().value();
220
+ }
215
221
  if (this->supports_two_points_) {
216
222
  if (call.get_target_temperature_low().has_value()) {
217
- this->target_temperature_low = *call.get_target_temperature_low();
218
- validate_target_temperature_low();
223
+ this->target_temperature_low = call.get_target_temperature_low().value();
219
224
  }
220
225
  if (call.get_target_temperature_high().has_value()) {
221
- this->target_temperature_high = *call.get_target_temperature_high();
222
- validate_target_temperature_high();
226
+ target_temperature_high_changed = this->target_temperature_high != call.get_target_temperature_high().value();
227
+ this->target_temperature_high = call.get_target_temperature_high().value();
223
228
  }
229
+ // ensure the two set points are valid and adjust one of them if necessary
230
+ this->validate_target_temperatures(target_temperature_high_changed ||
231
+ (this->prev_mode_ == climate::CLIMATE_MODE_COOL));
224
232
  } else {
225
233
  if (call.get_target_temperature().has_value()) {
226
- this->target_temperature = *call.get_target_temperature();
227
- validate_target_temperature();
234
+ this->target_temperature = call.get_target_temperature().value();
235
+ this->validate_target_temperature();
228
236
  }
229
237
  }
230
238
  // make any changes happen
231
- refresh();
239
+ this->refresh();
232
240
  }
233
241
 
234
242
  climate::ClimateTraits ThermostatClimate::traits() {
@@ -237,47 +245,47 @@ climate::ClimateTraits ThermostatClimate::traits() {
237
245
  if (this->humidity_sensor_ != nullptr)
238
246
  traits.set_supports_current_humidity(true);
239
247
 
240
- if (supports_auto_)
248
+ if (this->supports_auto_)
241
249
  traits.add_supported_mode(climate::CLIMATE_MODE_AUTO);
242
- if (supports_heat_cool_)
250
+ if (this->supports_heat_cool_)
243
251
  traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL);
244
- if (supports_cool_)
252
+ if (this->supports_cool_)
245
253
  traits.add_supported_mode(climate::CLIMATE_MODE_COOL);
246
- if (supports_dry_)
254
+ if (this->supports_dry_)
247
255
  traits.add_supported_mode(climate::CLIMATE_MODE_DRY);
248
- if (supports_fan_only_)
256
+ if (this->supports_fan_only_)
249
257
  traits.add_supported_mode(climate::CLIMATE_MODE_FAN_ONLY);
250
- if (supports_heat_)
258
+ if (this->supports_heat_)
251
259
  traits.add_supported_mode(climate::CLIMATE_MODE_HEAT);
252
260
 
253
- if (supports_fan_mode_on_)
261
+ if (this->supports_fan_mode_on_)
254
262
  traits.add_supported_fan_mode(climate::CLIMATE_FAN_ON);
255
- if (supports_fan_mode_off_)
263
+ if (this->supports_fan_mode_off_)
256
264
  traits.add_supported_fan_mode(climate::CLIMATE_FAN_OFF);
257
- if (supports_fan_mode_auto_)
265
+ if (this->supports_fan_mode_auto_)
258
266
  traits.add_supported_fan_mode(climate::CLIMATE_FAN_AUTO);
259
- if (supports_fan_mode_low_)
267
+ if (this->supports_fan_mode_low_)
260
268
  traits.add_supported_fan_mode(climate::CLIMATE_FAN_LOW);
261
- if (supports_fan_mode_medium_)
269
+ if (this->supports_fan_mode_medium_)
262
270
  traits.add_supported_fan_mode(climate::CLIMATE_FAN_MEDIUM);
263
- if (supports_fan_mode_high_)
271
+ if (this->supports_fan_mode_high_)
264
272
  traits.add_supported_fan_mode(climate::CLIMATE_FAN_HIGH);
265
- if (supports_fan_mode_middle_)
273
+ if (this->supports_fan_mode_middle_)
266
274
  traits.add_supported_fan_mode(climate::CLIMATE_FAN_MIDDLE);
267
- if (supports_fan_mode_focus_)
275
+ if (this->supports_fan_mode_focus_)
268
276
  traits.add_supported_fan_mode(climate::CLIMATE_FAN_FOCUS);
269
- if (supports_fan_mode_diffuse_)
277
+ if (this->supports_fan_mode_diffuse_)
270
278
  traits.add_supported_fan_mode(climate::CLIMATE_FAN_DIFFUSE);
271
- if (supports_fan_mode_quiet_)
279
+ if (this->supports_fan_mode_quiet_)
272
280
  traits.add_supported_fan_mode(climate::CLIMATE_FAN_QUIET);
273
281
 
274
- if (supports_swing_mode_both_)
282
+ if (this->supports_swing_mode_both_)
275
283
  traits.add_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
276
- if (supports_swing_mode_horizontal_)
284
+ if (this->supports_swing_mode_horizontal_)
277
285
  traits.add_supported_swing_mode(climate::CLIMATE_SWING_HORIZONTAL);
278
- if (supports_swing_mode_off_)
286
+ if (this->supports_swing_mode_off_)
279
287
  traits.add_supported_swing_mode(climate::CLIMATE_SWING_OFF);
280
- if (supports_swing_mode_vertical_)
288
+ if (this->supports_swing_mode_vertical_)
281
289
  traits.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);
282
290
 
283
291
  for (auto &it : this->preset_config_) {
@@ -299,14 +307,15 @@ climate::ClimateAction ThermostatClimate::compute_action_(const bool ignore_time
299
307
  return climate::CLIMATE_ACTION_OFF;
300
308
  }
301
309
  // do not change the action if an "ON" timer is running
302
- if ((!ignore_timers) &&
303
- (timer_active_(thermostat::TIMER_IDLE_ON) || timer_active_(thermostat::TIMER_COOLING_ON) ||
304
- timer_active_(thermostat::TIMER_FANNING_ON) || timer_active_(thermostat::TIMER_HEATING_ON))) {
310
+ if ((!ignore_timers) && (this->timer_active_(thermostat::THERMOSTAT_TIMER_IDLE_ON) ||
311
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_COOLING_ON) ||
312
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_FANNING_ON) ||
313
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_HEATING_ON))) {
305
314
  return this->action;
306
315
  }
307
316
 
308
317
  // ensure set point(s) is/are valid before computing the action
309
- this->validate_target_temperatures();
318
+ this->validate_target_temperatures(this->prev_mode_ == climate::CLIMATE_MODE_COOL);
310
319
  // everything has been validated so we can now safely compute the action
311
320
  switch (this->mode) {
312
321
  // if the climate mode is OFF then the climate action must be OFF
@@ -340,6 +349,22 @@ climate::ClimateAction ThermostatClimate::compute_action_(const bool ignore_time
340
349
  target_action = climate::CLIMATE_ACTION_HEATING;
341
350
  }
342
351
  break;
352
+ case climate::CLIMATE_MODE_AUTO:
353
+ if (this->supports_two_points_) {
354
+ if (this->cooling_required_() && this->heating_required_()) {
355
+ // this is bad and should never happen, so just stop.
356
+ // target_action = climate::CLIMATE_ACTION_IDLE;
357
+ } else if (this->cooling_required_()) {
358
+ target_action = climate::CLIMATE_ACTION_COOLING;
359
+ } else if (this->heating_required_()) {
360
+ target_action = climate::CLIMATE_ACTION_HEATING;
361
+ }
362
+ } else if (this->supports_cool_ && this->cooling_required_()) {
363
+ target_action = climate::CLIMATE_ACTION_COOLING;
364
+ } else if (this->supports_heat_ && this->heating_required_()) {
365
+ target_action = climate::CLIMATE_ACTION_HEATING;
366
+ }
367
+ break;
343
368
  default:
344
369
  break;
345
370
  }
@@ -362,7 +387,7 @@ climate::ClimateAction ThermostatClimate::compute_supplemental_action_() {
362
387
  }
363
388
 
364
389
  // ensure set point(s) is/are valid before computing the action
365
- this->validate_target_temperatures();
390
+ this->validate_target_temperatures(this->prev_mode_ == climate::CLIMATE_MODE_COOL);
366
391
  // everything has been validated so we can now safely compute the action
367
392
  switch (this->mode) {
368
393
  // if the climate mode is OFF then the climate action must be OFF
@@ -420,18 +445,18 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
420
445
  case climate::CLIMATE_ACTION_OFF:
421
446
  case climate::CLIMATE_ACTION_IDLE:
422
447
  if (this->idle_action_ready_()) {
423
- this->start_timer_(thermostat::TIMER_IDLE_ON);
448
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_IDLE_ON);
424
449
  if (this->action == climate::CLIMATE_ACTION_COOLING)
425
- this->start_timer_(thermostat::TIMER_COOLING_OFF);
450
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_COOLING_OFF);
426
451
  if (this->action == climate::CLIMATE_ACTION_FAN) {
427
452
  if (this->supports_fan_only_action_uses_fan_mode_timer_) {
428
- this->start_timer_(thermostat::TIMER_FAN_MODE);
453
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_FAN_MODE);
429
454
  } else {
430
- this->start_timer_(thermostat::TIMER_FANNING_OFF);
455
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_FANNING_OFF);
431
456
  }
432
457
  }
433
458
  if (this->action == climate::CLIMATE_ACTION_HEATING)
434
- this->start_timer_(thermostat::TIMER_HEATING_OFF);
459
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_HEATING_OFF);
435
460
  // trig = this->idle_action_trigger_;
436
461
  ESP_LOGVV(TAG, "Switching to IDLE/OFF action");
437
462
  this->cooling_max_runtime_exceeded_ = false;
@@ -441,10 +466,10 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
441
466
  break;
442
467
  case climate::CLIMATE_ACTION_COOLING:
443
468
  if (this->cooling_action_ready_()) {
444
- this->start_timer_(thermostat::TIMER_COOLING_ON);
445
- this->start_timer_(thermostat::TIMER_COOLING_MAX_RUN_TIME);
469
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_COOLING_ON);
470
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_COOLING_MAX_RUN_TIME);
446
471
  if (this->supports_fan_with_cooling_) {
447
- this->start_timer_(thermostat::TIMER_FANNING_ON);
472
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_FANNING_ON);
448
473
  trig_fan = this->fan_only_action_trigger_;
449
474
  }
450
475
  this->cooling_max_runtime_exceeded_ = false;
@@ -455,10 +480,10 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
455
480
  break;
456
481
  case climate::CLIMATE_ACTION_HEATING:
457
482
  if (this->heating_action_ready_()) {
458
- this->start_timer_(thermostat::TIMER_HEATING_ON);
459
- this->start_timer_(thermostat::TIMER_HEATING_MAX_RUN_TIME);
483
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_HEATING_ON);
484
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_HEATING_MAX_RUN_TIME);
460
485
  if (this->supports_fan_with_heating_) {
461
- this->start_timer_(thermostat::TIMER_FANNING_ON);
486
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_FANNING_ON);
462
487
  trig_fan = this->fan_only_action_trigger_;
463
488
  }
464
489
  this->heating_max_runtime_exceeded_ = false;
@@ -470,9 +495,9 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
470
495
  case climate::CLIMATE_ACTION_FAN:
471
496
  if (this->fanning_action_ready_()) {
472
497
  if (this->supports_fan_only_action_uses_fan_mode_timer_) {
473
- this->start_timer_(thermostat::TIMER_FAN_MODE);
498
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_FAN_MODE);
474
499
  } else {
475
- this->start_timer_(thermostat::TIMER_FANNING_ON);
500
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_FANNING_ON);
476
501
  }
477
502
  trig = this->fan_only_action_trigger_;
478
503
  ESP_LOGVV(TAG, "Switching to FAN_ONLY action");
@@ -481,8 +506,8 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
481
506
  break;
482
507
  case climate::CLIMATE_ACTION_DRYING:
483
508
  if (this->drying_action_ready_()) {
484
- this->start_timer_(thermostat::TIMER_COOLING_ON);
485
- this->start_timer_(thermostat::TIMER_FANNING_ON);
509
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_COOLING_ON);
510
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_FANNING_ON);
486
511
  trig = this->dry_action_trigger_;
487
512
  ESP_LOGVV(TAG, "Switching to DRYING action");
488
513
  action_ready = true;
@@ -525,14 +550,14 @@ void ThermostatClimate::switch_to_supplemental_action_(climate::ClimateAction ac
525
550
  switch (action) {
526
551
  case climate::CLIMATE_ACTION_OFF:
527
552
  case climate::CLIMATE_ACTION_IDLE:
528
- this->cancel_timer_(thermostat::TIMER_COOLING_MAX_RUN_TIME);
529
- this->cancel_timer_(thermostat::TIMER_HEATING_MAX_RUN_TIME);
553
+ this->cancel_timer_(thermostat::THERMOSTAT_TIMER_COOLING_MAX_RUN_TIME);
554
+ this->cancel_timer_(thermostat::THERMOSTAT_TIMER_HEATING_MAX_RUN_TIME);
530
555
  break;
531
556
  case climate::CLIMATE_ACTION_COOLING:
532
- this->cancel_timer_(thermostat::TIMER_COOLING_MAX_RUN_TIME);
557
+ this->cancel_timer_(thermostat::THERMOSTAT_TIMER_COOLING_MAX_RUN_TIME);
533
558
  break;
534
559
  case climate::CLIMATE_ACTION_HEATING:
535
- this->cancel_timer_(thermostat::TIMER_HEATING_MAX_RUN_TIME);
560
+ this->cancel_timer_(thermostat::THERMOSTAT_TIMER_HEATING_MAX_RUN_TIME);
536
561
  break;
537
562
  default:
538
563
  return;
@@ -547,15 +572,15 @@ void ThermostatClimate::trigger_supplemental_action_() {
547
572
 
548
573
  switch (this->supplemental_action_) {
549
574
  case climate::CLIMATE_ACTION_COOLING:
550
- if (!this->timer_active_(thermostat::TIMER_COOLING_MAX_RUN_TIME)) {
551
- this->start_timer_(thermostat::TIMER_COOLING_MAX_RUN_TIME);
575
+ if (!this->timer_active_(thermostat::THERMOSTAT_TIMER_COOLING_MAX_RUN_TIME)) {
576
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_COOLING_MAX_RUN_TIME);
552
577
  }
553
578
  trig = this->supplemental_cool_action_trigger_;
554
579
  ESP_LOGVV(TAG, "Calling supplemental COOLING action");
555
580
  break;
556
581
  case climate::CLIMATE_ACTION_HEATING:
557
- if (!this->timer_active_(thermostat::TIMER_HEATING_MAX_RUN_TIME)) {
558
- this->start_timer_(thermostat::TIMER_HEATING_MAX_RUN_TIME);
582
+ if (!this->timer_active_(thermostat::THERMOSTAT_TIMER_HEATING_MAX_RUN_TIME)) {
583
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_HEATING_MAX_RUN_TIME);
559
584
  }
560
585
  trig = this->supplemental_heat_action_trigger_;
561
586
  ESP_LOGVV(TAG, "Calling supplemental HEATING action");
@@ -633,7 +658,7 @@ void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode, bo
633
658
  this->prev_fan_mode_trigger_->stop_action();
634
659
  this->prev_fan_mode_trigger_ = nullptr;
635
660
  }
636
- this->start_timer_(thermostat::TIMER_FAN_MODE);
661
+ this->start_timer_(thermostat::THERMOSTAT_TIMER_FAN_MODE);
637
662
  if (trig != nullptr) {
638
663
  trig->trigger();
639
664
  }
@@ -653,13 +678,13 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_
653
678
  this->prev_mode_trigger_->stop_action();
654
679
  this->prev_mode_trigger_ = nullptr;
655
680
  }
656
- Trigger<> *trig = this->auto_mode_trigger_;
681
+ Trigger<> *trig = this->off_mode_trigger_;
657
682
  switch (mode) {
658
- case climate::CLIMATE_MODE_OFF:
659
- trig = this->off_mode_trigger_;
683
+ case climate::CLIMATE_MODE_AUTO:
684
+ trig = this->auto_mode_trigger_;
660
685
  break;
661
686
  case climate::CLIMATE_MODE_HEAT_COOL:
662
- // trig = this->auto_mode_trigger_;
687
+ trig = this->heat_cool_mode_trigger_;
663
688
  break;
664
689
  case climate::CLIMATE_MODE_COOL:
665
690
  trig = this->cool_mode_trigger_;
@@ -673,11 +698,12 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_
673
698
  case climate::CLIMATE_MODE_DRY:
674
699
  trig = this->dry_mode_trigger_;
675
700
  break;
701
+ case climate::CLIMATE_MODE_OFF:
676
702
  default:
677
703
  // we cannot report an invalid mode back to HA (even if it asked for one)
678
704
  // and must assume some valid value
679
- mode = climate::CLIMATE_MODE_HEAT_COOL;
680
- // trig = this->auto_mode_trigger_;
705
+ mode = climate::CLIMATE_MODE_OFF;
706
+ // trig = this->off_mode_trigger_;
681
707
  }
682
708
  if (trig != nullptr) {
683
709
  trig->trigger();
@@ -685,8 +711,9 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_
685
711
  this->mode = mode;
686
712
  this->prev_mode_ = mode;
687
713
  this->prev_mode_trigger_ = trig;
688
- if (publish_state)
714
+ if (publish_state) {
689
715
  this->publish_state();
716
+ }
690
717
  }
691
718
 
692
719
  void ThermostatClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mode, bool publish_state) {
@@ -732,35 +759,44 @@ void ThermostatClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mo
732
759
 
733
760
  bool ThermostatClimate::idle_action_ready_() {
734
761
  if (this->supports_fan_only_action_uses_fan_mode_timer_) {
735
- return !(this->timer_active_(thermostat::TIMER_COOLING_ON) || this->timer_active_(thermostat::TIMER_FAN_MODE) ||
736
- this->timer_active_(thermostat::TIMER_HEATING_ON));
762
+ return !(this->timer_active_(thermostat::THERMOSTAT_TIMER_COOLING_ON) ||
763
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_FAN_MODE) ||
764
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_HEATING_ON));
737
765
  }
738
- return !(this->timer_active_(thermostat::TIMER_COOLING_ON) || this->timer_active_(thermostat::TIMER_FANNING_ON) ||
739
- this->timer_active_(thermostat::TIMER_HEATING_ON));
766
+ return !(this->timer_active_(thermostat::THERMOSTAT_TIMER_COOLING_ON) ||
767
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_FANNING_ON) ||
768
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_HEATING_ON));
740
769
  }
741
770
 
742
771
  bool ThermostatClimate::cooling_action_ready_() {
743
- return !(this->timer_active_(thermostat::TIMER_IDLE_ON) || this->timer_active_(thermostat::TIMER_FANNING_OFF) ||
744
- this->timer_active_(thermostat::TIMER_COOLING_OFF) || this->timer_active_(thermostat::TIMER_HEATING_ON));
772
+ return !(this->timer_active_(thermostat::THERMOSTAT_TIMER_IDLE_ON) ||
773
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_FANNING_OFF) ||
774
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_COOLING_OFF) ||
775
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_HEATING_ON));
745
776
  }
746
777
 
747
778
  bool ThermostatClimate::drying_action_ready_() {
748
- return !(this->timer_active_(thermostat::TIMER_IDLE_ON) || this->timer_active_(thermostat::TIMER_FANNING_OFF) ||
749
- this->timer_active_(thermostat::TIMER_COOLING_OFF) || this->timer_active_(thermostat::TIMER_HEATING_ON));
779
+ return !(this->timer_active_(thermostat::THERMOSTAT_TIMER_IDLE_ON) ||
780
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_FANNING_OFF) ||
781
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_COOLING_OFF) ||
782
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_HEATING_ON));
750
783
  }
751
784
 
752
- bool ThermostatClimate::fan_mode_ready_() { return !(this->timer_active_(thermostat::TIMER_FAN_MODE)); }
785
+ bool ThermostatClimate::fan_mode_ready_() { return !(this->timer_active_(thermostat::THERMOSTAT_TIMER_FAN_MODE)); }
753
786
 
754
787
  bool ThermostatClimate::fanning_action_ready_() {
755
788
  if (this->supports_fan_only_action_uses_fan_mode_timer_) {
756
- return !(this->timer_active_(thermostat::TIMER_FAN_MODE));
789
+ return !(this->timer_active_(thermostat::THERMOSTAT_TIMER_FAN_MODE));
757
790
  }
758
- return !(this->timer_active_(thermostat::TIMER_IDLE_ON) || this->timer_active_(thermostat::TIMER_FANNING_OFF));
791
+ return !(this->timer_active_(thermostat::THERMOSTAT_TIMER_IDLE_ON) ||
792
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_FANNING_OFF));
759
793
  }
760
794
 
761
795
  bool ThermostatClimate::heating_action_ready_() {
762
- return !(this->timer_active_(thermostat::TIMER_IDLE_ON) || this->timer_active_(thermostat::TIMER_COOLING_ON) ||
763
- this->timer_active_(thermostat::TIMER_FANNING_OFF) || this->timer_active_(thermostat::TIMER_HEATING_OFF));
796
+ return !(this->timer_active_(thermostat::THERMOSTAT_TIMER_IDLE_ON) ||
797
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_COOLING_ON) ||
798
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_FANNING_OFF) ||
799
+ this->timer_active_(thermostat::THERMOSTAT_TIMER_HEATING_OFF));
764
800
  }
765
801
 
766
802
  void ThermostatClimate::start_timer_(const ThermostatClimateTimerIndex timer_index) {
@@ -958,37 +994,25 @@ bool ThermostatClimate::supplemental_heating_required_() {
958
994
  (this->supplemental_action_ == climate::CLIMATE_ACTION_HEATING));
959
995
  }
960
996
 
961
- void ThermostatClimate::dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config,
962
- bool is_default_preset) {
963
- ESP_LOGCONFIG(TAG, " %s Is Default: %s", preset_name, YESNO(is_default_preset));
964
-
997
+ void ThermostatClimate::dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config) {
965
998
  if (this->supports_heat_) {
966
- if (this->supports_two_points_) {
967
- ESP_LOGCONFIG(TAG, " %s Default Target Temperature Low: %.1f°C", preset_name,
968
- config.default_temperature_low);
969
- } else {
970
- ESP_LOGCONFIG(TAG, " %s Default Target Temperature Low: %.1f°C", preset_name, config.default_temperature);
971
- }
999
+ ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.1f°C",
1000
+ this->supports_two_points_ ? config.default_temperature_low : config.default_temperature);
972
1001
  }
973
1002
  if ((this->supports_cool_) || (this->supports_fan_only_)) {
974
- if (this->supports_two_points_) {
975
- ESP_LOGCONFIG(TAG, " %s Default Target Temperature High: %.1f°C", preset_name,
976
- config.default_temperature_high);
977
- } else {
978
- ESP_LOGCONFIG(TAG, " %s Default Target Temperature High: %.1f°C", preset_name, config.default_temperature);
979
- }
1003
+ ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.1f°C",
1004
+ this->supports_two_points_ ? config.default_temperature_high : config.default_temperature);
980
1005
  }
981
1006
 
982
1007
  if (config.mode_.has_value()) {
983
- ESP_LOGCONFIG(TAG, " %s Default Mode: %s", preset_name,
984
- LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_)));
1008
+ ESP_LOGCONFIG(TAG, " Default Mode: %s", LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_)));
985
1009
  }
986
1010
  if (config.fan_mode_.has_value()) {
987
- ESP_LOGCONFIG(TAG, " %s Default Fan Mode: %s", preset_name,
1011
+ ESP_LOGCONFIG(TAG, " Default Fan Mode: %s",
988
1012
  LOG_STR_ARG(climate::climate_fan_mode_to_string(*config.fan_mode_)));
989
1013
  }
990
1014
  if (config.swing_mode_.has_value()) {
991
- ESP_LOGCONFIG(TAG, " %s Default Swing Mode: %s", preset_name,
1015
+ ESP_LOGCONFIG(TAG, " Default Swing Mode: %s",
992
1016
  LOG_STR_ARG(climate::climate_swing_mode_to_string(*config.swing_mode_)));
993
1017
  }
994
1018
  }
@@ -1106,6 +1130,7 @@ ThermostatClimate::ThermostatClimate()
1106
1130
  heat_action_trigger_(new Trigger<>()),
1107
1131
  supplemental_heat_action_trigger_(new Trigger<>()),
1108
1132
  heat_mode_trigger_(new Trigger<>()),
1133
+ heat_cool_mode_trigger_(new Trigger<>()),
1109
1134
  auto_mode_trigger_(new Trigger<>()),
1110
1135
  idle_action_trigger_(new Trigger<>()),
1111
1136
  off_mode_trigger_(new Trigger<>()),
@@ -1147,43 +1172,43 @@ void ThermostatClimate::set_heat_overrun(float overrun) { this->heating_overrun_
1147
1172
  void ThermostatClimate::set_supplemental_cool_delta(float delta) { this->supplemental_cool_delta_ = delta; }
1148
1173
  void ThermostatClimate::set_supplemental_heat_delta(float delta) { this->supplemental_heat_delta_ = delta; }
1149
1174
  void ThermostatClimate::set_cooling_maximum_run_time_in_sec(uint32_t time) {
1150
- this->timer_[thermostat::TIMER_COOLING_MAX_RUN_TIME].time =
1175
+ this->timer_[thermostat::THERMOSTAT_TIMER_COOLING_MAX_RUN_TIME].time =
1151
1176
  1000 * (time < this->min_timer_duration_ ? this->min_timer_duration_ : time);
1152
1177
  }
1153
1178
  void ThermostatClimate::set_cooling_minimum_off_time_in_sec(uint32_t time) {
1154
- this->timer_[thermostat::TIMER_COOLING_OFF].time =
1179
+ this->timer_[thermostat::THERMOSTAT_TIMER_COOLING_OFF].time =
1155
1180
  1000 * (time < this->min_timer_duration_ ? this->min_timer_duration_ : time);
1156
1181
  }
1157
1182
  void ThermostatClimate::set_cooling_minimum_run_time_in_sec(uint32_t time) {
1158
- this->timer_[thermostat::TIMER_COOLING_ON].time =
1183
+ this->timer_[thermostat::THERMOSTAT_TIMER_COOLING_ON].time =
1159
1184
  1000 * (time < this->min_timer_duration_ ? this->min_timer_duration_ : time);
1160
1185
  }
1161
1186
  void ThermostatClimate::set_fan_mode_minimum_switching_time_in_sec(uint32_t time) {
1162
- this->timer_[thermostat::TIMER_FAN_MODE].time =
1187
+ this->timer_[thermostat::THERMOSTAT_TIMER_FAN_MODE].time =
1163
1188
  1000 * (time < this->min_timer_duration_ ? this->min_timer_duration_ : time);
1164
1189
  }
1165
1190
  void ThermostatClimate::set_fanning_minimum_off_time_in_sec(uint32_t time) {
1166
- this->timer_[thermostat::TIMER_FANNING_OFF].time =
1191
+ this->timer_[thermostat::THERMOSTAT_TIMER_FANNING_OFF].time =
1167
1192
  1000 * (time < this->min_timer_duration_ ? this->min_timer_duration_ : time);
1168
1193
  }
1169
1194
  void ThermostatClimate::set_fanning_minimum_run_time_in_sec(uint32_t time) {
1170
- this->timer_[thermostat::TIMER_FANNING_ON].time =
1195
+ this->timer_[thermostat::THERMOSTAT_TIMER_FANNING_ON].time =
1171
1196
  1000 * (time < this->min_timer_duration_ ? this->min_timer_duration_ : time);
1172
1197
  }
1173
1198
  void ThermostatClimate::set_heating_maximum_run_time_in_sec(uint32_t time) {
1174
- this->timer_[thermostat::TIMER_HEATING_MAX_RUN_TIME].time =
1199
+ this->timer_[thermostat::THERMOSTAT_TIMER_HEATING_MAX_RUN_TIME].time =
1175
1200
  1000 * (time < this->min_timer_duration_ ? this->min_timer_duration_ : time);
1176
1201
  }
1177
1202
  void ThermostatClimate::set_heating_minimum_off_time_in_sec(uint32_t time) {
1178
- this->timer_[thermostat::TIMER_HEATING_OFF].time =
1203
+ this->timer_[thermostat::THERMOSTAT_TIMER_HEATING_OFF].time =
1179
1204
  1000 * (time < this->min_timer_duration_ ? this->min_timer_duration_ : time);
1180
1205
  }
1181
1206
  void ThermostatClimate::set_heating_minimum_run_time_in_sec(uint32_t time) {
1182
- this->timer_[thermostat::TIMER_HEATING_ON].time =
1207
+ this->timer_[thermostat::THERMOSTAT_TIMER_HEATING_ON].time =
1183
1208
  1000 * (time < this->min_timer_duration_ ? this->min_timer_duration_ : time);
1184
1209
  }
1185
1210
  void ThermostatClimate::set_idle_minimum_time_in_sec(uint32_t time) {
1186
- this->timer_[thermostat::TIMER_IDLE_ON].time =
1211
+ this->timer_[thermostat::THERMOSTAT_TIMER_IDLE_ON].time =
1187
1212
  1000 * (time < this->min_timer_duration_ ? this->min_timer_duration_ : time);
1188
1213
  }
1189
1214
  void ThermostatClimate::set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; }
@@ -1274,6 +1299,7 @@ Trigger<> *ThermostatClimate::get_cool_mode_trigger() const { return this->cool_
1274
1299
  Trigger<> *ThermostatClimate::get_dry_mode_trigger() const { return this->dry_mode_trigger_; }
1275
1300
  Trigger<> *ThermostatClimate::get_fan_only_mode_trigger() const { return this->fan_only_mode_trigger_; }
1276
1301
  Trigger<> *ThermostatClimate::get_heat_mode_trigger() const { return this->heat_mode_trigger_; }
1302
+ Trigger<> *ThermostatClimate::get_heat_cool_mode_trigger() const { return this->heat_cool_mode_trigger_; }
1277
1303
  Trigger<> *ThermostatClimate::get_off_mode_trigger() const { return this->off_mode_trigger_; }
1278
1304
  Trigger<> *ThermostatClimate::get_fan_mode_on_trigger() const { return this->fan_mode_on_trigger_; }
1279
1305
  Trigger<> *ThermostatClimate::get_fan_mode_off_trigger() const { return this->fan_mode_off_trigger_; }
@@ -1295,64 +1321,69 @@ Trigger<> *ThermostatClimate::get_preset_change_trigger() const { return this->p
1295
1321
  void ThermostatClimate::dump_config() {
1296
1322
  LOG_CLIMATE("", "Thermostat", this);
1297
1323
 
1324
+ ESP_LOGCONFIG(TAG,
1325
+ " On boot, restore from: %s\n"
1326
+ " Use Start-up Delay: %s",
1327
+ this->on_boot_restore_from_ == thermostat::DEFAULT_PRESET ? "DEFAULT_PRESET" : "MEMORY",
1328
+ YESNO(this->use_startup_delay_));
1298
1329
  if (this->supports_two_points_) {
1299
1330
  ESP_LOGCONFIG(TAG, " Minimum Set Point Differential: %.1f°C", this->set_point_minimum_differential_);
1300
1331
  }
1301
- ESP_LOGCONFIG(TAG, " Use Start-up Delay: %s", YESNO(this->use_startup_delay_));
1302
1332
  if (this->supports_cool_) {
1303
1333
  ESP_LOGCONFIG(TAG,
1304
1334
  " Cooling Parameters:\n"
1305
1335
  " Deadband: %.1f°C\n"
1306
- " Overrun: %.1f°C",
1307
- this->cooling_deadband_, this->cooling_overrun_);
1308
- if ((this->supplemental_cool_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) > 0)) {
1309
- ESP_LOGCONFIG(TAG,
1310
- " Supplemental Delta: %.1f°C\n"
1311
- " Maximum Run Time: %" PRIu32 "s",
1312
- this->supplemental_cool_delta_,
1313
- this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) / 1000);
1314
- }
1315
- ESP_LOGCONFIG(TAG,
1336
+ " Overrun: %.1f°C\n"
1316
1337
  " Minimum Off Time: %" PRIu32 "s\n"
1317
1338
  " Minimum Run Time: %" PRIu32 "s",
1318
- this->timer_duration_(thermostat::TIMER_COOLING_OFF) / 1000,
1319
- this->timer_duration_(thermostat::TIMER_COOLING_ON) / 1000);
1339
+ this->cooling_deadband_, this->cooling_overrun_,
1340
+ this->timer_duration_(thermostat::THERMOSTAT_TIMER_COOLING_OFF) / 1000,
1341
+ this->timer_duration_(thermostat::THERMOSTAT_TIMER_COOLING_ON) / 1000);
1342
+ if ((this->supplemental_cool_delta_ > 0) ||
1343
+ (this->timer_duration_(thermostat::THERMOSTAT_TIMER_COOLING_MAX_RUN_TIME) > 0)) {
1344
+ ESP_LOGCONFIG(TAG,
1345
+ " Maximum Run Time: %" PRIu32 "s\n"
1346
+ " Supplemental Delta: %.1f°C",
1347
+ this->timer_duration_(thermostat::THERMOSTAT_TIMER_COOLING_MAX_RUN_TIME) / 1000,
1348
+ this->supplemental_cool_delta_);
1349
+ }
1320
1350
  }
1321
1351
  if (this->supports_heat_) {
1322
1352
  ESP_LOGCONFIG(TAG,
1323
1353
  " Heating Parameters:\n"
1324
1354
  " Deadband: %.1f°C\n"
1325
- " Overrun: %.1f°C",
1326
- this->heating_deadband_, this->heating_overrun_);
1327
- if ((this->supplemental_heat_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) > 0)) {
1328
- ESP_LOGCONFIG(TAG,
1329
- " Supplemental Delta: %.1f°C\n"
1330
- " Maximum Run Time: %" PRIu32 "s",
1331
- this->supplemental_heat_delta_,
1332
- this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) / 1000);
1333
- }
1334
- ESP_LOGCONFIG(TAG,
1355
+ " Overrun: %.1f°C\n"
1335
1356
  " Minimum Off Time: %" PRIu32 "s\n"
1336
1357
  " Minimum Run Time: %" PRIu32 "s",
1337
- this->timer_duration_(thermostat::TIMER_HEATING_OFF) / 1000,
1338
- this->timer_duration_(thermostat::TIMER_HEATING_ON) / 1000);
1358
+ this->heating_deadband_, this->heating_overrun_,
1359
+ this->timer_duration_(thermostat::THERMOSTAT_TIMER_HEATING_OFF) / 1000,
1360
+ this->timer_duration_(thermostat::THERMOSTAT_TIMER_HEATING_ON) / 1000);
1361
+ if ((this->supplemental_heat_delta_ > 0) ||
1362
+ (this->timer_duration_(thermostat::THERMOSTAT_TIMER_HEATING_MAX_RUN_TIME) > 0)) {
1363
+ ESP_LOGCONFIG(TAG,
1364
+ " Maximum Run Time: %" PRIu32 "s\n"
1365
+ " Supplemental Delta: %.1f°C",
1366
+ this->timer_duration_(thermostat::THERMOSTAT_TIMER_HEATING_MAX_RUN_TIME) / 1000,
1367
+ this->supplemental_heat_delta_);
1368
+ }
1339
1369
  }
1340
1370
  if (this->supports_fan_only_) {
1341
1371
  ESP_LOGCONFIG(TAG,
1342
- " Fanning Minimum Off Time: %" PRIu32 "s\n"
1343
- " Fanning Minimum Run Time: %" PRIu32 "s",
1344
- this->timer_duration_(thermostat::TIMER_FANNING_OFF) / 1000,
1345
- this->timer_duration_(thermostat::TIMER_FANNING_ON) / 1000);
1372
+ " Fan Parameters:\n"
1373
+ " Minimum Off Time: %" PRIu32 "s\n"
1374
+ " Minimum Run Time: %" PRIu32 "s",
1375
+ this->timer_duration_(thermostat::THERMOSTAT_TIMER_FANNING_OFF) / 1000,
1376
+ this->timer_duration_(thermostat::THERMOSTAT_TIMER_FANNING_ON) / 1000);
1346
1377
  }
1347
1378
  if (this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ ||
1348
1379
  this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ ||
1349
1380
  this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_ ||
1350
1381
  this->supports_fan_mode_quiet_) {
1351
1382
  ESP_LOGCONFIG(TAG, " Minimum Fan Mode Switching Time: %" PRIu32 "s",
1352
- this->timer_duration_(thermostat::TIMER_FAN_MODE) / 1000);
1383
+ this->timer_duration_(thermostat::THERMOSTAT_TIMER_FAN_MODE) / 1000);
1353
1384
  }
1354
- ESP_LOGCONFIG(TAG, " Minimum Idle Time: %" PRIu32 "s", this->timer_[thermostat::TIMER_IDLE_ON].time / 1000);
1355
1385
  ESP_LOGCONFIG(TAG,
1386
+ " Minimum Idle Time: %" PRIu32 "s\n"
1356
1387
  " Supported MODES:\n"
1357
1388
  " AUTO: %s\n"
1358
1389
  " HEAT/COOL: %s\n"
@@ -1362,8 +1393,9 @@ void ThermostatClimate::dump_config() {
1362
1393
  " FAN_ONLY: %s\n"
1363
1394
  " FAN_ONLY_ACTION_USES_FAN_MODE_TIMER: %s\n"
1364
1395
  " FAN_ONLY_COOLING: %s",
1365
- YESNO(this->supports_auto_), YESNO(this->supports_heat_cool_), YESNO(this->supports_heat_),
1366
- YESNO(this->supports_cool_), YESNO(this->supports_dry_), YESNO(this->supports_fan_only_),
1396
+ this->timer_[thermostat::THERMOSTAT_TIMER_IDLE_ON].time / 1000, YESNO(this->supports_auto_),
1397
+ YESNO(this->supports_heat_cool_), YESNO(this->supports_heat_), YESNO(this->supports_cool_),
1398
+ YESNO(this->supports_dry_), YESNO(this->supports_fan_only_),
1367
1399
  YESNO(this->supports_fan_only_action_uses_fan_mode_timer_), YESNO(this->supports_fan_only_cooling_));
1368
1400
  if (this->supports_cool_) {
1369
1401
  ESP_LOGCONFIG(TAG, " FAN_WITH_COOLING: %s", YESNO(this->supports_fan_with_cooling_));
@@ -1382,40 +1414,39 @@ void ThermostatClimate::dump_config() {
1382
1414
  " MIDDLE: %s\n"
1383
1415
  " FOCUS: %s\n"
1384
1416
  " DIFFUSE: %s\n"
1385
- " QUIET: %s",
1386
- YESNO(this->supports_fan_mode_on_), YESNO(this->supports_fan_mode_off_),
1387
- YESNO(this->supports_fan_mode_auto_), YESNO(this->supports_fan_mode_low_),
1388
- YESNO(this->supports_fan_mode_medium_), YESNO(this->supports_fan_mode_high_),
1389
- YESNO(this->supports_fan_mode_middle_), YESNO(this->supports_fan_mode_focus_),
1390
- YESNO(this->supports_fan_mode_diffuse_), YESNO(this->supports_fan_mode_quiet_));
1391
- ESP_LOGCONFIG(TAG,
1417
+ " QUIET: %s\n"
1392
1418
  " Supported SWING MODES:\n"
1393
1419
  " BOTH: %s\n"
1394
1420
  " OFF: %s\n"
1395
1421
  " HORIZONTAL: %s\n"
1396
1422
  " VERTICAL: %s\n"
1397
1423
  " Supports TWO SET POINTS: %s",
1424
+ YESNO(this->supports_fan_mode_on_), YESNO(this->supports_fan_mode_off_),
1425
+ YESNO(this->supports_fan_mode_auto_), YESNO(this->supports_fan_mode_low_),
1426
+ YESNO(this->supports_fan_mode_medium_), YESNO(this->supports_fan_mode_high_),
1427
+ YESNO(this->supports_fan_mode_middle_), YESNO(this->supports_fan_mode_focus_),
1428
+ YESNO(this->supports_fan_mode_diffuse_), YESNO(this->supports_fan_mode_quiet_),
1398
1429
  YESNO(this->supports_swing_mode_both_), YESNO(this->supports_swing_mode_off_),
1399
1430
  YESNO(this->supports_swing_mode_horizontal_), YESNO(this->supports_swing_mode_vertical_),
1400
1431
  YESNO(this->supports_two_points_));
1401
1432
 
1402
- ESP_LOGCONFIG(TAG, " Supported PRESETS: ");
1403
- for (auto &it : this->preset_config_) {
1404
- const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(it.first));
1405
-
1406
- ESP_LOGCONFIG(TAG, " Supports %s: %s", preset_name, YESNO(true));
1407
- this->dump_preset_config_(preset_name, it.second, it.first == this->default_preset_);
1433
+ if (!this->preset_config_.empty()) {
1434
+ ESP_LOGCONFIG(TAG, " Supported PRESETS:");
1435
+ for (auto &it : this->preset_config_) {
1436
+ const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(it.first));
1437
+ ESP_LOGCONFIG(TAG, " %s:%s", preset_name, it.first == this->default_preset_ ? " (default)" : "");
1438
+ this->dump_preset_config_(preset_name, it.second);
1439
+ }
1408
1440
  }
1409
1441
 
1410
- ESP_LOGCONFIG(TAG, " Supported CUSTOM PRESETS: ");
1411
- for (auto &it : this->custom_preset_config_) {
1412
- const auto *preset_name = it.first.c_str();
1413
-
1414
- ESP_LOGCONFIG(TAG, " Supports %s: %s", preset_name, YESNO(true));
1415
- this->dump_preset_config_(preset_name, it.second, it.first == this->default_custom_preset_);
1442
+ if (!this->custom_preset_config_.empty()) {
1443
+ ESP_LOGCONFIG(TAG, " Supported CUSTOM PRESETS:");
1444
+ for (auto &it : this->custom_preset_config_) {
1445
+ const auto *preset_name = it.first.c_str();
1446
+ ESP_LOGCONFIG(TAG, " %s:%s", preset_name, it.first == this->default_custom_preset_ ? " (default)" : "");
1447
+ this->dump_preset_config_(preset_name, it.second);
1448
+ }
1416
1449
  }
1417
- ESP_LOGCONFIG(TAG, " On boot, restore from: %s",
1418
- this->on_boot_restore_from_ == thermostat::DEFAULT_PRESET ? "DEFAULT_PRESET" : "MEMORY");
1419
1450
  }
1420
1451
 
1421
1452
  ThermostatClimateTargetTempConfig::ThermostatClimateTargetTempConfig() = default;