TMCU
TMCU communication uses numbered datapoints to exchange data with an external microcontroller. Each datapoint has a unique ID and data type.
Getting the TMCU Handle
const tmcuHandle = TMCU.get();
Reading Datapoints
Get a handle to a datapoint to read its current value and listen for changes:
const dp = tmcuHandle.getDatapoint(1);
if (dp) {
console.log('Current value:', dp.v);
dp.on('change', (res) => {
console.log('New value:', res.v);
});
}
Writing Datapoints
Send values to the hardware:
tmcuHandle.writeDatapoint(dpId, dataType, value);
Parameters:
dpId— Datapoint ID numberdataType— Data type constant:TMCU.DT_BOOL— BooleanTMCU.DT_INT— Signed 32-bit integerTMCU.DT_ENUM— Enumeration (integer index)TMCU.DT_RAW— Raw binary data
value— Value to send
Example:
tmcuHandle.writeDatapoint(1, TMCU.DT_BOOL, true);
tmcuHandle.writeDatapoint(2, TMCU.DT_INT, 100);
Bidirectional Synchronization Pattern
The core pattern for synchronizing hardware with virtual components:
Device to App
Listen for hardware changes and update the virtual component. Read the current value on startup, then subscribe to future changes:
const DP_STATE = 1;
const stateHandle = Service.getVCHandle('state');
const tmcuHandle = TMCU.get();
const dpState = tmcuHandle.getDatapoint(DP_STATE);
if (dpState) {
// Push initial value
if (dpState.v !== stateHandle.getValue()) {
stateHandle.setValue(dpState.v);
}
// Prevent update loop: only update if value actually changed
dpState.on('change', (res) => {
if (res.v !== stateHandle.getValue()) {
stateHandle.setValue(res.v);
}
});
}
App to Device
Listen for virtual component changes (user interactions) and write to hardware:
stateHandle.on('change', ({ value }) => {
tmcuHandle.writeDatapoint(DP_STATE, TMCU.DT_BOOL, value);
});
TMCU Examples
Simple Boolean Sync
For on/off controls:
const DP_STATE = 1;
const tmcuHandle = TMCU.get();
const stateHandle = Service.getVCHandle('state');
// Device to app
const dpState = tmcuHandle.getDatapoint(DP_STATE);
if (dpState) {
if (dpState.v !== stateHandle.getValue()) {
stateHandle.setValue(dpState.v);
}
dpState.on('change', (res) => {
if (res.v !== stateHandle.getValue()) {
stateHandle.setValue(res.v);
}
});
}
// App to device
stateHandle.on('change', ({ value }) => {
tmcuHandle.writeDatapoint(DP_STATE, TMCU.DT_BOOL, value);
});
Number with Transformation
For values that need scaling or unit conversion:
const DP_BRIGHTNESS = 2;
const tmcuHandle = TMCU.get();
const brightnessHandle = Service.getVCHandle('brightness');
// Hardware: 0-255, Component: 0-100%
const syncBrightness = (raw: unknown) => {
if (typeof raw !== 'number') return;
const percent = Math.round((raw / 255) * 100);
if (percent === brightnessHandle.getValue()) return;
brightnessHandle.setValue(percent);
};
// Device to app
const dpBrightness = tmcuHandle.getDatapoint(DP_BRIGHTNESS);
if (dpBrightness) {
syncBrightness(dpBrightness.v);
dpBrightness.on('change', (res) => syncBrightness(res.v));
}
// App to device
brightnessHandle.on('change', ({ value }) => {
const hwValue = Math.round((value / 100) * 255);
tmcuHandle.writeDatapoint(DP_BRIGHTNESS, TMCU.DT_INT, hwValue);
});
Read-Only Sensor
For sensors and status that users cannot change:
const DP_TEMP = 3;
const tmcuHandle = TMCU.get();
const temperatureHandle = Service.getVCHandle('temperature');
// Only device to app, no write listener
const syncTemp = (raw: unknown) => {
if (typeof raw !== 'number') return;
const temp = raw / 10; // Hardware reports as integer tenths
if (temp === temperatureHandle.getValue()) return;
temperatureHandle.setValue(temp);
};
const dpTemp = tmcuHandle.getDatapoint(DP_TEMP);
if (dpTemp) {
syncTemp(dpTemp.v);
dpTemp.on('change', (res) => syncTemp(res.v));
}
Enum/Mode Selection
For predefined options like heat/cool/off:
const DP_MODE = 2;
const MODES = ['off', 'heat', 'cool'];
const tmcuHandle = TMCU.get();
const modeHandle = Service.getVCHandle('mode');
// Device to app: hardware sends index, we map to string
const syncMode = (index: number) => {
const mode = MODES[index];
if (mode === modeHandle.getValue()) return;
modeHandle.setValue(mode);
};
const dpMode = tmcuHandle.getDatapoint(DP_MODE);
if (dpMode) {
syncMode(dpMode.v);
dpMode.on('change', (res) => syncMode(res.v));
}
// App to device: user selects string, we map to index
modeHandle.on('change', ({ value }) => {
const index = MODES.indexOf(value);
tmcuHandle.writeDatapoint(DP_MODE, TMCU.DT_INT, index);
});
Multiple Components
Complex devices with many independent controls:
const DP_STATE = 1;
const DP_BRIGHTNESS = 2;
const DP_COLOR_TEMP = 3;
const tmcuHandle = TMCU.get();
const stateHandle = Service.getVCHandle('state');
const brightnessHandle = Service.getVCHandle('brightness');
const colorTempHandle = Service.getVCHandle('color_temperature');
// Sync state
const dpState = tmcuHandle.getDatapoint(DP_STATE);
if (dpState) {
if (dpState.v !== stateHandle.getValue()) {
stateHandle.setValue(dpState.v);
}
dpState.on('change', (res) => {
if (res.v !== stateHandle.getValue()) {
stateHandle.setValue(res.v);
}
});
}
stateHandle.on('change', ({ value }) => {
tmcuHandle.writeDatapoint(DP_STATE, TMCU.DT_BOOL, value);
});
// Sync brightness
const dpBrightness = tmcuHandle.getDatapoint(DP_BRIGHTNESS);
if (dpBrightness) {
if (typeof dpBrightness.v === 'number' && dpBrightness.v !== brightnessHandle.getValue()) {
brightnessHandle.setValue(dpBrightness.v);
}
dpBrightness.on('change', (res) => {
if (typeof res.v !== 'number') return;
if (res.v === brightnessHandle.getValue()) return;
brightnessHandle.setValue(res.v);
});
}
brightnessHandle.on('change', ({ value }) => {
tmcuHandle.writeDatapoint(DP_BRIGHTNESS, TMCU.DT_INT, value);
});
// Sync color temperature
const dpColorTemp = tmcuHandle.getDatapoint(DP_COLOR_TEMP);
if (dpColorTemp) {
if (typeof dpColorTemp.v === 'number' && dpColorTemp.v !== colorTempHandle.getValue()) {
colorTempHandle.setValue(dpColorTemp.v);
}
dpColorTemp.on('change', (res) => {
if (typeof res.v !== 'number') return;
if (res.v === colorTempHandle.getValue()) return;
colorTempHandle.setValue(res.v);
});
}
colorTempHandle.on('change', ({ value }) => {
tmcuHandle.writeDatapoint(DP_COLOR_TEMP, TMCU.DT_INT, value);
});