No-tool backup + installer delivered through the stock Shelly SD-card OTA.
Camera-fw.zip installer package (out/Camera-fw.zip)
thingino/<image>.bin optional payload: full flash image
thingino/<image>.bin.sha256 `sha256sum` line for it (required with a payload)
Without a thingino/ payload the run is backup only and the camera
returns to stock by itself.
- Stock app finds
Camera-fw.zip, writes the installer squashfs into the inactive root slot and reboots into it (stock kernel,root=/dev/mtdblock4). - Installer pivots into RAM, refuses anything but the known 8-partition 32 MiB layout, finds which slot it runs from.
- Mounts the card, dumps all 8 partitions to
shelly-backup-<id>-NN/raw/, builds one 32 MiB restore image with both root slots = the stock root, writesSHA256SUMSandshelly-stock-restore-32M.bin.sha256. The factory partition (mtd7: encrypted MAC, model, batch, device keys) is additionally saved twice from two independent reads,<backupdir>/shelly-factory-<id>.binand<sdroot>/shelly-factory-<id>-NN.bin, each with a.sha256; both must match each other and a third read. Thingino'sdatapartition formats over it later, so after the install the card holds the only copies. Keeping them is the user's job. - Verifies: every raw dump against a fresh flash read, restore image size and sha256 read back from the card. Nothing is erased before this passes.
- Payload present and its sha256 correct: erases and writes mtd1..mtd6 from the image, then mtd0 last. mtd7 (the factory partition, last 128 KiB) is never written by the installer; a full 32 MiB image whose data partition reaches into it has that part skipped, and the new firmware's jffs2 reclaims the block on first mount.
- No payload: renames
Camera-fw.ziptoCamera-fw.zip.done(otherwise stock re-applies it on every boot), copies the stock root back into the slot it booted from, verifies, reboots into stock.
Every failure before step 5 ends the same way as step 6. The log is
install.log on the card (and in the backup directory); the last line is
RESULT: .... LED (GPIO 40): slow blink = working, fast blink = flashing or
error, solid = done.
Backup id = first 12 hex of sha256(mtd7), stable per device.
info.txt records slot, whether the device was virgin (shellyfs1 still
0xFF), cmdline, /proc/mtd, U-Boot env strings, stock build id.
shelly-stock-restore-32M.bin is a plain full-chip image: write it back with
the same slice-writer from Thingino, sf update from a U-Boot console,
thingino-dfu / bootrom USB, or a programmer. On a virgin device it is
byte-identical to the original flash.
.github/ci/build-busybox.sh . # -> ./busybox (static mipsel; needs gcc-mipsel-linux-gnu)
BUSYBOX=./busybox ./build.sh # -> out/root, out/manifest.json, out/Camera-fw.zip
.github/ci/package.sh # -> out/shelly-installer-sdcard.zip (+ .sha256)
The installer userspace is that one static busybox plus rootfs/linuxrc
and rootfs/installer.sh. build.sh needs mksquashfs (xz) and
qemu-mipsel-static (user mode, to list the busybox applets); without
BUSYBOX= it falls back to lifting busybox and glibc from an extracted
stock rootfs (STOCK=), which is how it was first developed.
The GitHub workflow (.github/workflows/build.yaml) does exactly those
three steps on every push to main and on manual dispatch. It republishes
the rolling latest release, so the current SD-card zip is always at:
https://github.com/thingino/shelly-installer/releases/download/latest/shelly-installer-sdcard.zip
Unzip it onto a FAT32 card, then optionally drop the Thingino full image and
its .sha256 into thingino/. (The same files are also attached to each run
as a build artifact.)