charts
This commit is contained in:
parent
9581a3ce2c
commit
345666cb2e
20
Dockerfile
20
Dockerfile
@ -1,9 +1,9 @@
|
||||
FROM python:3.12-alpine3.20 AS build_bluepy
|
||||
|
||||
RUN apk add \
|
||||
bluez \
|
||||
make \
|
||||
git \glib-dev \
|
||||
git \
|
||||
glib-dev \
|
||||
gcc \
|
||||
build-base \
|
||||
freetype-dev \
|
||||
@ -16,20 +16,22 @@ RUN git clone https://github.com/IanHarvey/bluepy.git && \
|
||||
python3.12 setup.py install
|
||||
|
||||
FROM python:3.12-alpine3.20
|
||||
|
||||
WORKDIR = /src
|
||||
COPY python/src/ .
|
||||
COPY python/requierements.txt .
|
||||
COPY python/docker_entrypoint.sh /
|
||||
WORKDIR /src
|
||||
COPY ./python/src/ .
|
||||
COPY ./python/requierements.txt .
|
||||
COPY ./python/docker_entrypoint.sh /
|
||||
RUN mkdir data
|
||||
VOLUME data
|
||||
RUN touch DOCKER
|
||||
VOLUME /src/data
|
||||
|
||||
RUN apk add sudo bluez
|
||||
RUN apk add --no-cache sudo bluez tzdata
|
||||
ENV TZ=Europe/Berlin
|
||||
|
||||
# Copy bluepy from the bluepy build stage
|
||||
COPY --from=build_bluepy /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
|
||||
COPY --from=build_bluepy /usr/local/bin /usr/local/bin
|
||||
|
||||
RUN pip3.12 install -r requierements.txt && rm requierements.txt
|
||||
# RUN echo '@reboot root python3.12 /src/serve_json.py' >> /etc/crontab
|
||||
|
||||
ENTRYPOINT sh /docker_entrypoint.sh
|
||||
@ -3,5 +3,5 @@ version: '3'
|
||||
services:
|
||||
atc_mithermometer_gateway:
|
||||
image: dasmoorhuhn/atc-mithermometer-gateway:develop-alpine
|
||||
container_name: ATC_MiThermometer_Gateway
|
||||
container_name: ATC_MiThermometer_Gateway_Build
|
||||
build: .
|
||||
@ -1,8 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
# service dbus start
|
||||
# bluetoothd &
|
||||
|
||||
/bin/sh
|
||||
|
||||
sudo python3 main.py
|
||||
python3.12 api_endpoints.py &
|
||||
sudo python3.12 main.py
|
||||
@ -1,4 +1,6 @@
|
||||
pyyaml
|
||||
bs4
|
||||
lxml
|
||||
requests
|
||||
requests
|
||||
flask
|
||||
flask_cors
|
||||
39
python/src/api_endpoints.py
Normal file
39
python/src/api_endpoints.py
Normal file
@ -0,0 +1,39 @@
|
||||
import os
|
||||
from flask import Flask
|
||||
from flask import jsonify
|
||||
from flask import send_from_directory
|
||||
from flask_cors import CORS
|
||||
from flask_cors import cross_origin
|
||||
|
||||
|
||||
class API:
|
||||
"""
|
||||
API endpoints
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.app = Flask(import_name='backend', static_folder='/data', static_url_path='')
|
||||
CORS(self.app)
|
||||
self.app.config['CORS_HEADERS'] = 'Content-Type'
|
||||
|
||||
# --------Static Routes-------
|
||||
@self.app.route('/charts')
|
||||
@cross_origin()
|
||||
def serve_index():
|
||||
return send_from_directory('/src', 'chart.html')
|
||||
|
||||
@self.app.route('/json')
|
||||
@cross_origin()
|
||||
def serve_get_list_of_json():
|
||||
workdir, filename = os.path.split(os.path.abspath(__file__))
|
||||
return jsonify(os.listdir(f'{workdir}/data'))
|
||||
|
||||
@self.app.route('/json/<path:path>')
|
||||
@cross_origin()
|
||||
def serve_json(path):
|
||||
workdir, filename = os.path.split(os.path.abspath(__file__))
|
||||
return send_from_directory(f'{workdir}/data', path)
|
||||
|
||||
|
||||
api = API()
|
||||
api.app.run(host='0.0.0.0', port=8000)
|
||||
128
python/src/chart.html
Normal file
128
python/src/chart.html
Normal file
@ -0,0 +1,128 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Messdaten Charts</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.chartContainer {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
height: 500px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
canvas {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Messdaten Charts</h1>
|
||||
<div id="charts"></div>
|
||||
|
||||
<script>
|
||||
async function fetchJSONFiles() {
|
||||
try {
|
||||
const response = await fetch('http://localhost:8000/json');
|
||||
const jsonFiles = await response.json();
|
||||
return jsonFiles;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der JSON-Dateien:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchData(filename) {
|
||||
try {
|
||||
const response = await fetch(`http://localhost:8000/json/${filename}`);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(`Fehler beim Abrufen der Datei ${filename}:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function parseChartData(data) {
|
||||
const timestamps = data.map(entry => new Date(entry.timestamp));
|
||||
const temperatures = data.map(entry => entry.temperature);
|
||||
const humidity = data.map(entry => entry.humidity);
|
||||
const name = data.length > 0 ? data[0].name : 'Unbekannt';
|
||||
const room = data.length > 0 ? data[0].room : 'Unbekannt';
|
||||
|
||||
return { timestamps, temperatures, humidity, name, room };
|
||||
}
|
||||
|
||||
async function renderCharts() {
|
||||
const jsonFiles = await fetchJSONFiles();
|
||||
const chartsContainer = document.getElementById('charts');
|
||||
|
||||
for (const file of jsonFiles) {
|
||||
const data = await fetchData(file);
|
||||
const { timestamps, temperatures, humidity, name, room } = parseChartData(data);
|
||||
|
||||
const chartContainer = document.createElement('div');
|
||||
chartContainer.className = 'chartContainer';
|
||||
chartContainer.innerHTML = `<h2>Gerät: ${name}, Raum: ${room}</h2><canvas></canvas>`;
|
||||
chartsContainer.appendChild(chartContainer);
|
||||
|
||||
const ctx = chartContainer.querySelector('canvas').getContext('2d');
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: timestamps,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Temperatur (°C)',
|
||||
data: temperatures,
|
||||
borderColor: 'rgba(255, 99, 132, 1)',
|
||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||
fill: false,
|
||||
},
|
||||
{
|
||||
label: 'Luftfeuchtigkeit (%)',
|
||||
data: humidity,
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
fill: false,
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'minute'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: `Gerät: ${name}, Raum: ${room}`
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderCharts();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,17 +1,19 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from data_class import Data
|
||||
from devices import Device
|
||||
|
||||
workdir, filename = os.path.split(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def log_to_json(devices):
|
||||
workdir, filename = os.path.split(os.path.abspath(__file__))
|
||||
|
||||
for device in devices:
|
||||
dev, data_obj, from_config = device
|
||||
data_obj: Data
|
||||
from_config: Device
|
||||
file_name = f'{workdir}{os.sep}data{os.sep}{str(data_obj.mac).replace(":", "-")}.json'
|
||||
file_name = f'{workdir}/data/{str(data_obj.mac).replace(":", "-")}.json'
|
||||
print(file_name)
|
||||
|
||||
try:
|
||||
with open(file_name, 'r') as file: data = json.load(file)
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
from time import sleep
|
||||
|
||||
from log_data import log_to_json
|
||||
from discovery import start_discovery
|
||||
|
||||
|
||||
def start_loop(interval=60):
|
||||
while True:
|
||||
start_discovery()
|
||||
devices = start_discovery()
|
||||
log_to_json(devices)
|
||||
sleep(interval)
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
from discovery import start_discovery
|
||||
from loop import start_loop
|
||||
from log_data import log_to_json
|
||||
from loop import start_loop
|
||||
|
||||
|
||||
devices = start_discovery()
|
||||
log_to_json(devices)
|
||||
# devices = start_discovery()
|
||||
# log_to_json(devices)
|
||||
|
||||
start_loop()
|
||||
|
||||
@ -1,12 +1,21 @@
|
||||
TAG=develop-alpine
|
||||
CONTAINER=dasmoorhuhn/atc-mithermometer-gateway:$TAG
|
||||
|
||||
sudo killall -9 bluetoothd
|
||||
docker stop $CONTAINER > /dev/null 2>&1
|
||||
sudo killall -9 bluetoothd > /dev/null 2>&1
|
||||
# docker stop $CONTAINER > /dev/null 2>&1
|
||||
sh stop_docker.sh
|
||||
docker container rm ATC_MiThermometer_Gateway > /dev/null 2>&1
|
||||
echo Start container...
|
||||
docker run \
|
||||
--cap-add=SYS_ADMIN \
|
||||
--cap-add=NET_ADMIN \
|
||||
--net=host \
|
||||
--name=ATC_MiThermometer_Gateway \
|
||||
--restart unless-stopped \
|
||||
--tty \
|
||||
-ti \
|
||||
-v /var/run/dbus/:/var/run/dbus/ \
|
||||
-v ./data:/src/data \
|
||||
$CONTAINER
|
||||
$CONTAINER \
|
||||
|
||||
docker container rm ATC_MiThermometer_Gateway > /dev/null 2>&1
|
||||
3
stop_docker.sh
Normal file
3
stop_docker.sh
Normal file
@ -0,0 +1,3 @@
|
||||
echo Stopping container gracefully...
|
||||
docker stop ATC_MiThermometer_Gateway
|
||||
echo Done
|
||||
Loading…
x
Reference in New Issue
Block a user