Showing
14 changed files
with
1567 additions
and
305 deletions
... | ... | @@ -30,6 +30,7 @@ |
30 | 30 | "angular-material": "1.1.1", |
31 | 31 | "angular-material-data-table": "^0.10.9", |
32 | 32 | "angular-material-icons": "^0.7.1", |
33 | + "angular-material-expansion-panel": "^0.7.2", | |
33 | 34 | "angular-messages": "1.5.8", |
34 | 35 | "angular-route": "1.5.8", |
35 | 36 | "angular-sanitize": "1.5.8", | ... | ... |
... | ... | @@ -39,6 +39,7 @@ import uiRouter from 'angular-ui-router'; |
39 | 39 | import angularJwt from 'angular-jwt'; |
40 | 40 | import 'angular-drag-and-drop-lists'; |
41 | 41 | import mdDataTable from 'angular-material-data-table'; |
42 | +import 'angular-material-expansion-panel'; | |
42 | 43 | import ngTouch from 'angular-touch'; |
43 | 44 | import 'angular-carousel'; |
44 | 45 | import 'clipboard'; |
... | ... | @@ -82,6 +83,7 @@ import 'md-color-picker/dist/mdColorPicker.min.css'; |
82 | 83 | import 'mdPickers/dist/mdPickers.min.css'; |
83 | 84 | import 'angular-hotkeys/build/hotkeys.min.css'; |
84 | 85 | import 'angular-carousel/dist/angular-carousel.min.css'; |
86 | +import 'angular-material-expansion-panel/dist/md-expansion-panel.min.css'; | |
85 | 87 | import '../scss/main.scss'; |
86 | 88 | |
87 | 89 | import AppConfig from './app.config'; |
... | ... | @@ -103,6 +105,7 @@ angular.module('thingsboard', [ |
103 | 105 | angularJwt, |
104 | 106 | 'dndLists', |
105 | 107 | mdDataTable, |
108 | + 'material.components.expansionPanels', | |
106 | 109 | ngTouch, |
107 | 110 | 'angular-carousel', |
108 | 111 | 'ngclipboard', | ... | ... |
... | ... | @@ -332,6 +332,24 @@ export default angular.module('thingsboard.types', []) |
332 | 332 | toDouble: 'extension.to-double', |
333 | 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 | 353 | extensionOpcSecurityTypes: { |
336 | 354 | Basic128Rsa15: "Basic128Rsa15", |
337 | 355 | Basic256: "Basic256", |
... | ... | @@ -339,8 +357,8 @@ export default angular.module('thingsboard.types', []) |
339 | 357 | None: "None" |
340 | 358 | }, |
341 | 359 | extensionIdentityType: { |
342 | - anonymous: "anonymous", | |
343 | - username: "username" | |
360 | + anonymous: "extension.anonymous", | |
361 | + username: "extension.username" | |
344 | 362 | }, |
345 | 363 | extensionKeystoreType: { |
346 | 364 | PKCS12: "PKCS12", | ... | ... |
... | ... | @@ -30,11 +30,10 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
30 | 30 | vm.allExtensions = allExtensions; |
31 | 31 | |
32 | 32 | |
33 | - if (extension) { // Editing | |
34 | - //vm.configuration = vm.extension.configuration; | |
33 | + if (extension) { | |
35 | 34 | vm.extension = angular.copy(extension); |
36 | 35 | editTransformers(vm.extension); |
37 | - } else { // Add new | |
36 | + } else { | |
38 | 37 | vm.extension = {}; |
39 | 38 | } |
40 | 39 | |
... | ... | @@ -65,8 +64,6 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
65 | 64 | |
66 | 65 | vm.save = save; |
67 | 66 | function save() { |
68 | - saveTransformers(); | |
69 | - | |
70 | 67 | let $errorElement = angular.element('[name=theForm]').find('.ng-invalid'); |
71 | 68 | |
72 | 69 | if ($errorElement.length) { |
... | ... | @@ -78,11 +75,10 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
78 | 75 | |
79 | 76 | if ($errorElementTop !== $mdDialogTop) { |
80 | 77 | angular.element('md-dialog-content').animate({ |
81 | - scrollTop: $mdDialogScroll + ($errorElementTop - $mdDialogTop) - 20 | |
78 | + scrollTop: $mdDialogScroll + ($errorElementTop - $mdDialogTop) - 50 | |
82 | 79 | }, 500); |
83 | 80 | $errorElement.eq(0).focus(); |
84 | 81 | } |
85 | - | |
86 | 82 | } else { |
87 | 83 | |
88 | 84 | if(vm.isAdd) { |
... | ... | @@ -94,6 +90,9 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
94 | 90 | } |
95 | 91 | } |
96 | 92 | |
93 | + $mdDialog.hide(); | |
94 | + saveTransformers(); | |
95 | + | |
97 | 96 | var editedValue = angular.toJson(vm.allExtensions); |
98 | 97 | |
99 | 98 | attributeService |
... | ... | @@ -104,8 +103,6 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
104 | 103 | [{key:"configuration", value:editedValue}] |
105 | 104 | ) |
106 | 105 | .then(function success() { |
107 | - $scope.theForm.$setPristine(); | |
108 | - $mdDialog.hide(); | |
109 | 106 | }); |
110 | 107 | |
111 | 108 | } |
... | ... | @@ -131,21 +128,60 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
131 | 128 | }; |
132 | 129 | |
133 | 130 | function saveTransformers() { |
134 | - var config = vm.extension.configuration.converterConfigurations; | |
135 | 131 | if(vm.extension.type == types.extensionType.http) { |
136 | - for(let i=0;i<config.length;i++) { | |
137 | - for(let j=0;j<config[i].converters.length;j++){ | |
138 | - for(let k=0;k<config[i].converters[j].attributes.length;k++){ | |
139 | - if(config[i].converters[j].attributes[k].transformerType == "toDouble"){ | |
140 | - config[i].converters[j].attributes[k].transformer = {type: "intToDouble"}; | |
132 | + var config = vm.extension.configuration.converterConfigurations; | |
133 | + if(config && config.length > 0) { | |
134 | + for(let i=0;i<config.length;i++) { | |
135 | + for(let j=0;j<config[i].converters.length;j++){ | |
136 | + for(let k=0;k<config[i].converters[j].attributes.length;k++){ | |
137 | + if(config[i].converters[j].attributes[k].transformerType == "toDouble"){ | |
138 | + config[i].converters[j].attributes[k].transformer = {type: "intToDouble"}; | |
139 | + } | |
140 | + delete config[i].converters[j].attributes[k].transformerType; | |
141 | + } | |
142 | + for(let l=0;l<config[i].converters[j].timeseries.length;l++) { | |
143 | + if(config[i].converters[j].timeseries[l].transformerType == "toDouble"){ | |
144 | + config[i].converters[j].timeseries[l].transformer = {type: "intToDouble"}; | |
145 | + } | |
146 | + delete config[i].converters[j].timeseries[l].transformerType; | |
141 | 147 | } |
142 | - delete config[i].converters[j].attributes[k].transformerType; | |
143 | 148 | } |
144 | - for(let l=0;l<config[i].converters[j].timeseries.length;l++) { | |
145 | - if(config[i].converters[j].timeseries[l].transformerType == "toDouble"){ | |
146 | - config[i].converters[j].timeseries[l].transformer = {type: "intToDouble"}; | |
149 | + } | |
150 | + } | |
151 | + } | |
152 | + if(vm.extension.type == types.extensionType.mqtt) { | |
153 | + var brokers = vm.extension.configuration.brokers; | |
154 | + if(brokers && brokers.length > 0) { | |
155 | + for(let i=0;i<brokers.length;i++) { | |
156 | + if(brokers[i].mapping && brokers[i].mapping.length > 0) { | |
157 | + for(let j=0;j<brokers[i].mapping.length;j++) { | |
158 | + if(brokers[i].mapping[j].converterType == "json") { | |
159 | + delete brokers[i].mapping[j].converter.nameExp; | |
160 | + delete brokers[i].mapping[j].converter.typeExp; | |
161 | + } | |
162 | + delete brokers[i].mapping[j].converterType; | |
163 | + } | |
164 | + } | |
165 | + if(brokers[i].connectRequests && brokers[i].connectRequests.length > 0) { | |
166 | + for(let j=0;j<brokers[i].connectRequests.length;j++) { | |
167 | + delete brokers[i].connectRequests[j].nameExp; | |
168 | + } | |
169 | + } | |
170 | + if(brokers[i].disconnectRequests && brokers[i].disconnectRequests.length > 0) { | |
171 | + for(let j=0;j<brokers[i].disconnectRequests.length;j++) { | |
172 | + delete brokers[i].disconnectRequests[j].nameExp; | |
173 | + } | |
174 | + } | |
175 | + if(brokers[i].attributeRequests && brokers[i].attributeRequests.length > 0) { | |
176 | + for(let j=0;j<brokers[i].attributeRequests.length;j++) { | |
177 | + delete brokers[i].attributeRequests[j].nameExp; | |
178 | + } | |
179 | + for(let j=0;j<brokers[i].attributeRequests.length;j++) { | |
180 | + delete brokers[i].attributeRequests[j].attrKey; | |
181 | + } | |
182 | + for(let j=0;j<brokers[i].attributeRequests.length;j++) { | |
183 | + delete brokers[i].attributeRequests[j].requestId; | |
147 | 184 | } |
148 | - delete config[i].converters[j].timeseries[l].transformerType; | |
149 | 185 | } |
150 | 186 | } |
151 | 187 | } |
... | ... | @@ -153,8 +189,8 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
153 | 189 | } |
154 | 190 | |
155 | 191 | function editTransformers(extension) { |
156 | - var config = extension.configuration.converterConfigurations; | |
157 | 192 | if(extension.type == types.extensionType.http) { |
193 | + var config = extension.configuration.converterConfigurations; | |
158 | 194 | for(let i=0;i<config.length;i++) { |
159 | 195 | for(let j=0;j<config[i].converters.length;j++){ |
160 | 196 | for(let k=0;k<config[i].converters[j].attributes.length;k++){ |
... | ... | @@ -180,6 +216,67 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
180 | 216 | } |
181 | 217 | } |
182 | 218 | } |
219 | + if(extension.type == types.extensionType.mqtt) { | |
220 | + var brokers = extension.configuration.brokers; | |
221 | + for(let i=0;i<brokers.length;i++) { | |
222 | + if(brokers[i].mapping && brokers[i].mapping.length > 0) { | |
223 | + for(let j=0;j<brokers[i].mapping.length;j++) { | |
224 | + if(brokers[i].mapping[j].converter.type == "json") { | |
225 | + if(brokers[i].mapping[j].converter.deviceNameTopicExpression) { | |
226 | + brokers[i].mapping[j].converter.nameExp = "deviceNameTopicExpression"; | |
227 | + } else { | |
228 | + brokers[i].mapping[j].converter.nameExp = "deviceNameJsonExpression"; | |
229 | + } | |
230 | + if(brokers[i].mapping[j].converter.deviceTypeTopicExpression) { | |
231 | + brokers[i].mapping[j].converter.typeExp = "deviceTypeTopicExpression"; | |
232 | + } else { | |
233 | + brokers[i].mapping[j].converter.typeExp = "deviceTypeJsonExpression"; | |
234 | + } | |
235 | + brokers[i].mapping[j].converterType = "json"; | |
236 | + } else { | |
237 | + brokers[i].mapping[j].converterType = "custom"; | |
238 | + } | |
239 | + } | |
240 | + } | |
241 | + if(brokers[i].connectRequests && brokers[i].connectRequests.length > 0) { | |
242 | + for(let j=0;j<brokers[i].connectRequests.length;j++) { | |
243 | + if(brokers[i].connectRequests[j].deviceNameTopicExpression) { | |
244 | + brokers[i].connectRequests[j].nameExp = "deviceNameTopicExpression"; | |
245 | + } else { | |
246 | + brokers[i].connectRequests[j].nameExp = "deviceNameJsonExpression"; | |
247 | + } | |
248 | + } | |
249 | + } | |
250 | + if(brokers[i].disconnectRequests && brokers[i].disconnectRequests.length > 0) { | |
251 | + for(let j=0;j<brokers[i].disconnectRequests.length;j++) { | |
252 | + if(brokers[i].disconnectRequests[j].deviceNameTopicExpression) { | |
253 | + brokers[i].disconnectRequests[j].nameExp = "deviceNameTopicExpression"; | |
254 | + } else { | |
255 | + brokers[i].disconnectRequests[j].nameExp = "deviceNameJsonExpression"; | |
256 | + } | |
257 | + } | |
258 | + } | |
259 | + if(brokers[i].attributeRequests && brokers[i].attributeRequests.length > 0) { | |
260 | + for(let j=0;j<brokers[i].attributeRequests.length;j++) { | |
261 | + if(brokers[i].attributeRequests[j].deviceNameTopicExpression) { | |
262 | + brokers[i].attributeRequests[j].nameExp = "deviceNameTopicExpression"; | |
263 | + } else { | |
264 | + brokers[i].attributeRequests[j].nameExp = "deviceNameJsonExpression"; | |
265 | + } | |
266 | + if(brokers[i].attributeRequests[j].attributeKeyTopicExpression) { | |
267 | + brokers[i].attributeRequests[j].attrKey = "attributeKeyTopicExpression"; | |
268 | + } else { | |
269 | + brokers[i].attributeRequests[j].attrKey = "attributeKeyJsonExpression"; | |
270 | + } | |
271 | + if(brokers[i].attributeRequests[j].requestIdTopicExpression) { | |
272 | + brokers[i].attributeRequests[j].requestId = "requestIdTopicExpression"; | |
273 | + } else { | |
274 | + brokers[i].attributeRequests[j].requestId = "requestIdJsonExpression"; | |
275 | + } | |
276 | + } | |
277 | + } | |
278 | + } | |
279 | + } | |
183 | 280 | } |
184 | 281 | } |
185 | 282 | ... | ... |
... | ... | @@ -36,16 +36,16 @@ |
36 | 36 | <md-content class="md-padding" layout="column"> |
37 | 37 | <fieldset ng-disabled="loading"> |
38 | 38 | <section flex layout="row"> |
39 | - <md-input-container flex="60" class="md-block"> | |
39 | + <md-input-container flex="60" class="md-block" md-is-error="theForm.extensionId.$touched && theForm.extensionId.$invalid"> | |
40 | 40 | <label translate>extension.extension-id</label> |
41 | 41 | <input required name="extensionId" ng-model="vm.extension.id" ng-change="vm.validateId()"> |
42 | 42 | <div ng-messages="theForm.extensionId.$error"> |
43 | - <div translate ng-message="required">extension.id-required</div> | |
43 | + <div translate ng-message="required">extension.field-required</div> | |
44 | 44 | <div translate ng-message="uniqueIdValidation">extension.unique-id-required</div> |
45 | 45 | </div> |
46 | 46 | </md-input-container> |
47 | 47 | |
48 | - <md-input-container flex="40" class="md-block"> | |
48 | + <md-input-container flex="40" class="md-block" md-is-error="theForm.extensionType.$touched && theForm.extensionType.$invalid"> | |
49 | 49 | <label translate>extension.extension-type</label> |
50 | 50 | |
51 | 51 | <md-select ng-disabled="!vm.isAdd" required name="extensionType" ng-change="vm.extensionTypeChange()" ng-model="vm.extension.type"> |
... | ... | @@ -55,16 +55,14 @@ |
55 | 55 | </md-select> |
56 | 56 | |
57 | 57 | <div ng-messages="theForm.extensionType.$error"> |
58 | - <div translate ng-message="required">extension.type-required</div> | |
58 | + <div translate ng-message="required">extension.field-required</div> | |
59 | 59 | </div> |
60 | 60 | </md-input-container> |
61 | 61 | </section> |
62 | - | |
63 | - <div tb-extension-form-http config="vm.extension.configuration" is-add="vm.isAdd" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.http"></div> | |
64 | - | |
65 | - <div tb-extension-form-opc configuration="vm.extension.configuration" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.opc"></div> | |
62 | + <div tb-extension-form-http config="vm.extension.configuration" is-add="vm.isAdd" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.http"></div> | |
63 | + <div tb-extension-form-mqtt config="vm.extension.configuration" is-add="vm.isAdd" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.mqtt"></div> | |
64 | + <div tb-extension-form-opc configuration="vm.extension.configuration" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.opc"></div> | |
66 | 65 | </fieldset> |
67 | - <!--<div>{{vm.extension}}</div>--> | |
68 | 66 | </md-content> |
69 | 67 | </div> |
70 | 68 | </md-dialog-content> |
... | ... | @@ -80,5 +78,4 @@ |
80 | 78 | </md-button> |
81 | 79 | </md-dialog-actions> |
82 | 80 | </form> |
83 | -</md-dialog> | |
84 | - | |
81 | +</md-dialog> | |
\ No newline at end of file | ... | ... |
... | ... | @@ -43,7 +43,7 @@ export default function ExtensionTableDirective() { |
43 | 43 | } |
44 | 44 | |
45 | 45 | /*@ngInject*/ |
46 | -function ExtensionTableController($scope, $filter, $document, $translate, types, $mdDialog, attributeService) { | |
46 | +function ExtensionTableController($scope, $filter, $document, $translate, types, $mdDialog, attributeService, telemetryWebsocketService) { | |
47 | 47 | |
48 | 48 | let vm = this; |
49 | 49 | |
... | ... | @@ -83,51 +83,6 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, |
83 | 83 | } |
84 | 84 | }); |
85 | 85 | |
86 | - $scope.$watch('vm.transferredAttributes', function () { | |
87 | - if (vm.transferredAttributes && vm.transferredAttributes.data && vm.transferredAttributes.data.length) { | |
88 | - vm.transferredAttributes.data | |
89 | - .some(attribute=>{ | |
90 | - if (attribute.key === "appliedConfiguration") { | |
91 | - vm.appliedConfiguration = attribute.value; | |
92 | - } | |
93 | - }); | |
94 | - | |
95 | - checkForSync(); | |
96 | - } | |
97 | - }); | |
98 | - | |
99 | - | |
100 | - checkForSync(); | |
101 | - function checkForSync() { | |
102 | - if (vm.appliedConfiguration === vm.extensionsJSON) { | |
103 | - vm.syncStatus = $translate.instant('extension.sync.sync'); | |
104 | - vm.syncLastTime = formatDate(); | |
105 | - } else { | |
106 | - vm.syncStatus = $translate.instant('extension.sync.not-sync'); | |
107 | - } | |
108 | - } | |
109 | - | |
110 | - | |
111 | - function formatDate(date) { | |
112 | - let d; | |
113 | - if (date) { | |
114 | - d = date; | |
115 | - } else { | |
116 | - d = new Date(); | |
117 | - } | |
118 | - | |
119 | - d = d.getFullYear() +'/'+ addZero(d.getMonth()+1) +'/'+ addZero(d.getDate()) + ' ' + addZero(d.getHours()) + ':' + addZero(d.getMinutes()) +':'+ addZero(d.getSeconds()); | |
120 | - return d; | |
121 | - | |
122 | - | |
123 | - function addZero(num) { | |
124 | - if ((angular.isNumber(num) && num < 10) || (angular.isString(num) && num.length === 1)) { | |
125 | - num = '0' + num; | |
126 | - } | |
127 | - return num; | |
128 | - } | |
129 | - } | |
130 | - | |
131 | 86 | function enterFilterMode() { |
132 | 87 | vm.query.search = ''; |
133 | 88 | } |
... | ... | @@ -288,57 +243,73 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, |
288 | 243 | } |
289 | 244 | |
290 | 245 | |
291 | - // vm.subscriptionId = null; | |
292 | - // $scope.checkSubscription = function() { | |
293 | - // var newSubscriptionId = null; | |
294 | - // if (vm.entityId && vm.entityType) { | |
295 | - // newSubscriptionId = attributeService.subscribeForEntityAttributes(vm.entityType, vm.entityId, 'extension/SHARED_SCOPE'); | |
296 | - // } | |
297 | - // if (vm.subscriptionId && vm.subscriptionId != newSubscriptionId) { | |
298 | - // attributeService.unsubscribeForEntityAttributes(vm.subscriptionId); | |
299 | - // } | |
300 | - // vm.subscriptionId = newSubscriptionId; | |
301 | - // } | |
302 | - // | |
303 | - // | |
304 | - // // $scope.attributesData = {}; | |
305 | - // // var entityAttributesSubscriptionMap = []; | |
306 | - // // | |
307 | - // $scope.subscribeForEntityAttributes = function (entityType=vm.entityType, entityId=vm.entityId, attributeScope="SHARED_SCOPE") { | |
308 | - // var subscriptionId = entityType + entityId + attributeScope; | |
309 | - // var entityAttributesSubscription = entityAttributesSubscriptionMap[subscriptionId]; | |
310 | - // if (!entityAttributesSubscription) { | |
311 | - // var subscriptionCommand = { | |
312 | - // entityType: entityType, | |
313 | - // entityId: entityId, | |
314 | - // scope: attributeScope | |
315 | - // }; | |
316 | - // | |
317 | - // var type = attributeScope === types.latestTelemetry.value ? | |
318 | - // types.dataKeyType.timeseries : types.dataKeyType.attribute; | |
319 | - // | |
320 | - // var subscriber = { | |
321 | - // subscriptionCommands: [subscriptionCommand], | |
322 | - // type: type, | |
323 | - // onData: function (data) { | |
324 | - // if (data.data) { | |
325 | - // onSubscriptionData(data.data, subscriptionId); | |
326 | - // } | |
327 | - // } | |
328 | - // }; | |
329 | - // entityAttributesSubscription = { | |
330 | - // subscriber: subscriber, | |
331 | - // attributes: null | |
332 | - // }; | |
333 | - // entityAttributesSubscriptionMap[subscriptionId] = entityAttributesSubscription; | |
334 | - // telemetryWebsocketService.subscribe(subscriber); | |
335 | - // } | |
336 | - // return subscriptionId; | |
337 | - // }; | |
338 | - // | |
339 | - // function onSubscriptionData(data/*, subscriptionId*/) { | |
340 | - // $scope.attributesData = data; | |
341 | - // } | |
342 | - | |
343 | - // telemetryWebsocketService.subscribe(subscriber); | |
246 | + | |
247 | + if (vm.entityId && vm.entityType) { | |
248 | + $scope.subscriber = { | |
249 | + subscriptionCommands: [{ | |
250 | + entityType: vm.entityType, | |
251 | + entityId: vm.entityId, | |
252 | + scope: 'CLIENT_SCOPE' | |
253 | + }], | |
254 | + type: 'attribute', | |
255 | + onData: function (data) { | |
256 | + if (data.data) { | |
257 | + onSubscriptionData(data.data/*, subscriptionId*/); | |
258 | + } | |
259 | + } | |
260 | + }; | |
261 | + } | |
262 | + telemetryWebsocketService.subscribe($scope.subscriber); | |
263 | + | |
264 | + | |
265 | + $scope.$on('$destroy', function() { | |
266 | + telemetryWebsocketService.unsubscribe($scope.subscriber); | |
267 | + }); | |
268 | + | |
269 | + | |
270 | + | |
271 | + | |
272 | + function onSubscriptionData(data/*, subscriptionId*/) { | |
273 | + //$scope.attributesData = data.; | |
274 | + | |
275 | + if (data.appliedConfiguration && data.appliedConfiguration[0] && data.appliedConfiguration[0][1]) { | |
276 | + vm.appliedConfiguration = data.appliedConfiguration[0][1]; | |
277 | + checkForSync(); | |
278 | + $scope.$digest(); | |
279 | + } | |
280 | + } | |
281 | + | |
282 | + | |
283 | + checkForSync(); | |
284 | + function checkForSync() { | |
285 | + if (vm.appliedConfiguration === vm.extensionsJSON) { | |
286 | + vm.syncStatus = $translate.instant('extension.sync.sync'); | |
287 | + vm.syncLastTime = formatDate(); | |
288 | + } else { | |
289 | + vm.syncStatus = $translate.instant('extension.sync.not-sync'); | |
290 | + } | |
291 | + } | |
292 | + | |
293 | + function formatDate(date) { | |
294 | + let d; | |
295 | + if (date) { | |
296 | + d = date; | |
297 | + } else { | |
298 | + d = new Date(); | |
299 | + } | |
300 | + | |
301 | + d = d.getFullYear() +'/'+ addZero(d.getMonth()+1) +'/'+ addZero(d.getDate()) + ' ' + addZero(d.getHours()) + ':' + addZero(d.getMinutes()) +':'+ addZero(d.getSeconds()); | |
302 | + return d; | |
303 | + | |
304 | + | |
305 | + function addZero(num) { | |
306 | + if ((angular.isNumber(num) && num < 10) || (angular.isString(num) && num.length === 1)) { | |
307 | + num = '0' + num; | |
308 | + } | |
309 | + return num; | |
310 | + } | |
311 | + } | |
312 | + | |
313 | + | |
314 | + //telemetryWebsocketService.subscribe(subscriber); | |
344 | 315 | } |
\ No newline at end of file | ... | ... |
... | ... | @@ -67,7 +67,6 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
67 | 67 | if (index > -1) { |
68 | 68 | scope.converterConfigs.splice(index, 1); |
69 | 69 | } |
70 | - scope.theForm.$setDirty(); | |
71 | 70 | }; |
72 | 71 | |
73 | 72 | scope.addConverter = function(converters) { |
... | ... | @@ -85,7 +84,6 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
85 | 84 | if (index > -1) { |
86 | 85 | converters.splice(index, 1); |
87 | 86 | } |
88 | - scope.theForm.$setDirty(); | |
89 | 87 | }; |
90 | 88 | |
91 | 89 | scope.addAttribute = function(attributes) { |
... | ... | @@ -98,13 +96,9 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
98 | 96 | if (index > -1) { |
99 | 97 | attributes.splice(index, 1); |
100 | 98 | } |
101 | - scope.theForm.$setDirty(); | |
102 | 99 | }; |
103 | 100 | |
104 | 101 | |
105 | - | |
106 | - | |
107 | - | |
108 | 102 | if(scope.isAdd) { |
109 | 103 | scope.converterConfigs = scope.config.converterConfigurations; |
110 | 104 | scope.addConverterConfig(); |
... | ... | @@ -112,28 +106,6 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
112 | 106 | scope.converterConfigs = scope.config.converterConfigurations; |
113 | 107 | } |
114 | 108 | |
115 | - | |
116 | - | |
117 | - scope.updateValidity = function() { | |
118 | - let valid = scope.converterConfigs && scope.converterConfigs.length > 0; | |
119 | - scope.theForm.$setValidity('converterConfigs', valid); | |
120 | - if(scope.converterConfigs.length) { | |
121 | - for(let i=0; i<scope.converterConfigs.length; i++) { | |
122 | - if(!scope.converterConfigs[i].converters.length) { | |
123 | - scope.theForm.$setValidity('converters', false); | |
124 | - break; | |
125 | - } else { | |
126 | - scope.theForm.$setValidity('converters', true); | |
127 | - } | |
128 | - } | |
129 | - } | |
130 | - }; | |
131 | - | |
132 | - scope.$watch('converterConfigs', function() { | |
133 | - scope.updateValidity(); | |
134 | - }, true); | |
135 | - | |
136 | - | |
137 | 109 | scope.transformerTypeChange = function(attribute) { |
138 | 110 | attribute.transformer = ""; |
139 | 111 | }; | ... | ... |
... | ... | @@ -23,14 +23,11 @@ |
23 | 23 | </md-card-title> |
24 | 24 | <md-card-content> |
25 | 25 | <v-accordion id="http-converter-configs-accordion" class="vAccordion--default"> |
26 | - <v-pane id="http-converters-pane" expanded="isAdd"> | |
26 | + <v-pane id="http-converters-pane" expanded="true"> | |
27 | 27 | <v-pane-header> |
28 | 28 | {{ 'extension.converter-configurations' | translate }} |
29 | 29 | </v-pane-header> |
30 | 30 | <v-pane-content> |
31 | - <div ng-if="converterConfigs.length === 0"> | |
32 | - <span translate layout-align="center center" class="tb-prompt">extension.add-config-prompt</span> | |
33 | - </div> | |
34 | 31 | <div ng-if="converterConfigs.length > 0"> |
35 | 32 | <ol class="list-group"> |
36 | 33 | <li class="list-group-item" ng-repeat="(configIndex, config) in converterConfigs"> |
... | ... | @@ -47,11 +44,11 @@ |
47 | 44 | <md-card> |
48 | 45 | <md-card-content> |
49 | 46 | |
50 | - <md-input-container class="md-block"> | |
47 | + <md-input-container class="md-block" md-is-error="theForm['httpConverterId_' + configIndex].$touched && theForm['httpConverterId_' + configIndex].$invalid"> | |
51 | 48 | <label translate>extension.converter-id</label> |
52 | 49 | <input required name="httpConverterId_{{configIndex}}" ng-model="config.converterId"> |
53 | 50 | <div ng-messages="theForm['httpConverterId_' + configIndex].$error"> |
54 | - <div translate ng-message="required">extension.converter-id-required</div> | |
51 | + <div translate ng-message="required">extension.field-required</div> | |
55 | 52 | </div> |
56 | 53 | </md-input-container> |
57 | 54 | <md-input-container class="md-block"> |
... | ... | @@ -59,14 +56,11 @@ |
59 | 56 | <input name="httpToken" ng-model="config.token" parse-to-null> |
60 | 57 | </md-input-container> |
61 | 58 | <v-accordion id="http-converters-accordion" class="vAccordion--default"> |
62 | - <v-pane id="http-converters-pane"> | |
59 | + <v-pane id="http-converters-pane" expanded="true"> | |
63 | 60 | <v-pane-header> |
64 | 61 | {{ 'extension.converters' | translate }} |
65 | 62 | </v-pane-header> |
66 | 63 | <v-pane-content> |
67 | - <div ng-if="config.converters.length === 0"> | |
68 | - <span translate layout-align="center center" class="tb-prompt">extension.add-converter-prompt</span> | |
69 | - </div> | |
70 | 64 | <div ng-if="config.converters.length > 0"> |
71 | 65 | <ol class="list-group"> |
72 | 66 | <li class="list-group-item" |
... | ... | @@ -84,18 +78,18 @@ |
84 | 78 | </md-button> |
85 | 79 | <md-card> |
86 | 80 | <md-card-content> |
87 | - <md-input-container class="md-block"> | |
81 | + <md-input-container class="md-block" md-is-error="theForm['httpDeviceNameExp_' + configIndex + converterIndex].$touched && theForm['httpDeviceNameExp_' + configIndex + converterIndex].$invalid"> | |
88 | 82 | <label translate>extension.device-name-expression</label> |
89 | 83 | <input required name="httpDeviceNameExp_{{configIndex}}{{converterIndex}}" ng-model="converter.deviceNameJsonExpression"> |
90 | 84 | <div ng-messages="theForm['httpDeviceNameExp_' + configIndex + converterIndex].$error"> |
91 | - <div translate ng-message="required">extension.device-name-expression-required</div> | |
85 | + <div translate ng-message="required">extension.field-required</div> | |
92 | 86 | </div> |
93 | 87 | </md-input-container> |
94 | - <md-input-container class="md-block"> | |
88 | + <md-input-container class="md-block" md-is-error="theForm['httpDeviceTypeExp_' + configIndex + converterIndex].$touched && theForm['httpDeviceTypeExp_' + configIndex + converterIndex].$invalid"> | |
95 | 89 | <label translate>extension.device-type-expression</label> |
96 | 90 | <input required name="httpDeviceTypeExp_{{configIndex}}{{converterIndex}}" ng-model="converter.deviceTypeJsonExpression"> |
97 | 91 | <div ng-messages="theForm['httpDeviceTypeExp_' + configIndex + converterIndex].$error"> |
98 | - <div translate ng-message="required">extension.device-type-expression-required</div> | |
92 | + <div translate ng-message="required">extension.field-required</div> | |
99 | 93 | </div> |
100 | 94 | </md-input-container> |
101 | 95 | |
... | ... | @@ -117,14 +111,14 @@ |
117 | 111 | <md-card> |
118 | 112 | <md-card-content> |
119 | 113 | <section flex layout="row"> |
120 | - <md-input-container flex="60" class="md-block"> | |
114 | + <md-input-container flex="60" class="md-block" md-is-error="theForm['httpAttributeKey_' + configIndex + converterIndex + attributeIndex].$touched && theForm['httpAttributeKey_' + configIndex + converterIndex + attributeIndex].$invalid"> | |
121 | 115 | <label translate>extension.key</label> |
122 | 116 | <input required name="httpAttributeKey_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.key"> |
123 | 117 | <div ng-messages="theForm['httpAttributeKey_' + configIndex + converterIndex + attributeIndex].$error"> |
124 | - <div translate ng-message="required">extension.required-key</div> | |
118 | + <div translate ng-message="required">extension.field-required</div> | |
125 | 119 | </div> |
126 | 120 | </md-input-container> |
127 | - <md-input-container flex="40" class="md-block"> | |
121 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['httpAttributeType_' + configIndex + converterIndex + attributeIndex].$touched && theForm['httpAttributeType_' + configIndex + converterIndex + attributeIndex].$invalid"> | |
128 | 122 | <label translate>extension.type</label> |
129 | 123 | <md-select required name="httpAttributeType_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.type"> |
130 | 124 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> |
... | ... | @@ -132,16 +126,16 @@ |
132 | 126 | </md-option> |
133 | 127 | </md-select> |
134 | 128 | <div ng-messages="theForm['httpAttributeType_' + configIndex + converterIndex + attributeIndex].$error"> |
135 | - <div translate ng-message="required">extension.required-type</div> | |
129 | + <div translate ng-message="required">extension.field-required</div> | |
136 | 130 | </div> |
137 | 131 | </md-input-container> |
138 | 132 | </section> |
139 | 133 | <section flex layout="row"> |
140 | - <md-input-container flex="60" class="md-block"> | |
134 | + <md-input-container flex="60" class="md-block" md-is-error="theForm['httpAttributeValue_' + configIndex + converterIndex + attributeIndex].$touched && theForm['httpAttributeValue_' + configIndex + converterIndex + attributeIndex].$invalid"> | |
141 | 135 | <label translate>extension.value</label> |
142 | 136 | <input required name="httpAttributeValue_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.value"> |
143 | 137 | <div ng-messages="theForm['httpAttributeValue_' + configIndex + converterIndex + attributeIndex].$error"> |
144 | - <div translate ng-message="required">extension.required-value</div> | |
138 | + <div translate ng-message="required">extension.field-required</div> | |
145 | 139 | </div> |
146 | 140 | </md-input-container> |
147 | 141 | |
... | ... | @@ -182,11 +176,8 @@ |
182 | 176 | <div flex layout="row" layout-align="start center"> |
183 | 177 | <md-button class="md-primary md-raised" |
184 | 178 | ng-click="addAttribute(converter.attributes)" aria-label="{{ 'action.add' | translate }}"> |
185 | - <md-tooltip md-direction="top"> | |
186 | - {{ 'extension.add-attribute' | translate }} | |
187 | - </md-tooltip> | |
188 | 179 | <md-icon class="material-icons">add</md-icon> |
189 | - <span translate>action.add</span> | |
180 | + <span translate>extension.add-attribute</span> | |
190 | 181 | </md-button> |
191 | 182 | </div> |
192 | 183 | </v-pane-content> |
... | ... | @@ -212,14 +203,14 @@ |
212 | 203 | <md-card> |
213 | 204 | <md-card-content> |
214 | 205 | <section flex layout="row"> |
215 | - <md-input-container flex="60" class="md-block"> | |
206 | + <md-input-container flex="60" class="md-block" md-is-error="theForm['httpTimeseriesKey_' + configIndex + converterIndex + timeseriesIndex].$touched && theForm['httpTimeseriesKey_' + configIndex + converterIndex + timeseriesIndex].$invalid"> | |
216 | 207 | <label translate>extension.key</label> |
217 | 208 | <input required name="httpTimeseriesKey_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.key"> |
218 | 209 | <div ng-messages="theForm['httpTimeseriesKey_' + configIndex + converterIndex + timeseriesIndex].$error"> |
219 | - <div translate ng-message="required">extension.required-key</div> | |
210 | + <div translate ng-message="required">extension.field-required</div> | |
220 | 211 | </div> |
221 | 212 | </md-input-container> |
222 | - <md-input-container flex="40" class="md-block"> | |
213 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['httpTimeseriesType_' + configIndex + converterIndex + timeseriesIndex].$touched && theForm['httpTimeseriesType_' + configIndex + converterIndex + timeseriesIndex].$invalid"> | |
223 | 214 | <label translate>extension.type</label> |
224 | 215 | <md-select required name="httpTimeseriesType_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.type"> |
225 | 216 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> |
... | ... | @@ -227,16 +218,16 @@ |
227 | 218 | </md-option> |
228 | 219 | </md-select> |
229 | 220 | <div ng-messages="theForm['httpTimeseriesType_' + configIndex + converterIndex + timeseriesIndex].$error"> |
230 | - <div translate ng-message="required">extension.required-type</div> | |
221 | + <div translate ng-message="required">extension.field-required</div> | |
231 | 222 | </div> |
232 | 223 | </md-input-container> |
233 | 224 | </section> |
234 | 225 | <section flex layout="row"> |
235 | - <md-input-container flex="60" class="md-block"> | |
226 | + <md-input-container flex="60" class="md-block" md-is-error="theForm['httpTimeseriesValue_' + configIndex + converterIndex + timeseriesIndex].$touched && theForm['httpTimeseriesValue_' + configIndex + converterIndex + timeseriesIndex].$invalid"> | |
236 | 227 | <label translate>extension.value</label> |
237 | 228 | <input required name="httpTimeseriesValue_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.value"> |
238 | 229 | <div ng-messages="theForm['httpTimeseriesValue_' + configIndex + converterIndex + timeseriesIndex].$error"> |
239 | - <div translate ng-message="required">extension.required-value</div> | |
230 | + <div translate ng-message="required">extension.field-required</div> | |
240 | 231 | </div> |
241 | 232 | </md-input-container> |
242 | 233 | |
... | ... | @@ -277,11 +268,8 @@ |
277 | 268 | <div flex layout="row" layout-align="start center"> |
278 | 269 | <md-button class="md-primary md-raised" |
279 | 270 | ng-click="addAttribute(converter.timeseries)" aria-label="{{ 'action.add' | translate }}"> |
280 | - <md-tooltip md-direction="top"> | |
281 | - {{ 'extension.add-timeseries' | translate }} | |
282 | - </md-tooltip> | |
283 | 271 | <md-icon class="material-icons">add</md-icon> |
284 | - <span translate>action.add</span> | |
272 | + <span translate>extension.add-timeseries</span> | |
285 | 273 | </md-button> |
286 | 274 | </div> |
287 | 275 | </v-pane-content> |
... | ... | @@ -295,11 +283,8 @@ |
295 | 283 | <div flex layout="row" layout-align="start center"> |
296 | 284 | <md-button class="md-primary md-raised" |
297 | 285 | ng-click="addConverter(config.converters)" aria-label="{{ 'action.add' | translate }}"> |
298 | - <md-tooltip md-direction="top"> | |
299 | - {{ 'extension.add-converter' | translate }} | |
300 | - </md-tooltip> | |
301 | 286 | <md-icon class="material-icons">add</md-icon> |
302 | - <span translate>action.add</span> | |
287 | + <span translate>extension.add-converter</span> | |
303 | 288 | </md-button> |
304 | 289 | </div> |
305 | 290 | </v-pane-content> |
... | ... | @@ -314,11 +299,8 @@ |
314 | 299 | <div flex layout="row" layout-align="start center"> |
315 | 300 | <md-button class="md-primary md-raised" |
316 | 301 | ng-click="addConverterConfig()" aria-label="{{ 'action.add' | translate }}"> |
317 | - <md-tooltip md-direction="top"> | |
318 | - {{ 'extension.add-config' | translate }} | |
319 | - </md-tooltip> | |
320 | 302 | <md-icon class="material-icons">add</md-icon> |
321 | - <span translate>action.add</span> | |
303 | + <span translate>extension.add-config</span> | |
322 | 304 | </md-button> |
323 | 305 | </div> |
324 | 306 | </v-pane-content> | ... | ... |
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.deviceNameExpressions = { | |
37 | + deviceNameJsonExpression: "extension.converter-json", | |
38 | + deviceNameTopicExpression: "extension.topic" | |
39 | + }; | |
40 | + scope.deviceTypeExpressions = { | |
41 | + deviceTypeJsonExpression: "extension.converter-json", | |
42 | + deviceTypeTopicExpression: "extension.topic" | |
43 | + }; | |
44 | + scope.attributeKeyExpressions = { | |
45 | + attributeKeyJsonExpression: "extension.converter-json", | |
46 | + attributeKeyTopicExpression: "extension.topic" | |
47 | + }; | |
48 | + scope.requestIdExpressions = { | |
49 | + requestIdJsonExpression: "extension.converter-json", | |
50 | + requestIdTopicExpression: "extension.topic" | |
51 | + } | |
52 | + | |
53 | + scope.extensionCustomConverterOptions = { | |
54 | + useWrapMode: false, | |
55 | + mode: 'json', | |
56 | + showGutter: true, | |
57 | + showPrintMargin: true, | |
58 | + theme: 'github', | |
59 | + advanced: { | |
60 | + enableSnippets: true, | |
61 | + enableBasicAutocompletion: true, | |
62 | + enableLiveAutocompletion: true | |
63 | + }, | |
64 | + onLoad: function(_ace) { | |
65 | + _ace.$blockScrolling = 1; | |
66 | + } | |
67 | + }; | |
68 | + | |
69 | + scope.updateValidity = function () { | |
70 | + if(scope.brokers.length) { | |
71 | + for(let i=0;i<scope.brokers.length;i++) { | |
72 | + if(scope.brokers[i].credentials.type == scope.types.mqttCredentialTypes.pem.value) { | |
73 | + if(!(scope.brokers[i].credentials.caCert && scope.brokers[i].credentials.privateKey && scope.brokers[i].credentials.cert)) { | |
74 | + scope.theForm.$setValidity('cert.PEM', false); | |
75 | + break; | |
76 | + } else { | |
77 | + scope.theForm.$setValidity('cert.PEM', true); | |
78 | + } | |
79 | + } | |
80 | + } | |
81 | + } | |
82 | + }; | |
83 | + | |
84 | + scope.$watch('brokers', function() { | |
85 | + scope.updateValidity(); | |
86 | + }, true); | |
87 | + | |
88 | + scope.addBroker = function() { | |
89 | + var newBroker = { | |
90 | + host: "localhost", | |
91 | + port: 1882, | |
92 | + ssl: false, | |
93 | + retryInterval: 3000, | |
94 | + credentials: {type:"anonymous"}, | |
95 | + mapping: [], | |
96 | + connectRequests: [], | |
97 | + disconnectRequests: [], | |
98 | + attributeRequests: [], | |
99 | + attributeUpdates: [], | |
100 | + serverSideRpc: [] | |
101 | + }; | |
102 | + scope.brokers.push(newBroker); | |
103 | + }; | |
104 | + | |
105 | + scope.removeBroker = function(broker) { | |
106 | + var index = scope.brokers.indexOf(broker); | |
107 | + if (index > -1) { | |
108 | + scope.brokers.splice(index, 1); | |
109 | + } | |
110 | + }; | |
111 | + | |
112 | + if(scope.isAdd) { | |
113 | + scope.brokers = []; | |
114 | + scope.config.brokers = scope.brokers; | |
115 | + scope.addBroker(); | |
116 | + } else { | |
117 | + scope.brokers = scope.config.brokers; | |
118 | + } | |
119 | + | |
120 | + scope.addMap = function(mapping) { | |
121 | + var newMap = {topicFilter:"sensors", converter:{attributes:[],timeseries:[]}}; | |
122 | + | |
123 | + mapping.push(newMap); | |
124 | + }; | |
125 | + | |
126 | + scope.removeMap = function(map, mapping) { | |
127 | + var index = mapping.indexOf(map); | |
128 | + if (index > -1) { | |
129 | + mapping.splice(index, 1); | |
130 | + } | |
131 | + }; | |
132 | + | |
133 | + scope.addAttribute = function(attributes) { | |
134 | + var newAttribute = {type:"", key:"", value:""}; | |
135 | + attributes.push(newAttribute); | |
136 | + }; | |
137 | + | |
138 | + scope.removeAttribute = function(attribute, attributes) { | |
139 | + var index = attributes.indexOf(attribute); | |
140 | + if (index > -1) { | |
141 | + attributes.splice(index, 1); | |
142 | + } | |
143 | + }; | |
144 | + | |
145 | + scope.addConnectRequest = function(requests, type) { | |
146 | + var newRequest = {}; | |
147 | + if(type == "connect") { | |
148 | + newRequest.topicFilter = "sensors/connect"; | |
149 | + } else { | |
150 | + newRequest.topicFilter = "sensors/disconnect"; | |
151 | + } | |
152 | + requests.push(newRequest); | |
153 | + }; | |
154 | + | |
155 | + scope.addAttributeRequest = function(requests) { | |
156 | + var newRequest = { | |
157 | + topicFilter: "sensors/attributes", | |
158 | + clientScope: false, | |
159 | + responseTopicExpression: "sensors/${deviceName}/attributes/${responseId}", | |
160 | + valueExpression: "${attributeValue}" | |
161 | + }; | |
162 | + requests.push(newRequest); | |
163 | + }; | |
164 | + | |
165 | + scope.addAttributeUpdate = function(updates) { | |
166 | + var newUpdate = { | |
167 | + deviceNameFilter: ".*", | |
168 | + attributeFilter: ".*", | |
169 | + topicExpression: "sensor/${deviceName}/${attributeKey}", | |
170 | + valueExpression: "{\"${attributeKey}\":\"${attributeValue}\"}" | |
171 | + } | |
172 | + updates.push(newUpdate); | |
173 | + }; | |
174 | + | |
175 | + scope.addServerSideRpc = function(rpcRequests) { | |
176 | + var newRpc = { | |
177 | + deviceNameFilter: ".*", | |
178 | + methodFilter: "echo", | |
179 | + requestTopicExpression: "sensor/${deviceName}/request/${methodName}/${requestId}", | |
180 | + responseTopicExpression: "sensor/${deviceName}/response/${methodName}/${requestId}", | |
181 | + responseTimeout: 10000, | |
182 | + valueExpression: "${params}" | |
183 | + }; | |
184 | + rpcRequests.push(newRpc); | |
185 | + }; | |
186 | + | |
187 | + scope.changeCredentials = function(broker) { | |
188 | + var type = broker.credentials.type; | |
189 | + broker.credentials = {}; | |
190 | + broker.credentials.type = type; | |
191 | + }; | |
192 | + | |
193 | + scope.changeConverterType = function(map) { | |
194 | + if(map.converterType == "custom"){ | |
195 | + map.converter = ""; | |
196 | + } | |
197 | + if(map.converterType == "json") { | |
198 | + map.converter = {attributes:[],timeseries:[]}; | |
199 | + } | |
200 | + }; | |
201 | + | |
202 | + scope.changeNameExpression = function(element, type) { | |
203 | + if(element.nameExp == "deviceNameJsonExpression") { | |
204 | + if(element.deviceNameTopicExpression) { | |
205 | + delete element.deviceNameTopicExpression; | |
206 | + } | |
207 | + if(type) { | |
208 | + element.deviceNameJsonExpression = "${$.serialNumber}"; | |
209 | + } | |
210 | + } | |
211 | + if(element.nameExp == "deviceNameTopicExpression") { | |
212 | + if(element.deviceNameJsonExpression) { | |
213 | + delete element.deviceNameJsonExpression; | |
214 | + } | |
215 | + if(type && type == "connect") { | |
216 | + element.deviceNameTopicExpression = "(?<=sensor\\/)(.*?)(?=\\/connect)"; | |
217 | + } | |
218 | + if(type && type == "disconnect") { | |
219 | + element.deviceNameTopicExpression = "(?<=sensor\\/)(.*?)(?=\\/disconnect)"; | |
220 | + } | |
221 | + if(type && type == "attribute") { | |
222 | + element.deviceNameTopicExpression = "(?<=sensors\\/)(.*?)(?=\\/attributes)"; | |
223 | + } | |
224 | + } | |
225 | + }; | |
226 | + | |
227 | + scope.changeTypeExpression = function(converter) { | |
228 | + if(converter.typeExp == "deviceTypeJsonExpression") { | |
229 | + if(converter.deviceTypeTopicExpression) { | |
230 | + delete converter.deviceTypeTopicExpression; | |
231 | + } | |
232 | + } | |
233 | + if(converter.typeExp == "deviceTypeTopicExpression") { | |
234 | + if(converter.deviceTypeJsonExpression) { | |
235 | + delete converter.deviceTypeJsonExpression; | |
236 | + } | |
237 | + } | |
238 | + }; | |
239 | + | |
240 | + scope.changeAttrKeyExpression = function(request) { | |
241 | + if(request.attrKey == "attributeKeyJsonExpression") { | |
242 | + if(request.attributeKeyTopicExpression) { | |
243 | + delete request.attributeKeyTopicExpression; | |
244 | + } | |
245 | + request.attributeKeyJsonExpression = "${$.key}"; | |
246 | + } | |
247 | + if(request.attrKey == "attributeKeyTopicExpression") { | |
248 | + if(request.attributeKeyJsonExpression) { | |
249 | + delete request.attributeKeyJsonExpression; | |
250 | + } | |
251 | + request.attributeKeyTopicExpression = "(?<=attributes\\/)(.*?)(?=\\/request)"; | |
252 | + } | |
253 | + }; | |
254 | + | |
255 | + scope.changeRequestIdExpression = function(request) { | |
256 | + if(request.requestId == "requestIdJsonExpression") { | |
257 | + if(request.requestIdTopicExpression) { | |
258 | + delete request.requestIdTopicExpression; | |
259 | + } | |
260 | + request.requestIdJsonExpression = "${$.requestId}"; | |
261 | + } | |
262 | + if(request.requestId == "requestIdTopicExpression") { | |
263 | + if(request.requestIdJsonExpression) { | |
264 | + delete request.requestIdJsonExpression; | |
265 | + } | |
266 | + request.requestIdTopicExpression = "(?<=request\\/)(.*?)($)"; | |
267 | + } | |
268 | + }; | |
269 | + | |
270 | + scope.validateCustomConverter = function(model, editorName) { | |
271 | + if(model && model.length) { | |
272 | + try { | |
273 | + angular.fromJson(model); | |
274 | + scope.theForm[editorName].$setValidity('converterJSON', true); | |
275 | + } catch(e) { | |
276 | + scope.theForm[editorName].$setValidity('converterJSON', false); | |
277 | + } | |
278 | + } | |
279 | + }; | |
280 | + | |
281 | + scope.fileAdded = function($file, broker, fileType) { | |
282 | + var reader = new FileReader(); | |
283 | + reader.onload = function(event) { | |
284 | + scope.$apply(function() { | |
285 | + if(event.target.result) { | |
286 | + scope.theForm.$setDirty(); | |
287 | + var addedFile = event.target.result; | |
288 | + if (addedFile && addedFile.length > 0) { | |
289 | + if(fileType == "caCert") { | |
290 | + broker.credentials.caCertFileName = $file.name; | |
291 | + broker.credentials.caCert = addedFile.replace(/^data.*base64,/, ""); | |
292 | + } | |
293 | + if(fileType == "privateKey") { | |
294 | + broker.credentials.privateKeyFileName = $file.name; | |
295 | + broker.credentials.privateKey = addedFile.replace(/^data.*base64,/, ""); | |
296 | + } | |
297 | + if(fileType == "Cert") { | |
298 | + broker.credentials.certFileName = $file.name; | |
299 | + broker.credentials.cert = addedFile.replace(/^data.*base64,/, ""); | |
300 | + } | |
301 | + } | |
302 | + } | |
303 | + }); | |
304 | + }; | |
305 | + reader.readAsDataURL($file.file); | |
306 | + }; | |
307 | + | |
308 | + scope.clearFile = function(broker, fileType) { | |
309 | + scope.theForm.$setDirty(); | |
310 | + if(fileType == "caCert") { | |
311 | + broker.credentials.caCertFileName = null; | |
312 | + broker.credentials.caCert = null; | |
313 | + } | |
314 | + if(fileType == "privateKey") { | |
315 | + broker.credentials.privateKeyFileName = null; | |
316 | + broker.credentials.privateKey = null; | |
317 | + } | |
318 | + if(fileType == "Cert") { | |
319 | + broker.credentials.certFileName = null; | |
320 | + broker.credentials.cert = null; | |
321 | + } | |
322 | + }; | |
323 | + | |
324 | + $compile(element.contents())(scope); | |
325 | + }; | |
326 | + | |
327 | + return { | |
328 | + restrict: "A", | |
329 | + link: linker, | |
330 | + scope: { | |
331 | + config: "=", | |
332 | + isAdd: "=" | |
333 | + } | |
334 | + } | |
335 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -15,4 +15,846 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<div>MQTT</div> | |
\ No newline at end of file | ||
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="true"> | |
27 | + <v-pane-header> | |
28 | + {{ 'extension.brokers' | translate }} | |
29 | + </v-pane-header> | |
30 | + <v-pane-content> | |
31 | + <div ng-if="brokers.length > 0"> | |
32 | + <ol class="list-group"> | |
33 | + <li class="list-group-item" ng-repeat="(brokerIndex,broker) in brokers"> | |
34 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeBroker(broker)" ng-hide="brokers.length < 2"> | |
35 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | |
36 | + <md-tooltip md-direction="top"> | |
37 | + {{ 'action.remove' | translate }} | |
38 | + </md-tooltip> | |
39 | + </md-button> | |
40 | + <md-card> | |
41 | + <md-card-content> | |
42 | + <section flex layout="row"> | |
43 | + <md-input-container flex="40" class="md-block"> | |
44 | + <label translate>extension.port</label> | |
45 | + <input required type="number" min="1" max="65535" name="mqttPort_{{brokerIndex}}" ng-model="broker.port"> | |
46 | + <div ng-messages="theForm['mqttPort_' + brokerIndex].$error"> | |
47 | + <div translate ng-message="required">extension.field-required</div> | |
48 | + <div translate ng-message="min">extension.port-range</div> | |
49 | + <div translate ng-message="max">extension.port-range</div> | |
50 | + </div> | |
51 | + </md-input-container> | |
52 | + <md-input-container flex="60" class="md-block"> | |
53 | + <label translate>extension.host</label> | |
54 | + <input required name="mqttHost_{{brokerIndex}}" ng-model="broker.host"> | |
55 | + <div ng-messages="theForm['mqttHost_' + brokerIndex].$error"> | |
56 | + <div translate ng-message="required">extension.field-required</div> | |
57 | + </div> | |
58 | + </md-input-container> | |
59 | + </section> | |
60 | + <section flex layout="row"> | |
61 | + <md-input-container flex="40" class="md-block"> | |
62 | + <label translate>extension.retry-interval</label> | |
63 | + <input required type="number" name="mqttRetryInterval_{{brokerIndex}}" ng-model="broker.retryInterval"> | |
64 | + <div ng-messages="theForm['mqttRetryInterval_' + brokerIndex].$error"> | |
65 | + <div translate ng-message="required">extension.field-required</div> | |
66 | + </div> | |
67 | + </md-input-container> | |
68 | + <md-input-container flex="50" class="md-block"> | |
69 | + <label translate>extension.credentials</label> | |
70 | + <md-select required name="mqttCredentials_{{brokerIndex}}" ng-model="broker.credentials.type" ng-change="changeCredentials(broker)"> | |
71 | + <md-option ng-repeat="(credentialsType, credentialsValue) in types.mqttCredentialTypes" ng-value="credentialsValue.value"> | |
72 | + {{credentialsValue.name | translate}} | |
73 | + </md-option> | |
74 | + </md-select> | |
75 | + </md-input-container> | |
76 | + <md-input-container flex="10" class="md-block"> | |
77 | + <md-checkbox flex aria-label="{{ 'extension.ssl' | translate }}" | |
78 | + ng-model="broker.ssl">{{ 'extension.ssl' | translate }} | |
79 | + </md-checkbox> | |
80 | + </md-input-container> | |
81 | + </section> | |
82 | + <section flex layout="row" ng-if='broker.credentials.type == "basic"'> | |
83 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['mqttUsername_' + brokerIndex].$touched && theForm['mqttUsername_' + brokerIndex].$invalid"> | |
84 | + <label translate>extension.username</label> | |
85 | + <input required name="mqttUsername_{{brokerIndex}}" ng-model="broker.credentials.username"> | |
86 | + <div ng-messages="theForm['mqttUsername_' + brokerIndex].$error"> | |
87 | + <div translate ng-message="required">extension.field-required</div> | |
88 | + </div> | |
89 | + </md-input-container> | |
90 | + <md-input-container flex="60" class="md-block" md-is-error="theForm['mqttPassword_' + brokerIndex].$touched && theForm['mqttPassword_' + brokerIndex].$invalid"> | |
91 | + <label translate>extension.password</label> | |
92 | + <input required name="mqttPassword_{{brokerIndex}}" ng-model="broker.credentials.password"> | |
93 | + <div ng-messages="theForm['mqttPassword_' + brokerIndex].$error"> | |
94 | + <div translate ng-message="required">extension.field-required</div> | |
95 | + </div> | |
96 | + </md-input-container> | |
97 | + </section> | |
98 | + <section flex layout="column" ng-if='broker.credentials.type == "cert.PEM"' class="dropdown-section"> | |
99 | + <div class="tb-container" ng-class="broker.credentials.caCertFileName ? 'ng-valid' : 'ng-invalid'"> | |
100 | + <label class="tb-label" translate>extension.ca-cert</label> | |
101 | + <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "caCert")' class="tb-file-select-container"> | |
102 | + <div class="tb-file-clear-container"> | |
103 | + <md-button ng-click='clearFile(broker, "caCert")' class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ 'action.remove' | translate }}"> | |
104 | + <md-tooltip md-direction="top"> | |
105 | + {{ 'action.remove' | translate }} | |
106 | + </md-tooltip> | |
107 | + <md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons">close</md-icon> | |
108 | + </md-button> | |
109 | + </div> | |
110 | + <div class="alert tb-flow-drop" flow-drop> | |
111 | + <label for="caCertSelect_{{brokerIndex}}" translate>extension.drop-file</label> | |
112 | + <input class="file-input" flow-btn flow-attrs="{accept:'.pem'}" id="caCertSelect_{{brokerIndex}}"> | |
113 | + </div> | |
114 | + </div> | |
115 | + </div> | |
116 | + <div class="dropdown-messages"> | |
117 | + <div ng-if="!broker.credentials.caCertFileName" class="tb-error-message" translate>extension.no-file</div> | |
118 | + <div ng-if="broker.credentials.caCertFileName">{{broker.credentials.caCertFileName}}</div> | |
119 | + </div> | |
120 | + <div class="tb-container" ng-class="broker.credentials.privateKeyFileName ? 'ng-valid' : 'ng-invalid'"> | |
121 | + <label class="tb-label" translate>extension.private-key</label> | |
122 | + <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "privateKey")' class="tb-file-select-container"> | |
123 | + <div class="tb-file-clear-container"> | |
124 | + <md-button ng-click='clearFile(broker, "privateKey")' class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ 'action.remove' | translate }}"> | |
125 | + <md-tooltip md-direction="top"> | |
126 | + {{ 'action.remove' | translate }} | |
127 | + </md-tooltip> | |
128 | + <md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons">close</md-icon> | |
129 | + </md-button> | |
130 | + </div> | |
131 | + <div class="alert tb-flow-drop" flow-drop> | |
132 | + <label for="privateKeySelect_{{brokerIndex}}" translate>extension.drop-file</label> | |
133 | + <input class="file-input" flow-btn flow-attrs="{accept:'.pem'}" id="privateKeySelect_{{brokerIndex}}"> | |
134 | + </div> | |
135 | + </div> | |
136 | + </div> | |
137 | + <div class="dropdown-messages"> | |
138 | + <div ng-if="!broker.credentials.privateKeyFileName" class="tb-error-message" translate>extension.no-file</div> | |
139 | + <div ng-if="broker.credentials.privateKeyFileName">{{broker.credentials.privateKeyFileName}}</div> | |
140 | + </div> | |
141 | + <div class="tb-container" ng-class="broker.credentials.certFileName ? 'ng-valid' : 'ng-invalid'"> | |
142 | + <label class="tb-label" translate>extension.cert</label> | |
143 | + <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "Cert")' class="tb-file-select-container"> | |
144 | + <div class="tb-file-clear-container"> | |
145 | + <md-button ng-click='clearFile(broker, "Cert")' class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ 'action.remove' | translate }}"> | |
146 | + <md-tooltip md-direction="top"> | |
147 | + {{ 'action.remove' | translate }} | |
148 | + </md-tooltip> | |
149 | + <md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons">close</md-icon> | |
150 | + </md-button> | |
151 | + </div> | |
152 | + <div class="alert tb-flow-drop" flow-drop> | |
153 | + <label for="CertSelect_{{brokerIndex}}" translate>extension.drop-file</label> | |
154 | + <input class="file-input" flow-btn flow-attrs="{accept:'.pem'}" id="CertSelect_{{brokerIndex}}"> | |
155 | + </div> | |
156 | + </div> | |
157 | + </div> | |
158 | + <div class="dropdown-messages"> | |
159 | + <div ng-if="!broker.credentials.certFileName" class="tb-error-message" translate>extension.no-file</div> | |
160 | + <div ng-if="broker.credentials.certFileName">{{broker.credentials.certFileName}}</div> | |
161 | + </div> | |
162 | + </section> | |
163 | + | |
164 | + <v-accordion id="mqtt-mapping-accordion" class="vAccordion--default"> | |
165 | + <v-pane id="mqtt-mapping-pane"> | |
166 | + <v-pane-header> | |
167 | + {{ 'extension.mapping' | translate }} | |
168 | + </v-pane-header> | |
169 | + <v-pane-content> | |
170 | + <div ng-if="broker.mapping.length > 0"> | |
171 | + <ol class="list-group"> | |
172 | + <li class="list-group-item" ng-repeat="(mapIndex,map) in broker.mapping"> | |
173 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeMap(map, broker.mapping)"> | |
174 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | |
175 | + <md-tooltip md-direction="top"> | |
176 | + {{ 'action.remove' | translate }} | |
177 | + </md-tooltip> | |
178 | + </md-button> | |
179 | + <md-card> | |
180 | + <md-card-content> | |
181 | + <section flex layout="row"> | |
182 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['mqttConverterType_' + brokerIndex + mapIndex].$touched && theForm['mqttConverterType_' + brokerIndex + mapIndex].$invalid"> | |
183 | + <label translate>extension.converter-type</label> | |
184 | + <md-select required name="mqttConverterType_{{brokerIndex}}{{mapIndex}}" ng-model="map.converterType" ng-change="changeConverterType(map)"> | |
185 | + <md-option ng-repeat="(converterType, value) in types.mqttConverterTypes" ng-value="converterType"> | |
186 | + {{value | translate}} | |
187 | + </md-option> | |
188 | + </md-select> | |
189 | + <div ng-messages="theForm['mqttConverterType_' + brokerIndex + mapIndex].$error"> | |
190 | + <div translate ng-message="required">extension.field-required</div> | |
191 | + </div> | |
192 | + </md-input-container> | |
193 | + <md-input-container flex="60" class="md-block"> | |
194 | + <label translate>extension.topic-filter</label> | |
195 | + <input required name="mqttTopicFilter_{{brokerIndex}}{{mapIndex}}" ng-model="map.topicFilter"> | |
196 | + <div ng-messages="theForm['mqttTopicFilter_' + brokerIndex + mapIndex].$error"> | |
197 | + <div translate ng-message="required">extension.field-required</div> | |
198 | + </div> | |
199 | + </md-input-container> | |
200 | + </section> | |
201 | + | |
202 | + <div ng-if='map.converterType =="json"' ng-init="map.converter.type = 'json'"> | |
203 | + <section flex layout="row"> | |
204 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['mqttDeviceNameExpression_' + brokerIndex + mapIndex].$touched && theForm['mqttDeviceNameExpression_' + brokerIndex + mapIndex].$invalid"> | |
205 | + <label translate>extension.device-name-expression</label> | |
206 | + <md-select required name="mqttDeviceNameExpression_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.nameExp" ng-change="changeNameExpression(map.converter)"> | |
207 | + <md-option ng-repeat="(key, value) in deviceNameExpressions" ng-value='key'> | |
208 | + {{value | translate}} | |
209 | + </md-option> | |
210 | + </md-select> | |
211 | + <div ng-messages="theForm['mqttDeviceNameExpression_' + brokerIndex + mapIndex].$error"> | |
212 | + <div translate ng-message="required">extension.field-required</div> | |
213 | + </div> | |
214 | + </md-input-container> | |
215 | + <md-input-container ng-if="map.converter.nameExp == 'deviceNameJsonExpression'" flex="60" class="md-block" md-is-error="theForm['mqttJsonNameExp_' + brokerIndex + mapIndex].$touched && theForm['mqttJsonNameExp_' + brokerIndex + mapIndex].$invalid"> | |
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.field-required</div> | |
220 | + </div> | |
221 | + </md-input-container> | |
222 | + <md-input-container ng-if="map.converter.nameExp == 'deviceNameTopicExpression'" flex="60" class="md-block" md-is-error="theForm['mqttTopicNameExp_' + brokerIndex + mapIndex].$touched && theForm['mqttTopicNameExp_' + brokerIndex + mapIndex].$invalid"> | |
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.field-required</div> | |
227 | + </div> | |
228 | + </md-input-container> | |
229 | + </section> | |
230 | + <section flex layout="row"> | |
231 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['mqttDeviceTypeExpression_' + brokerIndex + mapIndex].$touched && theForm['mqttDeviceTypeExpression_' + brokerIndex + mapIndex].$invalid"> | |
232 | + <label translate>extension.device-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 deviceTypeExpressions" ng-value='key'> | |
235 | + {{value | translate}} | |
236 | + </md-option> | |
237 | + </md-select> | |
238 | + <div ng-messages="theForm['mqttDeviceTypeExpression_' + brokerIndex + mapIndex].$error"> | |
239 | + <div translate ng-message="required">extension.field-required</div> | |
240 | + </div> | |
241 | + </md-input-container> | |
242 | + <md-input-container ng-if="map.converter.typeExp == 'deviceTypeJsonExpression'" flex="60" class="md-block" md-is-error="theForm['mqttJsonTypeExp_' + brokerIndex + mapIndex].$touched && theForm['mqttJsonTypeExp_' + brokerIndex + mapIndex].$invalid"> | |
243 | + <label translate>extension.json-type-expression</label> | |
244 | + <input required name="mqttJsonTypeExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceTypeJsonExpression"> | |
245 | + <div ng-messages="theForm['mqttJsonTypeExp_' + brokerIndex + mapIndex].$error"> | |
246 | + <div translate ng-message="required">extension.field-required</div> | |
247 | + </div> | |
248 | + </md-input-container> | |
249 | + <md-input-container ng-if="map.converter.typeExp == 'deviceTypeTopicExpression'" flex="60" class="md-block" md-is-error="theForm['mqttTopicTypeExp_' + brokerIndex + mapIndex].$touched && theForm['mqttTopicTypeExp_' + brokerIndex + mapIndex].$invalid"> | |
250 | + <label translate>extension.topic-type-expression</label> | |
251 | + <input required name="mqttTopicTypeExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceTypeTopicExpression"> | |
252 | + <div ng-messages="theForm['mqttTopicTypeExp_' + brokerIndex + mapIndex].$error"> | |
253 | + <div translate ng-message="required">extension.field-required</div> | |
254 | + </div> | |
255 | + </md-input-container> | |
256 | + </section> | |
257 | + <section flex layout="row"> | |
258 | + <md-input-container flex="40" class="md-block"> | |
259 | + <label translate>extension.timeout</label> | |
260 | + <input type="number" name="mqttTimeout_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.timeout" parse-to-null> | |
261 | + </md-input-container> | |
262 | + <md-input-container flex="60" class="md-block" md-is-error="theForm['mqttFilterExpression' + brokerIndex + mapIndex].$touched && theForm['mqttFilterExpression' + brokerIndex + mapIndex].$invalid"> | |
263 | + <label translate>extension.filter-expression</label> | |
264 | + <input required name="mqttFilterExpression{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.filterExpression"> | |
265 | + <div ng-messages="theForm['mqttFilterExpression' + brokerIndex + mapIndex].$error"> | |
266 | + <div translate ng-message="required">extension.field-required</div> | |
267 | + </div> | |
268 | + </md-input-container> | |
269 | + </section> | |
270 | + </div> | |
271 | + | |
272 | + <div ng-if='map.converterType == "custom"'> | |
273 | + <div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>extension.transformer-json</div> | |
274 | + <div flex class="tb-extension-custom-transformer-panel"> | |
275 | + <div flex class="tb-extension-custom-transformer" | |
276 | + ui-ace="extensionCustomConverterOptions" | |
277 | + ng-model="map.converter" | |
278 | + name="mqttCustomConverter_{{brokerIndex}}{{mapIndex}}" | |
279 | + ng-change='validateCustomConverter(map.converter, "mqttCustomConverter_" + brokerIndex + mapIndex)' | |
280 | + required> | |
281 | + </div> | |
282 | + </div> | |
283 | + <div class="tb-error-messages" ng-messages="theForm['mqttCustomConverter_' + brokerIndex + mapIndex].$error" role="alert"> | |
284 | + <div ng-message="required" class="tb-error-message" translate>extension.converter-json-required</div> | |
285 | + <div ng-message="converterJSON" class="tb-error-message" translate>extension.converter-json-parse</div> | |
286 | + </div> | |
287 | + </div> | |
288 | + | |
289 | + <v-accordion ng-if='map.converterType =="json"' id="mqtt-attributes-accordion" class="vAccordion--default"> | |
290 | + <v-pane id="mqtt-attributes-pane"> | |
291 | + <v-pane-header> | |
292 | + {{ 'extension.attributes' | translate }} | |
293 | + </v-pane-header> | |
294 | + <v-pane-content> | |
295 | + <div ng-if="map.converter.attributes.length > 0"> | |
296 | + <ol class="list-group"> | |
297 | + <li class="list-group-item" ng-repeat="(attributeIndex, attribute) in map.converter.attributes"> | |
298 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeAttribute(attribute, map.converter.attributes)"> | |
299 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | |
300 | + <md-tooltip md-direction="top"> | |
301 | + {{ 'action.remove' | translate }} | |
302 | + </md-tooltip> | |
303 | + </md-button> | |
304 | + <md-card> | |
305 | + <md-card-content> | |
306 | + <section flex layout="row"> | |
307 | + <md-input-container flex="60" class="md-block" md-is-error="theForm['mqttAttributeKey_' + brokerIndex + mapIndex + attributeIndex].$touched && theForm['mqttAttributeKey_' + brokerIndex + mapIndex + attributeIndex].$invalid"> | |
308 | + <label translate>extension.key</label> | |
309 | + <input required name="mqttAttributeKey_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.key"> | |
310 | + <div ng-messages="theForm['mqttAttributeKey_' + brokerIndex + mapIndex + attributeIndex].$error"> | |
311 | + <div translate ng-message="required">extension.field-required</div> | |
312 | + </div> | |
313 | + </md-input-container> | |
314 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['mqttAttributeType_' + brokerIndex + mapIndex + attributeIndex].$touched && theForm['mqttAttributeType_' + brokerIndex + mapIndex + attributeIndex].$invalid"> | |
315 | + <label translate>extension.type</label> | |
316 | + <md-select required name="mqttAttributeType_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.type"> | |
317 | + <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> | |
318 | + {{attrTypeValue | translate}} | |
319 | + </md-option> | |
320 | + </md-select> | |
321 | + <div ng-messages="theForm['mqttAttributeType_' + brokerIndex + mapIndex + attributeIndex].$error"> | |
322 | + <div translate ng-message="required">extension.field-required</div> | |
323 | + </div> | |
324 | + </md-input-container> | |
325 | + </section> | |
326 | + <md-input-container class="md-block" md-is-error="theForm['mqttAttributeValue_' + brokerIndex + mapIndex + attributeIndex].$touched && theForm['mqttAttributeValue_' + brokerIndex + mapIndex + attributeIndex].$invalid"> | |
327 | + <label translate>extension.value</label> | |
328 | + <input required name="mqttAttributeValue_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.value"> | |
329 | + <div ng-messages="theForm['mqttAttributeValue_' + brokerIndex + mapIndex + attributeIndex].$error"> | |
330 | + <div translate ng-message="required">extension.field-required</div> | |
331 | + </div> | |
332 | + </md-input-container> | |
333 | + </md-card-content> | |
334 | + </md-card> | |
335 | + </li> | |
336 | + </ol> | |
337 | + </div> | |
338 | + <div flex layout="row" layout-align="start center"> | |
339 | + <md-button class="md-primary md-raised" | |
340 | + ng-click="addAttribute(map.converter.attributes)" aria-label="{{ 'action.add' | translate }}"> | |
341 | + <md-icon class="material-icons">add</md-icon> | |
342 | + <span translate>extension.add-attribute</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" md-is-error="theForm['mqttTimeseriesKey_' + brokerIndex + mapIndex + timeseriesIndex].$touched && theForm['mqttTimeseriesKey_' + brokerIndex + mapIndex + timeseriesIndex].$invalid"> | |
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.field-required</div> | |
372 | + </div> | |
373 | + </md-input-container> | |
374 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['mqttTimeseriesType_' + brokerIndex + mapIndex + timeseriesIndex].$touched && theForm['mqttTimeseriesType_' + brokerIndex + mapIndex + timeseriesIndex].$invalid"> | |
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.field-required</div> | |
383 | + </div> | |
384 | + </md-input-container> | |
385 | + </section> | |
386 | + <md-input-container class="md-block" md-is-error="theForm['mqttTimeseriesValue_' + brokerIndex + mapIndex + timeseriesIndex].$touched && theForm['mqttTimeseriesValue_' + brokerIndex + mapIndex + timeseriesIndex].$invalid"> | |
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.field-required</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-icon class="material-icons">add</md-icon> | |
402 | + <span translate>extension.add-timeseries</span> | |
403 | + </md-button> | |
404 | + </div> | |
405 | + </v-pane-content> | |
406 | + </v-pane> | |
407 | + </v-accordion> | |
408 | + | |
409 | + </md-card-content> | |
410 | + </md-card> | |
411 | + </li> | |
412 | + </ol> | |
413 | + </div> | |
414 | + <div flex layout="row" layout-align="start center"> | |
415 | + <md-button class="md-primary md-raised" | |
416 | + ng-click="addMap(broker.mapping)" aria-label="{{ 'action.add' | translate }}"> | |
417 | + <md-icon class="material-icons">add</md-icon> | |
418 | + <span translate>extension.add-map</span> | |
419 | + </md-button> | |
420 | + </div> | |
421 | + </v-pane-content> | |
422 | + </v-pane> | |
423 | + </v-accordion> | |
424 | + | |
425 | + <v-accordion id="mqtt-connect-requests-accordion" class="vAccordion--default"> | |
426 | + <v-pane id="mqtt-connect-requests-pane"> | |
427 | + <v-pane-header> | |
428 | + {{ 'extension.connect-requests' | translate }} | |
429 | + </v-pane-header> | |
430 | + <v-pane-content> | |
431 | + <div ng-if="broker.connectRequests.length > 0"> | |
432 | + <ol class="list-group"> | |
433 | + <li class="list-group-item" ng-repeat="(connectRequestIndex, connectRequest) in broker.connectRequests"> | |
434 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeAttribute(connectRequest, broker.connectRequests)"> | |
435 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | |
436 | + <md-tooltip md-direction="top"> | |
437 | + {{ 'action.remove' | translate }} | |
438 | + </md-tooltip> | |
439 | + </md-button> | |
440 | + <md-card> | |
441 | + <md-card-content> | |
442 | + <md-input-container class="md-block"> | |
443 | + <label translate>extension.topic-filter</label> | |
444 | + <input required name="conRequestTopicFilter_{{brokerIndex}}{{connectRequestIndex}}" ng-model="connectRequest.topicFilter"> | |
445 | + <div ng-messages="theForm['conRequestTopicFilter_' + brokerIndex + connectRequestIndex].$error"> | |
446 | + <div translate ng-message="required">extension.field-required</div> | |
447 | + </div> | |
448 | + </md-input-container> | |
449 | + <section flex layout="row"> | |
450 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['connectDeviceNameExpression_' + brokerIndex + connectRequestIndex].$touched && theForm['connectDeviceNameExpression_' + brokerIndex + connectRequestIndex].$invalid"> | |
451 | + <label translate>extension.device-name-expression</label> | |
452 | + <md-select required name="connectDeviceNameExpression_{{brokerIndex}}{{connectRequestIndex}}" ng-model="connectRequest.nameExp" ng-change="changeNameExpression(connectRequest, 'connect')"> | |
453 | + <md-option ng-repeat="(key, value) in deviceNameExpressions" ng-value='key'> | |
454 | + {{value | translate}} | |
455 | + </md-option> | |
456 | + </md-select> | |
457 | + <div ng-messages="theForm['connectDeviceNameExpression_' + brokerIndex + connectRequestIndex].$error"> | |
458 | + <div translate ng-message="required">extension.field-required</div> | |
459 | + </div> | |
460 | + </md-input-container> | |
461 | + <md-input-container ng-if="connectRequest.nameExp == 'deviceNameJsonExpression'" flex="60" class="md-block"> | |
462 | + <label translate>extension.json-name-expression</label> | |
463 | + <input required name="connectJsonNameExp_{{brokerIndex}}{{connectRequestIndex}}" ng-model="connectRequest.deviceNameJsonExpression"> | |
464 | + <div ng-messages="theForm['connectJsonNameExp_' + brokerIndex + connectRequestIndex].$error"> | |
465 | + <div translate ng-message="required">extension.field-required</div> | |
466 | + </div> | |
467 | + </md-input-container> | |
468 | + <md-input-container ng-if="connectRequest.nameExp == 'deviceNameTopicExpression'" flex="60" class="md-block"> | |
469 | + <label translate>extension.topic-name-expression</label> | |
470 | + <input required name="connectTopicNameExp_{{brokerIndex}}{{connectRequestIndex}}" ng-model="connectRequest.deviceNameTopicExpression"> | |
471 | + <div ng-messages="theForm['connectTopicNameExp_' + brokerIndex + connectRequestIndex].$error"> | |
472 | + <div translate ng-message="required">extension.field-required</div> | |
473 | + </div> | |
474 | + </md-input-container> | |
475 | + </section> | |
476 | + </md-card-content> | |
477 | + </md-card> | |
478 | + </li> | |
479 | + </ol> | |
480 | + </div> | |
481 | + <div flex layout="row" layout-align="start center"> | |
482 | + <md-button class="md-primary md-raised" | |
483 | + ng-click='addConnectRequest(broker.connectRequests, "connect")' aria-label="{{ 'action.add' | translate }}"> | |
484 | + <md-icon class="material-icons">add</md-icon> | |
485 | + <span translate>extension.add-connect-request</span> | |
486 | + </md-button> | |
487 | + </div> | |
488 | + </v-pane-content> | |
489 | + </v-pane> | |
490 | + </v-accordion> | |
491 | + | |
492 | + <v-accordion id="mqtt-disconnect-requests-accordion" class="vAccordion--default"> | |
493 | + <v-pane id="mqtt-disconnect-requests-pane"> | |
494 | + <v-pane-header> | |
495 | + {{ 'extension.disconnect-requests' | translate }} | |
496 | + </v-pane-header> | |
497 | + <v-pane-content> | |
498 | + <div ng-if="broker.disconnectRequests.length > 0"> | |
499 | + <ol class="list-group"> | |
500 | + <li class="list-group-item" ng-repeat="(disconnectRequestIndex, disconnectRequest) in broker.disconnectRequests"> | |
501 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeAttribute(disconnectRequest, broker.disconnectRequests)"> | |
502 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | |
503 | + <md-tooltip md-direction="top"> | |
504 | + {{ 'action.remove' | translate }} | |
505 | + </md-tooltip> | |
506 | + </md-button> | |
507 | + <md-card> | |
508 | + <md-card-content> | |
509 | + <md-input-container class="md-block"> | |
510 | + <label translate>extension.topic-filter</label> | |
511 | + <input required name="disconRequestTopicFilter_{{brokerIndex}}{{disconnectRequestIndex}}" ng-model="disconnectRequest.topicFilter"> | |
512 | + <div ng-messages="theForm['disconRequestTopicFilter_' + brokerIndex + disconnectRequestIndex].$error"> | |
513 | + <div translate ng-message="required">extension.field-required</div> | |
514 | + </div> | |
515 | + </md-input-container> | |
516 | + <section flex layout="row"> | |
517 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['disconnectDeviceNameExpression_' + brokerIndex + disconnectRequestIndex].$touched && theForm['disconnectDeviceNameExpression_' + brokerIndex + disconnectRequestIndex].$invalid"> | |
518 | + <label translate>extension.device-name-expression</label> | |
519 | + <md-select required name="disconnectDeviceNameExpression_{{brokerIndex}}{{disconnectRequestIndex}}" ng-model="disconnectRequest.nameExp" ng-change="changeNameExpression(disconnectRequest, 'disconnect')"> | |
520 | + <md-option ng-repeat="(key, value) in deviceNameExpressions" ng-value='key'> | |
521 | + {{value | translate}} | |
522 | + </md-option> | |
523 | + </md-select> | |
524 | + <div ng-messages="theForm['disconnectDeviceNameExpression_' + brokerIndex + disconnectRequestIndex].$error"> | |
525 | + <div translate ng-message="required">extension.field-required</div> | |
526 | + </div> | |
527 | + </md-input-container> | |
528 | + <md-input-container ng-if="disconnectRequest.nameExp == 'deviceNameJsonExpression'" flex="60" class="md-block"> | |
529 | + <label translate>extension.json-name-expression</label> | |
530 | + <input required name="disconnectJsonNameExp_{{brokerIndex}}{{disconnectRequestIndex}}" ng-model="disconnectRequest.deviceNameJsonExpression"> | |
531 | + <div ng-messages="theForm['disconnectJsonNameExp_' + brokerIndex + disconnectRequestIndex].$error"> | |
532 | + <div translate ng-message="required">extension.field-required</div> | |
533 | + </div> | |
534 | + </md-input-container> | |
535 | + <md-input-container ng-if="disconnectRequest.nameExp == 'deviceNameTopicExpression'" flex="60" class="md-block"> | |
536 | + <label translate>extension.topic-name-expression</label> | |
537 | + <input required name="disconnectTopicNameExp_{{brokerIndex}}{{disconnectRequestIndex}}" ng-model="disconnectRequest.deviceNameTopicExpression"> | |
538 | + <div ng-messages="theForm['disconnectTopicNameExp_' + brokerIndex + disconnectRequestIndex].$error"> | |
539 | + <div translate ng-message="required">extension.field-required</div> | |
540 | + </div> | |
541 | + </md-input-container> | |
542 | + </section> | |
543 | + </md-card-content> | |
544 | + </md-card> | |
545 | + </li> | |
546 | + </ol> | |
547 | + </div> | |
548 | + <div flex layout="row" layout-align="start center"> | |
549 | + <md-button class="md-primary md-raised" | |
550 | + ng-click='addConnectRequest(broker.disconnectRequests, "disconnect")' aria-label="{{ 'action.add' | translate }}"> | |
551 | + <md-icon class="material-icons">add</md-icon> | |
552 | + <span translate>extension.add-disconnect-request</span> | |
553 | + </md-button> | |
554 | + </div> | |
555 | + </v-pane-content> | |
556 | + </v-pane> | |
557 | + </v-accordion> | |
558 | + | |
559 | + <v-accordion id="mqtt-attribute-requests-accordion" class="vAccordion--default"> | |
560 | + <v-pane id="mqtt-attribute-requests-pane"> | |
561 | + <v-pane-header> | |
562 | + {{ 'extension.attribute-requests' | translate }} | |
563 | + </v-pane-header> | |
564 | + <v-pane-content> | |
565 | + <div ng-if="broker.attributeRequests.length > 0"> | |
566 | + <ol class="list-group"> | |
567 | + <li class="list-group-item" ng-repeat="(attributeRequestIndex, attributeRequest) in broker.attributeRequests"> | |
568 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeAttribute(attributeRequest, broker.attributeRequests)"> | |
569 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | |
570 | + <md-tooltip md-direction="top"> | |
571 | + {{ 'action.remove' | translate }} | |
572 | + </md-tooltip> | |
573 | + </md-button> | |
574 | + <md-card> | |
575 | + <md-card-content> | |
576 | + <section flex layout="row"> | |
577 | + <md-input-container flex="80" class="md-block"> | |
578 | + <label translate>extension.topic-filter</label> | |
579 | + <input required name="attributeRequestTopicFilter_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.topicFilter"> | |
580 | + <div ng-messages="theForm['attributeRequestTopicFilter_' + brokerIndex + attributeRequestIndex].$error"> | |
581 | + <div translate ng-message="required">extension.field-required</div> | |
582 | + </div> | |
583 | + </md-input-container> | |
584 | + <md-input-container flex="20" class="md-block"> | |
585 | + <md-checkbox flex aria-label="{{ 'extension.client-scope' | translate }}" | |
586 | + ng-model="attributeRequest.clientScope">{{ 'extension.client-scope' | translate }} | |
587 | + </md-checkbox> | |
588 | + </md-input-container> | |
589 | + </section> | |
590 | + <section flex layout="row"> | |
591 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['attrRequestDeviceNameExpression_' + brokerIndex + attributeRequestIndex].$touched && theForm['attrRequestDeviceNameExpression_' + brokerIndex + attributeRequestIndex].$invalid"> | |
592 | + <label translate>extension.device-name-expression</label> | |
593 | + <md-select required name="attrRequestDeviceNameExpression_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.nameExp" ng-change="changeNameExpression(attributeRequest, 'attribute')"> | |
594 | + <md-option ng-repeat="(key, value) in deviceNameExpressions" ng-value='key'> | |
595 | + {{value | translate}} | |
596 | + </md-option> | |
597 | + </md-select> | |
598 | + <div ng-messages="theForm['attrRequestDeviceNameExpression_' + brokerIndex + attributeRequestIndex].$error"> | |
599 | + <div translate ng-message="required">extension.field-required</div> | |
600 | + </div> | |
601 | + </md-input-container> | |
602 | + <md-input-container ng-if="attributeRequest.nameExp == 'deviceNameJsonExpression'" flex="60" class="md-block"> | |
603 | + <label translate>extension.json-name-expression</label> | |
604 | + <input required name="attrRequestJsonNameExp_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.deviceNameJsonExpression"> | |
605 | + <div ng-messages="theForm['attrRequestJsonNameExp_' + brokerIndex + attributeRequestIndex].$error"> | |
606 | + <div translate ng-message="required">extension.field-required</div> | |
607 | + </div> | |
608 | + </md-input-container> | |
609 | + <md-input-container ng-if="attributeRequest.nameExp == 'deviceNameTopicExpression'" flex="60" class="md-block"> | |
610 | + <label translate>extension.topic-name-expression</label> | |
611 | + <input required name="attrRequestTopicNameExp_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.deviceNameTopicExpression"> | |
612 | + <div ng-messages="theForm['attrRequestTopicNameExp_' + brokerIndex + attributeRequestIndex].$error"> | |
613 | + <div translate ng-message="required">extension.field-required</div> | |
614 | + </div> | |
615 | + </md-input-container> | |
616 | + </section> | |
617 | + | |
618 | + <section flex layout="row"> | |
619 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['attrRequestAttributeKeyExpression_' + brokerIndex + attributeRequestIndex].$touched && theForm['attrRequestAttributeKeyExpression_' + brokerIndex + attributeRequestIndex].$invalid"> | |
620 | + <label translate>extension.attribute-key-expression</label> | |
621 | + <md-select required name="attrRequestAttributeKeyExpression_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.attrKey" ng-change="changeAttrKeyExpression(attributeRequest)"> | |
622 | + <md-option ng-repeat="(key, value) in attributeKeyExpressions" ng-value='key'> | |
623 | + {{value | translate}} | |
624 | + </md-option> | |
625 | + </md-select> | |
626 | + <div ng-messages="theForm['attrRequestAttributeKeyExpression_' + brokerIndex + attributeRequestIndex].$error"> | |
627 | + <div translate ng-message="required">extension.field-required</div> | |
628 | + </div> | |
629 | + </md-input-container> | |
630 | + <md-input-container ng-if="attributeRequest.attrKey == 'attributeKeyJsonExpression'" flex="60" class="md-block"> | |
631 | + <label translate>extension.attr-json-key-expression</label> | |
632 | + <input required name="attrRequestJsonKeyExp_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.attributeKeyJsonExpression"> | |
633 | + <div ng-messages="theForm['attrRequestJsonKeyExp_' + brokerIndex + attributeRequestIndex].$error"> | |
634 | + <div translate ng-message="required">extension.field-required</div> | |
635 | + </div> | |
636 | + </md-input-container> | |
637 | + <md-input-container ng-if="attributeRequest.attrKey == 'attributeKeyTopicExpression'" flex="60" class="md-block"> | |
638 | + <label translate>extension.attr-topic-key-expression</label> | |
639 | + <input required name="attrRequestTopicKeyExp_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.attributeKeyTopicExpression"> | |
640 | + <div ng-messages="theForm['attrRequestTopicKeyExp_' + brokerIndex + attributeRequestIndex].$error"> | |
641 | + <div translate ng-message="required">extension.field-required</div> | |
642 | + </div> | |
643 | + </md-input-container> | |
644 | + </section> | |
645 | + | |
646 | + <section flex layout="row"> | |
647 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['attrRequestIdExpression_' + brokerIndex + attributeRequestIndex].$touched && theForm['attrRequestIdExpression_' + brokerIndex + attributeRequestIndex].$invalid"> | |
648 | + <label translate>extension.request-id-expression</label> | |
649 | + <md-select required name="attrRequestIdExpression_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.requestId" ng-change="changeRequestIdExpression(attributeRequest)"> | |
650 | + <md-option ng-repeat="(key, value) in requestIdExpressions" ng-value='key'> | |
651 | + {{value | translate}} | |
652 | + </md-option> | |
653 | + </md-select> | |
654 | + <div ng-messages="theForm['attrRequestIdExpression_' + brokerIndex + attributeRequestIndex].$error"> | |
655 | + <div translate ng-message="required">extension.field-required</div> | |
656 | + </div> | |
657 | + </md-input-container> | |
658 | + <md-input-container ng-if="attributeRequest.requestId == 'requestIdJsonExpression'" flex="60" class="md-block"> | |
659 | + <label translate>extension.request-id-json-expression</label> | |
660 | + <input required name="attrRequestJsonIdExp_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.requestIdJsonExpression"> | |
661 | + <div ng-messages="theForm['attrRequestJsonIdExp_' + brokerIndex + attributeRequestIndex].$error"> | |
662 | + <div translate ng-message="required">extension.field-required</div> | |
663 | + </div> | |
664 | + </md-input-container> | |
665 | + <md-input-container ng-if="attributeRequest.requestId == 'requestIdTopicExpression'" flex="60" class="md-block"> | |
666 | + <label translate>extension.request-id-topic-expression</label> | |
667 | + <input required name="attrRequestTopicIdExp_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.requestIdTopicExpression"> | |
668 | + <div ng-messages="theForm['attrRequestTopicIdExp_' + brokerIndex + attributeRequestIndex].$error"> | |
669 | + <div translate ng-message="required">extension.field-required</div> | |
670 | + </div> | |
671 | + </md-input-container> | |
672 | + </section> | |
673 | + | |
674 | + <md-input-container class="md-block"> | |
675 | + <label translate>extension.response-topic-expression</label> | |
676 | + <input required name="attributeRequestResponseTopicExp_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.responseTopicExpression"> | |
677 | + <div ng-messages="theForm['attributeRequestResponseTopicExp_' + brokerIndex + attributeRequestIndex].$error"> | |
678 | + <div translate ng-message="required">extension.field-required</div> | |
679 | + </div> | |
680 | + </md-input-container> | |
681 | + <md-input-container class="md-block"> | |
682 | + <label translate>extension.value-expression</label> | |
683 | + <input required name="attributeRequestValueExp_{{brokerIndex}}{{attributeRequestIndex}}" ng-model="attributeRequest.valueExpression"> | |
684 | + <div ng-messages="theForm['attributeRequestValueExp_' + brokerIndex + attributeRequestIndex].$error"> | |
685 | + <div translate ng-message="required">extension.field-required</div> | |
686 | + </div> | |
687 | + </md-input-container> | |
688 | + </md-card-content> | |
689 | + </md-card> | |
690 | + </li> | |
691 | + </ol> | |
692 | + </div> | |
693 | + <div flex layout="row" layout-align="start center"> | |
694 | + <md-button class="md-primary md-raised" | |
695 | + ng-click="addAttributeRequest(broker.attributeRequests)" aria-label="{{ 'action.add' | translate }}"> | |
696 | + <md-icon class="material-icons">add</md-icon> | |
697 | + <span translate>extension.add-attribute-request</span> | |
698 | + </md-button> | |
699 | + </div> | |
700 | + </v-pane-content> | |
701 | + </v-pane> | |
702 | + </v-accordion> | |
703 | + | |
704 | + <v-accordion id="mqtt-attribute-updates-accordion" class="vAccordion--default"> | |
705 | + <v-pane id="mqtt-attribute-updates-pane"> | |
706 | + <v-pane-header> | |
707 | + {{ 'extension.attribute-updates' | translate }} | |
708 | + </v-pane-header> | |
709 | + <v-pane-content> | |
710 | + <div ng-if="broker.attributeUpdates.length > 0"> | |
711 | + <ol class="list-group"> | |
712 | + <li class="list-group-item" ng-repeat="(attributeUpdateIndex, attributeUpdate) in broker.attributeUpdates"> | |
713 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeAttribute(attributeUpdate, broker.attributeUpdates)"> | |
714 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | |
715 | + <md-tooltip md-direction="top"> | |
716 | + {{ 'action.remove' | translate }} | |
717 | + </md-tooltip> | |
718 | + </md-button> | |
719 | + <md-card> | |
720 | + <md-card-content> | |
721 | + <section flex layout="row"> | |
722 | + <md-input-container flex="50" class="md-block"> | |
723 | + <label translate>extension.device-name-filter</label> | |
724 | + <input required name="attributeUpdateDeviceNameFilter_{{brokerIndex}}{{attributeUpdateIndex}}" ng-model="attributeUpdate.deviceNameFilter"> | |
725 | + <div ng-messages="theForm['attributeUpdateDeviceNameFilter_' + brokerIndex + attributeUpdateIndex].$error"> | |
726 | + <div translate ng-message="required">extension.field-required</div> | |
727 | + </div> | |
728 | + </md-input-container> | |
729 | + <md-input-container flex="50" class="md-block"> | |
730 | + <label translate>extension.attribute-filter</label> | |
731 | + <input required name="attributeUpdateAttributeFilter_{{brokerIndex}}{{attributeUpdateIndex}}" ng-model="attributeUpdate.attributeFilter"> | |
732 | + <div ng-messages="theForm['attributeUpdateAttributeFilter_' + brokerIndex + attributeUpdateIndex].$error"> | |
733 | + <div translate ng-message="required">extension.field-required</div> | |
734 | + </div> | |
735 | + </md-input-container> | |
736 | + </section> | |
737 | + <md-input-container class="md-block"> | |
738 | + <label translate>extension.topic-expression</label> | |
739 | + <input required name="attributeUpdateTopicExp_{{brokerIndex}}{{attributeUpdateIndex}}" ng-model="attributeUpdate.topicExpression"> | |
740 | + <div ng-messages="theForm['attributeUpdateTopicExp_' + brokerIndex + attributeUpdateIndex].$error"> | |
741 | + <div translate ng-message="required">extension.field-required</div> | |
742 | + </div> | |
743 | + </md-input-container> | |
744 | + <md-input-container class="md-block"> | |
745 | + <label translate>extension.value-expression</label> | |
746 | + <input required name="attributeUpdateValueExp_{{brokerIndex}}{{attributeUpdateIndex}}" ng-model="attributeUpdate.valueExpression"> | |
747 | + <div ng-messages="theForm['attributeUpdateValueExp_' + brokerIndex + attributeUpdateIndex].$error"> | |
748 | + <div translate ng-message="required">extension.field-required</div> | |
749 | + </div> | |
750 | + </md-input-container> | |
751 | + </md-card-content> | |
752 | + </md-card> | |
753 | + </li> | |
754 | + </ol> | |
755 | + </div> | |
756 | + <div flex layout="row" layout-align="start center"> | |
757 | + <md-button class="md-primary md-raised" | |
758 | + ng-click='addAttributeUpdate(broker.attributeUpdates)' aria-label="{{ 'action.add' | translate }}"> | |
759 | + <md-icon class="material-icons">add</md-icon> | |
760 | + <span translate>extension.add-attribute-update</span> | |
761 | + </md-button> | |
762 | + </div> | |
763 | + </v-pane-content> | |
764 | + </v-pane> | |
765 | + </v-accordion> | |
766 | + | |
767 | + <v-accordion id="mqtt-server-side-rpc-accordion" class="vAccordion--default"> | |
768 | + <v-pane id="mqtt-server-side-rpc-pane"> | |
769 | + <v-pane-header> | |
770 | + {{ 'extension.server-side-rpc' | translate }} | |
771 | + </v-pane-header> | |
772 | + <v-pane-content> | |
773 | + <div ng-if="broker.serverSideRpc.length > 0"> | |
774 | + <ol class="list-group"> | |
775 | + <li class="list-group-item" ng-repeat="(rpcIndex, rpc) in broker.serverSideRpc"> | |
776 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeAttribute(rpc, broker.serverSideRpc)"> | |
777 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | |
778 | + <md-tooltip md-direction="top"> | |
779 | + {{ 'action.remove' | translate }} | |
780 | + </md-tooltip> | |
781 | + </md-button> | |
782 | + <md-card> | |
783 | + <md-card-content> | |
784 | + <section flex layout="row"> | |
785 | + <md-input-container flex="50" class="md-block"> | |
786 | + <label translate>extension.device-name-filter</label> | |
787 | + <input required name="serverSideRpcDeviceNameFilter_{{brokerIndex}}{{rpcIndex}}" ng-model="rpc.deviceNameFilter"> | |
788 | + <div ng-messages="theForm['serverSideRpcDeviceNameFilter_' + brokerIndex + rpcIndex].$error"> | |
789 | + <div translate ng-message="required">extension.field-required</div> | |
790 | + </div> | |
791 | + </md-input-container> | |
792 | + <md-input-container flex="50" class="md-block"> | |
793 | + <label translate>extension.method-filter</label> | |
794 | + <input required name="serverSideRpcMethodFilter_{{brokerIndex}}{{rpcIndex}}" ng-model="rpc.methodFilter"> | |
795 | + <div ng-messages="theForm['serverSideRpcMethodFilter_' + brokerIndex + rpcIndex].$error"> | |
796 | + <div translate ng-message="required">extension.field-required</div> | |
797 | + </div> | |
798 | + </md-input-container> | |
799 | + </section> | |
800 | + <md-input-container class="md-block"> | |
801 | + <label translate>extension.request-topic-expression</label> | |
802 | + <input required name="serverSideRpcRequestTopicExp_{{brokerIndex}}{{rpcIndex}}" ng-model="rpc.requestTopicExpression"> | |
803 | + <div ng-messages="theForm['serverSideRpcRequestTopicExp_' + brokerIndex + rpcIndex].$error"> | |
804 | + <div translate ng-message="required">extension.field-required</div> | |
805 | + </div> | |
806 | + </md-input-container> | |
807 | + <md-input-container class="md-block"> | |
808 | + <label translate>extension.response-topic-expression</label> | |
809 | + <input name="serverSideRpcResponseTopicExp_{{brokerIndex}}{{rpcIndex}}" ng-model="rpc.responseTopicExpression" parse-to-null> | |
810 | + </md-input-container> | |
811 | + <section flex layout="row"> | |
812 | + <md-input-container flex="50" class="md-block"> | |
813 | + <label translate>extension.response-timeout</label> | |
814 | + <input type="number" name="serverSideRpcResponseTimeout_{{brokerIndex}}{{rpcIndex}}" ng-model="rpc.responseTimeout" parse-to-null> | |
815 | + </md-input-container> | |
816 | + <md-input-container flex="50" class="md-block"> | |
817 | + <label translate>extension.value-expression</label> | |
818 | + <input required name="serverSideRpcValueExp_{{brokerIndex}}{{rpcIndex}}" ng-model="rpc.valueExpression"> | |
819 | + <div ng-messages="theForm['serverSideRpcValueExp_' + brokerIndex + rpcIndex].$error"> | |
820 | + <div translate ng-message="required">extension.field-required</div> | |
821 | + </div> | |
822 | + </md-input-container> | |
823 | + </section> | |
824 | + </md-card-content> | |
825 | + </md-card> | |
826 | + </li> | |
827 | + </ol> | |
828 | + </div> | |
829 | + <div flex layout="row" layout-align="start center"> | |
830 | + <md-button class="md-primary md-raised" | |
831 | + ng-click='addServerSideRpc(broker.serverSideRpc)' aria-label="{{ 'action.add' | translate }}"> | |
832 | + <md-icon class="material-icons">add</md-icon> | |
833 | + <span translate>extension.add-server-side-rpc-request</span> | |
834 | + </md-button> | |
835 | + </div> | |
836 | + </v-pane-content> | |
837 | + </v-pane> | |
838 | + </v-accordion> | |
839 | + | |
840 | + </md-card-content> | |
841 | + </md-card> | |
842 | + </li> | |
843 | + </ol> | |
844 | + </div> | |
845 | + | |
846 | + <div flex layout="row" layout-align="start center"> | |
847 | + <md-button class="md-primary md-raised" | |
848 | + ng-click="addBroker()" aria-label="{{ 'action.add' | translate }}"> | |
849 | + <md-icon class="material-icons">add</md-icon> | |
850 | + <span translate>extension.add-broker</span> | |
851 | + </md-button> | |
852 | + </div> | |
853 | + </v-pane-content> | |
854 | + </v-pane> | |
855 | + </v-accordion> | |
856 | +<!--<pre> | |
857 | +{{config | json}} | |
858 | +</pre>--> | |
859 | + </md-card-content> | |
860 | +</md-card> | ... | ... |
... | ... | @@ -56,16 +56,16 @@ |
56 | 56 | <label translate>extension.opc-application-name</label> |
57 | 57 | <input required name="applicationName_{{serverIndex}}" ng-model="server.applicationName"> |
58 | 58 | <div ng-messages="theForm['applicationName_' + serverIndex].$error"> |
59 | - <div translate ng-message="required">extension.opc-field-required</div> | |
59 | + <div translate ng-message="required">extension.field-required</div> | |
60 | 60 | </div> |
61 | 61 | </md-input-container> |
62 | 62 | |
63 | 63 | |
64 | - <md-input-container flex="50" class="md-block"> | |
64 | + <md-input-container flex="50" class="md-block" md-is-error="theForm['applicationUri_' + serverIndex].$touched && theForm['applicationUri_' + serverIndex].$invalid"> | |
65 | 65 | <label translate>extension.opc-application-uri</label> |
66 | 66 | <input required name="applicationUri_{{serverIndex}}" ng-model="server.applicationUri"> |
67 | 67 | <div ng-messages="theForm['applicationUri_' + serverIndex].$error"> |
68 | - <div translate ng-message="required">extension.opc-field-required</div> | |
68 | + <div translate ng-message="required">extension.field-required</div> | |
69 | 69 | </div> |
70 | 70 | </md-input-container> |
71 | 71 | </div> |
... | ... | @@ -73,15 +73,15 @@ |
73 | 73 | |
74 | 74 | <div layout="row"> |
75 | 75 | <md-input-container flex="50" class="md-block"> |
76 | - <label translate>extension.opc-host</label> | |
76 | + <label translate>extension.host</label> | |
77 | 77 | <input required name="host_{{serverIndex}}" ng-model="server.host"> |
78 | 78 | <div ng-messages="theForm['host_' + serverIndex].$error"> |
79 | - <div translate ng-message="required">extension.opc-field-required</div> | |
79 | + <div translate ng-message="required">extension.field-required</div> | |
80 | 80 | </div> |
81 | 81 | </md-input-container> |
82 | 82 | |
83 | 83 | <md-input-container flex="50" class="md-block"> |
84 | - <label translate>extension.opc-port</label> | |
84 | + <label translate>extension.port</label> | |
85 | 85 | <input type="number" |
86 | 86 | required |
87 | 87 | name="port_{{serverIndex}}" |
... | ... | @@ -92,7 +92,7 @@ |
92 | 92 | <div ng-messages="theForm['port_' + serverIndex].$error"> |
93 | 93 | <div translate |
94 | 94 | ng-message="required" |
95 | - >extension.opc-field-required</div> | |
95 | + >extension.field-required</div> | |
96 | 96 | <div translate |
97 | 97 | ng-message="min" |
98 | 98 | >Port should be in a range from 1 to 65535</div> |
... | ... | @@ -113,12 +113,12 @@ |
113 | 113 | <div ng-messages="theForm['scanPeriodInSeconds_' + serverIndex].$error"> |
114 | 114 | <div translate |
115 | 115 | ng-message="required" |
116 | - >extension.opc-field-required</div> | |
116 | + >extension.field-required</div> | |
117 | 117 | </div> |
118 | 118 | </md-input-container> |
119 | 119 | |
120 | 120 | <md-input-container flex="50" class="md-block"> |
121 | - <label translate>extension.opc-timeout-in-millis</label> | |
121 | + <label translate>extension.timeout</label> | |
122 | 122 | <input type="number" |
123 | 123 | required name="timeoutInMillis_{{serverIndex}}" |
124 | 124 | ng-model="server.timeoutInMillis" |
... | ... | @@ -126,7 +126,7 @@ |
126 | 126 | <div ng-messages="theForm['timeoutInMillis_' + serverIndex].$error"> |
127 | 127 | <div translate |
128 | 128 | ng-message="required" |
129 | - >extension.opc-field-required</div> | |
129 | + >extension.field-required</div> | |
130 | 130 | </div> |
131 | 131 | </md-input-container> |
132 | 132 | </div> |
... | ... | @@ -145,7 +145,7 @@ |
145 | 145 | <div ng-messages="theForm['securityType_' + serverIndex].$error"> |
146 | 146 | <div translate |
147 | 147 | ng-message="required" |
148 | - >extension.opc-field-required</div> | |
148 | + >extension.field-required</div> | |
149 | 149 | </div> |
150 | 150 | </md-input-container> |
151 | 151 | |
... | ... | @@ -157,24 +157,23 @@ |
157 | 157 | > |
158 | 158 | <md-option ng-value="identityType" |
159 | 159 | ng-repeat="(identityType, identityValue) in types.extensionIdentityType" |
160 | - ><span ng-bind="::identityValue"></span></md-option> | |
160 | + ><span ng-bind="identityValue | translate"></span></md-option> | |
161 | 161 | </md-select> |
162 | 162 | <div ng-messages="theForm['identityType_' + serverIndex].$error"> |
163 | 163 | <div translate |
164 | 164 | ng-message="required" |
165 | - >extension.opc-field-required</div> | |
165 | + >extension.field-required</div> | |
166 | 166 | </div> |
167 | 167 | </md-input-container> |
168 | 168 | </div> |
169 | 169 | |
170 | - | |
171 | 170 | <div ng-if="server.identity.type != 'username'"> |
172 | 171 | <span class="" |
173 | 172 | ng-init="server.identity = {'type':'anonymous'}"></span> |
174 | 173 | </div> |
175 | 174 | <div layout="row" ng-if="server.identity.type == 'username'"> |
176 | - <md-input-container flex="50" class="md-block"> | |
177 | - <label translate>extension.opc-username</label> | |
175 | + <md-input-container flex="50" class="md-block" md-is-error="theForm['identityUsername_' + serverIndex].$touched && theForm['identityUsername_' + serverIndex].$invalid"> | |
176 | + <label translate>extension.username</label> | |
178 | 177 | <input required |
179 | 178 | name="identityUsername_{{serverIndex}}" |
180 | 179 | ng-model="server.identity.username" |
... | ... | @@ -182,25 +181,24 @@ |
182 | 181 | <div ng-messages="theForm['identityUsername_' + serverIndex].$error"> |
183 | 182 | <div translate |
184 | 183 | ng-message="required" |
185 | - >extension.opc-field-required</div> | |
184 | + >extension.field-required</div> | |
186 | 185 | </div> |
187 | 186 | </md-input-container> |
188 | 187 | |
189 | - <md-input-container flex="50" class="md-block"> | |
190 | - <label translate>extension.opc-password</label> | |
188 | + <md-input-container flex="50" class="md-block" md-is-error="theForm['identityPassword_' + serverIndex].$touched && theForm['identityPassword_' + serverIndex].$invalid"> | |
189 | + <label translate>extension.password</label> | |
191 | 190 | <input required |
192 | 191 | name="identityPassword_{{serverIndex}}" ng-model="server.identity.password"> |
193 | 192 | <div ng-messages="theForm['identityPassword_' + serverIndex].$error"> |
194 | 193 | <div translate |
195 | 194 | ng-message="required" |
196 | - >extension.opc-field-required</div> | |
195 | + >extension.field-required</div> | |
197 | 196 | </div> |
198 | 197 | </md-input-container> |
199 | 198 | </div> |
200 | 199 | |
201 | - | |
202 | 200 | <v-accordion id="opc-attributes-accordion" class="vAccordion--default"> |
203 | - <v-pane id="opc-attributes-pane"> | |
201 | + <v-pane id="opc-attributes-pane" expanded="true"> | |
204 | 202 | <v-pane-header> |
205 | 203 | {{ 'extension.opc-keystore' | translate }} |
206 | 204 | </v-pane-header> |
... | ... | @@ -212,38 +210,40 @@ |
212 | 210 | <md-option ng-value="keystoreType" ng-repeat="(keystoreType, keystoreValue) in types.extensionKeystoreType"><span ng-bind="::keystoreValue"></span></md-option> |
213 | 211 | </md-select> |
214 | 212 | <div ng-messages="theForm['keystoreType_'+serverIndex].$error"> |
215 | - <div translate ng-message="required">extension.opc-field-required</div> | |
213 | + <div translate ng-message="required">extension.field-required</div> | |
216 | 214 | </div> |
217 | 215 | </md-input-container> |
218 | 216 | |
219 | - <div class="tb-container" ng-class="{'ng-invalid':!server.keystore.file}"> | |
220 | - <span ng-init='fieldsToFill = {"fileName":"fileName", "file":"file"}'></span> | |
221 | - <label class="tb-label" translate>extension.opc-keystore-location</label> | |
222 | - <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, server.keystore, fieldsToFill)' class="tb-file-select-container"> | |
223 | - <div class="tb-file-clear-container"> | |
224 | - <md-button ng-click='clearFile(server.keystore, fieldsToFill)' class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ 'action.remove' | translate }}"> | |
225 | - <md-tooltip md-direction="top"> | |
226 | - {{ 'action.remove' | translate }} | |
227 | - </md-tooltip> | |
228 | - <md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons">close</md-icon> | |
229 | - </md-button> | |
230 | - </div> | |
231 | - <div class="alert tb-flow-drop" flow-drop> | |
232 | - <label for="dropFileKeystore_{{serverIndex}}" translate>extension.drop-file</label> | |
233 | - <input flow-attrs="{accept:'.pfx,.p12'}" | |
234 | - type="file" | |
235 | - class="file-input" | |
236 | - flow-btn id="dropFileKeystore_{{serverIndex}}" | |
237 | - name="keystoreFile" | |
238 | - ng-model="server.keystore.file" | |
239 | - > | |
217 | + <section class="dropdown-section"> | |
218 | + <div class="tb-container" ng-class="{'ng-invalid':!server.keystore.file}"> | |
219 | + <span ng-init='fieldsToFill = {"fileName":"fileName", "file":"file"}'></span> | |
220 | + <label class="tb-label" translate>extension.opc-keystore-location</label> | |
221 | + <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, server.keystore, fieldsToFill)' class="tb-file-select-container"> | |
222 | + <div class="tb-file-clear-container"> | |
223 | + <md-button ng-click='clearFile(server.keystore, fieldsToFill)' class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ 'action.remove' | translate }}"> | |
224 | + <md-tooltip md-direction="top"> | |
225 | + {{ 'action.remove' | translate }} | |
226 | + </md-tooltip> | |
227 | + <md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons">close</md-icon> | |
228 | + </md-button> | |
229 | + </div> | |
230 | + <div class="alert tb-flow-drop" flow-drop> | |
231 | + <label for="dropFileKeystore_{{serverIndex}}" translate>extension.drop-file</label> | |
232 | + <input flow-attrs="{accept:'.pfx,.p12'}" | |
233 | + type="file" | |
234 | + class="file-input" | |
235 | + flow-btn id="dropFileKeystore_{{serverIndex}}" | |
236 | + name="keystoreFile" | |
237 | + ng-model="server.keystore.file" | |
238 | + > | |
239 | + </div> | |
240 | 240 | </div> |
241 | 241 | </div> |
242 | - </div> | |
243 | - <div> | |
244 | - <div ng-if="!server.keystore[fieldsToFill.fileName]" class="tb-error-message" translate>extension.no-file</div> | |
245 | - <div ng-if="server.keystore[fieldsToFill.fileName]">{{server.keystore[fieldsToFill.fileName]}}</div> | |
246 | - </div> | |
242 | + <div class="dropdown-messages"> | |
243 | + <div ng-if="!server.keystore[fieldsToFill.fileName]" class="tb-error-message" translate>extension.no-file</div> | |
244 | + <div ng-if="server.keystore[fieldsToFill.fileName]">{{server.keystore[fieldsToFill.fileName]}}</div> | |
245 | + </div> | |
246 | + </section> | |
247 | 247 | |
248 | 248 | |
249 | 249 | <div flex layout="row"> |
... | ... | @@ -251,7 +251,7 @@ |
251 | 251 | <label translate>extension.opc-keystore-password</label> |
252 | 252 | <input required name="keystorePassword_{{serverIndex}}" ng-model="server.keystore.password"> |
253 | 253 | <div ng-messages="theForm['keystorePassword_' + serverIndex].$error"> |
254 | - <div translate ng-message="required">extension.opc-field-required</div> | |
254 | + <div translate ng-message="required">extension.field-required</div> | |
255 | 255 | </div> |
256 | 256 | </md-input-container> |
257 | 257 | |
... | ... | @@ -259,7 +259,7 @@ |
259 | 259 | <label translate>extension.opc-keystore-alias</label> |
260 | 260 | <input required name="keystoreAlias_{{serverIndex}}" ng-model="server.keystore.alias"> |
261 | 261 | <div ng-messages="theForm['keystoreAlias_' + serverIndex].$error"> |
262 | - <div translate ng-message="required">extension.opc-field-required</div> | |
262 | + <div translate ng-message="required">extension.field-required</div> | |
263 | 263 | </div> |
264 | 264 | </md-input-container> |
265 | 265 | </div> |
... | ... | @@ -268,7 +268,7 @@ |
268 | 268 | <label translate>extension.opc-keystore-key-password</label> |
269 | 269 | <input required name="keystoreKeyPassword_{{serverIndex}}" ng-model="server.keystore.keyPassword"> |
270 | 270 | <div ng-messages="theForm['keystoreKeyPassword_' + serverIndex].$error"> |
271 | - <div translate ng-message="required">extension.opc-field-required</div> | |
271 | + <div translate ng-message="required">extension.field-required</div> | |
272 | 272 | </div> |
273 | 273 | </md-input-container> |
274 | 274 | |
... | ... | @@ -282,7 +282,7 @@ |
282 | 282 | > |
283 | 283 | <v-pane id="opc-attributes-pane"> |
284 | 284 | <v-pane-header> |
285 | - {{ 'extension.opc-mapping' | translate }} | |
285 | + {{ 'extension.mapping' | translate }} | |
286 | 286 | </v-pane-header> |
287 | 287 | <v-pane-content> |
288 | 288 | <div ng-if="server.mapping.length > 0"> |
... | ... | @@ -312,7 +312,7 @@ |
312 | 312 | <div ng-messages="theForm['deviceNodePattern_' + serverIndex + mapIndex].$error"> |
313 | 313 | <div translate |
314 | 314 | ng-message="required" |
315 | - >extension.opc-field-required</div> | |
315 | + >extension.field-required</div> | |
316 | 316 | </div> |
317 | 317 | </md-input-container> |
318 | 318 | |
... | ... | @@ -325,7 +325,7 @@ |
325 | 325 | <div ng-messages="theForm['deviceNamePattern_' + serverIndex + mapIndex].$error"> |
326 | 326 | <div translate |
327 | 327 | ng-message="required" |
328 | - >extension.opc-field-required</div> | |
328 | + >extension.field-required</div> | |
329 | 329 | </div> |
330 | 330 | </md-input-container> |
331 | 331 | </div> |
... | ... | @@ -336,7 +336,7 @@ |
336 | 336 | > |
337 | 337 | <v-pane id="opc-attributes-pane"> |
338 | 338 | <v-pane-header> |
339 | - {{ 'extension.opc-mapping-attributes' | translate }} | |
339 | + {{ 'extension.attributes' | translate }} | |
340 | 340 | </v-pane-header> |
341 | 341 | <v-pane-content> |
342 | 342 | <div ng-show="map.attributes.length > 0"> |
... | ... | @@ -369,7 +369,7 @@ |
369 | 369 | <div ng-messages="theForm['opcAttributeKey_' + serverIndex + mapIndex + attributeIndex].$error"> |
370 | 370 | <div translate |
371 | 371 | ng-message="required" |
372 | - >extension.opc-field-required</div> | |
372 | + >extension.field-required</div> | |
373 | 373 | </div> |
374 | 374 | </md-input-container> |
375 | 375 | <md-input-container flex="40" class="md-block tb-container-for-select"> |
... | ... | @@ -400,7 +400,7 @@ |
400 | 400 | <div ng-messages="theForm['opcAttributeValue_' + serverIndex + mapIndex + attributeIndex].$error"> |
401 | 401 | <div translate |
402 | 402 | ng-message="required" |
403 | - >extension.opc-field-required</div> | |
403 | + >extension.field-required</div> | |
404 | 404 | </div> |
405 | 405 | </md-input-container> |
406 | 406 | |
... | ... | @@ -417,11 +417,8 @@ |
417 | 417 | ng-click="addNewAttribute(map.attributes)" |
418 | 418 | aria-label="{{ 'action.add' | translate }}" |
419 | 419 | > |
420 | - <md-tooltip md-direction="top"> | |
421 | - {{ 'extension.add-map' | translate }} | |
422 | - </md-tooltip> | |
423 | 420 | <md-icon class="material-icons">add</md-icon> |
424 | - <span translate>action.add</span> | |
421 | + <span translate>add-attribute</span> | |
425 | 422 | </md-button> |
426 | 423 | </div> |
427 | 424 | </v-pane-content> |
... | ... | @@ -431,7 +428,7 @@ |
431 | 428 | <v-accordion id="opc-timeseries-accordion" class="vAccordion--default"> |
432 | 429 | <v-pane id="opc-timeseries-pane"> |
433 | 430 | <v-pane-header> |
434 | - {{ 'extension.opc-timeseries' | translate }} | |
431 | + {{ 'extension.timeseries' | translate }} | |
435 | 432 | </v-pane-header> |
436 | 433 | <v-pane-content> |
437 | 434 | <div ng-show="map.timeseries.length > 0"> |
... | ... | @@ -460,7 +457,7 @@ |
460 | 457 | <div ng-messages="theForm['opcTimeseriesKey_' + serverIndex + mapIndex + timeseriesIndex].$error"> |
461 | 458 | <div translate |
462 | 459 | ng-message="required" |
463 | - >extension.opc-field-required</div> | |
460 | + >extension.field-required</div> | |
464 | 461 | </div> |
465 | 462 | </md-input-container> |
466 | 463 | <md-input-container flex="40" |
... | ... | @@ -480,7 +477,7 @@ |
480 | 477 | <div ng-messages="theForm['opcTimeseriesType_' + serverIndex + mapIndex + timeseriesIndex].$error"> |
481 | 478 | <div translate |
482 | 479 | ng-message="required" |
483 | - >extension.opc-field-required</div> | |
480 | + >extension.field-required</div> | |
484 | 481 | </div> |
485 | 482 | </md-input-container> |
486 | 483 | </section> |
... | ... | @@ -503,11 +500,8 @@ |
503 | 500 | ng-click="addNewAttribute(map.timeseries)" |
504 | 501 | aria-label="{{ 'action.add' | translate }}" |
505 | 502 | > |
506 | - <md-tooltip md-direction="top"> | |
507 | - {{ 'extension.add-timeseries' | translate }} | |
508 | - </md-tooltip> | |
509 | 503 | <md-icon class="material-icons">add</md-icon> |
510 | - <span translate>action.add</span> | |
504 | + <span translate>extension.add-timeseries</span> | |
511 | 505 | </md-button> |
512 | 506 | </div> |
513 | 507 | </v-pane-content> |
... | ... | @@ -528,11 +522,8 @@ |
528 | 522 | ng-click="addMap(server.mapping)" |
529 | 523 | aria-label="{{ 'action.add' | translate }}" |
530 | 524 | > |
531 | - <md-tooltip md-direction="top"> | |
532 | - {{ 'extension.add-map' | translate }} | |
533 | - </md-tooltip> | |
534 | 525 | <md-icon class="material-icons">add</md-icon> |
535 | - <span translate>action.add</span> | |
526 | + <span translate>extension.add-map</span> | |
536 | 527 | </md-button> |
537 | 528 | </div> |
538 | 529 | </v-pane-content> |
... | ... | @@ -553,7 +544,7 @@ |
553 | 544 | aria-label="{{ 'action.add' | translate }}" |
554 | 545 | > |
555 | 546 | <md-icon class="material-icons">add</md-icon> |
556 | - <span translate>extension.opc-add-another-server</span> | |
547 | + <span translate>extension.opc-add-server</span> | |
557 | 548 | </md-button> |
558 | 549 | </div> |
559 | 550 | ... | ... |
... | ... | @@ -22,6 +22,23 @@ |
22 | 22 | margin-top: 0; |
23 | 23 | padding-left: 3px; |
24 | 24 | } |
25 | + .tb-container { | |
26 | + width:100%; | |
27 | + } | |
28 | + .dropdown-messages { | |
29 | + .tb-error-message { | |
30 | + padding: 5px 0 0 0; | |
31 | + } | |
32 | + } | |
33 | + .dropdown-section { | |
34 | + margin-bottom: 30px; | |
35 | + } | |
36 | +} | |
37 | + | |
38 | +.extension-form.extension-mqtt { | |
39 | + md-checkbox{ | |
40 | + margin-left: 10px; | |
41 | + } | |
25 | 42 | } |
26 | 43 | |
27 | 44 | .tb-extension-custom-transformer-panel { | ... | ... |
... | ... | @@ -16,12 +16,14 @@ |
16 | 16 | |
17 | 17 | import ExtensionTableDirective from './extension-table.directive'; |
18 | 18 | import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive'; |
19 | +import ExtensionFormMqttDirective from './extensions-forms/extension-form-mqtt.directive' | |
19 | 20 | import ExtensionFormOpcDirective from './extensions-forms/extension-form-opc.directive'; |
20 | 21 | import {ParseToNull} from './extension-dialog.controller'; |
21 | 22 | |
22 | 23 | export default angular.module('thingsboard.extension', []) |
23 | 24 | .directive('tbExtensionTable', ExtensionTableDirective) |
24 | 25 | .directive('tbExtensionFormHttp', ExtensionFormHttpDirective) |
26 | + .directive('tbExtensionFormMqtt', ExtensionFormMqttDirective) | |
25 | 27 | .directive('tbExtensionFormOpc', ExtensionFormOpcDirective) |
26 | 28 | .directive('parseToNull', ParseToNull) |
27 | 29 | .name; |
\ No newline at end of file | ... | ... |
... | ... | @@ -738,13 +738,8 @@ export default angular.module('thingsboard.locale', []) |
738 | 738 | "id": "Id", |
739 | 739 | "extension-id": "Extension id", |
740 | 740 | "extension-type": "Extension type", |
741 | - "transformer-json": "JSON*", | |
742 | - "id-required": "Extension id is required.", | |
741 | + "transformer-json": "JSON *", | |
743 | 742 | "unique-id-required": "Current extension id already exists.", |
744 | - "type-required": "Extension type is required.", | |
745 | - "required-type": "Type is required.", | |
746 | - "required-key": "Key is required.", | |
747 | - "required-value": "Value is required.", | |
748 | 743 | "delete": "Delete extension", |
749 | 744 | "add": "Add extension", |
750 | 745 | "edit": "Edit extension", |
... | ... | @@ -754,18 +749,13 @@ export default angular.module('thingsboard.locale', []) |
754 | 749 | "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.", |
755 | 750 | "converters": "Converters", |
756 | 751 | "converter-id": "Converter id", |
757 | - "converter-id-required": "Converter id is required.", | |
758 | 752 | "configuration": "Configuration", |
759 | 753 | "converter-configurations": "Converter configurations", |
760 | 754 | "token": "Security token", |
761 | 755 | "add-converter": "Add converter", |
762 | - "add-converter-prompt": "Please add converter", | |
763 | 756 | "add-config": "Add converter configuration", |
764 | - "add-config-prompt": "Please add converter configuration", | |
765 | 757 | "device-name-expression": "Device name expression", |
766 | - "device-name-expression-required": "Device name expression is required.", | |
767 | 758 | "device-type-expression": "Device type expression", |
768 | - "device-type-expression-required": "Device type expression is required.", | |
769 | 759 | "custom": "Custom", |
770 | 760 | "to-double": "To Double", |
771 | 761 | "transformer": "Transformer", |
... | ... | @@ -777,7 +767,6 @@ export default angular.module('thingsboard.locale', []) |
777 | 767 | "timeseries": "Timeseries", |
778 | 768 | "add-timeseries": "Add timeseries", |
779 | 769 | |
780 | - | |
781 | 770 | "sync": { |
782 | 771 | "status": "Status", |
783 | 772 | "sync": "Sync", |
... | ... | @@ -785,18 +774,69 @@ export default angular.module('thingsboard.locale', []) |
785 | 774 | "last-sync-time": "Last sync time", |
786 | 775 | }, |
787 | 776 | |
788 | - "opc-field-required": "Field is required", | |
777 | + | |
778 | + "field-required": "Field is required", | |
779 | + "brokers": "Brokers", | |
780 | + "add-broker": "Add broker", | |
781 | + "host": "Host", | |
782 | + "port": "Port", | |
783 | + "port-range": "Port should be in a range from 1 to 65535.", | |
784 | + "ssl": "Ssl", | |
785 | + "credentials": "Credentials", | |
786 | + "username": "Username", | |
787 | + "password": "Password", | |
788 | + "retry-interval": "Retry interval in milliseconds", | |
789 | + "anonymous": "Anonymous", | |
790 | + "basic": "Basic", | |
791 | + "pem": "PEM", | |
792 | + "ca-cert": "CA certificate file *", | |
793 | + "private-key": "Private key file *", | |
794 | + "cert": "Certificate file *", | |
795 | + "no-file": "No file selected.", | |
796 | + "drop-file": "Drop a file or click to select a file to upload.", | |
797 | + "mapping": "Mapping", | |
798 | + "topic-filter": "Topic filter", | |
799 | + "converter-type": "Converter type", | |
800 | + "converter-json": "Json", | |
801 | + "json-name-expression": "Device name json expression", | |
802 | + "topic-name-expression": "Device name topic expression", | |
803 | + "json-type-expression": "Device type json expression", | |
804 | + "topic-type-expression": "Device type topic expression", | |
805 | + "attribute-key-expression": "Attribute key expression", | |
806 | + "attr-json-key-expression": "Attribute key json expression", | |
807 | + "attr-topic-key-expression": "Attribute key topic expression", | |
808 | + "request-id-expression": "Request id expression", | |
809 | + "request-id-json-expression": "Request id json expression", | |
810 | + "request-id-topic-expression": "Request id topic expression", | |
811 | + "response-topic-expression": "Response topic expression", | |
812 | + "value-expression": "Value expression", | |
813 | + "topic": "Topic", | |
814 | + "timeout": "Timeout in milliseconds", | |
815 | + "converter-json-required": "Converter json is required.", | |
816 | + "converter-json-parse": "Unable to parse converter json.", | |
817 | + "filter-expression": "Filter expression", | |
818 | + "connect-requests": "Connect requests", | |
819 | + "add-connect-request": "Add connect request", | |
820 | + "disconnect-requests": "Disconnect requests", | |
821 | + "add-disconnect-request": "Add disconnect request", | |
822 | + "attribute-requests": "Attribute requests", | |
823 | + "add-attribute-request": "Add attribute request", | |
824 | + "attribute-updates": "Attribute updates", | |
825 | + "add-attribute-update": "Add attribute update", | |
826 | + "server-side-rpc": "Server side RPC", | |
827 | + "add-server-side-rpc-request": "Add server-side RPC request", | |
828 | + "device-name-filter": "Device name filter", | |
829 | + "attribute-filter": "Attribute filter", | |
830 | + "method-filter": "Method filter", | |
831 | + "request-topic-expression": "Request topic expression", | |
832 | + "response-timeout": "Response timeout in milliseconds", | |
833 | + "topic-expression": "Topic expression", | |
834 | + "client-scope": "Client scope", | |
789 | 835 | "opc-server": "Servers", |
790 | - "opc-add-server-hint": "Add server", | |
791 | - "opc-add-server-prompt": "Please add server", | |
792 | - "opc-server-id": "Server id", | |
793 | - "opc-timeseries": "Timeseries", | |
836 | + "opc-add-server": "Add server", | |
794 | 837 | "opc-application-name": "Application name", |
795 | 838 | "opc-application-uri": "Application uri", |
796 | - "opc-host": "Host", | |
797 | - "opc-port": "Port", | |
798 | 839 | "opc-scan-period-in-seconds": "Scan period in seconds", |
799 | - "opc-timeout-in-millis": "Timeout in milliseconds", | |
800 | 840 | "opc-security": "Security", |
801 | 841 | "opc-identity": "Identity", |
802 | 842 | "opc-keystore": "Keystore", |
... | ... | @@ -806,19 +846,14 @@ export default angular.module('thingsboard.locale', []) |
806 | 846 | "opc-keystore-password":"Password", |
807 | 847 | "opc-keystore-alias":"Alias", |
808 | 848 | "opc-keystore-key-password":"Key password", |
809 | - "opc-mapping":"Mapping", | |
810 | 849 | "opc-device-node-pattern":"Device node pattern", |
811 | 850 | "opc-device-name-pattern":"Device name pattern", |
812 | - "opc-mapping-attributes":"Mapping attributes", | |
813 | - "opc-username":"Username", | |
814 | - "opc-password":"Password", | |
815 | - "opc-add-another-server":"Add another server", | |
816 | 851 | }, |
817 | 852 | "fullscreen": { |
818 | 853 | "expand": "Expand to fullscreen", |
819 | 854 | "exit": "Exit fullscreen", |
820 | 855 | "toggle": "Toggle fullscreen mode", |
821 | - "fullscreen": "Fullscreen", | |
856 | + "fullscreen": "Fullscreen" | |
822 | 857 | }, |
823 | 858 | "function": { |
824 | 859 | "function": "Function" |
... | ... | @@ -936,7 +971,6 @@ export default angular.module('thingsboard.locale', []) |
936 | 971 | "invalid-plugin-file-error": "Unable to import plugin: Invalid plugin data structure.", |
937 | 972 | "copyId": "Copy plugin Id", |
938 | 973 | "idCopiedMessage": "Plugin Id has been copied to clipboard" |
939 | - | |
940 | 974 | }, |
941 | 975 | "position": { |
942 | 976 | "top": "Top", | ... | ... |