esphome 2024.10.2__py3-none-any.whl → 2024.11.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 (239) hide show
  1. esphome/__main__.py +22 -4
  2. esphome/automation.py +29 -2
  3. esphome/components/animation/__init__.py +5 -8
  4. esphome/components/animation/animation.cpp +1 -1
  5. esphome/components/audio/__init__.py +9 -0
  6. esphome/components/audio/audio.h +21 -0
  7. esphome/components/axs15231/__init__.py +6 -0
  8. esphome/components/axs15231/touchscreen/__init__.py +36 -0
  9. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +64 -0
  10. esphome/components/axs15231/touchscreen/axs15231_touchscreen.h +27 -0
  11. esphome/components/bme68x_bsec2/__init__.py +1 -1
  12. esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +50 -47
  13. esphome/components/bme68x_bsec2/bme68x_bsec2.h +0 -2
  14. esphome/components/bytebuffer/__init__.py +5 -0
  15. esphome/components/bytebuffer/bytebuffer.h +421 -0
  16. esphome/components/climate/__init__.py +14 -13
  17. esphome/components/datetime/__init__.py +3 -3
  18. esphome/components/debug/debug_esp32.cpp +16 -8
  19. esphome/components/dfplayer/dfplayer.cpp +132 -6
  20. esphome/components/dfplayer/dfplayer.h +19 -53
  21. esphome/components/display/display.cpp +142 -0
  22. esphome/components/display/display.h +7 -0
  23. esphome/components/es8311/__init__.py +0 -0
  24. esphome/components/es8311/audio_dac.py +70 -0
  25. esphome/components/es8311/es8311.cpp +227 -0
  26. esphome/components/es8311/es8311.h +135 -0
  27. esphome/components/es8311/es8311_const.h +195 -0
  28. esphome/components/esp32/boards.py +199 -1
  29. esphome/components/esp32/gpio.py +3 -1
  30. esphome/components/esp32_ble/const_esp32c6.h +7 -0
  31. esphome/components/esp32_ble_client/ble_client_base.h +1 -1
  32. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +3 -0
  33. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -1
  34. esphome/components/esp32_rmt_led_strip/led_strip.cpp +2 -2
  35. esphome/components/esp32_rmt_led_strip/led_strip.h +2 -0
  36. esphome/components/esp32_rmt_led_strip/light.py +3 -1
  37. esphome/components/esp8266/gpio.py +7 -5
  38. esphome/components/ethernet/__init__.py +55 -1
  39. esphome/components/ethernet/ethernet_component.cpp +14 -1
  40. esphome/components/ethernet/ethernet_component.h +7 -1
  41. esphome/components/font/__init__.py +213 -108
  42. esphome/components/gp8403/output/__init__.py +1 -1
  43. esphome/components/host/gpio.py +6 -4
  44. esphome/components/http_request/__init__.py +12 -0
  45. esphome/components/http_request/http_request.h +65 -3
  46. esphome/components/http_request/http_request_arduino.cpp +4 -3
  47. esphome/components/http_request/http_request_idf.cpp +12 -14
  48. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  49. esphome/components/http_request/update/http_request_update.cpp +1 -1
  50. esphome/components/i2c_device/__init__.py +26 -0
  51. esphome/components/i2c_device/i2c_device.cpp +17 -0
  52. esphome/components/i2c_device/i2c_device.h +18 -0
  53. esphome/components/i2s_audio/__init__.py +1 -3
  54. esphome/components/i2s_audio/speaker/__init__.py +12 -4
  55. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +426 -200
  56. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +92 -33
  57. esphome/components/ili9xxx/display.py +5 -1
  58. esphome/components/image/__init__.py +5 -8
  59. esphome/components/image/image.cpp +14 -14
  60. esphome/components/image/image.h +20 -24
  61. esphome/components/internal_temperature/internal_temperature.cpp +51 -2
  62. esphome/components/internal_temperature/internal_temperature.h +1 -0
  63. esphome/components/ld2420/ld2420.cpp +1 -1
  64. esphome/components/libretiny/gpio.py +4 -2
  65. esphome/components/light/__init__.py +32 -1
  66. esphome/components/light/automation.py +39 -32
  67. esphome/components/light/effects.py +36 -36
  68. esphome/components/light/light_state.cpp +6 -16
  69. esphome/components/light/light_state.h +34 -0
  70. esphome/components/light/types.py +3 -1
  71. esphome/components/logger/logger_esp32.cpp +15 -0
  72. esphome/components/lvgl/__init__.py +202 -95
  73. esphome/components/lvgl/automation.py +42 -40
  74. esphome/components/lvgl/binary_sensor/__init__.py +8 -15
  75. esphome/components/lvgl/defines.py +14 -8
  76. esphome/components/lvgl/encoders.py +11 -8
  77. esphome/components/lvgl/keypads.py +77 -0
  78. esphome/components/lvgl/light/__init__.py +6 -8
  79. esphome/components/lvgl/lv_validation.py +2 -4
  80. esphome/components/lvgl/lvcode.py +3 -9
  81. esphome/components/lvgl/lvgl_esphome.cpp +210 -89
  82. esphome/components/lvgl/lvgl_esphome.h +113 -30
  83. esphome/components/lvgl/lvgl_proxy.h +17 -0
  84. esphome/components/lvgl/number/__init__.py +10 -15
  85. esphome/components/lvgl/schemas.py +4 -2
  86. esphome/components/lvgl/select/__init__.py +12 -37
  87. esphome/components/lvgl/select/lvgl_select.h +27 -33
  88. esphome/components/lvgl/sensor/__init__.py +8 -14
  89. esphome/components/lvgl/styles.py +3 -4
  90. esphome/components/lvgl/switch/__init__.py +8 -13
  91. esphome/components/lvgl/text/__init__.py +5 -6
  92. esphome/components/lvgl/text_sensor/__init__.py +15 -15
  93. esphome/components/lvgl/touchscreens.py +2 -3
  94. esphome/components/lvgl/trigger.py +7 -9
  95. esphome/components/lvgl/types.py +9 -3
  96. esphome/components/lvgl/widgets/__init__.py +32 -21
  97. esphome/components/lvgl/widgets/animimg.py +4 -3
  98. esphome/components/lvgl/widgets/dropdown.py +22 -10
  99. esphome/components/lvgl/widgets/img.py +2 -0
  100. esphome/components/lvgl/widgets/msgbox.py +6 -5
  101. esphome/components/lvgl/widgets/obj.py +4 -2
  102. esphome/components/lvgl/widgets/page.py +3 -2
  103. esphome/components/lvgl/widgets/qrcode.py +54 -0
  104. esphome/components/lvgl/widgets/roller.py +21 -14
  105. esphome/components/lvgl/widgets/tileview.py +2 -1
  106. esphome/components/max17043/__init__.py +1 -0
  107. esphome/components/max17043/automation.h +20 -0
  108. esphome/components/max17043/max17043.cpp +98 -0
  109. esphome/components/max17043/max17043.h +29 -0
  110. esphome/components/max17043/sensor.py +77 -0
  111. esphome/components/media_player/__init__.py +11 -0
  112. esphome/components/media_player/automation.h +10 -0
  113. esphome/components/media_player/media_player.cpp +4 -0
  114. esphome/components/midea/air_conditioner.cpp +17 -1
  115. esphome/components/mlx90393/sensor.py +1 -1
  116. esphome/components/modbus/modbus.cpp +24 -12
  117. esphome/components/modbus_controller/__init__.py +31 -1
  118. esphome/components/modbus_controller/automation.h +16 -0
  119. esphome/components/modbus_controller/const.py +2 -0
  120. esphome/components/modbus_controller/modbus_controller.cpp +14 -2
  121. esphome/components/modbus_controller/modbus_controller.h +9 -0
  122. esphome/components/mopeka_pro_check/mopeka_pro_check.cpp +40 -21
  123. esphome/components/mopeka_pro_check/mopeka_pro_check.h +9 -2
  124. esphome/components/mopeka_pro_check/sensor.py +41 -0
  125. esphome/components/mqtt/__init__.py +36 -0
  126. esphome/components/mqtt/mqtt_client.cpp +27 -3
  127. esphome/components/mqtt/mqtt_client.h +27 -2
  128. esphome/components/mqtt/mqtt_climate.cpp +4 -2
  129. esphome/components/mqtt/mqtt_component.cpp +6 -0
  130. esphome/components/mqtt/mqtt_component.h +4 -0
  131. esphome/components/mqtt/mqtt_const.h +6 -0
  132. esphome/components/online_image/online_image.cpp +2 -8
  133. esphome/components/online_image/online_image.h +2 -6
  134. esphome/components/opentherm/__init__.py +35 -9
  135. esphome/components/opentherm/binary_sensor/__init__.py +33 -0
  136. esphome/components/opentherm/const.py +11 -0
  137. esphome/components/opentherm/generate.py +142 -0
  138. esphome/components/opentherm/hub.cpp +130 -24
  139. esphome/components/opentherm/hub.h +62 -9
  140. esphome/components/opentherm/input.h +18 -0
  141. esphome/components/opentherm/input.py +51 -0
  142. esphome/components/opentherm/number/__init__.py +74 -0
  143. esphome/components/opentherm/number/number.cpp +40 -0
  144. esphome/components/opentherm/number/number.h +31 -0
  145. esphome/components/opentherm/opentherm.cpp +30 -0
  146. esphome/components/opentherm/opentherm.h +34 -2
  147. esphome/components/opentherm/opentherm_macros.h +151 -0
  148. esphome/components/opentherm/output/__init__.py +47 -0
  149. esphome/components/opentherm/output/output.cpp +18 -0
  150. esphome/components/opentherm/output/output.h +33 -0
  151. esphome/components/opentherm/schema.py +814 -0
  152. esphome/components/opentherm/sensor/__init__.py +51 -0
  153. esphome/components/opentherm/switch/__init__.py +43 -0
  154. esphome/components/opentherm/switch/switch.cpp +28 -0
  155. esphome/components/opentherm/switch/switch.h +20 -0
  156. esphome/components/opentherm/validate.py +31 -0
  157. esphome/components/pcd8544/display.py +8 -4
  158. esphome/components/prometheus/prometheus_handler.cpp +176 -14
  159. esphome/components/prometheus/prometheus_handler.h +25 -7
  160. esphome/components/qspi_amoled/display.py +1 -141
  161. esphome/components/qspi_dbi/display.py +185 -0
  162. esphome/components/qspi_dbi/models.py +64 -0
  163. esphome/components/{qspi_amoled/qspi_amoled.cpp → qspi_dbi/qspi_dbi.cpp} +95 -46
  164. esphome/components/{qspi_amoled/qspi_amoled.h → qspi_dbi/qspi_dbi.h} +26 -15
  165. esphome/components/rp2040/__init__.py +6 -3
  166. esphome/components/rp2040/gpio.py +5 -3
  167. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +20 -0
  168. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h +3 -2
  169. esphome/components/rtttl/rtttl.cpp +4 -1
  170. esphome/components/rtttl/rtttl.h +1 -0
  171. esphome/components/sdl/sdl_esphome.cpp +22 -5
  172. esphome/components/sdl/sdl_esphome.h +1 -0
  173. esphome/components/sdm_meter/sdm_meter.cpp +1 -1
  174. esphome/components/sensor/__init__.py +18 -8
  175. esphome/components/sensor/filter.cpp +19 -18
  176. esphome/components/sensor/filter.h +9 -10
  177. esphome/components/sgp4x/sgp4x.cpp +40 -74
  178. esphome/components/sgp4x/sgp4x.h +5 -3
  179. esphome/components/speaker/__init__.py +51 -5
  180. esphome/components/speaker/automation.h +25 -0
  181. esphome/components/speaker/speaker.h +72 -1
  182. esphome/components/spi/__init__.py +15 -14
  183. esphome/components/spi_device/__init__.py +4 -15
  184. esphome/components/ssd1306_spi/display.py +6 -2
  185. esphome/components/ssd1322_spi/display.py +6 -2
  186. esphome/components/ssd1325_spi/display.py +6 -2
  187. esphome/components/ssd1327_spi/display.py +6 -2
  188. esphome/components/ssd1331_spi/display.py +6 -2
  189. esphome/components/ssd1351_spi/display.py +6 -2
  190. esphome/components/st7567_spi/display.py +6 -2
  191. esphome/components/st7701s/display.py +5 -1
  192. esphome/components/st7735/display.py +10 -5
  193. esphome/components/st7789v/display.py +12 -7
  194. esphome/components/statsd/statsd.cpp +2 -0
  195. esphome/components/statsd/statsd.h +2 -0
  196. esphome/components/sun/sun.h +3 -0
  197. esphome/components/tc74/__init__.py +1 -0
  198. esphome/components/tc74/sensor.py +32 -0
  199. esphome/components/tc74/tc74.cpp +68 -0
  200. esphome/components/tc74/tc74.h +28 -0
  201. esphome/components/touchscreen/__init__.py +41 -50
  202. esphome/components/touchscreen/touchscreen.h +4 -8
  203. esphome/components/tuya/fan/tuya_fan.cpp +1 -1
  204. esphome/components/udp/udp_component.cpp +6 -3
  205. esphome/components/udp/udp_component.h +4 -2
  206. esphome/components/waveshare_epaper/display.py +6 -2
  207. esphome/components/web_server/web_server.cpp +22 -0
  208. esphome/components/web_server/web_server.h +3 -0
  209. esphome/components/weikai/weikai.h +2 -2
  210. esphome/components/wifi/wifi_component.cpp +2 -2
  211. esphome/components/wifi/wifi_component_esp32_arduino.cpp +4 -4
  212. esphome/components/wifi/wifi_component_esp8266.cpp +4 -4
  213. esphome/components/wifi/wifi_component_esp_idf.cpp +2 -2
  214. esphome/components/xpt2046/touchscreen/__init__.py +7 -32
  215. esphome/config_validation.py +3 -1
  216. esphome/const.py +9 -2
  217. esphome/core/defines.h +8 -2
  218. esphome/core/helpers.cpp +32 -17
  219. esphome/core/helpers.h +32 -16
  220. esphome/core/ring_buffer.cpp +2 -2
  221. esphome/core/ring_buffer.h +2 -2
  222. esphome/dashboard/core.py +25 -0
  223. esphome/dashboard/status/mdns.py +3 -4
  224. esphome/dashboard/web_server.py +54 -19
  225. esphome/espota2.py +36 -35
  226. esphome/helpers.py +68 -16
  227. esphome/mqtt.py +9 -2
  228. esphome/storage_json.py +4 -0
  229. esphome/writer.py +7 -18
  230. esphome/zeroconf.py +8 -6
  231. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/METADATA +7 -5
  232. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/RECORD +237 -191
  233. esphome/core/bytebuffer.cpp +0 -167
  234. esphome/core/bytebuffer.h +0 -144
  235. /esphome/components/{qspi_amoled → qspi_dbi}/__init__.py +0 -0
  236. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/LICENSE +0 -0
  237. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/WHEEL +0 -0
  238. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/entry_points.txt +0 -0
  239. {esphome-2024.10.2.dist-info → esphome-2024.11.0.dist-info}/top_level.txt +0 -0
@@ -89,6 +89,9 @@ class MQTTComponent : public Component {
89
89
  void disable_discovery();
90
90
  bool is_discovery_enabled() const;
91
91
 
92
+ /// Set the QOS for subscribe messages (used in discovery).
93
+ void set_subscribe_qos(uint8_t qos);
94
+
92
95
  /// Override this method to return the component type (e.g. "light", "sensor", ...)
93
96
  virtual std::string component_type() const = 0;
94
97
 
@@ -204,6 +207,7 @@ class MQTTComponent : public Component {
204
207
  bool command_retain_{false};
205
208
  bool retain_{true};
206
209
  uint8_t qos_{0};
210
+ uint8_t subscribe_qos_{0};
207
211
  bool discovery_enabled_{true};
208
212
  bool resend_state_{false};
209
213
  };
@@ -51,6 +51,7 @@ constexpr const char *const MQTT_COMMAND_TOPIC = "cmd_t";
51
51
  constexpr const char *const MQTT_CONFIGURATION_URL = "cu";
52
52
  constexpr const char *const MQTT_CURRENT_HUMIDITY_TEMPLATE = "curr_hum_tpl";
53
53
  constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "curr_hum_t";
54
+ constexpr const char *const MQTT_CURRENT_TEMPERATURE_STEP = "precision";
54
55
  constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "curr_temp_tpl";
55
56
  constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "curr_temp_t";
56
57
  constexpr const char *const MQTT_DEVICE = "dev";
@@ -180,6 +181,7 @@ constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "pr_mode_cmd_t";
180
181
  constexpr const char *const MQTT_PRESET_MODE_STATE_TOPIC = "pr_mode_stat_t";
181
182
  constexpr const char *const MQTT_PRESET_MODE_VALUE_TEMPLATE = "pr_mode_val_tpl";
182
183
  constexpr const char *const MQTT_PRESET_MODES = "pr_modes";
184
+ constexpr const char *const MQTT_QOS = "qos";
183
185
  constexpr const char *const MQTT_RED_TEMPLATE = "r_tpl";
184
186
  constexpr const char *const MQTT_RETAIN = "ret";
185
187
  constexpr const char *const MQTT_RGB_COMMAND_TEMPLATE = "rgb_cmd_tpl";
@@ -231,6 +233,7 @@ constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "hum_cmd_tpl
231
233
  constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "hum_cmd_t";
232
234
  constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "hum_state_tpl";
233
235
  constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "hum_stat_t";
236
+ constexpr const char *const MQTT_TARGET_TEMPERATURE_STEP = "temp_step";
234
237
  constexpr const char *const MQTT_TEMPERATURE_COMMAND_TEMPLATE = "temp_cmd_tpl";
235
238
  constexpr const char *const MQTT_TEMPERATURE_COMMAND_TOPIC = "temp_cmd_t";
236
239
  constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE = "temp_hi_cmd_tpl";
@@ -312,6 +315,7 @@ constexpr const char *const MQTT_COMMAND_TOPIC = "command_topic";
312
315
  constexpr const char *const MQTT_CONFIGURATION_URL = "configuration_url";
313
316
  constexpr const char *const MQTT_CURRENT_HUMIDITY_TEMPLATE = "current_humidity_template";
314
317
  constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "current_humidity_topic";
318
+ constexpr const char *const MQTT_CURRENT_TEMPERATURE_STEP = "precision";
315
319
  constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "current_temperature_template";
316
320
  constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "current_temperature_topic";
317
321
  constexpr const char *const MQTT_DEVICE = "device";
@@ -441,6 +445,7 @@ constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "preset_mode_comman
441
445
  constexpr const char *const MQTT_PRESET_MODE_STATE_TOPIC = "preset_mode_state_topic";
442
446
  constexpr const char *const MQTT_PRESET_MODE_VALUE_TEMPLATE = "preset_mode_value_template";
443
447
  constexpr const char *const MQTT_PRESET_MODES = "preset_modes";
448
+ constexpr const char *const MQTT_QOS = "qos";
444
449
  constexpr const char *const MQTT_RED_TEMPLATE = "red_template";
445
450
  constexpr const char *const MQTT_RETAIN = "retain";
446
451
  constexpr const char *const MQTT_RGB_COMMAND_TEMPLATE = "rgb_command_template";
@@ -492,6 +497,7 @@ constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "target_humi
492
497
  constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "target_humidity_command_topic";
493
498
  constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "target_humidity_state_template";
494
499
  constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "target_humidity_state_topic";
500
+ constexpr const char *const MQTT_TARGET_TEMPERATURE_STEP = "temp_step";
495
501
  constexpr const char *const MQTT_TEMPERATURE_COMMAND_TEMPLATE = "temperature_command_template";
496
502
  constexpr const char *const MQTT_TEMPERATURE_COMMAND_TOPIC = "temperature_command_topic";
497
503
  constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE = "temperature_high_command_template";
@@ -215,16 +215,10 @@ void OnlineImage::draw_pixel_(int x, int y, Color color) {
215
215
  }
216
216
  case ImageType::IMAGE_TYPE_RGB565: {
217
217
  uint16_t col565 = display::ColorUtil::color_to_565(color);
218
- if (this->has_transparency()) {
219
- if (col565 == 0x0020) {
220
- col565 = 0;
221
- }
222
- if (color.w < 0x80) {
223
- col565 = 0x0020;
224
- }
225
- }
226
218
  this->buffer_[pos + 0] = static_cast<uint8_t>((col565 >> 8) & 0xFF);
227
219
  this->buffer_[pos + 1] = static_cast<uint8_t>(col565 & 0xFF);
220
+ if (this->has_transparency())
221
+ this->buffer_[pos + 2] = color.w;
228
222
  break;
229
223
  }
230
224
  case ImageType::IMAGE_TYPE_RGBA: {
@@ -86,13 +86,9 @@ class OnlineImage : public PollingComponent,
86
86
  Allocator allocator_{Allocator::Flags::ALLOW_FAILURE};
87
87
 
88
88
  uint32_t get_buffer_size_() const { return get_buffer_size_(this->buffer_width_, this->buffer_height_); }
89
- int get_buffer_size_(int width, int height) const {
90
- return std::ceil(image::image_type_to_bpp(this->type_) * width * height / 8.0);
91
- }
89
+ int get_buffer_size_(int width, int height) const { return (this->get_bpp() * width + 7u) / 8u * height; }
92
90
 
93
- int get_position_(int x, int y) const {
94
- return ((x + y * this->buffer_width_) * image::image_type_to_bpp(this->type_)) / 8;
95
- }
91
+ int get_position_(int x, int y) const { return (x + y * this->buffer_width_) * this->get_bpp() / 8; }
96
92
 
97
93
  ESPHOME_ALWAYS_INLINE bool auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; }
98
94
 
@@ -1,9 +1,11 @@
1
1
  from typing import Any
2
2
 
3
- from esphome import pins
4
3
  import esphome.codegen as cg
5
4
  import esphome.config_validation as cv
5
+ from esphome import pins
6
+ from esphome.components import sensor
6
7
  from esphome.const import CONF_ID, PLATFORM_ESP32, PLATFORM_ESP8266
8
+ from . import const, schema, validate, generate
7
9
 
8
10
  CODEOWNERS = ["@olegtarasov"]
9
11
  MULTI_CONF = True
@@ -15,15 +17,15 @@ CONF_DHW_ENABLE = "dhw_enable"
15
17
  CONF_COOLING_ENABLE = "cooling_enable"
16
18
  CONF_OTC_ACTIVE = "otc_active"
17
19
  CONF_CH2_ACTIVE = "ch2_active"
20
+ CONF_SUMMER_MODE_ACTIVE = "summer_mode_active"
21
+ CONF_DHW_BLOCK = "dhw_block"
18
22
  CONF_SYNC_MODE = "sync_mode"
19
-
20
- opentherm_ns = cg.esphome_ns.namespace("opentherm")
21
- OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component)
23
+ CONF_OPENTHERM_VERSION = "opentherm_version"
22
24
 
23
25
  CONFIG_SCHEMA = cv.All(
24
26
  cv.Schema(
25
27
  {
26
- cv.GenerateID(): cv.declare_id(OpenthermHub),
28
+ cv.GenerateID(): cv.declare_id(generate.OpenthermHub),
27
29
  cv.Required(CONF_IN_PIN): pins.internal_gpio_input_pin_schema,
28
30
  cv.Required(CONF_OUT_PIN): pins.internal_gpio_output_pin_schema,
29
31
  cv.Optional(CONF_CH_ENABLE, True): cv.boolean,
@@ -31,16 +33,23 @@ CONFIG_SCHEMA = cv.All(
31
33
  cv.Optional(CONF_COOLING_ENABLE, False): cv.boolean,
32
34
  cv.Optional(CONF_OTC_ACTIVE, False): cv.boolean,
33
35
  cv.Optional(CONF_CH2_ACTIVE, False): cv.boolean,
36
+ cv.Optional(CONF_SUMMER_MODE_ACTIVE, False): cv.boolean,
37
+ cv.Optional(CONF_DHW_BLOCK, False): cv.boolean,
34
38
  cv.Optional(CONF_SYNC_MODE, False): cv.boolean,
39
+ cv.Optional(CONF_OPENTHERM_VERSION): cv.positive_float,
35
40
  }
36
- ).extend(cv.COMPONENT_SCHEMA),
41
+ )
42
+ .extend(
43
+ validate.create_entities_schema(
44
+ schema.INPUTS, (lambda _: cv.use_id(sensor.Sensor))
45
+ )
46
+ )
47
+ .extend(cv.COMPONENT_SCHEMA),
37
48
  cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]),
38
49
  )
39
50
 
40
51
 
41
52
  async def to_code(config: dict[str, Any]) -> None:
42
- # Create the hub, passing the two callbacks defined below
43
- # Since the hub is used in the callbacks, we need to define it first
44
53
  var = cg.new_Pvariable(config[CONF_ID])
45
54
  await cg.register_component(var, config)
46
55
 
@@ -52,6 +61,23 @@ async def to_code(config: dict[str, Any]) -> None:
52
61
  cg.add(var.set_out_pin(out_pin))
53
62
 
54
63
  non_sensors = {CONF_ID, CONF_IN_PIN, CONF_OUT_PIN}
64
+ input_sensors = []
55
65
  for key, value in config.items():
56
- if key not in non_sensors:
66
+ if key in non_sensors:
67
+ continue
68
+ if key in schema.INPUTS:
69
+ input_sensor = await cg.get_variable(value)
70
+ cg.add(
71
+ getattr(var, f"set_{key}_{const.INPUT_SENSOR.lower()}")(input_sensor)
72
+ )
73
+ input_sensors.append(key)
74
+ else:
57
75
  cg.add(getattr(var, f"set_{key}")(value))
76
+
77
+ if len(input_sensors) > 0:
78
+ generate.define_has_component(const.INPUT_SENSOR, input_sensors)
79
+ generate.define_message_handler(
80
+ const.INPUT_SENSOR, input_sensors, schema.INPUTS
81
+ )
82
+ generate.define_readers(const.INPUT_SENSOR, input_sensors)
83
+ generate.add_messages(var, input_sensors, schema.INPUTS)
@@ -0,0 +1,33 @@
1
+ from typing import Any
2
+
3
+ import esphome.config_validation as cv
4
+ from esphome.components import binary_sensor
5
+ from .. import const, schema, validate, generate
6
+
7
+ DEPENDENCIES = [const.OPENTHERM]
8
+ COMPONENT_TYPE = const.BINARY_SENSOR
9
+
10
+
11
+ def get_entity_validation_schema(entity: schema.BinarySensorSchema) -> cv.Schema:
12
+ return binary_sensor.binary_sensor_schema(
13
+ device_class=(
14
+ entity.device_class
15
+ or binary_sensor._UNDEF # pylint: disable=protected-access
16
+ ),
17
+ icon=(entity.icon or binary_sensor._UNDEF), # pylint: disable=protected-access
18
+ )
19
+
20
+
21
+ CONFIG_SCHEMA = validate.create_component_schema(
22
+ schema.BINARY_SENSORS, get_entity_validation_schema
23
+ )
24
+
25
+
26
+ async def to_code(config: dict[str, Any]) -> None:
27
+ await generate.component_to_code(
28
+ COMPONENT_TYPE,
29
+ schema.BINARY_SENSORS,
30
+ binary_sensor.BinarySensor,
31
+ generate.create_only_conf(binary_sensor.new_binary_sensor),
32
+ config,
33
+ )
@@ -0,0 +1,11 @@
1
+ OPENTHERM = "opentherm"
2
+
3
+ CONF_OPENTHERM_ID = "opentherm_id"
4
+ CONF_DATA_TYPE = "data_type"
5
+
6
+ SENSOR = "sensor"
7
+ BINARY_SENSOR = "binary_sensor"
8
+ SWITCH = "switch"
9
+ NUMBER = "number"
10
+ OUTPUT = "output"
11
+ INPUT_SENSOR = "input_sensor"
@@ -0,0 +1,142 @@
1
+ from collections.abc import Awaitable
2
+ from typing import Any, Callable
3
+
4
+ import esphome.codegen as cg
5
+ from esphome.const import CONF_ID
6
+ from . import const
7
+ from .schema import TSchema
8
+
9
+ opentherm_ns = cg.esphome_ns.namespace("opentherm")
10
+ OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component)
11
+
12
+
13
+ def define_has_component(component_type: str, keys: list[str]) -> None:
14
+ cg.add_define(
15
+ f"OPENTHERM_{component_type.upper()}_LIST(F, sep)",
16
+ cg.RawExpression(
17
+ " sep ".join(map(lambda key: f"F({key}_{component_type.lower()})", keys))
18
+ ),
19
+ )
20
+ for key in keys:
21
+ cg.add_define(f"OPENTHERM_HAS_{component_type.upper()}_{key}")
22
+
23
+
24
+ def define_message_handler(
25
+ component_type: str, keys: list[str], schemas: dict[str, TSchema]
26
+ ) -> None:
27
+ # The macros defined here should be able to generate things like this:
28
+ # // Parsing a message and publishing to sensors
29
+ # case MessageId::Message:
30
+ # // Can have multiple sensors here, for example for a Status message with multiple flags
31
+ # this->thing_binary_sensor->publish_state(parse_flag8_lb_0(response));
32
+ # this->other_binary_sensor->publish_state(parse_flag8_lb_1(response));
33
+ # break;
34
+ # // Building a message for a write request
35
+ # case MessageId::Message: {
36
+ # unsigned int data = 0;
37
+ # data = write_flag8_lb_0(some_input_switch->state, data); // Where input_sensor can also be a number/output/switch
38
+ # data = write_u8_hb(some_number->state, data);
39
+ # return opentherm_->build_request_(MessageType::WriteData, MessageId::Message, data);
40
+ # }
41
+
42
+ messages: dict[str, list[tuple[str, str]]] = {}
43
+ for key in keys:
44
+ msg = schemas[key].message
45
+ if msg not in messages:
46
+ messages[msg] = []
47
+ messages[msg].append((key, schemas[key].message_data))
48
+
49
+ cg.add_define(
50
+ f"OPENTHERM_{component_type.upper()}_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep)",
51
+ cg.RawExpression(
52
+ " msg_sep ".join(
53
+ [
54
+ f"MESSAGE({msg}) "
55
+ + " entity_sep ".join(
56
+ [
57
+ f"ENTITY({key}_{component_type.lower()}, {msg_data})"
58
+ for key, msg_data in keys
59
+ ]
60
+ )
61
+ + " postscript"
62
+ for msg, keys in messages.items()
63
+ ]
64
+ )
65
+ ),
66
+ )
67
+
68
+
69
+ def define_readers(component_type: str, keys: list[str]) -> None:
70
+ for key in keys:
71
+ cg.add_define(
72
+ f"OPENTHERM_READ_{key}",
73
+ cg.RawExpression(f"this->{key}_{component_type.lower()}->state"),
74
+ )
75
+
76
+
77
+ def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]):
78
+ messages: set[tuple[str, bool]] = set()
79
+ for key in keys:
80
+ messages.add((schemas[key].message, schemas[key].keep_updated))
81
+ for msg, keep_updated in messages:
82
+ msg_expr = cg.RawExpression(f"esphome::opentherm::MessageId::{msg}")
83
+ if keep_updated:
84
+ cg.add(hub.add_repeating_message(msg_expr))
85
+ else:
86
+ cg.add(hub.add_initial_message(msg_expr))
87
+
88
+
89
+ def add_property_set(var: cg.MockObj, config_key: str, config: dict[str, Any]) -> None:
90
+ if config_key in config:
91
+ cg.add(getattr(var, f"set_{config_key}")(config[config_key]))
92
+
93
+
94
+ Create = Callable[[dict[str, Any], str, cg.MockObj], Awaitable[cg.Pvariable]]
95
+
96
+
97
+ def create_only_conf(
98
+ create: Callable[[dict[str, Any]], Awaitable[cg.Pvariable]]
99
+ ) -> Create:
100
+ return lambda conf, _key, _hub: create(conf)
101
+
102
+
103
+ async def component_to_code(
104
+ component_type: str,
105
+ schemas: dict[str, TSchema],
106
+ type: cg.MockObjClass,
107
+ create: Create,
108
+ config: dict[str, Any],
109
+ ) -> list[str]:
110
+ """Generate the code for each configured component in the schema of a component type.
111
+
112
+ Parameters:
113
+ - component_type: The type of component, e.g. "sensor" or "binary_sensor"
114
+ - schema_: The schema for that component type, a list of available components
115
+ - type: The type of the component, e.g. sensor.Sensor or OpenthermOutput
116
+ - create: A constructor function for the component, which receives the config,
117
+ the key and the hub and should asynchronously return the new component
118
+ - config: The configuration for this component type
119
+
120
+ Returns: The list of keys for the created components
121
+ """
122
+ cg.add_define(f"OPENTHERM_USE_{component_type.upper()}")
123
+
124
+ hub = await cg.get_variable(config[const.CONF_OPENTHERM_ID])
125
+
126
+ keys: list[str] = []
127
+ for key, conf in config.items():
128
+ if not isinstance(conf, dict):
129
+ continue
130
+ id = conf[CONF_ID]
131
+ if id and id.type == type:
132
+ entity = await create(conf, key, hub)
133
+ if const.CONF_DATA_TYPE in conf:
134
+ schemas[key].message_data = conf[const.CONF_DATA_TYPE]
135
+ cg.add(getattr(hub, f"set_{key}_{component_type.lower()}")(entity))
136
+ keys.append(key)
137
+
138
+ define_has_component(component_type, keys)
139
+ define_message_handler(component_type, keys, schemas)
140
+ add_messages(hub, keys, schemas)
141
+
142
+ return keys
@@ -7,50 +7,147 @@ namespace esphome {
7
7
  namespace opentherm {
8
8
 
9
9
  static const char *const TAG = "opentherm";
10
-
11
- OpenthermData OpenthermHub::build_request_(MessageId request_id) {
10
+ namespace message_data {
11
+ bool parse_flag8_lb_0(OpenthermData &data) { return read_bit(data.valueLB, 0); }
12
+ bool parse_flag8_lb_1(OpenthermData &data) { return read_bit(data.valueLB, 1); }
13
+ bool parse_flag8_lb_2(OpenthermData &data) { return read_bit(data.valueLB, 2); }
14
+ bool parse_flag8_lb_3(OpenthermData &data) { return read_bit(data.valueLB, 3); }
15
+ bool parse_flag8_lb_4(OpenthermData &data) { return read_bit(data.valueLB, 4); }
16
+ bool parse_flag8_lb_5(OpenthermData &data) { return read_bit(data.valueLB, 5); }
17
+ bool parse_flag8_lb_6(OpenthermData &data) { return read_bit(data.valueLB, 6); }
18
+ bool parse_flag8_lb_7(OpenthermData &data) { return read_bit(data.valueLB, 7); }
19
+ bool parse_flag8_hb_0(OpenthermData &data) { return read_bit(data.valueHB, 0); }
20
+ bool parse_flag8_hb_1(OpenthermData &data) { return read_bit(data.valueHB, 1); }
21
+ bool parse_flag8_hb_2(OpenthermData &data) { return read_bit(data.valueHB, 2); }
22
+ bool parse_flag8_hb_3(OpenthermData &data) { return read_bit(data.valueHB, 3); }
23
+ bool parse_flag8_hb_4(OpenthermData &data) { return read_bit(data.valueHB, 4); }
24
+ bool parse_flag8_hb_5(OpenthermData &data) { return read_bit(data.valueHB, 5); }
25
+ bool parse_flag8_hb_6(OpenthermData &data) { return read_bit(data.valueHB, 6); }
26
+ bool parse_flag8_hb_7(OpenthermData &data) { return read_bit(data.valueHB, 7); }
27
+ uint8_t parse_u8_lb(OpenthermData &data) { return data.valueLB; }
28
+ uint8_t parse_u8_hb(OpenthermData &data) { return data.valueHB; }
29
+ int8_t parse_s8_lb(OpenthermData &data) { return (int8_t) data.valueLB; }
30
+ int8_t parse_s8_hb(OpenthermData &data) { return (int8_t) data.valueHB; }
31
+ uint16_t parse_u16(OpenthermData &data) { return data.u16(); }
32
+ uint16_t parse_u8_lb_60(OpenthermData &data) { return data.valueLB * 60; }
33
+ uint16_t parse_u8_hb_60(OpenthermData &data) { return data.valueHB * 60; }
34
+ int16_t parse_s16(OpenthermData &data) { return data.s16(); }
35
+ float parse_f88(OpenthermData &data) { return data.f88(); }
36
+
37
+ void write_flag8_lb_0(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 0, value); }
38
+ void write_flag8_lb_1(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 1, value); }
39
+ void write_flag8_lb_2(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 2, value); }
40
+ void write_flag8_lb_3(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 3, value); }
41
+ void write_flag8_lb_4(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 4, value); }
42
+ void write_flag8_lb_5(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 5, value); }
43
+ void write_flag8_lb_6(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 6, value); }
44
+ void write_flag8_lb_7(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 7, value); }
45
+ void write_flag8_hb_0(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 0, value); }
46
+ void write_flag8_hb_1(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 1, value); }
47
+ void write_flag8_hb_2(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 2, value); }
48
+ void write_flag8_hb_3(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 3, value); }
49
+ void write_flag8_hb_4(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 4, value); }
50
+ void write_flag8_hb_5(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 5, value); }
51
+ void write_flag8_hb_6(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 6, value); }
52
+ void write_flag8_hb_7(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 7, value); }
53
+ void write_u8_lb(const uint8_t value, OpenthermData &data) { data.valueLB = value; }
54
+ void write_u8_hb(const uint8_t value, OpenthermData &data) { data.valueHB = value; }
55
+ void write_s8_lb(const int8_t value, OpenthermData &data) { data.valueLB = (uint8_t) value; }
56
+ void write_s8_hb(const int8_t value, OpenthermData &data) { data.valueHB = (uint8_t) value; }
57
+ void write_u16(const uint16_t value, OpenthermData &data) { data.u16(value); }
58
+ void write_s16(const int16_t value, OpenthermData &data) { data.s16(value); }
59
+ void write_f88(const float value, OpenthermData &data) { data.f88(value); }
60
+
61
+ } // namespace message_data
62
+
63
+ OpenthermData OpenthermHub::build_request_(MessageId request_id) const {
12
64
  OpenthermData data;
13
65
  data.type = 0;
14
66
  data.id = 0;
15
67
  data.valueHB = 0;
16
68
  data.valueLB = 0;
17
69
 
18
- // First, handle the status request. This requires special logic, because we
19
- // wouldn't want to inadvertently disable domestic hot water, for example.
20
- // It is also included in the macro-generated code below, but that will
21
- // never be executed, because we short-circuit it here.
70
+ // We need this special logic for STATUS message because we have two options for specifying boiler modes:
71
+ // with static config values in the hub, or with separate switches.
22
72
  if (request_id == MessageId::STATUS) {
23
- bool const ch_enabled = this->ch_enable;
24
- bool dhw_enabled = this->dhw_enable;
25
- bool cooling_enabled = this->cooling_enable;
26
- bool otc_enabled = this->otc_active;
27
- bool ch2_enabled = this->ch2_active;
73
+ // NOLINTBEGIN
74
+ bool const ch_enabled = this->ch_enable && OPENTHERM_READ_ch_enable && OPENTHERM_READ_t_set > 0.0;
75
+ bool const dhw_enabled = this->dhw_enable && OPENTHERM_READ_dhw_enable;
76
+ bool const cooling_enabled =
77
+ this->cooling_enable && OPENTHERM_READ_cooling_enable && OPENTHERM_READ_cooling_control > 0.0;
78
+ bool const otc_enabled = this->otc_active && OPENTHERM_READ_otc_active;
79
+ bool const ch2_enabled = this->ch2_active && OPENTHERM_READ_ch2_active && OPENTHERM_READ_t_set_ch2 > 0.0;
80
+ bool const summer_mode_is_active = this->summer_mode_active && OPENTHERM_READ_summer_mode_active;
81
+ bool const dhw_blocked = this->dhw_block && OPENTHERM_READ_dhw_block;
82
+ // NOLINTEND
28
83
 
29
84
  data.type = MessageType::READ_DATA;
30
85
  data.id = MessageId::STATUS;
31
- data.valueHB = ch_enabled | (dhw_enabled << 1) | (cooling_enabled << 2) | (otc_enabled << 3) | (ch2_enabled << 4);
86
+ data.valueHB = ch_enabled | (dhw_enabled << 1) | (cooling_enabled << 2) | (otc_enabled << 3) | (ch2_enabled << 4) |
87
+ (summer_mode_is_active << 5) | (dhw_blocked << 6);
88
+
89
+ return data;
90
+ }
91
+
92
+ // Another special case is OpenTherm version number which is configured at hub level as a constant
93
+ if (request_id == MessageId::OT_VERSION_CONTROLLER) {
94
+ data.type = MessageType::WRITE_DATA;
95
+ data.id = MessageId::OT_VERSION_CONTROLLER;
96
+ data.f88(this->opentherm_version_);
97
+
98
+ return data;
99
+ }
32
100
 
33
101
  // Disable incomplete switch statement warnings, because the cases in each
34
102
  // switch are generated based on the configured sensors and inputs.
35
103
  #pragma GCC diagnostic push
36
104
  #pragma GCC diagnostic ignored "-Wswitch"
37
105
 
38
- // TODO: This is a placeholder for an auto-generated switch statement which builds request structure based on
39
- // which sensors are enabled in config.
106
+ // Next, we start with the write requests from switches and other inputs,
107
+ // because we would want to write that data if it is available, rather than
108
+ // request a read for that type (in the case that both read and write are
109
+ // supported).
110
+ switch (request_id) {
111
+ OPENTHERM_SWITCH_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_WRITE_MESSAGE, OPENTHERM_MESSAGE_WRITE_ENTITY, ,
112
+ OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, )
113
+ OPENTHERM_NUMBER_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_WRITE_MESSAGE, OPENTHERM_MESSAGE_WRITE_ENTITY, ,
114
+ OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, )
115
+ OPENTHERM_OUTPUT_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_WRITE_MESSAGE, OPENTHERM_MESSAGE_WRITE_ENTITY, ,
116
+ OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, )
117
+ OPENTHERM_INPUT_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_WRITE_MESSAGE, OPENTHERM_MESSAGE_WRITE_ENTITY, ,
118
+ OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, )
119
+ }
40
120
 
121
+ // Finally, handle the simple read requests, which only change with the message id.
122
+ switch (request_id) { OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_READ_MESSAGE, OPENTHERM_IGNORE, , , ) }
123
+ switch (request_id) {
124
+ OPENTHERM_BINARY_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_READ_MESSAGE, OPENTHERM_IGNORE, , , )
125
+ }
41
126
  #pragma GCC diagnostic pop
42
127
 
43
- return data;
44
- }
45
- return OpenthermData();
128
+ // And if we get here, a message was requested which somehow wasn't handled.
129
+ // This shouldn't happen due to the way the defines are configured, so we
130
+ // log an error and just return a 0 message.
131
+ ESP_LOGE(TAG, "Tried to create a request with unknown id %d. This should never happen, so please open an issue.",
132
+ request_id);
133
+ return {};
46
134
  }
47
135
 
48
- OpenthermHub::OpenthermHub() : Component() {}
136
+ OpenthermHub::OpenthermHub() : Component(), in_pin_{}, out_pin_{} {}
49
137
 
50
138
  void OpenthermHub::process_response(OpenthermData &data) {
51
139
  ESP_LOGD(TAG, "Received OpenTherm response with id %d (%s)", data.id,
52
140
  this->opentherm_->message_id_to_str((MessageId) data.id));
53
141
  ESP_LOGD(TAG, "%s", this->opentherm_->debug_data(data).c_str());
142
+
143
+ switch (data.id) {
144
+ OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_RESPONSE_MESSAGE, OPENTHERM_MESSAGE_RESPONSE_ENTITY, ,
145
+ OPENTHERM_MESSAGE_RESPONSE_POSTSCRIPT, )
146
+ }
147
+ switch (data.id) {
148
+ OPENTHERM_BINARY_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_RESPONSE_MESSAGE, OPENTHERM_MESSAGE_RESPONSE_ENTITY, ,
149
+ OPENTHERM_MESSAGE_RESPONSE_POSTSCRIPT, )
150
+ }
54
151
  }
55
152
 
56
153
  void OpenthermHub::setup() {
@@ -67,6 +164,13 @@ void OpenthermHub::setup() {
67
164
  // good practice anyway.
68
165
  this->add_repeating_message(MessageId::STATUS);
69
166
 
167
+ // Also ensure that we start communication with the STATUS message
168
+ this->initial_messages_.insert(this->initial_messages_.begin(), MessageId::STATUS);
169
+
170
+ if (this->opentherm_version_ > 0.0f) {
171
+ this->initial_messages_.insert(this->initial_messages_.begin(), MessageId::OT_VERSION_CONTROLLER);
172
+ }
173
+
70
174
  this->current_message_iterator_ = this->initial_messages_.begin();
71
175
  }
72
176
 
@@ -254,22 +358,24 @@ void OpenthermHub::handle_timeout_error_() {
254
358
  this->stop_opentherm_();
255
359
  }
256
360
 
257
- #define ID(x) x
258
- #define SHOW2(x) #x
259
- #define SHOW(x) SHOW2(x)
260
-
261
361
  void OpenthermHub::dump_config() {
262
362
  ESP_LOGCONFIG(TAG, "OpenTherm:");
263
363
  LOG_PIN(" In: ", this->in_pin_);
264
364
  LOG_PIN(" Out: ", this->out_pin_);
265
365
  ESP_LOGCONFIG(TAG, " Sync mode: %d", this->sync_mode_);
366
+ ESP_LOGCONFIG(TAG, " Sensors: %s", SHOW(OPENTHERM_SENSOR_LIST(ID, )));
367
+ ESP_LOGCONFIG(TAG, " Binary sensors: %s", SHOW(OPENTHERM_BINARY_SENSOR_LIST(ID, )));
368
+ ESP_LOGCONFIG(TAG, " Switches: %s", SHOW(OPENTHERM_SWITCH_LIST(ID, )));
369
+ ESP_LOGCONFIG(TAG, " Input sensors: %s", SHOW(OPENTHERM_INPUT_SENSOR_LIST(ID, )));
370
+ ESP_LOGCONFIG(TAG, " Outputs: %s", SHOW(OPENTHERM_OUTPUT_LIST(ID, )));
371
+ ESP_LOGCONFIG(TAG, " Numbers: %s", SHOW(OPENTHERM_NUMBER_LIST(ID, )));
266
372
  ESP_LOGCONFIG(TAG, " Initial requests:");
267
373
  for (auto type : this->initial_messages_) {
268
- ESP_LOGCONFIG(TAG, " - %d", type);
374
+ ESP_LOGCONFIG(TAG, " - %d (%s)", type, this->opentherm_->message_id_to_str((type)));
269
375
  }
270
376
  ESP_LOGCONFIG(TAG, " Repeating requests:");
271
377
  for (auto type : this->repeating_messages_) {
272
- ESP_LOGCONFIG(TAG, " - %d", type);
378
+ ESP_LOGCONFIG(TAG, " - %d (%s)", type, this->opentherm_->message_id_to_str((type)));
273
379
  }
274
380
  }
275
381