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 7d29d57dc9..7a2c273002 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -72,7 +72,7 @@ public class AlarmController extends BaseController { } } - @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) public void ackAlarm(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException { @@ -86,7 +86,7 @@ public class AlarmController extends BaseController { } } - @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) public void clearAlarm(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException { diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/device/DeviceAttributes.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/device/DeviceAttributes.java index 8628d0c16c..2cfeefb19e 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/device/DeviceAttributes.java +++ b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/device/DeviceAttributes.java @@ -91,4 +91,13 @@ public class DeviceAttributes { } return map; } + + @Override + public String toString() { + return "DeviceAttributes{" + + "clientSideAttributesMap=" + clientSideAttributesMap + + ", serverPrivateAttributesMap=" + serverPrivateAttributesMap + + ", serverPublicAttributesMap=" + serverPublicAttributesMap + + '}'; + } } diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/template/AbstractTemplatePluginAction.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/template/AbstractTemplatePluginAction.java index 9360af17ac..12958f5a3e 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/template/AbstractTemplatePluginAction.java +++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/template/AbstractTemplatePluginAction.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.extensions.core.action.template; +import lombok.extern.slf4j.Slf4j; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.runtime.parser.ParseException; @@ -35,6 +36,7 @@ import java.util.Optional; /** * @author Andrew Shvayka */ +@Slf4j public abstract class AbstractTemplatePluginAction extends SimpleRuleLifecycleComponent implements PluginAction { protected T configuration; protected Template template; @@ -69,6 +71,7 @@ public abstract class AbstractTemplatePluginAction producer; @@ -40,7 +42,7 @@ public class KafkaMsgHandler implements RuleMsgHandler { throw new RuleException("Unsupported message type " + msg.getClass().getName() + "!"); } KafkaActionPayload payload = ((KafkaActionMsg) msg).getPayload(); - + log.debug("Processing kafka payload: {}", payload); try { producer.send(new ProducerRecord<>(payload.getTopic(), payload.getMsgBody()), (metadata, e) -> { diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java index a78319afd9..d6a953a1f9 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java @@ -78,6 +78,7 @@ public class GatewaySessionCtx { Device newDevice = new Device(); newDevice.setTenantId(gateway.getTenantId()); newDevice.setName(deviceName); + newDevice.setType("default"); return deviceService.saveDevice(newDevice); }); GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device); diff --git a/ui/src/app/api/alarm.service.js b/ui/src/app/api/alarm.service.js new file mode 100644 index 0000000000..d9ab4ff338 --- /dev/null +++ b/ui/src/app/api/alarm.service.js @@ -0,0 +1,189 @@ +/* + * 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. + */ +export default angular.module('thingsboard.api.alarm', []) + .factory('alarmService', AlarmService) + .name; + +/*@ngInject*/ +function AlarmService($http, $q, $interval, $filter) { + var service = { + getAlarm: getAlarm, + saveAlarm: saveAlarm, + ackAlarm: ackAlarm, + clearAlarm: clearAlarm, + getAlarms: getAlarms, + pollAlarms: pollAlarms, + cancelPollAlarms: cancelPollAlarms + } + + return service; + + function getAlarm(alarmId, ignoreErrors, config) { + var deferred = $q.defer(); + var url = '/api/alarm/' + 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'; + if (!config) { + config = {}; + } + config = Object.assign(config, { ignoreErrors: ignoreErrors }); + $http.post(url, alarm, config).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; + } + + function ackAlarm(alarmId, ignoreErrors, config) { + var deferred = $q.defer(); + var url = '/api/alarm/' + alarmId + '/ack'; + if (!config) { + config = {}; + } + config = Object.assign(config, { ignoreErrors: ignoreErrors }); + $http.post(url, null, config).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; + } + + function clearAlarm(alarmId, ignoreErrors, config) { + var deferred = $q.defer(); + var url = '/api/alarm/' + alarmId + '/clear'; + if (!config) { + config = {}; + } + config = Object.assign(config, { ignoreErrors: ignoreErrors }); + $http.post(url, null, config).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; + } + + function getAlarms(entityType, entityId, pageLink, alarmStatus, ascOrder, config) { + var deferred = $q.defer(); + var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit; + + if (angular.isDefined(pageLink.startTime)) { + url += '&startTime=' + pageLink.startTime; + } + if (angular.isDefined(pageLink.endTime)) { + url += '&endTime=' + pageLink.endTime; + } + if (angular.isDefined(pageLink.idOffset)) { + url += '&offset=' + pageLink.idOffset; + } + if (alarmStatus) { + url += '&status=' + alarmStatus; + } + if (angular.isDefined(ascOrder) && ascOrder != null) { + url += '&ascOrder=' + (ascOrder ? 'true' : 'false'); + } + + $http.get(url, config).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; + } + + function fetchAlarms(alarmsQuery, pageLink, deferred, alarmsList) { + getAlarms(alarmsQuery.entityType, alarmsQuery.entityId, + pageLink, alarmsQuery.alarmStatus, false, {ignoreLoading: true}).then( + function success(alarms) { + if (!alarmsList) { + alarmsList = []; + } + alarmsList = alarmsList.concat(alarms.data); + if (alarms.hasNext && !alarmsQuery.limit) { + fetchAlarms(alarmsQuery, alarms.nextPageLink, deferred, alarmsList); + } else { + alarmsList = $filter('orderBy')(alarmsList, ['-createdTime']); + deferred.resolve(alarmsList); + } + }, + function fail() { + deferred.reject(); + } + ); + } + + function getAlarmsByQuery(alarmsQuery) { + var deferred = $q.defer(); + var time = Date.now(); + var pageLink; + if (alarmsQuery.limit) { + pageLink = { + limit: alarmsQuery.limit + }; + } else { + pageLink = { + limit: 100, + startTime: time - alarmsQuery.interval + }; + } + fetchAlarms(alarmsQuery, pageLink, deferred); + return deferred.promise; + } + + function onPollAlarms(alarmsQuery) { + getAlarmsByQuery(alarmsQuery).then( + function success(alarms) { + alarmsQuery.onAlarms(alarms); + }, + function fail() {} + ); + } + + function pollAlarms(entityType, entityId, alarmStatus, interval, limit, pollingInterval, onAlarms) { + var alarmsQuery = { + entityType: entityType, + entityId: entityId, + alarmStatus: alarmStatus, + interval: interval, + limit: limit, + onAlarms: onAlarms + }; + onPollAlarms(alarmsQuery); + return $interval(onPollAlarms, pollingInterval, 0, false, alarmsQuery); + } + + function cancelPollAlarms(pollPromise) { + if (angular.isDefined(pollPromise)) { + $interval.cancel(pollPromise); + } + } + +} diff --git a/ui/src/app/api/datasource.service.js b/ui/src/app/api/datasource.service.js index 27da468944..bfa31849f2 100644 --- a/ui/src/app/api/datasource.service.js +++ b/ui/src/app/api/datasource.service.js @@ -595,8 +595,8 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic } series = [time, value]; data.push(series); - update = true; } + update = true; } if (update) { datasourceData[datasourceKey].data = data; diff --git a/ui/src/app/app.js b/ui/src/app/app.js index f67be331a9..9ba34b4c29 100644 --- a/ui/src/app/app.js +++ b/ui/src/app/app.js @@ -67,6 +67,7 @@ import thingsboardApiEntityRelation from './api/entity-relation.service'; import thingsboardApiAsset from './api/asset.service'; import thingsboardApiAttribute from './api/attribute.service'; import thingsboardApiEntity from './api/entity.service'; +import thingsboardApiAlarm from './api/alarm.service'; import 'typeface-roboto'; import 'font-awesome/css/font-awesome.min.css'; @@ -124,6 +125,7 @@ angular.module('thingsboard', [ thingsboardApiAsset, thingsboardApiAttribute, thingsboardApiEntity, + thingsboardApiAlarm, uiRouter]) .config(AppConfig) .factory('globalInterceptor', GlobalInterceptor) diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index 47ac7a01ed..44e59a260d 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -59,6 +59,12 @@ export default angular.module('thingsboard.types', []) name: "aggregation.none" } }, + alarmStatus: { + activeUnack: "ACTIVE_UNACK", + activeAck: "ACTIVE_ACK", + clearedUnack: "CLEARED_UNACK", + clearedAck: "CLEARED_ACK" + }, position: { top: { value: "top", diff --git a/ui/src/app/dashboard/dashboard.controller.js b/ui/src/app/dashboard/dashboard.controller.js index ecf072d7a7..ac14f7953b 100644 --- a/ui/src/app/dashboard/dashboard.controller.js +++ b/ui/src/app/dashboard/dashboard.controller.js @@ -138,10 +138,10 @@ export default function DashboardController(types, dashboardUtils, widgetService } vm.mainLayoutHeight = function() { - if (vm.isEditingWidget && vm.editingLayoutCtx.id === 'main') { + if (!vm.isEditingWidget || vm.editingLayoutCtx.id === 'main') { return '100%'; } else { - return 'auto'; + return '0px'; } } @@ -154,10 +154,10 @@ export default function DashboardController(types, dashboardUtils, widgetService } vm.rightLayoutHeight = function() { - if (vm.isEditingWidget && vm.editingLayoutCtx.id === 'right') { + if (!vm.isEditingWidget || vm.editingLayoutCtx.id === 'right') { return '100%'; } else { - return 'auto'; + return '0px'; } } @@ -369,7 +369,7 @@ export default function DashboardController(types, dashboardUtils, widgetService } } - function openDashboardState(state) { + function openDashboardState(state, openRightLayout) { var layoutsData = dashboardUtils.getStateLayoutsData(vm.dashboard, state); if (layoutsData) { vm.dashboardCtx.state = state; @@ -387,7 +387,7 @@ export default function DashboardController(types, dashboardUtils, widgetService layoutVisibilityChanged = !vm.isMobile; } } - vm.isRightLayoutOpened = false; + vm.isRightLayoutOpened = openRightLayout ? true : false; updateLayouts(layoutVisibilityChanged); } diff --git a/ui/src/app/dashboard/states/default-state-controller.js b/ui/src/app/dashboard/states/default-state-controller.js index 782f59ea68..76ea9b801f 100644 --- a/ui/src/app/dashboard/states/default-state-controller.js +++ b/ui/src/app/dashboard/states/default-state-controller.js @@ -26,12 +26,13 @@ export default function DefaultStateController($scope, $location, $state, $state vm.navigatePrevState = navigatePrevState; vm.getStateId = getStateId; vm.getStateParams = getStateParams; + vm.getStateParamsByStateId = getStateParamsByStateId; vm.getStateName = getStateName; vm.displayStateSelection = displayStateSelection; - function openState(id, params) { + function openState(id, params, openRightLayout) { if (vm.states && vm.states[id]) { if (!params) { params = {}; @@ -42,11 +43,11 @@ export default function DefaultStateController($scope, $location, $state, $state } //append new state vm.stateObject[0] = newState; - gotoState(vm.stateObject[0].id, true); + gotoState(vm.stateObject[0].id, true, openRightLayout); } } - function updateState(id, params) { + function updateState(id, params, openRightLayout) { if (vm.states && vm.states[id]) { if (!params) { params = {}; @@ -57,7 +58,7 @@ export default function DefaultStateController($scope, $location, $state, $state } //replace with new state vm.stateObject[0] = newState; - gotoState(vm.stateObject[0].id, true); + gotoState(vm.stateObject[0].id, true, openRightLayout); } } @@ -76,6 +77,24 @@ export default function DefaultStateController($scope, $location, $state, $state return vm.stateObject[vm.stateObject.length-1].params; } + function getStateParamsByStateId(stateId) { + var stateObj = getStateObjById(stateId); + if (stateObj) { + return stateObj.params; + } else { + return null; + } + } + + function getStateObjById(id) { + for (var i=0; i < vm.stateObject.length; i++) { + if (vm.stateObject[i].id === id) { + return vm.stateObject[i]; + } + } + return null; + } + function getStateName(id, state) { var result = ''; var translationId = types.translate.dashboardStatePrefix + id; @@ -161,9 +180,9 @@ export default function DefaultStateController($scope, $location, $state, $state }, true); } - function gotoState(stateId, update) { + function gotoState(stateId, update, openRightLayout) { if (vm.dashboardCtrl.dashboardCtx.state != stateId) { - vm.dashboardCtrl.openDashboardState(stateId); + vm.dashboardCtrl.openDashboardState(stateId, openRightLayout); if (update) { updateLocation(); } diff --git a/ui/src/app/dashboard/states/entity-state-controller.js b/ui/src/app/dashboard/states/entity-state-controller.js index 8ee9285b5f..51cf67d07c 100644 --- a/ui/src/app/dashboard/states/entity-state-controller.js +++ b/ui/src/app/dashboard/states/entity-state-controller.js @@ -28,12 +28,13 @@ export default function EntityStateController($scope, $location, $state, $stateP vm.navigatePrevState = navigatePrevState; vm.getStateId = getStateId; vm.getStateParams = getStateParams; + vm.getStateParamsByStateId = getStateParamsByStateId; vm.getStateName = getStateName; vm.selectedStateIndex = -1; - function openState(id, params) { + function openState(id, params, openRightLayout) { if (vm.states && vm.states[id]) { resolveEntity(params).then( function success(entityName) { @@ -45,13 +46,13 @@ export default function EntityStateController($scope, $location, $state, $stateP //append new state vm.stateObject.push(newState); vm.selectedStateIndex = vm.stateObject.length-1; - gotoState(vm.stateObject[vm.stateObject.length-1].id, true); + gotoState(vm.stateObject[vm.stateObject.length-1].id, true, openRightLayout); } ); } } - function updateState(id, params) { + function updateState(id, params, openRightLayout) { if (vm.states && vm.states[id]) { resolveEntity(params).then( function success(entityName) { @@ -62,7 +63,7 @@ export default function EntityStateController($scope, $location, $state, $stateP } //replace with new state vm.stateObject[vm.stateObject.length - 1] = newState; - gotoState(vm.stateObject[vm.stateObject.length - 1].id, true); + gotoState(vm.stateObject[vm.stateObject.length - 1].id, true, openRightLayout); } ); } @@ -84,6 +85,24 @@ export default function EntityStateController($scope, $location, $state, $stateP return vm.stateObject[vm.stateObject.length-1].params; } + function getStateParamsByStateId(stateId) { + var stateObj = getStateObjById(stateId); + if (stateObj) { + return stateObj.params; + } else { + return null; + } + } + + function getStateObjById(id) { + for (var i=0; i < vm.stateObject.length; i++) { + if (vm.stateObject[i].id === id) { + return vm.stateObject[i]; + } + } + return null; + } + function getStateName(index) { var result = ''; if (vm.stateObject[index]) { @@ -223,9 +242,9 @@ export default function EntityStateController($scope, $location, $state, $stateP }); } - function gotoState(stateId, update) { + function gotoState(stateId, update, openRightLayout) { if (vm.dashboardCtrl.dashboardCtx.state != stateId) { - vm.dashboardCtrl.openDashboardState(stateId); + vm.dashboardCtrl.openDashboardState(stateId, openRightLayout); if (update) { updateLocation(); } diff --git a/ui/src/app/dashboard/states/states-component.directive.js b/ui/src/app/dashboard/states/states-component.directive.js index fb5e77c3f6..538c3e9620 100644 --- a/ui/src/app/dashboard/states/states-component.directive.js +++ b/ui/src/app/dashboard/states/states-component.directive.js @@ -29,15 +29,15 @@ export default function StatesComponent($compile, $templateCache, $controller, s var stateController = scope.dashboardCtrl.dashboardCtx.stateController; - stateController.openState = function(id, params) { + stateController.openState = function(id, params, openRightLayout) { if (scope.statesController) { - scope.statesController.openState(id, params); + scope.statesController.openState(id, params, openRightLayout); } } - stateController.updateState = function(id, params) { + stateController.updateState = function(id, params, openRightLayout) { if (scope.statesController) { - scope.statesController.updateState(id, params); + scope.statesController.updateState(id, params, openRightLayout); } } @@ -62,6 +62,14 @@ export default function StatesComponent($compile, $templateCache, $controller, s return {}; } } + + stateController.getStateParamsByStateId = function(id) { + if (scope.statesController) { + return scope.statesController.getStateParamsByStateId(id); + } else { + return null; + } + } } scope.$on('$destroy', function callOnDestroyHook() {