ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
dlms_meter.cpp
Go to the documentation of this file.
1// Ignore Zephyr. It doesn't have any encryption library.
2#if defined(USE_ESP32) || defined(USE_ARDUINO) || defined(USE_HOST)
3
4#include "dlms_meter.h"
6#include "esphome/core/log.h"
7
8#include <cinttypes>
9#include <cstdio>
10
12
13ESPHOME_LOG_TAG(TAG, "dlms_meter");
14static void log_callback(dlms_parser::LogLevel level, const char *fmt, va_list args) {
15 std::array<char, 256> buf;
16 vsnprintf(buf.data(), buf.size(), fmt, args);
17 switch (level) {
18 case dlms_parser::LogLevel::ERROR:
19 ESP_LOGE(TAG, "%s", buf.data());
20 break;
21 case dlms_parser::LogLevel::WARNING:
22 ESP_LOGW(TAG, "%s", buf.data());
23 break;
24 case dlms_parser::LogLevel::INFO:
25 ESP_LOGI(TAG, "%s", buf.data());
26 break;
27 case dlms_parser::LogLevel::VERBOSE:
28 ESP_LOGV(TAG, "%s", buf.data());
29 break;
30 case dlms_parser::LogLevel::VERY_VERBOSE:
31 ESP_LOGVV(TAG, "%s", buf.data());
32 break;
33 case dlms_parser::LogLevel::DEBUG:
34 ESP_LOGD(TAG, "%s", buf.data());
35 break;
36 }
37}
38
39DlmsMeterComponent::DlmsMeterComponent(const uint32_t receive_timeout_ms, const bool skip_crc_check,
40 const char *decryption_key, const char *authentication_key,
41 std::vector<CustomPattern> custom_patterns)
42 : receive_timeout_ms_(receive_timeout_ms),
43 skip_crc_check_(skip_crc_check),
44 custom_patterns_(std::move(custom_patterns)),
45 parser_([this](const dlms_parser::AxdrCapture &capture) { this->on_data_(capture); }, &this->decryptor_) {
46 dlms_parser::Logger::set_log_function(log_callback);
47
48 if (decryption_key != nullptr) {
49 const auto key = dlms_parser::Aes128GcmDecryptionKey::from_hex(decryption_key);
50 if (key.has_value()) {
51 this->parser_.set_decryption_key(*key);
52 } else {
53 ESP_LOGE(TAG, "Failed to set decryption key: invalid key format");
54 }
55 }
56
57 if (authentication_key != nullptr) {
58 const auto key = dlms_parser::Aes128GcmAuthenticationKey::from_hex(authentication_key);
59 if (key.has_value()) {
60 this->parser_.set_authentication_key(*key);
61 } else {
62 ESP_LOGE(TAG, "Failed to set authentication key: invalid key format");
63 }
64 }
65
66 this->parser_.set_skip_crc_check(this->skip_crc_check_);
67
68 this->parser_.load_default_patterns();
69
70 for (const auto &custom_pattern : this->custom_patterns_) {
71 this->parser_.register_pattern(custom_pattern.name, custom_pattern.pattern, custom_pattern.priority,
72 custom_pattern.default_obis);
73 }
74}
75
77
79 ESP_LOGCONFIG(TAG, "DLMS Meter:");
80 ESP_LOGCONFIG(TAG, " Receive Timeout: %" PRIu32 " ms", this->receive_timeout_ms_);
81 ESP_LOGCONFIG(TAG, " Skip CRC Check: %s", YESNO(this->skip_crc_check_));
82
83 std::array<char, 24> obis_buf;
84
85 for (const auto &custom_pattern : this->custom_patterns_) {
86 const auto default_obis = custom_pattern.default_obis.to_string(obis_buf);
87 ESP_LOGCONFIG(TAG, " Custom Pattern: '%s' (name: %s, priority: %d, default_obis: %.*s)", custom_pattern.pattern,
88 custom_pattern.name, custom_pattern.priority, static_cast<int>(default_obis.size()),
89 default_obis.data());
90 }
91
92#ifdef USE_SENSOR
93 for (const auto &item : this->sensors_) {
94 LOG_SENSOR(" ", "Numeric Sensor (OBIS)", item.sensor);
95 const auto obis = item.obis.to_string(obis_buf);
96 ESP_LOGCONFIG(TAG, " OBIS: %.*s", static_cast<int>(obis.size()), obis.data());
97 }
98#endif
99
100#ifdef USE_TEXT_SENSOR
101 for (const auto &item : this->text_sensors_) {
102 LOG_TEXT_SENSOR(" ", "Text Sensor (OBIS)", item.sensor);
103 const auto obis = item.obis.to_string(obis_buf);
104 ESP_LOGCONFIG(TAG, " OBIS: %.*s", static_cast<int>(obis.size()), obis.data());
105 }
106#endif
107
108#ifdef USE_BINARY_SENSOR
109 for (const auto &item : this->binary_sensors_) {
110 LOG_BINARY_SENSOR(" ", "Binary Sensor (OBIS)", item.sensor);
111 const auto obis = item.obis.to_string(obis_buf);
112 ESP_LOGCONFIG(TAG, " OBIS: %.*s", static_cast<int>(obis.size()), obis.data());
113 }
114#endif
115}
116
118 this->read_rx_buffer_();
119 if (this->bytes_accumulated_ > 0 &&
120 App.get_loop_component_start_time() - this->last_rx_char_time_ > this->receive_timeout_ms_) {
121 this->process_frame_();
122 }
123}
124
126 while (this->available()) {
127 this->read();
128 }
129}
130
132 int available = this->available();
133 if (available == 0)
134 return;
135
136 if (this->bytes_accumulated_ + available > this->rx_buffer_.size()) {
137 ESP_LOGW(TAG, "RX Buffer overflow. Frame too large! Dropping frame.");
138 this->bytes_accumulated_ = 0;
139
140 this->flush_rx_buffer_();
141 return;
142 }
143
144 bool success = this->read_array(this->rx_buffer_.data() + this->bytes_accumulated_, available);
145 if (!success) {
146 ESP_LOGW(TAG, "UART read failed. Dropping frame.");
147 this->bytes_accumulated_ = 0;
148 this->flush_rx_buffer_();
149 return;
150 }
151
153
155}
156
158 ESP_LOGV(TAG, "Processing frame of size: %zu bytes", this->bytes_accumulated_);
159
160 this->parser_.parse({this->rx_buffer_.data(), this->bytes_accumulated_});
161
162 this->bytes_accumulated_ = 0;
163}
164
165void DlmsMeterComponent::on_data_(const dlms_parser::AxdrCapture &capture) {
166 int updated_count = 0;
167
168 if (capture.is_numeric()) {
169#if defined(USE_SENSOR) || defined(USE_BINARY_SENSOR)
170 const float value = capture.value_as_float_with_scaler_applied();
171#endif
172#ifdef USE_SENSOR
173 for (const auto &item : this->sensors_) {
174 if (item.obis == capture.obis) {
175 item.sensor->publish_state(value);
176 updated_count++;
177 }
178 }
179#endif
180#ifdef USE_BINARY_SENSOR
181 for (const auto &item : this->binary_sensors_) {
182 if (item.obis == capture.obis) {
183 item.sensor->publish_state(value != 0.0f);
184 updated_count++;
185 }
186 }
187#endif
188 } else {
189#ifdef USE_TEXT_SENSOR
190 for (const auto &item : this->text_sensors_) {
191 if (item.obis == capture.obis) {
192 std::array<char, 128> value_buf;
193 const auto value = capture.value_as_string(value_buf);
194 item.sensor->publish_state(value.data(), value.size());
195 updated_count++;
196 }
197 }
198#endif
199 }
200
201 if (updated_count == 0) {
202 std::array<char, 24> obis_buf;
203 const auto obis = capture.obis.to_string(obis_buf);
204 ESP_LOGV(TAG, "Received OBIS %.*s, but no sensors are registered for it.", static_cast<int>(obis.size()),
205 obis.data());
206 }
207}
208
209#ifdef USE_SENSOR
210void DlmsMeterComponent::register_sensor(const dlms_parser::ObisId &obis, sensor::Sensor *sensor) {
211 this->sensors_.push_back({obis, sensor});
212}
213#endif
214
215#ifdef USE_TEXT_SENSOR
216void DlmsMeterComponent::register_text_sensor(const dlms_parser::ObisId &obis, text_sensor::TextSensor *sensor) {
217 this->text_sensors_.push_back({obis, sensor});
218}
219#endif
220
221#ifdef USE_BINARY_SENSOR
222void DlmsMeterComponent::register_binary_sensor(const dlms_parser::ObisId &obis, binary_sensor::BinarySensor *sensor) {
223 this->binary_sensors_.push_back({obis, sensor});
224}
225#endif
226
227} // namespace esphome::dlms_meter
228
229#endif
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
Base class for all binary_sensor-type classes.
DlmsMeterComponent(uint32_t receive_timeout_ms, bool skip_crc_check, const char *decryption_key, const char *authentication_key, std::vector< CustomPattern > custom_patterns)
StaticVector< SensorItem, DLMS_MAX_SENSORS > sensors_
Definition dlms_meter.h:124
void on_data_(const dlms_parser::AxdrCapture &capture)
std::array< uint8_t, 2048 > rx_buffer_
Definition dlms_meter.h:111
void register_text_sensor(const dlms_parser::ObisId &obis, text_sensor::TextSensor *sensor)
StaticVector< TextSensorItem, DLMS_MAX_TEXT_SENSORS > text_sensors_
Definition dlms_meter.h:127
void register_sensor(const dlms_parser::ObisId &obis, sensor::Sensor *sensor)
StaticVector< BinarySensorItem, DLMS_MAX_BINARY_SENSORS > binary_sensors_
Definition dlms_meter.h:130
void register_binary_sensor(const dlms_parser::ObisId &obis, binary_sensor::BinarySensor *sensor)
Base-class for all sensors.
Definition sensor.h:47
optional< std::array< uint8_t, N > > read_array()
Definition uart.h:40
ESPHOME_LOG_TAG(TAG, "dlms_meter")
size_t size_t const char * fmt
Definition helpers.h:1180
Application App
Global storage of Application pointer - only one Application can exist.
STL namespace.
static void uint32_t