Template for creating Widgets for the WG-Display-Embedded, included with an automatic build pipeline to create the final widget binary.
Widgets are Rust crates compiled to wasm32-unknown-unknown WebAssembly and packaged as WASM components using the WIT Component Model. They implement a standard interface (defined in WIT) and are installed and managed through the Web UI without reflashing the device.
Use the Widget Template as a starting point:
git clone https://github.com/Siryll/wg_display_embedded_widget_template my-widget
cd my-widgetThe template includes the correct Cargo.toml, WIT files, and a working lib.rs example.
For reference implementations see:
wg_display_embedded_widget_time— small time widgetwg_display_embedded_widget_aareguru— widget to display the current Aare temperaturewg_display_embedded_widget_public_transport— configurable widget to show public transport connections
- Push your widget to GitHub
- Create a new Tag, the Pipeline will automatically build and put the
widget.precompiled.wasmbinary into the release. - In the Web UI of the embedded WG-Display, click Install from URL and enter the direct download URL
package widget:widget;
export get-name: func() -> string;
export get-version: func() -> string;
export get-config-schema: func() -> string;
export get-run-update-cycle-seconds: func() -> u32;
export run: func(context: widget-context) -> widget-result;| Export | Return | Description |
|---|---|---|
get-name |
string |
Display name shown in the Web UI and on screen |
get-version |
string |
Semver string (e.g. "0.1.0") |
get-config-schema |
string |
JSON Schema for the configuration form. Return "{}" if no config is needed |
get-run-update-cycle-seconds |
u32 |
How often the widget should be invoked (seconds) |
run |
widget-result |
Main entry point — fetch data, compute output, return string |
record datetime {
seconds: u64,
nanoseconds: u32,
}
record widget-context {
last-invocation: datetime, // UTC time of previous run (0 on first run)
config: string, // JSON matching your config schema
}
record widget-result {
data: string, // Text displayed on screen (or error message)
}interface http {
type status = u16;
variant method { get, head, post, put, delete }
record response {
status: status,
content-length: option<u64>,
bytes: list<u8>,
}
request: func(method: method, url: string, body: option<list<u8>>) -> result<response>;
}Limitations:
- Maximum response size: ~512 KB (HTTP client buffer limit)
- Timeout: 30 seconds
- No TLS certificate verification
- Redirects: automatic, up to 5 hops
- No streaming — entire response is buffered
interface clocks {
now: func() -> datetime;
}Returns the current UTC time (synced from timeapi.io at boot). seconds is a Unix timestamp; nanoseconds is the sub-second part. Returns {0, 0} if time has not been synced yet.
interface logging {
enum level { debug, info, warn, error }
log: func(level: level, context: string, message: string);
}Output appears in the defmt serial monitor.
interface random {
get-random: func() -> u64;
}Returns a 64-bit random value from the ESP32 hardware RNG. Not cryptographically secure.
Return a valid JSON Schema from get-config-schema. The Web UI uses the jsonform library to render a form from the schema automatically.
In run(), parse the config from context.config:
#[derive(serde::Deserialize)]
struct Config {
location: String,
}
fn run(context: WidgetContext) -> WidgetResult {
let config: Config = serde_json::from_str(&context.config)
.unwrap_or(Config { location: "Bern".into() });
// ...
}git submodule update --initrustup target add wasm32-unknown-unknown
cargo build --release --manifest-path widget/Cargo.toml --target wasm32-unknown-unknownInstall wasm-tools:
cargo install wasm-toolswasm-tools component new \
widget/target/wasm32-unknown-unknown/release/widget.wasm \
-o widget/widget.component.wasmThe on-device runtime is unable to compile the binary at runtime, for that reason it needs to be compiled for the xtensa architecture beforehand.
cargo build --release --manifest-path wg_display_embedded_precompiler/Cargo.toml
./wg_display_embedded_precompiler/target/release/wg-display-embedded-precompiler widget/widget.component.wasm widget.precompiled.wasmIf your widget exceeds ~512 KB after precompilation, the installation will fail with an HTTP error. Reduce size by:
- Minimizing dependencies
- Using
default-features = falseon all crates - Avoiding large embedded data (fonts, lookup tables)
- Running
wasm-opt -Osbefore the component/precompile steps