2019-07-11 14:53:18 +02:00
/* globals Chart:false, feather:false */
2019-07-11 21:08:24 +02:00
//var ctx = document.getElementById('myChart')
//var mainContent = document.getElementById('mainContent')
2019-07-13 20:02:04 +02:00
class BlueWeatherDashboard {
constructor ( ) {
// cunstruct xmlhttp
2019-07-14 17:53:07 +02:00
this . initDone = false
2019-07-13 20:02:04 +02:00
this . blueweather = new BlueWeather ( )
2019-07-14 17:53:07 +02:00
this . params = {
loc : 1 ,
page : - 1 ,
maxVals : 100
}
this . restoreParms ( this . params )
this . loadDashboard ( this . params )
this . initDone = true
2019-07-13 20:02:04 +02:00
}
2019-07-14 17:53:07 +02:00
loadDashboard ( ) {
window . history . pushState ( "object or string" , "Title" , this . buildUrl ( { "params" : JSON . stringify ( this . params ) } ) ) ;
2019-07-11 21:08:24 +02:00
2019-07-13 20:02:04 +02:00
// page: -1 for dashboard or sensor id
var mainContent = document . getElementById ( 'mainContent' )
2019-07-14 17:53:07 +02:00
var loader = document . getElementById ( 'loader' )
2019-07-11 21:08:24 +02:00
2019-07-14 17:53:07 +02:00
mainContent . style = "opacity: 0;"
loader . style = "position: absolute; left: 50%; opacity: 1;"
//mainContent.classList.remove("opacity-animated");
2019-07-13 20:02:04 +02:00
2019-07-14 17:53:07 +02:00
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 ) {
2019-07-12 13:24:06 +02:00
// refresh sensors list
2019-07-13 20:02:04 +02:00
dashboard . loadSensors ( locationData . sensors )
2019-07-11 21:08:24 +02:00
2019-07-13 20:02:04 +02:00
// set acive page in navbar
document . getElementById ( "navbarPage" + page + "Link" ) . classList . add ( "active" )
if ( page === - 1 ) {
dashboard . setPageTitle ( "Dashboard" )
2019-07-14 17:53:07 +02:00
2019-07-14 21:38:37 +02:00
// create widget row
2019-07-14 17:53:07 +02:00
var firstRow = document . createElement ( "div" )
firstRow . classList . add ( "card-deck" )
2019-07-14 21:38:37 +02:00
// create table
var tableContainer = document . createElement ( "div" )
tableContainer . style = "overflow-x:auto;margin-top:20px;"
var table = document . createElement ( "table" )
tableContainer . appendChild ( table )
table . classList . add ( "table" , "table-hover" )
// create forst row of table
var tableHead = document . createElement ( "thead" )
table . appendChild ( tableHead )
tableHead . innerHTML = "<tr><th>Sensor</th><th>Minimum</th><th>Maximum</th><th>Average</th></tr>"
var tableBody = document . createElement ( "tbody" )
table . appendChild ( tableBody )
2019-07-14 17:53:07 +02:00
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 = [ ]
2019-07-14 21:38:37 +02:00
var sum = 0
var min
var max
2019-07-14 17:53:07 +02:00
for ( s = 0 ; s < locationData [ "measvalues" ] . length ; s ++ ) {
var thisValue = locationData [ "measvalues" ] [ s ]
if ( parseInt ( thisValue [ 'sensorid' ] ) === parseInt ( sensor . id ) ) {
vals . push ( thisValue )
2019-07-14 21:38:37 +02:00
sum += thisValue . measvalue
if ( thisValue . measvalue < min || min === undefined ) {
min = thisValue . measvalue
}
if ( thisValue . measvalue > max || max === undefined ) {
max = thisValue . measvalue
}
2019-07-14 17:53:07 +02:00
}
}
if ( vals . length < 1 ) {
continue
}
vals . sort ( function ( a , b ) {
return b . timestamp - a . timestamp
} )
2019-07-14 21:38:37 +02:00
// create table col
var tableRow = document . createElement ( "tr" )
tableRow . innerHTML = "<td>" + sensor . sensorname + " (" + valueType . valuetype + ", " + valueType . valueunit + ")" + "</td><td>" + min + "</td><td>" + max + "</td><td>" + ( Math . round ( ( sum / vals . length ) * 100 ) / 100 ) + "</td>"
tableBody . appendChild ( tableRow )
// - create widget card -
2019-07-14 17:53:07 +02:00
var latestVal = vals [ 0 ]
// 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" )
2019-07-14 18:51:43 +02:00
thisBody . innerHTML = '<h1 class="align-self-center mx-auto" style="font-size:10vw;margin-top: auto;margin-bottom: auto;">' + latestVal . measvalue + valueType . valueunit + '</h1>'
//thisBody.innerHTML = '<svg viewBox="0 0 56 18"><text x="0" y="15">Fit Meeeeeeeeeeee</text></svg>'
break
2019-07-14 17:53:07 +02:00
}
firstRow . appendChild ( thisWidget )
}
2019-07-14 21:38:37 +02:00
2019-07-14 17:53:07 +02:00
mainContent . innerHTML = "" ;
mainContent . appendChild ( firstRow )
2019-07-14 21:38:37 +02:00
mainContent . appendChild ( tableContainer )
2019-07-13 20:02:04 +02:00
}
else if ( page !== - 1 ) {
dashboard . loadDiagram ( "mainContent" , locationData , page )
}
2019-07-14 17:53:07 +02:00
loader . style = "position: absolute; left: 50%; opacity: 0;"
mainContent . classList . add ( "opacity-animated" ) ;
mainContent . style = "opacity: 1;"
2019-07-13 20:02:04 +02:00
} )
2019-07-14 17:53:07 +02:00
}
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 )
2019-07-11 21:08:24 +02:00
2019-07-14 21:38:37 +02:00
window . history . pushState ( "object or string" , "Title" , this . buildUrl ( { "params" : JSON . stringify ( this . params ) } ) ) ;
2019-07-14 17:53:07 +02:00
this . loadDashboard ( )
2019-07-11 21:08:24 +02:00
}
2019-07-13 20:02:04 +02:00
setTimeRange ( from = "" , to = "" ) {
this . blueweather . log ( "changing time range; form: " + from + "; to: " + to + "; (now is: " + new Date ( ) . getTime ( ) + ")" , 3 )
if ( from !== "" ) {
from = new Date ( ) . getTime ( ) / 1000 - from
}
if ( to !== "" ) {
to = new Date ( ) . getTime ( ) / 1000 - parseInt ( to )
}
2019-07-11 21:08:24 +02:00
2019-07-14 17:53:07 +02:00
this . updateParams ( { range : { to : to , from : from } } )
2019-07-11 21:08:24 +02:00
}
2019-07-11 14:53:18 +02:00
2019-07-13 20:02:04 +02:00
loadSensors ( sensors ) {
2019-07-11 14:53:18 +02:00
2019-07-13 20:02:04 +02:00
var sensorsList = document . getElementById ( "sensorsList" )
sensorsList . innerHTML = ""
2019-07-12 13:24:06 +02:00
2019-07-13 20:02:04 +02:00
for ( var i = 0 ; i < sensors . length ; i ++ ) {
var currentHTML = sensorsList . innerHTML
sensorsList . innerHTML = currentHTML +
2019-07-14 17:53:07 +02:00
"<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>"
2019-07-12 13:24:06 +02:00
}
2019-07-13 20:02:04 +02:00
feather . replace ( )
2019-07-12 13:24:06 +02:00
}
2019-07-13 20:02:04 +02:00
loadDiagram ( parentId , locationData , sensorId ) {
mainContent = document . getElementById ( parentId )
// get all relevant meassurement values
var vals = [ ]
2019-07-12 13:24:06 +02:00
2019-07-13 20:02:04 +02:00
for ( var i = 0 ; i < locationData [ "measvalues" ] . length ; i ++ ) {
var thisValue = locationData [ "measvalues" ] [ i ]
if ( parseInt ( thisValue [ 'sensorid' ] ) === sensorId ) {
vals . push ( thisValue )
}
2019-07-12 13:24:06 +02:00
}
2019-07-13 20:02:04 +02:00
// get the current sensor
var sensor ;
2019-07-12 13:24:06 +02:00
2019-07-13 20:02:04 +02:00
for ( i = 0 ; i < locationData [ 'sensors' ] . length ; i ++ ) {
var thisSensor = locationData [ 'sensors' ] [ i ]
if ( parseInt ( thisSensor . id ) === sensorId ) {
sensor = thisSensor ;
break ;
}
2019-07-12 13:24:06 +02:00
}
2019-07-13 20:02:04 +02:00
// get the value type
var valueType ;
2019-07-12 13:24:06 +02:00
2019-07-13 20:02:04 +02:00
for ( i = 0 ; i < locationData [ 'valuetypes' ] . length ; i ++ ) {
var thisValType = locationData [ 'valuetypes' ] [ i ]
if ( parseInt ( thisValType . id ) === parseInt ( sensor . valuetypeid ) ) {
valueType = thisValType ;
break ;
2019-07-11 21:08:24 +02:00
}
2019-07-13 20:02:04 +02:00
}
// build chart data array
var chartData = {
data : [ ] ,
datasets : [
2019-07-11 21:08:24 +02:00
{
2019-07-13 20:02:04 +02:00
label : sensor . sensorname + " (" + valueType . valuetype + ")" ,
yAxisID : 'yAxis' ,
showLine : true ,
data : vals
2019-07-11 21:08:24 +02:00
}
2019-07-11 14:53:18 +02:00
] ,
2019-07-13 20:02:04 +02:00
range : locationData . range
}
2019-07-14 17:53:07 +02:00
2019-07-13 20:02:04 +02:00
for ( i = 0 ; i < vals . length ; i ++ ) {
chartData . data . push ( { x : vals [ i ] [ "timestamp" ] , y : vals [ i ] [ "measvalue" ] } )
}
2019-07-14 18:51:43 +02:00
Object . assign ( chartData . datasets [ 0 ] , JSON . parse ( valueType . displayproperty ) [ "chart" ] [ "properties" ] )
2019-07-14 17:53:07 +02:00
Object . assign ( chartData . datasets [ 0 ] , { data : chartData . data , pointRadius : chartData . data . length > 500 ? 0 : 3 } )
2019-07-13 20:02:04 +02:00
mainContent . innerHTML = "<canvas class=\"my-4 w-100\" id=\"myChart\"></canvas>"
this . setPageTitle ( sensor . sensorname + " (" + valueType . valuetype + ")" )
//blueweather.log("creating chart with data: " + JSON.stringify(chartData), 3)
this . createDiagram ( "myChart" , chartData )
}
2019-07-14 17:53:07 +02:00
// --------------------
// - 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
} )
}
2019-07-13 20:02:04 +02:00
createDiagram ( canvasId , data ) {
// Graphs
var ctx = document . getElementById ( canvasId )
// eslint-disable-next-line no-unused-vars
var chartData = {
//labels: time,
datasets : data . datasets
} ;
var chartOptions = {
responsive : true ,
maintainAspectRatio : false ,
scales : {
yAxes : [
{
id : 'yAxis' ,
type : 'linear' ,
position : 'left' ,
}
] ,
xAxes : [ {
ticks : {
min : parseInt ( data . range . from ) ,
max : parseInt ( data . range . to ) ,
beginAtZero : false ,
stepSize : ( data . range . to - data . range . from ) / 10 ,
userCallback : function ( label , index , labels ) {
var d = new Date ( parseInt ( label ) * 1000 ) ;
2019-07-14 21:38:37 +02:00
var datestr = "" ;
var to = data . range . to
var from = data . range . from
if ( to === "" ) {
to = new Date ( ) . getTime ( )
}
var range = data . range . to - data . range . from
if ( range <= 60 * 60 * 24 ) {
// covers one day or less
datestr = ( "0" + d . getHours ( ) ) . slice ( - 2 ) + ":" +
( "0" + d . getMinutes ( ) ) . slice ( - 2 ) + ":" +
( "0" + d . getSeconds ( ) ) . slice ( - 2 )
}
else if ( range <= 60 * 60 * 24 * 31 ) {
// covers one month or less
datestr = ( "0" + d . getDate ( ) ) . slice ( - 2 ) + "-" +
( "0" + ( d . getMonth ( ) + 1 ) ) . slice ( - 2 ) + " " +
2019-07-13 20:02:04 +02:00
( "0" + d . getHours ( ) ) . slice ( - 2 ) + ":" +
( "0" + d . getMinutes ( ) ) . slice ( - 2 ) + ":" +
( "0" + d . getSeconds ( ) ) . slice ( - 2 )
2019-07-14 21:38:37 +02:00
}
else {
// covers more than a month
datestr =
d . getFullYear ( ) + "-" +
( "0" + d . getDate ( ) ) . slice ( - 2 ) + "-" +
( "0" + ( d . getMonth ( ) + 1 ) ) . slice ( - 2 )
}
2019-07-13 20:02:04 +02:00
return datestr ;
}
} ,
scaleLabel : {
display : true ,
labelString : 'time'
}
} ] ,
} ,
tooltips : {
callbacks : {
label : function ( tooltipItem , data ) {
var label = [ data . datasets [ tooltipItem . datasetIndex ] . label || '' ] ;
if ( label ) {
label [ 0 ] += ': ' ;
}
label [ 0 ] += Math . round ( tooltipItem . yLabel * 100 ) / 100 ;
2019-07-11 21:08:24 +02:00
2019-07-13 20:02:04 +02:00
var time = Math . round ( tooltipItem . xLabel * 100 ) / 100 ;
var d = new Date ( time * 1000 ) ;
2019-07-11 21:08:24 +02:00
var datestr =
2019-07-14 21:38:37 +02:00
d . getFullYear ( ) + "-" +
( "0" + d . getDate ( ) ) . slice ( - 2 ) + "-" +
( "0" + ( d . getMonth ( ) + 1 ) ) . slice ( - 2 ) + " " +
2019-07-11 21:08:24 +02:00
( "0" + d . getHours ( ) ) . slice ( - 2 ) + ":" +
( "0" + d . getMinutes ( ) ) . slice ( - 2 ) + ":" +
( "0" + d . getSeconds ( ) ) . slice ( - 2 )
;
2019-07-13 20:02:04 +02:00
label . push ( "Time: " + datestr ) ;
2019-07-11 21:08:24 +02:00
2019-07-13 20:02:04 +02:00
return label ;
2019-07-11 14:53:18 +02:00
}
2019-07-11 21:08:24 +02:00
}
2019-07-11 14:53:18 +02:00
}
2019-07-11 21:08:24 +02:00
2019-07-13 20:02:04 +02:00
} ;
2019-07-11 21:08:24 +02:00
2019-07-13 20:02:04 +02:00
var myChart = new Chart ( ctx , {
type : 'scatter' ,
data : chartData ,
options : chartOptions
} )
}
2019-07-11 21:08:24 +02:00
2019-07-13 20:02:04 +02:00
setPageTitle ( title ) {
document . getElementById ( "titleH1" ) . innerHTML = title
}
2019-07-14 17:53:07 +02:00
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 ] ;
}
}
}
}
2019-07-11 14:53:18 +02:00
2019-07-14 19:02:55 +02:00
feather . replace ( )
2019-07-13 20:02:04 +02:00
dashboard = new BlueWeatherDashboard ( )