Currently working on getting an API on Ignition to communicate with SmartCover - a data tracking package for manhole covers.
(Refer to this thread ---->SmartCover API with Ignition - #3 by nruimveld)
I see that there is Python code for an API with a token that would be applicable to Ignition. But I am not sure where or how I would insert this Python code on Ignition. Any insight? See below for code.
import requests
import csv
# variable to store the location id of interest
location_id = 'LOCATION_ID'
# variable to store the JWT token
token = 'TOKEN'
# set the authorization header
headers = {'Authorization': 'Bearer ' + token}
# set the request parameters
params = {
'location': location_id,
'start_time': '2020-07-01 00:00',
'end_time': '2020-07-10 00:00',
'data_type': '2' }
# make the GET request
response = requests.get('https://www.mysmartcover.com/api/locations/data.php',
params = params, headers = headers)
# store the response code
response_code = response.json()['response_code']
# store the data from the response
data = response.json()['data'][0]
# write the output to a CSV file
with open('output.csv', mode = 'w') as output_file:
output_writer = csv.writer(output_file, delimiter = ',')
output_writer.writerow(['datetime', 'level']) # write the column headers
for row in data:
output_writer.writerow(row)
In your case, importing the requests library will probably be unnecessary because of the built-in system.net.httpClient, system.net.httpGet and system.net.httpPost functions.
If you already have a token, you should be able to do something like this (off the top of my head):
import csv
# variable to store the location id of interest
location_id = 'LOCATION_ID'
# variable to store the JWT token
token = 'TOKEN'
# set the authorization header
headers = {'Authorization': 'Bearer ' + token}
# set the request parameters
params = {
'location': location_id,
'start_time': '2020-07-01 00:00',
'end_time': '2020-07-10 00:00',
'data_type': '2' }
# make the GET request
response = system.net.httpClient().get('https://www.mysmartcover.com/api/locations/data.php', params = params, headers = headers)
# store the response code
response_code = response.json['response_code']
# store the data from the response
data = response.json['data'][0]
# write the output to a CSV file
with open('output.csv', mode = 'w') as output_file:
output_writer = csv.writer(output_file, delimiter = ',')
output_writer.writerow(['datetime', 'level']) # write the column headers
for row in data:
output_writer.writerow(row)
I should specify better; I don't quite understand where to implement this code onto Ignition. I'm unsure what setting, binding, etc. I need to put this code into.
Do you actually want the output in a CSV file? I'd recommend a dataset tag, probably. Poll in a scheduled script as Nick recommends, build a dataset, write it to a tag. Then it's easy to pull into Vision or Perspective via a local binding.
I think you have a great idea. However, I am struggling with defining the 'LOCATION_ID' variable in the Python script. There are some insights within the API documentation provided by SmartCover, however, I am having a hard time making sense of it. Any ideas to define/quantify location id within that Python script?
You'll have to run their list method (manually, once) to determine the ID that corresponds to whatever unit(s) you're trying to retrieve historical data for; see page 5 where they're using curl (a commonly available tool to fetch from a URL via the command line).
I didn't think it was possible to use cURL on Windows, or within the schedule script. Maybe I am misunderstanding?
Is it possible to rewrite it in Python and add into the existing script?
Ah! I see, that was mentioned earlier in the thread.
I've been trying to use the function in the line as below, but seem to keep getting a 'httpClient not defined' error. Is there another way I should be writing this?
The full name is system.net.httpClient(), provided by Ignition. It is typically instantiated/configured in a project library script into a top level variable, and that variable (often client) is then used for the actual API requests. Take a closer look at the sample (Paul's) above.
(And you can search this forum for "system.net.httpClient" for many more examples.)
For the task of fetching the list of locations, you can use literally any HTTP client. You could try one with a GUI like Postman, Insomnia, etc, or download cURL (it's definitely available for Windows, if not already on your system), or use one built in to a 'real' IDE like IntelliJ/PyCharm/VSCode/etc, or use the script console in Ignition. Hell, you could do it from your web browser console using Javascript and the fetch API.
The point is that HTTP/REST is pretty much a base lingua franca for communication between services and it doesn't matter what you have, most everything can talk to it. My read of the PDF you linked before suggests that SmartCover is doing something like this:
Each 'user' account has X meters associated with it. Once you get your (extremely long-lived...) token, when you ask the locations API, it's going to give you that list of X devices. Each of those have an ID, which is probably literally the same ID SmartCover is using on their own backend, and so you have to pass that ID into any cover-specific API method you want to call.
So the actual act of fetching that list is probably not something you'll have to repeat very often (only when you buy new covers?) - so it may not be worth writing up a script if you can pull the data you want out manually with a graphical tool. The actual process of talking to the cover, absolutely should be scripted, because you're going to be repeating that often. But a 'semi-automatic' process could be totally sufficient to get you past your current roadblock.
Datasets are an Ignition-specific construct that are designed to hold homogenously-typed columns of data with an arbitrary number of rows, associated with an identifier for the column name. You'll notice that this is basically the same as CSV, which is good for your purposes.
So, your code sample from earlier:
Becomes something like:
headers = ["datetime", "level"]
dsData = []
for row in data:
dsData.append([row['datetime'], row['level']])
dataset = system.dataset.toDataSet(headers, dsData)
system.tag.writeBlocking(["path/to/dataset/tag"], [dataset])
(Exact details will depend on the format of the data you're getting from the API, and you'll probably have to go through extra steps to parse whatever timestamp they're returning into an actual Java date object; see system.date.parse for one possibility.)