Upgrading Currysmarter Roller Shutters to Tasmota 16 - Stopping Phantom Moves and Flash Wear
Some of my Currysmarter shutters closed and reopened to a random position, and upgrading them to stock Tasmota silently failed. Here's why, how I moved all 12 to Tasmota 16 (app and safeboot) with a small Berry driver, and a script so you can do the same.
In my last post about the Currysmarter roller shutters I wrote in Update #1 that some shutters closed and then reopened to a random percent, and I asked: "If you know more about this weird behavior, let me know please." Well, now I know. And while I was at it, I finally got all 12 of my shutters from the vendor's Tasmota 13.0.0 to Tasmota 16, app and safeboot.
This post is the story of how I took these devices apart (in software only), why they move on their own, why upgrading them to stock Tasmota "fails", and how I replaced the vendor rules with a small Berry driver. All of that is now packed into a GitHub repo with a single Python script, so you can do the same in a few minutes per shutter.
Just want to upgrade? Clone currysmarter-shutter-firmware, runpython3 currysmarter-upgrade.py check <ip>and thenpython3 currysmarter-upgrade.py upgrade <ip>. Jump to Part 6 for the details.
The Challenge
The Currysmarter Smart TASMOTA roller shutter switches have run my shutters for about a year now, and most of the time they just work. But a few things kept bugging me:
- Every now and then a shutter moved on its own. It closed, and a moment later it went up again to some random position.
- The button LEDs kept turning themselves on again. I literally had a Home Assistant automation called "Shutter LEDs alle 15min ausschalten" (turn off the shutter LEDs every 15 minutes).
- They ship with Tasmota 13.0.0 from 2023. When I tried to upgrade one to v15, it rebooted into safeboot and after another reboot it was back on v13. Nothing had changed.
So I took one shutter (the one in my studio) as a guinea pig and started looking at it. I did everything read-only first, through the Tasmota web API and Berry, without opening the device.
Part 1: What's Inside?
Here is what I found:
- ESP32-C3 with 4 MB flash. Tasmota reports it as
ESP8685 v0.4, which is the C3 in a smaller package. - A Tuya MCU on GPIO20/21 that actually drives the motor and handles the touch buttons. The ESP only talks to it over serial.
- The usual Tasmota safeboot partition layout: an 832 KB safeboot partition, a 2880 KB app partition and a 320 KB file system.
- App
13.0.0(Shutter)combined with a stock14.0.0development safeboot. - No secure boot and no flash encryption. I checked the images for signature blocks and read raw flash. Everything is plain and the hashes verify. So there is no vendor lock.
The Tuya MCU exposes a handful of datapoints (DPs):
- DP1 (enum): control,
0= open,1= stop,2= close. That's thetuyasend4 1,xmy Home Assistant covers send. - DP2 (value): position 0-100 %, both as a command and as a report.
- DP7 (bool): the button LEDs.
- DP8 (enum): no idea yet.
Tasmota's own shutter driver doesn't know anything about Tuya. So the vendor maps a non-existent DP4 to a dummy relay and glues Tasmota's Shutter1 and the MCU together with three rules. And that's where the trouble starts.
Part 2: Why Do the Shutters Move on Their Own?
The vendor rules do two things:
- Tasmota → MCU: every new
Shutter1#Targetis sent to the MCU asTuyaSend2 2,<target>. - MCU → Tasmota: when the MCU reports "stopped" (DP1), or every 2 minutes, and the positions differ by more than 2 %, Rule1 is switched off and a real
ShutterPosition <mcu>runs. That is a fake move with the dummy relay, made only to update Tasmota's model. When the relay switches off again, Rule1 comes back on.
To keep these two directions from feeding each other, the rules constantly switch each other on and off: on every relay change, every 20 minutes and on every boot. That's fragile, and I found two ways it breaks:
- After a reboot, Rule3 switches Rule1 on again, and Tasmota reports
Shutter1with the last saved target. The rule happily sends it to the motor. If you moved the shutter with the wall buttons since then, it drives back to the old position. On top of that, Rule3 reboots the device on every WiFi disconnect (ON Wifi#Disconnected DO Restart 1), so a short WiFi hiccup is enough. - The sync relies on the MCU's "stopped" report. On some of my units that report is simply unreliable: it says "stopped" during a whole run, or reports the direction only when the motor arrives. Combined with the fake
ShutterPositionmove and rules switching on and off around it, an intermediate position can end up as a new target. The shutter stops and drives to that "random percent".
And there are some more side effects:
- Every
rule1 0/rule1 1persists the settings to flash. My studio shutter showed a SaveCount of 71,675, and the others between 7,000 and 55,000. - On every WiFi connect the rules toggle the LEDs off/on/off/on and leave them on. That explains my 15-minute LED automation.
Part 3: Why the Upgrade to Stock Tasmota "Failed"
Before replacing anything, I wanted a current Tasmota. So why did my v15 upgrade end up back on v13?
On these devices there is only one app partition. An Upgrade 1 (or the web "Start upgrade") saves the settings, erases the OTA data and reboots into the safeboot. The safeboot then downloads the new app from the OtaUrl and writes it. If that download fails, the device stays in safeboot. The next reboot simply starts the untouched v13 again. It looks as if the upgrade was rolled back, but it never happened.
To see what the safeboot does, I pointed the shutter's syslog at my Mac and served the firmware from a small local web server (Caddy). The safeboot log was very clear:
OTA: Connection timeout after 5003 msThe shutters live in my IoT VLAN, and from there the safeboot simply couldn't open a TCP connection to the download server. UDP (syslog) worked, TCP didn't. After one firewall rule (and an allow rule in Little Snitch), the download went through and the studio shutter came up with Tasmota 16.0.0. All settings (WiFi, MQTT, template, shutter, even the rules) were migrated just fine.
Then I upgraded the safeboot too, since a 14.0-dev safeboot under a v16 app is not something I want to keep around. Two things I learned there:
Upgrade 2builds the safeboot URL from yourOtaUrl: it takes the file name, cuts it at the first dot, removes everything from the last dash and adds-safeboot.tasmota32c3.binbecomestasmota32c3-safeboot.bin, perfect. Buttasmota32c3-16.0.0.binbecomestasmota32c3-safeboot.0.0.bin, which of course doesn't exist. So use a file name without a version.- After
Upgrade 2, Tasmota 16 stays in the new safeboot. ARestart 3in the safeboot console switches back to the app.
Part 4: A Berry Driver Instead of the Vendor Rules
With Tasmota 16 on the device I had Berry available, Tasmota's small scripting language. I considered rewriting everything around a proper Tuya shutter integration. But that would have changed my whole Home Assistant setup. So I went for the less intrusive option: keep Tasmota's Shutter1 as the interface for Home Assistant, MQTT and the web UI, and replace only the glue between Tasmota and the MCU.
The driver (curry_shutter.be, about 270 lines including comments) does this:
- Only commanded moves go to the MCU. When you set a position in HA, Tasmota's model starts moving and the driver sends the target once. Idle reports (at boot, telemetry, after a sync) are ignored. So a reboot can never move the motor to a stale position anymore.
- The MCU stays the master of the position. When the shutter is moved with the wall buttons or
tuyasend4, the driver publishes the live position about once per second as a regularShutter1result, like Tasmota does during its own moves. When the motor stops, it syncs the model withShutterSetPosition1, which moves only the model and never the relay or the motor. - Motion is judged from the position (DP2), not from DP1. I found this one during the rollout: on two shutters DP1 reported "stopped" during a whole run, and the first driver version re-synced about 33 times per run. Since switching to DP2 it's exactly one sync per stop.
- LEDs stay off (configurable). It corrects them at most once every 10 seconds if the MCU turns them on.
- No rule toggling, so no extra flash writes, and no restart on WiFi loss. Tasmota reconnects on its own.
Here is how the studio shutter looks in Home Assistant now, moving down with the wall button and stopping at 64 %: 99, 93, 87, 81, 75, 69, and the final 64 one second after the stop.
The best part: my Home Assistant configuration didn't change at all. Same topics, same Shutter1, and both tuyasend4 and shutterposition work as before. The Var4 branch in my position_template from the last post isn't used anymore, because the vendor rules published the MCU position as Var4, and the driver publishes real Shutter1 results instead. That also means exact positions, like 64 %, instead of my old template's rounding to 5 % steps.
For a debug view, there is a CurryShutter command in the console that shows the MCU and Tasmota positions, the LED state and how often it forwarded or synced:
{"CurryShutter":{"Version":"1.1.0","MCU":{"Position":100,"Control":1,"LED":0},
"Tasmota":{"Position":100,"Direction":0},"Forwarded":0,"Synced":0,"LEDMode":0}}Part 5: Rolling It Out to 12 Shutters
After the studio, I did two more shutters by hand, checking everything as I went. Then I wrapped the exact same steps into a script that aborts at the first unexpected result, and ran it on the remaining nine.
It aborted exactly once, on my hallway shutter. Right after the reboot into v16, the still-active vendor rules ran one of their model resyncs, so Tasmota reported the shutter as "moving". That's precisely what the check was there for. The script now waits up to 60 seconds for the shutter to be idle, and the hallway went through on the second try.
Correct travel times matter for the live position in HA and for shutterposition. I didn't want to walk to every window with a stopwatch, so I measured them through Home Assistant instead: close all nine, open all nine, and time each run from the MCU's position reports. My small windows take 19-22 seconds, the doors and large windows 29-30 seconds.
The whole rollout of the nine shutters took about 35 minutes. The counters tell the story: my studio shutter had written its settings 71,675 times before. Now it writes them once overnight, and none of the shutters rebooted. And my LED automation? Disabled, because nothing turns the LEDs on anymore.
A Surprise in the Vendor Firmware
While comparing the vendor's newer 15.0.1 firmware (which turned out to be stock Tasmota 15.0.1 with their rules and defaults compiled in), I noticed something in the defaults: a second WiFi network "T-test" with the password "88888888". And sure enough, all 12 of my shutters had it configured as WiFi 2.
That means: if your own WiFi is unreachable, the shutter happily connects to any access point named "T-test". Since there's no web password by default, whoever runs that AP can reconfigure the device or flash any firmware onto it. Probably a leftover from factory testing, but not something I want in my house. I cleared it on all shutters with:
Backlog SSId2 1; Password2 1The upgrade script does this for you by default. And while you're at it, consider setting a WebPassword as well.
Part 6: Do It Yourself
Everything above is now in martinseener/currysmarter-shutter-firmware. You only need a Mac or Linux machine with Python 3.8 or newer. There are no packages to install and no separate web server to run, because the script brings its own.
git clone https://github.com/martinseener/currysmarter-shutter-firmware.git
cd currysmarter-shutter-firmware
# read-only: what's on the shutter, and can it reach this computer?
python3 currysmarter-upgrade.py check 192.168.1.50
# app + safeboot to Tasmota 16, Berry driver, vendor rules off, T-test removed
python3 currysmarter-upgrade.py upgrade 192.168.1.50 192.168.1.51
# optional: measure the travel times (this moves the shutter!)
python3 currysmarter-upgrade.py calibrate 192.168.1.50 --applyFor each shutter, upgrade does this:
- Runs the checks. It aborts if the hardware or template doesn't match, or if the shutter is moving.
- Makes the shutter call the script's web server (with Tasmota's
WebSend). That's exactly the connection the safeboot will need later. If it fails, nothing gets flashed, and you get a hint about the firewall instead of a shutter stuck in safeboot. - Downloads a config backup to
backups/. - Upgrades the app with
Upgrade 1and shows the download progress. - Upgrades the safeboot with
Upgrade 2and thenRestart 3. - Resets the
OtaUrlto the official Tasmota URL, so future updates come from Tasmota directly. - Installs and verifies the Berry driver.
- Clears the "T-test" WiFi.
You can choose between two firmware options:
--firmware custom(the default): official Tasmota v16.0.0 plus a small installer that carries the Berry driver. An OTA update only replaces the app, not the file system. So on first boot the firmware writes the driver files to the file system and the driver switches the vendor rules off. The device then shows16.0.0(currysmarter). The image is built from the official v16.0.0 tag bybuild/build.sh, and you can rebuild it yourself. I tested it on my living room window: the whole upgrade took about two minutes, and afterwards Home Assistant followed every move with an update each second, ending exactly where the shutter stopped.--firmware stock: the official, byte-identical Tasmota v16.0.0 release. The script uploads the driver afterwards. Add--no-driverif you want to keep the vendor rules and add the driver later withinstall-driver.
Both options use the official v16.0.0 safeboot. All files are checked against the SHA256 sums in firmware/manifest.json before the script serves them. If you'd rather type the commands yourself, the README has the manual console steps as well.
Troubleshooting
"the shutter cannot reach this computer"
The shutter needs to open a TCP connection to your computer on port 8000 (change it with --port). Check the firewall between your IoT network and your computer, and the macOS firewall or Little Snitch for python3. If your computer has several IPs, set the right one with --server-ip.
The shutter is stuck in safeboot
The safeboot couldn't download the firmware. The old app is still there, so python3 currysmarter-upgrade.py rescue <ip> (a Restart 3) brings it back. Alternatively, upload the firmware file in the safeboot web UI.
The safeboot upgrade fetches a wrong file
Remember the first-dot rule from Part 3. Use tasmota32c3.bin as the OtaUrl file name for Upgrade 2.
I want the vendor rules back
The driver only switches them off and keeps their text. Run Br curry_shutter.unload(), remove the load() line from autoexec.be and run Backlog Rule1 1; Rule2 1; Rule3 1.
Conclusion
I started with a shutter that sometimes moved on its own and an upgrade that silently "rolled back". Neither turned out to be a hardware problem or a vendor lock. The rules were fragile, and my network blocked the safeboot's download. Now all 12 shutters run Tasmota 16, app and safeboot. A Berry driver gives Home Assistant live positions, keeps the LEDs off and never sends a stale target to the motor. The flash isn't written all day long anymore, and the factory "T-test" WiFi is gone.
If you own these shutters, give the repo a try and let me know how it works for you. Bug reports and pull requests are very welcome on GitHub. And if you know what DP8 does, I'd love to hear it!
I also plan to update this repo when issues arise or a new Tasmota version is released, so we get constant new Tasmota versions for our Currysmarter Shutters.
Additional Resources
- currysmarter-shutter-firmware - The upgrade script, the Berry driver and the firmware
- Technical notes - Datapoints, vendor rules, flash layout and upgrade pitfalls
- Setting Up Currysmarter Tasmota Roller Shutters with Home Assistant - My first post with the HA configuration
- Buy on Amazon.de - Currysmarter Smart TASMOTA roller shutter switch
- Tasmota Safeboot - How the safeboot partition works
- Tasmota Berry - Scripting in Tasmota
- Tasmota TuyaMCU - TuyaMCU support in Tasmota
Disclaimer
Flashing firmware is at your own risk. The procedure in this post and the repo worked on my shutters, but I don't take any responsibility if you kill your shutter, your device ends up needing a serial flash, or anything else goes wrong.
A note on AI: the reverse engineering of these devices was supported by AI (Claude Opus 5.5).
Disclosure: This post contains affiliate links. If you purchase through these links, I may earn a commission at no extra cost to you.