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_valuesmethod, 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:
By data source name (recommended)¶
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¶

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) andapi_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_mastand use the Object Modelvirtual_met_mast. - It needs to have the
tempook_asset_idattribute, which contains the corresponding ID for the plant or reference tower in the TempoOk system.
-
Features and Links:
- The
featuresdefine the meteorological variables to be read (e.g., GHI, TEMP) and contain the mapping between thename_in_data_source(name in the API) and thename(standardized name). - The crucial link is that the set of
featuresassociated with thevirtual_met_mastmodel 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 thevirtual_met_mastmodel is associated with other sources.
- The
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 ifconnection_propertiesis 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_idwill be looked up in theperformance_db. -
(features_df¶DataFrame) –DataFrame defining the features to query. Must contain the columns
name_in_data_source(the name expected by the API) andname(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")