Skip to content

Instantly share code, notes, and snippets.

@averagehuman
Created February 27, 2018 10:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save averagehuman/c66e95ff01f9b8a3473970649c228c2c to your computer and use it in GitHub Desktop.
Save averagehuman/c66e95ff01f9b8a3473970649c228c2c to your computer and use it in GitHub Desktop.
Register an IoT device with IBM Watson IoT using curl
#!/bin/bash
command -v jq >/dev/null 2>&1 || {
echo >&2 "ERROR: jq is not installed. This is required for parsing json responses."; exit 1;
}
if [ $(id -u) != 0 ] || [ ! -w /etc ]; then
echo "ERROR: Requires root privileges (or write permissions to /etc). Try again with sudo."
exit 1
fi
function usage_and_exit() {
cat <<EOF
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Register an IoT device with the IBM Watson IoT service.
If no specific device id is given, then the identifier in /etc/machine-id is
used if that file exists, otherwise a hash is generated from the concatenation
of all the mac addresses of any network interface returned by ifconfig.
Usage
-----
ibm-iot-register-device <orgId> <deviceType> <deviceTypeDescription> [deviceId]
Example
-------
ibm-iot-register-device "skynet" "coffee-maker" "A self-aware espresso machine."
Api Credentials
---------------
The script calls the IBM IoT api via curl and this requires valid api
credentials to be passed in each request. These credentials must be in file:
/etc/watson/<orgId>/.credentials
This should contain variables 'apiKey' and 'apiToken' as follows:
apiKey="x-yyyyyy-zzzzz"
apiToken="not-a-secret"
Device Config
-------------
If successful, a configuration file for the device is written to:
/etc/watson/<orgId>/devices/<deviceType>/<deviceId>.cfg
This configuration file is suitable for use by ibmiotf, the ibm-watson-iot
python client.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
EOF
exit 1
}
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# Setup
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
orgId="$1"
deviceType="$2"
deviceTypeDesc="$3"
deviceId="$4"
deviceConfigFile=
credentials=
apiKey=
apiToken=
rootUrl=
addDeviceTypeUrl=
addDeviceUrl=
[[ -n "$orgId" ]] || usage_and_exit
[[ -n "$deviceType" ]] || usage_and_exit
[[ -n "$deviceTypeDesc" ]] || usage_and_exit
# If no fourth parameter was passed, determine a unique device id from the system
[[ -n "$deviceId" ]] || deviceId="$(grep -o -m 1 '^.\+' /etc/machine-id)"
[[ -n "$deviceId" ]] || deviceId="$(ifconfig | grep -oP 'HWaddr \K.*' | sed 's/://g' | sha256sum | cut -c -24)"
[[ -n "$deviceId" ]] || { echo "ERROR: couldn't determine the device id.";exit 1; }
deviceConfigFile="/etc/watson/$orgId/devices/$deviceType/$deviceId.cfg"
credentials="/etc/watson/$orgId/.credentials"
# Don't clobber existing config file
[[ ! -e $deviceConfigFile ]] || { echo "ERROR: a configuration already exists for this device type ($deviceConfigFile)";exit 1; }
# Credentials file must exist and contain the required fields
[[ -s $credentials ]] || { echo "ERROR: credentials file does not exist or is empty ($credentials).";exit 1; }
apiKey=$(grep -Po -m 1 '(?<=^apiKey=).*$' "$credentials" | sed "s/[\"\']//g")
apiToken=$(grep -Po -m 1 '(?<=^apiToken=).*$' "$credentials" | sed "s/[\"\']//g")
[[ -n "$apiKey" ]] || { echo "ERROR: missing credentials field - apiKey"; exit 1; }
[[ -n "$apiToken" ]] || { echo "ERROR: missing credentials field - apiToken"; exit 1;}
# Construct api endpoints
rootUrl="https://$orgId.internetofthings.ibmcloud.com/api/v0002"
addDeviceTypeUrl="$rootUrl/device/types"
addDeviceUrl="$rootUrl/bulk/devices/add"
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# Create Device Type
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
payload='{
"id": "'"$deviceType"'",
"description": "'"$deviceTypeDesc"'",
"classId": "Device"
}'
statusCode=$(curl -X "POST" \
-H "Content-Type: application/json" \
-u "$apiKey:$apiToken" \
-d "$payload" \
-s \
-o /dev/null \
-w "%{http_code}" \
$addDeviceTypeUrl)
if [ $statusCode = "201" ]; then
echo "Created device type $deviceType."
elif [ $statusCode = "409" ]; then
echo "Device type $deviceType already exists. Continuing."
else
echo "ERROR: Unexpected error when creating device type. Error status was $statusCode."
exit 1
fi
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# Create Device
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
payload='[
{
"typeId": "'"$deviceType"'",
"deviceId": "'"$deviceId"'"
}
]'
response=$(curl -X "POST" \
-H "Content-Type: application/json" \
-u "$apiKey:$apiToken" \
-d "$payload" \
-s \
$addDeviceUrl)
# The expected response body is a json list with one item:
#
# [{"typeId":"screenly-server","deviceId":"test","success":true,"authToken":"olc6ChFpuhEz"}]
#
# Response if a device of that type and id already exists:
#
# [{"typeId":"screenly-server","deviceId":"test","success":false}]
#
# Parse the response with jq in order to get success status and auth token.
success=$(echo $response | jq '.[0].success')
authToken=$(echo $response | jq '.[0].authToken' | sed 's/"//g')
if [[ $success = "true" && $authToken != "null" ]]; then
mkdir -p $(dirname $deviceConfigFile)
(cat <<EOF
[device]
domain=internetofthings.ibmcloud.com
port=8883
org=$orgId
type=$deviceType
id=$deviceId
auth-method=token
auth-token=$authToken
clean-session=true
EOF
) > $deviceConfigFile && echo "SUCCESS: wrote config file for device - $deviceConfigFile"
else
echo "ERROR: Unable to create device $orgId:$deviceType:$deviceId. It may already exist."
exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment