GitHub

ESPHome for the S60

esphome.yaml is the ESPHome the conversion installs when you choose ESPHome at the start. You do not need to build it to convert a plug: the tool installs the pre-built image in ../../artifacts/ and writes your plug’s Wi-Fi into it, in memory only.

It is also the config Home Assistant uses when it adopts a converted plug (dashboard_import), so it is one self-contained file. It includes no files of its own (system headers only) and no partition file: an app image does not contain the partition table, and the plug keeps Sonoff’s.

What it reports

Power, voltage and current are corrected as Sonoff’s own firmware corrects them, and the diagnostic Calibration sensor says how:

Energy is what the meter has counted since the plug started, in Wh, calibrated the same way. Daily energy, Monthly energy, Yearly energy and Total energy are counted from the same pulses, so they agree with it, and are kept through restarts and updates; the first three start again at midnight, on the first of the month and on 1 January. On a plug updated from 1.3.0 or earlier, they carry on from where they were.

Adopted into Home Assistant

Home Assistant’s ESPHome builder can adopt a converted plug. It uses this config, at the release the plug’s firmware came from, and your plug keeps that config until you choose a newer release: how it works, how to take a fix and how to customise it.

Changing it

Edit esphome.yaml, then build and publish it with python3 tools/repo/update_firmware.py esphome, as for a new ESPHome release (docs/updating-firmware.md): it raises the config’s project version, builds it in ESPHome’s own Docker image, checks the image can still be imprinted, and publishes it in artifacts/, with its lines in SHA256SUMS and firmware.json. ESPHome 2026.10.0 built the current image.

The command keeps the published bootloader unless you pass --new-bootloader: a different one is written to every plug on its next route through D5 or L1, and a power cut in that second leaves a plug that has to be opened. A change to the config’s app alone leaves the bootloader the same (1.1.0’s is 1.0.0’s).

Keep the name of the WiFi Signal sensor: the tool reads it, by that name, before it changes the firmware of a plug on ESPHome. If a plug’s ESPHome has no such sensor (one built before 1.1.0, or from a config without it), the tool asks the native API for the configuration dump instead, which prints the signal. If the API has an encryption key, the tool does not read the signal there.

Keep the names of the four energy totals: the first boot after 1.3.0 finds what each had saved by its name, and Home Assistant keeps its history by it. Keep the autoflash_calibration action’s name too: the tool gives the plug’s factory calibration through it (from 1.4.0).

The autoflash_wifi action (s60_wifi up to 1.1.0) gives the tool the plug’s Wi-Fi name and password when the plug leaves ESPHome: the next firmware needs them written in, and ESPHome gives them no other way. Keep its name. Anything that can use the API can call it - anything on your home network, while the API has no encryption key - just as it could send the plug new firmware while OTA has no password. Giving the API a key closes it; the tool then takes the details from the plug’s Sonoff backup, if your computer has it, or asks you for them.

If you add an update password later (password: under ota:), the tool asks for it when it takes the plug off ESPHome, or you can give it with --esphome-ota-password or as AUTOFLASH_ESPHOME_OTA_PASSWORD. If the plug has an API encryption key (encryption: under api:), the tool asks for it, or you can give it with --esphome-api-key or as AUTOFLASH_ESPHOME_API_KEY. The tool then reads the plug through the encrypted API, and sends its update encrypted where the plug takes that - as it must where encryption: is under ota:.

The last on_boot lambda also saves the Wi-Fi in use under the one fixed key ESPHome looks in when a build has no network of its own at start-up. A build that has one - this image, once the tool writes the Wi-Fi in - saves it under a key made from its config and ESPHome version instead. A build of this config with no Wi-Fi in it, such as Home Assistant’s ESPHome builder makes when it adopts the plug, would otherwise find nothing saved, and raise its own network rather than join yours. Keep it.

Keep the two AUTOFLASH-IMPRINT- placeholders as they are (S60-IMPRINT- up to 1.1.0): the tool finds and fills them, and refuses an image in which either appears more than once.

The plug is named esphome and ESPHome’s MAC suffix (esphome-abcdef), as every ESPHome the conversion installs is, whatever the plug, and its own network has that name too; its friendly name is esphome and the same suffix (esphome abcdef: ESPHome joins them with a space). Its comment, Sonoff-S60, says what plug it is, and the tool identifies the plug by it. Up to 1.1.0 it was sonoff-s60-abcdef, its network Sonoff-Setup-CDEF.

archive/esphome-prototype/FINDINGS.md records how this route was worked out and tested.