Submitting measures
Tips
A measure is always associated to a virtual object. Before submitting measures, ensure that the object is enabled and that a valid token is associated to it.
Submitting measures is done either through an HTTP POST request or through a MQTT publish packet to the output API. The measure is encoded in JSON format in the body of the request or in the publish packet payload. In this page, we show examples in different languages in how to do so.
Input API endpoints
The input API has two endpoints to submit measures:
POST /input/measures
: submit one measure as a JSON objectPOST /input/measures/bulk
: submit multiple measures using a JSON array.
Both endpoints support the following query parameters:
- ack: boolean, default: false
Whether or not to wait for the measure to be submitted to Kafka. - simulate: boolean, default: false
If set to true, the measure will be validated but won't be forwared to the pipeline. This is perfect for testing.
Both endpoints return the input as they have been interpreted by the API. Most of the time, everything except the timestamp (milliseconds added if missing) and the value (converted to string) should be the same.
In case of error, a JSON object with details is returned, for example:
{"exception":"BadTokenException","details":"the pair <objectId, token> is invalid"}
Measure format
Measures are JSON object.
Properties:
- objectId: required (integer - minimum: 0)
The unique id of the object to which this measure refers. - token: required (string - minLength: 32 - maxLength: 32)
A valid token for the object used for security. You can view/generate a token using the output-api/objects/{id}/tokens
endpoint or via the admin console. - timestamp: required (string)
Datetime in UTC following the formatyyyy-MM-dd'T'hh:mm:ss.SSS
. The granularity of a [saved] measure timestamp is milliseconds. - value: required (any)
The actual value of the measure. It's type (float, int, ...) should match the unit of the virtual object. If the API fails to parse the value according to the unit, the measure might be discarded later in the pipeline. - comment: (string - maxLength: 1024)
An optional comment associated with this measure.
Example
Query:
{ "objectId": 1, "token": "a087dd4e58a3f12523c09ab18d5507de", "timestamp": "2017-02-27T09:05:09", "value": 10 }
Response:
{ "objectId": 1, "token": "a087dd4e58a3f12523c09ab18d5507de", "timestamp": "2017-02-27T09:05:09.000", "value": "10" }
MQTT Endpoint
The MQTT endpoint provides an alternative protocol to submit measures on the bbdata platform. As of now, it is only a way to submit measures which will then be available using the output api, the subscription to topics in not supported yet.
The MQTT endpoint is available using the url mqtt.daplab.ch
and port 8883
. In order to use the endpoint, it is necessary to provide to following information regarding the MQTT protocol:
Topic
: The measure can be sent on any MQTT topic (not starting with $), this does not have any influance on the processing or resulting measure in the ouput api.User and password
: As of now, a default MQTT user is provided, with usernamebbdata
and passwordbbdata
. The identity verification is done using the token sent in the measure.Client identifier
: The MQTT client identifier has to be prefixed withbbdata_
, the connection will otherwise be refused.Quality of service
: All three levels of QoS are supported. QoS 0, 1 and 2 guarantee, respectively, at most once, at least once and exactly-once delivery semantics.
It is to be noted that it is necessary to use a TLS connection to connect to the MQTT endpoint, providing the appropriate certificate.
Measure format using MQTT endpoint
The measure format to be used with the MQTT endpoint is almost identical to the format described previously, with two differences:
- The comment field is mandatory, which can be an empty string
- An additional owner field set to null is required.
Example
publish payload:
{ "objectId": 1, "token": "a087dd4e58a3f12523c09ab18d5507de", "timestamp": "2017-02-27T09:05:09.000", "value": "10", "comment": "", "owner": null }
Submission code examples
Mosquitto
Mosquitto_pub is a cli tool which can be used to send mqtt publish packets.
mosquitto_pub \ -h mqtt.daplab.ch \ -p 8883 \ -t bbdata_topic \ -I bbdata_ \ -u bbdata -P bbdata \ -l -d \ --cafile daplabch.pem \
This command will read messages from the standard input, with one message per line, so simply type the measure in the json format (updating the values accordingly) to send it.
It is necessary to provide the pem-encoded ssl certificate in order to be able to connect to the endpoint.
Paho-mqtt
Paho-mqtt is a library which provides a simple MQTT client interface that can be used to connect to a broker and subscribe to topics or publish message.
import paho.mqtt.client as mqtt import json from datetime import datetime def build_measure(objectId, token, value, comment): return json.dumps({ 'objectId': objectId, 'token': token, 'timestamp': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3], 'value': value, 'comment': comment, 'owner': None }) def connect_handler(client, userdata, flag, rc): if rc == 0: print("Sending publish measure...") client.publish( topic="bbdata_mqtttopic", qos=2, payload=build_measure( 1, "a087dd4e58a3f12523c09ab18d5507de", "10", "" ) ) else: print("Error connecting to the mqtt broker...") def publish_handler(client, userdata, mid): print("Message sent...") client.disconnect() def disconnect_handler(client, userdata, rc): if rc != 0: print("Error while disconnecting") else: print("Disconnected") # Instanciate the mqtt client with an appropriate client id mqttc = mqtt.Client(client_id="bbdata_cid") #Setup the tls connection using the provided certificate. #A call to tls_set without parameters should work in most systems. #Use the ca_certs parameter if it is not the case mqttc.tls_set(ca_certs="daplabch.pem") #Provide the necessary mqtt user and password mqttc.username_pw_set(username="bbdata", password="bbdata") #Set handler functions mqttc.on_connect = connect_handler mqttc.on_publish = publish_handler mqttc.on_disconnect = disconnect_handler #Connect to the broker and wait for a disconnect before exiting print("Connecting to the broker...") mqttc.connect(host="mqtt.daplab.ch", port=8883, bind_address="0.0.0.0") mqttc.loop_forever()
Curl
Curl is a powerful command line tool, perfect for testing.
One line example:
curl -H 'Content-Type: application/json' -X POST -d '{ "objectId": 1, "token": "a087dd4e58a3f12523c09ab18d5507de", "timestamp": "2017-02-27T09:05:09", "value": 10 }' https://bbdata.daplab.ch/input/measures
The JSON body can also be read from a file. For example, say the JSON measure is in the file measure.json
in the current directory:
curl -H 'Content-Type: application/json' -X POST -d @measure.json https://bbdata.daplab.ch/input/measures
For testing, you might want to generate the timestamp dynamically. One way to go is to define the following function either in the current shell session or in your .bashrc
(so it is always available):
now_ms(){ # print UTC time in ISO format with millis python -c "from datetime import datetime; print datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]" }
You can now use it like this:
bbcurl -X POST -d '{ "objectId": 1, "timestamp": "'$(now_ms)'", "value": "2", "token": "9087dd4e58a3f12523c09ab18d5507de" }' https://bbdata.daplab.ch/input/measures
Python
A good library for HTTP in python is requests. Here is a full working example:
import requests from datetime import datetime BASE_URL = "https://bbdata.daplab.ch" DT_FORMAT = "%Y-%m-%dT%H:%M:%S" # to format time # virtual object information token = "9087dd4e58a3f12523c09ab18d5507de" objectId = 1 # create the measure measure = { "objectId": objectId, "timestamp": datetime.utcnow().strftime(DT_FORMAT), "value": 3.14, "token": token } # send the measure using requests and print the response r = requests.post(BASE_URL + '/input/measures',json = measure) print("status: %d, body: %s" % (r.status_code, r.text))
Java
There are many ways to make HTTP requests using Java. For this example, we used Apache HttpClient (maven). To deal with JSON, we recommend GSON, a powerful and easy-to-use library (maven).
Tips
If you are gonna use dates extensively, we strongly recommend the jodaTime library.
Here is a simple example:
public class HttpExample { static final String BASE_URL = "https://bbdata.daplab.ch"; static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); // to format dates static final String TOKEN = "9087dd4e58a3f12523c09ab18d5507de"; static final Integer OBJECT_ID = 1; public static void main(String[] args) ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); // create the measure Map<String, Object> measure = new TreeMap<>(); measure.put("objectId", OBJECT_ID); measure.put("token", TOKEN); measure.put("value", 3.14); measure.put("timestamp", DTF.format(now)); // encode the measure as JSON Gson gson = new GsonBuilder().create(); String body = gson.toJson(measure); System.out.println("body: " + body); try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { // do the POST HttpPost request = new HttpPost(BASE_URL + "/input/measures"); StringEntity params = new StringEntity(body); request.addHeader("content-type", "application/json"); request.setEntity(params); HttpResponse result = httpClient.execute(request); // print the response String response = EntityUtils.toString(result.getEntity(), "UTF-8"); System.out.println(response); } catch (IOException ex) { ex.printStackTrace(); } } }
Note that we use Java 8 utilities to deal with dates. In Java 7 and earlier, you can use:
SimpleDateFormat fmt = new SimpleDateFormat("YYYY-MM-dd'T'HH:mm:ss"); fmt.setTimeZone(TimeZone.getTimeZone( "UTC" )); String timestamp = fmt.format(new Date());
Gson also let you encode POJO into JSON. For the sake of simplicity, we used a simple TreeMap
, but a Measure
class would be cleaner for a real-life program.
Golang
Here is a full working example in Golang using only the built-in libraries:
package main import ( "time" "bytes" "net/http" "encoding/json" "os" "fmt" "io/ioutil" ) const ( dateFormat = "2006-01-02T15:04:05" baseUrl = "https://bbdata.daplab.ch" token = "9087dd4e58a3f12523c09ab18d5507de" objectId = 1 ) // measure structure type Measure struct { ObjectId int `json:"objectId"` Token string `json:"token"` Timestamp string `json:"timestamp"` Value interface{} `json:"value"` Comment string `json:"comment"` } func main() { now := time.Now().UTC() // create measure measure := Measure{ objectId, token, now.Format(dateFormat), 3.14, "", } // marshall measure to JSON body, err := json.Marshal(measure) if err != nil { fmt.Printf("error parsing measure: %s\n", string(body)) os.Exit(1) } fmt.Printf("body: %s\n", string(body)) // send POST response, err := http.Post(baseUrl + "/input/measures", "application/json", bytes.NewBuffer(body)) if err != nil { fmt.Printf("error sending measure: %s\n", err) os.Exit(1) } // print response fmt.Println("response:") fmt.Printf(" status: %d\n", response.StatusCode) if contents, err := ioutil.ReadAll(response.Body); err == nil { fmt.Printf(" body: %s\n", string(contents)) } }
NodeJS
This example uses only one package, request. You can easily download it using npm:
npm install request --save-deps
var request = require('request'); var dateFormat = "2006-01-02T15:04:05"; var baseUrl = "https://bbdata.daplab.ch"; var token = "9087dd4e58a3f12523c09ab18d5507de"; var objectId = 1; measure = { "objectId": objectId, "timestamp": new Date().toISOString().replace(/Z/, ""), "value": 3.14, "token": token }; request.post( baseUrl + '/input/measures', { json: measure }, function (error, response, body) { console.log("status:", response.status, "body:", body); } );