Empathic Building server ======================== .. warning:: The information on this page may be changed without prior notice. Overview -------- The Empathic Building system has the following major components: * The ReST based back-end (https://eu-api.empathicbuilding.com), running on Azure * Sensor gateways, pushing data to the back-end * Pusher.com, a third party publish-subscribe service that handles distribution of live updates * Web clients, providing the end-user visual interface to the system by loading data from the ReST back-end and subscribing to live updates from Pusher.com Authentication -------------- There are two types of user accounts on the Empathic Building back-end: * User accounts * Gateway accounts User accounts are used with the EB web based client application while gateway accounts are used to send in measurement data. Due to the nature of web applications, user accounts are subject to stricter security than gateway accounts, but they also have access to most API endpoints while gateway accounts can only send measurements and manipulate their own sensors. Obtaining an access token: user accounts ++++++++++++++++++++++++++++++++++++++++ Access to the API as a regular user is done using time-limited **access tokens**. Access tokens are obtained by sending a ``POST`` request to the ``/v1/login`` endpoint. The request must be of type ``application/x-www-form-urlencoded`` and should have two fields: #. email #. password If the login is successful, you will receive a JSON response with the following relevant fields: * ``access_token``: a token you can use to access the API * ``token_type``: the type of the token (to be added to the ``Authorization`` header) * ``expires_in``: the number of seconds after which the access token is no longer valid * ``refresh_token``: a token you can use to obtain a new access token Access tokens have a short validity period, so you need to check if the access token is still valid when submitting an API request. If not, you need to use the refresh token to obtain a new access token. This is done by sending a ``POST`` request to the ``/v1/token`` endpoint, with just one field (``refresh_token``) which contains the refresh token you obtained from ``/v1/login``. You will receive a new refresh and access token pair, as above. The previous refresh token is invalidated and must not be used again. If an attempt to use a refresh token twice is detected, the entire chain of refresh and access tokens are then invalidated and you need to start over. If the refresh token is not refreshed within its validity period, you will have to start over with ``/v1/login``. .. note:: Avoid using the ``/v1/login`` endpoint when possible as password validation is puts a much bigger strain on the system than a token refresh. Obtaining an access token: gateway accounts +++++++++++++++++++++++++++++++++++++++++++ A gateway account will have a static, non-expiring access token assigned to it. If you need to use the account to manage sensors (not usually necessary for a gateway account), you need to obtain the following pieces of information concerning the account #. an **organization ID** #. a **location ID** Using access token to authenticate against the API ++++++++++++++++++++++++++++++++++++++++++++++++++ Most API endpoints require authentication using the ``authorization`` HTTP header. For example, if the access token you received was ``fb9ba505321b4fe0a2c9f018c6e3f80a``, you need to add the header ``authorization: Bearer fb9ba505321b4fe0a2c9f018c6e3f80a`` to your HTTP requests. Browsing the generated API documentation ---------------------------------------- Generated API documentation is available at https://eu-api.empathicbuilding.com/doc. Note that invoking the endpoints from the API browser will most likely not work. Instead, a third party tool like Postman_ is recommended. .. _Postman: https://www.getpostman.com/ Subscribing to live data ------------------------ To get real time notifications on new, changed or deleted items in the database, you will need a `client library`_ for the Pusher.com service. You will then need to configure the client settings appropriately: * key: ``33d6c4f799c274f7e0bc`` (production) or ``64737761a47148863544`` (staging) * cluster: ``eu`` You will not need the ``app_id`` or ``secret`` configuration values. However, you will need to set ``https://eu-api.empathicbuilding.com/v1/pusher/auth`` as the authentication endpoint (``authEndpoint`` on the Javascript client) in order to subscribe to the EB channels (which are all private channels). If your selected Pusher client does not have the ability to use an authentication endpoint, you can directly call the authorization endpoint yourself (`API docs here`_), but the details on how to make this work is beyond the scope of this document. When you have the Pusher client set up, you can subscribe to: * Organization channels (``private-organization-``) * Location channels (``private-location-``) * The notifications channel (``notifications``) Organization channels receive events with the following topic names: * organization-modified (for the organization matching the channel) * organization-deleted (for the organization matching the channel) * location-created * location-modified * location-deleted * user-created * user-modified * user-deleted Location channels, on the other hand, receive events with these topics: * asset-created * asset-modified * asset-deleted * gateway-created * gateway-modified * gateway-deleted * sensor-created * sensor-modified * sensor-deleted The notifications channel contains the following topics: * notification-created * notification-modified * notification-deleted Events are gzip compressed, JSON encoded arrays so in order to decode them, you need to follow these steps: #. Decode the base64 encoded data into binary #. Decompress the binary data using a gzip library #. Decode the decompressed (UTF-8 encoded) text using JSON Each event contains an array of objects: * A ``-created`` event contains all non-confidential attributes of the newly created object * A ``-modified`` event contains the ID of the target object and the changes made to its non-confidential attributes * A ``-deleted`` event contains only the ID of the target object .. _client library: https://pusher.com/docs/libraries .. _API docs here: https://eu-api.empathicbuilding.com/doc#!/Pusher/post_v1_pusher_auth Sending measurement data ------------------------ Gateways can send measurement data to the service by posting a JSON encoded array of measurement objects to the `add measurements`_ endpoint. Each object must have certain common fields (specified below), plus any extra fields related to the specific measurement type. When a batch of measurements is received by the server, it is first validated and then sent to a queue for processing. From there, the batch will be picked up by a measurement data processor which will process the measurements using rules specific to the measurement type. If there are no errors, the processed measurements will be appended to the historical data in the database and the sensors' last communication and last measurement timestamps will be updated along with the last measurement data field. This change will generate a live data event through which the web clients will be informed that a sensor has updated measurement data. The data processors use the combination of the ``location_id``, ``vendor``, ``vendor_id`` and ``type`` fields to find matching sensors to update. If no match is found with this combination, a new sensor will be created. Thus, if a sensor is physically moved to a different location, a new sensor instance will be automatically created. The exception to this rule is location data, where location tags are found across all locations within the same organization. This allows users to move between different locations without having to change their associated location tag. .. note:: The maximum HTTP request size is 2 MB. Anything larger will result in a 413 error. .. note:: Duplicate timestamps are not allowed. Any measurements whose timestamps match previous data will be silently dropped. .. tip:: If no new data has been received but a gateway wishes to update the last communication timestamps of sensors, it can send empty sensor status updates for those sensors (with only the mandatory fields filled). .. _add measurements: https://eu-api.empathicbuilding.com/doc#!/Measurements/post_v1_measurements Measurement formats by type --------------------------- All measurements have certain common fields plus type specific fields. Timestamps are always represented as milliseconds since the UNIX epoch (1970-01-01 00:00:00 UTC). Percentages are always represented as floating point numbers ranging from 0.0 to 1.0. The common fields to all measurement types are: ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== type string Type of measurement (see below for the full list) time integer ms Timestamp of measurement valid_to integer ms (Optional) Timestamp after which the measurement is considered "expired" (default: 86400000 which equals 24 hours) vendor_id string Vendor specific sensor ID name string (Optional) Human-readable name of the sensor ============= ========= ==== =================================================== Temperature *********** Measurement type: ``temperature`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== value float °C Temperature ============= ========= ==== =================================================== Humidity ******** Measurement type: ``humidity`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== value float % Air humidity (from 0.0 to 1.0) ============= ========= ==== =================================================== CO₂ (Carbon Dioxide) ******************** Measurement type: ``co2`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== value float ppm Amount of CO₂ particles in the air ============= ========= ==== =================================================== Air pressure ************ Measurement type: ``airpressure`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== value float hPa Air pressure ============= ========= ==== =================================================== Occupancy ********* Measurement type: ``occupancy`` ============= ========= ======= ================================================ Field Data type Unit Description ============= ========= ======= ================================================ used integer Number (0 or more) of people (or other things) occupying a space total integer Total number of slots ============= ========= ======= ================================================ Noise ***** Measurement type: ``noise`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== value float dB Relative loudness of sounds in air ============= ========= ==== =================================================== Location ******** Measurement type: ``location`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== x float m Horizontal offset from the lower left corner of the floor plan y float m Vertical offset from the lower left corner of the floor plan z float m Altitude from the floor lon float ° Longitude lat float ° Latitude accuracy float m Maximum possible distance between the estimated location and the actual location floor_id string ID of the related floor asset group string One of ``employee``, ``external``, ``visitor``, ``maintenance``, ``cleaning``, ``asset`` ============= ========= ==== =================================================== .. note:: Either (x, y) or (lon, lat) are required. The ``floor_id`` value is always required. If only (lon, lat) are provided, the target building asset **must** have the ``north_vector`` property set to a valid value in order for the (x, y) values to be filled in. Reservation *********** Measurement type: ``reservation`` =============== ========= ==== =================================================== Field Data type Unit Description =============== ========= ==== =================================================== events array Array containing the ongoing (if any) and future reservations =============== ========= ==== =================================================== Each event in the array has the following structure: =============== ========= ==== =================================================== Field Data type Unit Description =============== ========= ==== =================================================== start integer ms Start time of the current event end integer ms End time of the current event subject string Name (usually in the form of "Lastname Firstname") of the person who made the current reservation (optional) user_id string EB user ID to whom this event belongs (optional) xo_id string External Object ID to whom this event belongs (optional) =============== ========= ==== =================================================== An event may optionally contain NoShow integration object data: =============== ========= ==== =================================================== Field Data type Unit Description =============== ========= ==== =================================================== confirmed boolean Whether an event is confirmed to be a no show reason string Explanation why an event is or isn't cancelled =============== ========= ==== =================================================== Satisfaction ************ Measurement type: ``satisfaction`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== value float % Satisfaction percentage (from 0.0 to 1.0) raw_value integer Raw survey value from the vendor ============= ========= ==== =================================================== Illuminance *********** Measurement type: ``illuminance`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== value float lux Illuminance ============= ========= ==== =================================================== Distance ******** Measurement type: ``distance`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== value integer mm Distance ============= ========= ==== =================================================== Stress levels ************* Measurement type: ``stress`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== value float % Stress level (from 0.0 to 1.0) ============= ========= ==== =================================================== Total Volatile Organic Compounds ******************************** Measurement type: ``tvoc`` ============= ========= ==== =================================================== Field Data type Unit Description ============= ========= ==== =================================================== value integer ppb Total VOC ============= ========= ==== =================================================== Sensor status updates ********************* Measurement type: ``sensorstatus`` ================= ============= ==== =================================================== Field Data type Unit Description ================= ============= ==== =================================================== battery_voltage float V Battery voltage, if the sensor is battery powered battery_level float % Battery charge level (from 0.0 to 1.0) battery_model string Battery model name, used to calculate the charge level from the voltage if it is missing (allowed values: ``cr2032``, ``cr17505``) link_quality float % Link quality (from 0.0 to 1.0) mesh_neighbors array[object] For mesh devices, the list of neighbors seen by the sensor, along with link qualities to them. mesh_role string For mesh devices, the current role of the sensor in the network (``sink``, ``headnode``, ``subnode``) mesh_flags array[string] For mesh devices, the currently configured flags on the sensor device mesh_sink_address string For mesh devices, vendor ID of the sink device mesh_next_hop string For mesh devices, the vendor ID of the next node on the way to the sink ================= ============= ==== =================================================== All fields are optional here. For the mesh neighbors array, each object has the following structure: ================= ============= ==== =================================================== Field Data type Unit Description ================= ============= ==== =================================================== vendor_id string Vendor ID of a neighboring device normalized_rssi float % Link quality with the neighbor (from 0.0 to 1.0) ================= ============= ==== =================================================== Sensor status updates do not cause new measurements to be inserted, but only updates to the ``last_status`` field of a sensor object. Practical examples (Python) --------------------------- The following examples have the following requirements: * Python 3.5 or later * requests_ * pysher_ (for the live data example) To subscribe to live sensor data (via modifications of the ``last_measurement`` attribute) at location ID 1:: import gzip import json from base64 import b64decode from pprint import pprint from time import sleep import requests from pysher import Pusher CHANNEL = 'private-location-1' AUTH_URI = 'https://eu-api.empathicbuilding.com/v1/pusher/auth' AUTH_HEADERS = {'authorization': 'Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'} # replace this def print_json(data): uncompressed = gzip.decompress(b64decode(data)) json_data = json.loads(uncompressed) pprint(json_data) def connect_handler(data): # Get the authorization token from the EB server data = json.loads(data) form = {'channel_name': CHANNEL, 'socket_id': data['socket_id']} response = requests.post(AUTH_URI, data=form, headers=AUTH_HEADERS) response.raise_for_status() token = response.json()['auth'] # Subscribe to the channel chan = pusher.subscribe(CHANNEL, auth=token) # Add an event handler for "sensor-modified" events chan.bind('sensor-modified', print_json) pusher = Pusher('33d6c4f799c274f7e0bc', cluster='eu', log_level=None) pusher.connection.bind('pusher:connection_established', connect_handler) pusher.connect() while True: sleep(1) To send a new measurement (23.756°C) on behalf of a sensor with vendor ID "ABC-123":: import requests AUTH_HEADERS = {'authorization': 'Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'} # replace this now_milliseconds = int(datetime.now().timestamp() * 1000) valid_to = now_milliseconds + 15 * 60000 # 15 minutes data = [ {'vendor_id': 'ABC-123', 'time': now_milliseconds, 'valid_to': valid_to, 'type': 'temperature', 'value': 23.756} ] response = requests.post('https://eu-api.empathicbuilding.com/v2/measurements', json=data, headers=AUTH_HEADERS) response.raise_for_status() .. _requests: https://pypi.org/project/requests/ .. _pysher: https://pypi.org/project/Pysher/ .. _measurements: https://eu-api.empathicbuilding.com/doc#/Measurements