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