Moved Json Pages to api folder and added auto update javascript to overview pages.

This commit is contained in:
Ziver Koc 2022-05-27 02:13:33 +02:00
parent 95ff5b81c0
commit ef365f360c
11 changed files with 210 additions and 56 deletions

View file

@ -5,20 +5,20 @@
<div class="panel-heading">Local Events</div>
<div class="panel-body">
<table class="table table-hover table-condensed">
<table id="event-device-table" class="table table-hover table-condensed">
<thead>
<th class="col-md-4">Name</th>
<th class="col-md-3">Type</th>
<th class="col-md-2">Data</th>
<th class="col-md-1">Data</th>
<th class="col-md-2">Last Update</th>
<th class="col-md-1 text-right">Actions</th>
<th class="col-md-2 text-right">Actions</th>
</thead>
{{#events}}
<tr>
<tr data-device-id="{{.getId()}}">
<td><a href="?id={{.getId()}}">{{.getName()}}</a></td>
<td>{{.getDeviceConfig().getClass().getSimpleName()}}</td>
<td>{{.getDeviceData()}}</td>
<td><span class="timestamp">{{.getDeviceData().getTimestamp()}}</span></td>
<td class="timestamp">{{.getDeviceData().getTimestamp()}}</td>
<td>
<form method="POST">
<input type="hidden" name="action" value="modify">
@ -44,4 +44,25 @@
$(this).closest('form').submit();
});
});
// Auto update
setInterval(function() {
fetch('/api/event')
.then(response => response.json())
.then(data => {
var table = document.getElementById('event-device-table');
for (const row of table.rows) {
var dataItem = data.find(item => item.id == row.dataset.deviceId);
if (dataItem) {
row.cells[2].innerHTML = dataItem.data?.valueStr;
row.cells[3].innerHTML = dataItem.data?.timestamp;
$(row.cells[3]).relTimestamp();
$(row.cells[4].querySelector('[type="checkbox"]')).bootstrapSwitch('state', dataItem.data?.value === 1, true);
}
}
});
}, 3000);
</script>

View file

@ -101,26 +101,32 @@ function createChart(elementId, url, updateTime=-1){
});
}
function updateChart(chart, url, updateTime=-1){
console.log('Updating chart: '+chart.element.id);
$.getJSON(url, function(json){
console.log('Updating chart: ' + chart.element.id);
$.getJSON(url, function(json) {
chart.load(getChartData(json));
});
if (updateTime > 0)
setTimeout(function(){ updateChart(chart, url, updateTime); }, updateTime);
if (updateTime > 0) {
setTimeout(function() {
updateChart(chart, url, updateTime);
}, updateTime);
}
}
function getChartData(json){
var dataXaxis = {};
var dataYaxis = {};
var data = [];
var labels = [];
json.forEach(function(sensor, i) {
var index = 'data'+i;
labels[index] = sensor.user +": "+ sensor.name;
dataXaxis[index] = 'data'+i+'x';
data.push([index+'x'].concat(sensor.timestamps));
data.push([index].concat(sensor.data));
if (sensor.type == "PowerConsumptionSensorData")
json.forEach(function(sensor, i) {
var index = 'data' + i;
labels[index] = sensor.user + ': ' + sensor.name;
dataXaxis[index] = 'data' + i + 'x';
data.push([index + 'x'].concat(sensor.aggregate.timestamps));
data.push([index].concat(sensor.aggregate.data));
if (sensor.type == 'PowerConsumptionSensorData')
dataYaxis[index] = 'y';
else //if (sensor.type == "TemperatureSensorData")
dataYaxis[index] = 'y2';

View file

@ -136,7 +136,7 @@
function drawMap(){
// Get map data
$.getJSON("/data/map?action=getdata", function(json){
$.getJSON("/api/map?action=getdata", function(json){
// reset map
svg.clear();
@ -183,7 +183,7 @@
$.ajax({
async: false,
dataType: "json",
url: "/data/map?",
url: "/api/map?",
data: {
action: "save",
id: element.attr("device-id"),

View file

@ -93,8 +93,8 @@
<script>
$(function(){
createChart("#min-chart", "/data/sensor?aggr=minute&id={{sensor.getId()}}", 5*60*1000);
createChart("#hour-chart", "/data/sensor?aggr=hour&id={{sensor.getId()}}", 60*60*1000);
createChart("#week-chart", "/data/sensor?aggr=week&id={{sensor.getId()}}", 7*24*60*60*1000);
createChart("#min-chart", "/api/sensor?aggr=minute&id={{sensor.getId()}}", 5*60*1000);
createChart("#hour-chart", "/api/sensor?aggr=hour&id={{sensor.getId()}}", 60*60*1000);
createChart("#week-chart", "/api/sensor?aggr=week&id={{sensor.getId()}}", 7*24*60*60*1000);
});
</script>

View file

@ -5,7 +5,7 @@
<div class="panel-heading">Local Sensors</div>
<div class="panel-body">
<table class="table table-hover table-condensed">
<table id="sensor-device-table" class="table table-hover table-condensed">
<thead>
<th class="col-md-4">Name</th>
<th class="col-md-3">Type</th>
@ -13,11 +13,11 @@
<th class="col-md-2">Last Update</th>
</thead>
{{#sensors}}
<tr>
<tr data-device-id="{{.getId()}}">
<td><a href="?id={{.getId()}}">{{.getName()}}</a></td>
<td>{{.getDeviceConfig().getClass().getSimpleName()}}</td>
<td>{{.getDeviceData()}}</td>
<td><span class="timestamp">{{.getDeviceData().getTimestamp()}}</span></td>
<td class="timestamp">{{.getDeviceData().getTimestamp()}}</td>
</tr>
{{/sensors}}
</table>
@ -31,4 +31,25 @@
$(this).closest('form').submit();
});
});
// Auto update
setInterval(function() {
fetch('/api/sensor')
.then(response => response.json())
.then(data => {
var table = document.getElementById('sensor-device-table');
for (const row of table.rows) {
var dataItem = data.find(item => item.id == row.dataset.deviceId);
if (dataItem) {
row.cells[2].innerHTML = dataItem.data?.valueStr;
row.cells[3].innerHTML = dataItem.data?.timestamp;
$(row.cells[3]).relTimestamp();
}
}
});
}, 3000);
</script>

View file

@ -6,6 +6,7 @@ import se.hal.util.HalDeviceChangeListener;
import zutil.db.DBConnection;
import zutil.db.bean.DBBean;
import zutil.log.LogUtil;
import zutil.parser.DataNode;
import zutil.parser.json.JSONParser;
import zutil.parser.json.JSONWriter;
import zutil.ui.conf.Configurator;
@ -214,4 +215,35 @@ public abstract class HalAbstractDevice<V extends HalAbstractDevice, C extends H
public List<HalDeviceReportListener> getReportListeners() {
return deviceListeners;
}
/**
* @return a DataNode object containing general Device information that can be written in JSON format.
*/
public DataNode getDataNode() {
DataNode deviceNode = new DataNode(DataNode.DataType.Map);
deviceNode.set("id", getId());
deviceNode.set("name", getName());
deviceNode.set("user", getUser().getUsername());
deviceNode.set("x", getX());
deviceNode.set("y", getY());
if (getDeviceConfig() != null) {
DataNode configNode = deviceNode.set("config", DataNode.DataType.Map);
configNode.set("type", getDeviceConfig().getClass().getSimpleName());
configNode.set("typeData", getDeviceConfig().getDeviceDataClass().getSimpleName());
for (Configurator.ConfigurationParam param : getDeviceConfigurator().getConfiguration()) {
configNode.set(param.getName(), param.getString());
}
}
if (getDeviceData() != null) {
DataNode dataNode = deviceNode.set("data", DataNode.DataType.Map);
dataNode.set("value", getDeviceData().getData());
dataNode.set("valueStr", getDeviceData().toString());
dataNode.set("timestamp", getDeviceData().getTimestamp());
}
return deviceNode;
}
}

View file

@ -0,0 +1,72 @@
package se.hal.page.api;
import se.hal.HalContext;
import se.hal.daemon.SensorDataAggregatorDaemon;
import se.hal.intf.HalJsonPage;
import se.hal.struct.Event;
import zutil.ArrayUtil;
import zutil.ObjectUtil;
import zutil.db.DBConnection;
import zutil.log.LogUtil;
import zutil.parser.DataNode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
/**
* Available HTTP Get Request parameters:
* <pre>
* Event filtering parameters:
* id: comma separated numeric id for specific events
* type: event data type name
* </pre>
*/
public class EventJsonPage extends HalJsonPage {
private static final Logger logger = LogUtil.getLogger();
public EventJsonPage() {
super("api/event");
}
@Override
public DataNode jsonRespond(
Map<String, Object> session,
Map<String, String> cookie,
Map<String, String> request) throws Exception{
DBConnection db = HalContext.getDB();
DataNode root = new DataNode(DataNode.DataType.List);
// Get sensors
String[] req_ids = new String[0];
if (request.get("id") != null)
req_ids = request.get("id").split(",");
String req_type = request.get("type");
List<Event> events = new ArrayList<>();
for (Event event : Event.getLocalEvents(db)) {
if (ArrayUtil.contains(req_ids, "" + event.getId())) { // id filtering
events.add(event);
} else if (!ObjectUtil.isEmpty(req_type) &&
event.getDeviceConfig().getDeviceDataClass().getSimpleName().contains(req_type)) { // device type filtering
events.add(event);
} else { // no options defined, then add all sensors
events.add(event);
}
}
// Generate DataNode
for (Event sensor : events) {
DataNode deviceNode = sensor.getDataNode();
root.add(deviceNode);
}
return root;
}
}

View file

@ -1,4 +1,4 @@
package se.hal.page;
package se.hal.page.api;
import se.hal.HalContext;
import se.hal.intf.HalAbstractDevice;
@ -20,7 +20,7 @@ public class MapJsonPage extends HalJsonPage {
private static final Logger logger = LogUtil.getLogger();
public MapJsonPage() {
super("data/map");
super("api/map");
}

View file

@ -1,4 +1,4 @@
package se.hal.page;
package se.hal.page.api;
import se.hal.HalContext;
import se.hal.daemon.SensorDataAggregatorDaemon;
@ -7,6 +7,7 @@ import se.hal.struct.Sensor;
import se.hal.util.AggregateDataListSqlResult;
import se.hal.util.UTCTimeUtility;
import zutil.ArrayUtil;
import zutil.ObjectUtil;
import zutil.db.DBConnection;
import zutil.log.LogUtil;
import zutil.parser.DataNode;
@ -32,7 +33,7 @@ public class SensorJsonPage extends HalJsonPage {
public SensorJsonPage() {
super("data/sensor");
super("api/sensor");
}
@Override
@ -44,27 +45,25 @@ public class SensorJsonPage extends HalJsonPage {
DBConnection db = HalContext.getDB();
DataNode root = new DataNode(DataNode.DataType.List);
//// Get sensors
String[] req_ids = null;
// Get sensors
String[] req_ids = new String[0];
if (request.get("id") != null)
req_ids = request.get("id").split(",");
String req_type = request.get("type");
List<Sensor> sensors = new ArrayList<>();
for (Sensor sensor : Sensor.getSensors(db)) {
if (sensor.getDeviceConfig() == null) // Show all sensors for now
continue;
if (req_ids == null && req_type==null) // no options defined, then add all sensors
if (ArrayUtil.contains(req_ids, "" + sensor.getId())) { // id filtering
sensors.add(sensor);
else if (req_ids != null && ArrayUtil.contains(req_ids, ""+sensor.getId())) // id filtering
} else if (!ObjectUtil.isEmpty(req_type) &&
sensor.getDeviceConfig().getDeviceDataClass().getSimpleName().contains(req_type)) { // device type filtering
sensors.add(sensor);
else if (req_type != null && !req_type.isEmpty() &&
sensor.getDeviceConfig().getDeviceDataClass().getSimpleName().contains(req_type)) // device type filtering
} else { // no options defined, then add all sensors
sensors.add(sensor);
}
}
//// Figure out aggregation period
// Figure out aggregation period
SensorDataAggregatorDaemon.AggregationPeriodLength aggrType = null;
long aggrLength = -1;
if (request.get("aggr") != null) {
@ -88,34 +87,33 @@ public class SensorJsonPage extends HalJsonPage {
}
}
/// Generate DataNode
// Generate DataNode
for (Sensor sensor : sensors) {
DataNode deviceNode = new DataNode(DataNode.DataType.Map);
deviceNode.set("id", sensor.getId());
deviceNode.set("name", sensor.getName());
deviceNode.set("user", sensor.getUser().getUsername());
deviceNode.set("type", sensor.getDeviceConfig().getDeviceDataClass().getSimpleName());
deviceNode.set("x", sensor.getX());
deviceNode.set("y", sensor.getY());
DataNode deviceNode = sensor.getDataNode();
if (aggrLength > 0) {
addAggregateDataToDataNode(deviceNode, aggrLength,
DataNode aggregateNode = getAggregateDataNode(aggrLength,
AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, aggrType, aggrLength));
deviceNode.set("aggregate", aggregateNode);
}
root.add(deviceNode);
}
return root;
}
private void addAggregateDataToDataNode(DataNode deviceNode, long endTime, List<AggregateDataListSqlResult.AggregateData> dataList) {
private DataNode getAggregateDataNode(long endTime, List<AggregateDataListSqlResult.AggregateData> dataList) {
DataNode aggregateNode = new DataNode(DataNode.DataType.Map);
DataNode timestampNode = new DataNode(DataNode.DataType.List);
DataNode dataNode = new DataNode(DataNode.DataType.List);
// end timestamp
if (endTime != UTCTimeUtility.INFINITY) {
timestampNode.add(System.currentTimeMillis() - endTime);
dataNode.add((String)null);
}
// actual data
for (AggregateDataListSqlResult.AggregateData data : dataList) {
timestampNode.add(data.timestamp);
@ -124,12 +122,15 @@ public class SensorJsonPage extends HalJsonPage {
else
dataNode.add(data.data);
}
// start timestamp
timestampNode.add(System.currentTimeMillis());
dataNode.add((String)null);
deviceNode.set("timestamps", timestampNode);
deviceNode.set("data", dataNode);
aggregateNode.set("timestamps", timestampNode);
aggregateNode.set("data", dataNode);
return aggregateNode;
}
}

View file

@ -12,8 +12,9 @@
{"se.hal.intf.HalDaemon": "se.hal.daemon.SensorDataAggregatorDaemon"},
{"se.hal.intf.HalDaemon": "se.hal.daemon.SensorDataCleanupDaemon"},
{"se.hal.intf.HalJsonPage": "se.hal.page.MapJsonPage"},
{"se.hal.intf.HalJsonPage": "se.hal.page.SensorJsonPage"},
{"se.hal.intf.HalJsonPage": "se.hal.page.api.MapJsonPage"},
{"se.hal.intf.HalJsonPage": "se.hal.page.api.EventJsonPage"},
{"se.hal.intf.HalJsonPage": "se.hal.page.api.SensorJsonPage"},
{"se.hal.intf.HalWebPage": "se.hal.page.MapWebPage"},
{"se.hal.intf.HalWebPage": "se.hal.page.SensorOverviewWebPage"},

View file

@ -20,10 +20,10 @@
<script>
$(function(){
createChart("#minute-power-chart", "/data/sensor?aggr=minute", 5*60*1000);
createChart("#hour-power-chart", "/data/sensor?aggr=hour", 60*60*1000);
createChart("#day-power-chart", "/data/sensor?aggr=day", 24*60*60*1000);
createChart("#week-power-chart", "/data/sensor?aggr=week", 7*24*60*60*1000);
createChart("#minute-power-chart", "/api/sensor?aggr=minute", 5*60*1000);
createChart("#hour-power-chart", "/api/sensor?aggr=hour", 60*60*1000);
createChart("#day-power-chart", "/api/sensor?aggr=day", 24*60*60*1000);
createChart("#week-power-chart", "/api/sensor?aggr=week", 7*24*60*60*1000);
});
</script>