From 5624fafb3a55662172a629bb09ea8a3ac105e8e5 Mon Sep 17 00:00:00 2001 From: 0hax <43876620+0hax@users.noreply.github.com> Date: Mon, 27 Sep 2021 22:32:08 +0200 Subject: [PATCH] Fix handling of timestamps in Teleinfo component. (#2392) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * teleinfo: avoid a buffer overflow. When reading tag or values, data is written to the buffer even if the size if bigger than the buffer. Add a new 'max_len' argument to get_field() to avoid this error. Signed-off-by: 0hax <0hax@protonmail.com> * teleinfo: read extra timestamp field for some tags. Some tags has an extra timestamp field that need to be read before the actual data. The code is inspired by Jpsy work: https://github.com/Jpsy/esphome/commit/29339c14f96ed7cf7a68911ca7d9bd5eb94955d6 Signed-off-by: 0hax <0hax@protonmail.com> * teleinfo: increase MAX_BUF_SIZE to suffice for 3-phase Linky in Standard mode. * teleinfo: handle DATE tag correctly. The DATE tag is special due its format and need to be handled separately. Fix from DrCoolzic. Signed-off-by: 0hax <0hax@protonmail.com> Co-authored-by: Jörg Wagner --- esphome/components/teleinfo/teleinfo.cpp | 37 +++++++++++++++++++++--- esphome/components/teleinfo/teleinfo.h | 4 ++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/esphome/components/teleinfo/teleinfo.cpp b/esphome/components/teleinfo/teleinfo.cpp index 8240615cc5..badd66ae83 100644 --- a/esphome/components/teleinfo/teleinfo.cpp +++ b/esphome/components/teleinfo/teleinfo.cpp @@ -7,7 +7,7 @@ namespace teleinfo { static const char *const TAG = "teleinfo"; /* Helpers */ -static int get_field(char *dest, char *buf_start, char *buf_end, int sep) { +static int get_field(char *dest, char *buf_start, char *buf_end, int sep, int max_len) { char *field_end; int len; @@ -15,6 +15,8 @@ static int get_field(char *dest, char *buf_start, char *buf_end, int sep) { if (!field_end) return 0; len = field_end - buf_start; + if (len >= max_len) + return len; strncpy(dest, buf_start, len); dest[len] = '\0'; @@ -106,9 +108,22 @@ void TeleInfo::loop() { * 0xa | Tag | 0x9 | Data | 0x9 | CRC | 0xd * ^^^^^^^^^^^^^^^^^^^^^^^^^ * Checksum is computed on the above in standard mode. + * + * Note that some Tags may have a timestamp in Standard mode. In this case + * the group would looks like this: + * 0xa | Tag | 0x9 | Timestamp | 0x9 | Data | 0x9 | CRC | 0xd + * + * The DATE tag is a special case. The group looks like this + * 0xa | Tag | 0x9 | Timestamp | 0x9 | 0x9 | CRC | 0xd + * */ while ((buf_finger = static_cast(memchr(buf_finger, (int) 0xa, buf_index_ - 1))) && ((buf_finger - buf_) < buf_index_)) { + /* + * Make sure timesamp is nullified between each tag as some tags don't + * have a timestamp + */ + timestamp_[0] = '\0'; /* Point to the first char of the group after 0xa */ buf_finger += 1; @@ -123,7 +138,7 @@ void TeleInfo::loop() { continue; /* Get tag */ - field_len = get_field(tag_, buf_finger, grp_end, separator_); + field_len = get_field(tag_, buf_finger, grp_end, separator_, MAX_TAG_SIZE); if (!field_len || field_len >= MAX_TAG_SIZE) { ESP_LOGE(TAG, "Invalid tag."); break; @@ -132,8 +147,22 @@ void TeleInfo::loop() { /* Advance buf_finger to after the tag and the separator. */ buf_finger += field_len + 1; - /* Get value (after next separator) */ - field_len = get_field(val_, buf_finger, grp_end, separator_); + /* + * If there is two separators and the tag is not equal to "DATE", + * it means there is a timestamp to read first. + */ + if (std::count(buf_finger, grp_end, separator_) == 2 && strcmp(tag_, "DATE") != 0) { + field_len = get_field(timestamp_, buf_finger, grp_end, separator_, MAX_TIMESTAMP_SIZE); + if (!field_len || field_len >= MAX_TIMESTAMP_SIZE) { + ESP_LOGE(TAG, "Invalid Timestamp"); + break; + } + + /* Advance buf_finger to after the first data and the separator. */ + buf_finger += field_len + 1; + } + + field_len = get_field(val_, buf_finger, grp_end, separator_, MAX_VAL_SIZE); if (!field_len || field_len >= MAX_VAL_SIZE) { ESP_LOGE(TAG, "Invalid Value"); break; diff --git a/esphome/components/teleinfo/teleinfo.h b/esphome/components/teleinfo/teleinfo.h index f10024691e..2be34cfb78 100644 --- a/esphome/components/teleinfo/teleinfo.h +++ b/esphome/components/teleinfo/teleinfo.h @@ -11,7 +11,8 @@ namespace teleinfo { */ static const uint8_t MAX_TAG_SIZE = 64; static const uint16_t MAX_VAL_SIZE = 256; -static const uint16_t MAX_BUF_SIZE = 1024; +static const uint16_t MAX_BUF_SIZE = 2048; +static const uint16_t MAX_TIMESTAMP_SIZE = 14; class TeleInfoListener { public: @@ -36,6 +37,7 @@ class TeleInfo : public PollingComponent, public uart::UARTDevice { uint32_t buf_index_{0}; char tag_[MAX_TAG_SIZE]; char val_[MAX_VAL_SIZE]; + char timestamp_[MAX_TIMESTAMP_SIZE]; enum State { OFF, ON,