Skip to content

App to App interface

The App to App interface is available in most testo Apps.

sequenceDiagram
  Your App->>testo App: Call testo App URI
  testo App->>testo App: Perform measurement
  testo App-->>Your App: Call response URI

testo App URIs

App URI Schema
testo Smart testosmartprobes://
testo 400 testosmartprobes://
testo 300 testot330i://
testo 330i testot330i://
testo Combustion testot330://

Calling testo App URI

[URI Schema]://start?bundleid=[bundleid]&userinfo=0

Examples

Android
Uri.Builder uriBuilder = new Uri.Builder();
uriBuilder.scheme("testosmartprobes"); // (1)
uriBuilder.authority("");
uriBuilder.appendPath("start");
uriBuilder.appendQueryParameter("userinfo", "");
uriBuilder.appendQueryParameter("bundleid", bundleId); // (2)
Intent intent = new Intent(Intent.ACTION_VIEW, uriBuilder.build());
startActivity(intent);
  1. Replace testosmartprobes with the proper URI schema from testo App URIs
  2. Insert the bundleId of your Android app
iOS (Objective C)
NSString *urlString = @"[URI Schema]://start?userinfo=0&bundleid=[bundleid]"; // (1)
NSURL *testoURL = [NSURL URLWithString:urlString];

[[UIApplication sharedApplication] openURL:testoURL];
  1. Replace testosmartprobes with the proper URI schema from testo App URIs and add your bundle ID

Reply to response URI

The testo app will call the following URI, where [bundleid] is replaced with your bundleid provided in the calling URI and [base64_encoded_json_data] is replaced by the JSON payload:

testoapp+[bundleid]://data?json=[base64_encoded_json_data]

Your app needs to register for handling this URL, this can be done in AndroidManifest.xml or in Info.plist for Android or iOS respectively.

Register for response URI on Android

This example assumes the bundle ID of de.testo.androiddatareceiverdemo:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="de.testo.androiddatareceiverdemo" android:versionName="1.1" android:versionCode="1" >
  <application [...]>
    <activity [...]>
      [...]
      <intent-filter>
        <data android:scheme="testoapp+androiddatareceiverdemo" />
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
      </intent-filter>
    </activity>
  </application>
</manifest>

Register for response URI on iOS

This example assumes the bundle ID of de.testo.DataReceiverDemo:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  [...]
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>testoapp+de.testo.DataReceiverDemo</string>
            </array>
            <key>CFBundleURLName</key>
            <string>de.testo.testoCallbackURL</string>
        </dict>
    </array>
</dict>
</plist>

testo Smart specifics

URI parameters

testosmartprobes://start?bundleid=[bundleid]&userinfo=[userinfo]&targetapplication=[application]
Parameter Required Description
bundleid yes Bundle ID of your app, used to build the response URI testoapp+[bundleid]://
userinfo yes Any string; returned unchanged in the response, e.g. to match the response to your request
targetapplication no Opens a measurement application in testo Smart directly, e.g. BasicViewClimateApplication

Note

Only the scheme testosmartprobes:// starts an App to App session. Both bundleid and userinfo must be present (userinfo may be empty), otherwise the call only opens testo Smart.

Sending the data back

sequenceDiagram
  Your App->>testo Smart: testosmartprobes://start?bundleid=...&userinfo=...
  testo Smart->>testo Smart: User performs and saves measurement
  testo Smart->>testo Smart: User exports the measurement as JSON
  testo Smart-->>Your App: testoapp+[bundleid]://data?userinfo=...&json=...
  1. testo Smart opens and the user performs the measurement as usual.
  2. The user exports the measurement in the JSON format (measurement list or measurement summary → Export). While an App to App session is active, the JSON export is not shared via the share sheet but sent to your app. Other formats (PDF, CSV) are exported as usual.
  3. testo Smart calls the response URI once; the session ends afterwards. If the user never exports, no response is sent (there is no error or cancel response).

Response URI

testoapp+[bundleid]://data?userinfo=[userinfo]&json=[encoded_json]

[encoded_json] is the JSON document, Base64 encoded with a URL-safe alphabet. Before decoding, replace the characters as follows:

In the URI Replace with
- +
_ /
, =
val encoded = uri.getQueryParameter("json") ?: return
val base64 = encoded.replace('-', '+').replace('_', '/').replace(',', '=')
val json = try {
    String(android.util.Base64.decode(base64, android.util.Base64.DEFAULT), Charsets.UTF_8)
} catch (e: IllegalArgumentException) {
    return // invalid payload
}
let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems
guard let encoded = items?.first(where: { $0.name == "json" })?.value else { return }
let base64 = encoded.replacingOccurrences(of: "-", with: "+")
                    .replacingOccurrences(of: "_", with: "/")
                    .replacingOccurrences(of: ",", with: "=")
guard let data = Data(base64Encoded: base64),
      let json = String(data: data, encoding: .utf8) else { return } // invalid payload

Payload size

The whole measurement is sent inside the URI. Base64 adds about 33 % to the size of the JSON, and there is no chunking or fallback. On Android the URI is passed in an intent, which is limited by the Binder transaction buffer (about 1 MB); larger payloads fail and your app receives nothing. iOS has no documented limit, but very long URLs are not reliable either. App to App is therefore meant for single measurements of moderate size — for long logging measurements use the regular file export. Images are not embedded in the JSON; the payload only references them by file name.

JSON payload

The payload is a measurement in the testo JSON format (TJF), the same format as the JSON export of testo Smart (.tjf files). The schema is published at github.com/testo/tjf (slim-schema-v1.4.0.json).

Top-level fields:

Field Description
$schema URL of the TJF schema
type Type of the measurement
timeStamp Time of the measurement
device Probes used: name, serial, firmwareVersion, partNumber, …
channels Measurement channels with type, unit and values (timeStamp, value)
customer, measuringPoint, organisation Customer, measuring site and company data, if assigned
additionalMeasurementInformation measurementuuid, appVersion, note, startTime, endTime
images, properties Attached images and further measurement properties