Introduction

Note

This page is a work in progress.

Six15’s Velocity integration process is being internally validated. Contact info@six-15.com if you find incorrect or incomplete information.

Ivanti’s Velocity tool allows legacy Terminal Emulator (TE) or Web applications to be wrapped in an easy-to-use mobile app. Velocity exposes a scripting API which can read the content of TE/Web page and to interact with the Android device. This API can be used to communicate with Six15’s app using the Intent Interface. This process allows any Velocity project to become HUD enabled.

The Velocity Console desktop application can be used to create and modify Velocity projects. The project can then be deployed and run on an Android device using the Velocity app.

The Velocity App and Velocity Console are available for download on Ivanti’s Website . The Velocity app is also available on the Google Play Store.

You can learn more about the Velocity Scripting API and the Velocity Console on Ivanti’s Website.

Hud Enable Process

  1. Obtain the Required Hardware and Software

  2. Verify your Configuration

  3. Record Terminal Screens

  4. Copy Recording to your PC

  5. Import Screens into Velocity Console

  6. Assign Templates to Screens

  7. Design a HUD Screen for each template

  8. Deploy the final project to an Android device

  9. Test and Validate the Deployment

1. Obtain the Required Hardware and Software

To successfully HUD enable a project, you will need the following:

Hardware:
  • PC running Windows 7 or higher.

  • An Android device running Android 6.0 (Marshmallow) or later with a USB-C port.

  • A USB cable to connect your Android device to your PC.

  • An ST1 HMD (Contact info@six-15.com).

Physical ST1 hardware is only needed to test and validate your integration. It isn’t strictly necessarily for any of these steps. A wireless file transfer method between the PC and Android device could replace the USB cable.

Software:

2. Verify your Configuration

It’s generally assumed Velocity is already in use, but if not follow the instructions on https://help.ivanti.com/wl/help/en_US/Velocity/2.1/admin/gettingStarted.htm.

Current Velocity users likely already have a host profile setup inside the Velocity app. This host profile can be used to record screens in the next step.

If you don’t have an existing host profile you will need to create one. You will need to know the IP address of your server, as well as related data, like port, protocol, login info, etc..

With this information in hand, there are multiple ways to create an initial project.

The project is not yet HUD enabled. It is only used to record screens in the next step.

3. Record Terminal Screens

Launch the Velocity application, and select your desired Host Profile (which likely has the same name as your project) to start a Velocity TE session. Make sure you have successfully connected to the server but do not yet navigate through your workflow.

Tap the menu icon (3 horizontal lines) in the bottom left.

Select “Screen Capture” to start recording your session.

Note

Although it defaults to enabled, some projects may have the “Screen Capture” option disabled. If this is the case you must enable the property in Velocity Console and re-deploy your project.

Once recording has started you’ll see a red recording icon.

While recording, perform the actions you would like to HUD enable. These should involve visiting every type of screen which should have a corresponding HUD screen. Make sure to purposefully trigger error screens and navigate through all relevant workflows.

End your recording by tapping the red recording icon, and select YES to save the recording.

Your recording is saved on the Android device inside the folder /sdcard/Android/data/com.wavelink.velocity/files/.

4. Copy Recording to your PC

The recording file on your Android device needs to be moved to your PC so it can be used by the Velocity Console.

To access this file, connect your Android device to a PC using a USB cable. You may need to enable file transfer by tapping on the charging notification, and switching “Use USB for” option to “File transfer”. Your device should be shown in your PC’s file explorer. Its name is normally the model name of your Android device.

In your file explorer, navigate to <android-device-name>/<device-internal-storage>/Android/data/com.wavelink.velocity/files/. Inside this folder are all screen recordings (.wltsc files) and deployment files (.wldep).

The desired .wltsc file should be named your profile’s name, followed by a number. The highest number is the most recent recording. Alternatively, sort by modified date to find the newest file.

Move the desired .wltsc file from the Android device to the PC by copying (or drag-n-drop) it to some location on your PC (i.e. your desktop).

5. Import Screens into Velocity Console

Open the Velocity Console application on your PC.

If you don’t already have a Velocity project (since you created it on-device, or online, or if was created by someone else) you’ll need to create a new one.

Note

Some projects may already have had customizations made to them, like being previously Voice Enabled or HUD enabled. Project may also have had their Modern UI’s customized, their predictive UI’s styled, or their scanner/keyboards setup customized.

If your deployment had this type of customization, you will need to use your existing project as a starting point instead of creating a new one from scratch.

When creating a project you’ll have to set you server IP address, and other configuration information. These values can be edited later on-device, but setting them here avoids needing to manually configure every deployed device.

With your project open, navigate the the screens tab by clicking the button in the top right.

../_images/velocity_console_screens_tab.png

On the screens tab, click the “Add Screens” button in the bottom left.

../_images/velocity_console_add_screens.png

In the file selection dialog, select the .wltsc file from the previous step, and click “Open”. Velocity Console should now show a list of screens in the left panel.

6. Assign Templates to Screens

Velocity automatically analyzes your screens and finds important identifying regions. It separates the screens into groups based on these regions. Screens which have a similar format (but may have different data) will be grouped together. Velocity can associates a single “template” with each of these groups.

By default every group of screens is marked as “predictive”. This means no template based customizations exist for that for that group. To make a group of screens “templated” instead of “predictive”, simply edit any UI element in the “ModernScreen” UI by changing any element property. An easy way to do this is to select a text element and change it’s alignment from “center” to “left”, and then back to “center”.

After making any tiny adjustment, every screen in that group will no longer be “predictive”, but will be “templated”. This means template based customizations can be made to the those screens. The first customization you should make is to assign the template a name. Use the the text box above the center panel (directly under the “Deploy” button) to set the name.

../_images/velocity_console_template_name.png

In the left hand panel, you should see every screen matching that template is now showing it’s corresponding template name.

Repeat this process for every screen which is still predictive and still lacking a template name. You may find that some screens are misidentified by Velocity causing too many or too few groups. This can be resolved by adding or removing identifying “key” regions. Doing this can split groups which no longer match, or merge groups which now do match. There may also be blank, partially broken, or incorrectly recorded screens. It’s best to ignore or delete these and not assign them templates.

7. Design a HUD Screen for each Template

Each template can be linked to scripts which run when a screen matching that template is active. These scripts can be used to display information the HUD. See the section below and our Velocity TE examples on GitHub to learn more.

Once all your scripts are written and linked, your project can be deployed to a .wldep file. With your Velocity project open in the Velocity Console, click the “Deploy” button at the top.

../_images/velocity_console_import_export_deploy.png

Pick a location to save the .wldep file (i.e. your desktop) then click “Save”. You can also export your project to a ZIP file to back-up or share all your work.

8. Deploy the final project to an Android device

The .wldep file created in the previous step needs to be transferred to the Android device. Make sure you’re using the .wldep file from the previous step, and not the file used for recording screens.

Connect your Android device to a PC using a USB cable. You may need to enable file transfer by tapping on the charging notification on the Android device and switching “Use USB for” option to “File transfer”. Your device should be shown in your PC’s file explorer. Its name is normally the model name of your Android device.

In your file explorer, navigate to <device-internal-storage>/Android/data/com.wavelink.velocity/files/. Inside this folder are all screen recordings (.wltsc files) and deployment files (.wldep). Move the .wltsc file from your PC onto the Android device by copying (or drag-n-dropping) the file into this folder.

You should see your project under the list of “Host Profiles” (you may need to restart the Velocity app).

If you didn’t set them in the Velocity Console, you may need to modify your server’s IP address or other configuration settings. This can be done by long pressing on the profile name to open the “Host Profile Options” dialog. Select “Edit”, and then enter the default password “system”. From here you can make any adjustments needed.

9. Test and Validate the Deployment

If you haven’t already, install and run the Six15 ST1 app. Go through the on-screen setup, including permissions, first time plug-in, and first time setup.

Back in the Velocity app, start your project by tapping on it’s name under the host profiles list.

Assuming you followed the recommended integration process, the Six15 Intent Interface will start automatically. When you navigate to a screen matching a template with an associated script, that script will run, extract the proscribed information from the TE screen, and display it on the ST1’s display.

Navigate through every screen, and make sure all HUD screens are shown as expected. If anything is wrong, make adjustments in Velocity Console, and redeploy the wldep file.

Writing Velocity Scripts for the HUD

Velocity’s JavaScript based scripting environment exposes their Velocity API to user defined scripts. The Velocity API exposes 3 API endpoints needed to HUD enable a Velocity project.

  1. Action.launch() to start the Intent Interface.

  2. Action.sendBroadcast() to send messages to the Intent Interface.

  3. ModernScreen.getTextById() to read template defined values from the terminal screen.

Note

On Android 11 and newer, Velocity’s Action.startService() call can be unreliable. This is due to background execution changes which stop apps (like Velocity) from talking to other apps (like the Six15 Service) unless that app is already running. This is often the case with the Six15 Service as the app likely isn’t running in the background. These background restrictions don’t apply to starting an Activity. By launching an activity instead, the activity can correctly start the service and then close itself. The activity is transparent and totally imperceptible.

HUD enabling a Velocity TE project requires 4 main development steps:

  1. Create a script to launch the Intent Interface

  2. Create HUD screens using Velocity scripts

  3. Link Scripts to Template Scopes

  4. Populate Script Parameters

These steps can be followed here, but alternatively, check out our Velocity TE example project on GitHub https://github.com/Six15-Technologies/Velocity-TE-Example and it’s associated video walk-through.

1. Create a script to launch the Intent Interface

The following code starts the Intent Interface using Velocity’s Action.launch() API.

Action.launch({
    package: "com.six15.st1_connect",
    class: "com.six15.intent_interface.IntentInterfaceActivity"
});

This script needs to be linked to a scope in order to run. It’s often OK to link this starting script with the “session” scope(no “@” prefix needed) so it runs as soon as the connection to the server starts.

There is a possibility of a race condition between starting the Intent Interface and the receiving the first piece of broadcasted text. By including the first screens information alongside “Action.launch” instead of a later “Action.sendBroadcast”, it ensures that the first piece of content is always shown reliably. In most cases this isn’t necessary.

The initial values could also be used to show an initial “Welcome” screen.

Action.launch({
    package: "com.six15.st1_connect",
    class: "com.six15.intent_interface.IntentInterfaceActivity",
    action: "com.six15.hudservice.ACTION_SEND_TEXT",
    extras: [
        { name: "text0", value: "Hello from Velocity", type: "string" },
        { name: "bg_color0", value: "BLUE", type: "string" },
    ]
});

If an initial screen is used, and that screen’s content is dynamically read from the TE, then the “session” scope can’t be used for linking. Instead, the scope for the screen containing that dynamic content must be used.

2. Creating HUD Screens Using Velocity Scripts

The following Velocity Script sends an image the the HUD using the Action.sendBroadcast() API.

The dynamically determined values (aisle, shelf, and level) are read from the terminal with “ModernScreen.getTextById()”

function sendTextToHud(event) {

    // Read the string value of the id
    // getTextById() often returns strings with trailing spaces. The Velocity UI removes them automatically, so we do too.
    var hud_aisle_str = ModernScreen.getTextById(aisle_id).trim();
    var hud_shelf_str = ModernScreen.getTextById(shelf_id).trim();
    var hud_level_str = ModernScreen.getTextById(level_id).trim();

    // Send a broadcast to the HUD.
    Action.sendBroadcast({
        action: "com.six15.hudservice.ACTION_SEND_TEXT",
        extras: [
            { name: "text0", value: "Scan Location", type: "string" },
            { name: "bg_color0", value: "#454e83", type: "string" },
            { name: "weight0", value: "1", type: "string" },

            { name: "text1", value: ["Aisle:", "Shelf:", "Level:"], type: "string" },
            { name: "bg_color1", value: "#20e5ff", type: "string" },
            { name: "color1", value: "BLACK", type: "string" },
            { name: "weight1", value: "1", type: "string" },

            { name: "text2", value: [hud_aisle_str, hud_shelf_str, hud_level_str], type: "string" },
            { name: "weight2", value: "2", type: "string" },

        ]
    });
}
sendTextToHud();
WLEvent.on("ScreenUpdated", sendTextToHud);

See the full section on the ACTION_SEND_TEXT to fully understand the data being broadcast.

This image would be shown on the HUD.

Intent Interface Example

Trimming strings read back from the Velocity API is recommended since they often have trailing spaces.

Adding a "ScreenUpdated" event which re-draws the HUD is recommended since the text fields may change without firing a new template enter event. This is especially important when the screen content changes without switching templates. The event is automatically removed when the associated template leaves scope so no cleanup is needed.

In this script, the various *_id variables are defined as parameters to the script. When defining a parameter, make sure the “String” box is checked. In this example other values like color, weight, etc… are hardcoded.

Using parameters vs hardcoded values is up to developer preference. In general we’ve found it’s better to reduce the number of parameters and reduce complexity, at the expense of having more scripts.

In addition to the straight forward logic here, more complex processing could be performed. For example, you might change the color of text to be red when a number is negative.

It’s also sometimes useful to run regular expressions on strings. For example, this line trims “_” or ” ” characters from the end of a string.

str_value = str_value.replace(/[_ ]+$/, ''); //Remove ending "_" or " " characters.

3. Linking Scripts to Template Scopes

Once written, a script needs to be linked to a template in order to tell it when to run. This will make the script run (i.e. show something on the HUD) whenever that template is active. This can be done using the “Link” button in the center of the “Advanced Configuration” tab.

../_images/velocity_console_link_script.png

The scope name should be the template name prefixed by an “@”. For example “@T_Work_Info”.

You can verify that a specific screen is properly linked by going back to the “Screens” tab, selecting the desired screen, selecting the “Screen” tab, and seeing the script name under the “Scripts” section.

../_images/velocity_console_verify_linked.png

4. Populating script parameters

Once linked, the script parameter must be defined for that linking. In the example above, the various *_id variables need to be set to “Component ID”s (like “added_637696438393953452” or “component-2”) that are located on the linked template. Other parameters could be set to other fixed values like “615”, “Scan Location”, “RED”, or “#ff0000”.

To determine the “Component ID”s of a specific UI component on a specific template, go to the “Screens” tab. Select the screen (make sure that screen’s template is the one you’re linking), then select the desired text box on that screen’s template. The “Component ID” is listed on the right under the “Elements” tab.

../_images/velocity_console_get_component_id.png

If there is no component matching the text you need, you can create a new one by selecting that region of the TE screen and clicking the arrow in the floating menu. Since the text could very in size, be sure to select the largest region the text could cover. This normally means selecting the entire width of the TE screen.

Back in the “Advanced Configuration” tab, set the parameter’s value to the desired “Component ID”.

Velocity Web

Velocity Web doesn’t have a template mechanism or “Component ID” like TE projects do. This means a Velocity script can’t use ModernScreen.getTextById(...) to read text from the screen.

Instead, code must be injected into the webpage’s JavaScript runtime and executed, and results read back. This can be done with the Velocity Scripting API, specifically View.insertJavaScript(...) and View.evaluateJavascript(...).

Once injected, code like document.querySelector(...).innerText and document.getElementById(...).innerText can find and read text from the page and construct an Intent Interface screen. The Velocity script (not the injected script) can then broadcast the Intent Interface screen to the Six15 Service with Action.sendBroadcast(...).

Six15 has created an example Velocity Web project which performs this JavaScript injection, broadcasts Intent Interface data, and displays images on the ST1. You can find it on our GitHub page https://github.com/Six15-Technologies/velocity-web-static-site.

Whenever a page loads, the Velocity script asks the injected JavaScript for an updated Intent Interface screen. This data is then broadcast to the Six15 Service and shown on the ST1. This example project also takes care of starting the Intent Interface and re-injecting JavaScript whenever the page re-loads.

The project on GitHub is configured to parse an example website (https://six15-technologies.github.io/velocity-web-static-site/). It uses simple (but non-trivial) text extraction to read data from an HTML <table>. It finds a <tr> (table row) which contains a <th> (table header) which matches some specified string. From that row it extracts text from a <td> (table data) at a specified column number.

...

function getLocationScreen() {
    var hud_location_str = findTableElementMatching("Location", 0)

    return [
        { name: "text0", value: "Scan Location", type: "string" },
        { name: "text1", value: hud_location_str, type: "string" },
        { name: "weight1", value: "3", type: "string" },
        { name: "bg_color0", value: "BLUE", type: "string" },
    ];
}

function findTableElementMatching(row_header_text, row_data_index) {
  return Array.from(document.getElementsByTagName("tr")).filter(
      tableRow => tableRow.getElementsByTagName("th")[0].innerText == row_header_text
  )[0].getElementsByTagName("td")[row_data_index].innerText
}

This image would be shown on the HUD.

Velocity Web Example

The scripts on our GitHub page are a good starting point for any Web project, but every deployment will need customizations to target different HTML elements. The logic can be modified depending on the scope (essentially URL) of the webpage. It’s fairly easy to customize the injected JavaScript but may require some level of creativity depending on the website.

During development the injected code can be tested outside Velocity using the Chrome Developer Tools. This makes iterating on the extraction code much faster. For example, the function findTableElementMatching(...){...} could be pasted into the Chrome Debug Console and executed by calling findTableElementMatching("Location", 0). It would print the expected string 'Shelf A1'.

Chrome can also help automating the process of writing code to extract text.

Under the Developer Tools “Elements” tab, right-clicking on an HTML element allows an option to “Copy -> Copy JS Path”. This returns a document.querySelector(...) command to access that HTML element. By simply appending .innerText to the end of this command, text can be read back.

For example, to extract the Location field on the example website (https://six15-technologies.github.io/velocity-web-static-site/), this could be used.

document.querySelector("body > form > table > tbody > tr:nth-child(2) > td").innerText

“Copy JS Path” bases its root at any node with an ID, so they can stay relatively stable on complex pages. For example, to extract the title of this section from this web page, use:

document.querySelector("#web > h1").innerText

Commands like document.querySelector(...) or document.querySelectorAll(...) can be made more or less complex to fit your needs. The types of things selectable on are CSS Selectors. See documentation on querySelector to lean more.