ttnsdk

package module
v0.0.0-...-5bae20a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 21, 2019 License: MIT Imports: 23 Imported by: 7

README

The Things Network Go SDK

Build Status Coverage Status GoDoc

The Things Network

Usage

Assuming you're working on a project github.com/your-username/your-project:

cd your-project
go mod init github.com/your-username/your-project
go get github.com/TheThingsNetwork/go-app-sdk

See the examples on GoDoc.

License

Source code for The Things Network is released under the MIT License, which can be found in the LICENSE file. A list of authors can be found in the AUTHORS file.

Documentation

Overview

Package ttnsdk implements the Go SDK for The Things Network.

This package wraps The Things Network's application and device management APIs (github.com/TheThingsNetwork/api) and the publish/subscribe API (github.com/TheThingsNetwork/ttn/mqtt). It works with the Discovery Server to retrieve the addresses of the Handler and MQTT server.

Example
package main

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"

	ttnsdk "github.com/TheThingsNetwork/go-app-sdk"
	ttnlog "github.com/TheThingsNetwork/go-utils/log"
	"github.com/TheThingsNetwork/go-utils/log/apex"
	"github.com/TheThingsNetwork/go-utils/random"
	"github.com/TheThingsNetwork/ttn/core/types"
)

const (
	sdkClientName = "my-amazing-app"
)

func main() {
	log := apex.Stdout() // We use a cli logger at Stdout
	log.MustParseLevel("debug")
	ttnlog.Set(log) // Set the logger as default for TTN

	// We get the application ID and application access key from the environment
	appID := os.Getenv("TTN_APP_ID")
	appAccessKey := os.Getenv("TTN_APP_ACCESS_KEY")

	// Create a new SDK configuration for the public community network
	config := ttnsdk.NewCommunityConfig(sdkClientName)
	config.ClientVersion = "2.0.5" // The version of the application

	// If you connect to a private network that does not use trusted certificates on the Discovery Server
	// (from Let's Encrypt for example), you have to manually trust the certificates. If you use the public community
	// network, you can just delete the next code block.
	if caCert := os.Getenv("TTN_CA_CERT"); caCert != "" {
		config.TLSConfig = new(tls.Config)
		certBytes, err := ioutil.ReadFile(caCert)
		if err != nil {
			log.WithError(err).Fatal("my-amazing-app: could not read CA certificate file")
		}
		config.TLSConfig.RootCAs = x509.NewCertPool()
		if ok := config.TLSConfig.RootCAs.AppendCertsFromPEM(certBytes); !ok {
			log.Fatal("my-amazing-app: could not read CA certificates")
		}
	}

	// Create a new SDK client for the application
	client := config.NewClient(appID, appAccessKey)

	// Make sure the client is closed before the function returns
	// In your application, you should call this before the application shuts down
	defer client.Close()

	// Manage devices for the application.
	devices, err := client.ManageDevices()
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not get device manager")
	}

	// List the first 10 devices
	deviceList, err := devices.List(10, 0)
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not get devices")
	}
	log.Info("my-amazing-app: found devices")
	for _, device := range deviceList {
		fmt.Printf("- %s", device.DevID)
	}

	// Create a new device
	dev := new(ttnsdk.Device)
	dev.AppID = appID
	dev.DevID = "my-new-device"
	dev.Description = "A new device in my amazing app"
	dev.AppEUI = types.AppEUI{0x70, 0xB3, 0xD5, 0x7E, 0xF0, 0x00, 0x00, 0x24} // Use the real AppEUI here
	dev.DevEUI = types.DevEUI{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} // Use the real DevEUI here

	// Set a random AppKey
	dev.AppKey = new(types.AppKey)
	random.FillBytes(dev.AppKey[:])

	err = devices.Set(dev)
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not create device")
	}

	// Get the device
	dev, err = devices.Get("my-new-device")
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not get device")
	}

	// Personalize the device with random session keys
	err = dev.PersonalizeRandom()
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not personalize device")
	}
	log.WithFields(ttnlog.Fields{
		"devAddr": dev.DevAddr,
		"nwkSKey": dev.NwkSKey,
		"appSKey": dev.AppSKey,
	}).Info("my-amazing-app: personalized device")

	// Start Publish/Subscribe client (MQTT)
	pubsub, err := client.PubSub()
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not get application pub/sub")
	}

	// Make sure the pubsub client is closed before the function returns
	// In your application, you should call this before the application shuts down
	defer pubsub.Close()

	// Get a publish/subscribe client for all devices
	allDevicesPubSub := pubsub.AllDevices()

	// Make sure the pubsub client is closed before the function returns
	// In your application, you will probably call this before the application shuts down
	// This also stops existing subscriptions, in case you forgot to unsubscribe
	defer allDevicesPubSub.Close()

	// Subscribe to activations
	activations, err := allDevicesPubSub.SubscribeActivations()
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not subscribe to activations")
	}
	log.Debug("After this point, the program won't show anything until we receive an activation.")
	for activation := range activations {
		log.WithFields(ttnlog.Fields{
			"appEUI":  activation.AppEUI.String(),
			"devEUI":  activation.DevEUI.String(),
			"devAddr": activation.DevAddr.String(),
		}).Info("my-amazing-app: received activation")
		break // normally you wouldn't do this
	}

	// Unsubscribe from activations
	err = allDevicesPubSub.UnsubscribeActivations()
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not unsubscribe from activations")
	}

	// Subscribe to events
	events, err := allDevicesPubSub.SubscribeEvents()
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not subscribe to events")
	}
	log.Debug("After this point, the program won't show anything until we receive an application event.")
	for event := range events {
		log.WithFields(ttnlog.Fields{
			"devID":     event.DevID,
			"eventType": event.Event,
		}).Info("my-amazing-app: received event")
		if event.Data != nil {
			eventJSON, _ := json.Marshal(event.Data)
			fmt.Println("Event data:" + string(eventJSON))
		}
		break // normally you wouldn't do this
	}

	// Unsubscribe from events
	err = allDevicesPubSub.UnsubscribeEvents()
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not unsubscribe from events")
	}

	// Get a publish/subscribe client scoped to my-test-device
	myNewDevicePubSub := pubsub.Device("my-new-device")

	// Make sure the pubsub client for this device is closed before the function returns
	// In your application, you will probably call this when you no longer need the device
	// This also stops existing subscriptions, in case you forgot to unsubscribe
	defer myNewDevicePubSub.Close()

	// Subscribe to uplink messages
	uplink, err := myNewDevicePubSub.SubscribeUplink()
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not subscribe to uplink messages")
	}
	log.Debug("After this point, the program won't show anything until we receive an uplink message from device my-new-device.")
	for message := range uplink {
		hexPayload := hex.EncodeToString(message.PayloadRaw)
		log.WithField("data", hexPayload).Info("my-amazing-app: received uplink")
		break // normally you wouldn't do this
	}

	// Unsubscribe from uplink
	err = myNewDevicePubSub.UnsubscribeUplink()
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not unsubscribe from uplink")
	}

	// Publish downlink message
	err = myNewDevicePubSub.Publish(&types.DownlinkMessage{
		AppID:      appID,           // can be left out, the SDK will fill this
		DevID:      "my-new-device", // can be left out, the SDK will fill this
		PayloadRaw: []byte{0xaa, 0xbc},
		FPort:      10,
		Schedule:   types.ScheduleLast, // allowed values: "replace" (default), "first", "last"
		Confirmed:  false,              // can be left out, default is false
	})
	if err != nil {
		log.WithError(err).Fatal("my-amazing-app: could not schedule downlink message")
	}

}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var ClientVersion = "2.x.x"

ClientVersion to use

DialOptions to use when connecting to components

Functions

func MoveDevice

func MoveDevice(devID string, from, to DeviceManager) (err error)

MoveDevice moves a device to another application

Types

type ApplicationManager

type ApplicationManager interface {
	// Get the payload format used in this application. If the payload format is "custom", you can get the custom JS
	// payload functions with the GetCustomPayloadFunctions() function.
	GetPayloadFormat() (string, error)

	// Set the payload format to use in this application. If you want to use custom JS payload functions, use the
	// SetCustomPayloadFunctions() function instead. If you want to disable payload conversion, pass an empty string.
	SetPayloadFormat(format string) error

	// Get the custom JS payload functions.
	GetCustomPayloadFunctions() (jsDecoder, jsConverter, jsValidator, jsEncoder string, err error)

	// Set the custom JS payload functions.
	//
	// Example Decoder:
	//
	// // Decoder (Array<byte>, uint8) returns (Object)
	// function Decoder(bytes, port) {
	//   var decoded = {};
	//   return decoded;
	// }
	//
	// Example Converter:
	//
	// // Converter (Object, uint8) returns (Object)
	// function Converter(decoded, port) {
	//   var converted = decoded;
	//   return converted;
	// }
	//
	// Example Validator:
	// // Validator (Object, uint8) returns (Boolean)
	// function Validator(converted, port) {
	//   return true;
	// }
	//
	// Example Encoder:
	//
	// // Validator (Object, uint8) returns (Array<byte>)
	// function Encoder(object, port) {
	//   var bytes = [];
	//   return bytes;
	// }
	SetCustomPayloadFunctions(jsDecoder, jsConverter, jsValidator, jsEncoder string) error
}

ApplicationManager manages an application.

type ApplicationPubSub

type ApplicationPubSub interface {
	Publish(devID string, downlink *types.DownlinkMessage) error
	Device(devID string) DevicePubSub
	AllDevices() DeviceSub
	Close()
}

ApplicationPubSub interface for publishing and subscribing to devices in an application

type Client

type Client interface {
	// Close the client and clean up all connections
	Close() error

	// Subscribe to uplink and events, publish downlink
	PubSub() (ApplicationPubSub, error)

	// Manage the application
	ManageApplication() (ApplicationManager, error)

	// Manage devices in the application
	ManageDevices() (DeviceManager, error)

	// Simulate uplink messages for a device (for testing)
	Simulate(devID string) (Simulator, error)
}

Client interface for The Things Network's API.

type ClientConfig

type ClientConfig struct {
	Logger log.Interface

	// The name of this client
	ClientName string

	// The version of this client (in the default config, this is the value of ttnsdk.ClientVersion)
	ClientVersion string

	// TLS Configuration only has to be set if connecting with servers that do not have trusted certificates.
	TLSConfig *tls.Config

	// Address of the Account Server (in the default config, this is https://account.thethingsnetwork.org)
	AccountServerAddress string

	// Client ID for the account server (if you registered your client)
	AccountServerClientID string

	// Client Secret for the account server (if you registered your client)
	AccountServerClientSecret string

	// Address of the Discovery Server (in the default config, this is discovery.thethings.network:1900)
	DiscoveryServerAddress string

	// Set this to true if the Discovery Server is insecure (not recommended)
	DiscoveryServerInsecure bool

	// Address of the Handler (optional)
	HandlerAddress string

	// Timeout for requests (in the default config, this is 10 seconds)
	RequestTimeout time.Duration
	// contains filtered or unexported fields
}

ClientConfig contains the configuration for the API client. Use the NewConfig() or NewCommunityConfig() functions to initialize your configuration, otherwise NewClient will panic.

func NewCommunityConfig

func NewCommunityConfig(clientName string) ClientConfig

NewCommunityConfig creates a new configuration for the API client that is pre-configured for the Public Community Network.

func NewConfig

func NewConfig(clientName, accountServerAddress, discoveryServerAddress string) ClientConfig

NewConfig creates a new configuration for the API client.

func (ClientConfig) NewClient

func (c ClientConfig) NewClient(appID, appAccessKey string) Client

NewClient creates a new API client from the configuration, using the given Application ID and Application access key.

type Device

type Device struct {
	SparseDevice
	FCntUp                uint32    `json:"f_cnt_up"`
	FCntDown              uint32    `json:"f_cnt_down"`
	DisableFCntCheck      bool      `json:"disable_f_cnt_check"`
	Uses32BitFCnt         bool      `json:"uses32_bit_f_cnt"`
	ActivationConstraints string    `json:"activation_constraints"`
	LastSeen              time.Time `json:"last_seen"`
	// contains filtered or unexported fields
}

Device in an application

func (*Device) Delete

func (d *Device) Delete() error

Delete the device. This function panics if this is a new device.

func (*Device) IsNew

func (d *Device) IsNew() bool

IsNew indicates whether the device is new.

func (*Device) Personalize

func (d *Device) Personalize(nwkSKey types.NwkSKey, appSKey types.AppSKey) error

Personalize a device by requesting a DevAddr from the network, and setting the NwkSKey and AppSKey to the given values. This function panics if this is a new device, so make sure you Get() the device first.

func (*Device) PersonalizeFunc

func (d *Device) PersonalizeFunc(personalizeFunc func(types.DevAddr) (types.NwkSKey, types.AppSKey)) error

PersonalizeFunc personalizes a device by requesting a DevAddr from the network, and setting the NwkSKey and AppSKey to the result of the personalizeFunc. This function panics if this is a new device, so make sure you Get() the device first.

func (*Device) PersonalizeRandom

func (d *Device) PersonalizeRandom() error

PersonalizeRandom personalizes a device by requesting a DevAddr from the network, and setting the NwkSKey and AppSKey to randomly generated values. This function panics if this is a new device, so make sure you Get() the device first.

func (*Device) SetManager

func (d *Device) SetManager(manager DeviceManager)

SetManager sets the manager of the device. This function panics if this is not a new device.

func (*Device) Update

func (d *Device) Update() error

Update the device. This function panics if this is a new device.

type DeviceList

type DeviceList []*SparseDevice

DeviceList is a slice of *SparseDevice.

func (DeviceList) AsDevices

func (d DeviceList) AsDevices() []*Device

AsDevices returns the DeviceList as a slice of *Device instead of *SparseDevice

type DeviceManager

type DeviceManager interface {
	// List devices in an application. Use the limit and offset for pagination. Requests that fetch many devices will be
	// very slow, which is often not necessary. If you use this function too often, the response will be cached by the
	// server, and you might receive outdated data.
	List(limit, offset uint64) (DeviceList, error)

	// Get details for a device
	Get(devID string) (*Device, error)

	// Create or Update a device.
	Set(*Device) error

	// Delete a device
	Delete(devID string) error
}

DeviceManager manages devices within an application

type DevicePub

type DevicePub interface {
	Publish(*types.DownlinkMessage) error
}

DevicePub interface for publishing downlink messages to the device

type DevicePubSub

type DevicePubSub interface {
	DevicePub
	DeviceSub
}

DevicePubSub combines the DevicePub and DeviceSub interfaces

type DeviceSub

type DeviceSub interface {
	SubscribeUplink() (<-chan *types.UplinkMessage, error)
	UnsubscribeUplink() error
	SubscribeEvents() (<-chan *types.DeviceEvent, error)
	UnsubscribeEvents() error
	SubscribeActivations() (<-chan *types.Activation, error)
	UnsubscribeActivations() error
	Close()
}

DeviceSub interface for subscribing to uplink messages and events from the device

type Simulator

type Simulator interface {
	Uplink(port uint8, payload []byte) error
}

Simulator simulates messages for devices

type SparseDevice

type SparseDevice struct {
	AppID       string            `json:"app_id"`
	DevID       string            `json:"dev_id"`
	AppEUI      types.AppEUI      `json:"app_eui"`
	DevEUI      types.DevEUI      `json:"dev_eui"`
	Description string            `json:"description,omitempty"`
	DevAddr     *types.DevAddr    `json:"dev_addr,omitempty"`
	NwkSKey     *types.NwkSKey    `json:"nwk_s_key,omitempty"`
	AppSKey     *types.AppSKey    `json:"app_s_key,omitempty"`
	AppKey      *types.AppKey     `json:"app_key,omitempty"`
	Latitude    float32           `json:"latitude,omitempty"`
	Longitude   float32           `json:"longitude,omitempty"`
	Altitude    int32             `json:"altitude,omitempty"`
	Attributes  map[string]string `json:"attributes,omitempty"`
}

SparseDevice contains most, but not all fields of the device. It's returned by List operations to save server resources

func (*SparseDevice) AsDevice

func (d *SparseDevice) AsDevice() *Device

AsDevice wraps the *SparseDevice and returns a *Device containing that sparse device

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL