.. _remotecontrol: Remote control ############## The **Open Ephys HTTP Server** enables remote control of the GUI via an HTTP API. Immediately upon launching, the GUI starts a server on port **37497** (:code:`EPHYS` on a phone keypad). You can confirm that the server is running by opening :code:`http://localhost:37497/api/processors` in a browser. If you are using a different computer, replace :code:`localhost` with the IP address of the machine running the GUI. The HTTP server can be disabled or re-enabled via the **File** menu. Most read-only endpoints use HTTP :code:`GET`, while endpoints that change GUI state use :code:`PUT` with a JSON body. Python examples below use the `requests `__ library, while Matlab examples use `webread `__ and `webwrite `__. Malformed JSON requests return HTTP 400 responses. Requests for missing processors, streams, or parameters return HTTP 404 responses. Quick API reference ------------------- .. csv-table:: :header: "Method", "Endpoint", "Description" :widths: 8, 46, 46 "GET", ":code:`/api/status`", "Return the current GUI mode." "PUT", ":code:`/api/status`", "Set the GUI mode to :code:`IDLE`, :code:`ACQUIRE`, or :code:`RECORD`." "GET", ":code:`/api/recording`", "Return default recording settings and all Record Node settings." "PUT", ":code:`/api/recording`", "Update default recording settings." "PUT", ":code:`/api/recording/`", "Update a specific Record Node." "GET", ":code:`/api/config`", "Return the current signal-chain configuration as XML wrapped in JSON." "PUT", ":code:`/api/load`", "Load a signal chain from disk." "PUT", ":code:`/api/save`", "Save the current signal chain to disk." "GET", ":code:`/api/processors/list`", "List processor types that can be added to the signal chain." "GET", ":code:`/api/processors`", "Return all processors currently in the signal chain." "GET", ":code:`/api/processors/`", "Return one processor and its streams." "GET", ":code:`/api/processors//parameters`", "Return all processor-level parameters." "GET", ":code:`/api/processors//parameters/`", "Return one processor-level parameter." "GET", ":code:`/api/processors//streams/`", "Return one stream and its parameters." "GET", ":code:`/api/processors//streams//parameters`", "Return all parameters for one stream." "GET", ":code:`/api/processors//streams//parameters/`", "Return one stream parameter." "PUT", ":code:`/api/processors//parameters/`", "Set one processor-level parameter." "PUT", ":code:`/api/processors//streams//parameters/`", "Set one stream parameter." "PUT", ":code:`/api/processors//config`", "Send a processor-specific configuration message." "PUT", ":code:`/api/message`", "Broadcast a message to all processors." "GET", ":code:`/api/processors/clear`", "Clear the signal chain." "PUT", ":code:`/api/processors/add`", "Add a processor to the signal chain." "PUT", ":code:`/api/processors/delete`", "Delete a processor from the signal chain." "GET", ":code:`/api/undo`", "Undo the previous action." "GET", ":code:`/api/redo`", "Redo the previous action." "GET", ":code:`/api/cpu`", "Return the current audio callback CPU usage." "GET", ":code:`/api/latency`", "Return processor latency information for each stream." "GET", ":code:`/api/audio/devices`", "List available audio device types and device names." "GET", ":code:`/api/audio/device`", "Return the currently selected audio device and supported rates and buffer sizes." "PUT", ":code:`/api/audio`", "Change the active audio device, sample rate, or buffer size." "PUT", ":code:`/api/quit`", "Close the GUI." Query and control acquisition state ----------------------------------- Use :code:`GET /api/status` to query the GUI mode and :code:`PUT /api/status` to change it. .. code-block:: python import requests status = requests.get("http://localhost:37497/api/status").json() requests.put( "http://localhost:37497/api/status", json={"mode": "ACQUIRE"}, ) .. code-block:: matlab status = webread('http://localhost:37497/api/status'); out = webwrite( 'http://localhost:37497/api/status', struct('mode','ACQUIRE'), weboptions('RequestMethod','put','MediaType','application/json')); The returned JSON contains a single :code:`mode` field: * :code:`IDLE` means the GUI is not acquiring data. * :code:`ACQUIRE` means the GUI is acquiring but not recording. * :code:`RECORD` means the GUI is both acquiring and recording. .. note:: The signal chain must contain at least one Record Node in order for :code:`RECORD` mode to succeed. Recording configuration ----------------------- Use :code:`GET /api/recording` to inspect the global recording configuration and the state of each Record Node. .. code-block:: python recording = requests.get("http://localhost:37497/api/recording").json() .. code-block:: json { "parent_directory": "/Users/neuroscientist/Documents/OpenEphys", "base_text": "AUTO", "prepend_text": "NONE", "append_text": "AUTO", "default_record_engine": "BINARY", "record_nodes": [ { "node_id": 102, "parent_directory": "/Users/neuroscientist/Documents/OpenEphys", "record_engine": "BINARY", "experiment_number": 1, "recording_number": 3, "is_synchronized": true } ] } Use :code:`PUT /api/recording` to update global defaults. Supported fields are: * :code:`parent_directory` * :code:`prepend_text` * :code:`base_text` * :code:`append_text` * :code:`default_record_engine` * :code:`start_new_directory` Example: .. code-block:: python requests.put( "http://localhost:37497/api/recording", json={ "parent_directory": "/Users/neuroscientist/Documents/Data", "base_text": "experiment_01", "append_text": "mouse_a", "default_record_engine": "BINARY", "start_new_directory": "true", }, ) .. code-block:: matlab out = webwrite( 'http://localhost:37497/api/recording', struct( 'parent_directory','/Users/neuroscientist/Documents/Data', 'base_text','experiment_01', 'append_text','mouse_a', 'default_record_engine','BINARY', 'start_new_directory','true'), weboptions('RequestMethod','put','MediaType','application/json')); Use :code:`PUT /api/recording/` to update a specific Record Node. Supported fields are :code:`parent_directory` and :code:`record_engine`. .. code-block:: python requests.put( "http://localhost:37497/api/recording/102", json={ "parent_directory": "/Users/neuroscientist/Documents/Data", "record_engine": "BINARY", }, ) Signal-chain configuration files -------------------------------- Use :code:`GET /api/config` to fetch the current GUI configuration. The response is JSON with the XML payload stored in the :code:`info` field. .. code-block:: python config = requests.get("http://localhost:37497/api/config").json() xml_text = config["info"] Load a saved signal chain with :code:`PUT /api/load`: .. code-block:: python requests.put( "http://localhost:37497/api/load", json={"path": "/Users/neuroscientist/Documents/OpenEphys/chain.xml"}, ) Save the current signal chain with :code:`PUT /api/save`: .. code-block:: python requests.put( "http://localhost:37497/api/save", json={"filepath": "/Users/neuroscientist/Documents/OpenEphys/chain.xml"}, ) .. note:: :code:`/api/save` does not overwrite an existing file. It returns a message if the target path already exists. Inspect processors, streams, and parameters ------------------------------------------- Use :code:`GET /api/processors/list` to list the processor types that can be added to the graph: .. code-block:: python available = requests.get("http://localhost:37497/api/processors/list").json() Use :code:`GET /api/processors` to inspect the current signal chain: .. code-block:: python graph = requests.get("http://localhost:37497/api/processors").json() The response has the following structure: .. code-block:: json { "processors": [ { "id": 100, "name": "File Reader", "parameters": [], "predecessor": null, "streams": [ { "name": "example_data", "source_id": 100, "sample_rate": 40000.0, "channel_count": 16, "parameters": [] } ] } ] } You can also query narrower endpoints: * :code:`/api/processors/` * :code:`/api/processors//parameters` * :code:`/api/processors//parameters/` * :code:`/api/processors//streams/` * :code:`/api/processors//streams//parameters` * :code:`/api/processors//streams//parameters/` .. note:: :code:`stream_index` is zero-based because the server indexes directly into each processor's stream list. Parameter values are returned as strings in the JSON response, together with a :code:`type` field that describes the parameter kind. Modify processors and parameters -------------------------------- Processor-level parameters can be changed with :code:`PUT /api/processors//parameters/`. .. code-block:: python requests.put( "http://localhost:37497/api/processors/101/parameters/high_cut", json={"value": 6000}, ) Stream parameters can be changed with :code:`PUT /api/processors//streams//parameters/`. .. code-block:: python requests.put( "http://localhost:37497/api/processors/101/streams/0/parameters/enable_stream", json={"value": True}, ) Accepted :code:`value` payloads are integers, floats, booleans, strings, and numeric arrays. Some parameters cannot be changed while acquisition is active; those requests return HTTP 400. Use :code:`PUT /api/processors//config` to send a processor-specific configuration message before starting acquisition: .. code-block:: python requests.put( "http://localhost:37497/api/processors/100/config", json={"text": "NP REFERENCE 3 1 1 TIP"}, ) To broadcast a message to all processors while acquisition is active, use :code:`PUT /api/message`: .. code-block:: python requests.put( "http://localhost:37497/api/message", json={"text": "ACQBOARD TRIGGER 1 100"}, ) .. tip:: Broadcast messages are saved by all Record Nodes, so they can be used to mark epochs within a recording. The signal chain can also be edited remotely: * :code:`GET /api/processors/clear` clears the graph. * :code:`PUT /api/processors/delete` deletes a processor when given :code:`{"id": 101}`. * :code:`PUT /api/processors/add` adds a processor when given :code:`{"name": "Bandpass Filter"}`. * :code:`PUT /api/processors/add` also accepts :code:`source_id` or :code:`dest_id` to position the processor relative to an existing node. * :code:`GET /api/undo` undoes the previous action. * :code:`GET /api/redo` redoes the previous action. Examples: .. code-block:: python requests.put( "http://localhost:37497/api/processors/add", json={"name": "Bandpass Filter", "source_id": 100}, ) requests.put( "http://localhost:37497/api/processors/delete", json={"id": 101}, ) Graph-editing endpoints that modify the signal chain are blocked while acquisition is active. Performance endpoints --------------------- Use :code:`GET /api/cpu` to retrieve the current audio callback CPU usage: .. code-block:: python usage = requests.get("http://localhost:37497/api/cpu").json() The returned JSON has the form: .. code-block:: json {"usage": 0.12} Use :code:`GET /api/latency` to inspect processor latency per stream: .. code-block:: python latency = requests.get("http://localhost:37497/api/latency").json() This returns one entry per processor, each with a list of stream names and their latency values. Audio device control -------------------- The audio endpoints let you inspect the available devices and change the currently selected device. Use :code:`GET /api/audio/devices` to list available device types and names: .. code-block:: python devices = requests.get("http://localhost:37497/api/audio/devices").json() This returns JSON in the form: .. code-block:: json { "devices": { "ALSA": ["Device A", "Device B"], "JACK": ["JACK Audio Connection Kit"] } } Use :code:`GET /api/audio/device` to inspect the current device: .. code-block:: json { "device_type": "ALSA", "device_name": "Device A", "sample_rate": 30000, "buffer_size": 512, "available_sample_rates": [30000, 44100, 48000], "available_buffer_sizes": [128, 256, 512, 1024] } Use :code:`PUT /api/audio` to change any combination of :code:`device_type`, :code:`device_name`, :code:`sample_rate`, and :code:`buffer_size`: .. code-block:: python requests.put( "http://localhost:37497/api/audio", json={ "device_type": "ALSA", "device_name": "Device A", "sample_rate": 30000, "buffer_size": 512, }, ) Close the GUI remotely ---------------------- To shut down the GUI, send an HTTP :code:`PUT` request to :code:`/api/quit`: .. code-block:: python requests.put("http://localhost:37497/api/quit") .. code-block:: matlab out = webwrite( 'http://localhost:37497/api/quit', struct(), weboptions('RequestMethod','put','MediaType','application/json'));