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
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())
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)
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])
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)
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)))
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
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)
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
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
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)
from asyncpushbullet import AsyncPushbullet from config import * pb = AsyncPushbullet(PUSHBULLET) push = await pb.async_push_note("Title","Body")
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)
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__
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