Finished?!
This commit is contained in:
2
climate-pinger.py
Normal file → Executable file
2
climate-pinger.py
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/local/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import adafruit_dht
|
import adafruit_dht
|
||||||
import mh_z19
|
import mh_z19
|
||||||
|
|||||||
BIN
climate-ranger
Executable file
BIN
climate-ranger
Executable file
Binary file not shown.
BIN
climate-server
BIN
climate-server
Binary file not shown.
6
go.mod
6
go.mod
@@ -1,12 +1,8 @@
|
|||||||
module djledda.de/ledda/climate-server
|
module climate-ranger
|
||||||
|
|
||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Freeaqingme/golang-sql-driver-mysql v1.0.3
|
github.com/Freeaqingme/golang-sql-driver-mysql v1.0.3
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
|
||||||
github.com/gorilla/handlers v1.5.1
|
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.4.1
|
|
||||||
github.com/warthog618/gpio v1.0.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
50
main.go
Normal file
50
main.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DEBUG = true
|
||||||
|
|
||||||
|
var mainLogger *log.Logger
|
||||||
|
var pythonLogger *log.Logger
|
||||||
|
var logFile *os.File
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := setup()
|
||||||
|
defer teardown()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
quitSnapshot := make(chan int, 1)
|
||||||
|
go saveSnapshotOnInterval(1, quitSnapshot)
|
||||||
|
startServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup() error {
|
||||||
|
err := InitDb()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logFile, err = os.OpenFile("./server-log", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Logger failed to initialise, this session will not be logged: ", err.Error())
|
||||||
|
mainLogger = log.New(os.Stdout, "Main: ", 0)
|
||||||
|
pythonLogger = log.New(os.Stdout, "Python Process: ", 0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mainLogger = log.New(logFile, "Main: ", log.Ldate | log.Ltime | log.Lshortfile)
|
||||||
|
pythonLogger = log.New(logFile, "Python Process: ", log.Ldate | log.Ltime | log.Lshortfile)
|
||||||
|
mainLogger.Printf("Session started")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func teardown() {
|
||||||
|
CloseDb()
|
||||||
|
err := logFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
11516
server-log
Normal file
11516
server-log
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,65 +4,42 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DEBUG = true
|
const ROOT_URL = ""
|
||||||
const ROOT_URL = "climate/"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
err := setup()
|
|
||||||
defer teardown()
|
|
||||||
if err == nil {
|
|
||||||
startServer()
|
|
||||||
} else {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setup() error {
|
|
||||||
err := InitDb()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func teardown() {
|
|
||||||
CloseDb()
|
|
||||||
}
|
|
||||||
|
|
||||||
func startServer() {
|
func startServer() {
|
||||||
port := "8001"
|
port := "8001"
|
||||||
r := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
r.HandleFunc("/", showCharts).Methods("GET")
|
router.HandleFunc("/", showCharts).Methods("GET")
|
||||||
r.HandleFunc("/", showCharts).Methods("GET").Queries("show-minutes", "{[0-9]+}")
|
router.HandleFunc("/", showCharts).Methods("GET").Queries("show-minutes", "{[0-9]+}")
|
||||||
r.HandleFunc("/", saveSnapshot).Methods("POST")
|
router.HandleFunc("/", saveSnapshot).Methods("POST")
|
||||||
r.HandleFunc("/data", sendData).Methods("GET")
|
router.HandleFunc("/data", sendData).Methods("GET")
|
||||||
r.HandleFunc("/data", sendData).Methods("GET").Queries("since", "")
|
router.HandleFunc("/data", sendData).Methods("GET").Queries("since", "")
|
||||||
r.HandleFunc("/data/now", createAndSendSnapshot).Methods("GET")
|
router.HandleFunc("/data/now", createAndSendSnapshot).Methods("GET")
|
||||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("webapp/dist/"))))
|
router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("webapp/dist/"))))
|
||||||
fmt.Printf("Listening on port %s...\n", port)
|
fmt.Printf("Listening on port %s...\n", port)
|
||||||
quitSnapshot := make(chan int, 1)
|
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), router))
|
||||||
go saveSnapshotOnInterval(30, quitSnapshot)
|
|
||||||
log.Fatal(http.ListenAndServe(":"+port, r))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func showCharts(w http.ResponseWriter, r *http.Request) {
|
func showCharts(w http.ResponseWriter, r *http.Request) {
|
||||||
minutes := r.FormValue("show-minutes")
|
minutes := r.FormValue("show-minutes")
|
||||||
if minutes != "" {
|
if minutes != "" {
|
||||||
if _, err := strconv.ParseInt(minutes, 10, 0); err != nil {
|
if _, err := strconv.ParseInt(minutes, 10, 0); err != nil {
|
||||||
http.Redirect(w, r, "/" + ROOT_URL + "/", 303)
|
http.Redirect(w, r, ROOT_URL, 303)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("Hello, anyoneo tehre?")
|
templater, err := template.ParseFiles("webapp/dist/charts.html")
|
||||||
http.ServeFile(w, r, "./webapp/dist/charts.html")
|
if internalErrorOnErr(fmt.Errorf("couldn't parse the template: %w", err), w, r) { return }
|
||||||
|
err = templater.Execute(w, ROOT_URL)
|
||||||
|
internalErrorOnErr(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendData(w http.ResponseWriter, r *http.Request) {
|
func sendData(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -71,7 +48,7 @@ func sendData(w http.ResponseWriter, r *http.Request) {
|
|||||||
if sinceQuery != "" {
|
if sinceQuery != "" {
|
||||||
newDateSince, err := time.Parse(time.RFC3339Nano, sinceQuery)
|
newDateSince, err := time.Parse(time.RFC3339Nano, sinceQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Redirect(w, r, "/" + ROOT_URL + "/", 303)
|
http.Redirect(w, r, ROOT_URL, 303)
|
||||||
}
|
}
|
||||||
dateSince = newDateSince
|
dateSince = newDateSince
|
||||||
}
|
}
|
||||||
@@ -80,7 +57,8 @@ func sendData(w http.ResponseWriter, r *http.Request) {
|
|||||||
json, err := createJsonFromSnapshotRecords(records)
|
json, err := createJsonFromSnapshotRecords(records)
|
||||||
if internalErrorOnErr(fmt.Errorf("couldn't create json from snapshots: %w", err), w, r) { return }
|
if internalErrorOnErr(fmt.Errorf("couldn't create json from snapshots: %w", err), w, r) { return }
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
fmt.Fprintf(w, string(json))
|
_, err = fmt.Fprintf(w, string(json))
|
||||||
|
internalErrorOnErr(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAndSendSnapshot(w http.ResponseWriter, r *http.Request) {
|
func createAndSendSnapshot(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -92,7 +70,8 @@ func createAndSendSnapshot(w http.ResponseWriter, r *http.Request) {
|
|||||||
json, err := createJsonFromSnapshotRecords([]*SnapshotRecord{dbSnapshotRecord})
|
json, err := createJsonFromSnapshotRecords([]*SnapshotRecord{dbSnapshotRecord})
|
||||||
if internalErrorOnErr(fmt.Errorf("couldn't create json from last snapshots: %w", err), w, r) { return }
|
if internalErrorOnErr(fmt.Errorf("couldn't create json from last snapshots: %w", err), w, r) { return }
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
fmt.Fprintf(w, string(json))
|
_, err = fmt.Fprintf(w, string(json))
|
||||||
|
internalErrorOnErr(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveSnapshot(w http.ResponseWriter, r *http.Request) {
|
func saveSnapshot(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -100,22 +79,18 @@ func saveSnapshot(w http.ResponseWriter, r *http.Request) {
|
|||||||
if internalErrorOnErr(fmt.Errorf("couldn't create snapshot from JSON: %w", err), w, r) { return }
|
if internalErrorOnErr(fmt.Errorf("couldn't create snapshot from JSON: %w", err), w, r) { return }
|
||||||
newId, err := writeSnapshotToDb(snapshotSub)
|
newId, err := writeSnapshotToDb(snapshotSub)
|
||||||
if internalErrorOnErr(fmt.Errorf("couldn't submit snapshot into the database: %w", err), w, r) { return }
|
if internalErrorOnErr(fmt.Errorf("couldn't submit snapshot into the database: %w", err), w, r) { return }
|
||||||
fmt.Fprintf(w, "{id: %v}", newId)
|
_, err = fmt.Fprintf(w, "{id: %v}", newId)
|
||||||
|
internalErrorOnErr(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveSnapshotOnInterval(seconds int64, stop chan int) {
|
func saveSnapshotOnInterval(seconds int64, stop chan int) {
|
||||||
var err error = nil
|
|
||||||
for {
|
for {
|
||||||
if err != nil {
|
|
||||||
log.Println(err.Error())
|
|
||||||
}
|
|
||||||
snapshot, err := getSnapshotFromPythonScript()
|
snapshot, err := getSnapshotFromPythonScript()
|
||||||
if err != nil {
|
if err == nil {
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err = writeSnapshotToDb(snapshot)
|
_, err = writeSnapshotToDb(snapshot)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
pythonLogger.Println(err.Error())
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-stop:
|
case <-stop:
|
||||||
@@ -127,16 +102,13 @@ func saveSnapshotOnInterval(seconds int64, stop chan int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getSnapshotFromPythonScript() (*SnapshotSubmission, error) {
|
func getSnapshotFromPythonScript() (*SnapshotSubmission, error) {
|
||||||
process := exec.Command("climate-pinger.py")
|
output, err := exec.Command("./climate-pinger.py").CombinedOutput()
|
||||||
process.Stdout = os.Stdout
|
|
||||||
process.Stderr = os.Stderr
|
|
||||||
err := process.Run()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf(
|
||||||
}
|
"Error:\n\t%s%w",
|
||||||
output, err := process.Output()
|
strings.Replace(string(output), "\n", "\n\t", -1),
|
||||||
if err != nil {
|
err,
|
||||||
return nil, err
|
)
|
||||||
}
|
}
|
||||||
tokens := strings.Split(string(output), "\n")
|
tokens := strings.Split(string(output), "\n")
|
||||||
if len(tokens) != 4 {
|
if len(tokens) != 4 {
|
||||||
@@ -160,10 +132,14 @@ func internalErrorOnErr(err error, w http.ResponseWriter, r *http.Request) bool
|
|||||||
r.URL,
|
r.URL,
|
||||||
err.Error())
|
err.Error())
|
||||||
}
|
}
|
||||||
fmt.Println(errorMessage)
|
logError(errorMessage)
|
||||||
http.Error(w, errorMessage, 500)
|
http.Error(w, errorMessage, 500)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func logError(errorMessage string) {
|
||||||
|
fmt.Println(errorMessage)
|
||||||
|
mainLogger.Println(errorMessage)
|
||||||
|
}
|
||||||
8
webapp/dist/charts.html
vendored
8
webapp/dist/charts.html
vendored
@@ -3,11 +3,11 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Ledda's Room Climate</title>
|
<title>Ledda's Room Climate</title>
|
||||||
<link href="/climate/static/styles.css" rel="stylesheet" />
|
<link href="{{.}}/static/styles.css" rel="stylesheet" />
|
||||||
<script src="/climate/static/main.js"></script>
|
<script src="{{.}}/static/main.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body id="root">
|
||||||
<div class="chart-container" style="position: relative; height:40vh; width:80vw; margin: auto">
|
<div class="chart-container center">
|
||||||
<canvas id="myChart"></canvas>
|
<canvas id="myChart"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</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 {
|
.overlay {
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
transition: opacity, 1s;
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: white;
|
display: flex;
|
||||||
opacity: 50%;
|
margin: auto;
|
||||||
display: table-cell;
|
align-items: center;
|
||||||
text-align: center;
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center > * {
|
||||||
|
align-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
.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 chart: Chart | null;
|
||||||
private latestSnapshot: Snapshot | null;
|
private latestSnapshot: Snapshot | null;
|
||||||
private onLoadedCallback: () => void = () => {};
|
private onLoadedCallback: () => void = () => {};
|
||||||
|
private onErrorCallback: (e: Error) => void = () => {};
|
||||||
constructor(
|
constructor(
|
||||||
private readonly rootUrl: string,
|
private readonly rootUrl: string,
|
||||||
private readonly canvasId: string,
|
private readonly canvasId: string,
|
||||||
@@ -30,21 +31,30 @@ class ClimateChart {
|
|||||||
}
|
}
|
||||||
this.urlEndpoint = this.rootUrl + "data/";
|
this.urlEndpoint = this.rootUrl + "data/";
|
||||||
this.urlEndpoint += "since/" + this.minutesDisplayed;
|
this.urlEndpoint += "since/" + this.minutesDisplayed;
|
||||||
this.initChart()
|
this.initChart().catch((e) => {this.onErrorCallback(e);});
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoaded(callback: () => void) {
|
onLoaded(callback: () => void) {
|
||||||
this.onLoadedCallback = callback;
|
this.onLoadedCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onErrored(callback: (e: Error) => void) {
|
||||||
|
this.onErrorCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
private static isCanvas(el: HTMLElement): el is HTMLCanvasElement {
|
private static isCanvas(el: HTMLElement): el is HTMLCanvasElement {
|
||||||
return el.tagName === "canvas";
|
return el.tagName === "CANVAS";
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getInitialDataBlob(): Promise<SnapshotRecords> {
|
private async getInitialDataBlob(): Promise<SnapshotRecords> {
|
||||||
|
try {
|
||||||
const data = await fetch(this.urlEndpoint);
|
const data = await fetch(this.urlEndpoint);
|
||||||
return await data.json();
|
return await data.json();
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async initChart() {
|
private async initChart() {
|
||||||
const canvasElement = document.getElementById(this.canvasId);
|
const canvasElement = document.getElementById(this.canvasId);
|
||||||
@@ -54,12 +64,18 @@ class ClimateChart {
|
|||||||
} else {
|
} else {
|
||||||
throw new Error(`improper HTML element passed, needed type canvas, got ${canvasElement.tagName}`);
|
throw new Error(`improper HTML element passed, needed type canvas, got ${canvasElement.tagName}`);
|
||||||
}
|
}
|
||||||
|
this.chart = new Chart(ctx, generateClimateChartConfig({}));
|
||||||
|
try {
|
||||||
const payload = await this.getInitialDataBlob();
|
const payload = await this.getInitialDataBlob();
|
||||||
this.latestSnapshot = payload.snapshots[payload.snapshots.length - 1];
|
this.latestSnapshot = payload.snapshots[payload.snapshots.length - 1];
|
||||||
this.chart = new Chart(ctx, generateClimateChartConfig(this.jsonToChartPoints(payload)));
|
this.insertSnapshots(...payload.snapshots);
|
||||||
setInterval(() => this.updateFromServer(), 30 * 1000);
|
setInterval(() => this.updateFromServer(), 30 * 1000);
|
||||||
this.onLoadedCallback();
|
this.onLoadedCallback();
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.onErrorCallback(new Error(`Server error: ${e}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private jsonToChartPoints(json: SnapshotRecords): {humidity: ChartPoint[], temp: ChartPoint[], co2: ChartPoint[]} {
|
private jsonToChartPoints(json: SnapshotRecords): {humidity: ChartPoint[], temp: ChartPoint[], co2: ChartPoint[]} {
|
||||||
const humidity = [];
|
const humidity = [];
|
||||||
@@ -77,6 +93,7 @@ class ClimateChart {
|
|||||||
private async updateFromServer() {
|
private async updateFromServer() {
|
||||||
const currentTime = (new Date(this.latestSnapshot.time)).toISOString();
|
const currentTime = (new Date(this.latestSnapshot.time)).toISOString();
|
||||||
const url = "/" + this.rootUrl + "data?since=" + currentTime;
|
const url = "/" + this.rootUrl + "data?since=" + currentTime;
|
||||||
|
try {
|
||||||
const payload: SnapshotRecords = await (await fetch(url)).json();
|
const payload: SnapshotRecords = await (await fetch(url)).json();
|
||||||
if (payload.snapshots.length > 0) {
|
if (payload.snapshots.length > 0) {
|
||||||
this.latestSnapshot = payload.snapshots[payload.snapshots.length - 1];
|
this.latestSnapshot = payload.snapshots[payload.snapshots.length - 1];
|
||||||
@@ -85,6 +102,10 @@ class ClimateChart {
|
|||||||
this.chart.update();
|
this.chart.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.onErrorCallback(new Error(`Server error: ${e}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private insertSnapshots(...snapshots: Snapshot[]) {
|
private insertSnapshots(...snapshots: Snapshot[]) {
|
||||||
for (const snapshot of snapshots) {
|
for (const snapshot of snapshots) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {ChartConfiguration, ChartPoint, TimeUnit} from "chart.js";
|
import {ChartConfiguration, ChartPoint, TimeUnit} from "chart.js";
|
||||||
|
|
||||||
interface ClimateChartSettings {
|
interface ClimateChartSettings {
|
||||||
humidity: ChartPoint[];
|
humidity?: ChartPoint[];
|
||||||
temp: ChartPoint[];
|
temp?: ChartPoint[];
|
||||||
co2: ChartPoint[];
|
co2?: ChartPoint[];
|
||||||
colors?: {
|
colors?: {
|
||||||
humidity?: string;
|
humidity?: string;
|
||||||
temp?: string;
|
temp?: string;
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import ClimateChart from "./ClimateChart";
|
import ClimateChart from "./ClimateChart";
|
||||||
|
|
||||||
const ROOT_URL: string = "climate/";
|
|
||||||
const CHART_DOM_ID: string = "myChart";
|
const CHART_DOM_ID: string = "myChart";
|
||||||
|
let rootUrl: string = "";
|
||||||
|
|
||||||
function createClimateChart() {
|
function createClimateChart() {
|
||||||
const pathname = window.location.pathname;
|
const pathname = window.location.pathname;
|
||||||
|
if (pathname !== "/") {
|
||||||
|
rootUrl += pathname.match(/\/[^?\s]*/)[0];
|
||||||
|
}
|
||||||
let minutesDisplayed = 60;
|
let minutesDisplayed = 60;
|
||||||
const argsStart = pathname.search(/\?minute-span=/);
|
const argsStart = pathname.search(/\?minute-span=/);
|
||||||
if (argsStart !== -1) {
|
if (argsStart !== -1) {
|
||||||
@@ -13,15 +16,24 @@ function createClimateChart() {
|
|||||||
minutesDisplayed = parsedMins;
|
minutesDisplayed = parsedMins;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ClimateChart(ROOT_URL, CHART_DOM_ID, minutesDisplayed);
|
return new ClimateChart(rootUrl, CHART_DOM_ID, minutesDisplayed);
|
||||||
}
|
}
|
||||||
|
|
||||||
const overlay = document.createElement('div');
|
const overlay = document.createElement('div');
|
||||||
overlay.innerText = 'Loading data...';
|
overlay.classList.add('overlay', 'center');
|
||||||
overlay.className = 'overlay';
|
const textContainer = document.createElement('span');
|
||||||
document.getRootNode().appendChild(overlay);
|
textContainer.innerText = 'Loading data...';
|
||||||
|
overlay.appendChild(textContainer);
|
||||||
|
|
||||||
const climateChart = createClimateChart();
|
document.onreadystatechange = (e) => {
|
||||||
climateChart.onLoaded(() => {
|
document.getElementById("root").appendChild(overlay);
|
||||||
|
const climateChart = createClimateChart();
|
||||||
|
climateChart.onLoaded(() => {
|
||||||
overlay.classList.add('hidden');
|
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