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.
// 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', {});
// 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));
}
// 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()
}
// 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()
// 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.
// 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,
});
// 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();
});
}
// 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()
}
// 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()
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:
{
"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.