mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 01:07:45 +01:00
Add max_telegram_length option to dsmr (#2674)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net> Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
parent
df6730be55
commit
dee5d639e2
4 changed files with 50 additions and 27 deletions
|
@ -15,6 +15,7 @@ CONF_DSMR_ID = "dsmr_id"
|
|||
CONF_DECRYPTION_KEY = "decryption_key"
|
||||
CONF_CRC_CHECK = "crc_check"
|
||||
CONF_GAS_MBUS_ID = "gas_mbus_id"
|
||||
CONF_MAX_TELEGRAM_LENGTH = "max_telegram_length"
|
||||
|
||||
# Hack to prevent compile error due to ambiguity with lib namespace
|
||||
dsmr_ns = cg.esphome_ns.namespace("esphome::dsmr")
|
||||
|
@ -46,6 +47,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.Optional(CONF_DECRYPTION_KEY): _validate_key,
|
||||
cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean,
|
||||
cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_,
|
||||
cv.Optional(CONF_MAX_TELEGRAM_LENGTH, default=1500): cv.int_,
|
||||
}
|
||||
).extend(uart.UART_DEVICE_SCHEMA),
|
||||
cv.only_with_arduino,
|
||||
|
@ -55,6 +57,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
async def to_code(config):
|
||||
uart_component = await cg.get_variable(config[CONF_UART_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], uart_component, config[CONF_CRC_CHECK])
|
||||
cg.add(var.set_max_telegram_length(config[CONF_MAX_TELEGRAM_LENGTH]))
|
||||
if CONF_DECRYPTION_KEY in config:
|
||||
cg.add(var.set_decryption_key(config[CONF_DECRYPTION_KEY]))
|
||||
await cg.register_component(var, config)
|
||||
|
|
|
@ -12,11 +12,15 @@ namespace dsmr {
|
|||
|
||||
static const char *const TAG = "dsmr";
|
||||
|
||||
void Dsmr::setup() {
|
||||
telegram_ = new char[max_telegram_len_]; // NOLINT
|
||||
}
|
||||
|
||||
void Dsmr::loop() {
|
||||
if (this->decryption_key_.empty())
|
||||
this->receive_telegram_();
|
||||
if (decryption_key_.empty())
|
||||
receive_telegram_();
|
||||
else
|
||||
this->receive_encrypted_();
|
||||
receive_encrypted_();
|
||||
}
|
||||
|
||||
bool Dsmr::available_within_timeout_() {
|
||||
|
@ -51,10 +55,10 @@ void Dsmr::receive_telegram_() {
|
|||
continue;
|
||||
|
||||
// Check for buffer overflow.
|
||||
if (telegram_len_ >= MAX_TELEGRAM_LENGTH) {
|
||||
if (telegram_len_ >= max_telegram_len_) {
|
||||
header_found_ = false;
|
||||
footer_found_ = false;
|
||||
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", MAX_TELEGRAM_LENGTH);
|
||||
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", max_telegram_len_);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -86,9 +90,7 @@ void Dsmr::receive_telegram_() {
|
|||
}
|
||||
|
||||
void Dsmr::receive_encrypted_() {
|
||||
// Encrypted buffer
|
||||
uint8_t buffer[MAX_TELEGRAM_LENGTH];
|
||||
size_t buffer_length = 0;
|
||||
encrypted_telegram_len_ = 0;
|
||||
size_t packet_size = 0;
|
||||
|
||||
while (true) {
|
||||
|
@ -114,39 +116,39 @@ void Dsmr::receive_encrypted_() {
|
|||
}
|
||||
|
||||
// Check for buffer overflow.
|
||||
if (buffer_length >= MAX_TELEGRAM_LENGTH) {
|
||||
if (encrypted_telegram_len_ >= max_telegram_len_) {
|
||||
header_found_ = false;
|
||||
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", MAX_TELEGRAM_LENGTH);
|
||||
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", max_telegram_len_);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer[buffer_length++] = c;
|
||||
encrypted_telegram_[encrypted_telegram_len_++] = c;
|
||||
|
||||
if (packet_size == 0 && buffer_length > 20) {
|
||||
if (packet_size == 0 && encrypted_telegram_len_ > 20) {
|
||||
// Complete header + data bytes
|
||||
packet_size = 13 + (buffer[11] << 8 | buffer[12]);
|
||||
packet_size = 13 + (encrypted_telegram_[11] << 8 | encrypted_telegram_[12]);
|
||||
ESP_LOGV(TAG, "Encrypted telegram size: %d bytes", packet_size);
|
||||
}
|
||||
if (buffer_length == packet_size && packet_size > 0) {
|
||||
if (encrypted_telegram_len_ == packet_size && packet_size > 0) {
|
||||
ESP_LOGV(TAG, "End of encrypted telegram found");
|
||||
GCM<AES128> *gcmaes128{new GCM<AES128>()};
|
||||
gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
|
||||
gcmaes128->setKey(decryption_key_.data(), gcmaes128->keySize());
|
||||
// the iv is 8 bytes of the system title + 4 bytes frame counter
|
||||
// system title is at byte 2 and frame counter at byte 15
|
||||
for (int i = 10; i < 14; i++)
|
||||
buffer[i] = buffer[i + 4];
|
||||
encrypted_telegram_[i] = encrypted_telegram_[i + 4];
|
||||
constexpr uint16_t iv_size{12};
|
||||
gcmaes128->setIV(&buffer[2], iv_size);
|
||||
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
|
||||
gcmaes128->setIV(&encrypted_telegram_[2], iv_size);
|
||||
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(telegram_),
|
||||
// the ciphertext start at byte 18
|
||||
&buffer[18],
|
||||
&encrypted_telegram_[18],
|
||||
// cipher size
|
||||
buffer_length - 17);
|
||||
encrypted_telegram_len_ - 17);
|
||||
delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
|
||||
telegram_len_ = strnlen(this->telegram_, sizeof(this->telegram_));
|
||||
telegram_len_ = strnlen(telegram_, max_telegram_len_);
|
||||
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", telegram_len_);
|
||||
ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
|
||||
ESP_LOGVV(TAG, "Decrypted telegram: %s", telegram_);
|
||||
|
||||
parse_telegram();
|
||||
|
||||
|
@ -162,7 +164,7 @@ bool Dsmr::parse_telegram() {
|
|||
ESP_LOGV(TAG, "Trying to parse telegram");
|
||||
::dsmr::ParseResult<void> res =
|
||||
::dsmr::P1Parser::parse(&data, telegram_, telegram_len_, false,
|
||||
this->crc_check_); // Parse telegram according to data definition. Ignore unknown values.
|
||||
crc_check_); // Parse telegram according to data definition. Ignore unknown values.
|
||||
if (res.err) {
|
||||
// Parsing error, show it
|
||||
auto err_str = res.fullError(telegram_, telegram_ + telegram_len_);
|
||||
|
@ -177,6 +179,7 @@ bool Dsmr::parse_telegram() {
|
|||
|
||||
void Dsmr::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "DSMR:");
|
||||
ESP_LOGCONFIG(TAG, " Max telegram length: %d", max_telegram_len_);
|
||||
|
||||
#define DSMR_LOG_SENSOR(s) LOG_SENSOR(" ", #s, this->s_##s##_);
|
||||
DSMR_SENSOR_LIST(DSMR_LOG_SENSOR, )
|
||||
|
@ -188,7 +191,11 @@ void Dsmr::dump_config() {
|
|||
void Dsmr::set_decryption_key(const std::string &decryption_key) {
|
||||
if (decryption_key.length() == 0) {
|
||||
ESP_LOGI(TAG, "Disabling decryption");
|
||||
this->decryption_key_.clear();
|
||||
decryption_key_.clear();
|
||||
if (encrypted_telegram_ != nullptr) {
|
||||
delete[] encrypted_telegram_;
|
||||
encrypted_telegram_ = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -196,7 +203,7 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) {
|
|||
ESP_LOGE(TAG, "Error, decryption key must be 32 character long");
|
||||
return;
|
||||
}
|
||||
this->decryption_key_.clear();
|
||||
decryption_key_.clear();
|
||||
|
||||
ESP_LOGI(TAG, "Decryption key is set");
|
||||
// Verbose level prints decryption key
|
||||
|
@ -207,8 +214,14 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) {
|
|||
strncpy(temp, &(decryption_key.c_str()[i * 2]), 2);
|
||||
decryption_key_.push_back(std::strtoul(temp, nullptr, 16));
|
||||
}
|
||||
|
||||
if (encrypted_telegram_ == nullptr) {
|
||||
encrypted_telegram_ = new uint8_t[max_telegram_len_]; // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
void Dsmr::set_max_telegram_length(size_t length) { max_telegram_len_ = length; }
|
||||
|
||||
} // namespace dsmr
|
||||
} // namespace esphome
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
namespace esphome {
|
||||
namespace dsmr {
|
||||
|
||||
static constexpr uint32_t MAX_TELEGRAM_LENGTH = 1500;
|
||||
static constexpr uint32_t READ_TIMEOUT_MS = 200;
|
||||
|
||||
using namespace ::dsmr::fields;
|
||||
|
@ -52,6 +51,8 @@ class Dsmr : public Component, public uart::UARTDevice {
|
|||
public:
|
||||
Dsmr(uart::UARTComponent *uart, bool crc_check) : uart::UARTDevice(uart), crc_check_(crc_check) {}
|
||||
|
||||
void setup() override;
|
||||
|
||||
void loop() override;
|
||||
|
||||
bool parse_telegram();
|
||||
|
@ -72,6 +73,8 @@ class Dsmr : public Component, public uart::UARTDevice {
|
|||
|
||||
void set_decryption_key(const std::string &decryption_key);
|
||||
|
||||
void set_max_telegram_length(size_t length);
|
||||
|
||||
// Sensor setters
|
||||
#define DSMR_SET_SENSOR(s) \
|
||||
void set_##s(sensor::Sensor *sensor) { s_##s##_ = sensor; }
|
||||
|
@ -97,8 +100,11 @@ class Dsmr : public Component, public uart::UARTDevice {
|
|||
bool available_within_timeout_();
|
||||
|
||||
// Telegram buffer
|
||||
char telegram_[MAX_TELEGRAM_LENGTH];
|
||||
size_t max_telegram_len_;
|
||||
char *telegram_{nullptr};
|
||||
int telegram_len_{0};
|
||||
uint8_t *encrypted_telegram_{nullptr};
|
||||
int encrypted_telegram_len_{0};
|
||||
|
||||
// Serial parser
|
||||
bool header_found_{false};
|
||||
|
|
|
@ -1308,6 +1308,7 @@ fingerprint_grow:
|
|||
dsmr:
|
||||
decryption_key: 00112233445566778899aabbccddeeff
|
||||
uart_id: uart6
|
||||
max_telegram_length: 1000
|
||||
|
||||
daly_bms:
|
||||
update_interval: 20s
|
||||
|
|
Loading…
Reference in a new issue