Skip to main content

HMI (Screen, Button, and Sound)

The AT1000 provides a simple yet functional Human-Machine Interface (HMI) that allows engineers to provide user feedback and interaction during test sequences. The HMI consists of:

  • A color LCD screen (80 × 160 resolution)
  • An audio speaker for notifications
  • A rotary knob for user interaction

The image below shows the different elements on the front panel of the AT1000 device:

HMI Capabilities​

The HMI is designed for:

  1. Displaying status messages and progress information to the user.
  2. Allowing simple user interactions during test sequences.

Built-in single-choice prompts let an operator answer a question with the knob, without programming a menu.

What the HMI is not made for

The AT1000's HMI and API are not designed for building complex navigation menus, displaying images, or playing user-defined sound effects. While the hardware is capable of advanced graphics and audio, the HMI is intentionally simplified for ease of use in test automation.

Note

The examples assume that tester is an open AT1000 controlling session.

Asking the operator a question​

Ask a question on the screen, then check the result status before using the answer.

const request = {
text: "Continue the run?",
choices: ["Yes", "No"],
};
const result = await tester.hmi.prompt(request);

if (result.status === "selected") {
console.log(`Selected ${result.selection.index}: ${result.selection.label}`);
} else if (result.status === "timed_out") {
console.log("No answer: the prompt timed out.");
} else if (result.status === "cancelled") {
console.log(`Prompt cancelled: ${result.reason}`);
}

Turn the knob to change the selected choice, then click to confirm. Holding the knob for more than five seconds invokes panel reclaim.

Long questions page automatically. The amber deadline bar disappears on the first rotation. The confirmed choice appears briefly before the previous screen returns, unless another owner or the panel replaces it.

See the request fields, results, and ownership rules before combining a prompt with other HMI operations.

Screen Control​

The AT1000 screen can display text messages, update the foreground and background colors, and show a progress bar.

Screen mutations conflict with a pending prompt. See prompt ownership.


// Set the screen colors (text: white, background: blue)
await tester.hmi.screen().colors({ text: "#FFFFFF", background: "#0000FF" });

//Clear all content on the screen
await tester.hmi.screen().clear();

// Display some text, and replace any previous text.
await tester.hmi.screen().print("Test is \nin progress...");

// Update the progress bar to 75%.
// Setting progress to 0 hides the progress bar
await tester.hmi.screen().progress(75);

Speaker Control​

The built-in speaker emits predefined sound effects for success, notifications, and failures. These sounds help alert users about test status.


// Play success sound at full volume
await tester.hmi.audio().play({ sound_id: "success", volume: 100 });

// Play a notification sound at 50% volume
await tester.hmi.audio().play({ sound_id: "notification_1", volume: 50 });

// Play a failure sound at 80% volume
await tester.hmi.audio().play({ sound_id: "failure", volume: 80 });

Knob Interaction​

The rotary knob allows for two types of user interactions:

  • Push interaction (pressing the knob)
  • Rotation interaction (turning left or right, returning pulse counts)

Raw knob reads conflict with a pending prompt. Use its result instead. See prompt ownership.


let event = await tester.hmi.knob().wait_event(2000); // timeout in milliseconds

if (event) {
console.log("Button press: " + event.button_pressed);
console.log("Knob rotation: " + event.rotation_delta);
}


API Reference​

Accessing HMI​

const hmi = tester.hmi;

HMI Module Methods​

prompt(request)​

Displays a single-choice prompt and waits for selection, timeout, or cancellation.

SDKContract
JavaScriptprompt(request: PromptRequestInput, options?: { signal?: AbortSignal }): Promise<PromptState>
Python syncprompt(request: PromptRequest) -> PromptState
Python asyncasync prompt(request: PromptRequest) -> PromptState
Rustprompt(&self, request: &PromptRequest) -> Result<PromptState, At1000Error>

With ikalogic_at1000.aio.AT1000, call the Python method with await. It accepts the same PromptRequest and returns the same PromptState.

Request fields

FieldMeaning and limits
textNonblank question, at most 256 UTF-8 bytes. Line feeds are allowed, but no other control characters.
choices1–32 choices in display order. JavaScript and Python accept strings or objects with label and optional color. Rust uses PromptChoice, with .into() for uncolored strings.
labelNonblank, at most 64 UTF-8 bytes, with no control characters. Duplicate labels are valid: position identifies a choice.
colorOptional choice background in #RRGGBB format. Default: #1769AA. The device chooses contrasting black or white text.
default_indexZero-based initial highlight, below the number of choices. Default: 0. It does not confirm a choice.
timeout_msInteger milliseconds from 1,000 through 1,800,000, inclusive. Default: 60,000. Guards an untouched prompt.

The LCD rejects question or label characters missing from its Montserrat font.

The deadline stops on rotation

The first knob rotation permanently disables the device deadline. This applies even if the highlight cannot move at an endpoint. timeout_ms is not a maximum duration for operator interaction.

For example, give a destructive choice a distinct background and make it the initial highlight:

const request = {
text: "Choose the next action.",
choices: [
{ label: "Retry", color: "#1769AA" },
"Skip",
{ label: "Abort run", color: "#B3261E" },
],
default_index: 2,
timeout_ms: 60000,
};

Result: PromptState

FieldMeaning
idOpaque prompt resource ID, not a choice identifier.
statuspending, selected, cancelled, or timed_out. The high-level SDK call waits for a terminal result.
selectionAlways present as { label, index }. Current highlight while pending, confirmed choice when selected, last highlight otherwise.
reasonCancellation only: client_cancelled, session_ended, panel_exit, or display_replaced.

Rust uses PromptStatus::{Pending, Selected, Cancelled, TimedOut} and Option<PromptCancelReason>. The reason variants are ClientCancelled, SessionEnded, PanelExit, and DisplayReplaced.

Prompt ownership and errors​

The controlling session owns the prompt. Only one prompt can be pending on a device.

Reserved LCD and knob

While a prompt is pending, another prompt, screen mutations, hmi.reset(), and raw knob reads return CONFIGURATION_CONFLICT. Audio and unrelated hardware operations remain available.

Timeout and server-side cancellation return results. Validation, transport, and API failures raise errors. Do not use whole-device tester.reset() to cancel a prompt: it is not an atomic cancellation operation.

JavaScript accepts an AbortController signal. For example, this gives the application its own ten-second cancellation deadline:

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 10000);
try {
const result = await tester.hmi.prompt(request, { signal: controller.signal });
console.log(result.status);
} finally {
clearTimeout(timer);
}

An abort rejects with the original abort reason and attempts server cancellation. Python synchronous interruption and async task cancellation also attempt best-effort server cancellation before propagating the original exception. Rust has no extra cancellation argument.

SDK waits follow hmi.prompt events and read the state again when their event window expires. They do not impose an unconditional local deadline after the operator engages.

Losing the network alone does not cancel the prompt. After engagement, it can remain pending until confirmation, cancellation, or session loss. Losing control of the device cancels a pending prompt.

Prompt REST endpoints​

Direct API clients must send the active X-AT1000-Session header on all three routes.

RequestResult
POST /v0/hmi/promptsCreate a prompt. Returns HTTP 201 and PromptState.
GET /v0/hmi/prompts/{id}Read the owning session's state.
DELETE /v0/hmi/prompts/{id}Cancel a pending prompt with client_cancelled. Returns an already-terminal state unchanged.

REST choices must be objects, rather than the SDK string shorthand:

{
"text": "Continue the run?",
"choices": [{"label": "Yes"}, {"label": "No"}],
"default_index": 1,
"timeout_ms": 60000
}

Example state after the operator confirms No:

{
"id": "42",
"status": "selected",
"selection": {"label": "No", "index": 1}
}

For a valid owning session, unknown, expired, or other-owner IDs return HTTP 404 RESOURCE_NOT_FOUND. The session gate can reject missing or revoked tokens first. Invalid fields return HTTP 400.

Terminal results last up to five minutes, with at most the newest 32 retained.

Other HMI module methods​

reset()​

Resets the HMI to its default state.

This returns CONFIGURATION_CONFLICT while a prompt is pending. See prompt ownership.

await tester.hmi.reset();

Returns: Promise<void> / None / Result<(), At1000Error>


screen()​

Access the screen interface for display operations.

const screen = tester.hmi.screen();

Returns: Screen instance / Screen in Rust (returned directly, not a Result)


audio()​

Access the audio interface for sound playback.

const audio = tester.hmi.audio();

Returns: Audio instance / Audio in Rust (returned directly, not a Result)


knob()​

Access the rotary knob interface for user input.

const knob = tester.hmi.knob();

Returns: Knob instance / Knob in Rust (returned directly, not a Result)


Screen Class​

Screen modifications return CONFIGURATION_CONFLICT while a prompt is pending. See prompt ownership.

print(text)​

Displays text on the screen.

await screen.print("Test in progress...");

Parameters:

  • text (string / str / &str): Text to display (max 1024 characters)

Returns: Promise<string> / str / Result<String, At1000Error> - The displayed text

Exceptions:

  • Throws validation error if text exceeds 1024 characters

colors(config)​

Sets the screen color scheme.

await screen.colors({
text: "#FFFFFF",
progress: "#00FF00",
background: "#000000"
});

Parameters:

  • config (Color / &ScreenColorsPatch in Rust): Color configuration object:
    • text (string / str / Option<String>, optional): Text color in hex format (#RRGGBB)
    • progress (string / str / Option<String>, optional): Progress bar color in hex format (#RRGGBB)
    • background (string / str / Option<String>, optional): Background color in hex format (#RRGGBB)

Returns: Promise<Color> / Color / Result<ScreenColors, At1000Error> - The applied color configuration

Exceptions:

  • Throws validation error if color strings are not valid hex colors (#RRGGBB format)

progress(value)​

Sets the progress bar value.

await screen.progress(50); // 50%
await screen.progress(0); // Hide progress bar

Parameters:

  • value (number / int / f64): Progress percentage (0 to 100). Setting to 0 hides the progress bar.

Returns: Promise<Progress> / Progress / Result<f64, At1000Error> - Object containing the progress value

Exceptions:

  • Throws validation error if value is outside the range [0, 100]

clear()​

Clears the screen display.

await screen.clear();

Returns: Promise<void> / None / Result<(), At1000Error>


clear_text()​

Clears only the text area, keeping colors and the progress bar.

await screen.clear_text();

Returns: Promise<void> / None / Result<(), At1000Error>


Audio Class​

play(config)​

Plays a sound with specified volume.

await audio.play({
sound_id: "success",
volume: 80
});

Parameters:

  • config (PlaybackConfiguration / &PlaybackConfiguration in Rust):
    • sound_id (string / str / String, required): Sound identifier - one of:
      • "success" - Success/pass sound
      • "warning" - Warning sound
      • "failure" - Failure/error sound
      • "notification_1" - Notification sound 1
      • "notification_2" - Notification sound 2
      • "imperial_march" - Imperial March theme !
    • volume (number / int / f64, required): Volume level (0 to 100)

Returns: Promise<PlaybackConfiguration> / PlaybackConfiguration / Result<PlaybackConfiguration, At1000Error> - The playback configuration

Exceptions:

  • Throws validation error if sound_id is not one of the valid values
  • Throws validation error if volume is outside the range [0, 100]

Knob Class​

Knob reads require control of the device and return CONFIGURATION_CONFLICT while a prompt is pending. See prompt ownership.

wait_event(timeout_ms)​

Waits for a knob event (rotation or button press) with a timeout.

const event = await knob.wait_event(5000); // Wait up to 5 seconds

if (event) {
console.log("Button pressed:", event.button_pressed);
console.log("Rotation delta:", event.rotation_delta);
} else {
console.log("Timeout - no event");
}

Parameters:

  • timeout_ms (number / int / Duration, optional): Timeout in milliseconds. Omit to wait indefinitely

Returns: Promise<KnobEvent | null> / KnobEvent | None / Result<Option<KnobEventResult>, At1000Error> - Event object or null/None if timeout

In Rust the timeout is a std::time::Duration and is mandatory; Duration::ZERO returns None without contacting the device.

KnobEvent properties:

  • button_pressed (boolean / bool / bool): True if button was pressed during this event
  • rotation_delta (number / int / i32): +1 for a clockwise step, -1 for a counter-clockwise step

is_button_held()​

Checks if the knob button is currently held down.

const held = await knob.is_button_held();
console.log("Button is", held ? "held" : "not held");

Returns: Promise<boolean> / bool / Result<bool, At1000Error> - True if button is pressed, false otherwise


is_knob_turned()​

Polls once and reports whether the knob moved at least one step since the previous poll.

const turned = await knob.is_knob_turned();

Returns: Promise<boolean> / bool / Result<bool, At1000Error>


Types​

Color​

{
text?: string, // Hex color: #RRGGBB
progress?: string, // Hex color: #RRGGBB
background?: string // Hex color: #RRGGBB
}

Rust: ikalogic_at1000::ScreenColorsPatch (what you send) and ikalogic_at1000::ScreenColors (what the device returns)

pub struct ScreenColorsPatch {
pub text: Option<String>,
pub progress: Option<String>,
pub background: Option<String>,
}

pub struct ScreenColors {
pub text: String,
pub progress: String,
pub background: String,
}

Progress​

{
progress: number // 0 to 100
}

Rust: f64 - Screen::progress takes and returns the percentage itself, with no wrapper type.

PlaybackConfiguration​

{
sound_id: "success" | "warning" | "failure" |
"notification_1" | "notification_2" | "imperial_march",
volume: number // 0 to 100
}

Rust: ikalogic_at1000::PlaybackConfiguration

pub struct PlaybackConfiguration {
pub sound_id: String,
pub volume: f64,
}

KnobEvent​

{
button_pressed: boolean,
rotation_delta: number
}

Rust: ikalogic_at1000::KnobEvent (returned by read_event()) and ikalogic_at1000::KnobEventResult (returned by wait_event())

pub struct KnobEvent {
pub button_pressed: bool,
pub button_held: bool,
pub rotation_delta: i32,
}

pub struct KnobEventResult {
pub button_pressed: bool,
pub rotation_delta: i32,
}

Notes​

  • Screen resolution is 80 × 160 pixels
  • Screen supports multi-line text with \n line breaks
  • Maximum screen text length is 1024 characters
  • Colors must be in hex format with leading # (e.g., #FF0000)
  • Volume ranges from 0 (mute) to 100 (maximum)
  • Knob rotation is cumulative since last read (positive = clockwise)
  • The wait_event() method blocks until an event occurs or timeout
  • Use clear() before updating screen to prevent text overlap
  • Progress bar value is independent of displayed text