# Welcome to element-iot-api's documentation! ```{toctree} --- maxdepth: 1 --- examples.md ``` ```{toctree} --- maxdepth: 1 hidden: true --- api/schemas.md api/parsers.md api/element.md ``` ## Installation via https ```bash pip install git+https://github.com/RUBclim/element-iot-api ``` via ssh ```bash 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 {class}`element.ElementApi`. ```python 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 {class}`pandas.DataFrame` as easy as: ```python 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 {meth}`element.ElementApi.decentlab_id_from_address`. ```python 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 {class}`element.ElementApi` instance. This time we need to specify a folder when calling {meth}`element.ElementApi.address_from_decentlab_id`. ```python 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 {meth}`element.ElementApi.get_folders`: ```python 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 {meth}`element.ElementApi.get_folder_slugs`: ```python api.get_folder_slugs() ``` which will return a list of of folder-slugs: ```console ['stadt-dortmund-klimasensoren-aktiv-sht35', ..., 'stadt-dortmund-klimasensoren-aktiv-atm41'] ``` ### Listing devices Each folder contains devices which you can list using {meth}`element.ElementApi.get_devices`. This will return the raw API response you can use to extract information. ```python 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 {meth}`element.ElementApi.get_device_addresses`. ```python 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 ```console ['DEC0054A6', 'DEC0054B0', ..., 'DEC0054C6'] ``` ### Getting detailed device information Based on the address e.g. `DEC0054C6`, we can get detailed information from one station using {meth}`element.ElementApi.get_device`. ```python api.get_device('DEC0054C6') ``` This returns the raw API response where you can extract data from. ### Getting data #### As readings As described [above](#quick-start), you can get data for one station using {meth}`element.ElementApi.get_readings`. `as_dataframe` will determine whether to return the raw API response or already a {class}`pandas.DataFrame`. ```python 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 {meth}`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. ```python 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 {func}`element.parsers.decode_BLG` to decode a message from the BLG sensor. ```python data = decode_BLG(msg=b'0254970003498800830BF7', hex=True) ``` This will return a dictionary similar to this: ```python { '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 {func}`element.parsers.decode_STH35` to decode a message from the SHT35 sensor. ```python data = decode_STH35(msg=b'0254A60003783F596E0C17', hex=True) ``` #### ATM41 Use {func}`element.parsers.decode_ATM41` to decode a message from the SHT35 sensor. ```python data = decode_ATM41( msg=b'02530400038283800080008000803488CD8076815C80CBA708816D817D80197FF680007FDB7FDB0AAE', hex=True, ) ``` ## Public API ```{eval-rst} .. automodule:: element :members: ``` ## Indices and tables - {ref}`genindex`