How to use IoT API with curl

IoT API with curl

In this tutorial we will use the API through the well known utility curl. This will show you how to create Hubs and Devices, as well as more advanced features of the Scaleway Elements IoT Hub: Hub Events and Routes.

The API reference is here: IoT API

Requirements: You have:

  • created an account and are logged into console.scaleway.com
  • installed curl on your local computer
  • mosquitto-clients (mqtt client), and jq (json parsing tool) installed on your computer to follow this guide.

Preliminary steps

You will need to 2 things to get started:

  • A token, which is an API key. You can get/create them from your Scaleway console, under the Account > Credentials page. For more information, see How to generate an API token.
  • Your organization ID.

Set the following variables from a terminal on your local computer:

IOT_API="https://api.scaleway.com/iot/v1/regions/fr-par"
SCW_TOKEN="<your scaleway token secret here>"
SCW_PROJECT="<your project ID here>"

Getting started with IoT Hub

Let’s learn how to create your first IoT Hub with Scaleway.

Set up the hub

The Hub creation is done through a REST endpoint. To call it, you’ll need to provide:

  • Your Organization ID.
  • A name. The name is not important for this tutorial.
  • A product plan. The product plan is not important for this tutorial.

1 . Save the output to hub.json file to make it easier later:

curl -sS -H "X-Auth-Token: $SCW_TOKEN" -d '{
    "project_id": "'$SCW_PROJECT'",
    "name": "my_first_hub",
    "product_plan": "plan_dedicated"
  }' $IOT_API/hubs > hub.json

jq < hub.json

hub.json will contain something like:

{
  "region": "fr-par",
  "id": "035eb275-6ee7-40a2-b68a-d84b84cc236e",
  "organization_id": "<your organization ID>",
  "project_id": "<your project ID>",
  "name": "my_first_hub",
  "status": "enabling",
  "product_plan": "plan_dedicated",
  "endpoint": "iot.fr-par.scw.cloud",
  "created_at": "2021-04-26T11:39:53.927Z",
  "updated_at": "2021-04-26T11:39:53.927Z",
  "enabled": true,
  "device_count": 0,
  "connected_device_count": 0,
  "disable_events": false,
  "events_topic_prefix": "$SCW/events",
  "enable_device_auto_provisioning": false,
  "has_custom_ca": false
}

2 . Poll the hub status until it is ready:

curl -sS -H "X-Auth-Token: $SCW_TOKEN" $IOT_API/hubs/$(jq -r '.id' hub.json) | jq -r '.status'

At some point, the status will switch to ready.

Set up the devices

Now we need to create 2 devices. You just need to provide:

  • Your Organization ID.
  • The Hub ID. This is the "id" field from the JSON response received while creating a hub.
  • A name. Again, the name is not important for this tutorial.

Note: As this tutorial aims to be simple and straightforward, the following commands are allowing the device to connect using insecure protocols, such as plain text MQTT or MQTTs without mutual authentication. In production, you should Deny Insecure connections to have the highest level of security. This is done by setting the field allow_insecure to false.

1 . Save the response to a file so we can use the fields later.

curl -sS -H "X-Auth-Token: $SCW_TOKEN" -d '{
    "hub_id": "'$(jq -r '.id' hub.json)'",
    "name": "my_first_device",
    "allow_insecure": true
  }' $IOT_API/devices > dev1.json

jq < dev1.json

The file dev1.json should contain something like:

{
  "device": {
    "id": "f926489b-92d4-4572-bc96-dfad83c3db4b",
    "name": "my_first_device",
    "status": "enabled",
    "hub_id": "035eb275-6ee7-40a2-b68a-d84b84cc236e",
    "created_at": "2021-04-26T11:42:43.552Z",
    "updated_at": "2021-04-26T11:42:43.552Z",
    "allow_insecure": true,
    "last_activity_at": "1970-01-01T00:00:00Z",
    "is_connected": false,
    "message_filters": {
      "publish": {
        "policy": "reject",
        "topics": []
      },
      "subscribe": {
        "policy": "reject",
        "topics": []
      }
    },
    "allow_multiple_connections": false,
    "description": ""
  },
  "certificate": {
    "crt": "<certificate here>",
    "key": "<certificate key here>"
  }
}

2 . Create a second device:

curl -sS -H "X-Auth-Token: $SCW_TOKEN" -d '{
    "hub_id": "'$(jq -r '.id' hub.json)'",
    "name": "my_second_device",
    "allow_insecure": true
  }' $IOT_API/devices > dev2.json

jq < dev2.json

Subscribe and Publish

Now that everything is set up, let’s simulate 2 devices and send data.

1 . Set up the subscriber:

# In one terminal
mosquitto_sub \
  -h $(jq -r '.endpoint' hub.json) \
  -i $(jq -r '.device.id' dev1.json) \
  -t mytopic/mysubtopic

2 . Run the publisher:

# In another terminal
mosquitto_pub \
  -h $(jq -r '.endpoint' hub.json) \
  -i $(jq -r '.device.id' dev2.json) \
  -t mytopic/mysubtopic \
  -m 'Hello, world!'

You should see the subscriber receive the Hello, world! message.

The secure way

If you require security, you can also connect your device to the Hub using TLS mutual authentication. With this method, the Hub can check the device’s identity, and the device can check the Hub’s identity.

Note: It is possible to connect to the Hub using TLS but without Mutual authentication. In this case, the device certificates are not needed as the Hub does not need to check the device identity. But the Hub certificate will still be needed as your client must check the hub’s identity.

1 . Download the IoT Hub CA:

curl -sS -O https://iot.s3.nl-ams.scw.cloud/certificates/fr-par/iot-hub-ca.pem
sha1sum iot-hub-ca.pem
# 13cf3e59ed52d4c4b6bc249e85539d5fd5d572fb  iot-hub-ca.pem

2 .Extract the certificates from the device JSON files, so that the mosquitto clients may use them:

jq -r '.certificate.crt' dev1.json > dev1.crt
jq -r '.certificate.key' dev1.json > dev1.key
jq -r '.certificate.crt' dev2.json > dev2.crt
jq -r '.certificate.key' dev2.json > dev2.key

3 . Run the same test as before, but with the added security:

# In one terminal
mosquitto_sub \
  -h $(jq -r '.endpoint' hub.json) -p 8883 \
  --cert dev1.crt --key dev1.key --cafile iot-hub-ca.pem \
  -i $(jq -r '.device.id' dev1.json) \
  -t mytopic/mysubtopic
# In another terminal
mosquitto_pub \
  -h $(jq -r '.endpoint' hub.json) -p 8883 \
  --cert dev2.crt --key dev2.key --cafile iot-hub-ca.pem \
  -i $(jq -r '.device.id' dev2.json) \
  -t mytopic/mysubtopic \
  -m 'Hello, SECURE world!'

Note: You can mix MQTT and MQTTs clients on the same hub.

Further reading

You can harness the real power of MQTT Pub/Sub with a few more lines of reading.

IoT Hub Events

Device information, such as events or errors, are reported on your Hub under the default topic prefix $SCW/events. For more information on Hub Events, you can read the IoT Hub events documentation.

In the following example we will trigger an event by trying to connect without security on a device where security is required.

1 . Connect to previously created dev1 device as events consumer (we want only error events)

# In one terminal
mosquitto_sub \
  -h $(jq -r '.endpoint' hub.json) -p 1883 \
  -i $(jq -r '.device.id' dev1.json) \
  -t '$SCW/events/error/#'

2 . Create a secured-only device and try to connect it in insecure mode:

curl -sS -H "X-Auth-Token: $SCW_TOKEN" -d '{
    "hub_id": "'$(jq -r '.id' hub.json)'",
    "name": "my_secured_device",
    "allow_insecure": false
  }' $IOT_API/devices > secure-dev.json

jq < secure-dev.json
# In another terminal
mosquitto_pub \
  -h $(jq -r '.endpoint' hub.json) -p 1883 \
  -i $(jq -r '.device.id' secure-dev.json) \
  -t mytopic/mysubtopic \
  -m 'This should fail'

The device dev1 will receive the following message, on the topic $SCW/events/device/<secure-dev-id>/error:

{
  "time": "2021-04-26T11:45:42Z",
  "severity": "error",
  "object-type": "device",
  "object-id": "dev3-id",
  "msg":"connection refused because this device requires mutual TLS authentication",
  "packet":"CONNECT: dup: false qos: 0 retain: false rLength: 48"
}

Routes

Routes are integrations with the Scaleway ecosystem: they can forward MQTT messages to Scaleway services.

The documentation is available at the following link: IoT Hub Routes

S3 Route

The S3 route allows you to put the payload of MQTT messages directly into Scaleway’s Object Storage.

Important: This section is an addition to the “Getting started with IoT Hub” above, make sure to follow it first!

As a prerequisite, you should have s3cmd installed and configured for Scaleway.

1 . Run the following commands in a terminal on your computer:

BUCKET="my-bucket-$RANDOM" # Buckets are globally unique, suffix with a random number
REGION="fr-par"
PREFIX="iot/messages"
# Create the bucket
s3cmd mb --region "$REGION" "s3://$BUCKET"
# Grant write access to IoT Hub S3 Route Service to your bucket
s3cmd setacl --region "$REGION" "s3://$BUCKET" --acl-grant=write:555c69c3-87d0-4bf8-80f1-99a2f757d031:555c69c3-87d0-4bf8-80f1-99a2f757d031
# Create the IoT Hub S3 Route
curl -sS -H "X-Auth-Token: $SCW_TOKEN" -d '{
    "hub_id": "'$(jq -r '.id' hub.json)'",
    "name": "my-s3-route",
    "topic": "hello/world",
    "s3_config": {
      "bucket_region": "'$REGION'",
      "bucket_name": "'$BUCKET'",
      "object_prefix": "'$PREFIX'",
      "strategy": "per_topic"
    }
  }' $IOT_API/routes | jq

The output will contain something like:

{
  "id": "112f9719-9c3c-4722-a6cb-ae6fc1278176",
  "name": "my-s3-route",
  "hub_id": "035eb275-6ee7-40a2-b68a-d84b84cc236e",
  "topic": "hello/world",
  "type": "s3",
  "created_at": "2021-04-26T11:51:00.713Z",
  "updated_at": "2021-04-26T11:51:00.713Z",
  "s3_config": {
    "bucket_region": "fr-par",
    "bucket_name": "my-bucket-13469",
    "object_prefix": "iot/messages",
    "strategy": "per_topic"
  }
}

2 . Publish and see the result.

sleep 5 # wait a little for the route to start
mosquitto_pub \
  -h $(jq -r '.endpoint' hub.json) \
  -i $(jq -r '.device.id' dev2.json) \
  -t hello/world \
  -m 'Hello, world!'

3 . An object iot/messages/hello/world should now be stored in your bucket with “hello world” as contents. Retrieve it using s3cmd

s3cmd get --region "$REGION" "s3://$BUCKET/$PREFIX/hello/world"
cat world

Database Route

Database route allows you to store messages in your database.

When creating such a route for one of you hubs, you need to specify a topic (wildcards are allowed), a database (with valid credentials) and a query to execute. That query may contains $TOPIC and $PAYLOAD variables.

The route will subscribe on this hub to this topic, and execute the query onto the given database for each received message:

  • First, $TOPIC and $PAYLOAD are replaced with the topic and payload of the received MQTT message,
  • Then the generated query is executed

NOTE: The topic database field must be a of text type, and payload a bytea

For now, we support only PostgreSQL database system. You can use a Scaleway Database instance, or any other PostgreSQL instance publicly accessible.

Let's practice

Important: This section is an addition to the “Getting started with IoT Hub” above, make sure you follow it first!

As a prerequisite, you must have a working PostgreSQL database, with valid credentials (username and password).

1 . Set your database credentials anc create the target database on your PostgreSQL host:

# Database settings
DBHOST=<your db host>
DBPORT=<your db port>
DBNAME=<your db name>
DBUSER=<your db user>
DBPASS=<your db password>

# Create the target database table
psql -h $DBHOST --port $DBPORT -U $DBUSER -d $DBNAME -c '
  CREATE TABLE messages (
    time timestamp,
    topic text,
    payload bytea
  )
'

# Create the IoT Hub Database Route
# The query will insert message topic and payload with current timestamp
curl -sS -H "X-Auth-Token: $SCW_TOKEN" -d '{
    "name": "my first database route",
    "hub_id": "'$(jq -r '.id' hub.json)'",
    "topic": "hello/world",
    "db_config": {
      "host": "'$DBHOST'",
      "port": '$DBPORT',
      "dbname": "'$DBNAME'",
      "username": "'$DBUSER'",
      "password": "'$DBPASS'",
      "query": "INSERT INTO messages VALUES (NOW(), $TOPIC, $PAYLOAD)"
    }
  }' $IOT_API/routes | jq

The output will contain something like

{
  "id": "1e0965a5-12f2-4db3-a680-203dfc210e29",
  "name": "my first database route",
  "hub_id": "035eb275-6ee7-40a2-b68a-d84b84cc236e",
  "topic": "hello/world",
  "type": "database",
  "created_at": "2021-04-26T11:54:01.291Z",
  "updated_at": "2021-04-26T11:54:01.291Z",
  "db_config": {
    "host": "127.0.0.1",
    "port": 5432,
    "dbname": "route_tests",
    "username": "jdoe",
    "password": "<your_pass>",
    "query": "INSERT INTO messages VALUES (NOW(), $TOPIC, $PAYLOAD)"
  }
}

2 . Publish a message and check whether it is inserted into the message table.

sleep 5 # wait a little for the route to start
mosquitto_pub \
  -h $(jq -r '.endpoint' hub.json) \
  -i $(jq -r '.device.id' dev2.json) \
  -t hello/world \
  -m 'Hello, world!'
psql -h $DBHOST --port $DBPORT -U $DBUSER -d $DBNAME -c "SELECT * FROM messages"

More advanced examples are available on the database route tips & tricks page.

Rest Route

Important: This section is an addition to the “Getting started with IoT Hub” above, make sure you follow it first!

Rest route allows you call any http(s) endpoint on received MQTT message. You can choose the HTTP verb use to call your REST uri, as well as adding extra headers.

We can see what a rest route would publish on a rest API by simply listening to the port 80 on a public IP.

You can use a Scaleway instance, or any other machine with a public IP address.

1 . Launch the following command on the machine, as root:

nc -p 80 -l

2 . Define a variable with the public IP address of your server:

RESTHOST=<the_public_ip_address>

3 . Create the route:

# Create the IoT Hub Rest Route
curl -sS -H "X-Auth-Token: $SCW_TOKEN" -d '{
    "name": "my first rest route",
    "hub_id": "'$(jq -r '.id' hub.json)'",
    "topic": "hello/world",
    "rest_config": {
      "verb": "post",
      "uri": "http://'$RESTHOST'/",
      "headers": {
        "X-My-Header": "Tutorial"
      }
    }
  }' $IOT_API/routes | jq

4 . Publish a message and check whether it triggers a request on the server:

sleep 5 # wait a little for the route to start
mosquitto_pub \
  -h $(jq -r '.endpoint' hub.json) \
  -i $(jq -r '.device.id' dev2.json) \
  -t hello/world \
  -m 'Hello, world!'

On your server the above nc output should be:

POST / HTTP/1.1
Host: <the_public_ip_address>
User-Agent: Go-http-client/1.1
Content-Length: 13
X-Mqtt-Retain: false
X-Mqtt-Topic: hello/world
X-My-Header: Tutorial
Accept-Encoding: gzip

Hello, world!

Learn more about Scaleway IoT Hub, discover how to add Devices to the hub, check the IoT Hub metrics or get started in a few clicks with the IoT Hub Kickstarts.

Discover the Cloud That Makes Sense