Indholdsfortegnelse

API Dokumentation

Introduktion

Dette dokument giver en kort introduktion til de API‘er, der er tilgængelige på EnergyDataDK-platformen. Hvis du er ny på platformen, anbefaler vi at starte med Platformguiden og Dataejerguiden for at få et generelt overblik over Energydata.dk og dens funktioner.

Det er muligt at importere- og eksportere data ved brug af API. De forskellige mekanismer er som følgende:
  • Batch import data – Kan anvendes når der er brug for at uploade store mængder data, enten historisk eller ikke-tidskritiske data.
  • Fetch data – For at downloade en eller flere datastrømmer
  • Connect via MQTT – Bliver brugt til at uploade (publish) data til en eller flere datastrømmer eller til at downloade (subscribe to) en eller flere datastrømmer. Adskillige værdier kan bliver offentliggjort samtidig, og MQTT‘s Quality of Service (QoS) er understøttet.
Det kræver en API token, en alfanumerisk kode som fungerer som både brugernavn og kodeord, for at kunne foretage disse handlinger. Det er vigtigt at påpege at du kun behøver læse-rettigheder for at kunne foretage batch download og oprette en token. Det kræves ejerskab eller skrive-rettigheder for at kunne foretage batch upload og MQTT forbindelse. Hvis du støder på fejl i dette dokument, eller har forbedringsforslag er du velkommen til at kontakte os på energydatadk@dtu.dk.

Terminologi

TermMeaning
API
(Application Programming Interface)
A set of rules that allows different software systems to communicate and exchange data with each other.
MQTT
(Message Queuing Telemetry Transport)
Lightweight messaging protocol used to transmit data between devices through a central server called a broker. It follows a publish/subscribe model: devices publish data to specific topics, and other devices subscribe to those topics to receive the messages.
MQTT Preffix A single alphanumeric string configured by data owner. This prefix is placed at the beginning of all MQTT topics associated with specific data set. It acts as a simple identifier that groups topics together and distinguishes them from those of other data sets.
MQTT Sufix (Topic)A unique label for a datastream. The suffix is added after the MQTT prefix and forms the full topic used when publishing or requesting data for that datastream.
TokenA token is a unique string used to authenticate and authorize access to an API or data service. It identifies the requester and ensures that only permitted systems or users can insert or retrieve data.
Deploy TokenA deploy token is a credential that is explicitly permitted to perform certain operations on specific datasets. It is intended for use when deploying devices or other systems in the field. Because a deploy token has only limited, predefined permissions, any compromise of the device or system using it results in minimal exposure.
You can link any of the licenses you have via group memberships to the token.
Personal Access TokenA personal access token is a credential that carries the same rights as the user account that issued it. It is intended for use on the issuing user’s own computer for local development and testing.
Warning: If compromised, a personal access token can be used to access everything available to the issuing user.

API token oprettelse

Du kan oprette og administrere dine API tokens ved at vælge API tokens fra Settings menuet (Fig. 1).
A dropdown menu titled 'Settings' with a 'Manage' section header. The menu includes the options: Groups, Datasets, API Tokens, and Alarms. 'API Tokens' is highlighted in blue text.
Figur 1. Setting: API Tokens

Hvilket tager dig til siden vist nedunder.

"Interface for creating an API token. The top section contains a 'Name' field with 'Onboarding_token' and a 'Token type' dropdown set to 'Deploy token'. Below are checkboxes for permissions: 'Batch import data' and 'Fetch data from datasets via API' are selected. A large table allows selecting licenses, showing columns for Permission, Dataset ID, and Group origin. The first row for 'Project Onboarding' with 'Owner' permission is checked."
Figur 2. API token formular
For at oprette et nyt token, skal du navngive det, vælge et type, og indstille rettigheder. Der er to typer tokens: Deploy token og Personal access token. Definitioner af begge typer er beskrevet i Terminologi sektionen af denne vejledning.

 

Token tilladelser
Tilladelserne som et specifik token kan tildeles er som følgende, flere valg er mulig:
  • Batch import data
  • Fetch data
  • Forbind til MQTT
For at selektere hvilket dataset du vil give tokenet adgang til, kan du bruge søgefeltet i nedre hjørnet til højre Figur 2. Så snart alt er indstillet kan du oprette tokenet ved trykke på “Create” knappen ved bunden af siden. En pop up kommer op hvor API nøglen bliver vist. Vær meget opmærksom på, at nøglen bliver kun vist den ene gang, der vil ikke være muligheder at se det igen senere.
Administrer API-tokens Tokenadministration er tilgængelig på den samme side, som bruges til at oprette nye tokens. Nederst på siden finder du en liste over alle de tokens, du har oprettet (fig. 3).
"The 'Manage API Tokens' section of a dashboard. A text on the left explains that users can delete existing tokens. On the right, a white card displays a list of three tokens: 'Onboarding_token', 'Token_test', and 'Token_batch_dataset1'. Each token has 'View' and 'Delete' text links aligned to the right side of the row."
Figur 3. Administration af API Tokens

Fra denne liste kan du se token detaljer, eller slette dem. Du vil dog ikke kunne se selve API nøglen.

Batch data upload

Hvis du har historisk data kan du uploade det hele ved at gøre brug af batch import API‘et. Du kan tilføje en, eller adskillige datastrømme til et datasæt som du har skrive-rettigheder til, eller som du ejer.

Før du uploader dataene, skal du have oprettet et datasæt på energydata.dk, som skal modtage dataene. Alle trin i oprettelsen af ​​datasættet er forklaret i afsnittet om oprettelse af datasæt i dataejervejledningen.

Det er vigtigt at sikre sig at CSV-filen til batch upload er korrekt formateret. En ukorrekt formateret fil vil blive afvist.

CSV-fil formatering til upload

Filen skal formateres som CSV i henhold til RFC4180. Derudover, hvis filen indeholder ikke-ascii-tegn, skal filen kodes som UTF-8.

Første række

Den første række i CSV-filen skal indeholde overskrifter. Overskriften i en given kolonne identificerer den datastrøm, som kolonnens data skal importeres til. Overskriften kan enten være det emne, der er knyttet til den givne datastrøm, f.eks. mit/emne, eller datastrøms-ID‘et, f.eks. 19323. Den første kolonne i den første række ignoreres. Som dataejer tildeler du emner (topics) til datastrømme under datasæt oprettelsen. Hvis du har fået tildelt ejerrettigheder fra en anden bruger, eller du ikke har ejerrettigheder og du derfor ikke kender emnerne, kan du finde frem til emnet og datastrøm ID på flere måder. Disse metoder er beskrevet under Topic og Datastrøm ID hentning.

Tidsstempel


Tidsstemplerne skal formateres som YYYY-MM-DD[T]HH:mm:ss.SSS[Z], f.eks. 2021-05-01T10:12:44.432Z for 1. maj 2023, 10:12:44.432 UTC. Tidsstemplet skal være i UTC-tidszonen.

Data

Datatypen i en kolonne skal matche datatypen af datastrømmen hvilket kolonnen tilhører. Hvis en datastrøm er en heltal, skal alle værdier af den datastrøm også være heltal.

Hvis et felt forbliver tom afhænger håndteringen af datatypen af den korresponderende datastrøm. Hvis datastrømmen er af streng typen, vil tom streng blive importeret. Hvis datastrømmen er af hel- eller dobbelttal, vil ingen værdi blive importeret.

Eksempel

Nedunder er en eksempel fil. Læg mærke til at den første datastrøm (117217) er en streng, den anden datastrøm (my/topic) er et dobbelttal, og den sidste datastrøm (119221) er et heltal.

;117217;my/topic;119221

2021-03-10T20:24:30.139Z;a_string;23.4121;-10

2021-03-10T20:24:31.144Z;"another string";999888777.121;0

2021-03-10T20:24:32.161Z;a third string;-1.33e-16;45

2021-03-10T20:24:33.186Z;;54.1;11

2021-03-10T20:24:34.201Z;a-fourth-string;;45

Læg mærke til at de tomme felter i række 5 (datastrøm 117217) og række 6 (my/topic). Til datastrøm 117217, bliver en tom streng importeret med tidsstempel 2021-03-10T20:24:33.186Z. Til datastrøm my/topic, vil ingen værdi blive importeret med timesstempel 2021-03-10T20:24:34.201Z.

Trin-for-trin instrukser

EnergyDataDK’s API URL is: https://admin.energydata.dk/api/v1/import

Du kan gøre dette fra din PCs terminal (command prompt), termerne mellem “<>” skal udfyldes med dine egne oplysninger.

Vigtig: når du udfylder oplysningerne nedunder skal hele pladsholder inklusiv < og > parenteser.

Eksempel:

  • Skabelon: ssh <username>@<ip_address>
  • Dit onput: ssh admin@123.45.67.89 (Not ssh <admin>@<123.45.67.89>)

1. Opret upload

 curl -H "Authorization: Bearer <your_token>" -H "Accept: application/json" -X POST https://admin.energydata.dk/api/v1/import --data-urlencode "importname=<import_name>" 

Systemet returnerer en række oplysninger:

{
 "user_id":<your_user_id>,
 "status":"stored",
 "name":"import_name",
 "updated_at":"2025-09-17T08:15:43.000000Z",
 "created_at":"2025-09-17T08:15:43.000000Z",
 "id":29598
}

Herfra skal vi bruge user_id for at komme videre til næste trin, at få fat i upload URL’en.

2. Få upload URL

Ved brug af ID’et, som vi fik fra det oprettede upload, skriver vi følgende i terminalen:

curl -H "Authorization: Bearer <your_token>" -H "Accept: application/json" "https://admin.energydata.dk/api/v1/import/<id>/upload_url" 
Læg mærke til det “id” i URL‘en. Systemet returnerer dit unikke link:
{
"upload_url":"https:\/\/s3.energydata.dk\/import\/inbox\/s123456\/a53f2b5d-0493-44ea-bed7-e48c26cf99ed.csv?X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=edk-s3-root%2F20250917%2Fdefault%2Fs3%2Faws4_request&X-Amz-Date=20250917T082537Z&X-Amz-SignedHeaders=host&X-Amz-Expires=7200&X-Amz-Signature=29744bc062fe06d545250c56b2cc8e19cdf4095b9fd936392fdb7f92ab88f1bc"
}

Til de næste trin skal “upload_url” bruges, dog skal hver “/” erstates med “\/”.

3. Upload CSV fil

Med brug af det modificerede “upload_url” fra den sidte trin, skriv følgende:

curl -H PUT -T "<path_to_file>" -H "Content-Type: application/octet-stream" "<upload_url>"

Systemet returnerer ikke noget, men vi skal stadig validere og indlæse uploaden.

4. Valider import

For at kunne validere importen skal “id” bruges igen:

curl -H "Authorization: Bearer <your_token>" -H "Accept: application/json" -X PUT https://admin.energydata.dk/api/v1/import/<id>/validate
Bemærk “id” i URL‘en. Systemet skal returnere:
{
 "id":29598,"name":"import_name",
 "status":"validating",
 "user_id":407,
 "datastreams":null,
 "validated_lines":0,
 "ingested_lines":0, 
 "min_ts":null,
 "max_ts":null,
 "validated_at":null,
 "ingested_at":null,
 "created_at":"2025-09-17T08:46:40.000000Z",
 "updated_at":"2025-09-17T09:39:48.000000Z"
}

To check that we have followed all steps properly, the “status” should be “validating”.

5. Indlæs import

Den sidste trin er indlæsning af importen:

curl -H "Authorization: Bearer <your_token>" -H "Accept: application/json" -X PUT https://admin.energydata.dk/api/v1/import/<id>/ingest
Bemærk igen “id” i URL‘en. Systemet skal returnere:
{
 "id":29598,
 "name":"import_name",
 "status":"ingesting",
 "user_id":407,
 "datastreams":[1205191,1205192],
 "validated_lines":<antal linjer>,
 "ingested_lines":0,
 "min_ts":"",
 "max_ts":"",
 "validated_at":"2025-09-17T09:39:55.000000Z",
 "ingested_at":null,"created_at":"2025-09-17T08:46:40.000000Z",
 "updated_at":"2025-09-17T10:59:05.000000Z"
}

Det er vigtig at kontrollere “status”, den skal være “ingesting”, ellers kan det betyde, at der er noget galt med din CSV-fil.

Eksempel af Python kode

Her er en eksempel af kode skrevet i Python for at batch uploade data. Når man downloader skal man ændre fil-typen fra “.txt” til “.py”, eller kopiere og indsætte i din foretrukne kode editor. Hvis du vælger at gøre sidstnævnte skal det fordeles over to filer, hver med samme navn som de downloadede filer.

Hovedfil – batch_upload.py Det er koden som skal bruges til at uploade din CSV-fil (<PATH_TO_YOUR_CSV_FILE>) til EnergyDataDK. Du skal indtaste dit API token (<YOUR_TOKEN>) samt IDerne som korresponderer til de datastrømme du vil uploade (<YOUR_DATASTREAMS_IDS>). Hvis du uploader en enkelt datastrøm, er dette angivet som [“datastream_ID”]. Hvis du ønsker at uploade flere datastrømme på én gang, kan du gøre det med [“datastream_ID1”, “datastream_ID2″…]. Det er unødvendigt at ændre noget andet i koden bortset fra disse tre input.
				
					import pytz
import csv
from datetime import datetime
from EnergyDataImport import EnergyDataImport
 

# ###### CONFIGURATION ######
CSV_FILE_PATH = r"<PATH_TO_YOUR_CSV_FILE>"
AUTH_TOKEN = "<YOUR_TOKEN>" 
DATASTREAM_IDS = ["<YOUR_DATASTREAMS_IDS>"]  # Format: ["datastream_ID1", "datastream_ID2"...]
IMPORT_NAME = "default.csv"
# ###########################


# ----- NO NEED TO MODIFY ANYTHING AFTER THIS POINT -----

def run_multi_import():

    # 1. Pass the list of IDs to the properties argument

    with EnergyDataImport(

        upload_filename=IMPORT_NAME,

        properties=DATASTREAM_IDS,  # Class creates a header with all IDs

        energydata_api_token=AUTH_TOKEN,

        overwrite=True

    ) as batch:
 
        with open(CSV_FILE_PATH, mode='r', encoding='utf-8') as f:

            reader = csv.reader(f, delimiter=';')

            next(reader) # Skip metadata line

            for row in reader:

                if not row or len(row) < (len(DATASTREAM_IDS) + 1):

                    continue

                # Parse timestamp (Column 0)

                raw_ts = datetime.strptime(row[0], "%Y-%m-%dT%H:%M:%S.%fZ")

                utc_ts = pytz.utc.localize(raw_ts)

                # 2. Extract values for all streams (Columns 1, 2, and 3)

                # Ensure the list length matches len(DATASTREAM_IDS)

                values = [float(row[i+1]) for i in range(len(row)-1)]

                # 3. Add the row to the batch

                batch.add_values(utc_ts, values)
 
        # Proceed with the standard lifecycle

        batch.upload()

        batch.validate()

        batch.ingest()
 
if __name__ == "__main__":

    run_multi_import()
 
				
			

Hjælpefil – EnergyDataImport.py

Denne kode har til formål at indeholde funktioner af det første fil. Inter behøver rettes.

				
					import requests
import csv
import time
import os
import pytz
import json
from datetime import datetime
from enum import Enum
from typing import List
from pathlib import Path

class EnergyDataImport:
    """
    A utility class to make batch importing to EnergyData.dk easier from Python.
    
    This class handles building a CSV file in the correct format to be used for 
    importing to EnergyData.dk via the batch API. It manages the entire lifecycle:
    creating the proper CSV, uploading, validating, and ingesting the file.
    """

    # The API host for EnergyDataDK import endpoints
    API_HOST = 'https://admin.energydata.dk/api/v1/import'
    API_HEADERS = { 'Accept' : 'application/json' }

    # The format required by EnergyDataDK for timestamps
    TIMESTAMP_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'

    class Status(Enum):
        """Internal state machine to track the progress of the import job."""
        UNINITIALIZED = 0
        OPEN = 1
        CLOSED = 2
        UPLOADING = 3
        STORED = 4
        VALIDATING = 5
        READY = 6
        INGESTING = 7
        DONE = 8
        ABORTED = 9
        ERROR = 10
    
    def __init__(
        self,
        upload_filename: str, 
        properties: List,  
        energydata_api_token: str,
        overwrite: bool = False, 
        tmp_dir: str = '/tmp/energydata_batch_upload', 
        autoclean_tmp_files: bool = True):
        """
        Constructor for class. Create an instance for each batch upload. 
        An instance cannot be reused between multiple imports.

        Args:
            upload_filename (str): Name for the import file generated locally and used on the server.
            properties (List): List of property IDs or Topics to which data will be added.
            energydata_api_token (str): API token from https://portal.energydata.dk/user#accesstokens.
            overwrite (bool): Controls whether to overwrite existing local or remote files.
            tmp_dir (str): Directory for temporary CSV storage before upload.
            autoclean_tmp_files (bool): If True, removes local files once the context is closed.
        """
        self.status = self.Status.UNINITIALIZED
        self.import_id = None
        self.upload_url = None
        self.added_lines = 0
        self.previous_ts = None
        self.upload_filename = upload_filename
        self.local_file_path = Path.joinpath(Path(tmp_dir), Path(upload_filename))
        self.properties = properties
        self.energydata_api_token = energydata_api_token
        self.overwrite = overwrite
        self.tmp_dir = tmp_dir
        self.autoclean_tmp_files = autoclean_tmp_files
        self.api_headers = dict(self.API_HEADERS)
        self.api_headers.update({'Authorization': f'Bearer {energydata_api_token}'})

    def __enter__(self):
        """
        Prepares the environment: creates the temp directory and opens the CSV file for writing.
        """
        self.__assert_status(self.Status.UNINITIALIZED)
        Path(self.tmp_dir).mkdir(parents=True, exist_ok=True)
        
        if not self.overwrite and Path.exists(self.local_file_path):
            raise Exception(f"File '{self.upload_filename}' already exists. Set overwrite=True.")
            
        self.fd = open(self.local_file_path, mode='w', newline='')
        # CSV format: delimiter ';', double quotes for strings, non-numeric quoting
        self.writer = csv.writer(self.fd, quoting=csv.QUOTE_NONNUMERIC, delimiter=';', 
                                quotechar='"', escapechar='\\', doublequote=False)
        # Header row: First column is empty (reserved for timestamp), followed by property list
        self.writer.writerow([''] + self.properties)

        self.__change_status(self.Status.UNINITIALIZED, self.Status.OPEN)
        return self

    def __exit__(self, type, value, traceback):
        """Closes the file descriptor and handles automatic cleanup of local files."""
        if hasattr(self, 'fd') and not self.fd.closed: self.fd.close()
        if self.autoclean_tmp_files and os.path.exists(self.local_file_path): 
            os.remove(self.local_file_path)
        self.status = self.Status.CLOSED

    def add_values(self, time: datetime, values: List):
        """
        Adds a row of data to the import buffer.

        Args:
            time (datetime): Timestamp. Must be timezone-aware.
            values (List): Data values matching the number of properties in the constructor.

        Raises:
            Exception: If time is not timezone-aware or if timestamps are not monotonically increasing.
        """
        self.__assert_status(self.Status.OPEN)
        if time.tzinfo == None:
            raise Exception("No timezone specified for the datetime object.")
        if len(values) != len(self.properties):
            raise Exception(f"Expected {len(self.properties)} values, found {len(values)}.")
        if self.previous_ts is not None and time <= self.previous_ts:
            raise Exception("Added timestamps must be strictly increasing.")
        
        self.previous_ts = time
        # Convert to UTC ISO format before writing
        self.writer.writerow(
            [time.astimezone(pytz.UTC).strftime(self.TIMESTAMP_FORMAT)] + list(values)
        )
        self.added_lines += 1

    def upload(self, print_progress = True):
        """
        Finalizes the CSV and uploads it to the server storage. Once called, state 
        transitions from OPEN to UPLOADING and finally to STORED.
        """
        self.__change_status(self.Status.OPEN, self.Status.UPLOADING)
        self.fd.close()
        
        # 1. Create the import job record
        res = requests.post(url=self.API_HOST, headers=self.api_headers, params={'importname': self.upload_filename})
        res.raise_for_status()
        self.import_id = res.json()['id']

        # 2. Get the secure S3 upload URL
        res = requests.get(url=f'{self.API_HOST}/{self.import_id}/upload_url', headers=self.api_headers)
        res.raise_for_status()
        self.upload_url = res.json()['upload_url'].replace("\\", "") 
        
        if print_progress: print(f'Starting file upload for job id: {self.import_id}')
        
        # 3. Upload the binary data
        with open(self.local_file_path, 'rb') as f:
            res = requests.put(self.upload_url, data=f, headers={"Content-Type": "application/octet-stream"})
        res.raise_for_status()

        self.__change_status(self.Status.UPLOADING, self.Status.STORED)
        if print_progress: print(f'Successfully stored job id {self.import_id}')

    def validate(self, errors_limit = 0, block = True, print_progress = True):
        """
        Triggers server-side validation. State transitions from STORED to VALIDATING 
        and finally to READY.
        """
        self.__change_status(self.Status.STORED, self.Status.VALIDATING)
        self.__api_put_request(block, 'validate', lambda s: self.__validation_progress(s, print_progress), {'errors_limit': errors_limit})

    def ingest(self, block = True, print_progress = True):
        """
        Triggers final ingestion. State transitions from READY to INGESTING 
        and finally to DONE.
        """
        self.__change_status(self.Status.READY, self.Status.INGESTING)
        self.__api_put_request(block, 'ingest', lambda s: self.__ingestion_progress(s, print_progress))

    def __api_put_request(self, block, path, progress_callback, body = None):
        """Helper to send PUT requests and poll for status updates."""
        res = requests.put(url=f'{self.API_HOST}/{self.import_id}/{path}', headers=self.api_headers, json=body)
        res.raise_for_status()
        while block:
            time.sleep(5)
            status = self.__get_status()
            if not progress_callback(status): break

    def __get_status(self):
        """Retrieves the current job metadata from the server."""
        res = requests.get(url=f'{self.API_HOST}/{self.import_id}', headers=self.api_headers)
        res.raise_for_status()
        return res.json()

    def __assert_status(self, expected):
        if expected != self.status:
            raise Exception(f"Expected status '{expected}', but found '{self.status}'")

    def __change_status(self, expected, to):
        self.__assert_status(expected)
        self.status = to
    
    def __validation_progress(self, status, print_progress):
        if print_progress: print(f'Validated {status["validated_lines"]}/{self.added_lines} lines')
        if status['status'] == 'validating': return True
        if status['status'] == 'ready':
            self.__change_status(self.Status.VALIDATING, self.Status.READY)
            return False
        raise Exception(f'Validation failed: {status["status"]}')

    def __ingestion_progress(self, status, print_progress):
        if print_progress: print(f'Ingested {status["ingested_lines"]}/{self.added_lines} lines')
        if status['status'] == 'ingesting': return True
        if status['status'] == 'done':
            self.__change_status(self.Status.INGESTING, self.Status.DONE)
            print("******Data uploaded successfully******")
            return False
        raise Exception(f'Ingestion failed: {status["status"]}')
				
			

Efter du har indsæt dit data, og kørt hovedfilen, batch_upload.py, vil adskillige beskeder blive vist i terminalen, blandt andet det antal linjer som blev indlæst, og hvornår dataet succesfuld blev indlæst.

Vær opmærksom på at indlæsningsprocessen kan tage et par minutter for større datasæt.

Fetch data

Du kan anvende fetch data API‘et for at downloade data fra en aller adskillige datastrømme fra et datasæt. Vær opmærksom at du skal bruge læse-rettigheder for at kunne hente dataet. Ressourcen kan blive brugt til at hente de seneste værdier, eller værdier fra et tidsinterval.

Vær opmærksom på at denne ressource ikke returnerer JSON ved succesfulde forespørgsler. I disse tilfælde bliver respons data sent tilbage til klienten som CSV. Du bør dog stadig specificere “Accept: application/json” i headeren for at sikre at fejl bliver returneret til klienten som JSON.

For at hente data, skal du bruge følgende URL: https://admin.energydata.dk/api/v1/datastreams/values, med API parametre, som er:

  • IDs: liste af datastrøm ID’er til at eksporter, adskilt med kommaer
  • From: til en specifik tidsrum, dato fra hvornår du ønsker eksportere, ISO8601 formateret dato.
  • To: til en specifik tidsrum, dato indtil hvornår du ønsker at eksportere, ISO8601 formateret dato.
  • Latest: Boolean (sandt eller falsk), indikerer om de seneste værdier skal hentes.

For at få adgang til datastrøms-id’erne, hvis du er ejeren, kan du se disse på den respektive datasætside ved at klikke på den datastrøm, du er interesseret i (se samme billede som i bad-uploaden). Hvis du kun har “Skrive”-rettigheder, kan du kun se disse selv ved at downloade en brøkdel af dataene fra webstedet, selvom de er tomme. Fra en sådan fil indeholder headerne de ønskede id’er og emner.

Hent værdier for en udspecificeret tidsrum

For at hente værdier fra et bestemt tidsrum, defineret med “from” og “to” indikatorer. Du kan gøre dette ved at skrive følgende i command prompt af din pc. Læg mærke til at værdierne mellem “ <>” referer til dem du skal udfylde med de oplysninger du vil hente, inklusiv output filen.

Vigtig: når du udfylder oplysningerne nedunder skal hele pladsholder inklusiv < og > parenteser.

Eksempel:

  • Skabelon: ssh <username>@<ip_address>
  • Dit onput: ssh admin@123.45.67.89 (Not ssh <admin>@<123.45.67.89>)

curl -X GET "https://admin.energydata.dk/api/v1/datastreams/values?ids=<list_of_datastreams>&from=<start_of_timespan>&to=<end_of_timespan>" -H "Accept: application/json" -H "Authorization: Bearer <your_token>" -o "<output_file.csv>"
Outputtet vil se nogenlunde sådan ud, men med de specifikke data fra din import. Output-CSV-filen gemmes i den mappe, hvor du arbejder.
% Total % Received % Xferd Average Speed Time Time Time Current

Dload Upload Total Spent Left Speed

100 46 0 46 0 0 271 0 --:--:-- --:--:-- --:--:-- 277

Hent seneste værdier

Denne metode returnerer den sidste række (eller tidsstempel) af den tilgængelige data for den specificerede datastrøm eller datastrømme. Du behøver ikke specificere “from” eller “to” kun datastrøm ID’et.

curl -X GET "https://admin.energydata.dk/api/v1/datastreams/values?ids=<list_of_datastreams>&latest=True" -H "Accept: application/json" -H "Authorization: Bearer <your_token>" -o "<output_file.csv>" 

Outputtet vil nogenlunde se end ud med følgende, men med den specifikke data fra din import. Output CSV-filen vil blive gemt i mappen du arbejder i.

% Total % Received % Xferd Average Speed Time Time Time Current

Dload Upload Total Spent Left Speed

100 32 0 32 0 0 218 0 --:--:-- --:--:-- --:--:-- 225

MQTT

MQTT API‘et gør det muligt at uploade (publish) data til en eller flere datastrømme, eller downloade (subscribe) en eller flere datastrømme i real time. Flere værdier kan blive uploadet samtidig, og MQTT’s Quality of Service (QoS) er supporteret. For at kunne bruge MQTT API’et skal du have skrive-rettigheder til datasættet, eller være dets ejer.

API‘et er bygget ovenpå MQTT protokollen, version 3.1.1. Mægleren er tilgængelig hos mqtts.energydata.dk og er åbn for MQTT forbindelser på port 8883.

Uploade data

Publicering af data giver dataudbydere mulighed for at sende data til lagring i EnergyDataDK. Det er muligt at udgive meddelelser med både enkelt- og flerværdier.

Enkeltværdi beskeder indeholder et tidsstempel og en værdi. Datastrømmen forbundet med beskeden er udledt fra MQTT emnet. Tidsstemplet skal være et heltal i Unix Epoch format, dvs. antallet af millisekunder siden 01-01-1970. Typen af værdien afhænger af hvad er specificeret på den specifikke datastrøm; streng, dobbelttal eller heltal. Et eksempel:

{
 "timestamp": 1521797973469,
 "value": 14.47
}
Multi-værdibeskeder bruges i situationer, hvor du har brug for at udgive flere forskellige, men relaterede værdier. De vil blive gemt med det samme tidsstempel i det samme datasæt, om emner angivet nedenfor. Beskeder med flere værdier publiceres med emnepræfikset som emnet for MQTT-beskeden, for eksempel mit-emne-præfiks.
{
 "timestamp": 1521797973469,
 "value": {
 "mit/topic/suffix1": 14.47,
 "mit/topic/suffix2": 34,
 "mit/topic/suffix3": 4.87,
 "mit/topic/suffix4": 1,
 …. ,
 "mit/topic/suffixn": 300
  }
}
Hvor emnepræfikset er det, der er specifikt for hver datastrøm. Alle meddelelses‑payloads skal serialiseres som JSON. En eksempel af Python kode for at uploade multiværdi beskeder kan findes nedunder, hvor termene imellem “<>” med dine egne oplysninger. Du bliver muligvis nødt til at pip installere paho-mqtt.
				
					import paho.mqtt.client as mqtt
import time
import json

# MQTT Configuration
broker_host = 'mqtts.energydata.dk'
broker_port = 8883
publish_topic = "<your_dataset_mqtt_prefix"
token = "<your_token>" 

client = mqtt.Client()
client.username_pw_set(token)
client.tls_set()

# Connect to the broker
print(f"Connecting to {broker_host}...")
client.connect(broker_host, broker_port, 60)

# Start the network loop in a non-blocking way
client.loop_start()

# Publish a message every second
try:
    for i in range(1000):
        timestamp = int(time.time() * 1000)
        message = json.dumps({
            'timestamp': timestamp,
            'value': {
                "<your_datastream_topic>": <your_values>,
                "<your_datastream_topic>": <your_values>}
                })
        # Publish with QoS1 and wait for acknowledgement
        message_info = client.publish(publish_topic, message, qos=1)
        message_info.wait_for_publish(timeout=10)
        print(f"Published: {message}")
        time.sleep(1)  # Wait 1 second for it to publish
except KeyboardInterrupt:
    print("Stopping publisher...")
finally:
    client.loop_stop()
    client.disconnect()
    print("Disconnected.")

				
			

Når du publicerer til EnergyDataDK ved hjælp af MQTT, bør du bruge Quality of Service (QoS) 1 for at sikre, at meddelelser leveres. Dette er især vigtigt, hvis du publicerer med en høj kapacitet, da overbelastningsbeskyttelsesmekanismer kan kassere dine meddelelser. Hvorfor og hvordan beskrives detaljeret nedenfor.

Når EnergydataDK MQTT-mægleren modtager en publiceringsbesked fra din klient, føjes beskeden til en kø af indgående beskeder. Denne kø er pr. klient og kan indeholde op til 1000 beskeder. Beskeder i køen fjernes fra køen serielt på en FIFO-måde. Når MQTT-brokeren fjernes fra køen, kontrollerer den, at klienten er autoriseret til at udgive beskeder om det givne emne.

Hvis godkendelsen lykkes, videresendes beskeden til lagring i EnergyDataDK og til alle klienter, der abonnerer på det givne emne. Hvis beskeden blev udgivet med QoS 1 eller 2, sender MQTT-brokeren bekræftelsen, når godkendelsen er gennemført.

Hvis godkendelsen mislykkes, afbrydes klientens forbindelse med det samme, og alle beskeder i køen for indgående beskeder kasseres.

Hvis MQTT-brokeren ikke kan følge med klienten, vil køen for indgående beskeder med tiden blive fuld. Når køen er fuld, kasserer MQTT-brokeren alle beskeder, den modtager fra klienten.

Det betyder, at din client skal begrænse sig selv for at undgå at miste beskeder. Du gør dette ved at udgive dine beskeder med QoS 1. Din klient skal derefter vente på at modtage en bekræftelse fra brokeren, før der udgives flere beskeder. Dette kan gøres i Python med paho-mqtt <https://pypi.python.org/pypi/pahomqtt/>_ library med funktionen wait_for_publish.

Abonnere på data

Det giver abonnenter mulighed for at modtage realtidsbeskeder med de data, der er offentliggjort på EnergyDataDK. Et eksempel på Python-kode om, hvordan man abonnerer på en specifik datastrøm for at hente dens livedata, vises nedenfor, hvor ordene i ” igen” står. <>” med dine egne oplysninger. Du bliver muligvis nødt til at pip installere paho-mqtt.

				
					import paho.mqtt.client as mqtt
from datetime import datetime, timezone

# MQTT Configuration
broker_host = 'mqtts.energydata.dk'
broker_port = 8883
subscribe_topic = "<mqtt_prefix/datastream_topic>" 
token = "<your_token>" 

# Callback when the client connects to the broker
def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print(f"Connected to {broker_host} successfully!")
        client.subscribe(subscribe_topic, qos=1)
        print(f"Subscribed to topic: {subscribe_topic}")
    else:
        print(f"Connection failed with code {rc}")

# Callback when a message is received
def on_message(client, userdata, msg):
    payload = msg.payload.decode()
    print(f"[{datetime.now(timezone.utc)}] Received `{payload}` from `{msg.topic}`")

# Create and configure the MQTT client
client = mqtt.Client()
client.username_pw_set(token)
client.tls_set()
client.on_connect = on_connect
client.on_message = on_message

				
			

MQTT præfikset er den som matcher datasættet, mens datastrøm emnet er til en specifik datastrøm.

Emne og Datastrøm ID hentning

Der findes tilfælde hvor du har brug for at skrive ind i datasættet eller læse datastrømme ved brug af API uden at du kender emnerne eller ID’er. Der findes et par metoder for at hente disse værdier.

Datasæt side

Hvis du er ejeren af datasættet kan du se emnet og datastrøm ID på oplysningssiden af den pågældende datastrøm (Fig.4b). Du kan tilgå denne side ved at selektere den datastrøm du du er interesseret i, i dit datasæt oversigt (Fig.4a).
"The Datastreams interface table. The highlighted eye icon next to 'Datastream_name' represents the visibility or 'view details' toggle for this specific stream. The row also includes a numerical value of 9 and a field for comments, with vertical dots on the far left indicating an options menu."
Figur 4a. Datastrøm information access point
"The Datastream details page showing the full configuration for ID 5540878. The interface highlights the unique stream ID and the associated MQTT or system Topic for identification. The Properties table provides a comprehensive breakdown of the stream's context, including its location in Kgs. Lyngby, its CC 4.0 data license, and its classification under the DTU organization."
Figur 4b. Datastrøm oplysninger

Metadata download

En anden mulighed er at download det fulde metadata for den pågældende datasæt ved at trykke på download ikonet, i den øvre højre hjørne af datastrømsiden (Fig. 5). Denne download indeholder metadata for alle datastrømme, samt deres respektive navne og ID’er.

The 'Datastreams' dashboard view. At the top, there are three tabs: 'SEARCH (15)', 'STAGED (0)', and 'EXPORT'. A blue box highlights a vertical three-dot (kebab) menu icon in the top right corner. Below the tabs is a data table with filterable columns for Datastream, Theme tag, Geo tag, Unit, and Data license. Each column header has a text input field for filtering.
Figur 5. Metadata download vindue

CSV Datastrøm Download

Den sidste mulighed er at downloade udvalgte datastrømme via CSV-eksport. Overskrifterne af det resulterende fil vil indeholde de relevante datastrøm ID’er og emner.

Indholdsfortegnelse