Skip to content

Events

Whenever something happens to the robot, the voice server posts a JSON event to every subscriber you have configured. A subscriber is any HTTP endpoint that accepts a POST. This is how a brain — personality, memory, vision — plugs in without anybody editing anybody else’s source.

In apiConfig.json:

"events": {
"enable": true,
"urls": ["http://127.0.0.1:8090/v1/events"]
}

The first URL is the primary subscriber. For a handful of events the server waits for its reply and acts on it. Every other URL is notified asynchronously and its response is discarded.

Delivery is deliberately weak: one retry after 500 ms, then the event is dropped and logged. Events never block the audio or gRPC path, and the outbound queue holds 256. Losing an event must never cost the robot a syllable.

{
"event": "sensor.pet",
"ts": "2026-09-02T19:40:12.345Z",
"esn": "005037a5",
"data": { }
}

esn is the robot’s serial number, so one server can carry several robots. data is per-event and may be absent.

EventFires whendata
wake_wordthe robot opens a voice stream, from the wake word or the back button
transcriptspeech-to-text finishedtext
intent.matchedan intent was chosen for the utteranceintent, text, params
response.startthe server starts a language-model answertext
response.endthat answer finished, spoken or interrupted
tts.start / tts.endthe robot speaks a line through the say helpertext on start
button.pressedthe back button interrupted a response
sensor.pickup / sensor.putdownhe was lifted, or set down
sensor.petthe touch sensor became active
sensor.chargerhis docked state changedon (bool)
face.seenthe camera recognised, or failed to recognise, a face; at most one per face per five secondsface_id, name (empty means a stranger)

Idle chatter is driven off these: face.seen means someone is about, sensor.charger means he is docked, and the voice events are how the brain knows not to talk over a conversation in progress.

EventFires whendata
cube.tappedhis cube was tappedobject_id, factory_id, robot_ts
cube.movedthe cube started moving after five seconds of stillness — not every joltobject_id, factory_id, robot_ts
cube.up_axisthe cube was turned onto a different faceup_axis, object_id, robot_ts
cube.connected / cube.disconnectedthe Bluetooth link to the cube came up or went downobject_id, factory_id

These come from a cube watch that is only open while something is using the cube. Any cube call from the dashboard starts it, and it closes ten minutes after the last one. Over MQTT they become a connected sensor, a moving sensor, an up-axis sensor and a Home Assistant device trigger, so a tap can start an automation.

While a vision session is running — the live view in the dashboard, or the always-on detectors in newer EmberOS builds — the robot’s own detections are forwarded as events too. With no session running, nothing is emitted.

Rate limits are per robot: faces one per two seconds per face, motion one per five seconds, everything else one per two seconds per kind. Rectangles are given in the 640-pixel-wide camera frame.

Eventdata
vision.faceid, name, expression, rect {x,y,w,h}
vision.motionarea as a fraction of the image, centroid {x,y}, ground {x,y} in mm from him when the engine had a ground point
vision.pettype (cat or dog), score, rect
vision.laserx, y, ground {x,y}
vision.lightstate (illuminated or darkened)
vision.person / vision.handscore, area, rect

Pets, laser, light and salient points need a recent EmberOS build; older robots produce only vision.face and vision.motion. Cube and charger sightings stay in the live view and are not forwarded. Over MQTT the vision events go out raw on the events topic; there are no dedicated Home Assistant entities for them.

What the brain does with them is one setting, off by default: when it is off, every decision is logged and nothing is said. When it is on, and it is not quiet hours and he is not mid-conversation, a known face goes through the ordinary greeting machinery, a cat gets one hello every ten minutes, and an unrecognised person gets a “who’s there?” only if a second setting is also on. Light level is recorded but never spoken. Motion, hands and laser dots are counted and nothing else. The brain answers immediately and reacts in the background, so a greeting’s language-model call never holds up the server’s delivery worker.

Recent EmberOS builds report the stop-on-cliff reflex firing while the server holds behaviour control, and the same vision session forwards it. By the time the event arrives the engine has already cancelled whatever was running; the event says why, and whether he reversed off the edge.

Eventdata
robot.cliff_stoppedcliff_detected_flags (bit 1 front-left, 2 front-right, 4 back-left, 8 back-right), backed_off, backed_off_mm, robot_ts

backed_off is true only when whoever took control asked for a slow back-off. The default is to stop dead, which leaves him sitting on the lip of the edge. Like the vision events this needs a live session, and the filter is only requested from robots new enough to know the name — older gateways can fault on a filter they do not recognise.

The social engine emits every decision it makes, so with dry run on — which is the default — a whole reaction plays out in the event log and nothing moves. See Social reactions for what the fields mean.

Eventdata
social.episode.startid, friend, kind, style, ism, trigger, guessed, dry_run, steps
social.episode.stepid, i, friend, dry_run, op, and that step’s own fields
social.episode.endid, friend, ism, result, ms, steps
social.skippedfriend, kind, reason, lingered_ms — an approach was judged and nothing ran, and why

For sensor.pickup, sensor.putdown, sensor.pet and cube.tapped the server does not just emit: it waits up to twenty seconds for the primary subscriber’s JSON reply and acts on it. There is currently one field:

{ "say": "Whoa. A little warning next time." }

That line is spoken through behaviour control. An empty object, an empty say, or any error at all means the robot stays quiet. Cooldowns, not talking over the user, and persona are the subscriber’s business, not the server’s.

A talk step in a social reaction asks in the same way, sending the friend, the kind, the style, the reaction and a hint; no answer means a stock line is used.

Anything a subscriber wants to do to the robot beyond speaking goes through the SDK routes instead.

Events not listed here do not exist. A new one is a call to events.Emit with a name, the robot’s serial and a data map, from wherever the thing actually happens — and a row in this table.