Skip to main content
Version: 1.0

Script Development

The script (script.svc.ts) is the runtime logic that runs on your device.

The script synchronizes changes bidirectionally: when hardware reports a change, the script updates the virtual component; when the user interacts with the UI (changing a virtual component), the script writes the change to hardware.

Start with the Manifest to define your device's controls, then use this guide to implement the script logic.

Virtual Component Handles​

Get a handle to a virtual component using its role (the key in vc from your manifest):

const stateHandle = Service.getVCHandle('state');
const brightnessHandle = Service.getVCHandle('brightness');

Component Handle Methods​

// Get the current value
const currentValue = stateHandle.getValue();

// Set a new value
stateHandle.setValue(true);

// Get manifest configuration
const config = stateHandle.getConfig();

// Listen for changes
stateHandle.on('change', ({ value }) => {
console.log('New value:', value);
});

// For button components, listen for triggers
openHandle.on('single_push', () => {
// Button was pressed
});

Hardware Communication​

Firmware extensions talk to hardware through a few channels:

  • UART — Direct serial byte stream to an external microcontroller.
  • TMCU — Numbered datapoints exchanged with an integrated MCU.

UART Example​

Direct serial communication with external devices via UART protocol.

Configuration​

Get and configure a UART port:

const uart = UART.get(0);  // Port 0
uart.configure({ baud: 115200, mode: '8N1' });

Parameters:

  • port — UART port number (0, 1, etc.)
  • baud — Baud rate (9600, 19200, 38400, 57600, 115200, etc.)
  • mode — Data format:
    • 8N1 — 8 data bits, no parity, 1 stop bit
    • Other modes: 8E1, 8O1, etc.

Writing Data​

Send strings to the external device:

uart.write('HELLO\n');
uart.write('STATE:ON\n');
uart.write('DIM:50\n');

Reading Data​

Listen for incoming data:

uart.recv((data: string) => {
console.log('Received:', data);
// Parse and handle the data
});

Bidirectional Sync​

const uart = UART.get(0);
uart.configure({ baud: 115200, mode: '8N1' });

const stateHandle = Service.getVCHandle('state');

let updatingFromUart = false;

// Device to app
uart.recv((data: string) => {
updatingFromUart = true;
stateHandle.setValue(data === 'CMD_ON');
updatingFromUart = false;
});

// App to device
stateHandle.on('change', (data: any) => {
if (updatingFromUart) return;
uart.write(data.value ? 'CMD_ON' : 'CMD_OFF');
});

See the UART page for more examples (state + brightness, button actions, etc.).

Timers and Intervals​

For time-based functionality like elapsed time or periodic updates:

const chargeHandle = Service.getVCHandle('charge');
const durationHandle = Service.getVCHandle('duration');

let chargeStartTime: number | null = null;
let intervalTimer: number | null = null;

chargeHandle.on('change', ({ value }) => {
if (value) {
// Start
durationHandle.setValue(0);
chargeStartTime = Date.now();
intervalTimer = setInterval(() => {
if (chargeStartTime) {
const minutes = Math.floor((Date.now() - chargeStartTime) / 60_000);
durationHandle.setValue(minutes);
}
}, 1000);
} else {
// Stop
chargeStartTime = null;
if (intervalTimer) {
clearInterval(intervalTimer);
intervalTimer = null;
}
}
});

Button Triggers​

For button components (which have no stored value), respond to single_push:

const openHandle = Service.getVCHandle('open');
const positionHandle = Service.getVCHandle('position');

openHandle.on('single_push', () => {
// User pressed the button in the app
uart.write('ACTION_EXPAND');
// Optionally update a position component to reflect the action
positionHandle.setValue(100);
});

Next: Web UI Development