The Things Network Client Part1

Basic connectivity

Over the last few months I have been using the community version of The Things Network(TTN) to test my LoRaWAN RakWireless RAK811 EVB based nanoFramework and TinyCLR clients.

As I was manually configuring TTN clients references to an application programming interface(API) caught my attention. In my day job I use tools from SmartBear and RicoSuter to generate .Net Core clients (for APSP.NET Core Web APIs I have build) from their OpenAPI descriptions.

The first step was to download the API swagger from The Things Network Github repository.

Things Network Github repository

I then used nSwagStudio to generate a C# client from a local copy of the API swagger (in the future I will use download the swagger and use the command line tools).

nSwag User Interface

At this point I had a basic client for the TTN network stack API which lacked support for the TTN security model etc. After looking at the TTN API documentation I figured out I need to add a header which contained an API Key from the TTN application configuration.

namespace TheThingsNetwork.API
{
	public partial class EndDeviceRegistryClient
	{
		public string ApiKey { set; get; }

		partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url)
		{
			if (!client.DefaultRequestHeaders.Contains("Authorization"))
			{
				client.DefaultRequestHeaders.Add("Authorization", $"Bearer {ApiKey}");
			}
		}
	}
}

In the TTN console on the overview page for my application I created an Access Key.

I then added some attributes to one of my devices so I had some addition device configuration data to display(I figured these could be useful for Azure IoT Hub configuration parameters etc. more about this later..)

Basic Device configuration in TTN Enterprise

I built a nasty console application which displayed some basic device configuration information to confirm I could authenticate and enumerate.

//---------------------------------------------------------------------------------
// Copyright (c) August 2020, devMobile Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SECURITY_ANONYMISE
//---------------------------------------------------------------------------------
namespace TheThingsNetwork.EndDeviceClient
{
	using System;
	using System.Collections.Generic;
	using System.Net.Http;
	using TheThingsNetwork.API;

	class Program
	{
		static void Main(string[] args)
		{
			Console.WriteLine("TheThingsNetwork.EndDeviceClient starting");

			if (args.Length != 3)
			{
				Console.WriteLine("EndDeviceClient <baseURL> <applicationId> <apiKey>");
				Console.WriteLine("Press <enter> to exit");
				Console.ReadLine();
				return;
			}
			string baseUrl = args[0];
#if !SECURITY_ANONYMISE
			Console.WriteLine($"baseURL: {baseUrl}");
#endif
			string applicationId = args[1];
#if !SECURITY_ANONYMISE
			Console.WriteLine($"applicationId: {applicationId}");
#endif
			string apiKey = args[2];
#if !SECURITY_ANONYMISE
			Console.WriteLine($"apiKey: {apiKey}");
			Console.WriteLine();
#endif

			using (HttpClient httpClient = new HttpClient())
			{
				EndDeviceRegistryClient endDeviceRegistryClient = new EndDeviceRegistryClient(baseUrl, httpClient);
				endDeviceRegistryClient.ApiKey = apiKey;

				try
				{
					V3EndDevices endDevices = endDeviceRegistryClient.ListAsync(applicationId).GetAwaiter().GetResult();

					foreach (V3EndDevice v3EndDevice in endDevices.End_devices)
					{
#if SECURITY_ANONYMISE
						v3EndDevice.Ids.Dev_eui[7] = 0x0;
						v3EndDevice.Ids.Dev_eui[8] = 0x0;
						v3EndDevice.Ids.Dev_eui[9] = 0x0;
						v3EndDevice.Ids.Dev_eui[10] = 0x0;
						v3EndDevice.Ids.Dev_eui[11] = 0x0;
#endif
						Console.WriteLine($"Device ID:{v3EndDevice.Ids.Device_id} DevEUI:{Convert.ToBase64String(v3EndDevice.Ids.Dev_eui)}");
						Console.WriteLine($"   CreatedAt: {v3EndDevice.Created_at:dd-MM-yy HH:mm:ss} UpdatedAt: {v3EndDevice.Updated_at:dd-MM-yy HH:mm:ss}");

						string[] fieldMaskPaths = { "name", "description", "attributes" };

						var endDevice = endDeviceRegistryClient.GetAsync(applicationId, v3EndDevice.Ids.Device_id, field_mask_paths: fieldMaskPaths).GetAwaiter().GetResult();

						Console.WriteLine($"   Name: {endDevice.Name}");
						Console.WriteLine($"   Description: {endDevice.Description}");
						if (endDevice.Attributes != null)
						{
							foreach (KeyValuePair<string, string> attribute in endDevice.Attributes)
							{
								Console.WriteLine($"      Key: {attribute.Key} Name: {attribute.Value}");
							}
						}
						Console.WriteLine();
					}
				}
				catch (Exception ex)
				{
					Console.WriteLine(ex.Message);
				}

				Console.WriteLine("Press <enter> to exit");
				Console.ReadLine();
			}
		}
	}
}

I added some code so I could anonymise the displayed configuration so I could take screen grabs without revealing any sensitive information.

TTN API Client V1

Initially I struggled with versioning issues as the TTN community network is running V2 and the github repository was for V3. I approached TTN and they gave me access to a “limited” account on the enterprise network.

I also struggled with the number of blank fields in responses and spent some time learning GO (the programming language TTN is built with) to figure out how to use fieldMaskPaths etc.

string[] fieldMaskPaths = { "name", "description", "attributes" };

V3EndDevice endDevice = endDeviceRegistryClient.GetAsync(applicationId, v3EndDevice.Ids.Device_id, field_mask_paths: fieldMaskPaths).GetAwaiter().GetResult();

Overall things went pretty well but I expect to basic GO programing skills one this project is finished.

As hinted at earlier in this post the end goal of this project is to build an Azure IoT hub integration.