Skip to content

Commit 20201ee

Browse files
Sunbrye LySunbrye Ly
authored andcommitted
Complete Write up Draft
1 parent 83a04c7 commit 20201ee

File tree

5 files changed

+188
-181
lines changed

5 files changed

+188
-181
lines changed

README.md

Lines changed: 61 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
## Build a starter Python3 IoT app with the InfluxDB API and client libraries
2-
3-
## Introduction
1+
## Build a starter Python IoT app with the InfluxDB API and client libraries
42

53
This guide is for python developers who want to build Internet-of-Things (IoT) applications using the InfluxDB API and client libraries.
64
InfluxDB API client libraries are maintained by InfluxData and the user community. As a developer, client libraries enable you to take advantage of:
@@ -13,30 +11,19 @@ deconstruct the flow of events and data between the app, devices, and InfluxDB.
1311
You'll see code samples that use InfluxDB API python client libraries to
1412
manage IoT devices, write data to InfluxDB, query data from InfluxDB, create visualizations, and monitor the health of devices and the application itself.
1513

16-
From start to finish, you will:
17-
- create a UI dashboard using Jinja
18-
- install the influxdb-client package
19-
- [create a configuration file for authentication with InfluxDB](#create-config.ini)
20-
- [create a virtual IoT device](#create-iot-virtual-device)
21-
- [store virtual IoT device to InfluxDB using the API](#store-virtual-iot-device-to-influxdb)
22-
- [query bucket for IoT device using the API](#query-bucket-for-device)
23-
- [write telemetry data to InfluxDB using the API](#write-telemetry-data)
24-
- [query telemetry data in InfluxDB using the API](#query-telemetry-data)
2514

2615
## Contents
27-
1. [Setup InfluxDB](#setup-influxdb)
28-
1. [InfluxDB API basics](#influxdb-api-basics)
29-
1. [Authorization and authentication in InfluxDB](#authorization-and-authentication-in-influxdb)
30-
1. Start with an API client library
31-
1. Create a bucket
32-
1. Measurements, time series
33-
2. Measurement schemas (aka bucket schemas)
34-
1. [Write data to InfluxDB](#write-data-to-influxdb)
35-
1. Write line protocol
36-
1. [Query InfluxDB](#query-influxdb)
37-
1. Send a Flux query
38-
1. Aggregate and downsample your data
39-
1. Create data visualizations
16+
From start to finish, you will:
17+
- [Create a UI dashboard using Jinja](#create-iot-center-dashboard)
18+
- [Setup InfluxDB](#install-influxdb)
19+
- [Install influxdb-client package](#install-influxdb-client-package)
20+
- [Create a configuration file for authentication with InfluxDB](#create-config.ini)
21+
- [Create a virtual IoT device](#create-iot-virtual-device)
22+
- [Store virtual IoT device to InfluxDB using the API](#store-virtual-iot-device-to-influxdb)
23+
- [Query bucket for IoT device using the API](#query-bucket-for-device)
24+
- [Write telemetry data to InfluxDB using the API](#write-telemetry-data)
25+
- [Query telemetry data in InfluxDB using the API](#query-telemetry-data)
26+
4027

4128
## InfluxDB API basics
4229

@@ -167,7 +154,17 @@ To learn more about InfluxDB data elements, schemas, and design principles, see
167154

168155
{{% /note %}}
169156

170-
## Create IoT Center Dashboard
157+
## Introducing IoT Center
158+
159+
The IoT Center architecture has four layers:
160+
161+
- **InfluxDB API**: InfluxDB v2 API.
162+
- **IoT device**: Virtual or physical devices write IoT data to the InfluxDB API.
163+
- **IoT Center UI**: User interface sends requests to IoT Center server and renders views for the browser.
164+
- **IoT Center server**: Server and API receives requests from the UI, sends requests to InfluxDB,
165+
and processes responses from InfluxDB.
166+
167+
## Create IoT Center
171168
You will be using Flask alongside Jinja to create your IoT Center application.
172169
Flask is a micro web framework that lets you develop web applications easily.
173170
Jinja is a web template engine used to help render the UI.
@@ -300,6 +297,13 @@ For a production application, we recommend you create a token with minimal permi
300297

301298
## Configure IoT App
302299

300+
### Install influxdb-client Package
301+
Use pip to install the influxdb-client package in your virtual environment.
302+
Additional information regarding the package can be found [here](https://pypi.org/project/influxdb-client/)
303+
```bash
304+
$ pip install influxdb-client
305+
```
306+
303307
### Create config.ini
304308

305309
The Python client library provides a client to easily interact with your InfluxDB instance. In order set up the client
@@ -454,7 +458,7 @@ Using the ```InfluxDBClient``` we create a ```WriteApi``` instance that will all
454458
We then create the record we want to write to the bucket using ```Point```. In this case, ```deviceauth``` is the name
455459
of the ```_measurement```. We use ```deviceId``` as the tag, and we include two separate fields named ```key``` and ```token``` to store
456460
the device authorization information (more information on authorization will be provided further along in the guide).
457-
We then use ```write_api``` to send the API request and write the record to your bucket.
461+
We then use ```write_api``` to send the API request to ```/api/v2/write``` and write the record to your bucket.
458462
```write_api``` returns ```None``` on success, so we check for any failures then return the ```device_id``` if the request
459463
was successful.
460464
```python
@@ -674,131 +678,39 @@ def create_authorization(device_id) -> Authorization:
674678
```create_authorization()```
675679
676680
677-
## Send API Requests
678-
Now that you have InfluxDB, an API token, and the Python client library, you will use the client library to send a request to the InfluxDB API.
679-
680-
681-
#### Example: list API endpoints
682-
683-
Use a client library to retrieve a list of InfluxDB API endpoints.
684-
685-
{{% api-endpoint method="GET" endpoint="http://localhost:8086/api/v2"%}}
686-
687-
{{< code-tabs-wrapper >}}
688-
{{% code-tabs %}}
689-
[Node.js](#nodejs)
690-
{{% /code-tabs %}}
691-
{{% code-tab-content %}}
692-
693-
1. Copy the example below and replace the INFLUX_URL and INFLUX_TOKEN values with your own.
694-
695-
```js
696-
697-
#!/usr/bin/env node
698-
699-
const { InfluxDB } = require('@influxdata/influxdb-client')
700-
const { RootAPI } = require('@influxdata/influxdb-client-apis')
701-
702-
const INFLUX_URL='http://192.168.1.2:8086'
703-
const INFLUX_TOKEN='29Ye1KH9VkASPR2DSfRfFd82OwGD-5HWkBj0Ju_m-DTgT4PHakgweD3p87mp45Y633njDllKkD5wVc0zMCVhIw=='
704-
705-
const influxdb = new InfluxDB({url: INFLUX_URL, token: INFLUX_TOKEN})
706-
const rootAPI = new RootAPI(influxdb)
707-
708-
rootAPI.getRoutes().then(routes => console.log(routes))
709-
710-
```
711-
712-
2. From your `iot-center-v2` directory, launch the `node` REPL.
713-
714-
```sh
715-
716-
node
717-
718-
```
719-
720-
3. At the prompt, paste the example code (from _Step 1_) and press `Enter`.
721-
722-
{{% /code-tab-content %}}
723-
{{< /code-tabs-wrapper >}}
724-
725-
726-
727-
728-
## Introducing IoT Center
729-
730-
The IoT Center architecture has four layers:
731-
732-
- **InfluxDB API**: InfluxDB v2 API.
733-
- **IoT device**: Virtual or physical devices write IoT data to the InfluxDB API.
734-
- **IoT Center UI**: User interface sends requests to IoT Center server and renders views for the browser.
735-
- **IoT Center server**: Server and API receives requests from the UI, sends requests to InfluxDB,
736-
and processes responses from InfluxDB.
737-
738-
### IoT Center: register and list IoT devices
681+
## Connecting the UI to the API
682+
Now that the core functionality has been implemented, we can now create a UI to perform these requests.
683+
Your IoT Dashboard will have four main pages.
684+
* Query Devices
685+
* Create Devices
686+
* Write Data
687+
* Query Data
739688
740-
The IoT Center **Device Registrations** page lists registered IoT devices
741-
and lets you register new devices.
742-
In IoT Center, a **registered device** is a virtual or physical IoT device with a unique InfluxDB authorization.
743-
For each registered device, you can do the following:
744-
- view a dashboard with data visualizations
745-
- view configuration
746-
- remove the device
689+
Within your templates directory, create the following files
690+
* ```create.html```(link outs to the files. UI code explanation out of scope) (Page to create virutal IoT device)
691+
* ```data.html``` (Page to query for telemetry data)
692+
* ```devices.html``` (Page to query for device data)
693+
* ```write.html``` (Page to write telemetry data)
747694
748-
### IoT Center: register a device
695+
Once those files have been created, update ```base.html``` and ```app.py``` to connect the routes.
696+
Without going into details regarding the UI, ```app.py``` serves our routes and runs our core logic.
749697
750-
When you click the "Register" button, IoT Center UI sends a request to the `/api/devices/DEVICE_ID` IoT Center server endpoint. In response, IoT Center server queries InfluxDB to find a point with device ID in `INFLUX_BUCKET_AUTH`.
751-
If no point exists (i.e., the device is not registered), IoT Center server uses the InfluxDB client library to send the following API requests:
752-
753-
2. `POST` request to `/api/v2/write` writes a `deviceauth` point with device ID to `INFLUX_BUCKET_AUTH` bucket.
754-
3. `POST` request to `/api/v2/authorizations` creates an authorization with the description **IoT Center: `DEVICE_ID`** and write permission to the `INFLUX_BUCKET` bucket.
755-
4. `POST` request to `/api/v2/write` writes a `deviceauth` point with device ID, API token, and authorization ID to `INFLUX_BUCKET_AUTH`.
756-
757-
758-
### IoT Center: list registered devices
759-
760-
To list registered devices, the IoT Center UI [DevicesPage](https://github.com/bonitoo-io/iot-center-v2/blob/3118c6576ad7bccf0b84b63f95350bdaa159324e/app/ui/src/pages/DevicesPage.tsx) component sends a request to the `/api/devices` IoT Center server endpoint.
761-
{{/* Source: https://github.com/bonitoo-io/iot-center-v2/blob/3118c6576ad7bccf0b84b63f95350bdaa159324e/app/ui/src/pages/DevicesPage.tsx */}}
762-
763-
When IoT Center server receives the request, the server calls the [`getDevices()`](ttps://github.com/bonitoo-io/iot-center-v2/blob/f5b4a0b663e2e14bcd4f6fddb35cab2de216e6b6/app/server/influxdb/devices.js#L16) function.
764-
`getDevices()` does the following:
765-
766-
1. Instantiates a query client from the InfluxDB client library.
767-
2. Builds a Flux query to retrieve all points with the `deviceauth` measurement from
768-
the `INFLUX_BUCKET_AUTH` bucket.
769-
3. Returns a `Promise` that sends the query to the `/api/v2/query` InfluxDB API endpoint and iterates over devices in the response.
770-
771-
#### Example
772-
773-
{{< code-tabs-wrapper >}}
774-
{{% code-tabs %}}
775-
[Node.js](#nodejs)
776-
{{% /code-tabs %}}
777-
{{% code-tab-content %}}
778-
779-
Flux query for devices in InfluxDB.
780-
The query doesn't return device API tokens unless a device ID is specified.
781-
782-
```js
783-
784-
const deviceFilter =
785-
deviceId !== undefined
786-
? flux` and r.deviceId == "${deviceId}"`
787-
: flux` and r._field != "token"`
788-
const fluxQuery = flux`from(bucket:${INFLUX_BUCKET_AUTH})
789-
|> range(start: 0)
790-
|> filter(fn: (r) => r._measurement == "deviceauth"${deviceFilter})
791-
|> last()`
698+
For example, in our ```data``` route, we retrieve ```device_id_input``` from ```data.html``` when user input is submitted
699+
and call ```get_measurements()``` with the provided input. After the query is ran, we re-render ```data.html``` with the query results.
792700
701+
```python
702+
@app.route('/data', methods=['GET', 'POST'])
703+
def data():
704+
if request.method == 'GET':
705+
return render_template('data.html', data=None)
706+
else:
707+
device_id = request.form.get('device_id_input', None)
708+
results = devices.get_measurements(device_id)
709+
return render_template('data.html', data=results)
793710
```
794711
795-
{{% /code-tab-content %}}
796-
{{< /code-tabs-wrapper >}}
797-
798-
{{% caption %}}[/app/server/influxdb/devices.js line 18](https://github.com/bonitoo-io/iot-center-v2/blob/f5b4a0b663e2e14bcd4f6fddb35cab2de216e6b6/app/server/influxdb/devices.js#L18){{% /caption %}}
799-
800-
### IoT Center: device details
801-
802-
IoT Center displays configuration details for a registered IoT device.
803-
IoT Center API composes device configuration from the device's authorization and your InfluxDB configuration properties.
804-
712+
Once the correct files have been created and updated, you can run the app to view the completed IoT Center at
713+
```http://localhost:5000```
714+
```bash
715+
flask run
716+
```

api/devices.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,16 @@ def get_buckets():
2828
return buckets
2929

3030

31-
def get_device(device_id) -> {}:
31+
def get_device() -> {}:
3232
influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'),
3333
token=config.get('APP', 'INFLUX_TOKEN'),
3434
org=config.get('APP', 'INFLUX_ORG'))
3535

3636
# Queries must be formatted with single and double quotes correctly
3737
query_api = QueryApi(influxdb_client)
3838
# query_api = influxdb_client.query_api()
39-
device_id = str(device_id)
40-
device_filter = f'r.deviceId == "{device_id}" and r._field != "token"'
39+
# device_id = str(device_id)
40+
device_filter = f'r._field != "token"'
4141
flux_query = f'from(bucket: "{config.get("APP", "INFLUX_BUCKET_AUTH")}") ' \
4242
f'|> range(start: 0) ' \
4343
f'|> filter(fn: (r) => r._measurement == "deviceauth" and {device_filter}) ' \
@@ -48,10 +48,11 @@ def get_device(device_id) -> {}:
4848

4949
# iterate through the result(s)
5050
# TODO maybe change this to only show, device_id, auth_id, auth_token?
51+
# iterate through the result(s)
5152
results = []
5253
for table in response:
53-
for record in table.records:
54-
results.append((record.get_field(), record.get_value()))
54+
results.append(table.records[0].values)
55+
5556
return results
5657

5758

app.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from flask import Flask, render_template, request
22
from api.helper_functions import get_current_time
3-
from api import devices, write_data
3+
from api import devices
44

55
app = Flask(__name__, template_folder='templates')
66

@@ -12,22 +12,23 @@ def index():
1212

1313
@app.route('/devices', methods=['GET', 'POST'])
1414
def get_devices():
15-
if request.method == 'GET':
16-
return render_template('devices.html')
15+
result = devices.get_device()
16+
if not result:
17+
return render_template('devices.html', data=None)
1718
else:
18-
device_id = devices.get_device('8fcbfbbb-d0db-40f5-8828-3f828b987a89')
19-
return render_template('devices.html', device_id=device_id)
19+
return render_template('devices.html', data=result)
2020

2121

2222
@app.route('/create')
2323
def create():
2424
return render_template('create.html', device_id=None)
2525

2626

27+
# On submit function for /create
2728
@app.route('/create_device', methods=['GET', 'POST'])
2829
def create_device():
2930
if request.method == 'GET':
30-
return render_template('devices.html', device_id=None)
31+
return render_template('create.html', device_id=None)
3132
else:
3233
device_id = request.form.get('device_id_input', None)
3334
device_id = devices.create_device(device_id)
@@ -59,7 +60,7 @@ def get_buckets():
5960
# 'schema_type': 'implicit',
6061
# 'type': 'user',
6162
# 'updated_at': datetime.datetime(2022, 3, 15, 17, 22, 33, 731038, tzinfo=tzutc())}
62-
# parse the response
63+
# parse the response and create a table instead
6364
return render_template('buckets.html', buckets=buckets)
6465

6566

@@ -71,9 +72,6 @@ def auth():
7172

7273
@app.route('/write', methods=['GET', 'POST'])
7374
def write():
74-
# Pass in device_id
75-
# Write data using the device_id?
76-
#
7775
if request.method == 'GET':
7876
return render_template('write.html', device_id=None)
7977
else:

templates/devices.html

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,32 @@
88
{% extends 'base.html' %}
99
{% block content %}
1010

11-
<form method="post" action="/devices">
12-
<input type="submit" class="btn btn-primary" name="get" value="get_value">
13-
<input type="submit" class="btn btn-primary" name="set" value="set_value">
14-
</form>
15-
16-
{% if device_id is not none %}
17-
<div>{{device_id}}</div>
11+
{% if data is none %}
12+
<div>
13+
<p>You currently do not have any virtual IoT devices registered.</p>
14+
</div>
1815
{% else %}
19-
<div>The Default Thing</div>
16+
<caption>The following is a list of your registered IoT devices.</caption>
17+
<table id="data" class="table table-striped">
18+
<thead>
19+
<tr>
20+
<th>Time</th>
21+
<th>Device</th>
22+
<th>Field</th>
23+
<th>Value</th>
24+
</tr>
25+
</thead>
26+
<tbody>
27+
{% for result in data %}
28+
<tr>
29+
<td>{{ result._time }}</td>
30+
<td>{{ result.deviceId }}</td>
31+
<td>{{ result._field }}</td>
32+
<td>{{ result._value }}</td>
33+
</tr>
34+
{% endfor %}
35+
</tbody>
36+
</table>
2037
{% endif %}
2138
{% endblock %}
2239
</body>

0 commit comments

Comments
 (0)