ControlTile
MoleculeAsync controls: a frame (`ControlTile`) plus `ControlButton`, `ControlSwitch`, `ControlSlider` and `ControlSetpoint`. Each takes the bound `value` and an `onCommit` that returns a Promise, and shows idle -> pending -> confirmed / mismatch / failed with the previous value restored on failure. The slider writes once on release. State machine: `useAsyncControl` (libs/hooks). No transport knowledge; all text is overridable through `messages`.
Heater
<ControlSwitch title="Heater" value={on} onCommit={write} />Heater
<ControlSwitch title="Heater" value={on} onCommit={() => Promise.reject(new Error('Device offline'))} />Heater
<ControlSwitch title="Heater" value={on} reported={deviceState} timeoutMs={2500} onCommit={write} />Fan speed
0 %40 %100 %
<ControlSlider title="Fan speed" value={speed} min={0} max={100} step={5} unit="%" onCommit={write} />Target temperature
<ControlSetpoint title="Target temperature" value={t} min={5} max={30} step={0.5} unit="°C" decimals={1} onCommit={write} />Gateway
Gateway
Gateway
<ControlButton label="Reboot" confirm="typed" confirmText="reboot" onCommit={(c) => api.reboot(c)} />Heater
Any control goes in the slot.
You do not have permission to use this control.
<ControlSwitch title="Heater" value={on} readOnly onCommit={write} />