ESPHome Shelly UNI Garage Door Controller

Here is my Shelly UNI Garage door controller with states.
Wanted a more advanced garage opener with more states than two.

This reports 5 states with text and icons:
Fully Open
Fully Closed
Partially Open
Door is Closing
Door is Opening

Shelly UNI is very small, good specs, can be powered with 12V – 36V DC.
Hörmann ProMatic 3 operates in 24V DC. No need of extra converters, just plug & play…

Shelly downsides:

  • Difficult to flash, the pins on the board are small.
  • ADC Power measurement is not good in my oppinion.

To get this to work, I had to create a virtual sensor (template sensor) in configuration.yaml (Homeassistant).
A real cover in ESPHome just to control garagedoor from Android Auto and other frontends.
I tried to create a template cover in configuration.yaml, but I did not like it because the icon color did not work right, sometimes it changed color in wrong states, and I had to expose relay to frontend.

How:
Simple text_sensor with states from sensors, homeassistant uses these “states” in template sensor that masquerades as a real Garage Cover with icons.
States come from magnet reed switches and voltage reading from ADC.

Some small issues:
Status changes to wrong status sometimes for a short period of time while opening or closing.
ADC sensor floods log

Time to start the project!

Hardware:
1x Shelly UNI
2x Magnet Reed Switch
1x DHT22 Sensor
1x Hörmann ProMatic3
Cables
Old ATX PCIe power connector

Connections:
DHT22
UNI pin4 Yellow to DHT VCC+
UNI pin5 Blue to DHT Data
UNI pin6 Green to DHT GND-

Magnet Reed Switch
Contact sensor top - GPIO12, UNI pin8 Light Brown and UNI pin1 Red (VCC Main)
Contact sensor bottom - GPIO13, UNI pin7 Dark Brown and UNI pin1 Red (VCC Main)

Relay 1 to Hörmann 20 & 21
Relay 2 to Hörmann 20 & 23 - for partial opening - Optional

promatic-button

Power UNI from backup battery pins.
Cut out one row with 2 connectors from ATX PCIe power connector, if you don’t have better connectors.
Middle pin positive (+), pin closest to the lamp negative (-)

promatic-power

UNI pin3 White to red wire on the motor
I used thick wire and pushed it in the connector, I will make a better Wago connection “later”.
ADC sensor has to be calibrated to your motor, check the vaues when you have commented lamda “filter” disabled.

promatic-motor

Now to the Code!

ESPHome code, Flash to Shelly UNI

substitutions:
  devicename: garage-2-uni
  friendly_name: Garage 2
  update_slow: '60s'              ## Wifi, Uptime...
  dht22_update: '60s'
  button_delay: '0.7s'
  timeout_reboot: '15min'
  sensor_invert_top: 'true'       ## NO Contact set to 'true' , NC contact set to 'false'
  sensor_invert_bottom: 'true'    ## NO Contact set to 'true' , NC contact set to 'false'
  hide_relay1: 'true'             ## Hide from frontend 'true', Show in frontend 'false'
  hide_relay2: 'true'             ## Hide from frontend 'true', Show in frontend 'false'
  hide_cover: 'false'             ## Hide from frontend 'true', Show in frontend 'false'

esphome:
  name: ${devicename}
  friendly_name: $friendly_name

esp8266:
  board: esp01_1m

logger:
  level: DEBUG
  logs:
    component: ERROR

    ## Common settings
wifi:
  ssid: !secret wifi_iot_ssid
  password: !secret wifi_iot_password
  power_save_mode: none
  reboot_timeout: $timeout_reboot

  ap:
    ssid: $friendly_name Fallback Hotspot
    password: !secret fallback_ap_password

captive_portal:
web_server:
  port: 80
  auth:
    username: !secret admin_user
    password: !secret fallback_ap_password
  
api:
  encryption:
    key: !secret esphome_api_key
  reboot_timeout: $timeout_reboot
 
ota:
  password: !secret esphome_api_password

packages:
  switch: !include include/switch_reboot.yaml
  sensor: !include include/sensor_wifi_uptime.yaml
  text_sensor: !include include/text_sensor_wifi_uptime.yaml
  binary_sensor: !include include/binary_sensor_status.yaml

    #### Shelly UNI PIN Guide ####
    ## Onboard               Internal LED, Status LED       # GPIO00
    ## Onboard               Relay 2                        # GPIO04
    ## Onboard               Relay 1                        # GPIO15

    ## UNI pin1 Red          VCC IN
    ## UNI pin2 Black        GND / N     
    ## UNI pin3 White        ADC_IN, analog PIN, ADC Range  # GPIO17
    ## UNI pin4 Yellow       VCC 3.3VDC Output, VCC Sensor
    ## UNI pin5 Blue         Data, AM2301                   # GPIO05
    ## UNI pin6 Green        Internal GND
    ## UNI pin7 Light Brown  Input 1, Switch_n1             # GPIO12
    ## UNI pin8 Dark Brown   Input 2, Switch_n2             # GPIO13

light:
    ## Enable onboard LED
  - platform: status_led
    name: "Status LED"
    pin: GPIO00
    internal: true

sensor:
    ## Temperature and Humidity,
    ##     UNI pin4 Yellow to DHT VCC+
    ##     UNI pin5 Blue to DHT Data
    ##     UNI pin6 Green to DHT GND- 
  - platform: dht
    pin: GPIO05
    model: DHT22
    update_interval: $dht22_update
    temperature:
      name: "Temperature"
      id: temperature
      accuracy_decimals: 1
    humidity:
      name: "Humidity"
      id: humidity
      accuracy_decimals: 0

    ## Analog Sensor ADC_IN, Filter to states
    ##      GPIO17, UNI pin3 White ADC_IN
    ##      Values: 2 = Closing, 4 = Idle, 6 = Opening
  - platform: adc
    pin: GPIO17
    name: "Door Motion Sensor"
    id: door_motion
    update_interval: 2s
    filters:
      - multiply: 10
      - lambda: |-
          if (x > 6.6) {
            return 6;
          } else if (x < 6.1) {
            return 2;
          } else {
            return 4;
          }
    internal: true

binary_sensor:
    ## Contact sensor top
    ##     GPIO12, UNI pin8 Light Brown and UNI pin1 Red (VCC Main)
  - platform: gpio
    pin:
      number: 12
      inverted: $sensor_invert_top 
    name: "End Sensor Top"
    id: end_sensor_top
    on_state:
      then: 
        - component.update: cover_state
    internal: true

    ## Contact sensor bottom
    ##     GPIO13, UNI pin7 Dark Brown and UNI pin1 Red (VCC Main)
  - platform: gpio
    pin:
      number: 13
      inverted: $sensor_invert_bottom 
    name: "End Sensor Bottom"
    id: end_sensor_bottom
    on_state:
      then: 
        - component.update: cover_state
    internal: true

switch:
    ## Button relay 1
  - platform: gpio
    pin: GPIO15
    name: "Relay 1"
    id: relay1
    internal: $hide_relay1
    ## Prevent relay1 and relay2 from being activated at the same time.
#    interlock: &interlock_group [relay1, relay2] 
    on_turn_on:
      - delay: $button_delay
      - switch.turn_off: relay1

    ## Button relay 2
  - platform: gpio
    pin: GPIO04
    name: "Relay 2"
    id: relay2
    internal: $hide_relay2
    ## Prevent relay1 and relay2 from being activated at the same time.
#    interlock: *interlock_group 
    on_turn_on:
      - delay: $button_delay
      - switch.turn_off: relay2

text_sensor:
    ## State to HomeAssistant, to create template sensor or template cover
  - platform: template
    name: Cover State
    icon: mdi:garage
    id: cover_state
#    update_interval: 2s
    lambda: |-
      if ((id(end_sensor_bottom).state == 0) && (id(end_sensor_top).state == 1)) {
        return {"Fully Open"};
      } else if ((id(end_sensor_bottom).state == 1) && (id(end_sensor_top).state == 0)) {
        return {"Fully Closed"};
      } else if ((id(end_sensor_bottom).state == 0) && (id(end_sensor_top).state == 0) && (id(door_motion).state == 4)) {
        return {"Partially Open"};
      } else if ((id(end_sensor_bottom).state == 0) && (id(end_sensor_top).state == 0) && (id(door_motion).state == 2)) {
        return {"Door is Closing"};
      } else if ((id(end_sensor_bottom).state == 0) && (id(end_sensor_top).state == 0) && (id(door_motion).state == 6)) {
        return {"Door is Opening"};
      } else {
        return {"Loading"};
      }

cover:
    ## Garage Cover, use in Android Auto, and other frontends, cloud solutions...
  - platform: template
    device_class: garage
    name: "Door"
    id: door_2
    internal: $hide_cover
    lambda: |-
      if ((id(end_sensor_bottom).state == 1) && (id(end_sensor_top).state == 0) && (id(door_motion).state == 4)) {
        return COVER_CLOSED;
      } else {
        return COVER_OPEN;
      }
    open_action:
      - switch.turn_on: relay1
    close_action:
      - switch.turn_on: relay1

Template sensor in configuration.yaml or sensor.yaml
to include sensors from sensor.yaml add sensor: !include sensor.yaml to configuration.yaml.

 # Garage 2 State Icons and State Text
  - platform: template
    sensors:
      garage_2_cover_state:
        friendly_name: Garage 2 Cover
        # Set state based on the state of a seperate sensor
        value_template: >-
            {{ states('sensor.garage_2_uni_cover_state') }}
        # Set the icon based on state of a seperate sensor
        icon_template: >-
          {% if is_state("sensor.garage_2_cover_state", "Fully Open") -%}
            mdi:garage-open
          {% elif is_state("sensor.garage_2_cover_state", "Fully Closed") -%}
            mdi:garage
          {% elif is_state("sensor.garage_2_cover_state", "Partially Open") -%}
            mdi:garage-alert
          {% elif is_state("sensor.garage_2_cover_state", "Door is Closing") -%}
            mdi:arrow-down-thick
          {% elif is_state("sensor.garage_2_cover_state", "Door is Opening") -%}
            mdi:arrow-up-thick
          {% else -%}
            mdi:alert-circle
          {% endif %}

If you want to use cover instead of sensor you can create a cover in configuration.yaml
I was not happy with the status color change, it changed randomly.
Don’t forget to expose relay to frontend.

  - platform: template
    covers:
      garage_door:
        device_class: garage
        friendly_name: "Garage2 Door"
        open_cover:
          service: switch.toggle
          target:
            entity_id: switch.garage_2_relay_1
        close_cover:
          service: switch.toggle
          target:
            entity_id: switch.garage_2_relay_1
        stop_cover:
          service: switch.toggle
          target:
            entity_id: switch.garage_2_relay_1
        icon_template: >-
          {% if is_state("sensor.garage_2_cover_state", "Fully Open") -%}
            mdi:garage-open
          {% elif is_state("sensor.garage_2_cover_state", "Fully Closed") -%}
            mdi:garage
          {% elif is_state("sensor.garage_2_cover_state", "Partially Open") -%}
            mdi:garage-alert
          {% elif is_state("sensor.garage_2_cover_state", "Door is Closing") -%}
            mdi:arrow-down-thick
          {% elif is_state("sensor.garage_2_cover_state", "Door is Opening") -%}
            mdi:arrow-up-thick
          {% else -%}
            mdi:alert-circle
          {% endif %}
1 Like

Would it work through Shelly Integration in HA?

Maby, I did not try. I like ESPhome, no need of cloud service. More control of the device. ESPhome is integrated in Home Assistant and has similar code.