Skip to content

TempoOK API

The TempoOkDataHandler is a specialized class, a subclass of DataHandler, designed to connect to and consume data from the TempoOk API, a service that provides historical meteorological data.

This class interacts directly with a web service (API) via HTTP requests. It is responsible for authenticating, building requests with the correct parameters, and processing the JSON responses to transform them into pandas DataFrames.

Its main feature is:

  • Fetching Historical Data (Features): Through the feature_values method, the class queries the TempoOk API to obtain time series of historical meteorological variables (such as solar radiation, temperature, and wind speed) for a given asset and period. It handles the mapping of asset names to API-specific IDs (tempook_asset_id), renames the returned variables to a standard format, and adjusts timestamps to the local timezone.

How to Use

1. Handler Initialization

The handler can be initialized in two ways:

This method fetches the credentials (host, API token) from the performance_db database.

from echo_dataimporter import TempoOkDataHandler

# Initialize using a pre-defined data source from the database
handler = TempoOkDataHandler(data_source_name="MyTempoOkAPI")

By direct connection properties

This method allows you to provide a configuration object with the connection details, without relying on the database.

from echo_dataimporter import TempoOkDataHandler
from echo_connhandler import HttpConnProperties, HttpHandler

# Manually define the connection properties
conn_props = HttpConnProperties(
    host="https://api.tempook-example.com",
    payload_kwargs={"api_token": "your_api_token_here"}
)

handler = TempoOkDataHandler(connection_properties=conn_props)

2. Fetching Historical Data

To fetch time-series of meteorological data, use the feature_values method.

import pandas as pd
from datetime import datetime
from echo_datetimerange import DateTimeRange

# 1. Define the search parameters
object_name = "MySolarAsset"
period = DateTimeRange(datetime(2025, 8, 1), datetime(2025, 8, 5))

# 2. Define the features (variables) to be fetched.
# Where 'name_in_data_source' is the name in the TempoOk API and 'name' is the standardized name you desire.
features_to_fetch = pd.DataFrame([
    {"name_in_data_source": "GHI", "name": "ghi_w_m2"},
    {"name_in_data_source": "TEMP", "name": "temperature_c"},
])

# 3. Call the method to fetch the data
historical_data = handler.feature_values(
    object_name=object_name,
    features_df=features_to_fetch,
    period=period,
    resource="solar" # Optional: "solar" or "wind"
)

Required Configuration

Diagram of relationships

For the TempoOkDataHandler to work, the entities in performance_db must be configured to reflect the relationship between the API, the virtual meteorological mast, and the desired variables.

  • The Data Source:

    • A data source instance must be created to store the API credentials.
    • It must have the connection attributes: host (API URL) and api_key (the authentication token).
    • This instance must belong to a specific Data Source Type (e.g., api_tempook).
  • The Object (Virtual Meteorological Mast):

    • Represents the "virtual" measurement mast whose data will be queried.
    • It must be of an Object Type met_mast and use the Object Model virtual_met_mast.
    • It needs to have the tempook_asset_id attribute, which contains the corresponding ID for the plant or reference tower in the TempoOk system.
  • Features and Links:

    • The features define the meteorological variables to be read (e.g., GHI, TEMP) and contain the mapping between the name_in_data_source (name in the API) and the name (standardized name).
    • The crucial link is that the set of features associated with the virtual_met_mast model must also be linked to the same Data Source Type (e.g., api_tempook). This ensures that when using this data source, only the relevant features from TempoOk are considered, even if the virtual_met_mast model is associated with other sources.

Class Definition

TempoOkDataHandler(data_source_name=None, connection_properties=None)

Subclass of DataHandler used to interact with TempoOk API.

This handler is initialized using connection details for the TempoOk API. It can be configured in two ways: by providing connection details directly via a connection_properties object, or by providing adata_source_name for a lookup in the performance_db.

At least one of these two parameters must be provided. If both are given, the connection_properties object will be used.

Parameters:

  • data_source_name

    (str | None, default: None ) –

    The name of the data source as defined in performance_db. It is used to fetch connection details if connection_properties is not provided. Defaults to None.

  • connection_properties

    (HttpConnProperties | None, default: None ) –

    A pre-configured object with connection details. If provided, it overrides any lookup from the database. It should contain: - host (str): The base URL of the TempoOk API. - payload_kwargs (dict, optional): A dictionary of parameters to be sent in the body of every request, typically used for authentication (e.g., {"api_token": "your_token_here"}). Defaults to None.

Source code in echo_dataimporter/tempook_handler.py
@validate_call
def __init__(
    self,
    data_source_name: str | None = None,
    connection_properties: HttpConnProperties | None = None,
) -> None:
    """Initializes the data handler for the TempoOk API.

    This handler is initialized using connection details for the TempoOk API. It can be configured in two ways: by providing connection details directly via a `connection_properties` object, or by providing a`data_source_name` for a lookup in the `performance_db`.

    At least one of these two parameters must be provided. If both are given, the `connection_properties` object will be used.

    Parameters
    ----------
    data_source_name : str | None, optional
        The name of the data source as defined in `performance_db`. It is used to fetch connection details if `connection_properties` is not provided. Defaults to None.
    connection_properties : HttpConnProperties | None, optional
        A pre-configured object with connection details. If provided, it overrides any lookup from the database. It should contain:
        - `host` (str): The base URL of the TempoOk API.
        - `payload_kwargs` (dict, optional): A dictionary of parameters to be sent in the body of every request, typically used for authentication (e.g., `{"api_token": "your_token_here"}`).
        Defaults to None.
    """
    # calling __init__ from base class
    super().__init__()

    if data_source_name is None and connection_properties is None:
        raise ValueError("Either data_source_name or connection_properties must be provided")

    if connection_properties is not None:
        logger.debug("connection_properties is provided, checking its type")
        if not isinstance(connection_properties, HttpConnProperties):
            raise TypeError("connection_properties must be an instance of HttpConnProperties")
        logger.debug("connection_properties is a valid HttpConnProperties instance")
        self.connection_properties = connection_properties
        self.conn_props = connection_properties
        self.conn_handler = HttpHandler(connection_properties)
        # logger de finalização
        logger.debug("TempoOkDataHandler initialized with provided connection_properties provided")
    else:
        if data_source_name is not None:
            ds_dict = self.perfdb.datasources.instances.get(
                data_source_types_names=[data_source_name],
                get_attributes=True,
                output_type="dict",
            )
            logger.debug("Data source types names provided, fetching data source definitions")

        if data_source_name not in ds_dict:
            raise RuntimeError(f"Data source '{data_source_name}' not defined in performance_db")

        ds_dict = ds_dict[data_source_name]

        if connection_properties is None:
            connection_properties = HttpConnProperties(
                host=ds_dict["host_address"],
                payload_kwargs={"api_token": ds_dict.get("api_key", None)},
            )
            logger.debug("Connection properties not provided, using default from performance_db: %s", connection_properties)

        self.conn_props = connection_properties

        self.conn_handler = HttpHandler(connection_properties)

alarm_definitions(object_types=None)

Method not applicable to TempoOK

Source code in echo_dataimporter/tempook_handler.py
def alarm_definitions(self, object_types: list[str] | None = None) -> pd.DataFrame:
    """Method not applicable to TempoOK"""
    raise NotImplementedError("Method not implemented yet")

alarm_history()

Method not applicable to TempoOK

Source code in echo_dataimporter/tempook_handler.py
def alarm_history(
    self,
) -> pd.DataFrame:
    """Method not applicable to TempoOK"""
    raise NotImplementedError("Method not implemented yet")

feature_definitions(**kwargs)

Method not applicable to TempoOK

Source code in echo_dataimporter/tempook_handler.py
def feature_definitions(self, **kwargs) -> pd.DataFrame:
    """Method not applicable to TempoOK"""
    raise NotImplementedError("Method not implemented yet")

feature_values(object_name, features_df, period, resource='solar', **kwargs)

Retrieves time-series data for a given object from the TempoOk API.

This method orchestrates the full data retrieval process. It queries the performance_db to find the object's tempook_asset_id, builds a request payload, and calls the API's /observed/range endpoint.

After receiving the data, it performs significant post-processing, including creating a time-indexed DataFrame, renaming columns to standardized names, and adjusting the timezone of the results.

Parameters:

  • object_name

    (str) –

    Name of the object whose tempook_asset_id will be looked up in the performance_db.

  • features_df

    (DataFrame) –

    DataFrame defining the features to query. Must contain the columns name_in_data_source (the name expected by the API) and name (the desired standardized name).

  • period

    (DateTimeRange) –

    The time range for the data query.

  • resource

    (str, default: 'solar' ) –

    The energy source type for the API query. Accepted values are "solar" or "wind". Defaults to "solar".

  • **kwargs

    (dict, default: {} ) –

    Arbitrary keyword arguments. The following is recognized: - model_id (int, optional): The model ID for the API query. Defaults to 100 if not provided.

Returns:

  • DataFrame

    A pandas DataFrame with the requested feature values. The index is a DatetimeIndex (adjusted to a local timezone, GMT-3) and the columns correspond to the standardized feature names.

Source code in echo_dataimporter/tempook_handler.py
def feature_values(
    self,
    object_name: str,
    features_df: pd.DataFrame,
    period: DateTimeRange,
    resource: str | None = "solar",
    **kwargs,
) -> pd.DataFrame:
    """
    Retrieves time-series data for a given object from the TempoOk API.

    This method orchestrates the full data retrieval process. It queries the `performance_db` to find the object's `tempook_asset_id`, builds a request payload, and calls the API's `/observed/range` endpoint.

    After receiving the data, it performs significant post-processing, including creating a time-indexed DataFrame, renaming columns to standardized names, and adjusting the timezone of the results.

    Parameters
    ----------
    object_name : str
        Name of the object whose `tempook_asset_id` will be looked up in the `performance_db`.
    features_df : pd.DataFrame
        DataFrame defining the features to query. Must contain the columns `name_in_data_source` (the name expected by the API) and `name` (the desired standardized name).
    period : DateTimeRange
        The time range for the data query.
    resource : str, optional
        The energy source type for the API query. Accepted values are "solar" or "wind". Defaults to "solar".
    **kwargs : dict, optional
        Arbitrary keyword arguments. The following is recognized:
        - `model_id` (int, optional): The model ID for the API query.
        Defaults to 100 if not provided.

    Returns
    -------
    pd.DataFrame
        A pandas DataFrame with the requested feature values. The index is a `DatetimeIndex` (adjusted to a local timezone, GMT-3) and the columns correspond to the standardized feature names.
    """
    logger.debug("Starting feature_values method in TempoOkDataHandler")

    if not isinstance(resource, str):
        raise TypeError("resource must be a string")
    logger.debug("resource is a valid string")
    if period is None:
        raise ValueError("period must be provided as a DateTimeRange object")
    if not isinstance(period, DateTimeRange):
        raise TypeError("period must be a DateTimeRange object")
    if period.start is None or period.end is None:
        raise ValueError("period must have valid start and end attributes")
    if period.start > period.end:
        raise ValueError("period start must be before period end")
    logger.debug("period is a valid DateTimeRange object with valid start and end attributes")

    # ainda que eu receba o datatime com horas, eu preciso ficar somente com ano, mes e dia
    period = DateTimeRange(
        datetime(period.start.year, period.start.month, period.start.day),
        datetime(period.end.year, period.end.month, period.end.day),
    )

    start_date = period.start.strftime("%Y%m%d")
    period.end = period.end + relativedelta(days=1)
    end_date = period.end.strftime("%Y%m%d")
    period.end = period.end - relativedelta(days=1)
    logger.debug("start_date: %s, end_date: %s", start_date, end_date)

    if features_df is None:
        raise ValueError("features_df must be provided with the features to be queried")
    if object_name is None:
        raise ValueError("object_name must be provided with the name of the object to be queried")

    tempook_asset_id = self.perfdb.objects.instances.attributes.get(
        object_names=[object_name],
        attribute_names=["tempook_asset_id"],
    )[object_name]["tempook_asset_id"]["attribute_value"]
    logger.debug("tempook_asset_id: %s", tempook_asset_id)

    if tempook_asset_id is None:
        raise ValueError(f"Object '{object_name}' does not have a valid 'tempook_asset_id' attribute")

    payload = {
        "power_source": resource,
        "id": tempook_asset_id,
        "variables": features_df["name_in_data_source"].tolist(),
        "start_date": start_date,
        "end_date": end_date,
        "model_id": kwargs.get("model_id", 100),
    }
    logger.debug("Payload for TempoOk API: %s", payload)

    response = self.conn_handler.post(endpoint="/observed/range", json=payload)
    logger.debug("Response status code: %s", response.status_code)

    if response.status_code != 200:
        raise RuntimeError(f"Failed to fetch data from TempoOk API: {response.status_code} - {response.text}")

    response_data = response.json()

    if len(response_data["times"]) == 0:
        raise ValueError("No data found for the given period and features")

    df = pd.DataFrame(
        {
            "dados_existentes": range(len(response_data["times"])),
        },
    )

    for coluna in response_data:
        coluna_series = pd.Series(response_data[coluna])
        df[coluna] = coluna_series

    df.index = df["times"]
    df = df.drop(columns=["times", "dados_existentes"])
    logger.debug("DataFrame created")

    original_col_name = features_df["name_in_data_source"].tolist()
    new_col_name = features_df["name"].tolist()

    if len(original_col_name) != len(new_col_name):
        raise ValueError("The length of original_col_name and new_col_name must be the same.")

    rename_dict = dict(zip(original_col_name, new_col_name, strict=False))
    df = df[original_col_name]
    df = df.rename(columns=rename_dict)
    df = df[new_col_name]
    logger.debug("DataFrame columns renamed according to features_df")

    df.index = pd.to_datetime(df.index, format="%Y-%m-%d %H:%M:%S")
    df.index = df.index - pd.Timedelta(hours=3)
    df = df.loc[period.start : period.end]
    logger.debug("DataFrame index set to datetime, adjusted for timezone and filtered by period")

    return df

object_types()

Method not applicable to TempoOK

Source code in echo_dataimporter/tempook_handler.py
def object_types(self) -> pd.DataFrame:
    """Method not applicable to TempoOK"""
    raise NotImplementedError("Method not implemented yet")

objects()

Method not applicable to TempoOK

Source code in echo_dataimporter/tempook_handler.py
def objects(self) -> pd.DataFrame:
    """Method not applicable to TempoOK"""
    raise NotImplementedError("Method not implemented yet")