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")
}
}
Index ¶
- Variables
- func MoveDevice(devID string, from, to DeviceManager) (err error)
- type ApplicationManager
- type ApplicationPubSub
- type Client
- type ClientConfig
- type Device
- func (d *Device) Delete() error
- func (d *Device) IsNew() bool
- func (d *Device) Personalize(nwkSKey types.NwkSKey, appSKey types.AppSKey) error
- func (d *Device) PersonalizeFunc(personalizeFunc func(types.DevAddr) (types.NwkSKey, types.AppSKey)) error
- func (d *Device) PersonalizeRandom() error
- func (d *Device) SetManager(manager DeviceManager)
- func (d *Device) Update() error
- type DeviceList
- type DeviceManager
- type DevicePub
- type DevicePubSub
- type DeviceSub
- type Simulator
- type SparseDevice
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ClientVersion = "2.x.x"
ClientVersion to use
var DialOptions = []grpc.DialOption{ grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient( rpclog.UnaryClientInterceptor(nil), )), grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient( restartstream.Interceptor(restartstream.DefaultSettings), rpclog.StreamClientInterceptor(nil), )), grpc.WithBlock(), }
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) Personalize ¶
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 ¶
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.
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 ¶
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 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