Voice (optional)¶
Recognises speech from the remote's microphone on the TV. The text is shown on screen and can be sent to a server you run, to a language model through OpenRouter, or both; the answer is shown as text on screen. Answers are not spoken. No audio is recorded or sent anywhere.
It is off and not installed by default. Nothing described here runs until you install it.
Installing¶
Voice in the manager app downloads about 80 MB into /media/developer/cleanyourtv/voice. The checksums are pinned in the
code; Remove voice deletes everything again.
| Part | From |
|---|---|
Speech engine libmoonshine.so and ONNX Runtime |
the moonshine-voice 0.1.5 package on PyPI (MIT) |
| 64-bit C library | ubuntu-base 22.04.5 for arm64 (this TV has a 32-bit userland on a 64-bit kernel, so the 64-bit recogniser brings its own) |
Model tiny-streaming-en (34 million parameters, int8) |
download.moonshine.ai (MIT) |
cytv-asr, a small program that feeds audio to the engine |
this repository (voice/asr.c, built with voice/build.sh) |
Using it¶
| Step | How |
|---|---|
| Choose the button | Voice > Learn the voice button. Try the remote's microphone button; if the hook does not see it, any spare button works. Then switch Listen on the voice button on. |
| Try without a button | Voice > Try it now, or curl -X POST localhost:8765/api/voice/listen |
| Send the text to a server | curl -X POST localhost:8765/api/voice/set -d '{"url":"http://server:port/path","token":"optional"}' |
| Pause LG's voice assistant | Voice > Switch off LG's voice assistant. It pauses LG's voiceconductor process while voice is on, so LG's own screen and its cloud recognition do not start on the same button. A reboot or turning the switch off undoes it. |
On screen a card changes in place while you speak: Listening (moving bars), Recognising or Thinking (dots), then your question in small type above the answer. Reading time follows the length of the answer.
Sending text to your own server¶
The TV sends POST with the JSON body {"text":"...","source":"cleanyourtv","language":"en","time":"..."} and, when a token is set,
Authorization: Bearer <token>. If the server answers with JSON ({"title","text","image","seconds"}) or plain text, the answer
is shown as a card. The address and token are stored in config.json on the TV only.
AI chat (OpenRouter)¶
In Voice, Set up from your phone shows a QR code. Scan it with a phone on the same Wi-Fi and enter:
- your OpenRouter key,
- a model (the list is read from OpenRouter; any model name can be typed),
- instructions, and, if you like, the address of your own server.
The page is open for ten minutes or until you save, at a random address. It is plain HTTP, so use it on a network you trust. Model on the TV chooses among the newest models of the main providers, with their price. AI chat (OpenRouter) switches the answers on and off.
The same from a shell:
curl -X POST localhost:8765/api/voice/set -d '{"llm":{"key":"sk-or-...","model":"openrouter/auto"}}'
curl -X POST localhost:8765/api/voice/set -d '{"llm":{"enabled":true}}'
What you say is sent as text to openrouter.ai with the model you chose (a short instruction asks for one to three plain
sentences; change it with "system"), and the answer appears on screen. The model remembers the last six exchanges for ten
minutes (Forget the conversation clears them). The key is stored in config.json on the TV only. With a server address set
as well, the server also receives the text and the model's answer ({"text","answer"}).
You can also ask by text, with the same result, and without the speech download:
It answers {"text","answer","error"}; add "card":false to skip the screen.
The speech model¶
Moonshine (Moonshine AI, MIT licence) runs on the TV; there is no cloud recogniser.
| Model | tiny-streaming-en: about 34 million parameters, encoder and decoder, int8 weights, 45 MB. |
| Runtime | ONNX Runtime on the CPU (two ARM cores on the G2). |
| Segmentation | The engine's voice-activity detection splits the audio into phrases. The service stops recording after 1.2 s of silence, after 7 s without speech, or after 15 s. |
| Cost on the G2 | Load 0.3 s; a 3 s phrase takes about 1.4 s; about 190 MB of memory while recognising, none when idle. |
| Reported accuracy | 12.0 % average word error rate on the Open ASR Leaderboard datasets, 4.8 % on LibriSpeech test-clean (figures from the Moonshine project; not measured through the remote's microphone). |
| Language | English. Moonshine also publishes German, Spanish, Arabic, Japanese, Mandarin, Tagalog and Vietnamese models and larger English ones (small 123 M, medium 245 M parameters). |
| Audio | 16 kHz mono from the microphone stream of LG's voiceinput service. Held in memory while recognising, then discarded; not written to disk or sent anywhere. |
The TV's programs are 32-bit ARM on a 64-bit kernel and the recogniser is a 64-bit program, so cytv-asr runs through the 64-bit
loader and libraries that the download places in the same folder. Nothing in the system folders is changed.
Only text goes further: to your server, to OpenRouter, or nowhere. LG's own assistant is a separate path with its own cloud recognition; Switch off LG's voice assistant pauses it while voice is on.
Home Assistant (Assist)¶
The recognised text can go to Home Assistant's Assist (POST /api/conversation/process),
which turns on lights, answers questions about your home and so on. Its answer is shown as text on the card. Answers are not spoken.
In Voice, Set up from your phone also asks for:
- the address of Home Assistant, for example
http://homeassistant.local:8123, - a long-lived access token (your Home Assistant profile page, under Security),
- whether to accept a self-signed certificate (only matters for
https://addresses), - which answers first.
Choosing Home Assistant (Assist) in the menu tests the connection and shows the name and version of your Home Assistant.
| Setting | Behaviour |
|---|---|
| Home Assistant first, then the AI (default) | The text goes to Assist. If Assist understood it, its answer is shown. If it did not (no_intent_match), or Home Assistant cannot be reached, the same text goes to the AI chat, when that is switched on. Without the AI, Assist's own message is shown. |
| Home Assistant only | The AI is never asked. |
| The AI only | Home Assistant is not asked. |
Assist agent lists the conversation agents of your Home Assistant (its conversation.* entities) and lets you choose one;
the default is the built-in agent. A conversation id is passed on, so follow-up questions keep their context; a new
conversation starts after ten quiet minutes or after Forget the conversation.
The same from a shell:
curl -X POST localhost:8765/api/voice/set -d '{"ha":{"url":"http://homeassistant.local:8123","token":"eyJ..."}}'
curl -X POST localhost:8765/api/voice/set -d '{"ha":{"route":"ha-only","agent":"conversation.home_assistant","selfSigned":false}}'
curl -X POST localhost:8765/api/voice/ha-test
curl -X POST localhost:8765/api/voice/ask -d '{"text":"turn on the living room light"}'
route is ha-first, ha-only or ai-only; {"ha":{"clear":true}} removes the address and the token. /api/voice/ask answers
{"text","answer","via","error"}, where via is ha or ai.
The token is stored in config.json on the TV only and is never returned by the service. Only the recognised text goes to your
Home Assistant. With a plain http:// address the token is visible on your network.