Showing
15 changed files
with
1626 additions
and
274 deletions
... | ... | @@ -30,6 +30,7 @@ |
30 | 30 | "angular-material": "1.1.1", |
31 | 31 | "angular-material-data-table": "^0.10.9", |
32 | 32 | "angular-material-icons": "^0.7.1", |
33 | + "angular-material-expansion-panel": "^0.7.2", | |
33 | 34 | "angular-messages": "1.5.8", |
34 | 35 | "angular-route": "1.5.8", |
35 | 36 | "angular-sanitize": "1.5.8", | ... | ... |
... | ... | @@ -39,6 +39,7 @@ import uiRouter from 'angular-ui-router'; |
39 | 39 | import angularJwt from 'angular-jwt'; |
40 | 40 | import 'angular-drag-and-drop-lists'; |
41 | 41 | import mdDataTable from 'angular-material-data-table'; |
42 | +import 'angular-material-expansion-panel'; | |
42 | 43 | import ngTouch from 'angular-touch'; |
43 | 44 | import 'angular-carousel'; |
44 | 45 | import 'clipboard'; |
... | ... | @@ -82,6 +83,7 @@ import 'md-color-picker/dist/mdColorPicker.min.css'; |
82 | 83 | import 'mdPickers/dist/mdPickers.min.css'; |
83 | 84 | import 'angular-hotkeys/build/hotkeys.min.css'; |
84 | 85 | import 'angular-carousel/dist/angular-carousel.min.css'; |
86 | +import 'angular-material-expansion-panel/dist/md-expansion-panel.min.css'; | |
85 | 87 | import '../scss/main.scss'; |
86 | 88 | |
87 | 89 | import AppConfig from './app.config'; |
... | ... | @@ -103,6 +105,7 @@ angular.module('thingsboard', [ |
103 | 105 | angularJwt, |
104 | 106 | 'dndLists', |
105 | 107 | mdDataTable, |
108 | + 'material.components.expansionPanels', | |
106 | 109 | ngTouch, |
107 | 110 | 'angular-carousel', |
108 | 111 | 'ngclipboard', | ... | ... |
... | ... | @@ -350,6 +350,20 @@ export default angular.module('thingsboard.types', []) |
350 | 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 | 367 | latestTelemetry: { |
354 | 368 | value: "LATEST_TELEMETRY", |
355 | 369 | name: "attribute.scope-latest-telemetry", | ... | ... |
... | ... | @@ -29,45 +29,88 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
29 | 29 | vm.entityId = entityId; |
30 | 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 | 61 | function cancel() { |
45 | 62 | $mdDialog.cancel(); |
46 | 63 | } |
64 | + | |
65 | + vm.save = save; | |
47 | 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 | 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 | 111 | vm.validateId = function() { |
69 | 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 | 115 | if(coincidenceArray.length) { |
73 | 116 | if(!vm.isAdd) { |
... | ... | @@ -82,11 +125,11 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
82 | 125 | } else { |
83 | 126 | $scope.theForm.extensionId.$setValidity('uniqueIdValidation', true); |
84 | 127 | } |
85 | - } | |
128 | + }; | |
86 | 129 | |
87 | 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 | 133 | if(config && config.length > 0) { |
91 | 134 | for(let i=0;i<config.length;i++) { |
92 | 135 | for(let j=0;j<config[i].converters.length;j++){ |
... | ... | @@ -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 | 154 | if(brokers && brokers.length > 0) { |
112 | 155 | for(let i=0;i<brokers.length;i++) { |
113 | 156 | if(brokers[i].mapping && brokers[i].mapping.length > 0) { |
... | ... | @@ -119,6 +162,27 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, |
119 | 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 | 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 | 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 | 20 | <md-toolbar> |
21 | 21 | <div class="md-toolbar-tools"> |
22 | 22 | <h2 translate>{{ vm.isAdd ? 'extension.add' : 'extension.edit'}}</h2> |
... | ... | @@ -26,49 +26,54 @@ |
26 | 26 | </md-button> |
27 | 27 | </div> |
28 | 28 | </md-toolbar> |
29 | + | |
29 | 30 | <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
31 | + | |
30 | 32 | <span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
33 | + | |
31 | 34 | <md-dialog-content> |
32 | 35 | <div class="md-dialog-content"> |
33 | 36 | <md-content class="md-padding" layout="column"> |
34 | 37 | <fieldset ng-disabled="loading"> |
35 | 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 | 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 | 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 | 44 | <div translate ng-message="uniqueIdValidation">extension.unique-id-required</div> |
42 | 45 | </div> |
43 | 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 | 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 | 52 | <md-option ng-repeat="(key,value) in vm.types.extensionType" ng-value="value"> |
48 | 53 | {{value}} |
49 | 54 | </md-option> |
50 | 55 | </md-select> |
56 | + | |
51 | 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 | 59 | </div> |
54 | 60 | </md-input-container> |
55 | 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 | 65 | </fieldset> |
61 | - | |
62 | - <!--<div>{{vm.newExtension}}</div>--> | |
63 | 66 | </md-content> |
64 | 67 | </div> |
65 | 68 | </md-dialog-content> |
69 | + | |
66 | 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 | 74 | {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} |
71 | 75 | </md-button> |
76 | + | |
72 | 77 | <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }} |
73 | 78 | </md-button> |
74 | 79 | </md-dialog-actions> | ... | ... |
... | ... | @@ -126,11 +126,13 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, |
126 | 126 | controllerAs: 'vm', |
127 | 127 | templateUrl: extensionDialogTemplate, |
128 | 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 | 136 | bindToController: true, |
135 | 137 | targetEvent: $event, |
136 | 138 | fullscreen: true, | ... | ... |
... | ... | @@ -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 | 57 | scope.addConverterConfig = function() { |
83 | 58 | var newConverterConfig = {converterId:"", converters:[]}; |
84 | 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 | 65 | scope.removeConverterConfig = function(config) { |
88 | 66 | var index = scope.converterConfigs.indexOf(config); |
89 | 67 | if (index > -1) { |
90 | 68 | scope.converterConfigs.splice(index, 1); |
91 | 69 | } |
92 | - scope.theForm.$setDirty(); | |
93 | - } | |
70 | + }; | |
94 | 71 | |
95 | 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 | 79 | converters.push(newConverter); |
98 | - } | |
80 | + }; | |
99 | 81 | |
100 | 82 | scope.removeConverter = function(converter, converters) { |
101 | 83 | var index = converters.indexOf(converter); |
102 | 84 | if (index > -1) { |
103 | 85 | converters.splice(index, 1); |
104 | 86 | } |
105 | - scope.theForm.$setDirty(); | |
106 | - } | |
87 | + }; | |
107 | 88 | |
108 | 89 | scope.addAttribute = function(attributes) { |
109 | 90 | var newAttribute = {type:"", key:"", value:""}; |
110 | 91 | attributes.push(newAttribute); |
111 | - } | |
92 | + }; | |
112 | 93 | |
113 | 94 | scope.removeAttribute = function(attribute, attributes) { |
114 | 95 | var index = attributes.indexOf(attribute); |
115 | 96 | if (index > -1) { |
116 | 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 | 109 | scope.transformerTypeChange = function(attribute) { |
122 | 110 | attribute.transformer = ""; |
123 | - } | |
111 | + }; | |
124 | 112 | |
125 | 113 | scope.validateTransformer = function (model, editorName) { |
126 | 114 | if(model && model.length) { |
... | ... | @@ -131,10 +119,10 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
131 | 119 | scope.theForm[editorName].$setValidity('transformerJSON', false); |
132 | 120 | } |
133 | 121 | } |
134 | - } | |
135 | - | |
122 | + }; | |
123 | + | |
136 | 124 | $compile(element.contents())(scope); |
137 | - } | |
125 | + }; | |
138 | 126 | |
139 | 127 | return { |
140 | 128 | restrict: "A", | ... | ... |
... | ... | @@ -23,18 +23,19 @@ |
23 | 23 | </md-card-title> |
24 | 24 | <md-card-content> |
25 | 25 | <v-accordion id="http-converter-configs-accordion" class="vAccordion--default"> |
26 | - <v-pane id="http-converters-pane" expanded="isAdd"> | |
26 | + <v-pane id="http-converters-pane" expanded="true"> | |
27 | 27 | <v-pane-header> |
28 | 28 | {{ 'extension.converter-configurations' | translate }} |
29 | 29 | </v-pane-header> |
30 | 30 | <v-pane-content> |
31 | - <div ng-if="converterConfigs.length === 0"> | |
32 | - <span translate layout-align="center center" class="tb-prompt">extension.add-config-prompt</span> | |
33 | - </div> | |
34 | 31 | <div ng-if="converterConfigs.length > 0"> |
35 | 32 | <ol class="list-group"> |
36 | - <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 | 39 | <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> |
39 | 40 | <md-tooltip md-direction="top"> |
40 | 41 | {{ 'action.remove' | translate }} |
... | ... | @@ -43,11 +44,11 @@ |
43 | 44 | <md-card> |
44 | 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 | 48 | <label translate>extension.converter-id</label> |
48 | 49 | <input required name="httpConverterId_{{configIndex}}" ng-model="config.converterId"> |
49 | 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 | 52 | </div> |
52 | 53 | </md-input-container> |
53 | 54 | <md-input-container class="md-block"> |
... | ... | @@ -55,18 +56,21 @@ |
55 | 56 | <input name="httpToken" ng-model="config.token" parse-to-null> |
56 | 57 | </md-input-container> |
57 | 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 | 60 | <v-pane-header> |
60 | 61 | {{ 'extension.converters' | translate }} |
61 | 62 | </v-pane-header> |
62 | 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 | 64 | <div ng-if="config.converters.length > 0"> |
67 | 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 | 74 | <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> |
71 | 75 | <md-tooltip md-direction="top"> |
72 | 76 | {{ 'action.remove' | translate }} |
... | ... | @@ -74,18 +78,18 @@ |
74 | 78 | </md-button> |
75 | 79 | <md-card> |
76 | 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 | 82 | <label translate>extension.device-name-expression</label> |
79 | 83 | <input required name="httpDeviceNameExp_{{configIndex}}{{converterIndex}}" ng-model="converter.deviceNameJsonExpression"> |
80 | 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 | 86 | </div> |
83 | 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 | 89 | <label translate>extension.device-type-expression</label> |
86 | 90 | <input required name="httpDeviceTypeExp_{{configIndex}}{{converterIndex}}" ng-model="converter.deviceTypeJsonExpression"> |
87 | 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 | 93 | </div> |
90 | 94 | </md-input-container> |
91 | 95 | |
... | ... | @@ -107,14 +111,14 @@ |
107 | 111 | <md-card> |
108 | 112 | <md-card-content> |
109 | 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 | 115 | <label translate>extension.key</label> |
112 | 116 | <input required name="httpAttributeKey_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.key"> |
113 | 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 | 119 | </div> |
116 | 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 | 122 | <label translate>extension.type</label> |
119 | 123 | <md-select required name="httpAttributeType_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.type"> |
120 | 124 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> |
... | ... | @@ -122,16 +126,16 @@ |
122 | 126 | </md-option> |
123 | 127 | </md-select> |
124 | 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 | 130 | </div> |
127 | 131 | </md-input-container> |
128 | 132 | </section> |
129 | 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 | 135 | <label translate>extension.value</label> |
132 | 136 | <input required name="httpAttributeValue_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.value"> |
133 | 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 | 139 | </div> |
136 | 140 | </md-input-container> |
137 | 141 | |
... | ... | @@ -172,11 +176,8 @@ |
172 | 176 | <div flex layout="row" layout-align="start center"> |
173 | 177 | <md-button class="md-primary md-raised" |
174 | 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 | 179 | <md-icon class="material-icons">add</md-icon> |
179 | - <span translate>action.add</span> | |
180 | + <span translate>extension.add-attribute</span> | |
180 | 181 | </md-button> |
181 | 182 | </div> |
182 | 183 | </v-pane-content> |
... | ... | @@ -202,14 +203,14 @@ |
202 | 203 | <md-card> |
203 | 204 | <md-card-content> |
204 | 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 | 207 | <label translate>extension.key</label> |
207 | 208 | <input required name="httpTimeseriesKey_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.key"> |
208 | 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 | 211 | </div> |
211 | 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 | 214 | <label translate>extension.type</label> |
214 | 215 | <md-select required name="httpTimeseriesType_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.type"> |
215 | 216 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> |
... | ... | @@ -217,16 +218,16 @@ |
217 | 218 | </md-option> |
218 | 219 | </md-select> |
219 | 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 | 222 | </div> |
222 | 223 | </md-input-container> |
223 | 224 | </section> |
224 | 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 | 227 | <label translate>extension.value</label> |
227 | 228 | <input required name="httpTimeseriesValue_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.value"> |
228 | 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 | 231 | </div> |
231 | 232 | </md-input-container> |
232 | 233 | |
... | ... | @@ -267,11 +268,8 @@ |
267 | 268 | <div flex layout="row" layout-align="start center"> |
268 | 269 | <md-button class="md-primary md-raised" |
269 | 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 | 271 | <md-icon class="material-icons">add</md-icon> |
274 | - <span translate>action.add</span> | |
272 | + <span translate>extension.add-timeseries</span> | |
275 | 273 | </md-button> |
276 | 274 | </div> |
277 | 275 | </v-pane-content> |
... | ... | @@ -285,11 +283,8 @@ |
285 | 283 | <div flex layout="row" layout-align="start center"> |
286 | 284 | <md-button class="md-primary md-raised" |
287 | 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 | 286 | <md-icon class="material-icons">add</md-icon> |
292 | - <span translate>action.add</span> | |
287 | + <span translate>extension.add-converter</span> | |
293 | 288 | </md-button> |
294 | 289 | </div> |
295 | 290 | </v-pane-content> |
... | ... | @@ -304,11 +299,8 @@ |
304 | 299 | <div flex layout="row" layout-align="start center"> |
305 | 300 | <md-button class="md-primary md-raised" |
306 | 301 | ng-click="addConverterConfig()" aria-label="{{ 'action.add' | translate }}"> |
307 | - <md-tooltip md-direction="top"> | |
308 | - {{ 'extension.add-config' | translate }} | |
309 | - </md-tooltip> | |
310 | 302 | <md-icon class="material-icons">add</md-icon> |
311 | - <span translate>action.add</span> | |
303 | + <span translate>extension.add-config</span> | |
312 | 304 | </md-button> |
313 | 305 | </div> |
314 | 306 | </v-pane-content> | ... | ... |
... | ... | @@ -33,14 +33,22 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
33 | 33 | scope.types = types; |
34 | 34 | scope.theForm = scope.$parent.theForm; |
35 | 35 | |
36 | - scope.nameExpressions = { | |
36 | + scope.deviceNameExpressions = { | |
37 | 37 | deviceNameJsonExpression: "extension.converter-json", |
38 | 38 | deviceNameTopicExpression: "extension.topic" |
39 | 39 | }; |
40 | - scope.typeExpressions = { | |
40 | + scope.deviceTypeExpressions = { | |
41 | 41 | deviceTypeJsonExpression: "extension.converter-json", |
42 | 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 | 53 | scope.extensionCustomConverterOptions = { |
46 | 54 | useWrapMode: false, |
... | ... | @@ -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 | 69 | scope.updateValidity = function () { |
70 | - var valid = scope.brokers && scope.brokers.length > 0; | |
71 | - scope.theForm.$setValidity('brokers', valid); | |
72 | 70 | if(scope.brokers.length) { |
73 | 71 | for(let i=0;i<scope.brokers.length;i++) { |
74 | 72 | if(scope.brokers[i].credentials.type == scope.types.mqttCredentialTypes.pem.value) { |
... | ... | @@ -81,57 +79,116 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
81 | 79 | } |
82 | 80 | } |
83 | 81 | } |
84 | - } | |
82 | + }; | |
85 | 83 | |
86 | 84 | scope.$watch('brokers', function() { |
87 | 85 | scope.updateValidity(); |
88 | 86 | }, true); |
89 | 87 | |
90 | 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 | 102 | scope.brokers.push(newBroker); |
93 | - } | |
103 | + }; | |
94 | 104 | |
95 | 105 | scope.removeBroker = function(broker) { |
96 | 106 | var index = scope.brokers.indexOf(broker); |
97 | 107 | if (index > -1) { |
98 | 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 | 120 | scope.addMap = function(mapping) { |
104 | 121 | var newMap = {topicFilter:"sensors", converter:{attributes:[],timeseries:[]}}; |
105 | 122 | |
106 | 123 | mapping.push(newMap); |
107 | - } | |
124 | + }; | |
108 | 125 | |
109 | 126 | scope.removeMap = function(map, mapping) { |
110 | 127 | var index = mapping.indexOf(map); |
111 | 128 | if (index > -1) { |
112 | 129 | mapping.splice(index, 1); |
113 | 130 | } |
114 | - scope.theForm.$setDirty(); | |
115 | - } | |
131 | + }; | |
116 | 132 | |
117 | 133 | scope.addAttribute = function(attributes) { |
118 | 134 | var newAttribute = {type:"", key:"", value:""}; |
119 | 135 | attributes.push(newAttribute); |
120 | - } | |
136 | + }; | |
121 | 137 | |
122 | 138 | scope.removeAttribute = function(attribute, attributes) { |
123 | 139 | var index = attributes.indexOf(attribute); |
124 | 140 | if (index > -1) { |
125 | 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 | 187 | scope.changeCredentials = function(broker) { |
131 | 188 | var type = broker.credentials.type; |
132 | 189 | broker.credentials = {}; |
133 | 190 | broker.credentials.type = type; |
134 | - } | |
191 | + }; | |
135 | 192 | |
136 | 193 | scope.changeConverterType = function(map) { |
137 | 194 | if(map.converterType == "custom"){ |
... | ... | @@ -140,20 +197,32 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
140 | 197 | if(map.converterType == "json") { |
141 | 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 | 227 | scope.changeTypeExpression = function(converter) { |
159 | 228 | if(converter.typeExp == "deviceTypeJsonExpression") { |
... | ... | @@ -166,7 +235,37 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
166 | 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 | 270 | scope.validateCustomConverter = function(model, editorName) { |
172 | 271 | if(model && model.length) { |
... | ... | @@ -177,7 +276,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
177 | 276 | scope.theForm[editorName].$setValidity('converterJSON', false); |
178 | 277 | } |
179 | 278 | } |
180 | - } | |
279 | + }; | |
181 | 280 | |
182 | 281 | scope.fileAdded = function($file, broker, fileType) { |
183 | 282 | var reader = new FileReader(); |
... | ... | @@ -204,7 +303,7 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
204 | 303 | }); |
205 | 304 | }; |
206 | 305 | reader.readAsDataURL($file.file); |
207 | - } | |
306 | + }; | |
208 | 307 | |
209 | 308 | scope.clearFile = function(broker, fileType) { |
210 | 309 | scope.theForm.$setDirty(); |
... | ... | @@ -220,10 +319,10 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr |
220 | 319 | broker.credentials.certFileName = null; |
221 | 320 | broker.credentials.cert = null; |
222 | 321 | } |
223 | - } | |
322 | + }; | |
224 | 323 | |
225 | 324 | $compile(element.contents())(scope); |
226 | - } | |
325 | + }; | |
227 | 326 | |
228 | 327 | return { |
229 | 328 | restrict: "A", | ... | ... |
... | ... | @@ -23,18 +23,15 @@ |
23 | 23 | </md-card-title> |
24 | 24 | <md-card-content> |
25 | 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 | 27 | <v-pane-header> |
28 | 28 | {{ 'extension.brokers' | translate }} |
29 | 29 | </v-pane-header> |
30 | 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 | 31 | <div ng-if="brokers.length > 0"> |
35 | 32 | <ol class="list-group"> |
36 | 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 | 35 | <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon> |
39 | 36 | <md-tooltip md-direction="top"> |
40 | 37 | {{ 'action.remove' | translate }} |
... | ... | @@ -47,7 +44,7 @@ |
47 | 44 | <label translate>extension.port</label> |
48 | 45 | <input required type="number" min="1" max="65535" name="mqttPort_{{brokerIndex}}" ng-model="broker.port"> |
49 | 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 | 48 | <div translate ng-message="min">extension.port-range</div> |
52 | 49 | <div translate ng-message="max">extension.port-range</div> |
53 | 50 | </div> |
... | ... | @@ -56,7 +53,7 @@ |
56 | 53 | <label translate>extension.host</label> |
57 | 54 | <input required name="mqttHost_{{brokerIndex}}" ng-model="broker.host"> |
58 | 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 | 57 | </div> |
61 | 58 | </md-input-container> |
62 | 59 | </section> |
... | ... | @@ -65,7 +62,7 @@ |
65 | 62 | <label translate>extension.retry-interval</label> |
66 | 63 | <input required type="number" name="mqttRetryInterval_{{brokerIndex}}" ng-model="broker.retryInterval"> |
67 | 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 | 66 | </div> |
70 | 67 | </md-input-container> |
71 | 68 | <md-input-container flex="50" class="md-block"> |
... | ... | @@ -76,30 +73,30 @@ |
76 | 73 | </md-option> |
77 | 74 | </md-select> |
78 | 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 | 77 | <md-checkbox flex aria-label="{{ 'extension.ssl' | translate }}" |
81 | 78 | ng-model="broker.ssl">{{ 'extension.ssl' | translate }} |
82 | 79 | </md-checkbox> |
83 | 80 | </md-input-container> |
84 | 81 | </section> |
85 | 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 | 84 | <label translate>extension.username</label> |
88 | 85 | <input required name="mqttUsername_{{brokerIndex}}" ng-model="broker.credentials.username"> |
89 | 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 | 88 | </div> |
92 | 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 | 91 | <label translate>extension.password</label> |
95 | 92 | <input required name="mqttPassword_{{brokerIndex}}" ng-model="broker.credentials.password"> |
96 | 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 | 95 | </div> |
99 | 96 | </md-input-container> |
100 | 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 | 100 | <label class="tb-label" translate>extension.ca-cert</label> |
104 | 101 | <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "caCert")' class="tb-file-select-container"> |
105 | 102 | <div class="tb-file-clear-container"> |
... | ... | @@ -120,7 +117,7 @@ |
120 | 117 | <div ng-if="!broker.credentials.caCertFileName" class="tb-error-message" translate>extension.no-file</div> |
121 | 118 | <div ng-if="broker.credentials.caCertFileName">{{broker.credentials.caCertFileName}}</div> |
122 | 119 | </div> |
123 | - <div class="tb-container"> | |
120 | + <div class="tb-container" ng-class="broker.credentials.privateKeyFileName ? 'ng-valid' : 'ng-invalid'"> | |
124 | 121 | <label class="tb-label" translate>extension.private-key</label> |
125 | 122 | <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, broker, "privateKey")' class="tb-file-select-container"> |
126 | 123 | <div class="tb-file-clear-container"> |
... | ... | @@ -182,7 +179,7 @@ |
182 | 179 | <md-card> |
183 | 180 | <md-card-content> |
184 | 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 | 183 | <label translate>extension.converter-type</label> |
187 | 184 | <md-select required name="mqttConverterType_{{brokerIndex}}{{mapIndex}}" ng-model="map.converterType" ng-change="changeConverterType(map)"> |
188 | 185 | <md-option ng-repeat="(converterType, value) in types.mqttConverterTypes" ng-value="converterType"> |
... | ... | @@ -190,64 +187,70 @@ |
190 | 187 | </md-option> |
191 | 188 | </md-select> |
192 | 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 | 191 | </div> |
195 | 192 | </md-input-container> |
196 | 193 | <md-input-container flex="60" class="md-block"> |
197 | 194 | <label translate>extension.topic-filter</label> |
198 | 195 | <input required name="mqttTopicFilter_{{brokerIndex}}{{mapIndex}}" ng-model="map.topicFilter"> |
199 | 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 | 198 | </div> |
202 | 199 | </md-input-container> |
203 | 200 | </section> |
204 | 201 | |
205 | 202 | <div ng-if='map.converterType =="json"' ng-init="map.converter.type = 'json'"> |
206 | 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 | 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 | 208 | {{value | translate}} |
212 | 209 | </md-option> |
213 | 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 | 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 | 216 | <label translate>extension.json-name-expression</label> |
217 | 217 | <input required name="mqttJsonNameExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceNameJsonExpression"> |
218 | 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 | 220 | </div> |
221 | 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 | 223 | <label translate>extension.topic-name-expression</label> |
224 | 224 | <input required name="mqttTopicNameExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceNameTopicExpression"> |
225 | 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 | 227 | </div> |
228 | 228 | </md-input-container> |
229 | 229 | </section> |
230 | 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 | 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 | 235 | {{value | translate}} |
236 | 236 | </md-option> |
237 | 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 | 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 | 243 | <label translate>extension.json-type-expression</label> |
241 | 244 | <input required name="mqttJsonTypeExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceTypeJsonExpression"> |
242 | 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 | 247 | </div> |
245 | 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 | 250 | <label translate>extension.topic-type-expression</label> |
248 | 251 | <input required name="mqttTopicTypeExp_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.deviceTypeTopicExpression"> |
249 | 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 | 254 | </div> |
252 | 255 | </md-input-container> |
253 | 256 | </section> |
... | ... | @@ -256,11 +259,11 @@ |
256 | 259 | <label translate>extension.timeout</label> |
257 | 260 | <input type="number" name="mqttTimeout_{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.timeout" parse-to-null> |
258 | 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 | 263 | <label translate>extension.filter-expression</label> |
261 | 264 | <input required name="mqttFilterExpression{{brokerIndex}}{{mapIndex}}" ng-model="map.converter.filterExpression"> |
262 | 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 | 267 | </div> |
265 | 268 | </md-input-container> |
266 | 269 | </section> |
... | ... | @@ -301,14 +304,14 @@ |
301 | 304 | <md-card> |
302 | 305 | <md-card-content> |
303 | 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 | 308 | <label translate>extension.key</label> |
306 | 309 | <input required name="mqttAttributeKey_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.key"> |
307 | 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 | 312 | </div> |
310 | 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 | 315 | <label translate>extension.type</label> |
313 | 316 | <md-select required name="mqttAttributeType_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.type"> |
314 | 317 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> |
... | ... | @@ -316,15 +319,15 @@ |
316 | 319 | </md-option> |
317 | 320 | </md-select> |
318 | 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 | 323 | </div> |
321 | 324 | </md-input-container> |
322 | 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 | 327 | <label translate>extension.value</label> |
325 | 328 | <input required name="mqttAttributeValue_{{brokerIndex}}{{mapIndex}}{{attributeIndex}}" ng-model="attribute.value"> |
326 | 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 | 331 | </div> |
329 | 332 | </md-input-container> |
330 | 333 | </md-card-content> |
... | ... | @@ -335,11 +338,8 @@ |
335 | 338 | <div flex layout="row" layout-align="start center"> |
336 | 339 | <md-button class="md-primary md-raised" |
337 | 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 | 341 | <md-icon class="material-icons">add</md-icon> |
342 | - <span translate>action.add</span> | |
342 | + <span translate>extension.add-attribute</span> | |
343 | 343 | </md-button> |
344 | 344 | </div> |
345 | 345 | </v-pane-content> |
... | ... | @@ -364,14 +364,14 @@ |
364 | 364 | <md-card> |
365 | 365 | <md-card-content> |
366 | 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 | 368 | <label translate>extension.key</label> |
369 | 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 | 372 | </div> |
373 | 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 | 375 | <label translate>extension.type</label> |
376 | 376 | <md-select required name="mqttTimeseriesType_{{brokerIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.type"> |
377 | 377 | <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType"> |
... | ... | @@ -379,15 +379,15 @@ |
379 | 379 | </md-option> |
380 | 380 | </md-select> |
381 | 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 | 383 | </div> |
384 | 384 | </md-input-container> |
385 | 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 | 387 | <label translate>extension.value</label> |
388 | 388 | <input required name="mqttTimeseriesValue_{{brokerIndex}}{{mapIndex}}{{timeseriesIndex}}" ng-model="timeseries.value"> |
389 | 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 | 391 | </div> |
392 | 392 | </md-input-container> |
393 | 393 | </md-card-content> |
... | ... | @@ -398,11 +398,8 @@ |
398 | 398 | <div flex layout="row" layout-align="start center"> |
399 | 399 | <md-button class="md-primary md-raised" |
400 | 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 | 401 | <md-icon class="material-icons">add</md-icon> |
405 | - <span translate>action.add</span> | |
402 | + <span translate>extension.add-timeseries</span> | |
406 | 403 | </md-button> |
407 | 404 | </div> |
408 | 405 | </v-pane-content> |
... | ... | @@ -417,16 +414,429 @@ |
417 | 414 | <div flex layout="row" layout-align="start center"> |
418 | 415 | <md-button class="md-primary md-raised" |
419 | 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 | 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 | 553 | </md-button> |
426 | 554 | </div> |
427 | 555 | </v-pane-content> |
428 | 556 | </v-pane> |
429 | 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 | 840 | </md-card-content> |
431 | 841 | </md-card> |
432 | 842 | </li> |
... | ... | @@ -436,18 +846,15 @@ |
436 | 846 | <div flex layout="row" layout-align="start center"> |
437 | 847 | <md-button class="md-primary md-raised" |
438 | 848 | ng-click="addBroker()" aria-label="{{ 'action.add' | translate }}"> |
439 | - <md-tooltip md-direction="top"> | |
440 | - {{ 'extension.add-broker' | translate }} | |
441 | - </md-tooltip> | |
442 | 849 | <md-icon class="material-icons">add</md-icon> |
443 | - <span translate>action.add</span> | |
850 | + <span translate>extension.add-broker</span> | |
444 | 851 | </md-button> |
445 | 852 | </div> |
446 | 853 | </v-pane-content> |
447 | 854 | </v-pane> |
448 | 855 | </v-accordion> |
449 | -<pre> | |
856 | +<!--<pre> | |
450 | 857 | {{config | json}} |
451 | -</pre> | |
858 | +</pre>--> | |
452 | 859 | </md-card-content> |
453 | 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 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -15,4 +15,543 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<div>OPC UA</div> | |
\ No newline at end of file | ||
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> | |
\ No newline at end of file | ... | ... |
... | ... | @@ -22,9 +22,6 @@ |
22 | 22 | margin-top: 0; |
23 | 23 | padding-left: 3px; |
24 | 24 | } |
25 | - .t-right { | |
26 | - text-align: right; | |
27 | - } | |
28 | 25 | .tb-container { |
29 | 26 | width:100%; |
30 | 27 | } |
... | ... | @@ -33,6 +30,15 @@ |
33 | 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 | 44 | .tb-extension-custom-transformer-panel { |
... | ... | @@ -48,4 +54,20 @@ |
48 | 54 | .ace_text-input { |
49 | 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 | } |
\ No newline at end of file | ... | ... |
... | ... | @@ -17,11 +17,13 @@ |
17 | 17 | import ExtensionTableDirective from './extension-table.directive'; |
18 | 18 | import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive'; |
19 | 19 | import ExtensionFormMqttDirective from './extensions-forms/extension-form-mqtt.directive' |
20 | +import ExtensionFormOpcDirective from './extensions-forms/extension-form-opc.directive'; | |
20 | 21 | import {ParseToNull} from './extension-dialog.controller'; |
21 | 22 | |
22 | 23 | export default angular.module('thingsboard.extension', []) |
23 | 24 | .directive('tbExtensionTable', ExtensionTableDirective) |
24 | 25 | .directive('tbExtensionFormHttp', ExtensionFormHttpDirective) |
25 | 26 | .directive('tbExtensionFormMqtt', ExtensionFormMqttDirective) |
27 | + .directive('tbExtensionFormOpc', ExtensionFormOpcDirective) | |
26 | 28 | .directive('parseToNull', ParseToNull) |
27 | 29 | .name; |
\ No newline at end of file | ... | ... |
... | ... | @@ -739,12 +739,7 @@ export default angular.module('thingsboard.locale', []) |
739 | 739 | "extension-id": "Extension id", |
740 | 740 | "extension-type": "Extension type", |
741 | 741 | "transformer-json": "JSON *", |
742 | - "id-required": "Extension id is required.", | |
743 | 742 | "unique-id-required": "Current extension id already exists.", |
744 | - "type-required": "Extension type is required.", | |
745 | - "required-type": "Type is required.", | |
746 | - "required-key": "Key is required.", | |
747 | - "required-value": "Value is required.", | |
748 | 743 | "delete": "Delete extension", |
749 | 744 | "add": "Add extension", |
750 | 745 | "edit": "Edit extension", |
... | ... | @@ -754,18 +749,13 @@ export default angular.module('thingsboard.locale', []) |
754 | 749 | "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.", |
755 | 750 | "converters": "Converters", |
756 | 751 | "converter-id": "Converter id", |
757 | - "converter-id-required": "Converter id is required.", | |
758 | 752 | "configuration": "Configuration", |
759 | 753 | "converter-configurations": "Converter configurations", |
760 | 754 | "token": "Security token", |
761 | 755 | "add-converter": "Add converter", |
762 | - "add-converter-prompt": "Please add converter", | |
763 | 756 | "add-config": "Add converter configuration", |
764 | - "add-config-prompt": "Please add converter configuration", | |
765 | 757 | "device-name-expression": "Device name expression", |
766 | - "device-name-expression-required": "Device name expression is required.", | |
767 | 758 | "device-type-expression": "Device type expression", |
768 | - "device-type-expression-required": "Device type expression is required.", | |
769 | 759 | "custom": "Custom", |
770 | 760 | "to-double": "To Double", |
771 | 761 | "transformer": "Transformer", |
... | ... | @@ -773,25 +763,20 @@ export default angular.module('thingsboard.locale', []) |
773 | 763 | "json-parse": "Unable to parse transformer json.", |
774 | 764 | "attributes": "Attributes", |
775 | 765 | "add-attribute": "Add attribute", |
766 | + "add-map": "Add mapping element", | |
776 | 767 | "timeseries": "Timeseries", |
777 | 768 | "add-timeseries": "Add timeseries", |
778 | - | |
769 | + "field-required": "Field is required", | |
779 | 770 | "brokers": "Brokers", |
780 | 771 | "add-broker": "Add broker", |
781 | - "add-broker-prompt": "Please add broker", | |
782 | 772 | "host": "Host", |
783 | - "host-required": "Host is required.", | |
784 | 773 | "port": "Port", |
785 | - "port-required": "Port is required.", | |
786 | 774 | "port-range": "Port should be in a range from 1 to 65535.", |
787 | 775 | "ssl": "Ssl", |
788 | 776 | "credentials": "Credentials", |
789 | 777 | "username": "Username", |
790 | - "username-required": "Username is required.", | |
791 | 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 | 780 | "anonymous": "Anonymous", |
796 | 781 | "basic": "Basic", |
797 | 782 | "pem": "PEM", |
... | ... | @@ -801,28 +786,59 @@ export default angular.module('thingsboard.locale', []) |
801 | 786 | "no-file": "No file selected.", |
802 | 787 | "drop-file": "Drop a file or click to select a file to upload.", |
803 | 788 | "mapping": "Mapping", |
804 | - "add-map": "Add map", | |
805 | 789 | "topic-filter": "Topic filter", |
806 | - "topic-filter-required": "Topic filter is required.", | |
807 | 790 | "converter-type": "Converter type", |
808 | - "converter-type-required": "Converter type is required.", | |
809 | 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 | 804 | "topic": "Topic", |
821 | - "timeout": "Timeout", | |
805 | + "timeout": "Timeout in milliseconds", | |
822 | 806 | "converter-json-required": "Converter json is required.", |
823 | 807 | "converter-json-parse": "Unable to parse converter json.", |
824 | 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 | 843 | "fullscreen": { |
828 | 844 | "expand": "Expand to fullscreen", | ... | ... |