Developers

The hairobo API

Injected automatically — you do not include a script. window.hairobo exists before your code runs.

Everything returns a Promise. Calls resolve false rather than throwing when the host declines them — no permission, no dock, rate limited. Write for that.

const spoke = await hairobo.voice.speak('Hello');
if (!spoke) { /* muted, busy, or not permitted — carry on */ }

voice — needs voice

voice.speak(text) → Promise<bool>

Speaks through the robot in its own voice. Capped at 300 characters, and at most one line every 0.4s; a call inside the gap resolves false and is dropped, not queued. Resolves false while the robot is already talking.

eyes — needs eyes

eyes.look(x, y) → Promise<bool>

Points the gaze. Both are roughly −1…1, x right-positive, y up-positive.

eyes.express(name) → Promise<bool>

One of happy, curious, thinking, sad, excited, calm. Unknown names are ignored rather than erroring.

led — needs led

led.set(color) → Promise<bool>

Hex string, e.g. '#37D2F0'. The dock's ring.

led.reset() → Promise<bool>

Back to whatever the app would show. Call this when you finish — though teardown does it for you if you forget.

motor — needs motor · needs a dock

Degrees are relative to where the head is now. The host clamps them, so ask for what you want and let it decide.

motor.pan(degrees) → Promise<bool>

Turns left/right. Clamped to ±20° per call.

motor.tilt(degrees) → Promise<bool>

Rolls the head. Clamped to ±10°, and refused entirely if it would take the phone more than 25° from level — past that the dock's base can strike the phone. Also refused if the app cannot currently read its own angle.

motor.center() → Promise<bool>

Return to level. Takes a second or two of visible movement.

At most one motor move every 0.35s. Anything faster resolves false and is dropped, so an animation loop can call freely without building a backlog.

Movement reads best with a deadzone — turning on every small change looks nervous rather than attentive. See follow-me/.

camera — needs camera

You get positions, never pixels. There is no API that returns a frame, and the image never leaves the phone.

camera.start() → Promise<bool> · camera.stop() → Promise<bool>

Start it, then listen. Stopped automatically when your plugin closes.

Events arrive at roughly 6/second:

hairobo.on('face', (f) => {
  if (!f.detected) return;
  moveEyes(f.x, f.y);      // both roughly -1..1
});

hairobo.on('hand', (h) => { /* h.x only */ });

data — needs data

data.weather() → Promise<object>

Current weather from the app. Your only source of outside information — you have no network of your own.

Always available

info.get() → Promise<object>

App build, whether a dock is connected, which permissions you were granted, whether the screen is dimmed. Check info.get() before offering robot features, rather than discovering there is no dock by being refused.

log(message)

Goes to the app's debug log. Invisible in release builds — your own debugging, not a user-facing channel.

wait(ms) → Promise

on(event, fn)

version — the API version this host provides. Currently 2.

Feature-detect anything new

On an older app, a newer namespace is undefined, and the property access throws before your .catch can run — leaving your plugin silently stuck:

if (!hairobo.camera) {
  status.textContent = 'Needs a newer Hai Robo';
  return;
}

Either that, or set minAppBuild and refuse to install at all.

← Previous manifest.json Next → Permissions
Preorder Deskmate dock

Reserve your dock.

Drop your details and we'll email you the moment it ships, with early-bird pricing.