A synchronous and asyncio-based client for pushbullet.com
Project description
This is a python library for synchronous and asyncio-based communication with the wonderful Pushbullet service. It allows you to send push notifications to your computer, Android, and iOS devices.
In order to use the API you need an API key that can be obtained here. This is user specific and is used instead of passwords.
This is a fork of the synchronous-only pushbullet.py project from randomchars, which uses the pushbullet namespace. This project uses asyncpushbullet. I have made some changes to the pushbullet package, and you ought to be able to use the regular synchronous functions alongside the asyncio-enabled versions. Over time these have drifter further apart, so if you drop in the asyncpushbullet package in place of an older pushbullet package, you will probably need to make some changes to your code in terms of function names and error handling.
Installation
The easiest way is to just open your favorite terminal and type
pip install asyncpushbullet
Alternatively you can clone this repo and install it with
python setup.py install
Requirements
requests: Used in synchronous Pushbullet superclass
python-magic: Guesses at filetypes for uploading, optional
aiohttp: Foundational to the asyncio-enabled AsyncPushbullet classes
tqdm: For some command line scripts
pillow: Used in some example GUI code
Usage
Command Line (optional)
The asyncpushbullet package has some scripts that can be run from the command line. One is for sending pushes. Two are for listening for and responding to pushes.
There are several ways to authenticate your Pushbullet.com API key when using the command line:
Set the PUSHBULLET_API_KEY environment variable.
Use the --key command line option and include the key as an argument.
Use the --key-file command line option and point to a text file containing the API key.
Use the --oauth2 flag once to authenticate your command line tools using OAuth2.
Pushing a Note from the Command Line
You can push a note from the command line and specify a title and body.
$ pbpush --title "Hello World" --body "nothing to see"
Uploading and Pushing a File from the Command Line
You can upload and push a file as well.
$ pbpush --file homework.txt --title "Homework" --body "Avoid the dog."
The flags available for the pbpush command line script:
usage: pbpush [-h] [-k KEY] [--key-file KEY_FILE] [--proxy PROXY] [-t TITLE] [-b BODY] [-d DEVICE] [--list-devices] [-u URL] [-f FILE] [--transfer.sh] [-q] [--oauth2] [--debug] [-v] [--version] optional arguments: -h, --help show this help message and exit -k KEY, --key KEY Your Pushbullet.com API key --key-file KEY_FILE Text file containing your Pushbullet.com API key --proxy PROXY Optional web proxy -t TITLE, --title TITLE Title of your push -b BODY, --body BODY Body of your push (- means read from stdin) -d DEVICE, --device DEVICE Destination device nickname --list-devices List registered device names -u URL, --url URL URL of link being pushed -f FILE, --file FILE Pathname to file to push --transfer.sh Use www.transfer.sh website for uploading files (use with --file) -q, --quiet Suppress all output --oauth2 Register your command line tool using OAuth2 --debug Turn on debug logging -v, --verbose Turn on verbose logging (INFO messages) --version show program's version number and exit
There is also a variant of pbpush called pbtransfer that makes it even faster and easier to send off files using the http://transfer.sh service.
$ pbtransfer somefile.jpg someotherfile.mp4
The flags available for the pbtransfer command line script:
usage: pbtransfer [-h] [-k KEY] [--key-file KEY_FILE] [--proxy PROXY] [-d DEVICE] [--list-devices] [-f FILE] [-q] [--oauth2] [--debug] [-v] [--version] [files [files ...]] positional arguments: files Remaining arguments will be files to push optional arguments: -h, --help show this help message and exit -k KEY, --key KEY Your Pushbullet.com API key --key-file KEY_FILE Text file containing your Pushbullet.com API key --proxy PROXY Optional web proxy -d DEVICE, --device DEVICE Destination device nickname --list-devices List registered device names -f FILE, --file FILE Pathname to file to push -q, --quiet Suppress all output --oauth2 Register your command line tool using OAuth2 --debug Turn on debug logging -v, --verbose Turn on verbose logging (INFO messages) --version show program's version number and exit
Listening for and Responding to Pushes
You can listen for pushes and respond. To simply echo pushes to the console:
$ pblisten --echo
You can have a script called whenever a push arrives. The --exec flag takes its following arguments as a script to call and any parameters to pass that script. The script will be called with those parameters and with the push (json encoded) sent via stdin.
$ pblisten --exec handle_new_push.sh
You can even have multiple actions listed at one time:
$ pblisten --exec handle_new_push.sh --exec record_in_log.sh
Your script can respond via its stdout in order to send push(es) back. An example response:
[
{
"title" : "Fish Food Served",
"body" : "Your automated fish feeding gadget has fed your fish. "
},
{ "title" : "Second push", "body" : "Second body" }
]
Or if you only want to send one push, there is a simpler form for your response:
{ "title" : "title here", "body" : "body here"}
Finally instead of --exec, you can use --exec-simple to skip json altogether. Your script will receive the push via stdin except that the first line will be the title of the push, and the subsequent lines will be the body.
$ pblisten --exec-simple handle_new_push.sh
You can throttle how many pushes are received in a period of time using the --throttle-count and --throttle-seconds flags.
If a device nickname is specified, and there is no device with that nickname, a new device will be created with that nickname.
The flags available for the pblisten command line script:
usage: pblisten [-h] [-k KEY] [--key-file KEY_FILE] [-e] [-x EXEC [EXEC ...]] [-s EXEC_SIMPLE [EXEC_SIMPLE ...]] [-p EXEC_PYTHON [EXEC_PYTHON ...]] [-t TIMEOUT] [--throttle-count THROTTLE_COUNT] [--throttle-seconds THROTTLE_SECONDS] [-d DEVICE] [--list-devices] [--proxy PROXY] [--debug] [-v] [-q] [--oauth2] [--clear-oauth2] [--version] optional arguments: -h, --help show this help message and exit -k KEY, --key KEY Your Pushbullet.com API key --key-file KEY_FILE Text file containing your Pushbullet.com API key -e, --echo ACTION: Echo push as json to stdout -x EXEC [EXEC ...], --exec EXEC [EXEC ...] ACTION: Execute a script to receive push as json via stdin. Your script can write json to stdout to send pushes back. [ { "title" : "Fish Food Served", "body" : "Your automated fish feeding gadget has fed your fish. " }, { "title" : "Second push", "body" : "Second body" } ] Or simpler form for a single push: { "title" : "title here", "body" : "body here"} -s EXEC_SIMPLE [EXEC_SIMPLE ...], --exec-simple EXEC_SIMPLE [EXEC_SIMPLE ...] ACTION: Execute a script to receive push in simplified form via stdin. The first line of stdin will be the title, and subsequent lines will be the body. Your script can write lines back to stdout to send a single push back. The first line of stdout will be the title, and subsequent lines will be the body. -p EXEC_PYTHON [EXEC_PYTHON ...], --exec-python EXEC_PYTHON [EXEC_PYTHON ...] ACTION: Load the given python file and execute it by calling its on_push(p, pb) function with 2 arguments: the push that was received and a live/connected AsyncPushbullet object with which responses may be sent. -t TIMEOUT, --timeout TIMEOUT Timeout in seconds to use for actions being called (default 30). --throttle-count THROTTLE_COUNT Pushes will be throttled to this many pushes (default 10) in a certain number of seconds (default 10) --throttle-seconds THROTTLE_SECONDS Pushes will be throttled to a certain number of pushes (default 10) in this many seconds (default 10) -d DEVICE, --device DEVICE Only listen for pushes targeted at given device name --list-devices List registered device names --proxy PROXY Optional web proxy --debug Turn on debug logging -v, --verbose Turn on verbose logging (INFO messages) -q, --quiet Suppress all output --oauth2 Register your command line tool using OAuth2 --clear-oauth2 Clears/unregisters the oauth2 token --version show program's version number and exit
Developer Docs
The following instructions relate to using asyncpushbullet within your own Python code.
Quick Start
Here is a well-behaved example right off the bat to take a look at:
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A basic, complete example of using AsyncPushbullet to interact with the Pushbullet.com service.
"""
import asyncio
import os
import sys
import traceback
sys.path.append("..") # Since examples are buried one level into source tree
from asyncpushbullet import AsyncPushbullet, InvalidKeyError, PushbulletError, LiveStreamListener
API_KEY = "" # YOUR API KEY
PROXY = os.environ.get("https_proxy") or os.environ.get("http_proxy")
EXIT_INVALID_KEY = 1
EXIT_PUSHBULLET_ERROR = 2
EXIT_OTHER = 3
def main():
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
loop = asyncio.get_event_loop()
return loop.run_until_complete(_run())
if __name__ == "__main__":
if API_KEY == "":
with open("../api_key.txt") as f:
API_KEY = f.read().strip()
sys.exit(main())
Authentication
To create an AsyncPushbullet object:
from asyncpushbullet import AsyncPushbullet
pb = AsyncPushbullet(api_key)
If your key is invalid (that is, the Pushbullet API returns a 401), an InvalidKeyError is raised the first time communication is made. To check right away for the validity of your key, you can use the verify_key() or async_verify_key() functions, in synchronous or asynchronous mode as appropriate.
from asyncpushbullet import AsyncPushbullet
...
pb = AsyncPushbullet(api_key)
await pb.async_verify_key()
...
await pb.async_close()
or even better – this is preferred because it neatly closes sessions using the async with context manager.
from asyncpushbullet import AsyncPushbullet
...
async def _run():
async with AsyncPushbullet(api_key) as pb:
# Do stuff
loop.create_task(_run())
Event Loops
AsyncPushbullet expects its async functions to operate on only one event loop. Create a new AsyncPushbullet object if you need to operate on multiple event loops. If you need to close an AsyncPushbullet from another loop or thread, use the close_all_threadsafe().
Using a proxy
When specified, all requests to the API will be made through the proxy.
from asyncpushbullet import AsyncPushbullet
pb = AsyncPushbullet(api_key, proxy="https://user:pass@10.10.1.10:3128/")
Pushing a text note
push = await pb.async_push_note("This is the title", "This is the body")
push is a dictionary containing the data returned by the Pushbullet API.
Pushing an address
Pushing addresses is no longer supported by pushbullet.com and has been dropped in asyncpushbullet.
Pushing a list
Pushing lists is no longer supported by pushbullet.com and has been dropped in asyncpushbullet.
Pushing a link
push = await pb.async_push_link("Cool site", "https://github.com")
Pushing a file
Pushing files is a two part process. First you need to upload the file, and after that you can push it like you would anything else.
async def upload_my_file(pb: AsyncPushbullet, filename: str):
# The actual upload
info = await pb.async_upload_file(filename)
# Push as a file:
await pb.async_push_file(info["file_name"], info["file_url"], info["file_type"],
title="File Arrived!", body="Please enjoy your file")
# or Push as a link:
await pb.async_push_link("Link to File Arrived!", info["file_url"], body="Please enjoy your file")
async_upload_file() returns a dictionary containing file_type, file_url and file_name keys, which are the same parameters that async_push_file() requires.
You can also upload a file to the https://transfer.sh service using async_upload_file_to_transfer_sh. The https://transfer.sh service allows file uploads up to 10GB in size, and links last only two weeks.
...
info = await pb.async_upload_file_to_transfer_sh(filename)
...
Working with pushes
You can also view all previous pushes:
pushes = await pb.async_get_pushes()
Pushes is a list containing dictionaries that have push data. You can use this data to dismiss notifications or delete pushes.
latest = pushes[0]
# We already read it, so let's dismiss it
await pb.async_dismiss_push(latest.get("iden"))
# And you can delete it
await pb.async_delete_push(latest.get("iden"))
Both of these raise a PushbulletError if there’s an error.
You can also delete all of your pushes (be careful):
await pb.async_delete_pushes()
Pushing to specific devices
So far all our pushes went to all connected devices, but there’s a way to limit that.
First we need to get hold of some devices.
# Get all devices that the current user has access to.
devices = await pb.async_get_devices()
print(devices)
# [Device('Motorola Moto G'), Device('N7'), Device('Chrome')]
# Or retrieve a device by its name. Returns None if not found.
motog = await pb.async_get_device(nickname='Motorola Moto G')
We can pass the device to push methods:
push = await pb.async_push_note("Hello world!", "We're using the api.", device=motog)
Creating new devices
Creating a new device is easy too, you only need to specify a name for it. Though you can also specify manufacturer, model and icon too.
coffee = await pb.async_new_device("MyCoffeePotGadget")
# or
motog = await pb.async_new_device("MotoG", manufacturer="Motorola", model="G", icon="android")
Now you can use it like any other device.
Editing devices
You can change the nickname, the manufacturer, model and icon of the device. The new Device object is returned.
coffee = await pb.async_new_device("MyCoffeePotGadget")
coffee2 = await pb.async_edit_device(coffee, manufacturer="Me!")
Deleting devices
Of course, you can also delete devices, even those not added by your code.
await pb.async_remove_device(coffee)
Channels
You can also send pushes to channels. First, create a channel on the Pushbullet website (also make sure to subscribe to that channel). All channels which belong to the current user can be retrieved as follows:
# Get all channels created by the current user
channels = await pb.async_get_channels()
print(channels)
# [Channel('My Channel' 'channel_identifier')]
# Or retrieve a channel by its name. Returns None if not found.
mychannel = await pb.async_get_channel('My Channel')
Then you can send a push to all subscribers of this channel like so:
push = await pb.async_push_note("Hello Channel!", channel=mychannel)
Contacts
Contacts, which are known as “Chats” in Pushbullet’s terminilogy, work just like devices:
# Get all chats that the current user has access to.
chats = await pb.async_get_chats()
print(chats)
# [Chat('Pushbullet Team' < pushbullet - team @ pushbullet.com >:
# {'active': True,
# 'created': 1484549777.2763588,
# 'modified': 1484549777.276366,
# 'muted': None,
# 'with': {'email': 'pushbullet-team@pushbullet.com',
# 'email_normalized': 'pushbullet-team@pushbullet.com',
# 'iden': 'ujzob6qgcYm',
# 'image_url': 'https://static.pushbullet.com/google-user/4308fcd45302c1dde28c5d86d7654da31bd32e70e9c28cac4a29d7f35c193e51',
# 'name': 'Pushbullet Team',
# 'type': 'user'}})]
# How to access properties
print("Active:", chats[0].active)
print("Email:", chats[0].with_email)
# Or retrieve a chat by its email. Returns None if not found.
peter = await pb.async_get_chat('peter@gmail.com')
Now we can use the chat objects like we did with pb or with the devices.:
push = await pb.async_push_note("Hello world!", "We're using the api.", chat=peter)
Adding new chats
bob = await pb.async_new_chat("Bob", "bob@gmail.com")
Sending SMS messages
The author (Robert Harder) does not have any Android devices, so he has not been able to test the asyncio versions of the sms functions. In theory they should work. :-/
motog = await pb.async_get_device(nickname='Motorola Moto G')
push = await pb.async_push_sms(motog, "+3615555678", "Wowza!")
Sending Ephemerals
The Pushbullet service has ephemeral messages that are not stored and are used for, wait for it, ephemeral or transient messaging such as the universal clipboard functionality. You can send these messages as well.
msg = {"body": "something I copied", "type": "clip"}
await pb.async_push_ephemeral(msg)
msg = {"foobar": "Some control message you use for your IoT devices."}
await pb.async_push_ephemerals(msg)
End-To-End encryption
The End-to-End notes are from the original Pushbullet project.
You activate end-to-end encryption by specifying your encryption key during the construction of the Pushbullet instance:
from pushbullet import Pushbullet
pb = Pushbullet(api_key, "My secret password")
When specified, all sent SMS will be encrypted. Note that the use of end-to-end encryption requires the cryptography package. Since end-to-end encryption is only supported for SMS at the moment, the cryptography library is not specified as a dependency of pushbullet.py and should be installed seperatly by running pip install cryptography.
Note that Pushbullet supportes End-To-End encryption only in SMS, notification mirroring and universal copy & paste. Your pushes will not be end-to-end encrypted.
Error checking
If the Pushbullet api returns an error code an __ InvalidKeyError or a PushbulletError is raised. The first __ two are both subclasses of PushbulletError
The pushbullet api documetation contains a list of possible status codes.
Listening for Pushes
To listen for pushes, use the LiveStreamListener class in an async for loop:
async def _run():
async with AsyncPushbullet(api_key) as pb:
async with LiveStreamListener(pb) as pl:
print("Awaiting pushes...")
async for push in pl:
print("Got a push:", push)
loop = asyncio.get_event_loop()
loop.run_until_complete(_run())
TODO
More tests. Write them all.
License
MIT license. See LICENSE for full text.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file asyncpushbullet-0.19.8.tar.gz
.
File metadata
- Download URL: asyncpushbullet-0.19.8.tar.gz
- Upload date:
- Size: 60.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.11.0 pkginfo/1.4.2 requests/2.19.1 setuptools/39.0.1 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/3.7.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 51e8fb743c3c840bfb099da8b1df719686fd6de61270a9d96897d92e592961f9 |
|
MD5 | 654df86516b3d96388bb29a419f0e8cc |
|
BLAKE2b-256 | cc9245ca4085ecc8c36b1d04c8e265164547f62eca886e3eb8094c2f7c33447a |