Controlling roller shutters using a Shelly 2.5, ESPHome and Home Assistant

Our house has roller shutters on all windows and most of them have an electric motor to open and close them. The motors are Schellenberg shaft motors (not sure this is the right English term), that you can configure end positions for and then by applying current to the right pair of pins they move the shutters up and down until the current is removed or one of the endstops is reached. As the motors were installed over a long period of time, the attached control units varied in functionality: different ways to program open/close times, with and without battery for the clock/settings etc, but they all had something in common: no central management and pretty ugly. We decided to replace those control units with regular 2 gang light switches that match the other switches in the house. And as we didn't want to lose the automatic open/close timer, we had to add some smarts to it.

Shelly 2.5 and ESPHome

Say hello to Shelly 2.5! The Shelly 2.5 is an ESP8266 with two relays attached to it in a super tiny package you can hide in the wall behind a regular switch. Pretty nifty. It originally comes with a Mongoose OS based firmware and an own app, but ain't nobody needs that, especially nobody who wants to implement some crude logic. That said, the original firmware isn't bad. It has a no-cloud mode, a REST API and does support MQTT for integration into Home Assistant and others.

My ESP firmware of choice is ESPHome, which you can flash easily onto a Shelly (no matter 1, 2 or 2.5) using a USB TTL adapter that provides 3.3V. Home Assistant has native ESPHome support with auto-discovery, which is much nicer than manually attaching things to MQTT topics.

To get ESPHome compiled for the Shelly 2.5, we'll need a basic configuration like this:

  name: shelly25
  platform: ESP8266
  board: modwifi
  arduino_version: 2.4.2

We're using board: modwifi as the Shelly 2.5 (and all other Shellys) has 2MB flash and the usually recommended esp01_1m would only use 1MB of that - otherwise the configurations are identical (see the PlatformIO entries for modwifi and esp01_1m). And arduino_version: 2.4.2 is what the Internet suggests is the most stable SDK version, and who will argue with the Internet?!

Now an ESP8266 without network connection is boring, so we add a WiFi configuration:

  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: none

The !secret commands load the named variables from secrets.yaml. While testing, I found the network connection of the Shelly very unreliable, especially when placed inside the wall and thus having rather bad bad reception (-75dBm according to ESPHome). However, setting power_save_mode: none explicitly seems to have fixed this, even if NONE is supposed to be the default on ESP8266.

At this point the Shelly has a working firmware and WiFi, but does not expose any of its features: no switches, no relays, no power meters.

To fix that we first need to find out the GPIO pins all these are connected to. Thankfully we can basically copy paste the definition from the Tasmota (another open-source firmware for ESPs) template:

pin_led1: GPIO0
pin_button1: GPIO2
pin_relay1: GPIO4
pin_switch2n: GPIO5
pin_sda: GPIO12
pin_switch1n: GPIO13
pin_scl: GPIO14
pin_relay2: GPIO15
pin_ade7953: GPIO16
pin_temp: A0

If we place that into the substitutions section of the ESPHome config, we can use the names everywhere and don't have to remember the pin numbers.

The configuration for the ADE7953 power sensor and the NTC temperature sensor are taken verbatim from the ESPHome documentation, so there is no need to repeat them here.

The configuration for the switches and relays are also rather straight forward:

  - platform: gpio
    pin: ${pin_switch1n}
    name: "Switch #1"
    internal: true
    id: switch1

  - platform: gpio
    pin: ${pin_switch2n}
    name: "Switch #2"
    internal: true
    id: switch2

  - platform: gpio
    pin: ${pin_relay1}
    name: "Relay #1"
    internal: true
    id: relay1
    interlock: &interlock_group [relay1, relay2]

  - platform: gpio
    pin: ${pin_relay2}
    name: "Relay #2"
    internal: true
    id: relay2
    interlock: *interlock_group

All marked as internal: true, as we don't need them visible in Home Assistant.

ESPHome and Schellenberg roller shutters

Now that we have a working Shelly 2.5 with ESPHome, how do we control Schellenberg (and other) roller shutters with it?

Well, first of all we need to connect the Up and Down wires of the shutter motor to the two relays of the Shelly. And if they would not be marked as internal: true, they would show up in Home Assistant and we would be able to flip them on and off, moving the shutters. But this would also mean that we need to flip them off each time after use, as while the motor knows when to stop and will do so, applying current to both wires at the same time produces rather interesting results. So instead of fiddling around with the relays directly, we define a time-based cover in our configuration:

  - platform: time_based
    name: "${location} Rolladen"
    id: rolladen

      - switch.turn_on: relay2
    open_duration: ${open_duration}

      - switch.turn_on: relay1
    close_duration: ${close_duration}

      - switch.turn_off: relay1
      - switch.turn_off: relay2

We use a time-based cover because that's the easiest thing that will also turn the relays off for us after the shutters have been opened/closed, as the motor does not "report" any state back. We could use the integrated power meter of the Shelly to turn off when the load falls under a threshold, but I was too lazy and this works just fine as it is.

Next, let's add the physical switches to it. We could just add on_press automations to the binary GPIO sensors we configured for the two switch inputs the Shelly has. But if you have kids at home, you'll know that they like to press ALL THE THINGS and what could be better than a small kill-switch against small fingers?

  [ previous definitions go here ]

  - platform: template
    id: block_control
    name: "${location} Block Control"
    optimistic: true

  - platform: template
    name: "Move UP"
    internal: true
    lambda: |-
      if (id(switch1).state && !id(block_control).state) {
        return true;
      } else {
        return false;
      then: rolladen
        cover.stop: rolladen

  - platform: template
    name: "Move DOWN"
    internal: true
    lambda: |-
      if (id(switch2).state && !id(block_control).state) {
        return true;
      } else {
        return false;
        cover.close: rolladen
        cover.stop: rolladen

This adds three more template switches. The first one, "Block Control", is exposed to Home Assistant, has no lambda definition and is set to optimistic: true, which makes is basically a dumb switch that can be flipped at will and the only thing it does is storing the binary on/off state. The two others are almost identical. The name differs, obviously, and so does the on_turn_on automation (one triggers the cover to open, the other to close). And the really interesting part is the lambda that monitors one of the physical switches and if that is turned on, plus "Block Control" is off, reports the switch as turned on, thus triggering the automation. With this we can now block the physical switches via Home Assistant, while still being able to control the shutters via the same.

All this (and a bit more) can be found in my esphome-configs repository on GitHub, enjoy!


Christian wrote on 2020-05-20 18:21:

Hi Evgeni,

Erstmal ein grosses Kompliment an dich, cooler Blog!!

Ich beschäftige mich seit ein paar Wochen mit ESPs und Home Assistant.

Nun bin ich an der Rolladensteuerung.

Dazu habe ich mir nun ein paar tuya Module bestellt, die ich dann mit ESPHome flashen möchte. Falls dich das Modul interessiert:

Habe nun deinen Beitrag gelesen und finde ihn GENIAL, ganz besonders die Kindersicherung.

Aber nach kurzem trocken-lesen, ein paar Fragen:

  • Wenn die Rolladen mit einem Taster betätigt werden, kannst du sie über einen erneutes tasten anhalten?

  • Wenn du die Rolladen über den taster bedienst, wird ein Stop "gemessen" oder wird der Status irgendwie anders an HomeAssistant geschickt?

Würde mich freuen wenn du mir kurz antwortest.

Grüße Christian

Evgeni wrote on 2020-05-20 19:30:


Das Modul sieht dem Shelly ziemlich ähnlich :)

Zu deinen Fragen:

  • ich habe Schalter, keine Taster, dran, und ja, wenn ich den Schalter auf "aus" stelle hält der Rolladen an (siehe Sicherlich kriegt man ähnliches auch mit einem Taster hin.

  • die Rolladen sind in esphome als "time cover" definiert, d.h. esphome kann aus der Dauer die sich der Rolladen bewegt ungefähr berechnen wie weit offen er ist. Und dies wird auch an HomeAssistant übermittelt. Ist nicht sehr genau, aber mir reicht's.

Hoffe das hilft dir!

Grüße Evgeni

Christian wrote on 2020-05-20 21:45:

Moin Evgeni,

super, dass macht mir Mut und erfüllt quasi schon alle meine Anforderungen. Ob Taster oder Schalter, dass bekomme ich irgendwie hin. Dass der Schalter bei dir Time Based ist, konnte ich sogar erkennen aus der Konfig und den Lambda, aber das HA das auch mitbekommt konnte ich daraus nicht ableiten.

Dann warte ich mal auf die Dinger und probiere es aus :)

In jedemfall, vielen Dank für deine Antwort.

Grüße Christian

Evgeni wrote on 2020-05-21 05:10:


wenn du meine Config mit einem Taster benutzt wirst du den Taster festhalten müssen, bis der Rolladen ganz bewegt ist, beim Loslassen gibt es ein "off" event. Alternativ können binaey sensors irgendwie "click" und "double click" events, evtl geht damit was. Oder man müsste im "on" Lambda prüfen ob der Rolladen sich gerade bewegt und entsprechend "stop" senden.

Hime Assistant hat eine "esphome" Integration die mit der esphome api spricht, der kriegt eigentlich alles mit was dann passiert, ausser es ist als "internal" markiert.

Ach, darf/soll ich unsere Unterhaltung hier als Kommentare in mein Blog werfen?


Send your comments to and I will publish them here (if you want).