#pragma once #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "esphome/core/helpers.h" #include "esphome/components/time/real_time_clock.h" #include "esphome/components/uart/uart.h" #include #include namespace esphome { namespace samsung_vfd { static const char *const TAG = "samsung_vfd"; // Control bytes, taken from older Arduino sketches that drove this display. // The one known to work only ever used ESC @ and 0x0C; 0x0B ("home") came // from an earlier test sketch. That sketch also sent 0xB0, which is left out: // it is most likely a printable glyph, not a command. static const uint8_t CMD_ESC = 0x1B; static const uint8_t CMD_INIT = 0x40; // ESC @ -> reset / init display static const uint8_t CMD_CLEAR = 0x0C; // FF -> clear screen (cursor to home) static const uint8_t CMD_HOME = 0x0B; // VT -> cursor home class SamsungVfd : public Component, public uart::UARTDevice { public: void setup() override; void dump_config() override; // Before the default (DATA) priority, so restored entity values can be shown. float get_setup_priority() const override { return setup_priority::HARDWARE; } void set_size(uint8_t cols, uint8_t rows) { this->cols_ = cols == 0 ? 20 : cols; this->rows_ = rows == 0 ? 2 : rows; } void set_send_newlines(bool v) { send_newlines_ = v; } uint8_t cols() const { return cols_; } uint8_t rows() const { return rows_; } /// Replace the whole screen with `text`, wrapping it over the available lines. void print(const std::string &text, bool centered = false); /// Replace a single line (0-based). Text is truncated/padded to the column count. void print_line(uint8_t line, const std::string &text, bool centered = false); void clear(); void home(); std::string line_text(uint8_t line) const { return line < lines_.size() ? lines_[line] : std::string(); } protected: void init_display_(); void resize_lines_(); /// Fit `text` (already single-byte) into one line: truncate or pad, optionally centred. std::string fit_(const std::string &text, bool centered) const; /// Rebuild the wire payload from the shadow buffer and send it only if it changed. void flush_(); void write_str_(const std::string &s); /// Map UTF-8 to one byte per character: accented Latin-1 letters lose their /// accent, control characters become spaces, anything else becomes '?'. static std::string sanitize_(const std::string &text, bool keep_newlines); uint8_t cols_{20}; uint8_t rows_{2}; bool send_newlines_{false}; std::vector lines_; std::string last_payload_; }; // ---------- actions ---------- template class PrintAction : public Action, public Parented { public: TEMPLATABLE_VALUE(std::string, text) TEMPLATABLE_VALUE(int, line) TEMPLATABLE_VALUE(bool, centered) void play(const Ts &...x) override { std::string text = this->text_.value(x...); bool centered = this->centered_.has_value() && this->centered_.value(x...); if (this->line_.has_value()) this->parent_->print_line(static_cast(this->line_.value(x...)), text, centered); else this->parent_->print(text, centered); } }; /// Like print, but formats the current time with strftime (e.g. "%H:%M:%S"). template class PrintfAction : public Action, public Parented { public: TEMPLATABLE_VALUE(std::string, format) TEMPLATABLE_VALUE(int, line) TEMPLATABLE_VALUE(bool, centered) void set_time(time::RealTimeClock *rtc) { this->rtc_ = rtc; } void play(const Ts &...x) override { auto now = this->rtc_->now(); if (!now.is_valid()) return; // clock not synchronised yet, leave the screen as it is std::string text = now.strftime(this->format_.value(x...)); bool centered = this->centered_.has_value() && this->centered_.value(x...); if (this->line_.has_value()) this->parent_->print_line(static_cast(this->line_.value(x...)), text, centered); else this->parent_->print(text, centered); } protected: time::RealTimeClock *rtc_{nullptr}; }; template class ClearAction : public Action, public Parented { public: void play(const Ts &...x) override { this->parent_->clear(); } }; template class HomeAction : public Action, public Parented { public: void play(const Ts &...x) override { this->parent_->home(); } }; } // namespace samsung_vfd } // namespace esphome