Docs / Contact / sdks/go/examples
Go SDK: Examples
Practical examples for common use cases with the Tendrl Go SDK.
Periodic Data Collection with Tether#
The Tether method calls a function on a schedule and publishes its return value:
// Collect and publish system metrics every 30 seconds
stop := client.Tether("system-metrics", func() (interface{}, error) {
return map[string]interface{}{
"cpu_usage": getCPUUsage(),
"mem_usage": getMemUsage(),
"goroutines": runtime.NumGoroutine(),
}, nil
}, []string{"system", "metrics"}, 30*time.Second)
// Stop when done
defer stop()
Return an error to skip publishing that cycle without stopping the tether:
stop := client.Tether("sensor", func() (interface{}, error) {
value, err := readSensor()
if err != nil {
return nil, err // Skips this cycle, tries again next interval
}
return map[string]interface{}{"value": value}, nil
}, []string{"sensor"}, 5*time.Second)
Synchronous Publish with Response#
Wait for the server to acknowledge a message:
messageID, err := client.Publish(
map[string]interface{}{
"action": "provision",
"device": "sensor-042",
},
[]string{"provisioning"},
"", // entity (empty = self)
true, // waitResponse
10, // timeout seconds
)
if err != nil {
log.Printf("Failed: %v", err)
} else {
log.Printf("Confirmed: %s", messageID)
}
Inbound Message Routing#
Route incoming messages by tag with client.On():
client.On(tendrl.MessageRoute{
Tag: "diagnostic",
Handler: func(msg tendrl.IncomingMessage) error {
log.Println("Diagnostic:", msg.Data)
return nil
},
})
client.On(tendrl.MessageRoute{
Tag: "ai-response",
Handler: func(msg tendrl.IncomingMessage) error {
log.Println("AI:", msg.Data)
return nil
},
})
client.On(tendrl.MessageRoute{
Tags: []string{"alert", "anomaly"},
Handler: func(msg tendrl.IncomingMessage) error {
log.Println("Alert:", msg.Data)
return nil
},
})
client.SetMessageCheckRate(2 * time.Second)
client.SetMessageCheckLimit(10)
Cross-Account Messaging#
Send messages to entities in other accounts:
err := client.PublishCrossAccount(
map[string]interface{}{
"command": "status_request",
},
"account123:us-west:entity:device-001",
[]string{"query"},
)
State Table Management#
The Go SDK reads the state table but does not write it. Register a handler and the managed client polls for changes at the message-check interval:
client.OnState(func(state map[string]interface{}) error {
fmt.Printf("state changed: %v\n", state)
return nil
})
// Or poll once, on your own schedule
if err := client.CheckState(); err != nil {
log.Println("state poll failed:", err)
}
To write, call the REST endpoints directly with the entity's API key — there is no UpdateState or ReplaceState method:
func writeState(apiKey string, method string, body map[string]interface{}) error {
baseURL := os.Getenv("TENDRL_APP_URL")
if baseURL == "" {
baseURL = "https://app.tendrl.com/api"
}
payload, err := json.Marshal(body)
if err != nil {
return err
}
req, err := http.NewRequest(method, baseURL+"/entities/status-table", bytes.NewReader(payload))
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("status-table %s returned %s", method, resp.Status)
}
return nil
}
// Merge new data (PATCH; existing keys preserved)
err := writeState(apiKey, http.MethodPatch, map[string]interface{}{
"firmware": "2.1.0",
"last_boot": time.Now().Format(time.RFC3339),
})
// Replace entire state (PUT; removes all previous keys)
err = writeState(apiKey, http.MethodPut, map[string]interface{}{
"firmware": "2.1.0",
"status": "active",
})
Connectivity Monitoring#
Check network status and react to changes:
if client.IsOnline() {
// Send queued data
client.PublishAsync(data, tags)
} else {
log.Println("Offline; message will be stored")
}
// Get detailed connectivity state
connState := client.GetConnectivityState()
fmt.Printf("Online: %v, Last check: %s\n",
connState.Online, connState.LastCheck)
Flexible Data Types#
The SDK accepts any JSON-serializable type:
// Maps
client.PublishAsync(map[string]interface{}{"key": "value"}, tags)
// Structs
type SensorReading struct {
ID string `json:"id"`
Value float64 `json:"value"`
Unit string `json:"unit"`
}
client.PublishAsync(SensorReading{
ID: "temp-1", Value: 23.5, Unit: "celsius",
}, tags)
// Slices
client.PublishAsync([]int{1, 2, 3}, tags)
// Strings (auto-wrapped as {"data": "..."})
client.PublishAsync("simple message", tags)
// Primitives
client.PublishAsync(42, tags)
client.PublishAsync(3.14, tags)
client.PublishAsync(true, tags)
Custom Heartbeat#
Send a manual heartbeat with specific metrics:
err := client.PublishHeartbeat(tendrl.HeartbeatData{
MemFree: getMemFree(),
MemTotal: getMemTotal(),
DiskFree: getDiskFree(),
DiskSize: getDiskTotal(),
})
Tendrl