Showing
10 changed files
with
811 additions
and
20 deletions
@@ -332,6 +332,24 @@ export default angular.module('thingsboard.types', []) | @@ -332,6 +332,24 @@ export default angular.module('thingsboard.types', []) | ||
332 | toDouble: 'extension.to-double', | 332 | toDouble: 'extension.to-double', |
333 | custom: 'extension.custom' | 333 | custom: 'extension.custom' |
334 | }, | 334 | }, |
335 | + mqttConverterTypes: { | ||
336 | + json: 'extension.converter-json', | ||
337 | + custom: 'extension.custom' | ||
338 | + }, | ||
339 | + mqttCredentialTypes: { | ||
340 | + anonymous: { | ||
341 | + value: "anonymous", | ||
342 | + name: "extension.anonymous" | ||
343 | + }, | ||
344 | + basic: { | ||
345 | + value: "basic", | ||
346 | + name: "extension.basic" | ||
347 | + }, | ||
348 | + pem: { | ||
349 | + value: "cert.PEM", | ||
350 | + name: "extension.pem" | ||
351 | + } | ||
352 | + }, | ||
335 | latestTelemetry: { | 353 | latestTelemetry: { |
336 | value: "LATEST_TELEMETRY", | 354 | value: "LATEST_TELEMETRY", |
337 | name: "attribute.scope-latest-telemetry", | 355 | name: "attribute.scope-latest-telemetry", |
@@ -45,6 +45,7 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | @@ -45,6 +45,7 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | ||
45 | $mdDialog.cancel(); | 45 | $mdDialog.cancel(); |
46 | } | 46 | } |
47 | function save() { | 47 | function save() { |
48 | + $mdDialog.hide(); | ||
48 | saveTransformers(); | 49 | saveTransformers(); |
49 | if(vm.isAdd) { | 50 | if(vm.isAdd) { |
50 | vm.allExtensions.push(vm.newExtension); | 51 | vm.allExtensions.push(vm.newExtension); |
@@ -60,7 +61,6 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | @@ -60,7 +61,6 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | ||
60 | attributeService.saveEntityAttributes(vm.entityType, vm.entityId, types.attributesScope.shared.value, [{key:"configuration", value:editedValue}]).then( | 61 | attributeService.saveEntityAttributes(vm.entityType, vm.entityId, types.attributesScope.shared.value, [{key:"configuration", value:editedValue}]).then( |
61 | function success() { | 62 | function success() { |
62 | $scope.theForm.$setPristine(); | 63 | $scope.theForm.$setPristine(); |
63 | - $mdDialog.hide(); | ||
64 | } | 64 | } |
65 | ); | 65 | ); |
66 | } | 66 | } |
@@ -85,21 +85,39 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | @@ -85,21 +85,39 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | ||
85 | } | 85 | } |
86 | 86 | ||
87 | function saveTransformers() { | 87 | function saveTransformers() { |
88 | - var config = vm.newExtension.configuration.converterConfigurations; | ||
89 | if(vm.newExtension.type == types.extensionType.http) { | 88 | if(vm.newExtension.type == types.extensionType.http) { |
90 | - for(let i=0;i<config.length;i++) { | ||
91 | - for(let j=0;j<config[i].converters.length;j++){ | ||
92 | - for(let k=0;k<config[i].converters[j].attributes.length;k++){ | ||
93 | - if(config[i].converters[j].attributes[k].transformerType == "toDouble"){ | ||
94 | - config[i].converters[j].attributes[k].transformer = {type: "intToDouble"}; | 89 | + var config = vm.newExtension.configuration.converterConfigurations; |
90 | + if(config && config.length > 0) { | ||
91 | + for(let i=0;i<config.length;i++) { | ||
92 | + for(let j=0;j<config[i].converters.length;j++){ | ||
93 | + for(let k=0;k<config[i].converters[j].attributes.length;k++){ | ||
94 | + if(config[i].converters[j].attributes[k].transformerType == "toDouble"){ | ||
95 | + config[i].converters[j].attributes[k].transformer = {type: "intToDouble"}; | ||
96 | + } | ||
97 | + delete config[i].converters[j].attributes[k].transformerType; | ||
98 | + } | ||
99 | + for(let l=0;l<config[i].converters[j].timeseries.length;l++) { | ||
100 | + if(config[i].converters[j].timeseries[l].transformerType == "toDouble"){ | ||
101 | + config[i].converters[j].timeseries[l].transformer = {type: "intToDouble"}; | ||
102 | + } | ||
103 | + delete config[i].converters[j].timeseries[l].transformerType; | ||
95 | } | 104 | } |
96 | - delete config[i].converters[j].attributes[k].transformerType; | ||
97 | } | 105 | } |
98 | - for(let l=0;l<config[i].converters[j].timeseries.length;l++) { | ||
99 | - if(config[i].converters[j].timeseries[l].transformerType == "toDouble"){ | ||
100 | - config[i].converters[j].timeseries[l].transformer = {type: "intToDouble"}; | 106 | + } |
107 | + } | ||
108 | + } | ||
109 | + if(vm.newExtension.type == types.extensionType.mqtt) { | ||
110 | + var brokers = vm.newExtension.configuration.brokers; | ||
111 | + if(brokers && brokers.length > 0) { | ||
112 | + for(let i=0;i<brokers.length;i++) { | ||
113 | + if(brokers[i].mapping && brokers[i].mapping.length > 0) { | ||
114 | + for(let j=0;j<brokers[i].mapping.length;j++) { | ||
115 | + if(brokers[i].mapping[j].converterType == "json") { | ||
116 | + delete brokers[i].mapping[j].converter.nameExp; | ||
117 | + delete brokers[i].mapping[j].converter.typeExp; | ||
118 | + } | ||
119 | + delete brokers[i].mapping[j].converterType; | ||
101 | } | 120 | } |
102 | - delete config[i].converters[j].timeseries[l].transformerType; | ||
103 | } | 121 | } |
104 | } | 122 | } |
105 | } | 123 | } |
@@ -107,8 +125,8 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | @@ -107,8 +125,8 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | ||
107 | } | 125 | } |
108 | 126 | ||
109 | function editTransformers(extension) { | 127 | function editTransformers(extension) { |
110 | - var config = extension.configuration.converterConfigurations; | ||
111 | if(extension.type == types.extensionType.http) { | 128 | if(extension.type == types.extensionType.http) { |
129 | + var config = extension.configuration.converterConfigurations; | ||
112 | for(let i=0;i<config.length;i++) { | 130 | for(let i=0;i<config.length;i++) { |
113 | for(let j=0;j<config[i].converters.length;j++){ | 131 | for(let j=0;j<config[i].converters.length;j++){ |
114 | for(let k=0;k<config[i].converters[j].attributes.length;k++){ | 132 | for(let k=0;k<config[i].converters[j].attributes.length;k++){ |
@@ -134,6 +152,30 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | @@ -134,6 +152,30 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | ||
134 | } | 152 | } |
135 | } | 153 | } |
136 | } | 154 | } |
155 | + if(extension.type == types.extensionType.mqtt) { | ||
156 | + var brokers = extension.configuration.brokers; | ||
157 | + for(let i=0;i<brokers.length;i++) { | ||
158 | + if(brokers[i].mapping && brokers[i].mapping.length > 0) { | ||
159 | + for(let j=0;j<brokers[i].mapping.length;j++) { | ||
160 | + if(brokers[i].mapping[j].converter.type == "json") { | ||
161 | + if(brokers[i].mapping[j].converter.deviceNameTopicExpression) { | ||
162 | + brokers[i].mapping[j].converter.nameExp = "deviceNameTopicExpression"; | ||
163 | + } else { | ||
164 | + brokers[i].mapping[j].converter.nameExp = "deviceNameJsonExpression"; | ||
165 | + } | ||
166 | + if(brokers[i].mapping[j].converter.deviceTypeTopicExpression) { | ||
167 | + brokers[i].mapping[j].converter.typeExp = "deviceTypeTopicExpression"; | ||
168 | + } else { | ||
169 | + brokers[i].mapping[j].converter.typeExp = "deviceTypeJsonExpression"; | ||
170 | + } | ||
171 | + brokers[i].mapping[j].converterType = "json"; | ||
172 | + } else { | ||
173 | + brokers[i].mapping[j].converterType = "custom"; | ||
174 | + } | ||
175 | + } | ||
176 | + } | ||
177 | + } | ||
178 | + } | ||
137 | } | 179 | } |
138 | } | 180 | } |
139 | 181 |
@@ -55,6 +55,7 @@ | @@ -55,6 +55,7 @@ | ||
55 | </section> | 55 | </section> |
56 | 56 | ||
57 | <div tb-extension-form-http config="vm.configuration" is-add="vm.isAdd" ng-if="vm.newExtension.type && vm.newExtension.type == vm.types.extensionType.http"></div> | 57 | <div tb-extension-form-http config="vm.configuration" is-add="vm.isAdd" ng-if="vm.newExtension.type && vm.newExtension.type == vm.types.extensionType.http"></div> |
58 | + <div tb-extension-form-mqtt config="vm.configuration" is-add="vm.isAdd" ng-if="vm.newExtension.type && vm.newExtension.type == vm.types.extensionType.mqtt"></div> | ||
58 | 59 | ||
59 | </fieldset> | 60 | </fieldset> |
60 | 61 |
@@ -132,7 +132,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -132,7 +132,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
132 | } | 132 | } |
133 | } | 133 | } |
134 | } | 134 | } |
135 | - | 135 | + |
136 | $compile(element.contents())(scope); | 136 | $compile(element.contents())(scope); |
137 | } | 137 | } |
138 | 138 |
1 | +/* | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | + | ||
17 | +import './extension-form.scss'; | ||
18 | + | ||
19 | +/* eslint-disable angular/log */ | ||
20 | + | ||
21 | +import extensionFormMqttTemplate from './extension-form-mqtt.tpl.html'; | ||
22 | + | ||
23 | +/* eslint-enable import/no-unresolved, import/default */ | ||
24 | + | ||
25 | +/*@ngInject*/ | ||
26 | +export default function ExtensionFormHttpDirective($compile, $templateCache, $translate, types) { | ||
27 | + | ||
28 | + var linker = function(scope, element) { | ||
29 | + | ||
30 | + var template = $templateCache.get(extensionFormMqttTemplate); | ||
31 | + element.html(template); | ||
32 | + | ||
33 | + scope.types = types; | ||
34 | + scope.theForm = scope.$parent.theForm; | ||
35 | + | ||
36 | + scope.nameExpressions = { | ||
37 | + deviceNameJsonExpression: "extension.converter-json", | ||
38 | + deviceNameTopicExpression: "extension.topic" | ||
39 | + }; | ||
40 | + scope.typeExpressions = { | ||
41 | + deviceTypeJsonExpression: "extension.converter-json", | ||
42 | + deviceTypeTopicExpression: "extension.topic" | ||
43 | + }; | ||
44 | + | ||
45 | + scope.extensionCustomConverterOptions = { | ||
46 | + useWrapMode: false, | ||
47 | + mode: 'json', | ||
48 | + showGutter: true, | ||
49 | + showPrintMargin: true, | ||
50 | + theme: 'github', | ||
51 | + advanced: { | ||
52 | + enableSnippets: true, | ||
53 | + enableBasicAutocompletion: true, | ||
54 | + enableLiveAutocompletion: true | ||
55 | + }, | ||
56 | + onLoad: function(_ace) { | ||
57 | + _ace.$blockScrolling = 1; | ||
58 | + } | ||
59 | + }; | ||
60 | + | ||
61 | + | ||
62 | + if(scope.isAdd) { | ||
63 | + scope.brokers = []; | ||
64 | + scope.config.brokers = scope.brokers; | ||
65 | + } else { | ||
66 | + scope.brokers = scope.config.brokers; | ||
67 | + } | ||
68 | + | ||
69 | + scope.updateValidity = function () { | ||
70 | + var valid = scope.brokers && scope.brokers.length > 0; | ||
71 | + scope.theForm.$setValidity('brokers', valid); | ||
72 | + if(scope.brokers.length) { | ||
73 | + for(let i=0;i<scope.brokers.length;i++) { | ||
74 | + if(scope.brokers[i].credentials.type == scope.types.mqttCredentialTypes.pem.value) { | ||
75 | + if(!(scope.brokers[i].credentials.caCert && scope.brokers[i].credentials.privateKey && scope.brokers[i].credentials.cert)) { | ||
76 | + scope.theForm.$setValidity('cert.PEM', false); | ||
77 | + break; | ||
78 | + } else { | ||
79 | + scope.theForm.$setValidity('cert.PEM', true); | ||
80 | + } | ||
81 | + } | ||
82 | + } | ||
83 | + } | ||
84 | + } | ||
85 | + | ||
86 | + scope.$watch('brokers', function() { | ||
87 | + scope.updateValidity(); | ||
88 | + }, true); | ||
89 | + | ||
90 | + scope.addBroker = function() { | ||
91 | + var newBroker = {host:"localhost", port:1882, ssl:false, retryInterval:3000, credentials:{type:"anonymous"}, mapping:[]}; | ||
92 | + scope.brokers.push(newBroker); | ||
93 | + } | ||
94 | + | ||
95 | + scope.removeBroker = function(broker) { | ||
96 | + var index = scope.brokers.indexOf(broker); | ||
97 | + if (index > -1) { | ||
98 | + scope.brokers.splice(index, 1); | ||
99 | + } | ||
100 | + scope.theForm.$setDirty(); | ||
101 | + } | ||
102 | + | ||
103 | + scope.addMap = function(mapping) { | ||
104 | + var newMap = {topicFilter:"sensors", converter:{attributes:[],timeseries:[]}}; | ||
105 | + | ||
106 | + mapping.push(newMap); | ||
107 | + } | ||
108 | + | ||
109 | + scope.removeMap = function(map, mapping) { | ||
110 | + var index = mapping.indexOf(map); | ||
111 | + if (index > -1) { | ||
112 | + mapping.splice(index, 1); | ||
113 | + } | ||
114 | + scope.theForm.$setDirty(); | ||
115 | + } | ||
116 | + | ||
117 | + scope.addAttribute = function(attributes) { | ||
118 | + var newAttribute = {type:"", key:"", value:""}; | ||
119 | + attributes.push(newAttribute); | ||
120 | + } | ||
121 | + | ||
122 | + scope.removeAttribute = function(attribute, attributes) { | ||
123 | + var index = attributes.indexOf(attribute); | ||
124 | + if (index > -1) { | ||
125 | + attributes.splice(index, 1); | ||
126 | + } | ||
127 | + scope.theForm.$setDirty(); | ||
128 | + } | ||
129 | + | ||
130 | + scope.changeCredentials = function(broker) { | ||
131 | + var type = broker.credentials.type; | ||
132 | + broker.credentials = {}; | ||
133 | + broker.credentials.type = type; | ||
134 | + } | ||
135 | + | ||
136 | + scope.changeConverterType = function(map) { | ||
137 | + if(map.converterType == "custom"){ | ||
138 | + map.converter = ""; | ||
139 | + } | ||
140 | + if(map.converterType == "json") { | ||
141 | + map.converter = {attributes:[],timeseries:[]}; | ||
142 | + } | ||
143 | + } | ||
144 | + | ||
145 | + scope.changeNameExpression = function(converter) { | ||
146 | + if(converter.nameExp == "deviceNameJsonExpression") { | ||
147 | + if(converter.deviceNameTopicExpression) { | ||
148 | + delete converter.deviceNameTopicExpression; | ||
149 | + } | ||
150 | + } | ||
151 | + if(converter.nameExp == "deviceNameTopicExpression") { | ||
152 | + if(converter.deviceNameJsonExpression) { | ||
153 | + delete converter.deviceNameJsonExpression; | ||
154 | + } | ||
155 | + } | ||
156 | + } | ||
157 | + | ||
158 | + scope.changeTypeExpression = function(converter) { | ||
159 | + if(converter.typeExp == "deviceTypeJsonExpression") { | ||
160 | + if(converter.deviceTypeTopicExpression) { | ||
161 | + delete converter.deviceTypeTopicExpression; | ||
162 | + } | ||
163 | + } | ||
164 | + if(converter.typeExp == "deviceTypeTopicExpression") { | ||
165 | + if(converter.deviceTypeJsonExpression) { | ||
166 | + delete converter.deviceTypeJsonExpression; | ||
167 | + } | ||
168 | + } | ||
169 | + } | ||
170 | + | ||
171 | + scope.validateCustomConverter = function(model, editorName) { | ||
172 | + if(model && model.length) { | ||
173 | + try { | ||
174 | + angular.fromJson(model); | ||
175 | + scope.theForm[editorName].$setValidity('converterJSON', true); | ||
176 | + } catch(e) { | ||
177 | + scope.theForm[editorName].$setValidity('converterJSON', false); | ||
178 | + } | ||
179 | + } | ||
180 | + } | ||
181 | + | ||
182 | + scope.fileAdded = function($file, broker, fileType) { | ||
183 | + var reader = new FileReader(); | ||
184 | + reader.onload = function(event) { | ||
185 | + scope.$apply(function() { | ||
186 | + if(event.target.result) { | ||
187 | + scope.theForm.$setDirty(); | ||
188 | + var addedFile = event.target.result; | ||
189 | + if (addedFile && addedFile.length > 0) { | ||
190 | + if(fileType == "caCert") { | ||
191 | + broker.credentials.caCertFileName = $file.name; | ||
192 | + broker.credentials.caCert = addedFile.replace(/^data.*base64,/, ""); | ||
193 | + } | ||
194 | + if(fileType == "privateKey") { | ||
195 | + broker.credentials.privateKeyFileName = $file.name; | ||
196 | + broker.credentials.privateKey = addedFile.replace(/^data.*base64,/, ""); | ||
197 | + } | ||
198 | + if(fileType == "Cert") { | ||
199 | + broker.credentials.certFileName = $file.name; | ||
200 | + broker.credentials.cert = addedFile.replace(/^data.*base64,/, ""); | ||
201 | + } | ||
202 | + } | ||
203 | + } | ||
204 | + }); | ||
205 | + }; | ||
206 | + reader.readAsDataURL($file.file); | ||
207 | + } | ||
208 | + | ||
209 | + scope.clearFile = function(broker, fileType) { | ||
210 | + scope.theForm.$setDirty(); | ||
211 | + if(fileType == "caCert") { | ||
212 | + broker.credentials.caCertFileName = null; | ||
213 | + broker.credentials.caCert = null; | ||
214 | + } | ||
215 | + if(fileType == "privateKey") { | ||
216 | + broker.credentials.privateKeyFileName = null; | ||
217 | + broker.credentials.privateKey = null; | ||
218 | + } | ||
219 | + if(fileType == "Cert") { | ||
220 | + broker.credentials.certFileName = null; | ||
221 | + broker.credentials.cert = null; | ||
222 | + } | ||
223 | + } | ||
224 | + | ||
225 | + $compile(element.contents())(scope); | ||
226 | + } | ||
227 | + | ||
228 | + return { | ||
229 | + restrict: "A", | ||
230 | + link: linker, | ||
231 | + scope: { | ||
232 | + config: "=", | ||
233 | + isAdd: "=" | ||
234 | + } | ||
235 | + } | ||
236 | +} |
@@ -15,4 +15,439 @@ | @@ -15,4 +15,439 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<div>MQTT</div> | ||
18 | +<md-card class="extension-form extension-mqtt"> | ||
19 | + <md-card-title name="testValid"> | ||
20 | + <md-card-title-text> | ||
21 | + <span translate class="md-headline">extension.configuration</span> | ||
22 | + </md-card-title-text> | ||
23 | + </md-card-title> | ||
24 | + <md-card-content> | ||
25 | + <v-accordion id="mqtt-brokers-accordion" class="vAccordion--default"> | ||
26 | + <v-pane id="mqtt-brokers-pane" expanded="isAdd"> | ||
27 | + <v-pane-header> | ||
28 | + {{ 'extension.brokers' | translate }} | ||
29 | + </v-pane-header> | ||
30 | + <v-pane-content> | ||
31 | + <div ng-if="brokers.length === 0"> | ||
32 | + <span translate layout-align="center center" class="tb-prompt">extension.add-broker-prompt</span> | ||
33 | + </div> | ||
34 | + <div ng-if="brokers.length > 0"> | ||
35 | + <ol class="list-group"> | ||
36 | + <li class="list-group-item" ng-repeat="(brokerIndex,broker) in brokers"> | ||
37 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeBroker(broker)"> | ||
38 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | ||
39 | + <md-tooltip md-direction="top"> | ||
40 | + {{ 'action.remove' | translate }} | ||
41 | + </md-tooltip> | ||
42 | + </md-button> | ||
43 | + <md-card> | ||
44 | + <md-card-content> | ||
45 | + <section flex layout="row"> | ||
46 | + <md-input-container flex="40" class="md-block"> | ||
47 | + <label translate>extension.port</label> | ||
48 | + <input required type="number" min="1" max="65535" name="mqttPort_{{brokerIndex}}" ng-model="broker.port"> | ||
49 | + <div ng-messages="theForm['mqttPort_' + brokerIndex].$error"> | ||
50 | + <div translate ng-message="required">extension.port-required</div> | ||
51 | + <div translate ng-message="min">extension.port-range</div> | ||
52 | + <div translate ng-message="max">extension.port-range</div> | ||
53 | + </div> | ||
54 | + </md-input-container> | ||
55 | + <md-input-container flex="60" class="md-block"> | ||
56 | + <label translate>extension.host</label> | ||
57 | + <input required name="mqttHost_{{brokerIndex}}" ng-model="broker.host"> | ||
58 | + <div ng-messages="theForm['mqttHost_' + brokerIndex].$error"> | ||
59 | + <div translate ng-message="required">extension.host-required</div> | ||
60 | + </div> | ||
61 | + </md-input-container> | ||
62 | + </section> | ||
63 | + <section flex layout="row"> | ||
64 | + <md-input-container flex="40" class="md-block"> | ||
65 | + <label translate>extension.retry-interval</label> | ||
66 | + <input required type="number" name="mqttRetryInterval_{{brokerIndex}}" ng-model="broker.retryInterval"> | ||
67 | + <div ng-messages="theForm['mqttRetryInterval_' + brokerIndex].$error"> | ||
68 | + <div translate ng-message="required">extension.retry-interval-required</div> | ||
69 | + </div> | ||
70 | + </md-input-container> | ||
71 | + <md-input-container flex="50" class="md-block"> | ||
72 | + <label translate>extension.credentials</label> | ||
73 | + <md-select required name="mqttCredentials_{{brokerIndex}}" ng-model="broker.credentials.type" ng-change="changeCredentials(broker)"> | ||
74 | + <md-option ng-repeat="(credentialsType, credentialsValue) in types.mqttCredentialTypes" ng-value="credentialsValue.value"> | ||
75 | + {{credentialsValue.name | translate}} | ||
76 | + </md-option> | ||
77 | + </md-select> | ||
78 | + </md-input-container> | ||
79 | + <md-input-container flex="10" class="md-block t-right"> | ||
80 | + <md-checkbox flex aria-label="{{ 'extension.ssl' | translate }}" | ||
81 | + ng-model="broker.ssl">{{ 'extension.ssl' | translate }} | ||
82 | + </md-checkbox> | ||
83 | + </md-input-container> | ||
84 | + </section> | ||
85 | + <section flex layout="row" ng-if='broker.credentials.type == "basic"'> | ||
86 | + <md-input-container flex="40" class="md-block"> | ||
87 | + <label translate>extension.username</label> | ||
88 | + <input required name="mqttUsername_{{brokerIndex}}" ng-model="broker.credentials.username"> | ||
89 | + <div ng-messages="theForm['mqttUsername_' + brokerIndex].$error"> | ||
90 | + <div translate ng-message="required">extension.username-required</div> | ||
91 | + </div> | ||
92 | + </md-input-container> | ||
93 | + <md-input-container flex="60" class="md-block"> | ||
94 | + <label translate>extension.password</label> | ||
95 | + <input required name="mqttPassword_{{brokerIndex}}" ng-model="broker.credentials.password"> | ||
96 | + <div ng-messages="theForm['mqttPassword_' + brokerIndex].$error"> | ||
97 | + <div translate ng-message="required">extension.password-required</div> | ||
98 | + </div> | ||
99 | + </md-input-container> | ||
100 | + </section> | ||
101 | + <section flex layout="column" ng-if='broker.credentials.type == "cert.PEM"'> | ||
102 | + <div class="tb-container"> | ||
103 | + <label class="tb-label" translate>extension.ca-cert</label> | ||
104 | + <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "caCert")' class="tb-file-select-container"> | ||
105 | + <div class="tb-file-clear-container"> | ||
106 | + <md-button ng-click='clearFile(broker, "caCert")' class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ 'action.remove' | translate }}"> | ||
107 | + <md-tooltip md-direction="top"> | ||
108 | + {{ 'action.remove' | translate }} | ||
109 | + </md-tooltip> | ||
110 | + <md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons">close</md-icon> | ||
111 | + </md-button> | ||
112 | + </div> | ||
113 | + <div class="alert tb-flow-drop" flow-drop> | ||
114 | + <label for="caCertSelect_{{brokerIndex}}" translate>extension.drop-file</label> | ||
115 | + <input class="file-input" flow-btn flow-attrs="{accept:'.pem'}" id="caCertSelect_{{brokerIndex}}"> | ||
116 | + </div> | ||
117 | + </div> | ||
118 | + </div> | ||
119 | + <div class="dropdown-messages"> | ||
120 | + <div ng-if="!broker.credentials.caCertFileName" class="tb-error-message" translate>extension.no-file</div> | ||
121 | + <div ng-if="broker.credentials.caCertFileName">{{broker.credentials.caCertFileName}}</div> | ||
122 | + </div> | ||
123 | + <div class="tb-container"> | ||
124 | + <label class="tb-label" translate>extension.private-key</label> | ||
125 | + <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "privateKey")' class="tb-file-select-container"> | ||
126 | + <div class="tb-file-clear-container"> | ||
127 | + <md-button ng-click='clearFile(broker, "privateKey")' class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ 'action.remove' | translate }}"> | ||
128 | + <md-tooltip md-direction="top"> | ||
129 | + {{ 'action.remove' | translate }} | ||
130 | + </md-tooltip> | ||
131 | + <md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons">close</md-icon> | ||
132 | + </md-button> | ||
133 | + </div> | ||
134 | + <div class="alert tb-flow-drop" flow-drop> | ||
135 | + <label for="privateKeySelect_{{brokerIndex}}" translate>extension.drop-file</label> | ||
136 | + <input class="file-input" flow-btn flow-attrs="{accept:'.pem'}" id="privateKeySelect_{{brokerIndex}}"> | ||
137 | + </div> | ||
138 | + </div> | ||
139 | + </div> | ||
140 | + <div class="dropdown-messages"> | ||
141 | + <div ng-if="!broker.credentials.privateKeyFileName" class="tb-error-message" translate>extension.no-file</div> | ||
142 | + <div ng-if="broker.credentials.privateKeyFileName">{{broker.credentials.privateKeyFileName}}</div> | ||
143 | + </div> | ||
144 | + <div class="tb-container" ng-class="broker.credentials.certFileName ? 'ng-valid' : 'ng-invalid'"> | ||
145 | + <label class="tb-label" translate>extension.cert</label> | ||
146 | + <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "Cert")' class="tb-file-select-container"> | ||
147 | + <div class="tb-file-clear-container"> | ||
148 | + <md-button ng-click='clearFile(broker, "Cert")' class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ 'action.remove' | translate }}"> | ||
149 | + <md-tooltip md-direction="top"> | ||
150 | + {{ 'action.remove' | translate }} | ||
151 | + </md-tooltip> | ||
152 | + <md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons">close</md-icon> | ||
153 | + </md-button> | ||
154 | + </div> | ||
155 | + <div class="alert tb-flow-drop" flow-drop> | ||
156 | + <label for="CertSelect_{{brokerIndex}}" translate>extension.drop-file</label> | ||
157 | + <input class="file-input" flow-btn flow-attrs="{accept:'.pem'}" id="CertSelect_{{brokerIndex}}"> | ||
158 | + </div> | ||
159 | + </div> | ||
160 | + </div> | ||
161 | + <div class="dropdown-messages"> | ||
162 | + <div ng-if="!broker.credentials.certFileName" class="tb-error-message" translate>extension.no-file</div> | ||
163 | + <div ng-if="broker.credentials.certFileName">{{broker.credentials.certFileName}}</div> | ||
164 | + </div> | ||
165 | + </section> | ||
166 | + | ||
167 | + <v-accordion id="mqtt-mapping-accordion" class="vAccordion--default"> | ||
168 | + <v-pane id="mqtt-mapping-pane"> | ||
169 | + <v-pane-header> | ||
170 | + {{ 'extension.mapping' | translate }} | ||
171 | + </v-pane-header> | ||
172 | + <v-pane-content> | ||
173 | + <div ng-if="broker.mapping.length > 0"> | ||
174 | + <ol class="list-group"> | ||
175 | + <li class="list-group-item" ng-repeat="(mapIndex,map) in broker.mapping"> | ||
176 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeMap(map, broker.mapping)"> | ||
177 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | ||
178 | + <md-tooltip md-direction="top"> | ||
179 | + {{ 'action.remove' | translate }} | ||
180 | + </md-tooltip> | ||
181 | + </md-button> | ||
182 | + <md-card> | ||
183 | + <md-card-content> | ||
184 | + <section flex layout="row"> | ||
185 | + <md-input-container flex="40" class="md-block"> | ||
186 | + <label translate>extension.converter-type</label> | ||
187 | + <md-select required name="mqttConverterType_{{brokerIndex}}{{mapIndex}}" ng-model="map.converterType" ng-change="changeConverterType(map)"> | ||
188 | + <md-option ng-repeat="(converterType, value) in types.mqttConverterTypes" ng-value="converterType"> | ||
189 | + {{value | translate}} | ||
190 | + </md-option> | ||
191 | + </md-select> | ||
192 | + <div ng-messages="theForm['mqttConverterType_' + brokerIndex + mapIndex].$error"> | ||
193 | + <div translate ng-message="required">extension.converter-type-required</div> | ||
194 | + </div> | ||
195 | + </md-input-container> | ||
196 | + <md-input-container flex="60" class="md-block"> | ||
197 | + <label translate>extension.topic-filter</label> | ||
198 | + <input required name="mqttTopicFilter_{{brokerIndex}}{{mapIndex}}" ng-model="map.topicFilter"> | ||
199 | + <div ng-messages="theForm['mqttTopicFilter_' + brokerIndex + mapIndex].$error"> | ||
200 | + <div translate ng-message="required">extension.topic-filter-required</div> | ||
201 | + </div> | ||
202 | + </md-input-container> | ||
203 | + </section> | ||
204 | + | ||
205 | + <div ng-if='map.converterType =="json"' ng-init="map.converter.type = 'json'"> | ||
206 | + <section flex layout="row"> | ||
207 | + <md-input-container flex="40" class="md-block"> | ||
208 | + <label translate>extension.name-expression</label> | ||
209 | + <md-select required name="mqttDeviceNameExpression_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.nameExp" ng-change="changeNameExpression(map.converter)"> | ||
210 | + <md-option ng-repeat="(key, value) in nameExpressions" ng-value='key'> | ||
211 | + {{value | translate}} | ||
212 | + </md-option> | ||
213 | + </md-select> | ||
214 | + </md-input-container> | ||
215 | + <md-input-container ng-if="map.converter.nameExp == 'deviceNameJsonExpression'" flex="60" class="md-block"> | ||
216 | + <label translate>extension.json-name-expression</label> | ||
217 | + <input required name="mqttJsonNameExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceNameJsonExpression"> | ||
218 | + <div ng-messages="theForm['mqttJsonNameExp_' + brokerIndex + mapIndex].$error"> | ||
219 | + <div translate ng-message="required">extension.json-name-expression-required</div> | ||
220 | + </div> | ||
221 | + </md-input-container> | ||
222 | + <md-input-container ng-if="map.converter.nameExp == 'deviceNameTopicExpression'" flex="60" class="md-block"> | ||
223 | + <label translate>extension.topic-name-expression</label> | ||
224 | + <input required name="mqttTopicNameExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceNameTopicExpression"> | ||
225 | + <div ng-messages="theForm['mqttTopicNameExp_' + brokerIndex + mapIndex].$error"> | ||
226 | + <div translate ng-message="required">extension.topic-name-expression-required</div> | ||
227 | + </div> | ||
228 | + </md-input-container> | ||
229 | + </section> | ||
230 | + <section flex layout="row"> | ||
231 | + <md-input-container flex="40" class="md-block"> | ||
232 | + <label translate>extension.type-expression</label> | ||
233 | + <md-select required name="mqttDeviceTypeExpression_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.typeExp" ng-change="changeTypeExpression(map.converter)"> | ||
234 | + <md-option ng-repeat="(key, value) in typeExpressions" ng-value='key'> | ||
235 | + {{value | translate}} | ||
236 | + </md-option> | ||
237 | + </md-select> | ||
238 | + </md-input-container> | ||
239 | + <md-input-container ng-if="map.converter.typeExp == 'deviceTypeJsonExpression'" flex="60" class="md-block"> | ||
240 | + <label translate>extension.json-type-expression</label> | ||
241 | + <input required name="mqttJsonTypeExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceTypeJsonExpression"> | ||
242 | + <div ng-messages="theForm['mqttJsonTypeExp_' + brokerIndex + mapIndex].$error"> | ||
243 | + <div translate ng-message="required">extension.json-type-expression-required</div> | ||
244 | + </div> | ||
245 | + </md-input-container> | ||
246 | + <md-input-container ng-if="map.converter.typeExp == 'deviceTypeTopicExpression'" flex="60" class="md-block"> | ||
247 | + <label translate>extension.topic-type-expression</label> | ||
248 | + <input required name="mqttTopicTypeExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceTypeTopicExpression"> | ||
249 | + <div ng-messages="theForm['mqttTopicTypeExp_' + brokerIndex + mapIndex].$error"> | ||
250 | + <div translate ng-message="required">extension.topic-type-expression-required</div> | ||
251 | + </div> | ||
252 | + </md-input-container> | ||
253 | + </section> | ||
254 | + <section flex layout="row"> | ||
255 | + <md-input-container flex="40" class="md-block"> | ||
256 | + <label translate>extension.timeout</label> | ||
257 | + <input type="number" name="mqttTimeout_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.timeout" parse-to-null> | ||
258 | + </md-input-container> | ||
259 | + <md-input-container flex="60" class="md-block"> | ||
260 | + <label translate>extension.filter-expression</label> | ||
261 | + <input required name="mqttFilterExpression{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.filterExpression"> | ||
262 | + <div ng-messages="theForm['mqttFilterExpression' + brokerIndex + mapIndex].$error"> | ||
263 | + <div translate ng-message="required">extension.filter-expression-required</div> | ||
264 | + </div> | ||
265 | + </md-input-container> | ||
266 | + </section> | ||
267 | + </div> | ||
268 | + | ||
269 | + <div ng-if='map.converterType == "custom"'> | ||
270 | + <div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>extension.transformer-json</div> | ||
271 | + <div flex class="tb-extension-custom-transformer-panel"> | ||
272 | + <div flex class="tb-extension-custom-transformer" | ||
273 | + ui-ace="extensionCustomConverterOptions" | ||
274 | + ng-model="map.converter" | ||
275 | + name="mqttCustomConverter_{{brokerIndex}}{{mapIndex}}" | ||
276 | + ng-change='validateCustomConverter(map.converter, "mqttCustomConverter_" + brokerIndex + mapIndex)' | ||
277 | + required> | ||
278 | + </div> | ||
279 | + </div> | ||
280 | + <div class="tb-error-messages" ng-messages="theForm['mqttCustomConverter_' + brokerIndex + mapIndex].$error" role="alert"> | ||
281 | + <div ng-message="required" class="tb-error-message" translate>extension.converter-json-required</div> | ||
282 | + <div ng-message="converterJSON" class="tb-error-message" translate>extension.converter-json-parse</div> | ||
283 | + </div> | ||
284 | + </div> | ||
285 | + | ||
286 | + <v-accordion ng-if='map.converterType =="json"' id="mqtt-attributes-accordion" class="vAccordion--default"> | ||
287 | + <v-pane id="mqtt-attributes-pane"> | ||
288 | + <v-pane-header> | ||
289 | + {{ 'extension.attributes' | translate }} | ||
290 | + </v-pane-header> | ||
291 | + <v-pane-content> | ||
292 | + <div ng-if="map.converter.attributes.length > 0"> | ||
293 | + <ol class="list-group"> | ||
294 | + <li class="list-group-item" ng-repeat="(attributeIndex, attribute) in map.converter.attributes"> | ||
295 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeAttribute(attribute, map.converter.attributes)"> | ||
296 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | ||
297 | + <md-tooltip md-direction="top"> | ||
298 | + {{ 'action.remove' | translate }} | ||
299 | + </md-tooltip> | ||
300 | + </md-button> | ||
301 | + <md-card> | ||
302 | + <md-card-content> | ||
303 | + <section flex layout="row"> | ||
304 | + <md-input-container flex="60" class="md-block"> | ||
305 | + <label translate>extension.key</label> | ||
306 | + <input required name="mqttAttributeKey_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.key"> | ||
307 | + <div ng-messages="theForm['mqttAttributeKey_' + brokerIndex + mapIndex + attributeIndex].$error"> | ||
308 | + <div translate ng-message="required">extension.required-key</div> | ||
309 | + </div> | ||
310 | + </md-input-container> | ||
311 | + <md-input-container flex="40" class="md-block"> | ||
312 | + <label translate>extension.type</label> | ||
313 | + <md-select required name="mqttAttributeType_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.type"> | ||
314 | + <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> | ||
315 | + {{attrTypeValue | translate}} | ||
316 | + </md-option> | ||
317 | + </md-select> | ||
318 | + <div ng-messages="theForm['mqttAttributeType_' + brokerIndex + mapIndex + attributeIndex].$error"> | ||
319 | + <div translate ng-message="required">extension.required-type</div> | ||
320 | + </div> | ||
321 | + </md-input-container> | ||
322 | + </section> | ||
323 | + <md-input-container class="md-block"> | ||
324 | + <label translate>extension.value</label> | ||
325 | + <input required name="mqttAttributeValue_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.value"> | ||
326 | + <div ng-messages="theForm['mqttAttributeValue_' + brokerIndex + mapIndex + attributeIndex].$error"> | ||
327 | + <div translate ng-message="required">extension.required-value</div> | ||
328 | + </div> | ||
329 | + </md-input-container> | ||
330 | + </md-card-content> | ||
331 | + </md-card> | ||
332 | + </li> | ||
333 | + </ol> | ||
334 | + </div> | ||
335 | + <div flex layout="row" layout-align="start center"> | ||
336 | + <md-button class="md-primary md-raised" | ||
337 | + ng-click="addAttribute(map.converter.attributes)" aria-label="{{ 'action.add' | translate }}"> | ||
338 | + <md-tooltip md-direction="top"> | ||
339 | + {{ 'extension.add-attribute' | translate }} | ||
340 | + </md-tooltip> | ||
341 | + <md-icon class="material-icons">add</md-icon> | ||
342 | + <span translate>action.add</span> | ||
343 | + </md-button> | ||
344 | + </div> | ||
345 | + </v-pane-content> | ||
346 | + </v-pane> | ||
347 | + </v-accordion> | ||
348 | + | ||
349 | + <v-accordion ng-if='map.converterType =="json"' id="mqtt-timeseries-accordion" class="vAccordion--default"> | ||
350 | + <v-pane id="mqtt-timeseries-pane"> | ||
351 | + <v-pane-header> | ||
352 | + {{ 'extension.timeseries' | translate }} | ||
353 | + </v-pane-header> | ||
354 | + <v-pane-content> | ||
355 | + <div ng-if="map.converter.timeseries.length > 0"> | ||
356 | + <ol class="list-group"> | ||
357 | + <li class="list-group-item" ng-repeat="(timeseriesIndex, timeseries) in map.converter.timeseries"> | ||
358 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeAttribute(timeseries, map.converter.timeseries)"> | ||
359 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | ||
360 | + <md-tooltip md-direction="top"> | ||
361 | + {{ 'action.remove' | translate }} | ||
362 | + </md-tooltip> | ||
363 | + </md-button> | ||
364 | + <md-card> | ||
365 | + <md-card-content> | ||
366 | + <section flex layout="row"> | ||
367 | + <md-input-container flex="60" class="md-block"> | ||
368 | + <label translate>extension.key</label> | ||
369 | + <input required name="mqttTimeseriesKey_{{brokerIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.key"> | ||
370 | + <div ng-messages="theForm['mqtTtimeseriesKey_' + brokerIndex + mapIndex + timeseriesIndex].$error"> | ||
371 | + <div translate ng-message="required">extension.required-key</div> | ||
372 | + </div> | ||
373 | + </md-input-container> | ||
374 | + <md-input-container flex="40" class="md-block"> | ||
375 | + <label translate>extension.type</label> | ||
376 | + <md-select required name="mqttTimeseriesType_{{brokerIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.type"> | ||
377 | + <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> | ||
378 | + {{attrTypeValue | translate}} | ||
379 | + </md-option> | ||
380 | + </md-select> | ||
381 | + <div ng-messages="theForm['mqttTimeseriesType_' + brokerIndex + mapIndex + timeseriesIndex].$error"> | ||
382 | + <div translate ng-message="required">extension.required-type</div> | ||
383 | + </div> | ||
384 | + </md-input-container> | ||
385 | + </section> | ||
386 | + <md-input-container class="md-block"> | ||
387 | + <label translate>extension.value</label> | ||
388 | + <input required name="mqttTimeseriesValue_{{brokerIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.value"> | ||
389 | + <div ng-messages="theForm['mqttTimeseriesValue_' + brokerIndex + mapIndex + timeseriesIndex].$error"> | ||
390 | + <div translate ng-message="required">extension.required-value</div> | ||
391 | + </div> | ||
392 | + </md-input-container> | ||
393 | + </md-card-content> | ||
394 | + </md-card> | ||
395 | + </li> | ||
396 | + </ol> | ||
397 | + </div> | ||
398 | + <div flex layout="row" layout-align="start center"> | ||
399 | + <md-button class="md-primary md-raised" | ||
400 | + ng-click="addAttribute(map.converter.timeseries)" aria-label="{{ 'action.add' | translate }}"> | ||
401 | + <md-tooltip md-direction="top"> | ||
402 | + {{ 'extension.add-timeseries' | translate }} | ||
403 | + </md-tooltip> | ||
404 | + <md-icon class="material-icons">add</md-icon> | ||
405 | + <span translate>action.add</span> | ||
406 | + </md-button> | ||
407 | + </div> | ||
408 | + </v-pane-content> | ||
409 | + </v-pane> | ||
410 | + </v-accordion> | ||
411 | + | ||
412 | + </md-card-content> | ||
413 | + </md-card> | ||
414 | + </li> | ||
415 | + </ol> | ||
416 | + </div> | ||
417 | + <div flex layout="row" layout-align="start center"> | ||
418 | + <md-button class="md-primary md-raised" | ||
419 | + ng-click="addMap(broker.mapping)" aria-label="{{ 'action.add' | translate }}"> | ||
420 | + <md-tooltip md-direction="top"> | ||
421 | + {{ 'extension.add-map' | translate }} | ||
422 | + </md-tooltip> | ||
423 | + <md-icon class="material-icons">add</md-icon> | ||
424 | + <span translate>action.add</span> | ||
425 | + </md-button> | ||
426 | + </div> | ||
427 | + </v-pane-content> | ||
428 | + </v-pane> | ||
429 | + </v-accordion> | ||
430 | + </md-card-content> | ||
431 | + </md-card> | ||
432 | + </li> | ||
433 | + </ol> | ||
434 | + </div> | ||
435 | + | ||
436 | + <div flex layout="row" layout-align="start center"> | ||
437 | + <md-button class="md-primary md-raised" | ||
438 | + ng-click="addBroker()" aria-label="{{ 'action.add' | translate }}"> | ||
439 | + <md-tooltip md-direction="top"> | ||
440 | + {{ 'extension.add-broker' | translate }} | ||
441 | + </md-tooltip> | ||
442 | + <md-icon class="material-icons">add</md-icon> | ||
443 | + <span translate>action.add</span> | ||
444 | + </md-button> | ||
445 | + </div> | ||
446 | + </v-pane-content> | ||
447 | + </v-pane> | ||
448 | + </v-accordion> | ||
449 | +<pre> | ||
450 | +{{config | json}} | ||
451 | +</pre> | ||
452 | + </md-card-content> | ||
453 | +</md-card> |
1 | -/* | 1 | +/** |
2 | * Copyright © 2016-2017 The Thingsboard Authors | 2 | * Copyright © 2016-2017 The Thingsboard Authors |
3 | * | 3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
@@ -13,7 +13,6 @@ | @@ -13,7 +13,6 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | - | ||
17 | .extension-form { | 16 | .extension-form { |
18 | li > .md-button { | 17 | li > .md-button { |
19 | color: rgba(0, 0, 0, 0.7); | 18 | color: rgba(0, 0, 0, 0.7); |
@@ -23,6 +22,17 @@ | @@ -23,6 +22,17 @@ | ||
23 | margin-top: 0; | 22 | margin-top: 0; |
24 | padding-left: 3px; | 23 | padding-left: 3px; |
25 | } | 24 | } |
25 | + .t-right { | ||
26 | + text-align: right; | ||
27 | + } | ||
28 | + .tb-container { | ||
29 | + width:100%; | ||
30 | + } | ||
31 | + .dropdown-messages { | ||
32 | + .tb-error-message { | ||
33 | + padding: 5px 0 0 0; | ||
34 | + } | ||
35 | + } | ||
26 | } | 36 | } |
27 | 37 | ||
28 | .tb-extension-custom-transformer-panel { | 38 | .tb-extension-custom-transformer-panel { |
@@ -16,10 +16,12 @@ | @@ -16,10 +16,12 @@ | ||
16 | 16 | ||
17 | import ExtensionTableDirective from './extension-table.directive'; | 17 | import ExtensionTableDirective from './extension-table.directive'; |
18 | import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive'; | 18 | import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive'; |
19 | +import ExtensionFormMqttDirective from './extensions-forms/extension-form-mqtt.directive' | ||
19 | import {ParseToNull} from './extension-dialog.controller'; | 20 | import {ParseToNull} from './extension-dialog.controller'; |
20 | 21 | ||
21 | export default angular.module('thingsboard.extension', []) | 22 | export default angular.module('thingsboard.extension', []) |
22 | .directive('tbExtensionTable', ExtensionTableDirective) | 23 | .directive('tbExtensionTable', ExtensionTableDirective) |
23 | .directive('tbExtensionFormHttp', ExtensionFormHttpDirective) | 24 | .directive('tbExtensionFormHttp', ExtensionFormHttpDirective) |
25 | + .directive('tbExtensionFormMqtt', ExtensionFormMqttDirective) | ||
24 | .directive('parseToNull', ParseToNull) | 26 | .directive('parseToNull', ParseToNull) |
25 | .name; | 27 | .name; |
@@ -738,7 +738,7 @@ export default angular.module('thingsboard.locale', []) | @@ -738,7 +738,7 @@ export default angular.module('thingsboard.locale', []) | ||
738 | "id": "Id", | 738 | "id": "Id", |
739 | "extension-id": "Extension id", | 739 | "extension-id": "Extension id", |
740 | "extension-type": "Extension type", | 740 | "extension-type": "Extension type", |
741 | - "transformer-json": "JSON*", | 741 | + "transformer-json": "JSON *", |
742 | "id-required": "Extension id is required.", | 742 | "id-required": "Extension id is required.", |
743 | "unique-id-required": "Current extension id already exists.", | 743 | "unique-id-required": "Current extension id already exists.", |
744 | "type-required": "Extension type is required.", | 744 | "type-required": "Extension type is required.", |
@@ -775,6 +775,54 @@ export default angular.module('thingsboard.locale', []) | @@ -775,6 +775,54 @@ export default angular.module('thingsboard.locale', []) | ||
775 | "add-attribute": "Add attribute", | 775 | "add-attribute": "Add attribute", |
776 | "timeseries": "Timeseries", | 776 | "timeseries": "Timeseries", |
777 | "add-timeseries": "Add timeseries", | 777 | "add-timeseries": "Add timeseries", |
778 | + | ||
779 | + "brokers": "Brokers", | ||
780 | + "add-broker": "Add broker", | ||
781 | + "add-broker-prompt": "Please add broker", | ||
782 | + "host": "Host", | ||
783 | + "host-required": "Host is required.", | ||
784 | + "port": "Port", | ||
785 | + "port-required": "Port is required.", | ||
786 | + "port-range": "Port should be in a range from 1 to 65535.", | ||
787 | + "ssl": "Ssl", | ||
788 | + "credentials": "Credentials", | ||
789 | + "username": "Username", | ||
790 | + "username-required": "Username is required.", | ||
791 | + "password": "Password", | ||
792 | + "password-required": "Password is required.", | ||
793 | + "retry-interval": "Retry interval", | ||
794 | + "retry-interval-required": "Retry interval is required.", | ||
795 | + "anonymous": "Anonymous", | ||
796 | + "basic": "Basic", | ||
797 | + "pem": "PEM", | ||
798 | + "ca-cert": "CA certificate file *", | ||
799 | + "private-key": "Private key file *", | ||
800 | + "cert": "Certificate file *", | ||
801 | + "no-file": "No file selected.", | ||
802 | + "drop-file": "Drop a file or click to select a file to upload.", | ||
803 | + "mapping": "Mapping", | ||
804 | + "add-map": "Add map", | ||
805 | + "topic-filter": "Topic filter", | ||
806 | + "topic-filter-required": "Topic filter is required.", | ||
807 | + "converter-type": "Converter type", | ||
808 | + "converter-type-required": "Converter type is required.", | ||
809 | + "converter-json": "Json", | ||
810 | + "name-expression": "Name expression", | ||
811 | + "type-expression": "Type expression", | ||
812 | + "json-name-expression": "Json name expression", | ||
813 | + "json-name-expression-required": "Json name expression is required.", | ||
814 | + "topic-name-expression": "Topic name expression", | ||
815 | + "topic-name-expression-required": "Topic name expression is required.", | ||
816 | + "json-type-expression": "Json type expression", | ||
817 | + "json-type-expression-required": "Json type expression is required.", | ||
818 | + "topic-type-expression": "Topic type expression", | ||
819 | + "topic-type-expression-required": "Topic type expression is required.", | ||
820 | + "topic": "Topic", | ||
821 | + "timeout": "Timeout", | ||
822 | + "converter-json-required": "Converter json is required.", | ||
823 | + "converter-json-parse": "Unable to parse converter json.", | ||
824 | + "filter-expression": "Filter expression", | ||
825 | + "filter-expression-required": "Filter expression is required." | ||
778 | }, | 826 | }, |
779 | "fullscreen": { | 827 | "fullscreen": { |
780 | "expand": "Expand to fullscreen", | 828 | "expand": "Expand to fullscreen", |
@@ -898,7 +946,6 @@ export default angular.module('thingsboard.locale', []) | @@ -898,7 +946,6 @@ export default angular.module('thingsboard.locale', []) | ||
898 | "invalid-plugin-file-error": "Unable to import plugin: Invalid plugin data structure.", | 946 | "invalid-plugin-file-error": "Unable to import plugin: Invalid plugin data structure.", |
899 | "copyId": "Copy plugin Id", | 947 | "copyId": "Copy plugin Id", |
900 | "idCopiedMessage": "Plugin Id has been copied to clipboard" | 948 | "idCopiedMessage": "Plugin Id has been copied to clipboard" |
901 | - | ||
902 | }, | 949 | }, |
903 | "position": { | 950 | "position": { |
904 | "top": "Top", | 951 | "top": "Top", |