Skip to content
LBougxPublic

About

Replace your Xiaomi Robot Vacuum S20's voice prompts with your own recordings, over the local network. No cloud, no rooting.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

s20-voice

Give your robot vacuum your own voice.

Replace the Xiaomi Robot Vacuum S20's voice prompts with your own recordings — entirely over your local network. No cloud account, no rooting, no soldering.

tested on firmware python licence


You get a small web app that runs on your own machine. It lists all 80 prompts, tells you when each one is spoken, lets you drop in a recording, plays it back through the robot's own speaker so you can check it before committing, matches its loudness to the factory pack, and installs.

On Windows: download, unzip, double-click Console.bat. The launcher installs what is missing, creates the config file and opens it for you — you only supply your vacuum's IP address and token. Details below.

┌─ Voice console ──────────────────── Docked · 100% · volume 10/10 · fr_FR ──┐
│  9 replaced of 80            [All] Replaced Original    [Settings] [Install] │
├──────────────────────────────────────────────────────────────────────────────┤
│  Localisation · for finding it, and for it to find itself        5 prompts   │
│                                                                              │
│  ▶  11_1.mp3   " Je suis là, je suis là, je suis là "                        │
│                You press "find robot" in the app, to locate it under a sofa. │
│                2.35 s  +3.4 dB      [On the robot] [▶ my voice] [Restore]    │
└──────────────────────────────────────────────────────────────────────────────┘

The finding that makes this work

The vacuum accepts a voice pack from any HTTP server — but only if the URL reproduces the path used by Xiaomi's CDN:

http://<your-pc>:8088/ijai-2s-map.record/multilanguage/xiaomi3/S20_20240110/fr_FR.tgz

Serve the identical archive from http://<your-pc>:8088/fr_FR.tgz and the robot downloads it, reports 100 % progress — and silently ignores it. The domain is never checked; only the shape of the path is. That one detail took fifteen failed attempts to isolate, and it is why no DNS spoofing, no TLS certificate and no cloud call are needed.

Three traps worth knowing before you experiment:

  • Installing the language that is already active does nothing. You must switch to a different language code first. The app does this for you by bouncing through Xiaomi's official German pack.
  • The status codes tell you nothing. State 15 dropping to 0 within three seconds is the normal pattern — for success and failure alike. It is exactly what Xiaomi's own working URL produces. Only listening tells you.
  • Never test with a single replaced file. That mistake produced a wrong diagnosis for days: the pack was installing perfectly, but the prompt being triggered was a different file than assumed.

The full protocol, the measurements and every dead end are in FINDINGS.md.


Get started — Windows

Two double-clicks. The launcher does everything else.

  1. Download. Green Code button above → Download ZIP → unzip it anywhere.

  2. Double-click Console.bat. It checks what you need, installs anything missing, then creates config.json and opens it for you.

  3. Fill in two values in the file it just opened, then save and close:

    {
      "ip": "192.168.1.42",
      "token": "0123456789abcdef0123456789abcdef"
    }
  4. Double-click Console.bat again. It downloads the official voice pack and opens the app in your browser.

That's it. No terminal, no pip, no git.

The two things you have to supply

Where to get it
IP address your router's device list, or the Mi Home app under the vacuum's settings
miIO token Xiaomi-cloud-tokens-extractor — sign in with your Xiaomi account, it prints the token

The launcher handles the rest: it installs cryptography and ffmpeg if they are missing, and fetches the official pack. Python 3.9+ is the one thing it cannot install for itself — if it is absent, the launcher tells you and points you at python.org. Tick "Add python.exe to PATH" during that install.

config.json is gitignored: your token never leaves your machine.

macOS, Linux, or if you prefer a terminal

git clone https://github.com/LBougx/s20-voice.git
cd s20-voice
pip install cryptography                 # ffmpeg must be on your PATH

cp config.example.json config.json       # then fill in ip + token
python telecharger_pack.py               # fetch the official pack
python console.py                        # opens http://localhost:8088

Not French?

Set lang to your pack's language code and everything follows — telecharger_pack.py fetches that pack and the app works on it:

{ "ip": "…", "token": "…", "lang": "de_DE", "interface": "en" }

The interface picks its own language from lang, or you can pin it with interface (fr and en ship today). If no prompt mapping exists for your language yet, rows show filenames only and the app says so — the mechanism still works, you just don't get the "what triggers this" column until someone contributes one.

python-miio is deliberately not a dependency: its netifaces requirement has no prebuilt wheel past Python 3.8. Everything here speaks the miIO protocol directly, with cryptography as the only third-party package.


Using it

Each prompt gets a row: play the original, read what triggers it, drop in your own audio in any format — conversion is automatic — and hear it on the robot.

On the robot sends the sound to the vacuum and plays it through its speaker in about five seconds. It borrows prompt 11_18, which nothing triggers in normal use, so your other customisations stay untouched. Under the hood it calls MIoT action 7.11 (set-listening-voice) — the sample the Mi Home app plays when you pick a voice.

Two different volumes

Worth being explicit about, because confusing them wastes time:

Setting What it changes Takes effect
Vacuum volume (0–10) the device's own setting, MIoT 4.2 instantly
Recording level (LUFS) the gain applied to your files after re-encoding

Home recordings typically land 3 to 12 dB below Xiaomi's prompts — which is why your voice sounds like a whisper between two announcements. Eleven decibels is heard as roughly half as loud. The default target, −17.9 LUFS, is the median of the official pack.

Your source files are kept in mes_voix/_sources/, so changing the level re-encodes from the originals — no cumulative loss however often you adjust it.

High fidelity

On by default. The 78 spoken prompts are 16 kHz / 32 kb/s, but the pack's two non-verbal sounds are 44.1 kHz — up to 320 kb/s, and one is stereo. The decoder handles far better than speech quality, so your recordings go in at 44.1 kHz / 128 kb/s instead of being crushed.


What's in here

File Role
console.py the whole tool: local web app, miIO client, pack builder
Console.bat Windows launcher — checks Python, installs deps, opens the browser
telecharger_pack.py downloads the official Xiaomi pack into packs/
lire_aspi.py read-only diagnostic; masks your token and MAC in its output
data/fr_FR.json the 80 prompts: text, trigger, duration, grouped by situation
data/ui.json interface strings, one block per language
FINDINGS.md the protocol, the measurements, the dead ends

Source comments are in French; the interface and this documentation are translated. The method applies to any language pack.

data/fr_FR.json may help you even if you never run the code: all 80 filenames mapped to what the robot says and when. That mapping was done by ear, and the numbering is not sequential — 11_1 is the "find robot" phrase, 11_18 a shorter variant of it that nothing ever triggers.


Beyond voice

The local protocol exposes much more, all without the cloud:

  • 4.1 — plays the locate prompt on demand, turning the robot into a remote-triggerable speaker for any script or automation
  • 7.16 — 1=Forward, 2=Left, 3=Right, 4=Back, 5=Stop: manual driving, which the Mi Home app does not offer on this model
  • 3.1, 2.1, 7.8–7.15 — battery, status, and wear on brushes, filter and mop pad, for your own dashboards
  • services 8, 9, 10, 12 — schedules, virtual walls, maps, do-not-disturb

Full spec: miot-spec.org


Ruled out by experiment

So you don't spend time on these:

Approach Result
Public HTTPS hosting (catbox.moe) the robot never downloads at all
Cloud commands rejected with -704220043 before any download
Rebooting after install no effect — the language label survives a power cut while the audio does not change
Rooting xiaomi.vacuum.d106gl is on Dustbuilder's "very unlikely to ever be supported" list

Contributing

Two things are worth contributing, and neither requires touching the code.

A prompt mapping for your language. Identify the 80 prompts in de_DE, en_US, it_IT… and drop a data/<lang>.json following the shape of data/fr_FR.json. The app picks it up automatically. Expect it to take about 40 minutes by ear — the numbering gives no clue, and the app's built-in player makes it bearable.

An interface translation. Copy the en block in data/ui.json under a new two-letter key and translate the values. Nothing else to change.

python telecharger_pack.py --toutes sweeps 28 known language codes across the three dated CDN folders, so you can check what exists for your region.

Reports from other firmware versions or nearby models are welcome too: open an issue with the output of lire_aspi.py (it masks your token and MAC for you).


Safety

Everything here is reversible. If a pack misbehaves, change the language from the Mi Home app and the official pack comes straight back — that path was verified before anything else was attempted.

This talks only to a device on your own network, using your own token. Nothing is sent anywhere else.

Xiaomi's audio files are not redistributed here. telecharger_pack.py fetches them on demand from Xiaomi's own public CDN.


Licence

MIT — see LICENSE. Xiaomi's audio files and trademarks belong to Xiaomi; this project is not affiliated with or endorsed by Xiaomi.

About

Replace your Xiaomi Robot Vacuum S20's voice prompts with your own recordings, over the local network. No cloud, no rooting.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages