started
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
from flask import Flask, Response, json, jsonify, render_template, send_from_directory
|
||||
import requests
|
||||
app = Flask(__name__)
|
||||
|
||||
thermoRoute = "http://192.168.178.25:5000/"
|
||||
|
||||
@app.route('/<path:path>')
|
||||
def send_js(path):
|
||||
return send_from_directory('', path)
|
||||
|
||||
@app.route("/")
|
||||
def root():
|
||||
return render_template("index2.html")
|
||||
|
||||
@app.route("/stats")
|
||||
def stats():
|
||||
return jsonify(requests.get(thermoRoute).json())
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/* colours */
|
||||
:root{
|
||||
--primary: #FFE9D2;
|
||||
--secondary: #FFE1C4;
|
||||
--title: #FF8816;
|
||||
}
|
||||
|
||||
/* layout styles */
|
||||
nav{
|
||||
background: var(--primary);
|
||||
border-bottom: 10px solid var(--secondary);
|
||||
}
|
||||
nav a{
|
||||
text-transform: uppercase;
|
||||
color: var(--title);
|
||||
}
|
||||
nav a span{
|
||||
font-weight: bold;
|
||||
}
|
||||
nav .sidenav-trigger{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* recipe styles */
|
||||
.recipes{
|
||||
margin-top: 20px;
|
||||
}
|
||||
.card-panel.recipe{
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
box-shadow: 0px 1px 3px rgba(90,90,90,0.1);
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 6fr 1fr;
|
||||
grid-template-areas: "image details delete";
|
||||
position: relative;
|
||||
}
|
||||
.recipe img{
|
||||
grid-area: image;
|
||||
max-width: 60px;
|
||||
}
|
||||
.recipe-details{
|
||||
grid-area: details;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.recipe-delete{
|
||||
grid-area: delete;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
.recipe-delete i{
|
||||
font-size: 18px;
|
||||
}
|
||||
.recipe-title{
|
||||
font-weight: bold;
|
||||
}
|
||||
.recipe-ingredients{
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/* form-styles */
|
||||
.add-btn{
|
||||
background: var(--title) !important;
|
||||
}
|
||||
input{
|
||||
box-shadow: none !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
}
|
||||
.side-form button{
|
||||
background: var(--title);
|
||||
box-shadow: 1px 1px 3px rgba(90,90,90,0.2);
|
||||
}
|
||||
form .input-field{
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
if('serviceWorker' in navigator){
|
||||
navigator.serviceWorker.register('/static/sw.js')
|
||||
.then(reg => console.log('service worker registered', reg))
|
||||
.catch(err => console.log('service worker not registered', err));
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
const dbconnect = window.indexedDB.open('temps', 1);
|
||||
var db = null
|
||||
|
||||
const interval = setInterval(writeTo, 5000);
|
||||
|
||||
|
||||
dbconnect.onupgradeneeded = ev => {
|
||||
console.log('Upgrade DB');
|
||||
const db = ev.target.result;
|
||||
let store = db.createObjectStore('Temperatures', { keyPath: 'timestamp'});
|
||||
store.createIndex("timestamp", "timestamp", { unique: false });
|
||||
store.createIndex("temp", "temp", { unique: false });
|
||||
|
||||
store = db.createObjectStore('Humidities', { keyPath: 'timestamp'});
|
||||
store.createIndex("timestamp", "timestamp", { unique: false });
|
||||
store.createIndex("humidity", "humidity", { unique: false });
|
||||
}
|
||||
|
||||
|
||||
dbconnect.onsuccess = ev => {
|
||||
console.log('DB-Upgrade erfolgreich');
|
||||
db = ev.target.result;
|
||||
|
||||
}
|
||||
|
||||
function write(type, data){
|
||||
const transaction = db.transaction(type, 'readwrite');
|
||||
const store = transaction.objectStore(type);
|
||||
|
||||
data.forEach(el => store.add(el));
|
||||
|
||||
transaction.onerror = ev => {
|
||||
console.error('Ein Fehler ist aufgetreten!', ev.target.error.message);
|
||||
};
|
||||
|
||||
transaction.oncomplete = ev => {
|
||||
//console.log('Daten wurden erfolgreich hinzugefügt! ' + type);
|
||||
const store = db.transaction(type, 'readonly').objectStore(type);
|
||||
//const query = store.get(1); // Einzel-Query
|
||||
const query = store.openCursor()
|
||||
query.onerror = ev => {
|
||||
console.error('Anfrage fehlgeschlagen!', ev.target.error.message);
|
||||
};
|
||||
|
||||
query.onsuccess = ev => {
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function writeTo(){
|
||||
|
||||
getJSON("/stats",
|
||||
function (error, data) {
|
||||
let date = new Date()
|
||||
let time = date.getTime()
|
||||
let writeData = [
|
||||
{timestamp: time, temp: data["temperature"]}
|
||||
];
|
||||
|
||||
write("Temperatures", writeData)
|
||||
|
||||
writeData = [
|
||||
{timestamp: time, temp: data["humidity"]}
|
||||
];
|
||||
|
||||
write("Humidities", writeData)
|
||||
},
|
||||
function(){
|
||||
console.log("Error while getting temps")
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// TODO: replace with fetch
|
||||
function getJSON(url, callback, fallback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'json';
|
||||
xhr.onload = function () {
|
||||
var status = xhr.status;
|
||||
if (status < 400) {
|
||||
callback(null, xhr.response);
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
|
||||
function readLast(n){
|
||||
let objectStore = db.transaction("Temperatures").objectStore("Temperatures");
|
||||
let data = [];
|
||||
|
||||
objectStore.openCursor(null, "prev").onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if (cursor && data.length < n) {
|
||||
//console.log(cursor.value);
|
||||
data.push(cursor.value)
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
return data
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"name": "Thermo",
|
||||
"short_name": "thermo",
|
||||
"start_url": "/index.html",
|
||||
"display": "standalone",
|
||||
"background_color": "#FFE9D2",
|
||||
"theme_color": "#FFE1C4",
|
||||
"orientation": "portrait-primary",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/img/icons/icon-72x72.png",
|
||||
"type": "image/png",
|
||||
"sizes": "72x72"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icons/icon-96x96.png",
|
||||
"type": "image/png",
|
||||
"sizes": "96x96"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icons/icon-128x128.png",
|
||||
"type": "image/png",
|
||||
"sizes": "128x128"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icons/icon-144x144.png",
|
||||
"type": "image/png",
|
||||
"sizes": "144x144"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icons/icon-152x152.png",
|
||||
"type": "image/png",
|
||||
"sizes": "152x152"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icons/icon-192x192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icons/icon-384x384.png",
|
||||
"type": "image/png",
|
||||
"sizes": "384x384"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icons/icon-512x512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
// install event
|
||||
self.addEventListener('install', evt => {
|
||||
console.log('service worker installed');
|
||||
});
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>thermo</title>
|
||||
<link type="text/css" href="/static/css/styles.css" rel="stylesheet">
|
||||
|
||||
<link rel="manifest" href="/static/manifest.json">
|
||||
<!-- ios support -->
|
||||
<link rel="apple-touch-icon" href="/static/img/icons/icon-96x96.png">
|
||||
<meta name="apple-mobile-web-app-status-bar" content="#FFE1C4">
|
||||
</head>
|
||||
<body class="grey lighten-4">
|
||||
<script src="/static/js/app.js"></script>
|
||||
<script src="/static/js/ui.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>thermo</title>
|
||||
<link type="text/css" href="/static/css/styles.css" rel="stylesheet">
|
||||
<!-- ios support -->
|
||||
<link rel="apple-touch-icon" href="/static/img/icons/icon-96x96.png">
|
||||
<meta name="apple-mobile-web-app-status-bar" content="#FFE1C4">
|
||||
</head>
|
||||
<body class="grey lighten-4">
|
||||
<canvas id="line-chart" width="800" height="450"></canvas>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
|
||||
<script>
|
||||
//https://www.chartjs.org/docs/latest/developers/updates.html
|
||||
new Chart(document.getElementById("line-chart"), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050],
|
||||
datasets: [{
|
||||
data: [86,114,106,106,107,111,133,221,783,2478],
|
||||
label: "Temperature",
|
||||
borderColor: "#3e95cd",
|
||||
fill: false
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Climate'
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="/static/js/ui.js"></script>
|
||||
</body>
|
||||
</html>
|
||||