Tornado Backend for a simple websocket based IoT platform. Implemented as part of the lab course IoT 2 - Aufbau eines Sensornetzwerks at TUM.
git clone https://github.com/Bitfroest/iot_tornado_backend.git
git submodule update --recursive --remote
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
Go into the frontend folder and execute
npm install
And
npm run build
Copy the build folder into the backendfolder
cp -r build ../backend/
Install Docker CE from https://docs.docker.com/install/
When the image cannot be found locally, docker automatically searches in the Docker repository.
docker run --name station-postgres -e POSTGRES_PASSWORD=<secret_password> -e POSTGRES_DB=iot -d postgres
Replace '<secret_password>' with your own password or your set it to postgres (Note: Remember the password as it is needed later for the 'database.ini' file)
Create database by executing the sql file into the running postgres docker container:
cat iot_database.sql | docker exec -i station-postgres psql -U postgres
Go to the 'Team Station/backend' folder and copy 'database.template.ini' and rename it to 'database.ini' (Update the line password if you have set a password different from postgres).. Execute the docker command within the backend folder:
docker build -t station-python
Run the container:
docker run -it -d --name station-python --link station-postgres:postgres -v "$PWD":/usr/src/app -p 80:8888 station-python
List all running docker containers:
docker container ls
docker ps --all
Start, Restart, Stop, Remove Docker Container
//start
docker start <container-name>
//restart
docker restart <container-name>
//stop
docker stop <container-name>
//remove
docker rm <container-name>
Type the IP address of the raspberry Pi in your browser and check if the server responds (Note: You need to be in the same network)
As of now there are hundrets of possibilities to use in IoT for the backend (server side) and/ or frontend (client side). In the following we want to describe what we have choosen for the practical lab course and why we choose what we choose.
Start Apache2 server
$ sudo /etc/init.d/apache2 restart
Stop Apache2 server
$ sudo /etc/init.d/apache2 stop
Create symlink for domain
sudo ln -s /etc/apache2/sites-available/example.com /etc/apache2/sites-enabled/example.com
Backup database
docker exec -t -u postgres your-db-container pg_dumpall -c > dump_`date +%d-%m-%Y"_"%H_%M_%S`.sql
Restore database or apply migrations/ database changes via sql file
cat your_dump.sql | docker exec -i your-db-container psql -Upostgres
psql into docker container to do queries
docker exec -ti NAME_OF_CONTAINER psql -U YOUR_POSTGRES_USERNAME
./
./api/sensors
./api/sensor/<id>
./api/sensortypes
./api/sensortype/<id>
./api/sensor/<id>/data
./api/sensor/<id>/data/<attribute>
Websockets for Sensordata./ws/<sensor_id>
./api/sensors
GET, POST
Returns:
JSON array of sensors
{
"id": 1,
"name": "TestSensor",
"note": "",
"coordinates": "0101000020E610000000000000000000000000000000000000",
"id_sensor_type": 1,
"coordinatesjson": {
"type": "Point",
"coordinates": [
0,
0
]
}
}
./api/sensortypes
GET, POST
JSON array of sensor types
[
{
"id": 1,
"name": "TestType",
"typedef": {
"Trash": {
"type": "Wastebin",
"position": "center"
},
"Battery": {
"type": "Battery"
}
}
},
]
./api/sensor(type)/<id>
GET, PUT, DELETE
JSON of sensor or sensor type
./api/sensor/<sensor_id>/data
GET, POST
JSON array of (sensor) data
similar to websocket endpoint
{ "results": [
"id": 83,
"value": {
"Trash": 0.01
},
"id_sensor": 1,
"timestamp": {
"$date": 1536529153266
},
"created": {
"$date": 1536529153266
}
],
"id_sensor": "1"
}
./api/sensor/<sensor_id>/data/<key>
GET
JSON array of data
last 100 entries of a key/ attribute
{ "results": [
"id": 83,
"value": {
"Trash": 0.01
},
"id_sensor": 1,
"timestamp": {
"$date": 1536529153266
},
"created": {
"$date": 1536529153266
}
],
"id_sensor": "1"
}
./ws/<sensor_id>
get last entries of all keys
push new data
{ "results": [
"id": 83,
"value": {
"Trash": 0.01
},
"id_sensor": 1,
"timestamp": {
"$date": 1536529153266
},
"created": {
"$date": 1536529153266
}
],
"id_sensor": "1"
}
The following code block is an example of setting the battery to 30% at a specified timestamp.
{
"value": {
"battery" : 0.3
},
"timestamp" : "2018-08-22 12:32:30.495815"
}
Timestamps should be in a standardized format which is understandable by PostgreSQL like the ISO 8601 format
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z"
...
"text": {
"type": "Text",
"label": "Test",
"placeholder": "Bla"
}
...
You can use the inputType
to change the input type of the input field between text
(default), number
and textarea
.
...
"text2" : {
"type": "Text"
"label": "Nummer Eingabe 2"
"inputType": "number"
}
...
...
"time": {
"type": "DateTimePicker"
}
...
...
"time": {
"type": "DateTimePicker",
"label": "Zeit",
"dateFormat": "LLL",
"timeFormat": "HH:mm",
"timeIntervals": 15,
"showTimeSelect": true,
"showTimeSelectOnly": true
}
...
...
"alarm": {
"type": "Alarm",
"label": "Calendar Week Alarm"
}
...
...
"select": {
"type": "Select",
"label": "Dropdown",
"options": [
{
"label": "Chocolate",
"value": "chocolate"
},
{
"label": "Vanilla",
"value": "vanilla"
},
{
"label": "Strawberry",
"value": "strawberry"
}
]
}
...
...
"switch": {
"type": "Boolean",
"style": "ParkingLot"
}
...
...
"switch2": {
"type": "Boolean",
"label": "Test",
"style": "Switch"
}
...
...
"switch3": {
"type": "Boolean",
"style": "Switch",
"isDisabled": true
}
...
...
"battery": {
"type": "Battery"
}
...
...
"temperature": {
"type": "ValuePlot",
"unit": "°C",
"label": "Temperature",
"FlexibleWidthXYPlot": {
"xType": "time",
"height": 300,
"yDomain": [
0,
40
]
}
}
...