Skip to content

Remote Control

With the device connected, you can remotely control your device, similarly to the Monitor App and start, stop, save and annotate recordings.

Start a Recording

It is possible to remotely control your device. Use device.recording_start to start a recording on the device and return the recording ID.

start_stop_recordings.py
print("Starting recording")
recording_id = device.recording_start()
Started recording with id 54e7d0bb-5b4c-40e6-baed-f2e11eb4f53b

Stop & Save a Recording

You can stop and save a recording using device.recording_stop_and_save. Note that if you have a mandatory question that is not filled, the recording will not be saved until that question is answered.

start_stop_recordings.py
device.recording_stop_and_save()
Recording stopped and saved

Cancel a Recording

You can also cancel (device.recording_cancel) a recording if you don't want to save it, just be aware that this will delete the recording and all its data.

start_stop_recordings.py
device.recording_cancel()

Save Events

A common requirement is to save events with your recording. This is made possible using the device.send_event method. By default, the Neon device receiving the event will assign a timestamp to it, using the time of arrival. Optionally, you can set a custom nanosecond timestamp for your event instead. We also show you how to measure the clock offset between the Neon device and client computer, which may be useful for synchronising third-party clocks with your Neon device.

send_event.py
print(device.send_event("test event; timestamped at arrival"))
Event(name=test event; timestamped at arrival recording_id=None timestamp_unix_ns=1744271292116000000 datetime=2025-04-10 09:48:12.116000)

send_event.py
# send event with current timestamp
print(
    device.send_event(
        "test event; timestamped by the client, relying on NTP for sync",
        event_timestamp_unix_ns=time.time_ns(),
    )
Event(name=test event; timestamped by the client, relying on NTP for sync recording_id=None timestamp_unix_ns=1744271291692745000 datetime=2025-04-10 09:48:11.692745)

send_event.py
# Estimate clock offset between Companion device and client script
# (only needs to be done once)
estimate = device.estimate_time_offset()
clock_offset_ns = round(estimate.time_offset_ms.mean * 1_000_000)
print(f"Clock offset: {clock_offset_ns:_d} ns")

# send event with current timestamp, but correct it manual for possible clock offset
current_time_ns_in_client_clock = time.time_ns()
current_time_ns_in_companion_clock = current_time_ns_in_client_clock - clock_offset_ns
print(
    device.send_event(
        "test event; timestamped by the client, manual clock offset correction",
        event_timestamp_unix_ns=current_time_ns_in_companion_clock,
    )
)
Clock offset: -437_790_000 ns
Event(name=test event; timestamped by the client, manual clock offset correction recording_id=None timestamp_unix_ns=1744271293119536000 datetime=2025-04-10 09:48:13.119536)

Events will only be saved if a recording is running. If you send an event while the Companion app is not recording, it will be discarded.

Check for Errors

You can also monitor the recording for potential errors. Add the device.get_errors method to your code to get notified of errors (if they happen) during your recording.

Neon +2.9.0 +1.5.0

start_stop_recordings.py
# Check for errors while recording runs
start_time = time.time()
while time.time() - start_time < 5:
    for e in device.get_errors():
        print("Error:", e)
Error: Recording Watchdog failure

Full Code Examples

Check the whole example code here
start_stop_recordings.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import time

from pupil_labs.realtime_api.simple import discover_one_device

# Look for devices. Returns as soon as it has found the first device.
print("Looking for the next best device...")
device = discover_one_device(max_search_duration_seconds=10)
if device is None:
    print("No device found.")
    raise SystemExit(-1)

print("Starting recording")
recording_id = device.recording_start()
print(f"Started recording with id {recording_id}")

# Check for errors while recording runs
start_time = time.time()
while time.time() - start_time < 5:
    for e in device.get_errors():
        print("Error:", e)

    time.sleep(1)

device.recording_stop_and_save()

print("Recording stopped and saved")
# device.recording_cancel()  # uncomment to cancel recording

device.close()
send_event.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import time

from pupil_labs.realtime_api.simple import discover_one_device

# Look for devices. Returns as soon as it has found the first device.
print("Looking for the next best device...")
device = discover_one_device(max_search_duration_seconds=10)
if device is None:
    print("No device found.")
    raise SystemExit(-1)

print(device.send_event("test event; timestamped at arrival"))

# send event with current timestamp
print(
    device.send_event(
        "test event; timestamped by the client, relying on NTP for sync",
        event_timestamp_unix_ns=time.time_ns(),
    )
)

# Estimate clock offset between Companion device and client script
# (only needs to be done once)
estimate = device.estimate_time_offset()
clock_offset_ns = round(estimate.time_offset_ms.mean * 1_000_000)
print(f"Clock offset: {clock_offset_ns:_d} ns")

# send event with current timestamp, but correct it manual for possible clock offset
current_time_ns_in_client_clock = time.time_ns()
current_time_ns_in_companion_clock = current_time_ns_in_client_clock - clock_offset_ns
print(
    device.send_event(
        "test event; timestamped by the client, manual clock offset correction",
        event_timestamp_unix_ns=current_time_ns_in_companion_clock,
    )
)


device.close()