flash.moe/public/temp.php
2022-02-04 04:25:57 +00:00

331 lines
14 KiB
PHP

<?php
define('FM_TEMP_KEY', 'kND861svbydCLywutu78tRmlpWdzoRLPcVZSrnxerh3KbLwfwvfvgC5hzax8gvYm');
define('FM_TEMP_INT', 10);
ini_set('display_errors', 'on');
error_reporting(-1);
date_default_timezone_set('UTC');
mb_internal_encoding('UTF-8');
try {
$pdo = new PDO('mysql:unix_socket=/var/run/mysqld/mysqld.sock;dbname=website;charset=utf8mb4', 'website', 'A3NjVvHRkHAxiYgk8MM4ZrCwrLVyPIYX', [
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "
SET SESSION
sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION',
time_zone = '+00:00';
",
]);
} catch(Exception $ex) {
http_response_code(500);
echo '<h3>Unable to connect to database</h3>';
die($ex->getMessage());
}
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(isset($_GET['since_3mo'])) {
$since = (int)filter_input(INPUT_GET, 'since_3mo', FILTER_SANITIZE_NUMBER_INT);
$temps = $pdo->prepare('SELECT MAX(`temp_celcius`) AS `temp_celcius_max`, AVG(`temp_celcius`) AS `temp_celcius_avg`, MIN(`temp_celcius`) AS `temp_celcius_min`, UNIX_TIMESTAMP(DATE(`temp_datetime`)) AS `temp_datetime` FROM `fm_temperature` WHERE `temp_datetime` > DATE(FROM_UNIXTIME(:since)) AND `temp_datetime` > NOW() - INTERVAL 3 MONTH GROUP BY DATE(`temp_datetime`) ORDER BY `temp_datetime` ASC LIMIT 92');
$temps->bindValue('since', $since);
$temps->execute();
header('Content-Type: application/json; charset=utf-8');
echo json_encode($temps->fetchAll(PDO::FETCH_ASSOC));
return;
}
if(isset($_GET['since_6mo'])) {
$since = (int)filter_input(INPUT_GET, 'since_6mo', FILTER_SANITIZE_NUMBER_INT);
$temps = $pdo->prepare('SELECT MAX(`temp_celcius`) AS `temp_celcius_max`, AVG(`temp_celcius`) AS `temp_celcius_avg`, MIN(`temp_celcius`) AS `temp_celcius_min`, UNIX_TIMESTAMP(DATE(`temp_datetime`)) AS `temp_datetime` FROM `fm_temperature` WHERE `temp_datetime` > DATE(FROM_UNIXTIME(:since)) AND `temp_datetime` > NOW() - INTERVAL 6 MONTH GROUP BY DATE(`temp_datetime`) ORDER BY `temp_datetime` ASC LIMIT 92');
$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);
date_default_timezone_set('Europe/Amsterdam');
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 class="chart">
<canvas id="-chart-mo" 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,
lastMo: 0,
history: [],
historyMo: [],
lastTemp: null,
lastDateTime: null,
chart: null,
chartMo: 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();
};
temp.refreshMo = 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.lastMo = temp.temp_datetime;
this.historyMo.push(temp);
}
this.refreshUIMo();
}.bind(this);
xhr.open('GET', '/temp.php?since_6mo=' + encodeURIComponent(parseInt(this.lastMo).toString()));
xhr.send();
};
temp.refreshUIMo = function() {
var take = Math.min(92, this.historyMo.length),
datasetMax = this.chartMo.data.datasets[0],
datasetAvg = this.chartMo.data.datasets[1],
datasetMin = this.chartMo.data.datasets[2];
this.chart.data.labels = [];
datasetMax.data = [];
datasetAvg.data = [];
datasetMin.data = [];
for(var i = this.historyMo.length - take; i < this.historyMo.length; ++i) {
var temp = this.historyMo[i];
this.chartMo.data.labels.push(new Date(temp.temp_datetime * 1000).toDateString());
datasetMax.data.push(temp.temp_celcius_max);
datasetAvg.data.push(temp.temp_celcius_avg);
datasetMin.data.push(temp.temp_celcius_min);
}
this.chartMo.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);
var ctxMo = document.getElementById('-chart-mo').getContext('2d');
temp.chartMo = new Chart(ctxMo, {
type: 'line',
data: {
datasets: [
{
label: 'Maximum Temperature',
data: [],
borderColor: '#400',
backgroundColor: '#800',
},
{
label: 'Average Temperature',
data: [],
borderColor: '#040',
backgroundColor: '#080',
},
{
label: 'Minimum Temperature',
data: [],
borderColor: '#004',
backgroundColor: '#008',
},
],
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Daily Temperature (6 months)',
},
},
scales: {
y: {
suggestedMax: 34,
suggestedMin: 20,
ticks: {
stepSize: 1,
},
},
},
},
});
temp.refreshMo();
setInterval(temp.refreshMo.bind(temp), 31 * 24 * 60 * 1000);
};
</script>
</body>
</html>