diff --git a/ui/package.json b/ui/package.json index cb8fdbd913..81f457417a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -30,6 +30,7 @@ "angular-material": "1.1.1", "angular-material-data-table": "^0.10.9", "angular-material-icons": "^0.7.1", + "angular-material-expansion-panel": "^0.7.2", "angular-messages": "1.5.8", "angular-route": "1.5.8", "angular-sanitize": "1.5.8", diff --git a/ui/src/app/app.js b/ui/src/app/app.js index 4a6f3f0a8d..5b1908928e 100644 --- a/ui/src/app/app.js +++ b/ui/src/app/app.js @@ -39,6 +39,7 @@ import uiRouter from 'angular-ui-router'; import angularJwt from 'angular-jwt'; import 'angular-drag-and-drop-lists'; import mdDataTable from 'angular-material-data-table'; +import 'angular-material-expansion-panel'; import ngTouch from 'angular-touch'; import 'angular-carousel'; import 'clipboard'; @@ -82,6 +83,7 @@ import 'md-color-picker/dist/mdColorPicker.min.css'; import 'mdPickers/dist/mdPickers.min.css'; import 'angular-hotkeys/build/hotkeys.min.css'; import 'angular-carousel/dist/angular-carousel.min.css'; +import 'angular-material-expansion-panel/dist/md-expansion-panel.min.css'; import '../scss/main.scss'; import AppConfig from './app.config'; @@ -103,6 +105,7 @@ angular.module('thingsboard', [ angularJwt, 'dndLists', mdDataTable, + 'material.components.expansionPanels', ngTouch, 'angular-carousel', 'ngclipboard', diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index 52c08d21a5..5b3c9e4ac4 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -332,6 +332,24 @@ export default angular.module('thingsboard.types', []) toDouble: 'extension.to-double', custom: 'extension.custom' }, + mqttConverterTypes: { + json: 'extension.converter-json', + custom: 'extension.custom' + }, + mqttCredentialTypes: { + anonymous: { + value: "anonymous", + name: "extension.anonymous" + }, + basic: { + value: "basic", + name: "extension.basic" + }, + pem: { + value: "cert.PEM", + name: "extension.pem" + } + }, extensionOpcSecurityTypes: { Basic128Rsa15: "Basic128Rsa15", Basic256: "Basic256", @@ -339,8 +357,8 @@ export default angular.module('thingsboard.types', []) None: "None" }, extensionIdentityType: { - anonymous: "anonymous", - username: "username" + anonymous: "extension.anonymous", + username: "extension.username" }, extensionKeystoreType: { PKCS12: "PKCS12", diff --git a/ui/src/app/extension/extension-dialog.controller.js b/ui/src/app/extension/extension-dialog.controller.js index cfec9cf8b4..fe5825c8ee 100644 --- a/ui/src/app/extension/extension-dialog.controller.js +++ b/ui/src/app/extension/extension-dialog.controller.js @@ -30,11 +30,10 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, vm.allExtensions = allExtensions; - if (extension) { // Editing - //vm.configuration = vm.extension.configuration; + if (extension) { vm.extension = angular.copy(extension); editTransformers(vm.extension); - } else { // Add new + } else { vm.extension = {}; } @@ -65,8 +64,6 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, vm.save = save; function save() { - saveTransformers(); - let $errorElement = angular.element('[name=theForm]').find('.ng-invalid'); if ($errorElement.length) { @@ -78,11 +75,10 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, if ($errorElementTop !== $mdDialogTop) { angular.element('md-dialog-content').animate({ - scrollTop: $mdDialogScroll + ($errorElementTop - $mdDialogTop) - 20 + scrollTop: $mdDialogScroll + ($errorElementTop - $mdDialogTop) - 50 }, 500); $errorElement.eq(0).focus(); } - } else { if(vm.isAdd) { @@ -94,6 +90,9 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, } } + $mdDialog.hide(); + saveTransformers(); + var editedValue = angular.toJson(vm.allExtensions); attributeService @@ -104,8 +103,6 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, [{key:"configuration", value:editedValue}] ) .then(function success() { - $scope.theForm.$setPristine(); - $mdDialog.hide(); }); } @@ -131,21 +128,60 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, }; function saveTransformers() { - var config = vm.extension.configuration.converterConfigurations; if(vm.extension.type == types.extensionType.http) { - for(let i=0;i 0) { + for(let i=0;i 0) { + for(let i=0;i 0) { + for(let j=0;j 0) { + for(let j=0;j 0) { + for(let j=0;j 0) { + for(let j=0;j 0) { + for(let j=0;j 0) { + for(let j=0;j 0) { + for(let j=0;j 0) { + for(let j=0;j
- +
-
extension.id-required
+
extension.field-required
extension.unique-id-required
- + @@ -55,16 +55,14 @@
-
extension.type-required
+
extension.field-required
- -
- -
+
+
+
- @@ -80,5 +78,4 @@ - - + \ No newline at end of file diff --git a/ui/src/app/extension/extension-table.directive.js b/ui/src/app/extension/extension-table.directive.js index e109ad954d..03dd58e7cd 100644 --- a/ui/src/app/extension/extension-table.directive.js +++ b/ui/src/app/extension/extension-table.directive.js @@ -43,7 +43,7 @@ export default function ExtensionTableDirective() { } /*@ngInject*/ -function ExtensionTableController($scope, $filter, $document, $translate, types, $mdDialog, attributeService) { +function ExtensionTableController($scope, $filter, $document, $translate, types, $mdDialog, attributeService, telemetryWebsocketService) { let vm = this; @@ -83,51 +83,6 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, } }); - $scope.$watch('vm.transferredAttributes', function () { - if (vm.transferredAttributes && vm.transferredAttributes.data && vm.transferredAttributes.data.length) { - vm.transferredAttributes.data - .some(attribute=>{ - if (attribute.key === "appliedConfiguration") { - vm.appliedConfiguration = attribute.value; - } - }); - - checkForSync(); - } - }); - - - checkForSync(); - function checkForSync() { - if (vm.appliedConfiguration === vm.extensionsJSON) { - vm.syncStatus = $translate.instant('extension.sync.sync'); - vm.syncLastTime = formatDate(); - } else { - vm.syncStatus = $translate.instant('extension.sync.not-sync'); - } - } - - - function formatDate(date) { - let d; - if (date) { - d = date; - } else { - d = new Date(); - } - - d = d.getFullYear() +'/'+ addZero(d.getMonth()+1) +'/'+ addZero(d.getDate()) + ' ' + addZero(d.getHours()) + ':' + addZero(d.getMinutes()) +':'+ addZero(d.getSeconds()); - return d; - - - function addZero(num) { - if ((angular.isNumber(num) && num < 10) || (angular.isString(num) && num.length === 1)) { - num = '0' + num; - } - return num; - } - } - function enterFilterMode() { vm.query.search = ''; } @@ -288,57 +243,73 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, } - // vm.subscriptionId = null; - // $scope.checkSubscription = function() { - // var newSubscriptionId = null; - // if (vm.entityId && vm.entityType) { - // newSubscriptionId = attributeService.subscribeForEntityAttributes(vm.entityType, vm.entityId, 'extension/SHARED_SCOPE'); - // } - // if (vm.subscriptionId && vm.subscriptionId != newSubscriptionId) { - // attributeService.unsubscribeForEntityAttributes(vm.subscriptionId); - // } - // vm.subscriptionId = newSubscriptionId; - // } - // - // - // // $scope.attributesData = {}; - // // var entityAttributesSubscriptionMap = []; - // // - // $scope.subscribeForEntityAttributes = function (entityType=vm.entityType, entityId=vm.entityId, attributeScope="SHARED_SCOPE") { - // var subscriptionId = entityType + entityId + attributeScope; - // var entityAttributesSubscription = entityAttributesSubscriptionMap[subscriptionId]; - // if (!entityAttributesSubscription) { - // var subscriptionCommand = { - // entityType: entityType, - // entityId: entityId, - // scope: attributeScope - // }; - // - // var type = attributeScope === types.latestTelemetry.value ? - // types.dataKeyType.timeseries : types.dataKeyType.attribute; - // - // var subscriber = { - // subscriptionCommands: [subscriptionCommand], - // type: type, - // onData: function (data) { - // if (data.data) { - // onSubscriptionData(data.data, subscriptionId); - // } - // } - // }; - // entityAttributesSubscription = { - // subscriber: subscriber, - // attributes: null - // }; - // entityAttributesSubscriptionMap[subscriptionId] = entityAttributesSubscription; - // telemetryWebsocketService.subscribe(subscriber); - // } - // return subscriptionId; - // }; - // - // function onSubscriptionData(data/*, subscriptionId*/) { - // $scope.attributesData = data; - // } - - // telemetryWebsocketService.subscribe(subscriber); + + if (vm.entityId && vm.entityType) { + $scope.subscriber = { + subscriptionCommands: [{ + entityType: vm.entityType, + entityId: vm.entityId, + scope: 'CLIENT_SCOPE' + }], + type: 'attribute', + onData: function (data) { + if (data.data) { + onSubscriptionData(data.data/*, subscriptionId*/); + } + } + }; + } + telemetryWebsocketService.subscribe($scope.subscriber); + + + $scope.$on('$destroy', function() { + telemetryWebsocketService.unsubscribe($scope.subscriber); + }); + + + + + function onSubscriptionData(data/*, subscriptionId*/) { + //$scope.attributesData = data.; + + if (data.appliedConfiguration && data.appliedConfiguration[0] && data.appliedConfiguration[0][1]) { + vm.appliedConfiguration = data.appliedConfiguration[0][1]; + checkForSync(); + $scope.$digest(); + } + } + + + checkForSync(); + function checkForSync() { + if (vm.appliedConfiguration === vm.extensionsJSON) { + vm.syncStatus = $translate.instant('extension.sync.sync'); + vm.syncLastTime = formatDate(); + } else { + vm.syncStatus = $translate.instant('extension.sync.not-sync'); + } + } + + function formatDate(date) { + let d; + if (date) { + d = date; + } else { + d = new Date(); + } + + d = d.getFullYear() +'/'+ addZero(d.getMonth()+1) +'/'+ addZero(d.getDate()) + ' ' + addZero(d.getHours()) + ':' + addZero(d.getMinutes()) +':'+ addZero(d.getSeconds()); + return d; + + + function addZero(num) { + if ((angular.isNumber(num) && num < 10) || (angular.isString(num) && num.length === 1)) { + num = '0' + num; + } + return num; + } + } + + + //telemetryWebsocketService.subscribe(subscriber); } \ No newline at end of file diff --git a/ui/src/app/extension/extensions-forms/extension-form-http.directive.js b/ui/src/app/extension/extensions-forms/extension-form-http.directive.js index 4c09078096..9484ab46f2 100644 --- a/ui/src/app/extension/extensions-forms/extension-form-http.directive.js +++ b/ui/src/app/extension/extensions-forms/extension-form-http.directive.js @@ -67,7 +67,6 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr if (index > -1) { scope.converterConfigs.splice(index, 1); } - scope.theForm.$setDirty(); }; scope.addConverter = function(converters) { @@ -85,7 +84,6 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr if (index > -1) { converters.splice(index, 1); } - scope.theForm.$setDirty(); }; scope.addAttribute = function(attributes) { @@ -98,13 +96,9 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr if (index > -1) { attributes.splice(index, 1); } - scope.theForm.$setDirty(); }; - - - if(scope.isAdd) { scope.converterConfigs = scope.config.converterConfigurations; scope.addConverterConfig(); @@ -112,28 +106,6 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr scope.converterConfigs = scope.config.converterConfigurations; } - - - scope.updateValidity = function() { - let valid = scope.converterConfigs && scope.converterConfigs.length > 0; - scope.theForm.$setValidity('converterConfigs', valid); - if(scope.converterConfigs.length) { - for(let i=0; i - + {{ 'extension.converter-configurations' | translate }} -
- extension.add-config-prompt -
  1. @@ -47,11 +44,11 @@ - +
    -
    extension.converter-id-required
    +
    extension.field-required
    @@ -59,14 +56,11 @@ - + {{ 'extension.converters' | translate }} -
    - extension.add-converter-prompt -
    1. - +
      -
      extension.device-name-expression-required
      +
      extension.field-required
      - +
      -
      extension.device-type-expression-required
      +
      extension.field-required
      @@ -117,14 +111,14 @@
      - +
      -
      extension.required-key
      +
      extension.field-required
      - + @@ -132,16 +126,16 @@
      -
      extension.required-type
      +
      extension.field-required
      - +
      -
      extension.required-value
      +
      extension.field-required
      @@ -182,11 +176,8 @@
      - - {{ 'extension.add-attribute' | translate }} - add - action.add + extension.add-attribute
      @@ -212,14 +203,14 @@
      - +
      -
      extension.required-key
      +
      extension.field-required
      - + @@ -227,16 +218,16 @@
      -
      extension.required-type
      +
      extension.field-required
      - +
      -
      extension.required-value
      +
      extension.field-required
      @@ -277,11 +268,8 @@
      - - {{ 'extension.add-timeseries' | translate }} - add - action.add + extension.add-timeseries
      @@ -295,11 +283,8 @@
      - - {{ 'extension.add-converter' | translate }} - add - action.add + extension.add-converter
      @@ -314,11 +299,8 @@
      - - {{ 'extension.add-config' | translate }} - add - action.add + extension.add-config
      diff --git a/ui/src/app/extension/extensions-forms/extension-form-mqtt.directive.js b/ui/src/app/extension/extensions-forms/extension-form-mqtt.directive.js new file mode 100644 index 0000000000..41e9aad636 --- /dev/null +++ b/ui/src/app/extension/extensions-forms/extension-form-mqtt.directive.js @@ -0,0 +1,335 @@ +/* + * 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 './extension-form.scss'; + +/* eslint-disable angular/log */ + +import extensionFormMqttTemplate from './extension-form-mqtt.tpl.html'; + +/* eslint-enable import/no-unresolved, import/default */ + +/*@ngInject*/ +export default function ExtensionFormHttpDirective($compile, $templateCache, $translate, types) { + + var linker = function(scope, element) { + + var template = $templateCache.get(extensionFormMqttTemplate); + element.html(template); + + scope.types = types; + scope.theForm = scope.$parent.theForm; + + scope.deviceNameExpressions = { + deviceNameJsonExpression: "extension.converter-json", + deviceNameTopicExpression: "extension.topic" + }; + scope.deviceTypeExpressions = { + deviceTypeJsonExpression: "extension.converter-json", + deviceTypeTopicExpression: "extension.topic" + }; + scope.attributeKeyExpressions = { + attributeKeyJsonExpression: "extension.converter-json", + attributeKeyTopicExpression: "extension.topic" + }; + scope.requestIdExpressions = { + requestIdJsonExpression: "extension.converter-json", + requestIdTopicExpression: "extension.topic" + } + + scope.extensionCustomConverterOptions = { + useWrapMode: false, + mode: 'json', + showGutter: true, + showPrintMargin: true, + theme: 'github', + advanced: { + enableSnippets: true, + enableBasicAutocompletion: true, + enableLiveAutocompletion: true + }, + onLoad: function(_ace) { + _ace.$blockScrolling = 1; + } + }; + + scope.updateValidity = function () { + if(scope.brokers.length) { + for(let i=0;i -1) { + scope.brokers.splice(index, 1); + } + }; + + if(scope.isAdd) { + scope.brokers = []; + scope.config.brokers = scope.brokers; + scope.addBroker(); + } else { + scope.brokers = scope.config.brokers; + } + + scope.addMap = function(mapping) { + var newMap = {topicFilter:"sensors", converter:{attributes:[],timeseries:[]}}; + + mapping.push(newMap); + }; + + scope.removeMap = function(map, mapping) { + var index = mapping.indexOf(map); + if (index > -1) { + mapping.splice(index, 1); + } + }; + + scope.addAttribute = function(attributes) { + var newAttribute = {type:"", key:"", value:""}; + attributes.push(newAttribute); + }; + + scope.removeAttribute = function(attribute, attributes) { + var index = attributes.indexOf(attribute); + if (index > -1) { + attributes.splice(index, 1); + } + }; + + scope.addConnectRequest = function(requests, type) { + var newRequest = {}; + if(type == "connect") { + newRequest.topicFilter = "sensors/connect"; + } else { + newRequest.topicFilter = "sensors/disconnect"; + } + requests.push(newRequest); + }; + + scope.addAttributeRequest = function(requests) { + var newRequest = { + topicFilter: "sensors/attributes", + clientScope: false, + responseTopicExpression: "sensors/${deviceName}/attributes/${responseId}", + valueExpression: "${attributeValue}" + }; + requests.push(newRequest); + }; + + scope.addAttributeUpdate = function(updates) { + var newUpdate = { + deviceNameFilter: ".*", + attributeFilter: ".*", + topicExpression: "sensor/${deviceName}/${attributeKey}", + valueExpression: "{\"${attributeKey}\":\"${attributeValue}\"}" + } + updates.push(newUpdate); + }; + + scope.addServerSideRpc = function(rpcRequests) { + var newRpc = { + deviceNameFilter: ".*", + methodFilter: "echo", + requestTopicExpression: "sensor/${deviceName}/request/${methodName}/${requestId}", + responseTopicExpression: "sensor/${deviceName}/response/${methodName}/${requestId}", + responseTimeout: 10000, + valueExpression: "${params}" + }; + rpcRequests.push(newRpc); + }; + + scope.changeCredentials = function(broker) { + var type = broker.credentials.type; + broker.credentials = {}; + broker.credentials.type = type; + }; + + scope.changeConverterType = function(map) { + if(map.converterType == "custom"){ + map.converter = ""; + } + if(map.converterType == "json") { + map.converter = {attributes:[],timeseries:[]}; + } + }; + + scope.changeNameExpression = function(element, type) { + if(element.nameExp == "deviceNameJsonExpression") { + if(element.deviceNameTopicExpression) { + delete element.deviceNameTopicExpression; + } + if(type) { + element.deviceNameJsonExpression = "${$.serialNumber}"; + } + } + if(element.nameExp == "deviceNameTopicExpression") { + if(element.deviceNameJsonExpression) { + delete element.deviceNameJsonExpression; + } + if(type && type == "connect") { + element.deviceNameTopicExpression = "(?<=sensor\\/)(.*?)(?=\\/connect)"; + } + if(type && type == "disconnect") { + element.deviceNameTopicExpression = "(?<=sensor\\/)(.*?)(?=\\/disconnect)"; + } + if(type && type == "attribute") { + element.deviceNameTopicExpression = "(?<=sensors\\/)(.*?)(?=\\/attributes)"; + } + } + }; + + scope.changeTypeExpression = function(converter) { + if(converter.typeExp == "deviceTypeJsonExpression") { + if(converter.deviceTypeTopicExpression) { + delete converter.deviceTypeTopicExpression; + } + } + if(converter.typeExp == "deviceTypeTopicExpression") { + if(converter.deviceTypeJsonExpression) { + delete converter.deviceTypeJsonExpression; + } + } + }; + + scope.changeAttrKeyExpression = function(request) { + if(request.attrKey == "attributeKeyJsonExpression") { + if(request.attributeKeyTopicExpression) { + delete request.attributeKeyTopicExpression; + } + request.attributeKeyJsonExpression = "${$.key}"; + } + if(request.attrKey == "attributeKeyTopicExpression") { + if(request.attributeKeyJsonExpression) { + delete request.attributeKeyJsonExpression; + } + request.attributeKeyTopicExpression = "(?<=attributes\\/)(.*?)(?=\\/request)"; + } + }; + + scope.changeRequestIdExpression = function(request) { + if(request.requestId == "requestIdJsonExpression") { + if(request.requestIdTopicExpression) { + delete request.requestIdTopicExpression; + } + request.requestIdJsonExpression = "${$.requestId}"; + } + if(request.requestId == "requestIdTopicExpression") { + if(request.requestIdJsonExpression) { + delete request.requestIdJsonExpression; + } + request.requestIdTopicExpression = "(?<=request\\/)(.*?)($)"; + } + }; + + scope.validateCustomConverter = function(model, editorName) { + if(model && model.length) { + try { + angular.fromJson(model); + scope.theForm[editorName].$setValidity('converterJSON', true); + } catch(e) { + scope.theForm[editorName].$setValidity('converterJSON', false); + } + } + }; + + scope.fileAdded = function($file, broker, fileType) { + var reader = new FileReader(); + reader.onload = function(event) { + scope.$apply(function() { + if(event.target.result) { + scope.theForm.$setDirty(); + var addedFile = event.target.result; + if (addedFile && addedFile.length > 0) { + if(fileType == "caCert") { + broker.credentials.caCertFileName = $file.name; + broker.credentials.caCert = addedFile.replace(/^data.*base64,/, ""); + } + if(fileType == "privateKey") { + broker.credentials.privateKeyFileName = $file.name; + broker.credentials.privateKey = addedFile.replace(/^data.*base64,/, ""); + } + if(fileType == "Cert") { + broker.credentials.certFileName = $file.name; + broker.credentials.cert = addedFile.replace(/^data.*base64,/, ""); + } + } + } + }); + }; + reader.readAsDataURL($file.file); + }; + + scope.clearFile = function(broker, fileType) { + scope.theForm.$setDirty(); + if(fileType == "caCert") { + broker.credentials.caCertFileName = null; + broker.credentials.caCert = null; + } + if(fileType == "privateKey") { + broker.credentials.privateKeyFileName = null; + broker.credentials.privateKey = null; + } + if(fileType == "Cert") { + broker.credentials.certFileName = null; + broker.credentials.cert = null; + } + }; + + $compile(element.contents())(scope); + }; + + return { + restrict: "A", + link: linker, + scope: { + config: "=", + isAdd: "=" + } + } +} \ No newline at end of file diff --git a/ui/src/app/extension/extensions-forms/extension-form-mqtt.tpl.html b/ui/src/app/extension/extensions-forms/extension-form-mqtt.tpl.html index e55ad6ca2f..7950c75be5 100644 --- a/ui/src/app/extension/extensions-forms/extension-form-mqtt.tpl.html +++ b/ui/src/app/extension/extensions-forms/extension-form-mqtt.tpl.html @@ -15,4 +15,846 @@ limitations under the License. --> -
      MQTT
      \ No newline at end of file + + + + extension.configuration + + + + + + + {{ 'extension.brokers' | translate }} + + +
      +
        +
      1. + + + + {{ 'action.remove' | translate }} + + + + +
        + + + +
        +
        extension.field-required
        +
        extension.port-range
        +
        extension.port-range
        +
        +
        + + + +
        +
        extension.field-required
        +
        +
        +
        +
        + + + +
        +
        extension.field-required
        +
        +
        + + + + + {{credentialsValue.name | translate}} + + + + + {{ 'extension.ssl' | translate }} + + +
        +
        + + + +
        +
        extension.field-required
        +
        +
        + + + +
        +
        extension.field-required
        +
        +
        +
        + + + + + + {{ 'extension.mapping' | translate }} + + +
        +
          +
        1. + + + + {{ 'action.remove' | translate }} + + + + +
          + + + + + {{value | translate}} + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          + +
          +
          + + + + + {{value | translate}} + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          +
          + + + + + {{value | translate}} + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          +
          + + + + + + + +
          +
          extension.field-required
          +
          +
          +
          +
          + +
          +
          extension.transformer-json
          +
          +
          +
          +
          + +
          + + + + + {{ 'extension.attributes' | translate }} + + +
          +
            +
          1. + + + + {{ 'action.remove' | translate }} + + + + +
            + + + +
            +
            extension.field-required
            +
            +
            + + + + + {{attrTypeValue | translate}} + + +
            +
            extension.field-required
            +
            +
            +
            + + + +
            +
            extension.field-required
            +
            +
            +
            +
            +
          2. +
          +
          +
          + + add + extension.add-attribute + +
          +
          +
          +
          + + + + + {{ 'extension.timeseries' | translate }} + + +
          +
            +
          1. + + + + {{ 'action.remove' | translate }} + + + + +
            + + + +
            +
            extension.field-required
            +
            +
            + + + + + {{attrTypeValue | translate}} + + +
            +
            extension.field-required
            +
            +
            +
            + + + +
            +
            extension.field-required
            +
            +
            +
            +
            +
          2. +
          +
          +
          + + add + extension.add-timeseries + +
          +
          +
          +
          + +
          +
          +
        2. +
        +
        +
        + + add + extension.add-map + +
        +
        +
        +
        + + + + + {{ 'extension.connect-requests' | translate }} + + +
        +
          +
        1. + + + + {{ 'action.remove' | translate }} + + + + + + + +
          +
          extension.field-required
          +
          +
          +
          + + + + + {{value | translate}} + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          +
          +
          +
        2. +
        +
        +
        + + add + extension.add-connect-request + +
        +
        +
        +
        + + + + + {{ 'extension.disconnect-requests' | translate }} + + +
        +
          +
        1. + + + + {{ 'action.remove' | translate }} + + + + + + + +
          +
          extension.field-required
          +
          +
          +
          + + + + + {{value | translate}} + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          +
          +
          +
        2. +
        +
        +
        + + add + extension.add-disconnect-request + +
        +
        +
        +
        + + + + + {{ 'extension.attribute-requests' | translate }} + + +
        +
          +
        1. + + + + {{ 'action.remove' | translate }} + + + + +
          + + + +
          +
          extension.field-required
          +
          +
          + + {{ 'extension.client-scope' | translate }} + + +
          +
          + + + + + {{value | translate}} + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          + +
          + + + + + {{value | translate}} + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          + +
          + + + + + {{value | translate}} + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          + + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          +
          +
        2. +
        +
        +
        + + add + extension.add-attribute-request + +
        +
        +
        +
        + + + + + {{ 'extension.attribute-updates' | translate }} + + +
        +
          +
        1. + + + + {{ 'action.remove' | translate }} + + + + +
          + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          +
          +
        2. +
        +
        +
        + + add + extension.add-attribute-update + +
        +
        +
        +
        + + + + + {{ 'extension.server-side-rpc' | translate }} + + +
        +
          +
        1. + + + + {{ 'action.remove' | translate }} + + + + +
          + + + +
          +
          extension.field-required
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          +
          + + + +
          +
          extension.field-required
          +
          +
          + + + + +
          + + + + + + + +
          +
          extension.field-required
          +
          +
          +
          +
          +
          +
        2. +
        +
        +
        + + add + extension.add-server-side-rpc-request + +
        +
        +
        +
        + +
        +
        +
      2. +
      +
      + +
      + + add + extension.add-broker + +
      +
      +
      +
      + +
      +
      diff --git a/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html b/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html index 8c959db152..3cc1e1b3f2 100644 --- a/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html +++ b/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html @@ -56,16 +56,16 @@
      -
      extension.opc-field-required
      +
      extension.field-required
      - +
      -
      extension.opc-field-required
      +
      extension.field-required
    @@ -73,15 +73,15 @@
    - +
    -
    extension.opc-field-required
    +
    extension.field-required
    - +
    extension.opc-field-required
    + >extension.field-required
    Port should be in a range from 1 to 65535
    @@ -113,12 +113,12 @@
    extension.opc-field-required
    + >extension.field-required
- +
extension.opc-field-required
+ >extension.field-required
@@ -145,7 +145,7 @@
extension.opc-field-required
+ >extension.field-required
@@ -157,24 +157,23 @@ > + >
extension.opc-field-required
+ >extension.field-required
-
- - + +
extension.opc-field-required
+ >extension.field-required
- - + +
extension.opc-field-required
+ >extension.field-required
- - + {{ 'extension.opc-keystore' | translate }} @@ -212,38 +210,40 @@
-
extension.opc-field-required
+
extension.field-required
-
- - -
-
- - - {{ 'action.remove' | translate }} - - close - -
-
- - +
-
-
extension.no-file
-
{{server.keystore[fieldsToFill.fileName]}}
-
+ +
@@ -251,7 +251,7 @@
-
extension.opc-field-required
+
extension.field-required
@@ -259,7 +259,7 @@
-
extension.opc-field-required
+
extension.field-required
@@ -268,7 +268,7 @@
-
extension.opc-field-required
+
extension.field-required
@@ -282,7 +282,7 @@ > - {{ 'extension.opc-mapping' | translate }} + {{ 'extension.mapping' | translate }}
@@ -312,7 +312,7 @@
extension.opc-field-required
+ >extension.field-required
@@ -325,7 +325,7 @@
extension.opc-field-required
+ >extension.field-required
@@ -336,7 +336,7 @@ > - {{ 'extension.opc-mapping-attributes' | translate }} + {{ 'extension.attributes' | translate }}
@@ -369,7 +369,7 @@
extension.opc-field-required
+ >extension.field-required
@@ -400,7 +400,7 @@
extension.opc-field-required
+ >extension.field-required
@@ -417,11 +417,8 @@ ng-click="addNewAttribute(map.attributes)" aria-label="{{ 'action.add' | translate }}" > - - {{ 'extension.add-map' | translate }} - add - action.add + add-attribute
@@ -431,7 +428,7 @@ - {{ 'extension.opc-timeseries' | translate }} + {{ 'extension.timeseries' | translate }}
@@ -460,7 +457,7 @@
extension.opc-field-required
+ >extension.field-required
extension.opc-field-required
+ >extension.field-required
@@ -503,11 +500,8 @@ ng-click="addNewAttribute(map.timeseries)" aria-label="{{ 'action.add' | translate }}" > - - {{ 'extension.add-timeseries' | translate }} - add - action.add + extension.add-timeseries
@@ -528,11 +522,8 @@ ng-click="addMap(server.mapping)" aria-label="{{ 'action.add' | translate }}" > - - {{ 'extension.add-map' | translate }} - add - action.add + extension.add-map
@@ -553,7 +544,7 @@ aria-label="{{ 'action.add' | translate }}" > add - extension.opc-add-another-server + extension.opc-add-server diff --git a/ui/src/app/extension/extensions-forms/extension-form.scss b/ui/src/app/extension/extensions-forms/extension-form.scss index 142516c56c..cc3e0526df 100644 --- a/ui/src/app/extension/extensions-forms/extension-form.scss +++ b/ui/src/app/extension/extensions-forms/extension-form.scss @@ -22,6 +22,23 @@ margin-top: 0; padding-left: 3px; } + .tb-container { + width:100%; + } + .dropdown-messages { + .tb-error-message { + padding: 5px 0 0 0; + } + } + .dropdown-section { + margin-bottom: 30px; + } +} + +.extension-form.extension-mqtt { + md-checkbox{ + margin-left: 10px; + } } .tb-extension-custom-transformer-panel { diff --git a/ui/src/app/extension/index.js b/ui/src/app/extension/index.js index 4c9f812787..acb97be60d 100644 --- a/ui/src/app/extension/index.js +++ b/ui/src/app/extension/index.js @@ -16,12 +16,14 @@ import ExtensionTableDirective from './extension-table.directive'; import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive'; +import ExtensionFormMqttDirective from './extensions-forms/extension-form-mqtt.directive' import ExtensionFormOpcDirective from './extensions-forms/extension-form-opc.directive'; import {ParseToNull} from './extension-dialog.controller'; export default angular.module('thingsboard.extension', []) .directive('tbExtensionTable', ExtensionTableDirective) .directive('tbExtensionFormHttp', ExtensionFormHttpDirective) + .directive('tbExtensionFormMqtt', ExtensionFormMqttDirective) .directive('tbExtensionFormOpc', ExtensionFormOpcDirective) .directive('parseToNull', ParseToNull) .name; \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index 8272fbbbfa..6ee16cadce 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -738,13 +738,8 @@ export default angular.module('thingsboard.locale', []) "id": "Id", "extension-id": "Extension id", "extension-type": "Extension type", - "transformer-json": "JSON*", - "id-required": "Extension id is required.", + "transformer-json": "JSON *", "unique-id-required": "Current extension id already exists.", - "type-required": "Extension type is required.", - "required-type": "Type is required.", - "required-key": "Key is required.", - "required-value": "Value is required.", "delete": "Delete extension", "add": "Add extension", "edit": "Edit extension", @@ -754,18 +749,13 @@ export default angular.module('thingsboard.locale', []) "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.", "converters": "Converters", "converter-id": "Converter id", - "converter-id-required": "Converter id is required.", "configuration": "Configuration", "converter-configurations": "Converter configurations", "token": "Security token", "add-converter": "Add converter", - "add-converter-prompt": "Please add converter", "add-config": "Add converter configuration", - "add-config-prompt": "Please add converter configuration", "device-name-expression": "Device name expression", - "device-name-expression-required": "Device name expression is required.", "device-type-expression": "Device type expression", - "device-type-expression-required": "Device type expression is required.", "custom": "Custom", "to-double": "To Double", "transformer": "Transformer", @@ -777,7 +767,6 @@ export default angular.module('thingsboard.locale', []) "timeseries": "Timeseries", "add-timeseries": "Add timeseries", - "sync": { "status": "Status", "sync": "Sync", @@ -785,18 +774,69 @@ export default angular.module('thingsboard.locale', []) "last-sync-time": "Last sync time", }, - "opc-field-required": "Field is required", + + "field-required": "Field is required", + "brokers": "Brokers", + "add-broker": "Add broker", + "host": "Host", + "port": "Port", + "port-range": "Port should be in a range from 1 to 65535.", + "ssl": "Ssl", + "credentials": "Credentials", + "username": "Username", + "password": "Password", + "retry-interval": "Retry interval in milliseconds", + "anonymous": "Anonymous", + "basic": "Basic", + "pem": "PEM", + "ca-cert": "CA certificate file *", + "private-key": "Private key file *", + "cert": "Certificate file *", + "no-file": "No file selected.", + "drop-file": "Drop a file or click to select a file to upload.", + "mapping": "Mapping", + "topic-filter": "Topic filter", + "converter-type": "Converter type", + "converter-json": "Json", + "json-name-expression": "Device name json expression", + "topic-name-expression": "Device name topic expression", + "json-type-expression": "Device type json expression", + "topic-type-expression": "Device type topic expression", + "attribute-key-expression": "Attribute key expression", + "attr-json-key-expression": "Attribute key json expression", + "attr-topic-key-expression": "Attribute key topic expression", + "request-id-expression": "Request id expression", + "request-id-json-expression": "Request id json expression", + "request-id-topic-expression": "Request id topic expression", + "response-topic-expression": "Response topic expression", + "value-expression": "Value expression", + "topic": "Topic", + "timeout": "Timeout in milliseconds", + "converter-json-required": "Converter json is required.", + "converter-json-parse": "Unable to parse converter json.", + "filter-expression": "Filter expression", + "connect-requests": "Connect requests", + "add-connect-request": "Add connect request", + "disconnect-requests": "Disconnect requests", + "add-disconnect-request": "Add disconnect request", + "attribute-requests": "Attribute requests", + "add-attribute-request": "Add attribute request", + "attribute-updates": "Attribute updates", + "add-attribute-update": "Add attribute update", + "server-side-rpc": "Server side RPC", + "add-server-side-rpc-request": "Add server-side RPC request", + "device-name-filter": "Device name filter", + "attribute-filter": "Attribute filter", + "method-filter": "Method filter", + "request-topic-expression": "Request topic expression", + "response-timeout": "Response timeout in milliseconds", + "topic-expression": "Topic expression", + "client-scope": "Client scope", "opc-server": "Servers", - "opc-add-server-hint": "Add server", - "opc-add-server-prompt": "Please add server", - "opc-server-id": "Server id", - "opc-timeseries": "Timeseries", + "opc-add-server": "Add server", "opc-application-name": "Application name", "opc-application-uri": "Application uri", - "opc-host": "Host", - "opc-port": "Port", "opc-scan-period-in-seconds": "Scan period in seconds", - "opc-timeout-in-millis": "Timeout in milliseconds", "opc-security": "Security", "opc-identity": "Identity", "opc-keystore": "Keystore", @@ -806,19 +846,14 @@ export default angular.module('thingsboard.locale', []) "opc-keystore-password":"Password", "opc-keystore-alias":"Alias", "opc-keystore-key-password":"Key password", - "opc-mapping":"Mapping", "opc-device-node-pattern":"Device node pattern", "opc-device-name-pattern":"Device name pattern", - "opc-mapping-attributes":"Mapping attributes", - "opc-username":"Username", - "opc-password":"Password", - "opc-add-another-server":"Add another server", }, "fullscreen": { "expand": "Expand to fullscreen", "exit": "Exit fullscreen", "toggle": "Toggle fullscreen mode", - "fullscreen": "Fullscreen", + "fullscreen": "Fullscreen" }, "function": { "function": "Function" @@ -936,7 +971,6 @@ export default angular.module('thingsboard.locale', []) "invalid-plugin-file-error": "Unable to import plugin: Invalid plugin data structure.", "copyId": "Copy plugin Id", "idCopiedMessage": "Plugin Id has been copied to clipboard" - }, "position": { "top": "Top",