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);
- Replace
testosmartprobeswith the proper URI schema from testo App URIs - 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];
- Replace
testosmartprobeswith 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=...
- testo Smart opens and the user performs the measurement as usual.
- 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.
- 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 |