#pragma once #ifdef USE_ARDUINO #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/text_sensor/text_sensor.h" #include "esphome/components/uart/uart.h" #include "esphome/core/log.h" #include "esphome/core/defines.h" // don't include because it puts everything in global namespace #include #include #include namespace esphome { namespace dsmr { using namespace ::dsmr::fields; // DSMR_**_LIST generated by ESPHome and written in esphome/core/defines #if !defined(DSMR_SENSOR_LIST) && !defined(DSMR_TEXT_SENSOR_LIST) // Neither set, set it to a dummy value to not break build #define DSMR_TEXT_SENSOR_LIST(F, SEP) F(identification) #endif #if defined(DSMR_SENSOR_LIST) && defined(DSMR_TEXT_SENSOR_LIST) #define DSMR_BOTH , #else #define DSMR_BOTH #endif #ifndef DSMR_SENSOR_LIST #define DSMR_SENSOR_LIST(F, SEP) #endif #ifndef DSMR_TEXT_SENSOR_LIST #define DSMR_TEXT_SENSOR_LIST(F, SEP) #endif #define DSMR_DATA_SENSOR(s) s #define DSMR_COMMA , using MyData = ::dsmr::ParsedData; 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(); void publish_sensors(MyData &data) { #define DSMR_PUBLISH_SENSOR(s) \ if (data.s##_present && this->s_##s##_ != nullptr) \ s_##s##_->publish_state(data.s); DSMR_SENSOR_LIST(DSMR_PUBLISH_SENSOR, ) #define DSMR_PUBLISH_TEXT_SENSOR(s) \ if (data.s##_present && this->s_##s##_ != nullptr) \ s_##s##_->publish_state(data.s.c_str()); DSMR_TEXT_SENSOR_LIST(DSMR_PUBLISH_TEXT_SENSOR, ) }; void dump_config() override; void set_decryption_key(const std::string &decryption_key); void set_max_telegram_length(size_t length) { this->max_telegram_len_ = length; } void set_request_pin(GPIOPin *request_pin) { this->request_pin_ = request_pin; } void set_request_interval(uint32_t interval) { this->request_interval_ = interval; } void set_receive_timeout(uint32_t timeout) { this->receive_timeout_ = timeout; } // Sensor setters #define DSMR_SET_SENSOR(s) \ void set_##s(sensor::Sensor *sensor) { s_##s##_ = sensor; } DSMR_SENSOR_LIST(DSMR_SET_SENSOR, ) #define DSMR_SET_TEXT_SENSOR(s) \ void set_##s(text_sensor::TextSensor *sensor) { s_##s##_ = sensor; } DSMR_TEXT_SENSOR_LIST(DSMR_SET_TEXT_SENSOR, ) // handled outside dsmr void set_telegram(text_sensor::TextSensor *sensor) { s_telegram_ = sensor; } protected: void receive_telegram_(); void receive_encrypted_telegram_(); void reset_telegram_(); /// Wait for UART data to become available within the read timeout. /// /// The smart meter might provide data in chunks, causing available() to /// return 0. When we're already reading a telegram, then we don't return /// right away (to handle further data in an upcoming loop) but wait a /// little while using this method to see if more data are incoming. /// By not returning, we prevent other components from taking so much /// time that the UART RX buffer overflows and bytes of the telegram get /// lost in the process. bool available_within_timeout_(); // Request telegram uint32_t request_interval_; bool request_interval_reached_(); GPIOPin *request_pin_{nullptr}; uint32_t last_request_time_{0}; bool requesting_data_{false}; bool ready_to_request_data_(); void start_requesting_data_(); void stop_requesting_data_(); // Read telegram uint32_t receive_timeout_; bool receive_timeout_reached_(); size_t max_telegram_len_; char *telegram_{nullptr}; size_t bytes_read_{0}; uint8_t *crypt_telegram_{nullptr}; size_t crypt_telegram_len_{0}; size_t crypt_bytes_read_{0}; uint32_t last_read_time_{0}; bool header_found_{false}; bool footer_found_{false}; // handled outside dsmr text_sensor::TextSensor *s_telegram_{nullptr}; // Sensor member pointers #define DSMR_DECLARE_SENSOR(s) sensor::Sensor *s_##s##_{nullptr}; DSMR_SENSOR_LIST(DSMR_DECLARE_SENSOR, ) #define DSMR_DECLARE_TEXT_SENSOR(s) text_sensor::TextSensor *s_##s##_{nullptr}; DSMR_TEXT_SENSOR_LIST(DSMR_DECLARE_TEXT_SENSOR, ) std::vector decryption_key_{}; bool crc_check_; }; } // namespace dsmr } // namespace esphome #endif // USE_ARDUINO