Touch to Identify
An example of how to use the REST API with a simple script that identifies devices by touch.

Overview

A popular feature in DT Studio for both our customers and us internally is the ability to locate a sensor by simply touching it. Like any other function in DT Studio, the implementation is based on our REST API, and we will here show how simple it is to recreate using the stream endpoint.

Preliminaries

    Basic Auth For simplicity, we here use Basic Auth for authentication. We recommend replacing this with an OAuth2 flow for integrations more complex than local experimentation.
    Service Account Credentials You must create and know the credentials of a Service Account. Any role will suffice.
    Streaming Best Practices While this example is based on our other example for Streaming Events, it does not implement the same retry policy or other best practices as it is not the focus here. You are, however, free to combine the two examples for a more robust touch-event-listening loop.

Example Code

The following points summarize the provided example code.
    Sends a GET request to the REST API to initialize an event stream.
    Keep the TCP connection open while receiving events.
    When receiving a Touch Event, fetch and print the source device information.
    Break the stream.

Environment Setup

If you wish to run the code locally, make sure you have a working runtime environment.
Python 3.9
Python API
Node.js 14
The following packages are required by the example code and must be installed.
1
pip install requests==2.25.1
Copied!
The latest version of our Python API can be installed through pip.
1
pip install --upgrade disruptive
Copied!
The following modules are required by the example code and must be installed.
1
npm install [email protected]
2
npm install [email protected]
3
npm install [email protected]
Copied!

Source

The following code snippet implements the touch-to-identify listening loop.
Python 3.9
Python API
Node.js 14
1
import os
2
import json
3
import requests # pip install requests==2.25.1
4
5
# Service Account credentials.
6
SERVICE_ACCOUNT_KEY_ID = os.environ.get('DT_SERVICE_ACCOUNT_KEY_ID')
7
SERVICE_ACCOUNT_SECRET = os.environ.get('DT_SERVICE_ACCOUNT_SECRET')
8
9
# Construct API URL.
10
PROJECT_ID = os.environ.get('DT_PROJECT_ID')
11
API_BASE = 'https://api.d21s.com/v2/'
12
DEVICES_STREAM_URL = '{}projects/{}/devices:stream'.format(
13
API_BASE,
14
PROJECT_ID
15
)
16
17
if __name__ == '__main__':
18
# Set up a stream connection.
19
print('Waiting for touch event...')
20
stream = requests.get(
21
url=DEVICES_STREAM_URL,
22
auth=(SERVICE_ACCOUNT_KEY_ID, SERVICE_ACCOUNT_SECRET),
23
stream=True,
24
params={
25
'event_types': ['touch'],
26
},
27
)
28
29
# Iterate through the events as they come in (one event per line).
30
for line in stream.iter_lines():
31
# Decode the response payload and isolate event dictionary.
32
payload = json.loads(line.decode('ascii'))
33
34
# Halt at errors
35
if 'result' not in payload.keys():
36
print(payload)
37
break
38
39
# Fetch touched device blob for more detailed information.
40
event = payload['result']['event']
41
device = requests.get(
42
url=API_BASE + event['targetName'],
43
auth=(SERVICE_ACCOUNT_KEY_ID, SERVICE_ACCOUNT_SECRET),
44
).json()
45
46
# Print some device information.
47
print('\nTouch event received:')
48
print(json.dumps(device, indent=4))
49
50
# Stop stream as we've found a device.
51
break
Copied!
1
import os
2
import disruptive as dt
3
4
# Environment variables for authentication credentials and target.
5
KEY_ID = os.environ.get('DT_SERVICE_ACCOUNT_KEY_ID')
6
SECRET = os.environ.get('DT_SERVICE_ACCOUNT_SECRET')
7
EMAIL = os.environ.get('DT_SERVICE_ACCOUNT_EMAIL')
8
PROJECT_ID = os.environ.get('DT_PROJECT_ID')
9
10
if __name__ == '__main__':
11
# Authenticate using Service Account credentials.
12
dt.default_auth = dt.Auth.service_account(KEY_ID, SECRET, EMAIL)
13
14
# Enable logging to see what's happening under the hood.
15
dt.log_level = 'debug'
16
17
# Set up a stream for all touch events in a project.
18
for e in dt.Stream.event_stream(PROJECT_ID, event_types=['touch']):
19
# When a touch event is received, fetch the source device.
20
touched_device = dt.Device.get_device(e.device_id)
21
22
# Print information about the touched device.
23
print(touched_device)
24
25
# Stop the stream as we've found a device.
26
break
Copied!
1
const EventSource = require("eventsource") // npm install [email protected]
2
const jwt = require('jsonwebtoken') // npm install [email protected]
3
const axios = require('axios').default // npm install [email protected]
4
5
// Service Account credentials
6
const serviceAccountKeyID = process.env.DT_SERVICE_ACCOUNT_KEY_ID
7
const serviceAccountSecret = process.env.DT_SERVICE_ACCOUNT_SECRET
8
const serviceAccountEmail = process.env.DT_SERVICE_ACCOUNT_EMAIL
9
10
// Construct API URL
11
const projectID = process.env.DT_PROJECT_ID
12
const apiBase = 'https://api.d21s.com/v2/'
13
const devicesStreamUrl = apiBase + `projects/${projectID}/devices:stream`
14
15
async function main() {
16
17
let retryCount = 0
18
let stream
19
setupStream()
20
21
async function setupStream() {
22
// Since EventSource does not support setting HTTP headers, we need to use
23
// OAuth for authentication. The received access token will be set as a
24
// `token` query parameter when setting up the stream in the next step.
25
const accessToken = await getAccessToken(
26
serviceAccountKeyID,
27
serviceAccountEmail,
28
serviceAccountSecret,
29
)
30
31
// Add query parameters to the URL
32
let url = devicesStreamUrl
33
url += `?eventTypes=touch` // Filters out all events except touch
34
url += `&token=${accessToken}` // The access token for authentication
35
36
// Set up a new stream with callback functions for messages
37
console.log('Waiting for touch event...')
38
stream = new EventSource(url)
39
stream.onmessage = handleStreamMessage
40
}
41
42
async function handleStreamMessage(message) {
43
// Parse the payload as JSON
44
const data = JSON.parse(message.data)
45
if (!data || !data.result) {
46
return
47
}
48
49
// Parse the event object
50
const event = data.result.event
51
52
// Close stream as we've found a device
53
stream.close()
54
55
// Send GET request to fetch device information.
56
const response = await axios({
57
method: 'GET',
58
url: apiBase + event.targetName,
59
auth: {
60
username: process.env.DT_SERVICE_ACCOUNT_KEY_ID,
61
password: process.env.DT_SERVICE_ACCOUNT_SECRET,
62
}
63
})
64
65
// Print response contents.
66
console.log('\nTouch event received:')
67
console.log(JSON.stringify(response.data, null, 2))
68
}
69
}
70
main().catch((err) => {console.log(err)})
71
72
// Fetches an access token. See the following guide for documentation:
73
// https://developer.d12s.com/docs/authentication/oauth2
74
async function getAccessToken(keyID, email, secret) {
75
// Construct the JWT header
76
let jwtHeaders = {
77
'alg': 'HS256',
78
'kid': keyID,
79
}
80
81
// Construct the JWT payload
82
let jwtPayload = {
83
'iat': Math.floor(Date.now() / 1000), // current unixtime
84
'exp': Math.floor(Date.now() / 1000) + 3600, // expiration unixtime
85
'aud': 'https://identity.disruptive-technologies.com/oauth2/token',
86
'iss': email,
87
}
88
89
// Sign and encode JWT with the secret
90
const jwtEncoded = jwt.sign(
91
jwtPayload,
92
secret,
93
{
94
header: jwtHeaders,
95
algorithm: 'HS256',
96
},
97
)
98
99
// Prepare POST request data
100
const requestObject = {
101
'assertion': jwtEncoded,
102
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
103
}
104
105
// Converts the requestObject to a Form URL-Encoded string
106
const requestData = Object.keys(requestObject).map(function(key) {
107
return encodeURIComponent(key)+'='+encodeURIComponent(requestObject[key])
108
}).join('&')
109
110
// Exchange JWT for access token
111
const accessTokenResponse = await axios({
112
method: 'POST',
113
url: 'https://identity.disruptive-technologies.com/oauth2/token',
114
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
115
data: requestData,
116
}).catch(function (error) {
117
if (error.response) {
118
console.log(error.response.data)
119
}
120
throw error
121
})
122
123
// Return the access token in the request
124
return accessTokenResponse.data.access_token
125
}
Copied!

Expected Output

Once a touch event has been received, the device information is printed and stream terminated.
1
Waiting for touch event...
2
3
Touch event received:
4
{
5
"name": "projects/c0md3mm0c7bet3vico8g/devices/emuc0uc989qdqebrvv29so0",
6
"type": "touch",
7
"labels": {
8
"name": "my-favorite-sensor",
9
"virtual-sensor": ""
10
},
11
"reported": {
12
"networkStatus": {
13
"signalStrength": 99,
14
"rssi": 0,
15
"updateTime": "2021-03-03T17:21:53.080314Z",
16
"cloudConnectors": [
17
{
18
"id": "emulated-ccon",
19
"signalStrength": 99,
20
"rssi": 0
21
}
22
],
23
"transmissionMode": "LOW_POWER_STANDARD_MODE"
24
},
25
"batteryStatus": null,
26
"touch": {
27
"updateTime": "2021-03-03T17:22:51.059514Z"
28
}
29
}
30
}
31
32
Terminating stream.
Copied!
Last modified 1mo ago