ESPHome  2024.5.1
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 - 1;
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  if (this->buffer_[this->pos_] == 0x00) { // end of message
41  this->pos_ += 1;
42  } else if (is_list) { // list
43  this->pos_ += 1;
44  node->nodes.reserve(parse_length);
45  for (size_t i = 0; i != parse_length; i++) {
46  SmlNode child_node = SmlNode();
47  if (!this->setup_node(&child_node))
48  return false;
49  node->nodes.emplace_back(child_node);
50  }
51  } else { // value
52  node->value_bytes =
53  bytes(this->buffer_.begin() + this->pos_ + 1, this->buffer_.begin() + this->pos_ + parse_length);
54  this->pos_ += parse_length;
55  }
56  return true;
57 }
58 
59 std::vector<ObisInfo> SmlFile::get_obis_info() {
60  std::vector<ObisInfo> obis_info;
61  for (auto const &message : messages) {
62  SmlNode message_body = message.nodes[3];
63  uint16_t message_type = bytes_to_uint(message_body.nodes[0].value_bytes);
64  if (message_type != SML_GET_LIST_RES)
65  continue;
66 
67  SmlNode get_list_response = message_body.nodes[1];
68  bytes server_id = get_list_response.nodes[1].value_bytes;
69  SmlNode val_list = get_list_response.nodes[4];
70 
71  for (auto const &val_list_entry : val_list.nodes) {
72  obis_info.emplace_back(server_id, val_list_entry);
73  }
74  }
75  return obis_info;
76 }
77 
78 std::string bytes_repr(const bytes &buffer) {
79  std::string repr;
80  for (auto const value : buffer) {
81  repr += str_sprintf("%02x", value & 0xff);
82  }
83  return repr;
84 }
85 
86 uint64_t bytes_to_uint(const bytes &buffer) {
87  uint64_t val = 0;
88  for (auto const value : buffer) {
89  val = (val << 8) + value;
90  }
91  return val;
92 }
93 
94 int64_t bytes_to_int(const bytes &buffer) {
95  uint64_t tmp = bytes_to_uint(buffer);
96  int64_t val;
97 
98  // sign extension for abbreviations of leading ones (e.g. 3 byte transmissions, see 6.2.2 of SML protocol definition)
99  // see https://stackoverflow.com/questions/42534749/signed-extension-from-24-bit-to-32-bit-in-c
100  if (buffer.size() < 8) {
101  const int bits = buffer.size() * 8;
102  const uint64_t m = 1u << (bits - 1);
103  tmp = (tmp ^ m) - m;
104  }
105 
106  val = (int64_t) tmp;
107  return val;
108 }
109 
110 std::string bytes_to_string(const bytes &buffer) { return std::string(buffer.begin(), buffer.end()); }
111 
112 ObisInfo::ObisInfo(bytes server_id, SmlNode val_list_entry) : server_id(std::move(server_id)) {
113  this->code = val_list_entry.nodes[0].value_bytes;
114  this->status = val_list_entry.nodes[1].value_bytes;
115  this->unit = bytes_to_uint(val_list_entry.nodes[3].value_bytes);
116  this->scaler = bytes_to_int(val_list_entry.nodes[4].value_bytes);
117  SmlNode value_node = val_list_entry.nodes[5];
118  this->value = value_node.value_bytes;
119  this->value_type = value_node.type;
120 }
121 
122 std::string ObisInfo::code_repr() const {
123  return str_sprintf("%d-%d:%d.%d.%d", this->code[0], this->code[1], this->code[2], this->code[3], this->code[4]);
124 }
125 
126 } // namespace sml
127 } // namespace esphome
std::string bytes_repr(const bytes &buffer)
Definition: sml_parser.cpp:78
SmlFile(bytes buffer)
Definition: sml_parser.cpp:8
STL namespace.
int64_t bytes_to_int(const bytes &buffer)
Definition: sml_parser.cpp:94
mopeka_std_values val[4]
uint64_t bytes_to_uint(const bytes &buffer)
Definition: sml_parser.cpp:86
std::string bytes_to_string(const bytes &buffer)
Definition: sml_parser.cpp:110
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:112
std::vector< ObisInfo > get_obis_info()
Definition: sml_parser.cpp:59
uint16_t length
Definition: tt21100.cpp:12
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
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:122
std::vector< SmlNode > messages
Definition: sml_parser.h:38
std::vector< SmlNode > nodes
Definition: sml_parser.h:18