Files
floorbell/static/main.html

207 lines
5.8 KiB
HTML

<!DOCTYPE html>
<html lang="en" onclick="document.getElementById('unmute').style.display = 'none';">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Fáfnir Doorbell</title>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous"></script>
<style>
body {
background-color: #33194d;
color: white;
}
.flash {
--flash-half-count: 60;
--flash-half-duration: .5s;
animation-delay: 0s,
var(--flash-half-duration),
calc( (var(--flash-half-count) - 1) * var(--flash-half-duration) );
animation-duration: var(--flash-half-duration);
animation-name: fade, flashing, fade;
animation-timing-function: ease-in, ease-in-out, ease-in;
animation-iteration-count: 1, calc(var(--flash-half-count) - 2), 1;
animation-direction: normal, alternate, reverse;
animation-play-state: paused;
}
@keyframes fade {
from {
background-color: #33194d;
}
to {
background-color: orange;
}
}
@keyframes flashing {
from {
background-color: orange;
}
to {
background-color: rebeccapurple;
}
}
#status_box {
background-color: black;
margin: 3em auto;
width: 30%;
border-style: ridge;
border-color: yellow;
border-width: 5px;
padding: 15px;
text-align: center;
}
#status_box table {
text-align: left;
}
#status_box th {
padding-right: 2em;
}
.status {
font-family: monospace;
font-size: 250%;
font-variant: small-caps;
}
.connected {
color: green;
}
.disconnected {
color: red;
}
#unmute {
display: none;
min-width: 60%;
}
</style>
</head>
<body class="flash">
<section id="status_box">
<table>
<tr>
<th>Status:</th>
<td id="status_line"></td>
</tr><tr>
<th>Last Ring:</th>
<td id="last_ring" class="status"></td>
</tr>
</table>
<button id="unmute">Click Anywhere To Enable Sound</button>
</section>
<audio id="chime" src="indoor.mp3"></audio>
<audio id="beep_seek" preload="auto" src="disconnected.mp3"></audio>
<audio id="beep_conn" src="connected.mp3"></audio>
<template id="connected">
<span class="status connected">Connected</span>
</template>
<template id="disconnected">
<span class="status disconnected">Disconnected</span>
</template>
<script>
"use strict";
const CONNECT_TIMEOUT = 1_000;
const KEEPALIVE_TIMEOUT = 10_000;
let sse;
let reconnect;
let ping;
document.getElementById('beep_seek').load();
connect();
function connect() {
console.log('connecting');
set_status('disconnected');
if (sse) {
sse.onopen = undefined;
sse.close();
}
if (ping) { clearTimeout(ping); }
sse = new EventSource('events');
let beep_seek = document.getElementById('beep_seek');
beep_seek.play().catch(() => { $('#unmute').show() }); // Need user interatction for sound
sse.onopen = () => {
console.log('connected');
set_status('connected');
let beep_conn = document.getElementById('beep_conn');
beep_conn.play();
clearTimeout(reconnect);
keepalive();
sse.onerror = () => {
console.log('sse error');
connect();
};
sse.addEventListener('ping', msg => {
clearTimeout(ping);
keepalive();
});
sse.addEventListener('ring', msg => {
let chime = document.getElementById('chime');
if (chime.paused) {
chime.play();
}
let now = new Date();
let hours = now.getHours();
let hours12 = (hours + 11) % 12 + 1;
let mins = now.getMinutes();
mins = (mins < 10) ? '0' + mins : mins;
let secs = now.getSeconds();
secs = (secs < 10) ? '0' + secs : secs;
let demidiemity = (hours / 12)|0 ? 'PM' : 'AM';
$('#last_ring').text(`${hours12}:${mins}:${secs} ${demidiemity}`);
// CSS Animations don't provide a way to restart them.
// Instead, we can temporarily override the animation property to 'none' then the
// animation will restart when we re-inheirit from the stylesheet.
let flashers = $('.flash').each((_, e) => {
e.style.animation = '';
e.style.animationPlayState = 'running';
let end_count = 0; // Each sub-animation fires its own event
let $e = $(e);
$e.off();
$e.on('animationend',
ev => { if (++end_count == 3 ) ev.target.style.animation = 'none paused'});
});
});
};
reconnect = setTimeout(connect, CONNECT_TIMEOUT);
}
function get_template(name) {
return $('#' + name).map((_, n) => n.content).clone();
}
function keepalive() {
ping = setTimeout(() => {
console.warn('keepalive ping timed out');
connect();
}, KEEPALIVE_TIMEOUT);
}
function set_status(status_template) {
const template = get_template(status_template);
const status_line = $('#status_line');
status_line.empty();
status_line.append(template);
}
</script>
</body>
</html>