Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions docs/source/Resources/Devices/Devices_Type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -559,3 +559,43 @@ DeviceDataRestore
| **file_address**: str
| File path in TagoIO Files to restore data from (CSV format).


.. _DeviceStatisticsQuery:

DeviceStatisticsQuery
---------------------

Query parameters for device statistics. All fields are optional.

**Attributes:**

| **periodicity**: Literal["hour", "day", "month"]
| Interval between items (default: "hour"). Also sets the default range and the longest range allowed: 1 month for "hour", 3 months for "day", and 12 months for "month".

| **start_date**: Union[str, datetime]
| Start of the range. Send it together with end_date. If either one is missing, the default range for periodicity is used: the last 24 hours for "hour", the last 3 months for "day", and the last 12 months for "month".

| **end_date**: Union[str, datetime]
| End of the range. Must not be before start_date.

| **timezone**: str
| IANA timezone used to read start_date and end_date as local times (default: "UTC"). Send the dates as strings without a UTC offset when you set it.


.. _DeviceStatistic:

DeviceStatistic
---------------

Counters of a device at one point in time.

**Attributes:**

| **time**: datetime
| When the counters were recorded.

| [Optional] **device_input**: int or float
| Data records added to the device since the start of the month. Missing when there was no input this month.

| [Optional] **device_output**: int or float
| Data records returned by the device since the start of the month. Missing when there was no output this month.
39 changes: 39 additions & 0 deletions docs/source/Resources/Devices/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,45 @@ Sends data to a device. Accepts a single data object or an array of data objects
print(result) # Successfully Inserted


==========
statistics
==========

Retrieves the data input and output counters of a device over time.
Requires the Control Tower add-on. Counters are month-to-date totals that reset at the start of each calendar month (UTC).
When the range includes the current time, the first item holds the live counters.

**See:**

https://docs.tago.io/docs/api/device-statistics Device Statistics

https://docs.tago.io/docs/tagoio/addons/control-tower Control Tower

**Parameters:**

| **deviceID**: :ref:`GenericID`
| Device ID

| *Optional* **queryParams**: :ref:`DeviceStatisticsQuery`
| Periodicity, date range, and timezone

**Returns:**

| list[:ref:`DeviceStatistic`]

.. code-block::
:caption: **Example:**

# If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management.
resources = Resources()
statistics = resources.devices.statistics("device-id-123", {
"periodicity": "day",
"start_date": "2026-09-01",
"end_date": "2026-09-30",
})
print(statistics) # [{'time': datetime.datetime(2026, 9, 1, 0, 0), 'device_input': 1250, 'device_output': 8730}, ...]


===========
tokenCreate
===========
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "tagoio-sdk"
version = "5.1.7"
version = "5.1.8"
description = "Official Python SDK for TagoIO"
authors = [{name = "TagoIO Inc.", email = "contact@tago.io"}]
license = {text = "Apache-2.0"}
Expand Down
38 changes: 38 additions & 0 deletions src/tagoio_sdk/modules/Resources/Device_Type.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,3 +649,41 @@ class DeviceDataRestore(TypedDict):
"""
File path in TagoIO Files to restore data from (CSV format).
"""


class DeviceStatisticsQuery(TypedDict, total=False):
periodicity: Literal["hour", "day", "month"]
"""
Interval between items (default: "hour"). Also sets the default range and the longest range allowed:
1 month for "hour", 3 months for "day", and 12 months for "month".
"""
start_date: Union[str, datetime]
"""
Start of the range. Send it together with end_date. If either one is missing, the default range
for periodicity is used: the last 24 hours for "hour", the last 3 months for "day",
and the last 12 months for "month".
"""
end_date: Union[str, datetime]
"""
End of the range. Must not be before start_date.
"""
timezone: str
"""
IANA timezone used to read start_date and end_date as local times (default: "UTC").
Send the dates as strings without a UTC offset when you set it.
"""


class DeviceStatistic(TypedDict, total=False):
time: datetime
"""
When the counters were recorded.
"""
device_input: Union[int, float]
"""
Data records added to the device since the start of the month. Missing when there was no input this month.
"""
device_output: Union[int, float]
"""
Data records returned by the device since the start of the month. Missing when there was no output this month.
"""
39 changes: 39 additions & 0 deletions src/tagoio_sdk/modules/Resources/Devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from tagoio_sdk.modules.Resources.Device_Type import DeviceEmptyParams
from tagoio_sdk.modules.Resources.Device_Type import DeviceListItem
from tagoio_sdk.modules.Resources.Device_Type import DeviceQuery
from tagoio_sdk.modules.Resources.Device_Type import DeviceStatistic
from tagoio_sdk.modules.Resources.Device_Type import DeviceStatisticsQuery
from tagoio_sdk.modules.Resources.Device_Type import DeviceTokenDataList
from tagoio_sdk.modules.Resources.Device_Type import ListDeviceTokenQuery
from tagoio_sdk.modules.Resources.Device_Type import TokenData
Expand Down Expand Up @@ -611,6 +613,43 @@ def amount(self, deviceID: GenericID) -> Union[int, float]:
)
return result

def statistics(
self, deviceID: GenericID, queryParams: Optional[DeviceStatisticsQuery] = None
) -> List[DeviceStatistic]:
"""
@description:
Retrieves the data input and output counters of a device over time.
Requires the Control Tower add-on. Counters are month-to-date totals that reset at the start of each
calendar month (UTC). When the range includes the current time, the first item holds the live counters.

@see:
https://docs.tago.io/docs/api/device-statistics Device Statistics
https://docs.tago.io/docs/tagoio/addons/control-tower Control Tower

@example:
If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management.
```python
resources = Resources()
statistics = resources.devices.statistics("device-id-123", {
"periodicity": "day",
"start_date": "2026-09-01",
"end_date": "2026-09-30",
})
print(statistics) # [{'time': datetime.datetime(2026, 9, 1, 0, 0), 'device_input': 1250, 'device_output': 8730}, ...]
```
"""
result = self.doRequest(
{
"path": f"/device/{deviceID}/statistics",
"method": "GET",
"params": queryParams or {},
}
)

result = dateParserList(result, ["time"])

return result

def getChunk(self, deviceID: GenericID) -> List[DeviceChunkData]:
"""
@description:
Expand Down
57 changes: 57 additions & 0 deletions tests/Resources/test_devices.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os

from datetime import datetime
from typing import Any
from typing import Dict

Expand Down Expand Up @@ -423,6 +424,62 @@ def testGetChunk(requests_mock: Mocker) -> None:
assert result[0]["amount"] == 1000


def testStatistics(requests_mock: Mocker) -> None:
"""Test statistics method of Devices class."""
device_id = "device-id-123"
requests_mock.get(
f"https://api.tago.io/device/{device_id}/statistics",
json={
"status": True,
"result": [
{
"time": "2026-09-01T00:00:00.000Z",
"device_input": 1250,
"device_output": 8730,
},
{"time": "2026-09-02T00:00:00.000Z", "device_input": 2480},
],
},
)

resources = Resources({"token": "your_token_value"})
result = resources.devices.statistics(
device_id,
{
"periodicity": "day",
"start_date": "2026-09-01",
"end_date": "2026-09-30",
"timezone": "America/Sao_Paulo",
},
)

assert requests_mock.last_request.qs == {
"periodicity": ["day"],
"start_date": ["2026-09-01"],
"end_date": ["2026-09-30"],
"timezone": ["america/sao_paulo"],
}
assert result[0]["time"] == datetime(2026, 9, 1)
assert result[0]["device_input"] == 1250
assert result[0]["device_output"] == 8730
assert "device_output" not in result[1]


def testStatisticsWithoutQuery(requests_mock: Mocker) -> None:
"""Test statistics method of Devices class without query params."""
device_id = "device-id-123"
requests_mock.get(
f"https://api.tago.io/device/{device_id}/statistics",
json={"status": True, "result": []},
)

resources = Resources({"token": "your_token_value"})
result = resources.devices.statistics(device_id)

assert requests_mock.last_request.qs == {}
assert result == []


def testDeleteChunk(requests_mock: Mocker) -> None:
"""Test deleteChunk method of Devices class."""
device_id = "device-id-123"
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading