added charts
This commit is contained in:
parent
961fba65be
commit
72762340ce
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
devices.yml
|
devices.yml
|
||||||
|
history.*
|
||||||
194
Charts/data.json
Normal file
194
Charts/data.json
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-22T23:59:06+02:00",
|
||||||
|
"mac": "A4:C1:38:46:D7:75",
|
||||||
|
"temperature": 20.4,
|
||||||
|
"humidity": 69,
|
||||||
|
"battery_percent": 87,
|
||||||
|
"battery_volt": 2.985,
|
||||||
|
"count": 241
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:46:D7:75",
|
||||||
|
"name": "4",
|
||||||
|
"room": "dinner_room"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-22T23:59:07+02:00",
|
||||||
|
"mac": "A4:C1:38:DD:9F:EB",
|
||||||
|
"temperature": 20.3,
|
||||||
|
"humidity": 70,
|
||||||
|
"battery_percent": 85,
|
||||||
|
"battery_volt": 2.975,
|
||||||
|
"count": 240
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:DD:9F:EB",
|
||||||
|
"name": "5",
|
||||||
|
"room": "kitchen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-22T23:59:07+02:00",
|
||||||
|
"mac": "A4:C1:38:9A:81:25",
|
||||||
|
"temperature": 25.1,
|
||||||
|
"humidity": 51,
|
||||||
|
"battery_percent": 82,
|
||||||
|
"battery_volt": 2.946,
|
||||||
|
"count": 143
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:9A:81:25",
|
||||||
|
"name": "6",
|
||||||
|
"room": "server"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-22T23:59:09+02:00",
|
||||||
|
"mac": "A4:C1:38:83:05:E8",
|
||||||
|
"temperature": 22.2,
|
||||||
|
"humidity": 61,
|
||||||
|
"battery_percent": 80,
|
||||||
|
"battery_volt": 2.933,
|
||||||
|
"count": 32
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:83:05:E8",
|
||||||
|
"name": "my_room",
|
||||||
|
"room": "my_room"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-23T00:13:48+02:00",
|
||||||
|
"mac": "A4:C1:38:DD:9F:EB",
|
||||||
|
"temperature": 20.3,
|
||||||
|
"humidity": 70,
|
||||||
|
"battery_percent": 85,
|
||||||
|
"battery_volt": 2.974,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:DD:9F:EB",
|
||||||
|
"name": "5",
|
||||||
|
"room": "kitchen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-23T00:13:49+02:00",
|
||||||
|
"mac": "A4:C1:38:46:D7:75",
|
||||||
|
"temperature": 20.4,
|
||||||
|
"humidity": 69,
|
||||||
|
"battery_percent": 87,
|
||||||
|
"battery_volt": 2.986,
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:46:D7:75",
|
||||||
|
"name": "4",
|
||||||
|
"room": "dinner_room"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-23T00:13:50+02:00",
|
||||||
|
"mac": "A4:C1:38:83:05:E8",
|
||||||
|
"temperature": 22.2,
|
||||||
|
"humidity": 61,
|
||||||
|
"battery_percent": 80,
|
||||||
|
"battery_volt": 2.932,
|
||||||
|
"count": 90
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:83:05:E8",
|
||||||
|
"name": "my_room",
|
||||||
|
"room": "my_room"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-23T00:13:55+02:00",
|
||||||
|
"mac": "A4:C1:38:9A:81:25",
|
||||||
|
"temperature": 25.1,
|
||||||
|
"humidity": 51,
|
||||||
|
"battery_percent": 82,
|
||||||
|
"battery_volt": 2.945,
|
||||||
|
"count": 160
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:9A:81:25",
|
||||||
|
"name": "6",
|
||||||
|
"room": "server"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-23T00:14:11+02:00",
|
||||||
|
"mac": "A4:C1:38:83:05:E8",
|
||||||
|
"temperature": 22.3,
|
||||||
|
"humidity": 61,
|
||||||
|
"battery_percent": 81,
|
||||||
|
"battery_volt": 2.932,
|
||||||
|
"count": 92
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:83:05:E8",
|
||||||
|
"name": "my_room",
|
||||||
|
"room": "my_room"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-23T00:14:11+02:00",
|
||||||
|
"mac": "A4:C1:38:46:D7:75",
|
||||||
|
"temperature": 20.4,
|
||||||
|
"humidity": 69,
|
||||||
|
"battery_percent": 87,
|
||||||
|
"battery_volt": 2.986,
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:46:D7:75",
|
||||||
|
"name": "4",
|
||||||
|
"room": "dinner_room"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-23T00:14:12+02:00",
|
||||||
|
"mac": "A4:C1:38:9A:81:25",
|
||||||
|
"temperature": 25.1,
|
||||||
|
"humidity": 51,
|
||||||
|
"battery_percent": 82,
|
||||||
|
"battery_volt": 2.945,
|
||||||
|
"count": 160
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:9A:81:25",
|
||||||
|
"name": "6",
|
||||||
|
"room": "server"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"timestamp": "2024-06-23T00:14:12+02:00",
|
||||||
|
"mac": "A4:C1:38:DD:9F:EB",
|
||||||
|
"temperature": 20.3,
|
||||||
|
"humidity": 70,
|
||||||
|
"battery_percent": 85,
|
||||||
|
"battery_volt": 2.974,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"mac": "A4:C1:38:DD:9F:EB",
|
||||||
|
"name": "5",
|
||||||
|
"room": "kitchen"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
143
Charts/index.html
Normal file
143
Charts/index.html
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Geräte-Diagramme</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #121212;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
.chart-container {
|
||||||
|
width: 80%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
background: #1e1e1e;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.chart {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.device-info {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Geräte-Diagramme</h1>
|
||||||
|
<div id="charts"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
fetch('data.json')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(jsonData => {
|
||||||
|
const deviceDataMap = new Map();
|
||||||
|
jsonData.forEach(entry => {
|
||||||
|
const deviceMac = entry.device.mac;
|
||||||
|
if (!deviceDataMap.has(deviceMac)) {
|
||||||
|
deviceDataMap.set(deviceMac, {info: entry.device, data: []});
|
||||||
|
}
|
||||||
|
deviceDataMap.get(deviceMac).data.push(entry.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
deviceDataMap.forEach((deviceData, deviceMac) => createChart(deviceData));
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error loading data:', error));
|
||||||
|
|
||||||
|
function createChart(deviceData) {
|
||||||
|
const labels = deviceData.data.map(entry => new Date(entry.timestamp).toLocaleTimeString());
|
||||||
|
const temperatures = deviceData.data.map(entry => entry.temperature);
|
||||||
|
const humidities = deviceData.data.map(entry => entry.humidity);
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'chart-container';
|
||||||
|
|
||||||
|
const deviceInfo = document.createElement('div');
|
||||||
|
deviceInfo.className = 'device-info';
|
||||||
|
deviceInfo.innerHTML = `<strong>${deviceData.info.name}</strong> (${deviceData.info.room})`;
|
||||||
|
container.appendChild(deviceInfo);
|
||||||
|
|
||||||
|
const ctx = document.createElement('canvas');
|
||||||
|
ctx.className = 'chart';
|
||||||
|
container.appendChild(ctx);
|
||||||
|
|
||||||
|
document.getElementById('charts').appendChild(container);
|
||||||
|
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Temperatur (°C)',
|
||||||
|
data: temperatures,
|
||||||
|
borderColor: 'rgba(75, 192, 192, 1)',
|
||||||
|
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||||
|
yAxisID: 'y-axis-temp'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Feuchtigkeit (%)',
|
||||||
|
data: humidities,
|
||||||
|
borderColor: 'rgba(153, 102, 255, 1)',
|
||||||
|
backgroundColor: 'rgba(153, 102, 255, 0.2)',
|
||||||
|
yAxisID: 'y-axis-humidity'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
scales: {
|
||||||
|
yAxes: [
|
||||||
|
{
|
||||||
|
id: 'y-axis-temp',
|
||||||
|
type: 'linear',
|
||||||
|
position: 'left',
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
fontColor: 'white'
|
||||||
|
},
|
||||||
|
scaleLabel: {
|
||||||
|
display: true,
|
||||||
|
labelString: 'Temperatur (°C)',
|
||||||
|
fontColor: 'white'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'y-axis-humidity',
|
||||||
|
type: 'linear',
|
||||||
|
position: 'right',
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
fontColor: 'white'
|
||||||
|
},
|
||||||
|
scaleLabel: {
|
||||||
|
display: true,
|
||||||
|
labelString: 'Feuchtigkeit (%)',
|
||||||
|
fontColor: 'white'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
xAxes: [
|
||||||
|
{
|
||||||
|
ticks: {
|
||||||
|
fontColor: 'white'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
fontColor: 'white'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
107
Charts/script.js
Normal file
107
Charts/script.js
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
fetch('data.json')
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok ' + response.statusText);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
const devices = {};
|
||||||
|
|
||||||
|
data.forEach(entry => {
|
||||||
|
const mac = entry.device.mac;
|
||||||
|
if (!devices[mac]) {
|
||||||
|
devices[mac] = {
|
||||||
|
temperatureData: [],
|
||||||
|
humidityData: [],
|
||||||
|
labels: [],
|
||||||
|
name: entry.device.name,
|
||||||
|
room: entry.device.room
|
||||||
|
};
|
||||||
|
}
|
||||||
|
devices[mac].labels.push(new Date(entry.data.timestamp));
|
||||||
|
devices[mac].temperatureData.push(entry.data.temperature);
|
||||||
|
devices[mac].humidityData.push(entry.data.humidity);
|
||||||
|
});
|
||||||
|
|
||||||
|
const chartsContainer = document.getElementById('chartsContainer');
|
||||||
|
|
||||||
|
Object.keys(devices).forEach(mac => {
|
||||||
|
const device = devices[mac];
|
||||||
|
|
||||||
|
const chartContainer = document.createElement('div');
|
||||||
|
chartContainer.className = 'chart-container';
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.id = `chart-${mac}`;
|
||||||
|
|
||||||
|
const title = document.createElement('h3');
|
||||||
|
title.textContent = `Device ${device.name} in ${device.room}`;
|
||||||
|
|
||||||
|
chartContainer.appendChild(title);
|
||||||
|
chartContainer.appendChild(canvas);
|
||||||
|
chartsContainer.appendChild(chartContainer);
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: device.labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Temperatur (°C)',
|
||||||
|
data: device.temperatureData,
|
||||||
|
borderColor: 'rgba(255, 99, 132, 1)',
|
||||||
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||||
|
borderWidth: 1,
|
||||||
|
yAxisID: 'y1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Luftfeuchtigkeit (%)',
|
||||||
|
data: device.humidityData,
|
||||||
|
borderColor: 'rgba(54, 162, 235, 1)',
|
||||||
|
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||||
|
borderWidth: 1,
|
||||||
|
yAxisID: 'y2'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
unit: 'minute',
|
||||||
|
tooltipFormat: 'll HH:mm'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y1: {
|
||||||
|
type: 'linear',
|
||||||
|
position: 'left',
|
||||||
|
beginAtZero: true,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Temperatur (°C)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y2: {
|
||||||
|
type: 'linear',
|
||||||
|
position: 'right',
|
||||||
|
beginAtZero: true,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Luftfeuchtigkeit (%)'
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
drawOnChartArea: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error loading JSON data:', error));
|
||||||
|
});
|
||||||
20
Charts/styles.css
Normal file
20
Charts/styles.css
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
width: 80%;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
@ -29,11 +29,11 @@ class Data:
|
|||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
return {
|
return {
|
||||||
'timestamp': self.timestamp,
|
"timestamp": self.timestamp,
|
||||||
'mac': self.mac,
|
"mac": self.mac,
|
||||||
'temperature': self.temperature,
|
"temperature": self.temperature,
|
||||||
'humidity': self.humidity,
|
"humidity": self.humidity,
|
||||||
'battery_percent': self.battery_percent,
|
"battery_percent": self.battery_percent,
|
||||||
'battery_volt': self.battery_volt,
|
"battery_volt": self.battery_volt,
|
||||||
'count': self.count,
|
"count": self.count,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,13 @@ class Device:
|
|||||||
self.name = data['name']
|
self.name = data['name']
|
||||||
self.room = data['room']
|
self.room = data['room']
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
return {
|
||||||
|
"mac": self.mac,
|
||||||
|
"name": self.name,
|
||||||
|
"room": self.room
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_devices():
|
def get_devices():
|
||||||
devices_list = []
|
devices_list = []
|
||||||
|
|||||||
@ -29,7 +29,8 @@ class ScanDelegate(DefaultDelegate):
|
|||||||
if len(val) != 30: return False
|
if len(val) != 30: return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_atc_device(self, dev, data_obj):
|
@staticmethod
|
||||||
|
def is_atc_device(dev, data_obj):
|
||||||
global devices
|
global devices
|
||||||
if 'A4:C1:38' not in dev.addr.upper(): return False
|
if 'A4:C1:38' not in dev.addr.upper(): return False
|
||||||
for device in devices:
|
for device in devices:
|
||||||
|
|||||||
@ -1 +1,32 @@
|
|||||||
# TODO
|
import os
|
||||||
|
import json
|
||||||
|
from data_class import Data
|
||||||
|
from devices import Device
|
||||||
|
|
||||||
|
workdir, filename = os.path.split(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def log_to_txt(devices):
|
||||||
|
with open(f'{workdir}{os.sep}history.json', 'r') as file:
|
||||||
|
data = json.load(file)
|
||||||
|
|
||||||
|
with open(f'{workdir}{os.sep}history.json', 'w') as file:
|
||||||
|
for device in devices:
|
||||||
|
dev, data_obj, from_config = device
|
||||||
|
data_obj:Data
|
||||||
|
from_config:Device
|
||||||
|
data.append({
|
||||||
|
"data": data_obj.to_json(),
|
||||||
|
"device": from_config.to_json()
|
||||||
|
})
|
||||||
|
final_data = {"measurements": data}
|
||||||
|
file.write(json.dumps(data, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
def log_to_mongodb(data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def log_to_mqtt(data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
from discovery import start_discovery
|
from discovery import start_discovery
|
||||||
from loop import start_loop
|
from loop import start_loop
|
||||||
|
from log_data import log_to_txt
|
||||||
|
|
||||||
|
|
||||||
devices = start_discovery()
|
devices = start_discovery()
|
||||||
|
log_to_txt(devices)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user