Watch entities

Monitoring entities in Lattice using the Lattice SDK

This shows how to use the SDK to fetch entities from Lattice and stream real-time information about entity components.

Complete the steps to learn how to use the following APIs:

  • GetEntity — Retrieves a single entity from Lattice.
  • StreamEntities — Establishes a persistent connection to stream entity events.

Before you begin

  • To configure your app to watch entities, set up your Lattice environment.
  • Learn about required components and various entity shapes in Lattice.

Get an entity

Get details of a specific entity using entity_ID and the GetEntity API:

1

Find the entity URL

Open the entity details panel from the Lattice UI toolbar:

Shows the drop down menu where you can see the entity details.
2

Copy the entity ID

Choose Copy Content from the drop down menu, then choose Copy Asset URL:

Shows the drop down menu where you can copy asset URL and find the entity ID.

Extract the unique entity ID from this URL. For example, in the following, the entity ID is YOUR_ENTITY_ID: https://your_lattice_url.com/c2/entities/YOUR_ENTITY_ID

3

Get the entity object

Use the GetEntity API action to retrieve a single entity object from Lattice. Replace $ENTITY_ID in the following example with the entity ID you copied in the previous step:

1package main
2
3import (
4 "context"
5 "fmt"
6 "net/http"
7 "os"
8 "time"
9
10 "github.com/anduril/lattice-sdk-go/v2/client"
11 "github.com/anduril/lattice-sdk-go/v2/option"
12)
13
14func main() {
15 // Get environment variables
16 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
17 environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
18
19 // Remove sandboxesToken from the following statements if you are not developing on Sandboxes.
20 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
21
22 // Check required environment variables
23 if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
24 fmt.Println("Missing required environment variables")
25 os.Exit(1)
26 }
27
28 // Initialize headers for sandbox authorization
29 headers := http.Header{}
30 headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
31
32 // Create the client
33 LatticeClient := client.NewClient(
34 option.WithToken(environmentToken),
35 option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
36 option.WithHTTPHeader(headers),
37 )
38
39 // Continuously get the entity
40 for {
41 // Create context for the request
42 ctx := context.Background()
43
44 // Get the entity
45 entity, err := LatticeClient.Entities.GetEntity(
46 ctx,
47 "<ENTITY_ID>",
48 )
49
50 // Handle errors
51 if err != nil {
52 fmt.Printf("Error: %v\n", err)
53 } else {
54 fmt.Printf("Asset name | %s\n", *entity.Aliases.Name)
55 fmt.Printf("Asset location | %f, %f\n", *entity.Location.Position.LatitudeDegrees, *entity.Location.Position.LongitudeDegrees)
56 }
57
58 // Wait before next request
59 time.Sleep(5 * time.Second)
60 }
61}
4

Verify the response

If successful, you’ll see the entity’saliases.name and its real-time location logged in the console:

$Asset name | Demo-Sim-Asset1
>Asset location | 37.7749, -122.4194

Stream entities

The StreamEntities API establishes a persistent connection to stream entity events as they occur. The stream sends two types of events: entity and heartbeat.

Use the following optional parameters to control the frequency and type of data fetched from your environment: heartbeatIntervalMS, preExistingOnly, and componentsToInclude.

To stream entities from Lattice, do the following:

1

Stream all components

To get stream of all entity components from your environment, including new entities as they are updated, use the default options.

By default, the preExistingOnly option is set to false, instructing Lattice to establish a persistent, real-time connection with the client:

1package main
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "io"
8 "log"
9 "net/http"
10 "os"
11 "time"
12
13 Lattice "github.com/anduril/lattice-sdk-go/v2"
14 "github.com/anduril/lattice-sdk-go/v2/client"
15 "github.com/anduril/lattice-sdk-go/v2/option"
16)
17
18func main() {
19 ctx, cancel := context.WithCancel(context.Background())
20 defer cancel()
21
22 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
23 environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
24 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
25
26 if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
27 log.Fatal("Required environment variables not set.")
28 }
29
30 headers := http.Header{}
31 headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
32
33 latticeClient := client.NewClient(
34 option.WithToken(environmentToken),
35 option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
36 option.WithHTTPHeader(headers),
37 )
38
39 // Create the entity stream.
40 stream, err := latticeClient.Entities.StreamEntities(ctx, &Lattice.EntityStreamRequest{
41 PreExistingOnly: Lattice.Bool(false),
42 })
43 if err != nil {
44 log.Fatalf("Failed to create entity stream: %v", err)
45 }
46 defer stream.Close()
47
48 for {
49 select {
50 case <-ctx.Done():
51 log.Printf("Context canceled: %v", ctx.Err())
52 return
53 default:
54 // Continue processing
55 }
56 message, err := stream.Recv()
57
58 if errors.Is(err, io.EOF) {
59 log.Println("Stream completed successfully.")
60 return
61 }
62
63 if err != nil {
64 log.Printf("Error receiving message: %v", err)
65 continue
66 }
67
68 // Process the message based on its type
69 switch message.Event {
70 case "heartbeat":
71 timestamp := *message.Heartbeat.Timestamp
72 log.Printf("Heartbeat: %s", timestamp.Format(time.RFC3339))
73 case "entity":
74 log.Printf("Entity: %s", *message.Entity.Entity.EntityID)
75 default:
76 log.Printf("Unknown event type: %s", message.Event)
77 }
78 }
79}

If successful, you get a stream of all entities as they are updated in Lattice:

$Entity: Demo-Sim-Asset2
>Entity: adsbEntity
>Entity: esim.adsb.aus-4005
2

Stream specific components

Use componentsToInclude and provide a list of components in camelCase. For example, aliases, and locationUncertainty:

1package main
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "io"
8 "log"
9 "net/http"
10 "os"
11 "time"
12
13 Lattice "github.com/anduril/lattice-sdk-go/v2"
14 "github.com/anduril/lattice-sdk-go/v2/client"
15 "github.com/anduril/lattice-sdk-go/v2/option"
16)
17
18func main() {
19 ctx, cancel := context.WithCancel(context.Background())
20 defer cancel()
21
22 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
23 environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
24 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
25
26 if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
27 log.Fatal("Required environment variables not set.")
28 }
29
30 headers := http.Header{}
31 headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
32
33 latticeClient := client.NewClient(
34 option.WithToken(environmentToken),
35 option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
36 option.WithHTTPHeader(headers),
37 )
38
39 // Create the entity stream.
40 stream, err := latticeClient.Entities.StreamEntities(ctx, &Lattice.EntityStreamRequest{
41 PreExistingOnly: Lattice.Bool(false),
42 // Define a list of components to control which entities are fetched.
43 // If set, Lattice streams only entities with the components you provide.
44 ComponentsToInclude: []string{"aliases", "locationUncertainty"},
45 })
46 if err != nil {
47 log.Fatalf("Failed to create entity stream: %v", err)
48 }
49 defer stream.Close()
50
51 for {
52 select {
53 case <-ctx.Done():
54 log.Printf("Context canceled: %v", ctx.Err())
55 return
56 default:
57 // Continue processing
58 }
59 message, err := stream.Recv()
60
61 // Handle stream completion
62 if errors.Is(err, io.EOF) {
63 log.Println("Stream completed successfully.")
64 return
65 }
66
67 if err != nil {
68 log.Printf("Error receiving message: %v", err)
69 continue
70 }
71
72 // Process the event based whether it is a heartbeat or entity event.
73 switch message.Event {
74 case "heartbeat":
75 timestamp := *message.Heartbeat.Timestamp
76 log.Printf("Heartbeat: %s", timestamp.Format(time.RFC3339))
77 case "entity":
78 log.Printf("Entity: %s", *message.Entity.Entity.Aliases.Name)
79 default:
80 log.Printf("Unknown event type: %s", message.Event)
81 }
82 }
83}

If successful, you receive a real-time stream of entities with aliases populated:

$Entity: ADS-B: N113PF
>Entity: DIVE
>Entity: FISHING VESSEL (37958)

What’s next?