Finished?!
This commit is contained in:
8
webapp/dist/charts.html
vendored
8
webapp/dist/charts.html
vendored
@@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ledda's Room Climate</title>
|
||||
<link href="/climate/static/styles.css" rel="stylesheet" />
|
||||
<script src="/climate/static/main.js"></script>
|
||||
<link href="{{.}}/static/styles.css" rel="stylesheet" />
|
||||
<script src="{{.}}/static/main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="chart-container" style="position: relative; height:40vh; width:80vw; margin: auto">
|
||||
<body id="root">
|
||||
<div class="chart-container center">
|
||||
<canvas id="myChart"></canvas>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
4
webapp/dist/main.js
vendored
4
webapp/dist/main.js
vendored
File diff suppressed because one or more lines are too long
31
webapp/dist/styles.css
vendored
31
webapp/dist/styles.css
vendored
@@ -1,15 +1,36 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
transition: opacity, 1s;
|
||||
opacity: 100%;
|
||||
}
|
||||
|
||||
.center {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
opacity: 50%;
|
||||
display: table-cell;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
margin: auto;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.center > * {
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
opacity: 0%;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
padding: 10vw;
|
||||
width: calc(100% - 20vw);
|
||||
height: calc(100% - 20vw);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ class ClimateChart {
|
||||
private chart: Chart | null;
|
||||
private latestSnapshot: Snapshot | null;
|
||||
private onLoadedCallback: () => void = () => {};
|
||||
private onErrorCallback: (e: Error) => void = () => {};
|
||||
constructor(
|
||||
private readonly rootUrl: string,
|
||||
private readonly canvasId: string,
|
||||
@@ -30,20 +31,29 @@ class ClimateChart {
|
||||
}
|
||||
this.urlEndpoint = this.rootUrl + "data/";
|
||||
this.urlEndpoint += "since/" + this.minutesDisplayed;
|
||||
this.initChart()
|
||||
this.initChart().catch((e) => {this.onErrorCallback(e);});
|
||||
}
|
||||
|
||||
onLoaded(callback: () => void) {
|
||||
this.onLoadedCallback = callback;
|
||||
}
|
||||
|
||||
onErrored(callback: (e: Error) => void) {
|
||||
this.onErrorCallback = callback;
|
||||
}
|
||||
|
||||
private static isCanvas(el: HTMLElement): el is HTMLCanvasElement {
|
||||
return el.tagName === "canvas";
|
||||
return el.tagName === "CANVAS";
|
||||
}
|
||||
|
||||
private async getInitialDataBlob(): Promise<SnapshotRecords> {
|
||||
const data = await fetch(this.urlEndpoint);
|
||||
return await data.json();
|
||||
try {
|
||||
const data = await fetch(this.urlEndpoint);
|
||||
return await data.json();
|
||||
}
|
||||
catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private async initChart() {
|
||||
@@ -54,11 +64,17 @@ class ClimateChart {
|
||||
} else {
|
||||
throw new Error(`improper HTML element passed, needed type canvas, got ${canvasElement.tagName}`);
|
||||
}
|
||||
const payload = await this.getInitialDataBlob();
|
||||
this.latestSnapshot = payload.snapshots[payload.snapshots.length - 1];
|
||||
this.chart = new Chart(ctx, generateClimateChartConfig(this.jsonToChartPoints(payload)));
|
||||
setInterval(() => this.updateFromServer(), 30 * 1000);
|
||||
this.onLoadedCallback();
|
||||
this.chart = new Chart(ctx, generateClimateChartConfig({}));
|
||||
try {
|
||||
const payload = await this.getInitialDataBlob();
|
||||
this.latestSnapshot = payload.snapshots[payload.snapshots.length - 1];
|
||||
this.insertSnapshots(...payload.snapshots);
|
||||
setInterval(() => this.updateFromServer(), 30 * 1000);
|
||||
this.onLoadedCallback();
|
||||
}
|
||||
catch (e) {
|
||||
this.onErrorCallback(new Error(`Server error: ${e}`));
|
||||
}
|
||||
}
|
||||
|
||||
private jsonToChartPoints(json: SnapshotRecords): {humidity: ChartPoint[], temp: ChartPoint[], co2: ChartPoint[]} {
|
||||
@@ -77,12 +93,17 @@ class ClimateChart {
|
||||
private async updateFromServer() {
|
||||
const currentTime = (new Date(this.latestSnapshot.time)).toISOString();
|
||||
const url = "/" + this.rootUrl + "data?since=" + currentTime;
|
||||
const payload: SnapshotRecords = await (await fetch(url)).json();
|
||||
if (payload.snapshots.length > 0) {
|
||||
this.latestSnapshot = payload.snapshots[payload.snapshots.length - 1];
|
||||
this.removeExpiredData(currentTime);
|
||||
this.insertSnapshots(...payload.snapshots);
|
||||
this.chart.update();
|
||||
try {
|
||||
const payload: SnapshotRecords = await (await fetch(url)).json();
|
||||
if (payload.snapshots.length > 0) {
|
||||
this.latestSnapshot = payload.snapshots[payload.snapshots.length - 1];
|
||||
this.removeExpiredData(currentTime);
|
||||
this.insertSnapshots(...payload.snapshots);
|
||||
this.chart.update();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.onErrorCallback(new Error(`Server error: ${e}`));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {ChartConfiguration, ChartPoint, TimeUnit} from "chart.js";
|
||||
|
||||
interface ClimateChartSettings {
|
||||
humidity: ChartPoint[];
|
||||
temp: ChartPoint[];
|
||||
co2: ChartPoint[];
|
||||
humidity?: ChartPoint[];
|
||||
temp?: ChartPoint[];
|
||||
co2?: ChartPoint[];
|
||||
colors?: {
|
||||
humidity?: string;
|
||||
temp?: string;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import ClimateChart from "./ClimateChart";
|
||||
|
||||
const ROOT_URL: string = "climate/";
|
||||
const CHART_DOM_ID: string = "myChart";
|
||||
let rootUrl: string = "";
|
||||
|
||||
function createClimateChart() {
|
||||
const pathname = window.location.pathname;
|
||||
if (pathname !== "/") {
|
||||
rootUrl += pathname.match(/\/[^?\s]*/)[0];
|
||||
}
|
||||
let minutesDisplayed = 60;
|
||||
const argsStart = pathname.search(/\?minute-span=/);
|
||||
if (argsStart !== -1) {
|
||||
@@ -13,15 +16,24 @@ function createClimateChart() {
|
||||
minutesDisplayed = parsedMins;
|
||||
}
|
||||
}
|
||||
return new ClimateChart(ROOT_URL, CHART_DOM_ID, minutesDisplayed);
|
||||
return new ClimateChart(rootUrl, CHART_DOM_ID, minutesDisplayed);
|
||||
}
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.innerText = 'Loading data...';
|
||||
overlay.className = 'overlay';
|
||||
document.getRootNode().appendChild(overlay);
|
||||
overlay.classList.add('overlay', 'center');
|
||||
const textContainer = document.createElement('span');
|
||||
textContainer.innerText = 'Loading data...';
|
||||
overlay.appendChild(textContainer);
|
||||
|
||||
const climateChart = createClimateChart();
|
||||
climateChart.onLoaded(() => {
|
||||
overlay.classList.add('hidden');
|
||||
})
|
||||
document.onreadystatechange = (e) => {
|
||||
document.getElementById("root").appendChild(overlay);
|
||||
const climateChart = createClimateChart();
|
||||
climateChart.onLoaded(() => {
|
||||
overlay.classList.add('hidden');
|
||||
});
|
||||
climateChart.onErrored((e) => {
|
||||
overlay.classList.remove('hidden');
|
||||
textContainer.innerText = `An error occurred: ${e}\nTry restarting the page.`;
|
||||
});
|
||||
document.onreadystatechange = () => {};
|
||||
};
|
||||
Reference in New Issue
Block a user