ESPHome  2024.7.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; // fill byte detected -> no more messages
14 
15  SmlNode message = SmlNode();
16  if (!this->setup_node(&message))
17  break;
18  this->messages.emplace_back(message);
19  }
20 }
21 
23  uint8_t type = this->buffer_[this->pos_] >> 4; // type including overlength info
24  uint8_t length = this->buffer_[this->pos_] & 0x0f; // length including TL bytes
25  bool is_list = (type & 0x07) == SML_LIST;
26  bool has_extended_length = type & 0x08; // we have a long list/value (>15 entries)
27  uint8_t parse_length = length;
28  if (has_extended_length) {
29  length = (length << 4) + (this->buffer_[this->pos_ + 1] & 0x0f);
30  parse_length = length;
31  this->pos_ += 1;
32  }
33 
34  if (this->pos_ + parse_length >= this->buffer_.size())
35  return false;
36 
37  node->type = type & 0x07;
38  node->nodes.clear();
39  node->value_bytes.clear();
40 
41  // if the list is a has_extended_length list with e.g. 16 elements this is a 0x00 byte but not the end of message
42  if (!has_extended_length && this->buffer_[this->pos_] == 0x00) { // end of message
43  this->pos_ += 1;
44  } else if (is_list) { // list
45  this->pos_ += 1;
46  node->nodes.reserve(parse_length);
47  for (size_t i = 0; i != parse_length; i++) {
48  SmlNode child_node = SmlNode();
49  if (!this->setup_node(&child_node))
50  return false;
51  node->nodes.emplace_back(child_node);
52  }
53  } else { // value
54  node->value_bytes =
55  bytes(this->buffer_.begin() + this->pos_ + 1, this->buffer_.begin() + this->pos_ + parse_length);
56  this->pos_ += parse_length;
57  }
58  return true;
59 }
60 
61 std::vector<ObisInfo> SmlFile::get_obis_info() {
62  std::vector<ObisInfo> obis_info;
63  for (auto const &message : messages) {
64  SmlNode message_body = message.nodes[3];
65  uint16_t message_type = bytes_to_uint(message_body.nodes[0].value_bytes);
66  if (message_type != SML_GET_LIST_RES)
67  continue;
68 
69  SmlNode get_list_response = message_body.nodes[1];
70  bytes server_id = get_list_response.nodes[1].value_bytes;
71  SmlNode val_list = get_list_response.nodes[4];
72 
73  for (auto const &val_list_entry : val_list.nodes) {
74  obis_info.emplace_back(server_id, val_list_entry);
75  }
76  }
77  return obis_info;
78 }
79 
80 std::string bytes_repr(const bytes &buffer) {
81  std::string repr;
82  for (auto const value : buffer) {
83  repr += str_sprintf("%02x", value & 0xff);
84  }
85  return repr;
86 }
87 
88 uint64_t bytes_to_uint(const bytes &buffer) {
89  uint64_t val = 0;
90  for (auto const value : buffer) {
91  val = (val << 8) + value;
92  }
93  return val;
94 }
95 
96 int64_t bytes_to_int(const bytes &buffer) {
97  uint64_t tmp = bytes_to_uint(buffer);
98  int64_t val;
99 
100  // sign extension for abbreviations of leading ones (e.g. 3 byte transmissions, see 6.2.2 of SML protocol definition)
101  // see https://stackoverflow.com/questions/42534749/signed-extension-from-24-bit-to-32-bit-in-c
102  if (buffer.size() < 8) {
103  const int bits = buffer.size() * 8;
104  const uint64_t m = 1u << (bits - 1);
105  tmp = (tmp ^ m) - m;
106  }
107 
108  val = (int64_t) tmp;
109  return val;
110 }
111 
112 std::string bytes_to_string(const bytes &buffer) { return std::string(buffer.begin(), buffer.end()); }
113 
114 ObisInfo::ObisInfo(bytes server_id, SmlNode val_list_entry) : server_id(std::move(server_id)) {
115  this->code = val_list_entry.nodes[0].value_bytes;
116  this->status = val_list_entry.nodes[1].value_bytes;
117  this->unit = bytes_to_uint(val_list_entry.nodes[3].value_bytes);
118  this->scaler = bytes_to_int(val_list_entry.nodes[4].value_bytes);
119  SmlNode value_node = val_list_entry.nodes[5];
120  this->value = value_node.value_bytes;
121  this->value_type = value_node.type;
122 }
123 
124 std::string ObisInfo::code_repr() const {
125  return str_sprintf("%d-%d:%d.%d.%d", this->code[0], this->code[1], this->code[2], this->code[3], this->code[4]);
126 }
127 
128 } // namespace sml
129 } // namespace esphome
std::string bytes_repr(const bytes &buffer)
Definition: sml_parser.cpp:80
SmlFile(bytes buffer)
Definition: sml_parser.cpp:8
STL namespace.
int64_t bytes_to_int(const bytes &buffer)
Definition: sml_parser.cpp:96
mopeka_std_values val[4]
uint64_t bytes_to_uint(const bytes &buffer)
Definition: sml_parser.cpp:88
std::string bytes_to_string(const bytes &buffer)
Definition: sml_parser.cpp:112
uint8_t m
Definition: bl0939.h:20
std::string str_sprintf(const char *fmt,...)
Definition: helpers.cpp:312
uint8_t type
ObisInfo(bytes server_id, SmlNode val_list_entry)
Definition: sml_parser.cpp:114
std::vector< ObisInfo > get_obis_info()
Definition: sml_parser.cpp:61
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
const bytes buffer_
Definition: sml_parser.h:42
std::string code_repr() const
Definition: sml_parser.cpp:124
std::vector< SmlNode > messages
Definition: sml_parser.h:38
std::vector< SmlNode > nodes
Definition: sml_parser.h:18