Wiring a button on the Unreal side
One event in, a handful of functions out — all on PromethistLink. A graph never touches the
subsystem hosting the socket:
| Command | What the graph does |
|---|---|
|
|
|
|
|
Do the thing, then |
The assistance branch demonstrates reporting progress without an acknowledgement. Swap the delay
for an HTTP call to Teams and the tablet requires no changes. It already renders a key and does
not care what clears it. Publish false from BeginPlay too, as the shipped graph does: a level
travel mid-wait destroys the Delay and the graph, so a key left published true locks the button
lit with nothing alive to clear it.
A flip button needs its BeginPlay publish too, for the opposite reason. A tablet cannot know
which way an unpublished toggle will move. Rather than guess, it keeps the button disabled. A
graph that only publishes the key upon being pressed ships a control that can never be pressed.
Nothing logs this. This is exactly what the shipped Subtitles button relies on: subtitles is
published by the project, not the plugin.
The say branch implements the Joke button entirely. The phrase travels with the command, letting
the graph forward Value without knowing the visitor’s language. Adding a second canned phrase
requires a change only to the tablet.
On Tablet Command fires for every command, including those answered by C++, allowing projects
to react to pause or volume without owning them. Unrecognised commands are logged rather than
dropped, exposing typos on the first press. There are two exceptions that never reach the graph:
sys.resync, which the socket layer answers directly, and messages refused by the per-client rate
budget, logged as Dropped tablet message '<string>'.
Driving the operator controls from the project rather than from the tablet uses the same functions the tablet’s commands land on, avoiding a second path to maintain:
| Blueprint node | |
|---|---|
|
take the installation out of service, or ask whether it is |
|
whether a person is holding it, as opposed to a failing check |
|
tell the component a named part of the installation works, or does not |
|
ask what it currently believes about itself |
|
the audio-and-microphone half of a pause |
|
the language the next connect will use — never empty, so it can drive a Connect pin |
|
say something to the agent as if the visitor had |
|
publish a key for a button to render from |
|
the same, for a number and for a list — use these rather than formatting either into a string, which the tablet would then have to parse back |
SetVolumePercent, SetMicMuted, SetLocale and RestartSession are public C++ but not
Blueprint nodes. They exist for the tablet’s commands to land on. The component exposes nodes
only when needed, not in anticipation. To promote one, add a UFUNCTION(BlueprintCallable,
Category = "Promethist|Control") line above it.
The setters publish their state key — inline for Set Blackout, and within the same frame for the
rest, since MirrorTabletState() runs every tick — so the tablet follows changes it did not make.
Is Blacked Out and Get Locale are pure reads, and Send Text To Agent carries no key. Events
go the other way: On Blackout Changed, On Pause Changed, On Volume Changed, and On Locale
Changed. Bind these to drive animations or widgets rather than watching the command channel for a
press that may not have come from a tablet. On Blackout Changed also fires on the first tick
after a level travel restores a lockout, so driven visuals return automatically.
Commands marked C++ above are answered inside UPromethistLinkComponent because they govern its
own settings (audio, microphone, socket). A graph rewiring them manually would only recreate
mistakes the component already avoids.
The session command is deliberately split: connecting is the graph’s, because the agent, level
and backend are project decisions. C++ does exactly one thing here: it raises sessionBusy before
the graph starts. Otherwise, a Blueprint connect taking seconds would leave the button reading
"Start", inviting a second tap.
The state machine reaching Ready — meaning the graph actually calls Connect — is what clears
it. A session handler connecting via another route never clears it. Consequently, Start, Restart
and the language picker sit greyed out on all tablets until a 20-second watchdog gives up, leaving
only one Warning line as a clue. If your graph answers session without reaching Connect,
this watchdog is the contract you rely on.
This dividing line is worth stating, as it is crucial to get right when adding a button:
-
C++ when the command is the component — changing audio, the microphone, the socket, or whether connecting is allowed. Anything that must remain true regardless of the loaded graph belongs here. Blackout is the clearest case: as a Blueprint bool it would just be advisory, allowing any other connect path to bypass it.
-
Blueprint when the command is a project decision — what to show, what to play, what to say, which agent to connect to. A partner changes these without touching C++.
Either way, the graph still hears every command on On Tablet Command, so reacting to a
natively-handled one costs nothing.