ESPHome & Input Shift Registers

I started a project to control turning on and off 12 relays with an ESP32 & 2 shift registers (74HC595). I used the SN74HC595 component in ESPHome to make that happen, and it works great. In HA, I have the 12 switches controlling the relays - exactly what I wanted.

Now, I want to add physical buttons to the ESP32 to turn on/off those relays. The plan was to have 12 buttons - 1 for each relay, and I was hoping to use shift registers on the button input like I did with the output. From what I can tell, the 74HC595 isn’t the right component for that, and what I really need are 74HC165 shift registers.

Been poking around for a couple days, but haven’t found the right solution. I do have enough GPIOs on the ESP board where I could just wire them directly to the board, but where’s the fun in that?

Is it possible to use a 74HC595 as the shift register to accept the button inputs? I know I need to create a separate “hub” for the inputs, but it doesn’t look like the 74HC595 is the right component from what I’ve researched.

I don’t see a pre-built component for 74HC165 shift registers - anyone have any experience in using them with an ESP32 board?

Or, is there another component that I should be using for my use case?

2 Likes

Well today is your lucky day my friend , because after days of looking all over the internet and not finding anything , I just wrote and tested the code for custom 74HC165 binary sensors to use in my own project today and was going to share it in this site so that other people can use it in theirs works and check for any possible bugs , so I can fix them :sweat_smile:
So for adding a custom component to ESPHome , first you should add a file called “74HC165.h” to ESPHome configuration directory by File editor ADD-ON , then save this code inside that file :

#include "esphome.h"

class SN74HC165Component : public PollingComponent, public BinarySensor {
  byte pinNum;
  private:
    const byte LATCH = 12;  // ESP pin number that should be connected to pin 1 of 74hc165s
    const byte DATA  = 39;  // ESP pin number that should be connected to pin 9 of first 74hc165
    const byte CLOCK = 16; // ESP pin number that should be connected to pin 2 of 74hc165s
    const byte NUMBER_OF_CHIPS = 2; // Total number of daisy-chained 74HC165s

  bool getBit(unsigned long x, byte n) {
    bool value = bitRead(x, n);
    return value;
  }
  
  unsigned long shiftinput= 1;
  unsigned long oldshiftinput= 0;

 public:
  SN74HC165Component (byte pin) : PollingComponent(100) { pinNum = pin; }   // Change the number inside "PollingComponent(100)" to change update interval in miliseconds 
  float get_setup_priority() const override { return esphome::setup_priority::HARDWARE; }

  void setup() override {
    pinMode(LATCH, OUTPUT); 
    pinMode(CLOCK, OUTPUT); 
    pinMode(DATA, INPUT); 
    digitalWrite(CLOCK, LOW); 
    digitalWrite(LATCH, HIGH);
  }

  void update() override {
    digitalWrite(CLOCK, LOW); 
    digitalWrite(LATCH, LOW); 
    digitalWrite(LATCH, HIGH); 

    shiftinput = shiftIn(DATA, CLOCK, MSBFIRST);
    if (NUMBER_OF_CHIPS > 1) { 
      shiftinput |= shiftIn(DATA, CLOCK, MSBFIRST) << 8;
    }
    if (NUMBER_OF_CHIPS > 2) { 
      shiftinput |= shiftIn(DATA, CLOCK, MSBFIRST) << 16;
    }
    if (NUMBER_OF_CHIPS > 3) { 
      shiftinput |= shiftIn(DATA, CLOCK, MSBFIRST) << 24;
    }
    
    if (shiftinput != oldshiftinput) {
      publish_state(getBit(shiftinput, pinNum));
      oldshiftinput = shiftinput;
    }  
  }
};

and then add a include key to your ESPHome .yaml file :

and finally define your inputs by using a custom platform in the same .yaml file :

binary_sensor:
  - platform: custom
    lambda: |-
      auto X00 = new SN74HC165Component(0);
      auto X01 = new SN74HC165Component(1);
      auto X02 = new SN74HC165Component(2);
      auto X03 = new SN74HC165Component(3);
      auto X04 = new SN74HC165Component(4);
      auto X05 = new SN74HC165Component(5);
      auto X06 = new SN74HC165Component(6);
      auto X07 = new SN74HC165Component(7);
      auto X08 = new SN74HC165Component(8);
      auto X09 = new SN74HC165Component(9);
      auto X10 = new SN74HC165Component(10);
      auto X11 = new SN74HC165Component(11);
      auto X12 = new SN74HC165Component(12);
      auto X13 = new SN74HC165Component(13);
      auto X14 = new SN74HC165Component(14);
      auto X15 = new SN74HC165Component(15);
      App.register_component(X00);
      App.register_component(X01);
      App.register_component(X02);
      App.register_component(X03);
      App.register_component(X04);
      App.register_component(X05);
      App.register_component(X06);
      App.register_component(X07);
      App.register_component(X08);
      App.register_component(X09);
      App.register_component(X10);
      App.register_component(X11);
      App.register_component(X12);
      App.register_component(X13);
      App.register_component(X14);
      App.register_component(X15);
      return {X00,X01,X02,X03,X04,X05,X06,X07,X08,X09,X10,X11,X12,X13,X14,X15};

    binary_sensors:
     - name: "Input 1"
     - name: "Input 2"
     - name: "Input 3"
     - name: "Input 4"
     - name: "Input 5"
     - name: "Input 6"
     - name: "Input 7"
     - name: "Input 8"
     - name: "Input 9"
     - name: "Input 10"
     - name: "Input 11"
     - name: "Input 12"
     - name: "Input 13"
     - name: "Input 14"
     - name: "Input 15"
     - name: "Input 16"

Connect your chips to ESP like this and you should see all of 74HC165s pins as input entities in your ESPHome device :vulcan_salute:
http://domoticx.com/wp-content/uploads/2020/07/74HC165-daisy-chain-schematic.png

Just some notes , LATCH and CLOCK pins on your ESP should be OUTPUT capable , and DATA pin should be INPUT capable ! (some gpios are not)

Excellent instructions and nice clean code. Great job! I ordered some 74HC165’s and should have them in the next few days. Will post back with my results.

Got my shift registers, wired them up, setup my ESP, and everything works great. Exactly what I needed.

Thanks again. You should consider doing a PR and adding this as a component to core ESPHome.

I have a netgain hyper9 motor and controller. Controller is hooked up to CAN bus. I am using an esp32 with mcp2515 to read the data. The format is as in the image here. I can read a byte ok and get a number but can not correctly read the word (2bytes) nor can I get the correct bitmap. I am using esphome to read this through a lambda function. Are there any c++ / esphome lambda people that could help.

Did you make a PR to add this to the Component list?

Here is my conf for using 32Bit Outputs and 32BitInputs, using 4x74HC595 and 4x74HC165

Because of the channel-numbering (it used to start with 0, i want it to start with 1) i made a little change with the Clock toggeling.

Binswitch.h (for 74HC595):

#include "esphome.h"

//  global variable to store state of bits
uint64_t output_state = 0;
// pins of the Output shift register (74HC595)
const byte outputLatch_pin = 15;
const byte clock_pin = 14;
const byte mosi_pin = 13;

class binSwitch : public Component, public Switch
{
 byte pinNum;
 bool SetBit(bool pinValue, uint64_t number)
 {
   if (pinValue)
     bitSet(output_state, number);
   else
     bitClear(output_state, number);
   SetOutputs();
   return pinValue;
 }
 void SetOutputs()
 {
   digitalWrite(clock_pin, LOW);
   digitalWrite(outputLatch_pin, LOW);
   shiftOut(mosi_pin, clock_pin, LSBFIRST, output_state);
   shiftOut(mosi_pin, clock_pin, LSBFIRST, output_state >> 8);
   shiftOut(mosi_pin, clock_pin, LSBFIRST, output_state >> 16);
   shiftOut(mosi_pin, clock_pin, LSBFIRST, output_state >> 24);
   digitalWrite(outputLatch_pin, HIGH);
 }
public:
 // pin - output pin of the shift register (0..31)
 binSwitch(byte pin) { pinNum = pin; publish_state(false);}
 void setup() override
 {
   publish_state(false);
   pinMode(outputLatch_pin, OUTPUT);
   pinMode(clock_pin, OUTPUT);
   pinMode(mosi_pin, OUTPUT);
   digitalWrite(outputLatch_pin, HIGH);
   SetOutputs();
 }
 void write_state(bool state) override
 {
   publish_state(SetBit(state, pinNum));
 }
};

and 74HC165.h:

#include "esphome.h"

class SN74HC165Component : public PollingComponent, public BinarySensor 
{
  byte pinNum;
  private:
    const byte LATCH = 4;  // ESP pin number that should be connected to pin 1 of 74hc165s
    const byte DATA  = 5;  // ESP pin number that should be connected to pin 9 of first 74hc165
    const byte CLOCK = 2; // ESP pin number that should be connected to pin 2 of 74hc165s

  bool getBit(unsigned long x, byte n) 
  {
    bool value = bitRead(x, n);
    return value;
  }
  
  unsigned long shiftinput= 1;
  unsigned long oldshiftinput= 0;

 public:
  SN74HC165Component (byte pin) : PollingComponent(100) { pinNum = pin; }   // Change the number inside "PollingComponent(100)" to change update interval in miliseconds 
  float get_setup_priority() const override { return esphome::setup_priority::HARDWARE; }

  void setup() override 
  {
    pinMode(LATCH, OUTPUT); 
    pinMode(CLOCK, OUTPUT); 
    pinMode(DATA, INPUT); 
    digitalWrite(CLOCK, LOW); 
    digitalWrite(CLOCK, HIGH); 
    digitalWrite(LATCH, HIGH);
  }

  void update() override 
  {
    digitalWrite(CLOCK, LOW); 
    digitalWrite(CLOCK, HIGH); 
    digitalWrite(LATCH, LOW); 
    digitalWrite(LATCH, HIGH); 

    shiftinput = shiftIn(DATA, CLOCK, LSBFIRST);
    shiftinput |= shiftIn(DATA, CLOCK, LSBFIRST) << 8;
    shiftinput |= shiftIn(DATA, CLOCK, LSBFIRST) << 16;
    shiftinput |= shiftIn(DATA, CLOCK, LSBFIRST) << 24;
    
    if (shiftinput != oldshiftinput) 
    {
      publish_state(getBit(shiftinput, pinNum));
      oldshiftinput = shiftinput;
    }  
  }
};

and here the ESPhome yaml:

esphome:
  name: test_esp_shift
  platform: ESP8266
  board: d1_mini
  includes:
    - binSwitch.h
    - 74HC165.h
    
wifi:
  ssid: "yourSSID"
  password: "yourPW"
  
  manual_ip:
    static_ip: 192.168.2.244
    gateway: 192.168.2.1
    subnet: 255.255.252.0

# Enable Home Assistant API
api:
  password: "yourPW"

ota:
  password: "yourPW"

web_server:
  port: 80


switch:
- platform: custom
  lambda: |-
    auto switch1 = new binSwitch(0);
    auto switch2 = new binSwitch(1);
    auto switch3 = new binSwitch(2);
    auto switch4 = new binSwitch(3);
    auto switch5 = new binSwitch(4);
    auto switch6 = new binSwitch(5);
    auto switch7 = new binSwitch(6);
    auto switch8 = new binSwitch(7);
    auto switch9 = new binSwitch(8);
    auto switch10 = new binSwitch(9);
    auto switch11 = new binSwitch(10);
    auto switch12 = new binSwitch(11);
    auto switch13 = new binSwitch(12);
    auto switch14 = new binSwitch(13);
    auto switch15 = new binSwitch(14);
    auto switch16 = new binSwitch(15);
    auto switch17 = new binSwitch(16);
    auto switch18 = new binSwitch(17);
    auto switch19 = new binSwitch(18);
    auto switch20 = new binSwitch(19);
    auto switch21 = new binSwitch(20);
    auto switch22 = new binSwitch(21);
    auto switch23 = new binSwitch(22);
    auto switch24 = new binSwitch(23);
    auto switch25 = new binSwitch(24);
    auto switch26 = new binSwitch(25);
    auto switch27 = new binSwitch(26);
    auto switch28 = new binSwitch(27);
    auto switch29 = new binSwitch(28);
    auto switch30 = new binSwitch(29);
    auto switch31 = new binSwitch(30);
    auto switch32 = new binSwitch(31);
    App.register_component(switch1);
    App.register_component(switch2);
    App.register_component(switch3);
    App.register_component(switch4);
    App.register_component(switch5);
    App.register_component(switch6);
    App.register_component(switch7);
    App.register_component(switch8);
    App.register_component(switch9);
    App.register_component(switch10);
    App.register_component(switch11);
    App.register_component(switch12);
    App.register_component(switch13);
    App.register_component(switch14);
    App.register_component(switch15);
    App.register_component(switch16);
    App.register_component(switch17);
    App.register_component(switch18);
    App.register_component(switch19);
    App.register_component(switch20);
    App.register_component(switch21);
    App.register_component(switch22);
    App.register_component(switch23);
    App.register_component(switch24);
    App.register_component(switch25);
    App.register_component(switch26);
    App.register_component(switch27);
    App.register_component(switch28);
    App.register_component(switch29);
    App.register_component(switch30);
    App.register_component(switch31);
    App.register_component(switch32);
    return {switch1,switch2,switch3,switch4,switch5,switch6,switch7,switch8,switch9,switch10,switch11,switch12,switch13,switch14,switch15,switch16,switch17,switch18,switch19,switch20,switch21,switch22,switch23,switch24,switch25,switch26,switch27,switch28,switch29,switch30,switch31,switch32};

  switches:
    - name: "Switch 1"
      id: switch_1
    - name: "Switch 2"
      id: switch_2
    - name: "Switch 3"
      id: switch_3
    - name: "Switch 4"
      id: switch_4
    - name: "Switch 5"
      id: switch_5
    - name: "Switch 6"
      id: switch_6
    - name: "Switch 7"
      id: switch_7
    - name: "Switch 8"
      id: switch_8
    - name: "Switch 9"
      id: switch_9
    - name: "Switch 10"
      id: switch_10
    - name: "Switch 11"
      id: switch_11
    - name: "Switch 12"
      id: switch_12
    - name: "Switch 13"
      id: switch_13
    - name: "Switch 14"
      id: switch_14
    - name: "Switch 15"
      id: switch_15
    - name: "Switch 16"
      id: switch_16
    - name: "Switch 17"
      id: switch_17
    - name: "Switch 18"
      id: switch_18
    - name: "Switch 19"
      id: switch_19
    - name: "Switch 20"
      id: switch_20
    - name: "Switch 21"
      id: switch_21
    - name: "Switch 22"
      id: switch_22
    - name: "Switch 23"
      id: switch_23
    - name: "Switch 24"
      id: switch_24
    - name: "Switch 25"
      id: switch_25
    - name: "Switch 26"
      id: switch_26
    - name: "Switch 27"
      id: switch_27
    - name: "Switch 28"
      id: switch_28
    - name: "Switch 29"
      id: switch_29
    - name: "Switch 30"
      id: switch_30
    - name: "Switch 31"
      id: switch_31
    - name: "Switch 32"
      id: switch_32

binary_sensor:
  - platform: custom
    lambda: |-
      auto X01 = new SN74HC165Component(0);
      auto X02 = new SN74HC165Component(1);
      auto X03 = new SN74HC165Component(2);
      auto X04 = new SN74HC165Component(3);
      auto X05 = new SN74HC165Component(4);
      auto X06 = new SN74HC165Component(5);
      auto X07 = new SN74HC165Component(6);
      auto X08 = new SN74HC165Component(7);
      auto X09 = new SN74HC165Component(8);
      auto X10 = new SN74HC165Component(9);
      auto X11 = new SN74HC165Component(10);
      auto X12 = new SN74HC165Component(11);
      auto X13 = new SN74HC165Component(12);
      auto X14 = new SN74HC165Component(13);
      auto X15 = new SN74HC165Component(14);
      auto X16 = new SN74HC165Component(15);
      auto X17 = new SN74HC165Component(16);
      auto X18 = new SN74HC165Component(17);
      auto X19 = new SN74HC165Component(18);
      auto X20 = new SN74HC165Component(19);
      auto X21 = new SN74HC165Component(20);
      auto X22 = new SN74HC165Component(21);
      auto X23 = new SN74HC165Component(22);
      auto X24 = new SN74HC165Component(23);
      auto X25 = new SN74HC165Component(24);
      auto X26 = new SN74HC165Component(25);
      auto X27 = new SN74HC165Component(26);
      auto X28 = new SN74HC165Component(27);
      auto X29 = new SN74HC165Component(28);
      auto X30 = new SN74HC165Component(29);
      auto X31 = new SN74HC165Component(30);
      auto X32 = new SN74HC165Component(31);
      App.register_component(X01);
      App.register_component(X02);
      App.register_component(X03);
      App.register_component(X04);
      App.register_component(X05);
      App.register_component(X06);
      App.register_component(X07);
      App.register_component(X08);
      App.register_component(X09);
      App.register_component(X10);
      App.register_component(X11);
      App.register_component(X12);
      App.register_component(X13);
      App.register_component(X14);
      App.register_component(X15);
      App.register_component(X16);
      App.register_component(X17);
      App.register_component(X18);
      App.register_component(X19);
      App.register_component(X20);
      App.register_component(X21);
      App.register_component(X22);
      App.register_component(X23);
      App.register_component(X24);
      App.register_component(X25);
      App.register_component(X26);
      App.register_component(X27);
      App.register_component(X28);
      App.register_component(X29);
      App.register_component(X30);
      App.register_component(X31);
      App.register_component(X32);
      return {X01,X02,X03,X04,X05,X06,X07,X08,X09,X10,X11,X12,X13,X14,X15,X16,X17,X18,X19,X20,X21,X22,X23,X24,X25,X26,X27,X28,X29,X30,X31,X32};

    binary_sensors:
     - name: "Input 1"
     - name: "Input 2"
     - name: "Input 3"
     - name: "Input 4"
     - name: "Input 5"
     - name: "Input 6"
     - name: "Input 7"
     - name: "Input 8"
     - name: "Input 9"
     - name: "Input 10"
     - name: "Input 11"
     - name: "Input 12"
     - name: "Input 13"
     - name: "Input 14"
     - name: "Input 15"
     - name: "Input 16"
     - name: "Input 17"
     - name: "Input 18"
     - name: "Input 19"
     - name: "Input 20"
     - name: "Input 21"
     - name: "Input 22"
     - name: "Input 23"
     - name: "Input 24"
     - name: "Input 25"
     - name: "Input 26"
     - name: "Input 27"
     - name: "Input 28"
     - name: "Input 29"
     - name: "Input 30"
     - name: "Input 31"
     - name: "Input 32"     

in HA there are two Cards:
Outputs:

type: entities
entities:
  - entity: switch.switch_1
  - entity: switch.switch_2
  - entity: switch.switch_3
  - entity: switch.switch_4
  - entity: switch.switch_5
  - entity: switch.switch_6
  - entity: switch.switch_7
  - entity: switch.switch_8
  - entity: switch.switch_9
  - entity: switch.switch_10
  - entity: switch.switch_11
  - entity: switch.switch_12
  - entity: switch.switch_13
  - entity: switch.switch_14
  - entity: switch.switch_15
  - entity: switch.switch_16
  - entity: switch.switch_17
  - entity: switch.switch_18
  - entity: switch.switch_19
  - entity: switch.switch_20
  - entity: switch.switch_21
  - entity: switch.switch_22
  - entity: switch.switch_23
  - entity: switch.switch_24
  - entity: switch.switch_25
  - entity: switch.switch_26
  - entity: switch.switch_27
  - entity: switch.switch_28
  - entity: switch.switch_29
  - entity: switch.switch_30
  - entity: switch.switch_31
  - entity: switch.switch_32
state_color: true
title: ESP Shiftboard Outputs

and for Inputs:

type: entities
entities:
  - entity: binary_sensor.input_1
  - entity: binary_sensor.input_2
  - entity: binary_sensor.input_3
  - entity: binary_sensor.input_4
  - entity: binary_sensor.input_5
  - entity: binary_sensor.input_6
  - entity: binary_sensor.input_7
  - entity: binary_sensor.input_8
  - entity: binary_sensor.input_9
  - entity: binary_sensor.input_10
  - entity: binary_sensor.input_11
  - entity: binary_sensor.input_12
  - entity: binary_sensor.input_13
  - entity: binary_sensor.input_14
  - entity: binary_sensor.input_15
  - entity: binary_sensor.input_16
  - entity: binary_sensor.input_17
  - entity: binary_sensor.input_18
  - entity: binary_sensor.input_19
  - entity: binary_sensor.input_20
  - entity: binary_sensor.input_21
  - entity: binary_sensor.input_22
  - entity: binary_sensor.input_23
  - entity: binary_sensor.input_24
  - entity: binary_sensor.input_25
  - entity: binary_sensor.input_26
  - entity: binary_sensor.input_27
  - entity: binary_sensor.input_28
  - entity: binary_sensor.input_29
  - entity: binary_sensor.input_30
  - entity: binary_sensor.input_31
  - entity: binary_sensor.input_32
state_color: true
title: ESP Shiftboard Inputs

I hope this helps someone!

@mega-hz Do you have a schematic how you’ve connected the shift registers?

1 Like

Hi did you publish the yaml for the relays part you mention in your first paragraph? I would love to see how you did that if you would share, I need it!

Hi Tony,

Here’s my yaml for my sprinkler system I mentioned in my original post. There’s a lot going on here, but the relay section is in there. I have 12 sprinkler zones, and wanted to be sure there’s only 1 active zone at a time, so you can see where I’m turning on and off the relays to assure just a single active zone. I also included physical momentary switches that will turn zones on and off. I also have a Waveshare e-paper display showing info about the current weather, time, sprinkler schedule, sprinkler state, and zone info. I’ve also included the yaml for the HA dashboard where you can set the schedule and manage the sprinkler from an HA screen.

Having said all of that, I never actually finished the project. I got all of the physical components working and the software working, but never actually installed it. I ran into issues with the Waveshare e-paper being very flaky and that caused me to put it on the back burner, and I never got back to it. I believe the e-paper issue is now fixed in core ESPHOME, but am not 100% sure on that.

No doubt there’s a more elegant way of doing what I’ve done, but hope this helps in some way…

ESPHome Device (Part 1 of 2)

esphome:
  name: sprinklers
    
esp32:
  board: nodemcu-32s
  framework:
    type: arduino

packages:
  base_config: !include common/base_config.yaml

substitutions:  
  # Define general substitutions
  devicename: sprinklers
  friendly_name: "Sprinklers"

###################
# General configs #
###################
#external_components:  # This no longer works as the version in my_components are old, but left the code block in as sample for using custom code
#  - source:
#      type: local
#      path: /config/esphome/my_components   #included to fix issue with ePaper display

external_components:  # From this post on esphome - https://github.com/esphome/issues/issues/2334
  - source:
      type: git
      url: https://github.com/velaar/esphome
      ref: test_23_7
    components: [ waveshare_epaper, display]

# Status LED - blue led to know if connected to network
status_led:
  pin: 
    number: GPIO02
    inverted: TRUE

#####################
# Global variables  #
#####################

globals:
  - id: active_zone
    type: int
    restore_value: no
    initial_value: '99'

#######################################################################################
# Additional Sensors                                                                  #
# Returns the version of ESPHome the device is running.                               #
# Gets the current conditions icon from Home Assistant weather for the ePaper display #
# Gets the other entities from HA for use on the ePaper display.                      #
#######################################################################################

text_sensor:
  - platform: homeassistant
    name: forecast
    id: weather_icon
    entity_id: weather.home
    internal: true   
    
  - platform: homeassistant
    entity_id: input_text.sprinkler_status
    id: displaystatus
    
  - platform: homeassistant
    entity_id: sensor.sprinkler_s1_display_master
    id: s1displaymasterstate

  - platform: homeassistant
    entity_id: sensor.sprinkler_s1_display_days
    id: s1displaydays

  - platform: homeassistant
    entity_id: sensor.sprinkler_s1_display_zones
    id: s1displayzones
    
  - platform: homeassistant
    entity_id: sensor.sprinkler_s2_display_master
    id: s2displaymasterstate
    
  - platform: homeassistant
    entity_id: sensor.sprinkler_s2_display_days
    id: s2displaydays
    
  - platform: homeassistant
    entity_id: sensor.sprinkler_s2_display_zones
    id: s2displayzones
    
  - platform: homeassistant
    entity_id: input_datetime.s1_starttime
    id: s1starttime
    
  - platform: homeassistant
    entity_id: input_datetime.s2_starttime
    id: s2starttime
    
  - platform: homeassistant
    entity_id: sensor.sprinkler_display_active_zone
    id: displayactivezone

  - platform: homeassistant
    entity_id: sensor.sprinkler_display_time_remaining
    id: displaytimeremaining

#################################
# Gets time from Home Assistant #
#################################
time:
  - platform: homeassistant
    id: homeassistant_time

sensor:
#####################################
# Gets precipitation values from HA #
#####################################
  - platform: homeassistant
    name: "Outside Precipitation"
    entity_id: sensor.home_precipitation
    unit_of_measurement: "in"
    accuracy_decimals: 0
    id: precipitation
    internal: true
    
##################################################
# Display fonts & glyphs used for ePaoer display #
##################################################
font:
  - file: 'fonts/Google_Sans_Bold.ttf'
    id: font1
    size: 14
    
  - file: 'fonts/Google_Sans_Bold.ttf'
    id: font4
    size: 12
    
  - file: 'fonts/Google_Sans_Medium.ttf'
    id: font2
    size: 12

  - file: 'fonts/Google_Sans_Medium.ttf'
    id: font_10
    size: 10
    
  - file: 'fonts/Google_Sans_Bold.ttf'
    id: font3
    size: 18

  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: display_icons_20
    size: 14
    glyphs:
      - "\U000F08BE" # 3 Line Wifi
      - "\U000F08BD" # 2 Line Wifi
      - "\U000F08BC" # 1 Line Wifi
      - "\U000F08BF" # 0 Line Wifi
      
  # Weather condition icons      
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: conditions_22
    size: 22
    glyphs:
      - "\U000F0594" # clear-night
      - "\U000F0590" # cloudy
      - "\U000F0595" # partlycloudy
      - "\U000F0591" # fog      
      - "\U000F0592" # hail
      - "\U000F0593" # lightning
      - "\U000F067E" # lightning-rainy
      - "\U000F0596" # pouring
      - "\U000F0597" # rainy
      - "\U000F0F36" # snowy
      - "\U000F067F" # snowy-rainy
      - "\U000F0599" # sunny
      - "\U000F059D" # windy
      - "\U000F059E" # windy-variant
      - "\U000F0F38" # exceptional 
      
  # Weather condition icons      
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: conditions_10
    size: 10
    glyphs:
      - "\U000F0596" # pouring

#########################################################################################
# Button Congiguration  - buttons connected to MCP23017 IO Expansion Board:             #
# https://www.waveshare.com/w/upload/8/8e/MCP2307_IO_Expansion_Board_User_Manual_EN.pdf #
# 4 wires needed - VCC, GND, SDA, and SCL                                               #
# 10k ohm resistors between expansion board and button                                  #
#########################################################################################

# I2C bus for Waveshare MCP23017 IO Expansion Board
i2c:
  sda: 21  #SDA blue wire
  scl: 22  #SCL yellow wire

# Configure board - address is default from the manual
mcp23017:
  - id: 'mcp23017_hub'
    address: 0x27
    
# Individual inputs
binary_sensor:
  - platform: gpio
    name: "Sprinkler Button 1"
    pin:
      mcp23xxx: mcp23017_hub
      number: 0  # to pin PA0
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_1
        - delay: 50ms
        - component.update: mydisplay
  
  - platform: gpio
    name: "Sprinkler Button 2"
    pin:
      mcp23xxx: mcp23017_hub
      number: 1  # to pin PA1
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_2
        - component.update: mydisplay

  - platform: gpio
    name: "Sprinkler Button 3"
    pin:
      mcp23xxx: mcp23017_hub
      number: 2  # to pin PA2
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_3
        - component.update: mydisplay

  - platform: gpio
    name: "Sprinkler Button 4"
    pin:
      mcp23xxx: mcp23017_hub
      number: 3  # to pin PA3
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_4
        - component.update: mydisplay

  - platform: gpio
    name: "Sprinkler Button 5"
    pin:
      mcp23xxx: mcp23017_hub
      number: 4  # to pin PA4
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_5
        - component.update: mydisplay
        
  - platform: gpio
    name: "Sprinkler Button 6"
    pin:
      mcp23xxx: mcp23017_hub
      number: 5  # to pin PA5
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_6
        - component.update: mydisplay
        
  - platform: gpio
    name: "Sprinkler Button 7"
    pin:
      mcp23xxx: mcp23017_hub
      number: 6  # to pin PA6
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_7
        - component.update: mydisplay

  - platform: gpio
    name: "Sprinkler Button 8"
    pin:
      mcp23xxx: mcp23017_hub
      number: 7  # to pin PA7
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_8
        - component.update: mydisplay
        
  - platform: gpio
    name: "Sprinkler Button 9"
    pin:
      mcp23xxx: mcp23017_hub
      number: 8  # to pin PB0
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_9
        - component.update: mydisplay
        
  - platform: gpio
    name: "Sprinkler Button 10"
    pin:
      mcp23xxx: mcp23017_hub
      number: 9  # to pin PB1
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_10
        - component.update: mydisplay
        
  - platform: gpio
    name: "Sprinkler Button 11"
    pin:
      mcp23xxx: mcp23017_hub
      number: 10  # to pin PB2
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_11
        - component.update: mydisplay

  - platform: gpio
    name: "Sprinkler Button 12"
    pin:
      mcp23xxx: mcp23017_hub
      number: 11  # to pin PB3
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: zone_12
        - component.update: mydisplay

##############################################################################
# The following 2 buttons change the state of the schedules.  A single press #
# toggles between on/off, and a double press triggers an immediate run.      #
# Pressing the buttons sent an event to Home Assistant, and an automation in #
# the sprinklers.yaml file handle the automation when the event is received. #
##############################################################################
        
  - platform: gpio
    name: "Sprinkler Schedule 1 Button"
    pin:
      mcp23xxx: mcp23017_hub
      number: 12  # to pin PB4
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_multi_click:
      - timing:
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at least 0.2s
        then:
          - logger.log: "Double Clicked 1"
          - homeassistant.event:
              event: esphome.sprinkler_s1_button
              data:
               title: dbl_click
      - timing:
          - ON for 1s to 4s
          - OFF for at least 0.5s
        then:
          - logger.log: "Single Long Clicked 1"
          - homeassistant.event:
              event: esphome.sprinkler_s1_button
              data:
               title: long_click
      - timing:
          - ON for at most 1s
          - OFF for at least 0.5s
        then:
          - logger.log: "Single Short Clicked 1"
          - homeassistant.event:
              event: esphome.sprinkler_s1_button
              data:
               title: short_click
  
  - platform: gpio
    name: "Sprinkler Schedule 2 Button"
    pin:
      mcp23xxx: mcp23017_hub
      number: 13  # to pin PB5
      mode:
        input: true
        pullup: true
      inverted: true
    filters:
      - delayed_on: 10ms
    on_multi_click:
      - timing:
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at least 0.2s
        then:
          - logger.log: "Double Clicked 2"
          - homeassistant.event:
              event: esphome.sprinkler_s2_button
              data:
               title: dbl_click
      - timing:
          - ON for 1s to 4s
          - OFF for at least 0.5s
        then:
          - logger.log: "Single Long Clicked 2"
          - homeassistant.event:
              event: esphome.sprinkler_s2_button
              data:
               title: long_click
      - timing:
          - ON for at most 1s
          - OFF for at least 0.5s
        then:
          - logger.log: "Single Short Clicked 2"
          - homeassistant.event:
              event: esphome.sprinkler_s2_button
              data:
               title: short_click

####################################################################
# Sprinkler Relays & LEDs                                          #
# Both the relays and the LEDs are using SN74HC595 shift registers #
# to expand the output GPIOs.                                      #
# 220 ohm resistor between shift register and LED - side (shorter) #
####################################################################

# Shift Register Configuration - Sprinkler Zones
sn74hc595:
  - id: 'sprinkler_sn74hc595'
    clock_pin: GPIO14 # white wire
    latch_pin: GPIO27 #blue wire
    data_pin: GPIO26 # yellow wire    
    sr_count: 2
    
# Shift Register Configuration - Sprinkler Zone LEDs
  - id: 'sprinkler_sn74hc595_led'
    clock_pin: GPIO25 # white wire    
    latch_pin: GPIO33 #blue wire
    data_pin: GPIO32 # yellow wire    

    sr_count: 2

ESPHome Device (Part 2 of 2)


# Define Sprinkler Zones - Shift Register starts with Pin #0
switch:
  - platform: gpio
    name: "Sprinkler Zone 1"
    icon: "mdi:sprinkler"
    id: zone_1
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 0
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              #- switch.turn_off: zone_1
              - switch.turn_off: zone_2
              - switch.turn_off: zone_3
              - switch.turn_off: zone_4
              - switch.turn_off: zone_5
              - switch.turn_off: zone_6
              - switch.turn_off: zone_7
              - switch.turn_off: zone_8
              - switch.turn_off: zone_9
              - switch.turn_off: zone_10
              - switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_1
        - globals.set:
            id: active_zone
            value: '1'
    on_turn_off:
        - switch.turn_off: led_1
        - globals.set:
            id: active_zone
            value: '99'

  - platform: gpio
    name: "Sprinkler Zone 2"
    icon: "mdi:sprinkler"
    id: zone_2
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 1
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              #- switch.turn_off: zone_2
              - switch.turn_off: zone_3
              - switch.turn_off: zone_4
              - switch.turn_off: zone_5
              - switch.turn_off: zone_6
              - switch.turn_off: zone_7
              - switch.turn_off: zone_8
              - switch.turn_off: zone_9
              - switch.turn_off: zone_10
              - switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_2
        - globals.set:
            id: active_zone
            value: '2'
    on_turn_off:
        - switch.turn_off: led_2
        - globals.set:
            id: active_zone
            value: '99'
        
  - platform: gpio
    name: "Sprinkler Zone 3"
    icon: "mdi:sprinkler"
    id: zone_3
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 2
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              - switch.turn_off: zone_2
              #- switch.turn_off: zone_3
              - switch.turn_off: zone_4
              - switch.turn_off: zone_5
              - switch.turn_off: zone_6
              - switch.turn_off: zone_7
              - switch.turn_off: zone_8
              - switch.turn_off: zone_9
              - switch.turn_off: zone_10
              - switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_3
        - globals.set:
            id: active_zone
            value: '3'
    on_turn_off:
        - switch.turn_off: led_3
        - globals.set:
            id: active_zone
            value: '99'

  - platform: gpio
    name: "Sprinkler Zone 4"
    icon: "mdi:sprinkler"
    id: zone_4
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 3
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              - switch.turn_off: zone_2
              - switch.turn_off: zone_3
              #- switch.turn_off: zone_4
              - switch.turn_off: zone_5
              - switch.turn_off: zone_6
              - switch.turn_off: zone_7
              - switch.turn_off: zone_8
              - switch.turn_off: zone_9
              - switch.turn_off: zone_10
              - switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_4
        - globals.set:
            id: active_zone
            value: '4'
    on_turn_off:
        - switch.turn_off: led_4
        - globals.set:
            id: active_zone
            value: '99'

  - platform: gpio
    name: "Sprinkler Zone 5"
    icon: "mdi:sprinkler"
    id: zone_5
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 4
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              - switch.turn_off: zone_2
              - switch.turn_off: zone_3
              - switch.turn_off: zone_4
              #- switch.turn_off: zone_5
              - switch.turn_off: zone_6
              - switch.turn_off: zone_7
              - switch.turn_off: zone_8
              - switch.turn_off: zone_9
              - switch.turn_off: zone_10
              - switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_5
        - globals.set:
            id: active_zone
            value: '5'
    on_turn_off:
        - switch.turn_off: led_5
        - globals.set:
            id: active_zone
            value: '99'

  - platform: gpio
    name: "Sprinkler Zone 6"
    icon: "mdi:sprinkler"
    id: zone_6
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 5
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              - switch.turn_off: zone_2
              - switch.turn_off: zone_3
              - switch.turn_off: zone_4
              - switch.turn_off: zone_5
              #- switch.turn_off: zone_6
              - switch.turn_off: zone_7
              - switch.turn_off: zone_8
              - switch.turn_off: zone_9
              - switch.turn_off: zone_10
              - switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_6
        - globals.set:
            id: active_zone
            value: '6'
    on_turn_off:
        - switch.turn_off: led_6
        - globals.set:
            id: active_zone
            value: '99'
            
  - platform: gpio
    name: "Sprinkler Zone 7"
    icon: "mdi:sprinkler"
    id: zone_7
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 6
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              - switch.turn_off: zone_2
              - switch.turn_off: zone_3
              - switch.turn_off: zone_4
              - switch.turn_off: zone_5
              - switch.turn_off: zone_6
              #- switch.turn_off: zone_7
              - switch.turn_off: zone_8
              - switch.turn_off: zone_9
              - switch.turn_off: zone_10
              - switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_7
        - globals.set:
            id: active_zone
            value: '7'
    on_turn_off:
        - switch.turn_off: led_7
        - globals.set:
            id: active_zone
            value: '99'

  - platform: gpio
    name: "Sprinkler Zone 8"
    icon: "mdi:sprinkler"
    id: zone_8
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 7
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              - switch.turn_off: zone_2
              - switch.turn_off: zone_3
              - switch.turn_off: zone_4
              - switch.turn_off: zone_5
              - switch.turn_off: zone_6
              - switch.turn_off: zone_7
              #- switch.turn_off: zone_8
              - switch.turn_off: zone_9
              - switch.turn_off: zone_10
              - switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_8
        - globals.set:
            id: active_zone
            value: '8'
    on_turn_off:
        - switch.turn_off: led_8
        - globals.set:
            id: active_zone
            value: '99'

  - platform: gpio
    name: "Sprinkler Zone 9"
    icon: "mdi:sprinkler"
    id: zone_9
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 8
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              - switch.turn_off: zone_2
              - switch.turn_off: zone_3
              - switch.turn_off: zone_4
              - switch.turn_off: zone_5
              - switch.turn_off: zone_6
              - switch.turn_off: zone_7
              - switch.turn_off: zone_8
              #- switch.turn_off: zone_9
              - switch.turn_off: zone_10
              - switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_9
        - globals.set:
            id: active_zone
            value: '9'
    on_turn_off:
        - switch.turn_off: led_9
        - globals.set:
            id: active_zone
            value: '99'

  - platform: gpio
    name: "Sprinkler Zone 10"
    icon: "mdi:sprinkler"
    id: zone_10
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 9
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              - switch.turn_off: zone_2
              - switch.turn_off: zone_3
              - switch.turn_off: zone_4
              - switch.turn_off: zone_5
              - switch.turn_off: zone_6
              - switch.turn_off: zone_7
              - switch.turn_off: zone_8
              - switch.turn_off: zone_9
              #- switch.turn_off: zone_10
              - switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_10
        - globals.set:
            id: active_zone
            value: '10'
    on_turn_off:
        - switch.turn_off: led_10
        - globals.set:
            id: active_zone
            value: '99'

  - platform: gpio
    name: "Sprinkler Zone 11"
    icon: "mdi:sprinkler"
    id: zone_11
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 10
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              - switch.turn_off: zone_2
              - switch.turn_off: zone_3
              - switch.turn_off: zone_4
              - switch.turn_off: zone_5
              - switch.turn_off: zone_6
              - switch.turn_off: zone_7
              - switch.turn_off: zone_8
              - switch.turn_off: zone_9
              - switch.turn_off: zone_10
              #- switch.turn_off: zone_11
              - switch.turn_off: zone_12
        - switch.turn_on: led_11
        - globals.set:
            id: active_zone
            value: '11'
    on_turn_off:
        - switch.turn_off: led_11
        - globals.set:
            id: active_zone
            value: '99'

  - platform: gpio
    name: "Sprinkler Zone 12"
    icon: "mdi:sprinkler"
    id: zone_12
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595
      number: 11
      inverted: false
      mode: output
    on_turn_on:
      then:
        - if:
            condition:
              lambda: |-
                return (id(active_zone) != 'z');
            then:
              - switch.turn_off: zone_1
              - switch.turn_off: zone_2
              - switch.turn_off: zone_3
              - switch.turn_off: zone_4
              - switch.turn_off: zone_5
              - switch.turn_off: zone_6
              - switch.turn_off: zone_7
              - switch.turn_off: zone_8
              - switch.turn_off: zone_9
              - switch.turn_off: zone_10
              - switch.turn_off: zone_11
              #- switch.turn_off: zone_12
        - switch.turn_on: led_12
        - globals.set:
            id: active_zone
            value: '12'
    on_turn_off:
        - switch.turn_off: led_12
        - globals.set:
            id: active_zone
            value: '99'      

###################################################################
# Define Sprinkler Zone LEDs - Shift Register starts with Pin #0  #
# On the physical board, LEDs are using 220 Ohm resistors         #
###################################################################
  - platform: gpio
    name: "Sprinkler Zone 1 LED"
    icon: "mdi:led-outline"
    id: led_1
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 0
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 2 LED"
    icon: "mdi:led-outline"
    id: led_2
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 1
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 3 LED"
    icon: "mdi:led-outline"
    id: led_3
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 2
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 4 LED"
    icon: "mdi:led-outline"
    id: led_4
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 3
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 5 LED"
    icon: "mdi:led-outline"
    id: led_5
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 4
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 6 LED"
    icon: "mdi:led-outline"
    id: led_6
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 5
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 7 LED"
    icon: "mdi:led-outline"
    id: led_7
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 6
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 8 LED"
    icon: "mdi:led-outline"
    id: led_8
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 7
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 9 LED"
    icon: "mdi:led-outline"
    id: led_9
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 8
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 10 LED"
    icon: "mdi:led-outline"
    id: led_10
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 9
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 11 LED"
    icon: "mdi:led-outline"
    id: led_11
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 10
      inverted: false
  - platform: gpio
    name: "Sprinkler Zone 12 LED"
    icon: "mdi:led-outline"
    id: led_12
    restore_mode: ALWAYS_OFF
    pin:
      sn74hc595: sprinkler_sn74hc595_led
      number: 11
      inverted: false


##########################################################
# Display Configuration - using Waveshare 2.9" V2 ePaper #
##########################################################

#  SPI bus for Waveshare epaper display
spi:
  clk_pin: GPIO18 #yellow wire
  mosi_pin: GPIO23 #blue wire (din)

display:
  - platform: waveshare_epaper
    id: mydisplay
    rotation: 270
    #reset_duration: 200ms
    reset_pin: GPIO16 #(RX2) white wire - optional
    dc_pin: GPIO17 #(TX2) green wire
    cs_pin: GPIO05 #orange wire
    busy_pin: GPIO19 #purple wire - optional

    model: 2.90inv2 #296 x 128 pixels
    #update_interval: 30s
    #full_update_every: 1
    reset_duration: 2ms
    update_interval: 1s
    full_update_every: 3600
    
    lambda: |-
      // Prints Date and Time in top left corner with rectangle box
      it.strftime(3,2, id(font4), "%a %b %d %I:%M %p", id(homeassistant_time).now());
      it.printf(160,2, id(font4), "%s", id(displaystatus).state.c_str());
      it.rectangle(0, 0, 296, 18);
      
      // Prints WiFi Strength
      if (id(wifi_level).state >= -50) {
      it.printf(290, 9, id(display_icons_20), TextAlign::CENTER_RIGHT, "\U000F08BE");}
      if (id(wifi_level).state >= -60) {
      it.printf(290, 9, id(display_icons_20), TextAlign::CENTER_RIGHT, "\U000F08BD");}
      if (id(wifi_level).state >= -70) {
      it.printf(290, 9, id(display_icons_20), TextAlign::CENTER_RIGHT, "\U000F08BC");}
      if (id(wifi_level).state >= -90) {
      it.printf(290, 9, id(display_icons_20), TextAlign::CENTER_RIGHT, "\U000F08BF");}
      
      // Schedule Header
      it.print(4, 19, id(font1), "Schedule");
      it.line(2, 36, 72, 36); 
      it.print(152,19, id(font1), "Days / Zones");
      it.line(120, 36, 294, 36); 
      
      // Schedule 1
      it.print(4,44, id(font3), "1");
      it.printf(22, 39, id(font2), "%s", id(s1displaymasterstate).state.c_str());
      it.printf(140,39, id(font2), "%s", id(s1displaydays).state.c_str());
      it.printf(22,55, id(font2), "%s", id(s1starttime).state.c_str());
      it.printf(120, 55, id(font2), "%s",id(s1displayzones).state.c_str());
      
      it.line(2, 71, 294, 71);
      
      // Schedule 2
      it.print(4, 78, id(font3), "2");
      it.printf(22, 73, id(font2), "%s", id(s2displaymasterstate).state.c_str());
      it.printf(138, 73, id(font2), "%s",id(s2displaydays).state.c_str());
      it.printf(22,89, id(font2), "%s", id(s2starttime).state.c_str());
      it.printf(120, 89, id(font2), "%s",id(s2displayzones).state.c_str());
      
      it.rectangle(0, 106, 296, 22); 
      
      // Weather Condition
      if (id(weather_icon).state == "clear-night") {
      it.printf(3, 107, id(conditions_22), "\U000F0594");}
      if (id(weather_icon).state == "cloudy") {
      it.printf(3, 107, id(conditions_22), "\U000F0590");}
      if (id(weather_icon).state == "partlycloudy") {
      it.printf(3, 107, id(conditions_22), "\U000F0595");}
      if (id(weather_icon).state == "fog") {
      it.printf(3, 107, id(conditions_22), "\U000F0591");}
      if (id(weather_icon).state == "hail") {
      it.printf(3, 107, id(conditions_22), "\U000F0592");}
      if (id(weather_icon).state == "lightning") {
      it.printf(3, 107, id(conditions_22), "\U000F0593");}
      if (id(weather_icon).state == "lightning-rainy") {
      it.printf(3, 107, id(conditions_22), "\U000F067E");}
      if (id(weather_icon).state == "pouring") {
      it.printf(3, 107, id(conditions_22), "\U000F0596");}
      if (id(weather_icon).state == "rainy") {
      it.printf(3, 107, id(conditions_22), "\U000F0597");}
      if (id(weather_icon).state == "snowy") {
      it.printf(3, 107, id(conditions_22), "\U000F0F36");}
      if (id(weather_icon).state == "snowy-rainy") {
      it.printf(3, 107, id(conditions_22), "\U000F067F");}
      if (id(weather_icon).state == "sunny") {
      it.printf(3, 107, id(conditions_22), "\U000F0599");}
      if (id(weather_icon).state == "windy") {
      it.printf(3, 107, id(conditions_22), "\U000F059D");}
      if (id(weather_icon).state == "windy-variant") {
      it.printf(3, 107, id(conditions_22), "\U000F059E");}
      if (id(weather_icon).state == "exceptional") {
      it.printf(3, 107, id(conditions_22), "\U000F0F38");} 
      
      it.print(31, 110, id(font2), "Precip:");
      it.printf(75, 108, id(font1), "%.2f", id(precipitation).state);  //Need to fix - set up Visual Crossing WeatherToday
      it.print(109, 112, id(font_10), "in");
      
      // Active Zone & Time Remaining
      it.print(123, 110, id(font2), "Act Zone:");
      it.printf(185, 108, id(font1), "%s", id(displayactivezone).state.c_str());
      //it.print(185, 108, id(font1), "??");        //Need to fix
      it.print(206, 110, id(font2), "Mins Rem:");
      it.printf(270, 108, id(font1), "%s", id(displaytimeremaining).state.c_str());
      //it.print(270, 108, id(font1), "??");        //Need to fix

Dashboard code:

 - title: Sprinklers
    path: sprinklers
    badges: []
    cards:
      - type: grid
        title: Sprinklers
        cards:
          - type: entity
            entity: input_text.sprinkler_status
            name: Status
          - type: grid
            cards:
              - type: entity
                entity: sensor.sprinkler_display_active_zone
                name: Active Zone
              - type: entity
                entity: sensor.sprinkler_display_time_remaining
                name: Time Remaining
            columns: 2
            square: false
          - type: entities
            entities:
              - entity: switch.sprinkler_zone_1
              - entity: switch.sprinkler_zone_2
              - entity: switch.sprinkler_zone_3
              - entity: switch.sprinkler_zone_4
              - entity: switch.sprinkler_zone_5
              - entity: switch.sprinkler_zone_6
              - entity: switch.sprinkler_zone_7
              - entity: switch.sprinkler_zone_8
              - entity: switch.sprinkler_zone_9
              - entity: switch.sprinkler_zone_10
              - entity: switch.sprinkler_zone_11
              - entity: switch.sprinkler_zone_12
            title: Sprinkler Valves
            show_header_toggle: false
        columns: 1
        square: false
        card_mod:
          style: |
            .ha-card {
              font-size: 30px;
            }
      - type: grid
        title: Schedule 1
        cards:
          - type: grid
            cards:
              - type: entities
                title: Active / Inactive
                entities:
                  - entity: input_boolean.sprinkler_s1_master
                    name: ' '
                    icon: mdi:switch
                card_mod:
                  style: |
                    .card-header {
                      font-size: 18px;
                    }
              - type: entities
                title: Start Time
                entities:
                  - entity: input_datetime.s1_starttime
                    name: ' '
                    icon: mdi:clock
                card_mod:
                  style: |
                    .card-header {
                      font-size: 18px;
                    }
            columns: 2
            square: false
          - type: grid
            cards:
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_day_1m
                name: M
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_day_2t
                name: T
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_day_3w
                name: W
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_day_4th
                name: Th
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_day_5f
                name: F
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_day_6sat
                name: Sat
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_day_7sun
                name: Sun
                tap_action:
                  action: toggle
            columns: 7
          - type: grid
            cards:
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_1
                name: 1
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_2
                name: 2
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_3
                name: 3
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_4
                name: 4
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_5
                name: 5
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_6
                name: 6
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_7
                name: 7
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_8
                name: 8
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_9
                name: 9
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_10
                name: 10
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_11
                name: 11
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s1_zone_12
                name: 12
                tap_action:
                  action: toggle
            columns: 12
          - type: entities
            entities:
              - entity: input_number.sprinkler_s1_z1_runtime
                name: Zone 1 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_1', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z2_runtime
                name: Zone 2 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_2', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z3_runtime
                name: Zone 3 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_3', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z4_runtime
                name: Zone 4 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_4', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z5_runtime
                name: Zone 5 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_5', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z6_runtime
                name: Zone 6 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_6', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z7_runtime
                name: Zone 7 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_7', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z8_runtime
                name: Zone 8 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_8', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z9_runtime
                name: Zone 9 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_9', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z10_runtime
                name: Zone 10 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_10', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z11_runtime
                name: Zone 11 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_11', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s1_z12_runtime
                name: Zone 12 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s1_zone_12', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
        columns: 1
        square: false
      - type: grid
        title: Schedule 2
        cards:
          - type: grid
            cards:
              - type: entities
                title: Active / Inactive
                entities:
                  - entity: input_boolean.sprinkler_s2_master
                    name: ' '
                    icon: mdi:switch
                card_mod:
                  style: |
                    .card-header {
                      font-size: 18px;
                     }
              - type: entities
                title: Start Time
                entities:
                  - entity: input_datetime.s2_starttime
                    name: ' '
                    icon: mdi:clock
                card_mod:
                  style: |
                    .card-header {
                     font-size: 18px;
                    }
            columns: 2
            square: false
          - type: grid
            cards:
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_day_1m
                name: M
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_day_2t
                name: T
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_day_3w
                name: W
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_day_4th
                name: Th
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_day_5f
                name: F
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_day_6sat
                name: Sat
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_day_7sun
                name: Sun
                tap_action:
                  action: toggle
            columns: 7
          - type: grid
            cards:
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_1
                name: 1
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_2
                name: 2
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_3
                name: 3
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_4
                name: 4
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_5
                name: 5
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_6
                name: 6
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_7
                name: 7
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_8
                name: 8
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_9
                name: 9
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_10
                name: 10
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_11
                name: 11
                tap_action:
                  action: toggle
              - type: custom:button-card
                entity: input_boolean.sprinkler_s2_zone_12
                name: 12
                tap_action:
                  action: toggle
            columns: 12
          - type: entities
            entities:
              - entity: input_number.sprinkler_s2_z1_runtime
                name: Zone 1 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_1', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z2_runtime
                name: Zone 2 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_2', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z3_runtime
                name: Zone 3 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_3', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z4_runtime
                name: Zone 4 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_4', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z5_runtime
                name: Zone 5 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_5', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z6_runtime
                name: Zone 6 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_6', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z7_runtime
                name: Zone 7 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_7', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z8_runtime
                name: Zone 8 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_8', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z9_runtime
                name: Zone 9 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_9', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z10_runtime
                name: Zone 10 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_10', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z11_runtime
                name: Zone 11 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_11', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
              - entity: input_number.sprinkler_s2_z12_runtime
                name: Zone 12 Run Time
                card_mod:
                  style: |
                    :host {
                     opacity:
                     {% if is_state('input_boolean.sprinkler_s2_zone_12', 'on') %}
                       100%
                     {% else %}
                       20%
                     {% endif %}
                    }
        columns: 1
        square: false

Finally, I also have the following in a yaml package in my HA config folder…

#####################################################
# Start time and master switch for schedule 1 and 2 #
# Used in Lovelace to set the start time and to     #
# turn schedule 1 and 2 on or off                   #
# Overall sprinkler status.                         #
# Run now sensors - triggered from physical button  #
# long press on ESPHome device.                     #
# All off sensors - triggered from physical button  #
# double press on ESPHome device.                   #
#####################################################
input_datetime:
  s1_starttime: # Start time for schedule 1
    name: s1_starttime
    has_date: false
    has_time: true

  s2_starttime: # Start time for schedule 2
    name: s2_starttime
    has_date: false
    has_time: true

input_text:  
 sprinkler_status: # Idle, Sched 1 Running, Sched 2 Running, Manual
    name: sprinkler_status
    icon: mdi:sprinkler

input_boolean:
  sprinkler_s1_master: # On/Off for schedule 1
    name: sprinkler_s1_master
    icon: mdi:calendar

  sprinkler_s2_master: # On/Off for schedule 2
    name: sprinkler_s2_master
    icon: mdi:calendar

  sprinkler_s1_runnow: # Run now for schedule 1
    name: sprinkler_s1_runnow

  sprinkler_s2_runnow: # Run now for schedule 2
    name: sprinkler_s2_runnow

  sprinkler_s1_alloff: # All off for schedule 1 - to stop if running
    name: sprinkler_s1_alloff

  sprinkler_s2_alloff: # All off for schedule 2 - to stop if running
    name: sprinkler_s2_alloff

  ###############################################################
  # Schedule 1 - Days of week                                   #
  # Used in Lovelace to select which days to run for schedule 1 #
  ###############################################################
  sprinkler_s1_day_1m:
    name: sprinkler_s1_day_1m
    icon: mdi:calendar-check
  sprinkler_s1_day_2t:
    name: sprinkler_s1_day_2t
    icon: mdi:calendar-check
  sprinkler_s1_day_3w:
    name: sprinkler_s1_day_3w
    icon: mdi:calendar-check
  sprinkler_s1_day_4th:
    name: sprinkler_s1_day_4th
    icon: mdi:calendar-check
  sprinkler_s1_day_5f:
    name: sprinkler_s1_day_5f
    icon: mdi:calendar-check
  sprinkler_s1_day_6sat:
    name: sprinkler_s1_day_6sat
    icon: mdi:calendar-check
  sprinkler_s1_day_7sun:
    name: sprinkler_s1_day_7sun
    icon: mdi:calendar-check

  ###############################################################
  # Schedule 2 - Days of week                                   #
  # Used in Lovelace to select which days to run for schedule 2 #
  ###############################################################
  sprinkler_s2_day_1m:
    name: sprinkler_s2_day_1m
    icon: mdi:calendar-check
  sprinkler_s2_day_2t:
    name: sprinkler_s2_day_2t
    icon: mdi:calendar-check
  sprinkler_s2_day_3w:
    name: sprinkler_s2_day_3w
    icon: mdi:calendar-check
  sprinkler_s2_day_4th:
    name: sprinkler_s2_day_4th
    icon: mdi:calendar-check
  sprinkler_s2_day_5f:
    name: sprinkler_s2_day_5f
    icon: mdi:calendar-check
  sprinkler_s2_day_6sat:
    name: sprinkler_s2_day_6sat
    icon: mdi:calendar-check
  sprinkler_s2_day_7sun:
    name: sprinkler_s2_day_7sun
    icon: mdi:calendar-check

  ################################################################
  # Schedule 1 - Zones                                           #
  # Used in Lovelace to select which zones to run for schedule 1 #
  ################################################################
  sprinkler_s1_zone_1:
    name: sprinkler_s1_zone_1
    icon: mdi:water-circle
  sprinkler_s1_zone_2:
    name: sprinkler_s1_zone_2
    icon: mdi:water-circle
  sprinkler_s1_zone_3:
    name: sprinkler_s1_zone_3
    icon: mdi:water-circle
  sprinkler_s1_zone_4:
    name: sprinkler_s1_zone_4
    icon: mdi:water-circle
  sprinkler_s1_zone_5:
    name: sprinkler_s1_zone_5
    icon: mdi:water-circle
  sprinkler_s1_zone_6:
    name: sprinkler_s1_zone_6
    icon: mdi:water-circle
  sprinkler_s1_zone_7:
    name: sprinkler_s1_zone_7
    icon: mdi:water-circle
  sprinkler_s1_zone_8:
    name: sprinkler_s1_zone_8
    icon: mdi:water-circle
  sprinkler_s1_zone_9:
    name: sprinkler_s1_zone_9
    icon: mdi:water-circle
  sprinkler_s1_zone_10:
    name: sprinkler_s1_zone_10
    icon: mdi:water-circle
  sprinkler_s1_zone_11:
    name: sprinkler_s1_zone_11
    icon: mdi:water-circle
  sprinkler_s1_zone_12:
    name: sprinkler_s1_zone_12
    icon: mdi:water-circle

  ################################################################
  # Schedule 2 - Zones                                           #
  # Used in Lovelace to select which zones to run for schedule 2 #
  ################################################################
  sprinkler_s2_zone_1:
    name: sprinkler_s2_zone_1
    icon: mdi:water-circle
  sprinkler_s2_zone_2:
    name: sprinkler_s2_zone_2
    icon: mdi:water-circle
  sprinkler_s2_zone_3:
    name: sprinkler_s2_zone_3
    icon: mdi:water-circle
  sprinkler_s2_zone_4:
    name: sprinkler_s2_zone_4
    icon: mdi:water-circle
  sprinkler_s2_zone_5:
    name: sprinkler_s2_zone_5
    icon: mdi:water-circle
  sprinkler_s2_zone_6:
    name: sprinkler_s2_zone_6
    icon: mdi:water-circle
  sprinkler_s2_zone_7:
    name: sprinkler_s2_zone_7
    icon: mdi:water-circle
  sprinkler_s2_zone_8:
    name: sprinkler_s2_zone_8
    icon: mdi:water-circle
  sprinkler_s2_zone_9:
    name: sprinkler_s2_zone_9
    icon: mdi:water-circle
  sprinkler_s2_zone_10:
    name: sprinkler_s2_zone_10
    icon: mdi:water-circle
  sprinkler_s2_zone_11:
    name: sprinkler_s2_zone_11
    icon: mdi:water-circle
  sprinkler_s2_zone_12:
    name: sprinkler_s2_zone_12
    icon: mdi:water-circle

########################################################################
# Used for time remaining calculation.  Value set in Node Ref flow     #
# sensor.sprinkler_time_remaining defined below is integer value       #
########################################################################
input_number:
  sprinkler_time_remaining:
    name: sprinkler_time_remaining
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer-sand-complete

  ##############################
  # Schedule 1 runtime sliders #
  # Used in Lovelace           #
  ##############################
  sprinkler_s1_z1_runtime:
    name: sprinkler_s1_z1_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z2_runtime:
    name: sprinkler_s1_z2_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z3_runtime:
    name: sprinkler_s1_z3_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z4_runtime:
    name: sprinkler_s1_z4_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z5_runtime:
    name: sprinkler_s1_z5_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z6_runtime:
    name: sprinkler_s1_z6_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z7_runtime:
    name: sprinkler_s1_z7_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z8_runtime:
    name: sprinkler_s1_z8_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z9_runtime:
    name: sprinkler_s1_z9_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z10_runtime:
    name: sprinkler_s1_z10_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z11_runtime:
    name: sprinkler_s1_z11_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s1_z12_runtime:
    name: sprinkler_s1_z12_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer

  ##############################
  # Schedule 2 runtime sliders #
  # Used in Lovelace           #
  ##############################
  sprinkler_s2_z1_runtime:
    name: sprinkler_s2_z1_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z2_runtime:
    name: sprinkler_s2_z2_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z3_runtime:
    name: sprinkler_s2_z3_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z4_runtime:
    name: sprinkler_s2_z4_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z5_runtime:
    name: sprinkler_s2_z5_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z6_runtime:
    name: sprinkler_s2_z6_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z7_runtime:
    name: sprinkler_s2_z7_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z8_runtime:
    name: sprinkler_s2_z8_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z9_runtime:
    name: sprinkler_s2_z9_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z10_runtime:
    name: sprinkler_s2_z10_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z11_runtime:
    name: sprinkler_s2_z11_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer
  sprinkler_s2_z12_runtime:
    name: sprinkler_s2_z12_runtime
    min: 0
    max: 60
    step: 1
    unit_of_measurement: minutes
    icon: mdi:timer

###################################
# Sensors used for ePaper display #
###################################
sensor:
  - platform: template
    sensors:
      sprinkler_display_time_remaining: # Used in Lovelace and for ePaper display - removes the decimal and displays the integer
        unit_of_measurement: minutes
        value_template: >-
          {{ (states('input_number.sprinkler_time_remaining') | int) }}

      sprinkler_s1_display_master: # Used for ePaper display 
        value_template: >-
          {% if is_state('input_boolean.sprinkler_s1_master', 'on') %}
            ACTIVE
          {% else %}
            INACTIVE
          {% endif %}

      sprinkler_s2_display_master: # Used for ePaper display
        value_template: >-
          {% if is_state('input_boolean.sprinkler_s2_master', 'on') %}
            ACTIVE
          {% else %}
            INACTIVE
          {% endif %}

      sprinkler_s1_display_days: # Used for ePaper display
        value_template: >-
          {{ iif(is_state('input_boolean.sprinkler_s1_day_1m', 'on'), 'M ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_day_2t', 'on'), 'T ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_day_3w', 'on'), 'W ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_day_4th', 'on'), 'Th ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_day_5f', 'on'), 'F ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_day_6sat', 'on'), 'Sat ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_day_7sun', 'on'), 'Sun ', '- ') }}

      sprinkler_s2_display_days: # Used for ePaper display
         value_template: >-
          {{ iif(is_state('input_boolean.sprinkler_s2_day_1m', 'on'), 'M ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_day_2t', 'on'), 'T ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_day_3w', 'on'), 'W ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_day_4th', 'on'), 'Th ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_day_5f', 'on'), 'F ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_day_6sat', 'on'), 'Sat ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_day_7sun', 'on'), 'Sun ', '- ') }}

      sprinkler_s1_display_zones: # Used for ePaper display
         value_template: >-
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_1', 'on'), '1 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_2', 'on'), '2 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_3', 'on'), '3 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_4', 'on'), '4 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_5', 'on'), '5 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_6', 'on'), '6 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_7', 'on'), '7 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_8', 'on'), '8 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_9', 'on'), '9 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_10', 'on'), '10 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_11', 'on'), '11 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s1_zone_12', 'on'), '12 ', '- ') }}

      sprinkler_s2_display_zones: # Used for ePaper display
         value_template: >-
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_1', 'on'), '1 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_2', 'on'), '2 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_3', 'on'), '3 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_4', 'on'), '4 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_5', 'on'), '5 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_6', 'on'), '6 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_7', 'on'), '7 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_8', 'on'), '8 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_9', 'on'), '9 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_10', 'on'), '10 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_11', 'on'), '11 ', '- ') }}
          {{ iif(is_state('input_boolean.sprinkler_s2_zone_12', 'on'), '12 ', '- ') }}

      sprinkler_display_active_zone: # Used in Lovelace and for ePaper display
        value_template: >-
          {% if is_state('switch.sprinkler_zone_1', 'on') %} 1
          {% elif is_state('switch.sprinkler_zone_2', 'on') %} 2
          {% elif is_state('switch.sprinkler_zone_3', 'on') %} 3
          {% elif is_state('switch.sprinkler_zone_4', 'on') %} 4
          {% elif is_state('switch.sprinkler_zone_5', 'on') %} 5
          {% elif is_state('switch.sprinkler_zone_6', 'on') %} 6
          {% elif is_state('switch.sprinkler_zone_7', 'on') %} 7
          {% elif is_state('switch.sprinkler_zone_8', 'on') %} 8
          {% elif is_state('switch.sprinkler_zone_9', 'on') %} 9
          {% elif is_state('switch.sprinkler_zone_10', 'on') %} 10
          {% elif is_state('switch.sprinkler_zone_11', 'on') %} 11
          {% elif is_state('switch.sprinkler_zone_12', 'on') %} 12
          {% else %} -
          {% endif %}

      #sprinkler_active_zone_runtime: # Not used
      #  value_template: >-
      #    {% if is_state('switch.sprinkler_zone_1', 'on') %} states["input_number.sprinkler_s1_z1_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_2', 'on') %} states["input_number.sprinkler_s1_z2_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_3', 'on') %} states["input_number.sprinkler_s1_z3_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_4', 'on') %} states["input_number.sprinkler_s1_z4_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_5', 'on') %} states["input_number.sprinkler_s1_z5_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_6', 'on') %} states["input_number.sprinkler_s1_z6_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_7', 'on') %} states["input_number.sprinkler_s1_z7_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_8', 'on') %} states["input_number.sprinkler_s1_z8_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_9', 'on') %} states["input_number.sprinkler_s1_z9_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_10', 'on') %} states["input_number.sprinkler_s1_z10_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_11', 'on') %} states["input_number.sprinkler_s1_z11_runtime"].state
      #    {% elif is_state('switch.sprinkler_zone_12', 'on') %} states["input_number.sprinkler_s1_z12_runtime"].state
      #    {% else %} 0
      #    {% endif %}


##################
# Customizations #
##################
homeassistant:
  customize:
    sensor.sprinkler_display_time_remaining:
      icon: mdi:timer-sand-complete
    
    sensor.sprinkler_display_active_zone:
      icon: mdi:sprinkler

########################################################
# Automations for pressing physical controller buttons #
# Single click toggles schedule on/off                 #
# Double click triggers a "run now" of the schedule    #
# Long click turns all zones off.                      #
########################################################
automation: 
  - alias: "Sprinkler Sched 1 On/Off" # Sets Schedule 1 on or off on button click
    trigger:
      - platform: event
        event_type: esphome.sprinkler_s1_button
        event_data:
          title: short_click
    action:
      service: input_boolean.toggle
      data: {}
      target:
        entity_id: input_boolean.sprinkler_s1_master

  - alias: "Sprinkler Sched 2 On/Off" # Sets Schedule 1 on or off on button click
    trigger:
      - platform: event
        event_type: esphome.sprinkler_s2_button
        event_data:
          title: short_click
    action:
      service: input_boolean.toggle
      data: {}
      target:
        entity_id: input_boolean.sprinkler_s2_master

  - alias: "Sprinkler Sched 1 Run Now" # Starts a run now of schedule 1
    trigger:
      - platform: event
        event_type: esphome.sprinkler_s1_button
        event_data:
          title: dbl_click
    action:
      - service: input_boolean.turn_on
        data: {}
        target:
          entity_id: input_boolean.sprinkler_s1_runnow
      - delay:
          milliseconds: 500
      - service: input_boolean.turn_off
        data: {}
        target:
          entity_id: input_boolean.sprinkler_s1_runnow

  - alias: "Sprinkler Sched 2 Run Now" # Starts a run now of schedule 2
    trigger:
      - platform: event
        event_type: esphome.sprinkler_s2_button
        event_data:
          title: dbl_click
    action:
      - service: input_boolean.turn_on
        data: {}
        target:
          entity_id: input_boolean.sprinkler_s2_runnow
      - delay:
          milliseconds: 500
      - service: input_boolean.turn_off
        data: {}
        target:
          entity_id: input_boolean.sprinkler_s2_runnow

  - alias: "Sprinkler Sched 1 All Off" # Turns all zones off
    trigger:
      - platform: event
        event_type: esphome.sprinkler_s1_button
        event_data:
          title: long_click
    action:
      - service: input_boolean.turn_on
        data: {}
        target:
          entity_id: input_boolean.sprinkler_s1_alloff
      - delay:
          milliseconds: 500
      - service: input_boolean.turn_off
        data: {}
        target:
          entity_id: input_boolean.sprinkler_s1_alloff

  - alias: "Sprinkler Sched 2 All Off"  # Turns all zones off
    trigger:
      - platform: event
        event_type: esphome.sprinkler_s2_button
        event_data:
          title: long_click
    action:
      - service: input_boolean.turn_on
        data: {}
        target:
          entity_id: input_boolean.sprinkler_s2_alloff
      - delay:
          milliseconds: 500
      - service: input_boolean.turn_off
        data: {}
        target:
          entity_id: input_boolean.sprinkler_s2_alloff

  #- alias: 'who turned on the sprinkler?' # Determines how sprinkler was turned on.
  #  description: ''
  #  trigger:
  #    - platform: state
  #      entity_id: switch.sprinkler_zone_1
  #  condition: []
  #  action:
  #    - choose:
  #        - conditions:
  #            - condition: template
  #              value_template: '{{ trigger.to_state.context.user_id == none }}'
  #            - condition: template
  #              value_template: '{{ trigger.to_state.context.parent_id == none }}'
  #          sequence:
  #            - service: notify.notify
  #              data:
  #                message: The sprinkler was turned on by a button
  #        - conditions:
  #            - condition: template
  #              value_template: '{{ trigger.to_state.context.user_id == none }}'
  #          sequence:
  #            - service: notify.notify
  #              data:
  #                message: The sprinkler was turned on the Home Assistant
  #        - conditions:
  #            - condition: template
  #              value_template: '{{ trigger.to_state.context.user_id != none }}'
  #          sequence:
  #            - service: notify.notify
  #              data:
  #                message: The sprinkler was turned on by a user
  #      default: []
  #  mode: single