Welcome to element-iot-api’s documentation!¶
Installation¶
via https
pip install git+https://github.com/RUBclim/element-iot-api
via ssh
pip install git+ssh://git@github.com/RUBclim/element-iot-api
Quick start¶
To get started interacting with the API, you will need an API key. Do not store the API key in your code and especially do not commit the API key. Provide the API key via environment variables.
Get started by creating an instance of element.ElementApi
.
import os
from element import ElementApi
api = ElementApi(
api_location='https://dew21.element-iot.com/api/v1/',
api_key=os.environ['API_KEY'],
)
If you already know the address of an device, you can get data as a
pandas.DataFrame
as easy as:
df = api.get_readings(
device_name='DEC005304',
start=datetime(2024, 8, 15, 9, 0),
end=datetime(2024, 8, 15, 10, 0),
as_dataframe=True,
)
measured_at |
air_humidity |
air_temperature |
battery_voltage |
device_id |
protocol_version |
---|---|---|---|---|---|
2024-08-15 09:00:43.730454+00:00 |
71.4382 |
21.1841 |
3.073 |
21668 |
2 |
… |
… |
… |
… |
… |
… |
2024-08-15 09:55:42.164904+00:00 |
63.4684 |
22.6661 |
3.073 |
21668 |
2 |
Converting between identifiers¶
There are multiple identifiers per station on the one hand the decentlab_id
(e.g.
21668
) which is the serial number, an integer and the hexadecimal mac address (e.g.
DEC005304
).
decentlab_id → address¶
You can easily get the decentlab_id
from the address using
element.ElementApi.decentlab_id_from_address()
.
api.decentlab_id_from_address('DEC0054A4')
This will return 21668
. The result will be cached, so no request is made, the next
time you call it.
address → decentlab_id¶
Converting from the address to the decentlab_id
is no so easy, since there is no
direct mapping possible in the API. So the first time calling this may be slow.
Afterwards it will also be cached in the element.ElementApi
instance.
This time we need to specify a folder when calling
element.ElementApi.address_from_decentlab_id()
.
api.address_from_decentlab_id(
decentlab_id=21668,
folder='stadt-dortmund-klimasensoren-aktiv-sht35'
)
This will return DEC0054A4
. The result will be cached, so no request is made, the next
time you call it.
listing the folders¶
The Element system is structured and organized based on folders where sensors are stored
in. You can get a list of all available folders by calling
element.ElementApi.get_folders()
:
api.get_folders()
…which will return the raw api request. Or if you’re just interested in the
folder-slugs, which are combined identifiers for each folder, you can use
element.ElementApi.get_folder_slugs()
:
api.get_folder_slugs()
which will return a list of of folder-slugs:
['stadt-dortmund-klimasensoren-aktiv-sht35', ..., 'stadt-dortmund-klimasensoren-aktiv-atm41']
Listing devices¶
Each folder contains devices which you can list using
element.ElementApi.get_devices()
. This will return the raw API response you can
use to extract information.
api.get_devices()
If you are just interested in the device addresses, e.g. to retrieve data from all
devices in a folder, you can use element.ElementApi.get_device_addresses()
.
api.get_device_addresses(folder='stadt-dortmund-klimasensoren-aktiv-sht35')
…which will return a list of strings which correspon to the device addresses of all devices in the folder
['DEC0054A6', 'DEC0054B0', ..., 'DEC0054C6']
Getting detailed device information¶
Based on the address e.g. DEC0054C6
, we can get detailed information from one station
using element.ElementApi.get_device()
.
api.get_device('DEC0054C6')
This returns the raw API response where you can extract data from.
Getting data¶
As readings¶
As described above, you can get data for one station using
element.ElementApi.get_readings()
. as_dataframe
will determine whether to return
the raw API response or already a pandas.DataFrame
.
data = api.get_readings(
device_name='DEC0054C6',
start=datetime(2024, 8, 1, 0, 0),
end=datetime(2024, 8, 10, 0, 0),
as_dataframe=True,
)
Additionally, you may specify the following keyword-arguments:
sort: how to sort the data (by either
measured_at
orinserted_at
)sort_direction: either
asc
ordesc
limit: how many value to fetch per paginated request
max_page: how many pages of pagination to get maximum to avoid infinite pagination
timeout: Tells the server when to timeout the requests. For large data downloads via streaming this should be a larger value, otherwise only parts of the available data are downloaded. Once the timeout is reached, the server terminates the stream with a json-message.
stream: whether or not to stream the data instead of paginated requests. This will allow a faster download of large amounts of data
From packets¶
If the original data was not parsed or incorrectly parsed, you can get the data directly
from the unparsed packets. The raw packets can be retrieved using
element.ElementApi.get_packets()
by either specifying the device_name
or the
folder
(to get data for all stations in this folder). If you want to just get the
measurements, you need to specify packet_type='up'
, for uplink packages.
packets = api.get_packets(
folder='stadt-dortmund-klimasensoren-aktiv-sht35',
packet_type='up',
start=datetime(2024, 8, 1, 0, 0),
end=datetime(2024, 8, 10, 0, 0),
)
This will return the raw package data, where you will need to extract the message from und subsequently parse it.
Parsing packets¶
Code for parsing the packets is provided by decentlab and was vendored into this repo.
This packages provides functions for parsing different sensor types.
BLG¶
Use element.parsers.decode_BLG()
to decode a message from the BLG sensor.
data = decode_BLG(msg=b'0254970003498800830BF7', hex=True)
This will return a dictionary similar to this:
{
'Battery voltage': {
'unit': 'V',
'value': 3.063,
},
'Device ID': 21655,
'Protocol version': 2,
'Temperature': {
'unit': '°C',
'value': 47.728822273125274,
},
'Thermistor resistance': {
'unit': 'Ω',
'value': 36877.08418433659,
},
'Voltage ratio': {
'unit': None,
'value': 0.012840747833251953,
},
}
SHT35¶
Use element.parsers.decode_STH35()
to decode a message from the SHT35 sensor.
data = decode_STH35(msg=b'0254A60003783F596E0C17', hex=True)
ATM41¶
Use element.parsers.decode_ATM41()
to decode a message from the SHT35 sensor.
data = decode_ATM41(
msg=b'02530400038283800080008000803488CD8076815C80CBA708816D817D80197FF680007FDB7FDB0AAE',
hex=True,
)
Public API¶
- class element.ElementApi(api_location, api_key) None [source]¶
Class to interact with the Element API. The instance should be, if possible, passed to functions so the internal cache can be utilized.
- Parameters:
- address_from_decentlab_id(decentlab_id, folder) str [source]¶
Retrieve the address in the hexadecimal format e.g.
DEC0054B0
from the decenlab id e.g.21680
.The issue is, that the decentlab serial number/id is not part of the regular metadata in the IoT system. As far as we can see, you only get this when requesting actual data. Hence this may be really slow since in the worst case we have to go through all available stations. We try our best to cache this as part of the instance.
- decentlab_id_from_address(address, folder=None) int [source]¶
Get the decentlab id in the format of e.g.
21680
from the hexadecimal device address e.g.DEC0054B0
.- Parameters:
address (
str
) – the address of the device in a hexadecimal format as retrieved from the devices’s mac-address e.g.DEC0054B0
folder (
str
|None
) – The folder in the Element IoT system to query for this this can be e.g.'stadt-dortmund-klimasensoren-inaktiv-sht35'
if not specified, searching the cache may be slower (default:None
)
- Return type:
- get_device(address) ApiReturn[Device] [source]¶
Get information for a single device via the hexadecimal address.
- Parameters:
address (
str
) – the address of the device in a hexadecimal format as retrieved from the devices’s mac-address e.g.DEC0054B0
, If only thedecentlab_id
is present, this may be retrieved usingaddress_from_decentlab_id()
.- Return type:
- get_device_addresses(folder) list[str] [source]¶
Get the hexadecimal addresses e.g.
DEC0054B0
from all available devices in the folder(-slug)
- get_devices(folder) ApiReturn[list[Device]] [source]¶
Get all available devices in the current
folder
- get_folder_slugs() list[str] [source]¶
Get all available folder slugs. This can be:
'stadt-dortmund-klimasensoren-inaktiv-sht35'
- get_folders() ApiReturn[list[Folder]] [source]¶
Get the folders from the API as the raw return values. If you just want the slugs (names), use
get_folder_slugs()
.
- get_packets(*, device_name=None, folder=None, packet_type=None, start=None, end=None, limit=100, stream=False, timeout=None, max_pages=None) ApiReturn[list[Packet]] [source]¶
Get the original packets from the API. This is returned as the raw API-return-value. The sorting is fixed to
transceived_at
.- Parameters:
device_name (
str
|None
) – The name of the device as the hexadecimal address e.g.DEC0054B0
. If only thedecentlab_id
is present, this may be retrieved usingaddress_from_decentlab_id()
. This is mutually exclusive withfolder
(default:None
)folder (
str
|None
) – The folder in the Element IoT system to query for this this can be e.g.'stadt-dortmund-klimasensoren-inaktiv-sht35'
. This is mutually exclusive withdevice_name
(default:None
)packet_type (
Literal
['up'
,'down'
] |None
) – Filter for packet_types (eitherup
ordown
) ifNone
all package types are returned (default:None
)start (
datetime
|None
) – The datetime to start getting readings for. IfNone
, all available readings will be retrieved. (default:None
)end (
datetime
|None
) – The datetime to stop getting readings for. IfNone
, all available readings will be retrieved. (default:None
)limit (
int
) – How many values to fetch per API request (must be between 1 and 100). (default:100
)stream (
bool
) – Whether to stream the data or not. This is useful for very large datasets.limit
is ignored when streaming, usestart
andend
to limit the data. (default:False
)timeout (
int
|None
) – The timeout for the request in milliseconds. The server will close the connection after this time. This sometimes needs to be increased for very large datasets. It must be at least 250 ms and at most 180000 ms. (default:None
)max_pages (
int
|None
) – After how many pages of pagination we stop, to avoid infinitely requesting data from the API. (default:None
)
- Return type:
- get_readings(device_name, *, sort='measured_at', sort_direction='asc', start=None, end=None, limit=100, max_pages=None, stream=False, timeout=None, as_dataframe=False) ApiReturn[list[Reading]] | DataFrame [source]¶
Get acutal readings from the API. This may be returned as the raw API-return-value or already converted to a
pandas.DataFrame
.- Parameters:
device_name (
str
) – The name of the device as the hexadecimal address e.g.DEC0054B0
. If only thedecentlab_id
is present, this may be retrieved usingaddress_from_decentlab_id()
.sort (
Literal
['measured_at'
,'inserted_at'
]) – How the values should be sorted, currently this can only bemeasured_at
orinserted_at
. (default:'measured_at'
)sort_direction (
Literal
['asc'
,'desc'
]) – The direction the sorting should be applied. Eitherasc
for ascending ordesc
for descending. (default:'asc'
)start (
datetime
|None
) – The datetime to start getting readings for. IfNone
, all available readings will be retrieved. (default:None
)end (
datetime
|None
) – The datetime to stop getting readings for. IfNone
, all available readings will be retrieved. (default:None
)limit (
int
) – How many values to fetch per API request (must be between 1 and 100). (default:100
)max_pages (
int
|None
) – After how many pages of pagination we stop, to avoid infinitely requesting data from the API. (default:None
)stream (
bool
) – Whether to stream the data or not. This is useful for very large datasets.limit
is ignored when streaming, usestart
andend
to limit the data. (default:False
)timeout (
int
|None
) – The timeout for the request in milliseconds. The server will close the connection after this time. This sometimes needs to be increased for very large datasets. (default:None
)as_dataframe (
bool
) – Determines whether this function returns apandas.DataFrame
or the raw API return (which is the default) (default:False
)
- Return type:
- element.decode_ATM41(msg, hex=False, protocol_version=2) ATM41Measurement [source]¶
Decode the message returned from the Meter ATM41 weather station (ATM41) sensor.
- Parameters:
msg (
bytes
) – byte-string returned e.g.b'02530400038283800080008000803488CD8076815C80CBA708816D817D80197FF680007FDB7FDB0AAE'
hex (
bool
) – whether or not the provided message is in hexadecimals (default:False
)protocol_version (
int
) – The expected version of the protocol. If theprotocol_version
in themsg
does not match this version, an exception will be raised (default:2
)
- Return type:
- element.decode_BLG(msg, hex=False, protocol_version=2) BLGMeasurement [source]¶
Decode the message returned from the blackglobe sensor (BLG).
- Parameters:
msg (
bytes
) – byte-string returned e.g.b'0254970003498800830BF7'
hex (
bool
) – whether or not the provided message is in hexadecimals (default:False
)protocol_version (
int
) – The expected version of the protocol. If theprotocol_version
in themsg
does not match this version, an exception will be raised (default:2
)
- Return type:
- element.decode_STH35(msg, hex=False, protocol_version=2) SHT35Measurement [source]¶
Decode the message returned from the temperature and relative humidity sensor (SHT35).
- Parameters:
msg (
bytes
) – byte-string returned e.g.b'0254A60003783F596E0C17'
hex (
bool
) – whether or not the provided message is in hexadecimals (default:False
)protocol_version (
int
) – The expected version of the protocol. If theprotocol_version
in themsg
does not match this version, an exception will be raised (default:2
)
- Return type: