Simple swing gate controller made smart with Shelly i4 and esphome

There are a few examples of making gate controller smart. It is especially important to be able to safely open or close the gate remotely e.g. via google assistant without seeing the state of the gate. A simple controller with step-by-step control does not allow to just close or just open the gate, it will act depending on the previous state of the gate. By adding Shelly Plus i4 I’m able to know if the gate is closing or opening, with additional reed switch I know when it is fully closed and based on the time it takes to open I know when it is fully open. Controlling the gate is simple, I’ve connected the external switch input to an esp8266 board running esphome. Using the same board to connect the reed switch for the fully closed position. With that I’ve configured the cover component in esphome:

button:
  - platform: template
    name: Gate
    icon: "mdi:autorenew"
    id: gate
    on_press:
      - switch.turn_on: d5_switch
      - delay: 1000ms
      - switch.turn_off: d5_switch
switch:
  - platform: gpio
    pin: 14
    name: "Gate GPIO D5"
    id: d5_switch
binary_sensor:
  - platform: gpio
    pin: 4 #D2
    name: "${friendly_name} closed"
    id: gate_closed
cover:
  - platform: template
    name: "Brama"
    device_class: "gate"
    lambda: |-
      if (id(gate_closed).state) {
        return COVER_CLOSED;
      } else {
        return COVER_OPEN;
      }
    open_action:
      - button.press: gate
    close_action:
      - button.press: gate
    stop_action:
      - button.press: gate

Shelly Plus i4 is connected as per below diagram:

Shelly can’t be directly connected to the motor phase wires as it will heat a lot and won’t sense the state correctly. To make it possible a resistor of approximately 500kΩ to 1MΩ in series to Shelly‘s input has to be used. With such connection the state of inputs in Shelly i4 is reversed (when motors are running it shows OFF and when motors are off the inputs are in ON state). The logic can be reversed in Shelly i4 configuration. I have used the following resistors soldered in line with connecting cables:
Resistor: thin film;SMD;0207 MELF;560kΩ;1W;±1%;Ø2,2x5,9mm
Manufacturer: Viking; OEM nr: CSRV0207FTDT5603;

Now having all the required information I have created a template cover in HA which is more smart than just the cover from esphome:

cover:
  - platform: template
    covers:
      gate:
        device_class: gate
        friendly_name: "Gate"
        value_template: >
          {% if states("binary_sensor.gate_left_closing") == "on" or states("binary_sensor.gate_right_closing") == "on" %}
            closing
          {% elif  states("binary_sensor.gate_left_opening") == "on" or states("binary_sensor.gate_right_opening") == "on" %}
            opening
          {% elif states("cover.brama") == "closed" %}
            closed
          {% else %}
            open
          {% endif %}
        position_template: >
          {% if states("cover.brama") == "closed" %}
            0
          {% elif states("input_boolean.gate_open") == "on" %}
            100
          {% else %}
            50
          {% endif %}     
        open_cover:
          service: script.gate_open
        close_cover:
          service: script.gate_close
        stop_cover:
          service: script.gate_stop

and the 3 scripts to control the gate:

gate_close:
  alias: Gate Close
  sequence:
  - choose:
    - conditions:
      - condition: state
        entity_id: cover.gate
        state: open
      - condition: state
        entity_id: input_select.gate_last_action
        state: opening
      sequence:
      - service: cover.close_cover
        data: {}
        target:
          entity_id: cover.brama
    - conditions:
      - condition: state
        entity_id: cover.gate
        state: opening
      sequence:
      - service: cover.stop_cover
        data: {}
        target:
          entity_id: cover.brama
      - delay:
          hours: 0
          minutes: 0
          seconds: 2
          milliseconds: 0
      - service: cover.close_cover
        data: {}
        target:
          entity_id: cover.brama
    - conditions:
      - condition: state
        entity_id: cover.gate
        state: open
      - condition: state
        entity_id: input_select.gate_last_action
        state: closing
      sequence:
      - service: cover.stop_cover
        data: {}
        target:
          entity_id: cover.brama
      - delay:
          hours: 0
          minutes: 0
          seconds: 2
          milliseconds: 0
      - service: cover.stop_cover
        data: {}
        target:
          entity_id: cover.brama
      - delay:
          hours: 0
          minutes: 0
          seconds: 2
          milliseconds: 0
      - service: cover.close_cover
        data: {}
        target:
          entity_id: cover.brama
    default: []
  mode: restart
  icon: mdi:gate
gate_open:
  alias: Gate Open
  sequence:
  - choose:
    - conditions:
      - condition: state
        entity_id: cover.gate
        state: closed
      - condition: state
        entity_id: input_select.gate_last_action
        state: closing
      sequence:
      - service: cover.open_cover
        data: {}
        target:
          entity_id: cover.brama
    - conditions:
      - condition: state
        entity_id: cover.gate
        state: closing
      sequence:
      - service: cover.stop_cover
        data: {}
        target:
          entity_id: cover.brama
      - delay:
          hours: 0
          minutes: 0
          seconds: 2
          milliseconds: 0
      - service: cover.open_cover
        data: {}
        target:
          entity_id: cover.brama
    - conditions:
      - condition: state
        entity_id: cover.gate
        state: open
      - condition: state
        entity_id: input_select.gate_last_action
        state: opening
      - condition: numeric_state
        entity_id: cover.gate
        attribute: current_position
        below: '100'
      sequence:
      - service: cover.stop_cover
        data: {}
        target:
          entity_id: cover.brama
      - delay:
          hours: 0
          minutes: 0
          seconds: 2
          milliseconds: 0
      - service: cover.stop_cover
        data: {}
        target:
          entity_id: cover.brama
      - delay:
          hours: 0
          minutes: 0
          seconds: 2
          milliseconds: 0
      - service: cover.open_cover
        data: {}
        target:
          entity_id: cover.brama
    default: []
  mode: restart
  icon: mdi:gate
gate_stop:
  alias: Gate Stop
  sequence:
  - choose:
    - conditions:
      - condition: or
        conditions:
        - condition: state
          entity_id: cover.gate
          state: opening
        - condition: state
          entity_id: cover.gate
          state: closing
      sequence:
      - service: cover.stop_cover
        data: {}
        target:
          entity_id: cover.brama
    default: []
  mode: restart
  icon: mdi:gate

There are two helpers which allow to know the last action (input_select.gate_last_action) of the gate and if the gate has been fully opened (input_boolean.gate_open), those helpers are set by simple automations:

- id: '1659002340623'
  alias: Gate - last action
  description: ''
  trigger:
  - platform: state
    entity_id:
    - cover.gate
    to: closing
    id: closing
  - platform: state
    entity_id:
    - cover.gate
    to: opening
    id: opening
  condition: []
  action:
  - choose:
    - conditions:
      - condition: trigger
        id: closing
      sequence:
      - service: input_select.select_option
        data:
          option: closing
        target:
          entity_id: input_select.gate_last_action
    default:
    - service: input_select.select_option
      data:
        option: opening
      target:
        entity_id: input_select.gate_last_action
  mode: single
- id: '1659004921882'
  alias: Gate opening for 30 seconds
  description: ''
  trigger:
  - platform: state
    entity_id:
    - cover.gate
    to: opening
    for:
      hours: 0
      minutes: 0
      seconds: 28
    id: open
  - platform: state
    entity_id:
    - cover.gate
    to: closing
    for:
      hours: 0
      minutes: 0
      seconds: 0
    id: closing
  condition: []
  action:
  - choose:
    - conditions:
      - condition: trigger
        id: open
      sequence:
      - service: input_boolean.turn_on
        data: {}
        target:
          entity_id: input_boolean.gate_open
    default:
    - service: input_boolean.turn_off
      data: {}
      target:
        entity_id: input_boolean.gate_open
  mode: single