{ "info": { "author": "methylDragon", "author_email": "methylDragon@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "# Emotiv Cortex2 Python Client\n\n[![PyPI version](https://badge.fury.io/py/cortex2.svg)](https://badge.fury.io/py/cortex2)\n\nAuthor: methylDragon\n\n---\n\n![Image result for emotiv epoc](https://raw.githubusercontent.com/methylDragon/emotiv-cortex2-python-client/master/assets/Epoc-product-image.png)\n\n![Image result for emotiv logo](https://raw.githubusercontent.com/methylDragon/emotiv-cortex2-python-client/master/assets/Emotiv_logo.svg.png)\n\nImage sources: [Emotiv](emotiv.com)\n\n\n\n## Introduction\n\nUnofficial Python client for the Emotiv EEG Cortex 2 API.\n\nFeatures the entire JSON-RPC API communicated via asynchronous websockets for speed.\n\nThe Cortex 2 app is used to host a websocket web server gateway interface that takes JSON requests and returns JSON data.\n\nThis Python client is designed to be a wrapper client for said API. It streams multi-session sensor data using a separate asynchronous thread!\n\nAPI Reference: https://emotiv.gitbook.io/cortex-api/\n\nPyPI Link: \n\n\n\n## Support my efforts!\n\n [![Yeah! Buy the DRAGON a COFFEE!](https://raw.githubusercontent.com/methylDragon/emotiv-cortex2-python-client/master/assets/COFFEE%20BUTTON%20%E3%83%BE(%C2%B0%E2%88%87%C2%B0%5E).png)](https://www.buymeacoffee.com/methylDragon)\n\n[Or leave a tip! \u30fe(\u00b0\u2207\u00b0*)](https://www.paypal.me/methylDragon)\n\n\n\n## Requirements\n\n- Python 3.6 or above\n\n\n\n## Installation\n\n```shell\npip install cortex2\n```\n\n\n\n## Example Usage\n\nRemember to set up your client ID and secret by [registering](https://www.emotiv.com/developer/)\n\nAlso ensure you've started the EmotivApp, since it hosts the websocket server! (You'll probably have to use Windows or Mac.)\n\nIf the client fails to connect, you might have to restart the script using the client.\n\n```python\nfrom cortex2 import EmotivCortex2Client\n\nurl = \"wss://localhost:6868\"\n\n# Remember to start the Emotiv App before you start!\n# Start client with authentication\nclient = EmotivCortex2Client(url,\n client_id='CLIENT_ID',\n client_secret=\"CLIENT_SECRET\",\n check_response=True,\n authenticate=True,\n debug=False)\n\n# Test API connection by using the request access method\nclient.request_access()\n\n# Explicit call to Authenticate (approve and get Cortex Token)\nclient.authenticate()\n\n# Connect to headset, connect to the first one found, and start a session for it\nclient.query_headsets()\nclient.connect_headset(0)\nclient.create_session(0)\n\n# Subscribe to the motion and mental command streams\n# Spins up a separate subscription thread\nclient.subscribe(streams=[\"mot\", \"com\"])\n\n# Test message handling speed\na = client.subscriber_messages_handled\ntime.sleep(5)\nb = client.subscriber_messages_handled\nprint((b - a) / 5)\n\n# Grab a single instance of data\nprint(client.receive_data())\n\n# Continously grab data, while making requests periodically\nwhile True:\n counter += 1\n # time.sleep(0.1)\n\n if counter % 5000 == 0:\n print(client.request_access())\n\n # Try stopping the subscriber thread\n if counter == 50000:\n client.stop_subscriber()\n break\n\n try:\n # Check the latest data point from the motion stream, from the first session\n print(list(client.data_streams.values())[0]['mot'][0])\n except:\n pass\n```\n\n**You can also connect by explicitly stating IDs!**\n\n```python\nclient.connect_headset(headset_id=\"EPOCPLUS-3B9AXXXX\")\nclient.create_session(headset_id=\"EPOCPLUS-3B9AXXXX\")\n```\n\n**You can do a lot more! The entire API is covered, and everything generally works the same way.**\n\n\n\n## Additional Notable Features\n\n- The websockets API is wrapped with helper functions to do request ID checking, asynchronous message handling, and some basic data sanitisation\n- Threads are used to try to allow for subscription to **multiple** sessions and **multiple** headsets\n - The threads have a helper function that automatically splits the data streams so you may work on the streams independently as opposed to all lumped together as one object\n- Automatic syncing helper methods for detecting existing sessions and headsets will fire off when the relevant methods are called. (Eg. when trying to update, connect, or disconnect headsets, the client will automatically query and update the dictionary of seen headsets.)\n\n\n\n## Notable Class Attributes\n\n| Name | Type | Description | Example |\n| --------------------------- | ----------------- | ------------------------------------------------------------ | ----------------------------------------------------- |\n| debug | bool | Debug flag. Slows down data subscription rate if set. | |\n| client_id | str | Client ID | |\n| client_secret | str | Client Secret | |\n| cortex_token | str | Cortex Token | |\n| approved | bool | True if access is granted | |\n| authorized | bool | True if cortex token is issued | |\n| headsets | OrderedDict() | OrderedDict of headset object dicts seen | |\n| connected_headsets | OrderedDict() | OrderedDict of connected headset object dicts | |\n| sessions | OrderedDict() | OrderedDict of seen session object dicts | |\n| data_deque_size | int | Maximum size of each subscriber data buffer | |\n| data_streams | dict | Sensor data streams keyed by session | {session_id: {data_stream_deques_types: [data]}, ...} |\n| subscribed_streams | dict | Data stream names and descriptions that are subscribed, keyed by session | {session_id: {stream_names: info}, ...} |\n| subscriber_spinning | bool | Tracks whether the subscriber thread is running | |\n| subscriber_messages_handled | int | Counts how many subscriber messages have been handled | |\n| subscriber_reading | threading.Event() | Event flag to pause the subscriber without killing it | |\n\n\n\n## Class Methods\n\n### Class Helpers\n\n- `send_authed_request(method=None, params={}, request=None)`\n- `send_request(method=None, params={}, request=None)`\n- `set_client_id(client_id)`\n- `set_client_secret(client_secret)`\n- `_verify_key(key, response, field=None`)\n- `_exit_handler(\\_signo, _stack_frame)`\n\n---\n\n### Connecting to Cortex API\n\n- `get_cortex_info()`\n\n---\n\n### Authentication\n\n- `get_user_login()`\n- `request_access()`\n- `has_access_right()`\n- `authorize()`\n- `generate_new_token()`\n- `get_user_information()`\n- `get_license_info()`\n\n\n\n- **Helpers**\n - `authenticate()`\n\n---\n\n### Headsets\n\n- `query_headsets(id=None, sync=True)`\n- `control_device(command, headset_id=None, mappings=None)`\n- `refresh_headsets()`\n- `connect_headset(headset_id_idx=0, headset_id=None, mappings=None)`\n- `disconnect_headset(headset_id)`\n- `update_headset(settings, headset_id_idx=0, headset_id=None)`\n- `maximise_headset(headset_id_idx=0, headset_id=None)`\n- `maximise_headset_no_motion(headset_id_idx=0, headset_id=None)`\n\n\n\n- **Helpers**\n - `sync_headsets(response=None, query=True)`\n - `_get_headset_type(headset_id)`\n\n---\n\n### Sessions\n\n- `create_session(headset_id_idx=0, headset_id=None)`\n- `create_activated_session(headset_id_idx=0, headset_id=None)`\n- `update_session(status, session_id_idx=0, session_id=None)`\n- `activate_session(session_id_idx=0, session_id=None)`\n- `close_session(session_id_idx=0, session_id=None)`\n- `query_sessions(sync=True)`\n\n\n\n- **Helpers**\n - `sync_sessions(response=None, query=True)`\n\n---\n\n### Data Subscription\n\n- `subscribe(streams, session_id_idx=0, session_id=None)`\n- `unsubscribe(streams, session_id_idx=0, session_id=None)`\n\n\n\n- #### **Helpers**\n\n - `receive_data()`\n - `spin_subscriber()`\n - `stop_subscriber()`\n - `pause_subscriber()`\n - `resume_subscriber()`\n - `_subscriber_thread()`\n - `_create_data_streams(session_id)`\n - `_delete_data_streams(session_id)`\n - `_split_and_update_data_streams(data_sample)`\n\n---\n\n### Records\n\n- `create_record(title, session_id_idx=0, session_id=None, description=None, subject_name=None, tags=None)`\n- `stop_record(title, session_id_idx=0, session_id=None)`\n- `update_record(record_id, description=None, tags=None)`\n- `delete_record(record_ids)`\n- `export_record(record_ids, folder, format, stream_types, version=None)`\n- `export_edf(record_ids, folder, stream_types)`\n- `export_csv_v1(record_ids, folder, stream_types)`\n- `export_csv_v2(record_ids, folder, stream_types)`\n- `query_records(query, order_by={'startDatetime': \"DESC\"}, limit=0, offset=0, include_markers=False)`\n- `get_record_infos(record_ids)`\n\n---\n\n### Markers\n\n- `inject_marker(time, value, label, session_id_idx=0, session_id=None, port=None, extras=None)`\n- `update_marker(marker_id, time, session_id_idx=0, session_id=None, extras=None)`\n\n---\n\n### BCI\n\n- `query_profiles()`\n- `get_current_profile(headset_id_idx=0, headset_id=None)`\n- `setup_profile(status, profile, headset_id_idx=0, headset_id=None, new_profile_name=None)`\n- `create_profile(profile)`\n- `load_profile(profile, headset_id_idx=0, headset_id=None)`\n- `unload_profile(profile, headset_id_idx=0, headset_id=None)`\n- `save_profile(profile)`\n- `rename_profile(profile, headset_id_idx=0, headset_id=None, new_profile_name=None)`\n- `delete_profile(profile)`\n- `load_guest_profile(headset_id_idx=0, headset_id=None)`\n- `get_detection_info(detection)`\n- `get_mental_command_info()`\n- `get_facial_expression_info()`\n- `training(detection, status, action, session_id_idx=0, session_id=None)`\n- `start_training(detection, action, session_id_idx=0, session_id=None)`\n- `accept_training(detection, action, session_id_idx=0, session_id=None)`\n- `reject_training(detection, action, session_id_idx=0, session_id=None)`\n- `reset_training(detection, action, session_id_idx=0, session_id=None)`\n- `erase_training(detection, action, session_id_idx=0, session_id=None)`\n\n\n\n---\n\n### Advanced BCI\n\n- `get_trained_signature_actions(detection, profile=None, session=None)`\n- `get_training_time(detection, session_id_idx=0, session_id=None)`\n- `facial_expression_signature_type(status, profile=None, session=None, signature=None)`\n- `get_facial_expression_signature_type(profile=None, session=None)`\n- `set_facial_expression_signature_type(profile=None, session=None, signature=None)`\n- `facial_expression_threshold(status, action, profile=None, session=None, value=None)`\n- `get_facial_expression_threshold(action, profile=None, session=None)`\n- `set_facial_expression_threshold(action, profile=None, session=None, value=None)`\n- `mental_command_active_action(status, profile=None, session=None, actions=[])`\n- `get_mental_command_active_action(profile=None, session=None)`\n- `set_mental_command_active_action(profile=None, session=None, actions=[])`\n- `mental_command_brain_map(profile=None, session=None)`\n- `mental_command_get_skill_rating(profile=None, session=None, action=None)`\n- `mental_command_training_threshold(profile=None, session=None)`\n- `mental_command_action_level(status, profile=None, session=None, level=None)`\n- `get_mental_command_action_level(profile=None, session=None)`\n- `set_mental_command_action_level(profile=None, session=None, level=None)`\n- `mental_command_action_sensitivity(status, profile=None, session=None, values=[])`\n- `get_mental_command_action_sensitivity(status, profile=None, session=None)`\n- `set_mental_command_action_sensitivity(status, profile=None, session=None, values=[])`\n\n# EmotivCortex2Client Changelog\n\n## v1.0.1 - 2019_07_09\n\n### Changed\n\n- Fixed submodule import bug\n\n\n\n## v1.0.0 - 2019_07_09\n\n### Added\n\n- Initial release:\n - WebsocketClient with tested asynchronicity\n - EmotivCortex2Client with full API\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "https://pypi.org/project/cortex2/", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/methylDragon/emotiv-cortex2-python-client", "keywords": "emotiv,eeg,client,websockets,api,bci,brain", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "cortex2", "package_url": "https://pypi.org/project/cortex2/", "platform": "", "project_url": "https://pypi.org/project/cortex2/", "project_urls": { "Download": "https://pypi.org/project/cortex2/", "Homepage": "https://github.com/methylDragon/emotiv-cortex2-python-client" }, "release_url": "https://pypi.org/project/cortex2/1.0.1/", "requires_dist": [ "websockets (>=7.0)" ], "requires_python": "", "summary": "Comprehensive threaded, asynchronous Python client for the Emotiv EEG Cortex 2 API", "version": "1.0.1" }, "last_serial": 5507554, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "9603e447a65a6ae8103dec9c4dc788c1", "sha256": "461d8faa69241bce87348e93318223825370b9038f71d02be4bb49e518745fcf" }, "downloads": -1, "filename": "cortex2-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "9603e447a65a6ae8103dec9c4dc788c1", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 24791, "upload_time": "2019-07-08T17:17:42", "url": "https://files.pythonhosted.org/packages/30/54/377922856efd17a6ddaf37a89b4c49392ce1ba311c9e670c2f80f94874fe/cortex2-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "92c2de9121e2cbd5104fe64b21fbd2cf", "sha256": "6e457d3a7c57794484cb16c37a7aa2dde7af52aac421527697ea8981bf3d433c" }, "downloads": -1, "filename": "cortex2-1.0.0.tar.gz", "has_sig": false, "md5_digest": "92c2de9121e2cbd5104fe64b21fbd2cf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16864, "upload_time": "2019-07-08T17:17:45", "url": "https://files.pythonhosted.org/packages/95/86/cbe7d9f76768daadde00c4c5f0d65754a8bb2c66fa0e38c6ff2a65ab010b/cortex2-1.0.0.tar.gz" } ], "1.0.0.post1": [ { "comment_text": "", "digests": { "md5": "641b1a077ff38cf714e8cda026ca1c1b", "sha256": "98d5330a0ffecb605cf4c960d0d97b4bdca2e8e7841aef7db0a1f0b1a593295f" }, "downloads": -1, "filename": "cortex2-1.0.0.post1-py3-none-any.whl", "has_sig": false, "md5_digest": "641b1a077ff38cf714e8cda026ca1c1b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 24895, "upload_time": "2019-07-08T17:25:13", "url": "https://files.pythonhosted.org/packages/e0/d1/21ad46222bb5f144d2cfcd21a7334af440fbf372ed14547787f8653b666c/cortex2-1.0.0.post1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6eb56252cd12df29b74b270bc3466b8b", "sha256": "17dbcd9a45634ca1f4aac705121c6c52eb3c51235c4f26afe5d9f52cd1bb692e" }, "downloads": -1, "filename": "cortex2-1.0.0.post1.tar.gz", "has_sig": false, "md5_digest": "6eb56252cd12df29b74b270bc3466b8b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16906, "upload_time": "2019-07-09T05:47:38", "url": "https://files.pythonhosted.org/packages/4b/ab/e281c253907c9fa66453049179e1b86b98fb501a9fb7761bff57ea0e9ee3/cortex2-1.0.0.post1.tar.gz" } ], "1.0.0.post2": [ { "comment_text": "", "digests": { "md5": "95ec9e8e8efdf2f5456890a6dc8bdcf6", "sha256": "45f53cfa3a71977b273b6d54f5cb707b160e50155a1bd0393a4800756b225559" }, "downloads": -1, "filename": "cortex2-1.0.0.post2-py3-none-any.whl", "has_sig": false, "md5_digest": "95ec9e8e8efdf2f5456890a6dc8bdcf6", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 24896, "upload_time": "2019-07-09T05:48:07", "url": "https://files.pythonhosted.org/packages/69/68/1e7298f62cac79fee7dce27ecb7a0825a03bd4cbf3f97757b0ca8f20b1cb/cortex2-1.0.0.post2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a303b100bf264e24382de78253861cec", "sha256": "06a5657151523eb153c3a80fc9504be588e2065f749932cc8cc1dc96331281a6" }, "downloads": -1, "filename": "cortex2-1.0.0.post2.tar.gz", "has_sig": false, "md5_digest": "a303b100bf264e24382de78253861cec", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16952, "upload_time": "2019-07-09T05:48:20", "url": "https://files.pythonhosted.org/packages/97/9f/9bb4beeff027c2315b650d75af3f749f0af25a24f156c749460f5f86ef3e/cortex2-1.0.0.post2.tar.gz" } ], "1.0.0.post3": [ { "comment_text": "", "digests": { "md5": "4a77a6c8e03e905a0372fcb593be4ba4", "sha256": "aba120a1255153cebd36f929eaca9e7699a3890d8eb2b13ec571ef5d1e9821f6" }, "downloads": -1, "filename": "cortex2-1.0.0.post3-py3-none-any.whl", "has_sig": false, "md5_digest": "4a77a6c8e03e905a0372fcb593be4ba4", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 24951, "upload_time": "2019-07-09T14:19:17", "url": "https://files.pythonhosted.org/packages/79/d7/13220114c9c20538543e8290d48923500223434cc18b8e3349d616832353/cortex2-1.0.0.post3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8bab5ddff6c476951223e7b00c81c733", "sha256": "b6a26445e53b24977041e6918837b6cd594b622d4a4c8c89fa606b0215be38b6" }, "downloads": -1, "filename": "cortex2-1.0.0.post3.tar.gz", "has_sig": false, "md5_digest": "8bab5ddff6c476951223e7b00c81c733", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17055, "upload_time": "2019-07-09T14:19:19", "url": "https://files.pythonhosted.org/packages/a1/22/e3fab48707394c69cec7b6c141aaacca7323946815a4815c82402eac5dba/cortex2-1.0.0.post3.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "9863543e82298110fc65e12c3909bb45", "sha256": "89ad872f9398cba73aa5c79929f53efbb145b585cb6b77561e9bb1b4ce368a1e" }, "downloads": -1, "filename": "cortex2-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "9863543e82298110fc65e12c3909bb45", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 27621, "upload_time": "2019-07-09T14:40:49", "url": "https://files.pythonhosted.org/packages/74/c4/fb6c429f0a4eedf53313dd3fdd8fe4c1246abca3ef8e0a994429308e77a7/cortex2-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "7487d8a8ba7aff32edd2b61aead1ea86", "sha256": "7ec8d211815ab355ee79d5a64a4850f97ec442f6282c1402c889a667cb642570" }, "downloads": -1, "filename": "cortex2-1.0.1.tar.gz", "has_sig": false, "md5_digest": "7487d8a8ba7aff32edd2b61aead1ea86", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18574, "upload_time": "2019-07-09T14:40:51", "url": "https://files.pythonhosted.org/packages/de/44/e3c12cd8fba058d659a36fe68378ca6b166fdd4b2329f690a556300df0ce/cortex2-1.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "9863543e82298110fc65e12c3909bb45", "sha256": "89ad872f9398cba73aa5c79929f53efbb145b585cb6b77561e9bb1b4ce368a1e" }, "downloads": -1, "filename": "cortex2-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "9863543e82298110fc65e12c3909bb45", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 27621, "upload_time": "2019-07-09T14:40:49", "url": "https://files.pythonhosted.org/packages/74/c4/fb6c429f0a4eedf53313dd3fdd8fe4c1246abca3ef8e0a994429308e77a7/cortex2-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "7487d8a8ba7aff32edd2b61aead1ea86", "sha256": "7ec8d211815ab355ee79d5a64a4850f97ec442f6282c1402c889a667cb642570" }, "downloads": -1, "filename": "cortex2-1.0.1.tar.gz", "has_sig": false, "md5_digest": "7487d8a8ba7aff32edd2b61aead1ea86", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18574, "upload_time": "2019-07-09T14:40:51", "url": "https://files.pythonhosted.org/packages/de/44/e3c12cd8fba058d659a36fe68378ca6b166fdd4b2329f690a556300df0ce/cortex2-1.0.1.tar.gz" } ] }