Skip to main content
Version: 1.0

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:

  1. Manifest — Define what controls your device exposes
  2. Script Development — Implement hardware synchronization logic
  3. Web UI Development — Build the user interface with Svelte
  4. TMCU — Reference for TMCU datapoint communication
  5. UART — Reference for UART serial communication