started to work on dashboard
This commit is contained in:
parent
6bc5fba318
commit
9c619a9538
4 changed files with 273 additions and 41 deletions
|
@ -120,3 +120,11 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
.opacity-animated {
|
||||||
|
-webkit-transition: opacity 1s; /* For Safari 3.1 to 6.0 */
|
||||||
|
transition: opacity 1s;
|
||||||
|
}
|
|
@ -44,7 +44,7 @@
|
||||||
<div class="sidebar-sticky">
|
<div class="sidebar-sticky">
|
||||||
<ul class="nav flex-column">
|
<ul class="nav flex-column">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" id="navbarPage-1Link" onclick="setPage(-1)" href="#">
|
<a class="nav-link" id="navbarPage-1Link" onclick="dashboard.updateParams({page: -1})" href="#">
|
||||||
<span data-feather="home"></span>
|
<span data-feather="home"></span>
|
||||||
Dashboard <span class="sr-only">(current)</span>
|
Dashboard <span class="sr-only">(current)</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -108,32 +108,50 @@
|
||||||
<div
|
<div
|
||||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
<h1 class="h2" id="titleH1">Dashboard</h1>
|
<h1 class="h2" id="titleH1">Dashboard</h1>
|
||||||
<div class="btn-toolbar mb-2 mb-md-0">
|
<div class="btn-toolbar mb-2 mb-md-0" role="toolbar">
|
||||||
<div class="btn-group mr-2">
|
<div class="btn-group mr-2">
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary">Share</button>
|
<button type="button" class="btn btn-sm btn-outline-secondary">Share</button>
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary">Export</button>
|
<button type="button" class="btn btn-sm btn-outline-secondary">Export</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group mr-2" role="group">
|
||||||
<div class="dropdown" class="h-100">
|
<div class="dropdown" class="h-100">
|
||||||
<button id="dropdownMenuButton" type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle"
|
<button id="dropdownMenuButton" type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle"
|
||||||
data-toggle="dropdown">
|
data-toggle="dropdown">
|
||||||
<span data-feather="calendar"></span>
|
<span data-feather="calendar"></span>
|
||||||
|
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
||||||
<form class="px-4 py-3">
|
<form class="px-4 py-3">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-light" onclick="dashboard.setTimeRange(86400)">Today</button>
|
<button type="button" class="btn btn-light" onclick="dashboard.setTimeRange(86400)">Today</button>
|
||||||
<button type="button" class="btn btn-light" onclick="dashboard.setTimeRange(604800)">This Week</button>
|
<button type="button" class="btn btn-light" onclick="dashboard.setTimeRange(604800)">This
|
||||||
<button type="button" class="btn btn-light" onclick="dashboard.setTimeRange(18144000)">This Month</button>
|
Week</button>
|
||||||
|
<button type="button" class="btn btn-light" onclick="dashboard.setTimeRange(18144000)">This
|
||||||
|
Month</button>
|
||||||
|
<button type="button" class="btn btn-light" onclick="dashboard.setTimeRange(18144000)">This
|
||||||
|
Year</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group mr-2" role="group">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="dashboard.loadDashboard()">
|
||||||
|
<span data-feather="refresh-cw"></span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="mainContent" style="height: 100%">
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loader" class="d-flex justify-content-center opacity-animated" style="position: absolute; left: 50%;">
|
||||||
|
<div class="spinner-border" role="status">
|
||||||
|
<span class="sr-only">Loading...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="mainContent" class="opacity-animated" style="height: 100%">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
245
js/dashboard.js
245
js/dashboard.js
|
@ -7,24 +7,36 @@ class BlueWeatherDashboard {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// cunstruct xmlhttp
|
// cunstruct xmlhttp
|
||||||
|
this.initDone = false
|
||||||
this.blueweather = new BlueWeather()
|
this.blueweather = new BlueWeather()
|
||||||
this.loc = this.blueweather.findGetParameter("locId")
|
|
||||||
this.currentPage = -1
|
this.params = {
|
||||||
this.setPage(this.currentPage)
|
loc: 1,
|
||||||
|
page: -1,
|
||||||
|
maxVals: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
setPage(page) {
|
this.restoreParms(this.params)
|
||||||
document.getElementById("navbarPage" + this.currentPage + "Link").classList.remove("active")
|
|
||||||
document.getElementById("navbarPage" + page + "Link").classList.add("active")
|
this.setTimeRange()
|
||||||
|
this.loadDashboard(this.params)
|
||||||
|
this.initDone = true
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDashboard() {
|
||||||
|
window.history.pushState("object or string", "Title", this.buildUrl({ "params": JSON.stringify(this.params) }));
|
||||||
|
|
||||||
this.currentPage = page
|
|
||||||
// page: -1 for dashboard or sensor id
|
// page: -1 for dashboard or sensor id
|
||||||
var mainContent = document.getElementById('mainContent')
|
var mainContent = document.getElementById('mainContent')
|
||||||
|
var loader = document.getElementById('loader')
|
||||||
|
|
||||||
// set page to loading state
|
mainContent.style = "opacity: 0;"
|
||||||
mainContent.innerHTML = "<div class=\"d-flex justify-content-center\"><div class=\"spinner-border\" role=\"status\"><span class=\"sr-only\">Loading...</span></div></div>"
|
loader.style = "position: absolute; left: 50%; opacity: 1;"
|
||||||
|
//mainContent.classList.remove("opacity-animated");
|
||||||
|
|
||||||
this.blueweather.getLocationData(this.loc, undefined, undefined, true, function (locationData) {
|
var page = this.params.page
|
||||||
|
|
||||||
|
this.blueweather.getLocationData(this.params.loc, { from: this.params.range.from, to: this.params.range.to }, this.params.maxVals, true, function (locationData) {
|
||||||
// refresh sensors list
|
// refresh sensors list
|
||||||
dashboard.loadSensors(locationData.sensors)
|
dashboard.loadSensors(locationData.sensors)
|
||||||
|
|
||||||
|
@ -33,21 +45,141 @@ class BlueWeatherDashboard {
|
||||||
|
|
||||||
if (page === -1) {
|
if (page === -1) {
|
||||||
dashboard.setPageTitle("Dashboard")
|
dashboard.setPageTitle("Dashboard")
|
||||||
mainContent.innerHTML = "<p>This is your great dashboard :))</p>"
|
|
||||||
|
var firstRow = document.createElement("div")
|
||||||
|
firstRow.classList.add("card-deck")
|
||||||
|
|
||||||
|
for (var i = 0; i < locationData.sensors.length; i++) {
|
||||||
|
var sensor = locationData.sensors[i]
|
||||||
|
|
||||||
|
// get the valuetype of the current sensor
|
||||||
|
var valueType;
|
||||||
|
|
||||||
|
for (var s = 0; s < locationData.valuetypes.length; s++) {
|
||||||
|
var thisValType = locationData.valuetypes[s]
|
||||||
|
if (parseInt(thisValType.id) === parseInt(sensor.valuetypeid)) {
|
||||||
|
valueType = thisValType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all relevant meassurement values
|
||||||
|
var vals = []
|
||||||
|
|
||||||
|
for (s = 0; s < locationData["measvalues"].length; s++) {
|
||||||
|
var thisValue = locationData["measvalues"][s]
|
||||||
|
if (parseInt(thisValue['sensorid']) === parseInt(sensor.id)) {
|
||||||
|
vals.push(thisValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(vals.length < 1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
vals.sort(function(a,b){
|
||||||
|
return b.timestamp - a.timestamp
|
||||||
|
})
|
||||||
|
|
||||||
|
var latestVal = vals[0]
|
||||||
|
|
||||||
|
// - create html widget -
|
||||||
|
// create card
|
||||||
|
var thisWidget = document.createElement("div")
|
||||||
|
thisWidget.classList.add("card")
|
||||||
|
|
||||||
|
// create header
|
||||||
|
var thisHeader = document.createElement("div")
|
||||||
|
thisHeader.classList.add("card-header")
|
||||||
|
thisHeader.innerHTML = sensor.sensorname + " (" + valueType.valuetype + ")"
|
||||||
|
thisWidget.appendChild(thisHeader)
|
||||||
|
|
||||||
|
// create body
|
||||||
|
var thisBody = document.createElement("div")
|
||||||
|
thisBody.classList.add("card-body")
|
||||||
|
thisWidget.appendChild(thisBody)
|
||||||
|
|
||||||
|
// create footer
|
||||||
|
var thisFooter = document.createElement("div")
|
||||||
|
thisFooter.classList.add("card-footer")
|
||||||
|
// fill footer
|
||||||
|
var thisUpdateTime = document.createElement("small")
|
||||||
|
thisUpdateTime.classList.add("text-muted")
|
||||||
|
var lastUpdated = new Date(latestVal.timestamp * 1000)
|
||||||
|
thisUpdateTime.innerHTML = "last updated " + (lastUpdated.getDate() === new Date().getDate() ? "today" : lastUpdated.getFullYear+"-"+lastUpdated.getMonth()+"-"+lastUpdated.getDate()) + " at " + lastUpdated.getHours() + ":" + lastUpdated.getMinutes()
|
||||||
|
thisFooter.appendChild(thisUpdateTime)
|
||||||
|
thisWidget.appendChild(thisFooter)
|
||||||
|
|
||||||
|
// - create widget body -
|
||||||
|
var displayProperties = JSON.parse(valueType.displayproperty).widget
|
||||||
|
|
||||||
|
switch (displayProperties.type) {
|
||||||
|
case "doughnut":
|
||||||
|
// the widget is gauge-like
|
||||||
|
var thisChart = document.createElement("canvas")
|
||||||
|
thisChart.id = "chartOf" + sensor.sensorname
|
||||||
|
|
||||||
|
var chartData = {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: sensor.sensorname + " (" + valueType.valuetype + ")",
|
||||||
|
data: [
|
||||||
|
parseInt(latestVal.measvalue),
|
||||||
|
100 -parseInt( latestVal.measvalue)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
labels: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(chartData.datasets[0], displayProperties.properties)
|
||||||
|
|
||||||
|
dashboard.createPercentCircle(thisChart, chartData)
|
||||||
|
|
||||||
|
thisBody.appendChild(thisChart)
|
||||||
|
|
||||||
|
break
|
||||||
|
case "text":
|
||||||
|
// the widget is pure text
|
||||||
|
thisWidget.classList.add("text-center")
|
||||||
|
thisBody.innerHTML = '<h5 class="card-title">' + latestVal.measvalue + valueType.valueunit + '</h5>'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
firstRow.appendChild(thisWidget)
|
||||||
|
}
|
||||||
|
|
||||||
|
mainContent.innerHTML = "";
|
||||||
|
mainContent.appendChild(firstRow)
|
||||||
}
|
}
|
||||||
else if (page !== -1) {
|
else if (page !== -1) {
|
||||||
dashboard.loadDiagram("mainContent", locationData, page)
|
dashboard.loadDiagram("mainContent", locationData, page)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
|
loader.style = "position: absolute; left: 50%; opacity: 0;"
|
||||||
|
mainContent.classList.add("opacity-animated");
|
||||||
|
mainContent.style = "opacity: 1;"
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreParms(defaults = {}) {
|
||||||
|
this.params = Object.assign(defaults, JSON.parse(this.findGetParameter("params")))
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParams(newParams) {
|
||||||
|
if (newParams.page !== this.params.page && this.initDone) {
|
||||||
|
document.getElementById("navbarPage" + this.params.page + "Link").classList.remove("active")
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this.params, newParams)
|
||||||
|
|
||||||
|
this.loadDashboard()
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeRange(from = "", to = "") {
|
setTimeRange(from = "", to = "") {
|
||||||
this.blueweather.log("changing time range; form: " + from + "; to: " + to + "; (now is: " + new Date().getTime() + ")", 3)
|
this.blueweather.log("changing time range; form: " + from + "; to: " + to + "; (now is: " + new Date().getTime() + ")", 3)
|
||||||
|
|
||||||
// set page to loading state
|
|
||||||
mainContent.innerHTML = "<div class=\"d-flex justify-content-center\"><div class=\"spinner-border\" role=\"status\"><span class=\"sr-only\">Loading...</span></div></div>"
|
|
||||||
|
|
||||||
if (from !== "") {
|
if (from !== "") {
|
||||||
from = new Date().getTime() / 1000 - from
|
from = new Date().getTime() / 1000 - from
|
||||||
}
|
}
|
||||||
|
@ -56,9 +188,7 @@ class BlueWeatherDashboard {
|
||||||
to = new Date().getTime() / 1000 - parseInt(to)
|
to = new Date().getTime() / 1000 - parseInt(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.blueweather.getLocationData(this.loc, { from: from, to: to }, undefined, true, function (locationData) {
|
this.updateParams({ range: { to: to, from: from } })
|
||||||
dashboard.loadDiagram("mainContent", locationData, dashboard.currentPage)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +200,7 @@ class BlueWeatherDashboard {
|
||||||
for (var i = 0; i < sensors.length; i++) {
|
for (var i = 0; i < sensors.length; i++) {
|
||||||
var currentHTML = sensorsList.innerHTML
|
var currentHTML = sensorsList.innerHTML
|
||||||
sensorsList.innerHTML = currentHTML +
|
sensorsList.innerHTML = currentHTML +
|
||||||
"<li class=\"nav-item\"><a class=\"nav-link\" id=\"navbarPage" + sensors[i]["id"] + "Link\" href=\"#\" onclick=\"dashboard.setPage(" + sensors[i]["id"] + ")\"><span data-feather=\"file\"></span>" + sensors[i]["sensorname"] + "</a></li>"
|
"<li class=\"nav-item\"><a class=\"nav-link\" id=\"navbarPage" + sensors[i]["id"] + "Link\" href=\"#\" onclick=\"dashboard.updateParams({page: " + sensors[i]["id"] + "})\"><span data-feather=\"file\"></span>" + sensors[i]["sensorname"] + "</a></li>"
|
||||||
}
|
}
|
||||||
|
|
||||||
feather.replace()
|
feather.replace()
|
||||||
|
@ -131,7 +261,7 @@ class BlueWeatherDashboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(chartData.datasets[0], JSON.parse(valueType.displayproperty))
|
Object.assign(chartData.datasets[0], JSON.parse(valueType.displayproperty))
|
||||||
Object.assign(chartData.datasets[0], {data: chartData.data, pointRadius: chartData.data.length > 500 ? 0 : 3})
|
Object.assign(chartData.datasets[0], { data: chartData.data, pointRadius: chartData.data.length > 500 ? 0 : 3 })
|
||||||
|
|
||||||
mainContent.innerHTML = "<canvas class=\"my-4 w-100\" id=\"myChart\"></canvas>"
|
mainContent.innerHTML = "<canvas class=\"my-4 w-100\" id=\"myChart\"></canvas>"
|
||||||
this.setPageTitle(sensor.sensorname + " (" + valueType.valuetype + ")")
|
this.setPageTitle(sensor.sensorname + " (" + valueType.valuetype + ")")
|
||||||
|
@ -141,6 +271,39 @@ class BlueWeatherDashboard {
|
||||||
this.createDiagram("myChart", chartData)
|
this.createDiagram("myChart", chartData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// - helper functions -
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
createPercentCircle(canvasElement, data) {
|
||||||
|
var randomScalingFactor = function () {
|
||||||
|
return Math.round(Math.random() * 100);
|
||||||
|
};
|
||||||
|
var ctx = canvasElement//document.getElementById(canvasId)
|
||||||
|
|
||||||
|
var chartData = data
|
||||||
|
var chartOptions = {
|
||||||
|
responsive: true,
|
||||||
|
circumference: Math.PI,
|
||||||
|
rotation: Math.PI,
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
animateScale: true,
|
||||||
|
animateRotate: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var myChart = new Chart(ctx, {
|
||||||
|
type: 'doughnut',
|
||||||
|
data: chartData,
|
||||||
|
options: chartOptions
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
createDiagram(canvasId, data) {
|
createDiagram(canvasId, data) {
|
||||||
// Graphs
|
// Graphs
|
||||||
var ctx = document.getElementById(canvasId)
|
var ctx = document.getElementById(canvasId)
|
||||||
|
@ -233,6 +396,48 @@ class BlueWeatherDashboard {
|
||||||
setPageTitle(title) {
|
setPageTitle(title) {
|
||||||
document.getElementById("titleH1").innerHTML = title
|
document.getElementById("titleH1").innerHTML = title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findGetParameter(parameterName) {
|
||||||
|
var result = null,
|
||||||
|
tmp = [];
|
||||||
|
location.search
|
||||||
|
.substr(1)
|
||||||
|
.split("&")
|
||||||
|
.forEach(function (item) {
|
||||||
|
tmp = item.split("=");
|
||||||
|
if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildUrl(params) {
|
||||||
|
var basepath = window.location.href.split("?")[0]
|
||||||
|
|
||||||
|
basepath += "?"
|
||||||
|
|
||||||
|
for (var param in params) {
|
||||||
|
basepath += param + "=" + params[param] + "&"
|
||||||
|
}
|
||||||
|
|
||||||
|
return basepath
|
||||||
|
}
|
||||||
|
|
||||||
|
whichTransitionEvent() {
|
||||||
|
var t;
|
||||||
|
var el = document.createElement('fakeelement');
|
||||||
|
var transitions = {
|
||||||
|
'transition': 'transitionend',
|
||||||
|
'OTransition': 'oTransitionEnd',
|
||||||
|
'MozTransition': 'transitionend',
|
||||||
|
'WebkitTransition': 'webkitTransitionEnd'
|
||||||
|
}
|
||||||
|
|
||||||
|
for (t in transitions) {
|
||||||
|
if (el.style[t] !== undefined) {
|
||||||
|
return transitions[t];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboard = new BlueWeatherDashboard()
|
dashboard = new BlueWeatherDashboard()
|
|
@ -4,7 +4,6 @@
|
||||||
<head>
|
<head>
|
||||||
<title>humidity Chart</title>
|
<title>humidity Chart</title>
|
||||||
<script src="js/Chart.min.js"></script>
|
<script src="js/Chart.min.js"></script>
|
||||||
<script src="js/utils.js"></script>
|
|
||||||
<style>
|
<style>
|
||||||
canvas {
|
canvas {
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
|
@ -33,8 +32,8 @@
|
||||||
randomScalingFactor(),
|
randomScalingFactor(),
|
||||||
],
|
],
|
||||||
backgroundColor: [
|
backgroundColor: [
|
||||||
window.chartColors.blue,
|
'rgb(54, 162, 235)',
|
||||||
window.chartColors.white,
|
'lightgrey',
|
||||||
],
|
],
|
||||||
label: 'Dataset 1'
|
label: 'Dataset 1'
|
||||||
}],
|
}],
|
||||||
|
@ -45,6 +44,8 @@
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
|
circumference: Math.PI,
|
||||||
|
rotation: Math.PI,
|
||||||
legend: {
|
legend: {
|
||||||
position: 'top',
|
position: 'top',
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue