Firmware Extension Development
Firmware extensions enable custom implementation for Shelly X devices.
Every extension consists of three files working together:
- manifest.svc.json — Defines what controls and properties your device has
- script.svc.ts — Defines the logic that keeps hardware and controls in sync
- web.svc.svelte — Defines the user interface
Complete Example: Smart Light with Brightness
Here's a working example that creates a dimmable light with two key features: on/off button and brightness slider.
manifest.svc.json
{
"handlers": [],
"vc": {
"state": {
"type": "boolean",
"access": "crw",
"config": {
"name": "Power",
"default_value": false,
"meta": { "ui": { "view": "toggle", "titles": ["Off", "On"] } }
}
},
"brightness": {
"type": "number",
"access": "crw",
"config": {
"name": "Brightness",
"default_value": 100,
"min": 0,
"max": 100,
"meta": { "ui": { "view": "slider", "unit": "%" } }
}
}
},
"meta": {
"groups": [{ "type": "switch" }, { "type": "light" }],
"roles_order": ["state", "brightness"]
}
}
script.svc.ts
const uart = UART.get(0);
uart.configure({ baud: 115200, mode: "8N1" });
const stateHandle = Service.getVCHandle('state');
const brightnessHandle = Service.getVCHandle('brightness');
stateHandle.on('change', ({ value }) => {
uart.write(value ? '1' : '0');
});
brightnessHandle.on('change', ({ value }) => {
uart.write(String(value));
});
uart.recv((data: string) => {
const value = parseInt(data.trim(), 10);
if (!isNaN(value)) {
if (value === 0 || value === 1) {
stateHandle.setValue(value === 1);
} else {
brightnessHandle.setValue(value);
}
}
});
web.svc.svelte
<script lang="ts">
export let service: any;
const { state, brightness } = service.components.get();
</script>
<div class="light-control">
<button on:click={() => state.set(!$state.value)}>
{$state.value ? 'ON' : 'OFF'}
</button>
{#if $state.value}
<div class="brightness-control">
<label>Brightness: {$brightness.value}%</label>
<input
type="range"
min="0"
max="100"
value={$brightness.value}
on:change={(e) => brightness.set(+e.target.value)}
/>
</div>
{/if}
</div>
<style>
.light-control { padding: 20px; }
button { padding: 10px 20px; cursor: pointer; }
input[type="range"] { width: 100%; }
</style>
Next Steps
Follow these guides to build a complete firmware extension:
- Manifest — Define what controls your device exposes
- Script Development — Implement hardware synchronization logic
- Web UI Development — Build the user interface with Svelte
- TMCU — Reference for TMCU datapoint communication
- UART — Reference for UART serial communication