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

Use device.recording_start to start a recording on the device and return the recording ID.

start_stop_recordings.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    async with Device.from_discovered_device(dev_info) as device:
        # get update when recording is fully started
        notifier = StatusUpdateNotifier(device, callbacks=[on_status_update])
        await notifier.receive_updates_start()
        recording_id = await device.recording_start()
        print(f"Initiated recording with id {recording_id}")
        await asyncio.sleep(5)
        print("Stopping recording")
        await device.recording_stop_and_save()
        # await control.recording_cancel()  # uncomment to cancel recording
        await asyncio.sleep(2)  # wait for confirmation via auto-update
        await notifier.receive_updates_stop()
Initiated recording with id 54e7d0bb-5b4c-40e6-baed-f2e11eb4f53b

With a recording ongoing, you can:

Send event

Save events 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.

send_event.py
1
await device.send_event("test event")
Event(name=test event recording_id=None timestamp_unix_ns=1744271292116000000 datetime=2025-04-10 09:48:12.116000)

send_event.py
 await device.send_event(
    "test event", event_timestamp_unix_ns=time.time_ns()
)
Event(name=test event recording_id=None timestamp_unix_ns=1744271291692745000 datetime=2025-04-10 09:48:11.692745)

Events will only be saved if the recording is running. If you send an event while there is no recording, it will be discarded.

Check for Errors

Neon +2.9.0 +1.5.0

Errors can happen, but now you can also monitor them remotely, by looking for stream_errors on the component while updating the status, you can get notified of WatchDog errors during your recording.

start_stop_recordings.py
def on_status_update(component):
    if isinstance(component, Recording):
        if component.action == "ERROR":
            print(f"Error : {component.message}")

    elif isinstance(component, Sensor) and component.stream_error:
        print(f"Stream error in sensor {component.sensor}")

            ...

        # get update when recording is fully started
        notifier = StatusUpdateNotifier(device, callbacks=[on_status_update])
Error: Recording Watchdog failure

Stop & Save a Recording

Likewise 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
await 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. This will delete the recording and all its data.

start_stop_recordings.py
await device.recording_cancel()

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
30
31
32
33
34
35
36
37
38
import asyncio

from pupil_labs.realtime_api import Device, Network, StatusUpdateNotifier
from pupil_labs.realtime_api.models import Recording, Sensor


def on_status_update(component):
    if isinstance(component, Recording):
        if component.action == "ERROR":
            print(f"Error : {component.message}")

    elif isinstance(component, Sensor) and component.stream_error:
        print(f"Stream error in sensor {component.sensor}")


async def main():
    async with Network() as network:
        dev_info = await network.wait_for_new_device(timeout_seconds=5)
    if dev_info is None:
        print("No device could be found! Abort")
        return

    async with Device.from_discovered_device(dev_info) as device:
        # get update when recording is fully started
        notifier = StatusUpdateNotifier(device, callbacks=[on_status_update])
        await notifier.receive_updates_start()
        recording_id = await device.recording_start()
        print(f"Initiated recording with id {recording_id}")
        await asyncio.sleep(5)
        print("Stopping recording")
        await device.recording_stop_and_save()
        # await control.recording_cancel()  # uncomment to cancel recording
        await asyncio.sleep(2)  # wait for confirmation via auto-update
        await notifier.receive_updates_stop()


if __name__ == "__main__":
    asyncio.run(main())
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
import asyncio
import time

from pupil_labs.realtime_api import Device, Network


async def main():
    async with Network() as network:
        dev_info = await network.wait_for_new_device(timeout_seconds=5)
    if dev_info is None:
        print("No device could be found! Abort")
        return

    async with Device.from_discovered_device(dev_info) as device:
        # send event without timestamp
        print(await device.send_event("test event"))

        # send event with current timestamp
        print(
            await device.send_event(
                "test event", event_timestamp_unix_ns=time.time_ns()
            )
        )


if __name__ == "__main__":
    asyncio.run(main())