Implementation Examples
This section provides concise examples for various programming languages. Fundamentally, any language that supports WebSockets or HTTP can be used to communicate with the device.
The ID command (/id) is used here to demonstrate how to retrieve identification information from a connected Zahner IM7 Workstation. The following examples show how to send this command using raw WebSocket connections across different languages.
Each example produces the following JSON response from the IM7 device:
data received:
{
"type": "REPLY",
"status": "SUCCESS",
"request_id": "3bcbaaf4-c27e-4bad-ae23-de30a1a0d8fb",
"workstation": {
"protocol_version": 2,
"serial_number": "73000000",
"model_name": "IM7x",
"firmware_version": "p2",
"cpu_card_uuid": "106096c6-203d-47a1-86e8-d2e671c93b9e",
"system_mac": "00C06AFFFFFE",
"system_name": "My Zahner Workstation"
}
}
All languages use at least the ID command for which the response was listed as an example. If additional commands have been implemented as examples, their responses are displayed separately as needed.
Python WebSocket
ID Command
This example demonstrates raw WebSocket communication in Python to illustrate the general procedure. For easier access to the IM7 API, we recommend using the provided Python library.
import json
from websocket import create_connection
id_command = {
"request_id": "3bcbaaf4-c27e-4bad-ae23-de30a1a0d8fb",
"do": "/id",
}
socket_url = "ws://localhost:1994"
web_socket = create_connection(socket_url)
web_socket.send(json.dumps(id_command))
result = web_socket.recv()
result_data = json.loads(result)
print(f"data received:\n{json.dumps(result_data, indent=2)}")
web_socket.close()
Stop Command
It is also possible to stop jobs via WebSocket by sending the /stop command. The job that was stopped then has the status: “status_detail”: “STOPPED_BY_USER”.
import json
from websocket import create_connection
id_command = {
"request_id": "3bcbaaf4-c27e-4bad-ae23-de30a1a0d8fb",
"do": "/stop",
}
socket_url = "ws://localhost:1994"
web_socket = create_connection(socket_url)
web_socket.send(json.dumps(id_command))
result = web_socket.recv()
result_data = json.loads(result)
print(f"data received:\n{json.dumps(result_data, indent=2)}")
web_socket.close()
DC Data Retrieval and Decoding
DC measurement data can get quite large. To keep the transfer reliable and compatible across different systems, the device sends the numeric values as a Base64-encoded string.
In plain terms: Base64 is just a way to package “raw bytes” into text so it can be transported safely. To actually work with the numbers (for example to plot them), the client needs to unpack that text back into bytes and interpret those bytes as measurement values.
The Python example below walks through the process step by step. Even if you are not a programmer, you can read it as a checklist of what happens:
Request the data for a specific job (the get_data_command).
Read the response events: one message provides the column information (names/units), another contains the actual data payload.
Decode the Base64 payload back into bytes.
Convert the bytes into numeric values (64-bit floating point numbers little endian).
Split the long list of numbers into separate columns (“tracks”) using the column count from the header.
import json
import time
import base64
import struct
from websocket import create_connection
socket_url = "ws://localhost:1994"
web_socket = create_connection(socket_url)
job_id = "bf76433f-39aa-4915-9318-8784aaca2e55"
get_data_command = {
"do": "/job/data",
"job_id": f"{job_id}",
"request_id": "get-data-uuid-example"
}
web_socket.send(json.dumps(get_data_command))
result = web_socket.recv()
result_data = json.loads(result)
print(f"data received:\n{json.dumps(result_data, indent=2)}")
column_count = 0
base64_data = None
dimensions = []
for entry in result_data.get("data", []):
if entry.get("type") == "EVENT":
event = entry.get("event", {})
if event.get("type") == "LIVE_DATA_HEADER":
dimensions = event["header"]["columns"]["dimensions"]
column_count = len(dimensions)
print(f"Found header with {column_count} columns")
elif event.get("type") == "LIVE_DATA_ROWS":
base64_data = event["data"]
print(f"Extracted data: {base64_data}")
if base64_data and column_count > 0:
decoded_bytes = base64.b64decode(base64_data)
double_count = len(decoded_bytes) // 8
doubles = struct.unpack(f"{double_count}d", decoded_bytes)
row_count = double_count // column_count
print(f"Decoded {double_count} doubles, {row_count} rows")
columns_data = []
for i in range(column_count):
start = i * row_count
end = start + row_count
col_data = doubles[start:end]
columns_data.append(col_data)
dimension_name = dimensions[i] if i < len(dimensions) else "unknown"
print(f"Column {i} ('{dimension_name}') data: {col_data}")
web_socket.close()
data received:
{
"type": "REPLY",
"status": "SUCCESS",
"data": [
{
"type": "EVENT",
"event": {
"type": "LIVE_DATA_HEADER",
"header": {
"measurement_type": "OCV/OCP Scan",
"measurement_type_short": "ocv",
"columns": {
"dimensions": [
"time",
"voltage",
"current",
"debug"
],
"units": [
"s",
"V",
"A",
"bits"
],
"urns": [
"time",
"2557874741:POT:U~43043:PAD_U",
"2557874741:POT:I~43043:PAD_I",
"debug"
]
}
},
"job_info": {
"job_id": "bf76433f-39aa-4915-9318-8784aaca2e55",
"status": "FINISHED",
"status_detail": "RUN_TO_COMPLETION",
"job_meta": {},
"created": "2026-01-27T08:43:12.075085Z",
"type": "ocv",
"parameters": {
"duration": 2,
"output_data_rate": 2
},
"started": "2026-01-27T08:43:12.075085Z",
"ended": "2026-01-27T08:43:14.164460Z",
"error": {
"number": 0,
"code": "NONE",
"message": "no error occoured",
"message_parameters": []
}
}
}
},
{
"type": "EVENT",
"event": {
"type": "LIVE_DATA_ROWS",
"data": "AAAAAAAA4D8AAAAAAADwPwAAAAAAAPg/AAAAAAAAAECollrQ6asIv8hG2F/rZQi/AMwSQH5lB7+AS6OXE80Gv2SuNwQy2FC96pnQSNgBUb1msIyqIK1SvUQMpCCFg1O9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
}
},
{
"type": "EVENT",
"event": {
"type": "LIVE_DATA_FINISHED",
"job_info": {
"job_id": "bf76433f-39aa-4915-9318-8784aaca2e55",
"status": "FINISHED",
"status_detail": "RUN_TO_COMPLETION",
"job_meta": {},
"created": "2026-01-27T08:43:12.075085Z",
"type": "ocv",
"parameters": {
"duration": 2,
"output_data_rate": 2
},
"started": "2026-01-27T08:43:12.075085Z",
"ended": "2026-01-27T08:43:14.164460Z",
"error": {
"number": 0,
"code": "NONE",
"message": "no error occoured",
"message_parameters": []
}
}
}
}
],
"request_id": "get-data-uuid-example"
}
Found header with 4 columns
Extracted data: AAAAAAAA4D8AAAAAAADwPwAAAAAAAPg/AAAAAAAAAECollrQ6asIv8hG2F/rZQi/AMwSQH5lB7+AS6OXE80Gv2SuNwQy2FC96pnQSNgBUb1msIyqIK1SvUQMpCCFg1O9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
Decoded 16 doubles, 4 rows
Column 0 ('time') data: (0.5, 1.0, 1.5, 2.0)
Column 1 ('voltage') data: (-4.7057221349162015e-05, -4.653572612424228e-05, -4.462520156191946e-05, -4.348960915625307e-05)
Column 2 ('current') data: (-2.393749297032741e-13, -2.4168694052009246e-13, -2.6540589739164965e-13, -2.773070678257643e-13)
Column 3 ('shunt') data: (0.0, 0.0, 0.0, 0.0)
JavaScript WebSocket
const socket_url = 'ws://localhost:1994';
const socket = new WebSocket(socket_url);
const id_command = {
"request_id": "3bcbaaf4-c27e-4bad-ae23-de30a1a0d8fb",
"do": "/id",
};
socket.onopen = function(event) {
// Send the command as a JSON string
socket.send(JSON.stringify(id_command));
};
socket.onmessage = function(event) {
// Parse the received JSON string
const result_data = JSON.parse(event.data);
// Print formatted JSON
console.log(`data received:\n${JSON.stringify(result_data, null, 2)}`);
socket.close();
};
PowerShell WebSocket
$socketUrl = "ws://localhost:1994"
$ws = New-Object System.Net.WebSockets.ClientWebSocket
$cts = New-Object System.Threading.CancellationTokenSource
# Connect to the WebSocket
$ws.ConnectAsync($socketUrl, $cts.Token).Wait()
# Create the JSON command
$idCommand = @{
request_id = "3bcbaaf4-c27e-4bad-ae23-de30a1a0d8fb"
do = "/id"
}
$jsonString = $idCommand | ConvertTo-Json -Compress
$buffer = [System.Text.Encoding]::UTF8.GetBytes($jsonString)
# Send the command
$ws.SendAsync([ArraySegment[byte]]$buffer, [System.Net.WebSockets.WebSocketMessageType]::Text, $true, $cts.Token).Wait()
# Receive the response
$buffer = New-Object byte[] 4096
$result = $ws.ReceiveAsync([ArraySegment[byte]]$buffer, $cts.Token)
$result.Wait()
# Decode and print the response
$responseString = [System.Text.Encoding]::UTF8.GetString($buffer, 0, $result.Result.Count)
$responseData = $responseString | ConvertFrom-Json
Write-Host "data received:"
$responseData | ConvertTo-Json -Depth 5
# Close the connection
$ws.CloseAsync([System.Net.WebSockets.WebSocketCloseStatus]::NormalClosure, "Closing", $cts.Token).Wait()
Bash WebSocket using websocat and jq
SOCKET_URL="ws://localhost:1994"
ID_COMMAND='{"request_id": "3bcbaaf4-c27e-4bad-ae23-de30a1a0d8fb", "do": "/id"}'
RESPONSE=$(echo $ID_COMMAND | websocat -1 $SOCKET_URL)
echo "data received:"
echo $RESPONSE | jq .
Bash HTTP using curl and jq
The IM7 also supports HTTP requests but we recommend using WebSockets for full functionality.
ID Command
URL="http://localhost:1994/id"
RESPONSE=$(curl -s $URL)
echo "data received:"
echo $RESPONSE | jq .
Switch On
As an addition for example the Switch On command via HTTP with the required parameters.
URL="http://host.docker.internal:1994/job/start"
PAYLOAD='{
"request_id": "3bcbaaf4-c27e-4bad-ae23-de30a1a0d8fb",
"job": {
"type": "switch_on",
"parameters": {
"potentiostat": "MAIN:1:POT",
"coupling": "POTENTIOSTATIC",
"bias": 0,
"voltage_range_index": 0,
"compliance_range_index": 0
}
}
}'
RESPONSE=$(curl -s -X POST -H "Content-Type: application/json" -d "$PAYLOAD" $URL)
echo "data received:"
echo $RESPONSE | jq .
The output shown below represents only the initial response, indicating that the job has been successfully submitted but has not yet completed. As detailed in the main WebSocket documentation, the job is finished only when a message with “type”:”JOB_DONE” is received. Alternatively, the job status in the job list can be polled until completion.
data received:
{
"type": "REPLY",
"status": "SUCCESS",
"request_id": "3bcbaaf4-c27e-4bad-ae23-de30a1a0d8fb",
"job_info": {
"job_id": "27a6688c-6ba7-48ae-ab2c-f58d378b68ff",
"status": "PENDING",
"status_detail": "NOT_FINISHED",
"job_meta": {},
"created": "2025-12-17T10:14:05.807353Z",
"type": "switch_on",
"parameters": {
"potentiostat": "MAIN:1:POT",
"coupling": "POTENTIOSTATIC",
"bias": 0,
"voltage_range_index": 0,
"compliance_range_index": 0
},
"started": "2025-12-17T10:14:05.807353Z",
"error": {
"number": 0,
"code": "NONE",
"message": "no error occoured",
"message_parameters": []
}
}
}
Job List
Use the following HTTP GET command to retrieve the complete list of jobs and their statuses.
URL="http://host.docker.internal:1994/job/list"
RESPONSE=$(curl -s $URL)
echo "data received:"
echo $RESPONSE | jq .
data received:
{
"type": "REPLY",
"status": "SUCCESS",
"jobs": [
{
"job_id": "0a6b9787-1328-43aa-b5c5-6011f7b5a769",
"status": "FINISHED",
"status_detail": "RUN_TO_COMPLETION",
"job_meta": {},
"created": "2025-12-17T10:41:21.467971Z",
"type": "switch_on",
"parameters": {
"potentiostat": "MAIN:1:POT",
"coupling": "POTENTIOSTATIC",
"bias": 0,
"voltage_range_index": 0,
"compliance_range_index": 0
},
"started": "2025-12-17T10:41:21.467971Z",
"ended": "2025-12-17T10:41:21.858092Z",
"error": {
"number": 0,
"code": "NONE",
"message": "no error occoured",
"message_parameters": []
}
}
]
}
Stop Command
The following example demonstrates how to stop a running job using the /stop HTTP command. The job that was stopped has the following status: “status_detail”: “STOPPED_BY_USER”.
URL="http://host.docker.internal:1994/stop"
RESPONSE=$(curl -s $URL)
echo "data received:"
echo $RESPONSE | jq .
C# WebSocket
using System;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Text.Json;
class Program
{
static async Task Main(string[] args)
{
using (ClientWebSocket ws = new ClientWebSocket())
{
Uri serverUri = new Uri("ws://localhost:1994");
CancellationTokenSource cts = new CancellationTokenSource();
await ws.ConnectAsync(serverUri, cts.Token);
var idCommand = new
{
request_id = "3bcbaaf4-c27e-4bad-ae23-de30a1a0d8fb",
@do = "/id"
};
string jsonString = JsonSerializer.Serialize(idCommand);
byte[] bytesToSend = Encoding.UTF8.GetBytes(jsonString);
await ws.SendAsync(new ArraySegment<byte>(bytesToSend), WebSocketMessageType.Text, true, cts.Token);
using (var ms = new MemoryStream())
{
var buffer = new byte[1024];
WebSocketReceiveResult result;
do
{
result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), cts.Token);
ms.Write(buffer, 0, result.Count);
} while (!result.EndOfMessage);
string responseString = Encoding.UTF8.GetString(ms.ToArray());
using (JsonDocument doc = JsonDocument.Parse(responseString))
{
var options = new JsonSerializerOptions { WriteIndented = true };
Console.WriteLine("data received:\n" + JsonSerializer.Serialize(doc.RootElement, options));
}
}
await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", cts.Token);
}
}
}