# Custom wake word ("Rufus") Javis uses [openWakeWord](https://github.com/dscripka/openWakeWord). The built-in models are English phrases (`hey_jarvis`, `alexa`, `hey_mycroft`, `hey_rhasspy`, `timer`, `weather`). Anything else — e.g. **Rufus** — is a small custom `.onnx` classifier that you train once on synthetic speech (no recordings of your own voice needed) and drop into Javis. Slaves download it from the master automatically. ## 1. How Javis loads wake-word models `wake_word.model` in `config.yaml` accepts three forms: | Value | Meaning | |-------|---------| | `hey_jarvis` | built-in openWakeWord model | | `rufus` | custom model at `/models/wakeword/rufus.onnx` (default `~/.javis/models/wakeword/rufus.onnx`) | | `D:/models/rufus.onnx` | explicit path to a custom model | The detector keys predictions by the file stem, so the file **must** be named `.onnx`. Slaves receive the bare name (`rufus`) from the master and fetch the file via `GET /api/v1/wake_word/model` on first use, caching it under `/models/rufus.onnx`. ## 2. Train the model Training runs on **Linux** (WSL2 on Windows works) or free Google Colab; it needs a GPU for the ~10 min synthetic-speech step but finishes in well under an hour. ### Option A — Colab (no setup) Open openWakeWord's [automatic_model_training.ipynb](https://github.com/dscripka/openWakeWord/blob/main/notebooks/automatic_model_training.ipynb) in Colab, run the cells top to bottom, and change only the config cell: ```python config["target_phrase"] = ["rufus", "hey rufus", "roofus", "hey roofus"] config["model_name"] = "rufus" config["n_samples"] = 5000 # positives; more = better, 20k+ for production config["n_samples_val"] = 1000 config["steps"] = 20000 ``` Download `my_custom_model/rufus.onnx` when it finishes. ### Option B — locally (WSL2 / Linux, reproducible) ```bash # one-time environment python3 -m venv ~/oww && source ~/oww/bin/activate git clone https://github.com/dscripka/openwakeword && pip install -e ./openwakeword git clone https://github.com/rhasspy/piper-sample-generator wget -O piper-sample-generator/models/en_US-libritts_r-medium.pt \ https://github.com/rhasspy/piper-sample-generator/releases/download/v2.0.0/en_US-libritts_r-medium.pt pip install piper-phonemize webrtcvad mutagen torchinfo torchmetrics speechbrain \ audiomentations torch-audiomentations acoustics pronouncing datasets deep-phonemizer # data: room impulse responses, background noise, pre-computed negative features # (copy the "Download Data" cells from the notebook — ~3 GB total) # config cp openwakeword/examples/custom_model.yml rufus.yml # target_phrase: ["rufus", "hey rufus", "roofus", "hey roofus"] # model_name: rufus # n_samples: 5000 n_samples_val: 1000 steps: 20000 # background_paths: ["./audioset_16k", "./fma"] # false_positive_validation_data_path: validation_set_features.npy # feature_data_files: {ACAV100M_sample: openwakeword_features_ACAV100M_2000_hrs_16bit.npy} python openwakeword/openwakeword/train.py --training_config rufus.yml --generate_clips python openwakeword/openwakeword/train.py --training_config rufus.yml --augment_clips python openwakeword/openwakeword/train.py --training_config rufus.yml --train_model # → my_custom_model/rufus.onnx ``` ### Tips that matter for a German household - **Spell out the German pronunciation.** The synthetic voices are English; add phonetic variants to `target_phrase` so the model hears the "u" you actually say: `"roofus"`, `"ruufus"`. Include both the bare name and `"hey "`. - **Add adversarial negatives.** `custom_negative_phrases` in the YAML (e.g. `["rufen", "ruf mal", "ruhe", "bus", "roof"]`) sharply cuts false wakes on similar-sounding words. - **Two-syllable names with a hard consonant** ("Rufus", "Jarvis") work far better than short or vowel-heavy ones. - Short names false-trigger more: expect to run `threshold: 0.6–0.7` rather than the default 0.5. Tune in **Settings → Wake Word**; the Status tab shows the live score. ## 3. Install in Javis ```powershell mkdir $env:USERPROFILE\.javis\models\wakeword copy rufus.onnx $env:USERPROFILE\.javis\models\wakeword\ ``` `config.yaml`: ```yaml wake_word: model: rufus threshold: 0.6 ``` Restart Javis. The log shows `openWakeWord loaded (…\rufus.onnx, key=rufus, …)`. Paired slaves pick up the new name on their next config poll (≤ 60 s), download the model from the master and reload the detector — nothing to copy by hand. For a slave that cannot reach the master at that moment, place the file at `~/.javis-slave/models/rufus.onnx` (or `%LOCALAPPDATA%\JavisSlave\models\`). ## 4. Verify - Say the name at normal speaking distance: the wake chime plays and the Status tab's *Wake score* bar jumps. Watch for a minute of normal conversation without triggers; if it fires spuriously, raise the threshold or retrain with more negatives. - Quick offline check without Javis: ```powershell python -c "from openwakeword.model import Model; m=Model(wakeword_models=[r'%USERPROFILE%\.javis\models\wakeword\rufus.onnx'], inference_framework='onnx'); print(m.predict_clip(r'test.wav'))" ``` ## Troubleshooting | Symptom | Cause / fix | |---------|-------------| | `wake word model 'rufus' is neither a built-in … nor a custom .onnx` | File missing or misnamed — it must be `rufus.onnx` in `models/wakeword/`. | | Slave says `Wake-Word-Modell 'rufus' konnte nicht geladen werden` | Master unreachable during download or master itself has no custom file; check `GET http://master:8765/api/v1/wake_word/model` returns 200 with slave credentials. | | Fires on TV / music | Raise `threshold`; retrain with more `background_paths` hours and `custom_negative_phrases`. | | Misses when spoken quickly / far away | Add phrase variants, more `n_samples` (20k+), and set `augmentation_rounds` higher in the YAML. |