Introduction of JS based pages
This commit is contained in:
parent
c4cb9ff458
commit
7d64788154
24 changed files with 24446 additions and 30 deletions
|
|
@ -18,8 +18,8 @@ subprojects {
|
||||||
apply plugin: 'java-library'
|
apply plugin: 'java-library'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'se.koc:zutil:1.0.314'
|
//implementation 'se.koc:zutil:1.0.314'
|
||||||
//implementation 'se.koc:zutil:1.0.0-SNAPSHOT'
|
implementation 'se.koc:zutil:1.0.0-SNAPSHOT'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
testImplementation 'org.hamcrest:hamcrest-core:2.2'
|
testImplementation 'org.hamcrest:hamcrest-core:2.2'
|
||||||
|
|
|
||||||
0
gradlew
vendored
Executable file → Normal file
0
gradlew
vendored
Executable file → Normal file
40
hal-core/resources/web/js/hal.js
vendored
40
hal-core/resources/web/js/hal.js
vendored
|
|
@ -22,7 +22,7 @@ $(function(){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var obj = {};
|
let obj = {};
|
||||||
$.each(this[0].attributes, function() {
|
$.each(this[0].attributes, function() {
|
||||||
if(this.specified) {
|
if(this.specified) {
|
||||||
obj[this.name] = this.value;
|
obj[this.name] = this.value;
|
||||||
|
|
@ -42,26 +42,32 @@ $(function(){
|
||||||
// Converts all timestamps to human readable time and date
|
// Converts all timestamps to human readable time and date
|
||||||
$.fn.relTimestamp = function() {
|
$.fn.relTimestamp = function() {
|
||||||
return this.each(function() {
|
return this.each(function() {
|
||||||
var timestamp = parseInt($(this).text());
|
let timestamp = parseInt($(this).text());
|
||||||
var timestampNow = Date.now();
|
|
||||||
var timeDiff = timestampNow - timestamp;
|
|
||||||
|
|
||||||
if(timeDiff < 10 * 60 * 1000) // less than 10 min
|
$(this).text(getRelTimestamp(timestamp));
|
||||||
$(this).text(moment(timestamp).fromNow());
|
|
||||||
else if(timeDiff < 24 * 60 * 60 * 1000) // less than 24 hours
|
|
||||||
$(this).text(moment(timestamp).fromNow() + " ("+moment(timestamp).format("HH:mm")+")");
|
|
||||||
else
|
|
||||||
$(this).text(moment(timestamp).format("YYYY-MM-DD HH:mm"));
|
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Converts all timestamps to human readable time and date
|
||||||
|
function getRelTimestamp(timestamp) {
|
||||||
|
let timestampNow = Date.now();
|
||||||
|
let timeDiff = timestampNow - timestamp;
|
||||||
|
|
||||||
|
if(timeDiff < 10 * 60 * 1000) // less than 10 min
|
||||||
|
return moment(timestamp).fromNow();
|
||||||
|
else if(timeDiff < 24 * 60 * 60 * 1000) // less than 24 hours
|
||||||
|
return moment(timestamp).fromNow() + " ("+moment(timestamp).format("HH:mm")+")";
|
||||||
|
else
|
||||||
|
return moment(timestamp).format("YYYY-MM-DD HH:mm");
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// Chart functions
|
// Chart functions
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
|
||||||
function createChart(elementId, url, updateTime=-1){
|
function createChart(elementId, url, updateTime=-1){
|
||||||
var tickConf = {count: 20};
|
let tickConf = {count: 20};
|
||||||
if (updateTime < 60*60*1000)
|
if (updateTime < 60*60*1000)
|
||||||
tickConf['format'] = '%H:%M';
|
tickConf['format'] = '%H:%M';
|
||||||
else if (updateTime < 24*60*60*1000)
|
else if (updateTime < 24*60*60*1000)
|
||||||
|
|
@ -116,10 +122,10 @@ function updateChart(chart, url, updateTime=-1){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getChartData(json){
|
function getChartData(json){
|
||||||
var dataXaxis = {};
|
let dataXaxis = {};
|
||||||
var dataYaxis = {};
|
let dataYaxis = {};
|
||||||
var data = [];
|
let data = [];
|
||||||
var labels = [];
|
let labels = [];
|
||||||
|
|
||||||
json.forEach(function(sensor, i) {
|
json.forEach(function(sensor, i) {
|
||||||
var index = 'data' + i;
|
var index = 'data' + i;
|
||||||
|
|
@ -166,8 +172,8 @@ function initDynamicModalForm(modalId, formTemplateId = null, templateID = null)
|
||||||
|
|
||||||
// click event
|
// click event
|
||||||
$("#" + modalId).on('show.bs.modal', function (event) {
|
$("#" + modalId).on('show.bs.modal', function (event) {
|
||||||
var button = $(event.relatedTarget);
|
let button = $(event.relatedTarget);
|
||||||
var modal = $(this);
|
let modal = $(this);
|
||||||
|
|
||||||
modal.find(" input, select").val('').change(); // Reset all inputs
|
modal.find(" input, select").val('').change(); // Reset all inputs
|
||||||
|
|
||||||
|
|
|
||||||
39
hal-core/resources/web/js/vue/components/AlertComponent.js
vendored
Normal file
39
hal-core/resources/web/js/vue/components/AlertComponent.js
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
export default {
|
||||||
|
props: [
|
||||||
|
"level", // ERROR, WARNING, SUCCESS, INFO
|
||||||
|
"message"
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return { }
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
levelClass() {
|
||||||
|
switch(this.level) {
|
||||||
|
case "ERROR": return "alert-danger";
|
||||||
|
case "WARNING": return "alert-warning";
|
||||||
|
case "SUCCESS": return "alert-success";
|
||||||
|
case "INFO":
|
||||||
|
default: return "alert-info";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
levelIcon() {
|
||||||
|
switch(this.level) {
|
||||||
|
case "ERROR": return "glyphicon-minus-sign";
|
||||||
|
case "WARNING": return "glyphicon-warning-sign";
|
||||||
|
case "SUCCESS": return "glyphicon-ok-circle";
|
||||||
|
case "INFO":
|
||||||
|
default: return "glyphicon-info-sign";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div id="" data-alert-id="" class="alert alert-dismissible fade in" :class="levelClass">
|
||||||
|
<button type="button" class="close" data-dismiss="alert">
|
||||||
|
<span>×</span>
|
||||||
|
</button>
|
||||||
|
<span class="glyphicon" :class="levelIcon"></span>
|
||||||
|
<strong class="alert-title">{{message}}</strong>
|
||||||
|
<span class="alert-description"></span>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
29
hal-core/resources/web/js/vue/components/AlertListComponent.js
vendored
Normal file
29
hal-core/resources/web/js/vue/components/AlertListComponent.js
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import Alert from 'AlertComponent'
|
||||||
|
import { alertStore } from 'AlertStore'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
alertStore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Alert,
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
alertStore.enableAutoLoad(true);
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div v-if="alertStore.loading" class="modal fade in" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="false">
|
||||||
|
<div class="modal-dialog modal-sm">
|
||||||
|
<div class="modal-content modal-body">
|
||||||
|
<i class="glyphicon glyphicon-refresh glyphicon-refresh-animate"></i>
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="alert-container">
|
||||||
|
<Alert v-for="a in alertStore.alerts" v-bind="a" />
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
17
hal-core/resources/web/js/vue/components/App.js
vendored
Normal file
17
hal-core/resources/web/js/vue/components/App.js
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import AlertList from 'AlertListComponent'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
AlertList,
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class="container col-md-12">
|
||||||
|
<AlertList />
|
||||||
|
|
||||||
|
<router-view></router-view>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
35
hal-core/resources/web/js/vue/components/EventActionComponent.js
vendored
Normal file
35
hal-core/resources/web/js/vue/components/EventActionComponent.js
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import {eventStore} from 'EventStore'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
'id': {default: 0},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
let internalEvent = eventStore.getEvent(this.id);
|
||||||
|
return {
|
||||||
|
event: internalEvent,
|
||||||
|
checkboxChecked: internalEvent.data?.valueStr == 'ON',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="action" value="modify">
|
||||||
|
<input type="hidden" name="action-id" :value="event.id">
|
||||||
|
|
||||||
|
<div class="btn-toolbar pull-right">
|
||||||
|
<template v-if="event.dataType === 'ColorEventData'">
|
||||||
|
<input type="hidden" name="type" value="color">
|
||||||
|
<input type="color" name="data" onchange="this.form.submit()" :value="event.data?.valueStr">
|
||||||
|
</template>
|
||||||
|
<template v-else-if="event.dataType === 'LevelEventData'">
|
||||||
|
<input type="hidden" name="type" value="level">
|
||||||
|
<input class="styled-slider slider-progress" type="range" name="data" min="0" max="100" step="10" onchange="this.form.submit()" :value="event.data?.value * 100">
|
||||||
|
</template>
|
||||||
|
<template v-else-if="event.dataType === 'OnOffEventData'">
|
||||||
|
<input type="hidden" name="type" value="on-off">
|
||||||
|
<input class="switch" type="checkbox" name="data" onchange="this.form.submit()" v-model="checkboxChecked">
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
`
|
||||||
|
}
|
||||||
88
hal-core/resources/web/js/vue/components/EventDetailPageComponent.js
vendored
Normal file
88
hal-core/resources/web/js/vue/components/EventDetailPageComponent.js
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
import {eventStore} from 'EventStore'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
var id = this.$route.params.id;
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
event: eventStore.getEvent(id),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<h1 class="page-header">Details for <a href="?id={{event.id}}">{{event.name}}</a></h1>
|
||||||
|
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="panel panel-default drop-shadow">
|
||||||
|
<div class="panel-heading">Configuration</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table table-hover table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-right">Event ID:</th>
|
||||||
|
<th>{{event.id}</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="text-right">Name:</th>
|
||||||
|
<th>{{event.name}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-right">Type:</th>
|
||||||
|
<td>{{event.configType}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="text-right">Owner:</th>
|
||||||
|
<td>{{event.owner}} <p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="text-right">State:</th>
|
||||||
|
<td>
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="action" value="modify">
|
||||||
|
<input type="hidden" name="action-id" value="{{event.id}}">
|
||||||
|
|
||||||
|
<div class="btn-toolbar pull-left">
|
||||||
|
<input class="toggle-switch" type="checkbox" name="enabled"
|
||||||
|
data-on-color="danger">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr v-for="(confName, confValue) in event.config">
|
||||||
|
<th class="text-right">{{name}}:</th>
|
||||||
|
<td>{{confValue}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-7">
|
||||||
|
<div class="panel panel-default drop-shadow">
|
||||||
|
<div class="panel-heading">History data</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table table-hover table-condensed">
|
||||||
|
<thead>
|
||||||
|
<th class="col-md-6">Timestamp</th>
|
||||||
|
<th class="col-md-2">Raw Data</th>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><span class="timestamp">{{event.data.timestamp}}</span></td>
|
||||||
|
<td>{{event.data.value}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(function (){
|
||||||
|
$(".toggle-switch").on("switchChange.bootstrapSwitch", function (event, state) {
|
||||||
|
$(this).closest('form').submit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
`
|
||||||
|
}
|
||||||
21
hal-core/resources/web/js/vue/components/EventOverviewPageComponent.js
vendored
Normal file
21
hal-core/resources/web/js/vue/components/EventOverviewPageComponent.js
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import EventTable from 'EventTableComponent'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
EventTable,
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<h1 class="page-header">Event Overview</h1>
|
||||||
|
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="panel panel-default drop-shadow">
|
||||||
|
<div class="panel-heading">Local Events</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<EventTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
34
hal-core/resources/web/js/vue/components/EventTableComponent.js
vendored
Normal file
34
hal-core/resources/web/js/vue/components/EventTableComponent.js
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import {eventStore} from 'EventStore'
|
||||||
|
import EventTableRow from 'EventTableRowComponent'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
eventStore,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
EventTableRow,
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
eventStore.enableAutoLoad(true);
|
||||||
|
},
|
||||||
|
unmounted() {
|
||||||
|
eventStore.enableAutoLoad(false);
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<table id="event-device-table2" class="table table-hover table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="col-md-4">Name</th>
|
||||||
|
<th class="col-md-3">Type</th>
|
||||||
|
<th class="col-md-1">Data</th>
|
||||||
|
<th class="col-md-2">Last Update</th>
|
||||||
|
<th class="col-md-2 text-right">Actions</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<EventTableRow v-for="e in eventStore.events" :key="e.id" :id="e.id" />
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
`
|
||||||
|
}
|
||||||
29
hal-core/resources/web/js/vue/components/EventTableRowComponent.js
vendored
Normal file
29
hal-core/resources/web/js/vue/components/EventTableRowComponent.js
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import {eventStore} from 'EventStore'
|
||||||
|
import EventAction from 'EventActionComponent'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
'id': {default: 0},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
let event = eventStore.getEvent(this.id)
|
||||||
|
return {
|
||||||
|
event: event,
|
||||||
|
timestamp: getRelTimestamp(event.data.timestamp)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
EventAction
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<tr :data-device-id="event.id">
|
||||||
|
<td><a :href="'?id=' + event.id">{{ event.name }}</a></td>
|
||||||
|
<td>{{ event.configType }}</td>
|
||||||
|
<td>{{ event.data.valueStr }}</td>
|
||||||
|
<td class="timestamp">{{ timestamp }}</td>
|
||||||
|
<td>
|
||||||
|
<EventAction :id="event.id"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`
|
||||||
|
}
|
||||||
46
hal-core/resources/web/js/vue/stores/AlertStore.js
vendored
Normal file
46
hal-core/resources/web/js/vue/stores/AlertStore.js
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
|
export const alertStore = reactive({
|
||||||
|
loading: false,
|
||||||
|
alerts: [],
|
||||||
|
pollTimer: null,
|
||||||
|
|
||||||
|
enableAutoLoad(enabled = true) {
|
||||||
|
if (enabled && this.pollTimer !== null) {
|
||||||
|
this.pollTimer = setInterval(function() {
|
||||||
|
load();
|
||||||
|
}, 3000);
|
||||||
|
} else {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
this.pollTimer == null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
load() {
|
||||||
|
fetch('/api/alert?action=poll')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
data.forEach(alert => {
|
||||||
|
alert.source = "server";
|
||||||
|
alertStore.alerts.push(alert);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setLoading(l = true) {
|
||||||
|
this.loading = l;
|
||||||
|
},
|
||||||
|
|
||||||
|
addAlertInfo(message) {
|
||||||
|
this.alerts.push({source: "local", level: "info", message: message});
|
||||||
|
},
|
||||||
|
addAlertSuccess(message) {
|
||||||
|
this.alerts.push({source: "local", level: "success", message: message});
|
||||||
|
},
|
||||||
|
addAlertWarning(message) {
|
||||||
|
this.alerts.push({source: "local", level: "warning", message: message});
|
||||||
|
},
|
||||||
|
AddAlertError(message) {
|
||||||
|
this.alerts.push({source: "local", level: "danger", message: message});
|
||||||
|
},
|
||||||
|
});
|
||||||
49
hal-core/resources/web/js/vue/stores/EventStore.js
vendored
Normal file
49
hal-core/resources/web/js/vue/stores/EventStore.js
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
import { alertStore } from 'AlertStore'
|
||||||
|
|
||||||
|
export const eventStore = reactive({
|
||||||
|
events: [],
|
||||||
|
pollTimerId: null,
|
||||||
|
pollInterval: 10000, // 10 sec
|
||||||
|
|
||||||
|
enableAutoLoad(enabled = true) {
|
||||||
|
if (enabled) {
|
||||||
|
if (this.pollTimerId !== null)
|
||||||
|
return; // Timer already initialized
|
||||||
|
eventStore.load();
|
||||||
|
|
||||||
|
this.pollTimerId = setInterval(function() {
|
||||||
|
eventStore.load();
|
||||||
|
}, this.pollInterval);
|
||||||
|
} else {
|
||||||
|
clearInterval(this.pollTimerId);
|
||||||
|
this.pollTimerId == null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
load() {
|
||||||
|
alertStore.setLoading(true);
|
||||||
|
|
||||||
|
fetch('/api/event', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
if (json['error'] != null) {
|
||||||
|
alertStore.alertError(json['error']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventStore.events = json;
|
||||||
|
alertStore.setLoading(false);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getEvent(id) {
|
||||||
|
let event = eventStore.events.find(event => id == event.id);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
});
|
||||||
44
hal-core/resources/web/js/vue/stores/SensorStore.js
vendored
Normal file
44
hal-core/resources/web/js/vue/stores/SensorStore.js
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
import { alertStore } from 'AlertStore'
|
||||||
|
|
||||||
|
export const sensorStore = reactive({
|
||||||
|
sensors: {},
|
||||||
|
pollTimerId: null,
|
||||||
|
pollInterval: 10000, // 10 sec
|
||||||
|
|
||||||
|
enableAutoLoad(enabled = true) {
|
||||||
|
if (enabled) {
|
||||||
|
if (this.pollTimerId !== null)
|
||||||
|
return; // Timer already initialized
|
||||||
|
sensorStore.load();
|
||||||
|
|
||||||
|
this.pollTimerId = setInterval(function() {
|
||||||
|
sensorStore.load();
|
||||||
|
}, this.pollInterval);
|
||||||
|
} else {
|
||||||
|
clearInterval(this.pollTimerId);
|
||||||
|
this.pollTimerId == null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
load() {
|
||||||
|
alertStore.setLoading(true);
|
||||||
|
|
||||||
|
fetch('/api/sensor', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
if (json['error'] != null) {
|
||||||
|
alertStore.alertError(json['error']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sensorStore.sensors = json;
|
||||||
|
alertStore.setLoading(false);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
});
|
||||||
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.js
vendored
Normal file
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.prod.js
vendored
Normal file
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.prod.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
16633
hal-core/resources/web/js/vue/vue.esm-browser.js
vendored
Normal file
16633
hal-core/resources/web/js/vue/vue.esm-browser.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
11
hal-core/resources/web/js/vue/vue.esm-browser.prod.js
vendored
Normal file
11
hal-core/resources/web/js/vue/vue.esm-browser.prod.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -19,11 +19,12 @@
|
||||||
<script src="js/lib/bootstrap-switch.min.js"></script>
|
<script src="js/lib/bootstrap-switch.min.js"></script>
|
||||||
<script src="js/lib/moment.js"></script>
|
<script src="js/lib/moment.js"></script>
|
||||||
<script src="js/hal.js"></script>
|
<script src="js/hal.js"></script>
|
||||||
<script src="js/hal_alert.js"></script>
|
<!-- <script src="js/hal_alert.js"></script> -->
|
||||||
|
|
||||||
<!-- charts -->
|
<!-- charts -->
|
||||||
<script src="js/lib/d3.js"></script>
|
<script src="js/lib/d3.min.js"></script>
|
||||||
<script src="js/lib/c3.js"></script>
|
<script src="js/lib/force-graph.min.js"></script>
|
||||||
|
<script src="js/lib/c3.min.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
@ -39,10 +40,52 @@
|
||||||
{{/side_navigation}}
|
{{/side_navigation}}
|
||||||
{{^side_navigation}}<div class="main">{{/side_navigation}}
|
{{^side_navigation}}<div class="main">{{/side_navigation}}
|
||||||
<div id="alert-container"></div>
|
<div id="alert-container"></div>
|
||||||
|
|
||||||
|
<div id="app"></div>
|
||||||
|
|
||||||
{{content}}
|
{{content}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="importmap">
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"vue": "./js/vue/vue.esm-browser.js",
|
||||||
|
"vue-router": "./js/vue/vue-router.esm-browser.js",
|
||||||
|
"@vue/devtools-api": "https://unpkg.com/@vue/devtools-api@6.4.5/lib/esm/index.js",
|
||||||
|
|
||||||
|
{{#javascriptModules}}"{{.getModuleName()}}": "{{.getScriptPath()}}",
|
||||||
|
{{/javascriptModules}}
|
||||||
|
|
||||||
|
"App": "./js/vue/components/App.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
{{#javascriptPages}}import {{.getModuleName()}} from '{{.getModuleName()}}'
|
||||||
|
{{/javascriptPages}}
|
||||||
|
|
||||||
|
import App from 'App'
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes: [
|
||||||
|
{{#javascriptPages}}{ name: "{{.getModuleName()}}", path: "{{.getPage()}}", component: {{.getModuleName()}} },
|
||||||
|
{{/javascriptPages}}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
app.use(router);
|
||||||
|
|
||||||
|
app.mount('#app');
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ package se.hal;
|
||||||
import se.hal.daemon.HalExternalWebDaemon;
|
import se.hal.daemon.HalExternalWebDaemon;
|
||||||
import se.hal.intf.*;
|
import se.hal.intf.*;
|
||||||
import se.hal.intf.HalJavascriptModule.HalJsModule;
|
import se.hal.intf.HalJavascriptModule.HalJsModule;
|
||||||
|
import se.hal.page.EmptyWebPage;
|
||||||
import se.hal.page.StartupWebPage;
|
import se.hal.page.StartupWebPage;
|
||||||
import se.hal.struct.PluginConfig;
|
import se.hal.struct.PluginConfig;
|
||||||
import zutil.db.DBConnection;
|
import zutil.db.DBConnection;
|
||||||
|
|
@ -138,18 +139,22 @@ public class HalServer {
|
||||||
http.setDefaultPage(filePage);
|
http.setDefaultPage(filePage);
|
||||||
http.setPage("/", new HttpRedirectPage("/map"));
|
http.setPage("/", new HttpRedirectPage("/map"));
|
||||||
|
|
||||||
for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalApiEndpoint.class); it.hasNext(); )
|
|
||||||
registerPage(it.next());
|
|
||||||
for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalWebPage.class); it.hasNext(); )
|
|
||||||
registerPage(it.next());
|
|
||||||
for (Iterator<HalJavascriptModule> it = pluginManager.getSingletonIterator(HalJavascriptModule.class); it.hasNext(); ) {
|
for (Iterator<HalJavascriptModule> it = pluginManager.getSingletonIterator(HalJavascriptModule.class); it.hasNext(); ) {
|
||||||
HalJsModule[] jsModules = it.next().getJavascriptModules();
|
HalJsModule[] jsModules = it.next().getJavascriptModules();
|
||||||
|
|
||||||
if (jsModules != null) {
|
if (jsModules != null) {
|
||||||
for (HalJsModule module : jsModules)
|
for (HalJsModule module : jsModules) {
|
||||||
HalWebPage.addJavascriptModule(module);
|
HalWebPage.addJavascriptModule(module);
|
||||||
|
|
||||||
|
if (module instanceof HalJavascriptModule.HalJsModulePage)
|
||||||
|
registerPage(((HalJavascriptModule.HalJsModulePage) module).getPage(), new EmptyWebPage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalApiEndpoint.class); it.hasNext(); )
|
||||||
|
registerPage(it.next());
|
||||||
|
for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalWebPage.class); it.hasNext(); )
|
||||||
|
registerPage(it.next());
|
||||||
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
@ -202,6 +207,10 @@ public class HalServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<HalDaemon> getAllDaemons() {
|
||||||
|
return daemons;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the given page with the intranet Hal web server.
|
* Registers the given page with the intranet Hal web server.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
public abstract class HalWebPage implements HttpPage{
|
public abstract class HalWebPage implements HttpPage {
|
||||||
private static final String TEMPLATE_MAIN = HalContext.RESOURCE_WEB_ROOT + "/main_index.tmpl";
|
private static final String TEMPLATE_MAIN = HalContext.RESOURCE_WEB_ROOT + "/main_index.tmpl";
|
||||||
private static final String TEMPLATE_NAVIGATION = HalContext.RESOURCE_WEB_ROOT + "/main_nav.tmpl";
|
private static final String TEMPLATE_NAVIGATION = HalContext.RESOURCE_WEB_ROOT + "/main_nav.tmpl";
|
||||||
private static final String TEMPLATE_SIDE_NAVIGATION = HalContext.RESOURCE_WEB_ROOT + "/main_nav_side.tmpl";
|
private static final String TEMPLATE_SIDE_NAVIGATION = HalContext.RESOURCE_WEB_ROOT + "/main_nav_side.tmpl";
|
||||||
|
|
@ -83,7 +83,12 @@ public abstract class HalWebPage implements HttpPage{
|
||||||
main.set("side_navigation", subNavigationTemplate);
|
main.set("side_navigation", subNavigationTemplate);
|
||||||
main.set("javascriptModules", jsModules);
|
main.set("javascriptModules", jsModules);
|
||||||
main.set("javascriptPages", jsPages);
|
main.set("javascriptPages", jsPages);
|
||||||
main.set("content", httpRespond(session, cookie, request));
|
|
||||||
|
Templator body = httpRespond(session, cookie, request);
|
||||||
|
if (body == null)
|
||||||
|
main.set("content", "");
|
||||||
|
else
|
||||||
|
main.set("content", body);
|
||||||
|
|
||||||
out.print(main.compile());
|
out.print(main.compile());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
||||||
44
hal-core/src/se/hal/page/EmptyWebPage.java
Normal file
44
hal-core/src/se/hal/page/EmptyWebPage.java
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package se.hal.page;
|
||||||
|
|
||||||
|
import se.hal.EventControllerManager;
|
||||||
|
import se.hal.HalContext;
|
||||||
|
import se.hal.intf.HalEventData;
|
||||||
|
import se.hal.intf.HalWebPage;
|
||||||
|
import se.hal.struct.Event;
|
||||||
|
import se.hal.struct.devicedata.ColorEventData;
|
||||||
|
import se.hal.struct.devicedata.LevelEventData;
|
||||||
|
import se.hal.struct.devicedata.OnOffEventData;
|
||||||
|
import se.hal.util.DeviceNameComparator;
|
||||||
|
import se.hal.util.HistoryDataListSqlResult;
|
||||||
|
import se.hal.util.HistoryDataListSqlResult.HistoryData;
|
||||||
|
import zutil.ObjectUtil;
|
||||||
|
import zutil.db.DBConnection;
|
||||||
|
import zutil.io.file.FileUtil;
|
||||||
|
import zutil.log.LogUtil;
|
||||||
|
import zutil.parser.Templator;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A empty page with an empty body content generated from server side.
|
||||||
|
*/
|
||||||
|
public class EmptyWebPage extends HalWebPage {
|
||||||
|
|
||||||
|
public EmptyWebPage() {
|
||||||
|
super("empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Templator httpRespond(
|
||||||
|
Map<String, Object> session,
|
||||||
|
Map<String, String> cookie,
|
||||||
|
Map<String, String> request)
|
||||||
|
throws Exception{
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
package se.hal.page;
|
package se.hal.page;
|
||||||
|
|
||||||
import se.hal.intf.HalJavascriptModule;
|
import se.hal.intf.HalJavascriptModule;
|
||||||
|
import se.hal.intf.HalWebPage;
|
||||||
|
|
||||||
public class JavascriptModules implements HalJavascriptModule {
|
public class JavascriptModules implements HalJavascriptModule {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HalJsModule[] getJavascriptModules() {
|
public HalJsModule[] getJavascriptModules() {
|
||||||
|
HalWebPage.getRootNav().createSubNav("Events").createSubNav("/event_overview", "Overview");
|
||||||
|
|
||||||
return new HalJsModule[] {
|
return new HalJsModule[] {
|
||||||
new HalJsModule("AlertStore", "./js/vue/stores/AlertStore.js"),
|
new HalJsModule("AlertStore", "./js/vue/stores/AlertStore.js"),
|
||||||
new HalJsModule("EventStore", "./js/vue/stores/EventStore.js"),
|
new HalJsModule("EventStore", "./js/vue/stores/EventStore.js"),
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@
|
||||||
{"se.hal.intf.HalApiEndpoint": "se.hal.page.api.RoomApiEndpoint"},
|
{"se.hal.intf.HalApiEndpoint": "se.hal.page.api.RoomApiEndpoint"},
|
||||||
{"se.hal.intf.HalApiEndpoint": "se.hal.page.api.SensorApiEndpoint"},
|
{"se.hal.intf.HalApiEndpoint": "se.hal.page.api.SensorApiEndpoint"},
|
||||||
|
|
||||||
{"se.hal.intf.HalWebPage": "se.hal.page.EventOverviewWebPage"},
|
{"se.hal.intf.HalJavascriptModule": "se.hal.page.JavascriptModules"},
|
||||||
|
{"DISABLEDse.hal.intf.HalWebPage": "se.hal.page.EventOverviewWebPage"},
|
||||||
{"se.hal.intf.HalWebPage": "se.hal.page.EventConfigWebPage"},
|
{"se.hal.intf.HalWebPage": "se.hal.page.EventConfigWebPage"},
|
||||||
{"se.hal.intf.HalWebPage": "se.hal.page.PropertyConfigWebPage"},
|
{"se.hal.intf.HalWebPage": "se.hal.page.PropertyConfigWebPage"},
|
||||||
{"se.hal.intf.HalWebPage": "se.hal.page.PluginConfigWebPage"},
|
{"se.hal.intf.HalWebPage": "se.hal.page.PluginConfigWebPage"},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue