From 30a542e76347880ca77321b2d0480980dc90d630 Mon Sep 17 00:00:00 2001
From: Attila Darazs <darazs@gmail.com>
Date: Mon, 3 Jun 2019 19:36:00 +0200
Subject: [PATCH] Add backlight handling for lcd_pcf8574 (#573)

* Add backlight handling for lcd_pcf8574

Switch the backlight on or off by calling id(mydisplay).backlight()
or id(mydisplay).no_backlight() in lamda functions (assuming mydisplay
is the custom id for the LCD).

* Use abstract method


Co-authored-by: Attila Darazs <attila@darazs.com>
Co-authored-by: Otto Winter <otto@otto-winter.com>
---
 esphome/components/lcd_base/__init__.py          |  8 +-------
 esphome/components/lcd_base/lcd_display.cpp      |  2 +-
 esphome/components/lcd_base/lcd_display.h        |  5 +----
 esphome/components/lcd_gpio/display.py           |  9 ++++++++-
 esphome/components/lcd_gpio/gpio_lcd_display.h   |  4 ++++
 esphome/components/lcd_pcf8574/display.py        |  8 +++++++-
 .../components/lcd_pcf8574/pcf8574_display.cpp   | 16 ++++++++++++++--
 esphome/components/lcd_pcf8574/pcf8574_display.h |  9 +++++++++
 8 files changed, 45 insertions(+), 16 deletions(-)

diff --git a/esphome/components/lcd_base/__init__.py b/esphome/components/lcd_base/__init__.py
index 27f65f9336..bff194578c 100644
--- a/esphome/components/lcd_base/__init__.py
+++ b/esphome/components/lcd_base/__init__.py
@@ -1,12 +1,11 @@
 import esphome.codegen as cg
 import esphome.config_validation as cv
 from esphome.components import display
-from esphome.const import CONF_DIMENSIONS, CONF_LAMBDA
+from esphome.const import CONF_DIMENSIONS
 from esphome.core import coroutine
 
 lcd_base_ns = cg.esphome_ns.namespace('lcd_base')
 LCDDisplay = lcd_base_ns.class_('LCDDisplay', cg.PollingComponent)
-LCDDisplayRef = LCDDisplay.operator('ref')
 
 
 def validate_lcd_dimensions(value):
@@ -28,8 +27,3 @@ def setup_lcd_display(var, config):
     yield cg.register_component(var, config)
     yield display.register_display(var, config)
     cg.add(var.set_dimensions(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1]))
-
-    if CONF_LAMBDA in config:
-        lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')],
-                                          return_type=cg.void)
-        cg.add(var.set_writer(lambda_))
diff --git a/esphome/components/lcd_base/lcd_display.cpp b/esphome/components/lcd_base/lcd_display.cpp
index af6b8304eb..51541049b1 100644
--- a/esphome/components/lcd_base/lcd_display.cpp
+++ b/esphome/components/lcd_base/lcd_display.cpp
@@ -107,7 +107,7 @@ void LCDDisplay::update() {
   for (uint8_t i = 0; i < this->rows_ * this->columns_; i++)
     this->buffer_[i] = ' ';
 
-  this->writer_(*this);
+  this->call_writer();
   this->display();
 }
 void LCDDisplay::command_(uint8_t value) { this->send(value, false); }
diff --git a/esphome/components/lcd_base/lcd_display.h b/esphome/components/lcd_base/lcd_display.h
index 200600eb9c..791f31ace3 100644
--- a/esphome/components/lcd_base/lcd_display.h
+++ b/esphome/components/lcd_base/lcd_display.h
@@ -12,11 +12,8 @@ namespace lcd_base {
 
 class LCDDisplay;
 
-using lcd_writer_t = std::function<void(LCDDisplay &)>;
-
 class LCDDisplay : public PollingComponent {
  public:
-  void set_writer(lcd_writer_t &&writer) { this->writer_ = std::move(writer); }
   void set_dimensions(uint8_t columns, uint8_t rows) {
     this->columns_ = columns;
     this->rows_ = rows;
@@ -54,11 +51,11 @@ class LCDDisplay : public PollingComponent {
   virtual void send(uint8_t value, bool rs) = 0;
 
   void command_(uint8_t value);
+  virtual void call_writer() = 0;
 
   uint8_t columns_;
   uint8_t rows_;
   uint8_t *buffer_{nullptr};
-  lcd_writer_t writer_;
 };
 
 }  // namespace lcd_base
diff --git a/esphome/components/lcd_gpio/display.py b/esphome/components/lcd_gpio/display.py
index 1f98955ece..91498d59c9 100644
--- a/esphome/components/lcd_gpio/display.py
+++ b/esphome/components/lcd_gpio/display.py
@@ -2,7 +2,8 @@ import esphome.codegen as cg
 import esphome.config_validation as cv
 from esphome import pins
 from esphome.components import lcd_base
-from esphome.const import CONF_DATA_PINS, CONF_ENABLE_PIN, CONF_RS_PIN, CONF_RW_PIN, CONF_ID
+from esphome.const import CONF_DATA_PINS, CONF_ENABLE_PIN, CONF_RS_PIN, CONF_RW_PIN, CONF_ID, \
+    CONF_LAMBDA
 
 AUTO_LOAD = ['lcd_base']
 
@@ -42,3 +43,9 @@ def to_code(config):
     if CONF_RW_PIN in config:
         rw = yield cg.gpio_pin_expression(config[CONF_RW_PIN])
         cg.add(var.set_rw_pin(rw))
+
+    if CONF_LAMBDA in config:
+        lambda_ = yield cg.process_lambda(config[CONF_LAMBDA],
+                                          [(GPIOLCDDisplay.operator('ref'), 'it')],
+                                          return_type=cg.void)
+        cg.add(var.set_writer(lambda_))
diff --git a/esphome/components/lcd_gpio/gpio_lcd_display.h b/esphome/components/lcd_gpio/gpio_lcd_display.h
index ed3b0c1137..01f6f95d9a 100644
--- a/esphome/components/lcd_gpio/gpio_lcd_display.h
+++ b/esphome/components/lcd_gpio/gpio_lcd_display.h
@@ -8,6 +8,7 @@ namespace lcd_gpio {
 
 class GPIOLCDDisplay : public lcd_base::LCDDisplay {
  public:
+  void set_writer(std::function<void(GPIOLCDDisplay &)> &&writer) { this->writer_ = std::move(writer); }
   void setup() override;
   void set_data_pins(GPIOPin *d0, GPIOPin *d1, GPIOPin *d2, GPIOPin *d3) {
     this->data_pins_[0] = d0;
@@ -36,10 +37,13 @@ class GPIOLCDDisplay : public lcd_base::LCDDisplay {
   void write_n_bits(uint8_t value, uint8_t n) override;
   void send(uint8_t value, bool rs) override;
 
+  void call_writer() override { this->writer_(*this); }
+
   GPIOPin *rs_pin_{nullptr};
   GPIOPin *rw_pin_{nullptr};
   GPIOPin *enable_pin_{nullptr};
   GPIOPin *data_pins_[8]{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+  std::function<void(GPIOLCDDisplay &)> writer_;
 };
 
 }  // namespace lcd_gpio
diff --git a/esphome/components/lcd_pcf8574/display.py b/esphome/components/lcd_pcf8574/display.py
index 2bc04a283f..2bbb3a2f7b 100644
--- a/esphome/components/lcd_pcf8574/display.py
+++ b/esphome/components/lcd_pcf8574/display.py
@@ -1,7 +1,7 @@
 import esphome.codegen as cg
 import esphome.config_validation as cv
 from esphome.components import lcd_base, i2c
-from esphome.const import CONF_ID
+from esphome.const import CONF_ID, CONF_LAMBDA
 
 DEPENDENCIES = ['i2c']
 AUTO_LOAD = ['lcd_base']
@@ -18,3 +18,9 @@ def to_code(config):
     var = cg.new_Pvariable(config[CONF_ID])
     yield lcd_base.setup_lcd_display(var, config)
     yield i2c.register_i2c_device(var, config)
+
+    if CONF_LAMBDA in config:
+        lambda_ = yield cg.process_lambda(config[CONF_LAMBDA],
+                                          [(PCF8574LCDDisplay.operator('ref'), 'it')],
+                                          return_type=cg.void)
+        cg.add(var.set_writer(lambda_))
diff --git a/esphome/components/lcd_pcf8574/pcf8574_display.cpp b/esphome/components/lcd_pcf8574/pcf8574_display.cpp
index 59491c7d5a..e3002da25d 100644
--- a/esphome/components/lcd_pcf8574/pcf8574_display.cpp
+++ b/esphome/components/lcd_pcf8574/pcf8574_display.cpp
@@ -6,9 +6,13 @@ namespace lcd_pcf8574 {
 
 static const char *TAG = "lcd_pcf8574";
 
+static const uint8_t LCD_DISPLAY_BACKLIGHT_ON = 0x08;
+static const uint8_t LCD_DISPLAY_BACKLIGHT_OFF = 0x00;
+
 void PCF8574LCDDisplay::setup() {
   ESP_LOGCONFIG(TAG, "Setting up PCF8574 LCD Display...");
-  if (!this->write_bytes(0x08, nullptr, 0)) {
+  this->backlight_value_ = LCD_DISPLAY_BACKLIGHT_ON;
+  if (!this->write_bytes(this->backlight_value_, nullptr, 0)) {
     this->mark_failed();
     return;
   }
@@ -29,7 +33,7 @@ void PCF8574LCDDisplay::write_n_bits(uint8_t value, uint8_t n) {
     // Ugly fix: in the super setup() with n == 4 value needs to be shifted left
     value <<= 4;
   }
-  uint8_t data = value | 0x08;  // Enable backlight
+  uint8_t data = value | this->backlight_value_;  // Set backlight state
   this->write_bytes(data, nullptr, 0);
   // Pulse ENABLE
   this->write_bytes(data | 0x04, nullptr, 0);
@@ -41,6 +45,14 @@ void PCF8574LCDDisplay::send(uint8_t value, bool rs) {
   this->write_n_bits((value & 0xF0) | rs, 0);
   this->write_n_bits(((value << 4) & 0xF0) | rs, 0);
 }
+void PCF8574LCDDisplay::backlight() {
+  this->backlight_value_ = LCD_DISPLAY_BACKLIGHT_ON;
+  this->write_bytes(this->backlight_value_, nullptr, 0);
+}
+void PCF8574LCDDisplay::no_backlight() {
+  this->backlight_value_ = LCD_DISPLAY_BACKLIGHT_OFF;
+  this->write_bytes(this->backlight_value_, nullptr, 0);
+}
 
 }  // namespace lcd_pcf8574
 }  // namespace esphome
diff --git a/esphome/components/lcd_pcf8574/pcf8574_display.h b/esphome/components/lcd_pcf8574/pcf8574_display.h
index 133679c501..4db3afb9b0 100644
--- a/esphome/components/lcd_pcf8574/pcf8574_display.h
+++ b/esphome/components/lcd_pcf8574/pcf8574_display.h
@@ -9,13 +9,22 @@ namespace lcd_pcf8574 {
 
 class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice {
  public:
+  void set_writer(std::function<void(PCF8574LCDDisplay &)> &&writer) { this->writer_ = std::move(writer); }
   void setup() override;
   void dump_config() override;
+  void backlight();
+  void no_backlight();
 
  protected:
   bool is_four_bit_mode() override { return true; }
   void write_n_bits(uint8_t value, uint8_t n) override;
   void send(uint8_t value, bool rs) override;
+
+  void call_writer() override { this->writer_(*this); }
+
+  // Stores the current state of the backlight.
+  uint8_t backlight_value_;
+  std::function<void(PCF8574LCDDisplay &)> writer_;
 };
 
 }  // namespace lcd_pcf8574