WebSocket Tracking API

The PickPoint Tracking API is a WebSocket-based solution that enables two-way communication for real-time monitoring and data gathering from connected devices. It specializes in collecting GPS locations and measurement data from devices integrated with the API. This data can be actively listened to and displayed on dashboards or by any other subscribers, facilitating immediate responsiveness and detailed device tracking in various applications. For making your first steps in tracking we recommend to start from GeoLab Training for Device Tracking. This section guides you how to be prepared step-by-step with examples for a few languages.

Dependencies

To implement a WebSocket connection, you can choose a suitable library. We recommend using Socket.IO. This library support most mainstream languages, like Swift, Kotlin, JavaScript, Java, C++, Rust, etc. Socket.IO offers a comprehensive solution for connection management, enhancing flexibility with fallback options and enabling automatic reconnections. In any case, if you prefer you can still use pure WebSocket connectivity.

Prerequisites

This feature is available on our Professional and Premium subscription plans. To get started, ensure you have created a device. You can do this manually through the Tracking Section, or programmatically via the Devices API.

Connect Your Device and Start Tracking

Please use device uid as client-id and secret client-secret to connect to the server. You can find these credentials in the Tracking Section or by using the Devices API. To start tracking, emit the track:start event from the device side. This event notifies the server to start tracking the device and begin sending location updates.

javascriptdartkotlinswiftrust
Copy
Copied
// Note: This code requires the Socket.IO package.
// Learn more at: https://www.npmjs.com/package/socket.io-client

import {io} from 'socket.io-client';

const clientId = 'CLIENT_ID';
const clientSecret = 'CLIENT_SECRET';
const socket = io('wss://ws.pickpoint.io', {
  transports: ['websocket'],
  path: '/v2/tracking',
  query: {
    'client-id': clientId,
    'client-secret': clientSecret,
  }
});

socket.on('error', (error) => {
  console.error(JSON.stringify(error));
});

socket.on('track:started', (data) => {
  console.log('Track Started:', data);

  socket.emit('location:add', {
    trackUid: data.trackUid,
    latitude: 37.7749,
    longitude: -122.4194,
  });
});

socket.emit('track:start', {});
Copy
Copied
// Note: This code requires the rust_socketio crate for Rust.
// Learn more at: https://crates.io/crates/rust_socketio

use rust_socketio::{ClientBuilder, Payload, RawClient, TransportType};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::{env, thread::sleep, time::Duration};
use rust_socketio::client::Client;

fn start(socket: &Client) {
  socket
    .emit("track:start", "{}")
    .expect("Server unreachable");
}

fn stop(socket: &RawClient) {
  socket
    .emit("track:stop", "{}")
    .expect("Server unreachable");
}

fn send_location(track_id: &str, socket: &RawClient) {
  println!("Sending location for track: {}", track_id);
  let data = json!({
    "trackUid": track_id,
    "latitude": -6.175110,
    "longitude": 106.87,
  });

  println!("Data: {}", data.to_string());
  socket
    .emit("location:add", data.to_string())
    .expect("Server unreachable");
}

fn main() {
  let client_id: String = "CLIENT_ID".parse().unwrap();
  let client_secret: String = "CLIENT_SECRET".parse().unwrap();

  let connection_url = format!(
    "wss://ws.pickpoint.io/v2/tracking/?client-id={}&client-secret={}",
    client_id, client_secret
  );

  println!("Connecting to the server: {}", connection_url);

  let socket = ClientBuilder::new(connection_url)
    .transport_type(TransportType::Websocket)
    .on("error", |err, _| eprintln!("Error: {:#?}", err))
    .on("track:started", |payload: Payload, socket: RawClient| {
      println!("Track started: {:#?}", payload);
      match payload {
        Payload::Text(text) => {
          let str = text[0].to_string();
          let data: Value = serde_json::from_str(str.as_str()).unwrap();
          send_location(data["trackUid"].as_str().unwrap(), &socket);
        },
        _ => {}
      }
    }).connect()
    .expect("Connection failed");

  sleep(Duration::from_secs(1));
  start(&socket);
  sleep(Duration::from_secs(5));
}
Copy
Copied
// Note: This code requires the socket.io-client package for Kotlin.
// Learn more at: https://github.com/socketio/socket.io-client-java

import io.socket.client.IO
import io.socket.client.Socket
import io.socket.engineio.client.transports.WebSocket
import org.json.JSONObject

fun main() {
  val clientId = "a6bd41a1-efdf-4614-85f4-7eef8d26d89b";
  val clientSecret = "aklQAettTlggWTnqeNsKldBgOKwzDhEn";
  val options = IO.Options.builder()
    .setTransports(arrayOf(WebSocket.NAME))
    .setPath("/v2/tracking")
    .setQuery("clientId=$clientId&clientSecret=$clientSecret")
    .build()

  val socket: Socket = IO.socket("wss://ws.pickpoint.io", options)
  socket.once(Socket.EVENT_CONNECT) {
    socket.on("track:started") { args ->
      val startData = args[0] as JSONObject

      // send locations
      var data = JSONObject()
        .put("trackUid", startData.getString("trackUid"))
        .put("latitude", 37.7749)
        .put("longitude", -122.4194)

      socket.emit("location:add", data)

      // stop tracking
      socket.emit("track:stop", JSONObject())
    }

    socket.emit("track:start", JSONObject())
  }
  socket.connect()
}
Copy
Copied
// Note: This code requires the Socket.IO package.
// Learn more at: https://github.com/socketio/socket.io-client-swift

import Foundation
import SocketIO

let url = URL(string: "wss://ws.pickpoint.io")!

// These are your credentials to connect to the server
let clientID = "CLIENT_ID"
let clientSecret = "CLIENT_SECRET"

let manager = SocketManager(socketURL: url, config: [
  .log(true),
  .forceWebsockets(true),
  .path("/v2/tracking"),
  .compress,
  .connectParams([
    "client-id": clientID,
    "client-secret": clientSecret,
    "transports": ["websocket"]]
  ),
])

let socket = manager.defaultSocket
socket.on(clientEvent: .connect) { data, ack in
  // Before sending the location updates, you should start the track
  // Use a subscription to the "start" event to know when the track has started
  socket.on("track:started") { payload, ack in

    let data = payload[0] as! [String: Any];
    let trackUid = data["trackUid"] as! String;
    // After the track has started, you can send location updates
    let location = ["trackUid": trackUid, "latitude": -6.174, "longitude": 106.865036]
    socket.emit("location:add", location)
  }

  // Now that the socket is connected, you can start the track
  let trackStart = ["location": ["latitude": -6.17511, "longitude": 106.865036]]
  socket.emit("track:start", trackStart)
}
socket.connect()

CFRunLoopRun()
Copy
Copied
// Note: This code requires the rust_socketio crate for Rust.
// Learn more at: https://crates.io/crates/rust_socketio

use rust_socketio::{ClientBuilder, Payload, RawClient, TransportType};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::{env, thread::sleep, time::Duration};
use rust_socketio::client::Client;

fn start(socket: &Client) {
  socket
    .emit("track:start", "{}")
    .expect("Server unreachable");
}

fn stop(socket: &RawClient) {
  socket
    .emit("track:stop", "{}")
    .expect("Server unreachable");
}

fn send_location(track_id: &str, socket: &RawClient) {
  println!("Sending location for track: {}", track_id);
  let data = json!({
    "trackUid": track_id,
    "latitude": -6.175110,
    "longitude": 106.87,
  });

  println!("Data: {}", data.to_string());
  socket
    .emit("location:add", data.to_string())
    .expect("Server unreachable");
}

fn main() {
  let client_id: String = "CLIENT_ID".parse().unwrap();
  let client_secret: String = "CLIENT_SECRET".parse().unwrap();

  let connection_url = format!(
    "wss://ws.pickpoint.io/v2/tracking/?client-id={}&client-secret={}",
    client_id, client_secret
  );

  println!("Connecting to the server: {}", connection_url);

  let socket = ClientBuilder::new(connection_url)
    .transport_type(TransportType::Websocket)
    .on("error", |err, _| eprintln!("Error: {:#?}", err))
    .on("track:started", |payload: Payload, socket: RawClient| {
      println!("Track started: {:#?}", payload);
      match payload {
        Payload::Text(text) => {
          let str = text[0].to_string();
          let data: Value = serde_json::from_str(str.as_str()).unwrap();
          send_location(data["trackUid"].as_str().unwrap(), &socket);
        },
        _ => {}
      }
    }).connect()
    .expect("Connection failed");

  sleep(Duration::from_secs(1));
  start(&socket);
  sleep(Duration::from_secs(5));
}

Subscribe to Device Updates

To receive real-time updates from the PickPoint server, subscribe to the device using the device:subscribe event. This event allows you to listen for the location:added event, which is triggered whenever a new location is added to the device. To connect to the server, use the device uid as the client-id. You can find this credential in the Tracking Section and obtain your API key from the API Key Section.

javascriptdartkotlinswiftrust
Copy
Copied
// Note: This code requires the Socket.IO package.
// Learn more at: https://www.npmjs.com/package/socket.io-client

import {io} from "socket.io-client";

const apiKey = 'API_KEY';
const clientId = 'CLIENT_ID';

const socket = io('wss://ws.pickpoint.io', {
  transports: ["websocket"],
  path: "/v2/tracking",
  query: {
    "x-api-key": apiKey,
  }
});

socket.on("error", (error) => {
  console.error(JSON.stringify(error));
});

socket.on('location:added', (data) => {
  console.log('location', data);
});

socket.emit('device:subscribe', {
  deviceUid: clientId,
});
Copy
Copied
// Note: This code requires the socket_io_client package for Dart.
// Learn more at: https://pub.dev/packages/socket_io_client

import 'dart:convert';
import 'package:socket_io_client/socket_io_client.dart';

void main(List<String> arguments) async {
  final apiKey = 'API_KEY';
  final clientId = 'CLIENT_ID';

  Socket socket = io(
      'wss://ws.pickpoint.io',
      OptionBuilder()
          .setQuery({'x-api-key': apiKey})
          .setTransports(['websocket'])
          .setPath('/v2/tracking')
          .build());

  print('Connecting to the server...');
  socket.onError((data) => print('Error: $data'));
  socket.onAny((event, data) => print('Event: $event, data: $data'));
  socket.onConnectError((data) => print('Connection error: $data'));

  socket.on('location:added', (data) {
    print('Location: $data');
  });

  socket.onConnect((_) => {
    socket.emit('device:subscribe', jsonEncode({
      'deviceUid': clientId,
    }))
  });

  await Future.delayed(Duration(milliseconds: 500000), () async {
    socket.disconnect();
  });
}
Copy
Copied
// Note: This code requires the socket.io-client package for Kotlin.
// Learn more at: https://github.com/socketio/socket.io-client-java

import io.socket.client.IO
import io.socket.client.Socket
import io.socket.engineio.client.transports.WebSocket
import org.json.JSONObject

fun main() {
  val apiKey = "API_KEY";
  val clientId = "CLIENT_ID";
  val options = IO.Options.builder()
    .setTransports(arrayOf(WebSocket.NAME))
    .setPath("/v2/tracking")
    .setQuery("x-api-key=$apiKey")
    .build()

  val socket: Socket = IO.socket("wss://ws.pickpoint.io", options)
  socket.once(Socket.EVENT_CONNECT) {
    socket.on("location:added") { args ->
      println(args[0])
    }
    socket.emit("device:subscribe", JSONObject().put("deviceUid", clientId))
  }
  socket.connect()
}
Copy
Copied
// Note: This code requires the Socket.IO package.
// Learn more at: https://github.com/socketio/socket.io-client-swift

import Foundation
import SocketIO

let url = URL(string: "wss://ws.pickpoint.io")!

// These are your credentials to connect to the server
let clientID = "CLIENT_ID"
let apiKey = "API_KEY"

let manager = SocketManager(socketURL: url, config: [
  .log(true),
  .forceWebsockets(true),
  .path("/v2/tracking"),
  .compress,
  .connectParams([
    "x-api-key": apiKey,
    // This is required to force the use of WebSockets
    "transports": ["websocket"]]
  ),
])

let socket = manager.defaultSocket
socket.on(clientEvent: .connect) { data, ack in
  socket.on("location:added") { payload, ack in
    let data = payload[0] as! [String: Any];
    NSLog("\(data)")
  }

  socket.emit("device:subscribe", ["deviceUid": clientID])
}
socket.connect()

CFRunLoopRun()
Copy
Copied
use rust_socketio::{ClientBuilder, Payload, RawClient, TransportType};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::{env, thread::sleep, time::Duration};
use rust_socketio::client::Client;

fn subscribe_to_device(client_id: String, socket: &Client) {
  socket
    .emit("device:subscribe", json!({
      "deviceUid": client_id,
    }))
    .expect("Server unreachable");
}

fn main() {
  let api_key: String = "API_KEY".parse().unwrap();
  let client_id: String = "CLIENT_ID".parse().unwrap();

  let connection_url = format!(
    "wss://ws.pickpoint.io/v2/tracking/?x-api-key={}",
    api_key
  );

  println!("Connecting to the server: {}", connection_url);

  let socket = ClientBuilder::new(connection_url)
    .transport_type(TransportType::Websocket)
    .on("error", |err, _| eprintln!("Error: {:#?}", err))
    .on("location:added", |payload: Payload, socket: RawClient| {
      println!("Location: {:#?}", payload);
    }).connect()
    .expect("Connection failed");

  subscribe_to_device(client_id, &socket);

  sleep(Duration::from_secs(300));
}

Location Data Format

The location data format is as follows:

Copy
Copied
{
  "trackUid": "TRACK_UID",
  "latitude": 37.7749,
  "longitude": -122.4194,
  "timestamp": "2021-09-01T12:00:00Z",
  "accuracy": 10,
  "speed": 10,
  "heading": 90,
  "altitude": 100
}

Stop Tracking

To stop tracking, emit the track:stop event from device side. This event notifies the server to stop tracking the device and cease sending location updates. Disconnect from the server after stopping the track if necessary.

Unsubscribe from Device Updates

To stop receiving real-time updates from the PickPoint server, unsubscribe from the device using the device:unsubscribe event.

Copyright © PickPoint.io LLC 2024. All rights reserved.