diff --git a/hal-core/resources/web/api/openapi.json b/hal-core/resources/web/api/openapi.json index 8c2100ff..4f512162 100644 --- a/hal-core/resources/web/api/openapi.json +++ b/hal-core/resources/web/api/openapi.json @@ -1,6 +1,17 @@ { "components": { "schemas": { + "alertClass": { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "level": {"type": "string"}, + "ttl": {"type": "integer"}, + "title": {"type": "string"}, + "description": {"type": "string"} + } + }, + "eventClass": { "type": "object", "properties": { @@ -111,6 +122,50 @@ "openapi": "3.0.1", "paths": { + "/alert": { + "get": { + "responses": { + "200": { + "description": "A successful response.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/components/schemas/alertClass" + } + } + } + } + } + }, + "parameters": [ + { + "schema": { + "type": "string", + "enum": [ + "poll", + "peek", + "dismiss" + ] + }, + "in": "query", + "name": "action", + "required": true + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "id", + "required": false + } + ] + } + }, + "/event": { "get": { "responses": { @@ -119,8 +174,11 @@ "content": { "application/json": { "schema": { - "type": "object", - "$ref": "#/components/schemas/eventClass" + "type": "array", + "items": { + "type": "object", + "$ref": "#/components/schemas/eventClass" + } } } } @@ -163,8 +221,11 @@ "content": { "application/json": { "schema": { - "type": "object", - "$ref": "#/components/schemas/roomClass" + "type": "array", + "items": { + "type": "object", + "$ref": "#/components/schemas/roomClass" + } } } } @@ -191,8 +252,11 @@ "content": { "application/json": { "schema": { - "type": "object", - "$ref": "#/components/schemas/sensorClass" + "type": "array", + "items": { + "type": "object", + "$ref": "#/components/schemas/sensorClass" + } } } } diff --git a/hal-core/resources/web/js/hal_alert.js b/hal-core/resources/web/js/hal_alert.js new file mode 100644 index 00000000..7cbedc52 --- /dev/null +++ b/hal-core/resources/web/js/hal_alert.js @@ -0,0 +1,84 @@ +// -------------------------------------------------------- +// Autostart +// -------------------------------------------------------- + +"use strict"; + +var alertDivId = "alert-container" +var alertTemplate = { + ERROR: ` +
+ + +   + +
`, + WARNING: ` +
+ + +   + +
`, + SUCCESS: ` +
+ + +   + +
`, + INFO: ` +
+ + +   + +
` +} + +$(function(){ + updateAlerts(); + + setInterval(function() { + updateAlerts(); + }, 3000); // 3 sec +}); + +function updateAlerts() { + fetch('/api/alert?action=poll') + .then(response => response.json()) + .then(data => { + data.forEach(alert => { + var alertElement = $("#alert-id-" + alert.id); + if (alertElement.length <= 0) { + alertElement = $(alertTemplate[alert.level]); + $("#" + alertDivId).append(alertElement); + + alertElement.attr("id", "alert-id-" + alert.id); + alertElement.data("alert-id", alert.id); + alertElement.find(".close").click(dismissEvent); + } + + alertElement.find(".alert-title").html(alert.title); + alertElement.find(".alert-description").html(alert.description); + }); + }); +} + +function dismissEvent(e) { + dismissAlert($(e.target).parent().parent().data("alert-id")); +} +function dismissAlert(id) { + fetch('/api/alert?action=dismiss&id=' + id) + .then(response => response.json()) + .then(data => { + }); +} \ No newline at end of file diff --git a/hal-core/resources/web/js/map.js b/hal-core/resources/web/js/hal_map.js similarity index 100% rename from hal-core/resources/web/js/map.js rename to hal-core/resources/web/js/hal_map.js diff --git a/hal-core/resources/web/main_alerts.tmpl b/hal-core/resources/web/main_alerts.tmpl deleted file mode 100644 index 82d13b81..00000000 --- a/hal-core/resources/web/main_alerts.tmpl +++ /dev/null @@ -1,50 +0,0 @@ -{{#alerts}} - {{#.isError()}} - - {{/.isError()}} - {{#.isWarning()}} - - {{/.isWarning()}} - {{#.isSuccess()}} - - {{/.isSuccess()}} - {{#.isInfo()}} - - {{/.isInfo()}} -{{/alerts}} - - \ No newline at end of file diff --git a/hal-core/resources/web/main_index.tmpl b/hal-core/resources/web/main_index.tmpl index f2836eb6..892656d7 100644 --- a/hal-core/resources/web/main_index.tmpl +++ b/hal-core/resources/web/main_index.tmpl @@ -19,6 +19,7 @@ + @@ -37,7 +38,7 @@
{{/side_navigation}} {{^side_navigation}}
{{/side_navigation}} - {{alerts}} +
{{content}}
diff --git a/hal-core/resources/web/map.tmpl b/hal-core/resources/web/map.tmpl index 3419e8e6..c38ae6a4 100644 --- a/hal-core/resources/web/map.tmpl +++ b/hal-core/resources/web/map.tmpl @@ -50,7 +50,7 @@ - + diff --git a/hal-core/src/se/hal/HalContext.java b/hal-core/src/se/hal/HalContext.java index cfd5ac42..e6c3bc19 100644 --- a/hal-core/src/se/hal/HalContext.java +++ b/hal-core/src/se/hal/HalContext.java @@ -5,6 +5,7 @@ import zutil.db.DBConnection; import zutil.db.handler.PropertiesSQLResult; import zutil.io.file.FileUtil; import zutil.log.LogUtil; +import zutil.ui.UserMessageManager; import java.io.File; import java.io.FileReader; @@ -50,6 +51,7 @@ public class HalContext { private static Properties fileConf = new Properties(); private static Properties dbConf = new Properties(); + private static UserMessageManager messageManager = new UserMessageManager(); static { // Set default values to get Hal up and running @@ -162,4 +164,8 @@ public class HalContext { HalContext.db = db; } + + public static UserMessageManager getUserMessageManager() { + return messageManager; + } } diff --git a/hal-core/src/se/hal/HalServer.java b/hal-core/src/se/hal/HalServer.java index a51adeba..bdc98628 100644 --- a/hal-core/src/se/hal/HalServer.java +++ b/hal-core/src/se/hal/HalServer.java @@ -3,7 +3,6 @@ package se.hal; import se.hal.daemon.HalExternalWebDaemon; import se.hal.intf.*; -import se.hal.page.HalAlertManager; import se.hal.page.StartupWebPage; import se.hal.struct.PluginConfig; import zutil.db.DBConnection; @@ -51,8 +50,6 @@ public class HalServer { // init logging LogUtil.readConfiguration("logging.properties"); - HalAlertManager.initialize(); - http = new HttpServer(HalContext.getIntegerProperty(HalContext.CONFIG_HTTP_PORT)); http.setDefaultPage(new StartupWebPage()); http.start(); @@ -138,7 +135,6 @@ public class HalServer { http.setDefaultPage(filePage); http.setPage("/", new HttpRedirectPage("/map")); - http.setPage(HalAlertManager.getInstance().getUrl(), HalAlertManager.getInstance()); for (Iterator it = pluginManager.getSingletonIterator(HalApiEndpoint.class); it.hasNext(); ) registerPage(it.next()); diff --git a/hal-core/src/se/hal/action/AlertAction.java b/hal-core/src/se/hal/action/AlertAction.java index 5bf0528c..577e62cc 100644 --- a/hal-core/src/se/hal/action/AlertAction.java +++ b/hal-core/src/se/hal/action/AlertAction.java @@ -1,7 +1,7 @@ package se.hal.action; +import se.hal.HalContext; import se.hal.intf.HalAction; -import se.hal.page.HalAlertManager; import zutil.log.LogUtil; import zutil.ui.UserMessageManager.MessageTTL; import zutil.ui.conf.Configurator; @@ -20,17 +20,19 @@ public class AlertAction implements HalAction { private MessageLevel severity = MessageLevel.INFO; @Configurator.Configurable("Alert Message") private MessageTTL ttl = MessageTTL.ONE_VIEW; - @Configurator.Configurable("Alert Message") - private String message = ""; + @Configurator.Configurable("Alert Title") + private String title = ""; + @Configurator.Configurable("Alert Description") + private String description = ""; @Override public void execute() { - HalAlertManager.getInstance().addAlert(new UserMessage(severity, message, ttl)); + HalContext.getUserMessageManager().add(new UserMessage(severity, title, description, ttl)); } public String toString(){ - return "Send Alert: " + severity + ": " + message; + return "Send Alert: " + severity + ": " + title; } } diff --git a/hal-core/src/se/hal/daemon/HalExternalWebDaemon.java b/hal-core/src/se/hal/daemon/HalExternalWebDaemon.java index 06112ca1..e5308278 100644 --- a/hal-core/src/se/hal/daemon/HalExternalWebDaemon.java +++ b/hal-core/src/se/hal/daemon/HalExternalWebDaemon.java @@ -4,7 +4,6 @@ import org.shredzone.acme4j.exception.AcmeException; import se.hal.HalContext; import se.hal.intf.HalDaemon; import se.hal.intf.HalWebPage; -import se.hal.page.HalAlertManager; import se.hal.util.HalAcmeDataStore; import se.hal.util.HalOAuth2RegistryStore; import zutil.log.LogUtil; @@ -79,12 +78,12 @@ public class HalExternalWebDaemon implements HalDaemon { startHttpServer(); } else { logger.warning("Missing '" + CONFIG_HTTP_EXTERNAL_PORT + "' and '" + CONFIG_HTTP_EXTERNAL_DOMAIN + "' configuration, will not setup external web-server."); - HalAlertManager.getInstance().addAlert(new UserMessageManager.UserMessage( + HalContext.getUserMessageManager().add(new UserMessageManager.UserMessage( UserMessageManager.MessageLevel.WARNING, "Missing '" + CONFIG_HTTP_EXTERNAL_PORT + "' and '" + CONFIG_HTTP_EXTERNAL_DOMAIN + "' configuration, will not setup external web-server.", UserMessageManager.MessageTTL.DISMISSED)); } } catch (Exception e) { logger.log(Level.SEVERE, "Was unable to initiate external web-server.", e); - HalAlertManager.getInstance().addAlert(new UserMessageManager.UserMessage( + HalContext.getUserMessageManager().add(new UserMessageManager.UserMessage( UserMessageManager.MessageLevel.ERROR, "Was unable to initiate external web-server: " + e.getMessage(), UserMessageManager.MessageTTL.DISMISSED)); } } @@ -119,7 +118,7 @@ public class HalExternalWebDaemon implements HalDaemon { acmeDataStore.storeCertificate(certificate); logger.info("SSL certificate successfully generated."); - HalAlertManager.getInstance().addAlert(new UserMessageManager.UserMessage( + HalContext.getUserMessageManager().add(new UserMessageManager.UserMessage( UserMessageManager.MessageLevel.INFO, "SSL certificate successfully generated for external web-server.", UserMessageManager.MessageTTL.DISMISSED)); } else { logger.warning("No SSL certificate is configured for external web-server, will run server in unsecure mode (not recommended)."); diff --git a/hal-core/src/se/hal/daemon/SensorDataAggregatorDaemon.java b/hal-core/src/se/hal/daemon/SensorDataAggregatorDaemon.java index 092d82a5..95986d94 100644 --- a/hal-core/src/se/hal/daemon/SensorDataAggregatorDaemon.java +++ b/hal-core/src/se/hal/daemon/SensorDataAggregatorDaemon.java @@ -3,7 +3,6 @@ package se.hal.daemon; import se.hal.HalContext; import se.hal.intf.HalDaemon; import se.hal.intf.HalSensorConfig.AggregationMethod; -import se.hal.page.HalAlertManager; import se.hal.struct.Sensor; import se.hal.util.UTCTimePeriod; import se.hal.util.UTCTimeUtility; @@ -119,7 +118,7 @@ public class SensorDataAggregatorDaemon implements HalDaemon, Runnable { "at "+dbMaxRawTimestamp+"", MessageTTL.DISMISSED); alertMap.put(sensor.getId(), alert); - HalAlertManager.getInstance().addAlert(alert); + HalContext.getUserMessageManager().add(alert); } else { // Sensor has responded remove alert diff --git a/hal-core/src/se/hal/intf/HalWebPage.java b/hal-core/src/se/hal/intf/HalWebPage.java index 1a14baac..dc43dc68 100644 --- a/hal-core/src/se/hal/intf/HalWebPage.java +++ b/hal-core/src/se/hal/intf/HalWebPage.java @@ -1,7 +1,6 @@ package se.hal.intf; import se.hal.HalContext; -import se.hal.page.HalAlertManager; import se.hal.struct.User; import zutil.db.DBConnection; import zutil.io.file.FileUtil; @@ -78,7 +77,6 @@ public abstract class HalWebPage implements HttpPage{ main.set("navigation", navigationTemplate); main.set("side_navigation", subNavigationTemplate); main.set("content", httpRespond(session, cookie, request)); - main.set("alerts", HalAlertManager.getInstance().generateAlerts()); // do last so we don't miss any alerts out.print(main.compile()); } catch (Exception e) { diff --git a/hal-core/src/se/hal/page/EventConfigWebPage.java b/hal-core/src/se/hal/page/EventConfigWebPage.java index 7f456166..f022c3d2 100644 --- a/hal-core/src/se/hal/page/EventConfigWebPage.java +++ b/hal-core/src/se/hal/page/EventConfigWebPage.java @@ -64,7 +64,7 @@ public class EventConfigWebPage extends HalWebPage { if (event == null) { logger.warning("Unknown event id: " + id); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.ERROR, "Unknown event id: " + id, MessageTTL.ONE_VIEW)); } } @@ -81,7 +81,7 @@ public class EventConfigWebPage extends HalWebPage { event.save(db); EventControllerManager.getInstance().register(event); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully created new event: " + event.getName(), MessageTTL.ONE_VIEW)); break; @@ -95,7 +95,7 @@ public class EventConfigWebPage extends HalWebPage { event.getDeviceConfigurator().setValues(request).applyConfiguration(); event.save(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully saved event: "+event.getName(), MessageTTL.ONE_VIEW)); } break; @@ -106,7 +106,7 @@ public class EventConfigWebPage extends HalWebPage { EventControllerManager.getInstance().deregister(event); event.delete(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully removed event: "+event.getName(), MessageTTL.ONE_VIEW)); } break; @@ -120,7 +120,7 @@ public class EventConfigWebPage extends HalWebPage { if (controller instanceof HalScannableController) { ((HalScannableController) controller).startScan(); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Initiated scanning on controller: " + controller.getClass().getName(), MessageTTL.ONE_VIEW)); } } diff --git a/hal-core/src/se/hal/page/HalAlertManager.java b/hal-core/src/se/hal/page/HalAlertManager.java deleted file mode 100644 index 42d781ae..00000000 --- a/hal-core/src/se/hal/page/HalAlertManager.java +++ /dev/null @@ -1,84 +0,0 @@ -package se.hal.page; - -import se.hal.HalContext; -import zutil.io.file.FileUtil; -import zutil.log.LogUtil; -import zutil.net.http.HttpHeader; -import zutil.net.http.HttpPage; -import zutil.net.http.HttpPrintStream; -import zutil.parser.Templator; -import zutil.ui.UserMessageManager; -import zutil.ui.UserMessageManager.UserMessage; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class HalAlertManager implements HttpPage { - private static final Logger logger = LogUtil.getLogger(); - private static final String TEMPLATE = HalContext.RESOURCE_WEB_ROOT + "/main_alerts.tmpl"; - private static final String PAGE_NAME = "alert"; - private static HalAlertManager instance; - - private UserMessageManager messageManager = new UserMessageManager(); - - - private HalAlertManager() {} - - - public String getUrl() { - return "/" + PAGE_NAME; - } - - public void addAlert(UserMessage alert) { - messageManager.add(alert); - } - - - public Templator generateAlerts() { - try { - List messages = messageManager.getMessages(); - for (UserMessage msg : messages) { - msg.decreaseTTL(); - } - - Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); - tmpl.set("serviceUrl", getUrl()); - tmpl.set("alerts", messages); - return tmpl; - } catch (IOException e) { - logger.log(Level.SEVERE, null, e); - } - return null; - } - - @Override - public void respond(HttpPrintStream out, - HttpHeader headers, - Map session, - Map cookie, - Map request) throws IOException { - - if (request.containsKey("action")) { - if (request.get("action").equals("dismiss")) { - // parse alert id - int id = Integer.parseInt(request.get("id")); - // Find alert - UserMessage msg = messageManager.get(id); - if (msg != null) - msg.dismiss(); - } - } - } - - - - public static void initialize() { - instance = new HalAlertManager(); - } - public static HalAlertManager getInstance() { - return instance; - } -} diff --git a/hal-core/src/se/hal/page/PluginConfigWebPage.java b/hal-core/src/se/hal/page/PluginConfigWebPage.java index e405cd6b..615586a1 100644 --- a/hal-core/src/se/hal/page/PluginConfigWebPage.java +++ b/hal-core/src/se/hal/page/PluginConfigWebPage.java @@ -43,10 +43,10 @@ public class PluginConfigWebPage extends HalWebPage { HalServer.enablePlugin(name, (request.containsKey("enabled") && "on".equals(request.get("enabled")))); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully updated plugin " + name + ", change will take affect after restart.", MessageTTL.ONE_VIEW)); } else { - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.ERROR, "Hal-Core cannot be disabled as it is critical component of Hal.", MessageTTL.ONE_VIEW)); } break; @@ -66,14 +66,14 @@ public class PluginConfigWebPage extends HalWebPage { if (controller instanceof HalScannableController) { ((HalScannableController) controller).startScan(); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Initiated scanning on controller: " + controllerName, MessageTTL.ONE_VIEW)); } else { - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.ERROR, "Controller " + controllerName + " does not support scanning.", MessageTTL.ONE_VIEW)); } } else { - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.ERROR, "Unable to find controller: " + controllerName, MessageTTL.ONE_VIEW)); } break; diff --git a/hal-core/src/se/hal/page/RoomConfigWebPage.java b/hal-core/src/se/hal/page/RoomConfigWebPage.java index be1355c7..fe912c6c 100644 --- a/hal-core/src/se/hal/page/RoomConfigWebPage.java +++ b/hal-core/src/se/hal/page/RoomConfigWebPage.java @@ -73,7 +73,7 @@ public class RoomConfigWebPage extends HalWebPage { if (room == null) { logger.warning("Unknown room id: " + id); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.ERROR, "Unknown room id: " + id, MessageTTL.ONE_VIEW)); } } @@ -90,7 +90,7 @@ public class RoomConfigWebPage extends HalWebPage { room.setName(request.get("name")); room.save(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully updated room: " + room.getName(), MessageTTL.ONE_VIEW)); } break; @@ -100,7 +100,7 @@ public class RoomConfigWebPage extends HalWebPage { logger.info("Removing room(id: " + room.getId() + "): " + room.getName()); room.delete(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully removed room: " + room.getName(), MessageTTL.ONE_VIEW)); } break; diff --git a/hal-core/src/se/hal/page/SensorConfigWebPage.java b/hal-core/src/se/hal/page/SensorConfigWebPage.java index 4f720224..38e0cfe4 100644 --- a/hal-core/src/se/hal/page/SensorConfigWebPage.java +++ b/hal-core/src/se/hal/page/SensorConfigWebPage.java @@ -66,7 +66,7 @@ public class SensorConfigWebPage extends HalWebPage { if (sensor == null) { logger.warning("Unknown sensor id: " + sensorId); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.ERROR, "Unknown sensor id: " + sensorId, MessageTTL.ONE_VIEW)); } } @@ -77,7 +77,7 @@ public class SensorConfigWebPage extends HalWebPage { if (user == null) { logger.warning("Unknown user id: " + userId); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.ERROR, "Unknown user id: " + userId, MessageTTL.ONE_VIEW)); } } @@ -99,7 +99,7 @@ public class SensorConfigWebPage extends HalWebPage { sensor.save(db); SensorControllerManager.getInstance().register(sensor); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully created new sensor: " + sensor.getName(), MessageTTL.ONE_VIEW)); break; @@ -113,7 +113,7 @@ public class SensorConfigWebPage extends HalWebPage { sensor.getDeviceConfigurator().setValues(request).applyConfiguration(); sensor.save(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully saved sensor: " + sensor.getName(), MessageTTL.ONE_VIEW)); } break; @@ -124,7 +124,7 @@ public class SensorConfigWebPage extends HalWebPage { SensorControllerManager.getInstance().deregister(sensor); sensor.delete(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully removed sensor: " + sensor.getName(), MessageTTL.ONE_VIEW)); } break; @@ -138,7 +138,7 @@ public class SensorConfigWebPage extends HalWebPage { if (controller instanceof HalScannableController) { ((HalScannableController) controller).startScan(); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Initiated scanning on controller: " + controller.getClass().getName(), MessageTTL.ONE_VIEW)); } } @@ -156,7 +156,7 @@ public class SensorConfigWebPage extends HalWebPage { user.setExternal(true); user.save(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully created new external user with host: "+user.getHostname(), MessageTTL.ONE_VIEW)); break; @@ -167,7 +167,7 @@ public class SensorConfigWebPage extends HalWebPage { user.setPort(Integer.parseInt(request.get("port"))); user.save(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully saved external user with host: "+user.getHostname(), MessageTTL.ONE_VIEW)); } break; @@ -176,7 +176,7 @@ public class SensorConfigWebPage extends HalWebPage { logger.info("Removing external user: " + user.getHostname()); user.delete(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully removed user with host: "+user.getHostname(), MessageTTL.ONE_VIEW)); } break; @@ -191,7 +191,7 @@ public class SensorConfigWebPage extends HalWebPage { sensor.setSynced(Boolean.parseBoolean(request.get("sync"))); sensor.save(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully saved external sensor: " + sensor.getName(), MessageTTL.ONE_VIEW)); } break; diff --git a/hal-core/src/se/hal/page/TriggerWebPage.java b/hal-core/src/se/hal/page/TriggerWebPage.java index 63893676..0c8f9b87 100644 --- a/hal-core/src/se/hal/page/TriggerWebPage.java +++ b/hal-core/src/se/hal/page/TriggerWebPage.java @@ -83,7 +83,7 @@ public class TriggerWebPage extends HalWebPage { case "create_trigger": if (flow == null) { logger.warning("Invalid flow id: " + request.get("flow-id")); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.ERROR, "Invalid flow id: " + request.get("flow-id"), MessageTTL.ONE_VIEW)); break; } @@ -111,7 +111,7 @@ public class TriggerWebPage extends HalWebPage { // Triggers case "create_action": if (flow == null) { - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.ERROR, "Invalid flow id", MessageTTL.ONE_VIEW)); break; } diff --git a/hal-core/src/se/hal/page/UserConfigWebPage.java b/hal-core/src/se/hal/page/UserConfigWebPage.java index 2ba36916..4dbecc48 100644 --- a/hal-core/src/se/hal/page/UserConfigWebPage.java +++ b/hal-core/src/se/hal/page/UserConfigWebPage.java @@ -49,7 +49,7 @@ public class UserConfigWebPage extends HalWebPage { localUser.setAddress(request.get("address")); localUser.save(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully saved profile changes", MessageTTL.ONE_VIEW)); break; } diff --git a/hal-core/src/se/hal/page/api/AlertApiEndpoint.java b/hal-core/src/se/hal/page/api/AlertApiEndpoint.java new file mode 100644 index 00000000..d75cb426 --- /dev/null +++ b/hal-core/src/se/hal/page/api/AlertApiEndpoint.java @@ -0,0 +1,114 @@ +package se.hal.page.api; + +import se.hal.HalContext; +import se.hal.intf.HalApiEndpoint; +import se.hal.struct.Event; +import zutil.ArrayUtil; +import zutil.ObjectUtil; +import zutil.db.DBConnection; +import zutil.log.LogUtil; +import zutil.parser.DataNode; +import zutil.ui.UserMessageManager; +import zutil.ui.UserMessageManager.UserMessage; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +/** + * RESTish API for accessing and managing user alert. + * For web interface definition see the OpenApi definition hal-core/resources/web/api/doc.html + */ +public class AlertApiEndpoint extends HalApiEndpoint { + private static final Logger logger = LogUtil.getLogger(); + + + public AlertApiEndpoint() { + super("api/alert"); + } + + @Override + public DataNode jsonRespond( + Map session, + Map cookie, + Map request) throws Exception{ + + // -------------------------------------- + // Filter alerts + // -------------------------------------- + + String[] req_ids = new String[0]; + if (request.get("id") != null) + req_ids = request.get("id").split(","); + + // Filter devices + + List messages = new ArrayList<>(); + for (UserMessage message : HalContext.getUserMessageManager().getMessages()) { + boolean filter_match = true; + + // id filtering + if (!ObjectUtil.isEmpty((Object) req_ids) && !ArrayUtil.contains(req_ids, "" + message.getId())) { + filter_match = false; + } + + // Check the filter + if (filter_match) { + messages.add(message); + } + } + + if (ObjectUtil.isEmpty(request.get("action"))) { + messages.clear(); // Reset the message list + } else { + switch (request.get("action")) { + case "poll": + for (UserMessage message : messages) { + message.decreaseTTL(); + } + case "peek": + break; + + case "dismiss": + for (UserMessage message : messages) { + message.dismiss(); + } + break; + + default: + messages.clear(); + break; + } + } + + // -------------------------------------- + // Generate DataNode + // -------------------------------------- + + DataNode root = new DataNode(DataNode.DataType.List); + + for (UserMessage message : messages) { + root.add(getUserMessageDataNode(message)); + } + + return root; + } + + /** + * @param message the source alert to generate data node for. + * @return a DataNode containing relevant information from the given alert. Will return null if the message is null. + */ + public static DataNode getUserMessageDataNode(UserMessage message) { + if (message == null) + return null; + + DataNode node = new DataNode(DataNode.DataType.Map); + node.set("id", message.getId()); + node.set("level", message.getLevel().toString()); + node.set("ttl", message.getTitle()); + node.set("title", message.getTitle()); + node.set("description", message.getDescription()); + return node; + } +} diff --git a/hal-core/src/se/hal/page/api/EventApiEndpoint.java b/hal-core/src/se/hal/page/api/EventApiEndpoint.java index e0a3e094..bde0a58a 100644 --- a/hal-core/src/se/hal/page/api/EventApiEndpoint.java +++ b/hal-core/src/se/hal/page/api/EventApiEndpoint.java @@ -15,12 +15,8 @@ import java.util.Map; import java.util.logging.Logger; /** - * Available HTTP Get Request parameters: - *
- * Event filtering parameters:
- * id: comma separated numeric id for specific events
- * type: event data type name
- * 
+ * RESTish API for accessing and managing Events. + * For web interface definition see the OpenApi definition hal-core/resources/web/api/doc.html */ public class EventApiEndpoint extends HalApiEndpoint { private static final Logger logger = LogUtil.getLogger(); @@ -57,7 +53,7 @@ public class EventApiEndpoint extends HalApiEndpoint { boolean filter_match = true; // id filtering - if (!ObjectUtil.isEmpty(req_ids) && !ArrayUtil.contains(req_ids, "" + event.getId())) { + if (!ObjectUtil.isEmpty((Object) req_ids) && !ArrayUtil.contains(req_ids, "" + event.getId())) { filter_match = false; } diff --git a/hal-core/src/se/hal/page/api/RoomApiEndpoint.java b/hal-core/src/se/hal/page/api/RoomApiEndpoint.java index 79232877..eeb90f95 100644 --- a/hal-core/src/se/hal/page/api/RoomApiEndpoint.java +++ b/hal-core/src/se/hal/page/api/RoomApiEndpoint.java @@ -16,6 +16,7 @@ import java.util.logging.Logger; /** * REST service to fetch event device information. + * For web interface definition see the OpenApi definition hal-core/resources/web/api/doc.html */ public class RoomApiEndpoint extends HalApiEndpoint { private static final Logger logger = LogUtil.getLogger(); @@ -49,7 +50,7 @@ public class RoomApiEndpoint extends HalApiEndpoint { boolean filter_match = true; // id filtering - if (!ObjectUtil.isEmpty(req_ids) && !ArrayUtil.contains(req_ids, "" + room.getId())) { + if (!ObjectUtil.isEmpty((Object) req_ids) && !ArrayUtil.contains(req_ids, "" + room.getId())) { filter_match = false; } diff --git a/hal-core/src/se/hal/page/api/SensorApiEndpoint.java b/hal-core/src/se/hal/page/api/SensorApiEndpoint.java index 7999e58b..1234773c 100644 --- a/hal-core/src/se/hal/page/api/SensorApiEndpoint.java +++ b/hal-core/src/se/hal/page/api/SensorApiEndpoint.java @@ -18,15 +18,8 @@ import java.util.Map; import java.util.logging.Logger; /** - * Available HTTP Get Request parameters: - *
- * Sensor filtering parameters:
- * id: comma separated numeric id for specific sensors
- * type: sensor data type name
- *
- * Data filtering parameters:
- * aggr: Aggregation periods, needs to be provided to retrieve data. Possible values: minute,hour,day,week
- * 
+ * RESTish API for accessing and managing Sensors. + * For web interface definition see the OpenApi definition hal-core/resources/web/api/doc.html */ public class SensorApiEndpoint extends HalApiEndpoint { private static final Logger logger = LogUtil.getLogger(); @@ -62,7 +55,7 @@ public class SensorApiEndpoint extends HalApiEndpoint { boolean filter_match = true; // id filtering - if (!ObjectUtil.isEmpty(reqIds) && !ArrayUtil.contains(reqIds, "" + sensor.getId())) { + if (!ObjectUtil.isEmpty((Object) reqIds) && !ArrayUtil.contains(reqIds, "" + sensor.getId())) { filter_match = false; } diff --git a/hal-core/src/se/hal/plugin.json b/hal-core/src/se/hal/plugin.json index 2ed25d6e..9b89f5dd 100644 --- a/hal-core/src/se/hal/plugin.json +++ b/hal-core/src/se/hal/plugin.json @@ -12,6 +12,7 @@ {"se.hal.intf.HalDaemon": "se.hal.daemon.SensorDataAggregatorDaemon"}, {"se.hal.intf.HalDaemon": "se.hal.daemon.SensorDataCleanupDaemon"}, + {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.AlertApiEndpoint"}, {"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.RoomApiEndpoint"}, diff --git a/hal-core/src/se/hal/struct/Room.java b/hal-core/src/se/hal/struct/Room.java index 26b08d00..4789014b 100644 --- a/hal-core/src/se/hal/struct/Room.java +++ b/hal-core/src/se/hal/struct/Room.java @@ -1,11 +1,11 @@ package se.hal.struct; +import se.hal.page.api.AlertApiEndpoint; import zutil.db.DBConnection; import zutil.db.bean.DBBean; import zutil.db.bean.DBBeanSQLResultHandler; import zutil.parser.DataNode; import zutil.ui.UserMessageManager.UserMessage; -import zutil.ui.conf.Configurator; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -77,16 +77,21 @@ public class Room extends DBBean { } public DataNode getDataNode() { - DataNode deviceNode = new DataNode(DataNode.DataType.Map); - deviceNode.set("id", getId()); - deviceNode.set("name", getName()); + DataNode rootNode = new DataNode(DataNode.DataType.Map); + rootNode.set("id", getId()); + rootNode.set("name", getName()); - DataNode mapNode = deviceNode.set("map", DataNode.DataType.Map); + DataNode mapNode = rootNode.set("map", DataNode.DataType.Map); mapNode.set("x", getMapX()); mapNode.set("y", getMapY()); mapNode.set("width", getMapWidth()); mapNode.set("height", getMapHeight()); - return deviceNode; + if (roomAlert != null) { + DataNode alertNode = AlertApiEndpoint.getUserMessageDataNode(roomAlert); + rootNode.set("alert", alertNode); + } + + return rootNode; } } diff --git a/plugins/hal-nvr/src/se/hal/plugin/nvr/page/CameraConfigWebPage.java b/plugins/hal-nvr/src/se/hal/plugin/nvr/page/CameraConfigWebPage.java index 4bd49796..1246d628 100644 --- a/plugins/hal-nvr/src/se/hal/plugin/nvr/page/CameraConfigWebPage.java +++ b/plugins/hal-nvr/src/se/hal/plugin/nvr/page/CameraConfigWebPage.java @@ -24,16 +24,13 @@ package se.hal.plugin.nvr.page; -import se.hal.EventControllerManager; import se.hal.HalContext; import se.hal.intf.HalWebPage; -import se.hal.page.HalAlertManager; import se.hal.plugin.nvr.CameraControllerManager; import se.hal.plugin.nvr.struct.Camera; import se.hal.struct.Room; import se.hal.util.ClassConfigurationFacade; import se.hal.struct.User; -import se.hal.util.RoomValueProvider; import zutil.ObjectUtil; import zutil.db.DBConnection; import zutil.io.file.FileUtil; @@ -46,6 +43,7 @@ import java.util.logging.Logger; import static zutil.ui.UserMessageManager.*; + public class CameraConfigWebPage extends HalWebPage { private static final Logger logger = LogUtil.getLogger(); private static final String TEMPLATE = HalContext.RESOURCE_WEB_ROOT + "/camera_config.tmpl"; @@ -86,7 +84,7 @@ public class CameraConfigWebPage extends HalWebPage { if (camera == null) { logger.warning("Unknown camera id: " + id); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.ERROR, "Unknown camera id: " + id, MessageTTL.ONE_VIEW)); } } @@ -103,7 +101,7 @@ public class CameraConfigWebPage extends HalWebPage { camera.save(db); CameraControllerManager.getInstance().register(camera); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully created new camera: " + camera.getName(), MessageTTL.ONE_VIEW)); break; @@ -117,7 +115,7 @@ public class CameraConfigWebPage extends HalWebPage { camera.getDeviceConfigurator().setValues(request).applyConfiguration(); camera.save(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully saved camera: " + camera.getName(), MessageTTL.ONE_VIEW)); } break; @@ -128,7 +126,7 @@ public class CameraConfigWebPage extends HalWebPage { CameraControllerManager.getInstance().deregister(camera); camera.delete(db); - HalAlertManager.getInstance().addAlert(new UserMessage( + HalContext.getUserMessageManager().add(new UserMessage( MessageLevel.SUCCESS, "Successfully removed camera: " + camera.getName(), MessageTTL.ONE_VIEW)); } break; diff --git a/plugins/hal-powerchallenge/src/se/hal/plugin/powerchallenge/daemon/PCDataSynchronizationClient.java b/plugins/hal-powerchallenge/src/se/hal/plugin/powerchallenge/daemon/PCDataSynchronizationClient.java index 8d317e99..8e771406 100644 --- a/plugins/hal-powerchallenge/src/se/hal/plugin/powerchallenge/daemon/PCDataSynchronizationClient.java +++ b/plugins/hal-powerchallenge/src/se/hal/plugin/powerchallenge/daemon/PCDataSynchronizationClient.java @@ -26,7 +26,6 @@ package se.hal.plugin.powerchallenge.daemon; import se.hal.HalContext; import se.hal.intf.HalDaemon; -import se.hal.page.HalAlertManager; import se.hal.plugin.powerchallenge.daemon.PCDataSynchronizationDaemon.PeerDataRspDTO; import se.hal.plugin.powerchallenge.daemon.PCDataSynchronizationDaemon.SensorDTO; import se.hal.plugin.powerchallenge.daemon.PCDataSynchronizationDaemon.SensorDataDTO; @@ -157,7 +156,7 @@ public class PCDataSynchronizationClient implements HalDaemon, Runnable { } catch (NoRouteToHostException|UnknownHostException|ConnectException|SocketTimeoutException e) { logger.warning("Unable to connect to "+ user.getHostname()+":"+user.getPort() +", "+ e.getMessage()); - HalAlertManager.getInstance().addAlert(new UserMessage(MessageLevel.WARNING, + HalContext.getUserMessageManager().add(new UserMessage(MessageLevel.WARNING, "Unable to connect to user with host: "+user.getHostname(), MessageTTL.DISMISSED)); } catch (Exception e) { logger.log(Level.SEVERE, null, e);