Ignition integration with Peplink Incontrol2 API

Hey Guys

Been playing around with Ignition for a week now and absolutely love it.
I’m looking to integrate API calls to Peplink’s InControl2

I can get the calls working on Postman for example

PUT or DELETE
URL - https://api.ic.peplink.com/rest/o/{organization_id}/g/{group_id}/device_tags
(Authentication token) ****************************
(body - raw data)
{data:
{device_ids:[1,10],
tag_names:[allowinternet]}
}

This call allows me to add or delete a tag (not a tag in Ignition) to a device in InControl2’s management which allows for rules to be acted upon e.g. firewall rules change to allow or block internet

Could someone please explain to me how to take a functioning call in Postman and integrate it into a tag in Ignition.
I have already tired following some examples but nothing seams to quite function.

Have a great day!

This thread should help you out
Connecting to Smart Home Devices - Maker - Inductive Automation Forum

Awesome, that’s a great one. Will work my way through it now

I use the same method for the API helper functions I am writing for Ecobee Smart Thermostats.

Peplink do provide a sample shell script.
I think it needs to be completely re-written in python to work in Ignition though.

#!/bin/bash

## Sample script for making IC2 API calls
## Revision 1.3
## Last modified: 2019-06-21
## By Billy Lau, Michael Chan

#
## Please input your InControl 2 OAuth client_id, client_secret and redirect_uri below
## You can optain a set of client ID and secret from your user profile page by clicking 
## your email address on the top of the InControl 2 screen, scrolling to the "Client 
## Application" section, clicking the "New Client" button, and filling in the form.
##
## For the redirect_uri, you may fill in http://www.peplink.com in the above form
## and the redirect_uri variable below
client_id=""
client_secret=""
redirect_uri=""

# For InControl 2, the api_server_prefix is https://api.ic.peplink.com.  
# For InControl appliances, this is https://{SERVER_NAME_HERE}.
api_server_prefix="https://api.ic.peplink.com"

# For InControl 2, set 1 to verify the API service's SSL certificate.
# For InControl appliances without a valid SSL certificate, set this to 0 to ignore the certificate validity.
verify_ssl_cert=1

################# Code for OAuth2 token handling goes below ##################
access_token_file="${HOME}/.access_token"
refresh_token_file="${HOME}/.refresh_token"
tmpfile="/tmp/ic2.tmpfile.$$"
# InControl token endpoint
ic2_token_url="${api_server_prefix}/api/oauth2/token"
# InControl authorization endpoint
ic2_auth_url="${api_server_prefix}/api/oauth2/auth?client_id=${client_id}&response_type=code"

if [ "${client_id}" == "" ] || [ "${client_secret}" == "" ] || [ "${redirect_uri}" == "" ]; then
	echo "Please edit this script and enter Client ID and Client Secret"
	exit 1
fi

# Check if jq has been installed
if ! command -v jq > /dev/null 2>&1 ; then
	echo "Error: Missing jq command."
	echo "Run \"apt install jq\" or \"yum install jq\" to install."
	exit 2
fi

if [ "$verify_ssl_cert" != "0" ]; then
	curl_opt=" -k "
else
	curl_opt=""
fi

function save_tokens() {
	result=$(cat)
	local -n access_token_tmp=$1
	local -n refresh_token_tmp=$2
	access_token_tmp=`echo "${result}"|jq -r .access_token`
	refresh_token_tmp=`echo "${result}"|jq -r .refresh_token`
	expires_in=`echo "${result}"|jq -r .expires_in`
	if [ "${access_token_tmp}" == "" ]; then
		echo "Unable to obtain an access token. Process aborted"
		echo "Returned: $result"
		exit 3
	fi
	# Save the access token and refresh token."
	echo ${access_token_tmp} > ${access_token_file}
	echo ${refresh_token_tmp} > ${refresh_token_file}
	# Set the files' last modified date to the tokens' expiry date
	touch -d @$(( $(date +%s) + expires_in )) ${access_token_file}
	touch -d @$(( $(date +%s) + expires_in + 30*60*60 )) ${refresh_token_file}
}

# If the access token file has not expired
if [ -f ${access_token_file} ] && [ $(stat -c %Y ${access_token_file}) -gt $(date +%s) ]; then
	access_token=$(cat ${access_token_file})
# If the access token is invalid but the refresh token has not expired, acquire new access and refresh tokens
elif [ -f ${refresh_token_file} ] && [ $(stat -c %Y ${refresh_token_file}) -gt $(date +%s) ]; then
	ic2_token_params="client_id=${client_id}&client_secret=${client_secret}&grant_type=refresh_token&refresh_token=$(cat ${refresh_token_file})"
	curl $curl_opt -so $tmpfile -X POST -d "$ic2_token_params" "${ic2_token_url}"
	if ! save_tokens access_token refresh_token < $tmpfile; then
		rm -f $tmpfile
		exit 4
	fi
else
	echo ""
	echo "Start a web browser, visit the following URL and follow the instructions."
	echo ""
	echo "${ic2_auth_url}"
	echo ""
	echo "You will be redirected to $redirect_uri?code=CODE_HERE."
	echo -n "Please enter the 'code' in the redirected URL here: "
	read code

	if [ "${code}" == "" ]; then
		echo "Error: The code is empty.  Process aborted"
		exit 5
	fi

	ic2_token_params="client_id=${client_id}&client_secret=${client_secret}&grant_type=authorization_code&code=${code}&redirect_uri=${redirect_uri}"

	## POST data to token endpoint
	curl $curl_opt -so $tmpfile --data "${ic2_token_params}" "${ic2_token_url}"
	if ! save_tokens access_token refresh_token < $tmpfile; then
		rm -f $tmpfile
		exit 6
	fi
fi
rm -f $tmpfile

##################################################################################
# Application code goes below.  This sample script firstly gets all accessible   #
# organizations. Then for each organization, print its name and list all groups' #
# name under it.                                                                 #
##################################################################################
echo ""
echo "Listing all accessible organizations' group names."
echo ""

curl $curl_opt -so $tmpfile "${api_server_prefix}/rest/o?access_token=${access_token}"
if grep -q Unauthorized $tmpfile ; then
	echo "The saved access token is invalid.  Rerun this script to obtain a new one"
	rm -f ${access_token_file}
	exit 7
fi

for org in $(jq -r '.data[] | @base64' $tmpfile); do
	org_name=$(echo $org |base64 --decode | jq -r '.name')
	org_id=$(echo $org |base64 --decode | jq -r '.id')
	echo "Groups under the organization: $org_name"
	curl $curl_opt -s "${api_server_prefix}/rest/o/$org_id/g?access_token=${access_token}" | \
		jq -r '.data[] | .name'
	echo ""
done
rm -f $tmpfile

Is there a simple way to convert this code or does it need to be completely re-written?

It needs to be re-written in my opinion. I would start with getting your tokens and saving them. To start you can save them into tags, then worry about securely storing them later. I have my API helper functions also save and monitor the expiration time of the token. Once expired I automatically refresh the tokens, so I never have to worry about an expired token during a call. Once you have that, it really isn’t that bad to convert a curl into a system.net.httpPost call.

If you need some more inspiration, when I have my API project finished, I will be posting it to Ignition Exchange where you can download it and use it as a guide.

You can call this shell script from Ignition using system.util.execute() but I don’t think it will do what you need it to this way.

So I can get the following working in postman and PyCharm as a PUT and DELETE but can not get it running in Ignition. Any ideas?

I have tried system.net.httpPut system.net.httpDelete but I keep getting error in the script consul. Any pointers on how to format the information below for it to function in the script editor?

import http.client

import json

conn = http.client.HTTPSConnection("api.ic.peplink.com")

payload = "{data:{device_ids:[31,32],tag_names:[(CREATE_THE_TAG_HERE)]}}"

headers = {

'Authorization': 'Bearer (REPLACE_WITH_BEARER_ID)',

'Content-Type': 'application/json',

'Cookie': 'JSESSIONID=(REPLACE_WITH_COOKIE)'

}

conn.request("DELETE", "/rest/o/(ORG_ID)/device_tags", payload, headers)

res = conn.getresponse()

data = res.read()

print(data.decode("utf-8"))

Thank You