Showing
15 changed files
with
1626 additions
and
274 deletions
@@ -30,6 +30,7 @@ | @@ -30,6 +30,7 @@ | ||
30 | "angular-material": "1.1.1", | 30 | "angular-material": "1.1.1", |
31 | "angular-material-data-table": "^0.10.9", | 31 | "angular-material-data-table": "^0.10.9", |
32 | "angular-material-icons": "^0.7.1", | 32 | "angular-material-icons": "^0.7.1", |
33 | + "angular-material-expansion-panel": "^0.7.2", | ||
33 | "angular-messages": "1.5.8", | 34 | "angular-messages": "1.5.8", |
34 | "angular-route": "1.5.8", | 35 | "angular-route": "1.5.8", |
35 | "angular-sanitize": "1.5.8", | 36 | "angular-sanitize": "1.5.8", |
@@ -39,6 +39,7 @@ import uiRouter from 'angular-ui-router'; | @@ -39,6 +39,7 @@ import uiRouter from 'angular-ui-router'; | ||
39 | import angularJwt from 'angular-jwt'; | 39 | import angularJwt from 'angular-jwt'; |
40 | import 'angular-drag-and-drop-lists'; | 40 | import 'angular-drag-and-drop-lists'; |
41 | import mdDataTable from 'angular-material-data-table'; | 41 | import mdDataTable from 'angular-material-data-table'; |
42 | +import 'angular-material-expansion-panel'; | ||
42 | import ngTouch from 'angular-touch'; | 43 | import ngTouch from 'angular-touch'; |
43 | import 'angular-carousel'; | 44 | import 'angular-carousel'; |
44 | import 'clipboard'; | 45 | import 'clipboard'; |
@@ -82,6 +83,7 @@ import 'md-color-picker/dist/mdColorPicker.min.css'; | @@ -82,6 +83,7 @@ import 'md-color-picker/dist/mdColorPicker.min.css'; | ||
82 | import 'mdPickers/dist/mdPickers.min.css'; | 83 | import 'mdPickers/dist/mdPickers.min.css'; |
83 | import 'angular-hotkeys/build/hotkeys.min.css'; | 84 | import 'angular-hotkeys/build/hotkeys.min.css'; |
84 | import 'angular-carousel/dist/angular-carousel.min.css'; | 85 | import 'angular-carousel/dist/angular-carousel.min.css'; |
86 | +import 'angular-material-expansion-panel/dist/md-expansion-panel.min.css'; | ||
85 | import '../scss/main.scss'; | 87 | import '../scss/main.scss'; |
86 | 88 | ||
87 | import AppConfig from './app.config'; | 89 | import AppConfig from './app.config'; |
@@ -103,6 +105,7 @@ angular.module('thingsboard', [ | @@ -103,6 +105,7 @@ angular.module('thingsboard', [ | ||
103 | angularJwt, | 105 | angularJwt, |
104 | 'dndLists', | 106 | 'dndLists', |
105 | mdDataTable, | 107 | mdDataTable, |
108 | + 'material.components.expansionPanels', | ||
106 | ngTouch, | 109 | ngTouch, |
107 | 'angular-carousel', | 110 | 'angular-carousel', |
108 | 'ngclipboard', | 111 | 'ngclipboard', |
@@ -350,6 +350,20 @@ export default angular.module('thingsboard.types', []) | @@ -350,6 +350,20 @@ export default angular.module('thingsboard.types', []) | ||
350 | name: "extension.pem" | 350 | name: "extension.pem" |
351 | } | 351 | } |
352 | }, | 352 | }, |
353 | + extensionOpcSecurityTypes: { | ||
354 | + Basic128Rsa15: "Basic128Rsa15", | ||
355 | + Basic256: "Basic256", | ||
356 | + Basic256Sha256: "Basic256Sha256", | ||
357 | + None: "None" | ||
358 | + }, | ||
359 | + extensionIdentityType: { | ||
360 | + anonymous: "extension.anonymous", | ||
361 | + username: "extension.username" | ||
362 | + }, | ||
363 | + extensionKeystoreType: { | ||
364 | + PKCS12: "PKCS12", | ||
365 | + JKS: "JKS" | ||
366 | + }, | ||
353 | latestTelemetry: { | 367 | latestTelemetry: { |
354 | value: "LATEST_TELEMETRY", | 368 | value: "LATEST_TELEMETRY", |
355 | name: "attribute.scope-latest-telemetry", | 369 | name: "attribute.scope-latest-telemetry", |
@@ -29,45 +29,88 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | @@ -29,45 +29,88 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | ||
29 | vm.entityId = entityId; | 29 | vm.entityId = entityId; |
30 | vm.allExtensions = allExtensions; | 30 | vm.allExtensions = allExtensions; |
31 | 31 | ||
32 | - vm.configuration = {}; | ||
33 | - vm.newExtension = {id:"",type:"",configuration:vm.configuration}; | ||
34 | 32 | ||
35 | - if(!vm.isAdd) { | ||
36 | - vm.newExtension = angular.copy(extension); | ||
37 | - vm.configuration = vm.newExtension.configuration; | ||
38 | - editTransformers(vm.newExtension); | 33 | + if (extension) { |
34 | + vm.extension = angular.copy(extension); | ||
35 | + editTransformers(vm.extension); | ||
36 | + } else { | ||
37 | + vm.extension = {}; | ||
39 | } | 38 | } |
40 | 39 | ||
41 | - vm.cancel = cancel; | ||
42 | - vm.save = save; | ||
43 | 40 | ||
41 | + vm.extensionTypeChange = function () { | ||
42 | + | ||
43 | + if (vm.extension.type === "HTTP") { | ||
44 | + vm.extension.configuration = { | ||
45 | + "converterConfigurations": [] | ||
46 | + }; | ||
47 | + } | ||
48 | + if (vm.extension.type === "MQTT") { | ||
49 | + vm.extension.configuration = { | ||
50 | + "brokers": [] | ||
51 | + }; | ||
52 | + } | ||
53 | + if (vm.extension.type === "OPC UA") { | ||
54 | + vm.extension.configuration = { | ||
55 | + "servers": [] | ||
56 | + }; | ||
57 | + } | ||
58 | + }; | ||
59 | + | ||
60 | + vm.cancel = cancel; | ||
44 | function cancel() { | 61 | function cancel() { |
45 | $mdDialog.cancel(); | 62 | $mdDialog.cancel(); |
46 | } | 63 | } |
64 | + | ||
65 | + vm.save = save; | ||
47 | function save() { | 66 | function save() { |
48 | - $mdDialog.hide(); | ||
49 | - saveTransformers(); | ||
50 | - if(vm.isAdd) { | ||
51 | - vm.allExtensions.push(vm.newExtension); | 67 | + let $errorElement = angular.element('[name=theForm]').find('.ng-invalid'); |
68 | + | ||
69 | + if ($errorElement.length) { | ||
70 | + | ||
71 | + let $mdDialogScroll = angular.element('md-dialog-content').scrollTop(); | ||
72 | + let $mdDialogTop = angular.element('md-dialog-content').offset().top; | ||
73 | + let $errorElementTop = angular.element('[name=theForm]').find('.ng-invalid').eq(0).offset().top; | ||
74 | + | ||
75 | + | ||
76 | + if ($errorElementTop !== $mdDialogTop) { | ||
77 | + angular.element('md-dialog-content').animate({ | ||
78 | + scrollTop: $mdDialogScroll + ($errorElementTop - $mdDialogTop) - 50 | ||
79 | + }, 500); | ||
80 | + $errorElement.eq(0).focus(); | ||
81 | + } | ||
52 | } else { | 82 | } else { |
53 | - var index = vm.allExtensions.indexOf(extension); | ||
54 | - if(index > -1) { | ||
55 | - vm.allExtensions[index] = vm.newExtension; | 83 | + |
84 | + if(vm.isAdd) { | ||
85 | + vm.allExtensions.push(vm.extension); | ||
86 | + } else { | ||
87 | + var index = vm.allExtensions.indexOf(extension); | ||
88 | + if(index > -1) { | ||
89 | + vm.allExtensions[index] = vm.extension; | ||
90 | + } | ||
56 | } | 91 | } |
57 | - } | ||
58 | 92 | ||
59 | - var editedValue = angular.toJson(vm.allExtensions); | 93 | + $mdDialog.hide(); |
94 | + saveTransformers(); | ||
60 | 95 | ||
61 | - attributeService.saveEntityAttributes(vm.entityType, vm.entityId, types.attributesScope.shared.value, [{key:"configuration", value:editedValue}]).then( | ||
62 | - function success() { | ||
63 | - $scope.theForm.$setPristine(); | ||
64 | - } | ||
65 | - ); | 96 | + var editedValue = angular.toJson(vm.allExtensions); |
97 | + | ||
98 | + attributeService | ||
99 | + .saveEntityAttributes( | ||
100 | + vm.entityType, | ||
101 | + vm.entityId, | ||
102 | + types.attributesScope.shared.value, | ||
103 | + [{key:"configuration", value:editedValue}] | ||
104 | + ) | ||
105 | + .then(function success() { | ||
106 | + }); | ||
107 | + | ||
108 | + } | ||
66 | } | 109 | } |
67 | 110 | ||
68 | vm.validateId = function() { | 111 | vm.validateId = function() { |
69 | var coincidenceArray = vm.allExtensions.filter(function(ext) { | 112 | var coincidenceArray = vm.allExtensions.filter(function(ext) { |
70 | - return ext.id == vm.newExtension.id; | 113 | + return ext.id == vm.extension.id; |
71 | }); | 114 | }); |
72 | if(coincidenceArray.length) { | 115 | if(coincidenceArray.length) { |
73 | if(!vm.isAdd) { | 116 | if(!vm.isAdd) { |
@@ -82,11 +125,11 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | @@ -82,11 +125,11 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | ||
82 | } else { | 125 | } else { |
83 | $scope.theForm.extensionId.$setValidity('uniqueIdValidation', true); | 126 | $scope.theForm.extensionId.$setValidity('uniqueIdValidation', true); |
84 | } | 127 | } |
85 | - } | 128 | + }; |
86 | 129 | ||
87 | function saveTransformers() { | 130 | function saveTransformers() { |
88 | - if(vm.newExtension.type == types.extensionType.http) { | ||
89 | - var config = vm.newExtension.configuration.converterConfigurations; | 131 | + if(vm.extension.type == types.extensionType.http) { |
132 | + var config = vm.extension.configuration.converterConfigurations; | ||
90 | if(config && config.length > 0) { | 133 | if(config && config.length > 0) { |
91 | for(let i=0;i<config.length;i++) { | 134 | for(let i=0;i<config.length;i++) { |
92 | for(let j=0;j<config[i].converters.length;j++){ | 135 | for(let j=0;j<config[i].converters.length;j++){ |
@@ -106,8 +149,8 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | @@ -106,8 +149,8 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | ||
106 | } | 149 | } |
107 | } | 150 | } |
108 | } | 151 | } |
109 | - if(vm.newExtension.type == types.extensionType.mqtt) { | ||
110 | - var brokers = vm.newExtension.configuration.brokers; | 152 | + if(vm.extension.type == types.extensionType.mqtt) { |
153 | + var brokers = vm.extension.configuration.brokers; | ||
111 | if(brokers && brokers.length > 0) { | 154 | if(brokers && brokers.length > 0) { |
112 | for(let i=0;i<brokers.length;i++) { | 155 | for(let i=0;i<brokers.length;i++) { |
113 | if(brokers[i].mapping && brokers[i].mapping.length > 0) { | 156 | if(brokers[i].mapping && brokers[i].mapping.length > 0) { |
@@ -119,6 +162,27 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | @@ -119,6 +162,27 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | ||
119 | delete brokers[i].mapping[j].converterType; | 162 | delete brokers[i].mapping[j].converterType; |
120 | } | 163 | } |
121 | } | 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; | ||
184 | + } | ||
185 | + } | ||
122 | } | 186 | } |
123 | } | 187 | } |
124 | } | 188 | } |
@@ -174,6 +238,43 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | @@ -174,6 +238,43 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, | ||
174 | } | 238 | } |
175 | } | 239 | } |
176 | } | 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 | + } | ||
177 | } | 278 | } |
178 | } | 279 | } |
179 | } | 280 | } |
@@ -15,8 +15,8 @@ | @@ -15,8 +15,8 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<md-dialog aria-label="{{ (vm.isAdd ? 'extension.add' : 'extension.edit' ) | translate }}" style="min-width: 1000px;"> | ||
19 | - <form name="theForm" ng-submit="vm.save()"> | 18 | +<md-dialog class="extensionDialog" aria-label="{{ (vm.isAdd ? 'extension.add' : 'extension.edit' ) | translate }}"> |
19 | + <form name="theForm" ng-submit="vm.save()" novalidate> | ||
20 | <md-toolbar> | 20 | <md-toolbar> |
21 | <div class="md-toolbar-tools"> | 21 | <div class="md-toolbar-tools"> |
22 | <h2 translate>{{ vm.isAdd ? 'extension.add' : 'extension.edit'}}</h2> | 22 | <h2 translate>{{ vm.isAdd ? 'extension.add' : 'extension.edit'}}</h2> |
@@ -26,49 +26,54 @@ | @@ -26,49 +26,54 @@ | ||
26 | </md-button> | 26 | </md-button> |
27 | </div> | 27 | </div> |
28 | </md-toolbar> | 28 | </md-toolbar> |
29 | + | ||
29 | <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> | 30 | <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
31 | + | ||
30 | <span style="min-height: 5px;" flex="" ng-show="!loading"></span> | 32 | <span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
33 | + | ||
31 | <md-dialog-content> | 34 | <md-dialog-content> |
32 | <div class="md-dialog-content"> | 35 | <div class="md-dialog-content"> |
33 | <md-content class="md-padding" layout="column"> | 36 | <md-content class="md-padding" layout="column"> |
34 | <fieldset ng-disabled="loading"> | 37 | <fieldset ng-disabled="loading"> |
35 | <section flex layout="row"> | 38 | <section flex layout="row"> |
36 | - <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"> |
37 | <label translate>extension.extension-id</label> | 40 | <label translate>extension.extension-id</label> |
38 | - <input required name="extensionId" ng-model="vm.newExtension.id" ng-change="vm.validateId()"> | 41 | + <input required name="extensionId" ng-model="vm.extension.id" ng-change="vm.validateId()"> |
39 | <div ng-messages="theForm.extensionId.$error"> | 42 | <div ng-messages="theForm.extensionId.$error"> |
40 | - <div translate ng-message="required">extension.id-required</div> | 43 | + <div translate ng-message="required">extension.field-required</div> |
41 | <div translate ng-message="uniqueIdValidation">extension.unique-id-required</div> | 44 | <div translate ng-message="uniqueIdValidation">extension.unique-id-required</div> |
42 | </div> | 45 | </div> |
43 | </md-input-container> | 46 | </md-input-container> |
44 | - <md-input-container flex="40" class="md-block"> | 47 | + |
48 | + <md-input-container flex="40" class="md-block" md-is-error="theForm.extensionType.$touched && theForm.extensionType.$invalid"> | ||
45 | <label translate>extension.extension-type</label> | 49 | <label translate>extension.extension-type</label> |
46 | - <md-select ng-disabled="!vm.isAdd" required name="extensionType" ng-model="vm.newExtension.type"> | 50 | + |
51 | + <md-select ng-disabled="!vm.isAdd" required name="extensionType" ng-change="vm.extensionTypeChange()" ng-model="vm.extension.type"> | ||
47 | <md-option ng-repeat="(key,value) in vm.types.extensionType" ng-value="value"> | 52 | <md-option ng-repeat="(key,value) in vm.types.extensionType" ng-value="value"> |
48 | {{value}} | 53 | {{value}} |
49 | </md-option> | 54 | </md-option> |
50 | </md-select> | 55 | </md-select> |
56 | + | ||
51 | <div ng-messages="theForm.extensionType.$error"> | 57 | <div ng-messages="theForm.extensionType.$error"> |
52 | - <div translate ng-message="required">extension.type-required</div> | 58 | + <div translate ng-message="required">extension.field-required</div> |
53 | </div> | 59 | </div> |
54 | </md-input-container> | 60 | </md-input-container> |
55 | </section> | 61 | </section> |
56 | - | ||
57 | - <div tb-extension-form-http config="vm.configuration" is-add="vm.isAdd" ng-if="vm.newExtension.type && vm.newExtension.type == vm.types.extensionType.http"></div> | ||
58 | - <div tb-extension-form-mqtt config="vm.configuration" is-add="vm.isAdd" ng-if="vm.newExtension.type && vm.newExtension.type == vm.types.extensionType.mqtt"></div> | ||
59 | - | 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> | ||
60 | </fieldset> | 65 | </fieldset> |
61 | - | ||
62 | - <!--<div>{{vm.newExtension}}</div>--> | ||
63 | </md-content> | 66 | </md-content> |
64 | </div> | 67 | </div> |
65 | </md-dialog-content> | 68 | </md-dialog-content> |
69 | + | ||
66 | <md-dialog-actions layout="row"> | 70 | <md-dialog-actions layout="row"> |
67 | - <span flex></span> | ||
68 | - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" | ||
69 | - class="md-raised md-primary"> | 71 | + <md-button type="submit" |
72 | + class="md-raised md-primary" | ||
73 | + > | ||
70 | {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} | 74 | {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} |
71 | </md-button> | 75 | </md-button> |
76 | + | ||
72 | <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }} | 77 | <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }} |
73 | </md-button> | 78 | </md-button> |
74 | </md-dialog-actions> | 79 | </md-dialog-actions> |
@@ -126,11 +126,13 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, | @@ -126,11 +126,13 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, | ||
126 | controllerAs: 'vm', | 126 | controllerAs: 'vm', |
127 | templateUrl: extensionDialogTemplate, | 127 | templateUrl: extensionDialogTemplate, |
128 | parent: angular.element($document[0].body), | 128 | parent: angular.element($document[0].body), |
129 | - locals: { isAdd: isAdd, | ||
130 | - allExtensions: vm.allExtensions, | ||
131 | - entityId: vm.entityId, | ||
132 | - entityType: vm.entityType, | ||
133 | - extension: extension}, | 129 | + locals: { |
130 | + isAdd: isAdd, | ||
131 | + allExtensions: vm.allExtensions, | ||
132 | + entityId: vm.entityId, | ||
133 | + entityType: vm.entityType, | ||
134 | + extension: extension | ||
135 | + }, | ||
134 | bindToController: true, | 136 | bindToController: true, |
135 | targetEvent: $event, | 137 | targetEvent: $event, |
136 | fullscreen: true, | 138 | fullscreen: true, |
@@ -53,74 +53,62 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -53,74 +53,62 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
53 | } | 53 | } |
54 | }; | 54 | }; |
55 | 55 | ||
56 | - if(scope.isAdd) { | ||
57 | - scope.converterConfigs = []; | ||
58 | - scope.config.converterConfigurations = scope.converterConfigs; | ||
59 | - } else { | ||
60 | - scope.converterConfigs = scope.config.converterConfigurations; | ||
61 | - } | ||
62 | - | ||
63 | - scope.updateValidity = function() { | ||
64 | - var valid = scope.converterConfigs && scope.converterConfigs.length > 0; | ||
65 | - scope.theForm.$setValidity('converterConfigs', valid); | ||
66 | - if(scope.converterConfigs.length) { | ||
67 | - for(let i=0;i<scope.converterConfigs.length;i++) { | ||
68 | - if(!scope.converterConfigs[i].converters.length) { | ||
69 | - scope.theForm.$setValidity('converters', false); | ||
70 | - break; | ||
71 | - } else { | ||
72 | - scope.theForm.$setValidity('converters', true); | ||
73 | - } | ||
74 | - } | ||
75 | - } | ||
76 | - }; | ||
77 | - | ||
78 | - scope.$watch('converterConfigs', function() { | ||
79 | - scope.updateValidity(); | ||
80 | - }, true); | ||
81 | 56 | ||
82 | scope.addConverterConfig = function() { | 57 | scope.addConverterConfig = function() { |
83 | var newConverterConfig = {converterId:"", converters:[]}; | 58 | var newConverterConfig = {converterId:"", converters:[]}; |
84 | scope.converterConfigs.push(newConverterConfig); | 59 | scope.converterConfigs.push(newConverterConfig); |
85 | - } | 60 | + |
61 | + scope.converterConfigs[scope.converterConfigs.length - 1].converters = []; | ||
62 | + scope.addConverter(scope.converterConfigs[scope.converterConfigs.length - 1].converters); | ||
63 | + }; | ||
86 | 64 | ||
87 | scope.removeConverterConfig = function(config) { | 65 | scope.removeConverterConfig = function(config) { |
88 | var index = scope.converterConfigs.indexOf(config); | 66 | var index = scope.converterConfigs.indexOf(config); |
89 | if (index > -1) { | 67 | if (index > -1) { |
90 | scope.converterConfigs.splice(index, 1); | 68 | scope.converterConfigs.splice(index, 1); |
91 | } | 69 | } |
92 | - scope.theForm.$setDirty(); | ||
93 | - } | 70 | + }; |
94 | 71 | ||
95 | scope.addConverter = function(converters) { | 72 | scope.addConverter = function(converters) { |
96 | - var newConverter = {deviceNameJsonExpression:"", deviceTypeJsonExpression:"", attributes:[], timeseries:[]}; | 73 | + var newConverter = { |
74 | + deviceNameJsonExpression:"", | ||
75 | + deviceTypeJsonExpression:"", | ||
76 | + attributes:[], | ||
77 | + timeseries:[] | ||
78 | + }; | ||
97 | converters.push(newConverter); | 79 | converters.push(newConverter); |
98 | - } | 80 | + }; |
99 | 81 | ||
100 | scope.removeConverter = function(converter, converters) { | 82 | scope.removeConverter = function(converter, converters) { |
101 | var index = converters.indexOf(converter); | 83 | var index = converters.indexOf(converter); |
102 | if (index > -1) { | 84 | if (index > -1) { |
103 | converters.splice(index, 1); | 85 | converters.splice(index, 1); |
104 | } | 86 | } |
105 | - scope.theForm.$setDirty(); | ||
106 | - } | 87 | + }; |
107 | 88 | ||
108 | scope.addAttribute = function(attributes) { | 89 | scope.addAttribute = function(attributes) { |
109 | var newAttribute = {type:"", key:"", value:""}; | 90 | var newAttribute = {type:"", key:"", value:""}; |
110 | attributes.push(newAttribute); | 91 | attributes.push(newAttribute); |
111 | - } | 92 | + }; |
112 | 93 | ||
113 | scope.removeAttribute = function(attribute, attributes) { | 94 | scope.removeAttribute = function(attribute, attributes) { |
114 | var index = attributes.indexOf(attribute); | 95 | var index = attributes.indexOf(attribute); |
115 | if (index > -1) { | 96 | if (index > -1) { |
116 | attributes.splice(index, 1); | 97 | attributes.splice(index, 1); |
117 | } | 98 | } |
118 | - scope.theForm.$setDirty(); | 99 | + }; |
100 | + | ||
101 | + | ||
102 | + if(scope.isAdd) { | ||
103 | + scope.converterConfigs = scope.config.converterConfigurations; | ||
104 | + scope.addConverterConfig(); | ||
105 | + } else { | ||
106 | + scope.converterConfigs = scope.config.converterConfigurations; | ||
119 | } | 107 | } |
120 | 108 | ||
121 | scope.transformerTypeChange = function(attribute) { | 109 | scope.transformerTypeChange = function(attribute) { |
122 | attribute.transformer = ""; | 110 | attribute.transformer = ""; |
123 | - } | 111 | + }; |
124 | 112 | ||
125 | scope.validateTransformer = function (model, editorName) { | 113 | scope.validateTransformer = function (model, editorName) { |
126 | if(model && model.length) { | 114 | if(model && model.length) { |
@@ -131,10 +119,10 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -131,10 +119,10 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
131 | scope.theForm[editorName].$setValidity('transformerJSON', false); | 119 | scope.theForm[editorName].$setValidity('transformerJSON', false); |
132 | } | 120 | } |
133 | } | 121 | } |
134 | - } | ||
135 | - | 122 | + }; |
123 | + | ||
136 | $compile(element.contents())(scope); | 124 | $compile(element.contents())(scope); |
137 | - } | 125 | + }; |
138 | 126 | ||
139 | return { | 127 | return { |
140 | restrict: "A", | 128 | restrict: "A", |
@@ -23,18 +23,19 @@ | @@ -23,18 +23,19 @@ | ||
23 | </md-card-title> | 23 | </md-card-title> |
24 | <md-card-content> | 24 | <md-card-content> |
25 | <v-accordion id="http-converter-configs-accordion" class="vAccordion--default"> | 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 | <v-pane-header> | 27 | <v-pane-header> |
28 | {{ 'extension.converter-configurations' | translate }} | 28 | {{ 'extension.converter-configurations' | translate }} |
29 | </v-pane-header> | 29 | </v-pane-header> |
30 | <v-pane-content> | 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 | <div ng-if="converterConfigs.length > 0"> | 31 | <div ng-if="converterConfigs.length > 0"> |
35 | <ol class="list-group"> | 32 | <ol class="list-group"> |
36 | - <li class="list-group-item" ng-repeat="(configIndex,config) in converterConfigs"> | ||
37 | - <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeConverterConfig(config)"> | 33 | + <li class="list-group-item" ng-repeat="(configIndex, config) in converterConfigs"> |
34 | + <md-button aria-label="{{ 'action.remove' | translate }}" | ||
35 | + class="md-icon-button" | ||
36 | + ng-click="removeConverterConfig(config)" | ||
37 | + ng-hide="converterConfigs.length < 2" | ||
38 | + > | ||
38 | <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | 39 | <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> |
39 | <md-tooltip md-direction="top"> | 40 | <md-tooltip md-direction="top"> |
40 | {{ 'action.remove' | translate }} | 41 | {{ 'action.remove' | translate }} |
@@ -43,11 +44,11 @@ | @@ -43,11 +44,11 @@ | ||
43 | <md-card> | 44 | <md-card> |
44 | <md-card-content> | 45 | <md-card-content> |
45 | 46 | ||
46 | - <md-input-container class="md-block"> | 47 | + <md-input-container class="md-block" md-is-error="theForm['httpConverterId_' + configIndex].$touched && theForm['httpConverterId_' + configIndex].$invalid"> |
47 | <label translate>extension.converter-id</label> | 48 | <label translate>extension.converter-id</label> |
48 | <input required name="httpConverterId_{{configIndex}}" ng-model="config.converterId"> | 49 | <input required name="httpConverterId_{{configIndex}}" ng-model="config.converterId"> |
49 | <div ng-messages="theForm['httpConverterId_' + configIndex].$error"> | 50 | <div ng-messages="theForm['httpConverterId_' + configIndex].$error"> |
50 | - <div translate ng-message="required">extension.converter-id-required</div> | 51 | + <div translate ng-message="required">extension.field-required</div> |
51 | </div> | 52 | </div> |
52 | </md-input-container> | 53 | </md-input-container> |
53 | <md-input-container class="md-block"> | 54 | <md-input-container class="md-block"> |
@@ -55,18 +56,21 @@ | @@ -55,18 +56,21 @@ | ||
55 | <input name="httpToken" ng-model="config.token" parse-to-null> | 56 | <input name="httpToken" ng-model="config.token" parse-to-null> |
56 | </md-input-container> | 57 | </md-input-container> |
57 | <v-accordion id="http-converters-accordion" class="vAccordion--default"> | 58 | <v-accordion id="http-converters-accordion" class="vAccordion--default"> |
58 | - <v-pane id="http-converters-pane"> | 59 | + <v-pane id="http-converters-pane" expanded="true"> |
59 | <v-pane-header> | 60 | <v-pane-header> |
60 | {{ 'extension.converters' | translate }} | 61 | {{ 'extension.converters' | translate }} |
61 | </v-pane-header> | 62 | </v-pane-header> |
62 | <v-pane-content> | 63 | <v-pane-content> |
63 | - <div ng-if="config.converters.length === 0"> | ||
64 | - <span translate layout-align="center center" class="tb-prompt">extension.add-converter-prompt</span> | ||
65 | - </div> | ||
66 | <div ng-if="config.converters.length > 0"> | 64 | <div ng-if="config.converters.length > 0"> |
67 | <ol class="list-group"> | 65 | <ol class="list-group"> |
68 | - <li class="list-group-item" ng-repeat="(converterIndex,converter) in config.converters"> | ||
69 | - <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeConverter(converter, config.converters)"> | 66 | + <li class="list-group-item" |
67 | + ng-repeat="(converterIndex,converter) in config.converters" | ||
68 | + > | ||
69 | + <md-button aria-label="{{ 'action.remove' | translate }}" | ||
70 | + class="md-icon-button" | ||
71 | + ng-click="removeConverter(converter, config.converters)" | ||
72 | + ng-hide="config.converters.length < 2" | ||
73 | + > | ||
70 | <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | 74 | <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> |
71 | <md-tooltip md-direction="top"> | 75 | <md-tooltip md-direction="top"> |
72 | {{ 'action.remove' | translate }} | 76 | {{ 'action.remove' | translate }} |
@@ -74,18 +78,18 @@ | @@ -74,18 +78,18 @@ | ||
74 | </md-button> | 78 | </md-button> |
75 | <md-card> | 79 | <md-card> |
76 | <md-card-content> | 80 | <md-card-content> |
77 | - <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"> |
78 | <label translate>extension.device-name-expression</label> | 82 | <label translate>extension.device-name-expression</label> |
79 | <input required name="httpDeviceNameExp_{{configIndex}}{{converterIndex}}" ng-model="converter.deviceNameJsonExpression"> | 83 | <input required name="httpDeviceNameExp_{{configIndex}}{{converterIndex}}" ng-model="converter.deviceNameJsonExpression"> |
80 | <div ng-messages="theForm['httpDeviceNameExp_' + configIndex + converterIndex].$error"> | 84 | <div ng-messages="theForm['httpDeviceNameExp_' + configIndex + converterIndex].$error"> |
81 | - <div translate ng-message="required">extension.device-name-expression-required</div> | 85 | + <div translate ng-message="required">extension.field-required</div> |
82 | </div> | 86 | </div> |
83 | </md-input-container> | 87 | </md-input-container> |
84 | - <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"> |
85 | <label translate>extension.device-type-expression</label> | 89 | <label translate>extension.device-type-expression</label> |
86 | <input required name="httpDeviceTypeExp_{{configIndex}}{{converterIndex}}" ng-model="converter.deviceTypeJsonExpression"> | 90 | <input required name="httpDeviceTypeExp_{{configIndex}}{{converterIndex}}" ng-model="converter.deviceTypeJsonExpression"> |
87 | <div ng-messages="theForm['httpDeviceTypeExp_' + configIndex + converterIndex].$error"> | 91 | <div ng-messages="theForm['httpDeviceTypeExp_' + configIndex + converterIndex].$error"> |
88 | - <div translate ng-message="required">extension.device-type-expression-required</div> | 92 | + <div translate ng-message="required">extension.field-required</div> |
89 | </div> | 93 | </div> |
90 | </md-input-container> | 94 | </md-input-container> |
91 | 95 | ||
@@ -107,14 +111,14 @@ | @@ -107,14 +111,14 @@ | ||
107 | <md-card> | 111 | <md-card> |
108 | <md-card-content> | 112 | <md-card-content> |
109 | <section flex layout="row"> | 113 | <section flex layout="row"> |
110 | - <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"> |
111 | <label translate>extension.key</label> | 115 | <label translate>extension.key</label> |
112 | <input required name="httpAttributeKey_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.key"> | 116 | <input required name="httpAttributeKey_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.key"> |
113 | <div ng-messages="theForm['httpAttributeKey_' + configIndex + converterIndex + attributeIndex].$error"> | 117 | <div ng-messages="theForm['httpAttributeKey_' + configIndex + converterIndex + attributeIndex].$error"> |
114 | - <div translate ng-message="required">extension.required-key</div> | 118 | + <div translate ng-message="required">extension.field-required</div> |
115 | </div> | 119 | </div> |
116 | </md-input-container> | 120 | </md-input-container> |
117 | - <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"> |
118 | <label translate>extension.type</label> | 122 | <label translate>extension.type</label> |
119 | <md-select required name="httpAttributeType_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.type"> | 123 | <md-select required name="httpAttributeType_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.type"> |
120 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> | 124 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> |
@@ -122,16 +126,16 @@ | @@ -122,16 +126,16 @@ | ||
122 | </md-option> | 126 | </md-option> |
123 | </md-select> | 127 | </md-select> |
124 | <div ng-messages="theForm['httpAttributeType_' + configIndex + converterIndex + attributeIndex].$error"> | 128 | <div ng-messages="theForm['httpAttributeType_' + configIndex + converterIndex + attributeIndex].$error"> |
125 | - <div translate ng-message="required">extension.required-type</div> | 129 | + <div translate ng-message="required">extension.field-required</div> |
126 | </div> | 130 | </div> |
127 | </md-input-container> | 131 | </md-input-container> |
128 | </section> | 132 | </section> |
129 | <section flex layout="row"> | 133 | <section flex layout="row"> |
130 | - <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"> |
131 | <label translate>extension.value</label> | 135 | <label translate>extension.value</label> |
132 | <input required name="httpAttributeValue_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.value"> | 136 | <input required name="httpAttributeValue_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.value"> |
133 | <div ng-messages="theForm['httpAttributeValue_' + configIndex + converterIndex + attributeIndex].$error"> | 137 | <div ng-messages="theForm['httpAttributeValue_' + configIndex + converterIndex + attributeIndex].$error"> |
134 | - <div translate ng-message="required">extension.required-value</div> | 138 | + <div translate ng-message="required">extension.field-required</div> |
135 | </div> | 139 | </div> |
136 | </md-input-container> | 140 | </md-input-container> |
137 | 141 | ||
@@ -172,11 +176,8 @@ | @@ -172,11 +176,8 @@ | ||
172 | <div flex layout="row" layout-align="start center"> | 176 | <div flex layout="row" layout-align="start center"> |
173 | <md-button class="md-primary md-raised" | 177 | <md-button class="md-primary md-raised" |
174 | ng-click="addAttribute(converter.attributes)" aria-label="{{ 'action.add' | translate }}"> | 178 | ng-click="addAttribute(converter.attributes)" aria-label="{{ 'action.add' | translate }}"> |
175 | - <md-tooltip md-direction="top"> | ||
176 | - {{ 'extension.add-attribute' | translate }} | ||
177 | - </md-tooltip> | ||
178 | <md-icon class="material-icons">add</md-icon> | 179 | <md-icon class="material-icons">add</md-icon> |
179 | - <span translate>action.add</span> | 180 | + <span translate>extension.add-attribute</span> |
180 | </md-button> | 181 | </md-button> |
181 | </div> | 182 | </div> |
182 | </v-pane-content> | 183 | </v-pane-content> |
@@ -202,14 +203,14 @@ | @@ -202,14 +203,14 @@ | ||
202 | <md-card> | 203 | <md-card> |
203 | <md-card-content> | 204 | <md-card-content> |
204 | <section flex layout="row"> | 205 | <section flex layout="row"> |
205 | - <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"> |
206 | <label translate>extension.key</label> | 207 | <label translate>extension.key</label> |
207 | <input required name="httpTimeseriesKey_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.key"> | 208 | <input required name="httpTimeseriesKey_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.key"> |
208 | <div ng-messages="theForm['httpTimeseriesKey_' + configIndex + converterIndex + timeseriesIndex].$error"> | 209 | <div ng-messages="theForm['httpTimeseriesKey_' + configIndex + converterIndex + timeseriesIndex].$error"> |
209 | - <div translate ng-message="required">extension.required-key</div> | 210 | + <div translate ng-message="required">extension.field-required</div> |
210 | </div> | 211 | </div> |
211 | </md-input-container> | 212 | </md-input-container> |
212 | - <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"> |
213 | <label translate>extension.type</label> | 214 | <label translate>extension.type</label> |
214 | <md-select required name="httpTimeseriesType_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.type"> | 215 | <md-select required name="httpTimeseriesType_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.type"> |
215 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> | 216 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> |
@@ -217,16 +218,16 @@ | @@ -217,16 +218,16 @@ | ||
217 | </md-option> | 218 | </md-option> |
218 | </md-select> | 219 | </md-select> |
219 | <div ng-messages="theForm['httpTimeseriesType_' + configIndex + converterIndex + timeseriesIndex].$error"> | 220 | <div ng-messages="theForm['httpTimeseriesType_' + configIndex + converterIndex + timeseriesIndex].$error"> |
220 | - <div translate ng-message="required">extension.required-type</div> | 221 | + <div translate ng-message="required">extension.field-required</div> |
221 | </div> | 222 | </div> |
222 | </md-input-container> | 223 | </md-input-container> |
223 | </section> | 224 | </section> |
224 | <section flex layout="row"> | 225 | <section flex layout="row"> |
225 | - <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"> |
226 | <label translate>extension.value</label> | 227 | <label translate>extension.value</label> |
227 | <input required name="httpTimeseriesValue_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.value"> | 228 | <input required name="httpTimeseriesValue_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.value"> |
228 | <div ng-messages="theForm['httpTimeseriesValue_' + configIndex + converterIndex + timeseriesIndex].$error"> | 229 | <div ng-messages="theForm['httpTimeseriesValue_' + configIndex + converterIndex + timeseriesIndex].$error"> |
229 | - <div translate ng-message="required">extension.required-value</div> | 230 | + <div translate ng-message="required">extension.field-required</div> |
230 | </div> | 231 | </div> |
231 | </md-input-container> | 232 | </md-input-container> |
232 | 233 | ||
@@ -267,11 +268,8 @@ | @@ -267,11 +268,8 @@ | ||
267 | <div flex layout="row" layout-align="start center"> | 268 | <div flex layout="row" layout-align="start center"> |
268 | <md-button class="md-primary md-raised" | 269 | <md-button class="md-primary md-raised" |
269 | ng-click="addAttribute(converter.timeseries)" aria-label="{{ 'action.add' | translate }}"> | 270 | ng-click="addAttribute(converter.timeseries)" aria-label="{{ 'action.add' | translate }}"> |
270 | - <md-tooltip md-direction="top"> | ||
271 | - {{ 'extension.add-timeseries' | translate }} | ||
272 | - </md-tooltip> | ||
273 | <md-icon class="material-icons">add</md-icon> | 271 | <md-icon class="material-icons">add</md-icon> |
274 | - <span translate>action.add</span> | 272 | + <span translate>extension.add-timeseries</span> |
275 | </md-button> | 273 | </md-button> |
276 | </div> | 274 | </div> |
277 | </v-pane-content> | 275 | </v-pane-content> |
@@ -285,11 +283,8 @@ | @@ -285,11 +283,8 @@ | ||
285 | <div flex layout="row" layout-align="start center"> | 283 | <div flex layout="row" layout-align="start center"> |
286 | <md-button class="md-primary md-raised" | 284 | <md-button class="md-primary md-raised" |
287 | ng-click="addConverter(config.converters)" aria-label="{{ 'action.add' | translate }}"> | 285 | ng-click="addConverter(config.converters)" aria-label="{{ 'action.add' | translate }}"> |
288 | - <md-tooltip md-direction="top"> | ||
289 | - {{ 'extension.add-converter' | translate }} | ||
290 | - </md-tooltip> | ||
291 | <md-icon class="material-icons">add</md-icon> | 286 | <md-icon class="material-icons">add</md-icon> |
292 | - <span translate>action.add</span> | 287 | + <span translate>extension.add-converter</span> |
293 | </md-button> | 288 | </md-button> |
294 | </div> | 289 | </div> |
295 | </v-pane-content> | 290 | </v-pane-content> |
@@ -304,11 +299,8 @@ | @@ -304,11 +299,8 @@ | ||
304 | <div flex layout="row" layout-align="start center"> | 299 | <div flex layout="row" layout-align="start center"> |
305 | <md-button class="md-primary md-raised" | 300 | <md-button class="md-primary md-raised" |
306 | ng-click="addConverterConfig()" aria-label="{{ 'action.add' | translate }}"> | 301 | ng-click="addConverterConfig()" aria-label="{{ 'action.add' | translate }}"> |
307 | - <md-tooltip md-direction="top"> | ||
308 | - {{ 'extension.add-config' | translate }} | ||
309 | - </md-tooltip> | ||
310 | <md-icon class="material-icons">add</md-icon> | 302 | <md-icon class="material-icons">add</md-icon> |
311 | - <span translate>action.add</span> | 303 | + <span translate>extension.add-config</span> |
312 | </md-button> | 304 | </md-button> |
313 | </div> | 305 | </div> |
314 | </v-pane-content> | 306 | </v-pane-content> |
@@ -33,14 +33,22 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -33,14 +33,22 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
33 | scope.types = types; | 33 | scope.types = types; |
34 | scope.theForm = scope.$parent.theForm; | 34 | scope.theForm = scope.$parent.theForm; |
35 | 35 | ||
36 | - scope.nameExpressions = { | 36 | + scope.deviceNameExpressions = { |
37 | deviceNameJsonExpression: "extension.converter-json", | 37 | deviceNameJsonExpression: "extension.converter-json", |
38 | deviceNameTopicExpression: "extension.topic" | 38 | deviceNameTopicExpression: "extension.topic" |
39 | }; | 39 | }; |
40 | - scope.typeExpressions = { | 40 | + scope.deviceTypeExpressions = { |
41 | deviceTypeJsonExpression: "extension.converter-json", | 41 | deviceTypeJsonExpression: "extension.converter-json", |
42 | deviceTypeTopicExpression: "extension.topic" | 42 | deviceTypeTopicExpression: "extension.topic" |
43 | }; | 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 | + } | ||
44 | 52 | ||
45 | scope.extensionCustomConverterOptions = { | 53 | scope.extensionCustomConverterOptions = { |
46 | useWrapMode: false, | 54 | useWrapMode: false, |
@@ -58,17 +66,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -58,17 +66,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
58 | } | 66 | } |
59 | }; | 67 | }; |
60 | 68 | ||
61 | - | ||
62 | - if(scope.isAdd) { | ||
63 | - scope.brokers = []; | ||
64 | - scope.config.brokers = scope.brokers; | ||
65 | - } else { | ||
66 | - scope.brokers = scope.config.brokers; | ||
67 | - } | ||
68 | - | ||
69 | scope.updateValidity = function () { | 69 | scope.updateValidity = function () { |
70 | - var valid = scope.brokers && scope.brokers.length > 0; | ||
71 | - scope.theForm.$setValidity('brokers', valid); | ||
72 | if(scope.brokers.length) { | 70 | if(scope.brokers.length) { |
73 | for(let i=0;i<scope.brokers.length;i++) { | 71 | for(let i=0;i<scope.brokers.length;i++) { |
74 | if(scope.brokers[i].credentials.type == scope.types.mqttCredentialTypes.pem.value) { | 72 | if(scope.brokers[i].credentials.type == scope.types.mqttCredentialTypes.pem.value) { |
@@ -81,57 +79,116 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -81,57 +79,116 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
81 | } | 79 | } |
82 | } | 80 | } |
83 | } | 81 | } |
84 | - } | 82 | + }; |
85 | 83 | ||
86 | scope.$watch('brokers', function() { | 84 | scope.$watch('brokers', function() { |
87 | scope.updateValidity(); | 85 | scope.updateValidity(); |
88 | }, true); | 86 | }, true); |
89 | 87 | ||
90 | scope.addBroker = function() { | 88 | scope.addBroker = function() { |
91 | - var newBroker = {host:"localhost", port:1882, ssl:false, retryInterval:3000, credentials:{type:"anonymous"}, mapping:[]}; | 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 | + }; | ||
92 | scope.brokers.push(newBroker); | 102 | scope.brokers.push(newBroker); |
93 | - } | 103 | + }; |
94 | 104 | ||
95 | scope.removeBroker = function(broker) { | 105 | scope.removeBroker = function(broker) { |
96 | var index = scope.brokers.indexOf(broker); | 106 | var index = scope.brokers.indexOf(broker); |
97 | if (index > -1) { | 107 | if (index > -1) { |
98 | scope.brokers.splice(index, 1); | 108 | scope.brokers.splice(index, 1); |
99 | } | 109 | } |
100 | - scope.theForm.$setDirty(); | 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; | ||
101 | } | 118 | } |
102 | 119 | ||
103 | scope.addMap = function(mapping) { | 120 | scope.addMap = function(mapping) { |
104 | var newMap = {topicFilter:"sensors", converter:{attributes:[],timeseries:[]}}; | 121 | var newMap = {topicFilter:"sensors", converter:{attributes:[],timeseries:[]}}; |
105 | 122 | ||
106 | mapping.push(newMap); | 123 | mapping.push(newMap); |
107 | - } | 124 | + }; |
108 | 125 | ||
109 | scope.removeMap = function(map, mapping) { | 126 | scope.removeMap = function(map, mapping) { |
110 | var index = mapping.indexOf(map); | 127 | var index = mapping.indexOf(map); |
111 | if (index > -1) { | 128 | if (index > -1) { |
112 | mapping.splice(index, 1); | 129 | mapping.splice(index, 1); |
113 | } | 130 | } |
114 | - scope.theForm.$setDirty(); | ||
115 | - } | 131 | + }; |
116 | 132 | ||
117 | scope.addAttribute = function(attributes) { | 133 | scope.addAttribute = function(attributes) { |
118 | var newAttribute = {type:"", key:"", value:""}; | 134 | var newAttribute = {type:"", key:"", value:""}; |
119 | attributes.push(newAttribute); | 135 | attributes.push(newAttribute); |
120 | - } | 136 | + }; |
121 | 137 | ||
122 | scope.removeAttribute = function(attribute, attributes) { | 138 | scope.removeAttribute = function(attribute, attributes) { |
123 | var index = attributes.indexOf(attribute); | 139 | var index = attributes.indexOf(attribute); |
124 | if (index > -1) { | 140 | if (index > -1) { |
125 | attributes.splice(index, 1); | 141 | attributes.splice(index, 1); |
126 | } | 142 | } |
127 | - scope.theForm.$setDirty(); | ||
128 | - } | 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 | + }; | ||
129 | 186 | ||
130 | scope.changeCredentials = function(broker) { | 187 | scope.changeCredentials = function(broker) { |
131 | var type = broker.credentials.type; | 188 | var type = broker.credentials.type; |
132 | broker.credentials = {}; | 189 | broker.credentials = {}; |
133 | broker.credentials.type = type; | 190 | broker.credentials.type = type; |
134 | - } | 191 | + }; |
135 | 192 | ||
136 | scope.changeConverterType = function(map) { | 193 | scope.changeConverterType = function(map) { |
137 | if(map.converterType == "custom"){ | 194 | if(map.converterType == "custom"){ |
@@ -140,20 +197,32 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -140,20 +197,32 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
140 | if(map.converterType == "json") { | 197 | if(map.converterType == "json") { |
141 | map.converter = {attributes:[],timeseries:[]}; | 198 | map.converter = {attributes:[],timeseries:[]}; |
142 | } | 199 | } |
143 | - } | 200 | + }; |
144 | 201 | ||
145 | - scope.changeNameExpression = function(converter) { | ||
146 | - if(converter.nameExp == "deviceNameJsonExpression") { | ||
147 | - if(converter.deviceNameTopicExpression) { | ||
148 | - delete converter.deviceNameTopicExpression; | 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}"; | ||
149 | } | 209 | } |
150 | } | 210 | } |
151 | - if(converter.nameExp == "deviceNameTopicExpression") { | ||
152 | - if(converter.deviceNameJsonExpression) { | ||
153 | - delete converter.deviceNameJsonExpression; | 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)"; | ||
154 | } | 223 | } |
155 | } | 224 | } |
156 | - } | 225 | + }; |
157 | 226 | ||
158 | scope.changeTypeExpression = function(converter) { | 227 | scope.changeTypeExpression = function(converter) { |
159 | if(converter.typeExp == "deviceTypeJsonExpression") { | 228 | if(converter.typeExp == "deviceTypeJsonExpression") { |
@@ -166,7 +235,37 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -166,7 +235,37 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
166 | delete converter.deviceTypeJsonExpression; | 235 | delete converter.deviceTypeJsonExpression; |
167 | } | 236 | } |
168 | } | 237 | } |
169 | - } | 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 | + }; | ||
170 | 269 | ||
171 | scope.validateCustomConverter = function(model, editorName) { | 270 | scope.validateCustomConverter = function(model, editorName) { |
172 | if(model && model.length) { | 271 | if(model && model.length) { |
@@ -177,7 +276,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -177,7 +276,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
177 | scope.theForm[editorName].$setValidity('converterJSON', false); | 276 | scope.theForm[editorName].$setValidity('converterJSON', false); |
178 | } | 277 | } |
179 | } | 278 | } |
180 | - } | 279 | + }; |
181 | 280 | ||
182 | scope.fileAdded = function($file, broker, fileType) { | 281 | scope.fileAdded = function($file, broker, fileType) { |
183 | var reader = new FileReader(); | 282 | var reader = new FileReader(); |
@@ -204,7 +303,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -204,7 +303,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
204 | }); | 303 | }); |
205 | }; | 304 | }; |
206 | reader.readAsDataURL($file.file); | 305 | reader.readAsDataURL($file.file); |
207 | - } | 306 | + }; |
208 | 307 | ||
209 | scope.clearFile = function(broker, fileType) { | 308 | scope.clearFile = function(broker, fileType) { |
210 | scope.theForm.$setDirty(); | 309 | scope.theForm.$setDirty(); |
@@ -220,10 +319,10 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | @@ -220,10 +319,10 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr | ||
220 | broker.credentials.certFileName = null; | 319 | broker.credentials.certFileName = null; |
221 | broker.credentials.cert = null; | 320 | broker.credentials.cert = null; |
222 | } | 321 | } |
223 | - } | 322 | + }; |
224 | 323 | ||
225 | $compile(element.contents())(scope); | 324 | $compile(element.contents())(scope); |
226 | - } | 325 | + }; |
227 | 326 | ||
228 | return { | 327 | return { |
229 | restrict: "A", | 328 | restrict: "A", |
@@ -23,18 +23,15 @@ | @@ -23,18 +23,15 @@ | ||
23 | </md-card-title> | 23 | </md-card-title> |
24 | <md-card-content> | 24 | <md-card-content> |
25 | <v-accordion id="mqtt-brokers-accordion" class="vAccordion--default"> | 25 | <v-accordion id="mqtt-brokers-accordion" class="vAccordion--default"> |
26 | - <v-pane id="mqtt-brokers-pane" expanded="isAdd"> | 26 | + <v-pane id="mqtt-brokers-pane" expanded="true"> |
27 | <v-pane-header> | 27 | <v-pane-header> |
28 | {{ 'extension.brokers' | translate }} | 28 | {{ 'extension.brokers' | translate }} |
29 | </v-pane-header> | 29 | </v-pane-header> |
30 | <v-pane-content> | 30 | <v-pane-content> |
31 | - <div ng-if="brokers.length === 0"> | ||
32 | - <span translate layout-align="center center" class="tb-prompt">extension.add-broker-prompt</span> | ||
33 | - </div> | ||
34 | <div ng-if="brokers.length > 0"> | 31 | <div ng-if="brokers.length > 0"> |
35 | <ol class="list-group"> | 32 | <ol class="list-group"> |
36 | <li class="list-group-item" ng-repeat="(brokerIndex,broker) in brokers"> | 33 | <li class="list-group-item" ng-repeat="(brokerIndex,broker) in brokers"> |
37 | - <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeBroker(broker)"> | 34 | + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeBroker(broker)" ng-hide="brokers.length < 2"> |
38 | <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | 35 | <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> |
39 | <md-tooltip md-direction="top"> | 36 | <md-tooltip md-direction="top"> |
40 | {{ 'action.remove' | translate }} | 37 | {{ 'action.remove' | translate }} |
@@ -47,7 +44,7 @@ | @@ -47,7 +44,7 @@ | ||
47 | <label translate>extension.port</label> | 44 | <label translate>extension.port</label> |
48 | <input required type="number" min="1" max="65535" name="mqttPort_{{brokerIndex}}" ng-model="broker.port"> | 45 | <input required type="number" min="1" max="65535" name="mqttPort_{{brokerIndex}}" ng-model="broker.port"> |
49 | <div ng-messages="theForm['mqttPort_' + brokerIndex].$error"> | 46 | <div ng-messages="theForm['mqttPort_' + brokerIndex].$error"> |
50 | - <div translate ng-message="required">extension.port-required</div> | 47 | + <div translate ng-message="required">extension.field-required</div> |
51 | <div translate ng-message="min">extension.port-range</div> | 48 | <div translate ng-message="min">extension.port-range</div> |
52 | <div translate ng-message="max">extension.port-range</div> | 49 | <div translate ng-message="max">extension.port-range</div> |
53 | </div> | 50 | </div> |
@@ -56,7 +53,7 @@ | @@ -56,7 +53,7 @@ | ||
56 | <label translate>extension.host</label> | 53 | <label translate>extension.host</label> |
57 | <input required name="mqttHost_{{brokerIndex}}" ng-model="broker.host"> | 54 | <input required name="mqttHost_{{brokerIndex}}" ng-model="broker.host"> |
58 | <div ng-messages="theForm['mqttHost_' + brokerIndex].$error"> | 55 | <div ng-messages="theForm['mqttHost_' + brokerIndex].$error"> |
59 | - <div translate ng-message="required">extension.host-required</div> | 56 | + <div translate ng-message="required">extension.field-required</div> |
60 | </div> | 57 | </div> |
61 | </md-input-container> | 58 | </md-input-container> |
62 | </section> | 59 | </section> |
@@ -65,7 +62,7 @@ | @@ -65,7 +62,7 @@ | ||
65 | <label translate>extension.retry-interval</label> | 62 | <label translate>extension.retry-interval</label> |
66 | <input required type="number" name="mqttRetryInterval_{{brokerIndex}}" ng-model="broker.retryInterval"> | 63 | <input required type="number" name="mqttRetryInterval_{{brokerIndex}}" ng-model="broker.retryInterval"> |
67 | <div ng-messages="theForm['mqttRetryInterval_' + brokerIndex].$error"> | 64 | <div ng-messages="theForm['mqttRetryInterval_' + brokerIndex].$error"> |
68 | - <div translate ng-message="required">extension.retry-interval-required</div> | 65 | + <div translate ng-message="required">extension.field-required</div> |
69 | </div> | 66 | </div> |
70 | </md-input-container> | 67 | </md-input-container> |
71 | <md-input-container flex="50" class="md-block"> | 68 | <md-input-container flex="50" class="md-block"> |
@@ -76,30 +73,30 @@ | @@ -76,30 +73,30 @@ | ||
76 | </md-option> | 73 | </md-option> |
77 | </md-select> | 74 | </md-select> |
78 | </md-input-container> | 75 | </md-input-container> |
79 | - <md-input-container flex="10" class="md-block t-right"> | 76 | + <md-input-container flex="10" class="md-block"> |
80 | <md-checkbox flex aria-label="{{ 'extension.ssl' | translate }}" | 77 | <md-checkbox flex aria-label="{{ 'extension.ssl' | translate }}" |
81 | ng-model="broker.ssl">{{ 'extension.ssl' | translate }} | 78 | ng-model="broker.ssl">{{ 'extension.ssl' | translate }} |
82 | </md-checkbox> | 79 | </md-checkbox> |
83 | </md-input-container> | 80 | </md-input-container> |
84 | </section> | 81 | </section> |
85 | <section flex layout="row" ng-if='broker.credentials.type == "basic"'> | 82 | <section flex layout="row" ng-if='broker.credentials.type == "basic"'> |
86 | - <md-input-container flex="40" class="md-block"> | 83 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['mqttUsername_' + brokerIndex].$touched && theForm['mqttUsername_' + brokerIndex].$invalid"> |
87 | <label translate>extension.username</label> | 84 | <label translate>extension.username</label> |
88 | <input required name="mqttUsername_{{brokerIndex}}" ng-model="broker.credentials.username"> | 85 | <input required name="mqttUsername_{{brokerIndex}}" ng-model="broker.credentials.username"> |
89 | <div ng-messages="theForm['mqttUsername_' + brokerIndex].$error"> | 86 | <div ng-messages="theForm['mqttUsername_' + brokerIndex].$error"> |
90 | - <div translate ng-message="required">extension.username-required</div> | 87 | + <div translate ng-message="required">extension.field-required</div> |
91 | </div> | 88 | </div> |
92 | </md-input-container> | 89 | </md-input-container> |
93 | - <md-input-container flex="60" class="md-block"> | 90 | + <md-input-container flex="60" class="md-block" md-is-error="theForm['mqttPassword_' + brokerIndex].$touched && theForm['mqttPassword_' + brokerIndex].$invalid"> |
94 | <label translate>extension.password</label> | 91 | <label translate>extension.password</label> |
95 | <input required name="mqttPassword_{{brokerIndex}}" ng-model="broker.credentials.password"> | 92 | <input required name="mqttPassword_{{brokerIndex}}" ng-model="broker.credentials.password"> |
96 | <div ng-messages="theForm['mqttPassword_' + brokerIndex].$error"> | 93 | <div ng-messages="theForm['mqttPassword_' + brokerIndex].$error"> |
97 | - <div translate ng-message="required">extension.password-required</div> | 94 | + <div translate ng-message="required">extension.field-required</div> |
98 | </div> | 95 | </div> |
99 | </md-input-container> | 96 | </md-input-container> |
100 | </section> | 97 | </section> |
101 | - <section flex layout="column" ng-if='broker.credentials.type == "cert.PEM"'> | ||
102 | - <div class="tb-container"> | 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'"> | ||
103 | <label class="tb-label" translate>extension.ca-cert</label> | 100 | <label class="tb-label" translate>extension.ca-cert</label> |
104 | <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "caCert")' class="tb-file-select-container"> | 101 | <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "caCert")' class="tb-file-select-container"> |
105 | <div class="tb-file-clear-container"> | 102 | <div class="tb-file-clear-container"> |
@@ -120,7 +117,7 @@ | @@ -120,7 +117,7 @@ | ||
120 | <div ng-if="!broker.credentials.caCertFileName" class="tb-error-message" translate>extension.no-file</div> | 117 | <div ng-if="!broker.credentials.caCertFileName" class="tb-error-message" translate>extension.no-file</div> |
121 | <div ng-if="broker.credentials.caCertFileName">{{broker.credentials.caCertFileName}}</div> | 118 | <div ng-if="broker.credentials.caCertFileName">{{broker.credentials.caCertFileName}}</div> |
122 | </div> | 119 | </div> |
123 | - <div class="tb-container"> | 120 | + <div class="tb-container" ng-class="broker.credentials.privateKeyFileName ? 'ng-valid' : 'ng-invalid'"> |
124 | <label class="tb-label" translate>extension.private-key</label> | 121 | <label class="tb-label" translate>extension.private-key</label> |
125 | <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "privateKey")' class="tb-file-select-container"> | 122 | <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "privateKey")' class="tb-file-select-container"> |
126 | <div class="tb-file-clear-container"> | 123 | <div class="tb-file-clear-container"> |
@@ -182,7 +179,7 @@ | @@ -182,7 +179,7 @@ | ||
182 | <md-card> | 179 | <md-card> |
183 | <md-card-content> | 180 | <md-card-content> |
184 | <section flex layout="row"> | 181 | <section flex layout="row"> |
185 | - <md-input-container flex="40" class="md-block"> | 182 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['mqttConverterType_' + brokerIndex + mapIndex].$touched && theForm['mqttConverterType_' + brokerIndex + mapIndex].$invalid"> |
186 | <label translate>extension.converter-type</label> | 183 | <label translate>extension.converter-type</label> |
187 | <md-select required name="mqttConverterType_{{brokerIndex}}{{mapIndex}}" ng-model="map.converterType" ng-change="changeConverterType(map)"> | 184 | <md-select required name="mqttConverterType_{{brokerIndex}}{{mapIndex}}" ng-model="map.converterType" ng-change="changeConverterType(map)"> |
188 | <md-option ng-repeat="(converterType, value) in types.mqttConverterTypes" ng-value="converterType"> | 185 | <md-option ng-repeat="(converterType, value) in types.mqttConverterTypes" ng-value="converterType"> |
@@ -190,64 +187,70 @@ | @@ -190,64 +187,70 @@ | ||
190 | </md-option> | 187 | </md-option> |
191 | </md-select> | 188 | </md-select> |
192 | <div ng-messages="theForm['mqttConverterType_' + brokerIndex + mapIndex].$error"> | 189 | <div ng-messages="theForm['mqttConverterType_' + brokerIndex + mapIndex].$error"> |
193 | - <div translate ng-message="required">extension.converter-type-required</div> | 190 | + <div translate ng-message="required">extension.field-required</div> |
194 | </div> | 191 | </div> |
195 | </md-input-container> | 192 | </md-input-container> |
196 | <md-input-container flex="60" class="md-block"> | 193 | <md-input-container flex="60" class="md-block"> |
197 | <label translate>extension.topic-filter</label> | 194 | <label translate>extension.topic-filter</label> |
198 | <input required name="mqttTopicFilter_{{brokerIndex}}{{mapIndex}}" ng-model="map.topicFilter"> | 195 | <input required name="mqttTopicFilter_{{brokerIndex}}{{mapIndex}}" ng-model="map.topicFilter"> |
199 | <div ng-messages="theForm['mqttTopicFilter_' + brokerIndex + mapIndex].$error"> | 196 | <div ng-messages="theForm['mqttTopicFilter_' + brokerIndex + mapIndex].$error"> |
200 | - <div translate ng-message="required">extension.topic-filter-required</div> | 197 | + <div translate ng-message="required">extension.field-required</div> |
201 | </div> | 198 | </div> |
202 | </md-input-container> | 199 | </md-input-container> |
203 | </section> | 200 | </section> |
204 | 201 | ||
205 | <div ng-if='map.converterType =="json"' ng-init="map.converter.type = 'json'"> | 202 | <div ng-if='map.converterType =="json"' ng-init="map.converter.type = 'json'"> |
206 | <section flex layout="row"> | 203 | <section flex layout="row"> |
207 | - <md-input-container flex="40" class="md-block"> | ||
208 | - <label translate>extension.name-expression</label> | 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> | ||
209 | <md-select required name="mqttDeviceNameExpression_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.nameExp" ng-change="changeNameExpression(map.converter)"> | 206 | <md-select required name="mqttDeviceNameExpression_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.nameExp" ng-change="changeNameExpression(map.converter)"> |
210 | - <md-option ng-repeat="(key, value) in nameExpressions" ng-value='key'> | 207 | + <md-option ng-repeat="(key, value) in deviceNameExpressions" ng-value='key'> |
211 | {{value | translate}} | 208 | {{value | translate}} |
212 | </md-option> | 209 | </md-option> |
213 | </md-select> | 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> | 214 | </md-input-container> |
215 | - <md-input-container ng-if="map.converter.nameExp == 'deviceNameJsonExpression'" flex="60" class="md-block"> | 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> | 216 | <label translate>extension.json-name-expression</label> |
217 | <input required name="mqttJsonNameExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceNameJsonExpression"> | 217 | <input required name="mqttJsonNameExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceNameJsonExpression"> |
218 | <div ng-messages="theForm['mqttJsonNameExp_' + brokerIndex + mapIndex].$error"> | 218 | <div ng-messages="theForm['mqttJsonNameExp_' + brokerIndex + mapIndex].$error"> |
219 | - <div translate ng-message="required">extension.json-name-expression-required</div> | 219 | + <div translate ng-message="required">extension.field-required</div> |
220 | </div> | 220 | </div> |
221 | </md-input-container> | 221 | </md-input-container> |
222 | - <md-input-container ng-if="map.converter.nameExp == 'deviceNameTopicExpression'" flex="60" class="md-block"> | 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> | 223 | <label translate>extension.topic-name-expression</label> |
224 | <input required name="mqttTopicNameExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceNameTopicExpression"> | 224 | <input required name="mqttTopicNameExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceNameTopicExpression"> |
225 | <div ng-messages="theForm['mqttTopicNameExp_' + brokerIndex + mapIndex].$error"> | 225 | <div ng-messages="theForm['mqttTopicNameExp_' + brokerIndex + mapIndex].$error"> |
226 | - <div translate ng-message="required">extension.topic-name-expression-required</div> | 226 | + <div translate ng-message="required">extension.field-required</div> |
227 | </div> | 227 | </div> |
228 | </md-input-container> | 228 | </md-input-container> |
229 | </section> | 229 | </section> |
230 | <section flex layout="row"> | 230 | <section flex layout="row"> |
231 | - <md-input-container flex="40" class="md-block"> | ||
232 | - <label translate>extension.type-expression</label> | 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)"> | 233 | <md-select required name="mqttDeviceTypeExpression_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.typeExp" ng-change="changeTypeExpression(map.converter)"> |
234 | - <md-option ng-repeat="(key, value) in typeExpressions" ng-value='key'> | 234 | + <md-option ng-repeat="(key, value) in deviceTypeExpressions" ng-value='key'> |
235 | {{value | translate}} | 235 | {{value | translate}} |
236 | </md-option> | 236 | </md-option> |
237 | </md-select> | 237 | </md-select> |
238 | + <div ng-messages="theForm['mqttDeviceTypeExpression_' + brokerIndex + mapIndex].$error"> | ||
239 | + <div translate ng-message="required">extension.field-required</div> | ||
240 | + </div> | ||
238 | </md-input-container> | 241 | </md-input-container> |
239 | - <md-input-container ng-if="map.converter.typeExp == 'deviceTypeJsonExpression'" flex="60" class="md-block"> | 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"> |
240 | <label translate>extension.json-type-expression</label> | 243 | <label translate>extension.json-type-expression</label> |
241 | <input required name="mqttJsonTypeExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceTypeJsonExpression"> | 244 | <input required name="mqttJsonTypeExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceTypeJsonExpression"> |
242 | <div ng-messages="theForm['mqttJsonTypeExp_' + brokerIndex + mapIndex].$error"> | 245 | <div ng-messages="theForm['mqttJsonTypeExp_' + brokerIndex + mapIndex].$error"> |
243 | - <div translate ng-message="required">extension.json-type-expression-required</div> | 246 | + <div translate ng-message="required">extension.field-required</div> |
244 | </div> | 247 | </div> |
245 | </md-input-container> | 248 | </md-input-container> |
246 | - <md-input-container ng-if="map.converter.typeExp == 'deviceTypeTopicExpression'" flex="60" class="md-block"> | 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"> |
247 | <label translate>extension.topic-type-expression</label> | 250 | <label translate>extension.topic-type-expression</label> |
248 | <input required name="mqttTopicTypeExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceTypeTopicExpression"> | 251 | <input required name="mqttTopicTypeExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceTypeTopicExpression"> |
249 | <div ng-messages="theForm['mqttTopicTypeExp_' + brokerIndex + mapIndex].$error"> | 252 | <div ng-messages="theForm['mqttTopicTypeExp_' + brokerIndex + mapIndex].$error"> |
250 | - <div translate ng-message="required">extension.topic-type-expression-required</div> | 253 | + <div translate ng-message="required">extension.field-required</div> |
251 | </div> | 254 | </div> |
252 | </md-input-container> | 255 | </md-input-container> |
253 | </section> | 256 | </section> |
@@ -256,11 +259,11 @@ | @@ -256,11 +259,11 @@ | ||
256 | <label translate>extension.timeout</label> | 259 | <label translate>extension.timeout</label> |
257 | <input type="number" name="mqttTimeout_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.timeout" parse-to-null> | 260 | <input type="number" name="mqttTimeout_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.timeout" parse-to-null> |
258 | </md-input-container> | 261 | </md-input-container> |
259 | - <md-input-container flex="60" class="md-block"> | 262 | + <md-input-container flex="60" class="md-block" md-is-error="theForm['mqttFilterExpression' + brokerIndex + mapIndex].$touched && theForm['mqttFilterExpression' + brokerIndex + mapIndex].$invalid"> |
260 | <label translate>extension.filter-expression</label> | 263 | <label translate>extension.filter-expression</label> |
261 | <input required name="mqttFilterExpression{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.filterExpression"> | 264 | <input required name="mqttFilterExpression{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.filterExpression"> |
262 | <div ng-messages="theForm['mqttFilterExpression' + brokerIndex + mapIndex].$error"> | 265 | <div ng-messages="theForm['mqttFilterExpression' + brokerIndex + mapIndex].$error"> |
263 | - <div translate ng-message="required">extension.filter-expression-required</div> | 266 | + <div translate ng-message="required">extension.field-required</div> |
264 | </div> | 267 | </div> |
265 | </md-input-container> | 268 | </md-input-container> |
266 | </section> | 269 | </section> |
@@ -301,14 +304,14 @@ | @@ -301,14 +304,14 @@ | ||
301 | <md-card> | 304 | <md-card> |
302 | <md-card-content> | 305 | <md-card-content> |
303 | <section flex layout="row"> | 306 | <section flex layout="row"> |
304 | - <md-input-container flex="60" class="md-block"> | 307 | + <md-input-container flex="60" class="md-block" md-is-error="theForm['mqttAttributeKey_' + brokerIndex + mapIndex + attributeIndex].$touched && theForm['mqttAttributeKey_' + brokerIndex + mapIndex + attributeIndex].$invalid"> |
305 | <label translate>extension.key</label> | 308 | <label translate>extension.key</label> |
306 | <input required name="mqttAttributeKey_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.key"> | 309 | <input required name="mqttAttributeKey_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.key"> |
307 | <div ng-messages="theForm['mqttAttributeKey_' + brokerIndex + mapIndex + attributeIndex].$error"> | 310 | <div ng-messages="theForm['mqttAttributeKey_' + brokerIndex + mapIndex + attributeIndex].$error"> |
308 | - <div translate ng-message="required">extension.required-key</div> | 311 | + <div translate ng-message="required">extension.field-required</div> |
309 | </div> | 312 | </div> |
310 | </md-input-container> | 313 | </md-input-container> |
311 | - <md-input-container flex="40" class="md-block"> | 314 | + <md-input-container flex="40" class="md-block" md-is-error="theForm['mqttAttributeType_' + brokerIndex + mapIndex + attributeIndex].$touched && theForm['mqttAttributeType_' + brokerIndex + mapIndex + attributeIndex].$invalid"> |
312 | <label translate>extension.type</label> | 315 | <label translate>extension.type</label> |
313 | <md-select required name="mqttAttributeType_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.type"> | 316 | <md-select required name="mqttAttributeType_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.type"> |
314 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> | 317 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> |
@@ -316,15 +319,15 @@ | @@ -316,15 +319,15 @@ | ||
316 | </md-option> | 319 | </md-option> |
317 | </md-select> | 320 | </md-select> |
318 | <div ng-messages="theForm['mqttAttributeType_' + brokerIndex + mapIndex + attributeIndex].$error"> | 321 | <div ng-messages="theForm['mqttAttributeType_' + brokerIndex + mapIndex + attributeIndex].$error"> |
319 | - <div translate ng-message="required">extension.required-type</div> | 322 | + <div translate ng-message="required">extension.field-required</div> |
320 | </div> | 323 | </div> |
321 | </md-input-container> | 324 | </md-input-container> |
322 | </section> | 325 | </section> |
323 | - <md-input-container class="md-block"> | 326 | + <md-input-container class="md-block" md-is-error="theForm['mqttAttributeValue_' + brokerIndex + mapIndex + attributeIndex].$touched && theForm['mqttAttributeValue_' + brokerIndex + mapIndex + attributeIndex].$invalid"> |
324 | <label translate>extension.value</label> | 327 | <label translate>extension.value</label> |
325 | <input required name="mqttAttributeValue_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.value"> | 328 | <input required name="mqttAttributeValue_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.value"> |
326 | <div ng-messages="theForm['mqttAttributeValue_' + brokerIndex + mapIndex + attributeIndex].$error"> | 329 | <div ng-messages="theForm['mqttAttributeValue_' + brokerIndex + mapIndex + attributeIndex].$error"> |
327 | - <div translate ng-message="required">extension.required-value</div> | 330 | + <div translate ng-message="required">extension.field-required</div> |
328 | </div> | 331 | </div> |
329 | </md-input-container> | 332 | </md-input-container> |
330 | </md-card-content> | 333 | </md-card-content> |
@@ -335,11 +338,8 @@ | @@ -335,11 +338,8 @@ | ||
335 | <div flex layout="row" layout-align="start center"> | 338 | <div flex layout="row" layout-align="start center"> |
336 | <md-button class="md-primary md-raised" | 339 | <md-button class="md-primary md-raised" |
337 | ng-click="addAttribute(map.converter.attributes)" aria-label="{{ 'action.add' | translate }}"> | 340 | ng-click="addAttribute(map.converter.attributes)" aria-label="{{ 'action.add' | translate }}"> |
338 | - <md-tooltip md-direction="top"> | ||
339 | - {{ 'extension.add-attribute' | translate }} | ||
340 | - </md-tooltip> | ||
341 | <md-icon class="material-icons">add</md-icon> | 341 | <md-icon class="material-icons">add</md-icon> |
342 | - <span translate>action.add</span> | 342 | + <span translate>extension.add-attribute</span> |
343 | </md-button> | 343 | </md-button> |
344 | </div> | 344 | </div> |
345 | </v-pane-content> | 345 | </v-pane-content> |
@@ -364,14 +364,14 @@ | @@ -364,14 +364,14 @@ | ||
364 | <md-card> | 364 | <md-card> |
365 | <md-card-content> | 365 | <md-card-content> |
366 | <section flex layout="row"> | 366 | <section flex layout="row"> |
367 | - <md-input-container flex="60" class="md-block"> | 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> | 368 | <label translate>extension.key</label> |
369 | <input required name="mqttTimeseriesKey_{{brokerIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.key"> | 369 | <input required name="mqttTimeseriesKey_{{brokerIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.key"> |
370 | - <div ng-messages="theForm['mqtTtimeseriesKey_' + brokerIndex + mapIndex + timeseriesIndex].$error"> | ||
371 | - <div translate ng-message="required">extension.required-key</div> | 370 | + <div ng-messages="theForm['mqttTimeseriesKey_' + brokerIndex + mapIndex + timeseriesIndex].$error"> |
371 | + <div translate ng-message="required">extension.field-required</div> | ||
372 | </div> | 372 | </div> |
373 | </md-input-container> | 373 | </md-input-container> |
374 | - <md-input-container flex="40" class="md-block"> | 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> | 375 | <label translate>extension.type</label> |
376 | <md-select required name="mqttTimeseriesType_{{brokerIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.type"> | 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"> | 377 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> |
@@ -379,15 +379,15 @@ | @@ -379,15 +379,15 @@ | ||
379 | </md-option> | 379 | </md-option> |
380 | </md-select> | 380 | </md-select> |
381 | <div ng-messages="theForm['mqttTimeseriesType_' + brokerIndex + mapIndex + timeseriesIndex].$error"> | 381 | <div ng-messages="theForm['mqttTimeseriesType_' + brokerIndex + mapIndex + timeseriesIndex].$error"> |
382 | - <div translate ng-message="required">extension.required-type</div> | 382 | + <div translate ng-message="required">extension.field-required</div> |
383 | </div> | 383 | </div> |
384 | </md-input-container> | 384 | </md-input-container> |
385 | </section> | 385 | </section> |
386 | - <md-input-container class="md-block"> | 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> | 387 | <label translate>extension.value</label> |
388 | <input required name="mqttTimeseriesValue_{{brokerIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.value"> | 388 | <input required name="mqttTimeseriesValue_{{brokerIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.value"> |
389 | <div ng-messages="theForm['mqttTimeseriesValue_' + brokerIndex + mapIndex + timeseriesIndex].$error"> | 389 | <div ng-messages="theForm['mqttTimeseriesValue_' + brokerIndex + mapIndex + timeseriesIndex].$error"> |
390 | - <div translate ng-message="required">extension.required-value</div> | 390 | + <div translate ng-message="required">extension.field-required</div> |
391 | </div> | 391 | </div> |
392 | </md-input-container> | 392 | </md-input-container> |
393 | </md-card-content> | 393 | </md-card-content> |
@@ -398,11 +398,8 @@ | @@ -398,11 +398,8 @@ | ||
398 | <div flex layout="row" layout-align="start center"> | 398 | <div flex layout="row" layout-align="start center"> |
399 | <md-button class="md-primary md-raised" | 399 | <md-button class="md-primary md-raised" |
400 | ng-click="addAttribute(map.converter.timeseries)" aria-label="{{ 'action.add' | translate }}"> | 400 | ng-click="addAttribute(map.converter.timeseries)" aria-label="{{ 'action.add' | translate }}"> |
401 | - <md-tooltip md-direction="top"> | ||
402 | - {{ 'extension.add-timeseries' | translate }} | ||
403 | - </md-tooltip> | ||
404 | <md-icon class="material-icons">add</md-icon> | 401 | <md-icon class="material-icons">add</md-icon> |
405 | - <span translate>action.add</span> | 402 | + <span translate>extension.add-timeseries</span> |
406 | </md-button> | 403 | </md-button> |
407 | </div> | 404 | </div> |
408 | </v-pane-content> | 405 | </v-pane-content> |
@@ -417,16 +414,429 @@ | @@ -417,16 +414,429 @@ | ||
417 | <div flex layout="row" layout-align="start center"> | 414 | <div flex layout="row" layout-align="start center"> |
418 | <md-button class="md-primary md-raised" | 415 | <md-button class="md-primary md-raised" |
419 | ng-click="addMap(broker.mapping)" aria-label="{{ 'action.add' | translate }}"> | 416 | ng-click="addMap(broker.mapping)" aria-label="{{ 'action.add' | translate }}"> |
420 | - <md-tooltip md-direction="top"> | ||
421 | - {{ 'extension.add-map' | translate }} | ||
422 | - </md-tooltip> | ||
423 | <md-icon class="material-icons">add</md-icon> | 417 | <md-icon class="material-icons">add</md-icon> |
424 | - <span translate>action.add</span> | 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> | ||
425 | </md-button> | 553 | </md-button> |
426 | </div> | 554 | </div> |
427 | </v-pane-content> | 555 | </v-pane-content> |
428 | </v-pane> | 556 | </v-pane> |
429 | </v-accordion> | 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 | + | ||
430 | </md-card-content> | 840 | </md-card-content> |
431 | </md-card> | 841 | </md-card> |
432 | </li> | 842 | </li> |
@@ -436,18 +846,15 @@ | @@ -436,18 +846,15 @@ | ||
436 | <div flex layout="row" layout-align="start center"> | 846 | <div flex layout="row" layout-align="start center"> |
437 | <md-button class="md-primary md-raised" | 847 | <md-button class="md-primary md-raised" |
438 | ng-click="addBroker()" aria-label="{{ 'action.add' | translate }}"> | 848 | ng-click="addBroker()" aria-label="{{ 'action.add' | translate }}"> |
439 | - <md-tooltip md-direction="top"> | ||
440 | - {{ 'extension.add-broker' | translate }} | ||
441 | - </md-tooltip> | ||
442 | <md-icon class="material-icons">add</md-icon> | 849 | <md-icon class="material-icons">add</md-icon> |
443 | - <span translate>action.add</span> | 850 | + <span translate>extension.add-broker</span> |
444 | </md-button> | 851 | </md-button> |
445 | </div> | 852 | </div> |
446 | </v-pane-content> | 853 | </v-pane-content> |
447 | </v-pane> | 854 | </v-pane> |
448 | </v-accordion> | 855 | </v-accordion> |
449 | -<pre> | 856 | +<!--<pre> |
450 | {{config | json}} | 857 | {{config | json}} |
451 | -</pre> | 858 | +</pre>--> |
452 | </md-card-content> | 859 | </md-card-content> |
453 | </md-card> | 860 | </md-card> |
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 'brace/ext/language_tools'; | ||
18 | +import 'brace/mode/json'; | ||
19 | +import 'brace/theme/github'; | ||
20 | + | ||
21 | +import './extension-form.scss'; | ||
22 | + | ||
23 | +/* eslint-disable angular/log */ | ||
24 | + | ||
25 | +import extensionFormOpcTemplate from './extension-form-opc.tpl.html'; | ||
26 | + | ||
27 | +/* eslint-enable import/no-unresolved, import/default */ | ||
28 | + | ||
29 | +/*@ngInject*/ | ||
30 | +export default function ExtensionFormOpcDirective($compile, $templateCache, $translate, types) { | ||
31 | + | ||
32 | + | ||
33 | + var linker = function(scope, element) { | ||
34 | + | ||
35 | + | ||
36 | + function Server() { | ||
37 | + this.applicationName = "Thingsboard OPC-UA client"; | ||
38 | + this.applicationUri = ""; | ||
39 | + this.host = "localhost"; | ||
40 | + this.port = 49320; | ||
41 | + this.scanPeriodInSeconds = 10; | ||
42 | + this.timeoutInMillis = 5000; | ||
43 | + this.security = "Basic128Rsa15"; | ||
44 | + this.identity = { | ||
45 | + "type": "anonymous" | ||
46 | + }; | ||
47 | + this.keystore = { | ||
48 | + "type": "PKCS12", | ||
49 | + "location": "example.pfx", | ||
50 | + "password": "secret", | ||
51 | + "alias": "gateway", | ||
52 | + "keyPassword": "secret" | ||
53 | + }; | ||
54 | + this.mapping = [] | ||
55 | + } | ||
56 | + | ||
57 | + function Map() { | ||
58 | + this.deviceNodePattern = "Channel1\\.Device\\d+$"; | ||
59 | + this.deviceNamePattern = "Device ${_System._DeviceId}"; | ||
60 | + this.attributes = []; | ||
61 | + this.timeseries = []; | ||
62 | + } | ||
63 | + | ||
64 | + function Attribute() { | ||
65 | + this.key = "Tag1"; | ||
66 | + this.type = "string"; | ||
67 | + this.value = "${Tag1}"; | ||
68 | + } | ||
69 | + | ||
70 | + function Timeseries() { | ||
71 | + this.key = "Tag2"; | ||
72 | + this.type = "long"; | ||
73 | + this.value = "${Tag2}"; | ||
74 | + } | ||
75 | + | ||
76 | + | ||
77 | + var template = $templateCache.get(extensionFormOpcTemplate); | ||
78 | + element.html(template); | ||
79 | + | ||
80 | + scope.types = types; | ||
81 | + scope.theForm = scope.$parent.theForm; | ||
82 | + | ||
83 | + | ||
84 | + if (!scope.configuration.servers.length) { | ||
85 | + scope.configuration.servers.push(new Server()); | ||
86 | + } | ||
87 | + | ||
88 | + scope.addServer = function(serversList) { | ||
89 | + serversList.push(new Server()); | ||
90 | + // scope.addMap(serversList[serversList.length-1].mapping); | ||
91 | + | ||
92 | + scope.theForm.$setDirty(); | ||
93 | + }; | ||
94 | + | ||
95 | + scope.addMap = function(mappingList) { | ||
96 | + mappingList.push(new Map()); | ||
97 | + scope.theForm.$setDirty(); | ||
98 | + }; | ||
99 | + | ||
100 | + scope.addNewAttribute = function(attributesList) { | ||
101 | + attributesList.push(new Attribute()); | ||
102 | + scope.theForm.$setDirty(); | ||
103 | + }; | ||
104 | + | ||
105 | + scope.addNewTimeseries = function(timeseriesList) { | ||
106 | + timeseriesList.push(new Timeseries()); | ||
107 | + scope.theForm.$setDirty(); | ||
108 | + }; | ||
109 | + | ||
110 | + | ||
111 | + scope.removeItem = (item, itemList) => { | ||
112 | + var index = itemList.indexOf(item); | ||
113 | + if (index > -1) { | ||
114 | + itemList.splice(index, 1); | ||
115 | + } | ||
116 | + scope.theForm.$setDirty(); | ||
117 | + }; | ||
118 | + | ||
119 | + | ||
120 | + $compile(element.contents())(scope); | ||
121 | + | ||
122 | + | ||
123 | + scope.fileAdded = function($file, model, options) { | ||
124 | + let reader = new FileReader(); | ||
125 | + reader.onload = function(event) { | ||
126 | + scope.$apply(function() { | ||
127 | + if(event.target.result) { | ||
128 | + scope.theForm.$setDirty(); | ||
129 | + let addedFile = event.target.result; | ||
130 | + | ||
131 | + if (addedFile && addedFile.length > 0) { | ||
132 | + model[options.fileName] = $file.name; | ||
133 | + model[options.file] = addedFile.replace(/^data.*base64,/, ""); | ||
134 | + | ||
135 | + } | ||
136 | + } | ||
137 | + }); | ||
138 | + }; | ||
139 | + reader.readAsDataURL($file.file); | ||
140 | + | ||
141 | + }; | ||
142 | + | ||
143 | + scope.clearFile = function(model, options) { | ||
144 | + scope.theForm.$setDirty(); | ||
145 | + | ||
146 | + model[options.fileName] = null; | ||
147 | + model[options.file] = null; | ||
148 | + | ||
149 | + }; | ||
150 | + | ||
151 | + }; | ||
152 | + | ||
153 | + return { | ||
154 | + restrict: "A", | ||
155 | + link: linker, | ||
156 | + scope: { | ||
157 | + configuration: "=", | ||
158 | + isAdd: "=" | ||
159 | + } | ||
160 | + } | ||
161 | +} |
@@ -15,4 +15,543 @@ | @@ -15,4 +15,543 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<div>OPC UA</div> | ||
18 | +<md-card class="extension-form extension-opc"> | ||
19 | + <md-card-title> | ||
20 | + <md-card-title-text> | ||
21 | + <span translate class="md-headline">extension.configuration</span> | ||
22 | + </md-card-title-text> | ||
23 | + </md-card-title> | ||
24 | + | ||
25 | + <md-card-content> | ||
26 | + <v-accordion id="http-server-configs-accordion" class="vAccordion--default"> | ||
27 | + <v-pane id="http-servers-pane" expanded="true"> | ||
28 | + <v-pane-header> | ||
29 | + {{ 'extension.opc-server' | translate }} | ||
30 | + </v-pane-header> | ||
31 | + | ||
32 | + <v-pane-content> | ||
33 | + <div ng-if="configuration.servers.length === 0"> | ||
34 | + <span translate layout-align="center center" class="tb-prompt">extension.opc-add-server-prompt</span> | ||
35 | + </div> | ||
36 | + | ||
37 | + <div ng-if="configuration.servers.length > 0"> | ||
38 | + <ol class="list-group"> | ||
39 | + <li class="list-group-item" ng-repeat="(serverIndex, server) in configuration.servers"> | ||
40 | + <md-button aria-label="{{ 'action.remove' | translate }}" | ||
41 | + class="md-icon-button" | ||
42 | + ng-click="removeItem(server, configuration.servers)" | ||
43 | + ng-hide="configuration.servers.length < 2" | ||
44 | + > | ||
45 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | ||
46 | + <md-tooltip md-direction="top"> | ||
47 | + {{ 'action.remove' | translate }} | ||
48 | + </md-tooltip> | ||
49 | + </md-button> | ||
50 | + | ||
51 | + <md-card> | ||
52 | + <md-card-content> | ||
53 | + | ||
54 | + <div layout="row"> | ||
55 | + <md-input-container flex="50" class="md-block"> | ||
56 | + <label translate>extension.opc-application-name</label> | ||
57 | + <input required name="applicationName_{{serverIndex}}" ng-model="server.applicationName"> | ||
58 | + <div ng-messages="theForm['applicationName_' + serverIndex].$error"> | ||
59 | + <div translate ng-message="required">extension.field-required</div> | ||
60 | + </div> | ||
61 | + </md-input-container> | ||
62 | + | ||
63 | + | ||
64 | + <md-input-container flex="50" class="md-block" md-is-error="theForm['applicationUri_' + serverIndex].$touched && theForm['applicationUri_' + serverIndex].$invalid"> | ||
65 | + <label translate>extension.opc-application-uri</label> | ||
66 | + <input required name="applicationUri_{{serverIndex}}" ng-model="server.applicationUri"> | ||
67 | + <div ng-messages="theForm['applicationUri_' + serverIndex].$error"> | ||
68 | + <div translate ng-message="required">extension.field-required</div> | ||
69 | + </div> | ||
70 | + </md-input-container> | ||
71 | + </div> | ||
72 | + | ||
73 | + | ||
74 | + <div layout="row"> | ||
75 | + <md-input-container flex="50" class="md-block"> | ||
76 | + <label translate>extension.host</label> | ||
77 | + <input required name="host_{{serverIndex}}" ng-model="server.host"> | ||
78 | + <div ng-messages="theForm['host_' + serverIndex].$error"> | ||
79 | + <div translate ng-message="required">extension.field-required</div> | ||
80 | + </div> | ||
81 | + </md-input-container> | ||
82 | + | ||
83 | + <md-input-container flex="50" class="md-block"> | ||
84 | + <label translate>extension.port</label> | ||
85 | + <input type="number" | ||
86 | + required | ||
87 | + name="port_{{serverIndex}}" | ||
88 | + ng-model="server.port" | ||
89 | + min="1" | ||
90 | + max="65535" | ||
91 | + > | ||
92 | + <div ng-messages="theForm['port_' + serverIndex].$error"> | ||
93 | + <div translate | ||
94 | + ng-message="required" | ||
95 | + >extension.field-required</div> | ||
96 | + <div translate | ||
97 | + ng-message="min" | ||
98 | + >Port should be in a range from 1 to 65535</div> | ||
99 | + <div translate | ||
100 | + ng-message="max" | ||
101 | + >Port should be in a range from 1 to 65535</div> | ||
102 | + </div> | ||
103 | + </md-input-container> | ||
104 | + </div> | ||
105 | + | ||
106 | + <div layout="row"> | ||
107 | + <md-input-container flex="50" class="md-block"> | ||
108 | + <label translate>extension.opc-scan-period-in-seconds</label> | ||
109 | + <input type="number" | ||
110 | + required | ||
111 | + name="scanPeriodInSeconds_{{serverIndex}}" | ||
112 | + ng-model="server.scanPeriodInSeconds"> | ||
113 | + <div ng-messages="theForm['scanPeriodInSeconds_' + serverIndex].$error"> | ||
114 | + <div translate | ||
115 | + ng-message="required" | ||
116 | + >extension.field-required</div> | ||
117 | + </div> | ||
118 | + </md-input-container> | ||
119 | + | ||
120 | + <md-input-container flex="50" class="md-block"> | ||
121 | + <label translate>extension.timeout</label> | ||
122 | + <input type="number" | ||
123 | + required name="timeoutInMillis_{{serverIndex}}" | ||
124 | + ng-model="server.timeoutInMillis" | ||
125 | + > | ||
126 | + <div ng-messages="theForm['timeoutInMillis_' + serverIndex].$error"> | ||
127 | + <div translate | ||
128 | + ng-message="required" | ||
129 | + >extension.field-required</div> | ||
130 | + </div> | ||
131 | + </md-input-container> | ||
132 | + </div> | ||
133 | + | ||
134 | + <div layout="row"> | ||
135 | + | ||
136 | + <md-input-container flex="50" class="md-block tb-container-for-select"> | ||
137 | + <label translate>extension.opc-security</label> | ||
138 | + <md-select required | ||
139 | + name="securityType_{{serverIndex}}" | ||
140 | + ng-model="server.security"> | ||
141 | + <md-option ng-value="securityType" | ||
142 | + ng-repeat="(securityType, securityValue) in types.extensionOpcSecurityTypes" | ||
143 | + ><span ng-bind="::securityValue"></span></md-option> | ||
144 | + </md-select> | ||
145 | + <div ng-messages="theForm['securityType_' + serverIndex].$error"> | ||
146 | + <div translate | ||
147 | + ng-message="required" | ||
148 | + >extension.field-required</div> | ||
149 | + </div> | ||
150 | + </md-input-container> | ||
151 | + | ||
152 | + <md-input-container flex="50" class="md-block tb-container-for-select"> | ||
153 | + <label translate>extension.opc-identity</label> | ||
154 | + <md-select required | ||
155 | + name="identityType_{{serverIndex}}" | ||
156 | + ng-model="server.identity.type" | ||
157 | + > | ||
158 | + <md-option ng-value="identityType" | ||
159 | + ng-repeat="(identityType, identityValue) in types.extensionIdentityType" | ||
160 | + ><span ng-bind="identityValue | translate"></span></md-option> | ||
161 | + </md-select> | ||
162 | + <div ng-messages="theForm['identityType_' + serverIndex].$error"> | ||
163 | + <div translate | ||
164 | + ng-message="required" | ||
165 | + >extension.field-required</div> | ||
166 | + </div> | ||
167 | + </md-input-container> | ||
168 | + </div> | ||
169 | + | ||
170 | + <div ng-if="server.identity.type != 'username'"> | ||
171 | + <span class="" | ||
172 | + ng-init="server.identity = {'type':'anonymous'}"></span> | ||
173 | + </div> | ||
174 | + <div layout="row" ng-if="server.identity.type == 'username'"> | ||
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> | ||
177 | + <input required | ||
178 | + name="identityUsername_{{serverIndex}}" | ||
179 | + ng-model="server.identity.username" | ||
180 | + > | ||
181 | + <div ng-messages="theForm['identityUsername_' + serverIndex].$error"> | ||
182 | + <div translate | ||
183 | + ng-message="required" | ||
184 | + >extension.field-required</div> | ||
185 | + </div> | ||
186 | + </md-input-container> | ||
187 | + | ||
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> | ||
190 | + <input required | ||
191 | + name="identityPassword_{{serverIndex}}" ng-model="server.identity.password"> | ||
192 | + <div ng-messages="theForm['identityPassword_' + serverIndex].$error"> | ||
193 | + <div translate | ||
194 | + ng-message="required" | ||
195 | + >extension.field-required</div> | ||
196 | + </div> | ||
197 | + </md-input-container> | ||
198 | + </div> | ||
199 | + | ||
200 | + <v-accordion id="opc-attributes-accordion" class="vAccordion--default"> | ||
201 | + <v-pane id="opc-attributes-pane" expanded="true"> | ||
202 | + <v-pane-header> | ||
203 | + {{ 'extension.opc-keystore' | translate }} | ||
204 | + </v-pane-header> | ||
205 | + <v-pane-content> | ||
206 | + | ||
207 | + <md-input-container class="md-block tb-container-for-select"> | ||
208 | + <label translate>extension.opc-keystore-type</label> | ||
209 | + <md-select required name="keystoreType_{{serverIndex}}" ng-model="server.keystore.type"> | ||
210 | + <md-option ng-value="keystoreType" ng-repeat="(keystoreType, keystoreValue) in types.extensionKeystoreType"><span ng-bind="::keystoreValue"></span></md-option> | ||
211 | + </md-select> | ||
212 | + <div ng-messages="theForm['keystoreType_'+serverIndex].$error"> | ||
213 | + <div translate ng-message="required">extension.field-required</div> | ||
214 | + </div> | ||
215 | + </md-input-container> | ||
216 | + | ||
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 | + </div> | ||
241 | + </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 | + | ||
248 | + | ||
249 | + <div flex layout="row"> | ||
250 | + <md-input-container flex="50" class="md-block"> | ||
251 | + <label translate>extension.opc-keystore-password</label> | ||
252 | + <input required name="keystorePassword_{{serverIndex}}" ng-model="server.keystore.password"> | ||
253 | + <div ng-messages="theForm['keystorePassword_' + serverIndex].$error"> | ||
254 | + <div translate ng-message="required">extension.field-required</div> | ||
255 | + </div> | ||
256 | + </md-input-container> | ||
257 | + | ||
258 | + <md-input-container flex="50" class="md-block"> | ||
259 | + <label translate>extension.opc-keystore-alias</label> | ||
260 | + <input required name="keystoreAlias_{{serverIndex}}" ng-model="server.keystore.alias"> | ||
261 | + <div ng-messages="theForm['keystoreAlias_' + serverIndex].$error"> | ||
262 | + <div translate ng-message="required">extension.field-required</div> | ||
263 | + </div> | ||
264 | + </md-input-container> | ||
265 | + </div> | ||
266 | + | ||
267 | + <md-input-container class="md-block"> | ||
268 | + <label translate>extension.opc-keystore-key-password</label> | ||
269 | + <input required name="keystoreKeyPassword_{{serverIndex}}" ng-model="server.keystore.keyPassword"> | ||
270 | + <div ng-messages="theForm['keystoreKeyPassword_' + serverIndex].$error"> | ||
271 | + <div translate ng-message="required">extension.field-required</div> | ||
272 | + </div> | ||
273 | + </md-input-container> | ||
274 | + | ||
275 | + </v-pane-content> | ||
276 | + </v-pane> | ||
277 | + </v-accordion> | ||
278 | + | ||
279 | + | ||
280 | + <v-accordion id="opc-attributes-accordion" | ||
281 | + class="vAccordion--default" | ||
282 | + > | ||
283 | + <v-pane id="opc-attributes-pane"> | ||
284 | + <v-pane-header> | ||
285 | + {{ 'extension.mapping' | translate }} | ||
286 | + </v-pane-header> | ||
287 | + <v-pane-content> | ||
288 | + <div ng-if="server.mapping.length > 0"> | ||
289 | + <ol class="list-group"> | ||
290 | + <li class="list-group-item" | ||
291 | + ng-repeat="(mapIndex, map) in server.mapping" | ||
292 | + > | ||
293 | + <md-button aria-label="{{ 'action.remove' | translate }}" | ||
294 | + class="md-icon-button" | ||
295 | + ng-click="removeItem(map, server.mapping)" | ||
296 | + > | ||
297 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | ||
298 | + <md-tooltip md-direction="top"> | ||
299 | + {{ 'action.remove' | translate }} | ||
300 | + </md-tooltip> | ||
301 | + </md-button> | ||
302 | + | ||
303 | + <md-card> | ||
304 | + <md-card-content> | ||
305 | + <div flex layout="row"> | ||
306 | + <md-input-container flex="50" class="md-block"> | ||
307 | + <label translate>extension.opc-device-node-pattern</label> | ||
308 | + <input required | ||
309 | + name="deviceNodePattern_{{serverIndex}}{{mapIndex}}" | ||
310 | + ng-model="map.deviceNodePattern" | ||
311 | + > | ||
312 | + <div ng-messages="theForm['deviceNodePattern_' + serverIndex + mapIndex].$error"> | ||
313 | + <div translate | ||
314 | + ng-message="required" | ||
315 | + >extension.field-required</div> | ||
316 | + </div> | ||
317 | + </md-input-container> | ||
318 | + | ||
319 | + <md-input-container flex="50" class="md-block"> | ||
320 | + <label translate>extension.opc-device-name-pattern</label> | ||
321 | + <input required | ||
322 | + name="deviceNamePattern_{{serverIndex}}{{mapIndex}}" | ||
323 | + ng-model="map.deviceNamePattern" | ||
324 | + > | ||
325 | + <div ng-messages="theForm['deviceNamePattern_' + serverIndex + mapIndex].$error"> | ||
326 | + <div translate | ||
327 | + ng-message="required" | ||
328 | + >extension.field-required</div> | ||
329 | + </div> | ||
330 | + </md-input-container> | ||
331 | + </div> | ||
332 | + | ||
333 | + | ||
334 | + <v-accordion id="opc-attributes-accordion" | ||
335 | + class="vAccordion--default" | ||
336 | + > | ||
337 | + <v-pane id="opc-attributes-pane"> | ||
338 | + <v-pane-header> | ||
339 | + {{ 'extension.attributes' | translate }} | ||
340 | + </v-pane-header> | ||
341 | + <v-pane-content> | ||
342 | + <div ng-show="map.attributes.length > 0"> | ||
343 | + <ol class="list-group"> | ||
344 | + <li class="list-group-item" | ||
345 | + ng-repeat="(attributeIndex, attribute) in map.attributes" | ||
346 | + > | ||
347 | + <md-button aria-label="{{ 'action.remove' | translate }}" | ||
348 | + class="md-icon-button" | ||
349 | + ng-click="removeItem(attribute, map.attributes)"> | ||
350 | + <ng-md-icon icon="close" | ||
351 | + aria-label="{{ 'action.remove' | translate }}" | ||
352 | + ></ng-md-icon> | ||
353 | + <md-tooltip md-direction="top"> | ||
354 | + {{ 'action.remove' | translate }} | ||
355 | + </md-tooltip> | ||
356 | + </md-button> | ||
357 | + <md-card> | ||
358 | + <md-card-content> | ||
359 | + | ||
360 | + <section flex | ||
361 | + layout="row" | ||
362 | + > | ||
363 | + <md-input-container flex="60" class="md-block"> | ||
364 | + <label translate>extension.key</label> | ||
365 | + <input required | ||
366 | + name="opcAttributeKey_{{serverIndex}}{{mapIndex}}{{attributeIndex}}" | ||
367 | + ng-model="attribute.key" | ||
368 | + > | ||
369 | + <div ng-messages="theForm['opcAttributeKey_' + serverIndex + mapIndex + attributeIndex].$error"> | ||
370 | + <div translate | ||
371 | + ng-message="required" | ||
372 | + >extension.field-required</div> | ||
373 | + </div> | ||
374 | + </md-input-container> | ||
375 | + <md-input-container flex="40" class="md-block tb-container-for-select"> | ||
376 | + <label translate>extension.type</label> | ||
377 | + <md-select required name="opcAttributeType_{{serverIndex}}{{mapIndex}}{{attributeIndex}}" | ||
378 | + ng-model="attribute.type" | ||
379 | + > | ||
380 | + <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" | ||
381 | + ng-value="attrType" | ||
382 | + > | ||
383 | + {{attrTypeValue | translate}} | ||
384 | + </md-option> | ||
385 | + </md-select> | ||
386 | + <div ng-messages="theForm['opcAttributeType_' + serverIndex + mapIndex + attributeIndex].$error"> | ||
387 | + <div translate | ||
388 | + ng-message="required" | ||
389 | + >extension.required-type</div> | ||
390 | + </div> | ||
391 | + </md-input-container> | ||
392 | + </section> | ||
393 | + | ||
394 | + <section flex layout="row"> | ||
395 | + <md-input-container flex="100" class="md-block"> | ||
396 | + <label translate>extension.value</label> | ||
397 | + <input required name="opcAttributeValue_{{serverIndex}}{{mapIndex}}{{attributeIndex}}" | ||
398 | + ng-model="attribute.value" | ||
399 | + > | ||
400 | + <div ng-messages="theForm['opcAttributeValue_' + serverIndex + mapIndex + attributeIndex].$error"> | ||
401 | + <div translate | ||
402 | + ng-message="required" | ||
403 | + >extension.field-required</div> | ||
404 | + </div> | ||
405 | + </md-input-container> | ||
406 | + | ||
407 | + </section> | ||
408 | + | ||
409 | + | ||
410 | + </md-card-content> | ||
411 | + </md-card> | ||
412 | + </li> | ||
413 | + </ol> | ||
414 | + </div> | ||
415 | + <div flex layout="row" layout-align="start center"> | ||
416 | + <md-button class="md-primary md-raised" | ||
417 | + ng-click="addNewAttribute(map.attributes)" | ||
418 | + aria-label="{{ 'action.add' | translate }}" | ||
419 | + > | ||
420 | + <md-icon class="material-icons">add</md-icon> | ||
421 | + <span translate>add-attribute</span> | ||
422 | + </md-button> | ||
423 | + </div> | ||
424 | + </v-pane-content> | ||
425 | + </v-pane> | ||
426 | + </v-accordion> | ||
427 | + | ||
428 | + <v-accordion id="opc-timeseries-accordion" class="vAccordion--default"> | ||
429 | + <v-pane id="opc-timeseries-pane"> | ||
430 | + <v-pane-header> | ||
431 | + {{ 'extension.timeseries' | translate }} | ||
432 | + </v-pane-header> | ||
433 | + <v-pane-content> | ||
434 | + <div ng-show="map.timeseries.length > 0"> | ||
435 | + <ol class="list-group"> | ||
436 | + <li class="list-group-item" | ||
437 | + ng-repeat="(timeseriesIndex, timeseries) in map.timeseries" | ||
438 | + > | ||
439 | + <md-button aria-label="{{ 'action.remove' | translate }}" | ||
440 | + class="md-icon-button" | ||
441 | + ng-click="removeItem(timeseries, map.timeseries)" | ||
442 | + > | ||
443 | + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> | ||
444 | + <md-tooltip md-direction="top"> | ||
445 | + {{ 'action.remove' | translate }} | ||
446 | + </md-tooltip> | ||
447 | + </md-button> | ||
448 | + <md-card> | ||
449 | + <md-card-content> | ||
450 | + <section flex layout="row"> | ||
451 | + <md-input-container flex="60" class="md-block"> | ||
452 | + <label translate>extension.key</label> | ||
453 | + <input required | ||
454 | + name="opcTimeseriesKey_{{serverIndex}}{{mapIndex}}{{timeseriesIndex}}" | ||
455 | + ng-model="timeseries.key" | ||
456 | + > | ||
457 | + <div ng-messages="theForm['opcTimeseriesKey_' + serverIndex + mapIndex + timeseriesIndex].$error"> | ||
458 | + <div translate | ||
459 | + ng-message="required" | ||
460 | + >extension.field-required</div> | ||
461 | + </div> | ||
462 | + </md-input-container> | ||
463 | + <md-input-container flex="40" | ||
464 | + class="md-block tb-container-for-select" | ||
465 | + > | ||
466 | + <label translate>extension.type</label> | ||
467 | + <md-select required | ||
468 | + name="opcTimeseriesType_{{serverIndex}}{{mapIndex}}{{timeseriesIndex}}" | ||
469 | + ng-model="timeseries.type" | ||
470 | + > | ||
471 | + <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" | ||
472 | + ng-value="attrType" | ||
473 | + > | ||
474 | + {{attrTypeValue | translate}} | ||
475 | + </md-option> | ||
476 | + </md-select> | ||
477 | + <div ng-messages="theForm['opcTimeseriesType_' + serverIndex + mapIndex + timeseriesIndex].$error"> | ||
478 | + <div translate | ||
479 | + ng-message="required" | ||
480 | + >extension.field-required</div> | ||
481 | + </div> | ||
482 | + </md-input-container> | ||
483 | + </section> | ||
484 | + <section flex layout="row"> | ||
485 | + <md-input-container flex="100" class="md-block"> | ||
486 | + <label translate>extension.value</label> | ||
487 | + <input required name="opcTimeseriesValue_{{serverIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.value"> | ||
488 | + <div ng-messages="theForm['opcTimeseriesValue_' + serverIndex + mapIndex + timeseriesIndex].$error"> | ||
489 | + <div translate ng-message="required">extension.required-value</div> | ||
490 | + </div> | ||
491 | + </md-input-container> | ||
492 | + </section> | ||
493 | + </md-card-content> | ||
494 | + </md-card> | ||
495 | + </li> | ||
496 | + </ol> | ||
497 | + </div> | ||
498 | + <div flex layout="row" layout-align="start center"> | ||
499 | + <md-button class="md-primary md-raised" | ||
500 | + ng-click="addNewAttribute(map.timeseries)" | ||
501 | + aria-label="{{ 'action.add' | translate }}" | ||
502 | + > | ||
503 | + <md-icon class="material-icons">add</md-icon> | ||
504 | + <span translate>extension.add-timeseries</span> | ||
505 | + </md-button> | ||
506 | + </div> | ||
507 | + </v-pane-content> | ||
508 | + </v-pane> | ||
509 | + </v-accordion> | ||
510 | + | ||
511 | + | ||
512 | + </md-card-content> | ||
513 | + </md-card> | ||
514 | + </li> | ||
515 | + </ol> | ||
516 | + </div> | ||
517 | + <div flex | ||
518 | + layout="row" | ||
519 | + layout-align="start center" | ||
520 | + > | ||
521 | + <md-button class="md-primary md-raised" | ||
522 | + ng-click="addMap(server.mapping)" | ||
523 | + aria-label="{{ 'action.add' | translate }}" | ||
524 | + > | ||
525 | + <md-icon class="material-icons">add</md-icon> | ||
526 | + <span translate>extension.add-map</span> | ||
527 | + </md-button> | ||
528 | + </div> | ||
529 | + </v-pane-content> | ||
530 | + </v-pane> | ||
531 | + </v-accordion> | ||
532 | + | ||
533 | + </md-card-content> | ||
534 | + </md-card> | ||
535 | + </li> | ||
536 | + </ol> | ||
537 | + | ||
538 | + <div flex | ||
539 | + layout="row" | ||
540 | + layout-align="start center" | ||
541 | + > | ||
542 | + <md-button class="md-primary md-raised" | ||
543 | + ng-click="addServer(configuration.servers)" | ||
544 | + aria-label="{{ 'action.add' | translate }}" | ||
545 | + > | ||
546 | + <md-icon class="material-icons">add</md-icon> | ||
547 | + <span translate>extension.opc-add-server</span> | ||
548 | + </md-button> | ||
549 | + </div> | ||
550 | + | ||
551 | + </div> | ||
552 | + </v-pane-content> | ||
553 | + </v-pane> | ||
554 | + </v-accordion> | ||
555 | + <!--{{config}}--> | ||
556 | + </md-card-content> | ||
557 | +</md-card> |
@@ -22,9 +22,6 @@ | @@ -22,9 +22,6 @@ | ||
22 | margin-top: 0; | 22 | margin-top: 0; |
23 | padding-left: 3px; | 23 | padding-left: 3px; |
24 | } | 24 | } |
25 | - .t-right { | ||
26 | - text-align: right; | ||
27 | - } | ||
28 | .tb-container { | 25 | .tb-container { |
29 | width:100%; | 26 | width:100%; |
30 | } | 27 | } |
@@ -33,6 +30,15 @@ | @@ -33,6 +30,15 @@ | ||
33 | padding: 5px 0 0 0; | 30 | padding: 5px 0 0 0; |
34 | } | 31 | } |
35 | } | 32 | } |
33 | + .dropdown-section { | ||
34 | + margin-bottom: 30px; | ||
35 | + } | ||
36 | +} | ||
37 | + | ||
38 | +.extension-form.extension-mqtt { | ||
39 | + md-checkbox{ | ||
40 | + margin-left: 10px; | ||
41 | + } | ||
36 | } | 42 | } |
37 | 43 | ||
38 | .tb-extension-custom-transformer-panel { | 44 | .tb-extension-custom-transformer-panel { |
@@ -48,4 +54,20 @@ | @@ -48,4 +54,20 @@ | ||
48 | .ace_text-input { | 54 | .ace_text-input { |
49 | position:absolute!important | 55 | position:absolute!important |
50 | } | 56 | } |
57 | +} | ||
58 | + | ||
59 | +.extensionDialog { | ||
60 | + min-width: 1000px; | ||
61 | +} | ||
62 | + | ||
63 | +.tb-container-for-select { | ||
64 | + height: 58px; | ||
65 | +} | ||
66 | + | ||
67 | +.tb-drop-file-input-hide { | ||
68 | + height: 200%; | ||
69 | + display: block; | ||
70 | + position: absolute; | ||
71 | + bottom: 0; | ||
72 | + width: 100%; | ||
51 | } | 73 | } |
@@ -17,11 +17,13 @@ | @@ -17,11 +17,13 @@ | ||
17 | import ExtensionTableDirective from './extension-table.directive'; | 17 | import ExtensionTableDirective from './extension-table.directive'; |
18 | import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive'; | 18 | import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive'; |
19 | import ExtensionFormMqttDirective from './extensions-forms/extension-form-mqtt.directive' | 19 | import ExtensionFormMqttDirective from './extensions-forms/extension-form-mqtt.directive' |
20 | +import ExtensionFormOpcDirective from './extensions-forms/extension-form-opc.directive'; | ||
20 | import {ParseToNull} from './extension-dialog.controller'; | 21 | import {ParseToNull} from './extension-dialog.controller'; |
21 | 22 | ||
22 | export default angular.module('thingsboard.extension', []) | 23 | export default angular.module('thingsboard.extension', []) |
23 | .directive('tbExtensionTable', ExtensionTableDirective) | 24 | .directive('tbExtensionTable', ExtensionTableDirective) |
24 | .directive('tbExtensionFormHttp', ExtensionFormHttpDirective) | 25 | .directive('tbExtensionFormHttp', ExtensionFormHttpDirective) |
25 | .directive('tbExtensionFormMqtt', ExtensionFormMqttDirective) | 26 | .directive('tbExtensionFormMqtt', ExtensionFormMqttDirective) |
27 | + .directive('tbExtensionFormOpc', ExtensionFormOpcDirective) | ||
26 | .directive('parseToNull', ParseToNull) | 28 | .directive('parseToNull', ParseToNull) |
27 | .name; | 29 | .name; |
@@ -739,12 +739,7 @@ export default angular.module('thingsboard.locale', []) | @@ -739,12 +739,7 @@ export default angular.module('thingsboard.locale', []) | ||
739 | "extension-id": "Extension id", | 739 | "extension-id": "Extension id", |
740 | "extension-type": "Extension type", | 740 | "extension-type": "Extension type", |
741 | "transformer-json": "JSON *", | 741 | "transformer-json": "JSON *", |
742 | - "id-required": "Extension id is required.", | ||
743 | "unique-id-required": "Current extension id already exists.", | 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 | "delete": "Delete extension", | 743 | "delete": "Delete extension", |
749 | "add": "Add extension", | 744 | "add": "Add extension", |
750 | "edit": "Edit extension", | 745 | "edit": "Edit extension", |
@@ -754,18 +749,13 @@ export default angular.module('thingsboard.locale', []) | @@ -754,18 +749,13 @@ export default angular.module('thingsboard.locale', []) | ||
754 | "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.", | 749 | "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.", |
755 | "converters": "Converters", | 750 | "converters": "Converters", |
756 | "converter-id": "Converter id", | 751 | "converter-id": "Converter id", |
757 | - "converter-id-required": "Converter id is required.", | ||
758 | "configuration": "Configuration", | 752 | "configuration": "Configuration", |
759 | "converter-configurations": "Converter configurations", | 753 | "converter-configurations": "Converter configurations", |
760 | "token": "Security token", | 754 | "token": "Security token", |
761 | "add-converter": "Add converter", | 755 | "add-converter": "Add converter", |
762 | - "add-converter-prompt": "Please add converter", | ||
763 | "add-config": "Add converter configuration", | 756 | "add-config": "Add converter configuration", |
764 | - "add-config-prompt": "Please add converter configuration", | ||
765 | "device-name-expression": "Device name expression", | 757 | "device-name-expression": "Device name expression", |
766 | - "device-name-expression-required": "Device name expression is required.", | ||
767 | "device-type-expression": "Device type expression", | 758 | "device-type-expression": "Device type expression", |
768 | - "device-type-expression-required": "Device type expression is required.", | ||
769 | "custom": "Custom", | 759 | "custom": "Custom", |
770 | "to-double": "To Double", | 760 | "to-double": "To Double", |
771 | "transformer": "Transformer", | 761 | "transformer": "Transformer", |
@@ -773,25 +763,20 @@ export default angular.module('thingsboard.locale', []) | @@ -773,25 +763,20 @@ export default angular.module('thingsboard.locale', []) | ||
773 | "json-parse": "Unable to parse transformer json.", | 763 | "json-parse": "Unable to parse transformer json.", |
774 | "attributes": "Attributes", | 764 | "attributes": "Attributes", |
775 | "add-attribute": "Add attribute", | 765 | "add-attribute": "Add attribute", |
766 | + "add-map": "Add mapping element", | ||
776 | "timeseries": "Timeseries", | 767 | "timeseries": "Timeseries", |
777 | "add-timeseries": "Add timeseries", | 768 | "add-timeseries": "Add timeseries", |
778 | - | 769 | + "field-required": "Field is required", |
779 | "brokers": "Brokers", | 770 | "brokers": "Brokers", |
780 | "add-broker": "Add broker", | 771 | "add-broker": "Add broker", |
781 | - "add-broker-prompt": "Please add broker", | ||
782 | "host": "Host", | 772 | "host": "Host", |
783 | - "host-required": "Host is required.", | ||
784 | "port": "Port", | 773 | "port": "Port", |
785 | - "port-required": "Port is required.", | ||
786 | "port-range": "Port should be in a range from 1 to 65535.", | 774 | "port-range": "Port should be in a range from 1 to 65535.", |
787 | "ssl": "Ssl", | 775 | "ssl": "Ssl", |
788 | "credentials": "Credentials", | 776 | "credentials": "Credentials", |
789 | "username": "Username", | 777 | "username": "Username", |
790 | - "username-required": "Username is required.", | ||
791 | "password": "Password", | 778 | "password": "Password", |
792 | - "password-required": "Password is required.", | ||
793 | - "retry-interval": "Retry interval", | ||
794 | - "retry-interval-required": "Retry interval is required.", | 779 | + "retry-interval": "Retry interval in milliseconds", |
795 | "anonymous": "Anonymous", | 780 | "anonymous": "Anonymous", |
796 | "basic": "Basic", | 781 | "basic": "Basic", |
797 | "pem": "PEM", | 782 | "pem": "PEM", |
@@ -801,28 +786,59 @@ export default angular.module('thingsboard.locale', []) | @@ -801,28 +786,59 @@ export default angular.module('thingsboard.locale', []) | ||
801 | "no-file": "No file selected.", | 786 | "no-file": "No file selected.", |
802 | "drop-file": "Drop a file or click to select a file to upload.", | 787 | "drop-file": "Drop a file or click to select a file to upload.", |
803 | "mapping": "Mapping", | 788 | "mapping": "Mapping", |
804 | - "add-map": "Add map", | ||
805 | "topic-filter": "Topic filter", | 789 | "topic-filter": "Topic filter", |
806 | - "topic-filter-required": "Topic filter is required.", | ||
807 | "converter-type": "Converter type", | 790 | "converter-type": "Converter type", |
808 | - "converter-type-required": "Converter type is required.", | ||
809 | "converter-json": "Json", | 791 | "converter-json": "Json", |
810 | - "name-expression": "Name expression", | ||
811 | - "type-expression": "Type expression", | ||
812 | - "json-name-expression": "Json name expression", | ||
813 | - "json-name-expression-required": "Json name expression is required.", | ||
814 | - "topic-name-expression": "Topic name expression", | ||
815 | - "topic-name-expression-required": "Topic name expression is required.", | ||
816 | - "json-type-expression": "Json type expression", | ||
817 | - "json-type-expression-required": "Json type expression is required.", | ||
818 | - "topic-type-expression": "Topic type expression", | ||
819 | - "topic-type-expression-required": "Topic type expression is required.", | 792 | + "json-name-expression": "Device name json expression", |
793 | + "topic-name-expression": "Device name topic expression", | ||
794 | + "json-type-expression": "Device type json expression", | ||
795 | + "topic-type-expression": "Device type topic expression", | ||
796 | + "attribute-key-expression": "Attribute key expression", | ||
797 | + "attr-json-key-expression": "Attribute key json expression", | ||
798 | + "attr-topic-key-expression": "Attribute key topic expression", | ||
799 | + "request-id-expression": "Request id expression", | ||
800 | + "request-id-json-expression": "Request id json expression", | ||
801 | + "request-id-topic-expression": "Request id topic expression", | ||
802 | + "response-topic-expression": "Response topic expression", | ||
803 | + "value-expression": "Value expression", | ||
820 | "topic": "Topic", | 804 | "topic": "Topic", |
821 | - "timeout": "Timeout", | 805 | + "timeout": "Timeout in milliseconds", |
822 | "converter-json-required": "Converter json is required.", | 806 | "converter-json-required": "Converter json is required.", |
823 | "converter-json-parse": "Unable to parse converter json.", | 807 | "converter-json-parse": "Unable to parse converter json.", |
824 | "filter-expression": "Filter expression", | 808 | "filter-expression": "Filter expression", |
825 | - "filter-expression-required": "Filter expression is required." | 809 | + "connect-requests": "Connect requests", |
810 | + "add-connect-request": "Add connect request", | ||
811 | + "disconnect-requests": "Disconnect requests", | ||
812 | + "add-disconnect-request": "Add disconnect request", | ||
813 | + "attribute-requests": "Attribute requests", | ||
814 | + "add-attribute-request": "Add attribute request", | ||
815 | + "attribute-updates": "Attribute updates", | ||
816 | + "add-attribute-update": "Add attribute update", | ||
817 | + "server-side-rpc": "Server side RPC", | ||
818 | + "add-server-side-rpc-request": "Add server-side RPC request", | ||
819 | + "device-name-filter": "Device name filter", | ||
820 | + "attribute-filter": "Attribute filter", | ||
821 | + "method-filter": "Method filter", | ||
822 | + "request-topic-expression": "Request topic expression", | ||
823 | + "response-timeout": "Response timeout in milliseconds", | ||
824 | + "topic-expression": "Topic expression", | ||
825 | + "client-scope": "Client scope", | ||
826 | + "opc-server": "Servers", | ||
827 | + "opc-add-server": "Add server", | ||
828 | + "opc-application-name": "Application name", | ||
829 | + "opc-application-uri": "Application uri", | ||
830 | + "opc-scan-period-in-seconds": "Scan period in seconds", | ||
831 | + "opc-security": "Security", | ||
832 | + "opc-identity": "Identity", | ||
833 | + "opc-keystore": "Keystore", | ||
834 | + "opc-type": "Type", | ||
835 | + "opc-keystore-type":"Type", | ||
836 | + "opc-keystore-location":"Location *", | ||
837 | + "opc-keystore-password":"Password", | ||
838 | + "opc-keystore-alias":"Alias", | ||
839 | + "opc-keystore-key-password":"Key password", | ||
840 | + "opc-device-node-pattern":"Device node pattern", | ||
841 | + "opc-device-name-pattern":"Device name pattern", | ||
826 | }, | 842 | }, |
827 | "fullscreen": { | 843 | "fullscreen": { |
828 | "expand": "Expand to fullscreen", | 844 | "expand": "Expand to fullscreen", |