Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
Showing results for 
Search instead for 
Did you mean: 
0 Kudos
In this short tutorial, we will build a SAP Analytics Cloud, Analytics Application Slideshow controller. The application is displayed in the big screen and people can interact with it to play the slideshow with a wireless device via Bluetooth.

Let's prepare what we need to build:

  • HTML5 App

  • SAP Analytics Cloud, Analytics Application

  • BBC micro:bit Setup


Create an HTML5 app with the codes below and hosted in NodeJS server or in SAP Cloud Platform.

In these scripts, it tries to search the available micro:bit and register the necessary Bluetooth services to listen to the button press-events for button A and B.
Once the event is triggered (handleCharacteristicValueChanged1 - for button A and handleCharacteristicValueChanged2 - for button B), it will send the relevant message to the child frame where the SAP Analytics Application resides.
<title>SAP Analytics Cloud Slideshows</title>
<meta name="description" content="SAP Analytics Cloud">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">

<script src="./web/bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<!-- Polymer components -->
<link rel="import" href="./web/bower_components/paper-progress/paper-progress.html">
<link rel="import" href="./web/bower_components/paper-button/paper-button.html">
<link rel="import" href="./web/bower_components/iron-icons/iron-icons.html">
<link rel="import" href="./web/bower_components/iron-icons/image-icons.html">
<link rel="import" href="./web/bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="./web/bower_components/paper-card/paper-card.html">
<link rel="import" href="./web/bower_components/paper-dialog/paper-dialog.html">
<link rel="import" href="./web/bower_components/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="./web/bower_components/iron-flex-layout/iron-flex-layout.html">

<link rel="import" href="./web/bower_components/paper-dialog/paper-dialog.html">
<link rel="import" href="./web/bower_components/paper-button/paper-button.html">
<link rel="import" href="./web/bower_components/paper-input/paper-input.html">

<link rel="import" href="./web/bower_components/paper-styles/color.html">
<link rel="stylesheet" href="./web/bower_components/paper-styles/demo.css">

<style is="custom-style">
paper-progress {
width: 100%;
} {
paper-progress-active-color: var(--paper-light-blue-500);
paper-progress-secondary-color: var(--paper-light-blue-100);
} {
--paper-toggle-button-checked-bar-color: var(--paper-light-blue-500);
--paper-toggle-button-checked-button-color: var(--paper-light-blue-500);
--paper-toggle-button-checked-ink-color: var(--paper-light-blue-500);
--paper-toggle-button-unchecked-bar-color: var(--paper-light-blue-900);
--paper-toggle-button-unchecked-button-color: var(--paper-light-blue-900);
--paper-toggle-button-unchecked-ink-color: var(--paper-light-blue-900);

paper-button {
display: block;
width: 100px;
height: 40px;
min-width: 0em;
margin: 0.2em 0.2em;
} {
color: var(--paper-light-blue-500);
paper-button-flat-focus-color: var(--paper-light-blue-50);

#cards {
margin-left: auto;
margin-right: auto;
max-width: 300px;

paper-card {
--paper-card-header-text: {
font-size: 10px;
margin-bottom: 1px;
margin-top: 1px;
width: 100%;

.flex {

paper-button.indigo {
background-color: var(--paper-indigo-500);
color: white;
--paper-button-raised-keyboard-focus: {
background-color: var(--paper-pink-a200) !important;
color: white !important;

<body unresolved>
<div id="cards">
<paper-card heading="SAP Analytics Cloud Demo">
<div class="card-content">
<paper-toggle-button class="blue" id="connect">Connect</paper-toggle-button>
<paper-progress id="progress" indeterminate></paper-progress>
<paper-dialog id="no-bluetooth">
<h2>No Web Bluetooth</h2>
<p>The Web Bluetooth API is missing. Please enable it at chrome://flags/#enable-web-bluetooth and try again.</p>
<paper-dialog id="errorDialog">
<p>Could not connect to bluetooth device!</p>
'use strict';
document.addEventListener('WebComponentsReady', () => {
let connectToggle = document.querySelector('#connect');
let progress = document.querySelector('#progress');
let dialog = document.querySelector('#errorDialog');
let gattServer;
let commandService;
let writeCharacteristic;
let writeClientReqCharacteristic;
let writeClientEvtCharacteristic;
let busy = false;
let commandQueue = [];
progress.hidden = true;

function bindEvent(element, eventName, eventHandler) {
if (element.addEventListener) {
element.addEventListener(eventName, eventHandler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, eventHandler);

// Create the iframe
var iframe = document.createElement('iframe');
iframe.setAttribute('src', iframeSource);
iframe.setAttribute('id', 'the_iframe');
iframe.setAttribute('frameborder', 0);
iframe.setAttribute('style', 'overflow: hidden; height: 100%; width: 100%; position: absolute;');


// Send a message to the child iframe
var iframeEl = document.getElementById('the_iframe');
// Send a message to the child iframe
var sendMessage = function(msg) {
// Make sure you are sending a string, and to stringify JSON
iframeEl.contentWindow.postMessage(msg, '*');

// Listen to message from child window
bindEvent(window, 'message', function(e) {
if ( !== "embed:ready" && ! {

function handleCharacteristicValueChanged1(event) {
//Button A
let value =;
let a = [];
// Convert raw data bytes to hex values just for the sake of showing something.
// In the "real" world, you'd use data.getUint8, data.getUint16 or even
// TextDecoder to process raw data bytes.
for (let i = 0; i < value.byteLength; i++) {
a.push('0x' + ('00' + value.getUint8(i).toString(16)).slice(-2));
if (a.join(' ') == '0x01') {
console.log("Button A");


function handleCharacteristicValueChanged2(event) {
//Button B
let value =;
let a = [];
// Convert raw data bytes to hex values just for the sake of showing something.
// In the "real" world, you'd use data.getUint8, data.getUint16 or even
// TextDecoder to process raw data bytes.
for (let i = 0; i < value.byteLength; i++) {
a.push('0x' + ('00' + value.getUint8(i).toString(16)).slice(-2));
if (a.join(' ') == '0x01') {
console.log("Button B");

* Connect to command characteristic.
connectToggle.addEventListener('click', () => {
if (gattServer != null && gattServer.connected) {
if (gattServer.disconnect) {
} else {
connectToggle.checked = true;
progress.hidden = false;
if (writeCharacteristic == null) {
filters: [{
namePrefix: 'BBC micro:bit',
optionalServices: ['e95d9882-251d-470a-a062-fa1922dfa9a8'] //BUTTONSERVICE_SERVICE_UUID
.then(device => {
console.log('Connecting to GATT Server...');
return device.gatt.connect();
.then(server => {
console.log('> Found GATT server');
gattServer = server;
// Get command service
return gattServer.getPrimaryService('e95d9882-251d-470a-a062-fa1922dfa9a8'); //BUTTONSERVICE_SERVICE_UUID
.then(service => {
console.log('> Found command service');
commandService = service;
return commandService.getCharacteristic('e95dda90-251d-470a-a062-fa1922dfa9a8'); //BUTTON1STATE_CHARACTERISTIC_UUID
.then(characteristic => {
console.log('> Found write characteristic - Start Notification Button 1');

return characteristic.startNotifications().then(_ => {
console.log('> Notifications started');
.then(service => {
console.log('> Found command service Button B');
return commandService.getCharacteristic('e95dda91-251d-470a-a062-fa1922dfa9a8'); //BUTTON2STATE_CHARACTERISTIC_UUID
.then(characteristic2 => {
console.log('> Found write characteristic - Start Notification Button 2');

return characteristic2.startNotifications().then(_ => {
console.log('> Notifications started');
progress.hidden = true;
} else {
progress.hidden = true;

* Check if browser supports Web Bluetooth API.
if (navigator.bluetooth == undefined) {
document.getElementById("no-bluetooth").style.display = "block";
* Reset the app variable states.
function resetVariables() {
busy = false;
progress.hidden = true;
gattServer = null;
commandService = null;
writeCharacteristic = null;
writeClientReqCharacteristic = null;
writeClientEvtCharacteristic = null;
connectToggle.checked = false;
* API error handler.
function handleError(error) {

Replace the variable iframeSource with the SAP Analytics Cloud, Analytics Application URL.

SAP Analytics Cloud, Analytics Application

  • Insert 10 Tab Strips and add the image inside each Tab Strip.

  • Cover the header of the Tab Strip for example using a shape.

  • Insert Web Page and add the link to HTML5 app.

  • Under Scripting > Script Variables, insert index variable and set the default value to 1.

  • Also insert Total_tab variable and set the default value to the total number of Tab Strip which is 10.

  • Under Canvas > onPostMessageReceived, insert the following scripts:
    Index = ConvertUtils.stringToInteger(message) + Index;

    if(Index > Total_Tab) {
    Index = 1;
    if(Index < 1) {
    Index = Total_Tab;

    var tabx = ConvertUtils.numberToString(Index);

    TabStrip_1.setSelectedKey("Tab_" + tabx);​

  • Save the app.

micro:bit Setup

All the necessary setup is completed now we can see it in action.

Run the App

Open the link to the HTML5 App and click Connect button to connect to the micro:bit.

Press the button A on micro:bit to move to the next slide and button B to move to the earlier slide.

See it it in action:

Labels in this area