Skip to content

Commit

Permalink
8.6 release
Browse files Browse the repository at this point in the history
More small-screen friendly and responsive layout (no more label/bubble
overlaps)
Top menu buttons collapsed in 1 MENU button that slides menu from the
side
Show Metric values in Node labels via {MetricLabel} token
Show Node icon in Node edit screen
Node detail top section styled
Hidden nodes show in light red when visible
Graph x-axis scaled correctly when incoming data is plotted
  • Loading branch information
LowPowerLab committed Oct 7, 2016
1 parent 62f71ae commit e3d1813
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 29 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "RaspberryPi-Gateway",
"version": "0.2.0",
"version": "8.6",
"description": "RaspberryPi Home Automation Gateway",
"main": "gateway.js",
"repository": "https://github.com/LowPowerLab/RaspberryPi-Gateway.git",
Expand Down
Binary file added www/images/icon_multinode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
184 changes: 156 additions & 28 deletions www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
.ui-content { padding-top:0; }
.btn-text, #loadingsocket { font-size: 32px; }
#loadingsocket, #loadinggraph {padding:15px;}
.labelbold { font-weight:bold !important; }
.labelbold { font-weight:bold !important; overflow:hidden; text-overflow:ellipsis;white-space:nowrap; }
#wrap { padding: 12px; }
.ui-listview>li.hiddenNode { display:none; }
.ui-listview>li.hiddenNodeShow { display:block; }
Expand Down Expand Up @@ -142,18 +142,104 @@
#rawActionIDspan div { border-radius: 0.3em 0 0 0.3em; }
#rawActionTextspan div { border-radius: 0; }
#rawActionSend { border-radius: 0 0.3em 0.3em 0; }

.hiddenNodeShow a { background-color: #ffe5e5 !important; }

#nodeList > li.ui-li-has-count > a { padding-right:2.5em; }
@media (max-width: 599px) {
#nodeList > li.ui-li-has-count > a { padding-left: 5.25em; }
#nodeList > li.ui-li-has-count > a > span.ui-li-count {
top:70%;
}}
@media (max-width: 767px) {
.sideButton { display:none !important; }
}

#nav-panel-popup {
right: 0 !important;
left: auto !important;
}
#nav-panel {
width: 200px;
background: rgba(0,0,0,.7);
border: 1px solid #000;
border-right: none;
/*margin: -1px 0;*/
/*padding-top:20px;*/

}
#nav-panel > ul { margin-top: 0 !important; }
#nav-panel .ui-btn {
/*margin: 2em 15px;*/
}

.content-panel {
margin: 1em 0;
border: 1px solid #ddd;
display: block;
box-shadow: 0 1px 3px rgba(0,0,0,.15);
border-radius: .3125em;
text-shadow: 0 1px 0 #f3f3f3;
padding-right: .5em;
}

@media (max-width: 539px) {
.content-panel { padding: 0 .5em; }
.nodeDetailImageWrapper { text-align:center; }
#nodeDetailInputList { padding-left: 0; padding-top: 0; margin-top:0;}
}

@media (max-width: 448px) {
label.labelbold { margin: .4em .4em .1em .4em; }
}

@media (min-width: 540px) {
.nodeDetailImageWrapper {
position:absolute;
float:left;
padding-top:10px;
}
#nodeDetailInputList { padding-left: 130px; }
}

@media (min-width: 1000px) {
.nodeDetailImageWrapper { padding-left: 10%; }
#nodeDetailInputList { padding-left: 25%; padding-right: 10%; }
}
@media (min-width: 1600px) {
.nodeDetailImageWrapper { padding-left: 20%; }
#nodeDetailInputList { padding-left: 30%; padding-right: 25%; }
}
</style>
</head>
<body>
<div data-role="page" id="homepage">
<div id="nav-panel" data-role="popup" data-corners="false" data-overlay-theme="b" data-theme="b" data-shadow="true" data-tolerance="0,0">
<ul data-role="listview" data-theme="a" data-divider-theme="a" style="margin-top:-16px;" class="nav-search">
<li data-icon="delete">
<a id="nav-panel-close" href="#" data-rel="close">Close (Esc)</a>
</li>

<li data-icon="eye" data-iconpos="left">
<a id="btnHiddenNodesToggle" href="#">Show Hidden</a>
</li>
<li data-icon="search">
<a id="btnSearch" href="#">Search</a>
</li>
<li data-icon="fa-terminal">
<a href="#logpage">Log / Terminal</a>
</li>
<li data-icon="fa-gear">
<a href="#settingspage">Settings</a>
</li>
</ul>
</div><!-- /panel -->

<div data-role="header">
<a href="http://lowpowerlab.com/gateway" target="new" data-role="none"><img src="images/logo.png" alt="LowPowerLab" title="LowPowerLab.com" style="float:left;display:inline;max-width:30px;padding:4px"/></a>
<h1>Moteino Gateway Dashboard</h1>
<div class="ui-btn-right" data-role="controlgroup" data-type="horizontal" data-mini="true">
<a id="btnHiddenNodesToggle" href="#" data-role="button" data-icon="eye" data-iconpos="notext" title="Show hidden nodes">Show hidden</a>
<a id="btnSearch" href="#" data-role="button" data-icon="search" data-iconpos="notext" title="search nodes">Search</a>
<a href="#logpage" data-role="button" data-icon="fa-terminal" data-iconpos="notext" title="terminal/log">Log</a>
<a href="#settingspage" data-role="button" data-icon="fa-gear" data-iconpos="notext" title="settings">Settings</a>
<a class="sideMenuButton" href="#nav-panel" data-icon="bars" data-role="button" data-rel="popup" data-transition="slide" data-position-to="window" >Menu</a>
</div>
</div>

Expand Down Expand Up @@ -194,13 +280,18 @@ <h3><span id="nodeDetailTitle">Node details</span> <span class="nodeUpdated">x</
</div>
</div>
<div data-role="main" class="ui-content">
<div class="ui-field-contain">
<label for="nodeMoteType" class="labelbold">Type:</label>
<select id="nodeMoteType" data-mini="true"></select>
<label for="nodeLabel" class="labelbold">Label:</label>
<input type="text" name="nodeLabel" id="nodeLabel" placeholder="node label..." />
<label for="nodeDescr" class="labelbold">Description:</label>
<input type="text" name="nodeDescr" id="nodeDescr" placeholder="description/location..." />
<div class="content-panel">
<div class="nodeDetailImageWrapper">
<img id="nodeDetailImage" class="productimg">
</div>
<div id="nodeDetailInputList" class="ui-field-contain">
<label for="nodeMoteType" class="labelbold">Type:</label>
<select id="nodeMoteType" data-mini="true"></select>
<label for="nodeLabel" class="labelbold">Label:</label>
<input type="text" name="nodeLabel" id="nodeLabel" placeholder="node label..." />
<label for="nodeDescr" class="labelbold">Description:</label>
<input type="text" name="nodeDescr" id="nodeDescr" placeholder="description/location..." />
</div>
</div>
<ul id="metricList" data-role="listview" data-inset="true" data-theme="a" data-dividertheme="b"></ul>

Expand Down Expand Up @@ -671,11 +762,20 @@ <h1>Settings</h1>

function addGraphDataPoint(point) {
if (graphFrozen) return;
graphData.push(point);
if (graphData.length > 10)
graphData.shift(); //remove first point in graph
graphOptions.xaxis.min = graphView.start = graphData[0][0];
graphOptions.xaxis.max = graphView.end = graphData[graphData.length-1][0];

//shift visible timeline
graphOptions.xaxis.min = graphView.start = point[0] - (graphView.end - graphView.start); //calculate new min
graphOptions.xaxis.max = graphView.end = point[0];

graphData.push(point); //add new data point

//remove points that are invisible after shifting the visible timeline
var i = 0;
while (graphData[i]) {
if (graphData[i++][0] < graphView.start)
graphData.shift();
}

//plot = $.plot(metricGraph, [graphData], graphOptions);
$.plot(metricGraph, [{label:graphOptions.legendLbl, data:graphData}], graphOptions);
//redraw alternative:
Expand All @@ -684,14 +784,26 @@ <h1>Settings</h1>
//plot.draw();
$(graphStat).clone().appendTo('#metricGraph');
}

/// resolves any metrics in the label or description of a node
function nodeResolveString(theString, metrics) {
for(var key in metrics)
{
if (/\{.+\}/ig.test(theString) == false) return theString; //if string has no more "{...}" just return it
metric = metrics[key];
theString = theString.replace('{'+metric.label+'}', metricsValues({0:metric}, true));
theString = theString.replace('{'+metric.label+':updated}', ago(metric.updated).tag);
}
return theString;
}

function metricsValues(metrics) {
function metricsValues(metrics, ignorePin=false) {
var label = '';
var metric;
for(var key in metrics)
{
metric = metrics[key];
if(metric.pin=='1' || metrics.length==1)
if(metric.pin=='1' || metrics.length==1 || ignorePin)
{
var agoText = ago(metric.updated).text;
//metric.value + (metric.unit || '')
Expand All @@ -703,9 +815,9 @@ <h1>Settings</h1>
return label;
}

function getNodeIcon(nodeType) {
if (motesDef != undefined && nodeType != undefined && motesDef[nodeType] != undefined)
return motesDef[nodeType].icon || 'icon_default.png';
function getNodeIcon(node) {
if (motesDef != undefined && node.type != undefined && motesDef[node.type] != undefined)
return motesDef[node.type].icon || 'icon_default.png';
return 'icon_default.png';
};

Expand All @@ -730,7 +842,7 @@ <h1>Settings</h1>
nodes[node._id] = node;
var nodeValue = metricsValues(node.metrics);
var lowBat = node.metrics.V != null && node.metrics.V.value < 3.55;
var newLI = $('<li id="' + node._id + '"><a node-id="' + node._id + '" href="#nodedetails" class="nodedetails"><img class="productimg" src="images/'+getNodeIcon(node.type)+'"><h2>' + (node.label || node._id) + ' ' + resolveRSSIImage(node.rssi) + ' ' + (lowBat ? '<img src="images/lowbattery.png" style="max-width:32px"/> ' : '') + ago(node.updated, 0).tag + (node.hidden ? ' <img class="listIcon20px" src="images/icon_hidden.png" />' : '') + '</h2><p>' + (node.descr || '&nbsp;') + '</p>' + (nodeValue ? '<span class="ui-li-count ui-li-count16">' + nodeValue + '</span>' : '') + '</a></li>');
var newLI = $('<li id="' + node._id + '"><a node-id="' + node._id + '" href="#nodedetails" class="nodedetails"><img class="productimg" src="images/'+getNodeIcon(node)+'"><h2>' + (nodeResolveString(node.label, node.metrics) || node._id) + ' ' + resolveRSSIImage(node.rssi) + ' ' + (lowBat ? '<img src="images/lowbattery.png" style="max-width:32px"/> ' : '') + ago(node.updated, 0).tag + (node.hidden ? ' <img class="listIcon20px" src="images/icon_hidden.png" />' : '') + '</h2><p>' + (nodeResolveString(node.descr, node.metrics) || '&nbsp;') + '</p>' + (nodeValue ? '<span class="ui-li-count ui-li-count16">' + nodeValue + '</span>' : '') + '</a></li>');
var existingNode = $('#nodeList li#' + node._id);
if (node.hidden)
if (showHiddenNodes)
Expand Down Expand Up @@ -774,6 +886,7 @@ <h1>Settings</h1>
$('#nodeList').listview('refresh'); //re-render the listview
}

/// calculated a style for an "ago" label based on a given timestamp in the past
function ago(time, agoPrefix)
{
agoPrefix = (typeof agoPrefix !== 'undefined') ? agoPrefix : true;
Expand All @@ -799,6 +912,7 @@ <h1>Settings</h1>
return {text:updated,color:theColor,tag:'<span data-time="'+time+'" class="nodeAgo" style="color:'+theColor+';">'+updated+'</span>'};
}

/// updates the "ago" labels text and color according the to elapsed time since the last update timestamp associated with those labels
function updateAgos()
{
$("span.nodeAgo").each(function(){
Expand All @@ -823,13 +937,14 @@ <h1>Settings</h1>
if ($("#searchBox").is(":visible"))
{
$("#searchBox").slideUp('fast');
$("#btnSearch").removeClass('ui-btn-b');
$("#btnSearch").css('background-color', '');
}
else
{
$("#searchBox").slideDown('fast');
$("#btnSearch").addClass('ui-btn-b');
$("#btnSearch").css('background-color', '#9bffbe');
}
$( "#nav-panel" ).popup( "close" );
});

$("#btnHiddenNodesToggle").click("tap", function(event) {
Expand All @@ -842,9 +957,10 @@ <h1>Settings</h1>
else
{
$(".hiddenNode").removeClass('hiddenNode').addClass('hiddenNodeShow');
$('#btnHiddenNodesToggle').css('background-color', '#D00');
$('#btnHiddenNodesToggle').css('background-color', '#ffcaca');
showHiddenNodes = true;
}
$( "#nav-panel" ).popup( "close" );
});

$("#btnHideNodeToggle").click("tap", function(event) {
Expand Down Expand Up @@ -872,10 +988,11 @@ <h1>Settings</h1>

function refreshNodeDetails(node) {
$('#nodeLabel').val(node.label || '');
$('#nodeDetailTitle').html(node.label || 'Node details');
$('#nodeDetailTitle').html(nodeResolveString(node.label, node.metrics) || 'Node details');
$('#nodeMoteType').val(node.type || '');
$("#nodeMoteType").selectmenu('refresh',true);
$('#nodeDescr').val(node.descr || '');
$('#nodeDetailImage').attr('src', 'images/'+getNodeIcon(node));

if (node.hidden) $("#btnHideNodeToggle").addClass('hidden').css('background-color','#D00');
else $("#btnHideNodeToggle").removeClass('hidden').css('background-color','');;
Expand Down Expand Up @@ -1023,7 +1140,7 @@ <h1>Settings</h1>
socket.emit('EDITNODEEVENT', selectedNodeId, eventKey, null, true);
});

$('#nodeLabel').keyup(function() {$('#nodeDetailTitle').html($('#nodeLabel').val() || 'no label');});
$('#nodeLabel').keyup(function() {$('#nodeDetailTitle').html( nodeResolveString($('#nodeLabel').val(), nodes[selectedNodeId].metrics) || 'Node details');});
$('#metricLabel').keyup(function() {$('#metricDetailTitle').html($('#metricLabel').val() || 'no label');});

$("#btnPinMetric").click("tap", function(event) {
Expand Down Expand Up @@ -1174,6 +1291,17 @@ <h1>Settings</h1>
'font-weight':'bold',
'text-shadow':'0 1px 0 black',
});

$( "#nav-panel" ).on({
popupbeforeposition: function() {
var h = $( window ).height();
$( "#nav-panel" ).css( "height", h );
}
});

$("#nav-panel-close").click("tap", function(event) {
$( "#nav-panel" ).popup( "close" );
});
});
}
</script>
Expand Down

0 comments on commit e3d1813

Please sign in to comment.