diff --git a/cpp-qt/cortexclient/Config.h b/cpp-qt/cortexclient/Config.h
index f064dcc..ef0fdb8 100644
--- a/cpp-qt/cortexclient/Config.h
+++ b/cpp-qt/cortexclient/Config.h
@@ -18,9 +18,9 @@ along with this program. If not, see
#include
/*
- * To get a client id and a client secret, you must connect to your Emotiv
- * account on emotiv.com and create a Cortex app.
- * https://www.emotiv.com/my-account/cortex-apps/
+ * Enter your application Client ID and Client Secret below.
+ * You can obtain these credentials after registering your App ID with the Cortex SDK for development.
+ * For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
*/
static const QString ClientId = "The client id of your Cortex app goes here";
static const QString ClientSecret = "The client secret of your Cortex app goes here";
diff --git a/csharp/CortexAccess/Config.cs b/csharp/CortexAccess/Config.cs
index de71066..427882e 100644
--- a/csharp/CortexAccess/Config.cs
+++ b/csharp/CortexAccess/Config.cs
@@ -3,9 +3,9 @@
static class Config
{
/*
- * To get a client id and a client secret, you must connect to your Emotiv
- * account on emotiv.com and create a Cortex app.
- * https://www.emotiv.com/my-account/cortex-apps/
+ * Enter your application Client ID and Client Secret below.
+ * You can obtain these credentials after registering your App ID with the Cortex SDK for development.
+ * For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
*/
public static string AppClientId = "put_your_application_client_id_here";
public static string AppClientSecret = "put_your_application_client_secret_here";
diff --git a/python/README.md b/python/README.md
index e3bdab3..a9c904c 100644
--- a/python/README.md
+++ b/python/README.md
@@ -21,8 +21,7 @@ Before running the examples, please ensure you have completed the following step
3. **Obtain an EMOTIV Headset or Create a Virtual Device**:
- Purchase a headset from the [EMOTIV online store](https://www.emotiv.com/), **or**
- Use a virtual headset in the EMOTIV Launcher by following [these instructions](https://emotiv.gitbook.io/emotiv-launcher/devices-setting-up-virtual-brainwear-r/creating-a-virtual-brainwear-device).
-4. **Get Client ID & Secret**: Log in to your Emotiv account at [emotiv.com](https://www.emotiv.com/my-account/cortex-apps/) and create a Cortex app. [Register here](https://id.emotivcloud.com/eoidc/account/registration/) if you don't have an account.
-5. **Authorize Examples**: The first time you run these examples, you may need to grant permission for your application to work with Emotiv Cortex.
+4. **Create your Cortex App**: Once you register your Cortex App ID, you will receive a Client ID and Client Secret, which serve as unique identifiers for your software application. For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
---
@@ -46,6 +45,7 @@ Demonstrates how to:
- Create a new record
- Stop a record
- Export recorded data to CSV or EDF
+- Query records and request to download record data
See: [Records](https://emotiv.gitbook.io/cortex-api/records)
### 4. `marker.py` — Inject Markers
diff --git a/python/cortex.py b/python/cortex.py
index 27685bb..98c879f 100644
--- a/python/cortex.py
+++ b/python/cortex.py
@@ -26,6 +26,7 @@
import time
import json
from datetime import datetime
+from pathlib import Path
# define request id
QUERY_HEADSET_ID = 1
@@ -53,6 +54,8 @@
UPDATE_MARKER_REQUEST_ID = 23
UNSUB_REQUEST_ID = 24
REFRESH_HEADSET_LIST_ID = 25
+QUERY_RECORDS_ID = 26
+REQUEST_DOWNLOAD_RECORDS_ID = 27
#define error_code
ERR_PROFILE_ACCESS_DENIED = -32046
@@ -80,13 +83,31 @@
class Cortex(Dispatcher):
- _events_ = ['inform_error','create_session_done', 'query_profile_done', 'load_unload_profile_done',
+ _events_ = ['inform_error', 'authorize_done', 'create_session_done', 'query_profile_done', 'load_unload_profile_done',
'save_profile_done', 'get_mc_active_action_done','mc_brainmap_done', 'mc_action_sensitivity_done',
- 'mc_training_threshold_done', 'create_record_done', 'stop_record_done','warn_cortex_stop_all_sub', 'warn_record_post_processing_done',
+ 'mc_training_threshold_done', 'create_record_done', 'stop_record_done','warn_cortex_stop_all_sub',
+ 'warn_record_post_processing_done', 'query_records_done', 'request_download_records_done',
'inject_marker_done', 'update_marker_done', 'export_record_done', 'new_data_labels',
'new_com_data', 'new_fe_data', 'new_eeg_data', 'new_mot_data', 'new_dev_data',
'new_met_data', 'new_pow_data', 'new_sys_data']
def __init__(self, client_id, client_secret, debug_mode=False, **kwargs):
+ """
+ Initialize a Cortex instance with authentication and configuration options.
+ Args:
+ client_id (str): The client ID for authentication. Must not be empty.
+ client_secret (str): The client secret for authentication. Must not be empty.
+ debug_mode (bool, optional): Enable debug mode. Defaults to False.
+ **kwargs: Additional configuration options.
+ license (str, optional): (Obsolete) This parameter is kept for backward compatibility. Always set to empty string
+ debit (int, optional): Debit value for usage.
+ headset_id (str, optional): ID of the headset to connect.
+ auto_create_session (bool, optional): Automatically create session if True. For export and query records, don't need to create session.
+ Raises:
+ ValueError: If client_id or client_secret is empty.
+ Description:
+ This constructor sets up the Cortex instance with required authentication credentials and optional configuration parameters.
+ It validates the presence of client_id and client_secret, and allows further customization via keyword arguments.
+ """
self.session_id = ''
self.headset_id = ''
@@ -94,6 +115,7 @@ def __init__(self, client_id, client_secret, debug_mode=False, **kwargs):
self.debit = 10
self.license = ''
self.isHeadsetConnected = False
+ self.auto_create_session = True
if client_id == '':
raise ValueError('Empty your_app_client_id. Please fill in your_app_client_id before running the example.')
@@ -113,6 +135,8 @@ def __init__(self, client_id, client_secret, debug_mode=False, **kwargs):
self.debit = value
elif key == 'headset_id':
self.headset_id = value
+ elif key == 'auto_create_session':
+ self.auto_create_session = value
def open(self):
url = "wss://localhost:6868"
@@ -126,7 +150,11 @@ def open(self):
# As default, a Emotiv self-signed certificate is required.
# If you don't want to use the certificate, please replace by the below line by sslopt={"cert_reqs": ssl.CERT_NONE}
- sslopt = {'ca_certs': "../certificates/rootCA.pem", "cert_reqs": ssl.CERT_REQUIRED}
+ file_dir_path = Path(__file__).resolve().parent
+ parent_dir_path = file_dir_path.parent
+
+ certificate_path = Path(parent_dir_path, 'certificates', 'rootCA.pem')
+ sslopt = {'ca_certs': str(certificate_path), "cert_reqs": ssl.CERT_REQUIRED}
self.websock_thread = threading.Thread(target=self.ws.run_forever, args=(None, sslopt), name=thread_name)
self.websock_thread .start()
@@ -160,173 +188,237 @@ def handle_result(self, recv_dic):
req_id = recv_dic['id']
result_dic = recv_dic['result']
- if req_id == HAS_ACCESS_RIGHT_ID:
- access_granted = result_dic['accessGranted']
- if access_granted == True:
- # authorize
- self.authorize()
- else:
- # request access
- self.request_access()
- elif req_id == REQUEST_ACCESS_ID:
- access_granted = result_dic['accessGranted']
-
- if access_granted == True:
- # authorize
- self.authorize()
- else:
- # wait approve from Emotiv Launcher
- msg = result_dic['message']
- warnings.warn(msg)
- elif req_id == AUTHORIZE_ID:
- print("Authorize successfully.")
- self.auth = result_dic['cortexToken']
+ # Dictionary dispatch pattern for better readability and performance
+ handler = self._get_result_handler(req_id)
+ if handler:
+ handler(result_dic)
+ else:
+ print('No handling for response of request ' + str(req_id))
+
+ def _get_result_handler(self, req_id):
+ """Return the appropriate handler function for the given request ID."""
+ handlers = {
+ HAS_ACCESS_RIGHT_ID: self._handle_has_access_right,
+ REQUEST_ACCESS_ID: self._handle_request_access,
+ AUTHORIZE_ID: self._handle_authorize,
+ QUERY_HEADSET_ID: self._handle_query_headset,
+ CREATE_SESSION_ID: self._handle_create_session,
+ SUB_REQUEST_ID: self._handle_sub_request,
+ UNSUB_REQUEST_ID: self._handle_unsub_request,
+ QUERY_PROFILE_ID: self._handle_query_profile,
+ SETUP_PROFILE_ID: self._handle_setup_profile,
+ GET_CURRENT_PROFILE_ID: self._handle_get_current_profile,
+ DISCONNECT_HEADSET_ID: self._handle_disconnect_headset,
+ MENTAL_COMMAND_ACTIVE_ACTION_ID: self._handle_mental_command_active_action,
+ MENTAL_COMMAND_TRAINING_THRESHOLD: self._handle_mental_command_training_threshold,
+ MENTAL_COMMAND_BRAIN_MAP_ID: self._handle_mental_command_brain_map,
+ SENSITIVITY_REQUEST_ID: self._handle_sensitivity_request,
+ QUERY_RECORDS_ID: self._handle_query_records,
+ REQUEST_DOWNLOAD_RECORDS_ID: self._handle_request_download_records,
+ CREATE_RECORD_REQUEST_ID: self._handle_create_record_request,
+ STOP_RECORD_REQUEST_ID: self._handle_stop_record_request,
+ EXPORT_RECORD_ID: self._handle_export_record,
+ INJECT_MARKER_REQUEST_ID: self._handle_inject_marker_request,
+ UPDATE_MARKER_REQUEST_ID: self._handle_update_marker_request,
+ }
+ return handlers.get(req_id)
+
+ def _handle_has_access_right(self, result_dic):
+ access_granted = result_dic['accessGranted']
+ if access_granted == True:
+ # authorize
+ self.authorize()
+ else:
+ # request access
+ self.request_access()
+
+ def _handle_request_access(self, result_dic):
+ access_granted = result_dic['accessGranted']
+
+ if access_granted == True:
+ # authorize
+ self.authorize()
+ else:
+ # wait approve from Emotiv Launcher
+ msg = result_dic['message']
+ warnings.warn(msg)
+
+ def _handle_authorize(self, result_dic):
+ print("Authorize successfully.")
+ self.auth = result_dic['cortexToken']
+ if self.auto_create_session:
#After successful authorization, the app will call the API refresh headset list for the first time
self.refresh_headset_list()
# query headsets
self.query_headset()
- elif req_id == QUERY_HEADSET_ID:
- self.headset_list = result_dic
- found_headset = False
- headset_status = ''
- for ele in self.headset_list:
- hs_id = ele['id']
- status = ele['status']
- connected_by = ele['connectedBy']
- print('headsetId: {0}, status: {1}, connected_by: {2}'.format(hs_id, status, connected_by))
- if self.headset_id != '' and self.headset_id == hs_id:
- found_headset = True
- headset_status = status
-
- if len(self.headset_list) == 0:
- self.isHeadsetConnected = False
- warnings.warn("No headset available. Please turn on a headset.")
- elif self.headset_id == '':
- # set first headset is default headset
- self.headset_id = self.headset_list[0]['id']
- # call query headet again
+ else:
+ self.emit('authorize_done')
+
+ def _handle_query_headset(self, result_dic):
+ self.headset_list = result_dic
+ found_headset = False
+ headset_status = ''
+ for ele in self.headset_list:
+ hs_id = ele['id']
+ status = ele['status']
+ connected_by = ele['connectedBy']
+ print('headsetId: {0}, status: {1}, connected_by: {2}'.format(hs_id, status, connected_by))
+ if self.headset_id != '' and self.headset_id == hs_id:
+ found_headset = True
+ headset_status = status
+
+ if len(self.headset_list) == 0:
+ self.isHeadsetConnected = False
+ warnings.warn("No headset available. Please turn on a headset.")
+ elif self.headset_id == '':
+ # set first headset is default headset
+ self.headset_id = self.headset_list[0]['id']
+ # call query headet again
+ self.query_headset()
+ elif found_headset == False:
+ warnings.warn("Can not found the headset " + self.headset_id + ". Please make sure the id is correct.")
+ elif found_headset == True:
+ if headset_status == 'connected':
+ self.isHeadsetConnected = True
+ # create session with the headset
+ self.create_session()
+ elif headset_status == 'discovered':
+ self.connect_headset(self.headset_id)
+ elif headset_status == 'connecting':
+ # wait 3 seconds and query headset again
+ time.sleep(3)
self.query_headset()
- elif found_headset == False:
- warnings.warn("Can not found the headset " + self.headset_id + ". Please make sure the id is correct.")
- elif found_headset == True:
- if headset_status == 'connected':
- self.isHeadsetConnected = True
- # create session with the headset
- self.create_session()
- elif headset_status == 'discovered':
- self.connect_headset(self.headset_id)
- elif headset_status == 'connecting':
- # wait 3 seconds and query headset again
- time.sleep(3)
- self.query_headset()
- else:
- warnings.warn('query_headset resp: Invalid connection status ' + headset_status)
- elif req_id == CREATE_SESSION_ID:
- self.session_id = result_dic['id']
- print("The session " + self.session_id + " is created successfully.")
- self.emit('create_session_done', data=self.session_id)
- elif req_id == SUB_REQUEST_ID:
- # handle data label
- for stream in result_dic['success']:
- stream_name = stream['streamName']
- stream_labels = stream['cols']
- print('The data stream '+ stream_name + ' is subscribed successfully.')
- # ignore com, fac and sys data label because they are handled in on_new_data
- if stream_name != 'com' and stream_name != 'fac':
- self.extract_data_labels(stream_name, stream_labels)
-
- for stream in result_dic['failure']:
- stream_name = stream['streamName']
- stream_msg = stream['message']
- print('The data stream '+ stream_name + ' is subscribed unsuccessfully. Because: ' + stream_msg)
- elif req_id == UNSUB_REQUEST_ID:
- for stream in result_dic['success']:
- stream_name = stream['streamName']
- print('The data stream '+ stream_name + ' is unsubscribed successfully.')
-
- for stream in result_dic['failure']:
- stream_name = stream['streamName']
- stream_msg = stream['message']
- print('The data stream '+ stream_name + ' is unsubscribed unsuccessfully. Because: ' + stream_msg)
-
- elif req_id == QUERY_PROFILE_ID:
- profile_list = []
- for ele in result_dic:
- if 'name' in ele:
- profile_name = str(ele['name'])
- read_only = ele['readOnly']
- print('profile name :', profile_name, " readonly :", read_only)
- profile_list.append(profile_name)
- else:
- print('Result does not contain name field.')
-
- self.emit('query_profile_done', data=profile_list)
- elif req_id == SETUP_PROFILE_ID:
- action = result_dic['action']
- if action == 'create':
- profile_name = result_dic['name']
- if profile_name == self.profile_name:
- # load profile
- self.setup_profile(profile_name, 'load')
- elif action == 'load':
- print('load profile successfully')
- self.emit('load_unload_profile_done', isLoaded=True)
- elif action == 'unload':
- self.emit('load_unload_profile_done', isLoaded=False)
- elif action == 'save':
- self.emit('save_profile_done')
- elif req_id == GET_CURRENT_PROFILE_ID:
- print(result_dic)
- name = result_dic['name']
- if name is None:
- # no profile loaded with the headset
- print('get_current_profile: no profile loaded with the headset ' + self.headset_id)
- self.setup_profile(self.profile_name, 'load')
else:
- loaded_by_this_app = result_dic['loadedByThisApp']
- print('get current profile rsp: ' + name + ", loadedByThisApp: " + str(loaded_by_this_app))
- if name != self.profile_name:
- warnings.warn("There is profile " + name + " is loaded for headset " + self.headset_id)
- elif loaded_by_this_app == True:
- self.emit('load_unload_profile_done', isLoaded=True)
- else:
- self.setup_profile(self.profile_name, 'unload')
- # warnings.warn("The profile " + name + " is loaded by other applications")
- elif req_id == DISCONNECT_HEADSET_ID:
- print("Disconnect headset " + self.headset_id)
- self.headset_id = ''
- elif req_id == MENTAL_COMMAND_ACTIVE_ACTION_ID:
- self.emit('get_mc_active_action_done', data=result_dic)
- elif req_id == MENTAL_COMMAND_TRAINING_THRESHOLD:
- self.emit('mc_training_threshold_done', data=result_dic)
- elif req_id == MENTAL_COMMAND_BRAIN_MAP_ID:
- self.emit('mc_brainmap_done', data=result_dic)
- elif req_id == SENSITIVITY_REQUEST_ID:
- self.emit('mc_action_sensitivity_done', data=result_dic)
- elif req_id == CREATE_RECORD_REQUEST_ID:
- self.record_id = result_dic['record']['uuid']
- self.emit('create_record_done', data=result_dic['record'])
- elif req_id == STOP_RECORD_REQUEST_ID:
- self.emit('stop_record_done', data=result_dic['record'])
- elif req_id == EXPORT_RECORD_ID:
- # handle data lable
- success_export = []
- for record in result_dic['success']:
- record_id = record['recordId']
- success_export.append(record_id)
-
- for record in result_dic['failure']:
- record_id = record['recordId']
- failure_msg = record['message']
- print('export_record resp failure cases: '+ record_id + ":" + failure_msg)
-
- self.emit('export_record_done', data=success_export)
- elif req_id == INJECT_MARKER_REQUEST_ID:
- self.emit('inject_marker_done', data=result_dic['marker'])
- elif req_id == UPDATE_MARKER_REQUEST_ID:
- self.emit('update_marker_done', data=result_dic['marker'])
+ warnings.warn('query_headset resp: Invalid connection status ' + headset_status)
+
+ def _handle_create_session(self, result_dic):
+ self.session_id = result_dic['id']
+ print("The session " + self.session_id + " is created successfully.")
+ self.emit('create_session_done', data=self.session_id)
+
+ def _handle_sub_request(self, result_dic):
+ # handle data label
+ for stream in result_dic['success']:
+ stream_name = stream['streamName']
+ stream_labels = stream['cols']
+ print('The data stream '+ stream_name + ' is subscribed successfully.')
+ # ignore com, fac and sys data label because they are handled in on_new_data
+ if stream_name != 'com' and stream_name != 'fac':
+ self.extract_data_labels(stream_name, stream_labels)
+
+ for stream in result_dic['failure']:
+ stream_name = stream['streamName']
+ stream_msg = stream['message']
+ print('The data stream '+ stream_name + ' is subscribed unsuccessfully. Because: ' + stream_msg)
+
+ def _handle_unsub_request(self, result_dic):
+ for stream in result_dic['success']:
+ stream_name = stream['streamName']
+ print('The data stream '+ stream_name + ' is unsubscribed successfully.')
+
+ for stream in result_dic['failure']:
+ stream_name = stream['streamName']
+ stream_msg = stream['message']
+ print('The data stream '+ stream_name + ' is unsubscribed unsuccessfully. Because: ' + stream_msg)
+
+ def _handle_query_profile(self, result_dic):
+ profile_list = []
+ for ele in result_dic:
+ if 'name' in ele:
+ profile_name = str(ele['name'])
+ read_only = ele['readOnly']
+ print('profile name :', profile_name, " readonly :", read_only)
+ profile_list.append(profile_name)
+ else:
+ print('Result does not contain name field.')
+
+ self.emit('query_profile_done', data=profile_list)
+
+ def _handle_setup_profile(self, result_dic):
+ action = result_dic['action']
+ if action == 'create':
+ profile_name = result_dic['name']
+ if profile_name == self.profile_name:
+ # load profile
+ self.setup_profile(profile_name, 'load')
+ elif action == 'load':
+ print('load profile successfully')
+ self.emit('load_unload_profile_done', isLoaded=True)
+ elif action == 'unload':
+ self.emit('load_unload_profile_done', isLoaded=False)
+ elif action == 'save':
+ self.emit('save_profile_done')
+
+ def _handle_get_current_profile(self, result_dic):
+ print(result_dic)
+ name = result_dic['name']
+ if name is None:
+ # no profile loaded with the headset
+ print('get_current_profile: no profile loaded with the headset ' + self.headset_id)
+ self.setup_profile(self.profile_name, 'load')
else:
- print('No handling for response of request ' + str(req_id))
+ loaded_by_this_app = result_dic['loadedByThisApp']
+ print('get current profile rsp: ' + name + ", loadedByThisApp: " + str(loaded_by_this_app))
+ if name != self.profile_name:
+ warnings.warn("There is profile " + name + " is loaded for headset " + self.headset_id)
+ elif loaded_by_this_app == True:
+ self.emit('load_unload_profile_done', isLoaded=True)
+ else:
+ self.setup_profile(self.profile_name, 'unload')
+ # warnings.warn("The profile " + name + " is loaded by other applications")
+
+ def _handle_disconnect_headset(self, result_dic):
+ print("Disconnect headset " + self.headset_id)
+ self.headset_id = ''
+
+ def _handle_mental_command_active_action(self, result_dic):
+ self.emit('get_mc_active_action_done', data=result_dic)
+
+ def _handle_mental_command_training_threshold(self, result_dic):
+ self.emit('mc_training_threshold_done', data=result_dic)
+
+ def _handle_mental_command_brain_map(self, result_dic):
+ self.emit('mc_brainmap_done', data=result_dic)
+
+ def _handle_sensitivity_request(self, result_dic):
+ self.emit('mc_action_sensitivity_done', data=result_dic)
+
+ def _handle_query_records(self, result_dic):
+ record_num = result_dic['count']
+ limit_num = result_dic['limit']
+ offset_num = result_dic['offset']
+ records = result_dic['records']
+ self.emit('query_records_done', data=records, count=record_num, limit=limit_num, offset=offset_num)
+
+ def _handle_request_download_records(self, result_dic):
+ self.emit('request_download_records_done', data=result_dic)
+
+ def _handle_create_record_request(self, result_dic):
+ self.record_id = result_dic['record']['uuid']
+ self.emit('create_record_done', data=result_dic['record'])
+
+ def _handle_stop_record_request(self, result_dic):
+ self.emit('stop_record_done', data=result_dic['record'])
+
+ def _handle_export_record(self, result_dic):
+ # handle data lable
+ success_export = []
+ for record in result_dic['success']:
+ record_id = record['recordId']
+ success_export.append(record_id)
+
+ for record in result_dic['failure']:
+ record_id = record['recordId']
+ failure_msg = record['message']
+ print('export_record resp failure cases: '+ record_id + ":" + failure_msg)
+
+ self.emit('export_record_done', data=success_export)
+
+ def _handle_inject_marker_request(self, result_dic):
+ self.emit('inject_marker_done', data=result_dic['marker'])
+
+ def _handle_update_marker_request(self, result_dic):
+ self.emit('update_marker_done', data=result_dic['marker'])
def handle_error(self, recv_dic):
req_id = recv_dic['id']
@@ -334,33 +426,57 @@ def handle_error(self, recv_dic):
self.emit('inform_error', error_data=recv_dic['error'])
def handle_warning(self, warning_dic):
-
if self.debug:
print(warning_dic)
+
warning_code = warning_dic['code']
warning_msg = warning_dic['message']
- if warning_code == ACCESS_RIGHT_GRANTED:
- # call authorize again
- self.authorize()
- elif warning_code == HEADSET_CONNECTED:
- # query headset again then create session
- self.query_headset()
- elif warning_code == CORTEX_AUTO_UNLOAD_PROFILE:
- self.profile_name = ''
- elif warning_code == CORTEX_STOP_ALL_STREAMS:
- # print(warning_msg['behavior'])
- session_id = warning_msg['sessionId']
- if session_id == self.session_id:
- self.emit('warn_cortex_stop_all_sub', data=session_id)
- self.session_id = ''
- elif warning_code == CORTEX_RECORD_POST_PROCESSING_DONE:
- record_id = warning_msg['recordId']
- self.emit('warn_record_post_processing_done', data=record_id)
- elif warning_code == HEADSET_SCANNING_FINISHED:
- # After headset scanning finishes, if no headset is connected yet, the app should call the controlDevice("refresh") again
- # We recommend the app should NOT call controlDevice("refresh") when a headset is connected, to have the best data stream quality.
- if (self.isHeadsetConnected == False):
- self.refresh_headset_list()
+
+ # Dictionary dispatch pattern for better readability and performance
+ handler = self._get_warning_handler(warning_code)
+ if handler:
+ handler(warning_msg)
+ # Note: No else clause needed as some warning codes may not require handling
+
+ def _get_warning_handler(self, warning_code):
+ """Return the appropriate handler function for the given warning code."""
+ handlers = {
+ ACCESS_RIGHT_GRANTED: self._handle_access_right_granted,
+ HEADSET_CONNECTED: self._handle_headset_connected,
+ CORTEX_AUTO_UNLOAD_PROFILE: self._handle_cortex_auto_unload_profile,
+ CORTEX_STOP_ALL_STREAMS: self._handle_cortex_stop_all_streams,
+ CORTEX_RECORD_POST_PROCESSING_DONE: self._handle_cortex_record_post_processing_done,
+ HEADSET_SCANNING_FINISHED: self._handle_headset_scanning_finished,
+ }
+ return handlers.get(warning_code)
+
+ def _handle_access_right_granted(self, warning_msg):
+ # call authorize again
+ self.authorize()
+
+ def _handle_headset_connected(self, warning_msg):
+ # query headset again then create session
+ self.query_headset()
+
+ def _handle_cortex_auto_unload_profile(self, warning_msg):
+ self.profile_name = ''
+
+ def _handle_cortex_stop_all_streams(self, warning_msg):
+ # print(warning_msg['behavior'])
+ session_id = warning_msg['sessionId']
+ if session_id == self.session_id:
+ self.emit('warn_cortex_stop_all_sub', data=session_id)
+ self.session_id = ''
+
+ def _handle_cortex_record_post_processing_done(self, warning_msg):
+ record_id = warning_msg['recordId']
+ self.emit('warn_record_post_processing_done', data=record_id)
+
+ def _handle_headset_scanning_finished(self, warning_msg):
+ # After headset scanning finishes, if no headset is connected yet, the app should call the controlDevice("refresh") again
+ # We recommend the app should NOT call controlDevice("refresh") when a headset is connected, to have the best data stream quality.
+ if (self.isHeadsetConnected == False):
+ self.refresh_headset_list()
def handle_stream_data(self, result_dic):
if result_dic.get('com') != None:
@@ -705,6 +821,41 @@ def train_request(self, detection, action, status):
self.ws.send(json.dumps(train_request_json))
+ def query_records(self, query_params):
+ print('query records --------------------------------')
+
+ params_val = {"cortexToken": self.auth}
+
+ for key, value in query_params.items():
+ params_val.update({key: value})
+
+ query_records_request = {
+ "jsonrpc": "2.0",
+ "method": "queryRecords",
+ "params": params_val,
+ "id": QUERY_RECORDS_ID
+ }
+ if self.debug:
+ print('query records request:\n', json.dumps(query_records_request, indent=4))
+
+ self.ws.send(json.dumps(query_records_request))
+
+ def request_download_records(self, recordIds):
+ print('request to download records --------------------------------')
+
+ params_val = {"cortexToken": self.auth, 'recordIds': recordIds}
+
+ download_records_request = {
+ "jsonrpc": "2.0",
+ "method": "requestToDownloadRecordData",
+ "params": params_val,
+ "id": REQUEST_DOWNLOAD_RECORDS_ID
+ }
+ if self.debug:
+ print('requestToDownloadRecordData request:\n', json.dumps(download_records_request, indent=4))
+
+ self.ws.send(json.dumps(download_records_request))
+
def create_record(self, title, **kwargs):
print('create record --------------------------------')
diff --git a/python/facial_expression_train.py b/python/facial_expression_train.py
index 61b3e18..fc881df 100644
--- a/python/facial_expression_train.py
+++ b/python/facial_expression_train.py
@@ -258,9 +258,11 @@ def on_inform_error(self, *args, **kwargs):
def main():
- # Please fill your application clientId and clientSecret before running script
- your_app_client_id = ''
- your_app_client_secret = ''
+ # Enter your application Client ID and Client Secret below.
+ # You can obtain these credentials after registering your App ID with the Cortex SDK for development.
+ # For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
+ your_app_client_id = 'put_your_app_client_id_here'
+ your_app_client_secret = 'put_your_app_client_secret_here'
# Init Train
t=Train(your_app_client_id, your_app_client_secret)
diff --git a/python/live_advance.py b/python/live_advance.py
index 69eab76..c1b4c1a 100644
--- a/python/live_advance.py
+++ b/python/live_advance.py
@@ -275,9 +275,11 @@ def on_inform_error(self, *args, **kwargs):
def main():
- # Please fill your application clientId and clientSecret before running script
- your_app_client_id = ''
- your_app_client_secret = ''
+ # Enter your application Client ID and Client Secret below.
+ # You can obtain these credentials after registering your App ID with the Cortex SDK for development.
+ # For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
+ your_app_client_id = 'put_your_app_client_id_here'
+ your_app_client_secret = 'put_your_app_client_secret_here'
# Init live advance
l = LiveAdvance(your_app_client_id, your_app_client_secret)
diff --git a/python/marker.py b/python/marker.py
index a6b535c..1c75355 100644
--- a/python/marker.py
+++ b/python/marker.py
@@ -174,7 +174,6 @@ def on_warn_record_post_processing_done(self, *args, **kwargs):
# - Please reference to https://emotiv.gitbook.io/cortex-api/ first.
# - Connect your headset with dongle or bluetooth. You can see the headset via Emotiv Launcher
# - Please make sure the your_app_client_id and your_app_client_secret are set before starting running.
-# - In the case you borrow license from others, you need to add license = "xxx-yyy-zzz" as init parameter
# - Check the on_create_session_done() to see a record is created.
# RESULT
# - record data then inject marker each 3 seconds
@@ -184,9 +183,11 @@ def on_warn_record_post_processing_done(self, *args, **kwargs):
def main():
- # Please fill your application clientId and clientSecret before running script
- your_app_client_id = ''
- your_app_client_secret = ''
+ # Enter your application Client ID and Client Secret below.
+ # You can obtain these credentials after registering your App ID with the Cortex SDK for development.
+ # For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
+ your_app_client_id = 'put_your_app_client_id_here'
+ your_app_client_secret = 'put_your_app_client_secret_here'
m = Marker(your_app_client_id, your_app_client_secret)
diff --git a/python/mental_command_train.py b/python/mental_command_train.py
index 728445e..0b4f986 100644
--- a/python/mental_command_train.py
+++ b/python/mental_command_train.py
@@ -269,9 +269,11 @@ def on_inform_error(self, *args, **kwargs):
def main():
- # Please fill your application clientId and clientSecret before running script
- your_app_client_id = ''
- your_app_client_secret = ''
+ # Enter your application Client ID and Client Secret below.
+ # You can obtain these credentials after registering your App ID with the Cortex SDK for development.
+ # For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
+ your_app_client_id = 'put_your_app_client_id_here'
+ your_app_client_secret = 'put_your_app_client_secret_here'
# Init Train
t=Train(your_app_client_id, your_app_client_secret)
diff --git a/python/query_records.py b/python/query_records.py
new file mode 100644
index 0000000..15aaf13
--- /dev/null
+++ b/python/query_records.py
@@ -0,0 +1,205 @@
+from cortex import Cortex
+
+class Records():
+ """
+ A class to query records, request to download undownloaded records, and export local records
+
+ Attributes
+ ----------
+ c : Cortex
+ Cortex communicate with Emotiv Cortex Service
+
+ Methods
+ -------
+ start():
+ start authorize process.
+ query_records():
+ To query records owned by the current user
+ request_download_records()
+ To request to download records if they are not at local machine
+ export_record()
+ To export records to CSV/ EDF files
+ """
+ def __init__(self, app_client_id, app_client_secret, **kwargs):
+ """
+ Constructs cortex client and bind a function to query records, request to download and export records
+ If you do not want to log request and response message , set debug_mode = False. The default is True
+ """
+ print("Query Records __init__")
+ self.c = Cortex(app_client_id, app_client_secret, debug_mode=True, **kwargs)
+ self.c.bind(authorize_done=self.on_authorize_done)
+ self.c.bind(query_records_done=self.on_query_records_done)
+ self.c.bind(export_record_done=self.on_export_record_done)
+ self.c.bind(request_download_records_done=self.on_request_download_records_done)
+ self.c.bind(inform_error=self.on_inform_error)
+
+ def start(self):
+ """
+ To open websocket and process authorizing step.
+ After get authorize_done The program will query records
+ Returns
+ -------
+ None
+ """
+ self.c.open()
+
+ def query_records(self, license_id, application_id):
+ """
+ To query records
+ Parameters
+ ----------
+ orderBy : array of object, required : Specify how to sort the records.
+ query: object, required: An object to filter the records.
+ If you set an empty object, it will return all records created by your application
+ If you want get records created by other application, you need set licenseId and applicationId as parameter of query object
+ More detail at https://emotiv.gitbook.io/cortex-api/records/queryrecords
+ Returns
+ -------
+ """
+
+ query_obj = {}
+ if license_id != '':
+ query_obj["licenseId"] = license_id
+ if application_id != '':
+ query_obj["applicationId"] = application_id
+
+ query_params = {
+ "orderBy": [{ "startDatetime": "DESC" }],
+ "query": query_obj,
+ "includeSyncStatusInfo":True
+ }
+ self.c.query_records(query_params)
+
+ def request_download_records(self, record_ids):
+ """
+ To request to download records
+ Parameters
+ ----------
+ record_ids : list, required: list of wanted record ids
+ More detail at https://emotiv.gitbook.io/cortex-api/records/requesttodownloadrecorddata
+ Returns
+ -------
+ None
+ """
+ self.c.request_download_records(record_ids)
+
+ def export_record(self, record_ids, license_ids):
+ """
+ To export records
+ By default, you can only export the records that were created by your application.
+ If you want to export a record that was created by another applications
+ then you must provide the license ids of those applications in the parameter licenseIds.
+ Parameters
+ record_ids: list, required: list of wanted export record ids
+ license_ids: list, no required: list of license id of other applications
+ ----------
+ More detail at https://emotiv.gitbook.io/cortex-api/records/exportrecord
+ Returns
+ -------
+ None
+ """
+ folder = '' # your place to export, you should have write permission, for example: 'C:\\Users\\NTT\\Desktop'
+ stream_types = ['EEG', 'MOTION', 'PM', 'BP']
+ export_format = 'CSV' # support 'CSV' or 'EDF'
+ version = 'V2'
+ self.c.export_record(folder, stream_types, export_format, record_ids, version, licenseIds=license_ids)
+
+
+ # callbacks functions
+ def on_authorize_done(self, *args, **kwargs):
+ print('on_authorize_done')
+ # query records
+ self.query_records(self.license_id, self.application_id)
+
+ # callbacks functions
+ def on_query_records_done(self, *args, **kwargs):
+ data = kwargs.get('data')
+ count = kwargs.get('count')
+ print('on_query_records_done: total records are {0}'.format(count))
+ # print(data)
+ not_downloaded_record_Ids = []
+ export_record_Ids = []
+ license_ids = []
+ for item in data:
+ uuid = item['uuid']
+ sync_status = item["syncStatus"]["status"]
+ application_id = item["applicationId"]
+ license_id = item["licenseId"]
+ print("recordId {0}, applicationId {1}, sync status {2}".format(uuid, application_id, sync_status))
+ if (sync_status == "notDownloaded") :
+ not_downloaded_record_Ids.append(uuid)
+ elif (sync_status == "neverUploaded") or (sync_status == "downloaded"):
+ export_record_Ids.append(uuid)
+ if license_id not in license_ids:
+ license_ids.append(license_id)
+
+ # download records has not downloaded to local machine
+ if len(not_downloaded_record_Ids) > 0:
+ self.request_download_records(not_downloaded_record_Ids)
+
+ # Open comment below to export records
+ # if len(export_record_Ids) > 0: # or export records are in local machine
+ # self.export_record(export_record_Ids, license_ids)
+
+ def on_export_record_done(self, *args, **kwargs):
+ print('on_export_record_done: the successful record exporting as below:')
+ data = kwargs.get('data')
+ print(data)
+ self.c.close()
+
+ def on_request_download_records_done(self, *args, **kwargs):
+ data = kwargs.get('data')
+ success_records = []
+ for item in data['success']:
+ record_Id = item['recordId']
+ print('The record '+ record_Id + ' is downloaded successfully.')
+ success_records.append(record_Id)
+
+ for item in data['failure']:
+ record_Id = item['recordId']
+ failed_msg = item['message']
+ print('The record '+ record_Id + ' is downloaded unsuccessfully. Because: ' + failed_msg)
+
+ self.c.close()
+
+ def on_inform_error(self, *args, **kwargs):
+ error_data = kwargs.get('error_data')
+ print(error_data)
+
+# -----------------------------------------------------------
+#
+# GETTING STARTED
+# - Please reference to https://emotiv.gitbook.io/cortex-api/ first.
+# - Please make sure the your_app_client_id and your_app_client_secret are set before starting running.
+# RESULT
+# - on_query_records_done: will show all records filtered by query condition in query_record
+# - on_export_record_done: will show the successful exported record
+# - on_request_download_records_done: will show all success and failure download case
+#
+# -----------------------------------------------------------
+
+def main():
+
+ # Enter your application Client ID and Client Secret below.
+ # You can obtain these credentials after registering your App ID with the Cortex SDK for development.
+ # For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
+ your_app_client_id = 'put_your_app_client_id_here'
+ your_app_client_secret = 'put_your_app_client_secret_here'
+
+ # Don't need to create session in this case
+ r = Records(your_app_client_id, your_app_client_secret, auto_create_session= False)
+
+ # As default, the Program will query records of your application.
+ # In the case, you want to query records created from other application (such as EmotivPRO).
+ # You need set license_id and application_id of the application
+ # If set license_id without application_id. It will return records created from all applications use the license_id
+ # If set both license_id and application_id. It will return records from the application has the application_id
+ r.license_id = ''
+ r.application_id = ''
+
+ r.start()
+
+if __name__ =='__main__':
+ main()
+
+# -----------------------------------------------------------
diff --git a/python/record.py b/python/record.py
index 6f3c611..6060186 100644
--- a/python/record.py
+++ b/python/record.py
@@ -134,7 +134,6 @@ def on_inform_error(self, *args, **kwargs):
# - Please reference to https://emotiv.gitbook.io/cortex-api/ first.
# - Connect your headset with dongle or bluetooth. You can see the headset via Emotiv Launcher
# - Please make sure the your_app_client_id and your_app_client_secret are set before starting running.
-# - In the case you borrow license from others, you need to add license = "xxx-yyy-zzz" as init parameter
# - Check the on_create_session_done() to see how to create a record.
# - Check the on_warn_cortex_stop_all_sub() to see how to export record
# RESULT
@@ -147,9 +146,11 @@ def on_inform_error(self, *args, **kwargs):
def main():
- # Please fill your application clientId and clientSecret before running script
- your_app_client_id = ''
- your_app_client_secret = ''
+ # Enter your application Client ID and Client Secret below.
+ # You can obtain these credentials after registering your App ID with the Cortex SDK for development.
+ # For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
+ your_app_client_id = 'put_your_app_client_id_here'
+ your_app_client_secret = 'put_your_app_client_secret_here'
r = Record(your_app_client_id, your_app_client_secret)
diff --git a/python/sub_data.py b/python/sub_data.py
index fba3347..181ec25 100644
--- a/python/sub_data.py
+++ b/python/sub_data.py
@@ -217,7 +217,6 @@ def on_inform_error(self, *args, **kwargs):
# - Please reference to https://emotiv.gitbook.io/cortex-api/ first.
# - Connect your headset with dongle or bluetooth. You can see the headset via Emotiv Launcher
# - Please make sure the your_app_client_id and your_app_client_secret are set before starting running.
-# - In the case you borrow license from others, you need to add license = "xxx-yyy-zzz" as init parameter
# RESULT
# - the data labels will be retrieved at on_new_data_labels
# - the data will be retreived at on_new_[dataStream]_data
@@ -226,9 +225,11 @@ def on_inform_error(self, *args, **kwargs):
def main():
- # Please fill your application clientId and clientSecret before running script
- your_app_client_id = ''
- your_app_client_secret = ''
+ # Enter your application Client ID and Client Secret below.
+ # You can obtain these credentials after registering your App ID with the Cortex SDK for development.
+ # For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
+ your_app_client_id = 'put_your_app_client_id_here'
+ your_app_client_secret = 'put_your_app_client_secret_here'
s = Subcribe(your_app_client_id, your_app_client_secret)
diff --git a/unity/Assets/Plugins/AppConfig.cs b/unity/Assets/Plugins/AppConfig.cs
index 2a8914c..fc533d4 100644
--- a/unity/Assets/Plugins/AppConfig.cs
+++ b/unity/Assets/Plugins/AppConfig.cs
@@ -3,18 +3,24 @@
///
public static class AppConfig
{
- public static string AppVersion = "1.1.0";
- public static string AppName = "UnityApp";
- public static string ClientId = "put_your_client_id_here";
- public static string ClientSecret = "put_your_client_secret_here";
- public static bool IsDataBufferUsing = false; // Set false if you want to display data directly to MessageLog without storing in Data Buffer
- public static bool AllowSaveLogToFile = false; // Set true to save log to file and cortex token to local file for next time use
-
+ /*
+ * Enter your application Client ID and Client Secret below.
+ * You can obtain these credentials after registering your App ID with the Cortex SDK for development.
+ * For instructions, visit: https://emotiv.gitbook.io/cortex-api#create-a-cortex-app
+ */
+ public static string ClientId = "put_your_client_id_here";
+ public static string ClientSecret = "put_your_client_secret_here";
+
+ public static string AppVersion = "1.1.0";
+ public static string AppName = "UnityApp";
+ public static bool IsDataBufferUsing = false; // Set false if you want to display data directly to MessageLog without storing in Data Buffer
+ public static bool AllowSaveLogToFile = false; // Set true to save log to file and cortex token to local file for next time use
+
#if !USE_EMBEDDED_LIB && !UNITY_ANDROID && !UNITY_IOS
// only for desktop without embedded cortex
- public static string AppUrl = "wss://localhost:6868"; // for desktop without embedded cortex
+ public static string AppUrl = "wss://localhost:6868"; // for desktop without embedded cortex
#else
- public static string AppUrl = ""; // Don't need AppUrl for mobile and embedded cortex
+ public static string AppUrl = ""; // Don't need AppUrl for mobile and embedded cortex
#endif
}
\ No newline at end of file
diff --git a/unity/Assets/Plugins/Emotiv-Unity-Plugin b/unity/Assets/Plugins/Emotiv-Unity-Plugin
index a6334de..245f4e0 160000
--- a/unity/Assets/Plugins/Emotiv-Unity-Plugin
+++ b/unity/Assets/Plugins/Emotiv-Unity-Plugin
@@ -1 +1 @@
-Subproject commit a6334def9755dc884c0417aed6e2a03796cbb88c
+Subproject commit 245f4e0b1a75c19fb70cc3d93846152fb1d5fb33
diff --git a/unity/Assets/SimpleExample.cs b/unity/Assets/SimpleExample.cs
index 32baf9a..53ed419 100644
--- a/unity/Assets/SimpleExample.cs
+++ b/unity/Assets/SimpleExample.cs
@@ -292,9 +292,14 @@ public void onExportRecordBtnClick()
// Determine the folder path for export. Default to Desktop folder (or persistent data path on mobile)
string folderPath;
- #if UNITY_ANDROID || UNITY_IOS
+ #if UNITY_ANDROID
// On mobile, use persistent data path
folderPath = Application.persistentDataPath;
+ #elif UNITY_IOS
+ // On iOS do not need to specify the folder.
+ // Cortex exports the data to the "Documents" folder of the current application.
+ // Empty path lets the plugin handle default Documents folder location
+ folderPath = "";
#else
// On desktop, use Desktop folder
folderPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);