Autoscaling Profile Limits

autoscalingprofilelimitsmonitoringmanagement

Monitor and manage autoscaling profile limits and usage

pythonautoscaling-profiles-limits.py
"""
TagoIO - Analysis Example
Auto Scaling analysis

Check out the SDK documentation on: https://js.sdk.tago.io

Ths is a script to automatically check your current usage, and auto-scale your account if needed.
You can get the analysis template with all the Environment Variables here:
        https://admin.tago.io/template/62151212ec8d8f0012c52772

In order to use this analysis, you must setup all the environment variables needed.
You're also required to create an Action of trigger type Schedule, and choose to run this analysis.
In the action you set how often you want to run this script to check your limits. It can set to a minimum of 1 minute.

Environment Variables
In order to use this analysis, you must setup the Environment Variable table.
account_token: Your account token. Check the steps at the end to understand how to generate it.
input: 95. The 95 value will scale data input when it reachs 95% of the usage. Keep it blank to not scale data input.
output: 95
data_records: 95
analysis: 95
sms: 95
email: 95
push_notification: 95
file_storage: 95

Steps to generate an account_token:
1 - Enter the following link: https://admin.tago.io/account/
2 - Select your Profile.
3 - Enter Tokens tab.
4 - Generate a new Token with Expires Never.
5 - Press the Copy Button and place at the Environment Variables tab of this analysis.
"""

from dataclasses import dataclass
from typing import Optional

from tagoio_sdk import Account, Analysis
from tagoio_sdk.modules.Utils.envToJson import envToJson
from tagoio_sdk.modules.Account.Billing_Type import (
    BillingPrices,
    BillingSubscriptionServices,
)


@dataclass
class CheckAutoScaleSource:
    type: str
    current_value: int
    limit: int
    scale: float
    billing: BillingPrices
    account_limit: BillingSubscriptionServices


def check_auto_scale(data: CheckAutoScaleSource) -> Optional[int]:
    # Stop if current use is less than 95% of what was hired.
    if data.limit <= 0 or data.limit * (data.scale * 0.01) > data.current_value:
        return None

    service_billing = next(
        (
            obj
            for obj in data.billing[data.type]
            if obj["amount"] > data.account_limit[data.type]["limit"]
        ),
        None,
    )
    return service_billing["amount"] if service_billing else None


def get_profile_id_by_token(account: Account, token: str) -> Optional[str]:
    profiles = account.profile.list()

    for profile in profiles:
        token_exist = [
            obj
            for obj in account.profile.tokenList(profileID=profile["id"])
            if obj["token"] == token
        ]
        if token_exist:
            return profile["id"]
    raise Exception(
        "Profile not found for the account token in the environment variable"
    )


def my_analysis(context, list: list = None):
    # Get the environment variables and parses it to a JSON
    environment = envToJson(environment=context.environment)

    if not environment:
        raise ValueError("[ERROR] environment variable empty.")

    if not environment.get("account_token"):
        raise ValueError(
            "[ERROR] You must enter a valid account_token in the environment variable"
        )

    # Setup the account and get's the ID of the profile the account token belongs to.
    account = Account({"token": environment["account_token"]})
    profile_id = get_profile_id_by_token(
        account=account, token=environment["account_token"]
    )

    # Get the current subscriptions of our account for all the services.
    services_limit = (account.billing.getSubscription())["services"]

    # get current limit and used resources of the profile.
    summary = account.profile.summary(profileID=profile_id)
    limit, limit_used = summary["limit"], summary["limit_used"]

    # get the tiers of all services, so we know the next tier for our limits.
    billing_prices = account.billing.getPrices()

    # Check each service to see if it needs scaling
    auto_scale_services = {}
    for statistic_key in limit:
        if statistic_key not in environment:
            continue

        if not environment[statistic_key].isnumeric():
            print(
                f"[ERROR] Ignoring {statistic_key}, because the environment variable value is not a number."
            )
            continue

        scale = float(environment[statistic_key])
        if scale == 0:
            continue

        data = CheckAutoScaleSource(
            type=statistic_key,
            current_value=limit_used[statistic_key],
            limit=limit[statistic_key],
            scale=scale,
            billing=billing_prices,
            account_limit=services_limit,
        )
        result = check_auto_scale(data=data)
        if result:
            auto_scale_services[statistic_key] = {"limit": result}

    # Stop if no auto-scale needed
    if not auto_scale_services:
        print("Services are okay, no auto-scaling needed.")
        return "Services are okay, no auto-scaling needed."

    print(f"Auto-scaling the services: {', '.join(auto_scale_services.keys())}")
    # Update our subscription, so we are actually scaling the account.
    try:
        billing_success = account.billing.editSubscription(
            subscription={"services": auto_scale_services}
        )
    except Exception as error:
        print(f"[ERROR] {error}")
        return error

    if not billing_success:
        return

    # Stop here if account has only one profile. No need to reallocate resources
    profiles = account.profile.list()
    if len(profiles) > 1:
        # Make sure we realocate only what we just subscribed
        amount_to_relocate = {}
        for key in services_limit:
            amount_to_relocate[key] = services_limit[key]["limit"] - (
                amount_to_relocate.get(key, {}).get("limit", 0)
            )

        # Allocate all the subscribed limit to the profile.
        try:
            account.billing.editAllocation(
                allocation={
                    "profile": profile_id,
                    **amount_to_relocate,
                }
            )
        except Exception as error:
            print(f"[ERROR] {error}")
            return error

    return billing_success


# The analysis token in only necessary to run the analysis outside TagoIO
# To run the tests you need to comment out the line below
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Statistical Data Analysis

statisticsaverageminmaxdatacalculation

Calculate minimum, maximum, and average values from device variables

pythonavg-min-max.py
"""
Analysis Example
Minimum, maximum, and average

Get the minimum, maximum, and the average value of the variable temperature from your device,
and save these values in new variables

Instructions
To run this analysis you need to add a device token to the environment variables,
To do that, go to your device, then token and copy your token.
Go the the analysis, then environment variables,
type device_token on key, and paste your token on value
"""

from tagoio_sdk import Analysis, Device


def temperature_minimum(device: Device) -> None:
    """Record the minimum temperature of the last day in the bucket variable

    Args:
        device (Device): Instance of the Device class
    """
    # This is a filter to get the minimum value of the variable temperature in the last day
    min_filter = {"variables": "temperature", "query": "min", "start_date": "1 day"}

    # Now we use the filter for the device to get the data
    # check if the variable min has any value
    # if so, we crete a new object to send to TagoIO
    min_result = device.getData(queryParams=min_filter)

    if min_result:
        min_value = {
            "variable": "temperature_minimum",
            "value": min_result[0]["value"],
            "unit": "F",
        }

        # now we insert the new object with the minimum value
        device.sendData(data=min_value)
        print(f"Temperature Minimum - {min_result[0]['value']}")
    else:
        print("Minimum value not found")


def temperature_maximum(device: Device) -> None:
    """Record the maximum temperature of the last day in the bucket variable

    Args:
        device (Device): Instance of the Device class
    """
    # This is a filter to get the maximum value of the variable temperature in the last day
    max_filter = {
        "variables": "temperature",
        "query": "max",
        "start_date": "1 day",
    }

    max_result = device.getData(queryParams=max_filter)
    if max_result:
        max_value = {
            "variable": "temperature_maximum",
            "value": max_result[0]["value"],
            "unit": "F",
        }

        # now we insert the new object with the Maximum value
        device.sendData(data=max_value)

        print(f"Temperature Maximum - {max_result[0]['value']}")

    else:
        print("Maximum value not found")


def temperature_average(device: Device) -> None:
    """Record the average of the last day's temperatures in the bucket variable

    Args:
        device (Device): Instance of the Device class
    """
    # This is a filter to get the last 1000 values of the variable temperature in the last day
    average_filter = {
        "variable": "temperature",
        "qty": 1000,
        "start_date": "1 day",
    }

    average = device.getData(queryParams=average_filter)
    if average:
        temperature_average = 0
        for item in average:
            temperature_average = float(temperature_average) + float(item["value"])

        temperature_average = temperature_average / len(average)

        average_value = {
            "variable": "temperature_average",
            "value": temperature_average,
            "unit": "F",
        }

        device.sendData(data=average_value)

        print(f"Temperature Average - {temperature_average}")
    else:
        print("No result found for the avg calculation")


# The function myAnalysis will run when you execute your analysis
def my_analysis(context, scope: list) -> None:
    # reads the value of device_token from the environment variable
    device_token = list(
        filter(
            lambda device_token: device_token["key"] == "device_token",
            context.environment,
        )
    )
    device_token = device_token[0]["value"]

    if not device_token:
        raise ValueError("Missing value: 'device_token' Environment Variable.")

    my_device = Device({"token": device_token})

    temperature_minimum(device=my_device)
    temperature_maximum(device=my_device)
    temperature_average(device=my_device)


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis({"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Configuration Parameters for Dynamic Last Value

configurationparametersdynamicwidgetdashboard

Manage configuration parameters for dynamic last value widgets

pythonconfiguration-parameters-for-dynamic-last-value.py
"""
Analysis Example
Configuration parameters for dynamic last value

Set the configurations parameters with the last value of a given variable,
in this example it is the "temperature" variable

Environment Variables
In order to use this analysis, you must setup the Environment Variable table.

account_token: Your account token. Check bellow how to get this.

Steps to generate an account_token:
1 - Enter the following link: https://admin.tago.io/account/
2 - Select your Profile.
3 - Enter Tokens tab.
4 - Generate a new Token with Expires Never.
5 - Press the Copy Button and place at the Environment Variables tab of this analysis.
"""

from queue import Queue
from datetime import datetime

from tagoio_sdk import Account, Analysis
from tagoio_sdk.modules.Utils.envToJson import envToJson
from tagoio_sdk.modules.Utils.getDevice import getDevice


def get_param(params: list, key: str) -> dict:
    """Get the desired parameter from the list of parameters

    Args:
        params (list): list of parameters
        key (str): parameter desired to return

    Returns:
        dict: object with the key and value of the parameter you chose
    """
    return next(
        (x for x in params if x["key"] == key),
        {"key": key, "value": "-", "sent": False},
    )


def apply_device_calculation(device: dict, timezone: str) -> None:
    deviceID, name, account = device["id"], device["name"], device["account"]
    deviceInfoText = f"{name}({deviceID})"
    print(f"Processing Device {deviceInfoText})")
    device = getDevice(account, deviceID)

    # Get the temperature variable inside the device bucket.
    # notice it will get the last record at the time the analysis is running.
    dataResult = device.getData({"variables": ["temperature"], "query": "last_value"})
    if not dataResult:
        print(f"No data found for {deviceInfoText}")
        return

    # Get configuration params list of the device
    deviceParams = account.devices.paramList(deviceID)

    # get the variable temperature from our dataResult array
    temperature = next(
        (data for data in dataResult if data["variable"] == "temperature"), None
    )
    if temperature:
        # get the config. parameter with key temperature
        temperatureParam = get_param(deviceParams, "temperature")
        # get the config. parameter with key last_record_time
        lastRecordParam = get_param(deviceParams, "last_record_time")

        timeString = (
            datetime.fromtimestamp(temperature["time"])
            .astimezone(timezone)
            .strftime("%Y/%m/%d %I:%M %p")
        )

        # creates or edit the tempreature Param with the value of temperature.
        # creates or edit the last_record_time Param with the time of temperature.
        # Make sure to cast the value to STRING, otherwise you'll get an error.
        account.devices.paramSet(
            deviceID,
            [
                {**temperatureParam, "value": str(temperature["value"])},
                {**lastRecordParam, "value": timeString},
            ],
        )


def my_analysis(context: any, scope: list = None) -> None:
    environment = envToJson(context.environment)

    if not environment.get("account_token"):
        raise ValueError("Missing account_token environment var")
    # Make sure you have account_token tag in the environment variable of the analysis.
    account = Account({"token": environment["account_token"]})

    # Create a queue, so we don't run on Throughput errors.
    # The queue will make sure we check only 5 devices simultaneously.
    processQueue = Queue(maxsize=5)
    processQueue.put_nowait(apply_device_calculation)

    # fetch device list filtered by tags.
    # Device list always return an Array with DeviceInfo object.
    deviceList = account.devices.listDevice(
        {
            "amount": 500,
            "fields": ["id", "name", "tags"],
            "filter": {"tags": [{"key": "type", "value": "sensor"}]},
        }
    )

    for device in deviceList:
        processQueue.put(
            device={"id": device["id"], "name": device["name"], "account": account},
            timezone=account.info().get("timezone", "America/New_York"),
        )

    # Wait for all queue to be processed
    processQueue.join()


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Console Hello World

basicconsolehellologgingdebug

Basic hello world example showing how to send messages to the analysis console

pythonconsole.py
"""
Analysis Example
Hello World

Learn how to send messages to the console located on the TagoIO analysis screen.
You can use this principle to show any information during and after development.
"""

from tagoio_sdk import Analysis


# The function myAnalysis will run when you execute your analysis
def myAnalysis(context, scope: list) -> None:
    # This will log "Hello World" at the TagoIO Analysis console
    print("Hello World")

    #  This will log the environment to the TagoIO Analysis console
    print("Environment:", context.environment)

    #  This will log the scope to the TagoIO Analysis console
    print("my scope:", scope)


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis({"token": "MY-ANALYSIS-TOKEN-HERE"}).init(myAnalysis)

Create Device

devicecreatedashboardautomation

Create new devices programmatically using dashboard inputs

pythoncreate-device.py
"""
Analysis Example
Creating devices using dashboard

Using an Input Widget in the dashboard, you will be able to create devices in your account.
You can get the dashboard template to use here: https://admin.tago.io/template/6143555a314cef001871ec78
Use a dummy HTTPs device with the dashboard.

Environment Variables
In order to use this analysis, you must setup the Environment Variable table.
  account_token: Your account token. Check bellow how to get this.

Steps to generate an account_token:
1 - Enter the following link: https://admin.tago.io/account/
2 - Select your Profile.
3 - Enter Tokens tab.
4 - Generate a new Token with Expires Never.
5 - Press the Copy Button and place at the Environment Variables tab of this analysis.
"""

from tagoio_sdk import Analysis, Device, Account
from tagoio_sdk.modules.Utils.getTokenByName import getTokenByName
from tagoio_sdk.modules.Account.Device_Type import DeviceCreateInfo


def add_configuration_parameter_to_device(account: Account, device_id: str) -> None:
    account.devices.paramSet(
        deviceID=device_id, configObj={"key": "param_key", "value": "10", "sent": False}
    )


def send_feedback_to_dashboard(account: Account, device_id: str) -> None:
    dashboard_token = getTokenByName(account=account, deviceID=device_id)
    device = Device(params={"token": dashboard_token})

    # To add any data to the device that was just created:
    # device.sendData({ "variable": "temperature", value: 17 })

    device.sendData(
        data={
            "variable": "validation",
            "value": "Device successfully created!",
            "metadata": {"type": "success"},
        }
    )


def parse_new_device(scope: list[dict]) -> DeviceCreateInfo:
    # Get the variables sent by the widget/dashboard.
    device_network = [obj for obj in scope if obj["variable"] == "device_network"]
    device_connector = [obj for obj in scope if obj["variable"] == "device_connector"]
    device_name = [obj for obj in scope if obj["variable"] == "device_name"]
    device_eui = [obj for obj in scope if obj["variable"] == "device_eui"]

    if not device_network or not device_network[0]["value"]:
        raise TypeError('Missing "device_network" in the data scope.')
    elif not device_connector or not device_connector[0]["value"]:
        raise TypeError('Missing "device_connector" in the data scope.')
    elif not device_eui or not device_eui[0]["value"]:
        raise TypeError('Missing "device_eui" in the data scope.')

    return {
        "name": device_name[0]["value"],
        "serie_number": device_eui[0]["value"],
        "tags": [
            # You can add custom tags here.
            {"key": "type", "value": "sensor"},
            {"key": "device_eui", "value": device_eui[0]["value"]},
        ],
        "connector": device_connector[0]["value"],
        "network": device_network[0]["value"],
        "active": True,
        "type": "immutable",
        "chunk_period": "month",  # consider change
        "chunk_retention": 1,  # consider change
    }


def start_analysis(context: list[dict], scope: list[dict]) -> None:
    if not scope:
        return print("The analysis must be triggered by a widget.")

    # reads the value of account_token from the environment variable
    account_token = list(
        filter(
            lambda account_token: account_token["key"] == "account_token",
            context.environment,
        )
    )
    account_token = account_token[0]["value"]

    if not account_token:
        return print("Missing account_token Environment Variable.")

    account = Account(params={"token": account_token})

    new_device = parse_new_device(scope=scope)

    result = account.devices.create(deviceObj=new_device)
    print(result)

    add_configuration_parameter_to_device(
        account=account, device_id=result["device_id"]
    )

    send_feedback_to_dashboard(account=account, device_id=scope[0]["device"])


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(start_analysis)

Custom Data Retention

dataretentioncleanupmanagementautomation

Automatically remove old data from devices based on custom retention policies

pythondata-retention.py
"""
Analysis Example
Custom Data Retention

Use your account token to get the list of devices, then go to each device removing the
variables you chooses.

Instructions
To run this analysis you need to add an account token to the environment variables,
To do that, go to your account settings, then token and copy your token.
Go the the analysis, then environment variables,
type account_token on key, and paste your token on value
"""

from tagoio_sdk import Analysis, Account, Device
from tagoio_sdk.modules.Utils.getTokenByName import getTokenByName


# The function myAnalysis will run when you execute your analysis
def my_analysis(context, scope: list):
    # reads the value of account_token from the environment variable
    account_token = next(
        (item for item in context.environment if item["key"] == "account_token"), None
    )

    if not account_token:
        raise ValueError("Missing 'account_token' in the environment variables")

    account = Account({"token": account_token["value"]})

    # Bellow is an empty filter.
    # Examples of filter:
    # { tags: [{ key: 'tag-key', value: 'tag-value' }]}
    # { name: 'name*' }
    # { name: '*name' }
    # { bucket: 'bucket-id' }
    filter = {}

    devices = account.devices.listDevice(
        {
            "page": 1,
            "fields": ["id"],
            "filter": filter,
            "amount": 100,
        }
    )

    for device_obj in devices:
        token = getTokenByName(account, device_obj["id"])
        device = Device({"token": token})

        variables = ["temperature"]
        qty = 100  # remove 100 registers of each variable
        end_date = "30 days"  # registers old than 30 days

        result = device.deleteData(
            {"variables": variables, "qty": qty, "end_date": end_date}
        )
        print(result)


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Data Transaction Counter

transactiondataanalyticscalculationreporting

Calculate total transactions by device and group results by user tags

pythondata-transaction.py
"""
Analysis Example
Get users total transactions

This analysis must run by an Scheduled Action.
It gets a total amount of transactions by device, calculating by the total amount of data in the bucket
each time the analysis run. Group the result by a tag.

Environment Variables
In order to use this analysis, you must setup the Environment Variable table.

device_token: Token of a device where the total transactions will be stored. Get this in the Device's page.
account_token: Your account token. Check bellow how to get this.

Steps to generate an account_token:
1 - Enter the following link: https://admin.tago.io/account/
2 - Select your Profile.
3 - Enter Tokens tab.
4 - Generate a new Token with Expires Never.
5 - Press the Copy Button and place at the Environment Variables tab of this analysis.
"""

from tagoio_sdk import Analysis, Account, Device
from tagoio_sdk.modules.Utils.envToJson import envToJson


def calculate_user_transactions(
    account: Account, storage: Device, user_value: str, device_list: list
) -> None:
    # Collect the data amount for each device.
    # Result of bucket_results is:
    # [0, 120, 500, 0, 1000]
    for device in device_list:
        total_transactions = account.buckets.amount(device["bucket"])

        # Get the total transactions of the last analysis run.
        # Group is used to get only for this user.
        # You can change that to get a specific device for the user, instead of using a global storage device.
        # One way to do that is by just finding the device using a tag, see example:
        #
        # [user_device] = account.devices.list({'page': 1, 'fields': ['id', 'name', 'bucket', 'tags'], 'filter': {'tags': [{'key': 'user_device', 'value': user_value}]}, 'amount': 1})
        # device_token = Utils.getTokenByName(account, user_device['id'])
        # storage = Device({'token': device_token})
        last_total_transactions = storage.getData(
            {"variable": "last_transactions", "qty": 1, "group": user_value}
        )

        if not last_total_transactions:
            last_total_transactions = [{"value": 0}]

        last_total_transactions = last_total_transactions[0]

        result = total_transactions - last_total_transactions["value"]

        # Store the current total of transactions, the result for this analysis run and the key.
        # Now you can just plot these variables in a dynamic table.
        storage.sendData(
            data=[
                {
                    "variable": "last_transactions",
                    "value": total_transactions,
                    "group": user_value,
                },
                {
                    "variable": "transactions_result",
                    "value": result,
                    "group": user_value,
                },
                {"variable": "user", "value": user_value, "group": user_value},
            ]
        )

    print("Done!")


def my_analysis(context: any, scope: list = None) -> None:
    # Transform all Environment Variable to JSON.
    environment = envToJson(context.environment)

    if not environment.get("account_token"):
        raise ValueError(
            "You must setup an account_token in the Environment Variables."
        )

    elif not environment.get("device_token"):
        raise ValueError("You must setup an device_token in the Environment Variables.")

    # Instance the account class
    account = Account(params={"token": environment["account_token"]})
    storage = Device(params={"token": environment["device_token"]})

    # Setup the tag we will be searching in the device list
    tag_to_search = "user_email"

    # Get the device_list and group it by the tag value.
    device_list = account.devices.listDevice(
        {
            "page": 1,
            "fields": ["id", "name", "bucket", "tags"],
            "filter": {"tags": [{"key": tag_to_search}]},
            "amount": 10000,
        }
    )

    grouped_device_list = {}

    for device in device_list:
        tag_value = None

        for tag in device["tags"]:
            if tag["key"] == tag_to_search:
                tag_value = tag["value"]
                break

        if tag_value:
            if tag_value not in grouped_device_list:
                grouped_device_list[tag_value] = []
            grouped_device_list[tag_value].append(device)

    grouped_device_list = [
        {"value": key, "device_list": value}
        for key, value in grouped_device_list.items()
    ]

    # Call a new function for each group in assynchronous way.
    calculate_user_transactions(
        account=account,
        storage=storage,
        user_value=grouped_device_list[0]["value"],
        device_list=grouped_device_list[0]["device_list"],
    )


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Device List

devicelistaccountmanagement

Get and display list of devices in your account

pythondevice-list.py
"""
Analysis Example
Get Device List

This analysis retrieves the device list of your account and print to the console.
There are examples on how to apply filter.

Environment Variables
In order to use this analysis, you must setup the Environment Variable table.

account_token: Your account token

Steps to generate an account_token:
1 - Enter the following link: https://admin.tago.io/account/
2 - Select your Profile.
3 - Enter Tokens tab.
4 - Generate a new Token with Expires Never.
5 - Press the Copy Button and place at the Environment Variables tab of this analysis.
"""

from tagoio_sdk import Account, Analysis
from tagoio_sdk.modules.Account.Device_Type import DeviceInfoList


def get_device_list(account: Account) -> list[DeviceInfoList]:
    """Retrieves the device list of your account.

    Args:
        account (Account): Instance of the class Account

    Returns:
        list[DeviceInfoList]: List of devices
    """
    # Example of filtering devices by Tag.
    # You can filter by: name, last_input, last_output, bucket, etc.
    my_filter = {
        "tags": [
            {"key": "keyOfTagWeWantToSearch", "value": "valueOfTagWeWantToSearch"}
        ],
        # "bucket": "55d269211a2e236c25bb9859",
        # "name": "My Device",
        # "name": "My Dev*"
    }

    devices = account.devices.listDevice(
        {"page": 1, "fields": ["id", "tags"], "filter": my_filter, "amount": 20}
    )

    return devices


def my_analysis(context, scope: list) -> None:
    # reads the value of account_token from the environment variable
    account_token = list(
        filter(
            lambda account_token: account_token["key"] == "account_token",
            context.environment,
        )
    )
    if account_token:
        account_token = account_token[0].get("value")

    if not account_token:
        return print("Missing account_token Environment Variable.")

    account = Account(params={"token": account_token})
    list_devices = get_device_list(account=account)

    print(list_devices)
    print(f"Total devices: {len(list_devices)}")


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Device Offline Alert

deviceofflinealertmonitoringnotification

Monitor devices and send alerts when they haven't communicated within specified time intervals

pythondevice-offline.py
"""
Analysis Example
Device Offline Alert

This analysis must run by Time Interval. It checks if devices with given Tags
had communication in the past minutes. If not, it sends an email or sms alert.

Environment Variables
In order to use this analysis, you must setup the Environment Variable table.

account_token: Your account token
check_in_time: Minutes between the last input of the device before sending the notification.
tag_key: Device tag Key to filter the devices.
tag_value: Device tag Value to filter the devices.
email_list: Email list comma separated.
sms_list: Phone number list comma separated. The phone number must include the country code

Steps to generate an account_token:
1 - Enter the following link: https://admin.tago.io/account/
2 - Select your Profile.
3 - Enter Tokens tab.
4 - Generate a new Token with Expires Never.
5 - Press the Copy Button and place at the Environment Variables tab of this analysis.
"""

from datetime import datetime

from tagoio_sdk import Account, Analysis, Services
from tagoio_sdk.modules.Utils.envToJson import envToJson


def my_analysis(context, scope: list = None):
    # Transform all Environment Variable to JSON.
    env = envToJson(context.environment)

    if not env.get("account_token"):
        return print("You must setup an account_token in the Environment Variables.")
    elif not env.get("check_in_time"):
        return print("You must setup a check_in_time in the Environment Variables.")
    elif not env.get("tag_key"):
        return print("You must setup a tag_key in the Environment Variables.")
    elif not env.get("tag_value"):
        return print("You must setup a tag_value in the Environment Variables.")
    elif not env.get("email_list") and not env.get("sms_list"):
        return print(
            "You must setup an email_list or a sms_list in the Environment Variables."
        )

    check_in_time = int(env.get("check_in_time"))
    if check_in_time == 0:
        return print("The check_in_time must be a number.")

    account = Account(params={"token": env["account_token"]})

    # You can remove the comments on line 51 and 57 to use the Tag Filter.
    # filter = {'tags': [{'key': env['tag_key'], 'value': env['tag_value']}]}

    devices = account.devices.listDevice(
        queryObj={
            "page": 1,
            "amount": 1000,
            "fields": ["id", "name", "last_input"],
            # "filter": filter,
        }
    )

    if not devices:
        return print(
            f"No device found with given tags. Key: {env['tag_key']}, Value: {env['tag_value']} "
        )

    print("Checking devices: ", ", ".join(x["name"] for x in devices))

    alert_devices = []
    for device in devices:
        now = datetime.utcnow()

        # Check the difference in minutes.
        diff = (now - device["last_input"]).total_seconds() // 60
        if diff > check_in_time:
            alert_devices.append(device["name"])

    if not alert_devices:
        return print("All devices are okay.")

    print("Sending notifications")
    email_service = Services(params={"token": context.token}).email
    sms_service = Services(params={"token": context.token}).sms

    message = f"Hi!\nYou're receiving this alert because the following devices didn't send data in the last {check_in_time} minutes.\n\nDevices:"
    message += "\n".join(alert_devices)

    if env.get("email_list"):
        # Remove space in the string
        emails = env["email_list"].replace(" ", "")

        email_service.send(
            email={
                "to": emails,
                "subject": "Device Offline Alert",
                "message": message,
            }
        )

    if env.get("sms_list"):
        # Remove space in the string and convert to an Array.
        smsNumbers = env["sms_list"].replace(" ", "").split(",")

        for phone in smsNumbers:
            sms_service.send(
                sms={
                    "to": phone,
                    "message": message,
                }
            )


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Dynamic Notification

notificationdynamicemailsmspush

Send dynamic email, SMS and push notifications based on data conditions

pythondynamic-notification.py
"""
Analysis Example
Sending dynamic notification

Send notifications using analysis. It's include example for Email, SMS and Push Notification to TagoRUN Users.
In order for this example to work, you must create an action by variable and set to run this analysis.
Once the action is triggered with your conditions, the data will be sent to this analysis.

Environment Variables
In order to use this analysis, you must setup the Environment Variable table.
account_token: Your account token. Check bellow how to get this.

Steps to generate an account_token:
1 - Enter the following link: https://admin.tago.io/account/
2 - Select your Profile.
3 - Enter Tokens tab.
4 - Generate a new Token with Expires Never.
5 - Press the Copy Button and place at the Environment Variables tab of this analysis.
"""

from tagoio_sdk import Analysis, Account, Services
from tagoio_sdk.modules.Utils.envToJson import envToJson


def my_analysis(context, scope: list[dict]) -> None:
    if not scope:
        return print("This analysis must be triggered by an action.")

    # Get the environment variables.
    environment_variables = envToJson(context.environment)

    if environment_variables.get("account_token"):
        return print('Missing "account_token" environment variable')
    elif len(environment_variables["account_token"]) != 36:
        return print('Invalid "account_token" in the environment variable')

    # Instance the Account class
    account = Account({"token": environment_variables["account_token"]})

    # Get the device ID from the scope and retrieve device information.
    device_id = scope[0]["device"]
    device_info = account.devices.info(device_id)

    # Get the device name and tags from the device.
    # [TAG KEY]    [TAG VALUE]
    # email        example@tago.io
    # phone        +1XXxxxxxxx
    # user_id      5f495ae55ff03d0028d39fc5
    #
    # This is just a generic example how to get this information. You can get data from a device, search in tags, or any other way of correlation you have.
    # For example, you can get the email directly from the user_id if it was specified:
    # email = await account.run.user_info(user_id_tag["id"])["email"]
    device_name = device_info["name"]
    email_tag = next(
        (tag for tag in device_info["tags"] if tag["key"] == "email"), None
    )
    phone_tag = next(
        (tag for tag in device_info["tags"] if tag["key"] == "phone"), None
    )
    user_id_tag = next(
        (tag for tag in device_info["tags"] if tag["key"] == "user_id"), None
    )

    # Instance the SMS and Email service using the analysis token from the context.
    email_service = Services({"token": context.token}).email
    sms_service = Services({"token": context.token}).sms

    # Send the notifications and output the results to the analysis console.
    if email_tag:
        result = email_service.send(
            {
                "to": email_tag["value"],
                "subject": "Notification alert",
                "message": f"You received a notification for the device: {device_name}. Variable: {scope[0]['variable']}, Value: {scope[0]['value']}",
            }
        )
        print(result)
    else:
        print("Email not found for this device.")

    if phone_tag:
        result = sms_service.send(
            {
                "to": phone_tag["value"],
                "message": f"You received a notification for the device: {device_name}. Variable: {scope[0]['variable']}, Value: {scope[0]['value']}",
            }
        )
        print(result)
    else:
        print("Phone number not found for this device.")

    if user_id_tag:
        result = account.run.notificationCreate(
            user_id_tag["value"],
            {
                "title": "Notification Alert",
                "message": f"You received a notification for the device: {device_name}. Variable: {scope[0]['variable']}, Value: {scope[0]['value']}",
            },
        )
        print(result)
    else:
        print("User ID not found for this device.")


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Email Export

emailexportdataattachmentreporting

Export data and send via email as attachments

pythonemail-export.py
"""
Analysis Example
Email export

Learn how to send an email with data in a .csv file attachment.

This analysis will read the variable fuel_level from your device,
and send the values in a .csv file to an e-mail address

Instructions
To run this analysis you need to add a device token and the e-mail to the environment variables.
To do that, go to your device, then token and copy your token.
Go the the analysis, then environment variables,
type device_token on key, and paste your token on value
click the + button to add a new environment
on key, type email and on value, type the e-mail address
"""

from tagoio_sdk import Analysis, Device, Services
from tagoio_sdk.modules.Utils import envToJson


# The function myAnalysis will run when you execute your analysis
def my_analysis(context, scope: list[dict] = None) -> None:
    # reads the values from the environment and saves it in the variable env_vars
    env_vars = envToJson.envToJson(context.environment)

    if not env_vars.get("device_token"):
        raise ValueError("Missing value: 'device_token' environment variable not found")

    if not env_vars.get("email"):
        raise ValueError("Missing value: 'email' environment variable not found")

    device = Device({"token": env_vars["device_token"]})

    # Get the 5 last records of the variable fuel_level in the device bucket.
    fuel_list = device.getData({"variable": "fuel_level", "qty": 5})

    # Create csv header
    csv = "Fuel Level"

    # For each record in the fuel_list, add the value in the csv text.
    # Use \n to break the line.
    for item in fuel_list:
        csv = f"{csv},\n{item['value']}"

    # Print the csv text to the TagoIO analysis console, as a preview
    print(csv)

    # Start the email service
    email = Services({"token": context.token}).email

    # Send the email.
    service_response = email.send(
        {
            "message": "This is an example of a body message",
            "subject": "Exported File from TagoIO",
            "to": env_vars["email"],
            "attachment": {
                "archive": csv,
                "filename": "exported_file.csv",
            },
        }
    )

    print(service_response)


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Data Operations

datafindfilteroperationssearch

Find and operate on data from devices with various filters

pythonfind.py
"""
Analysis Example
Operate data from devices

Read information from a variable generated by devices,
run a simple calculation in real-time, and create a new variable with the output.

Instructions
To run this analysis you need to add a device token to the environment variables,
To do that, go to your device, then token and copy your token.
Go the the analysis, then environment variables,
type device_token on key, and paste your token on value
"""

from tagoio_sdk import Analysis, Device


def my_analysis(context, scope: list = None) -> str:
    # reads the value of account_token from the environment variable
    device_token = next(
        (item for item in context.environment if item["key"] == "device_token"), None
    )

    if not device_token:
        return print("Missing device_token environment variable")

    device = Device(params={"token": device_token["value"]})

    # create the filter options to get the data from TagoIO
    query_filter = {
        "variable": "temperature",
        "query": "last_item",
    }

    result_array = device.getData(queryParams=query_filter)

    # Check if the array is not empty
    if not result_array or not result_array[0]:
        return print("Empty Array")

    # query:last_item always returns only one value
    value = result_array[0]["value"]
    time = result_array[0]["time"]

    # print to the console at TagoIO
    print(f"The last record of the water_level is {value}. It was inserted at {time}")

    # Multiplies the water_level value by 2 and inserts it in another variable
    obj_to_save = {
        "variable": "temperature_double",
        "value": value * 2,
    }

    result = device.sendData(data=obj_to_save)
    print(result)


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Generate PDF Report

pdfreportgenerationemaildata

Generate PDF reports from device data and send via email

pythongenerate-pdf-report.py
"""
Analysis Example
Generate pdf report and send via email

Instructions
To run this analysis you need to add a email and device_token to the environment variables,
Go the the analysis, then environment variables,
type email on key, and insert your email on value
type device_token on key and insert your device token on value
"""

import base64
from datetime import datetime

from tagoio_sdk import Analysis, Device, Services
from tagoio_sdk.modules.Utils.envToJson import envToJson

DEVICE_VARIABLES = [
    "your_variable"
]  # enter the variable from your device you would like


def html_content_for_pdf(dataVal, dataVar) -> None:
    return f"""
    <head>
        <style>
            body, html {{
                margin: 0;
            }}
            table {{
                width: 100%;
                border-collapse: collapse;
            }}
            td {{
                border: 1px solid black;
                padding: 5px;
                padding-bottom: 25px;
                font-style: italic;
            }}
        </style>
    </head>
    <body>
    <table>
        <tr>
            <td colspan="7">Issue date: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</td>
        </tr>
        <tr>
            <td colspan="4">Start date: 2020-05-20 10:21:32</td>
            <td colspan="3">Stop date: 2020-10-08 22:56:19</td>
        </tr>
        <tr>
            <td colspan="4"> Report of the {dataVar}</td>
            <td colspan="3">Device Kitchen Oven 5</td>
        </tr>
        <tr>
            <td>Counter</td>
            <td>{dataVar}</td>
            <td>Time</td>
            <td>Date</td>
            <td>Temperature 2</td>
            <td>Time</td>
            <td>Date</td>
        </tr>
        <tr>
        <td>2</td>
        <td>{dataVal}</td>
        <td>10:53:20</td>
        <td>2020-06-10</td>
        <td>137</td>
        <td>10:53:20</td>
        <td>2020-06-10</td>
        </tr>
    </table>
    </body>
    </html>
    """


# The function myAnalysis will run when you execute your analysis
def my_analysis(context: any, scope: list = None) -> None:
    # reads the values from the environment and saves it in the variable envVars
    envVars = envToJson(context.environment)

    if not envVars.get("email"):
        raise ValueError("email environment variable not found")
    if not envVars.get("device_token"):
        raise ValueError("device_token environment variable not found")

    device = Device({"token": envVars["device_token"]})

    variables_buckets = device.getData(
        {
            "variables": DEVICE_VARIABLES,
            "start_date": "1 month",
            "qty": 10,
        }
    )

    dataParsed = "variable,value,unit,time"

    for variable in variables_buckets:
        dataParsed = f"{variable.get('variable')},{variable.get('value')},{variable.get('unit')},{variable.get('time')}"

    dataArray = dataParsed.split(",")
    dataVar = dataArray[0]
    dataVal = dataArray[1]

    html = html_content_for_pdf(dataVal, dataVar)

    options = {
        "displayHeaderFooter": True,
        "footerTemplate": '<div class="page-footer" style="width:100%; text-align:center; font-size:12px;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
        "margin": {
            "top": "1.5cm",
            "right": "1.5cm",
            "left": "1.5cm",
            "bottom": "1.5cm",
        },
    }

    base_64 = base64.b64encode(html.encode("utf-8")).decode("utf-8")

    # start the PDF service
    pdfService = Services({"token": context.token}).PDF
    pdf_base64 = pdfService.generate(
        {
            "base64": base_64,
            "options": options,
        }
    )

    # Start the email service
    emailService = Services({"token": context.token}).email

    # Send the email.
    emailService.send(
        {
            "to": envVars["email"],
            "subject": "Exported File from TagoIO",
            "message": "This is an example of a body message",
            "attachment": {
                "archive": pdf_base64.json()["result"],
                "type": "base64",
                "filename": "exportedfile.pdf",
            },
        }
    )

    print("Email sent successfully")


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

HTTP GET Request

httpgetapirequestexternal

Make HTTP GET requests to external APIs and services

pythonhttp-get.py
"""
Analysis Example
Post to HTTP Route

This analysis simple post to an HTTP route. It's a starting example for you to develop more
complex algorithms.
Follow the link of documentation https://api.docs.tago.io/
In this example we get the Account name and print to the console.
"""

import urllib.request

from tagoio_sdk import Analysis


URL_TAGOIO = "https://api.tago.io/info"


def my_analysis(context, scope: list = None) -> dict:
    account_token = next(
        (item for item in context.environment if item["key"] == "account_token"), None
    )

    if not account_token:
        raise ValueError("Missing 'account_token' in the environment variables")

    headers = {"Authorization": account_token["value"]}

    req = urllib.request.Request(URL_TAGOIO, headers=headers, method="GET")

    try:
        with urllib.request.urlopen(req) as response:
            result = response.read().decode("utf-8")
            print(result)
    except Exception as error:
        print(f"{error}")


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis(params={"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

MQTT Push

mqttpushbrokerdashboardcommunication

Push data to MQTT broker from dashboard interactions

pythonmqtt-push.py
"""
Analysis Example
Get Device List

Snippet to push data to MQTT. Follow this pattern within your application
If you want more details about MQTT, search "MQTT" in TagoIO help center.
You can find plenty of documentation about this topic.
TagoIO Team.

How to use?
In order to trigger this analysis you must setup a Dashboard.
Create a Widget "Form" and enter the variable 'push_payload' for the device you want to push with the MQTT.
In User Control, select this Analysis in the Analysis Option.
Save and use the form.
"""

from tagoio_sdk import Analysis, Services


# The function myAnalysis will run when you execute your analysis
def my_analysis(context, scope: list[dict]) -> None:
    if not scope:
        return print("This analysis must be triggered by a dashboard.")

    my_data = [obj for obj in scope if obj["variable"] == "push_payload"]
    if not my_data:
        return print("Couldn't find any variable in the scope.")

    # Create your data object to push to MQTT
    # In this case we're sending a JSON object.
    # You can send anything you want.
    # Example:
    # const myDataObject = 'This is a string';
    my_data_object = {
        "variable": "temperature_celsius",
        "value": (int(my_data[0]["value"]) - 32) * (5 / 9),
        "unit": "C",
    }

    # Create a object with the options you chooses
    options = {
        "retain": False,
        "qos": 0,
    }

    # Publishing to MQTT
    MQTT = Services({"token": context.token}).MQTT
    result = MQTT.publish(
        {
            # bucket: myData.bucket, // for legacy devices
            "bucket": my_data[0]["device"],  # for immutable/mutable devices
            "message": str(my_data_object),
            "topic": "tago/my_topic",
            "options": options,
        }
    )
    print(result)


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis({"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)

Send Notification

notificationemailsmspushmessaging

Send notifications via email, SMS, or push notification to users

pythonsend-notification.py
"""
Analysis Example
Send Notification to Yourself

The main function used by TagoIO to run the script.
It sends a notification to the account owner.

Environment Variables
You must setup the following Environment Variables:
message - Your Message
title - Your Title
"""

from tagoio_sdk import Analysis
from tagoio_sdk import Services
from tagoio_sdk.modules.Account.Notification_Type import NotificationCreate


def send_notification(token_profile: str, object: NotificationCreate) -> None:
    """Send Notification to Yourself

    Args:
                object (NotificationCreate): Notification Object
    """
    notification = Services({"token": token_profile}).Notification
    notification.send(notification=object)


# The function myAnalysis will run when you execute your analysis
def my_analysis(context, scope: list) -> None:
    message = list(
        filter(lambda message: message["key"] == "message", context.environment)
    )
    if not message:
        raise ValueError("Missing value: 'message' not found in environment variables")
    message = message[0].get("value")

    title = list(filter(lambda title: title["key"] == "title", context.environment))
    if not title:
        raise ValueError("Missing value: 'title' not found in environment variables")
    title = title[0]["value"]

    send_notification(
        token_profile=context.token, object={"message": message, "title": title}
    )


# The analysis token in only necessary to run the analysis outside TagoIO
Analysis({"token": "MY-ANALYSIS-TOKEN-HERE"}).init(my_analysis)