Skip to main content
All CollectionsUser Guides
Analytics: UbiFunctions User Guide
Analytics: UbiFunctions User Guide

Use UbiFunctions serverless computation environment to extract, transform, schedule, and analyze data.

Sergio M avatar
Written by Sergio M
Updated over a week ago

UbiFunctions is a serverless computing environment designed for IoT applications, eliminating the need for additional servers. This guide will walk you through the use of UbiFunctions to enhance your Ubidots experience, whether by extracting data from third-party APIs, customizing HTTP API gateways, or transforming and analyzing sensor data.

Requirements

1. What is an UbiFunction?

An UbiFunction is a user-defined function that executes specific logic on demand. It can be triggered in two main ways:

  1. HTTP Request: By making an HTTP GET or POST request to the UbiFunction's URL.

  2. Scheduled Execution: Based on a predefined repeat interval.

An UbiFunction is structured as follows:

  • Modules: These are the building blocks that your function uses. They contain reusable code and functionality.

  • Entrypoint: This is the main function that serves as the starting point for executing your business logic. In the context of UbiFunctions, this entrypoint is called main.

2. When to use UbiFunctions?

You can use UbiFunctions to extend Ubidots’ capabilities with your custom logic. Most use-cases fall into the following three categories:

  • Extracting data from 3rd party APIs

  • Customizing your own HTTP API gateway

  • Transforming and analyzing sensor data​

2.1. Extracting data from 3rd party APIs

Most web applications have an API and in most cases, you’ll be able to access your data programmatically without much difficulty.

You can use UbiFunctions to turn on or off a Bitcoin cluster based on the Bitcoin/energy price relationship or analyze the relationship between commodity prices and weather conditions.

Weather data extraction example:

  • AmbientWeather devices → AmbientWeather Cloud → Ubidots (see example)

2.2. Customizing your own HTTP API gateway

In cases where HTTP communication with Ubidots is problematic or changing HTTP methods, payloads, or URLs is difficult, UbiFunctions allows you to create custom HTTP APIs to ingest and transform data to fit Ubidots’ expected formats.

Sigfox Devices -->Sigfox Cloud --> Ubidots (show me how)

2.3. Transforming and analyzing sensor data

With UbiFunctions, you have access to many Python and Node.js libraries for data transformation and analysis.

Here some possible use-cases for data transformation and analysis:

  • Assign a GPS location to your devices, based in the neighboring WiFi routers or cell tower IDs. See example.

  • Assess the probability of an accident in the shop floor, based on a worker’s properties (i.e., city, production process, shift) and a machine’s variables (i.e., speed, temperature, pressure, vibration).

3. Understanding the function’s module

After accessing the functions page, from “devices” → “functions”, you’ll see a table with all your functions (in case you’ve already created any, that is) and the button to create them.

The functions’ table has the following characteristics:

  • A search bar to find specific functions based on their name.

  • The name of your functions.

  • The date in which the functions were created.

  • The functions’ URL.

  • A button (represented with three dots) next to each function with the following options:

    • Log: Detailed information about your function’s executions. Logs are explained below in this guide.

    • Edit: Functions can be modified using this option.

    • Disable: Used in case you need to temporarily turn off your function, but wish to keep it for later use.

    • Delete: Used to permanently delete your function.

By clicking on the “+” or the edit buttons, you’ll access the function and its configuration. Every function is divided into three major tabs which are explained below:

3.1. Function’s code editor

This tab contains the following features:

  • Code editor: Where you write your function’s code.

  • Files pane: Where you find all your function's files and directories and create more using the "+" button.

  • HTTP method dropdown menu: Choose to make either GET or POST requests. This option is also located in the function’s settings.

  • Function’s URL: Always available to be easily copied.

  • Runtime: Choose from the available options of Python or Node runtimes. This option is also located in the function’s settings.

  • Console: The output and results of your function’s tests are displayed here.

  • Save & run test button: Test your function with a dummy JSON.

  • Save & deploy button: Once your function is ready, save your latest changes and deploy it.

  • Dark mode button: Change the background color of the code editor between light and dark mode.

  • Files visibility button: Expand or collapse the files pane.

3.2. Function’s settings

The following are the available settings to define how a function works:

  • Name: Your function’s identifier. Keep in mind this name will later become part of the function's URL. The URL can’t be changed later, even if the function’s name is modified.

  • Runtime: The version of Python or Node that you choose to run your function with will define, to a large extent, what your function will be able to do. Check the available runtimes and the libraries that are supported for each one of them in this guide.

  • HTTP method: Either GET or POST can be chosen as the method. A GET method is used to request data from a specified resource, while POST is used to send data to a server to create/update a resource.

  • HTTPS endpoint URL: Here goes the URL of the 3rd party platform to which you'll make the requests.

  • MQTT host: Send data to this function using MQTT

  • MQTT username: Achieve MQTT authentication using the username displayed in this field and one of your tokens as the password.

  • MQTT topic: Payloads received in this topic will be forwarded to this function

  • Token: You can use the dropdown menu to select the token that will authenticate your requests. You can call in your function’s code using the token key; this spares you from having to copy-pasting API tokens in the code directly.

  • Environment variables: Select any number of environment variables you have previously created through the Global Properties module. With these properties, you can easily store global variables to speed development and keep resources organized. Follow the Global Properties guide to learn how to use them.

  • Max execution time: If a function isn't executed within this time, it's terminated and a 502 response is generated. By default, this time is 10 seconds and can be increased depending on your plan, as follows:

    • Entrepreneur: Up to 30 seconds.

    • Professional: Up to 30 seconds.

    • Industrial: Up to 60 seconds.

    • All Enterprise tiers: Up to 60 seconds.

Notes:

- Functions can have "max execution times" above 60 seconds if it's agreed upon between the user and Ubidots. However, if one of this "special" functions takes over 60 seconds to be executed (even with it's higher “max execution time”), it becomes asynchronous. This means that the function will run, but Ubidots won’t wait for its response; the response of an asynchronous function will be available in its logs.

- If you require higher execution times, reach out to us at sales@ubidots.com

  • Time-based trigger: If you need your function to run at periodic time intervals, define a time-based trigger, expressed in minutes. The minimum value is 1 and the maximum is 60.

    • Crontab expression: Specify, based on the date/time, the frequency at which the functions will be executed.

  • Raw function: Use raw functions to achieve an advanced level of customization. Raw functions allow you to:

    • Use a custom endpoint path.

    • Receive and respond custom headers.

    • Set custom response codes.

    • Receive and respond custom content-type headers for the request body and response.

    • Receive any type of data structure (object, string, array, images, etc.). Keep in mind, however, that it will be parsed to a string. Normal functions (those that don’t use raw) can only receive JSON in their bodies.

  • CORS policy: Used to enable the function being called from web browser's URLs different from parse.ubidots.com.

3.3. Function’s logs

The logs’ page is divided into two sections, the left pane and the content area. In them, you’ll find the following information.

  • Logs’ list: Located in the left pane, you’ll find the list of logs of all the executions of your function. Each item of the list contains the date and time of the execution, the execution’s ID, and the function’s execution time. Every item of the list can be clicked to access more information.

  • Results: Located in the content area, the results of the function’s return statement will be displayed in this field. If your function doesn’t have a return statement, a “null” message will be displayed.

  • Output: Located below the results field, whatever your function prints will be displayed here. Also, server messages will be displayed here as well when an error occurs on the server side or the function times out.

4. Coding an UbiFunction

Runtimes

UbiFunctions support either NodeJS LTS or Python3. This means you can use either JavaScript (Node.js) or Python, but not both simultaneously.

Function's arguments

The UbiFunction main script receives the arguments as a dictionary labeled "args", which you can then access in your code to execute the desired logic.

Code modularity

As stated before, UbiFunctions allows you to use multiple files:

  • Source files: These files contain code that can be executed.

  • Config files: These are tipically JSON or .conf files whose contents is not executable code but contain data and parameters required by your logic.

  • Data files: For example a .CSV file.

Default example

By default, every new UbiFunction is populated with a sample code that uses input data (token, device, & temp value) to make a request to Ubidots’ API. This sample code demonstrates a simple HTTP GET endpoint that receives an Ubidots token, a device label, and a temperature value as URL parameters, then uses this data to make an HTTP POST request to Ubidots’ API.

You can actually test the sample function by clicking on the “save & deploy” button, then paste the following URL into your web browser (updating the username and token of your account.)

Note: The identifier of any function within UbiFunctions is its name. Here, the test function is named "new-function" and accordingly, by testing the sample function provided, the UbiFunctions engine will identify this particular function in your account (no one else's) and report back a temperature value and token.

You may now begin coding your function. Please note that the return payload must always be in a dictionary format. Sending a single variable cannot be understood and must be returned in a dictionary format.

YES

return {"message": "OK"}

NO

return "ok"

To know what libraries UbiFunctions modules currently supports, please check this article UbiFunctions & Plugins: Supported libraries.

More examples:

Multiple files function example

Suppose for example that you have a fleet of several different devices on an LNS constantly sending data and you require to decode that data and then forward it to the corresponding device in Ubidots.

For this, you can create an UbiFunction and forward all the LNS data to it, where you'll have separate scripts for each device's decoder and properly use them from the main function.

For the example, proceed to create:

  • A folder called modules.

    • Within the modules folder, create a Python script called s2100.py. This is the decoder for the s2100 device.

    • Within modules folder, create a python script called s210x.py. This is the decoder for the s210x device.

    • within modules folder, create a python script called utils.py. This one will contain all utils scripts.

  • A config file containing configuration for each device type. This one is called device_config.json.

s2100.py

def s2100_decode(data: int) -> dict:
"""Decode data for s2100 device."""
temperature = (data & 0x3) >> 0
humidity = (data & (0x3 << 2)) >> 2
battery = (data & (0x3 << 4)) >> 4

return {
"temperature": temperature,
"humidity": humidity,
"battery": battery
}

s210x.py

def s210x_decode(data: int) -> dict:
"""Decode data for s210x device."""
soil_electrical_conductivity = (data & 0x3) >> 0
battery = (data & (0x3 << 2)) >> 2

return {
"soil_electrical_conductivity": soil_electrical_conductivity,
"battery": battery
}

utils.py

def parse_data(data_str: str) -> int:
"""Convert a binary string to an integer."""
return int(data_str, 2)

device_config.json

{
"s2100" : {"color" : "#fefefe"},
"s210x" : {"color" : "#ffffff"}
}

main.py

import requests
import time
import json
import os
from modules.utils import parse_data
from modules.s2100 import s2100_decode
from modules.s210x import s210x_decode

decoders = {
"s210x" : s2100_decode,
"s2100" : s210x_decode
}

def main(args):

"""Decode payload based on device type."""
device_type = args.get("device")
data_str = args.get("data")

if not device_type or device_type not in decoders:
print(f"Device type: {device_type} not supported...")
return {"status" : "error"}

# Convert binary string to integer
data = parse_data(data_str)
decoded_data = decoders[device_type](data)

# Open and read the JSON file
with open("device_config.json", 'r') as file:
config = json.load(file)

print(config[device_type])
print(decoded_data)
return {"status" : "success" , "data" : decoded_data}


This is how it should look like:​

If you execute this UbiFunction with the following test payload, it should output something like:

Test payload:

{"device": "s2100", "data": "0b01011010"}

UbiFunction's output:

{'color': '#fefefe'}
{'soil_electrical_conductivity': 2, 'battery': 2}

5. Running the function

During the development process, you will want to test your function to see if you are on the right track towards the desired output. To do so, click on the "save & run test” button. This will bring up a modal screen requesting some input data to test it with. Enter a JSON dictionary of sample inputs if required. The console will display the function’s output and logs. You may leave this blank if your function does not require an input.

In the case of Ubidots default sample function, enter the JSON payload below containing the parameters you look to pass through the HTTP GET request:

{
"token":"your-token",
"Device":"sample-function",
"Temperature":"45"
}

After clicking on the "save" button, a console window will emerge with both the function’s output and the logs of the results:

  • Function’s output: This is what your function returns. It must be in a JSON dictionary format. In your code, make sure to use "return" to quit the function and return the dictionary.

  • Logs: This is used to debug purposes. Use console.log("hello") to leave trails in this console.

Note: For your function to be fully deployed after clicking on the “save & deploy” button, wait some time (about 10 seconds are usually enough).

6. Billing of UbiFunctions

UbiFunctions are included in Industrial plans and above.

  • Executions are timed-out after 30 or 60 seconds, based on your plan. If you wish to extend this time, please reach out to our support team.

  • If any execution surpasses 2 seconds; it is counted as 2 executions (or more, based on time length of the execution)

  • Executions are billed at increments of $5 per million executions.

Did this answer your question?