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 or inserted_at)

  • sort_direction: either asc or desc

  • 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:
  • api_location (str) – The location where the Element API is hosted including the version e.g. https://dew21.element-iot.com/api/v1

  • api_key (str) – The API key as provided to you

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.

Parameters:
  • decentlab_id (int) – The decentlab serial nr/id in the format of e.g. 21680

  • folder (str) – The folder in the Element IoT system to query for this this can be e.g. 'stadt-dortmund-klimasensoren-inaktiv-sht35'

Return type:

str

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:

int

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 the decentlab_id is present, this may be retrieved using address_from_decentlab_id().

Return type:

ApiReturn[Device]

get_device_addresses(folder) list[str][source]

Get the hexadecimal addresses e.g. DEC0054B0 from all available devices in the folder(-slug)

Parameters:

folder (str) – The folder(-slug) to get the devices from

Return type:

list[str]

get_devices(folder) ApiReturn[list[Device]][source]

Get all available devices in the current folder

Parameters:

folder (str) – The folder(-slug) to get the devices from

Return type:

ApiReturn[list[Device]]

get_folder_slugs() list[str][source]

Get all available folder slugs. This can be: 'stadt-dortmund-klimasensoren-inaktiv-sht35'

Return type:

list[str]

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().

Return type:

ApiReturn[list[Folder]]

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 the decentlab_id is present, this may be retrieved using address_from_decentlab_id(). This is mutually exclusive with folder (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 with device_name (default: None)

  • packet_type (Literal['up', 'down'] | None) – Filter for packet_types (either up or down) if None all package types are returned (default: None)

  • start (datetime | None) – The datetime to start getting readings for. If None, all available readings will be retrieved. (default: None)

  • end (datetime | None) – The datetime to stop getting readings for. If None, 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, use start and end 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:

ApiReturn[list[Packet]]

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 the decentlab_id is present, this may be retrieved using address_from_decentlab_id().

  • sort (Literal['measured_at', 'inserted_at']) – How the values should be sorted, currently this can only be measured_at or inserted_at. (default: 'measured_at')

  • sort_direction (Literal['asc', 'desc']) – The direction the sorting should be applied. Either asc for ascending or desc for descending. (default: 'asc')

  • start (datetime | None) – The datetime to start getting readings for. If None, all available readings will be retrieved. (default: None)

  • end (datetime | None) – The datetime to stop getting readings for. If None, 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, use start and end 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 a pandas.DataFrame or the raw API return (which is the default) (default: False)

Return type:

ApiReturn[list[Reading]] | DataFrame

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 the protocol_version in the msg does not match this version, an exception will be raised (default: 2)

Return type:

ATM41Measurement

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 the protocol_version in the msg does not match this version, an exception will be raised (default: 2)

Return type:

BLGMeasurement

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 the protocol_version in the msg does not match this version, an exception will be raised (default: 2)

Return type:

SHT35Measurement

Indices and tables