ESPHome  2024.12.2
sml_parser.cpp
Go to the documentation of this file.
1 #include "esphome/core/helpers.h"
2 #include "constants.h"
3 #include "sml_parser.h"
4 
5 namespace esphome {
6 namespace sml {
7 
8 SmlFile::SmlFile(bytes buffer) : buffer_(std::move(buffer)) {
9  // extract messages
10  this->pos_ = 0;
11  while (this->pos_ < this->buffer_.size()) {
12  if (this->buffer_[this->pos_] == 0x00)
13  break; // EndOfSmlMsg
14 
15  SmlNode message = SmlNode();
16  if (!this->setup_node(&message))
17  break;
18  this->messages.emplace_back(message);
19  }
20 }
21 
23  // If the TL field is 0x00, this is the end of the message
24  // (see 6.3.1 of SML protocol definition)
25  if (this->buffer_[this->pos_] == 0x00) {
26  // Increment past this byte and signal that the message is done
27  this->pos_ += 1;
28  return true;
29  }
30 
31  // Extract data from initial TL field
32  uint8_t type = (this->buffer_[this->pos_] >> 4) & 0x07; // type without overlength info
33  bool overlength = (this->buffer_[this->pos_] >> 4) & 0x08; // overlength information
34  uint8_t length = this->buffer_[this->pos_] & 0x0f; // length (including TL bytes)
35 
36  // Check if we need additional length bytes
37  if (overlength) {
38  // Shift the current length to the higher nibble
39  // and add the lower nibble of the next byte to the length
40  length = (length << 4) + (this->buffer_[this->pos_ + 1] & 0x0f);
41  // We are basically done with the first TL field now,
42  // so increment past that, we now point to the second TL field
43  this->pos_ += 1;
44  // Decrement the length for value fields (not lists),
45  // since the byte we just handled is counted as part of the field
46  // in case of values but not for lists
47  if (type != SML_LIST)
48  length -= 1;
49 
50  // Technically, this is not enough, the standard allows for more than two length fields.
51  // However I don't think it is very common to have more than 255 entries in a list
52  }
53 
54  // We are done with the last TL field(s), so advance the position
55  this->pos_ += 1;
56  // and decrement the length for non-list fields
57  if (type != SML_LIST)
58  length -= 1;
59 
60  // Check if the buffer length is long enough
61  if (this->pos_ + length > this->buffer_.size())
62  return false;
63 
64  node->type = type;
65  node->nodes.clear();
66  node->value_bytes.clear();
67 
68  if (type == SML_LIST) {
69  node->nodes.reserve(length);
70  for (size_t i = 0; i != length; i++) {
71  SmlNode child_node = SmlNode();
72  if (!this->setup_node(&child_node))
73  return false;
74  node->nodes.emplace_back(child_node);
75  }
76  } else {
77  // Value starts at the current position
78  // Value ends "length" bytes later,
79  // (since the TL field is counted but already subtracted from length)
80  node->value_bytes = bytes(this->buffer_.begin() + this->pos_, this->buffer_.begin() + this->pos_ + length);
81  // Increment the pointer past all consumed bytes
82  this->pos_ += length;
83  }
84  return true;
85 }
86 
87 std::vector<ObisInfo> SmlFile::get_obis_info() {
88  std::vector<ObisInfo> obis_info;
89  for (auto const &message : messages) {
90  SmlNode message_body = message.nodes[3];
91  uint16_t message_type = bytes_to_uint(message_body.nodes[0].value_bytes);
92  if (message_type != SML_GET_LIST_RES)
93  continue;
94 
95  SmlNode get_list_response = message_body.nodes[1];
96  bytes server_id = get_list_response.nodes[1].value_bytes;
97  SmlNode val_list = get_list_response.nodes[4];
98 
99  for (auto const &val_list_entry : val_list.nodes) {
100  obis_info.emplace_back(server_id, val_list_entry);
101  }
102  }
103  return obis_info;
104 }
105 
106 std::string bytes_repr(const bytes &buffer) {
107  std::string repr;
108  for (auto const value : buffer) {
109  repr += str_sprintf("%02x", value & 0xff);
110  }
111  return repr;
112 }
113 
114 uint64_t bytes_to_uint(const bytes &buffer) {
115  uint64_t val = 0;
116  for (auto const value : buffer) {
117  val = (val << 8) + value;
118  }
119  return val;
120 }
121 
122 int64_t bytes_to_int(const bytes &buffer) {
123  uint64_t tmp = bytes_to_uint(buffer);
124  int64_t val;
125 
126  // sign extension for abbreviations of leading ones (e.g. 3 byte transmissions, see 6.2.2 of SML protocol definition)
127  // see https://stackoverflow.com/questions/42534749/signed-extension-from-24-bit-to-32-bit-in-c
128  if (buffer.size() < 8) {
129  const int bits = buffer.size() * 8;
130  const uint64_t m = 1ull << (bits - 1);
131  tmp = (tmp ^ m) - m;
132  }
133 
134  val = (int64_t) tmp;
135  return val;
136 }
137 
138 std::string bytes_to_string(const bytes &buffer) { return std::string(buffer.begin(), buffer.end()); }
139 
140 ObisInfo::ObisInfo(bytes server_id, SmlNode val_list_entry) : server_id(std::move(server_id)) {
141  this->code = val_list_entry.nodes[0].value_bytes;
142  this->status = val_list_entry.nodes[1].value_bytes;
143  this->unit = bytes_to_uint(val_list_entry.nodes[3].value_bytes);
144  this->scaler = bytes_to_int(val_list_entry.nodes[4].value_bytes);
145  SmlNode value_node = val_list_entry.nodes[5];
146  this->value = value_node.value_bytes;
147  this->value_type = value_node.type;
148 }
149 
150 std::string ObisInfo::code_repr() const {
151  return str_sprintf("%d-%d:%d.%d.%d", this->code[0], this->code[1], this->code[2], this->code[3], this->code[4]);
152 }
153 
154 } // namespace sml
155 } // namespace esphome
std::string bytes_repr(const bytes &buffer)
Definition: sml_parser.cpp:106
SmlFile(bytes buffer)
Definition: sml_parser.cpp:8
STL namespace.
int64_t bytes_to_int(const bytes &buffer)
Definition: sml_parser.cpp:122
mopeka_std_values val[4]
uint64_t bytes_to_uint(const bytes &buffer)
Definition: sml_parser.cpp:114
std::string bytes_to_string(const bytes &buffer)
Definition: sml_parser.cpp:138
std::string str_sprintf(const char *fmt,...)
Definition: helpers.cpp:320
uint8_t type
ObisInfo(bytes server_id, SmlNode val_list_entry)
Definition: sml_parser.cpp:140
std::vector< ObisInfo > get_obis_info()
Definition: sml_parser.cpp:87
uint16_t length
Definition: tt21100.cpp:12
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
std::vector< uint8_t > bytes
Definition: sml_parser.h:12
bool setup_node(SmlNode *node)
Definition: sml_parser.cpp:22
uint8_t m
Definition: bl0906.h:208
const bytes buffer_
Definition: sml_parser.h:42
std::string code_repr() const
Definition: sml_parser.cpp:150
std::vector< SmlNode > messages
Definition: sml_parser.h:38
std::vector< SmlNode > nodes
Definition: sml_parser.h:18