def run():
    """ Uses a callback scheduled on an event loop"""
    pb = AsyncPushbullet(API_KEY)
    listener = WebsocketListener(pb, on_connect=ws_connected,on_message=ws_msg_received,on_close=ws_closed)
    loop = asyncio.get_event_loop()
  
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        print("KeyboardInterrupt")
    finally:
        print("finally...")
        loop.run_until_complete(listener.close())
        loop.run_until_complete(pb.close())
def run():
    """ Uses a callback scheduled on an event loop"""
    pb = AsyncPushbullet(API_KEY)
    listener = WebsocketListener(pb, on_connect=ws_connected,on_message=ws_msg_received,on_close=ws_closed)
    loop = asyncio.get_event_loop()
  
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        print("KeyboardInterrupt")
    finally:
        print("finally...")
        loop.run_until_complete(listener.close())
        loop.run_until_complete(pb.close())
    def pushbullet(self) -> AsyncPushbullet:
        current_key = self.key_var.get()
        if self._pushbullet is not None:
            if current_key != self._pushbullet.api_key:
                self._pushbullet.close_all_threadsafe()
                self._pushbullet = None
        if self._pushbullet is None:
            self._pushbullet = AsyncPushbullet(
                api_key=current_key,
                # loop=self.ioloop,
                verify_ssl=False,
                proxy=self.proxy)

        return self._pushbullet
示例#4
0
 def get_pushbullet_client(cls):
     try:
         return cls._pushbullet_client
     except AttributeError:
         cls._pushbullet_client = AsyncPushbullet(api_key=conf.PB_API_KEY,
                                                  loop=LOOP)
         return cls._pushbullet_client
def main():
    proxy = os.environ.get("https_proxy") or os.environ.get("http_proxy")
    pb = AsyncPushbullet(API_KEY, proxy=proxy)

    async def _run():
        devices = await pb.async_get_devices()
        print("Devices:")
        for d in devices:
            print("\t", d.nickname)

        # Name of a device?
        if devices:
            name = devices[0].nickname
            this_device = await pb.async_get_device(nickname=name)
            print("Retrieved device by it's name '{}': {}".format(
                name, this_device))

        # Do we have a device named foobar?  Returns None if not found.
        name = "foobar"
        this_device = await pb.async_get_device(nickname=name)
        print("Retrieved device by it's name '{}': {}".format(
            name, this_device))

        await pb.async_close()

    loop = asyncio.get_event_loop()
    loop.run_until_complete(_run())
示例#6
0
        async def _listen():
            try:
                self.pushbullet = AsyncPushbullet(self.key_var.get(),
                                                  verify_ssl=False,
                                                  proxy=self.proxy_var.get())

                async with LiveStreamListener(self.pushbullet) as pl2:
                    self.pushbullet_listener = pl2
                    await self.connected(pl2)

                    async for push in pl2:
                        await self.push_received(push, pl2)

            except Exception as ex:
                print("Exception:", ex)
            finally:
                await self.disconnected(self.pushbullet_listener)
示例#7
0
    async def _run():
        try:
            async with AsyncPushbullet(API_KEY, proxy=PROXY) as pb:
                msg["type"] = "asynchronous_example"
                await pb.async_push_ephemeral(msg)  # Asynchronous IO

                # await pb.async_close()
        except Exception as ex:
            print("ERROR:", ex, file=sys.stderr, flush=True)
            traceback.print_tb(sys.exc_info()[2])
示例#8
0
    async def run():
        api_key = os.environ["PUSHBULLET_API_KEY"].strip()
        # pb = await AsyncPushbullet(api_key=api_key).connect()
        pb = AsyncPushbullet(api_key=api_key)
        handler = AsyncPushbulletLogHandler(pb, level=logging.WARNING)
        logger = logging.getLogger(__name__)
        logger.setLevel(logging.INFO)
        logger.addHandler(handler)

        logger.info("Normal stuff here.")
        logger.warning("Warning stuff here.")
        logger.warning("Warning 2")

        print("Done")
        await asyncio.sleep(2)
    async def _listen():
        try:
            async with AsyncPushbullet(API_KEY, proxy=PROXY) as pb:
                async with EphemeralComm(
                        pb, Msg) as ec:  # type: EphemeralComm[Msg]
                    await q.put(ec)
                    print(await ec.next(.5))
                    print(await ec.next(.5))
                    print(await ec.next(.5))
                    await ec.close()

        except Exception as ex:
            print("ERROR:", type(ex), ex, file=sys.stderr, flush=True)
            traceback.print_tb(sys.exc_info()[2])
        finally:
            print("AsyncPushbullet disconnected.", flush=True)
示例#10
0
            async def _listen():
                asyncio.get_event_loop().set_debug(True)
                try:
                    async with AsyncPushbullet(API_KEY,
                                               proxy=PROXY) as self.pushbullet:
                        self.pause_btn.configure(state=tk.NORMAL)
                        self.pushes.clear()

                        # Two ways to handle GUI manipulation.  Since these are
                        # just two-off commands, not in a loop, it doesn't matter
                        # very much which we use.
                        # Technique 1
                        # self.push_count_var.set(len(self.pushes))
                        # self.push_listbox.delete(0, tk.END)
                        # await asyncio.sleep(0)
                        # Technique 2
                        self.tk_schedule(self.push_count_var.set,
                                         len(self.pushes))
                        self.tk_schedule(self.push_listbox.delete, 0, tk.END)

                        self.push_iterator = self.pushbullet.pushes_asynciter(
                            limit=None, modified_after=0.0, active_only=True)
                        async for push in self.push_iterator:
                            # As pushes are retrieved -- it will take several calls to pushbullet
                            # to retrieve the long history of pushes -- they are processed on
                            # this async for loop.  Although the for loop obviously only processes
                            # one item at a time, they will come in bunches.  When a network request
                            # is processed, a batch of pushes maybe 1 to 20 will fire through quickly.
                            # We immediately schedule processing on the main thread, since we'll
                            # be updating the GUI.
                            self.tk_schedule(_recv_push, push)

                except Exception as ex:
                    print("Exception:", ex, file=sys.stderr, flush=True)
                    tb = sys.exc_info()[2]
                    traceback.print_tb(tb)

                    self.retrieve_btn.configure(state=tk.NORMAL)
                    self.pause_btn.configure(state=tk.DISABLED)
                else:
                    self.retrieve_btn.configure(text="Completed")
                    self.pause_btn.configure(text="Completed")
                    self.retrieve_btn.configure(state=tk.DISABLED)
                    self.pause_btn.configure(state=tk.DISABLED)
                    self.tk_schedule(
                        self.push_count_var.set,
                        "{:,} (Completed)".format(len(self.pushes)))
示例#11
0
    async def _run():
        try:
            async with AsyncPushbullet(API_KEY, proxy=PROXY) as pb:

                # List devices
                devices = await pb.async_get_devices()
                print("Devices:")
                for dev in devices:
                    print("\t", dev)

                # Send a push
                push = await pb.async_push_note(title="Success",
                                                body="I did it!")
                print("Push sent:", push)

                # Ways to listen for pushes
                async with LiveStreamListener(pb) as lsl:
                    # This will retrieve the previous push because it occurred
                    # after the enclosing AsyncPushbullet connection was made
                    push = await lsl.next_push()
                    print("Previous push, now received:", push)

                    # Alternately get pushes with a 3 second inter-push timeout
                    print(
                        "Awaiting pushes with 3 second inter-push timeout...")
                    async for push in lsl.timeout(3):
                        print("Push received:", push)

                    # Alternately get pushes forever
                    print("Awaiting pushes forever...")
                    async for push in lsl:
                        print("Push received:", push)

        except InvalidKeyError as ke:
            print(ke, file=sys.stderr)
            return EXIT_INVALID_KEY

        except PushbulletError as pe:
            print(pe, file=sys.stderr)
            return EXIT_PUSHBULLET_ERROR

        except Exception as ex:
            print(ex, file=sys.stderr)
            traceback.print_tb(sys.exc_info()[2])
            return EXIT_OTHER
示例#12
0
    async def _run():
        print("Connectiong to Pushbullet...", end="", flush=True)
        try:
            types_try_this = None
            # types_try_this=("push",)  # Default
            # types_try_this=("tickle")
            # types_try_this=("tickle", "push", "ephemeral")
            # types_try_this=("nop",)
            types_try_this = ()  # Everything
            async with AsyncPushbullet(api_key=API_KEY, proxy=PROXY) as pb:
                async with LiveStreamListener(pb, types=types_try_this) as lsl:
                    print("Connected.", flush=True)

                    # Wait indefinitely for pushes and other notifications
                    async for item in lsl:
                        print("Live stream item:", pprint.pformat(item))

        except Exception as ex:
            print("_run() exception:", ex)

        print("Disconnected.", flush=True)
示例#13
0
class PushApp():
    def __init__(self, root):
        self.window = root
        root.title("Async Pushbullet Upload Demo")
        self.log = logging.getLogger(__name__)

        # Data
        self.ioloop = None  # type: asyncio.AbstractEventLoop
        self.pushbullet = None  # type: AsyncPushbullet
        self.pushbullet_listener = None  # type: LiveStreamListener
        self.key_var = tk.StringVar()  # API key
        self.pushes_var = tk.StringVar()
        self.filename_var = tk.StringVar()
        self.btn_upload = None  # type: tk.Button
        self.proxy_var = tk.StringVar()

        # View / Control
        self.create_widgets()

        # Connections
        self.create_io_loop()
        self.key_var.set(API_KEY)
        self.filename_var.set(__file__)
        self.proxy_var.set(PROXY)

    def create_widgets(self):
        """
        API Key: [                  ]
                 <Connect>
        Filename: [                 ]
            <Browse>  <Upload>
        Pushes:
        +----------------------------+
        |                            |
        +----------------------------+
        """
        row = 0
        # API Key
        lbl_key = tk.Label(self.window, text="API Key:")
        lbl_key.grid(row=row, column=0, sticky=tk.W)
        txt_key = tk.Entry(self.window, textvariable=self.key_var)
        txt_key.grid(row=row, column=1, sticky=tk.W + tk.E)
        tk.Grid.grid_columnconfigure(self.window, 1, weight=1)
        txt_key.bind('<Return>', lambda x: self.connect_button_clicked())
        row += 1
        btn_connect = tk.Button(self.window, text="Connect", command=self.connect_button_clicked)
        btn_connect.grid(row=row, column=1, sticky=tk.W)
        row += 1

        # Proxy, if we want to show it
        # lbl_proxy = tk.Label(self.window, text="Proxy")
        # lbl_proxy.grid(row=row, column=0, sticky=tk.W)
        # txt_proxy = tk.Entry(self.window, textvariable=self.proxy_var)
        # txt_proxy.grid(row=row, column=1, sticky=tk.W + tk.E)
        # row += 1

        # File: [    ]
        lbl_file = tk.Label(self.window, text="File:")
        lbl_file.grid(row=row, column=0, sticky=tk.W)
        txt_file = tk.Entry(self.window, textvariable=self.filename_var)
        txt_file.grid(row=row, column=1, sticky=tk.W + tk.E)
        row += 1

        # <Browse>  <Upload>
        button_frame = tk.Frame(self.window)
        button_frame.grid(row=row, column=0, columnspan=2, sticky=tk.W + tk.E)
        row += 1
        btn_browse = tk.Button(button_frame, text="Browse...", command=self.browse_button_clicked)
        btn_browse.grid(row=0, column=0, sticky=tk.E)
        self.btn_upload = tk.Button(button_frame, text="Upload and Push", command=self.upload_button_clicked,
                                    state=tk.DISABLED)
        self.btn_upload.grid(row=0, column=1, sticky=tk.W)

        # Incoming pushes
        # +------------+
        # |            |
        # +------------+
        lbl_data = tk.Label(self.window, text="Incoming Pushes...")
        lbl_data.grid(row=row, column=0, sticky=tk.W)
        row += 1
        txt_data = BindableTextArea(self.window, textvariable=self.pushes_var, width=80, height=10)
        txt_data.grid(row=row, column=0, columnspan=2)

    def create_io_loop(self):
        """Creates a new thread to manage an asyncio event loop specifically for IO to/from Pushbullet."""
        assert self.ioloop is None  # This should only ever be run once

        def _run(loop):
            asyncio.set_event_loop(loop)
            loop.run_forever()

        self.ioloop = asyncio.new_event_loop()
        self.ioloop.set_exception_handler(self._ioloop_exc_handler)
        threading.Thread(target=partial(_run, self.ioloop), name="Thread-asyncio", daemon=True).start()

    def _ioloop_exc_handler(self, loop: asyncio.BaseEventLoop, context: dict):
        if "exception" in context:
            self.status = context["exception"]
        self.status = str(context)
        # Handle this more robustly in real-world code

    def connect_button_clicked(self):
        self.pushes_var.set("Connecting...")
        self.close()

        async def _listen():
            try:
                self.pushbullet = AsyncPushbullet(self.key_var.get(),
                                                  verify_ssl=False,
                                                  proxy=self.proxy_var.get())

                async with LiveStreamListener(self.pushbullet) as pl2:
                    self.pushbullet_listener = pl2
                    await self.connected(pl2)

                    async for push in pl2:
                        await self.push_received(push, pl2)

            except Exception as ex:
                print("Exception:", ex)
            finally:
                await self.disconnected(self.pushbullet_listener)

        asyncio.run_coroutine_threadsafe(_listen(), self.ioloop)

    def close(self):

        if self.pushbullet is not None:
            self.pushbullet.close_all_threadsafe()
            self.pushbullet = None
        if self.pushbullet_listener is not None:
            assert self.ioloop is not None
            pl = self.pushbullet_listener
            asyncio.run_coroutine_threadsafe(pl.close(), self.ioloop)
            self.pushbullet_listener = None

    def browse_button_clicked(self):
        print("browse_button_clicked")
        resp = filedialog.askopenfilename(parent=self.window, title="Open a File to Push")
        if resp != "":
            self.filename_var.set(resp)

    def upload_button_clicked(self):
        self.pushes_var.set(self.pushes_var.get() + "Uploading...")
        self.btn_upload["state"] = tk.DISABLED
        filename = self.filename_var.get()
        asyncio.run_coroutine_threadsafe(self.upload_file(filename), loop=self.ioloop)

    async def upload_file(self, filename: str):
        # This is the actual upload command
        info = await self.pushbullet.async_upload_file(filename)

        # Push a notification of the upload "as a file":
        await self.pushbullet.async_push_file(info["file_name"], info["file_url"], info["file_type"],
                                              title="File Arrived!", body="Please enjoy your file")

        # Push a notification of the upload "as a link":
        await self.pushbullet.async_push_link("Link to File Arrived!", info["file_url"], body="Please enjoy your file")
        self.btn_upload["state"] = tk.NORMAL
        self.pushes_var.set(self.pushes_var.get() + "Uploaded\n")

    async def connected(self, listener: LiveStreamListener):
        self.btn_upload["state"] = tk.NORMAL
        self.pushes_var.set(self.pushes_var.get() + "Connected\n")

    async def disconnected(self, listener: LiveStreamListener):
        self.btn_upload["state"] = tk.DISABLED
        self.pushes_var.set(self.pushes_var.get() + "Disconnected\n")

    async def push_received(self, p: dict, listener: LiveStreamListener):
        print("Push received:", p)
        prev = self.pushes_var.get()
        prev += "{}\n\n".format(p)
        self.pushes_var.set(prev)
def main():
    """ Uses a callback scheduled on an event loop"""

    pb = AsyncPushbullet(API_KEY, verify_ssl=False, proxy=PROXY)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(upload_file(pb, __file__))  # Upload this source code file as an example
示例#15
0
async def _run(args):
    # Logging levels
    if args.debug:  # Debug?
        print("Log level: DEBUG")
        logging.basicConfig(level=logging.DEBUG)
    elif args.verbose:  # Verbose?
        print("Log level: INFO")
        logging.basicConfig(level=logging.INFO)

    # Clear the oauth2 token?
    if args.clear_oauth2:
        oauth2.clear_oauth2_key()
        print("Successfully cleared/unregistered the oauth2 token.")
        print("The asyncpushbullet command line tools no longer have access to your pushbullet account.")
        sys.exit(errors.__EXIT_NO_ERROR__)

    # Request setting up oauth2 access?
    if args.oauth2:
        token = await oauth2.async_gain_oauth2_access()
        if token:
            print("Successfully authenticated using OAuth2.")
            print("You should now be able to use the command line tools without specifying an API key.")
            sys.exit(errors.__EXIT_NO_ERROR__)
        else:
            print("There was a problem authenticating.")
            sys.exit(errors.__ERR_UNKNOWN__)

    # Find a valid API key
    api_key = try_to_find_key(args, not args.quiet)
    if api_key is None:
        print("You must specify an API key.", file=sys.stderr)
        sys.exit(errors.__ERR_API_KEY_NOT_GIVEN__)

    # Proxy
    proxy = lambda: args.proxy or os.environ.get("https_proxy") or os.environ.get("http_proxy")

    # List devices?
    if args.list_devices:
        print("Devices:")
        try:
            _proxy = proxy() if callable(proxy) else proxy

            async with AsyncPushbullet(api_key, proxy=_proxy) as pb:
                async for dev in pb.devices_asynciter():
                    print("\t", dev.nickname)

        except InvalidKeyError as exc:
            print(exc, file=sys.stderr)
            return errors.__ERR_INVALID_API_KEY__
        except PushbulletError as exc:
            print(exc, file=sys.stderr)
            return errors.__ERR_CONNECTING_TO_PB__
        else:
            # sys.exit(0)
            return errors.__EXIT_NO_ERROR__

    # Throttle
    throttle_count = args.throttle_count
    throttle_seconds = args.throttle_seconds

    # Device
    device = args.device

    # Timeout
    timeout = DEFAULT_COMMAND_TIMEOUT
    if args.timeout:
        timeout = float(args.timeout)

    # Create ListenApp
    listen_app = ListenApp(api_key,
                           proxy=proxy,
                           throttle_count=throttle_count,
                           throttle_seconds=throttle_seconds,
                           device=device,
                           timeout=timeout)

    # Windows needs special event loop in order to launch processes on it
    proc_loop: asyncio.BaseEventLoop
    if sys.platform == 'win32':
        proc_loop = asyncio.ProactorEventLoop()
    else:
        proc_loop = asyncio.new_event_loop()  # Processes
        asyncio.get_child_watcher()  # Main loop

    def _thread_run(loop):
        asyncio.set_event_loop(loop)
        loop.run_forever()

    threading.Thread(target=partial(_thread_run, proc_loop), name="Thread-proc", daemon=True).start()

    # Add actions from command line arguments
    if args.exec:
        for cmd_opts in args.exec:
            cmd_path = cmd_opts[0]
            cmd_args = cmd_opts[1:]
            action = ExecutableAction(cmd_path, cmd_args, loop=proc_loop, timeout=timeout)
            listen_app.add_action(action)

    # Add actions from command line arguments
    if args.exec_simple:
        for cmd_opts in args.exec_simple:
            cmd_path = cmd_opts[0]
            cmd_args = cmd_opts[1:]
            action = ExecutableActionSimplified(cmd_path, cmd_args, loop=proc_loop, timeout=timeout)
            listen_app.add_action(action)

    # Add actions from command line arguments
    if args.exec_python:
        for cmd_opts in args.exec_python:
            cmd_path = cmd_opts[0]
            action = ExecutableActionPython(cmd_path)
            listen_app.add_action(action)

    # Echo
    if args.echo:
        listen_app.add_action(EchoAction())

    # Default action if none specified
    if not listen_app._actions:
        print("No actions specified -- defaulting to Echo.")
        listen_app.add_action(EchoAction())

    exit_code: int = None
    try:
        exit_code = await listen_app.run()
    except KeyboardInterrupt:
        print("Caught keyboard interrupt")
    finally:
        await listen_app.close()
        if exit_code is None:
            exit_code = errors.__EXIT_NO_ERROR__
        return exit_code
示例#16
0
    async def run(self):
        exit_code = 0
        while self.persistent_connection:
            try:
                # Live update the proxy setting
                _proxy = self.proxy() if callable(self.proxy) else self.proxy
                if _proxy:
                    self.log.info("Proxy: {}".format(_proxy))

                print("Connecting to pushbullet...", end="", flush=True)
                async with AsyncPushbullet(api_key=self.api_key, proxy=_proxy) as pb:
                    self._account = pb

                    # If filtering on device, find or create device with that name
                    if self.device_name is not None:
                        dev = await pb.async_get_device(nickname=self.device_name)
                        if dev is None:
                            dev = await pb.async_new_device(nickname=self.device_name)
                            if dev is None:
                                self.log.error("Device {} was not found and could not be created.")
                            else:
                                self.log.info("Device {} was not found, so we created it.".format(self.device_name))

                    async with LiveStreamListener(pb, only_this_device_nickname=self.device_name) as lsl:
                        print("Connected.", flush=True)
                        self.log.info("Connected to Pushbullet websocket.")
                        self._listener = lsl

                        if self.device_name is None:
                            print("Awaiting pushes...")
                        else:
                            print("Awaiting pushes to device {}...".format(self.device_name))

                        async for push in lsl:
                            self.log.info("Received push (title={}, body={}) {}"
                                          .format(push.get("title"), push.get("body"), push))
                            print("Received push (title={}, body={})"
                                  .format(push.get("title"), push.get("body")))

                            await self._throttle()

                            if push.get("iden") in self._sent_push_idens:
                                # This is one we sent - ignore it
                                self.log.debug(
                                    "Ignoring an incoming push that we sent. (iden={})".format(push.get('iden')))
                                continue

                            async def _call_on_push(_action: Action):
                                self.log.info("Calling action {}".format(repr(_action)))
                                try:
                                    await asyncio.wait_for(_action.on_push(push, self.wrapped_account),
                                                           timeout=self.action_timeout)
                                    await asyncio.sleep(0)

                                except asyncio.TimeoutError as te:
                                    err_msg = "Action {} timed out after {}+ seconds".format(_action,
                                                                                             self.action_timeout)
                                    await pb.async_push_note(title="AsyncPushbullet Error", body=err_msg)
                                    if not self.log.isEnabledFor(logging.DEBUG):
                                        err_msg += " (turn on --debug to see traceback)"
                                    self.log.warning(err_msg)
                                    if self.log.isEnabledFor(logging.DEBUG):
                                        traceback.print_tb(sys.exc_info()[2])
                                    del err_msg

                                except Exception as ex:
                                    err_msg = "Action {} caused exception {}".format(_action, ex)
                                    await pb.async_push_note(title="AsyncPushbullet Error", body=err_msg)
                                    if not self.log.isEnabledFor(logging.DEBUG):
                                        err_msg += " (turn on --debug to see traceback)"
                                    self.log.warning(err_msg)
                                    if self.log.isEnabledFor(logging.DEBUG):
                                        traceback.print_tb(sys.exc_info()[2])
                                    del err_msg

                                finally:
                                    self.log.debug("Leaving action {}".format(repr(_action)))

                            for a in self._actions:
                                asyncio.get_event_loop().create_task(_call_on_push(a))


            except InvalidKeyError as ex:
                print(flush=True)
                exit_code = errors.__ERR_INVALID_API_KEY__
                self.log.warning(ex)
                self.persistent_connection = False  # Invalid key results in immediate exit

            except Exception as ex:
                print(flush=True)
                err_msg = "{}: {}".format(ex.__class__.__name__, ex)
                if not self.log.isEnabledFor(logging.DEBUG):
                    err_msg += " (turn on --debug to see traceback)"
                self.log.warning(err_msg)
                if self.log.isEnabledFor(logging.DEBUG):
                    traceback.print_tb(sys.exc_info()[2])
                exit_code = errors.__ERR_UNKNOWN__  # exit code

            else:
                print("Connection closed.")

            finally:
                if self.persistent_connection:
                    print("Waiting {} seconds to try again...".format(self.persistent_connection_wait_interval),
                          flush=True)
                    await asyncio.sleep(self.persistent_connection_wait_interval)

        return exit_code
def main():
    recvd_push = json.loads(sys.stdin.read())  # Throw whatever exceptions to stderr

    if recvd_push.get("body", "").lower().strip() == "imagesnap":

        # Temp file to house the image file
        temp_img = tempfile.NamedTemporaryFile(delete=False, suffix=".jpg")
        temp_img.close()

        try:

            # Take a picture and upload
            # PRETEND TO TAKE A PICTURE
            import shutil
            fakepic = os.path.join(os.path.dirname(os.path.abspath(__file__)), "snapshot.jpg")
            shutil.copy(fakepic, temp_img.name)

            # Take a picture
            # proc = subprocess.run(["imagesnap", temp_img.name],
            proc = subprocess.run(["notepad.exe"],  # Debugging
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE,
                                  timeout=10,
                                  encoding=ENCODING)

            # Upload picture
            with AsyncPushbullet(API_KEY, proxy=PROXY) as pb:
                # resp = pb.upload_file(temp_img.name)  # Upload here
                resp = pb.upload_file_to_transfer_sh(temp_img.name)  # Upload here
            del pb

            file_type = resp.get("file_type")
            file_url = resp.get("file_url")
            file_name = resp.get("file_name")

            # Provide a response via stdout
            stdout_txt = proc.stdout
            stderr_txt = proc.stderr
            myresp = {
                "type": "file",
                "title": "Imagesnap",
                "body": "{}\n{}".format(stdout_txt, stderr_txt).strip(),
                "file_name": file_name,
                "file_type": file_type,
                "file_url": file_url,
                "received_push": recvd_push
            }
            dev_iden = recvd_push.get("source_device_iden")
            if dev_iden is not None:
                myresp["device_iden"] = dev_iden

            with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "response.json"), "w") as fout:
                fout.write(json.dumps(myresp, indent=4))

            print(json.dumps(myresp), flush=True)

        except Exception as e:
            # raise e
            print("Error:", e, file=sys.stderr)

        finally:

            os.remove(temp_img.name)
async def run_cmd_server(cmd, args: List = None):
    print("Remote command server.", flush=True)
    loop = asyncio.get_event_loop()
    args = args or []

    try:
        key = oauth2.get_oauth2_key()

        async with AsyncPushbullet(key, proxy=PROXY) as pb:
            stdout_queue = asyncio.Queue()
            stderr_queue = asyncio.Queue()
            async with EphemeralComm(pb,
                                     CMsg) as ec:  # type: EphemeralComm[CMsg]

                # msg = {"type": "console", "status": "command server connected to pushbullet"}
                # await pb.async_push_ephemeral(msg)
                kmsg = CMsg(status="command server connected to pushbullet")
                await ec.send(kmsg)

                async def _output_flusher(_q, name):
                    # name is from_stdout or from_stderr
                    while True:
                        lines = []
                        while len(lines) < 20:
                            line: bytes
                            try:
                                if lines:
                                    # If we have something to send, wait only a moment
                                    # to see if there's anything else coming.
                                    # print("Waiting with timeout", name)
                                    line = await asyncio.wait_for(_q.get(),
                                                                  timeout=0.25)
                                else:
                                    # print("Waiting without timeout", name)
                                    # If we have an empty queue, no need for the timeout
                                    line = await _q.get()

                            except asyncio.TimeoutError:
                                # print("TE")
                                break
                                # break  # while loop for length of lines
                            else:
                                # print(f"{name}: {line}")
                                if line is None:
                                    # print(f"{name} output flusher on server done!")
                                    # return  # We're done!
                                    lines.append(None)
                                    break
                                else:
                                    line = line.decode().rstrip()
                                    lines.append(line)

                        # print(f"{name} server LINES:", lines)
                        if lines:
                            try:
                                # msg = {"type": "console", name: lines}
                                # await pb.async_push_ephemeral(msg)
                                if name == "from_stdout":
                                    kmsg = CMsg(from_stdout=lines)
                                    await ec.send(kmsg)
                                elif name == "from_stderr":
                                    kmsg = CMsg(from_stderr=lines)
                                    await ec.send(kmsg)

                                if lines[-1] is None:
                                    # print(f"{name} found None - output flusher is returning")
                                    return  # We're done
                            except Exception as ex:
                                print("ERROR:",
                                      ex,
                                      file=sys.stderr,
                                      flush=True)
                                traceback.print_tb(sys.exc_info()[2])

                t1 = loop.create_task(
                    _output_flusher(stdout_queue, "from_stdout"))
                t2 = loop.create_task(
                    _output_flusher(stderr_queue, "from_stderr"))

                # async with LiveStreamListener(pb, types="ephemeral:console") as lsl:

                await async_execute_command(
                    cmd,
                    args,
                    provide_stdin=LiveStreamCommandListener(ec),
                    handle_stderr=stderr_queue.put,
                    # handle_stdout=print)
                    handle_stdout=stdout_queue.put)

                # print("ADDING None TO BOTH OUTPUT QUEUES")
                await stdout_queue.put(
                    None)  # mark that we're done for the output flushers
                await stderr_queue.put(None)  # mark that we're done

                await asyncio.gather(t1, t2)

        # print("SERVER asyncpush WITH BLOCK EXITED")

    except Exception as ex:
        print("ERROR:", ex, file=sys.stderr, flush=True)
        traceback.print_tb(sys.exc_info()[2])

    finally:
        print("Server tool closing ... ", end="", flush=True)
        print("Closed.", flush=True)
async def run_console():
    # Read console input from input() and write to pushbullet
    # Echo pushbullet from_stdout through print()
    print("Starting console for connecting with remote server", flush=True)
    stdout_task = None  # type: asyncio.Task
    try:
        key = oauth2.get_oauth2_key()

        async with AsyncPushbullet(key, proxy=PROXY) as pb:
            async with EphemeralComm(pb,
                                     CMsg) as ec:  # type: EphemeralComm[CMsg]
                # msg = {"type": "console", "status": "Console input connected to pushbullet"}
                # await pb.async_push_ephemeral(msg)
                kmsg = CMsg(status="Console input connected to pushbullet")
                await ec.send(kmsg)

                async with AsyncReadConsole("cmd input: ") as arc:

                    async def _dump_stdout():
                        try:
                            remote_stdout_closed = False
                            remote_stderr_closed = False
                            async for kmsg in ec.with_timeout(
                                    10, break_on_timeout=False):

                                if kmsg is None:
                                    # print("TIMEDOUT")
                                    if remote_stderr_closed or remote_stdout_closed:
                                        # We received a close from one but not the other.  Just quit.
                                        print("Remote command exited.",
                                              flush=True)
                                        await ec.close()  # TODO: error here
                                        # break
                                    continue

                                for line in kmsg.from_stdout:
                                    if line is None:
                                        # print("stdout closed.")
                                        remote_stdout_closed = True
                                    else:
                                        print(line, flush=True)

                                for line in kmsg.from_stderr:
                                    if line is None:
                                        # print("stderr closed.")
                                        remote_stderr_closed = True
                                    else:
                                        print(line,
                                              file=sys.stderr,
                                              flush=True)

                                if remote_stdout_closed and remote_stderr_closed:
                                    print("Remote command exited.", flush=True)
                                    await ec.close()
                            # print("end: async for kmsg in ec")

                        except Exception as ex:
                            print("ERROR in _dump_stdout:",
                                  ex,
                                  file=sys.stderr,
                                  flush=True)
                            traceback.print_tb(sys.exc_info()[2])
                        finally:
                            print('FINALLY: closing arc')
                            await arc.close()
                            print("arc.close() returned")

                    stdout_task = asyncio.get_event_loop().create_task(
                        _dump_stdout())

                    async for line in arc:
                        if line is None:
                            assert line is not None, "This should never happen"
                            break
                        else:
                            # msg = {"type": "console", "for_stdin": line}
                            # await pb.async_push_ephemeral(msg)
                            # print("Sending command: " + line)
                            kmsg = CMsg(for_stdin=[line])
                            await ec.send(kmsg)
                    print("exited async for line in arc:")

    except Exception as ex:
        print("ERROR in run_console:", ex, file=sys.stderr, flush=True)
        traceback.print_tb(sys.exc_info()[2])
    finally:
        print("Console tool closing ... ", end="", flush=True)
        if stdout_task:
            stdout_task.cancel()
        print("Closed.", flush=True)
示例#20
0
from asyncpushbullet import AsyncPushbullet
from config import *

pb = AsyncPushbullet(PUSHBULLET)

push = await pb.async_push_note("Title","Body")
示例#21
0
class PushApp():
    def __init__(self, root):
        self.window = root
        root.title("Async Pushbullet Demo")
        self.log = logging.getLogger(__name__)

        # Data
        self.pushbullet = None  # type: AsyncPushbullet
        self.pushbullet_listener = None  # type: LiveStreamListener
        self.key_var = tk.StringVar()  # API key
        self.pushes_var = tk.StringVar()
        self.ioloop = None  # type: asyncio.BaseEventLoop
        self.proxy_var = tk.StringVar()

        # View / Control
        self.create_widgets()

        # Connections
        self.create_io_loop()
        self.key_var.set(API_KEY)
        self.proxy_var.set(PROXY)

    def create_widgets(self):
        """
        API Key: [                  ]
                 <Connect>
        Pushes:
        +----------------------------+
        |                            |
        +----------------------------+
        """
        # API Key
        lbl_key = tk.Label(self.window, text="API Key:")
        lbl_key.grid(row=0, column=0, sticky=tk.W)
        txt_key = tk.Entry(self.window, textvariable=self.key_var)
        txt_key.grid(row=0, column=1, sticky=tk.W + tk.E)
        tk.Grid.grid_columnconfigure(self.window, 1, weight=1)
        txt_key.bind('<Return>', lambda x: self.connect_button_clicked())

        btn_connect = tk.Button(self.window,
                                text="Connect",
                                command=self.connect_button_clicked)
        btn_connect.grid(row=1, column=1, sticky=tk.W)

        btn_disconnect = tk.Button(self.window,
                                   text="Disconnect",
                                   command=self.disconnect_button_clicked)
        btn_disconnect.grid(row=2, column=1, sticky=tk.W)

        lbl_data = tk.Label(self.window, text="Incoming Pushes...")
        lbl_data.grid(row=4, column=0, sticky=tk.W)
        txt_data = BindableTextArea(self.window,
                                    textvariable=self.pushes_var,
                                    width=80,
                                    height=10)
        txt_data.grid(row=5, column=0, columnspan=2, sticky="NSEW")
        tk.Grid.grid_rowconfigure(self.window, 5, weight=1)

    def connect_button_clicked(self):
        self.close()

        async def _listen():
            try:
                self.pushbullet = AsyncPushbullet(self.key_var.get(),
                                                  verify_ssl=False,
                                                  proxy=self.proxy_var.get())

                async with LiveStreamListener(self.pushbullet) as pl2:
                    self.pushbullet_listener = pl2
                    await self.connected(pl2)

                    async for push in pl2:
                        await self.push_received(push, pl2)

            except Exception as ex:
                print("Exception:", ex)
            finally:
                await self.disconnected(self.pushbullet_listener)

        asyncio.run_coroutine_threadsafe(_listen(), self.ioloop)

    def create_io_loop(self):
        """Creates a new thread to manage an asyncio event loop specifically for IO to/from Pushbullet."""
        assert self.ioloop is None  # This should only ever be run once

        def _run(loop):
            asyncio.set_event_loop(loop)
            loop.run_forever()

        self.ioloop = asyncio.new_event_loop()
        self.ioloop.set_exception_handler(self._ioloop_exc_handler)
        threading.Thread(target=partial(_run, self.ioloop),
                         name="Thread-asyncio",
                         daemon=True).start()

    def _ioloop_exc_handler(self, loop: asyncio.BaseEventLoop, context: dict):
        if "exception" in context:
            self.status = context["exception"]
        self.status = str(context)
        # Handle this more robustly in real-world code

    def close(self):

        if self.pushbullet is not None:
            self.pushbullet.close_all_threadsafe()
            self.pushbullet = None
        if self.pushbullet_listener is not None:
            assert self.ioloop is not None
            pl = self.pushbullet_listener
            asyncio.run_coroutine_threadsafe(pl.close(), self.ioloop)
            self.pushbullet_listener = None

    def disconnect_button_clicked(self):
        self.close()

    async def connected(self, listener: LiveStreamListener):
        print("Connected to websocket")

    async def disconnected(self, listener: LiveStreamListener):
        print("Disconnected from websocket")

    async def push_received(self, p: dict, listener: LiveStreamListener):
        print("Push received:", p)
        prev = self.pushes_var.get()
        prev += "{}\n\n".format(p)
        self.pushes_var.set(prev)
示例#22
0
async def _run(args):
    # Logging levels
    if args.debug:  # Debug?
        print("Log level: DEBUG")
        logging.basicConfig(level=logging.DEBUG)
    elif args.verbose:  # Verbose?
        print("Log level: INFO")
        logging.basicConfig(level=logging.INFO)

    # Request setting up oauth2 access?
    if args.oauth2:
        token = await oauth2.async_gain_oauth2_access()
        if token:
            print("Successfully authenticated using OAuth2.")
            print(
                "You should now be able to use the command line tools without specifying an API key."
            )
            sys.exit(0)
        else:
            print("There was a problem authenticating.")
            sys.exit(1)

    # Find a valid API key
    api_key = try_to_find_key(args, not args.quiet)
    if api_key is None:
        print("You must specify an API key.", file=sys.stderr)
        sys.exit(errors.__ERR_API_KEY_NOT_GIVEN__)

    # Proxy
    proxy = lambda: args.proxy or os.environ.get(
        "https_proxy") or os.environ.get("http_proxy")

    try:
        # List devices?
        if args.list_devices:
            print("Devices:")
            async with AsyncPushbullet(api_key, proxy=proxy()) as pb:
                async for dev in pb.devices_asynciter():
                    print("\t", dev.nickname)
            return errors.__EXIT_NO_ERROR__

        # Specify a device?
        target_device = None  # type: Device
        if args.device:
            async with AsyncPushbullet(api_key, proxy=proxy()) as pb:
                target_device = await pb.async_get_device(nickname=args.device)

            if target_device is None:
                print("Device not found:", args.device, file=sys.stderr)
                return errors.__ERR_DEVICE_NOT_FOUND__
            else:
                print("Target device:", target_device.nickname)

        # Transfer single file?
        if getattr(args, "file", False):
            async with AsyncPushbullet(api_key, proxy=proxy()) as pb:
                return await _transfer_file(pb=pb,
                                            file_path=args.file,
                                            use_transfer_sh=args.transfer_sh,
                                            quiet=args.quiet,
                                            title=args.title,
                                            body=args.body,
                                            target_device=target_device)

        elif getattr(args, "files", False):
            async with AsyncPushbullet(api_key, proxy=proxy()) as pb:
                for file_path in args.files:  # type str
                    _ = await _transfer_file(pb=pb,
                                             file_path=file_path,
                                             use_transfer_sh=args.transfer_sh,
                                             quiet=args.quiet,
                                             title=args.title,
                                             body=args.body,
                                             target_device=target_device)

        # Push note
        elif args.title or args.body:

            async with AsyncPushbullet(api_key, proxy=proxy()) as pb:
                if args.body is not None and args.body == "-":
                    body = sys.stdin.read().rstrip()
                else:
                    body = args.body
                url = args.url
                if url is None:
                    if not args.quiet:
                        print("Pushing note...", end="", flush=True)
                    _ = await pb.async_push_note(title=args.title,
                                                 body=body,
                                                 device=target_device)
                    if not args.quiet:
                        print("Done.", flush=True)
                else:
                    if not args.quiet:
                        print("Pushing link...")
                    _ = await pb.async_push_link(title=args.title,
                                                 url=url,
                                                 body=body,
                                                 device=target_device)
                    if not args.quiet:
                        print("Done.", flush=True)

        else:
            print("Nothing to do.")
            return errors.__ERR_NOTHING_TO_DO__

    except InvalidKeyError as exc:
        print(exc, file=sys.stderr)
        return errors.__ERR_INVALID_API_KEY__

    except PushbulletError as exc:
        print(exc, file=sys.stderr)
        return errors.__ERR_CONNECTING_TO_PB__
示例#23
0
class GuiToolApp(TkAsyncioBaseApp):
    def __init__(self, root):
        super().__init__(root)
        self.window = root
        root.title("Pushbullet Account Management")
        self.log = logging.getLogger(__name__)

        # Data
        self._pushbullet = None  # type: AsyncPushbullet
        self.pushbullet_listener = None  # type: LiveStreamListener
        self.key_var = tk.StringVar()  # type: tk.StringVar  # API key
        self.pushes_var = tk.StringVar(
        )  # type: tk.StringVar  # Used in text box to display pushes received
        self.status_var = tk.StringVar(
        )  # type: tk.StringVar  # Bound to bottom of window status bar
        self.proxy = os.environ.get("https_proxy") or os.environ.get(
            "http_proxy")  # type: str

        # Related to Devices
        self.device_detail_var = tk.StringVar(
        )  # type: tk.StringVar  # Used in text box to display device details
        self.devices_in_listbox = None  # type: Tuple[Device]  # Cached devices that were retrieved
        self.device_tab_index = None  # type: int  # The index for the Devices tab

        # View / Control
        self.btn_connect = None  # type: tk.Button
        self.btn_disconnect = None  # type: tk.Button
        self.lb_device = None  # type: tk.Listbox
        self.btn_load_devices = None  # type: tk.Button
        self.lbl_photo = None  # type: tk.Label
        self.lbl_status = None  # type: tk.Label
        self.create_widgets()

        # Connections / Bindings
        tkinter_tools.bind_tk_var_to_method(partial(PREFS.set, "api_key"),
                                            self.key_var)
        self.key_var.set(API_KEY)

    @property
    def status(self):
        return str(self.status_var.get())

    @status.setter
    def status(self, val):
        self.tk(self.status_var.set, val)

    @property
    def pushbullet(self) -> AsyncPushbullet:
        current_key = self.key_var.get()
        if self._pushbullet is not None:
            if current_key != self._pushbullet.api_key:
                self._pushbullet.close_all_threadsafe()
                self._pushbullet = None
        if self._pushbullet is None:
            self._pushbullet = AsyncPushbullet(
                api_key=current_key,
                # loop=self.ioloop,
                verify_ssl=False,
                proxy=self.proxy)

        return self._pushbullet

    @pushbullet.setter
    def pushbullet(self, val: AsyncPushbullet):
        if val is None and self._pushbullet is not None:
            self._pushbullet.close_all_threadsafe()
        self._pushbullet = val

    def ioloop_exception_happened(self, extype, ex, tb, func):
        self.status = ex

    def create_widgets(self):
        parent = self.window

        # API Key
        frm_key = tk.Frame(parent)
        frm_key.grid(row=0, column=0, sticky="NSEW")
        lbl_key = tk.Label(frm_key, text="API Key:")
        lbl_key.grid(row=0, column=0, sticky=tk.W)
        txt_key = tk.Entry(frm_key, textvariable=self.key_var)
        txt_key.grid(row=0, column=1, sticky=tk.W + tk.E, columnspan=2)
        btn_oauth2 = tk.Button(frm_key, text="Authenticate online...")
        btn_oauth2.configure(command=partial(self.oauth2_clicked, btn_oauth2))
        btn_oauth2.grid(row=0, column=2, sticky=tk.W)
        tk.Grid.grid_columnconfigure(frm_key, 1, weight=1)
        tk.Grid.grid_columnconfigure(parent, 0, weight=1)

        # Top level notebook
        notebook = ttk.Notebook(parent)
        notebook.grid(row=1, column=0, sticky="NSEW", columnspan=2)
        # tk.Grid.grid_columnconfigure(parent, 0, weight=1)
        tk.Grid.grid_rowconfigure(parent, 1, weight=1)

        notebook.bind("<<NotebookTabChanged>>", self.notebook_tab_changed)

        # Status line
        status_line = tk.Frame(parent, borderwidth=2, relief=tk.GROOVE)
        status_line.grid(row=999, column=0, sticky="EW", columnspan=2)
        self.lbl_photo = tk.Label(
            status_line)  # , text="", width=16, height=16)
        self.lbl_photo.grid(row=0, column=0, sticky=tk.W)
        self.lbl_status = tk.Label(status_line, textvar=self.status_var)
        self.lbl_status.grid(row=0, column=1, sticky=tk.W)

        # Tab: Pushes
        pushes_frame = tk.Frame(notebook)
        notebook.add(pushes_frame, text="Pushes")
        self.create_widgets_pushes(pushes_frame)

        # Tab: Devices
        devices_frame = tk.Frame(notebook)
        notebook.add(devices_frame, text="Devices")
        self.device_tab_index = notebook.index(
            tk.END) - 1  # save tab pos for later
        self.create_widgets_devices(devices_frame)

    def create_widgets_pushes(self, parent: tk.Frame):

        self.btn_connect = tk.Button(parent,
                                     text="Connect",
                                     command=self.connect_button_clicked)
        self.btn_connect.grid(row=0, column=0, sticky=tk.W)

        self.btn_disconnect = tk.Button(parent,
                                        text="Disconnect",
                                        command=self.disconnect_button_clicked)
        self.btn_disconnect.grid(row=0, column=1, sticky=tk.W)
        self.btn_disconnect.configure(state=tk.DISABLED)

        btn_clear = tk.Button(parent,
                              text="Clear",
                              command=partial(self.pushes_var.set, ""))
        btn_clear.grid(row=0, column=2)

        txt_data = tkinter_tools.BindableTextArea(parent,
                                                  textvariable=self.pushes_var,
                                                  width=60,
                                                  height=20)
        txt_data.grid(row=1, column=0, sticky="NSEW", columnspan=3)
        tk.Grid.grid_columnconfigure(parent, 0, weight=1)
        tk.Grid.grid_columnconfigure(parent, 1, weight=1)
        tk.Grid.grid_rowconfigure(parent, 1, weight=1)

    def create_widgets_devices(self, parent: tk.Frame):

        scrollbar = tk.Scrollbar(parent, orient=tk.VERTICAL)
        self.lb_device = tk.Listbox(parent, yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.lb_device.yview)
        self.lb_device.grid(row=0, column=0, sticky="NSEW")
        self.lb_device.bind("<Double-Button-1>",
                            self.device_list_double_clicked)

        self.btn_load_devices = tk.Button(parent,
                                          text="Load Devices",
                                          command=self.load_devices_clicked)
        self.btn_load_devices.grid(row=1, column=0, sticky="EW")

        txt_device_details = tkinter_tools.BindableTextArea(
            parent, textvariable=self.device_detail_var, width=80, height=10)
        txt_device_details.grid(row=0, column=1, sticky="NSEW")
        tk.Grid.grid_columnconfigure(parent, 1, weight=1)
        tk.Grid.grid_rowconfigure(parent, 0, weight=1)

    # ########   G U I   E V E N T S   ########

    def notebook_tab_changed(self, event):
        nb = event.widget  # type: ttk.Notebook
        index = nb.index("current")
        if index == self.device_tab_index:
            # If there are no devices loaded, go ahead and try
            if self.devices_in_listbox is None:
                self.load_devices_clicked()

    def oauth2_clicked(self, btn: tk.Button):
        btn.configure(state=tk.DISABLED)
        self.status = "Authenticating online using OAuth2..."

        async def _auth():
            token = await oauth2.async_gain_oauth2_access()
            if token:
                self.tk(self.key_var.set, token)
                self.status = "Authentication using OAuth2 succeeded."
            else:
                self.status = "Authentication using OAuth2 failed."
            btn.configure(state=tk.NORMAL)

        self.io(_auth())

    def connect_button_clicked(self):
        self.status = "Connecting to Pushbullet..."
        self.btn_connect.configure(state=tk.DISABLED)
        self.btn_disconnect.configure(state=tk.DISABLED)

        if self.pushbullet is not None:
            self.pushbullet = None
        if self.pushbullet_listener is not None:
            pl = self.pushbullet_listener  # type: LiveStreamListener
            if pl is not None:
                self.io(pl.close())
            self.pushbullet_listener = None

        async def _listen():
            pl2 = None  # type: LiveStreamListener
            try:
                await self.verify_key()
                async with LiveStreamListener(self.pushbullet,
                                              types=()) as pl2:
                    self.pushbullet_listener = pl2
                    await self.pushlistener_connected(pl2)

                    async for push in pl2:
                        await self.push_received(push, pl2)

            except Exception as ex:
                pass
                print("guitool _listen caught exception", ex)
            finally:
                # if pl2 is not None:
                await self.pushlistener_closed(pl2)

        self.io(_listen())

    def disconnect_button_clicked(self):
        self.status = "Disconnecting from Pushbullet..."
        self.io(self.pushbullet_listener.close())

    def load_devices_clicked(self):
        self.btn_load_devices.configure(state=tk.DISABLED)
        self.status = "Loading devices..."
        self.lb_device.delete(0, tk.END)
        self.lb_device.insert(tk.END, "Loading...")
        self.devices_in_listbox = None

        async def _load():
            try:
                await self.verify_key()
                self.devices_in_listbox = tuple(
                    await self.pushbullet.async_get_devices())
                self.tk(self.lb_device.delete, 0, tk.END)
                for dev in self.devices_in_listbox:
                    self.tk(self.lb_device.insert, tk.END, str(dev.nickname))
                self.status = "Loaded {} devices".format(
                    len(self.devices_in_listbox))

            except Exception as ex:
                self.tk(self.lb_device.delete, 0, tk.END)
                self.status = "Error retrieving devices: {}".format(ex)
                raise ex
            finally:
                self.tk(self.btn_load_devices.configure, state=tk.NORMAL)

        # asyncio.run_coroutine_threadsafe(_load(), self.ioloop)
        self.io(_load())

    def device_list_double_clicked(self, event):
        items = self.lb_device.curselection()
        if len(items) == 0:
            print("No item selected")
            return

        if self.devices_in_listbox is None:
            print("No devices have been loaded")

        device = self.devices_in_listbox[int(items[0])]
        self.device_detail_var.set(repr(device))

    # ########   C A L L B A C K S  ########

    async def pushlistener_connected(self, listener: LiveStreamListener):
        self.status = "Connected to Pushbullet"
        try:
            me = await self.pushbullet.async_get_user()
            self.status = "Connected to Pushbullet: {}".format(me.get("name"))

        except Exception as ex:
            # print("To include image support: pip install pillow")
            pass
        finally:
            self.tk(self.btn_connect.configure, state=tk.DISABLED)
            self.tk(self.btn_disconnect.configure, state=tk.NORMAL)

    async def pushlistener_closed(self, listener: LiveStreamListener):
        # print_function_name()
        self.status = "Disconnected from Pushbullet"
        self.tk(self.btn_connect.configure, state=tk.NORMAL)
        self.tk(self.btn_disconnect.configure, state=tk.DISABLED)

    async def push_received(self, p: dict, listener: LiveStreamListener):
        # print("Push received:", p)
        push_type = p.get("type")
        if push_type == "push":
            push_type = "ephemeral"
        prev = self.pushes_var.get()
        prev += "Type: {}\n{}\n\n".format(push_type, pprint.pformat(p))
        self.tk(self.pushes_var.set, prev)

    # ########  O T H E R  ########

    async def verify_key(self):
        self.status = "Verifying API key..."
        api_key = self.key_var.get()

        try:
            await self.pushbullet.async_verify_key()
            self.status = "Valid API key: {}".format(api_key)

            if getattr(self.lbl_photo, "image_ref", None) is None:

                async def _load_pic():
                    try:
                        me = await self.pushbullet.async_get_user()
                        if "image_url" in me:
                            image_url = me.get("image_url")
                            try:
                                msg = await self.pushbullet._async_get_data(
                                    image_url)
                            except Exception as ex_get:
                                self.log.info(
                                    "Could not retrieve user photo from url {}"
                                    .format(image_url))
                            else:
                                photo_bytes = io.BytesIO(msg.get("raw"))
                                img = Image.open(photo_bytes)
                                label_size = self.lbl_photo.winfo_height()
                                img = img.resize((label_size, label_size),
                                                 Image.ANTIALIAS)
                                photo = PhotoImage(img)
                                self.tk(self.lbl_photo.configure, image=photo)
                                self.lbl_photo.image_ref = photo  # Save for garbage collection protection
                                self.log.info(
                                    "Loaded user image from url {}".format(
                                        image_url))

                    except Exception as ex:
                        # print(ex)
                        print("To include image support: pip install pillow")
                        # ex.with_traceback()
                        # raise ex

                asyncio.get_event_loop().create_task(_load_pic())

        except Exception as e:
            self.status = "Invalid API key: {}".format(api_key)
            self.tk(self.lbl_photo.configure, image="")
            self.lbl_photo.image_ref = None
            raise e