API Client Example
Below are some basic examples of how to get started writing an API client for the LightFi API using python or typescript.
Python Example API Client
Install OpenAPI generator
See instructions from OpenAPITools
Generate a client
An API client for python can be generated using the following example command:
openapi-generator-cli generate -i https://apiv2.lightfi.io/openapi.json -g python --additional-properties=floatStrictType=false -o python_api_client
After generating the API client, see the generated documentation for more info at python_api_client/README.md
Client script
Basic starting place
A basic example script to make an API call using python3
import openapi_client
from openapi_client.rest import ApiException
from pprint import pprint
configuration = openapi_client.Configuration(
host = "https://apiv2.lightfi.io"
)
configuration.access_token = "YOUR_ACCESS_TOKEN"
# Enter a context with an instance of the API client
with openapi_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = openapi_client.DefaultApi(api_client)
try:
api_response = api_instance.read_base_location_properties_locations_get()
print("The response of DefaultApi->read_base_location_properties_locations_get:\n")
pprint(api_response)
except ApiException as e:
print("Exception when calling DefaultApi->read_base_location_properties_locations_get: %s\n" % e)
Access token expiry checking
You can use a function like the following to check whether your access token is expired and needs refreshing
import jwt # pyJWT
from time import time
def check_token_expired(token: str):
unverified_claims = jwt.decode(token, options={"verify_signature": False})
exp = unverified_claims.get('exp', 0)
if exp > (time() + 10): # 10 seconds margin
return False # Token is not expired
return True # Token expired or will expire in the next 10s
Using a refresh token
To handle obtaining a new access_token
using your refresh_token
(see Obtaining an OAuth2 refresh token) you can use code like the following:
import httpx
import os
def refresh_oauth2_token(token_url: str, client_id: str, client_secret: str, refresh_token: str):
data = {
'grant_type': 'refresh_token',
'client_id': client_id,
'client_secret': client_secret,
'refresh_token': refresh_token,
}
response = httpx.post(token_url, data=data)
if response.status_code == 200:
token_data = response.json()
access_token = token_data['access_token']
return access_token
else:
raise Exception(f"Failed to refresh OAuth2 token: {response.text}")
# Example usage, set the environment variables with your values:
token_url = "https://lightfiv2.auth.eu-west-2.amazoncognito.com/oauth2/token"
client_id = os.environ["CLIENT_ID"]
client_secret = os.environ["CLIENT_SECRET"]
refresh_token = os.environ["REFRESH_TOKEN"]
access_token = refresh_oauth2_token(token_url, client_id, client_secret, refresh_token)
Periodic API polling with automatic token refresh
Combining the token validation and refreshing mechanisms, we can design a wrapper that automatically manages token acquisition for a long-running client that periodically fetches data. In the following example, the client polls for a new Carbon Dioxide reading from a sensor every minute and logs any new readings to the console:
import os
import time
from datetime import datetime
from typing import Callable, TypeVar
T = TypeVar("T") # To allow linter to recognise return types from API wrapper
import httpx
import jwt # pyJWT
import openapi_client
from openapi_client.models.var_name import VarName
from openapi_client.rest import ApiException
def check_access_token_expired(token: str | None) -> bool:
if token is None:
return True
unverified_claims = jwt.decode(token, options={"verify_signature": False})
exp = unverified_claims.get("exp", 0)
if exp > (time.time() + 10): # 10 seconds margin
return False # Token is not expired
return True # Token expired or will expire in the next 10s
def refresh_oauth2_token(token_url: str, client_id: str, client_secret: str, refresh_token: str):
data = {
"grant_type": "refresh_token",
"client_id": client_id,
"client_secret": client_secret,
"refresh_token": refresh_token,
}
response = httpx.post(token_url, data=data)
if response.status_code == 200:
token_data = response.json()
access_token = token_data["access_token"]
return access_token
else:
raise Exception(f"Failed to refresh OAuth2 token: {response.text}")
class OAuthAPI:
def __init__(
self, host: str, token_url: str, client_id: str, client_secret: str, refresh_token: str
):
self.configuration = openapi_client.Configuration(host=host)
self.token_url = token_url
self.client_id = client_id
self.client_secret = client_secret
self.refresh_token = refresh_token
self.access_token = None
def refresh_access_token(self):
self.access_token = refresh_oauth2_token(
self.token_url, self.client_id, self.client_secret, self.refresh_token
)
self.configuration.access_token = self.access_token
def call(self, api_call: Callable[..., T], *args, **kwargs) -> T:
if check_access_token_expired(self.access_token):
self.refresh_access_token()
try:
with openapi_client.ApiClient(self.configuration) as api_client:
return api_call(api_client, *args, **kwargs)
except ApiException as e:
if e.status == 401: # Unauthorized, access token might have expired
self.refresh_access_token()
with openapi_client.ApiClient(self.configuration) as api_client:
return api_call(api_client, *args, **kwargs)
else:
raise e
# Initialize OAuthAPI instance
host = "https://apiv2.lightfi.io"
token_url = "https://lightfiv2.auth.eu-west-2.amazoncognito.com/oauth2/token"
client_id = os.environ["CLIENT_ID"]
client_secret = os.environ["CLIENT_SECRET"]
refresh_token = os.environ["REFRESH_TOKEN"] # Remember to keep token up to date as required for your client settings
api = OAuthAPI(host, token_url, client_id, client_secret, refresh_token)
def get_sensor_latest_data(api_client, sensor_id):
api_instance = openapi_client.DefaultApi(api_client)
return api_instance.read_latest_sensor_information_sensors_sensor_id_get(
sensor_id, project=["latest", "value"]
)
sensor_id = "AQ2-FE753C0D7ADF"
timestamp, timestamp_last = None, None
value = None
while True:
api_response = api.call(get_sensor_latest_data, sensor_id)
if api_response.data is not None:
for datum in api_response.data:
if datum.var_name == VarName.CO2PPM:
if datum.time is not None and datum.value is not None:
timestamp = datetime.fromtimestamp(datum.time).strftime("%Y-%m-%d %H:%M:%S")
value = datum.value
if timestamp != timestamp_last:
print(f"{timestamp} CO2 level: {value} ppm")
timestamp_last = timestamp
time.sleep(60)
Typescript Example API Client
Install OpenAPI generator
See instructions from OpenAPITools
Generate a client
An API client for typescript can be generated using the following example command:
openapi-generator-cli generate -i https://apiv2.lightfi.io/openapi.json --generator-name typescript-axios -o src/services/api
Client Script
Necessary variables
API_URL= https://apiv2.lightfi.io
API_AUTH_TOKEN_URL= https://lightfiv2.auth.eu-west-2.amazoncognito.com/oauth2/token
API_TOKEN_CLIENT_ID= {{ To be accessed via administration }}
API_TOKEN_CLIENT_SECRET = {{{{ To be accessed via administration }}}}
API_REFRESH_TOKEN = {{ See documentation for how to obtain }}
Get Refresh Token
To obtain the refresh token see the documentation examples for Postman or python here.
Get Access Token
After generating refresh token. Access token can be generated using following function:
const getAccessToken = async () => {
const accessToken = await fetch(process.env.API_AUTH_TOKEN_URL, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "refresh_token",
client_id: API_TOKEN_CLIENT_ID,
client_secret: API_TOKEN_CLIENT_SECRET,
refresh_token: AUTHORIZATION_REFRESH_TOKEN,
}),
})
.then((response) => response.json())
.then((data) => data.access_token);
return accessToken;
};
[ Note: For continuously running application, accessToken
expiry is also available within the response json. It can be stored as variable and generate accessToken only upon expiration]
Generated Access token can be finally used to create api config and perform API calls
const apiConfig = async () =>
new Configuration({
basePath: API_URL,
accessToken: await getAccessToken(),
});
const apiService = async () => new DefaultApi(await apiConfig());
Example API Call
For instance to receive location properties from api call: