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 ¶
- 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