esphome 2024.9.1__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.
- esphome/components/aic3204/__init__.py +0 -0
- esphome/components/aic3204/aic3204.cpp +173 -0
- esphome/components/aic3204/aic3204.h +88 -0
- esphome/components/aic3204/audio_dac.py +52 -0
- esphome/components/aic3204/automation.h +23 -0
- esphome/components/alarm_control_panel/__init__.py +3 -4
- esphome/components/animation/__init__.py +16 -12
- esphome/components/api/api_connection.cpp +2 -0
- esphome/components/api/api_connection.h +3 -1
- esphome/components/api/api_frame_helper.cpp +2 -1
- esphome/components/api/api_frame_helper.h +2 -1
- esphome/components/api/api_server.cpp +2 -0
- esphome/components/api/api_server.h +3 -1
- esphome/components/api/custom_api_device.h +3 -2
- esphome/components/api/homeassistant_service.h +4 -3
- esphome/components/api/list_entities.cpp +2 -0
- esphome/components/api/list_entities.h +3 -2
- esphome/components/api/subscribe_state.cpp +2 -0
- esphome/components/api/subscribe_state.h +3 -2
- esphome/components/audio_dac/__init__.py +57 -0
- esphome/components/audio_dac/audio_dac.h +23 -0
- esphome/components/audio_dac/automation.h +43 -0
- esphome/components/bang_bang/bang_bang_climate.cpp +5 -2
- esphome/components/bedjet/bedjet_codec.cpp +4 -2
- esphome/components/binary_sensor/__init__.py +3 -4
- esphome/components/bl0906/sensor.py +3 -2
- esphome/components/button/__init__.py +3 -4
- esphome/components/ch422g/__init__.py +26 -17
- esphome/components/ch422g/ch422g.cpp +66 -49
- esphome/components/ch422g/ch422g.h +17 -19
- esphome/components/climate/__init__.py +3 -4
- esphome/components/cover/__init__.py +4 -5
- esphome/components/cse7766/cse7766.cpp +12 -1
- esphome/components/cse7766/cse7766.h +4 -0
- esphome/components/cse7766/sensor.py +13 -1
- esphome/components/cst816/touchscreen/__init__.py +7 -4
- esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +20 -19
- esphome/components/cst816/touchscreen/cst816_touchscreen.h +2 -0
- esphome/components/datetime/__init__.py +21 -14
- esphome/components/datetime/datetime_base.h +8 -1
- esphome/components/datetime/datetime_entity.cpp +2 -0
- esphome/components/datetime/datetime_entity.h +2 -0
- esphome/components/datetime/time_entity.cpp +2 -0
- esphome/components/datetime/time_entity.h +2 -0
- esphome/components/esp32/__init__.py +20 -4
- esphome/components/esp32_improv/__init__.py +82 -1
- esphome/components/esp32_improv/automation.h +72 -0
- esphome/components/esp32_improv/esp32_improv_component.cpp +13 -5
- esphome/components/esp32_improv/esp32_improv_component.h +15 -0
- esphome/components/ethernet/__init__.py +5 -0
- esphome/components/ethernet/ethernet_component.cpp +13 -0
- esphome/components/ethernet/ethernet_component.h +1 -0
- esphome/components/event/__init__.py +20 -12
- esphome/components/fan/__init__.py +3 -4
- esphome/components/gp2y1010au0f/__init__.py +0 -0
- esphome/components/gp2y1010au0f/gp2y1010au0f.cpp +67 -0
- esphome/components/gp2y1010au0f/gp2y1010au0f.h +52 -0
- esphome/components/gp2y1010au0f/sensor.py +61 -0
- esphome/components/gpio_expander/__init__.py +0 -0
- esphome/components/gpio_expander/cached_gpio.h +38 -0
- esphome/components/grove_gas_mc_v2/__init__.py +0 -0
- esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +88 -0
- esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +39 -0
- esphome/components/grove_gas_mc_v2/sensor.py +77 -0
- esphome/components/haier/climate.py +4 -3
- esphome/components/haier/haier_base.cpp +63 -8
- esphome/components/haier/haier_base.h +29 -3
- esphome/components/haier/hon_climate.cpp +122 -65
- esphome/components/haier/hon_climate.h +18 -2
- esphome/components/haier/smartair2_climate.cpp +21 -21
- esphome/components/haier/switch/__init__.py +91 -0
- esphome/components/haier/switch/beeper.cpp +14 -0
- esphome/components/haier/switch/beeper.h +18 -0
- esphome/components/haier/switch/display.cpp +14 -0
- esphome/components/haier/switch/display.h +18 -0
- esphome/components/haier/switch/health_mode.cpp +14 -0
- esphome/components/haier/switch/health_mode.h +18 -0
- esphome/components/haier/switch/quiet_mode.cpp +14 -0
- esphome/components/haier/switch/quiet_mode.h +18 -0
- esphome/components/hmac_md5/hmac_md5.cpp +2 -0
- esphome/components/hmac_md5/hmac_md5.h +2 -1
- esphome/components/i2s_audio/speaker/__init__.py +19 -0
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +2 -0
- esphome/components/ili9xxx/ili9xxx_display.h +1 -0
- esphome/components/image/__init__.py +12 -12
- esphome/components/image/image.cpp +44 -0
- esphome/components/image/image.h +17 -2
- esphome/components/inkplate6/display.py +2 -0
- esphome/components/inkplate6/inkplate.h +30 -2
- esphome/components/light/__init__.py +3 -4
- esphome/components/lock/__init__.py +3 -4
- esphome/components/lvgl/__init__.py +16 -5
- esphome/components/lvgl/defines.py +1 -0
- esphome/components/lvgl/hello_world.py +64 -0
- esphome/components/lvgl/lv_validation.py +159 -3
- esphome/components/lvgl/lvgl_esphome.cpp +0 -43
- esphome/components/lvgl/lvgl_esphome.h +0 -4
- esphome/components/lvgl/styles.py +3 -2
- esphome/components/lvgl/text/__init__.py +3 -3
- esphome/components/lvgl/widgets/__init__.py +2 -0
- esphome/components/lvgl/widgets/animimg.py +3 -4
- esphome/components/lvgl/widgets/dropdown.py +5 -1
- esphome/components/lvgl/widgets/meter.py +16 -11
- esphome/components/md5/__init__.py +6 -0
- esphome/components/md5/md5.cpp +2 -0
- esphome/components/md5/md5.h +2 -0
- esphome/components/micro_wake_word/__init__.py +7 -0
- esphome/components/mics_4514/sensor.py +11 -26
- esphome/components/modbus_controller/__init__.py +7 -5
- esphome/components/modbus_controller/binary_sensor/__init__.py +6 -6
- esphome/components/modbus_controller/number/__init__.py +5 -6
- esphome/components/modbus_controller/output/__init__.py +10 -14
- esphome/components/modbus_controller/select/__init__.py +1 -1
- esphome/components/modbus_controller/sensor/__init__.py +7 -7
- esphome/components/modbus_controller/switch/__init__.py +6 -7
- esphome/components/modbus_controller/text_sensor/__init__.py +8 -9
- esphome/components/mqtt/__init__.py +3 -0
- esphome/components/mqtt/mqtt_client.cpp +2 -0
- esphome/components/mqtt/mqtt_client.h +2 -0
- esphome/components/nau7802/__init__.py +0 -0
- esphome/components/nau7802/nau7802.cpp +323 -0
- esphome/components/nau7802/nau7802.h +121 -0
- esphome/components/nau7802/sensor.py +134 -0
- esphome/components/nextion/base_component.py +1 -0
- esphome/components/nextion/display.py +4 -0
- esphome/components/nextion/nextion.cpp +19 -4
- esphome/components/nextion/nextion.h +16 -0
- esphome/components/npi19/__init__.py +0 -0
- esphome/components/npi19/npi19.cpp +111 -0
- esphome/components/npi19/npi19.h +30 -0
- esphome/components/npi19/sensor.py +52 -0
- esphome/components/number/__init__.py +3 -5
- esphome/components/online_image/__init__.py +1 -1
- esphome/components/online_image/online_image.h +1 -2
- esphome/components/opentherm/__init__.py +57 -0
- esphome/components/opentherm/hub.cpp +277 -0
- esphome/components/opentherm/hub.h +110 -0
- esphome/components/opentherm/opentherm.cpp +568 -0
- esphome/components/opentherm/opentherm.h +347 -0
- esphome/components/pulse_counter/pulse_counter_sensor.cpp +8 -1
- esphome/components/pulse_counter/pulse_counter_sensor.h +1 -0
- esphome/components/radon_eye_ble/radon_eye_listener.cpp +10 -3
- esphome/components/remote_transmitter/__init__.py +18 -2
- esphome/components/remote_transmitter/remote_transmitter.h +6 -0
- esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +2 -0
- esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
- esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
- esphome/components/rp2040/__init__.py +13 -14
- esphome/components/select/__init__.py +3 -4
- esphome/components/sensor/__init__.py +3 -4
- esphome/components/shelly_dimmer/shelly_dimmer.cpp +32 -32
- esphome/components/shelly_dimmer/shelly_dimmer.h +2 -0
- esphome/components/st7701s/st7701s.cpp +21 -8
- esphome/components/st7701s/st7701s.h +2 -0
- esphome/components/switch/__init__.py +3 -4
- esphome/components/tca9555/__init__.py +72 -0
- esphome/components/tca9555/tca9555.cpp +140 -0
- esphome/components/tca9555/tca9555.h +64 -0
- esphome/components/tcs34725/tcs34725.cpp +62 -64
- esphome/components/tem3200/__init__.py +0 -0
- esphome/components/tem3200/sensor.py +55 -0
- esphome/components/tem3200/tem3200.cpp +151 -0
- esphome/components/tem3200/tem3200.h +30 -0
- esphome/components/template/binary_sensor/__init__.py +19 -6
- esphome/components/text/__init__.py +3 -4
- esphome/components/text_sensor/__init__.py +3 -4
- esphome/components/thermostat/climate.py +11 -9
- esphome/components/thermostat/thermostat_climate.cpp +21 -15
- esphome/components/tm1638/binary_sensor/__init__.py +3 -2
- esphome/components/tm1638/display.py +5 -5
- esphome/components/tm1638/output/__init__.py +3 -2
- esphome/components/tm1638/switch/__init__.py +3 -2
- esphome/components/touchscreen/touchscreen.cpp +2 -2
- esphome/components/update/__init__.py +3 -4
- esphome/components/valve/__init__.py +3 -4
- esphome/components/web_server/__init__.py +78 -22
- esphome/components/web_server/server_index_v3.h +3989 -3979
- esphome/components/web_server/web_server.cpp +219 -34
- esphome/components/web_server/web_server.h +10 -1
- esphome/components/wifi/wifi_component_esp_idf.cpp +4 -5
- esphome/config_validation.py +1 -0
- esphome/const.py +12 -2
- esphome/core/defines.h +4 -2
- esphome/core/helpers.cpp +46 -10
- esphome/core/helpers.h +8 -0
- esphome/core/ring_buffer.cpp +12 -2
- esphome/core/ring_buffer.h +3 -0
- esphome/voluptuous_schema.py +3 -1
- esphome/wizard.py +0 -3
- {esphome-2024.9.1.dist-info → esphome-2024.10.0.dist-info}/METADATA +5 -3
- {esphome-2024.9.1.dist-info → esphome-2024.10.0.dist-info}/RECORD +196 -147
- {esphome-2024.9.1.dist-info → esphome-2024.10.0.dist-info}/LICENSE +0 -0
- {esphome-2024.9.1.dist-info → esphome-2024.10.0.dist-info}/WHEEL +0 -0
- {esphome-2024.9.1.dist-info → esphome-2024.10.0.dist-info}/entry_points.txt +0 -0
- {esphome-2024.9.1.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
|