# Multiple files

## Overview

Supporting multiple files in UbiFunctions improves flexibility and scalability. When building serverless functions, splitting code into multiple files improves maintainability, enables reuse, and aligns with common software development practices.

UbiFunctions allows you to use multiple files such as:

* **Source files:** These files contain code that can be executed.
* **Config files:** These are typically `.json`, `.conf`, `.toml`, or similar files. They do not contain executable code. Instead, they provide the data and parameters your logic needs to run with a particular behavior.
* **Data files:** These are files that store information in various formats, such as `.csv`, `.txt`, or `.json`. They can contain structured, semi-structured, or unstructured data used as input for processing or analysis.

## Creating a file

To create a file in your UbiFunction root directory, click the **+** icon in the files pane, then select **New file**. Enter a name. The new file is added to your UbiFunction:

| <img src="/files/mStoTfiqqmAKNQjHHHZv" alt="" data-size="original"> | <img src="/files/oXYmbuOZJSsxatfs8aLm" alt="" data-size="original"> |
| ------------------------------------------------------------------- | ------------------------------------------------------------------- |

## Creating a folder

Folders help you organize modules more effectively. To create a folder, click the **+** icon in the files pane, then select **New folder**. Enter a name. The new folder is added to your UbiFunction:

| <img src="/files/9yUB4Nm8UMTlDCaCduoY" alt="" data-size="original"> | <img src="/files/zNvKeNITaIeWqupw1nSf" alt="" data-size="original"> |
| ------------------------------------------------------------------- | ------------------------------------------------------------------- |

### Creating files or folders within a folder

To create a file or folder inside an existing folder, hover over the folder, click the three-dot menu, and select **New file** or **New folder**.

| <img src="/files/gHttSogT3UrEtQddgdi7" alt="" data-size="original"> | <img src="/files/ZUMsk11TzAyPFVikTtiW" alt="" data-size="original"> |
| ------------------------------------------------------------------- | ------------------------------------------------------------------- |

## File structure and imports

When you add files or folders to a UbiFunction, they behave like a directory on your local machine, with the root directory as the working directory. For example, if you add `decoder.py|js` alongside the main script, you can import it like this:

{% tabs %}
{% tab title="JavaScript" %}

```javascript
const decoder = require('./decoder');
```

{% endtab %}

{% tab title="Python" %}

```python
import decoder
```

{% endtab %}
{% endtabs %}

Similarly, if you create a folder `utils` and add a file `parser.py` or `parser.js` inside it, the structure behaves like a subdirectory on your computer. To import from it, use:

{% tabs %}
{% tab title="JavaScript" %}

```javascript
const parser = require('./utils/parser');
```

{% endtab %}

{% tab title="Python" %}

```python
from utils import parser
```

{% endtab %}
{% endtabs %}

This means you can treat the UbiFunction file structure like a local project folder.

{% hint style="info" %}
UbiFunctions use a protected file system. You can create files and folders through the UI, but scripts run in a strictly read-only environment and cannot write files or create new files and folders.
{% endhint %}

## Data and config files

UbiFunctions let you include data and configuration files, such as `.json`, `.conf`, `.txt`, and `.csv`, to provide inputs or settings for your functions. To read a file, handle it the same way you would in a local script:

{% tabs %}
{% tab title="JavaScript" %}

```javascript
const fs = require('fs');

// Read a JSON file
// Adjust the file's path accordingly
const settings = JSON.parse(fs.readFileSync('settings.json', 'utf-8'));
```

{% endtab %}

{% tab title="Python" %}

```python
import json

# Read a JSON file
# Adjust the file's path accordingly
with open('settings.json', 'r') as file:
    settings = json.load(file)
print(settings)
```

{% endtab %}
{% endtabs %}

## Example

Suppose you have several device types on an LNS sending data continuously. For each uplink, you need to decode the payload and forward it to the corresponding device in Ubidots.

You can solve this with a UbiFunction that uses separate scripts for each device type and calls them from the main function.

For this example, create:

* A folder called *modules*. Inside it, create:
  * A Python script called *s2100.py*. This is the decoder for the s2100 device.
  * A Python script called *s210x.py*. This is the decoder for the s210x device.
  * A Python script called *utils.py*. This file contains shared utility functions.
* A configuration file called *device\_config.json* that stores the configuration for each device type.

It should look like this:\ <img src="/files/bYip2uwDXejqAailW8Gs" alt="" data-size="original">

The following are the contents of each script:

**s2100.py script**

```python
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 script <a href="#h_bc270579ec" id="h_bc270579ec"></a>

```python
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 script <a href="#h_e0291278e3" id="h_e0291278e3"></a>

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

```

\
**device\_config.json**

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

#### main.py <a href="#h_b306192085" id="h_b306192085"></a>

```python
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}
```

If you execute this UbiFunction with the following test payload:

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

It should output something like this:

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dev.ubidots.com/ubifunctions/storage/mutiple-files.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
