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