Climkit Developer API

Version v1 - October 24 2019

The Climkit Developer API allows registered and authorized developers to get metering and energy data from permitted Climkit installations.

In order to register, developers must obtain the rights to get data from the installation's owner and from Climkit SA.

Request for API access must be adressed to developer@climkit.io.

API credentials are different from Climkit portal personal account login infos.

Access information can only be used by the registered developer. Colleagues and co-workers must apply individually to get their own access rights.

All the http request examples below are shown using the Requests Python package (https://3.python-requests.org).

Climkit API Calls

The Climkit API server is run on https://api.climkit.io/

Authentication

URL : /api/v1/auth Method : POST Auth required : NO Data constraints

{
    "username": "<USESRNAME>",
    "password": "<PASSWORD>"
}

Climkit API uses JSON Web Tokens (https://jwt.io/introduction/ ) to securely transit information.

Once the user is logged in with their credentials (username and password), they receive a token that allows them to access permitted resources.

Authentication example:

import requests
import datetime
from bson import json_util

# Auth request with USERNAME and PASSWORD
r = requests.request('POST', 'https://api.climkit.io/api/v1/auth',
                     data=json_util.dumps({"username": '<USERNAME>', 
                                           "password": '<PASSWORD>'}), 
                     headers={"Content-Type": "application/json"})

# Response example if successful:
r.status_code
200 

# JSON Data
r.json()
{'access_token': '<TOKEN>'}

Installation infos

In order to make a call, the installation ID (CLIMKIT_ID) is needed. It is available on www.app.climkit.io under Configuration > Gateway > "ID Main meter".

URL : /api/v1/installation_infos/<CLIMKIT_ID> Method : GET Auth required : YES with valid Token Data constraints: NO

This simple method returns the installation's basic infos:

# Request
r = requests.request('GET', 'https://api.climkit.io/api/v1/installation_infos/<CLIMKIT_ID>', 
                     headers={"Content-Type": "application/json", "Authorization": "Bearer " + "<TOKEN>"})

r.status_code
200

# JSON response
r.json()
{'name': 'Installation XYZ',
 'postal_code': 1897,
 'address': 'Rue du Bourg',
 'street_number': '1',
 'city': 'Le Bouveret',
 'latitude': 47,
 'longitude': 7,
 'timezone': 'Europe/Zurich',
 'climkit_ref': 'C-XXXXX'}

Meter Raw Data

URL : /api/v1/energy_meters/<CLIMKIT_ID> Method : POST Auth required : Valid Token Headers:

{
    "Content-Type": "application/json", 
    "Authorization": "Bearer " + <TOKEN>"
}

Data constraints:

{
    "start": "<{"$date": TIMESTAMP}>",
    "end": "<{"$date": TIMESTAMP}>",
    "prim_ad": "<PRIMARY_ADDRESS INT>",
    "energy_type": "Energy type STR"

}

Details

Key Definition
start Timestamp at the last time the meter was read
end Timestamp at the previous time the meter was read
prim_ad Primary address
energy_type Allowed: [None, 'electricity', 'hot_water', 'cold_water', 'heat'] ; None=='electricity'

Climkit meters are read every 15 minutes. The raw data contains the data that was read directly from the meter such voltage, ampere, imported and exported energy etc.

Each meter has a single and unique primary address (integer from 1 to 255).

The tab Meters on the installation page on the Climkit portal indicates which meters are used.

Consumer meters usually have a primary address ranging from 11 to 99, production meters from 101 to 110 and the grid introduction point meter with the address 10. But exceptions are possible.

Trying to get a meter that doesn't exist with the installation returns a 400 error.

Example to get raw data from meter with primary address 12 between 2019-10-22 22:00 and 2019-10-23 16:00:

# Request
r = requests.request('POST', 'https://api.climkit.io/api/v1/energy_meters/<CLIMKIT_ID>', 
                     data=json_util.dumps({"start": datetime.datetime(2019,10,22,0,0), 
                                           "end": datetime.datetime(2019,10,23,16,0), 
                                           "prim_ad": 12,
                                           "energy_type": "electricity"}), 
                     headers={"Content-Type": "application/json", "Authorization": "Bearer " + "<TOKEN>"})

r.status_code
200 

# Response
r.json()
[{
  'connect': True,
  'prim_ad': 10,
  'L3_v': 239.17,
  'en_ex': 4108.73,
  'L1_a': 0.92,
  'en_im': 3078.35,
  'L1_w': 103.06,
  'L2_v': 239.03,
  'L2_a': 0.93,
  'L1_v': 239.4,
  'ser_nb': 0.0,
  'L3_w': 140.48,
  'L3_a': 1.39,
  'pass': 4517.0,
  'L2_w': 34.81,
  'period_end': {'$date': 1570666504},
  'period_start': {'$date': 1570665604}
},
...
]

Details

Key Definition
connect Boolean: Meter was read TRUE or FALSE
en_ex Energy exported (kWh)
en_im Energy imported (kWh)
L1 Phase number 1
w Instant power (W)
a Ampere (I)
v Volt (V)
pass Meter's passcode
period_end Timestamp at the last time the meter was read
period_start Timestamp at the previous time the meter was read
prim_ad Primary address

Energy Resampled Data

URL : /api/v1/energy_data/<CLIMKIT_ID> Method : POST Auth required : Valid token Headers:

{
    "Content-Type": "application/json", 
    "Authorization": "Bearer " + <TOKEN>"
}

Data constraints:

{
    "start": "<{"$date": TIMESTAMP}>",
    "end": "<{"$date": TIMESTAMP}>",
    "prim_ad": "<PRIMARY_ADDRESS INT>",
    "energy_type": "Energy type STR"
}

Details

Key Definition
start Timestamp at the last time the meter was read
end Timestamp at the previous time the meter was read
prim_ad Primary address [1 to 255] ; 0 returns all the meters of the selected energy type
energy_type Allowed: [None, 'electricity', 'hot_water', 'cold_water', 'heat'] ; None=='electricity'

Climkit processes raw meter data into usable energy timeseries.

Processed data is only valid for consumer meters.

Example to get consumed energy for meter with primary address 12:

# Request
r = requests.request('POST', 'https://api.climkit.io/api/v1/energy_data/<CLIMKIT_ID>', 
                     data=json_util.dumps({"start": datetime.datetime(2019,10,22,0,0), 
                                           "end": datetime.datetime(2019,10,23,16,0), 
                                           "prim_ad": 12}), 
                     headers={"Content-Type": "application/json", "Authorization": "Bearer " + "<TOKEN>"})

Using prim_ad=0 returns all the processed data for the installation such as consumer meters, solar production, surplus etc. See details below.

Example to get all the data:

# Request with prim ad 0 to get all the installation's data
r = requests.request('POST', 'https://api.climkit.io/api/v1/energy_data/<CLIMKIT_ID>', 
                     data=json_util.dumps({"start": datetime.datetime(2019,10,22,0,0), 
                                           "end": datetime.datetime(2019,10,23,16,0), 
                                           "prim_ad": 0}), 
                     headers={"Content-Type": "application/json", "Authorization": "Bearer " + "<TOKEN>"})

r.status_code
200 

# Response
r.json()

[{
  'prod': 0.010000000002605702,
  'conso_total': 1.3999999999990678,
  'to_grid': 0.0,
  'from_grid': 1.4100000000016735,
  'clean': 0.010000000002605702,
  '14': 0.01999999999998181,
  'grid_14': 0.020142857142876145,
  'clean_14': 0.00014285714289433238,
  'clean_17': 0.00021428571434149856,
  'clean_15': 7.142857144716619e-05,
  'grid_17': 0.030214285714314212,
  '13': 0.05000000000006821,
  '16': 0.03999999999996362,
  'grid_16': 0.04028571428575229,
  'grid_15': 0.010071428571438073,
  'grid_12': 0.2920714285719331,
  '15': 0.009999999999990905,
  'clean_11': 0.006857142858927954,
  'grid_11': 0.9668571428580548,
  'clean_12': 0.0020714285719694433,
  'clean_16': 0.00028571428578866477,
  '11': 0.9599999999991269,
  'clean_13': 0.00035714285723664296,
  '12': 0.2899999999999636,
  '17': 0.029999999999972715,
  'grid_13': 0.05035714285730485,
  'grid_low_or_high': 0,
  'to_grid_gain': 0.0,
  'period_end': {'$date': 1571896800000}
},
...
]

Details

All energy values are in kWh.

Key Definition
prod Total solar PV production
conso_total Total energy consumption in the building
to_grid Surplus solar energy pushed to the grid
from_grid Energy pulled (consumed) from the grid
clean Total solar energy consumed in the building
14 Total energy consumed from meter with primary address 14
grid_14 Consumed grid energy from meter 14
clean_14 Consumed solar energy from meter 14
period_end Timestamp at the end of the 15-minute period

Production and clean values a calculated from the consumed energy of the consumer meters, to_grid and from_grid values. This may result in small negative values which is totally normal.

If the installation is disconnected for a while, Climkit resamples the data over the disconnected period.

Warning: Resampling methods may changes energy data until all the meters are finally read and the installation connected to the internet.

Process energy and raw meter data in Python

The Python Pandas package https://pandas.pydata.org/ is a great way to process the timeseries data.

The module bson.json_util installed with the pymongo package https://api.mongodb.com/python/current/ is the best way to encode and decode json data, especially to render datetime objects from timestamps.

Example to export data to a CSV file with Pandas:

import pandas as pd
from bson import json_util

# Request with prim ad 0 to get all the installation's data
r = requests.request('POST', 'https://api.climkit.io/api/v1/energy_data/<CLIMKIT_ID>', 
                     data=json_util.dumps({"start": datetime.datetime(2019,10,22,0,0), 
                                           "end": datetime.datetime(2019,10,23,16,0), 
                                           "prim_ad": 0}), 
                     headers={"Content-Type": "application/json", "Authorization": "Bearer " + "<TOKEN>"})

r.status_code
200 

json_data = r.json()

# Deserializing the json data and converting the timestamps to datetime objects with json_util
data = json_util.loads(json_util.dumps(json_data, json_options=json_util.JSONOptions(tz_aware=False)))

# Creating a pandas dataframe
df = pd.DataFrame(data)

# Creating the index from the period_end column
df.index = df['period_end']

# Exporting to CSV file
df.to_csv('../filepath/data.csv')

IO Wp Modules

WP Modules are I/O modules that can be connected to the gateway for automation purposes.

Each module has a set a of INPUTS and/or OUTPUTS that can be edited with the API methods below.

INPUTS are sensor values that are collected from external devices.

OUTPUTS are parameter values that can be set in order to control external devices.

The Climkit gateway checks for the latest changes of the WP Modules configuration every 60 seconds before uploading the latest status.

Existing WP Modules:

Modules Functions Type Data
WP3066 8 DS18B20 temperature sensors inputs INPUT Degree Celsius
WP3076 1 Analog Output (AO) DC 0-20mA / 4-20mA OUTPUT Analog mA
WP3084 8 Analog Inputs (AI) DC 0-10V INPUT Analog V
WP8025 8 Normally open relays (2A/250V AC or 2A/30V DC) OUTPUT Digital
WP9038_AI 6 AI (DC 0-20mA / 4-20mA) INPUT Analog mA
WP9038_DI 4 Digital Inputs (DI) INPUT Digital
WP9038_DO 4 Digital Outputs (DO) OUTPUT Digital

WP Modules Config

The POST method below changes the config of the digital and analog outputs. The GET method below returns the current config the WP Modules.

URL : /api/v1/wp_modules_config/<CLIMKIT_ID> Method : GET or POST Auth required : Valid token Headers:

{
    "Content-Type": "application/json", 
    "Authorization": "Bearer " + <TOKEN>"
}

Data constraints for POST method with modules of type OUTPUT:

{
    "name": "<OUTPUT name> STR",
    "model": "<Module Model> STR",
    "active": "<OUTPUT Status> BOOL",
    "prim_ad": "<PRIMARY_ADDRESS INT>",
    "no": "<OUTPUT number> INT",
    "value": "<OUTPUT value> BOOL for Digital OUTPUT or FLOAT Analog OUTPUT"
}

Data constraints for POST method with modules of type INPUT:

{
    "name": "<INPUT name> STR",
    "model": "<Module Model> STR",
    "active": "<INPUT Status> BOOL",
    "prim_ad": "<PRIMARY_ADDRESS INT>",
    "no": "<INPUT number> INT",
}

Details

Key Definition Data Type
name Choose a name for the input or output. Ex: Outdoor temperature or Compressor controller STR max length 30
model WP modules model ['wp9038_DI', 'wp9038_DO', 'wp9038_AI', 'wp8025', 'wp3076', 'wp3066', 'wp3084']
prim_ad Primary address ; 0 returns all the meters of the selected energy type INT [0 to 255]
active Activate the input or the output BOOL
no Output number (Depends of the WP module) INT
value Boolean for digital outputs and float for analog outputs (Depends of the module) BOOL or FLOAT

Only the INPUT and OUTPUT set to active=TRUE are updated by the gateway.

Update WP Module I/O config example

Example to set True on digital output no 1 and to name it "Boiler" on module WP9038_DO with primary address 3:

r = requests.request('POST', 'https://api.climkit.io/api/v1/wp_modules_config/<CLIMKIT_ID>', 
data=json_util.dumps({"name":"Boiler", "model": "wp9038", "active": True, "value": True, "prim_ad": 3, "no": 1}), headers={"Content-Type": "application/json", "Authorization": "Bearer " + "<TOKEN>"})

r.json()
{'msg': 'Requested WP module updated'}

Example to rename temperature sensor input no 5 to '2nd floor' and set it to active on module WP3066 with primary address 2:

r = requests.request('POST', 'https://api.climkit.io/api/v1/wp_modules_config/<CLIMKIT_ID>', 
data=json_util.dumps({"name":"2nd floor", "model": "wp3066", "active": True, "prim_ad": 2, "no": 5}), 
headers={"Content-Type": "application/json", "Authorization": "Bearer " + "<TOKEN>"})

r.json()
{'msg': 'Requested WP module updated'}

GET WP Modules Config example

r = requests.request('POST', 'https://api.climkit.io/api/v1/wp_modules_config/<CLIMKIT_ID>', 
headers={"Content-Type": "application/json", "Authorization": "Bearer " + "<TOKEN>"})

WP Modules current status

The GET method below returns the currents status of the WP Modules inputs and outputs.

It is used to GET the latest values of all the active inputs such as the temperature sensors or mA analog inputs.

Only the inputs and outputs set to "active" are updated.

It also allows you to check if the current config has been uploaded properly to the gateway. So it may take a little bit to see the status changed if you have just updated the configuration.

Remember the status is updated from the gateway every 60 seconds.

URL : /api/v1/wp_modules_status/<CLIMKIT_ID> Method : GET Auth required : Valid token Headers:

{
    "Content-Type": "application/json", 
    "Authorization": "Bearer " + <TOKEN>"
}

GET WP Modules Status example

r = requests.request('POST', 'https://api.climkit.io/api/v1/wp_modules_status/<CLIMKIT_ID>', 
headers={"Content-Type": "application/json", "Authorization": "Bearer " + "<TOKEN>"})

r.json()

{'last_updated': {'$date': 1573058883890},
 'wp_modules': [{'prim_ad': 2,
   'model': 'wp3066',
   'baudrate': 9600,
   'temp_sensors': [{'active': True, 'name': 'Outdoors', 'no': 1, 'value': 21.7},
    {'active': True, 'name': 'Indoors', 'no': 2, 'value': 21.8},
    {'active': False, 'name': '', 'no': 3, 'value': '-'},
    {'active': False, 'name': '', 'no': 4, 'value': '-'},
    {'active': False, 'name': '', 'no': 5, 'value': '-'},
    {'active': False, 'name': '', 'no': 6, 'value': '-'},
    {'active': False, 'name': '', 'no': 7, 'value': '-'},
    {'active': False, 'name': '', 'no': 8, 'value': '-'}],
   'protocol': 'modbus'}]}

# Convert last_updated timestamp
datetime.datetime.fromtimestamp(1573058883890 / 1000)

datetime.datetime(2019, 11, 6, 17, 48, 3, 890000)

Thank you for using the Climkit Developer API ! Happy coding :)

-- Climkit SA - Rue Jean-Jacques Rousseau 5 - 1800 Vevey - Switzerland - www.climkit.io - © 2016-2020