esphome 2024.9.2__py3-none-any.whl → 2024.10.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. esphome/components/aic3204/__init__.py +0 -0
  2. esphome/components/aic3204/aic3204.cpp +173 -0
  3. esphome/components/aic3204/aic3204.h +88 -0
  4. esphome/components/aic3204/audio_dac.py +52 -0
  5. esphome/components/aic3204/automation.h +23 -0
  6. esphome/components/alarm_control_panel/__init__.py +3 -4
  7. esphome/components/animation/__init__.py +16 -12
  8. esphome/components/api/api_connection.cpp +2 -0
  9. esphome/components/api/api_connection.h +3 -1
  10. esphome/components/api/api_frame_helper.cpp +2 -1
  11. esphome/components/api/api_frame_helper.h +2 -1
  12. esphome/components/api/api_server.cpp +2 -0
  13. esphome/components/api/api_server.h +3 -1
  14. esphome/components/api/custom_api_device.h +3 -2
  15. esphome/components/api/homeassistant_service.h +4 -3
  16. esphome/components/api/list_entities.cpp +2 -0
  17. esphome/components/api/list_entities.h +3 -2
  18. esphome/components/api/subscribe_state.cpp +2 -0
  19. esphome/components/api/subscribe_state.h +3 -2
  20. esphome/components/audio_dac/__init__.py +57 -0
  21. esphome/components/audio_dac/audio_dac.h +23 -0
  22. esphome/components/audio_dac/automation.h +43 -0
  23. esphome/components/bang_bang/bang_bang_climate.cpp +5 -2
  24. esphome/components/bedjet/bedjet_codec.cpp +4 -2
  25. esphome/components/binary_sensor/__init__.py +3 -4
  26. esphome/components/button/__init__.py +3 -4
  27. esphome/components/ch422g/__init__.py +26 -17
  28. esphome/components/ch422g/ch422g.cpp +66 -49
  29. esphome/components/ch422g/ch422g.h +17 -19
  30. esphome/components/climate/__init__.py +3 -4
  31. esphome/components/cover/__init__.py +4 -5
  32. esphome/components/cse7766/cse7766.cpp +12 -1
  33. esphome/components/cse7766/cse7766.h +4 -0
  34. esphome/components/cse7766/sensor.py +13 -1
  35. esphome/components/cst816/touchscreen/__init__.py +7 -4
  36. esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +20 -19
  37. esphome/components/cst816/touchscreen/cst816_touchscreen.h +2 -0
  38. esphome/components/datetime/__init__.py +21 -14
  39. esphome/components/datetime/datetime_base.h +8 -1
  40. esphome/components/datetime/datetime_entity.cpp +2 -0
  41. esphome/components/datetime/datetime_entity.h +2 -0
  42. esphome/components/datetime/time_entity.cpp +2 -0
  43. esphome/components/datetime/time_entity.h +2 -0
  44. esphome/components/esp32/__init__.py +20 -4
  45. esphome/components/esp32_improv/__init__.py +82 -1
  46. esphome/components/esp32_improv/automation.h +72 -0
  47. esphome/components/esp32_improv/esp32_improv_component.cpp +13 -5
  48. esphome/components/esp32_improv/esp32_improv_component.h +15 -0
  49. esphome/components/ethernet/__init__.py +5 -0
  50. esphome/components/ethernet/ethernet_component.cpp +13 -0
  51. esphome/components/ethernet/ethernet_component.h +1 -0
  52. esphome/components/event/__init__.py +20 -12
  53. esphome/components/fan/__init__.py +3 -4
  54. esphome/components/gp2y1010au0f/__init__.py +0 -0
  55. esphome/components/gp2y1010au0f/gp2y1010au0f.cpp +67 -0
  56. esphome/components/gp2y1010au0f/gp2y1010au0f.h +52 -0
  57. esphome/components/gp2y1010au0f/sensor.py +61 -0
  58. esphome/components/gpio_expander/__init__.py +0 -0
  59. esphome/components/gpio_expander/cached_gpio.h +38 -0
  60. esphome/components/grove_gas_mc_v2/__init__.py +0 -0
  61. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +88 -0
  62. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +39 -0
  63. esphome/components/grove_gas_mc_v2/sensor.py +77 -0
  64. esphome/components/haier/climate.py +4 -3
  65. esphome/components/haier/haier_base.cpp +63 -8
  66. esphome/components/haier/haier_base.h +29 -3
  67. esphome/components/haier/hon_climate.cpp +122 -65
  68. esphome/components/haier/hon_climate.h +18 -2
  69. esphome/components/haier/smartair2_climate.cpp +21 -21
  70. esphome/components/haier/switch/__init__.py +91 -0
  71. esphome/components/haier/switch/beeper.cpp +14 -0
  72. esphome/components/haier/switch/beeper.h +18 -0
  73. esphome/components/haier/switch/display.cpp +14 -0
  74. esphome/components/haier/switch/display.h +18 -0
  75. esphome/components/haier/switch/health_mode.cpp +14 -0
  76. esphome/components/haier/switch/health_mode.h +18 -0
  77. esphome/components/haier/switch/quiet_mode.cpp +14 -0
  78. esphome/components/haier/switch/quiet_mode.h +18 -0
  79. esphome/components/hmac_md5/hmac_md5.cpp +2 -0
  80. esphome/components/hmac_md5/hmac_md5.h +2 -1
  81. esphome/components/i2s_audio/speaker/__init__.py +19 -0
  82. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  83. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +2 -0
  84. esphome/components/ili9xxx/ili9xxx_display.h +1 -0
  85. esphome/components/image/__init__.py +12 -12
  86. esphome/components/image/image.cpp +44 -0
  87. esphome/components/image/image.h +17 -2
  88. esphome/components/inkplate6/display.py +2 -0
  89. esphome/components/inkplate6/inkplate.h +30 -2
  90. esphome/components/light/__init__.py +3 -4
  91. esphome/components/lock/__init__.py +3 -4
  92. esphome/components/lvgl/__init__.py +16 -5
  93. esphome/components/lvgl/defines.py +1 -0
  94. esphome/components/lvgl/hello_world.py +64 -0
  95. esphome/components/lvgl/lv_validation.py +159 -3
  96. esphome/components/lvgl/lvgl_esphome.cpp +0 -43
  97. esphome/components/lvgl/lvgl_esphome.h +0 -4
  98. esphome/components/lvgl/styles.py +3 -2
  99. esphome/components/lvgl/text/__init__.py +3 -3
  100. esphome/components/lvgl/widgets/__init__.py +2 -0
  101. esphome/components/lvgl/widgets/animimg.py +3 -4
  102. esphome/components/lvgl/widgets/dropdown.py +5 -1
  103. esphome/components/lvgl/widgets/meter.py +16 -11
  104. esphome/components/md5/__init__.py +6 -0
  105. esphome/components/md5/md5.cpp +2 -0
  106. esphome/components/md5/md5.h +2 -0
  107. esphome/components/micro_wake_word/__init__.py +7 -0
  108. esphome/components/mics_4514/sensor.py +11 -26
  109. esphome/components/modbus_controller/__init__.py +7 -5
  110. esphome/components/modbus_controller/binary_sensor/__init__.py +6 -6
  111. esphome/components/modbus_controller/number/__init__.py +5 -6
  112. esphome/components/modbus_controller/output/__init__.py +10 -14
  113. esphome/components/modbus_controller/select/__init__.py +1 -1
  114. esphome/components/modbus_controller/sensor/__init__.py +7 -7
  115. esphome/components/modbus_controller/switch/__init__.py +6 -7
  116. esphome/components/modbus_controller/text_sensor/__init__.py +8 -9
  117. esphome/components/mqtt/__init__.py +3 -0
  118. esphome/components/mqtt/mqtt_client.cpp +2 -0
  119. esphome/components/mqtt/mqtt_client.h +2 -0
  120. esphome/components/nau7802/__init__.py +0 -0
  121. esphome/components/nau7802/nau7802.cpp +323 -0
  122. esphome/components/nau7802/nau7802.h +121 -0
  123. esphome/components/nau7802/sensor.py +134 -0
  124. esphome/components/nextion/base_component.py +1 -0
  125. esphome/components/nextion/display.py +4 -0
  126. esphome/components/nextion/nextion.cpp +19 -4
  127. esphome/components/nextion/nextion.h +16 -0
  128. esphome/components/npi19/__init__.py +0 -0
  129. esphome/components/npi19/npi19.cpp +111 -0
  130. esphome/components/npi19/npi19.h +30 -0
  131. esphome/components/npi19/sensor.py +52 -0
  132. esphome/components/number/__init__.py +3 -5
  133. esphome/components/online_image/__init__.py +1 -1
  134. esphome/components/online_image/online_image.h +1 -2
  135. esphome/components/opentherm/__init__.py +57 -0
  136. esphome/components/opentherm/hub.cpp +277 -0
  137. esphome/components/opentherm/hub.h +110 -0
  138. esphome/components/opentherm/opentherm.cpp +568 -0
  139. esphome/components/opentherm/opentherm.h +347 -0
  140. esphome/components/pulse_counter/pulse_counter_sensor.cpp +8 -1
  141. esphome/components/pulse_counter/pulse_counter_sensor.h +1 -0
  142. esphome/components/radon_eye_ble/radon_eye_listener.cpp +10 -3
  143. esphome/components/remote_transmitter/__init__.py +18 -2
  144. esphome/components/remote_transmitter/remote_transmitter.h +6 -0
  145. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +2 -0
  146. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
  147. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
  148. esphome/components/select/__init__.py +3 -4
  149. esphome/components/sensor/__init__.py +3 -4
  150. esphome/components/shelly_dimmer/shelly_dimmer.cpp +32 -32
  151. esphome/components/shelly_dimmer/shelly_dimmer.h +2 -0
  152. esphome/components/st7701s/st7701s.cpp +21 -8
  153. esphome/components/st7701s/st7701s.h +2 -0
  154. esphome/components/switch/__init__.py +3 -4
  155. esphome/components/tca9555/__init__.py +72 -0
  156. esphome/components/tca9555/tca9555.cpp +140 -0
  157. esphome/components/tca9555/tca9555.h +64 -0
  158. esphome/components/tcs34725/tcs34725.cpp +62 -64
  159. esphome/components/tem3200/__init__.py +0 -0
  160. esphome/components/tem3200/sensor.py +55 -0
  161. esphome/components/tem3200/tem3200.cpp +151 -0
  162. esphome/components/tem3200/tem3200.h +30 -0
  163. esphome/components/template/binary_sensor/__init__.py +19 -6
  164. esphome/components/text/__init__.py +3 -4
  165. esphome/components/text_sensor/__init__.py +3 -4
  166. esphome/components/thermostat/climate.py +11 -9
  167. esphome/components/thermostat/thermostat_climate.cpp +21 -15
  168. esphome/components/tm1638/binary_sensor/__init__.py +3 -2
  169. esphome/components/tm1638/display.py +5 -5
  170. esphome/components/tm1638/output/__init__.py +3 -2
  171. esphome/components/tm1638/switch/__init__.py +3 -2
  172. esphome/components/touchscreen/touchscreen.cpp +2 -2
  173. esphome/components/update/__init__.py +3 -4
  174. esphome/components/valve/__init__.py +3 -4
  175. esphome/components/web_server/__init__.py +78 -22
  176. esphome/components/web_server/server_index_v3.h +3989 -3979
  177. esphome/components/web_server/web_server.cpp +219 -34
  178. esphome/components/web_server/web_server.h +10 -1
  179. esphome/components/wifi/wifi_component_esp_idf.cpp +4 -5
  180. esphome/config_validation.py +1 -0
  181. esphome/const.py +12 -2
  182. esphome/core/defines.h +4 -2
  183. esphome/core/helpers.cpp +46 -10
  184. esphome/core/helpers.h +8 -0
  185. esphome/core/ring_buffer.cpp +12 -2
  186. esphome/core/ring_buffer.h +3 -0
  187. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/METADATA +5 -3
  188. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/RECORD +192 -143
  189. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/LICENSE +0 -0
  190. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/WHEEL +0 -0
  191. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/entry_points.txt +0 -0
  192. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,568 @@
1
+ /*
2
+ * OpenTherm protocol implementation. Originally taken from https://github.com/jpraus/arduino-opentherm, but
3
+ * heavily modified to comply with ESPHome coding standards and provide better logging.
4
+ * Original code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
5
+ * Public License, which is compatible with GPLv3 license, which covers C++ part of ESPHome project.
6
+ */
7
+
8
+ #include "opentherm.h"
9
+ #include "esphome/core/helpers.h"
10
+ #if defined(ESP32) || defined(USE_ESP_IDF)
11
+ #include "driver/timer.h"
12
+ #include "esp_err.h"
13
+ #endif
14
+ #ifdef ESP8266
15
+ #include "Arduino.h"
16
+ #endif
17
+ #include <string>
18
+ #include <sstream>
19
+ #include <bitset>
20
+
21
+ namespace esphome {
22
+ namespace opentherm {
23
+
24
+ using std::string;
25
+ using std::bitset;
26
+ using std::stringstream;
27
+ using std::to_string;
28
+
29
+ static const char *const TAG = "opentherm";
30
+
31
+ #ifdef ESP8266
32
+ OpenTherm *OpenTherm::instance_ = nullptr;
33
+ #endif
34
+
35
+ OpenTherm::OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout)
36
+ : in_pin_(in_pin),
37
+ out_pin_(out_pin),
38
+ #if defined(ESP32) || defined(USE_ESP_IDF)
39
+ timer_group_(TIMER_GROUP_0),
40
+ timer_idx_(TIMER_0),
41
+ #endif
42
+ mode_(OperationMode::IDLE),
43
+ error_type_(ProtocolErrorType::NO_ERROR),
44
+ capture_(0),
45
+ clock_(0),
46
+ data_(0),
47
+ bit_pos_(0),
48
+ timeout_counter_(-1),
49
+ device_timeout_(device_timeout) {
50
+ this->isr_in_pin_ = in_pin->to_isr();
51
+ this->isr_out_pin_ = out_pin->to_isr();
52
+ }
53
+
54
+ bool OpenTherm::initialize() {
55
+ #ifdef ESP8266
56
+ OpenTherm::instance_ = this;
57
+ #endif
58
+ this->in_pin_->pin_mode(gpio::FLAG_INPUT);
59
+ this->out_pin_->pin_mode(gpio::FLAG_OUTPUT);
60
+ this->out_pin_->digital_write(true);
61
+
62
+ #if defined(ESP32) || defined(USE_ESP_IDF)
63
+ return this->init_esp32_timer_();
64
+ #else
65
+ return true;
66
+ #endif
67
+ }
68
+
69
+ void OpenTherm::listen() {
70
+ this->stop_timer_();
71
+ this->timeout_counter_ = this->device_timeout_ * 5; // timer_ ticks at 5 ticks/ms
72
+
73
+ this->mode_ = OperationMode::LISTEN;
74
+ this->data_ = 0;
75
+ this->bit_pos_ = 0;
76
+
77
+ this->start_read_timer_();
78
+ }
79
+
80
+ void OpenTherm::send(OpenthermData &data) {
81
+ this->stop_timer_();
82
+ this->data_ = data.type;
83
+ this->data_ = (this->data_ << 12) | data.id;
84
+ this->data_ = (this->data_ << 8) | data.valueHB;
85
+ this->data_ = (this->data_ << 8) | data.valueLB;
86
+ if (!check_parity_(this->data_)) {
87
+ this->data_ = this->data_ | 0x80000000;
88
+ }
89
+
90
+ this->clock_ = 1; // clock starts at HIGH
91
+ this->bit_pos_ = 33; // count down (33 == start bit, 32-1 data, 0 == stop bit)
92
+ this->mode_ = OperationMode::WRITE;
93
+
94
+ this->start_write_timer_();
95
+ }
96
+
97
+ bool OpenTherm::get_message(OpenthermData &data) {
98
+ if (this->mode_ == OperationMode::RECEIVED) {
99
+ data.type = (this->data_ >> 28) & 0x7;
100
+ data.id = (this->data_ >> 16) & 0xFF;
101
+ data.valueHB = (this->data_ >> 8) & 0xFF;
102
+ data.valueLB = this->data_ & 0xFF;
103
+ return true;
104
+ }
105
+ return false;
106
+ }
107
+
108
+ bool OpenTherm::get_protocol_error(OpenThermError &error) {
109
+ if (this->mode_ != OperationMode::ERROR_PROTOCOL) {
110
+ return false;
111
+ }
112
+
113
+ error.error_type = this->error_type_;
114
+ error.bit_pos = this->bit_pos_;
115
+ error.capture = this->capture_;
116
+ error.clock = this->clock_;
117
+ error.data = this->data_;
118
+
119
+ return true;
120
+ }
121
+
122
+ void OpenTherm::stop() {
123
+ this->stop_timer_();
124
+ this->mode_ = OperationMode::IDLE;
125
+ }
126
+
127
+ void IRAM_ATTR OpenTherm::read_() {
128
+ this->data_ = 0;
129
+ this->bit_pos_ = 0;
130
+ this->mode_ = OperationMode::READ;
131
+ this->capture_ = 1; // reset counter and add as if read start bit
132
+ this->clock_ = 1; // clock is high at the start of comm
133
+ this->start_read_timer_(); // get us into 1/4 of manchester code. 5 timer ticks constitute 1 ms, which is 1 bit
134
+ // period in OpenTherm.
135
+ }
136
+
137
+ bool IRAM_ATTR OpenTherm::timer_isr(OpenTherm *arg) {
138
+ if (arg->mode_ == OperationMode::LISTEN) {
139
+ if (arg->timeout_counter_ == 0) {
140
+ arg->mode_ = OperationMode::ERROR_TIMEOUT;
141
+ arg->stop_timer_();
142
+ return false;
143
+ }
144
+ bool const value = arg->isr_in_pin_.digital_read();
145
+ if (value) { // incoming data (rising signal)
146
+ arg->read_();
147
+ }
148
+ if (arg->timeout_counter_ > 0) {
149
+ arg->timeout_counter_--;
150
+ }
151
+ } else if (arg->mode_ == OperationMode::READ) {
152
+ bool const value = arg->isr_in_pin_.digital_read();
153
+ uint8_t const last = (arg->capture_ & 1);
154
+ if (value != last) {
155
+ // transition of signal from last sampling
156
+ if (arg->clock_ == 1 && arg->capture_ > 0xF) {
157
+ // no transition in the middle of the bit
158
+ arg->mode_ = OperationMode::ERROR_PROTOCOL;
159
+ arg->error_type_ = ProtocolErrorType::NO_TRANSITION;
160
+ arg->stop_timer_();
161
+ return false;
162
+ } else if (arg->clock_ == 1 || arg->capture_ > 0xF) {
163
+ // transition in the middle of the bit OR no transition between two bit, both are valid data points
164
+ if (arg->bit_pos_ == BitPositions::STOP_BIT) {
165
+ // expecting stop bit
166
+ auto stop_bit_error = arg->verify_stop_bit_(last);
167
+ if (stop_bit_error == ProtocolErrorType::NO_ERROR) {
168
+ arg->mode_ = OperationMode::RECEIVED;
169
+ arg->stop_timer_();
170
+ return false;
171
+ } else {
172
+ // end of data not verified, invalid data
173
+ arg->mode_ = OperationMode::ERROR_PROTOCOL;
174
+ arg->error_type_ = stop_bit_error;
175
+ arg->stop_timer_();
176
+ return false;
177
+ }
178
+ } else {
179
+ // normal data point at clock high
180
+ arg->bit_read_(last);
181
+ arg->clock_ = 0;
182
+ }
183
+ } else {
184
+ // clock low, not a data point, switch clock
185
+ arg->clock_ = 1;
186
+ }
187
+ arg->capture_ = 1; // reset counter
188
+ } else if (arg->capture_ > 0xFF) {
189
+ // no change for too long, invalid mancheter encoding
190
+ arg->mode_ = OperationMode::ERROR_PROTOCOL;
191
+ arg->error_type_ = ProtocolErrorType::NO_CHANGE_TOO_LONG;
192
+ arg->stop_timer_();
193
+ return false;
194
+ }
195
+ arg->capture_ = (arg->capture_ << 1) | value;
196
+ } else if (arg->mode_ == OperationMode::WRITE) {
197
+ // write data to pin
198
+ if (arg->bit_pos_ == 33 || arg->bit_pos_ == 0) { // start bit
199
+ arg->write_bit_(1, arg->clock_);
200
+ } else { // data bits
201
+ arg->write_bit_(read_bit(arg->data_, arg->bit_pos_ - 1), arg->clock_);
202
+ }
203
+ if (arg->clock_ == 0) {
204
+ if (arg->bit_pos_ <= 0) { // check termination
205
+ arg->mode_ = OperationMode::SENT; // all data written
206
+ arg->stop_timer_();
207
+ }
208
+ arg->bit_pos_--;
209
+ arg->clock_ = 1;
210
+ } else {
211
+ arg->clock_ = 0;
212
+ }
213
+ }
214
+
215
+ return false;
216
+ }
217
+
218
+ #ifdef ESP8266
219
+ void IRAM_ATTR OpenTherm::esp8266_timer_isr() { OpenTherm::timer_isr(OpenTherm::instance_); }
220
+ #endif
221
+
222
+ void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) {
223
+ this->data_ = (this->data_ << 1) | value;
224
+ this->bit_pos_++;
225
+ }
226
+
227
+ ProtocolErrorType OpenTherm::verify_stop_bit_(uint8_t value) {
228
+ if (value) { // stop bit detected
229
+ return check_parity_(this->data_) ? ProtocolErrorType::NO_ERROR : ProtocolErrorType::PARITY_ERROR;
230
+ } else { // no stop bit detected, error
231
+ return ProtocolErrorType::INVALID_STOP_BIT;
232
+ }
233
+ }
234
+
235
+ void IRAM_ATTR OpenTherm::write_bit_(uint8_t high, uint8_t clock) {
236
+ if (clock == 1) { // left part of manchester encoding
237
+ this->isr_out_pin_.digital_write(!high); // low means logical 1 to protocol
238
+ } else { // right part of manchester encoding
239
+ this->isr_out_pin_.digital_write(high); // high means logical 0 to protocol
240
+ }
241
+ }
242
+
243
+ #if defined(ESP32) || defined(USE_ESP_IDF)
244
+
245
+ bool OpenTherm::init_esp32_timer_() {
246
+ // Search for a free timer. Maybe unstable, we'll see.
247
+ int cur_timer = 0;
248
+ timer_group_t timer_group = TIMER_GROUP_0;
249
+ timer_idx_t timer_idx = TIMER_0;
250
+ bool timer_found = false;
251
+
252
+ for (; cur_timer < SOC_TIMER_GROUP_TOTAL_TIMERS; cur_timer++) {
253
+ timer_config_t temp_config;
254
+ timer_group = cur_timer < 2 ? TIMER_GROUP_0 : TIMER_GROUP_1;
255
+ timer_idx = cur_timer < 2 ? (timer_idx_t) cur_timer : (timer_idx_t) (cur_timer - 2);
256
+
257
+ auto err = timer_get_config(timer_group, timer_idx, &temp_config);
258
+ if (err == ESP_ERR_INVALID_ARG) {
259
+ // Error means timer was not initialized (or other things, but we are careful with our args)
260
+ timer_found = true;
261
+ break;
262
+ }
263
+
264
+ ESP_LOGD(TAG, "Timer %d:%d seems to be occupied, will try another", timer_group, timer_idx);
265
+ }
266
+
267
+ if (!timer_found) {
268
+ ESP_LOGE(TAG, "No free timer was found! OpenTherm cannot function without a timer.");
269
+ return false;
270
+ }
271
+
272
+ ESP_LOGD(TAG, "Found free timer %d:%d", timer_group, timer_idx);
273
+ this->timer_group_ = timer_group;
274
+ this->timer_idx_ = timer_idx;
275
+
276
+ timer_config_t const config = {
277
+ .alarm_en = TIMER_ALARM_EN,
278
+ .counter_en = TIMER_PAUSE,
279
+ .intr_type = TIMER_INTR_LEVEL,
280
+ .counter_dir = TIMER_COUNT_UP,
281
+ .auto_reload = TIMER_AUTORELOAD_EN,
282
+ #if ESP_IDF_VERSION_MAJOR >= 5
283
+ .clk_src = TIMER_SRC_CLK_DEFAULT,
284
+ #endif
285
+ .divider = 80,
286
+ };
287
+
288
+ esp_err_t result;
289
+
290
+ result = timer_init(this->timer_group_, this->timer_idx_, &config);
291
+ if (result != ESP_OK) {
292
+ const auto *error = esp_err_to_name(result);
293
+ ESP_LOGE(TAG, "Failed to init timer. Error: %s", error);
294
+ return false;
295
+ }
296
+
297
+ result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
298
+ if (result != ESP_OK) {
299
+ const auto *error = esp_err_to_name(result);
300
+ ESP_LOGE(TAG, "Failed to set counter value. Error: %s", error);
301
+ return false;
302
+ }
303
+
304
+ result = timer_isr_callback_add(this->timer_group_, this->timer_idx_, reinterpret_cast<bool (*)(void *)>(timer_isr),
305
+ this, 0);
306
+ if (result != ESP_OK) {
307
+ const auto *error = esp_err_to_name(result);
308
+ ESP_LOGE(TAG, "Failed to register timer interrupt. Error: %s", error);
309
+ return false;
310
+ }
311
+
312
+ return true;
313
+ }
314
+
315
+ void IRAM_ATTR OpenTherm::start_esp32_timer_(uint64_t alarm_value) {
316
+ esp_err_t result;
317
+
318
+ result = timer_set_alarm_value(this->timer_group_, this->timer_idx_, alarm_value);
319
+ if (result != ESP_OK) {
320
+ const auto *error = esp_err_to_name(result);
321
+ ESP_LOGE(TAG, "Failed to set alarm value. Error: %s", error);
322
+ return;
323
+ }
324
+
325
+ result = timer_start(this->timer_group_, this->timer_idx_);
326
+ if (result != ESP_OK) {
327
+ const auto *error = esp_err_to_name(result);
328
+ ESP_LOGE(TAG, "Failed to start the timer. Error: %s", error);
329
+ return;
330
+ }
331
+ }
332
+
333
+ // 5 kHz timer_
334
+ void IRAM_ATTR OpenTherm::start_read_timer_() {
335
+ InterruptLock const lock;
336
+ this->start_esp32_timer_(200);
337
+ }
338
+
339
+ // 2 kHz timer_
340
+ void IRAM_ATTR OpenTherm::start_write_timer_() {
341
+ InterruptLock const lock;
342
+ this->start_esp32_timer_(500);
343
+ }
344
+
345
+ void IRAM_ATTR OpenTherm::stop_timer_() {
346
+ InterruptLock const lock;
347
+
348
+ esp_err_t result;
349
+
350
+ result = timer_pause(this->timer_group_, this->timer_idx_);
351
+ if (result != ESP_OK) {
352
+ const auto *error = esp_err_to_name(result);
353
+ ESP_LOGE(TAG, "Failed to pause the timer. Error: %s", error);
354
+ return;
355
+ }
356
+
357
+ result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
358
+ if (result != ESP_OK) {
359
+ const auto *error = esp_err_to_name(result);
360
+ ESP_LOGE(TAG, "Failed to set timer counter to 0 after pausing. Error: %s", error);
361
+ return;
362
+ }
363
+ }
364
+
365
+ #endif // END ESP32
366
+
367
+ #ifdef ESP8266
368
+ // 5 kHz timer_
369
+ void OpenTherm::start_read_timer_() {
370
+ InterruptLock const lock;
371
+ timer1_attachInterrupt(OpenTherm::esp8266_timer_isr);
372
+ timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max)
373
+ timer1_write(1000); // 5kHz
374
+ }
375
+
376
+ // 2 kHz timer_
377
+ void OpenTherm::start_write_timer_() {
378
+ InterruptLock const lock;
379
+ timer1_attachInterrupt(OpenTherm::esp8266_timer_isr);
380
+ timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max)
381
+ timer1_write(2500); // 2kHz
382
+ }
383
+
384
+ void OpenTherm::stop_timer_() {
385
+ InterruptLock const lock;
386
+ timer1_disable();
387
+ timer1_detachInterrupt();
388
+ }
389
+
390
+ #endif // END ESP8266
391
+
392
+ // https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd
393
+ bool OpenTherm::check_parity_(uint32_t val) {
394
+ val ^= val >> 16;
395
+ val ^= val >> 8;
396
+ val ^= val >> 4;
397
+ val ^= val >> 2;
398
+ val ^= val >> 1;
399
+ return (~val) & 1;
400
+ }
401
+
402
+ #define TO_STRING_MEMBER(name) \
403
+ case name: \
404
+ return #name;
405
+
406
+ const char *OpenTherm::operation_mode_to_str(OperationMode mode) {
407
+ switch (mode) {
408
+ TO_STRING_MEMBER(IDLE)
409
+ TO_STRING_MEMBER(LISTEN)
410
+ TO_STRING_MEMBER(READ)
411
+ TO_STRING_MEMBER(RECEIVED)
412
+ TO_STRING_MEMBER(WRITE)
413
+ TO_STRING_MEMBER(SENT)
414
+ TO_STRING_MEMBER(ERROR_PROTOCOL)
415
+ TO_STRING_MEMBER(ERROR_TIMEOUT)
416
+ default:
417
+ return "<INVALID>";
418
+ }
419
+ }
420
+ const char *OpenTherm::protocol_error_to_to_str(ProtocolErrorType error_type) {
421
+ switch (error_type) {
422
+ TO_STRING_MEMBER(NO_ERROR)
423
+ TO_STRING_MEMBER(NO_TRANSITION)
424
+ TO_STRING_MEMBER(INVALID_STOP_BIT)
425
+ TO_STRING_MEMBER(PARITY_ERROR)
426
+ TO_STRING_MEMBER(NO_CHANGE_TOO_LONG)
427
+ default:
428
+ return "<INVALID>";
429
+ }
430
+ }
431
+ const char *OpenTherm::message_type_to_str(MessageType message_type) {
432
+ switch (message_type) {
433
+ TO_STRING_MEMBER(READ_DATA)
434
+ TO_STRING_MEMBER(READ_ACK)
435
+ TO_STRING_MEMBER(WRITE_DATA)
436
+ TO_STRING_MEMBER(WRITE_ACK)
437
+ TO_STRING_MEMBER(INVALID_DATA)
438
+ TO_STRING_MEMBER(DATA_INVALID)
439
+ TO_STRING_MEMBER(UNKNOWN_DATAID)
440
+ default:
441
+ return "<INVALID>";
442
+ }
443
+ }
444
+
445
+ const char *OpenTherm::message_id_to_str(MessageId id) {
446
+ switch (id) {
447
+ TO_STRING_MEMBER(STATUS)
448
+ TO_STRING_MEMBER(CH_SETPOINT)
449
+ TO_STRING_MEMBER(CONTROLLER_CONFIG)
450
+ TO_STRING_MEMBER(DEVICE_CONFIG)
451
+ TO_STRING_MEMBER(COMMAND_CODE)
452
+ TO_STRING_MEMBER(FAULT_FLAGS)
453
+ TO_STRING_MEMBER(REMOTE)
454
+ TO_STRING_MEMBER(COOLING_CONTROL)
455
+ TO_STRING_MEMBER(CH2_SETPOINT)
456
+ TO_STRING_MEMBER(CH_SETPOINT_OVERRIDE)
457
+ TO_STRING_MEMBER(TSP_COUNT)
458
+ TO_STRING_MEMBER(TSP_COMMAND)
459
+ TO_STRING_MEMBER(FHB_SIZE)
460
+ TO_STRING_MEMBER(FHB_COMMAND)
461
+ TO_STRING_MEMBER(MAX_MODULATION_LEVEL)
462
+ TO_STRING_MEMBER(MAX_BOILER_CAPACITY)
463
+ TO_STRING_MEMBER(ROOM_SETPOINT)
464
+ TO_STRING_MEMBER(MODULATION_LEVEL)
465
+ TO_STRING_MEMBER(CH_WATER_PRESSURE)
466
+ TO_STRING_MEMBER(DHW_FLOW_RATE)
467
+ TO_STRING_MEMBER(DAY_TIME)
468
+ TO_STRING_MEMBER(DATE)
469
+ TO_STRING_MEMBER(YEAR)
470
+ TO_STRING_MEMBER(ROOM_SETPOINT_CH2)
471
+ TO_STRING_MEMBER(ROOM_TEMP)
472
+ TO_STRING_MEMBER(FEED_TEMP)
473
+ TO_STRING_MEMBER(DHW_TEMP)
474
+ TO_STRING_MEMBER(OUTSIDE_TEMP)
475
+ TO_STRING_MEMBER(RETURN_WATER_TEMP)
476
+ TO_STRING_MEMBER(SOLAR_STORE_TEMP)
477
+ TO_STRING_MEMBER(SOLAR_COLLECT_TEMP)
478
+ TO_STRING_MEMBER(FEED_TEMP_CH2)
479
+ TO_STRING_MEMBER(DHW2_TEMP)
480
+ TO_STRING_MEMBER(EXHAUST_TEMP)
481
+ TO_STRING_MEMBER(FAN_SPEED)
482
+ TO_STRING_MEMBER(FLAME_CURRENT)
483
+ TO_STRING_MEMBER(DHW_BOUNDS)
484
+ TO_STRING_MEMBER(CH_BOUNDS)
485
+ TO_STRING_MEMBER(OTC_CURVE_BOUNDS)
486
+ TO_STRING_MEMBER(DHW_SETPOINT)
487
+ TO_STRING_MEMBER(MAX_CH_SETPOINT)
488
+ TO_STRING_MEMBER(OTC_CURVE_RATIO)
489
+ TO_STRING_MEMBER(HVAC_STATUS)
490
+ TO_STRING_MEMBER(REL_VENT_SETPOINT)
491
+ TO_STRING_MEMBER(DEVICE_VENT)
492
+ TO_STRING_MEMBER(REL_VENTILATION)
493
+ TO_STRING_MEMBER(REL_HUMID_EXHAUST)
494
+ TO_STRING_MEMBER(SUPPLY_INLET_TEMP)
495
+ TO_STRING_MEMBER(SUPPLY_OUTLET_TEMP)
496
+ TO_STRING_MEMBER(EXHAUST_INLET_TEMP)
497
+ TO_STRING_MEMBER(EXHAUST_OUTLET_TEMP)
498
+ TO_STRING_MEMBER(NOM_REL_VENTILATION)
499
+ TO_STRING_MEMBER(OVERRIDE_FUNC)
500
+ TO_STRING_MEMBER(OEM_DIAGNOSTIC)
501
+ TO_STRING_MEMBER(BURNER_STARTS)
502
+ TO_STRING_MEMBER(CH_PUMP_STARTS)
503
+ TO_STRING_MEMBER(DHW_PUMP_STARTS)
504
+ TO_STRING_MEMBER(DHW_BURNER_STARTS)
505
+ TO_STRING_MEMBER(BURNER_HOURS)
506
+ TO_STRING_MEMBER(CH_PUMP_HOURS)
507
+ TO_STRING_MEMBER(DHW_PUMP_HOURS)
508
+ TO_STRING_MEMBER(DHW_BURNER_HOURS)
509
+ TO_STRING_MEMBER(OT_VERSION_CONTROLLER)
510
+ TO_STRING_MEMBER(OT_VERSION_DEVICE)
511
+ TO_STRING_MEMBER(VERSION_CONTROLLER)
512
+ TO_STRING_MEMBER(VERSION_DEVICE)
513
+ default:
514
+ return "<INVALID>";
515
+ }
516
+ }
517
+
518
+ string OpenTherm::debug_data(OpenthermData &data) {
519
+ stringstream result;
520
+ result << bitset<8>(data.type) << " " << bitset<8>(data.id) << " " << bitset<8>(data.valueHB) << " "
521
+ << bitset<8>(data.valueLB) << "\n";
522
+ result << "type: " << this->message_type_to_str((MessageType) data.type) << "; ";
523
+ result << "id: " << to_string(data.id) << "; ";
524
+ result << "HB: " << to_string(data.valueHB) << "; ";
525
+ result << "LB: " << to_string(data.valueLB) << "; ";
526
+ result << "uint_16: " << to_string(data.u16()) << "; ";
527
+ result << "float: " << to_string(data.f88());
528
+
529
+ return result.str();
530
+ }
531
+ std::string OpenTherm::debug_error(OpenThermError &error) {
532
+ stringstream result;
533
+ result << "type: " << this->protocol_error_to_to_str(error.error_type) << "; ";
534
+ result << "data: ";
535
+ result << format_hex(error.data);
536
+ result << "; clock: " << to_string(clock_);
537
+ result << "; capture: " << bitset<32>(error.capture);
538
+ result << "; bit_pos: " << to_string(error.bit_pos);
539
+
540
+ return result.str();
541
+ }
542
+
543
+ float OpenthermData::f88() { return ((float) this->s16()) / 256.0; }
544
+
545
+ void OpenthermData::f88(float value) { this->s16((int16_t) (value * 256)); }
546
+
547
+ uint16_t OpenthermData::u16() {
548
+ uint16_t const value = this->valueHB;
549
+ return (value << 8) | this->valueLB;
550
+ }
551
+
552
+ void OpenthermData::u16(uint16_t value) {
553
+ this->valueLB = value & 0xFF;
554
+ this->valueHB = (value >> 8) & 0xFF;
555
+ }
556
+
557
+ int16_t OpenthermData::s16() {
558
+ int16_t const value = this->valueHB;
559
+ return (value << 8) | this->valueLB;
560
+ }
561
+
562
+ void OpenthermData::s16(int16_t value) {
563
+ this->valueLB = value & 0xFF;
564
+ this->valueHB = (value >> 8) & 0xFF;
565
+ }
566
+
567
+ } // namespace opentherm
568
+ } // namespace esphome