Manually flashing a Gen 3 Shelly device

Submitted by davidc on Thu, 17/09/2026 - 16:04
I recently had to update a Shelly device I'd bought a few years ago, which had been sitting in a box and was running the ancient 1.3 firmware, and I wanted to upgrade it to 2.0. Doing so instantly bricked it: it hadn't recovered after 30 minutes so the firmware update had failed somehow, and there was no way to recover it, even with the button reset function. As it was long out of warranty despite never having been used, I attempted to recover it myself. These instructions relate only to Gen 3 devices (ESP32-C3-based). Firmware for older hardware lines might be found at archive.shelly-tools.de and I have no idea whether the flashing method is the same.

Connecting a UART

devices.esphome.io often has the UART pinout of ESP devices, and indeed it did for my 1PM Mini Gen 3. In this device the pads were tiny and I needed to connect four simultaneously, so I ended up having to solder flying leads onto RX, TX and 3V3 (for GND, I used one of the legs of a through-hole capacitor on the other side of the board). This left my hands free to short BootSel to GND while powering it on to enter the bootloader, and to run the flashing commands. Note that the device must not be mains-powered during this process, and your serial flasher needs to be set to 3.3V mode.

Downloading the firmware

Shelly don't provide any way for a human to browse and download firmware, so you need to get it from their APIs as if you were a device. These APIs also operate with a self-signed CA (which is presumably trusted by their firmware but not by a web browser). First you need the app ID of the particular device you're flashing. This is not the same as the device ID. If your device is working (or if you have an identical device to check), you can get it from the "app" key on the endpoint /rpc/Shelly.GetDeviceInfo on its HTTP server. Otherwise, search the internet or ask around.
{
  "name": "1PM Mini Gen3 #1 UPS",
  "id": "shelly1pmminig3-xxxxxx",
  "mac": "DCDA0CXXXXXX",
  "slot": 1,
  "model": "S3SW-001P8EU",
  "gen": 3,
  "fw_id": "20260710-101127/2.0.0-g87fbfa4",
  "ver": "2.0.0",
  "app": "Mini1PMG3",
  "auth_en": true,
  "auth_domain": "shelly1pmminig3-dcda0cxxxxxx",
  "matter": false,
  "provision": "complete",
  "enhanced_security": false
}
In this case, the app is Mini1PMG3. You then visit https://updates.shelly.cloud/update/[APP] and look for the URL of the actual firmware file under the release channel you want (stable or beta).
{
  "stable": {
    "version": "2.0.0",
    "build_id": "20260710-101127/2.0.0-g87fbfa4",
    "url": "https://fwcdn.shelly.cloud/gen2-ntest/Mini1PMG3/5cae1ab4171b029d0ac871dca71c464dbf8d8464d3dd210717207e1c25db4d06"
  },
  "beta": {
    "version": "2.0.1-beta2",
    "build_id": "20260910-125922/2.0.1-beta2-g5e8db8e",
    "url": "https://fwcdn.shelly.cloud/gen2-ntest/Mini1PMG3/41d7ca3419711635dc329a455bcc71b083d61a1bcba5d1e858cd826aa1713bc6"
  },
  "time": 1789557761
}
Alternatively, I've attached a bash script at the bottom of this page that will do it all for you (assuming you have curl and jq installed). Run it with just an app specified, and it'll list the available channels and versions. Add the channel you want, and it'll download the whole thing for you:
david@ha1:~ $ ./shelly-fw-downloader.sh
usage: ./shelly-fw-downloader.sh  []
david@ha1:~ $ ./shelly-fw-downloader.sh Mini1PMG3
channel version
------- -------
beta    2.0.1-beta3
stable  2.0.0
david@ha1:~ $ ./shelly-fw-downloader.sh Mini1PMG3 stable
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 3355k  100 3355k    0     0  13.6M      0 --:--:-- --:--:-- --:--:-- 13.6M
shelly-mini1pmg3-2.0.0.zip
david@ha1:~ $

Unpacking the firmware

The firmware is a ZIP archive, so simply unpack it. The manifest.json file is the index and describes what each file is and where it goes in flash. In particular, for each file in the archive, the addr field specifies (in decimal) the flash offset for files written to fixed addresses. This includes the partition table file. Once you've written the partition table, you can then also flash the files that are written directly into partitions (identified by having a ptn key instead of an addr). Note that some files are listed encrypt: true. I did not encrypt these files when flashing them, as my encryption key had been reset. Nevertheless, after flashing this all manually, I flashed it all again via the official OTA to make sure everything was correct.

Flashing the device

You'll need esptool.py and parttool.py, which are part of the ESP-IDF toolchain. You probably already have working copies if you have ESPHome or PlatformIO installed (use find . -name esptool.py to find them, noting that probably only one will work since there will be others dependent on virtualenvs you may not be able to activate from outside Docker). Enter bootloader mode by tying the BootSel pin to GND briefly while applying 3V3. esptool.py should then be able to identify the attached chip (or at least its MAC address) using the following command (in all the following examples, my serial port is /dev/ttyS0).
david@Robin:~/esphome/config/.esphome$ python3 platformio/packages/tool-esptoolpy/esptool.py --port /dev/ttyS0 chip-id
Erase the flash using:
esptool.py --port /dev/ttyS0 erase-flash
Flash each file that has a fixed address using:
esptool.py --port /dev/ttyS0 write_flash [address] [file]
Once you have flashed the partition table by this method, you can switch to parttool.py to flash the remaining files directly to their partitions.
parttool.py --port /dev/ttyS0 write_partition --partition-name [partition name] --input [file]
I did not bother to flash the nvs (non-volatile storage) partition, as I had already erased the whole flash.

Conclusion

With these steps complete, I rebooted the device and the Shelly firmware started successfully. This was enough to bring my 1PM Mini Gen 3 back from what appeared to be a completely unrecoverable firmware update failure. If your Gen 3 device is bricked after a failed OTA update, manually rebuilding the flash from the official firmware package is a viable method, although some soldering may be required.
Attachment Size
shelly-fw-downloader.sh.txt 1.56 KB