From dad2a26f22ad40f75d769675e17cc4ce22e759b0 Mon Sep 17 00:00:00 2001 From: Sergey Tarnavskiy Date: Fri, 24 Nov 2017 17:25:40 +0200 Subject: [PATCH] added mqtt form --- ui/src/app/common/types.constant.js | 18 + .../extension/extension-dialog.controller.js | 68 ++- .../app/extension/extension-dialog.tpl.html | 1 + ui/src/app/extension/extension-table.scss | 2 +- .../extension-form-http.directive.js | 2 +- .../extension-form-mqtt.directive.js | 236 ++++++++++ .../extension-form-mqtt.tpl.html | 437 +++++++++++++++++- .../extensions-forms/extension-form.scss | 14 +- ui/src/app/extension/index.js | 2 + ui/src/app/locale/locale.constant.js | 51 +- 10 files changed, 811 insertions(+), 20 deletions(-) create mode 100644 ui/src/app/extension/extensions-forms/extension-form-mqtt.directive.js diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index dff7635e0a..f78e0a8e45 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" + } + }, latestTelemetry: { value: "LATEST_TELEMETRY", name: "attribute.scope-latest-telemetry", diff --git a/ui/src/app/extension/extension-dialog.controller.js b/ui/src/app/extension/extension-dialog.controller.js index 3b13214acd..e5809291ab 100644 --- a/ui/src/app/extension/extension-dialog.controller.js +++ b/ui/src/app/extension/extension-dialog.controller.js @@ -45,6 +45,7 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, $mdDialog.cancel(); } function save() { + $mdDialog.hide(); saveTransformers(); if(vm.isAdd) { vm.allExtensions.push(vm.newExtension); @@ -60,7 +61,6 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, attributeService.saveEntityAttributes(vm.entityType, vm.entityId, types.attributesScope.shared.value, [{key:"configuration", value:editedValue}]).then( function success() { $scope.theForm.$setPristine(); - $mdDialog.hide(); } ); } @@ -85,21 +85,39 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, } function saveTransformers() { - var config = vm.newExtension.configuration.converterConfigurations; if(vm.newExtension.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
+
diff --git a/ui/src/app/extension/extension-table.scss b/ui/src/app/extension/extension-table.scss index 3d4c54a3bc..c6c19a5978 100644 --- a/ui/src/app/extension/extension-table.scss +++ b/ui/src/app/extension/extension-table.scss @@ -1,4 +1,4 @@ -/* +/** * Copyright © 2016-2017 The Thingsboard Authors * * Licensed under the Apache License, Version 2.0 (the "License"); 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 9a07f2b079..b185b4b604 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 @@ -132,7 +132,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr } } } - + $compile(element.contents())(scope); } 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..4101bb5eed --- /dev/null +++ b/ui/src/app/extension/extensions-forms/extension-form-mqtt.directive.js @@ -0,0 +1,236 @@ +/* + * 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.nameExpressions = { + deviceNameJsonExpression: "extension.converter-json", + deviceNameTopicExpression: "extension.topic" + }; + scope.typeExpressions = { + deviceTypeJsonExpression: "extension.converter-json", + deviceTypeTopicExpression: "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; + } + }; + + + if(scope.isAdd) { + scope.brokers = []; + scope.config.brokers = scope.brokers; + } else { + scope.brokers = scope.config.brokers; + } + + scope.updateValidity = function () { + var valid = scope.brokers && scope.brokers.length > 0; + scope.theForm.$setValidity('brokers', valid); + if(scope.brokers.length) { + for(let i=0;i -1) { + scope.brokers.splice(index, 1); + } + scope.theForm.$setDirty(); + } + + 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.theForm.$setDirty(); + } + + 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.theForm.$setDirty(); + } + + 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(converter) { + if(converter.nameExp == "deviceNameJsonExpression") { + if(converter.deviceNameTopicExpression) { + delete converter.deviceNameTopicExpression; + } + } + if(converter.nameExp == "deviceNameTopicExpression") { + if(converter.deviceNameJsonExpression) { + delete converter.deviceNameJsonExpression; + } + } + } + + 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.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..94ae789b16 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,439 @@ limitations under the License. --> -
MQTT
\ No newline at end of file + + + + extension.configuration + + + + + + + {{ 'extension.brokers' | translate }} + + +
+ extension.add-broker-prompt +
+
+
    +
  1. + + + + {{ 'action.remove' | translate }} + + + + +
    + + + +
    +
    extension.port-required
    +
    extension.port-range
    +
    extension.port-range
    +
    +
    + + + +
    +
    extension.host-required
    +
    +
    +
    +
    + + + +
    +
    extension.retry-interval-required
    +
    +
    + + + + + {{credentialsValue.name | translate}} + + + + + {{ 'extension.ssl' | translate }} + + +
    +
    + + + +
    +
    extension.username-required
    +
    +
    + + + +
    +
    extension.password-required
    +
    +
    +
    +
    +
    + +
    +
    + + + {{ 'action.remove' | translate }} + + close + +
    +
    + + +
    +
    +
    + +
    + +
    +
    + + + {{ 'action.remove' | translate }} + + close + +
    +
    + + +
    +
    +
    + +
    + +
    +
    + + + {{ 'action.remove' | translate }} + + close + +
    +
    + + +
    +
    +
    + +
    + + + + + {{ 'extension.mapping' | translate }} + + +
    +
      +
    1. + + + + {{ 'action.remove' | translate }} + + + + +
      + + + + + {{value | translate}} + + +
      +
      extension.converter-type-required
      +
      +
      + + + +
      +
      extension.topic-filter-required
      +
      +
      +
      + +
      +
      + + + + + {{value | translate}} + + + + + + +
      +
      extension.json-name-expression-required
      +
      +
      + + + +
      +
      extension.topic-name-expression-required
      +
      +
      +
      +
      + + + + + {{value | translate}} + + + + + + +
      +
      extension.json-type-expression-required
      +
      +
      + + + +
      +
      extension.topic-type-expression-required
      +
      +
      +
      +
      + + + + + + + +
      +
      extension.filter-expression-required
      +
      +
      +
      +
      + +
      +
      extension.transformer-json
      +
      +
      +
      +
      + +
      + + + + + {{ 'extension.attributes' | translate }} + + +
      +
        +
      1. + + + + {{ 'action.remove' | translate }} + + + + +
        + + + +
        +
        extension.required-key
        +
        +
        + + + + + {{attrTypeValue | translate}} + + +
        +
        extension.required-type
        +
        +
        +
        + + + +
        +
        extension.required-value
        +
        +
        +
        +
        +
      2. +
      +
      +
      + + + {{ 'extension.add-attribute' | translate }} + + add + action.add + +
      +
      +
      +
      + + + + + {{ 'extension.timeseries' | translate }} + + +
      +
        +
      1. + + + + {{ 'action.remove' | translate }} + + + + +
        + + + +
        +
        extension.required-key
        +
        +
        + + + + + {{attrTypeValue | translate}} + + +
        +
        extension.required-type
        +
        +
        +
        + + + +
        +
        extension.required-value
        +
        +
        +
        +
        +
      2. +
      +
      +
      + + + {{ 'extension.add-timeseries' | translate }} + + add + action.add + +
      +
      +
      +
      + +
      +
      +
    2. +
    +
    +
    + + + {{ 'extension.add-map' | translate }} + + add + action.add + +
    +
    +
    +
    +
    +
    +
  2. +
+
+ +
+ + + {{ 'extension.add-broker' | translate }} + + add + action.add + +
+
+
+
+
+{{config | json}}
+
+
+
diff --git a/ui/src/app/extension/extensions-forms/extension-form.scss b/ui/src/app/extension/extensions-forms/extension-form.scss index 82d065f660..f40970bc17 100644 --- a/ui/src/app/extension/extensions-forms/extension-form.scss +++ b/ui/src/app/extension/extensions-forms/extension-form.scss @@ -1,4 +1,4 @@ -/* +/** * Copyright © 2016-2017 The Thingsboard Authors * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - .extension-form { li > .md-button { color: rgba(0, 0, 0, 0.7); @@ -23,6 +22,17 @@ margin-top: 0; padding-left: 3px; } + .t-right { + text-align: right; + } + .tb-container { + width:100%; + } + .dropdown-messages { + .tb-error-message { + padding: 5px 0 0 0; + } + } } .tb-extension-custom-transformer-panel { diff --git a/ui/src/app/extension/index.js b/ui/src/app/extension/index.js index 78fd56d824..619374f987 100644 --- a/ui/src/app/extension/index.js +++ b/ui/src/app/extension/index.js @@ -16,10 +16,12 @@ 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 {ParseToNull} from './extension-dialog.controller'; export default angular.module('thingsboard.extension', []) .directive('tbExtensionTable', ExtensionTableDirective) .directive('tbExtensionFormHttp', ExtensionFormHttpDirective) + .directive('tbExtensionFormMqtt', ExtensionFormMqttDirective) .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 d8b861f7ed..3ac0ebcd44 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -738,7 +738,7 @@ export default angular.module('thingsboard.locale', []) "id": "Id", "extension-id": "Extension id", "extension-type": "Extension type", - "transformer-json": "JSON*", + "transformer-json": "JSON *", "id-required": "Extension id is required.", "unique-id-required": "Current extension id already exists.", "type-required": "Extension type is required.", @@ -775,6 +775,54 @@ export default angular.module('thingsboard.locale', []) "add-attribute": "Add attribute", "timeseries": "Timeseries", "add-timeseries": "Add timeseries", + + "brokers": "Brokers", + "add-broker": "Add broker", + "add-broker-prompt": "Please add broker", + "host": "Host", + "host-required": "Host is required.", + "port": "Port", + "port-required": "Port is required.", + "port-range": "Port should be in a range from 1 to 65535.", + "ssl": "Ssl", + "credentials": "Credentials", + "username": "Username", + "username-required": "Username is required.", + "password": "Password", + "password-required": "Password is required.", + "retry-interval": "Retry interval", + "retry-interval-required": "Retry interval is required.", + "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", + "add-map": "Add map", + "topic-filter": "Topic filter", + "topic-filter-required": "Topic filter is required.", + "converter-type": "Converter type", + "converter-type-required": "Converter type is required.", + "converter-json": "Json", + "name-expression": "Name expression", + "type-expression": "Type expression", + "json-name-expression": "Json name expression", + "json-name-expression-required": "Json name expression is required.", + "topic-name-expression": "Topic name expression", + "topic-name-expression-required": "Topic name expression is required.", + "json-type-expression": "Json type expression", + "json-type-expression-required": "Json type expression is required.", + "topic-type-expression": "Topic type expression", + "topic-type-expression-required": "Topic type expression is required.", + "topic": "Topic", + "timeout": "Timeout", + "converter-json-required": "Converter json is required.", + "converter-json-parse": "Unable to parse converter json.", + "filter-expression": "Filter expression", + "filter-expression-required": "Filter expression is required." }, "fullscreen": { "expand": "Expand to fullscreen", @@ -898,7 +946,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",