The S61’s emulator
BekenEmulator at the commit
UPSTREAM pins, with this project’s patches and the Python it imports, as a
container image - the way the S60’s emulator is kept (tools/espressif/emulator). The
tools in tools/beken run in it:
tools/beken/emulator/bk-emu.sh python tools/beken/seed_join.py
tools/beken/emulator/bk-emu.sh run captures/your-dump.bin --wifi-peer auto
tools/beken/emulator/bk-emu.sh test -k "Sonoff 4MB" # upstream's self-tests, under the patches
tools/beken/emulator/bk-emu.sh plug # the S61 on a home Wi-Fi of its own
tools/beken/emulator/bk-emu.sh computer tools/autoflash/cli.py run # the tool, on that Wi-Fi
tools/beken/emulator/bk-emu.sh power cycle # the plug switched off and on
tools/beken/emulator/bk-emu.sh reset # the plug as new, on Sonoff's firmware again
tools/beken/emulator/bk-emu.sh ui # OpenBeken's page, at http://127.0.0.1:8088/
bk-emu.sh plug runs the public S61 dump as a plug provisioned on a home
Wi-Fi, on a Docker network of its own (autoflash-beken, apart from the
S60’s emulator and everything else). The fork’s --net-tap puts the plug’s
radio on a TAP device, which plug-net.sh bridges with the container’s
interface; the container is that Wi-Fi’s router, .1: DHCP (the plug at
.50-.59), DNS (every name the router), and eWeLink, the local stub
(tools/beken/cloud_stub.py), which every connection the plug makes off the
network is sent to - nothing reaches the internet. tools/beken/plug.py is
the plug itself: it boots from its flash, kept in
captures/emulation/s61/autoflash-s61/ with its logs and the stub’s CA, so it
carries on across firmware - each restart the firmware asks for a boot of its
own, from the bootloader, as on a real plug - and Sonoff’s firmware is helped
online (cloud_online.StockHooks) only while it is the one that starts.
bk-emu.sh computer is a computer on that Wi-Fi: the tool there finds the
plug as on a home network, and its eWeLink is the stub. It works in its own
copy of this checkout (the plug’s work/), so its runs’ captures are its own;
an unattended run takes its eWeLink account from work/captures/ (a made-up
one: the stub takes any). Sonoff’s firmware, having fetched and staged a new
image whole and checked its CRC, restarts a second later, and the bootloader
installs it, as a real plug’s does (the firmware’s own code says so,
docs/beken-s61-emulation.md). A plug
that does not come back is switched off and on - bk-emu.sh power cycle, as
the tool then asks; an attended run there offers to do it itself: the plug’s
power switch is passed to the computer (AUTOFLASH_EMULATOR_POWER).
The plug’s web page goes through a proxy (bk-emu.sh ui, ui_proxy.py),
OpenBeken’s or ESPHome’s - ui tells which by the name the plug gave its
router - never opened straight. The emulated plug runs the real OpenBeken, but far
slower than a chip: each request leaves a connection in TIME_WAIT on the plug,
lwIP has room for only a few, and on the emulator’s clock they clear slowly. A
browser’s parallel requests and the page’s once-a-second polling fill them in
seconds, and the plug stops answering though its firmware still runs. The proxy
lets one request at a time reach the plug, at most one every 0.8 s, answers the
page’s polling from a 3-second cache, and passes every action straight on;
ui_proxy.py says more. ESPHome’s page keeps its live values
coming on one open connection (/events), which the proxy passes straight
through, one browser tab at a time.
Each command first ensures the image: one labelled with the hash of what it is built
from (bk-emu.sh tag: UPSTREAM, patches/, requirements.txt,
bk-emu.Dockerfile), pulled from ghcr.io/adamsthws/autoflash-beken-emu:<hash>,
which the beken-emulator-image workflow publishes for every version pushed,
or built where it is not published - from the upstream source archive on this
repository’s release
emulator-source-beken-d9a3772,
one archive named after its commit and never changed (git archive of the
pinned commit, which bk-emu.sh source reproduces byte for byte), downloaded
once into captures/emulation/cache/ and checked against the SHA-256
UPSTREAM pins. So the emulator needs nothing outside this repository. Upstream
publishes no licence. bk-emu.sh with no argument lists the rest; what each
patch does and why, how to change them, and how the pin moves, is in
docs/beken-s61-emulation.md.
No firmware is in the image or the archive. The public S61s dump is Sonoff’s
firmware, which this repository never holds, so bk-emu.sh dump fetches it
from upstream’s repository, which publishes it, into
captures/emulation/beken-firmwares/ (the tools do this before they run), and
bk-emu.sh test fetches upstream’s other devices’ dumps there too (about
280 MB). That directory is mounted into the container at the emulator’s
firmwares/, and the checkout at /work, as your own user; S61_DUMP names a
dump of your own, under captures/.