GitHub

Published firmware artifacts

Every file here is pinned in SHA256SUMS beside it (sha256sum -c SHA256SUMS in this directory), and the versions the tool installs in firmware.json. Per-device credentials and flash captures belong under the Git-ignored captures/ directory, never here.

File Purpose SHA-256
autoflash-bridge-sonoff-s60-4.4.4.ota Wrapped one-shot bridge the run installs; joins the home Wi-Fi stock was on, raising its own network only if that fails, lets the run back up the whole flash, gives Tasmota its settings, and starts even after an upload cut off part way 4424af5d59258f5c1998269c65bffc5d1341f519352fdc1bfd9c76e37b34f5bb
tasmota32c3-bluetooth.bin Berry-capable Tasmota used temporarily on the original partition layout a92cfadaff5a3c824f790471ba31464acd2076a24ff1fcad2de28574c5641708
autoflash-recovery-safeboot-imprintable.bin Credential-free recovery Safeboot template; saves the Wi-Fi for the official Tasmota installed after it ece36fd964c1443ef9fb74af87a0a257501c9e5ecfb1fb7a1c3aff1744bc29fd
tasmota-settings-template-15.6.0.0.bin Tasmota’s default settings with no Wi-Fi in them, for the bridge to fill in at D1 d63f03cb99540407037f36dc8155ec9b02d24425d3b02c52299713233e98f4a5
esphome-sonoff-s60-bootloader-1.2.0.bin ESPHome’s bootloader, from the build of 1.2.0: it replaces Sonoff’s when ESPHome is installed, so ESPHome can roll back an update that fails to start 3792bb665916d69c410c9fd10f69d1a6968e61f2a9e4969829a1c49f9ea6f433
esphome-sonoff-s60-imprintable-1.6.0.bin ESPHome for the S60, installed instead of the final Tasmota when the run is asked for ESPHome; no Wi-Fi or passwords in it, for the run to imprint the plug’s Wi-Fi ff4dbbccb202e9e663340f47b1797c027b0a3a1eb24462ad7460597a645f7875
autoflash-mover-1.1.0.bin A small ESPHome the run uses for a minute or two when leaving Tasmota: it fits in front of Tasmota in Sonoff’s layout, and takes the next firmware as an ordinary ESPHome update; two minutes away from the home Wi-Fi, it raises its own network, whose page takes new Wi-Fi details. Wi-Fi placeholders, like the image above f6e131e1599c886d742c151c3ba3f84a285bba462b438c36b611d577e01ad243
sonoff-s60-partition-table.bin Sonoff’s partition table, read from a stock plug: the one every conversion starts from, which the Tasmota route’s preflight requires byte for byte. Written back when a plug leaves Tasmota’s layout. Holds no secrets f63f66bbf23b9e291c7eb5dcf24be820190dacf4bf52af515d9664526a4f4daf
openbeken-sonoff-s61-1.1.0.rbl OpenBeken for the S61, with the 4 MB table, the hook before an OTA’s restart, the tool’s OpenBeken driver and the S61’s table for it built in (a run to ESPHome sends it, with a marker for its app after it, as the interim firmware): the rt_ota image the plug’s stock bootloader applies from its download slot, whether sent by the stock firmware’s updater, by OpenBeken’s own OTA or in a UF2 through ESPHome’s 07f45d62f32b8f89f782ae6460253c7dc17fdbaceb20b2521e6db2e59a254687
esphome-sonoff-s61-1.2.0.rbl ESPHome for the S61, on LibreTiny: the same rt_ota image form, installed the same ways. Nothing is written into it: its first boot reads the plug’s Wi-Fi and meter calibration from the stock store. The tool takes the plug’s backup before it, through OpenBeken as the interim firmware 2c61622bcbbc11b7ee1b1e9b082a73f4d35c19570471efb7b0e41a75ad181172

The Bluetooth image is the exact file used by the successful migrations. It was retrieved from Tasmota’s official moving install/firmware branch at firmware/unofficial/tasmota32c3-bluetooth.bin. Its embedded source identifier is f5b34a2; corresponding source is arendst/Tasmota@f5b34a26be51be2469737d2c43a62acb237c264f.

The settings template is Tasmota 15.6.0’s defaults, captured from a converted plug with tools/espressif/capture_tasmota_template.py and tested on one with --verify. Its Wi-Fi names, passwords, last access point, and addresses are blank or Tasmota’s defaults; it holds nothing about any network. It is only used with the Berry Tasmota pinned above (15.6.0.1, same settings layout). If it, or the Tasmota the interim firmware is made from (15.6.0), changes, re-check and re-capture it: see tools/autoflash/tasmota_settings.py. The official release a converted plug ends on can change without it (../docs/updating-firmware.md).

The imprintable Safeboot is built from the pinned Tasmota source with tools/espressif/safeboot/repartition/recovery-safeboot/volatile-wifi-defaults.patch and persist-settings.patch. On its first boot after the repartition it wipes NVS - by then only what the Sonoff firmware left and part of an earlier Tasmota record - saves its settings, with the Wi-Fi, where Tasmota reads them, and reads them back, so the official Tasmota installed next joins the network by itself. It logs whether that worked, and the run records it at F2. It never touches settings that are already there. The saved record has only what a Safeboot sets, so F4 starts with Reset 4, which rebuilds every other setting from official Tasmota’s defaults and keeps the Wi-Fi. It names itself as the tool, not one device, as every firmware a plug runs part way through a conversion does: autoflash-recovery-XXXX, the last two bytes of the plug’s Wi-Fi MAC. It saves that name, written out, with the Wi-Fi, so the Tasmota after it goes by it too until F4’s Reset 4 gives it Tasmota’s own (a name with a format in it Tasmota would take for its own default). What it logs starts “Autoflash recovery”. Until 20 October 2026 it was s60-recovery-NNNN (four decimal digits) and “S60 recovery”, saving no name, and the file was s60-recovery-safeboot-imprintable.bin; the run still reads the older log lines, from a plug a run of an older checkout left on it.

To publish a new one: tools/espressif/safeboot/configure_imprintable_safeboot.py writes the header with the placeholders, tools/espressif/safeboot/build_recovery_safeboot.sh builds it from the pinned Tasmota source, tools/espressif/safeboot/sanitise_safeboot.py blanks the build machine’s paths in place, and tools/espressif/safeboot/validate_imprintable_safeboot.py must then say it is publishable. A rebuild holds the same text as the published file but its build date and time; one built in a directory whose path is not as long as this one’s (38 characters) has everything after the first such path a few bytes along.

The imprintable Safeboot contains reserved placeholder bytes instead of real Wi-Fi credentials. It must not be flashed as published: the private bundle-preparation path must imprint and validate a per-device copy first.

From 4.4.1 on, a bridge is named by one version: the one its file is wrapped with, the one in its app description, and the one its status page reports (bridge 4.4.4). tools/espressif/build_vendor_ota.py takes it from the image and refuses any other. The stock updater is not given it: the sender wraps the same app again as 9.9.9 (ANNOUNCED_VERSION), above any Sonoff release, as the S61’s images are sent. The next bridge is 4.4.5. 4.4.1 has the Wi-Fi behaviour tried on a stock plug as “v6” on 8 October 2026; beyond that only its version, its name and a larger buffer for the status reply differ. 4.4.2 is 4.4.1 with the read-only GET /flash and GET /efuse of the research readout build, and GET /flash-sha256, the SHA-256 of the whole flash worked out on the plug, which read a stock plug’s flash on hardware on 8 October 2026, added so the run can keep a backup of the Sonoff firmware. 4.4.3 is 4.4.2 that starts even when Sonoff’s slot is no longer bootable - an upload of Tasmota into it cut off part way - where 4.4.2 refused to start, leaving a plug with no network at all; it reports fallback: none on its status page then. On its own network it also tries the home network again every three minutes while nobody is joined to it, and that network is open and called Sonoff-Setup-XXXX (the last two bytes of the plug’s Wi-Fi MAC) - it was S60-OTA-Bridge-XXXX, with a fixed password. 4.4.4 is 4.4.3 named as the tool’s, as every firmware a plug runs part way through a conversion is: autoflash-recovery-XXXX, on the home network (4.4.3 joined as s60-ota-bridge) and as its own (Sonoff-Setup-XXXX), with its page titled “Autoflash bridge” (it was “S60 OTA Bridge”). On its own network it saves Tasmota’s settings without Wi-Fi details, where 4.4.3 refused them, so Tasmota starts under the run’s name for it and makes its own network by that name, not its default tasmota-XXXXXX-NNNN. Its code is the same for any plug; the wrapper names the S60’s model, which is why the file names the S60. Tested on the emulator and on real plugs.

The version is well above any Sonoff release so far (1.2.0). The stock 1.2.0 updater does not compare versions - the upgrade command decides what is installed, and a plug on 1.2.0 took bridge v5 wrapped as 9.9.9 - but a later Sonoff firmware might refuse anything older than itself, so it has to stay higher than Sonoff’s. The version field holds at most seven characters.

The earlier bridge v3, wrapped as 1.2.1, is no longer published here. It is in the Git history: git log --all -- artifacts/s60-ota-bridge-v3-1.2.1.ota finds the last commit that has it.

The ESPHome image is built from ../devices/sonoff-s60/esphome.yaml (its project version is the one in the file name) with ESPHome 2026.10.0 and ESP-IDF 5.5.6, as esphome compile sonoff_s60.yaml, in ESPHome’s own Docker image for that release (firmware.json pins it by digest); firmware.ota.bin is the file published. The bootloader is bootloader/bootloader.bin from the build of 1.2.0, with ESPHome 2026.9.1 and ESP-IDF 5.5.5. The mover is ../devices/sonoff-s60/mover.yaml, built the same way, with ESPHome 2026.9.1: it drives no pins, so it is the same for any plug with an ESP32-C3 and 4 MB of flash, and from 1.1.0 it goes by autoflash-recovery-XXXX as the bridge does (1.0.0 was sonoff-s60-mover-xxxxxx on the home network, and raised Sonoff-Setup-XXXX). Like the imprintable Safeboot they hold placeholders instead of a network name and password, and the run writes the plug’s Wi-Fi into them in memory (tools/autoflash/imprint.py). It has no OTA password, web login or API encryption key, as ESPHome’s own ready-made images have none: your plug is open on your home network until you add them, as official Tasmota is. To publish a new one, change the config and build and publish it with tools/repo/update_firmware.py esphome, which ../docs/updating-firmware.md describes, as for a new ESPHome release; then update the files’ rows above.

ESPHome 1.1.0 adds a Wi-Fi signal sensor, which the run reads before it changes the firmware of a plug on ESPHome, and an s60_wifi action that gives the run the plug’s Wi-Fi name and password, so that leaving ESPHome needs neither typed. It also saves its Wi-Fi where a build of the config with none written in looks for it, so a plug that Home Assistant’s ESPHome builder rebuilds stays on the home Wi-Fi. Its bootloader is byte for byte 1.0.0’s, so no plug is given a new one. 1.0.0 is in the Git history: git log --all -- artifacts/esphome-sonoff-s60-imprintable-1.0.0.bin finds the last commit that has it.

ESPHome 1.2.0 is named as every ESPHome the conversion installs is, whatever the plug: esphome-abcdef (ESPHome’s own MAC suffix), and its own network has that name too - 1.1.0 was sonoff-s60-abcdef, its network Sonoff-Setup-CDEF. Its friendly name, Sonoff S60, still says what plug it is. Its Wi-Fi action is autoflash_wifi and its placeholders AUTOFLASH-IMPRINT-; the run still knows 1.1.0’s s60_wifi and S60-IMPRINT-, on plugs converted with it. Its bootloader is 1.1.0’s, byte for byte, as is its every other behaviour. 1.1.0 is in the Git history: git log --all -- artifacts/esphome-sonoff-s60-imprintable-1.1.0.bin.

ESPHome 1.3.0 is 1.2.0’s config built with ESPHome 2026.10.0 (ESP-IDF 5.5.6) where 1.2.0 was built with 2026.9.1 (ESP-IDF 5.5.5): the config is unchanged but for its version. The bootloader that build makes is ESP-IDF 5.5.6’s, which differs from 5.5.5’s in nothing an S60 uses (its changes are for other chips, secure boot and flash encryption), so the published one - 1.2.0’s, which starts 1.3.0 - is kept, and no plug is given a new one. The ESP-IDF 5.5.5 bootloader built again from 1.2.0’s config with ESPHome 2026.9.1 came out the same, byte for byte. 1.2.0 is in the Git history: git log --all -- artifacts/esphome-sonoff-s60-imprintable-1.2.0.bin.

ESPHome 1.4.0 is a change to 1.3.0’s config, built with ESPHome 2026.10.0 (ESP-IDF 5.5.6) as 1.3.0 was. It applies the 1.02 board factor and the plug’s factory calibration, from Sonoff’s NVS or, through its autoflash_calibration action, from the plug’s Sonoff backup, as the stock firmware does; and counts energy from the meter’s pulses, calibrated the same way, its totals carrying on from what 1.3.0 saved. The bootloader that build makes (ESP-IDF 5.5.6) differs from the published one, which is kept: no plug is given a new one. 1.3.0 is in the Git history: git log --all -- artifacts/esphome-sonoff-s60-imprintable-1.3.0.bin.

The S61’s two images are rt_ota images (.rbl), as its stock bootloader applies them. OpenBeken 1.1.0 is OpenBK7231T_App at ce1abad with this project’s 4 MB-table patches and OpenBeken driver (tools/beken/openbeken/) and the S61’s table for it, devices/sonoff-s61/drv_autoflash_sonoff_s61.c, built as OpenBK7238 with FLASH_SIZE=4M in tools/beken/openbeken/build.Dockerfile; ESPHome 1.2.0 for the S61 is ../devices/sonoff-s61/esphome.yaml built with ESPHome 2026.10.0 on LibreTiny 1.13.0, image_bk7238_app.ota.rbl of its build. firmware.json pins both under beken, with the commits, hashes and build images that made them; tools/repo/update_firmware.py openbeken and esphome --plug sonoff-s61 build and publish them (../docs/updating-firmware.md). In 1.1.0 both name the plug as every firmware the tool installs does - OpenBeken openbeken-abcdef, its hostnames too; ESPHome esphome-abcdef, with Sonoff-S61 as its comment - and ESPHome has no backup reader: the tool takes the backup through OpenBeken, installed first as the interim firmware, which knows itself by a marker sent after it that names its app. ESPHome 1.2.0 is 1.1.0 adopted into Home Assistant as a package rather than a copy of its config. 1.0.0 and 1.1.0 are in the Git history.

ESPHome 1.5.0 is a change to 1.4.0’s config, built with ESPHome 2026.10.0 (ESP-IDF 5.5.6) as 1.4.0 was. The bootloader that build makes (ESP-IDF 5.5.6) differs from the published one, which is kept: no plug is given a new one. It is the image built from the moved config (devices/sonoff-s60/esphome.yaml, so Home Assistant can adopt a plug on it again), and names the plug as every firmware the tool installs does: friendly name esphome abcdef, with Sonoff-S60 as its comment. 1.4.0 is in the Git history: git log --all -- artifacts/esphome-sonoff-s60-imprintable-1.4.0.bin.

ESPHome 1.6.0 is a change to 1.5.0’s config, built with ESPHome 2026.10.0 (ESP-IDF 5.5.6) as 1.5.0 was. The bootloader that build makes (ESP-IDF 5.5.6) differs from the published one, which is kept: no plug is given a new one. Its apparent power, reactive power and power factor are worked out each second from the calibrated voltage, current and power of the same meter readings, where they were the CSE7766’s own, from its raw readings, which logged each impossible power factor and reactive power, many a second under a load close to resistive. Reactive power is 0 while power is within 1.5% of voltage times current, and it and the power factor change only in a second steady with the last: as a load switches, the meter’s power follows its current a second or three behind. 1.5.0 is in the Git history: git log --all -- artifacts/esphome-sonoff-s60-imprintable-1.5.0.bin.