VS Code project template¶
FrSkyRC/ethos-lua-vscode-template is a complete VS Code project for Ethos Lua scripts. Press ▶ to deploy to the simulator or a USB radio, stream the radio's print() output, translate your script, and publish installable release zips from a git tag.
The deploy tooling is the same one large open-source Ethos suites use every day, with the project-specific parts removed. To understand how it works underneath (USB HID, drive discovery, safe copying), read VS Code deployments.
The template's starter tool in the Ethos 26.1 simulator, deployed with the German translation.
Quick start¶
- On GitHub, click Use this template → Create a new repository, then clone it.
- Install Python and VS Code.
-
Rename the starter project:
-
Open the folder in VS Code and accept the recommended extensions (Ethos, Python, Python Debugger, Lua).
- Click the Ethos status-bar item → pick radio, protocol and release.
- Run and Debug (Ctrl+Shift+D) → Deploy & Launch [SIM] → ▶.
Installing Python¶
The play buttons run Python scripts, so you need Python 3.10 or newer once per computer.
Install from python.org/downloads. On the first installer screen tick Add python.exe to PATH. Open a new terminal and check:
Then in VS Code: Ctrl+Shift+P → Python: Select Interpreter and choose it. The play buttons use the interpreter you select here.
You don't need to install packages by hand. The first deploy installs tqdm, pyserial, hidapi (and pywin32 on Windows) into that interpreter. To do it yourself: python -m pip install tqdm pyserial hidapi pywin32.
What you change per project¶
The one table to read
Everything you edit is listed here. .vscode/scripts/, bin/package/ and .github/ are shared tooling: don't edit them. That way you can update them from the template later, and every project built on it behaves the same.
| File | Change | Set by new_project.py? |
|---|---|---|
src/<project>/ |
Your Lua code: everything that runs on the radio (deployed to SCRIPTS:/<project>/) |
Folder renamed |
.vscode/deploy.json → tgt_name |
Project folder name; must match src/<project>/ |
✔ |
src/<project>/main.lua → BASE |
SCRIPTS:/<project>/, the absolute path for files used from callbacks (why) |
✔ |
src/<project>/ethos_lua_manifest.json |
name, key, version, introduction. key never changes after the first release |
✔ (name, key, folder) |
src/<project>/widget.lua → key |
Widget key, max 7 chars. Never changes after release | ✔ |
i18n/<lang>.json |
Your text, one file per language | project.name in en.json |
.vscode/launch.json → inputs.deployLanguage.options |
The languages offered by Set Deployment Language | |
.vscode/settings.json → ethos.board / protocol / release |
The simulator, normally set through the Ethos extension | |
.vscode/deploy.json → serial_*, ethossuite_bin |
Only for unusual radios. FrSky Suite is optional | |
Releases.md |
Release notes, one # X.Y.Z section per version |
|
README.md, AGENTS.md |
Describe your project | src/ paths |
The play buttons¶
Each entry in the Run and Debug dropdown is a launch.json configuration. Its preLaunchTask runs a task from tasks.json, which calls the deploy script with the Python interpreter you selected:
flowchart LR
P["▶ Deploy & Launch [SIM]<br/>(launch.json)"] -->|preLaunchTask| T["task: deploy.py --lang de --step i18n<br/>(tasks.json)"]
T --> S["simulators/X20S_FCC@nightly26/scripts/myproject/"]
P -->|postDebugTask| E["ethos.start<br/>(Ethos extension)"]
| ▶ Configuration | Runs | Result |
|---|---|---|
| Deploy & Launch [SIM] | deploy.py --lang <lang> --step i18n → ethos.start |
Mirrors src/<project>/ into the selected simulator, resolves translations, starts the simulator |
| Deploy Radio | deploy.py --radio ... |
Finds the radio over USB HID, switches to storage if needed, waits for its drive, full copy, then switches the radio to serial mode so it releases USB storage and runs the new code |
| Deploy Radio [Fast] | --fileext fast --radio |
Copies only changed files (MD5) and removes deleted ones. The everyday choice |
| Deploy Radio + Serial Debug (and [Fast]) | ... --radio-debug |
Deploys, then streams the radio's print() output until stopped |
| Radio: Serial Debug | --radio --connect-only |
No copy; stream print() output |
| Simulator: Set Simulator / Open Controls / Open Telemetry | Ethos extension commands | |
| Set Deployment Language | set_language.py <lang> |
Sets ethos.deploy.language for every deploy |
| Package: Build Zip [current language] / [all languages] | bin/package/build_package.py |
Installable zips in dist/ |
| Debug Radio Connection | debug_connection.py |
Diagnoses USB/HID/drive detection |
| Clear locks | deploy.py --clear-lock |
Removes a stale lock after a crashed deploy |
Deploy steps. --step <name> runs .vscode/scripts/deploy_step_<name>.py --out-dir <deployed folder> --lang <lang> --git-src <repo> after copying, on a local staging copy for radio deploys. The template ships i18n and a commented example step that writes build_info.lua. Add your own for generated files, sound packs, or stripping debug files.
Translations¶
User-facing text goes in Lua strings as tags, and the words go in i18n/<lang>.json:
Deploys and packages replace each tag with the selected language's text, falling back to English. On the radio the code is plain text with no lookup cost and no translation tables in RAM, which is why large projects use this approach rather than loading translations at runtime. Tags can transform text (@i18n(tool.print):upper()@, :truncate(8)); python .vscode/scripts/resolve_i18n_tags.py --list-transforms lists them. Missing keys are reported after each deploy.
For runtime language switching without a build step, see Translations.
Releases and git tags¶
Users install your script with FrSky Suite → Lua library → Install from local .zip or the web simulator's Upload a Lua plugin. The template builds those zips (one per language, each with a generated ethos_lua_manifest.json) and publishes them from a git tag.
To release version 1.2.0:
- Set
"version": "1.2.0"insrc/<project>/ethos_lua_manifest.json. -
Add a section at the top of
Releases.md: -
Commit, then tag and push the tag:
The Release workflow builds a zip per i18n/*.json language, validates each manifest, and publishes a GitHub release with the Releases.md notes. The same notes appear in FrSky Suite's Lua library.
| Tag | Publishes |
|---|---|
release/1.2.0 |
Release |
release/1.2.0-rc1 |
Release Candidate (pre-release) |
snapshot/1.2.0-dev3 |
Snapshot (pre-release) |
The manifest needs a numeric X.Y.Z, so 1.2.0-rc1 installs as 1.2.0. The full label is kept in build_info.lua inside the zip (return { version = "1.2.0-rc1", lang = "de", commit = "...", built = "..." }), which your script can read at load time.
Wrong tag? git tag -d release/1.2.0 && git push --delete origin release/1.2.0, and delete the GitHub release.
The CI workflow runs on every push and pull request. It builds every language, syntax-checks every Lua file with luac5.4 -p after translation, validates the manifests, and attaches test zips to the run.
Tested¶
Checked on Windows with the X20S simulator (Ethos 26.1):
- rename → simulator deploy in German → boot with no Lua errors → tool opened with translated labels (screenshot above) →
print()output ([hellow] Hallo Ethos) in the log; --all-langspackaging → both zips pass the manifest validator →luac5.4 -pon every file → CI green on GitHub.
The radio deploy path is the suites' code, unchanged apart from names. The USB protocol it uses (HID storage/serial switching, *.cpuid drive discovery, incremental copy) was verified on an X20S in VS Code deployments. The template's own --radio run so far has only shown the expected "radio not on USB" handling, with a clear error after 60 s.