diff --git a/hal-core/resource/resource/web/api/docs.html b/hal-core/resource/resource/web/api/docs.html
new file mode 100644
index 00000000..a187ec73
--- /dev/null
+++ b/hal-core/resource/resource/web/api/docs.html
@@ -0,0 +1,25 @@
+
+
+
+Hal OpenAPI Documentation
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hal-core/resource/resource/web/api/openapi.json b/hal-core/resource/resource/web/api/openapi.json
new file mode 100644
index 00000000..1fd17ddc
--- /dev/null
+++ b/hal-core/resource/resource/web/api/openapi.json
@@ -0,0 +1,189 @@
+{
+ "components": {
+ "schemas": {
+ "sensorClass": {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "object",
+ "$ref": "#/components/schemas/dataClass"
+ },
+ "name": {"type": "string"},
+ "id": {"type": "integer"},
+ "map_x": {"type": "number"},
+ "map_y": {"type": "number"},
+ "user": {"type": "string"},
+ "config": {
+ "type": "object",
+ "$ref": "#/components/schemas/configClass"
+ },
+ "aggregate": {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ },
+ "timestamps": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ }
+ },
+ "eventClass": {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "object",
+ "$ref": "#/components/schemas/dataClass"
+ },
+ "name": {"type": "string"},
+ "id": {"type": "integer"},
+ "map_x": {"type": "number"},
+ "map_y": {"type": "number"},
+ "user": {"type": "string"},
+ "config": {
+ "type": "object",
+ "$ref": "#/components/schemas/configClass"
+ }
+ }
+ },
+
+ "configClass": {
+ "type": "object",
+ "properties": {
+ "typeConfig": {"type": "string"},
+ "typeData": {"type": "string"}
+ }
+ },
+ "dataClass": {
+ "type": "object",
+ "properties": {
+ "valueStr": {"type": "string"},
+ "value": {"type": "number"},
+ "timestamp": {"type": "integer"}
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "description": "Hal Server",
+ "url": "/api"
+ }
+ ],
+ "openapi": "3.0.1",
+ "paths": {
+ "/event": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/eventClass"
+ }
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "schema": {
+ "type": "integer"
+ },
+ "in": "query",
+ "name": "id",
+ "required": false
+ },
+ {
+ "schema": {
+ "type": "string"
+ },
+ "in": "query",
+ "name": "typeConfig",
+ "required": false
+ },
+ {
+ "schema": {
+ "type": "string"
+ },
+ "in": "query",
+ "name": "typeData",
+ "required": false
+ }
+ ]
+ }
+ },
+ "/sensor": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/sensorClass"
+ }
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "schema": {
+ "type": "integer"
+ },
+ "in": "query",
+ "name": "id",
+ "required": false
+ },
+ {
+ "schema": {
+ "type": "string"
+ },
+ "in": "query",
+ "name": "typeConfig",
+ "required": false
+ },
+ {
+ "schema": {
+ "type": "string"
+ },
+ "in": "query",
+ "name": "typeData",
+ "required": false
+ },
+ {
+ "schema": {
+ "type": "string",
+ "enum": [
+ "min",
+ "hour",
+ "day",
+ "week",
+ ]
+ },
+ "in": "query",
+ "name": "aggregation",
+ "required": false
+ }
+ ]
+ }
+ }
+ },
+ "info": {
+ "description": "This API allows developers and external tools to interface to Hal data and trigger different actions.",
+ "title": "Hal REST API",
+ "version": ""
+ }
+}
\ No newline at end of file
diff --git a/hal-core/resource/resource/web/sensor_detail.tmpl b/hal-core/resource/resource/web/sensor_detail.tmpl
index aaa2fb72..f6979784 100644
--- a/hal-core/resource/resource/web/sensor_detail.tmpl
+++ b/hal-core/resource/resource/web/sensor_detail.tmpl
@@ -93,8 +93,8 @@
\ No newline at end of file
diff --git a/hal-core/resource/resource/web/startup.tmpl b/hal-core/resource/resource/web/startup.tmpl
index 457d3223..9a186300 100644
--- a/hal-core/resource/resource/web/startup.tmpl
+++ b/hal-core/resource/resource/web/startup.tmpl
@@ -5,8 +5,8 @@
HAL is initializing...
-
-
+
+
diff --git a/hal-core/src/se/hal/HalServer.java b/hal-core/src/se/hal/HalServer.java
index a303b03e..a51adeba 100644
--- a/hal-core/src/se/hal/HalServer.java
+++ b/hal-core/src/se/hal/HalServer.java
@@ -140,7 +140,7 @@ public class HalServer {
http.setPage("/", new HttpRedirectPage("/map"));
http.setPage(HalAlertManager.getInstance().getUrl(), HalAlertManager.getInstance());
- for (Iterator it = pluginManager.getSingletonIterator(HalJsonPage.class); it.hasNext(); )
+ for (Iterator it = pluginManager.getSingletonIterator(HalApiEndpoint.class); it.hasNext(); )
registerPage(it.next());
for (Iterator it = pluginManager.getSingletonIterator(HalWebPage.class); it.hasNext(); )
registerPage(it.next());
diff --git a/hal-core/src/se/hal/intf/HalAbstractDevice.java b/hal-core/src/se/hal/intf/HalAbstractDevice.java
index 6b22de5a..6392c990 100644
--- a/hal-core/src/se/hal/intf/HalAbstractDevice.java
+++ b/hal-core/src/se/hal/intf/HalAbstractDevice.java
@@ -224,12 +224,12 @@ public abstract class HalAbstractDevice
*/
-public class EventJsonPage extends HalJsonPage {
+public class EventApiEndpoint extends HalApiEndpoint {
private static final Logger logger = LogUtil.getLogger();
- public EventJsonPage() {
+ public EventApiEndpoint() {
super("api/event");
}
@@ -39,31 +39,49 @@ public class EventJsonPage extends HalJsonPage {
DBConnection db = HalContext.getDB();
DataNode root = new DataNode(DataNode.DataType.List);
- // Get Events
+ // --------------------------------------
+ // Get Action
+ // --------------------------------------
String[] req_ids = new String[0];
if (request.get("id") != null)
req_ids = request.get("id").split(",");
- String req_type = request.get("type");
+
+ String req_typeConfig = request.get("typeConfig");
+ String req_typeData = request.get("typeData");
+
+ // Filter devices
List events = new ArrayList<>();
for (Event event : Event.getLocalEvents(db)) {
- if (ArrayUtil.contains(req_ids, "" + event.getId())) { // id filtering
- events.add(event);
+ boolean filter_match = true;
+
+ // id filtering
+ if (!ObjectUtil.isEmpty(req_ids) && !ArrayUtil.contains(req_ids, "" + event.getId())) {
+ filter_match = false;
}
- if (!ObjectUtil.isEmpty(req_type) &&
- event.getDeviceConfig().getDeviceDataClass().getSimpleName().contains(req_type)) { // device type filtering
- events.add(event);
+ // device type filtering
+ if (!ObjectUtil.isEmpty(req_typeConfig) &&
+ !event.getDeviceConfig().getClass().getSimpleName().equals(req_typeConfig)) {
+ filter_match = false;
}
- // no options defined, then add all events
- if (ObjectUtil.isEmpty(req_ids, req_type)) {
+ // data type filtering
+ if (!ObjectUtil.isEmpty(req_typeData) &&
+ !event.getDeviceConfig().getDeviceDataClass().getSimpleName().equals(req_typeData)) {
+ filter_match = false;
+ }
+
+ // Check the filter
+ if (filter_match) {
events.add(event);
}
}
+ // --------------------------------------
// Generate DataNode
+ // --------------------------------------
for (Event event : events) {
DataNode deviceNode = event.getDataNode();
diff --git a/hal-core/src/se/hal/page/api/MapJsonPage.java b/hal-core/src/se/hal/page/api/MapApiEndpoint.java
similarity index 95%
rename from hal-core/src/se/hal/page/api/MapJsonPage.java
rename to hal-core/src/se/hal/page/api/MapApiEndpoint.java
index f256c3cb..5e25c68c 100644
--- a/hal-core/src/se/hal/page/api/MapJsonPage.java
+++ b/hal-core/src/se/hal/page/api/MapApiEndpoint.java
@@ -2,7 +2,7 @@ package se.hal.page.api;
import se.hal.HalContext;
import se.hal.intf.HalAbstractDevice;
-import se.hal.intf.HalJsonPage;
+import se.hal.intf.HalApiEndpoint;
import se.hal.struct.Event;
import se.hal.struct.Sensor;
import zutil.db.DBConnection;
@@ -16,10 +16,10 @@ import java.util.logging.Logger;
/**
* TODO: This json endpoint might not be needed as we have SensorJsonPage?
*/
-public class MapJsonPage extends HalJsonPage {
+public class MapApiEndpoint extends HalApiEndpoint {
private static final Logger logger = LogUtil.getLogger();
- public MapJsonPage() {
+ public MapApiEndpoint() {
super("api/map");
}
diff --git a/hal-core/src/se/hal/page/api/SensorJsonPage.java b/hal-core/src/se/hal/page/api/SensorApiEndpoint.java
similarity index 64%
rename from hal-core/src/se/hal/page/api/SensorJsonPage.java
rename to hal-core/src/se/hal/page/api/SensorApiEndpoint.java
index 65903d23..7999e58b 100644
--- a/hal-core/src/se/hal/page/api/SensorJsonPage.java
+++ b/hal-core/src/se/hal/page/api/SensorApiEndpoint.java
@@ -2,7 +2,7 @@ package se.hal.page.api;
import se.hal.HalContext;
import se.hal.daemon.SensorDataAggregatorDaemon;
-import se.hal.intf.HalJsonPage;
+import se.hal.intf.HalApiEndpoint;
import se.hal.struct.Sensor;
import se.hal.util.AggregateDataListSqlResult;
import se.hal.util.UTCTimeUtility;
@@ -28,11 +28,11 @@ import java.util.logging.Logger;
* aggr: Aggregation periods, needs to be provided to retrieve data. Possible values: minute,hour,day,week
*
*/
-public class SensorJsonPage extends HalJsonPage {
+public class SensorApiEndpoint extends HalApiEndpoint {
private static final Logger logger = LogUtil.getLogger();
- public SensorJsonPage() {
+ public SensorApiEndpoint() {
super("api/sensor");
}
@@ -45,60 +45,83 @@ public class SensorJsonPage extends HalJsonPage {
DBConnection db = HalContext.getDB();
DataNode root = new DataNode(DataNode.DataType.List);
- // Get sensors
- String[] req_ids = new String[0];
+ // --------------------------------------
+ // Get Action
+ // --------------------------------------
+
+ String[] reqIds = new String[0];
if (request.get("id") != null)
- req_ids = request.get("id").split(",");
- String req_type = request.get("type");
+ reqIds = request.get("id").split(",");
+
+ String reqTypeConfig = request.get("typeConfig");
+ String reqTypeData = request.get("typeData");
+
List sensors = new ArrayList<>();
for (Sensor sensor : Sensor.getSensors(db)) {
- if (ArrayUtil.contains(req_ids, "" + sensor.getId())) { // id filtering
- sensors.add(sensor);
+ boolean filter_match = true;
+
+ // id filtering
+ if (!ObjectUtil.isEmpty(reqIds) && !ArrayUtil.contains(reqIds, "" + sensor.getId())) {
+ filter_match = false;
}
- if (!ObjectUtil.isEmpty(req_type) &&
- sensor.getDeviceConfig().getDeviceDataClass().getSimpleName().contains(req_type)) { // device type filtering
- sensors.add(sensor);
+ // device type filtering
+ if (!ObjectUtil.isEmpty(reqTypeConfig) &&
+ !sensor.getDeviceConfig().getClass().getSimpleName().equals(reqTypeConfig)) {
+ filter_match = false;
}
- // no options defined, then add all sensors
- if (ObjectUtil.isEmpty(req_ids, req_type)) {
+ // data type filtering
+ if (!ObjectUtil.isEmpty(reqTypeData) &&
+ !sensor.getDeviceConfig().getDeviceDataClass().getSimpleName().equals(reqTypeData)) {
+ filter_match = false;
+ }
+
+ // Check the filter
+ if (filter_match) {
sensors.add(sensor);
}
}
- // Figure out aggregation period
+ // --------------------------------------
+ // Was aggregated data requested
+ // --------------------------------------
+
SensorDataAggregatorDaemon.AggregationPeriodLength aggrType = null;
- long aggrLength = -1;
- if (request.get("aggr") != null) {
- switch (request.get("aggr")) {
+ long aggregationLength = -1;
+
+ if (request.get("aggregation") != null) {
+ switch (request.get("aggregation")) {
case "minute":
aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.FIVE_MINUTES;
- aggrLength = UTCTimeUtility.DAY_IN_MS;
+ aggregationLength = UTCTimeUtility.DAY_IN_MS;
break;
case "hour":
aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.HOUR;
- aggrLength = UTCTimeUtility.WEEK_IN_MS;
+ aggregationLength = UTCTimeUtility.WEEK_IN_MS;
break;
case "day":
aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.DAY;
- aggrLength = UTCTimeUtility.INFINITY;
+ aggregationLength = UTCTimeUtility.INFINITY;
break;
case "week":
aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.WEEK;
- aggrLength = UTCTimeUtility.INFINITY;
+ aggregationLength = UTCTimeUtility.INFINITY;
break;
}
}
+ // --------------------------------------
// Generate DataNode
+ // --------------------------------------
+
for (Sensor sensor : sensors) {
DataNode deviceNode = sensor.getDataNode();
- if (aggrLength > 0) {
- DataNode aggregateNode = getAggregateDataNode(aggrLength,
- AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, aggrType, aggrLength));
+ if (aggregationLength > 0) {
+ DataNode aggregateNode = getAggregateDataNode(aggregationLength,
+ AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, aggrType, aggregationLength));
deviceNode.set("aggregate", aggregateNode);
}
diff --git a/hal-core/src/se/hal/plugin.json b/hal-core/src/se/hal/plugin.json
index 35711a8d..26962511 100644
--- a/hal-core/src/se/hal/plugin.json
+++ b/hal-core/src/se/hal/plugin.json
@@ -12,9 +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.api.MapJsonPage"},
- {"se.hal.intf.HalJsonPage": "se.hal.page.api.EventJsonPage"},
- {"se.hal.intf.HalJsonPage": "se.hal.page.api.SensorJsonPage"},
+ {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.EventApiEndpoint"},
+ {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.MapApiEndpoint"},
+ {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.SensorApiEndpoint"},
{"se.hal.intf.HalWebPage": "se.hal.page.MapWebPage"},
{"se.hal.intf.HalWebPage": "se.hal.page.SensorOverviewWebPage"},
diff --git a/plugins/hal-powerchallenge/resource/resource/web/pc_overview.tmpl b/plugins/hal-powerchallenge/resource/resource/web/pc_overview.tmpl
index 4c9965e7..fdc2bfe3 100644
--- a/plugins/hal-powerchallenge/resource/resource/web/pc_overview.tmpl
+++ b/plugins/hal-powerchallenge/resource/resource/web/pc_overview.tmpl
@@ -20,10 +20,10 @@