T-Mobile IoT Creators
Learn how to integrate Withthegrid platform with IoT Creators.
Connecting a device
In the following tutorial, temperature & humidity sensor Dragino - N95S31B will be connected to the platform.
Watch the video on Youtube
Sending downlinks
Downlinks are way to send messages to the device. This could be an instruction to reset the device or change its reporting interval.
Watch the video on Youtube
Register application URL
In order to be able to send commands to devices via IoT Creators API, we first need to register the application URL from previous tutorial as described in the IoT Creators documentation. In our case, the following request will return { "msg": "Success", "code": 1000 }, which means the request was successful.
curl -X PUT--header 'Content-Type: application/json'--header 'Accept: application/json'--header 'Authorization: Basic <your Base64 encoded username:password>'-d '{ "headers": {"authorization":"Basic <your Base64 encoded username:password>}, "url": "<your webhook URL>"}' http://api.scs.iot.telekom.com/m2m/applications/registrationCreate new commands
For more information refer to Command types.
Update the device type
In order to process a command, we need to extend the code we previously implemented. The way commands are processed and sent can be implemented in the corresponding device type. The following code will in case of a newCommand:
- Optional: Verify a command is valid
- Create a command-specific payload as defined by the Dragino - N95S31B documentation
- Send downlink request with required payload to IoT Creators
- Mark command as sent
- Optional: update a device field with additional information (e.g. new reporting interval)
// define the hashIds of commands this device type is expected to handleconst setIntervalCommandHashId = 'TO DO';const resetDeviceCommandHashId = 'TO DO';
// define the hashId of the reportType// will be known at step 8const reportTypeHashId = 'pd72ry';
// IoT Creators connection settingsconst iotCreatorsBaseUrl = 'https://api.scs.iot.telekom.com/m2m/endpoints/';// credential from https://portal.iotcreators.com/projects/<yourp_roject>/credentials as username:password to base64 with prefix Basicconst iotCreatorsToken = 'Basic <your Base64 encoded username:password>';
/*** Handle is the required function. It has two overloads, one for incoming HTTP requests and one for internal events* When this function is called for an incoming HTTP requests, the function should return information about the response* that should be returned.*/function handle(args: ArgumentsForIncomingRequestEvent, exec: Exec): IncomingRequestResponse;function handle(args: ArgumentsForEventWithoutResponse, exec: Exec): void;function handle(args: Arguments, exec: Exec): IncomingRequestResponse | void {
if (args.event.type === 'incomingRequest' || args.event.type === 'newCommand' || args.event.type === 'deletedCommand' || args.event.type === 'commandDue') { /** * Filter commands and send commands */ sendCommands(exec, args, filterCommands(exec, args)); }
if (args.event.type === 'incomingRequest') { const request = args.event.webRequest;
if (request.method === 'POST' && request.url.path === '/') { if (args.device.supplierDeviceIdentifier === 'iot-creators-registration') { return { statusCode: 204 }; }
// the device is submitting a condition report if (request.body === undefined || request.body.type !== 'json') { return { statusCode: 400, body: ['parameter_error', 'Body is not of type JSON'], }; }
// Set receive timestamp let generatedAt = new Date(); generatedAt.setMilliseconds(0);
// pass parsing of the report to the right report type parser exec.parseReport({ reportTypeHashId, payload: JSON.stringify({ generatedAt, payload: request.body.data, }), }); return { statusCode: 204 }; } return { statusCode: 404, body: ['not_found_error', 'Route cannot be found'], }; }}
function filterCommands(exec: Exec, args: Arguments): Command[] {
const scheduledCommands = args.scheduledCommands.filter((sC) => { let endOfCommand: Date | undefined; if (sC.endAt !== null) { endOfCommand = sC.endAt; } else if (sC.startAt !== null) { endOfCommand = sC.startAt; } else if (sC.sentAt !== null) { endOfCommand = new Date(sC.sentAt.valueOf() + sC.delay * 1000); } if (endOfCommand !== undefined && endOfCommand.valueOf() <= Date.now()) { // drop and do not send commands scheduled in the past exec.addLog(sC.hashId); exec.dropCommand(sC.hashId); return false; } if (sC.sentAt === null && sC.deletedAt !== null) { // drop and do not send commands that were deleted before they were sent. exec.dropCommand(sC.hashId); return false; }
return true; });
return scheduledCommands;}
function sendCommands(exec: Exec, args: Arguments, scheduledCommands: Command[]) { scheduledCommands.forEach((sC) => {
let payload: string; let requestbody: string; let url: string; // complete serialnumber of the device, including eventual leading "IMEI:" prefix. let deviceId: string = 'IMEI:'+args.device.supplierDeviceIdentifier;
exec.addLog("Commande Type hash id = " + sC.commandTypeHashId);
if (sC.commandTypeHashId === setIntervalCommandHashId && sC.fields !== null) { // Set Reporting Interval command if (typeof sC.fields.interval !== 'number') { throw new Error(`interval is a ${typeof sC.fields.interval}`); }
// exec.setDeviceFields([{ key: 'reportInterval', value: <FieldToServerFull>sC.fields.interval }]);
payload = createHexString(1, 2) + createHexString(sC.fields.interval, 6); payload = exec.uInt8ArrayToBase64(hexToBytes(payload)); requestbody = '{"resourceValue" : "' + payload + '"}'; url = iotCreatorsBaseUrl + deviceId + "/downlinkMsgBase64/0/data"; } else if (sC.commandTypeHashId === resetDeviceCommandHashId) { // Reset device command payload = "04ff"; payload = exec.uInt8ArrayToBase64(hexToBytes(payload)); requestbody = '{"resourceValue" : "' + payload + '"}'; url = iotCreatorsBaseUrl + deviceId + "/downlinkMsgBase64/0/data"; } else { throw new Error(`Cannot parse commandTypeHashId ${sC.commandTypeHashId}`); }
const header = { "Authorization": iotCreatorsToken, "Content-Type": "application/json", "Content-Length": "" + requestbody.length, "Host": "api.scs.iot.telekom.com" }
let response = exec.sendRequest({ url: url, method: "put", body: requestbody, headers: header });
if (response === null || response.statusCode < 200 || response.statusCode > 299) { throw new Error(`Schedule downlink failed with code ${response?.statusCode}`); }
exec.markCommandAsSent(sC.hashId); });}
function createHexString(value: any, width: number): string { if (value === null || width === null) { throw new Error("Create Hex String failed!"); } value = value.toString(16); width -= value.length; if (width > 0) { return new Array(width + (/\./.test(value) ? 2 : 1)).join('0') + value; } return value;}
function hexToBytes(hex: string): Uint8Array { for (var bytes = [], c = 0; c < hex.length; c += 2) bytes.push(parseInt(hex.substr(c, 2), 16)); return Uint8Array.from(bytes);}