thingsboard-memoizeit

Merge pull request #167 from thingsboard/feature/TB-63 TB-63:

6/13/2017 6:36:29 AM

Changes

Details

diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
index 19774bb..19c75e7 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
@@ -58,6 +58,19 @@ public class AlarmController extends BaseController {
     }
 
     @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+    @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET)
+    @ResponseBody
+    public AlarmInfo getAlarmInfoById(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException {
+        checkParameter("alarmId", strAlarmId);
+        try {
+            AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
+            return checkAlarmInfoId(alarmId);
+        } catch (Exception e) {
+            throw handleException(e);
+        }
+    }
+
+    @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/alarm", method = RequestMethod.POST)
     @ResponseBody
     public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException {
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index aba4b6e..c63dda7 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -27,6 +27,7 @@ import org.thingsboard.server.actors.service.ActorService;
 import org.thingsboard.server.common.data.*;
 import org.thingsboard.server.common.data.alarm.Alarm;
 import org.thingsboard.server.common.data.alarm.AlarmId;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
 import org.thingsboard.server.common.data.asset.Asset;
 import org.thingsboard.server.common.data.id.*;
 import org.thingsboard.server.common.data.page.TextPageLink;
@@ -351,6 +352,17 @@ public abstract class BaseController {
         }
     }
 
+    AlarmInfo checkAlarmInfoId(AlarmId alarmId) throws ThingsboardException {
+        try {
+            validateId(alarmId, "Incorrect alarmId " + alarmId);
+            AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(alarmId).get();
+            checkAlarm(alarmInfo);
+            return alarmInfo;
+        } catch (Exception e) {
+            throw handleException(e, false);
+        }
+    }
+
     protected void checkAlarm(Alarm alarm) throws ThingsboardException {
         checkNotNull(alarm);
         checkTenantId(alarm.getTenantId());
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
index 9c6f998..e48cf5b 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
@@ -55,6 +55,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName {
 
     public Alarm(Alarm alarm) {
         super(alarm.getId());
+        this.createdTime = alarm.getCreatedTime();
         this.tenantId = alarm.getTenantId();
         this.type = alarm.getType();
         this.originator = alarm.getOriginator();
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
index fb8a80d..3556d51 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
@@ -35,6 +35,8 @@ public interface AlarmService {
 
     ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId);
 
+    ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId);
+
     ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query);
 
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
index d152d96..58f7316 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
@@ -199,6 +199,23 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
     }
 
     @Override
+    public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId) {
+        log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId);
+        validateId(alarmId, "Incorrect alarmId " + alarmId);
+        return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()),
+                (AsyncFunction<Alarm, AlarmInfo>) alarm1 -> {
+                AlarmInfo alarmInfo = new AlarmInfo(alarm1);
+                return Futures.transform(
+                    entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>)
+                        originatorName -> {
+                            alarmInfo.setOriginatorName(originatorName);
+                            return alarmInfo;
+                        }
+                );
+        });
+    }
+
+    @Override
     public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) {
         ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query);
         if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
diff --git a/ui/src/app/alarm/alarm.scss b/ui/src/app/alarm/alarm.scss
new file mode 100644
index 0000000..aea5225
--- /dev/null
+++ b/ui/src/app/alarm/alarm.scss
@@ -0,0 +1,78 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.tb-alarm-container {
+  overflow-x: auto;
+}
+
+md-list.tb-alarm-table {
+  padding: 0px;
+  min-width: 700px;
+
+  md-list-item {
+    padding: 0px;
+  }
+
+  .tb-row {
+    height: 48px;
+    padding: 0px;
+    overflow: hidden;
+  }
+
+  .tb-row:hover {
+    background-color: #EEEEEE;
+  }
+
+  .tb-header:hover {
+    background: none;
+  }
+
+  .tb-header {
+    .tb-cell {
+      color: rgba(0,0,0,.54);
+      font-size: 12px;
+      font-weight: 700;
+      white-space: nowrap;
+      background: none;
+    }
+  }
+
+  .tb-cell {
+    padding: 0 24px;
+    margin: auto 0;
+    color: rgba(0,0,0,.87);
+    font-size: 13px;
+    vertical-align: middle;
+    text-align: left;
+    overflow: hidden;
+    .md-button {
+      padding: 0;
+      margin: 0;
+    }
+  }
+
+  .tb-cell.tb-number {
+    text-align: right;
+  }
+
+}
+
+#tb-alarm-content {
+  min-width: 400px;
+  min-height: 50px;
+  width: 100%;
+  height: 100%;
+}
diff --git a/ui/src/app/alarm/alarm-details-dialog.controller.js b/ui/src/app/alarm/alarm-details-dialog.controller.js
new file mode 100644
index 0000000..0cc05ef
--- /dev/null
+++ b/ui/src/app/alarm/alarm-details-dialog.controller.js
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import 'brace/ext/language_tools';
+import 'brace/mode/json';
+import 'brace/theme/github';
+import beautify from 'js-beautify';
+
+import './alarm-details-dialog.scss';
+
+const js_beautify = beautify.js;
+
+/*@ngInject*/
+export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types, alarmService, alarmId, showingCallback) {
+
+    var vm = this;
+
+    vm.alarmId = alarmId;
+    vm.types = types;
+    vm.alarm = null;
+
+    vm.alarmUpdated = false;
+
+    showingCallback.onShowing = function(scope, element) {
+        updateEditorSize(element);
+    }
+
+    vm.alarmDetailsOptions = {
+        useWrapMode: false,
+        mode: 'json',
+        showGutter: false,
+        showPrintMargin: false,
+        theme: 'github',
+        advanced: {
+            enableSnippets: false,
+            enableBasicAutocompletion: false,
+            enableLiveAutocompletion: false
+        },
+        onLoad: function (_ace) {
+            vm.editor = _ace;
+        }
+    };
+
+    vm.close = close;
+    vm.acknowledge = acknowledge;
+    vm.clear = clear;
+
+    loadAlarm();
+
+    function updateEditorSize(element) {
+        var newWidth = 600;
+        var newHeight = 200;
+        angular.element('#tb-alarm-details', element).height(newHeight.toString() + "px")
+            .width(newWidth.toString() + "px");
+        vm.editor.resize();
+    }
+
+    function loadAlarm() {
+        alarmService.getAlarmInfo(vm.alarmId).then(
+            function success(alarm) {
+                vm.alarm = alarm;
+                loadAlarmFields();
+            },
+            function fail() {
+                vm.alarm = null;
+            }
+        );
+    }
+
+    function loadAlarmFields() {
+        vm.createdTime = $filter('date')(vm.alarm.createdTime, 'yyyy-MM-dd HH:mm:ss');
+        vm.startTime = null;
+        if (vm.alarm.startTs) {
+            vm.startTime = $filter('date')(vm.alarm.startTs, 'yyyy-MM-dd HH:mm:ss');
+        }
+        vm.endTime = null;
+        if (vm.alarm.endTs) {
+            vm.endTime = $filter('date')(vm.alarm.endTs, 'yyyy-MM-dd HH:mm:ss');
+        }
+        vm.ackTime = null;
+        if (vm.alarm.ackTs) {
+            vm.ackTime = $filter('date')(vm.alarm.ackTs, 'yyyy-MM-dd HH:mm:ss')
+        }
+        vm.clearTime = null;
+        if (vm.alarm.clearTs) {
+            vm.clearTime = $filter('date')(vm.alarm.clearTs, 'yyyy-MM-dd HH:mm:ss');
+        }
+
+        vm.alarmSeverity = $translate.instant(types.alarmSeverity[vm.alarm.severity].name);
+
+        vm.alarmStatus = $translate.instant('alarm.display-status.' + vm.alarm.status);
+
+        vm.alarmDetails = null;
+        if (vm.alarm.details) {
+            vm.alarmDetails = angular.toJson(vm.alarm.details);
+            vm.alarmDetails = js_beautify(vm.alarmDetails, {indent_size: 4});
+        }
+    }
+
+    function acknowledge () {
+        alarmService.ackAlarm(vm.alarmId).then(
+            function success() {
+                vm.alarmUpdated = true;
+                loadAlarm();
+            }
+        );
+    }
+
+    function clear () {
+        alarmService.clearAlarm(vm.alarmId).then(
+            function success() {
+                vm.alarmUpdated = true;
+                loadAlarm();
+            }
+        );
+    }
+
+    function close () {
+        $mdDialog.hide(vm.alarmUpdated ? vm.alarm : null);
+    }
+
+}
diff --git a/ui/src/app/alarm/alarm-details-dialog.scss b/ui/src/app/alarm/alarm-details-dialog.scss
new file mode 100644
index 0000000..9b923d0
--- /dev/null
+++ b/ui/src/app/alarm/alarm-details-dialog.scss
@@ -0,0 +1,27 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.tb-alarm-details-panel {
+  margin-left: 15px;
+  border: 1px solid #C0C0C0;
+  height: 100%;
+  #tb-alarm-details {
+    min-width: 600px;
+    min-height: 200px;
+    width: 100%;
+    height: 100%;
+  }
+}
diff --git a/ui/src/app/alarm/alarm-details-dialog.tpl.html b/ui/src/app/alarm/alarm-details-dialog.tpl.html
new file mode 100644
index 0000000..c958201
--- /dev/null
+++ b/ui/src/app/alarm/alarm-details-dialog.tpl.html
@@ -0,0 +1,107 @@
+<!--
+
+    Copyright © 2016-2017 The Thingsboard Authors
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<md-dialog aria-label="{{ 'alarm.alarm-details' | translate }}">
+    <md-toolbar>
+        <div class="md-toolbar-tools">
+            <h2 translate>alarm.alarm-details</h2>
+            <span flex></span>
+            <md-button class="md-icon-button" ng-click="vm.close()">
+                <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
+            </md-button>
+        </div>
+    </md-toolbar>
+    <md-dialog-content>
+        <div class="md-dialog-content" layout="column">
+            <div layout="row">
+                <md-input-container class="md-block">
+                    <label translate>alarm.created-time</label>
+                    <input ng-model="vm.createdTime" readonly>
+                </md-input-container>
+                <md-input-container flex class="md-block">
+                    <label translate>alarm.originator</label>
+                    <input ng-model="vm.alarm.originatorName" readonly>
+                </md-input-container>
+            </div>
+            <div layout="row" ng-if="vm.startTime || vm.endTime">
+                <md-input-container ng-if="vm.startTime" flex class="md-block">
+                    <label translate>alarm.start-time</label>
+                    <input ng-model="vm.startTime" readonly>
+                </md-input-container>
+                <md-input-container ng-if="vm.endTime" flex class="md-block">
+                    <label translate>alarm.end-time</label>
+                    <input ng-model="vm.endTime" readonly>
+                </md-input-container>
+                <span flex ng-if="!vm.startTime || !vm.endTime"></span>
+            </div>
+            <div layout="row" ng-if="vm.ackTime || vm.clearTime">
+                <md-input-container ng-if="vm.ackTime" flex class="md-block">
+                    <label translate>alarm.ack-time</label>
+                    <input ng-model="vm.ackTime" readonly>
+                </md-input-container>
+                <md-input-container ng-if="vm.clearTime" flex class="md-block">
+                    <label translate>alarm.clear-time</label>
+                    <input ng-model="vm.clearTime" readonly>
+                </md-input-container>
+                <span flex ng-if="!vm.ackTime || !vm.clearTime"></span>
+            </div>
+            <div layout="row">
+                <md-input-container flex class="md-block">
+                    <label translate>alarm.type</label>
+                    <input ng-model="vm.alarm.type" readonly>
+                </md-input-container>
+                <md-input-container flex class="md-block">
+                    <label translate>alarm.severity</label>
+                    <input class="tb-severity" ng-class="vm.types.alarmSeverity[vm.alarm.severity].class"
+                           ng-model="vm.alarmSeverity" readonly>
+                </md-input-container>
+                <md-input-container flex class="md-block">
+                    <label translate>alarm.status</label>
+                    <input ng-model="vm.alarmStatus" readonly>
+                </md-input-container>
+            </div>
+            <div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>alarm.details</div>
+            <div flex class="tb-alarm-details-panel" layout="column">
+                <div flex id="tb-alarm-details" readonly
+                     ui-ace="vm.alarmDetailsOptions"
+                     ng-model="vm.alarmDetails">
+                </div>
+            </div>
+        </div>
+    </md-dialog-content>
+    <md-dialog-actions layout="row">
+        <md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeUnack ||
+                          vm.alarm.status==vm.types.alarmStatus.clearedUnack"
+                   class="md-raised md-primary"
+                   ng-disabled="loading"
+                   ng-click="vm.acknowledge()"
+                   style="margin-right:20px;">{{ 'alarm.acknowledge' |
+            translate }}
+        </md-button>
+        <md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeAck ||
+                          vm.alarm.status==vm.types.alarmStatus.activeUnack"
+                   class="md-raised md-primary"
+                   ng-disabled="loading"
+                   ng-click="vm.clear()">{{ 'alarm.clear' |
+            translate }}
+        </md-button>
+        <span flex></span>
+        <md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
+            translate }}
+        </md-button>
+    </md-dialog-actions>
+</md-dialog>
diff --git a/ui/src/app/alarm/alarm-header.directive.js b/ui/src/app/alarm/alarm-header.directive.js
new file mode 100644
index 0000000..b66a972
--- /dev/null
+++ b/ui/src/app/alarm/alarm-header.directive.js
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* eslint-disable import/no-unresolved, import/default */
+
+import alarmHeaderTemplate from './alarm-header.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function AlarmHeaderDirective($compile, $templateCache) {
+
+    var linker = function (scope, element) {
+
+        var template = $templateCache.get(alarmHeaderTemplate);
+        element.html(template);
+        $compile(element.contents())(scope);
+
+    }
+
+    return {
+        restrict: "A",
+        replace: false,
+        link: linker,
+        scope: false
+    };
+}
diff --git a/ui/src/app/alarm/alarm-row.directive.js b/ui/src/app/alarm/alarm-row.directive.js
new file mode 100644
index 0000000..9cb9bed
--- /dev/null
+++ b/ui/src/app/alarm/alarm-row.directive.js
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* eslint-disable import/no-unresolved, import/default */
+
+import alarmDetailsDialogTemplate from './alarm-details-dialog.tpl.html';
+
+import alarmRowTemplate from './alarm-row.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function AlarmRowDirective($compile, $templateCache, types, $mdDialog, $document) {
+
+    var linker = function (scope, element, attrs) {
+
+        var template = $templateCache.get(alarmRowTemplate);
+        element.html(template);
+
+        scope.alarm = attrs.alarm;
+        scope.types = types;
+
+        scope.showAlarmDetails = function($event) {
+            var onShowingCallback = {
+                onShowing: function(){}
+            }
+            $mdDialog.show({
+                controller: 'AlarmDetailsDialogController',
+                controllerAs: 'vm',
+                templateUrl: alarmDetailsDialogTemplate,
+                locals: {alarmId: scope.alarm.id.id, showingCallback: onShowingCallback},
+                parent: angular.element($document[0].body),
+                targetEvent: $event,
+                fullscreen: true,
+                skipHide: true,
+                onShowing: function(scope, element) {
+                    onShowingCallback.onShowing(scope, element);
+                }
+            }).then(function (alarm) {
+                if (alarm) {
+                    scope.alarm = alarm;
+                }
+            });
+        }
+
+        $compile(element.contents())(scope);
+    }
+
+    return {
+        restrict: "A",
+        replace: false,
+        link: linker,
+        scope: false
+    };
+}
diff --git a/ui/src/app/alarm/alarm-table.directive.js b/ui/src/app/alarm/alarm-table.directive.js
new file mode 100644
index 0000000..c93ea03
--- /dev/null
+++ b/ui/src/app/alarm/alarm-table.directive.js
@@ -0,0 +1,211 @@
+/*
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import './alarm.scss';
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import alarmTableTemplate from './alarm-table.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function AlarmTableDirective($compile, $templateCache, $rootScope, types, alarmService) {
+
+    var linker = function (scope, element) {
+
+        var template = $templateCache.get(alarmTableTemplate);
+
+        element.html(template);
+
+        scope.types = types;
+
+        scope.alarmSearchStatus = types.alarmSearchStatus.any;
+
+        var pageSize = 20;
+        var startTime = 0;
+        var endTime = 0;
+
+        scope.timewindow = {
+            history: {
+                timewindowMs: 24 * 60 * 60 * 1000 // 1 day
+            }
+        }
+
+        scope.topIndex = 0;
+
+        scope.theAlarms = {
+            getItemAtIndex: function (index) {
+                if (index > scope.alarms.data.length) {
+                    scope.theAlarms.fetchMoreItems_(index);
+                    return null;
+                }
+                var item = scope.alarms.data[index];
+                if (item) {
+                    item.indexNumber = index + 1;
+                }
+                return item;
+            },
+
+            getLength: function () {
+                if (scope.alarms.hasNext) {
+                    return scope.alarms.data.length + scope.alarms.nextPageLink.limit;
+                } else {
+                    return scope.alarms.data.length;
+                }
+            },
+
+            fetchMoreItems_: function () {
+                if (scope.alarms.hasNext && !scope.alarms.pending) {
+                    if (scope.entityType && scope.entityId && scope.alarmSearchStatus) {
+                        var promise = alarmService.getAlarms(scope.entityType, scope.entityId,
+                            scope.alarms.nextPageLink, scope.alarmSearchStatus, null, true, false);
+                        if (promise) {
+                            scope.alarms.pending = true;
+                            promise.then(
+                                function success(alarms) {
+                                    scope.alarms.data = scope.alarms.data.concat(alarms.data);
+                                    scope.alarms.nextPageLink = alarms.nextPageLink;
+                                    scope.alarms.hasNext = alarms.hasNext;
+                                    if (scope.alarms.hasNext) {
+                                        scope.alarms.nextPageLink.limit = pageSize;
+                                    }
+                                    scope.alarms.pending = false;
+                                },
+                                function fail() {
+                                    scope.alarms.hasNext = false;
+                                    scope.alarms.pending = false;
+                                });
+                        } else {
+                            scope.alarms.hasNext = false;
+                        }
+                    } else {
+                        scope.alarms.hasNext = false;
+                    }
+                }
+            }
+        };
+
+        scope.$watch("entityId", function(newVal, prevVal) {
+            if (newVal && !angular.equals(newVal, prevVal)) {
+                resetFilter();
+                reload();
+            }
+        });
+
+
+
+        function destroyWatchers() {
+            if (scope.alarmSearchStatusWatchHandle) {
+                scope.alarmSearchStatusWatchHandle();
+                scope.alarmSearchStatusWatchHandle = null;
+            }
+            if (scope.timewindowWatchHandle) {
+                scope.timewindowWatchHandle();
+                scope.timewindowWatchHandle = null;
+            }
+        }
+
+        function initWatchers() {
+            scope.alarmSearchStatusWatchHandle = scope.$watch("alarmSearchStatus", function(newVal, prevVal) {
+                if (newVal && !angular.equals(newVal, prevVal)) {
+                    reload();
+                }
+            });
+            scope.timewindowWatchHandle = scope.$watch("timewindow", function(newVal, prevVal) {
+                if (newVal && !angular.equals(newVal, prevVal)) {
+                    reload();
+                }
+            }, true);
+        }
+
+        function resetFilter() {
+            destroyWatchers();
+            scope.timewindow = {
+                history: {
+                    timewindowMs: 24 * 60 * 60 * 1000 // 1 day
+                }
+            };
+            scope.alarmSearchStatus = types.alarmSearchStatus.any;
+            initWatchers();
+        }
+
+        function updateTimeWindowRange () {
+            if (scope.timewindow.history.timewindowMs) {
+                var currentTime = (new Date).getTime();
+                startTime = currentTime - scope.timewindow.history.timewindowMs;
+                endTime = currentTime;
+            } else {
+                startTime = scope.timewindow.history.fixedTimewindow.startTimeMs;
+                endTime = scope.timewindow.history.fixedTimewindow.endTimeMs;
+            }
+        }
+
+        function reload () {
+            scope.topIndex = 0;
+            scope.selected = [];
+            updateTimeWindowRange();
+            scope.alarms = {
+                data: [],
+                nextPageLink: {
+                    limit: pageSize,
+                    startTime: startTime,
+                    endTime: endTime
+                },
+                hasNext: true,
+                pending: false
+            };
+            scope.theAlarms.getItemAtIndex(pageSize);
+        }
+
+        scope.noData = function() {
+            return scope.alarms.data.length == 0 && !scope.alarms.hasNext;
+        }
+
+        scope.hasData = function() {
+            return scope.alarms.data.length > 0;
+        }
+
+        scope.loading = function() {
+            return $rootScope.loading;
+        }
+
+        scope.hasScroll = function() {
+            var repeatContainer = scope.repeatContainer[0];
+            if (repeatContainer) {
+                var scrollElement = repeatContainer.children[0];
+                if (scrollElement) {
+                    return scrollElement.scrollHeight > scrollElement.clientHeight;
+                }
+            }
+            return false;
+        }
+
+        reload();
+
+        initWatchers();
+
+        $compile(element.contents())(scope);
+    }
+
+    return {
+        restrict: "E",
+        link: linker,
+        scope: {
+            entityType: '=',
+            entityId: '='
+        }
+    };
+}
diff --git a/ui/src/app/alarm/alarm-table.tpl.html b/ui/src/app/alarm/alarm-table.tpl.html
new file mode 100644
index 0000000..c32e39a
--- /dev/null
+++ b/ui/src/app/alarm/alarm-table.tpl.html
@@ -0,0 +1,49 @@
+<!--
+
+    Copyright © 2016-2017 The Thingsboard Authors
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<md-content flex class="md-padding tb-absolute-fill" layout="column">
+    <section layout="row">
+        <md-input-container class="md-block" style="width: 200px;">
+            <label translate>alarm.alarm-status</label>
+            <md-select ng-model="alarmSearchStatus" ng-disabled="loading()">
+                <md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus">
+                    {{ ('alarm.search-status.' + searchStatus) | translate }}
+                </md-option>
+            </md-select>
+        </md-input-container>
+        <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow>
+    </section>
+    <div flex layout="column" class="tb-alarm-container md-whiteframe-z1">
+        <md-list flex layout="column" class="tb-alarm-table">
+            <md-list class="tb-row tb-header" layout="row" tb-alarm-header>
+            </md-list>
+            <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"
+                                ng-show="loading()"></md-progress-linear>
+            <md-divider></md-divider>
+            <span translate layout-align="center center"
+                  style="margin-top: 25px;"
+                  class="tb-prompt" ng-show="noData()">alarm.no-alarms-prompt</span>
+            <md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer">
+                <md-list-item md-virtual-repeat="alarm in theAlarms" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}">
+                    <md-list class="tb-row" flex layout="row" tb-alarm-row alarm="{{alarm}}">
+                    </md-list>
+                    <md-divider flex></md-divider>
+                </md-list-item>
+            </md-virtual-repeat-container>
+        </md-list>
+    </div>
+</md-content>
diff --git a/ui/src/app/alarm/index.js b/ui/src/app/alarm/index.js
new file mode 100644
index 0000000..0ea4610
--- /dev/null
+++ b/ui/src/app/alarm/index.js
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import AlarmDetailsDialogController from './alarm-details-dialog.controller';
+import AlarmHeaderDirective from './alarm-header.directive';
+import AlarmRowDirective from './alarm-row.directive';
+import AlarmTableDirective from './alarm-table.directive';
+
+export default angular.module('thingsboard.alarm', [])
+    .controller('AlarmDetailsDialogController', AlarmDetailsDialogController)
+    .directive('tbAlarmHeader', AlarmHeaderDirective)
+    .directive('tbAlarmRow', AlarmRowDirective)
+    .directive('tbAlarmTable', AlarmTableDirective)
+    .name;
diff --git a/ui/src/app/api/alarm.service.js b/ui/src/app/api/alarm.service.js
index ca892f2..34e6b59 100644
--- a/ui/src/app/api/alarm.service.js
+++ b/ui/src/app/api/alarm.service.js
@@ -21,6 +21,7 @@ export default angular.module('thingsboard.api.alarm', [])
 function AlarmService($http, $q, $interval, $filter) {
     var service = {
         getAlarm: getAlarm,
+        getAlarmInfo: getAlarmInfo,
         saveAlarm: saveAlarm,
         ackAlarm: ackAlarm,
         clearAlarm: clearAlarm,
@@ -46,6 +47,21 @@ function AlarmService($http, $q, $interval, $filter) {
         return deferred.promise;
     }
 
+    function getAlarmInfo(alarmId, ignoreErrors, config) {
+        var deferred = $q.defer();
+        var url = '/api/alarm/info/' + alarmId;
+        if (!config) {
+            config = {};
+        }
+        config = Object.assign(config, { ignoreErrors: ignoreErrors });
+        $http.get(url, config).then(function success(response) {
+            deferred.resolve(response.data);
+        }, function fail() {
+            deferred.reject();
+        });
+        return deferred.promise;
+    }
+
     function saveAlarm(alarm, ignoreErrors, config) {
         var deferred = $q.defer();
         var url = '/api/alarm';
diff --git a/ui/src/app/asset/assets.tpl.html b/ui/src/app/asset/assets.tpl.html
index 11a118f..8370d3d 100644
--- a/ui/src/app/asset/assets.tpl.html
+++ b/ui/src/app/asset/assets.tpl.html
@@ -48,11 +48,16 @@
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
+            <tb-alarm-table flex entity-type="vm.types.entityType.asset"
+                            entity-id="vm.grid.operatingItem().id.id">
+            </tb-alarm-table>
+        </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'asset.events' | translate }}">
             <tb-event-table flex entity-type="vm.types.entityType.asset"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
-                            default-event-type="{{vm.types.eventType.alarm.value}}">
+                            default-event-type="{{vm.types.eventType.error.value}}">
             </tb-event-table>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/app/asset/index.js b/ui/src/app/asset/index.js
index 62ab201..2426e85 100644
--- a/ui/src/app/asset/index.js
+++ b/ui/src/app/asset/index.js
@@ -15,7 +15,6 @@
  */
 import uiRouter from 'angular-ui-router';
 import thingsboardGrid from '../components/grid.directive';
-import thingsboardEvent from '../event';
 import thingsboardApiUser from '../api/user.service';
 import thingsboardApiAsset from '../api/asset.service';
 import thingsboardApiCustomer from '../api/customer.service';
@@ -29,7 +28,6 @@ import AssetDirective from './asset.directive';
 export default angular.module('thingsboard.asset', [
     uiRouter,
     thingsboardGrid,
-    thingsboardEvent,
     thingsboardApiUser,
     thingsboardApiAsset,
     thingsboardApiCustomer
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index 2076093..b281ad1 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -72,6 +72,28 @@ export default angular.module('thingsboard.types', [])
                 ack: "ACK",
                 unack: "UNACK"
             },
+            alarmSeverity: {
+                "CRITICAL": {
+                    name: "alarm.severity-critical",
+                    class: "tb-critical"
+                },
+                "MAJOR": {
+                    name: "alarm.severity-major",
+                    class: "tb-major"
+                },
+                "MINOR": {
+                    name: "alarm.severity-minor",
+                    class: "tb-minor"
+                },
+                "WARNING": {
+                    name: "alarm.severity-warning",
+                    class: "tb-warning"
+                },
+                "INDETERMINATE": {
+                    name: "alarm.severity-indeterminate",
+                    class: "tb-indeterminate"
+                }
+            },
             aliasFilterType: {
                 entityList: {
                     value: 'entityList',
@@ -215,10 +237,6 @@ export default angular.module('thingsboard.types', [])
                 manages: "Manages"
             },
             eventType: {
-                alarm: {
-                    value: "ALARM",
-                    name: "event.type-alarm"
-                },
                 error: {
                     value: "ERROR",
                     name: "event.type-error"
diff --git a/ui/src/app/customer/customers.tpl.html b/ui/src/app/customer/customers.tpl.html
index 6c70a70..a6521f0 100644
--- a/ui/src/app/customer/customers.tpl.html
+++ b/ui/src/app/customer/customers.tpl.html
@@ -48,11 +48,16 @@
 								disable-attribute-scope-selection="true">
 			</tb-attribute-table>
 		</md-tab>
+		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
+			<tb-alarm-table flex entity-type="vm.types.entityType.customer"
+							entity-id="vm.grid.operatingItem().id.id">
+			</tb-alarm-table>
+		</md-tab>
 		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'customer.events' | translate }}">
 			<tb-event-table flex entity-type="vm.types.entityType.customer"
 							entity-id="vm.grid.operatingItem().id.id"
 							tenant-id="vm.grid.operatingItem().tenantId.id"
-							default-event-type="{{vm.types.eventType.alarm.value}}">
+							default-event-type="{{vm.types.eventType.error.value}}">
 			</tb-event-table>
 		</md-tab>
 		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/app/device/devices.tpl.html b/ui/src/app/device/devices.tpl.html
index 3ff6c1c..1e467be 100644
--- a/ui/src/app/device/devices.tpl.html
+++ b/ui/src/app/device/devices.tpl.html
@@ -49,11 +49,16 @@
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
+            <tb-alarm-table flex entity-type="vm.types.entityType.device"
+                            entity-id="vm.grid.operatingItem().id.id">
+            </tb-alarm-table>
+        </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'device.events' | translate }}">
             <tb-event-table flex entity-type="vm.types.entityType.device"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
-                            default-event-type="{{vm.types.eventType.alarm.value}}">
+                            default-event-type="{{vm.types.eventType.error.value}}">
             </tb-event-table>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/app/device/index.js b/ui/src/app/device/index.js
index ea42ee8..5a8adaf 100644
--- a/ui/src/app/device/index.js
+++ b/ui/src/app/device/index.js
@@ -15,7 +15,6 @@
  */
 import uiRouter from 'angular-ui-router';
 import thingsboardGrid from '../components/grid.directive';
-import thingsboardEvent from '../event';
 import thingsboardApiUser from '../api/user.service';
 import thingsboardApiDevice from '../api/device.service';
 import thingsboardApiCustomer from '../api/customer.service';
@@ -30,7 +29,6 @@ import DeviceDirective from './device.directive';
 export default angular.module('thingsboard.device', [
     uiRouter,
     thingsboardGrid,
-    thingsboardEvent,
     thingsboardApiUser,
     thingsboardApiDevice,
     thingsboardApiCustomer
diff --git a/ui/src/app/event/event.scss b/ui/src/app/event/event.scss
index 6fa3e67..8622b0d 100644
--- a/ui/src/app/event/event.scss
+++ b/ui/src/app/event/event.scss
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-md-list.tb-table {
+md-list.tb-event-table {
     padding: 0px;
 
     md-list-item {
@@ -64,7 +64,7 @@ md-list.tb-table {
 
 }
 
-#tb-content {
+#tb-event-content {
   min-width: 400px;
   min-height: 50px;
   width: 100%;
diff --git a/ui/src/app/event/event-content-dialog.controller.js b/ui/src/app/event/event-content-dialog.controller.js
index 19ddb8c..235cfcf 100644
--- a/ui/src/app/event/event-content-dialog.controller.js
+++ b/ui/src/app/event/event-content-dialog.controller.js
@@ -62,7 +62,7 @@ export default function EventContentDialogController($mdDialog, content, title, 
             }
             newWidth = 8 * maxLineLength + 16;
         }
-        $('#tb-content', element).height(newHeight.toString() + "px")
+        $('#tb-event-content', element).height(newHeight.toString() + "px")
             .width(newWidth.toString() + "px");
         vm.editor.resize();
     }
diff --git a/ui/src/app/event/event-content-dialog.tpl.html b/ui/src/app/event/event-content-dialog.tpl.html
index 4cf046b..7b4184c 100644
--- a/ui/src/app/event/event-content-dialog.tpl.html
+++ b/ui/src/app/event/event-content-dialog.tpl.html
@@ -27,7 +27,7 @@
         </md-toolbar>
         <md-dialog-content>
             <div class="md-dialog-content">
-                <div flex id="tb-content" readonly
+                <div flex id="tb-event-content" readonly
                      ui-ace="vm.contentOptions"
                      ng-model="vm.content">
                 </div>
diff --git a/ui/src/app/event/event-header.directive.js b/ui/src/app/event/event-header.directive.js
index 5e8cc7c..c43894e 100644
--- a/ui/src/app/event/event-header.directive.js
+++ b/ui/src/app/event/event-header.directive.js
@@ -18,7 +18,6 @@
 import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html';
 import eventHeaderStatsTemplate from './event-header-stats.tpl.html';
 import eventHeaderErrorTemplate from './event-header-error.tpl.html';
-import eventHeaderAlarmTemplate from './event-header-alarm.tpl.html';
 
 /* eslint-enable import/no-unresolved, import/default */
 
@@ -39,9 +38,6 @@ export default function EventHeaderDirective($compile, $templateCache, types) {
                 case types.eventType.error.value:
                     template = eventHeaderErrorTemplate;
                     break;
-                case types.eventType.alarm.value:
-                    template = eventHeaderAlarmTemplate;
-                    break;
             }
             return $templateCache.get(template);
         }
diff --git a/ui/src/app/event/event-row.directive.js b/ui/src/app/event/event-row.directive.js
index 9bff192..d1feb60 100644
--- a/ui/src/app/event/event-row.directive.js
+++ b/ui/src/app/event/event-row.directive.js
@@ -20,7 +20,6 @@ import eventErrorDialogTemplate from './event-content-dialog.tpl.html';
 import eventRowLcEventTemplate from './event-row-lc-event.tpl.html';
 import eventRowStatsTemplate from './event-row-stats.tpl.html';
 import eventRowErrorTemplate from './event-row-error.tpl.html';
-import eventRowAlarmTemplate from './event-row-alarm.tpl.html';
 
 /* eslint-enable import/no-unresolved, import/default */
 
@@ -41,9 +40,6 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
                 case types.eventType.error.value:
                     template = eventRowErrorTemplate;
                     break;
-                case types.eventType.alarm.value:
-                    template = eventRowAlarmTemplate;
-                    break;
             }
             return $templateCache.get(template);
         }
diff --git a/ui/src/app/event/event-table.tpl.html b/ui/src/app/event/event-table.tpl.html
index f44aeb3..82a921f 100644
--- a/ui/src/app/event/event-table.tpl.html
+++ b/ui/src/app/event/event-table.tpl.html
@@ -27,7 +27,7 @@
         </md-input-container>
         <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow>
     </section>
-    <md-list flex layout="column" class="md-whiteframe-z1 tb-table">
+    <md-list flex layout="column" class="md-whiteframe-z1 tb-event-table">
            <md-list class="tb-row tb-header" layout="row" tb-event-header event-type="{{eventType}}">
            </md-list>
         <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"
diff --git a/ui/src/app/layout/index.js b/ui/src/app/layout/index.js
index be63558..2a27c93 100644
--- a/ui/src/app/layout/index.js
+++ b/ui/src/app/layout/index.js
@@ -32,6 +32,8 @@ import thingsboardDashboardAutocomplete from '../components/dashboard-autocomple
 import thingsboardUserMenu from './user-menu.directive';
 
 import thingsboardEntity from '../entity';
+import thingsboardEvent from '../event';
+import thingsboardAlarm from '../alarm';
 import thingsboardTenant from '../tenant';
 import thingsboardCustomer from '../customer';
 import thingsboardUser from '../user';
@@ -61,6 +63,8 @@ export default angular.module('thingsboard.home', [
     thingsboardHomeLinks,
     thingsboardUserMenu,
     thingsboardEntity,
+    thingsboardEvent,
+    thingsboardAlarm,
     thingsboardTenant,
     thingsboardCustomer,
     thingsboardUser,
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index cd21747..a54eba4 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -108,9 +108,43 @@ export default angular.module('thingsboard.locale', [])
                 },
                 "alarm": {
                     "alarm": "Alarm",
+                    "alarms": "Alarms",
                     "select-alarm": "Select alarm",
                     "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
-                    "alarm-required": "Alarm is required"
+                    "alarm-required": "Alarm is required",
+                    "alarm-status": "Alarm status",
+                    "search-status": {
+                        "ANY": "Any",
+                        "ACTIVE": "Active",
+                        "CLEARED": "Cleared",
+                        "ACK": "Acknowledged",
+                        "UNACK": "Unacknowledged"
+                    },
+                    "display-status": {
+                        "ACTIVE_UNACK": "Active Unacknowledged",
+                        "ACTIVE_ACK": "Active Acknowledged",
+                        "CLEARED_UNACK": "Cleared Unacknowledged",
+                        "CLEARED_ACK": "Cleared Acknowledged"
+                    },
+                    "no-alarms-prompt": "No alarms found",
+                    "created-time": "Created time",
+                    "type": "Type",
+                    "severity": "Severity",
+                    "originator": "Originator",
+                    "details": "Details",
+                    "status": "Status",
+                    "alarm-details": "Alarm details",
+                    "start-time": "Start time",
+                    "end-time": "End time",
+                    "ack-time": "Acknowledged time",
+                    "clear-time": "Cleared time",
+                    "severity-critical": "Critical",
+                    "severity-major": "Major",
+                    "severity-minor": "Minor",
+                    "severity-warning": "Warning",
+                    "severity-indeterminate": "Indeterminate",
+                    "acknowledge": "Acknowledge",
+                    "clear": "Clear"
                 },
                 "alias": {
                     "add": "Add alias",
@@ -647,7 +681,6 @@ export default angular.module('thingsboard.locale', [])
                 },
                 "event": {
                     "event-type": "Event type",
-                    "type-alarm": "Alarm",
                     "type-error": "Error",
                     "type-lc-event": "Lifecycle event",
                     "type-stats": "Statistics",
diff --git a/ui/src/app/locale/locale.constant-es.js b/ui/src/app/locale/locale.constant-es.js
index e4d4b49..b9076ba 100644
--- a/ui/src/app/locale/locale.constant-es.js
+++ b/ui/src/app/locale/locale.constant-es.js
@@ -411,7 +411,6 @@
         },
         "event": {
               "event-type": "Tipo de evento",
-              "type-alarm": "Alarma",
               "type-error": "Error",
               "type-lc-event": "Ciclo de vida",
               "type-stats": "Estadísticas",
diff --git a/ui/src/app/locale/locale.constant-ko.js b/ui/src/app/locale/locale.constant-ko.js
index 32dbf49..0caeab8 100644
--- a/ui/src/app/locale/locale.constant-ko.js
+++ b/ui/src/app/locale/locale.constant-ko.js
@@ -378,7 +378,6 @@ export default function addLocaleKorean(locales) {
         },
         "event": {
             "event-type": "이벤트 타입",
-            "type-alarm": "알람",
             "type-error": "에러",
             "type-lc-event": "주기적 이벤트",
             "type-stats": "통계",
diff --git a/ui/src/app/locale/locale.constant-ru.js b/ui/src/app/locale/locale.constant-ru.js
index d7734b4..cded7a6 100644
--- a/ui/src/app/locale/locale.constant-ru.js
+++ b/ui/src/app/locale/locale.constant-ru.js
@@ -411,7 +411,6 @@ export default function addLocaleRussian(locales) {
         },
         "event": {
             "event-type": "Тип события",
-            "type-alarm": "Аварийное оповещение",
             "type-error": "Ошибка",
             "type-lc-event": "Событие жизненного цикла",
             "type-stats": "Статистика",
diff --git a/ui/src/app/locale/locale.constant-zh.js b/ui/src/app/locale/locale.constant-zh.js
index 3246070..62746c7 100644
--- a/ui/src/app/locale/locale.constant-zh.js
+++ b/ui/src/app/locale/locale.constant-zh.js
@@ -411,7 +411,6 @@ export default function addLocaleChinese(locales) {
         },
         "event" : {
             "event-type": "事件类型",
-            "type-alarm": "报警",
             "type-error": "错误",
             "type-lc-event": "生命周期事件",
             "type-stats": "类型统计",
diff --git a/ui/src/app/plugin/index.js b/ui/src/app/plugin/index.js
index 1c9e731..6173d62 100644
--- a/ui/src/app/plugin/index.js
+++ b/ui/src/app/plugin/index.js
@@ -16,7 +16,6 @@
 import uiRouter from 'angular-ui-router';
 import thingsboardGrid from '../components/grid.directive';
 import thingsboardJsonForm from '../components/json-form.directive';
-import thingsboardEvent from '../event';
 import thingsboardApiPlugin from '../api/plugin.service';
 import thingsboardApiComponentDescriptor from '../api/component-descriptor.service';
 
@@ -28,7 +27,6 @@ export default angular.module('thingsboard.plugin', [
     uiRouter,
     thingsboardGrid,
     thingsboardJsonForm,
-    thingsboardEvent,
     thingsboardApiPlugin,
     thingsboardApiComponentDescriptor
 ])
diff --git a/ui/src/app/plugin/plugins.tpl.html b/ui/src/app/plugin/plugins.tpl.html
index 5b03506..73b0adc 100644
--- a/ui/src/app/plugin/plugins.tpl.html
+++ b/ui/src/app/plugin/plugins.tpl.html
@@ -48,12 +48,16 @@
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'alarm.alarms' | translate }}">
+            <tb-alarm-table flex entity-type="vm.types.entityType.plugin"
+                            entity-id="vm.grid.operatingItem().id.id">
+            </tb-alarm-table>
+        </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'plugin.events' | translate }}">
             <tb-event-table flex entity-type="vm.types.entityType.plugin"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
-                            default-event-type="{{vm.types.eventType.lcEvent.value}}"
-                            disabled-event-types="{{vm.types.eventType.alarm.value}}">
+                            default-event-type="{{vm.types.eventType.lcEvent.value}}">
             </tb-event-table>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/app/rule/index.js b/ui/src/app/rule/index.js
index 6481920..2700ec7 100644
--- a/ui/src/app/rule/index.js
+++ b/ui/src/app/rule/index.js
@@ -17,7 +17,6 @@ import uiRouter from 'angular-ui-router';
 import thingsboardGrid from '../components/grid.directive';
 import thingsboardPluginSelect from '../components/plugin-select.directive';
 import thingsboardComponent from '../component';
-import thingsboardEvent from '../event';
 import thingsboardApiRule from '../api/rule.service';
 import thingsboardApiPlugin from '../api/plugin.service';
 import thingsboardApiComponentDescriptor from '../api/component-descriptor.service';
@@ -31,7 +30,6 @@ export default angular.module('thingsboard.rule', [
     thingsboardGrid,
     thingsboardPluginSelect,
     thingsboardComponent,
-    thingsboardEvent,
     thingsboardApiRule,
     thingsboardApiPlugin,
     thingsboardApiComponentDescriptor
diff --git a/ui/src/app/rule/rules.tpl.html b/ui/src/app/rule/rules.tpl.html
index 098bbee..336fe63 100644
--- a/ui/src/app/rule/rules.tpl.html
+++ b/ui/src/app/rule/rules.tpl.html
@@ -48,12 +48,16 @@
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'alarm.alarms' | translate }}">
+            <tb-alarm-table flex entity-type="vm.types.entityType.rule"
+                            entity-id="vm.grid.operatingItem().id.id">
+            </tb-alarm-table>
+        </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'rule.events' | translate }}">
             <tb-event-table flex entity-type="vm.types.entityType.rule"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
-                            default-event-type="{{vm.types.eventType.lcEvent.value}}"
-                            disabled-event-types="{{vm.types.eventType.alarm.value}}">
+                            default-event-type="{{vm.types.eventType.lcEvent.value}}">
             </tb-event-table>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/app/tenant/tenants.tpl.html b/ui/src/app/tenant/tenants.tpl.html
index 00350a4..e407291 100644
--- a/ui/src/app/tenant/tenants.tpl.html
+++ b/ui/src/app/tenant/tenants.tpl.html
@@ -46,11 +46,16 @@
 								disable-attribute-scope-selection="true">
 			</tb-attribute-table>
 		</md-tab>
+		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
+			<tb-alarm-table flex entity-type="vm.types.entityType.tenant"
+							entity-id="vm.grid.operatingItem().id.id">
+			</tb-alarm-table>
+		</md-tab>
 		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'tenant.events' | translate }}">
 			<tb-event-table flex entity-type="vm.types.entityType.tenant"
 							entity-id="vm.grid.operatingItem().id.id"
 							tenant-id="vm.types.id.nullUid"
-							default-event-type="{{vm.types.eventType.alarm.value}}">
+							default-event-type="{{vm.types.eventType.error.value}}">
 			</tb-event-table>
 		</md-tab>
 		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index bf44493..ab720c5 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -309,6 +309,24 @@ pre.tb-highlight {
   }
 }
 
+.tb-severity {
+  font-weight: bold;
+  &.tb-critical {
+    color: red !important;
+  }
+  &.tb-major {
+    color: orange !important;
+  }
+  &.tb-minor {
+    color: #ffca3d !important;
+  }
+  &.tb-warning {
+    color: #abab00 !important;
+  }
+  &.tb-indeterminate {
+    color: green !important;
+  }
+}
 
 /***********************
  * Flow