ESPHome 2026.3.3
Loading...
Searching...
No Matches
sml_parser.cpp
Go to the documentation of this file.
2#include "constants.h"
3#include "sml_parser.h"
4
5namespace esphome {
6namespace sml {
7
8SmlFile::SmlFile(const BytesView &buffer) : buffer_(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;
16 if (!this->setup_node(&message))
17 break;
18 this->messages.emplace_back(std::move(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 if (this->pos_ + 1 >= this->buffer_.size())
39 return false;
40 // Shift the current length to the higher nibble
41 // and add the lower nibble of the next byte to the length
42 length = (length << 4) + (this->buffer_[this->pos_ + 1] & 0x0f);
43 // We are basically done with the first TL field now,
44 // so increment past that, we now point to the second TL field
45 this->pos_ += 1;
46 // Decrement the length for value fields (not lists),
47 // since the byte we just handled is counted as part of the field
48 // in case of values but not for lists
49 if (type != SML_LIST)
50 length -= 1;
51
52 // Technically, this is not enough, the standard allows for more than two length fields.
53 // However I don't think it is very common to have more than 255 entries in a list
54 }
55
56 // We are done with the last TL field(s), so advance the position
57 this->pos_ += 1;
58 // and decrement the length for non-list fields
59 if (type != SML_LIST)
60 length -= 1;
61
62 // Check if the buffer length is long enough
63 if (this->pos_ + length > this->buffer_.size())
64 return false;
65
66 node->type = type;
67
68 if (type == SML_LIST) {
69 node->nodes.reserve(length);
70 for (size_t i = 0; i != length; i++) {
71 SmlNode child_node;
72 if (!this->setup_node(&child_node))
73 return false;
74 node->nodes.emplace_back(std::move(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 = buffer_.subview(this->pos_, length);
81 // Increment the pointer past all consumed bytes
82 this->pos_ += length;
83 }
84 return true;
85}
86
87std::vector<ObisInfo> SmlFile::get_obis_info() {
88 std::vector<ObisInfo> obis_info;
89 for (auto const &message : messages) {
90 const auto &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 const auto &get_list_response = message_body.nodes[1];
96 const auto &server_id = get_list_response.nodes[1].value_bytes;
97 const auto &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
106std::string bytes_repr(const BytesView &buffer) {
107 std::string repr;
108 for (auto const value : buffer) {
109 // max 3: 2 hex digits + null
110 char hex_buf[3];
111 snprintf(hex_buf, sizeof(hex_buf), "%02x", static_cast<unsigned int>(value));
112 repr += hex_buf;
113 }
114 return repr;
115}
116
117uint64_t bytes_to_uint(const BytesView &buffer) {
118 uint64_t val = 0;
119 for (auto const value : buffer) {
120 val = (val << 8) + value;
121 }
122 return val;
123}
124
125int64_t bytes_to_int(const BytesView &buffer) {
126 uint64_t tmp = bytes_to_uint(buffer);
127 int64_t val;
128
129 // sign extension for abbreviations of leading ones (e.g. 3 byte transmissions, see 6.2.2 of SML protocol definition)
130 // see https://stackoverflow.com/questions/42534749/signed-extension-from-24-bit-to-32-bit-in-c
131 if (buffer.size() < 8) {
132 const int bits = buffer.size() * 8;
133 const uint64_t m = 1ull << (bits - 1);
134 tmp = (tmp ^ m) - m;
135 }
136
137 val = (int64_t) tmp;
138 return val;
139}
140
141std::string bytes_to_string(const BytesView &buffer) { return std::string(buffer.begin(), buffer.end()); }
142
143ObisInfo::ObisInfo(const BytesView &server_id, const SmlNode &val_list_entry) : server_id(server_id) {
144 this->code = val_list_entry.nodes[0].value_bytes;
145 this->status = val_list_entry.nodes[1].value_bytes;
146 this->unit = bytes_to_uint(val_list_entry.nodes[3].value_bytes);
147 this->scaler = bytes_to_int(val_list_entry.nodes[4].value_bytes);
148 const auto &value_node = val_list_entry.nodes[5];
149 this->value = value_node.value_bytes;
150 this->value_type = value_node.type;
151}
152
153std::string ObisInfo::code_repr() const {
154 // max 20: "255-255:255.255.255" (19 chars) + null
155 char buf[20];
156 snprintf(buf, sizeof(buf), "%d-%d:%d.%d.%d", this->code[0], this->code[1], this->code[2], this->code[3],
157 this->code[4]);
158 return buf;
159}
160
161} // namespace sml
162} // namespace esphome
uint8_t m
Definition bl0906.h:1
const uint8_t * begin() const noexcept
Definition sml_parser.h:35
const uint8_t * end() const noexcept
Definition sml_parser.h:37
BytesView subview(size_t offset, size_t count) const noexcept
Definition sml_parser.h:30
size_t size() const noexcept
Definition sml_parser.h:23
std::string code_repr() const
ObisInfo(const BytesView &server_id, const SmlNode &val_list_entry)
SmlFile(const BytesView &buffer)
Definition sml_parser.cpp:8
const BytesView buffer_
Definition sml_parser.h:72
std::vector< ObisInfo > get_obis_info()
std::vector< SmlNode > messages
Definition sml_parser.h:68
bool setup_node(SmlNode *node)
std::vector< SmlNode > nodes
Definition sml_parser.h:48
const char * message
Definition component.cpp:38
uint16_t type
mopeka_std_values val[3]
std::string bytes_repr(const BytesView &buffer)
int64_t bytes_to_int(const BytesView &buffer)
std::string bytes_to_string(const BytesView &buffer)
uint64_t bytes_to_uint(const BytesView &buffer)
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint16_t length
Definition tt21100.cpp:0