You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

92 lines
2.6 KiB

#
# Name: ttn_storage_api.py
#
# Function:
# Fetch data from TTN, via storage API
#
# Author:
# Terry Moore, MCCI
#
# Use:
# import ttn_storage_api
# ttn_storage_api.sensor_pull_storage(...) -- see docs for args
#
# Or just cut/paste into your script.
#
import subprocess
import json
import pathlib
import re
class Error(Exception):
"""Base class for errors in this module"""
pass
class FetchError(Error):
"""Raised when sensor_pull_storage can't deal with input
Atrributes:
expression -- input expression where error occurred
message -- explanation of the error.
"""
def __init__(self, expression, message):
self.expression = expression
self.message = message
def sensor_pull_storage(appname, accesskey, timestring, *,data_folder = None, ttn_version=3):
"""
Pull data from TTN via the TTN storage API.
appname is the name of the TTN app
accesskey is the full accesskey from ttn. For TTN V3, this is is the
secret that is output when a key is created. For TTN V2, this is
the string from the console, starting with 'ttn-acount-v2.'
timestring indicates amount of data needed, e.g. '100h'.
ttn_version should be 2 or 3; 3 is default.
If data_folder is supplied, it is a string or a Path; it is taken as a directory,
and the name "sensors_lastperiod.json" is appended to form an output file name, and
the data is written to the resulting file, replacing any previous contents.
Otherwise, the data is returned as a Python array (for V3) or a string (for V2).
We've not really tested V2 extensively.
"""
args = [ "curl" ]
if ttn_version == 2:
args += [
"-X", "GET",
"--header", "Accept: application/json",
"--header", f"Authorization: key {accesskey}",
f"https://{appname}.data.thethingsnetwork.org/api/v2/query?last={timestring}"
]
elif ttn_version == 3:
args += [
"-G", f"https://nam1.cloud.thethings.network/api/v3/as/applications/{appname}/packages/storage/uplink_message",
"--header", f"Authorization: Bearer {accesskey}",
"--header", "Accept: text/event-stream",
"-d", f"last={timestring}",
"-d", "field_mask=up.uplink_message.decoded_payload",
]
else:
raise FetchError(f"Illegal ttn_version (not 2 or 3)")
# if the user supplied a data_folder, than tack on the args.
# list1 += list2 syntax means "append each element of list2 to list 1"
# pathlib.Path allows
if data_folder != None:
args += [ "-o", pathlib.Path(data_folder, "sensors_lastperiod.json") ]
result = subprocess.run(
args, shell=False, check=True, capture_output=True
)
sresult = result.stdout
if ttn_version == 3:
return list(map(json.loads, re.sub(r'\n+', '\n', sresult.decode()).splitlines()))
else:
return sresult