RaspberryPi on SAP HCP – IoT blog series part 6.2: Getting productive – completely
Table of Contents
In the last blog post of this blog post series we’ve setup the IoT Service for our scenario.
No Java code footprint in the cloud
With the functionality available on the HCP IoT Services we’ll now reduce the code footprint of our overall application. We no longer need the Java app on our free developer account, but we’ll leverage the OData service provided by the IoT Services and use that inside our HTML5 application.
Along with the IoT Services we’ll also be able to add a security feature to our app as the Raspberry Pi will have to provide an oAuth token together with each sensor value it will send to our IoT Service. So let’s get started.
1. Updating the Java app on the Raspberry Pi
In a first step we’ll update the Java code on the Raspberry Pi.
Step | Screenshot |
---|---|
1. Connect to your Raspberry Pi via ssh or any other proper way |
|
2. Open the file Temperature.java that you’ve used in blog post 3 of this blog post series. cd /home/pi/myscripts nano Temperature.java |
|
3. Substitute the code with the code I’ve listed up in the Appendix section of this blog post under Temperature.java. |
|
4. Adapt the following variables to your account and device type
Set also the following variables to the values you’ve setup in the last blog post when you’ve setup the IoT service
Save the file (press Ctrl and X and confirm with y) |
![]() |
5. Compile Temperature.java by entering the command javac Temperature.java | ![]() |
6. If you are interested, look into the code to find out how it sends the data to the IoT endpoint. |
2. Setting up destinations
Our app needs to access the list of devices, device types and, as well, the actual sensor data. For that you’ll have to create to additional destinations pointing you to the corresponding API.
2.1 Destination for devices and device types
2.2 Destination for the data
To retrieve the data from your IoT service we’ll need a new destination. You can find the API description for OData consumption in the online help of the SAP HCP IoT Services.
Step | Screenshot |
---|---|
1. Click on the New Destination link and create the destination Name: iot_devices_odata Type: HTTP URL: https://iotmmsXXXXXXXXXtrial.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/app.svc where XXXXXXXXX is your username (e.g. s1234567890) Proxy Type: Internet Authentication: Basic Authentication User: Your user name Password: Password for your user Click on Save |
![]() |
2. Now check if your 2 new destinations are there. It should look similar to the screenshot on the right. There should be an iot_devices_odata and an iot_sensordata_odata. | ![]() |
2.3. Important! Enable Basic Auth for iotmms application
In the documentation there is a note that we are authenticated with Basic Auth. Currently this needs to be enabled for the iotmms application by you manually.
Just follow the corresponding instructions in the documentation.
3. Update the HTML5 app
Now most of the work is done and we can look into the last part finalizing our app. We’ll adapt the code of our HTML5 dashboard app we’ve created in blog post 4 of this series. But this time we’ll use the Web IDE to do that.
Step | Screenshot |
---|---|
1. Go back to your account cockpit and click on the HTML5 Applications tab and click on the pencil icon next to the row of your fishdashboard application. | ![]() |
2. The Web IDE opens up. If necessary please enter your account credentials if the system asks you to do so. Once your project opens up double-click on the index.html file so that the code editor opens-up index.html |
![]() |
3. Now substitute the code of index.html with the code I’ve listed up in the Appendix section of this blog post under index.html. After you have done that save your changes by clicking on the Save icon at the top left. |
![]() |
4. In the index.html file you need to adapt one variable that contains the message type id of the message type you have defined in the previous blog post. Search in the index.html file for the row called
and substitute the id displayed there with yours. After that save your changes again. |
![]() |
5. Do the same for the neo-app.json file by substituting it with the content of the I’ve listed up in the Appendix section of this blog post under neo-app.json. | ![]() |
6. Deploy the app now.
Right-click on the fishdashboard folder and click Deploy > Deploy toSAP HANA Cloud Platform |
![]() |
7. In case you are asked for it provide your credentials and click on Login. | ![]() |
8. A pop-up window shows up and if you scroll down a bit in the window you’ll see the version number that will be used to deploy the app on your account. Click on RaspberryPi on SAP HCP – IoT blog series part 4: Create an SAPUI5 dashboard. |
![]() |
9. If everything worked out fine you should get a window telling you that you’ve successfully deployed your app. Now click on the link to Open the active version of the application. |
![]() |
10. If all worked fine you should see your app now. | ![]() |
4. Send your sensor data to the app
The last missing part is now to actually send the sensor values from the Raspberry Pi to your IoT Service.
To do so, go to your Raspberry Pi command line and enter
java Temperature
and you should see in your browser how the data is coming-in and changing. Congratulations!!
5. Summary
What you see now in this little app is that you no longer need a Java application providing the data to your HTML5 app. Instead you can use the OData service from the IoT services directly to do that job.
I propose you look into the index.html file to better understand how I’ve used the OData services to fetch all the information I’ve needed for the app. You’ll notice that I’m working with some filtering and sorting to get the information the way I needed. The corresponding documentation is very helpful and might provide you with some additional insights.
And now that we can access the data via an OData service, why not using some templates in the Web IDE to created other apps like a Fiori Master Detail application without having to code one line of code 🙂 ?
Have fun.
Best,
Rui
Appendix: Source code
Temperature.java
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import javax.net.ssl.HttpsURLConnection;
public class Temperature {
// Set this to true if you are working behind a proxy server.
// If you are working from home you should set this most probable to false
public static boolean USEPROXY = true;
// Add your account id here
// Get the devide ID and the OAuth Token from the Device List in your
// IoT Services Cockpit
public static String MY_ACCOUNT_ID = “YOURUSERIDtrial”;
public static String MESSAGETYPE_ID = “xxxxxxxxxxxxxxxxxx”;
public static String DEVICE_1_ID = “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx”;
public static String DEVICE_1_TOKEN = “xxxxxxxxxxxxxxxxxxxxxxxxxxxx”;
public static String DEVICE_2_ID = “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx”;
public static String DEVICE_2_TOKEN = “xxxxxxxxxxxxxxxxxxxxxxxxxxxx”;
public static void main(String args[]) {
String repeatString = null;
if (args.length > 0) {
repeatString = args[0];
} else {
repeatString = “10”;
}
int repeats = Integer.parseInt(repeatString);
for (int i = 0; i < repeats; i++) {
// send data every 2000ms
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
//Handle exception
}
//System.out.println(“Loop ” + (i + 1));
long timestamp = System.currentTimeMillis();
//double sensorTemperature = getOnboardTempSensor();
double sensorTemperature = getRandomValue(-5, 5);
String bodyMessage = buildBody(MESSAGETYPE_ID, sensorTemperature, timestamp);
sendToCloud(bodyMessage, DEVICE_1_ID, DEVICE_1_TOKEN , String.valueOf(i + 1));
sensorTemperature = getRandomValue(-5, 0);
//sensorTemperature = getOnboardTempSensor();
//sensorTemperature = getOneWireSensor(“28-0000060a4638”);
bodyMessage = buildBody(MESSAGETYPE_ID, sensorTemperature, timestamp);
sendToCloud(bodyMessage, DEVICE_2_ID, DEVICE_2_TOKEN , String.valueOf(i + 1));
}
}
// Creates random value within give limits
private static double getRandomValue(double min, double max) {
Random r = new Random();
double value = min + (max – min) * r.nextDouble();
// Round up the value to 2 decimal places
// E.g. will make out of 1.42342232 a 1.42
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(2, RoundingMode.HALF_UP);
return bd.doubleValue();
}
private static String buildBody(String messageType, Double sensorValue, long time) {
String body = “{“;
body += buildJson(“mode”, “async”) + “,”;
body += buildJson(“messageType”, messageType) + “,”;
body += ‘”‘ + “messages” + ‘”‘ + “:[“;
String messageContent = “{“;
messageContent += buildJson(“unit”, sensorValue) + “,”;
messageContent += buildJson(“value”, sensorValue) + “,”;
messageContent += buildJson(“storedAt”, time);
messageContent += “}”;
body += messageContent;
body += “]”;
body += “}”;
return body;
}
private static void sendToCloud(String body, String deviceId, String token, String loop) {
// long sensorTimestamp = System.currentTimeMillis();
String iotServiceMainLink = “https://iotmms” + MY_ACCOUNT_ID +”.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/data/”;
String url = iotServiceMainLink + deviceId;
byte[] postData = body.getBytes();
setProxy(USEPROXY);
try {
URL obj = new URL(url);
String responseMessage = “<NONE>”;
// System.out.println(” Calling url ” + url);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
con.setDoOutput(true);
con.setInstanceFollowRedirects(false);
// add request header
con.setRequestMethod(“POST”);
con.setRequestProperty(“Content-Type”, “application/json;charset=utf-8”);
con.setRequestProperty(“Authorization”, “Bearer ” + token);
// Send post request
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.write(postData);
wr.flush();
wr.close();
responseMessage = con.getResponseMessage();
// Get the response from the server for the received data
//InputStream in = con.getInputStream();
//String encoding = con.getContentEncoding();
//encoding = encoding == null ? “UTF-8” : encoding;
System.out.println(” – Loop ” + loop + ” sent body: ” + body);
//System.out.println(” – Response from server : ” + IOUtils.toString(in, encoding));
System.out.println(” – Response to connection : ” + responseMessage);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void setProxy(boolean needsProxy) {
Properties systemSettings = System.getProperties();
if (needsProxy == true) {
systemSettings.put(“https.proxyHost”, “proxy”);
systemSettings.put(“https.proxyPort”, “8080”);
systemSettings.put(“http.proxyHost”, “proxy”);
systemSettings.put(“http.proxyPort”, “8080”);
} else {
systemSettings.put(“http.proxySet”, “false”);
systemSettings.put(“https.proxySet”, “false”);
}
}
private static String buildJson(String param, String value) {
String result = “”;
char hk = ‘”‘;
String paramPart = hk + param + hk;
String valuePart = hk + value + hk;
result = paramPart + “:” + valuePart;
return result;
}
private static String buildJson(String param, long value) {
String result = “”;
char hk = ‘”‘;
String paramPart = hk + param + hk;
String valuePart = String.valueOf(value);
result = paramPart + “:” + valuePart;
return result;
}
private static String buildJson(String param, double value) {
String result = “”;
char hk = ‘”‘;
String paramPart = hk + param + hk;
String valuePart = String.valueOf(value);
result = paramPart + “:” + valuePart;
return result;
}
private static double getOnboardTempSensor() {
String command = “/opt/vc/bin/vcgencmd measure_temp”;
String fileContent = getCommandOutput(command);
String sensorValue = null;
if (fileContent != null && fileContent.length() > 0) {
String[] temp = fileContent.split(“=”);
sensorValue = temp[1].trim();
sensorValue = sensorValue.substring(0, sensorValue.length() – 2);
System.out.println(” – Measured temperature for CPU sensor is ” + sensorValue);
}
return Double.parseDouble(sensorValue);
}
private static double getOneWireSensor(String sensorHardwareId) {
String sensorValue = null;
String filename = “/sys/bus/w1/devices/” + sensorHardwareId + “/w1_slave”;
String fileContent = null;
fileContent = readFile(filename);
if (fileContent != null && fileContent.length() > 0) {
String[] temp = fileContent.split(“t=”);
sensorValue = temp[1].trim();
System.out.println(” – Measured temperature for 1wire sensor ” + sensorValue + ” is ” + sensorValue);
}
return Double.parseDouble(sensorValue);
}
private static String readFile(String filename) {
String result = null;
try {
List<String> lines = Files.readAllLines(Paths.get(filename), Charset.defaultCharset());
result = lines.get(1).toString();
} catch (IOException e) {
return null;
}
return result;
}
private static String getCommandOutput(String command) {
String result = “”;
String s;
Process p;
try {
p = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((s = br.readLine()) != null)
result += s;
p.waitFor();
p.destroy();
} catch (Exception e) {
}
return result;
}
}
index.html
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv=”X-UA-Compatible” content=”IE=edge” />
<title>Rui’s Fish Import/Export Inc.</title>
<!– You might want to use this bootstrap instead for local testing –>
<script src=”https://sapui5.hana.ondemand.com/resources/sap-ui-core.js“
type=”text/javascript” id=”sap-ui-bootstrap”
data-sap-ui-libs=”sap.ui.commons, sap.suite.ui.commons, sap.m, sap.ui.core, sap.makit”
data-sap-ui-theme=”sap_bluecrystal”>
</script>
<!– ###################################################### –>
<!– DATA MODEL functions –>
<!– ###################################################### –>
<script type=”text/javascript”>
var getModelFromURL = function(url) {
var url = url;
var oModel = new sap.ui.model.json.JSONModel();
oModel.loadData(url, null, false);
return oModel;
};
// Function to update the model of an UI object
var updateModelOfUiObject = function(id, model){
var object = sap.ui.getCore().getElementById(id);
object.setModel(model);
};
function updateDataModel(model){
model.refresh();
}
// Function to retrieve the list of devices
var getDeviceList = function(){
// <host> as described in https://help.hana.ondemand.com/iot/frameset.htm?77f28211989346559874381772e9dcc8.html
// Using the corresponding destination defined in neo-app.json
var devicesOdataUrl = “/rdms_api/devices”;
var oModelDevices = getModelFromURL(devicesOdataUrl);
var devices = oModelDevices.getData();
return devices;
};
// Function to retrieve the list of device types
var getDeviceTypeList = function(deviceTypeId){
var devicesTypesOdataUrl = “/rdms_api/devicetypes/” + deviceTypeId;
var oModelDeviceTypes = getModelFromURL(devicesTypesOdataUrl);
var deviceTypes = oModelDeviceTypes.getData();
return deviceTypes;
};
// Function to request url for the odata request –>
var buildRequestUrlForSensorData = function(messageId, maxNumberSensorValues, deviceId, sortOrder){
// Here the corresponding settings for the odata-magic later on
// orderby: using the TIMESTAMP and sorting it according to the sortOrder
// top : restricting the number of results
// format : set to use json
// filter : get all the sensor data for the specific DEVICE defined by the deviceId
var sensorDataUrl = “/sensordata/T_IOT_” + messageId + “?$orderby=C_STOREDAT ” + sortOrder + “&$top=” + maxNumberSensorValues + “&$format=json&$filter=G_DEVICE%20eq%20%27” + deviceId + “%27”;
return sensorDataUrl;
};
</script>
<!– ###################################################### –>
<!– All VIEWS –>
<!– ###################################################### –>
<!– Functions for the MAIN PAGE of the app –>
<script type=”text/javascript”>
var buildMainPage = function(id, title) {
var page = new sap.m.Page(id, {
title : title,
showNavButton : false
});
return page;
};
var buildChartPage = function(id, oModel, sensorNumber, chart) {
var chartPage = new sap.m.Page(id , {
showNavButton: true, navButtonPress: function(){app.back();},
content : [chart]
});
chartPage.setModel(oModel);
chartPage.bindProperty(“title”, “/d/results[” + sensorNumber + “]/G_DEVICE”);
return chartPage;
};
</script>
<!– Functions for creating the SENSOR TILES of the app –>
<script type=”text/javascript”>
var buildSensorTileToChart = function(oModel, deviceNumber, deviceName , deviceTypeName, iconName) {
var tile = new sap.m.StandardTile(“mySensorTile” + deviceNumber, {
numberUnit : “Celsius”,
infoState : “Success”,
icon : sap.ui.core.IconPool.getIconURI(iconName),
press : function(oEvent) { app.to(“detailPageChart_sensor” + deviceNumber);},
tap : function(oEvent) { app.to(“detailPageChart_sensor” + deviceNumber);},
info : deviceTypeName,
title : deviceName
});
tile.setModel(oModel);
// All the bindings
// Bind only the first value (the request sorts by the creation timestamp)
tile.bindProperty(“number”, “/d/results/0/C_VALUE”, function(bValue) {
returnVal = Math.round(bValue * 10) / 10 ;
return returnVal + “\u00b0”;
});
return tile;
};
</script>
<!– Function for creating the TEMPERATURE CHARTS OF A SENSOR –>
<script type=”text/javascript”>
var getChartForMetric = function(id, model, bindingName, minValue, maxValue){
var oChart = new sap.makit.Chart(id, {
height: “90%”,
width : “100%”,
type: sap.makit.ChartType.Line,
category : new sap.makit.Category({ column : “C_STOREDAT” }),
categoryAxis : new sap.makit.CategoryAxis({ displayLastLabel: true}),
valueAxis : new sap.makit.ValueAxis({ min:minValue, max:maxValue}),
values : [new sap.makit.Value({ expression : “C_VALUE”, displayName : “Temperature”})]
});
oChart.addColumn(new sap.makit.Column({name:”C_STOREDAT”, value:”{C_STOREDAT}”}));
oChart.addColumn(new sap.makit.Column({name:”C_VALUE”, value:”{C_VALUE}”, type:”number”}));
oChart.setModel(model);
oChart.bindRows(bindingName);
return oChart;
};
</script>
<!– ###################################################### –>
<!– THE MAIN APP –>
<!– ###################################################### –>
<script type=”text/javascript”>
jQuery.sap.require(“sap.ui.core.IconPool”);
jQuery.sap.require(“sap.ui.core.Icon”);
jQuery.sap.declare(“sap.ui.customized.FontIconContainer”);
// Maximum number of sensor values used for the chart
var maxNumberSensorValues = 10;
// The message ID you want to get the sensor values from
var messageId = “YOUR MESSAGE TYPE ID “.toUpperCase();
var idPageMain = “main”;
var app = new sap.m.App(“myApp”, {
initialPage : idPageMain
});
// Now create the page and place it into the HTML document
var mainPage = buildMainPage(idPageMain, “Rui’s Fish Import/Export Inc.”);
var appLink = window.location.href;
// Get list of devices
var devices = getDeviceList();
// Loop through all the devices that have been found
for (var deviceNumber = 0; deviceNumber < devices.length ; deviceNumber++){
var deviceId = devices[deviceNumber].id;
var deviceName = devices[deviceNumber].name;
// Get device type details for device
var deviceTypes = getDeviceTypeList(devices[deviceNumber].device_type);
var deviceTypeName = deviceTypes.name;
// Build the URL for the request to get the most recent sensor value
var singleSensorValueUrl = buildRequestUrlForSensorData(messageId, 1, deviceId, “desc”);
// Retrieve the model data for the singleSensorValueUrl (contains measured sensor value)
var oModelSingleSensorValue = getModelFromURL(singleSensorValueUrl);
// Build the URL for the request to get the most recent sensor values for the chart
var sensorValuesUrl = buildRequestUrlForSensorData(messageId, maxNumberSensorValues, deviceId, “desc”);
// Retrieve the model data for the singleSensorValueUrl (contains measured sensor values)
var oModelSensorValues = getModelFromURL(sensorValuesUrl);
// Build the tile to be displayed
var sensorTile = buildSensorTileToChart(oModelSingleSensorValue, deviceNumber, deviceName, deviceTypeName, “temperature”);
var chart_sensor = getChartForMetric(“chart_sensor” + deviceNumber, oModelSensorValues, “/d/results”, “”,””);
var detailPageChart_sensor = buildChartPage(“detailPageChart_sensor” + deviceNumber,oModelSensorValues, deviceNumber ,chart_sensor);
mainPage.addContent(sensorTile);
app.addPage(detailPageChart_sensor);
}
app.addPage(mainPage);
app.setBackgroundRepeat(true);
app.placeAt(“content”);
//Update the values in the tiles and charts every x ms
var updateInMilliseconds = 2000;
setInterval(function() {
for (var deviceNumber = 0; deviceNumber < devices.length ; deviceNumber++){
var deviceId = devices[deviceNumber].id;
var singleSensorValueUrl = buildRequestUrlForSensorData(messageId, 1, deviceId, “desc”);
var oModelSingleSensorValue = getModelFromURL(singleSensorValueUrl);
updateDataModel(oModelSingleSensorValue);
updateModelOfUiObject(“mySensorTile” + deviceNumber,oModelSingleSensorValue);
var sensorValuesUrl = buildRequestUrlForSensorData(messageId, maxNumberSensorValues, deviceId, “desc”);
var oModelSensorValues = getModelFromURL(sensorValuesUrl);
updateDataModel(oModelSensorValues);
updateModelOfUiObject(“chart_sensor” + deviceNumber,oModelSensorValues);
}
}, updateInMilliseconds);
</script>
</head>
<body class=”sapUiBody”>
<div id=”content”></div>
</body>
</html>
neo-app.json
{
“authenticationMethod”: “none”,
“routes”: [
{
“path”: “/sensordata”,
“target”: {
“type”: “destination”,
“name”: “iot_sensordata_odata”
},
“description”: “Fish import sensor data”
},
{
“path”: “/rdms_api”,
“target”: {
“type”: “destination”,
“name”: “iot_devices_odata”
},
“description”: “List of devices, device types and message types”
},
{
“path”: “/sapui5”,
“target”: {
“type”: “service”,
“name”: “sapui5”,
“entryPath”: “/resources”
},
“description”: “SAPUI5”
}
]
}
thanks Rui - very descriptive and awesome steps - easy to follow
Thanks for sharing.
Hello Rui,
Thanks for yet another easy to follow blog.
A few things kept me puzzling for a while though, using the java file you provided.
First of all I got an unknown service error. Turned out to be my fault. The Raspberry wasn't connected to the internet
Secondly I got a "conflict" error.
The message send by the java file turns out to be different from the messagetype defined in your previous blog. After changing the names and adding the unit to match the messagetype, it works fine.
Third are some changes to the html.index file. (btw, there is still a d-number in the comments)
As the messagetype is a little different, I also had to change the index.html file. I replaced all occurrences of C_TIMESTAMP into C_STORED_AT and C_TEMPERATURE in C_VALUE.
Another change in the index.html is the messageid. It was calling for the wrong table (not mine)
Now it is working great.
Thanks for this excelent blogserie. I had lots of fun folowing it
I will definately show this to my colleagues soon.
Kind regards,
Peter
Thanks Peter for the feedback and your hints wrt the message format.
Will fix that and add a comment here once I've finalized the changes.
Best,
Rui
Just found out I can't update the files in my blog post 🙁
Will think of a way to put my updated code into this blog post.
Ok. I've found a solution to update the code. I've removed the files I've had attached before and added the source code under a new section in the blog post called "Appendix".
Best,
Rui
Hello Rui,
Its a very good and easy tutorial. But i am getting a problem when i run the Temperature.java file. The problem is, i am getting service unavailable at the "Response to connection" as shown in the snapshot. Please help.
Hello Abhay,
Please check if your iotmms service is still running. On the trail instance, this service is stopped after 28 days by SAP.
You can find this application on the JAVA-applications tab in the cockpit.
Kind regards,
Peter Lans
Hello Lans,
My iotmms service is running and i could get the metadata from the browser also.
Hello Abhay,
A running iotmms service is good.
Please double check all your credentials on typo's.
- user-id (including 'trial')
- message id
- device id
- oauth token
Last, but not least, check if your device still has an internet connection (it sounds silly, but I ran into issues with that too)
Regards,
Peter Lans
Hello Lans,
Got the result. Problem was i din include the trial in the userid.
Thanks.
Hello Rui,
Can I get the data stored in the HANA Cloud from the raspberry pi ...? if yes, then how to get it...?
I got the push messages stored in the HANA Cloud but i need the actual data stored in the database i.e. values of the table "T_IOT_CONFIG" into the raspberry pi not into the UI5 app.
Hello Abhay, if you need to push messages to the device, please use IoT MMS Push API for that. A link to official documentation: SAP HANA Cloud Platform Internet of Things (IoT) Services
Regards,
Anton
Hello Anton,
I don't want to push the messages into the devices. I want to access the data from the columns C_Value, C_StoredAt etc.
Then you should refer to Consumption options SAP HANA Cloud Platform Internet of Things (IoT) Services
Hello Anton,
I followed the link which you sent. I used the following URL to fetch the data from a particular table in JSON format but unable to get it.
URL = "https://iotmms" + MY_ACCOUNT_ID +".hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/app.svc/NEO_1Y79TD2GLIXGQCW7LZ8OG4Z1K.T_IOT_CONFIG?$format=json
I got the connection as OK but the response I am getting is as follow
<html><head><meta http-equiv="cache-control" content="no-cache" /><meta http-equiv="pragma" content="no-cache" /></head><body style="background-color:#FFFFFF" onload="var url=window.location.hash;if(url&&0!==url.length){var anchorCookie='oucrstmvwjkttvcscixitlqve_anchor="'+encodeURIComponent(url)+'"';document.cookie=anchorCookie}document.forms[0].submit()"><p><script language="javascript">document.write("Please wait ...");</script></p><noscript><p>Note: Your browser does not support JavaScript or it is turned off. Press the button to proceed.</p></noscript><form method="post" action="https://accounts.sap.com/saml2/idp/sso/accounts.sap.com"><input type="hidden" name="SAMLRequest" value="PEF1dGhuUmVxdWVzdCB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpuczI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOm5zMz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIgeG1sbnM6bnM0PSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiIEFzc2VydGlvbkNvbnN1bWVyU2VydmljZVVSTD0iaHR0cHM6Ly9pb3RtbXNwMTk0MTQ2MDM2NnRyaWFsLmhhbmF0cmlhbC5vbmRlbWFuZC5jb20vY29tLnNhcC5pb3RzZXJ2aWNlcy5tbXMvdjEvYXBpL2h0dHAvYXBwLnN2Yy8kbWV0YWRhdGEmYW1wOyRmb3JtYXQ9anNvbiIgRGVzdGluYXRpb249Imh0dHBzOi8vYWNjb3VudHMuc2FwLmNvbS9zYW1sMi9pZHAvc3NvL2FjY291bnRzLnNhcC5jb20iIEZvcmNlQXV0aG49ImZhbHNlIiBJRD0iUzcyMGFmZGNiLTVhNTEtNDM4My1hZWM5LTYxMmFiM2UyM2FiZC0yS0xpU2xHakVEWF90ZUtleFhSMnVoVGxRTEJmWFhiTWIyYmQ5VmVLcG5rIiBJc3N1ZUluc3RhbnQ9IjIwMTUtMTEtMzBUMDc6MTc6MTUuODk1WiIgVmVyc2lvbj0iMi4wIj48bnMyOklzc3Vlcj5odHRwczovL253dHJpYWwub25kZW1hbmQuY29tPC9uczI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjUzcyMGFmZGNiLTVhNTEtNDM4My1hZWM5LTYxMmFiM2UyM2FiZC0yS0xpU2xHakVEWF90ZUtleFhSMnVoVGxRTEJmWFhiTWIyYmQ5VmVLcG5rIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5sTDdLcnVXK2lhZ01xV3hlbFdQVEJEYS85OTg9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPlpkcmtVK3Y5YVFiZzRmMmtjdzNqbXhwdDFjbnhYNmUyOU9uKzNxZ1FjTkJYdVFKaGlJU1FLSUF1TmF5Y0lvSXNWOGcvZC8yNVNsODJCWHRrSVZYYjREOUw3K2dabnJOUzZMNHNYeTFLdEJRN1FFVHZWMzg3S0ZrcW5tUVk2REJVQVJ6MzJwblptdWs2c1ovenVvRXUwQ3c0VDQweHFYcVViSDI4MDZDZ1JMcHdrZ0lLUWptUUxEU0U1VWJQMXhkRDJEU3Q5SmdGNU5DbTh2RCtWVUVieGpBUzF5UjY0bWM1VitDZVJMbkw3Vk82U1diM1BhSTZYV29YYUF0NENPUHQwL3dlSXo3K01DNXhZQTJpbTRpVm9sZGpiMlYyRFhQZ0x6eHp2WnpGSXptZHBYMnJabTlobVRDNlJqdUpHcDltbVhaUWtnV0VHVHd2VzJQbS9JQ0JqUT09PC9kczpTaWduYXR1cmVWYWx1ZT48L2RzOlNpZ25hdHVyZT48L0F1dGhuUmVxdWVzdD4="/><input type="hidden" name="RelayState" value="oucrstmvwjkttvcscixitlqve"/><noscript><input type="submit" value="Continue"/></noscript></form></body></html>
This is a logon page content you get back. OData API same to Push API is protected with Basic Auth. Please, follow the documentation SAP HANA Cloud Platform Internet of Things (IoT) Services and enable "custom" authentication configuration for your MMS instance.
Anton
Hi Abhay, did you follow the steps of this blog post, specifically what I've explained in step 2.3? You'll also find the link there that Anton refers to above.
Best,
Rui
Hi Rui,
I followed the above 2.3 step and i used the following code to encode .
byte[] encodedBytes = Base64.encodeBase64("username":"password".getBytes());
con.setRequestProperty("Authorization: ", "Basic " + encoded);
but still the same result.
And what do you get if you just use that link in a browser?
Hello Anton,
I am getting the following message.
No server is available to handle this request, or the application iotmmsp1941460366.hanatrial.ondemand.com is temporarily down for maintenance. Excuse us for the inconvenience.
URL : https://iotmmsp1941460366.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/app.svc/NEO_1Y79TD2GLIXGQCW7LZ8OG4Z…
from that i see that you are using the wrong URL it should be iotmmsp1941460366trial (it should end up with trial and not just with your p-user)
Solved the issue. Working perfectly. Thank you Anton
May i ask, where exactly did you write the mentioned piece of code?
I have the same issue i.e. able to check the data but if i run the application(updated index.html), i get the error similar to the one pasted earlier(... action="https://accounts.sap.com/saml2/idp/sso/accounts.sap.com"><input type="hidden" name="SAMLRequest" ....)
Hello Kumar,
I have written that code after the line
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
Thanks Abhay!
I probably have a different issue then. I have not updated the Java file as i wanted to see the html part working before. Strange enough i can access the Odata services setup via the URL's in the destination but with the application itself it comes with the error mentioned.
Rui/Anton - would you have any suggestions to resolve this?
Hi Kumar,
I would need some more context from you. In your browser could you please check for which exactly request you get that "html response" back? When app calls /rdms_api/ URL or /sensordata/ one? And also, tell which MMS version are you using? You can check that in right top corner when you open /com.sap.iotservices.mms URL
Regards,
Anton
Hi Anton,
The MMS version is 2.7.0. The error is coming in the function getDeviceList().
Also, i have received password reset notifications yesterday as well as today. I suspect this is linked to the issue we are discussing.
The below image may not be super clear but might give some required info on the issue.
Let me know if there is any more info required to understand the root-cause.
What happens if you try the first URL https://iotrdmsiotservices... URL in a browser window? Do you get the JSON response back (with a list of devices)? And that notification you have mentioned is about reseting your password or smth about your accoutn is blocked for the next hour etc?
The link by itself works fine. Just that i have to add /devicetypes to see the content. Below is the working link
https://iotrdmsiotservices-NNNNNNNtrial.hanatrial.ondemand.com/com.sap.iotservices.dms/api/devicetypes
and yes, the notification mail is about the account is blocked for 1 hour or i can reset with the provided link.
This would then mean that you mistyped your password in the destination config and when an app tries to call that URL periodically after 5 failing tries your account is being blocked. Edit the first destination and reinsert your password.
Regards,
Anton
I suspect if that is the issue. I just checked the connection with the feature in the latest HCP upgrade. Both the destinations show successful connection.
A returned HTML page is also a "successful" result with 200 HTTP code.
I am entering the password again but doubt it will resolve the issue.
Anyways, thanks for your attempt to help. Appreciate it.
I will now try few more things and if it still does not work, i will design all the layers again.
Hi Anton,
I tried running the app in my personal notebook and everything works perfectly. Have to review the issue with my company's security team to check what the issue is with the SAML authentication.
Happy New Year!!
Cheers,
Jayant
Hello Rui,
I am trying to get the database values into my own SAPUI5 application. I followed the same steps. i have used the following code to fetch the values from the database.
var url = "/rdms_api/devices";
var oModel = new sap.ui.model.json.JSONModel();
var oModel = oModel.loadData(url, null, false);
It is calling the following URL when loadData is called
URL : https://webidetestingui5app-p1941460366trial.dispatcher.hanatrial.ondemand.com/rdms_api/devices
I am getting the response code as 200 and when i open this URL in the browser it is showing the data but in the variable oModel i am getting undefined. I am not getting what's wrong.
Hello Abhay,
be careful, you instantiate the oModel variable twice
Should be:
var oModel = new sap.ui.model.json.JSONModel();
oModel.loadData(url, null, false);
and then you operate with oModel if needed.
Anton
Hello Anton,
I changed the lines then also i am not getting anything.
Hard to say what is wrong. I would suggest that you revert to Rui's code without any modifications and check if that works for you. And only then start to modify the sources.
Hello Anton,
i solved the previous issue.
Today i was trying to retrieve the data using the oData Model but getting the following error as shown in the image.
i used the following code.
var encode = "Basic " + btoa("username:password");
var url = "https://iotmmsp1941460366trial.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/app.svc/";
var oModel = new sap.ui.model.odata.ODataModel(url, {Authorization: encode});
Hello Abhay, this won't work. You have to use destinations in order to make HTTP requests from your HTML5 app to your IoT MMS instance. See Rui's setup above.
Anton
Hello Anton,
I used the Rui's code using the destination and its working fine. Actually i wanted to insert data into the database from the UI5. How do i do this.
You can do that by making the respective HTTP call to MMS. Please, follow the documentation SAP HANA Cloud Platform Internet of Things (IoT) Services
Hello Anton,
I have done this through java app but unable to do it through UI5 app. Please help
1. Create a destination to MMS (for your HTML5 app)
2. Prepare an AJAX call with the URL which referencing that destination.
3. Prepare the JSON payload based on the documentation I posted above.
4. Make the HTTP call from your UI.
Hello Anton,
I followed your steps.
i created a destination for the url as shown below
URL : https://iotmmsp1941460366trial.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/data
Later i used the following code to call the HTTP Post.
var url = "738bf1cd-aa03-459d-8e26-1ec18a2d86c4";
var data = {
"mode":"sync",
"messageType":"6e3966dc04005f82ae91",
"messages":[
{
"sensor":"sensor1",
"value":"20",
"timestamp":1413191650
},
{
"sensor":"sensor2",
"value":"25",
"timestamp":1413191999
}
]
};
$.ajax({
type: "POST",
data: data,
crossDomain: true,
url: url,
contentType: "application/json;charset=utf-8",
success: function (res, status, xhr) {
alert("Success");
//success code
},
error: function (jqXHR, textStatus, errorThrown) {
console.log("Got an error response: " + textStatus + errorThrown);
}
});
Now I am getting a an error
405 (Method Not Allowed)
It should not be
var url = "738bf1cd-aa03-459d-8e26-1ec18a2d86c4";
but
var url = "<path_of _your_destination_from_neo_app_json_file>/738bf1cd-aa03-459d-8e26-1ec18a2d86c4";
I missed the /mms in the url which was the path, now it is giving the error unauthorized.
I have given the authentication method as follows
"authenticationMethod": "Basic base64encoded user and password",
Dear, Abhay. Please, before escalating further, carefully read the online documentation. All IoT MMS Data Services APIs are protected with OAuth mechanism and not with Basic. SAP HANA Cloud Platform Internet of Things (IoT) Services
Regards,
Anton
i also used
"authenticationMethod": "Bearer b8f2c815072e36c93b41fa8966ee7a9"
A HTTP header name for your AJAX call should not be "authenticationMethod" but "Authorization"
Hello Anton,
Working perfectly thank you so much
service unavailable error
i did include trial in my userid
Hi Brandon,
2.3 is an obsolete configuration step. A "note in documentation" mentioned was also removed from documentation.
Regards,
Anton
You are getting blank page when accessing what URL exactly?
Well, accessing the html5 client app has nothing to do with authorization/authentication configuration of the IoT Services. You might want to check the browser logs at least. It is impossible to guess what might be wrong with the html5 application access.
As I told, at least the browser console output for some JS errors.
UI5 resources could not be loaded. Did you try to access the link https://sapui5.hana.ondemand.com/resources/sap-ui-core.js directly from your browser? If that gives you a long JS back then you should re-check what is wrong with your client app. Clear cache... Re-deploy...