201 lines
7.7 KiB
PHP
201 lines
7.7 KiB
PHP
|
<?php
|
||
|
require_once __DIR__ . '/_v4/includes.php';
|
||
|
|
||
|
define('FM_TEMP_KEY', 'kND861svbydCLywutu78tRmlpWdzoRLPcVZSrnxerh3KbLwfwvfvgC5hzax8gvYm');
|
||
|
define('FM_TEMP_INT', 10);
|
||
|
|
||
|
ini_set('display_errors', 'on');
|
||
|
error_reporting(-1);
|
||
|
|
||
|
if(isset($_POST['temp']) && isset($_POST['hash']) && isset($_POST['time'])) {
|
||
|
$temp = (string)filter_input(INPUT_POST, 'temp', FILTER_SANITIZE_STRING);
|
||
|
$hash = (string)filter_input(INPUT_POST, 'hash', FILTER_SANITIZE_STRING);
|
||
|
$time = (string)filter_input(INPUT_POST, 'time', FILTER_SANITIZE_NUMBER_INT);
|
||
|
|
||
|
if(!hash_equals(hash_hmac('sha256', $temp . '|' . $time, FM_TEMP_KEY), $hash))
|
||
|
return;
|
||
|
|
||
|
$time = floor((int)json_decode($time) / FM_TEMP_INT);
|
||
|
if($time !== floor(time() / FM_TEMP_INT))
|
||
|
return;
|
||
|
|
||
|
$insert = $pdo->prepare('INSERT INTO `fm_temperature` (`temp_celcius`) VALUES (:temp)');
|
||
|
$insert->bindValue('temp', (string)json_decode($temp));
|
||
|
$insert->execute();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(isset($_GET['since'])) {
|
||
|
$since = (int)filter_input(INPUT_GET, 'since', FILTER_SANITIZE_NUMBER_INT);
|
||
|
$temps = $pdo->prepare('SELECT `temp_celcius`, UNIX_TIMESTAMP(`temp_datetime`) AS `temp_datetime` FROM `fm_temperature` WHERE `temp_datetime` > FROM_UNIXTIME(:since) AND `temp_datetime` > NOW() - INTERVAL 1 DAY ORDER BY `temp_datetime` ASC LIMIT 288');
|
||
|
$temps->bindValue('since', $since);
|
||
|
$temps->execute();
|
||
|
header('Content-Type: application/json; charset=utf-8');
|
||
|
echo json_encode($temps->fetchAll(PDO::FETCH_ASSOC));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!empty($_GET['siri'])) {
|
||
|
header('Content-Type: text/plain; charset=utf-8');
|
||
|
$temps = $pdo->prepare('SELECT `temp_celcius`, UNIX_TIMESTAMP(`temp_datetime`) AS `temp_datetime` FROM `fm_temperature` ORDER BY `temp_datetime` DESC LIMIT 1');
|
||
|
$temps->execute();
|
||
|
$temps = $temps->fetch(PDO::FETCH_ASSOC);
|
||
|
printf('It was %2$.1f°C at %1$s.', date('H:i:s', $temps['temp_datetime']), $temps['temp_celcius']);
|
||
|
return;
|
||
|
}
|
||
|
?>
|
||
|
<!doctype html>
|
||
|
<html>
|
||
|
<head>
|
||
|
<meta charset="utf-8"/>
|
||
|
<title>Room Temperature</title>
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||
|
<link href="/css/electrolize/style.css" type="text/css" rel="stylesheet"/>
|
||
|
<style type="text/css">
|
||
|
* {
|
||
|
margin: 0;
|
||
|
padding: 0;
|
||
|
box-sizing: border-box;
|
||
|
position: relative;
|
||
|
outline-style: none;
|
||
|
}
|
||
|
html, body {
|
||
|
width: 100%;
|
||
|
height: 100%;
|
||
|
}
|
||
|
body {
|
||
|
background-color: #111;
|
||
|
color: #fff;
|
||
|
font: 12px/20px Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif;
|
||
|
padding: 1px;
|
||
|
}
|
||
|
.current {
|
||
|
text-align: center;
|
||
|
background-color: #333;
|
||
|
margin: 10px;
|
||
|
padding: 10px;
|
||
|
border-radius: 10px;
|
||
|
}
|
||
|
.current-temp {
|
||
|
font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif;
|
||
|
font-size: 3em;
|
||
|
line-height: 1.2em;
|
||
|
}
|
||
|
.current-datetime {
|
||
|
font-size: .9em;
|
||
|
line-height: 1.4em;
|
||
|
}
|
||
|
.chart {
|
||
|
background-color: #ddd;
|
||
|
margin: 10px;
|
||
|
padding: 10px;
|
||
|
border-radius: 10px;
|
||
|
}
|
||
|
</style>
|
||
|
</head>
|
||
|
<body>
|
||
|
<div class="main">
|
||
|
<div class="current">
|
||
|
<div class="current-temp">
|
||
|
<span id="-last-temp">--</span> °C
|
||
|
</div>
|
||
|
<div class="current-datetime">
|
||
|
<span id="-last-datetime">--:--:--</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="chart">
|
||
|
<canvas id="-chart" width="400" height="170"></canvas>
|
||
|
</div>
|
||
|
</div>
|
||
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/chart.js@3.3.2/dist/chart.min.js"></script>
|
||
|
<script type="text/javascript">
|
||
|
var temp = {
|
||
|
last: 0,
|
||
|
history: [],
|
||
|
lastTemp: null,
|
||
|
lastDateTime: null,
|
||
|
chart: null,
|
||
|
};
|
||
|
temp.getLastTemperature = function() {
|
||
|
if(this.history.length === 0)
|
||
|
return {temp_datetime: 0, temp_celcius: 0};
|
||
|
return this.history[this.history.length - 1];
|
||
|
};
|
||
|
temp.refresh = function() {
|
||
|
var xhr = new XMLHttpRequest;
|
||
|
xhr.onload = function() {
|
||
|
var temps = JSON.parse(xhr.responseText);
|
||
|
for(var i = 0; i < temps.length; ++i) {
|
||
|
var temp = temps[i];
|
||
|
this.last = temp.temp_datetime;
|
||
|
this.history.push(temp);
|
||
|
}
|
||
|
this.refreshUI();
|
||
|
}.bind(this);
|
||
|
xhr.open('GET', '/temp.php?since=' + encodeURIComponent(parseInt(this.last).toString()));
|
||
|
xhr.send();
|
||
|
};
|
||
|
temp.refreshUI = function() {
|
||
|
var temp = this.getLastTemperature();
|
||
|
this.lastTemp.textContent = (parseInt(temp.temp_celcius * 10) / 10).toFixed(1).toLocaleString();
|
||
|
this.lastDateTime.textContent = new Date(temp.temp_datetime * 1000).toLocaleString();
|
||
|
|
||
|
var take = Math.min(288, this.history.length),
|
||
|
dataset = this.chart.data.datasets[0];
|
||
|
this.chart.data.labels = [];
|
||
|
dataset.data = [];
|
||
|
for(var i = this.history.length - take; i < this.history.length; ++i) {
|
||
|
var temp = this.history[i];
|
||
|
this.chart.data.labels.push(new Date(temp.temp_datetime * 1000).toLocaleString());
|
||
|
dataset.data.push(temp.temp_celcius);
|
||
|
}
|
||
|
|
||
|
this.chart.update();
|
||
|
};
|
||
|
window.onload = function() {
|
||
|
temp.lastTemp = document.getElementById('-last-temp');
|
||
|
temp.lastDateTime = document.getElementById('-last-datetime');
|
||
|
|
||
|
var ctx = document.getElementById('-chart').getContext('2d');
|
||
|
temp.chart = new Chart(ctx, {
|
||
|
type: 'line',
|
||
|
data: {
|
||
|
datasets: [
|
||
|
{
|
||
|
label: 'Temperature',
|
||
|
data: [],
|
||
|
borderColor: '#111',
|
||
|
backgroundColor: '#222',
|
||
|
},
|
||
|
],
|
||
|
},
|
||
|
options: {
|
||
|
responsive: true,
|
||
|
plugins: {
|
||
|
legend: {
|
||
|
position: 'top',
|
||
|
},
|
||
|
title: {
|
||
|
display: true,
|
||
|
text: 'Temperature History',
|
||
|
},
|
||
|
},
|
||
|
scales: {
|
||
|
y: {
|
||
|
suggestedMax: 34,
|
||
|
suggestedMin: 20,
|
||
|
ticks: {
|
||
|
stepSize: 1,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
});
|
||
|
|
||
|
temp.refresh();
|
||
|
setInterval(temp.refresh.bind(temp), 5 * 60 * 1000);
|
||
|
};
|
||
|
</script>
|
||
|
</body>
|
||
|
</html>
|