{"action":"created","comment":{"url":"https://api.github.com/repos/stratosinc/pygatt/pulls/comments/33093479","id":33093479,"diff_hunk":"@@ -0,0 +1,1977 @@\n+from __future__ import print_function\n+\n+from binascii import hexlify\n+import logging\n+import serial\n+import time\n+import threading\n+\n+import bled112_bglib\n+from bled112_error import get_return_message\n+from bled112_constants import(\n+    ble_address_type, bondable, bonding, connection_status_flag,\n+    gap_discover_mode, gap_discoverable_mode, gap_connectable_mode,\n+    gatt_attribute_type_uuid, gatt_characteristic_descriptor_uuid,\n+    gatt_characteristic_type_uuid, gatt_service_uuid, scan_response_data_type,\n+    scan_response_packet_type\n+)\n+from constants import LOG_FORMAT, LOG_LEVEL\n+\n+\n+class Characteristic(object):\n+    \"\"\"\n+    GATT characteristic. For internal use within BLED112Backend.\n+    \"\"\"\n+    def __init__(self, name, handle):\n+        \"\"\"\n+        Sets the characteritic name and handle.\n+        \"\"\"\n+        self.handle = handle  # bytearray\n+        self.descriptors = {\n+            # uuid_string: handle\n+        }\n+\n+    def add_descriptor(self, uuid, handle):\n+        \"\"\"\n+        Add a characteristic descriptor to the dictionary of descriptors.\n+        \"\"\"\n+        self.descriptors[uuid] = handle\n+\n+\n+class AdvertisingAndScanInfo(object):\n+    \"\"\"\n+    Holds the advertising and scan response packet data from a device at a given\n+    address.\n+    \"\"\"\n+    def __init__(self):\n+        self.name = \"\"\n+        self.address = \"\"\n+        self.packet_data = {\n+            # scan_response_packet_type[xxx]: data_dictionary,\n+        }\n+\n+\n+class BLED112Backend(object):\n+    \"\"\"\n+    Pygatt BLE device backend using the Bluegiga BLED112.\n+    Only supports 1 device connection at a time.\n+    This object is NOT threadsafe.\n+    \"\"\"\n+    def __init__(self, serial_port, run=True, logfile=None):\n+        \"\"\"\n+        Initialize the BLED112 to be ready for use with a BLE device, i.e.,\n+        stop ongoing procedures, disconnect any connections, optionally start\n+        the receiver thread, and optionally delete any stored bonds.\n+\n+        serial_port -- The name of the serial port that the BLED112 is connected\n+                       to.\n+        run -- begin reveiving packets immediately.\n+        logfile -- the file to log to.\n+\n+        Locking total order:\n+        1) self._cond\n+        2) self._loglock\n+        \"\"\"\n+        # Set up logging\n+        self._loglock = threading.Lock()\n+        self._logger = logging.getLogger(__name__)\n+        self._logger.setLevel(LOG_LEVEL)\n+        if logfile is not None:\n+            handler = logging.FileHandler(logfile)\n+        else:  # print to stderr\n+            handler = logging.StreamHandler()\n+        formatter = logging.Formatter(fmt=LOG_FORMAT)\n+        handler.setLevel(LOG_LEVEL)\n+        handler.setFormatter(formatter)\n+        self._logger.addHandler(handler)\n+\n+        # Initialization\n+        self._lib = bled112_bglib.BGLib(loghandler=handler,\n+                                        loglevel=LOG_LEVEL)\n+        # Note: _ser is not protected by _main_thread_cond\n+        self._ser = serial.Serial(serial_port, timeout=0.25)\n+\n+        # Main thread (the one calling commands) waits on this. This also\n+        # provides mutual exclustion for BLED112Backend's state (except for the\n+        # log)\n+        self._main_thread_cond = threading.Condition()\n+\n+        # Packet receiving\n+        self._recvr_thread = None  # background thread to receive packets\n+        self._recvr_stop = True  # tell recvr thread not to run\n+\n+        # Flags to tell the main thread what to do when woken up from waiting","path":"pygatt/bled112_backend.py","position":103,"original_position":103,"commit_id":"178a8da9b4ac84a477dc23b4745b9a447f1f54b7","original_commit_id":"178a8da9b4ac84a477dc23b4745b9a447f1f54b7","user":{"login":"sandlerb","id":2380711,"avatar_url":"https://avatars.githubusercontent.com/u/2380711?v=3","gravatar_id":"","url":"https://api.github.com/users/sandlerb","html_url":"https://github.com/sandlerb","followers_url":"https://api.github.com/users/sandlerb/followers","following_url":"https://api.github.com/users/sandlerb/following{/other_user}","gists_url":"https://api.github.com/users/sandlerb/gists{/gist_id}","starred_url":"https://api.github.com/users/sandlerb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sandlerb/subscriptions","organizations_url":"https://api.github.com/users/sandlerb/orgs","repos_url":"https://api.github.com/users/sandlerb/repos","events_url":"https://api.github.com/users/sandlerb/events{/privacy}","received_events_url":"https://api.github.com/users/sandlerb/received_events","type":"User","site_admin":false},"body":"This is a lot of internal state.  Can any of it be removed and encapsulated elsewhere?","created_at":"2015-06-23T21:15:35Z","updated_at":"2015-06-23T21:15:35Z","html_url":"https://github.com/stratosinc/pygatt/pull/6#discussion_r33093479","pull_request_url":"https://api.github.com/repos/stratosinc/pygatt/pulls/6","_links":{"self":{"href":"https://api.github.com/repos/stratosinc/pygatt/pulls/comments/33093479"},"html":{"href":"https://github.com/stratosinc/pygatt/pull/6#discussion_r33093479"},"pull_request":{"href":"https://api.github.com/repos/stratosinc/pygatt/pulls/6"}}},"pull_request":{"url":"https://api.github.com/repos/stratosinc/pygatt/pulls/6","id":38158773,"html_url":"https://github.com/stratosinc/pygatt/pull/6","diff_url":"https://github.com/stratosinc/pygatt/pull/6.diff","patch_url":"https://github.com/stratosinc/pygatt/pull/6.patch","issue_url":"https://api.github.com/repos/stratosinc/pygatt/issues/6","number":6,"state":"open","locked":false,"title":"Add switchable backends","user":{"login":"sloboste","id":5903252,"avatar_url":"https://avatars.githubusercontent.com/u/5903252?v=3","gravatar_id":"","url":"https://api.github.com/users/sloboste","html_url":"https://github.com/sloboste","followers_url":"https://api.github.com/users/sloboste/followers","following_url":"https://api.github.com/users/sloboste/following{/other_user}","gists_url":"https://api.github.com/users/sloboste/gists{/gist_id}","starred_url":"https://api.github.com/users/sloboste/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sloboste/subscriptions","organizations_url":"https://api.github.com/users/sloboste/orgs","repos_url":"https://api.github.com/users/sloboste/repos","events_url":"https://api.github.com/users/sloboste/events{/privacy}","received_events_url":"https://api.github.com/users/sloboste/received_events","type":"User","site_admin":false},"body":"Opened the last one against master on accident. ","created_at":"2015-06-20T00:21:27Z","updated_at":"2015-06-23T21:15:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0b103f7321be5ed3de0a0b6c73921fce2fb8fd14","assignee":{"login":"sandlerb","id":2380711,"avatar_url":"https://avatars.githubusercontent.com/u/2380711?v=3","gravatar_id":"","url":"https://api.github.com/users/sandlerb","html_url":"https://github.com/sandlerb","followers_url":"https://api.github.com/users/sandlerb/followers","following_url":"https://api.github.com/users/sandlerb/following{/other_user}","gists_url":"https://api.github.com/users/sandlerb/gists{/gist_id}","starred_url":"https://api.github.com/users/sandlerb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sandlerb/subscriptions","organizations_url":"https://api.github.com/users/sandlerb/orgs","repos_url":"https://api.github.com/users/sandlerb/repos","events_url":"https://api.github.com/users/sandlerb/events{/privacy}","received_events_url":"https://api.github.com/users/sandlerb/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/stratosinc/pygatt/pulls/6/commits","review_comments_url":"https://api.github.com/repos/stratosinc/pygatt/pulls/6/comments","review_comment_url":"https://api.github.com/repos/stratosinc/pygatt/pulls/comments{/number}","comments_url":"https://api.github.com/repos/stratosinc/pygatt/issues/6/comments","statuses_url":"https://api.github.com/repos/stratosinc/pygatt/statuses/178a8da9b4ac84a477dc23b4745b9a447f1f54b7","head":{"label":"stratosinc:add-switchable-backends","ref":"add-switchable-backends","sha":"178a8da9b4ac84a477dc23b4745b9a447f1f54b7","user":{"login":"stratosinc","id":5115139,"avatar_url":"https://avatars.githubusercontent.com/u/5115139?v=3","gravatar_id":"","url":"https://api.github.com/users/stratosinc","html_url":"https://github.com/stratosinc","followers_url":"https://api.github.com/users/stratosinc/followers","following_url":"https://api.github.com/users/stratosinc/following{/other_user}","gists_url":"https://api.github.com/users/stratosinc/gists{/gist_id}","starred_url":"https://api.github.com/users/stratosinc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stratosinc/subscriptions","organizations_url":"https://api.github.com/users/stratosinc/orgs","repos_url":"https://api.github.com/users/stratosinc/repos","events_url":"https://api.github.com/users/stratosinc/events{/privacy}","received_events_url":"https://api.github.com/users/stratosinc/received_events","type":"Organization","site_admin":false},"repo":{"id":19384977,"name":"pygatt","full_name":"stratosinc/pygatt","owner":{"login":"stratosinc","id":5115139,"avatar_url":"https://avatars.githubusercontent.com/u/5115139?v=3","gravatar_id":"","url":"https://api.github.com/users/stratosinc","html_url":"https://github.com/stratosinc","followers_url":"https://api.github.com/users/stratosinc/followers","following_url":"https://api.github.com/users/stratosinc/following{/other_user}","gists_url":"https://api.github.com/users/stratosinc/gists{/gist_id}","starred_url":"https://api.github.com/users/stratosinc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stratosinc/subscriptions","organizations_url":"https://api.github.com/users/stratosinc/orgs","repos_url":"https://api.github.com/users/stratosinc/repos","events_url":"https://api.github.com/users/stratosinc/events{/privacy}","received_events_url":"https://api.github.com/users/stratosinc/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/stratosinc/pygatt","description":"Python wrapper for gatttool from BlueZ to access Bluetooth LE Devices","fork":true,"url":"https://api.github.com/repos/stratosinc/pygatt","forks_url":"https://api.github.com/repos/stratosinc/pygatt/forks","keys_url":"https://api.github.com/repos/stratosinc/pygatt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/stratosinc/pygatt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/stratosinc/pygatt/teams","hooks_url":"https://api.github.com/repos/stratosinc/pygatt/hooks","issue_events_url":"https://api.github.com/repos/stratosinc/pygatt/issues/events{/number}","events_url":"https://api.github.com/repos/stratosinc/pygatt/events","assignees_url":"https://api.github.com/repos/stratosinc/pygatt/assignees{/user}","branches_url":"https://api.github.com/repos/stratosinc/pygatt/branches{/branch}","tags_url":"https://api.github.com/repos/stratosinc/pygatt/tags","blobs_url":"https://api.github.com/repos/stratosinc/pygatt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/stratosinc/pygatt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/stratosinc/pygatt/git/refs{/sha}","trees_url":"https://api.github.com/repos/stratosinc/pygatt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/stratosinc/pygatt/statuses/{sha}","languages_url":"https://api.github.com/repos/stratosinc/pygatt/languages","stargazers_url":"https://api.github.com/repos/stratosinc/pygatt/stargazers","contributors_url":"https://api.github.com/repos/stratosinc/pygatt/contributors","subscribers_url":"https://api.github.com/repos/stratosinc/pygatt/subscribers","subscription_url":"https://api.github.com/repos/stratosinc/pygatt/subscription","commits_url":"https://api.github.com/repos/stratosinc/pygatt/commits{/sha}","git_commits_url":"https://api.github.com/repos/stratosinc/pygatt/git/commits{/sha}","comments_url":"https://api.github.com/repos/stratosinc/pygatt/comments{/number}","issue_comment_url":"https://api.github.com/repos/stratosinc/pygatt/issues/comments{/number}","contents_url":"https://api.github.com/repos/stratosinc/pygatt/contents/{+path}","compare_url":"https://api.github.com/repos/stratosinc/pygatt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/stratosinc/pygatt/merges","archive_url":"https://api.github.com/repos/stratosinc/pygatt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/stratosinc/pygatt/downloads","issues_url":"https://api.github.com/repos/stratosinc/pygatt/issues{/number}","pulls_url":"https://api.github.com/repos/stratosinc/pygatt/pulls{/number}","milestones_url":"https://api.github.com/repos/stratosinc/pygatt/milestones{/number}","notifications_url":"https://api.github.com/repos/stratosinc/pygatt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/stratosinc/pygatt/labels{/name}","releases_url":"https://api.github.com/repos/stratosinc/pygatt/releases{/id}","created_at":"2014-05-02T18:47:01Z","updated_at":"2015-06-23T19:50:24Z","pushed_at":"2015-06-23T15:31:26Z","git_url":"git://github.com/stratosinc/pygatt.git","ssh_url":"git@github.com:stratosinc/pygatt.git","clone_url":"https://github.com/stratosinc/pygatt.git","svn_url":"https://github.com/stratosinc/pygatt","homepage":"","size":13693,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":1,"forks":3,"open_issues":1,"watchers":3,"default_branch":"next"}},"base":{"label":"stratosinc:next","ref":"next","sha":"88af950793236ac5ffd5d97435ebc128b46e63e2","user":{"login":"stratosinc","id":5115139,"avatar_url":"https://avatars.githubusercontent.com/u/5115139?v=3","gravatar_id":"","url":"https://api.github.com/users/stratosinc","html_url":"https://github.com/stratosinc","followers_url":"https://api.github.com/users/stratosinc/followers","following_url":"https://api.github.com/users/stratosinc/following{/other_user}","gists_url":"https://api.github.com/users/stratosinc/gists{/gist_id}","starred_url":"https://api.github.com/users/stratosinc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stratosinc/subscriptions","organizations_url":"https://api.github.com/users/stratosinc/orgs","repos_url":"https://api.github.com/users/stratosinc/repos","events_url":"https://api.github.com/users/stratosinc/events{/privacy}","received_events_url":"https://api.github.com/users/stratosinc/received_events","type":"Organization","site_admin":false},"repo":{"id":19384977,"name":"pygatt","full_name":"stratosinc/pygatt","owner":{"login":"stratosinc","id":5115139,"avatar_url":"https://avatars.githubusercontent.com/u/5115139?v=3","gravatar_id":"","url":"https://api.github.com/users/stratosinc","html_url":"https://github.com/stratosinc","followers_url":"https://api.github.com/users/stratosinc/followers","following_url":"https://api.github.com/users/stratosinc/following{/other_user}","gists_url":"https://api.github.com/users/stratosinc/gists{/gist_id}","starred_url":"https://api.github.com/users/stratosinc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stratosinc/subscriptions","organizations_url":"https://api.github.com/users/stratosinc/orgs","repos_url":"https://api.github.com/users/stratosinc/repos","events_url":"https://api.github.com/users/stratosinc/events{/privacy}","received_events_url":"https://api.github.com/users/stratosinc/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/stratosinc/pygatt","description":"Python wrapper for gatttool from BlueZ to access Bluetooth LE Devices","fork":true,"url":"https://api.github.com/repos/stratosinc/pygatt","forks_url":"https://api.github.com/repos/stratosinc/pygatt/forks","keys_url":"https://api.github.com/repos/stratosinc/pygatt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/stratosinc/pygatt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/stratosinc/pygatt/teams","hooks_url":"https://api.github.com/repos/stratosinc/pygatt/hooks","issue_events_url":"https://api.github.com/repos/stratosinc/pygatt/issues/events{/number}","events_url":"https://api.github.com/repos/stratosinc/pygatt/events","assignees_url":"https://api.github.com/repos/stratosinc/pygatt/assignees{/user}","branches_url":"https://api.github.com/repos/stratosinc/pygatt/branches{/branch}","tags_url":"https://api.github.com/repos/stratosinc/pygatt/tags","blobs_url":"https://api.github.com/repos/stratosinc/pygatt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/stratosinc/pygatt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/stratosinc/pygatt/git/refs{/sha}","trees_url":"https://api.github.com/repos/stratosinc/pygatt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/stratosinc/pygatt/statuses/{sha}","languages_url":"https://api.github.com/repos/stratosinc/pygatt/languages","stargazers_url":"https://api.github.com/repos/stratosinc/pygatt/stargazers","contributors_url":"https://api.github.com/repos/stratosinc/pygatt/contributors","subscribers_url":"https://api.github.com/repos/stratosinc/pygatt/subscribers","subscription_url":"https://api.github.com/repos/stratosinc/pygatt/subscription","commits_url":"https://api.github.com/repos/stratosinc/pygatt/commits{/sha}","git_commits_url":"https://api.github.com/repos/stratosinc/pygatt/git/commits{/sha}","comments_url":"https://api.github.com/repos/stratosinc/pygatt/comments{/number}","issue_comment_url":"https://api.github.com/repos/stratosinc/pygatt/issues/comments{/number}","contents_url":"https://api.github.com/repos/stratosinc/pygatt/contents/{+path}","compare_url":"https://api.github.com/repos/stratosinc/pygatt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/stratosinc/pygatt/merges","archive_url":"https://api.github.com/repos/stratosinc/pygatt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/stratosinc/pygatt/downloads","issues_url":"https://api.github.com/repos/stratosinc/pygatt/issues{/number}","pulls_url":"https://api.github.com/repos/stratosinc/pygatt/pulls{/number}","milestones_url":"https://api.github.com/repos/stratosinc/pygatt/milestones{/number}","notifications_url":"https://api.github.com/repos/stratosinc/pygatt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/stratosinc/pygatt/labels{/name}","releases_url":"https://api.github.com/repos/stratosinc/pygatt/releases{/id}","created_at":"2014-05-02T18:47:01Z","updated_at":"2015-06-23T19:50:24Z","pushed_at":"2015-06-23T15:31:26Z","git_url":"git://github.com/stratosinc/pygatt.git","ssh_url":"git@github.com:stratosinc/pygatt.git","clone_url":"https://github.com/stratosinc/pygatt.git","svn_url":"https://github.com/stratosinc/pygatt","homepage":"","size":13693,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":1,"forks":3,"open_issues":1,"watchers":3,"default_branch":"next"}},"_links":{"self":{"href":"https://api.github.com/repos/stratosinc/pygatt/pulls/6"},"html":{"href":"https://github.com/stratosinc/pygatt/pull/6"},"issue":{"href":"https://api.github.com/repos/stratosinc/pygatt/issues/6"},"comments":{"href":"https://api.github.com/repos/stratosinc/pygatt/issues/6/comments"},"review_comments":{"href":"https://api.github.com/repos/stratosinc/pygatt/pulls/6/comments"},"review_comment":{"href":"https://api.github.com/repos/stratosinc/pygatt/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/stratosinc/pygatt/pulls/6/commits"},"statuses":{"href":"https://api.github.com/repos/stratosinc/pygatt/statuses/178a8da9b4ac84a477dc23b4745b9a447f1f54b7"}}}}