# PyWiliot: wiliot-api #

wiliot-api is a python library for accessing Wiliot's cloud APIs from Python

### Installing pyWiliot
````commandline
pip install wiliot-api
````

### Using pyWiliot
Wiliot package location can be found, by typing in the command line:
````commandline
pip show wiliot-api
````
Code examples for using PyWiliot-api (scroll down)
* [platform](#platform-api)
* [edge](#edge-api)
* [manufacturing](#manufacturing-api)



For more documentation and instructions, please contact us: support@wiliot.com


## Release Notes:

### Version 4.10.14:
* Updated the get_bridge_non_dismissed_commands function (API path changed)
* Added an option to use a custom base URL for manufacturing client
* Added the option to use the owner id of the manufacturing client instead of passing it for every api call

### Version 4.10.12:
* Added support on manufacturing client for parsing API

### Version 4.10.11:
* Added support on manufacturing client for pixels owner change to custom base URL


### Version 4.10.10:
* Added an option to use a custom base URL for all API calls

### Version 4.10.9:
* Added a function to unassign bridge template
* Added support for initiator name for platform client call
* Added common run name check for manufacturing client

### Version 4.10.8:
* Added functions for pixel verification related API endpoints

### Version 4.10.5:
* Fixed a bug in setting new labels or updating existing ones

### Version 4.10.4:
* Fixed a bug with updating the values of multiple labels in a single call

### Version 4.10.3:
* Updated copyright formatting and requirements
* Added support for conversation yield tester

### Version 4.10.2:
* Added a function to call API endpoint to delete bridge non-dismissed commands
* Fixed a bug in the function to delete labels
* Added an option for keeping cloud connection alive in between requests, as a default.
* Added a function to upload files to S3 for Wiliot's partners


### Version 4.10.1:
* Added a function to unclaim multiple bridges 
* Updated the function to unclaim a single bridge


### Version 4.10.0:
* Added a function to get available bridge versions
* Added a function to get a bridge's with non-dismissed commands
* Removed functions related to asset labels due to deprecation


### Version 4.9.4:
* Changed delay of retry in case of rate limit error (429) to use the value in the retry-after header of the response 


### Version 4.9.3:
* Better handling for API call failing due to rate limiting (automatic retries)
* Added functions to stop a debug survey, get all surveys and get a specific survey details (without downloading)

### Version 4.9.0:
* Added functions to call the API endpoints for starting a debug survey and for getting a debug survey results
* Added a function for disassociating and associating multiple pixels from an asset in a single call

### Version 4.8.7:
* Fixed bug when assigning a new value to an existing key with new labels
* Updated copyright notice
* Added text to this README to advise examples on this page (internal links don't work on PyPi)

### Version 4.8.6:
* Fixed bug for get reel id api
* Added support to upload data for new tester

### Version 4.8.4:

* Added support with the new get reel id cloud api
* Added support for new sensors api - upload and get sensors data
* Fixed route for getting categories

### Version 4.8.3:

* Added a function to get all key values for an entity types

### Version 4.8.2:

* Fixed a bug in updating bridge configuration

### Version 4.8.1:

* Fixed a bug in setting multiple key value pairs in a single call

### Version 4.8.0:

* Added support for new labels (key:value pairs for platform entities)

### Version 4.7.1:

* Added option to upload testers data to cloud
* Added option to get a new and unique reel id for manufacturing
* Added the option to get post process tables for manufacturing partners
* Added the option to add temperature calibration point to a tag

### Version 4.6.4:

* Changed get_asset() implementation to use dedicated endpoint instead of metadataFetch


### Version 4.6.3:

* Fixed a bug in functions to get all bridges and gateways that prevented all results from being returned on subsequent calls after the first one


### Version 4.6.2:

* Added optional parameters to filter returned gateways and bridges


### Version 4.5.0:

* Added option to change gateway name when updating its configuration
* Added functionality to filter the list of returned pixels by sub strings
* Implemented function to get pixel count
* Added function to batch create assets using a CSV file
* Added a function to get event counts in a time range

### Version 4.4.4:

* Added support for updating bridges with ### Version 3.16 and newer (MEL)

### Version 4.4.3:

* Added shipment api support
* Improved post function to handle different type of payload

### Version 4.4.2:

* Improved support for alternate clouds and regions

### Version 4.4.0:

* Forcing a token refresh when an access token is less than a minute from expiry
* Added support for alternate clouds and regions
* Streamlined API paths to match between the platform and edge modules

### Version 4.3.2:

* Added an option to get a binary file from the API
* Added a function for sending actions to a gateway

### Version 4.3.1:

~~* Added a function for sending actions to a gateway~~

### Version 4.3.0:

* Change code to match the changes in asset label API endpoint
* All 2xx status code returned by the API are considered a success

### Version 4.2.0:

* Changed API URLs to support new cloud environment
* Added support for pulling paginated bridge and gateway lists (when an owner has more than 100 bridges/gateways)
* Added a function to update multiple bridges' configuration in one call
* Removed GATEWAY as an association type for location - not supported by the platform
* Requiring at least one pixel to be provided when creating an asset

### Version 4.1.2:

* Changed additional functions use the metadataFetch API to support larger returned data sets:
    * Get Locations
    * Get zones
    * Get location associations
    * Get zone associations
* Added the ability to associate pixels to zones
* Added the ability to associate bridges and gateways to locations

### Version 4.1.0:

* Changed get_locations and get_categories to use the metadataFetch API endpoint to support:
    * Fetching more than the first 100 items
    * To return the underlying zones for each location when fetching locations
* Added calls to get, create and delete asset labels
* Changed get_asset function call to use the metdataFetch endpoint for compatibility with the get_assets call
* Removed an unsupported event type (geolocation)

### Version 4.0.3:

* Changed logic for get_assets in Platform module to remove limitation of only getting back the first 100 assets
* Fixed bug in call to add pixel to asset

### Version 4.0.2:

* First ### Version


The package previous content was published under the name 'wiliot' package.
for more information please read 'wiliot' package's release notes
  
## Examples

### Platform API

```python

# Import the library
from wiliot_api.platform.platform import *
import os

# Define an owner ID
owner_id = "test_owner1"  # ToDo: add here your owner ID

# Initialise an Wiliot client object
platform = PlatformClient(api_key=os.environ.get("WILIOT_PLATFORM_API_KEY"), owner_id=owner_id)

# Get the first 50 pixels owned by the account
print(platform.get_pixels())

# Get a list of all pixels (not recommended on accounts with many pixels)
pixels = []
next_p = None
while True:
    p, next_p = platform.get_pixels(next=next_p)
    pixels += p
    if next_p is None:
        break

# Get a list of categories
platform.get_categories()

# Get a list of asset type
platform.get_asset_types()

# Create a category
platform.create_category(name="Black_box", asset_type_id='Default', category_id="black-box-sku",
                         description="A generic black box")

# Get a list of assets
platform.get_assets()

# Create an asset
platform.create_asset(name="Black-box-118763", category_id="black-box-sku",
                      pixels=[{"tagId": "pixel-1-id", "role": TagRole.DEFAULT},
                            {"tagId": "pixel-2-id", "role": TagRole.DEFAULT}])

# Associate a pixel to an asset
platform.associate_pixel_to_asset("my-asset-id", pixel_id="pixel-id")
# Disassociate a pixel from an asset
platform.disassociate_pixel_from_asset(asset_id="my-asset-id", pixel_id="pixel-id")
# Replace pixels associated to an asset (disassociate and associate multiple pixels in a single call)
platform.update_pixel_to_asset_association(asset_id="my-asset-id", pixels_to_disassociate=["pixel-id-1", "pixel-id-2"],
                                           pixels_to_associate=["pixel-id-3", "pixel-id-4"])

# Delete an asset
platform.delete_asset(asset_id="my-asset-id")

# Locations and zones
# Get a list of locations
platform.get_locations()

# Get a list of zones in a location
platform.get_zones(location_id="my-location-id")

# Associate a bridge to a zone
platform.create_zone_association(location_id="my-location-id", zone_id="my-zone-id",
                                 association_type=ZoneAssociationType.BRIDGE, association_value="my-bridge-id")

# Labels (key:value pairs)
# Get a list of key:value pairs for an asset
platform.get_entity_keys_values(entity_type=EntityType.ASSET,
                                entity_id="my-asset-id")
# Supported entity types:
# * ASSET
# * LOCATION
# * ZONE
# * BRIDGE
# * GATEWAY

# Get a specific key:value for a zone
platform.get_entity_keys_values(entity_type=EntityType.ZONE,
                                entity_id="my-zone-id",
                                key="special_zone")

# Get all key values for a specific key and all bridges
platform.get_entity_type_keys_values(entity_type=EntityType.BRIDGE,
                                     key='autoUpdate')

# Set one or more key:value pairs for one or more entities (of the same type)
platform.set_keys_values_for_entities(entity_type=EntityType.GATEWAY,
                                      entity_ids=["gateway-id-1", "gateway-id-2"],
                                      keys_values={"special_gateway": "True"})

# The previous call will fail if the provided gateways already have a value for a key called "special_gateway" set
# To force an overwrite of existing key value, use the overwrite_existing argument (False by default)
platform.set_keys_values_for_entities(entity_type=EntityType.GATEWAY,
                                      entity_ids=["gateway-id-1", "gateway-id-2"],
                                      keys_values={"special_gateway": "True"},
                                      overwrite_existing=True)

# Delete key:value from one or more entities
platform.delete_entities_key_value(entity_type=EntityType.LOCATION,
                                   entity_ids=["location-1-id", "location-2-id"],
                                   key='special_zone')
```

### Edge API

```python
# Import the library
from wiliot_api.edge.edge import *
import os

# Define an owner ID
owner_id = "test_owner"  # ToDo: add here your owner ID

# Initialise an Wiliot edge client object
edge = EdgeClient(api_key=os.environ.get('WILIOT_EDGE_API_KEY'), owner_id=owner_id)

# Get a list of gateways owned by the owner
edge.get_gateways()

# Get details on a single gateway by its ID
edge.get_gateway(gateway_id="my-gateway-id")

# Update a gateway's configuration
# First get its current configuration
config = edge.get_gateway("my-gateway-id")["reportedConf"]
# Make the required change - for example, change the pacer interval
config["additional"]["pacerInterval"] = 15
# Update the gateway's configuration
edge.update_gateways_configuration(gateways=["my-gateway-id"], config=config)

# Register a new gateway
edge.register_gateway(gateways=["my-new-gateway-id"])

# Delete a gateway from the account (to move to another account, for example)
edge.delete_gateway(gateway_id="my-gateway-id")


# Bridges

# Get a list of all bridges registered under the account with their configuration
edge.get_bridges()

# Get a list of all online bridges
edge.get_bridges(online=True)

# Get a list of bridges connected to a certain gateway
edge.get_bridges(gateway_id="my-gateway-id")

# Claim a bridge - can be called for bridges connected to account gateways
# Once claimed, no other account can claim the same bridge
edge.claim_bridge(bridge_id="my-bridge-id")

# Unclaim (release) bridge from the account - making it available to claim  by another account
edge.unclaim_bridge(bridge_id="my-bridge-id")

# Unclaim multiple bridges 
edge.unclaim_bridges(bridge_ids=["bridge1-id", "bridge2-id"])

# Update a bridge configuration
# First, get its current configuration
config = edge.get_bridge(bridge_id="my-bridge-id")["config"]

# Get a list of bridge versions
edge.get_bridge_versions()

# Request the configuration change - single bridge
edge.update_bridge_configuration(bridge_id="my-bridge-id", config={'energyPattern': 63})

# Request the configuration change - multiple bridge
edge.update_bridges_configuration(bridge_ids=["bridge1-id", "bridge2-id"], config={'energyPattern': 63})

# Change a bridge's name
edge.update_bridge_configuration(bridge_id="my-bridge-id", name="my_bridge_name")

#  Get any commands that have not finished or have been dismissed for a bridge
edge.get_bridge_non_dismissed_commands(bridge_id="bridge-id-1")

# Get bridges connected to gateway
edge.get_bridges_connected_to_gateway("gateway-id")

# Start a 5-minute debug survey
res = edge.start_survey(name="my-unique-survey-name", duration_ms=300000, bridges=["bridge-id-1", "bridge-id-2"])
# Read the results into a file 
output_file_path = edge.get_survey_result(survey_id=res['id'], output_directory="/path/to/output/directory")
print(f"Debug survey results stored in {output_file_path}")
# Get information about all surveys in the past 30 days
surveys = edge.get_surveys()
# Stop a survey before it's finished
edge.stop_survey("survey-id")


```

### Manufacturing API
#### (for Wiliot's manufacturing clients)

```python
# Import the library
from wiliot_api.manufacturing.manufacturing import *
import os

# Initialise an Wiliot client object
wiliot_manufacturing = ManufacturingClient(os.environ.get('WILIOT_MANUFACTURING_API_KEY'))

# Change the owner for a sequence of pixel IDs - useful for changing consecutive IDs - like part of a reel
req = wiliot_manufacturing.change_pixel_owner_by_range("first_pixel_id", "last_pixel_id", "from_owner_id",
                                                       "to_owner_id")

# Change the owner for a list of pixel IDs - When non-consecutive IDs need changing - up to 3000 pixels
req = wiliot_manufacturing.change_pixel_owner_by_list(["tag_1", "tag_2", "tag_3"], "from_owner_id", "to_owner_id")

# Change the owner of pixels by file - when needing to change more than 3000, non-consecutive IDs
#
# The file should be formatted as follows:
# tagId
# tag_1_id
# tag_2_id
# .....
req = wiliot_manufacturing.change_pixel_owner_by_file("from_owner_id", "to_owner_id", "/path/to/file")

# Each of the functions above returns a string representing a request ID. To check on the status of the request
print(wiliot_manufacturing.get_pixel_change_request_status(req))

```
