Asema IoT Central

Application Programming Guide


Chapter 1. Introduction

Asema IoT is an integrated connectivity solution for Internet of Things (IoT) applications. It lets the user collect and analyze data from connected IoT devices as well as control various connected devices. Asema IoT is a compact application package that focuses on speed, efficiency and precision. Likewise, it is also designed to work across multiple platforms and architecture. Asema IoT offers three different programmable user interface methods; screenlets, HTML5 applications and reports. Each method has its own purpose, but all the methods follow a similar logic and to a large extent similar syntax so that engineers can flexibly choose the most appropriate approach for given application and use case.

This section provides introduction to various methods offered by Asema IoT.

1.1. Introduction to UX frameworks – QML, React, Angular, Vue and some comparison

Asema IoT is UX framework independent. So, if you hate some of the frameworks with all your guts, this is where you let that sigh out. Asema IoT comes with support libraries that are intentionally made as simple as possible to lower the learning curve and reduce code conflict when integrating various UX frameworks.

If you are a bleeding edge, top of the line UX guru, you may find the approach “old school”. But as mentioned earlier, the libraries are not meant to be bound by a framework. The libraries don’t do a lot of fancy stuff like TypeScript or ECMAScript but that’s to ensure if you use one of those, the libraries will work without having you take a Panadol for integration headaches, even when working with legacy environments. The example code in this document will help you to hook up basic functions with modern frameworks and encapsulate functionality.

Frameworks do provide considerable ease on application development, especially when they involve modern highly functional and responsive Single Page Applications (SPA). The three most popular frameworks for SPAs at the time of writing this manual are Angular, React and Vue with dozens of other frameworks on their tails with several server-side code generators. Likewise, QML is used for native, component-based user interfaces in embedded environments like Smart TVs or car dashboards, something IoT apps must often consider.

The choice of UX framework, however, is solely dependent on the developer and application requirements. All the frameworks mentioned above have considerable backing and support. React is developed by Facebook, Angular by Google, Vue by Evan You and his team and QML was developed by the Qt Project. All the frameworks are component based and use JavaScript as their core technology. Similarly, all the frameworks can also be used to develop mobile applications with methods for reactive interfaces and property binding for real-time display of values. Asema IoT attempts to cover all the required scenarios and provide developers with the choice to help them develop them deliver better apps in less time. You choose, we deliver. We offer highly efficient, component-based client-side user interfaces, independent of platform and architectural boundaries. This implies that while the syntax may change, the design, interface, structure etc. do not change, making it possible to jump from one technology to other (We’ve done that, and we know it's a pain in the ass).

The primary difference, however, is the fact that Angular, React and Vue run on standard browsers whereas QML runs on native browser of the Asema IoT. The former give flexibility while the latter giver efficiency. In Asema IoT, QML applications access the system through contexts and system internal modules implemented in C++ while the rest use a REST API. Therefore, QML is the closest to the core of the system and benefits from the native communication structure, making it the fastest in terms of pure performance. In addition to that, QML can also work its way around various restrictions created by JavaScript engines of the browsers. QML is also used as a wrapper for JavaScript code you can use for coding business rules in the Asema IoT. So, if you are developing for an embedded device or a screen on a machine or a software that your end user will simply install on an IoT device, QML is probably the way to go. However, for every other scenario, you’re probably better off with client-side component-based frameworks. And we’re definitely not discouraging you from mixing and matching.

1.2. Screenlets with QML

Screenlets are widgets or lightweight applications that can be placed on Asema IoT dashboard. They are familiar to programming concepts such as “applets” or “widgets” as each of these rely on underlying operating system to offer standardized methods for development, installation, configuration and removal of the program. The objective of screenlets is to offer developers easier and faster mean to develop desktop and mobile applications for Asema IoT. They can be used to add or augment functionality, create services or just to change the look and feel of the device software. Screenlets are meant to be straightforward, lightweight graphical applications that use underlying interfaces to perform complex operations. However, screenlets can also have multiple screens, menus, user interaction and significant amount of application logic and other features that may be found in a full-blown application software.

Screenlets are a mixture of markup and JavaScript, so most user interface programmers will feel right at home when developing screenlets. The declarative nature of the markup, however, gives a native and much granular set of tools compared to traditional HTML/CSS. Likewise, screenlets are not limited in ways JavaScript apps are on web browsers because as opposed to web apps which run on browser, Screenlets run on device. As they run on device, screenlets also offer better integration possibilities into the underlying hardware and software. Screenlets run on any platform where Asema IoT can be installed. So, if you have installed Asema IoT Dashboard on your smartphone, the screenlet will become the functionality of the Dashboard app. This saves you the trouble of developing a mobile app. Once you have authored a screenlet, you have both desktop and mobile apps done. The so called “contexts”, described in detail later, take care of replicating the environment for you so that all server connections from the mobile interface are handled automatically and transparently.

1.3. Web/ HTML5 views

Asema IoT central is a full featured web server, so it won’t be a huge surprise that you can build applications using the web development technologies you are familiar with. Asema IoT software includes a set of JavaScript libraries that make it convenient to communicate with the backend system, load objects to control and to handle various events and measurements. The software also includes a set of ready-made controls, graphs and other visual elements.

The included libraries help in creating AJAX applications where most of the application logic runs on the browser. In addition to that, the system also features a scripting engine, very much similar to node.js, that allows you to place all or part of application logic to the server itself.

For developers familiar with web development frameworks such as Angular, React or Vue, developing HTML5 applications with Asema IoT is a piece of cake. All you have to do is import one additional library and hook it up to the component model of your application. Examples in this document show you how to do it.

1.4. Reports

Reports are documents detailing the state of a particular system at any given time. While screenlets and web apps give dynamic, real-time and interactive view to the system, reports are more of a static view.

The most common format for reports is the good old PDF. While generating PDFs from a dataset could just be labelled as a “PDF generator”, reporting in Asema IoT is a part of application programming because just like other application formats, reports are programmed. It is true that the PDF only give a snapshot of some data, but the program that creates those reports is a dynamic, scripted application that at some point in execution says, “this is the state I am interested in, show the report”.

Reports in Asema IoT are programmed with the same QML/JavaScript frameworks as used for screenlets. As QML is a descriptive UI language, you can treat it just as a template for the PDF which you design graphically and just apply the template. Likewise, as QML can also contain application code that has all the hooks to the system and its data and services, a PDF report cab be generated by an application that autonomously fetches data from multiple sources, does some rigorous number crunching and then, at the point you desire, frees the view of the state and creates a PDF report.

Chapter 2. Becoming a developer

It’s simple really. You just need to open the Asema IoT Dashboard in developer mode.

The developer mode adds on additional features such as logging and reload abilities, so that you don’t have to close and open the application each time you make changes to your screenlet. In addition to that you also get the ability to manage installed screenlets and get easier interface to install or uninstall screenlets from local or remote source. Likewise, developer mode also allows scaling of the application to various sizes so that it is easier to see how the application behaves in different sizes. Additionally, the developer mode includes the screenlet management dialog that lets you manage the locally installed screenlets, including generating empty stubs for your screenlets to start with. In the developer mode it is also convenient to scale the application window to various sizes and see how the application behaves on different screens. The developer mode is shown below.

To access the developer mode, simply add a -D flag when launching the app. For example, in windows, you can navigate to the installation directory of Asema IoT Central and launch the application with a -D flag. I.e

Asema_IoT_Central_for_Windows.exe -D Similarly, in Linux, from the installation directory of Asema_IoT_Central ./asema_iot_central.sh -D

Chapter 3. Creating and Managing Screenlets

To create a screenlet, you can navigate to the Asema IoT dashboard on your web browser by going to http://127.0.0.1:8080/p/applications/ and logging in with your credentials. There you can navigate into Screenlet apps submenu and go to local store. In the page displayed on your screen you can simply type a name for the screenlet and click on create. In the same page you can also manage your locally installed screenlets or upload the screenlets you have hacked.

Alternatively, you can create your screenlets by creating subdirectories on Asema IoT central home directory. On a Linux you can find the Asema IoT Central home directory at:

/home/<user>/.local/share/Asema IoT Central for Linux/screenlet_installs/local

and on Windows at:

\Users\<user>\AppData\local\Asema IoT Central for Windows\screenlet_installs\local.

The screenlets that you have created through the local IoT dashboard can also be found in the same directory. Once you have created new screenlet via the browser window, you can navigate to the Asema IoT Central home directory to find blank autogenerated files ready for you to play with.

Chapter 4. Creating and Managing web apps

Creating and managing web apps is similar to creating and managing screenlets. In the Asema IoT Dashboard, under the section apps you can find a sidebar menu that says web apps. Click on that and you can type in a name for your app and just like that, your app is created. Similarly, you can find all those apps under or create your new ones in

home/<user>/.local/share/Asema IoT Central for Linux/public_html

in Linux and in Windows platforms, you can find it in

\Users\<user>\AppData\local\Asema IoT Central for Windows\public_html

You can access your public web pages from the “/pub” path of the server. So when you ae running your Asema IoT Central on the localhost with default settings, you can access your web pages on your browser from http://127.0.0.1:8080/pub/<name>.html.

Similar to screenlet management, you also get the options to manage existing web apps or install new ones when you go in the web apps menu in the Asema IoT Central Dashboard.

Chapter 5. Sample Application

As it is customary in our industry, we’ll start off with a good old “Hello World!” on all three views, screenlets, web view and the report. We’ll later expand the “Hello World!” into machine control display that adjusts parameters on a remote machine. On the web app front, this sample will use plain HTMl/CSS with some jQuery. This should be very familiar to most UI engineers. Framework specific examples are shown later in this document.

5.1. Hello World

The following section contains sample code for a “Hello World” app in all three views and brief description about how the code works.

5.1.1. Hello World! Screenlet

Let’s say hello to the screenlet world first. The basic screenlet in this example is a single file, called Screenlet.qml. You can create this with any editor you like or IDEs like the Qt Creator. To create a new screenlet, follow one of the methods mentioned in chapter 3. The screenlet example presented below displays the classic “Hello World!” text in the center of the screen.

import QtQuick 2.3

Item {
    anchors.fill: parent
    function open() {}
    function close() {}

    Text {
         id: helloW; 
         text: "Hello World!"; 
         anchors.centerIn: parent; 
         color: "white"; 
         font.pointSize: 12;
          }

    Text { 
        id: iAmGroot; 
        text: "I am Groot"; 
        anchors.top: helloW.bottom; 
        anchors.topMargin: 10; 
        anchors.horizontalCenter: parent.horizontalCenter; 
        color: "white"; 
        font.pointSize: 12;
         }    
}

Now, when you have done all that, you should get a nice little screenlet in your dashboard like the image below:

5.1.2. Hello World! Web page

As we have already mentioned, Asema IoT Central is also a standard web server. So you can use this to pretty much do everything that a standard web server can, like serving your HTML pages. For the purpose of this example, we’ll create a simple HTML page that just displays Hello World in the middle of your screen.

<html>
<head><title>Hello world</title></head>
<style>
h1 {
    color: #123456;
    font-size: 16px;
    font-weight: bold;
    width: 100px;
    height: 50px;
    text-align: center;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -50px;
    margin-top: -25px;
}
</style>
<body>
    <h1>Hello World!</h1>
</body>
</html>

We’re pretty certain that you know what is going on in that HTML file. It’ll just display the good old “Hello World” in the middle of your screen.

5.1.3. Reports

Finally, let's talk about reports. Though a PDF that displays “Hello World” might not be the most interesting thing to anyone, it's definitely a way to get started. The reports are created using QML templates and because of that may look similar to the “Hello World” screenlet we created earlier in this section. The difference between the screenlet and QML template for reports is that the report has additional property called “context”, one signal and a function called render(). Later chapters will provide more information about all that stuff, but for now let's have a look at the “Hello World!” template.

import QtQuick 2.3

Item {
    
    anchors.fill: parent
    property variant context;
    signal rendered;
    function render(gid) {
        rendered();
    return "OK";
    }

    Text {
         id: helloW; 
         text: "Hello World!"; 
         anchors.centerIn: parent; 
         color: "white"; 
         font.pointSize: 12;
          }

    Text { 
        id: iAmGroot; 
        text: "I am Groot"; 
        anchors.top: helloW.bottom; 
        anchors.topMargin: 10; 
        anchors.horizontalCenter: parent.horizontalCenter; 
        color: "white"; 
        font.pointSize: 12;
         }  
   }

Now that the template is ready, let's talking about using one. To use a template you can upload the file using Asema IoT Central admin interface. When you navigate to Visuals>Templates, you can find an interface to drag and drop your templates and save the templates that you have created. Let’s save the report as “helloworld.qml”.

The report template is now available in the reports of each object. You can test it with a dummy object. If you don’t have one you can go to Objects>Configure and click “Create new object” to start the wizard. Now fill in some values (anything will work for this test). Now when you go back to Object and click on object details, you can find your PDF template there. You can select it and run it by clicking on “Report”.

5.2. Adding Functionality

Now that we are able to create and deploy various views, adding functionality and user interaction to them is the next logical step. For this part, reports will be left out as they are primarily static. However, it is possible to create a functional report. For example, a functional report could, when run, send command to a remote device, wait for the device to answer with its measurement data and display the results directly into a neat PDF report. Reports in Asema IoT are actual applications and can perform all the tasks that Asema IoT applications can do, however, they lack an interactive user interface and therefore are excluded from this section.

5.2.1. Actuator Controller Screenlet

In this application, we will create a slider, link that to a property of some object and control its value with the slider. As Asema IoT binds the properties to actual physical devices via interfaces, controlling the property is equivalent to controlling the device itself. If you want to know more about hardware bindings and how to make those, consult the Asema IoT Hardware Programmer Manual. For the purpose of this manual, just assume it works.

There are all kinds of reusable components available in Asema IoT to develop controllers but we’ll be building one from scratch here so that you can hopefully get a better grab of it. The slider controller comprises of one rectangle that is the frame and another that is the handle. When you slide the handle, it will get values between minValue and maxValue. In this case 0-100.

import QtQuick 2.3
//Outer rectangle or the frame for the slider
Rectangle {
    id: valueSlider
    width: 200; height: 40
    color: "transparent"
    border.width: 3
    border.color: "steelblue"
    anchors.centerIn: parent
    property int maxValue: 0
    property int minValue: 100

//Inner rectangle or the slider

    Rectangle {
        id: handle
        width: 40; height: 40; radius: 8
        anchors.top: parent.top
        color: "steelblue"
        MouseArea {
            anchors.fill: parent
            drag{ target: parent; axis: Drag.XAxis;
            minimumX: 0;
            maximumX: valueSlider.width-handle.width 
            }
//Getting the changed values
        onMouseXChanged: {
            if (drag.active) {
                var sliderValue = Math.round((valueSlider.maxValue - valueSlider.minValue) * (-handle.x) / (valueSlider.width- handle.width));
            }
        }
        }
    }
}

There are a couple things in the snippet above that you might have noticed; the onMouseXChanged is the event that is triggered when the handle moves and sliderValue is the property that changes. It is entirely possible to bind this value to any other property of any other device; say temperature control of the radiator. But before moving to it, let's do the same on web page.

5.2.2. Actuator Controller Web

There are numerous libraries that provide numerous ways to provide slider functionality. Asema IoT being a library independent platform lets you use the library or framework of your preference to complete the task. For this example and for the sake of simplicity, we will not be using any external library in the sample application that follows. However, please note that the sample uses jQuery to capture the event of sliding. (jQuery comes bundled with Asema IoT Central, so you just have to import it). Have a look at the simple slider below:

<style>
    #slidecontainer { width: 200; }
    .slider {
    width: 100%; height: 40px;
    background: #d3d3d3; outline: none;
    }
</style>
<div id="slidecontainer">
    <input type="range" min="1" max="100" value="0" class="slider" id="slider">
</div>
<script type="text/javascript" src="/inc/libs/jquery.min.js"></script>
<script>
    $("#slider").on("change", function() { });
</script>

Now that we are done with creating UI elements for slider controls, we’ll take a look into binding those controls to a device.

5.3. Binding controls to a device

As mentioned in the previous chapters, the various controls developed can be bound to various devices and be used to control those. However, to bind a device to a controller, we need a handle to it i.e we need to load it from the system. There are multiple ways to find and load a device from Asema IoT Central; the proper method depends on your application requirements. In some applications you may want a controller for all devices and in some you may implement filters based on different variables like type, location, name or any other characteristics of the device. In the following example we’ll control a device called “HelloDemo”, so we’ll begin by finding and loading the device.

As usual, screenlets come first:

import QtQuick 2.3
import Jarvis 1.0

Rectangle {
    //Defining context and element  
    property ScreenletContext screenletContext
    property variant element
    …
    //Loading an Object
    function loadedObjectsReady(propertyName) {
        var list = screenletContext.objects[propertyName].list;
        element = list[0];
    }
    
    function open() {
        //Finding the required object from the pool of loaded objects
        screenletContext.objectsReady.connect(loadedObjectsReady);
        objectManager.findObjectsByName(screenletContext, "demo", "HelloDemo");
    }
}

As you can see in the example above, all the devices are loaded as objects. The main philosophy of the system being anything and everything is expressed as an object, even physical devices. So, if you were to get an Arduino, call it “HelloDemo” and hook it up to the system, you’d then have an object called ”HelloDemo” in the pool of objects in the system (Asema IoT Central). We also provide a JavaScript library for WebApps and “context” for Screenlets to load the objects. Once an object is loaded, say our “HelloDemo” object, changing the properties of the “HelloDemo” object will send a command to your Arduino.

Objects in Asema IoT are loaded asynchronously, so all you need to do is tell a system component called ObjectManager to fetch the object(s) and call loadedObjectReady when the object is ready. The resulting object should be loaded into a context called screenletContext which acts as our storage for application data. Once the required object is found and loaded, we select the first one and store the handle to it to a variable called element. Now we have loaded an object from the system that we can read and manipulate and therefore control remotely.

Before proceeding further, lets repeat the same process for the browser interface. It should be noted that manipulating objects via a browser requires Asema IoT libraries. The libraries should be loaded asynchronously after the DOM has finished rendering, async defer will do this for you. The following snippet shows how to do it.

//Loading required libraries
<script type="text/javascript" src="/inc/libs/jquery.min.js"></script>
<script async defer type="text/javascript" src="jarvis-1.0/import?callback=load"></script>

When the libraries load, they also establish the necessary callback connections to the server so that they can listen to events from the system. Once all connections are open, a callback is called. In this case that callback is defined to be load(). The following snippet is what it looks like:

var objectManager = null;
var element = null;

//Load objects when ready
function loadedObjectsReady(propertyName) {
var list = objectManager.objects[propertyName].list;
element = list[0];
}

//Find the required object
function objectManagerReady() {
objectManager.findObjectsByName("demo", "HelloDemo");
}


//Load the object
function load() {
objectManager = new ObjectManager();
objectManager.ready.connect(objectManagerReady);
objectManager.objectsReady.connect(loadedObjectsReady);
objectManager.start();
}

In a similar fashion to screenlets, we tell the ObjectManager to load an element from the pool of objects in the backend and call loadedObjectsReady() once done. The only difference being that ObjectManager on the web browser needs to be started with a start()before things can run.

Now that we have loaded the device, let's finally do some controlling. To control a device, we need to bind the value from our controller to a property of the device we loaded. For this example, we want to control a heater. One of the properties of a heater is it's temperature, and we can bind our slider to control the temperature of the heater and therefore change the property temperature of the heater. The details about the origin of properties and how properties bind to the actual hardware is explained in Hardware Programming Manual and is out of scope of this publication. For now, we assume temperature is a property bound to our device. For the screenlet, we just need to add the following line of code:

element.properties.temperature = sliderValue;

And in web version, we bind the elements to signals as follows:

$("#slider").on("change", function() {
element.setProperty("temperature", this.value);

Now we are all done. The sample application is now functionally ready, it may not have a polished interface but it can fully serve its required functionality. This application can be also run across all platforms, from mobile operating systems to desktop computers and Linux servers. It can be deployed anytime and anywhere from a cloud sever, right to the pocket of the end user and it even works offline.

For the sake of clarity and totality, the complete code for the screenlet app can be found in the snippet below.

import QtQuick 2.3
import Jarvis 1.0
Rectangle {
    id: mainRect
    width: 480
    height: 252
    property ScreenletContext screenletContext
    property variant element
    Rectangle {
        id: valueSlider
        width: 200; height: 40
        color: "transparent"
        border.width: 3
        border.color: "steelblue"
        anchors.centerIn: parent
        property int maxValue: 0
        property int minValue: 100
        Rectangle {
            id: handle
            width: 40; height: 40; radius: 8
            anchors.top: parent.top
            color: "steelblue"
            MouseArea {
                anchors.fill: parent
                drag{
                    target: parent; axis: Drag.XAxis;
                    minimumX: 0;
                    maximumX: valueSlider.width - handle.width
                }
                onMouseXChanged: {
                if (drag.active) {
                    var sliderValue = Math.round((valueSlider.maxValue –
                                      valueSlider.minValue) * (-handle.x) /              
                                      (valueSlider.width - handle.width));
                    element.properties.temperature = sliderValue;
                    }
                }
            }
        }
    }
    function loadedObjectsReady(propertyName) {
    var list = screenletContext.objects[propertyName].list;
    element = list[0];
    }
    function open() {
    screenletContext.objectsReady.connect(loadedObjectsReady);
    objectManager.findObjectsByName(screenletContext, "demo", "HelloDemo");
    }
}

And here is the corresponding snippet for a web app.

<html>
  <head>
    <title>Hello world</title>
    <script type="text/javascript" src="/inc/libs/jquery.min.js"></script>
    <script async defer type="text/javascript" src="jarvis-1.0/import?callback=load"></script>
  </head>
  <style>
    #slidecontainer {
      width: 200;
    }
    .slider {
      width: 100%;
      height: 40px;
      background: #d3d3d3;
      outline: none;
    }
  </style>

  <body>
    <div id="slidecontainer">
      <input
        type="range"
        min="1"
        max="100"
        value="0"
        class="slider"
        id="slider"
      />
    </div>
  </body>
  <script>
    var objectManager = null;
    var element = null;
    $("#slider").on("change", function() {
      element.setProperty("temperature", this.value);
    });
    function loadedObjectsReady(propertyName) {
      var list = objectManager.objects[propertyName].list;
      element = list[0];
    }
    function objectManagerReady() {
      objectManager.findObjectsByName("demo", "HelloDemo");
    }
    function load() {
      objectManager = new ObjectManager();
      objectManager.ready.connect(objectManagerReady);
      objectManager.objectsReady.connect(loadedObjectsReady);
      objectManager.start();
    }
  </script>
</html>

So, about two pages of fairly straightforward code and you have fully customized control application available across all platforms currently in use in mass market. Not bad, eh?

Chapter 6. IoT Objects and their structure

Asema IoT is a remote object manipulation system at its core, therefore, these objects are the main thing the programmers work with when developing applications. The Jarvis JavaScript library and the QML contexts all give the same API for manipulating remote objects, so once you learn the philosopy and the API, UX development is fairly easy across all fronts.

Asema IoT objects s reside in the object pools at each instance IoT server side software. When two systems are connected to each other or an object is connected to physical hardware, Asema IoT software syncs these objects automatically between the systems and the hardware interface. A UX programmer works completely transparently with them irrespective of their location. A security layer determines which objects a remote system may sync into its pool.

The object pool itself notifies applications when something happens with the objects in the pool, for instance when they are modified, added or removed. The application can then decide what it wants to do with this info, including reloading lists of objects or prompting the user of the changes. When a change happens in an object itself, similarly a notification is given. Such notifications propagate automatically across the connected systems so if a value of a sensor changes in some remote location, a local system that has loaded the sensor in its pool will be informed automatically. This notification then changes the state of the object in the pool and that state is notified to the user interface which usually automatically changes its display. The object pool itself notifies applications when something happens with the objects in the pool, for instance when they are modified, added or removed. The application can then decide what it wants to do with this info, including reloading lists of objects or prompting the user of the changes. When a change happens in an object itself, similarly a notification is given. Such notifications propagate automatically across the connected systems so if a value of a sensor changes in some remote location, a local system that has loaded the sensor in its pool will be informed automatically. This notification then changes the state of the object in the pool and that state is notified to the user interface which usually automatically changes its display.

Each object in Asema IoT has a basic structure comprised of three characteristics: properties, metadata and capabilities. Properties are the values that change, in object-oriented programming they could be considered as the variables. Metadata is a set of values that do not change and therefore, unlike properties, they do not have a history either, these would be read-only variables in object-oriented programming. Finally, capabilities describe the features of the object, they are the actions it can perform besides changing the property values, these would be equivalent to functions in object-oriented programming. To sum it up, one could say that metadata describes what an object is, capabilities say what it does, and properties mark its current state. All three characteristics are described below:

6.1. Properties

The properties of an object are available through the properties dictionary of the object or the getProperty and setProperty methods. In QML this can be shortened by simply addressing the name of the property with no need to call a method. So to read a property called "color", one would write c = myObject.properties.color and to write a property likewise myObject.properties.color = c.

6.2. Metadata

The metadata of an object can be read from the meta dictionary of the object or the getMeta method. Again, in QML there is no need to call a method specifically, to read a metadata field called "title", one would write t = myObject.meta.title. Note that because metadata is read-only, there is no set or write method. Metadata can however be edited from the user interface of Asema IoT software, but it is not meant to be write accessed from code. So, if you do need something writable, use a property.

6.3. Capabilities

The capabilities of an object define what it is capable of.

Chapter 7. Native apps with screenlets

Screeenlets, as mentioned in the introduction section, are lightweight applications that run on the Asema IoT dashboard. While any screenlet can scale to the size of the display it is running on, Asema IoT defines three pre-defined roles for the screenlets, which are -Mini Screenlets -Dashboard Screenlets -Full-screen Screenlets Because the standard view of Asema IoT Central and Asema IoT Dashboard is Dashboard format, dashboard role is mandatory while the other two are optional. A screenlet is a bundle of those three roles and therefore comprises one to three distinct application pacakges which usually share same components, graphics and logic.

7.1. Programming a Screenlet

Screenlets in Asema IoT are programmed with QML (Qt Meta Language or Qt Modeling Language), which is a JavaScript-based, declarative language for designing user interface-centric applications. QML has been designed as a part of the Qt user interface framework and offers extensive tools for programming and testing the software fully independently of the Asema IoT. With the developer mode of Asema IoT central and hardware emulation capabilities, a large majority of development work can be done and debugged conveniently in a standard PC environment. As the screenlets are scripted, no cross-compilers or other hardware specific tools are needed at any part of the process.

As screenlets rely fully on the QML syntax, the Qt site at http://www.qt.io and the documentation at http://doc.qt.io are good starting reference for QML programming. You can download the Qt Creator SDK from the Qt site and use it to program your QML apps. Qt Creator is a free tool and can be used to develop screenlets for Asema IoT.

7.1.1. Getting Started

The easiest way to create new screenlets is to open Asema IoT Central in developer mode (add the -D flag to command line). In the developer mode topbar, click on the sreenlet management icon to open the screenlet management dialog.Now, in the Manage tab type in the name of the new screenlet under the Screenlet name form and click on the Create new tab, this will create you the empty files for your new screenlet. The new screenlets will be located at

/home/<user>/.local/share/Asema IoT Central for Linux/screenlet_installs/local/<screenlet name>

and on Windows at

\Users\<user>\Application Data\Asema IoT Central for Windows\screenlet_installs\local\<screenlet name>.

You can use your prefered text editor or Qt Creator to browse to those location and create and edit your own screenlets. The developer mode is described in more detail in section 2.

7.1.2. Importing the libraries

The operating environment and the core of the systems inside each Asema IoT software instance is called Jarvis. Consequently, the API used in accessing all the underlying features is called the Jarvis API. Jarvis offers screenlets a network installer, very similar to app stores used on mobile phones where users can simply pick the screenlets they want and those will be automatically installed. Additionally, the framework includes a metadata format that defines the settings for the screenlets. To sum up, Jarvis offers a comprehensive API that allows programmers to access the underlying hardware and software features of the system.

To access the Jarvis API, you have to import it to your QML file, this happens at the start of your QML file with “import Jarvis 1.0”. This imports various static contexts. ScreenletContext is set during runtime, however, to be able to use it you must include a property to your screenlet for reference. Screenlet will fail to load without this property. In the example that follows, you can see to import various modules and define properties.

//Importing libraries
import QtQuick 2.3
import Jarvis 1.0

//Defining and its properties 
Item {
    id: myScreenlet
    property ScreenletContext screenletContext
}

In the above example we imported the Jarvis Api and also the QtQuick module for additional functionality. You could also import other Qt libraries like Qt Graphical Elements or Qt Positioning if required.

Important

Note that when you make PDF reports or when you program with server side scripting, these two applications have special imports. In PDF templates you must include import Templating 1.0. In server side scripting the correct import is import ScriptingEngine

7.1.3. Authoring Screenlet Code

Since QML is pure text, you can use a text editor of your choice to author screenlet code. Qt Creator itself is a great tool with syntax highlighting support and various editing tools that help spotting errors in code an help increase the overall efficiency. Qt Creator is available for most linux platforms in the package manager and also as a free download for various other platforms at the Qt download page at http://www.qt.io/download/. However, if Qt creator is not your cup of tea, you can use any text editor you like.

7.2. Components of a Screenlet

A screenlet code comprises of following components:

  • the dashboard screenlet file, the only compulsory screenlet, always called “Screenlet.qml”(note the capital S)

  • an optional mini screenlet file, always called “Mini.qml

  • an optional expanded screenlet file, always called “Expanded.qml

  • an XML definition file, always called “screenlet.xml” (note that the s is not capitalized here), that defines application metadata such as automatic settings that are stored in the database, descriptive texts for screenlet directory and help content

  • an optional number of multimedia content, JavaScript code, linked QML files etc. as desired by the developer

A dashboard screenlet always has the size 480px X 272px and can work in either horizontal or vertical screen orientation. You can test various screen orientations with Asema IoT Central as necessary. For more information please refer to section 7.3 “Common User interface interaction elements”.

7.2.1. Dashboard Screeenlets

Dashboard screenlets are the default screenlet file of the Asema IoT. An example screenlet code is displayed below:

import QtQuick 2.3
import Jarvis 1.0

Rectangle {
    id: screenletRect
    width: deviceContext.screenWidth
    height: deviceContext.screenHeight
    color: "white"
    
    property ScreenletContext screenletContext
    
    state: deviceContext.orientation ==
    DeviceContext.Landscape ? "LSC" : "PRT"
    
    function open() {
        screenletContext.numPages = 1;
        screenletContext.currentPage = 0;
    }
    
    function close() {}
    
    Connections {
        target: screenletContext
    
        onCurrentPageChanged: {}
        onActivate: {}
        onDeactivate: {}
        onSleep: {}
        onWakeup: {}
        onCurrentPageChanged: {}
        onSettingsChanged: {}
    }

    Text {
        id: sampleText; anchors.fill: parent
        color: "black"
        text: "This is just simple text on the page"
    }

    states: [
        State {
            name: "PRT"
        }
        State {
            name: "LSC"
        }
    ]
}

The above snippet creates a dashboard screenlet with a text “This is just a a simple text” in the dashboard screenlet. The screenlet in the above snippet also works in both portrait and landscape modes.

7.2.2. Mini Screenlets

Mini screenlets are smaller version of screenlets that work in a similar fashion to widgets in smartphones or ”tiles” in Windows. The mini screenlets usually appear as tiles in the navigation screen and when you click on a tile, it gets a signal of this and can change it’s display, toggle an action or open a larger version of the application. The example below shows a simple mini screenlet with all the necessary imports and connections that allow it to recognize when it has been clicked (onCurrentMiniPageChanged). The snippet below also demonstrates how temperature changes can be tracked with a connection to DeviceContext and then assigned to a variable property using a separate request to the context. The functionality is further explained as we dive deeper into contexts in later sections.

import QtQuick 2.3
import Jarvis 1.0

Item {
    id: miniScreenlet
    anchors.fill: parent
    property double current_temp_in_c : 0
    
    function open() {
    }

    function close() {
    }

    //Defining Connections
    Connections{
        target: model.context
        onSettingsChanged: {
        }
        onCurrentMiniPageChanged: {
            console.log("This mini should now move to next piece of content");
        }
    }

    Text {
        id: temperature_text
        text: current_temp_in_c
        color: "#ffffff"
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.horizontalCenterOffset :
        deviceContext.miniOrientation ==
        DeviceContext.Landscape ? 0 : 18
        anchors.top: parent.top
        smooth: true
    }

//Defining device context connection
    Connections{
        target: deviceContext
        onTemperatureChanged:{
            console.log("Looks like the temperature just changed.");
            current_temp_in_c = deviceContext.getTemperature();
        }
    }
}

7.2.3. Expanded Screenlets

Expanded screenlets are the exact opposite of mini screelets, as they are designed to work well when they are expanded to fit larger screens (that’s where the name comes from) contrary to mini screenlets which basically function as widgets. The key in authoring expanded screenlets is to make them scalable, as the displays, in theory, can be any kind and size of screens and expanded screenlets are supposed to fit those accordingly. Layout should be based almost exclusively on anchors and elements such as font sizes should be defined proportional to screen size and not with fixed values in pixels or otherwise.

To get the size of the screen available for the expanded screenlet, you can read the height and width of the device from DeviceContext with deviceContext.getFullScreeenWidth() and deviceContext.getFullScreeenHeight() methods. This acts as a starting point when you want to make an expanded screenlet that fills all of the available screen real estate. However, in many cases the screenlet size may change during runtime, the view mode may change and the screenlet may be asked to fit into a different sized window due to open or zoom actions. When this happens, the screenlet loader will automatically call a function called setMaxSize(), assuming you have implemented one. To fit various screen sizes, it is recommended to implement this function and modify the elements in the screen accordingly when the function is called.

In the example screenlet below, you can see an expanded screenlet in action and also notice how the function setMaxSize() works. Notice that the input from setMaxSize is simply entered into properties that automatically scale the rest of the elements. For more information about properties and how they are used, refer to section 7.9.3, “Properties, signals and methods”. Likewise, scaling in screenlets is done with a function call to provide additional flexibility to the developer to choose which elements to scale. Connecting elements to automatic properties would require you to juggle with enabling and disabling the properties, which eventually leads to errors as the order of change in the properties is not guaranteed. With a function call, however, you always know which event takes place first and can chose to implement scaling in the elements of your choice. Finally, here is the example expanded screenlet:

import QtQuick 2.3
import Jarvis 1.0
Item {
    id: screenletframe
    width: deviceContext.getFullScreenWidth()
    height: deviceContext.getFullScreenHeight()
    property ScreenletContext screenletContext
    
    function setMaxSize(max_x, max_y, preserve_aspect) {
        if (screenletframe.width > max_x ||
        screenletframe.height > max_y) {
            screenletframe.width = max_x;
            screenletframe.height = max_y;
        }
    }

    function open() {
    }

    function close() {
    }
    
    Text {
        id: hw
        anchors.verticalCenter: parent.verticalCenter
        anchors.horizontalCenter: parent.horizontalCenter
        color: "#ffffff"
        font.pixelSize: parent.height - 300
        font.bold: false
        smooth: true
        text: "Hello world!"
    }
}

7.3. Common user interface interaction elements

The user interface of an Asema IoT application has a set of common elements that can not be changed by screenlets. These are:

  • The top bar : the top bar contains ttatus icons to the system, an indicator of current screenlet and the “E” button serves as the home button in Asema IoT

  • The right menu and menu icons for each dashboard screenlet: The right menu is used for navigation within an screenlet. Note that you can not change the right menu, but you can replace it if you want. This is done by blocking the default menu and showing a custom menu on it’s place. Further details into how this can be done are discussed in section 7.13. “Custom page navigation

Note that the user may chose from a set of methods on how s/he prefers to interact with the display. For instance, navigation can be done by using the proximity sensors on the sides of the screen or by traditional touchscreen buttons. Touchscreen buttons, as we know, use some of the free screen area. The buttons are always placed in bottom right and bottom left corners in landscape and at top left and bottom left corners in portrait mode. Therefore, you should leave these areas devoid of any content to prevent navigation overlays. Below is the image of a sample screenlet with its controls:

The default color for touchscreen buttons is white. So if your screenlet uses a mainly dark background, your screenlet should look pretty good with the defaults, however, if you use a white or a pale background, you can tell the interface to invert the colors. When you invert the colors, the buttons will appear dark blue, to make that happen just call deviceContext.setInverted(true) in the open() function of your screenlet. Likewise, you can also detect and react to changes in screen orientation. The following example snippet does just that

import QtQuick 2.3
import Jarvis 1.0

Item {
    id: myScreenlet
    width: deviceContext.screenWidth
    height: deviceContext.screenHeight
    
    property ScreenletContext screenletContext
    
    state: (deviceContext.orientation ==
    DeviceContext.Portrait) ? 'PRT' : 'LSC'
    
    Text {
        id: sampleText
        text: "My sample"
        x: 40
        y: 30
    }
    states: [
        State {
            name: "PRT"
            PropertyChanges {
                target: sampleText
                x: 30
                y: 100
            }
        }
    ]
}

7.4. Setting up navigation

Jarvis will automatically generate the user interface for navigating between various screens of your screenlet. It will offer the necessary navigation arrows and guides and signal your screenlet whenever the user activates any of these. All you need to do is:

  1. Tell the system how many screens your screenlet has and define the screen which is loaded at startup

  2. When a signal comes, connect to the signal and change the screen with whatever method is suitable for your application logic.

To tell the system how many screens your screenlet has, you must set the screeenletContext.numPages and screenletContext.currentPage properties. The following example shows a screenlet with five pages in which the navigation starts from the second screen when the screenlet opens. Note that it’s perfectly legal to change these properties even when the screenlet is running, making it possible to dynamically expand the screenlet. The snippet below is an example:.

function open() {
    screenletContext.numPages = 5; //Number of Pages
    screenletContext.currentPage = 01; //Starting page
}

In the above snippet, you could see that the screenlet has 5 pages and the starting page is 1. To navigate these pages, plug into the onCurrentPageChanged signal of the ScreenletContext. The same applies to mini screenlets as well. If you have multiple pages in your mini screenlet, set model.screenletContext.numMiniPages to provide the number of mini pages at the open() function. For more detail on how to connect to navigation signals, see section 7.7, “Reacting to navigation events”.

7.5. Applying graphical design

QML is a declarative markup language intended originally for defining scalable user interfaces, so it is not a huge surprise that there is a significant amount of features dedicated to just that. A very comprehensive reference about it can be found at http://doc.qt.io/qt-5/qtquick-index.html. A few examples are provided in this document just to illustrate the possibilities. Let’s for example have a look at the snippet below:

import QtQuick 2.3
import Jarvis 1.0

Item {
    Rectangle {
        id: canvas
        anchors.fill: parent
        color: "transparent"
        border.color: "white"
        radius: 5
        Image {
            id: canvasBackground
            source: "img/backgrounds/background_default_1.png"
            fillMode: Image.PreserveAspectCrop
            width: screenletframe.width
            height: screenletframe.height
        }
    }
    Rectangle {
        id: rect
        anchors.top: parent.top
        anchors.topMargin: 50
        height: 100
        width: 100
        color: "lightgreen"
        opacity: 0.6
        radius: 8
        layer.enabled: true
        layer.effect: DropShadow {
            horizontalOffset: 3
            verticalOffset: 5
            radius: 8.0
            samples: 16
            color: Qt.rgba(0, 0, 0, 0.5)
            source: rect
            transparentBorder: true
        }
    }
}

The example snippet above draws two rectangles on the screen, one that covers the whole screen and one that is 100px X 100px in size. The larger rectangle has an image in it while the smaller one has a drop shadow and is slightly translucent. This shows some of the graphical capabilities of the platform such as the ability to add, rotate and manipulate images on the fly and manipulate opacity of elements and add graphical effects such as shadows. Numerous more examples can be found in the official QML manual.

Likewise, the above snippet also shows the layouting principles of QML. Objects can be hardcoded into certain size and position (similar to position: absolute in CSS), but the real power of QML comes from anchoring elements. When the position and size are anchored, they are always relative to other elements, especially the root element, the screen. Therefore, it is relatively straightforward to use anchors to design user interfaces that scale automatically and nicely to screens, rotate automatically etc. Likewise, QML also has extensive support for user interaction, including controls and inputs, mouse areas and gestures. It is therefore easy to make modern user interfaces, that can be pinched, swiped or rotated, using QML.

7.6. Reacting to system events

If you intend to run screenlets in a resource constrained device, it may be useful to follow certain conventions in reacting to what the Jarvis system tells the screenlets to do in order to ensure expected screenlet behavior. Most of this is related to gracefully suspending the screenlet when nothing is happening, thus saving processing resources and system memory.

7.6.1. Loading and Unloading

In most scenarios, it is desirable to set a certain set of initial values and load further data or perform a set of actions when a screenlet loads. As screenlet loading runs within a JavaScript engine and is asynchronous in nature, resources may appear to be used in a non-deterministic way. To solve the problem of initialization, Jarvis automatically calls a function called open() after the ScreenletContext property has been set up. This makes it possible for the instance to fetch setting s in a safe manner. Therefore, the recommended way to initialize a screenlet is to do any initializations that involve settings is to include them in the open() call. Note that you can choose to not implement open() at all, doing this simply skips the function call.

The similar procedure applies to the unloading process as well. If you need to clear things up or save any data upon unload, you can perform those actions in close() function. close() function is guaranteed to be called before the context unloads.

7.6.2. Starting, Stopping, Sleeping and Waking up

When the screen of a device goes to sleep state or activates the screensaver- screenlets should suspend actions that simply present data to the users. For example, if you run a timer that regularly polls data and displays it, you may want to suspend the application until the display is on again, there is no use displaying the data when nobody is watching. To activate and deactivate the applications, you can use the onActivate and onDeactivate events of the ScreeenletContext. The snippet below shows how you can connect to onActivate and onDeactivate events.

import QtQuick 2.3
import Jarvis 1.0
    Item {
    id: myScreenlet
    property ScreenletContext screenletContext
    Connections {
        target: screenletContext
        onActivate: {
            // activate your logic here
        }
        onDeactivate: {
            // deactivate timers etc
        }
    }
}

The code block above shows how to catch onActivate and onDeactivate events and perform desired actions when those events occur.

7.7. Reacting to navigation events

Navigation events are triggered and automatically signaled to the screenlet when the user performs an action to change the page. The method to handle navigation events is also straightforward, you must follow thee onCurrentPageChanged event which tells you the page your screenlet should be in. The page number is calculated automatically from the number of pages provided to the ScreenletContext. By conventions of QML, any number of pages for a screenlet should be stored in the same model. To switch to the correct page, you must change the index of the model to the page corresponding to the received signal. The snippet below shows the method

Connections {
    target: screenletContext
    onCurrentPageChanged:
    myScreenletPages.currentIndex = currentPage
}

In the above snippet you can see the example of handling navigation. However, it must be noted that you need to set the number of pages for this to work. Failure to provide the number of pages for your main and mini screenlets will mean that they do not receive page change notifications.

7.8. Contexts

Contexts are the system services that give you the access to the backend capabilities of Asema IoT. They are the libraries that you use to interact with the system and each of the contexts offers you an API that you can access. Asema IoT is a distributed system and each context in the system operates as its own scope, providing deeper view into the network of systems and devices. The names of the contexts describe what type pf access the context offers. Each context in Asema IoT represents one instance, hence the name context. The names and scopes of various contexts and their purpose is described in this section. The details about actual methods in each context and their API is out of scope of this manual, please refer to the separate Context API manual for further references.

7.8.1. Context names and scopes

As mentioned above, contexts allow the developer to use the backend capabilities of Asema IoT. Once imported and loaded, an application can listen to and interact with those contexts. There names and scopes of various contexts are described below:

  • ObjectContext: This is likely the context you’ll be working with the most. ObjectContext gives the access to the global object pool, the place where all the objects that represent various connected devices are stored.

  • systemContext: This context tells you about the current state of the system such as loaded screenlets. As a context represents single instance, being connected to one backend would provide you with one screenletContext. Likewise, if you interact with multiple backend systems, you may have multiple systemContext contexts, each representing one backend system.

  • deviceContext : This context gives access to everything related to a particular device, Each device usually has just one deviceContext and it tells you about stuff like CPU load, status of LEDs, status of Wifi or Bluetooth connection.

  • screenletContext: This context contains all the data about the corresponding screenlet and the interactions it can perform. Each screenlet has its own screenletContext and a screenlet can only read it’s own screenletContext. screenletContext automatically loads the settings available to the screenlet as defined in the XML definition of the screenlet and also tells you which page the screenlet should be in, whether it may be the result of navigation, sleep or wakeup.

  • GeoContext: GeoContext is the interface to all the geographical capabilities of the system and is an indispensable tool when coding apps with maps or geographical data. GeoContext knows, for example, how to calculate the distance or a route between two given points.

  • -PluginContext: PluginContext gives access to any extension or plugin that may have been included in the system. Plugins are often used automatically by drivers to enhance the functionality of thee system. However. If you need to point to a certain plugin directly and manually, this should do the trick.

7.8.2. Properties, signals and methods

Variables in the contexts are implemented as properties, meaning they are connected to the signaling core of the system. Because of that connection, whenever something happens in the system, models, graphical elements etc that have been assigned a value from a property will update themselves automatically. This eradicates the necessity to implement polling timers or worry about interrupts and callbacks and that kind of stuff.

The example code below will create two pieces of text on screen. The first one is clickable. When you click it, it displays "Please wait" briefly and then "Relay is ON" or "Relay is OFF" depending on the change the relay makes. The second text will automatically display the power consumption measured of an appliance. As consumption changes, the text changes automatically.

Text {
    id: statusText
    width: 50
    height: 10
    text: (relay.state==1 ? "Relay is ON" :
    ((relay.state==2 || relay.state==3) ?
    "Access denied" : "Relay is OFF"))
    MouseArea {
        anchors.fill: parent
        onPressed: {
            statusText.text = "Please wait.."
            device.flush()
            if (modelData.state == 1) {
                relay.turnOff(); }
            else { relay.turnOn(); }
        }
    }
    Text {
    id: consumptionText
    anchors.top: statusText.bottom
    width: 50
    text: relay.consumption
    }
}  

Note that while properties are powerful way to program interactive and smooth user interfaces, using them might take a bit of getting used to. Properties look a lot like JavaScript variables and you can use them in any construct, so it is tempting to use them as variables. However, that would be a bad idea as properties are dynamic. One such example is using a property for looping, since the value of the property is liable to change during the loop or because of the loop, it may result in some really weird effects that could be hard to debug. It is perfectly cool to use properties for conditionals or formulas as that may create some really powerful effects. However, it must be remembered that as properties are dynamic, the formulas and conditionals will be re-evaluated each time the property changes, so don’t count on that if statement to always yield the same result.

To avoid unexpected errors, especially in the logic code, it is advisable to use proper JavaScript variables instead of property and copy the value of the property into a variable for necessary processing when required. In the snippet below, we’ll develop a simple power meter.

Text {
    width: 100
    height: 20
    anchors.verticalCenter: parent.verticalCenter
    anchors.horizontalCenter: parent.horizontalCenter
    color: "white"
    font.pixelSize: 24
    text: myMeter.properties.currentInstantPowerInWatts
}

The above example demonstrates how properties can be used to create key functionality with very little code. The example will display a white text sized 24px in the middle of the screen that displays the power reading from the device loaded as “myMeter”. There is no need for separate application logic as everything is handled by property. No polling or updating the text is required as the text updates automatically when a new reading arrives. Additionally, as myMeter.properties.currentInstantPowerInWatts is a number, you can also use it to create graphs or perform some color changes to create a bit more of a flare.

7.9. Settings

Settings for a screenlet are defined in screenlet.xml file inside the screenlet’s directory. The XML file contains the metadata for the settings that you want the screenlet to have. This section makes you familiar with defining settings for your screenlet.

7.9.1. Defining settings in XML

As mentioned above, the settings for a screenlet are defined in an XML file. The contents of this file are used to create necessary database fields for storing the settings to a persistent storage. These values are used to configure user interfaces and then render appropriate forms for users to edit these values (you can provide defaults when needed). Once the screenlet loads, settings are fetched automatically from storage and are available to your screenlet in the screenletContext.settings.

For each setting you can define the following:

  • name: The name of the setting as it appears to your code.

  • label: The label added to the form that is presented to users while editing i.e. the name as it appears to the user. Note that the label can be localized by adding the language string per each desired language.

  • type: Data type of the setting to determine what kind of form control is rendered to the user. The value can be one of the following: string, integer, float, list, radiobuttongroup, checkboxgroup.

  • visible: Whether the setting is visible to the user. By adding non-visible settings that have default values, you can store dynamic parameters to your code.

  • editable. Whether the setting can be edited by the user. A non-editable setting is presented to the user but with controls disabled.

  • default_value: Default value for the setting.

Settings that offer user multiple choice (list, radiobuttongroup, checkboxgroup) can have further list of those items in XML tagged with list_item, radiobutton_item and checkbox_item respectively. The example below shows how settings of various types are defined.

<settings>
    <name>ExampleSettings</name>
    <setting>
        <name>just_a_string</name>
        <label>
            <fi_FI>Tekstiä</fi_FI>
            <en_US>Some text</en_US>
        </label>
        <type>string</type>
        <visible>True</visible>
        <editable>True</editable>
    </setting>
    <setting>
        <name>just_an_int</name>
        <label>
            <fi_FI>Kokonaisluku</fi_FI>
            <en_US>Integer</en_US>
        </label>
        <type>integer</type>
        <default_value>100</default_value>
        <visible>True</visible>
        <editable>True</editable>
    </setting>
    <setting>
        <name>list_of_things</name>
        <label>
            <fi_FI>Lista asioista</fi_FI>
            <en_US>List of things</en_US>
        </label>
        <type>list</type>
        <visible>True</visible>
        <editable>True</editable>
        <list_item>
            <type>String</type>
            <label>Name</label>
            <default_value></default_value>
        </list_item>
    </setting>
    <setting>
        <name>radiobutton_group</name>
        <label>
            <en_US>Select one</en_US>
            <fi_FI>Valitse yksi</fi_FI>
        </label>
        <type>radiobuttongroup</type>
        <visible>True</visible>
        <editable>True</editable>
        <radiobutton_item>
            <type>String</type>
            <value>1</value>
            <label>
                <en_US>One</en_US>
                <fi_FI>Yksi</fi_FI>
            </label>
            <selected>True</selected>
        </radiobutton_item>
        <radiobutton_item>
            <type>String</type>
            <value>2</value>
            <label>
                <en_US>Two</en_US>
                <fi_FI>Kaksi</fi_FI>
            </label>
            <selected>True</selected>
        </radiobutton_item>
    </setting>
    <setting>
        <name>checkbox_group</name>
        <label>
            <en_US>Select multiple</en_US>
            <fi_FI>Valitse useita</fi_FI>
        </label>
        <type>checkboxgroup</type>
        <visible>True</visible>
        <editable>True</editable>
        <checkbox_item>
            <type>String</type>
            <value>1</value>
            <label>
                <en_US>One</en_US>
                <fi_FI>Yksi</fi_FI>
            </label>
            <selected>True</selected>
        </checkbox_item>
        <checkbox_item>
            <type>String</type>
            <value>2</value>
            <label>
                <en_US>Two</en_US>
                <fi_FI>Kaksi</fi_FI>
            </label>
            <selected>True</selected>
        </checkbox_item>
    </setting>
</settings>

The above example shows a settings file with various settings along with localization options.

7.9.2. User access to settings

User access to settings happens through the settings form of the screenlet. The settings form is available either directly from the from the configuration interface of Asema IoT software, or the web admin view. The settings form is rendered automatically based on the metadata of the screenlet defined in the settings definition XML. It will automatically choose the appropriate controls for each data type and perform validation and saving to permanent storage.

Note that the settings can be changed while the screenlet is running, so it’s cool if the user changes few settings here and there from a secondary display or a remote-control web interface while the screenlet is running. You should therefore implement proper methods to track screenlet changes (this can be done by connecting to the screenletContext) and performing any reloading or reconfiguration once the signal is received.

7.9.3. Monitoring changes in settings

If your screenlet should change automatically when the user changes settings, possibly remotely though a web interface, then use the onSettingsChanged signal of ScreenletContext to do this. When you get the signal, the new settings have already been loaded to screenletContext.settings and are safe to use.

7.10. Graphic Canvas

The graphics canvas lets you draw arbitrary graphics on screen. In a Jarvis system the most common use for the canvas is drawing various charts that display energy use statistics. The canvas is an implementation of QML Canvas from Qt Labs. You can find full documentation and examples on how to draw on the canvas by searching it from Qt Labs website. To use the canvas, you must import the qmlcanvas library from “import "../../../../jarvis_app/lib/qmlcanvas/Canvas" ”. In the snippet below shows the method to import the library as well as to use the graphic canvas.

import QtQuick 2.3
import Jarvis 1.0
import "../../../../jarvis_app/lib/qmlcanvas/Canvas"

Canvas {
    id: graph
    width: 100
    height: 100
    x: 30
    y: 30

    function draw() {
        var ctx = getContext();
        ctx.strokeStyle = "rgba(255, 255, 255, 0.5)";
        ctx.lineWidth = 3;
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.lineTo(10, 30);
        ctx.lineTo(15, 45);
        ctx.lineTo(90, 90);
        ctx.stroke();
        ctx.closePath();
    }
}

7.11. Localization

Screenlets use the i18n framework of Qt so they are easy to localize by using standard tools from the Qt project. To localize a screenlet you must wrap each localizable string inside a qsTr function and then provide the .qm localization files for each language (see Qt documentation for more details). The localization files will be loaded automatically by Jarvis system. You need to strictly follow the naming convention of the files to make this possible. The convention is <screenlet name>_<language code>.qm. So, for instance if your screenlet is called "MyScreenlet" and you localize it in Dutch (thus the language code is nl_NL), the name of the localization file is MyScreenlet_nl_NL.qm. Don't forget the underscores!

For more information on the format of localization files, how to extract translation strings from the code and using tools like Qt Linguist to make the translations, please refer to Qt user guides.

7.12. Improving responsiveness

If your screenlet performs tasks that require heavy processing or simply take a long time to finish, you may notice that the user interface does not always react to the user input as desired. For example, if your screenlet loads data from a server when the end user presses a button, you may notice that the pressed effect doesn’t show until the task is complete. This renders the press effect useless as its purpose is to show that an action has begun and not that it has ended. The reason for the behavior is that events, including updating the button graphics and making the network call, go into the event loop of the user interface. Usually updating the screen ends up in the event queue after the network call and will be blocked by the network until completion.

To solve the issues mentioned above, you should flush the event queue after the button press event and before the network call. This way the user interface is updated immediately, and the event queue is empty, and you can then add a network call. Flushing can be performed by simply calling deviceContext.flush(). The example snippet below is an example:

MouseArea {
    anchors.fill: parent
    onClicked: {
        loadingIndicator.text = "Loading...";
        deviceContext.flush();
        systemContext.serverServiceRequest(...);
    }
}

The example snippet above makes sure it first sets the text on screen to “Loading…” before making the network call.

7.13. Custom page navigation

We all know that simple next/previous is not enough for all screenlets, so custom page navigation is there to address this issue and to add additional controls, for example up/down, enter/leave or rewind/forward and the like to your screenlet.

If your screenlet requires such navigation tools, you need to implement those as custom layers from the scratch. The user interface helps you in plugging your custom navigation to the event system so that you know when to display your custom navigation. The first thing you need to do to display your custom navigation is to disable the existing menu to avoid any conflicts. To disable the right menu, call screenletContext.preventPageButtons() in the open() function of your screenlet. Please note that the page buttons are enabled again automatically when the user navigates away from your screenlet. Likewise, to display your navigation when the user needs it, connect to the onNavigationRequested signal of the screenletContext and run whatever methods are needed to display your custom layer.

7.14. Manipulating the screensaver

In some cases, you may want to create a screenlet that stays on all the time and displays its information without being interrupted by the screensaver that turns of the screen. In this case you can simply disable the screensaver. This is done by calling deviceContext.disableScreensaver() in the open() function of your screenlet. The screensaver will be automatically enabled when the user navigates to some other screenlet. That is if the other screenlet does not disable screensavers like your last one.

7.15. Storing data temporarily and permanently

Data storage and manipulation is one of the major requisites of modern functional applications. In this section we will dive deeper into how to store data in Asema IoT systems.

7.15.1. Data cache

The data cache is a temporary data storage that keeps the data in the context of the screenlet. As contexts remain in memory when the user switches from one screenlet to the other, the data cache can be used to for instance keep a "session" in force. The data is unloaded when the user enters the configuration settings or reboots the Asema E.

The data cache is a dictionary (map) so you write values to it using a key-value pair and read with the key. The methods for accessing the cache are screenletContext.readFromCache(QString key) to access a value associated with the provided key and screenletContect.writeToCache(QString key, QVariant value)to write key-value pairs to the cache.

7.15.2. . User data

User data is stored in permanent storage. When you write any new user data, it is immediately flushed into the database of the Jarvis system backend. User data is regarded as one block of information and stored accordingly in the database. Your screenlet is responsible for parsing the data after it loads and for assembling multiple items into a single object before storage. If you write two values into the database with two distinct calls, the data from the latter call is persisted.

To read and write user data, you can use the methods screenletContext.getUserData() to get user data and screenletContect.setUserData(QString value) to set new user data.

Chapter 8. Web Applications

Web applications are probably the most popular and easiest way to take your application offerings to the masses. Asema IoT is n HTTP server that offers JSON REST and WebSockets API for your web application to interact with. If you have programmed REST/Ajax applications before, this is a very familiar and non-restrictive environment to work with, so this should be a cakewalk.

Asema IoT Central and Asema IoT Edge can serve files over HTML so it can serve self-contained applications that need no other server. Because the server "just" serves files and is framework agnostic, you can include whatever browser side libraries you wish into the applications. However, Asema IoT does not do any server side processing a la Node.js and is not intended to replace application servers found in Java or .net. Its sole intention is to provide some of this functionality in the form of server-side scripting.

With server-side scripting you can program small "servlets" capable of processing application logic, but it is not primarily meant for that purpose. The objective of server-side scripting is to allow the developer to set up some IoT logic onto the server side (such as constantly running state monitoring or communication between client applications). If you need heavy-duty application programming on the server side, it is recommended to use a dedicated application server. Asema IoT, being platform agnostic, works fine with it and also offers multiple options for integrating these both on server and client side.

In its simplest form, all you need to do server files through Asema IoT is to store html pages into the public_html directory of the software and browse them. Of course, this is not yet much of an IoT application so let's explore further what the system does to enable this. The magic lies in the JavaScript library bundle that comes with the software. It helps in managing the objects and integrating them into the rest of your application logic. In this section we will explore the capabilities of the Asema IoT platform and its libraries and use them to create functional IoT web apps.

8.1. Common program flow

As Asema IoT is framework agnostic, working with it always follows the same logic flow, irrespective of any front-end libraries or frameworks used. The purpose is to get from the IoT system a hook to the objects you want to manipulate and/or monitor. To help in the process, you use the "Jarvis" library which you can import from Asema IoT Central and Asema IoT Edge.

The Jarvis library includes a component called ObjectManager which acts as your hook to the backend of the Asama IoT. With the object manager you can load instances of objects and the API of these objects that lets you manipulate the properties, history of data, and meta information of objects.

The process to get a handle to the object is mentioned below:

  1. Initiate the ObjectManager instance and connect its signals to know when it is ready to load an object or when loading an object is complete.

  2. Start the ObjectManager, the library will then start to establish connections to the backend IoT logic. Once the connection is complete, it will inform you of the completion with a callback signal.

  3. Invoke one of the search functions of the ObjectManager to find the desired objects from the object pool in the backend. You can search for these objects by name, type and various other identifiers. The result of this search is called object bundle and can contain one or multiple objects.

  4. Once the search is complete, the ObjectManager will inform about this with a callback. In the callback, you can pick the objects you want from the object bundle and store them to local variables of your application.

  5. Start listening to the value signals of the objects to get data and use property setters of the object to control them. Connect these to the controls that you have built in your application and you are all set.

8.2. The Jarvis library

The Jarvis JavaScript library is a package that lets you automatically connect to the Asema IoT API. As it loads, it also establishes a Websocket connection with the server in order to receive notifications. Applications that use the Jarvis library can see the notifications through signal abstraction.Details about importing the Jarvis library can be found in section 5.3.

8.2.1. Reusing system components

8.2.2. Loading Objects

Loading objects to the interface is necessary to be able to manipulate objects. Loading objects in done with ObjectManager. As the contents of the objects may be in a galaxy far far away, the ObjectManager works asynchronously. Once the ObjectManager receives the required data, it calls a callback.

Multiple sets of objects can be loaded simultaneously. Each loading operation is identified by an identifier that can be freely chosen. The code snippet below details the steps involved in creating an ObjectManager, starting it, connecting it and loading two sets of objects. Note how in the find command an identifier firstSet and secondSet is given so that the callback loadedObjectsReady recognizes which objects it received.

var objectManager;

function loadedObjectsReady(propertyName) {
  if (propertyName == "firstSet") {
    console.log("Found freezer");
    var freezers = objectManager.objects[propertyName].list;
  } else if (propertyName == "secondSet") {
    console.log("Found buildings");
    var buildings = objectManager.objects[propertyName].list;
  }
}

function objectManagerReady() {
  objectManager.findObjectsByName("firstSet", "Freeezer");
  objectManager.findObjectsByMetaType("secondSet", "smartapi:Building");
}

function load() {
  objectManager = new ObjectManager();
  objectManager.ready.connect(objectManagerReady);
  objectManager.objectsReady.connect(loadedObjectsReady);
  objectManager.start();
}

In the above snippet, we created an ObjectManager and loaded two objects from the object pool.

8.2.3. Manipulating Objects

Once an object is loaded, it can be manipulated by setting its properties. The changes in properties are linked via the device driver to the physical property that defines them. In this example, lets consider we want to control the refrigerator by increasing its temperature by 2 degrees. In this case, there is also a metadata file that defines what the maximum temperature. The following snippet may serve as a reference.

var currentTemp = freezer.getProperty("temperature");
var maximumTemp = freezer.getMeta("maxTemperature");
if (currentTemp+2 < maximumTemp)
freezer.setProperty("temperature", currentTemp+2);

8.2.4. Reacting to signals and events

When a property of an object changes somewhere in the system, it is propagated automatically to the user interface objects, which in turn send a signal. You can capture this signal by connecting a callback to it.

If we take our old refrigerator as an example again but this time instead of controlling the freezer, we monitor it and if the temperature exceeds the defined maximum, we show an alert to the user.

var freezer;
function freezerPropertyChanged(key, value) {
  if (key == "temperature") {
    if (value > freezer.getMeta("maxTemperature"))
      alert("The freezer is running too hot!");
  }
}
function loadedObjectsReady(propertyName) {
  freezer = objectManager.objects.freezers.list[0];
  freezer.propertyChanged.connect(freezerPropertyChanged);
}

8.3. Old School static HTML and AJAX

While the old school static web pages may lack the bells and whistles of modern web applications and may seem outdated, they still do have a place in the world. Let’s say you just want to build a couple of controls for a remote device, you might not want to create an elaborate web app for that purpose when a simple web page or two would suffice. By simple we don’t intend to imply that your use of IoT is simple. You could be running a state of the art data analysis or sophisticated mathematical modeling in your back end but that does not necessarily require the development of complex and feature rich front end interfaces. Please refer to section 4 for details about creating and managing web apps.

8.4. Django style templated applications

Your web applications may grow over time and it may lead to difficulty in managing static web pages. If you refuse to build on top of client-side frameworks, Asema IoT offers you a way to split the user interface into templates on the server side. So, if you have worked with templating engines before, this approach should feel familiar. Python Django offers such functionality, so if it has been your main tool in the past, this should be a cakewalk for you.

In this approach you split the user interface into a template hierarchy and place lower level pages inside the so-called blocks of upper level pages. For example, in a scenario where all pages should have a common header and footer. There is no sense in copying the markup of those two to every page. Instead they are in a template which is applied to each page as it renders on the server.

Your templates should reside at public_html/templates on your Asema IoT Central installation. Once stored there, they are automatically picked up when a page that uses templates is recognized. As an example, below is a page that defines a template that has the header and the footer defined and open blocks for the page specific content:

<!DOCTYPE html>
<html>

<head>
    <title>{% block title %}{% endblock %}</title>
</head>

<body>
    <div id="header">
        <ul>
            <li><a href="#">Home</a></li>
            <li><a href="#">Products</a></li>
            <li><a href="#">Solutions</a></li>
        </ul>
    </div>
    <div id="content">
        {% block content %}{% endblock %}
    </div>
    <div id="footer">Copyright © 2018 Acme Ltd</div>
</body>

</html>

In the above snippet, there is a template that has a common header with navigation and a common footer that will appear on all pages using the template. We also have two blocks: title and content. A block is defined by {% block [block id] %}{% endblock %} tag. To make this template available, save it as public_html/templates/headerfooter.html.

To use the template, create another page that has an extends directive at the top. This will tell the rendering engine that you want to use a template. The snippet below shows such example

{% extends "templates/headerfooter.html" %}
{% block title %}ACME Corporation{% endblock %}

{% block content %}
<p>ACME is the leading supplier of Roadrunners and Coyotes.</p>
{% endblock %}

On the page in the above snippet, the blocks instead of being empty, contain the content of that block as defined in the template. When the server encounters such a block, the content is filled in. Hence, this page will render onto the browser as shown below.

<!DOCTYPE html>
<html>

<head>
    <title>ACME Corporation</title>
</head>

<body>
    <div id="header">
        <ul>
            <li><a href="#">Home</a></li>
            <li><a href="#">Products</a></li>
            <li><a href="#">Solutions</a></li>
        </ul>
    </div>
    <div id="content">
        <p>ACME is the leading supplier of Roadrunners and Coyotes.</p>
    </div>
    <div id="footer">Copyright © 2018 Acme Ltd</div>
</body>

</html>

The templates can have a hierarchy making it possible for a template to use the extends directive to another template. When a template inherits another template, say the base template, it is allowed to use and override common elements defined in blocks in the original template. An open block can reside either inside a block that refers to a top block or outside it.

8.5. jQuery

jQuery is one of the most popular JavaScript libraries. It is a DOM manipulation library that conceptually sits between a pure static page application and a client-side framework driven application. It does not offer the components that the component driven frameworks do but it does offer a whole lot of tools for making interface controls and plugins and in general interacting with the user interface.

Since jQuery is “just” a library, it offers quite a lot of flexibility in how it used. It does not need to be compiled or bundled in, which makes the learning curve easier. It works both with static pages and templated server-side approach and to some extent also with client-side frameworks (most client-side frameworks maintain their own shadow DOM model so jQuery really cannot modify that without some hacking). It is however recommended that you be careful when structuring the application in order to keep it maintainable.

Asema IoT uses jQuery in the admin interface of the Asema IoT Central and Asema IoT Edge so the library is readily available directly out of the server. The sample application using jQuery is provided in section 5.2.2.

8.6. Component framework driven applications

8.6.1. ReactJS

React JS is one of the most popular and rapidly growing frameworks. To get your React application working with Asema IoT, you need to find the hooks to the objects used by your application and bind them to the properties (props) of your UI components. This section will guide you to the steps involved in getting your React app neatly loaded in the system.

The very first step to creating any web app is to include the Jarvis library in your project. It is recommended that you do not bundle these files into main application bundle as version control is easier without explicit linking. The Jarvis libraries are loaded from the server to ensure that they are compatible with the API version hosted by the server.

The Asema IoT libraries are written in pure old school JavaScript, because of this, the easiest way to import the libraries is to put them in the index.html of your React application. Please note the /iot prefix in the import paths. See 8.7, “Using an HTTP proxy” for how and why this is used. The example snippet below shows a sample index.html file.

<!doctype html>

<head>
    <title>React example</title>
</head>
<script type="text/javascript">
    var objectManager = null;
    function load() {
        objectManager = new ObjectManager();
    }
</script>

<body>
    <div class="container">
        <div id="root"></div>
    </div>
    <script type="text/javascript" src="/iot/settings.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/network/json.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/network/websocket.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/objects/signal.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/objects/connectedobject.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/objects/objectmanager.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/objects/dataanalysismanager.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/objects/pluginmanager.js"></script>
    <script type="text/javascript">load();</script>
</body>

</html>

In the above example, note that one ObjectManager instance is created directly in the index. This is the simplest way to get an instance of ObjectManager running, this instance will reside in the window context of the application. Now we will proceed to link those objects to the UI components.

The snippet below is an example of a core React application (app.js) where objects are loaded. You can load objects into individual components but in this case, loading is done at the topmost parent so that they can be shared with children as props. The snippet below has a display that shows a property value of an object and a button that changes the values.

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      propValue: 0,
      myObject: null
    };
  }
  onPropertyChanged(key, value) {
    if (key == "humidity") this.setState({ propValue: value });
  }
  componentDidMount() {
    var propChange = function(key, value) {
      this.onPropertyChanged(key, value);
    }.bind(this);
    var objectReady = function(propertyName) {
      this.setState({
        myObject: window.objectManager.objects[propertyName].list[0]
      });
      this.state.myObject.propertyChanged.connect(propChange);
    }.bind(this);
    window.objectManager.ready.connect(function() {
      window.objectManager.findObjectByGid(
        "myObject",
        "ddcfd327dbb5fa840597d72f513ee42b5e2ff795"
      );
    });
    window.objectManager.objectsReady.connect(objectReady);
    window.objectManager.start();
  }
  render() {
    let opts = {
      size: 400,
      currentValue: this.state.propValue,
      needleSharp: true
    };
    return (
      <div>
        <PropertyDisplay propValue={this.state.propValue} />
        <hr />
        <PropertyButton
          element={this.state.myObject}
          property="humidity"
          value="30"
        />
        <hr />
      </div>
    );
  }
}

In the above snippet, most of the heavy lifting is done by the componentDidMount() method. We do the following things in this method.

  1. We create two callback functions and bind them to the context of our app. The first callback propChange is for the changes in out IoT object itself. This will be called whenever something changes in the IoT object. The second callback objectReady is for the ObjectManager. It will be called once we have the hook to the IoT object ready.

  2. Next, we connect to the objectManager.ready signal. Once the ObjectManager is ready and connected with the backend, this is the logic that will run. In this example we want to load an object (identified by its GID "ddcfd327...") from the backend. In the callback we will then use this.setState() function to take that object from the response and store it to the state of our React Component. We also connect to the objectManager.objectsReady signal to get notified once this loading is done.

  3. Finally, we start the ObjectManager and the whole process with our callbacks is initiated.

Now that we have an object to manipulate, the rest is pure React and has almost nothing to do with the IoT library anymore. We will follow the props of our App with various Components. Note how we filter out the properties of the object and in this application are only interested in the property "humidity" of our IoT object. Let's first create a display for the humidity value.

import React from "react";
const PropertyDisplay = ({ propValue }) => {
  return <div className="display">Current value: {propValue}</div>;
};
export default PropertyDisplay;

The above snippet allows us to see the real time changes in humidity data from our IoT sensor. Now, assuming that the humidity data came from a humidity controller, we will proceed to control the humidity. For the sake of simplicity, we will create a button for this purpose. The button will then set the value to a value of the corresponding Component.

import React from "react";
class PropertyButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      element: props.element,
      property: props.property,
      value: props.value
    };
  }
  componentWillReceiveProps(newProps) {
    this.setState({ element: newProps.element });
  }
  render() {
    return (
      <button
        onClick={() => {
          this.state.element.setProperty(this.state.property, this.state.value);
        }}
      >
        {this.state.value}
      </button>
    );
  }
}
export default PropertyButton;

In the above snippet, the only real IoT magic happening is this.state.element.setProperty(this.state.property, this.state.value);

The preceding code implies that when a button is clicked, the setProperty method is invoked for our IoT object(this.state.element). Now we can pass the property we want to set (this.state.property) and the value we want to set it to (this.state.value) as parameters to the function.

And that is about it. Now you are able to access the backend object and read and write properties. You don’t need to worry about all the stuff that happened when the properties are written, the backend of Asema IoT takes care of it.

8.6.2. Angular

In Angular (Angular2+ not AngularJS), integrating the Jarvis library works in similar way to that in React. Similar to the approach outlined in the previous chapter, we start by including the required JavaScript files into the index page, then use a couple of callbacks from them to get the objects we want, and finally operate with them in the proper application context. Below is a simple index.html. Nothing much here, just the tag app-root to attach our application into and a set of imports for the Jarvis library. Note the /iot prefix in the import paths. See 8.7, “Using an HTTP proxy” for how and why this is used.

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Angular demo</title>
    <base href="/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>

<body>
    <app-root></app-root>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/network/json.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/network/websocket.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/objects/signal.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/objects/connectedobject.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/objects/objectmanager.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/objects/dataanalysismanager.js"></script>
    <script type="text/javascript" src="/iot/inc/js/jarvis-1.0/objects/pluginmanager.js"></script>
</body>

</html>

Once the index page is set up, here is a sample for the angular app app.component.ts to define the parent component.

import { Component, Output, EventEmitter, OnInit } from "@angular/core";
declare var ObjectManager: any;
@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  title = "Angular IoT example";
  public myObject: any;
  public objectManager: any;
  @Output()
  temperature = 0;
  setProperty(temperature: number): void {
    this.temperature = temperature;
  }
  onButton(value: number) {
    this.myObject.setProperty("temperature", value);
  }
  ngOnInit() {
    var propChange = function(key, value) {
      if (key == "temperature") this.setProperty(value);
    }.bind(this);
    this.objectManager = new ObjectManager();
    this.objectManager.ready.connect(
      function() {
        this.objectManager.findObjectByGid(
          "myObject",
          "ddcfd327dbb5fa840597d72f513ee42b5e2ff795"
        );
      }.bind(this)
    );
    this.objectManager.objectsReady.connect(
      function(propertyName) {
        this.myObject = this.objectManager.objects[propertyName].list[0];
        this.myObject.propertyChanged.connect(propChange);
      }.bind(this)
    );
    this.objectManager.start();
  }
}

In the above snippet, we declared an object manager variable as declare var ObjectManager: any; to help in integration. As the ObjectManager lives in the files of windows context at runtime, this variable definition removes compiler warnings. Since the ObjectManager does not exist at compile time, a dummy value is fed to the compiler and a proper value is set at runtime.

In the above sample, we connect to the Asema IoT backend during the init ie. ngOnInit(). Here, we have the three mandatory callback functions. The callback functions need to be bound to the application context (bind( ) ), so that we don’t lose track of our application component when the window context calls them. Likewise, we require the objectManager.ready signal to be bound, once we call objectManager.start() to begin the lifecycle, this is the first callback that runs once the manager has connected to the IoT backend.

Next, objectManager.objectsReady signal will be called as a response to our search function, which in this case is findObjectByGid as we want to find an object with given global ID (GID) (which in this case is "ddcfd327dbb..."). The API reference lists other possible search functions. At this callback we store the object found into myObject and bind the propertyChanged signal to the final callback which is propChange. All property changes from our object now arrive here and can be distributed to our UI components.

That is all we need to do to hook up the backend. Now, we’ll proceed towards creating couple of simple components to perform actual action. In the code snippet that follows, we’ll create two components in the application definition (app.module.ts), one simple DisplayComponent that displays the value and a button ButtonComponent to change the value.

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { AppComponent } from "./app.component";
import { ButtonComponent } from "./button/button.component";
import { DisplayComponent } from "./display/display.component";
@NgModule({
  declarations: [AppComponent, ButtonComponent, DisplayComponent],
  imports: [BrowserModule, FormsModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

Now, some very simple template code that lays out our components on the screen (app.component.html).

<h1>{{title}}</h1>
<app-button (onButton)="onButton($event)" [buttonValue]="30"></app-button>
<app-display [tempValue]='temperature'></app-display>

Now that we have completed binding the button clicks upwards and the display values down to the child components, we’ll create a simple div to display the values (display.component.html).

<div class="display">Current value: {{tempValue}}</div>

and then the component definition (display.component.ts)

import { Component, Input, OnInit } from "@angular/core";
@Component({
  selector: "app-display",
  templateUrl: "./display.component.html",
  styleUrls: ["./display.component.css"]
})
export class DisplayComponent implements OnInit {
  @Input()
  tempValue: number;
  ngOnInit() {}
}

n the above snippet, we defined one property tempValue as an Input. This will feed the values from our IoT object into the display. Angular does the property binding automatically when we bind it in the tag <appdisplay [tempValue]='temperature'></app-display>. Doing this sets us up to display real time values to your screen.

Finally, to feed some values back into the IoT system, we'll make a button to set a property when pressed. Here's the template for our button (button.component.html):

<button (click)="changeObject()">{{buttonValue}}</button>

Now, we need a button tag and a event handler for the click event. The above snippet shows the code that receives the button click (button.component.ts).

import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core";
@Component({
  selector: "app-button",
  templateUrl: "./button.component.html",
  styleUrls: ["./button.component.css"]
})
export class ButtonComponent implements OnInit {
  @Input()
  buttonValue: number;
  @Output()
  onButton = new EventEmitter<number>();
  ngOnInit() {}
  changeObject() {
    this.onButton.emit(this.buttonValue);
  }
}

In the snippet above, we defined one input (buttonValue) that lets us tailor each button to send a particular value. Likewise, there is an event onButton which sends the button click. We have already defined a listener in app.component.ts to receive this event. The snippet for the listener is as follows:

onButton(value: number) {
    this.myObject.setProperty("temperature", value);
}

The listener in the snippet above contains the final piece of the puzzle: the myObject.setProperty() method. This is the method that contacts the IoT backend and manipulates the properties of the objects loaded. Once you have completed the above steps, the display will automatically react to the changes in value and display those.

8.6.3. Other frameworks

There are several other UI frameworks that help in creating component based JavaScript applications and single page applications. Covering all of them in a manual or by a library would be somewhat impossible. However, because the Jarvis library that comes with Asema IoT Central has a minimum amount of dependencies, jQuery being the only notable one, it should work without problems with a majority of the frameworks. However, as those are not tested, there are no guarantees of this.

While framework specific instructions cannot be given here, some general best practices can be listed based on our experience with the major frameworks that should help in the task. These are

  • Find the main index.html or top template of a template hierarchy and add the files of the Jarvis library as traditonal JavaScript import statements (i.e. <script type="text/javascript" src="..."/>
  • Test the loading of the ObjectManager component of Jarvis library early on and make a callback that fires a console message or some other event when it does. Remember that the component will not fire a ready signal before it has connected with WebSocket to the backend so trace network calls and see that WebSocket passes every network component fluently.
  • It may be necessary to build wrapper functions that the Jarvis library signals call as very traditional and simple JavaScript functions to ensure they are called. Do framework specific component value handling inside these functions.
  • Be careful with method visibility and call contexts! Many events will be fired in the window context unless you specifically bind them to a method context. If this happens, many of the properties and variables will be undefined in the callbacks. UI frameworks may do various tricks to retain their own context so experiment how a particular binding method works.

8.7. Using an HTTP proxy

When you work with Asema IoT Central, you can implement the application using Asema IoT Central as the sole server for the project, but you'll often be running it alongside multiple servers. Typical companions would be Java application servers (e.g. Glassfish, Tomcat) or node.js. The challenge is to be able to offer a seamless experience and avoid pointing the user (or the browser) to multiple addresses. A common way to solve this is proxying.

Both Asema IoT Central and its companions can act as proxies for the other. When a call is received by one of them to a given path, it will be automatically forwarded to the other server for processing. And while this works well for smaller projects, in heavy duty production it is recommended to have a dedicated load balancer and proxy in front. We like nginx for this process and this chapter includes instructions on how to set it up.

8.7.1. NAT rules and paths

It is beneficial and often necessary for Asema IoT backend to know the actual domain and address at which it is serving clients. When the system simply sits behind a firewall with some open ports, this is trivial. But most of the time the servers are in an internal network behind the firewall system and addresses are translated to the public using NAT (Network Address Translation). Traffic is often allocated to servers by recognizing the requests by their paths. To support such configurations, Asema IoT Central has settings for NAT rules and request paths.

Under System settings > Network servers at Asema IoT Central admin interface are settings for the network configuration. For each server (HTTP, Websocket, CoAP) there is the port to use at the server and the port into which requests are translated (NATted) into for outsiders. If your Asema IoT installation sits behind a NAT firewall, set the ports here accordingly. For instance, if the software serves at port 8080 but there is a proxy of firewall NAT in front that translates the port to 80 for the public, put 80 into the NATted port.

If you use an HTTP proxy, it will most likely forward the traffic to the correct port once it recognizes a path. To make Asema IoT Central translate all its paths to this path without needing to resort into rewriting path at the proxy, you can set the desired path at System settings > Network servers by using the setting HTTP default path prefix. If you for instance set the path prefix to be "/mypath" then the original path "/json" will become "/mypath/json". You can now easily set up the proxy to send everything that has "/mypath/..." as its path to the correct server where Asema IoT Central runs.

8.7.2. nginx

ngnix is a free and open source, high-performance and battle tested reverse proxy and load balancer. In this section we’ll walk you through setting up nginx. For this example, let’s assume you have a node.js running at 192.168.2.10:3000 and Asema IoT central at 192.168.2.11:8080. We should be able to seamlessly access both through ngnix at 192.168.2.1:80. To achieve this, you need to forward the node.js HTTP(S) traffic as well as the traffic from Asema IoT Central HTTP and WebSocket traffic to the correct server. In the sample nginx.conf file below, we have chosen “/iot” as the path to Asema IoT Central.

server {
    listen 80;
    server_name localhost;
# Forward node.js traffic, assumed to be at root path
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $host;
        proxy_buffering off;
        proxy_pass http://192.168.2.10:3000;
    }
# Forward IoT Central HTTP traffic
    location /iot {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $host;
        proxy_buffering off;
        proxy_pass http://192.168.2.11:8080;
    }
# Forward IoT Central Websocket traffic
    location /ws {
        proxy_pass http://192.168.2.11:8081;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

8.8. Server-side processing

8.8.1. Continuously running logic

Client-side applications are great means to offer a fluent user interface to the end user and to offload server processing to user devices. The downside of that is that they only run when they are in use. Since IoT is all about connectivity with and between the machines and not users, there is a natural need to have some logic running irrespective of user activity. Server-side scripts can be used for following tasks:

  1. To augment existing REST API and offer additional features to end users

  2. To have continuous data processing and event handling independent of user activity

Usually the implementation is the combination of these two: stateful request processing. In stateful processing a call from a user sets some object property to a state which is retained and changed according to the incoming requests and events so that the next call behaves differently. For example, if you implement a counter. The count is retained as a property in an IoT object i.e. an object that has officially and properly set up with the Asema IoT Central user interface. Whenever a request comes to the server, the counter is incremented by one, thus making changes to the initial state according to the incoming request.

However, there are many use cases where you’d like the server request script to retain the state. Consider you are making a service that averages temperatures from 100 sensors. All the sensors have a slight bias in the reading, so you have to calculate the average after a correction factor is applied. Lets further assume this correction factor comes from end users and could depend on various other factors like humidity or the time of the day. Therefore, it is a possibility that you could get two different types of read operations on the server, those with correction factor and those without it. When an admin calls the function with the correction factor, it is factored in when calculating the average reading of range of sensors (separate objects). The result of the processing is retained in the script and subsequent calls without a correction factor get this stored, precalculated reading as the "official and corrected" value.

Note

This manual gives just short samples of the various capabilities of server side scripts. To know more about all kind of neat tricks you can perform with Server-side scripting, consult the separate Server Side Scripting manual.

8.8.2. Creating and calling a script

Server Side Scripts are added and written into the web admin interface of Asema IoT Central. You can find the scripts under Applications>Server side scripts. To add a new script, simply click on "Add new".

The script is a QML snippet which must contain the include of ScriptingEngine and be wrapped inside a RequestHandlingScript structure. It must also include a method called onRequest from which script execution starts whenever a call is received. Like so

import ScriptingEngine 1.0
import QtQuick 2.3

RequestHandlingScript {
    function onRequest(request, response) {
        ...
    }
}
          

When you add a script, you need to give it a name, the script itself and the request path. When this request path is called with HTTP POST, the script gets called and the onRequest method is invoked. Note that the path always starts with the common script path which by default is script. So if you enter the request path "mypath" and your Asema IoT installation runs on IP 127.0.0.1 and port 8080, then the full path to the script is http://127.0.0.1:8080/script/mypath.

Note

The common script path can be changed from the Webserver setting and System settings. Make sure you match your call to this path.

8.8.3. Programming request handlers

If your application requires features that are not readily available by using just the objects and would need further support from the server in data processing or have stateful storage of values on the server,you can do this with server side scripts. These scripts are JavaScript that run on Asema IoT Centralat the server and supply data for your calls. You'll notice that the server side has the same familiar ObjectManager API as the UI side. So, if you've already learned how to use the API in your interfaces, doing the same on server side is relatively easy.

Each such script should implement an onRequest method that receives the request from your UI and a response hook to get the response. The snippet below show the creation of request handlers.

import ScriptingEngine 1.0
import QtQuick 2.3

RequestHandlingScript {
    property variant sampleObject: null
    function loadedObjectsReady(propertyName) {
        if (propertyName == "sample") {
            var json = JSON.parse(storedRequest().body);
            sampleObject = context.objects.sample.list[0];
            sendResponse(sampleObject.properties[json.property]);
        }
    }

    function onRequest(request, response) {
        store(request, response);
        var json = JSON.parse(request.body);
        if (sampleObject == null) {
            context.objectsReady.connect(loadedObjectsReady);
            objectManager.findObjectByGid(context, "sample", json.gid);
        } else {
            sendResponse(sampleObject.properties[json.property]);
        }
    }

    function sendResponse(propertyValue) {
        if (storedRequest().headers['content-type'] == 'application/json') {
        var resp;
        if (propertyValue != undefined) {
            resp = { a: 10, b: 100, property: propertyValue };
        } else {
            resp = { a: 10, b: 100, property: 'unknown' };
        }
        storedResponse().setHeader('content-type', 'application/json');
        storedResponse().write(JSON.stringify(resp));
        storedResponse().end();
        }
    }
}

The example above gets a raw response in the onRequest method, takes the body of the request and parses it into a JSON object. From the JSON object, a GID is extracted and the ObjectManager is requested to fetch it. Once found, sendResponse is used to send a requested property back to the caller together with some dummy data ("a" and "b") in this case.

8.8.4. Event Handling and data analysis

Server side scripts run in the same application engine as other scriptable customizations of Asema IoT Central. The scripts remain loaded in the engine and can therefore also react to events at times when they are not called. Of course, because the scripts are based on a request-response cycle, there is no way to immediately send some data of those events as there is no ongoing request to access. However, the values from events can be stored in a state variable and then applied once the next request comes in.

Let's have an example. In this one we load an object with ObjectManager and store it. A permanent variable is used to track the number of times a particular property is a) written, b) read. When a call to this script is made, the script returns a triple of the property value, number of times written and number of times read

import ScriptingEngine 1.0
import QtQuick 2.3

RequestHandlingScript {
    property variant myObject: null
    property int timesRead: 0
    property int timesWritten: 0
    
    function onPropertyChanged(key, value) {
        // Track the depth property
        // This method will be called for as long as the script stays
        // in memory (after first call that connects it), whether it 
        // is called or not.
        if (key == "depth") {
            timesWritten++;
        }
    }
    
    function onLoadedObjectsReady(propertyName) {
        if (propertyName == "trackedObject") {
            var json = JSON.parse(storedRequest().body);
            
            // store a handle to the object and connect its property changed
            // signal into logic here
            myObject = context.objects.trackedObject.list[0];
            myObject.propertyChanged.connect(onPropertyChanged);
            timesRead = 1;
            sendResponse();
        }
    }
    
    function onRequest(request, response) {
        store(request, response);
        var json = JSON.parse(request.body);
        
        // If the object has not been loaded yet, load it and then respond
        if (myObject == null) {
            context.objectsReady.connect(onLoadedObjectsReady);
            objectManager.findObjectByComponentId(context, "trackedObject", json.componentId);
        } 
        
        // Otherwise increase call counter and respond
        else {
            timesRead++;
            sendResponse();
        }
    }

    function sendResponse() {
        if (storedRequest().headers['content-type'] == 'application/json' && myObject != null) {
            var resp = { read: timesRead, written: timesWritten, property: myObject.properties["depth"] };
            storedResponse().setHeader('content-type', 'application/json');
            storedResponse().write(JSON.stringify(resp));
            storedResponse().end();
        }
    }
}
        

Once you have a handle to an object, you can of course manipulate the data on the server side before you send it back. All the math methods of JavaScript are at your disposal for e.g. changing timeseries data. Let's take an example of fetching, manipulating and sending back a series of data. As we've already worked on the "depth" property in the example above, the next example takes that depth data from the database and divides every value by two.

import ScriptingEngine 1.0
import QtQuick 2.3

RequestHandlingScript {
    property variant myObject: null
    
    function onLoadedObjectsReady(propertyName) {
        if (propertyName == "trackedObject") {
            myObject = context.objects.trackedObject.list[0];
            myObject.measurementValueTimeSeriesReady.connect(onTimeSeriesReady);
            fetchTimeSeries();
        }
    }
    
    function fetchTimeSeries() {
        if (myObject != null) {
            myObject.getMeasurementValueTimeSeries("depth", "2018-01-01T00:00:00", "2018-12-31T23:59:59")
        }
    }
    
    function onRequest(request, response) {
        store(request, response);
        var json = JSON.parse(request.body);
        
        // If the object has not been loaded yet, load it and then load the
        // timeseries for processing. Otherwise load the timeseries immediately
        if (myObject == null) {
            context.objectsReady.connect(onLoadedObjectsReady);
            objectManager.findObjectByComponentId(context, "trackedObject", json.componentId);
        } else {
            fetchTimeSeries();
        }
    }

    function onTimeSeriesReady(key, series) {
        if (storedRequest().headers['content-type'] == 'application/json' && myObject != null) {
            var responseSeries = [];
            for (var i = 0; i < series.length; i++) {
                var modified = { time: series[i].time, value: series[i].value/2 }
                responseSeries.push(modified);
            }
            var resp = { series: responseSeries };
            storedResponse().setHeader('content-type', 'application/json');
            storedResponse().write(JSON.stringify(resp));
            storedResponse().end();
        }
    }
}
           

8.8.5. Database Queries

If your application stores data at the Asema IoT Central database and you'd like to fetch that data to the UI, you can use the API provided by server side scripts. By issuing a rawQuery inside the script, you can execute raw SQL queries.

Important

Note that the database queries always use the data DB of the Asema IoT Central. The system configuration always resides in the configuration database which uses SQLite. If you have configured a different data DB than SQLite for your application, data on object properties and events will be stored in this other database but the system configuration will remain in the SQLite database. Queries from scripts are always performed in this other database and your database queries cannot access the system configuration tables.

Results from database queries are placed in a response object that contains the output of the raw query. For instance, if you'd do something like SELECT name, address FROM persons WHERE country='Norway'; and that returns three results, your query result will have three rows and each row two columns; column 0 for name and column 1 for address.The example of a rawQuery is displayed in the snippet below.

import ScriptingEngine 1.0
import QtQuick 2.3

RequestHandlingScript {
    function onRequest(request, response) {
        if (request.headers['content-type'] == 'application/json') {
            var json = JSON.parse(request.body);
            var resultRows = new Array();
            var queryResult = rawQuery("SELECT * from stats");
            for (var rowIndex in queryResult) {
                var row = queryResult[rowIndex];
                var resultColumns = new Array();
                for (var columnIndex in row) {
                    resultColumns.push(row[columnIndex]);
                }
                resultRows.push(resultColumns);
            }
            response.setHeader('content-type', 'application/json');
            response.write(JSON.stringify(resultRows));
            response.end();
        }
    }
}

The example above runs a query SELECT * from stats i.e. it fetches all values from the table "stats". It then iterates through the results and places the values into rows and columns or the response and creates a JSON compatible string out of them to send it back to the caller.

8.8.6. Debugging scripts

Like any scripted logic running inside the scripting engine of Asema IoT, Server Side Scripts have access to the standard logging procedures which output data into the system log. You can use the logging with the standard JavaScript console command.

import ScriptingEngine 1.0
import QtQuick 2.3

RequestHandlingScript {
    function onRequest(request, response) {
        console.log("My script was called!");
        ...
    }
}

Note

Note that the output location of the system log may be changed in settings. If you see no log output from the scripts, please first check from System settings that you are reading the correct log.

Chapter 9. Programming Reports

Reports are the static view of a given system at a given point in time. A reporting application in Asema IoT works in essentially three stages:

  1. A preparation stage at which objects and data are loaded to memory for processing

  2. A processing stage at which calculations and customizations are made based on the data that was received

  3. A rendering stage at which the processing engine renders the graphical output of the application and transforms it into a report.

The process of generating a report starts when the rendering engine calls the render() method of the template application and ends when the application sends the rendered signal. The process in between is asynchronous and the application may take the amount of time it requires.

9.1. Setting up a template application

The basic setup of a template application is one QML file which has the following basic structure and imports. The basic template is provided in the snippet below.

import QtQuick 2.6
import Templating 1.0
Item {
    property variant context;
    signal rendered;
    function render(gid) {
        return "OK";
    }
}

The above snippet is just a basic skeleton of a reporting application and whatever goes inside the Item is totally up to the programmer.

9.2. Loading objects

When rendering of a report for certain object is invoked at Asema IoT Central UI, the GID of this object is passed to the render() method of the template application which may use the information if needed. The application is free to load all the objects it has the access to. Whatever the choice, the loading of objects takes place in a similar way to screenlets. So, if you are an expert screenlet developer, this should be relatively simple.

The snippet below displays an example of loading one object and then setting the value of a property into a text field in the report.

property variant context;
signal rendered;
function loadedObjectsReady(propertyName) {
    var item = context.objects.myItem.list[0];
    try {
        speedDisplay.text = item.properties.speed;
    } catch (ex) {
        console.log("The object you tried to run report with does not have the property speed.")
    }
    rendered();
}
function render(gid) {
    context.objectsReady.connect(loadedObjectsReady);
    objectManager.findObjectByGid(context, "myItem", gid);
    return "OK";
}

9.3. Invoking rendering

Rendering is automatically invoked by the application engine after the template has been loaded into the memory. However, please note that the application loads when a report is requested and unloads immediately after the rendering is complete. Therefore, unlike server side scripts, you cannot hold a state variable in the template application.

9.4. Signaling Completion

Once all the data is in place and the graphical elements have been applied, the templating application should send the rendered signal. Sending a completion signal is simple as calling a function. The snippet below shows an example of how to send the signal.

function allDone() {
    rendered();
}

The rest of the rendering takes place automatically. However, make sure that you do not change any value in the graphics after calling the signal. The data is processed as soon as it is received but there is no guarantee that the changes made between sending the render signal and finishing rendering will be taken into account.

Chapter 10. Deploying and Distributing applications

This section focuses on the deployment and distribution of various Asema IoT applications.

10.1. Testing and publishing screenlets

This section details about the procedure involved in developing, testing and publishing screenlets.

10.1.1. The Screenlet SDK and Tester

To test if your shiny new screenlet runs properly on your Asema E, you can use the Asema Screenlet Tester, which is a part of the Asema Screenlet SDK. The screenlet tester is a PC program that runs the screenlets locally on your PC, making it easy to spot any errors and to quickly fix bugs and graphics before installing the screenlet on the target device. The tester emulates an Asema E device as closely as possible, giving you access to an emulated hardware interface that lets you simulate home appliances, device buttons, screensaver, and all other relevant functions. The tester also supports serving Asema E's with developer versions of screenlets you can conveniently test locally on the target Asema E before installing them.

While the tester tries to mimic an Asema E as closely as possible, it can never fully capture the user experience. The key difference is speed, the computing power of your PC is always better than that of a low powered embedded device. Therefore, it is important to first author the core screenlet with the SDK to make sure no critical errors exist and then do further tests on the actual device. To get to know more about the Asema Screenlet SDK, please refer to the separate Asema Screenlet SDK User Manual. The user manual has detailed instructions on the installation of the SDK as well as running, deploying, and publishing the screenlet.

10.1.2. Publishing and installing

Screenlets can be distributed in three ways, locally, group-wise or globally.

To permanently install a screenlet on an Asema E you must first upload it to asema.com. After upload it will appear in the screenlet store and can be installed by clicking it. The initial upload is private and is watermarked to your Asema E and is therefore visible only on that specific device. To make it public and available to everyone the screenlet must go through a review process.

To publish a screenlet, you have two options:

  1. Enable Developer tools on the user account you have created for asema.com. If developer tools are active, you have access to your personal screenlet directory by clicking the black tool icon at the top right corner of asema.com screen after you have logged in. Then follow instructions below.

    • To enable the developer tools, access the Main Setup of you Asema E and go to System Settings >Users and edit your user. You'll find a tick box that activates the tools for this login.

    • The benefit of uploading a screenlet at asema.com is that you have direct access to all metadata editing functions and can edit a description and add an icon straight away. The downside is that you need to first generate a program package with the SDK before you can upload anything.

  2. Use the Asema Screenlet SDK to upload the screenlet. This is a fast method, when you are happy with your screenlet, just click on "Publish" in the menu and off you go. The downside is that if you want to further edit your metadata and fine polish how the screenlet looks like in the screenlet directory, you have to login to asema.com separately.

In the following subsections, we will discuss those methods in detail.

10.1.2.1. Uploading a screenlet at asema.com

Uploading a screenlet at asema.com involves the following procedure.

  1. Login to the asema.com service and make sure developer mode tools are activated.

  2. Click on the black tool tip icon at the top right of the screen to enter screenlet editing.

  3. To add a new screenlet, simply click "Add". To edit an existing one, click "edit" at the screenlet you want to edit.

  4. At the Asema Screenlet Tester of the Screenlet SDK, open your screenlet (if not already open) and in the menu go to Screenlet > Publish.

  5. When you click on create, the tester will create a zip package to the screenlet install packages folder which in the default SDK installation is at /install_packages directory of your Asema installation home.

  6. Add the correct .zip package to the screenlet editing form at asema.com to publish your screenlet.

10.1.2.2. Uploading a screenlet with Asema Screenlet SDK

To learn more about Asema SDK and how to publish screenlets using SDK, please refer to the Asema Screenlet SDK User Manual.

10.1.2.3. Deleting a published screenlet

Deleting a screenlet published at asema.com involves following steps.

  1. Login to the asema.com service and make sure developer mode tools are activated.

  2. Click on the black tool tip icon at the top right of the screen to enter screenlet editing.

  3. You will see a list of the screenlets you have uploaded and not made public. Click on delete to remove it.

  4. If your screenlet is already public, contact us at support.asema.com.

10.1.3. Screenlet icon and other metadata

To change the look and feel of the screenlet as it appears on Asema E or the WebView, you can change the screenlet metadata at the asema.com service. Changing screenlet metadata in asema.com service involves following steps.

  1. Login to the asema.com service and make sure developer mode tools are activated.

  2. Click on the black tool tip icon at the top right of the screen to enter screenlet editing.

  3. You will see a list of the screenlets you have uploaded, click on “Edit” on the screenlet you want to change.

  4. You can add a description for the screenlet as well as alter its rating, category, screenshot, and icon. Simply edit the fields as necessary and upload the updated files. When adding a screenlet or an icon, please note the following.

    • File format for both is PNG

    • The correct image size of the icon is 76 x 76 pixels

    • The correct image size of the screenshot is 160 x 84 pixels

10.2. Deploying web applications

//Todo

10.3. Deploying reports

//TODO

Colophon

<bookinfo>Asema Electronics Ltd<copyright><year>2011-2019</year></copyright><legalnotice>

No part of this publication may be reproduced, published, stored in an electronic database, or transmitted, in any form or by any means, electronic, mechanical, recording, or otherwise, for any purpose, without the prior written permission from Asema Electronics Ltd.

Asema E is a registered trademark of Asema Electronics Ltd.

</legalnotice>
</bookinfo>