Code examples

A few examples how to use the library to retrieve data from the API

Data from readings for devices in a folder

This examples first retrieves all addresses in a folder using element.ElementApi.get_device_addresses() and then iterates over the address to retrieve readings from each device using element.ElementApi.get_readings(), subsequently combining them to one dataframe. Also corresponding metadata is gathered by calling element.ElementApi.get_device() and added to the pandas.DataFrame.

import os
from datetime import datetime
from typing import Any

import pandas as pd

from element import ElementApi


def _add_metadata(
        df: pd.DataFrame,
        api: ElementApi,
        device_name: str,
) -> pd.DataFrame:
    metadata: dict[str, Any] = {}
    device_info = api.get_device(address=device_name)
    location = device_info['body']['location']
    if location:
        metadata['lon'], metadata['lat'] = location['coordinates']
    else:
        metadata['lon'], metadata['lat'] = float('nan'), float('nan')

    metadata['name'] = device_info['body']['name']
    metadata['bemerkung'] = device_info['body']['fields']['gerateinformation']['bemerkung']  # noqa: E501
    metadata['strasse'] = device_info['body']['fields']['gerateinformation']['strasse']  # noqa: E501
    df_meta = pd.DataFrame(metadata, index=[device_name])
    return pd.merge(
        left=df.reset_index(),
        right=df_meta,
        how='left',
        on='name',
    )


def main() -> int:
    api = ElementApi(
        api_location='https://dew21.element-iot.com/api/v1/',
        api_key=os.environ['API_KEY'],
    )
    START = datetime(2024, 8, 9, 16, 0)
    # SHT35 devices
    print('getting SHT35 data...')
    sht35_addresses = api.get_device_addresses(
        'stadt-dortmund-klimasensoren-aktiv-sht35',
    )
    sht35_df_list = []
    for idx, addr in enumerate(sht35_addresses):
        print(f'{idx + 1}/{len(sht35_addresses)}')
        readings_df = api.get_readings(
            device_name=addr,
            start=START,
            as_dataframe=True,
        )
        readings_df['name'] = addr
        readings_df = _add_metadata(
            df=readings_df,
            api=api,
            device_name=addr,
        ).set_index('measured_at')
        sht35_df_list.append(readings_df)

    df_sht35 = pd.concat(sht35_df_list)
    df_sht35.to_csv('t.csv')
    return 0


if __name__ == '__main__':
    raise SystemExit(main())

Data from packets in a folder

This examples retrieves data from all three types of stations (SHT35, BLG, ATM41) and adds some metadata to the retrieved data an finally saves it to a csv file.

First raw, unparsed data is requested as packets using element.ElementApi.get_packets(). The data is the parsed, using the custom _parse function which uses a decoder e.g. element.parsers.decode_SHT35() to retrieve data from the packet. Finally metadata is added with a custom _add_metadata function by using element.ElementApi.get_device() and the decentlab_id is converted to an address by using element.ElementApi.address_from_decentlab_id().

import os
from collections import defaultdict
from collections.abc import Callable
from datetime import datetime
from typing import Any

import pandas as pd

from element import decode_ATM41
from element import decode_BLG
from element import decode_STH35
from element import ElementApi
from element.element_api import ApiReturn
from element.element_api import Packet
from element.parsers import ATM41Measurement
from element.parsers import BLGMeasurement
from element.parsers import SHT35Measurement

RType = SHT35Measurement | BLGMeasurement | ATM41Measurement


def _parse(
        packets: ApiReturn[list[Packet]],
        decoder: Callable[[bytes, bool, int], RType],
) -> pd.DataFrame:
    data: dict[str, Any] = defaultdict(list)
    for p in packets['body']:
        date = datetime.fromisoformat(p['inserted_at'])
        data['date'].append(date)
        payload = p['payload']
        if payload is not None:
            vals = decoder(payload, True, 2)
            for k, v in vals.items():
                if isinstance(v, dict):
                    data[k].append(v['value'])
                else:
                    data[k].append(v)

    return pd.DataFrame(data).set_index('date').sort_index()


def _add_metadata(
        df: pd.DataFrame,
        api: ElementApi,
        folder: str,
) -> pd.DataFrame:
    df_meta_list = []
    for device_id in df['Device ID'].dropna().unique():
        metadata: dict[str, Any] = {}
        device_info = api.get_device(
            api.address_from_decentlab_id(
                device_id,
                folder=folder,
            ),
        )
        location = device_info['body']['location']
        if location:
            metadata['lon'], metadata['lat'] = location['coordinates']
        else:
            metadata['lon'], metadata['lat'] = float('nan'), float('nan')

        metadata['name'] = device_info['body']['name']
        metadata['bemerkung'] = device_info['body']['fields']['gerateinformation']['bemerkung']  # noqa: E501
        metadata['strasse'] = device_info['body']['fields']['gerateinformation']['strasse']  # noqa: E501
        df_meta_list.append(pd.DataFrame(metadata, index=[device_id]))

    df_meta = pd.concat(df_meta_list)
    df_meta.index.name = 'Device ID'
    return pd.merge(
        left=df.reset_index(),
        right=df_meta,
        how='left',
        on='Device ID',
    )


def main() -> int:
    api = ElementApi(
        api_location='https://dew21.element-iot.com/api/v1/',
        api_key=os.environ['API_KEY'],
    )
    START = datetime(2024, 8, 9, 16, 0)
    print('getting SHT35 data...')
    packets_ta = api.get_packets(
        folder='stadt-dortmund-klimasensoren-aktiv-sht35',
        packet_type='up',
        start=START,
    )
    df_ta = _parse(packets=packets_ta, decoder=decode_STH35)
    df_ta = _add_metadata(
        df=df_ta,
        api=api,
        folder='stadt-dortmund-klimasensoren-aktiv-sht35',
    ).set_index('date')
    df_ta.to_csv('temp_data.csv')

    print('getting BG data...')
    packets_bg = api.get_packets(
        folder='stadt-dortmund-klimasensoren-aktiv-blackglobe',
        packet_type='up',
        start=START,
    )
    df_bg = _parse(packets=packets_bg, decoder=decode_BLG)
    df_bg = _add_metadata(
        df=df_bg,
        api=api,
        folder='stadt-dortmund-klimasensoren-aktiv-blackglobe',
    )
    df_bg.to_csv('bg_data.csv')

    print('getting ATM41 data...')
    packets_atm41 = api.get_packets(
        folder='stadt-dortmund-klimasensoren-aktiv-atm41',
        packet_type='up',
        start=START,
    )
    df_atm41 = _parse(packets=packets_atm41, decoder=decode_ATM41)
    df_atm41 = _add_metadata(
        df=df_atm41,
        api=api,
        folder='stadt-dortmund-klimasensoren-aktiv-atm41',
    )
    df_atm41.to_csv('atm41_data.csv')
    return 0


if __name__ == '__main__':
    raise SystemExit(main())