The real dock. motor.* turns it, led.* lights the ring at the base and eyes.* moves the face on the phone. motor.pan yaws the head; motor.tilt rolls the phone on its mount. Limits are the device's: pan is clamped to ±20°, tilt to ±10° and refused past 25° from level, one move per 0.35s. The head's lift is the app's own axis and sits under Host.
Following a face is motor.followFace, not a loop you write.
It hands the job to the app's own tracker, so there is nothing here to show you:
the simulator has no tracker, and it logs that the call is on rather than faking
motion that would teach you the wrong feel. Test that one on a device.
If your plugin already tracks faces by hand, delete that code before you call it. Two control loops arguing move worse than either alone.
Nothing loaded yet.
The simulator provides hairobo before your first script runs, exactly as
the app does. Three things it does not do: it reads your folder off disk and
never consults manifest.json, so a file missing from
files still runs here and is simply absent on the robot; it does not
enforce the no-network rule; and it cannot tell you how your plugin behaves with a
real face. Test on a device before you publish.
Uncheck one to see what your plugin does when it is refused.
The app drives this axis itself, for levelling and auto-centring. No plugin API turns it: lift-down can knock the phone off the mount, so it is gated on live app state a plugin does not have. See the API docs.
Synthetic, at the device's 6 Hz. Nothing like a real room, which is the point.
No model here: assistant.ask answers with an obviously fake reply so you can exercise turn-taking and rendering without spending the user's real allowance. Tick the box to make it reject with "daily limit reached", which is what a spent allowance does on the device. It does not resolve empty, and it does not clear itself, so show the user rather than retrying.
What mic.listen resolves with. Leave it empty and it resolves null. Silence is an ordinary outcome, not an error, and a real plugin hits it constantly. You get the transcript, never audio, and on the device the app draws its own listening indicator, so a plugin cannot listen invisibly.
A character declares mode: "face" and draws the whole face. It receives one state event, sent here on every change and once a second, carrying state, mood, gaze, idle seconds, and docked and dimmed from State above. The eyes go drowsy at 45s and asleep at 70s on the device, so match that unless you have a reason not to.
Two things this simulator cannot show you. The app draws its listening indicator, captions, settings button and dock badge on top of your character, and a character takes no touches: tapping the face talks to the robot. Build it to be watched, not tapped.
Sends a command event, { text, phrase }. On the device your phrases
are tried after every built-in command and before the assistant, and the plugin
runs with no screen: it hears the words, acts, and stops.
While a plugin answers, the app draws its name on the robot's face, and the plugin cannot draw, hide or fake it. The badge over the plugin panel here is a stand-in for that, so nothing teaches you that you own the whole screen. It comes down when your plugin calls command.done, as on the device.