Package: pybrightsign
pybrightsignBrightSign APIs for humans.  Python module to simplify using the BrightSign BSN/BSNEEREST API.NOTE: Now in Beta.  Pretty stable, but still subject to breaking changes in later releases.Install withpipas usual:pip install pybrightsignIntroductionThis library handles many of the fussy details when using BrightSign APIs (esp. the Upload API), freeing the developer from much boilerplate code.construct aServerobjectauthorize itNOTE: as of API version2019/03, the credentials object needs to includeclient_idandclient_secretuse itsrequestsobject - as you would the plain-jane requests object but simpleruse its handy utility methodsHere is a full example, using theDevices endpointto show a list the names of all devices in a network:frompybrightsignimportServercreds={'network':'Demo','username':'user@example.com','password':'swordfish'}# First create a server object using the domain name of your BSNEE instancebsnee=Server('brightsign.example.com')bsnee.authorize(creds)# Now use the server's requests object as you would normally,# without worrying about api version, tokens, url prefix, basic headers, etc.response=bsnee.requests.get('/devices')response.raise_for_status()devices=response.json()['items']fordeviceindevices:print(device['name'])If you have ever wrestled with theUpload endpoint, you will love how easy it now is to upload stuff.  Here is a full example that uploads a video, an image (to a particular folder), then a web site (including the index.html and all css, js, images, etc. in the same folder):frompybrightsignimportServercreds={'network':'Demo','username':'user@example.com','password':'swordfish'}bsnee=Server('brightsign.example.com')bsnee.authorize(creds)bsnee.upload_file('promo.mp4')bsnee.upload_file('logo.png',r'\Shared\Images')bsnee.upload_web_folder('./website/index.html')MethodsThis section will cover the following methods:theServerclass's constructorauthorize(creds)requests(not a method, but it's covered here)prior knowledge of therequestsmodule is pretty much requiredyou should also be aware of theBrightSign's REST API and its endpointsyou do not need to worry about theUpload endpoints, as they are completely wrapped by the upload utility methodsget_network_names()switch_network(network_name)move_device_to_group(device_id, new_group_id)Upload utility methodsupload_file(filepath, [to_folder])upload_web_folder(site_name, index_path)ConstructorTo construct aServerobject you need a valid domain name of the BSN/BSN.Cloud instance.  The following constructor examples all begin with:frompybrightsignimportServerdomain='brightsign.example.com'NOTE:  If you are following along, please replacebrightsign.example.comwith your domain name or these examples will fail.MinimalThe most basic construction uses only the domain name - which is required.server=Server(domain)This assumes the API subdomain isapiand all calls to the API will begin with 'https://api.brightsign.example.com'.  You can confirm this (and the API version) like this, for example:>>>print(server)https://api.brightsign.example.com/2019/03/RESTAPI SubdomainIf your API is located in a different subdomain thanapi, you can specify it like thisserver=Server(domain,api_subdomain='apitest')Now all API calls will begin with 'https://apitest.brightsign.example.com'API VersionAlthough optional, if you know the API version you should specify it.  If you do not the constructing aServerobject prints the following warning:WARNING: api_version (None) is invalid for https://api.brightsign.cri.com.  Attempting to discover correct api_version. This may take several seconds. To remove this warning (and this delay), please supply a valid api_version.API versions are in the form ofYYYY/MM.  The constructor tries to discover the API version by first checking the known versions (from most recent to oldest).  If a valid version is not found, it then tries the current year and current month, and keeps trying the previous month until it either finds a valid version or reaches the oldest known version (no point trying beyond that).  This is handy if you do not know the version, but clearly this takes time that can be avoided if you tell it the version.server=server(domain,api_version='2019/03')AuthorizeOnce you have constructed aServerobject (using any of the means above), you must authorize it before making calls.  Create a dictionary with your credentials and pass it to theauthorize()method.creds={'network':'Demo','username':'user@example.org','password':'swordfish'}server.authorize(creds)You can use the server object to see where you are connected any with which user:>>>print(server)https://api.brightsign.example.com/2019/03/RESTasDemo/user@example.orgRequestsOnce authorized, you can now use the server'srequestsobject.  Use this exactly as you would a regularrequestsobject exceptyou don't have to manage the auth token, nor create a headers dict with the token.you don't have to use a full URL - only the endpoint path.  For example, instead of usinghttps://api.brightsign.example.com/2019/03/REST/contentsyou can simply use/contentsDefault headersBy default all request haveAccept: application/jsonin the headers,In the case of POST, PUT, or PATCH will also haveContent-type: application/jsonIf you need different Accept or Content-type headers - or if you need additional headers, pass a headers dict as you normally would.  It will get merged into the managed headers (i.e. the one that has the token)Examples will make all of this clearer:Let's say you want to POST a presentation.  Using onlyrequestsyou would do something like this (assuming you had created theget_presentation()andget_token()methods)token=get_token()presentation=get_presentation()# returns a JSON string of the presentation to POSTheaders={'Accept':'application/json','Content-type':'application/json','Authentication':f'Bearer{token}'}response=requests.post('https://api.brightsign.example.com/2019/03/REST/presentations',data=presentation,headers=headers)Withpybrightsignyou don't needget_tokens(), and your code would look like this (after theserverobject was authorized):presentation=get_presentation()server.requests.post('/presentations',data=presentation)If you wanted a list of devices in XML insetead of JSON, you can override theAccept:header like this:reponse=server.get('/devices',headers={'Accept':'application/xml'})You can stop reading here and you will have the full power of BrightSign APIs as your disposal.  There are some utility methods which make some routine tasks a bit simpler.  Each of these can be done with just the server's requests, but why would you?List NetworksTo see which networks you have access to you could useserver.requests.get('/self/networks'), iterate through the response jsonitemsarray and pull out thenamefield of each object.  Or useget_network_names()>>>server.get_network_names()['admin','Demo','OtherNetwork']Switch NetworksIf you are authorized on one network, you can easily switch to a different network without having to re-authorize:>>>server.switch_network('OtherNetwork')>>>print(server)https://api.brightsign.example.com/2019/03/RESTasNewNetwork/user@example.orgMove a deviceTo move a device from one group to another:server.move_device_to_group(device_id,new_group_id)device_idcan be the device's ID number, or its serial numbernew_group_idcan be the group's ID number or its name.Upload a file or a WebsiteTo upload a file, you could wrestle with theUpload endpoint, or just useupload_file()server.upload_file('./img/logo.png')You can optionally specify which folder (virtualPath) to upload to:server.upload_file('./img/logo.png','\\Shared\\Images')Backslashes in the virtualPath must be escaped with backslashes.  Or use python'sr-stringnotation (raw string)server.upload_file('./img/logo.png',r'\Shared\Images')Similarly, to upload a Website, point to the path/name of the site's index file.  This will upload the index file and all other files in the same folder and its subfolders:For example, if your website is a set of files like this:./my-site
|   index.html
|
|--css
|       style.css
|
|--img
|       background.jpg
|       logo.png
|
|--js
    |--app
    |     ui.js
    |  
    |--utils
          api.js
          tools.jsYou upload this folder with the name 'MySite' (for example) like this:server.upload_web_folder('MySite','./my-site/index.html')Replaceindex.htmlwith the actual site's entry page is (e.g.default.htm)