Commit e3b39bedf691ef5b1e057c29d2c1fa72d185fa60
Committed by
GitHub
1 parent
fbed5655
WIP_Gate way form (#2370)
* gateWayForm: start branch * gateWayForm: start branch2 * gateWayForm: start add new form to gateway_widgets.json * gateWayForm: start add new logs.conf * Fix html and clear js * gateWayForm: start add new222 * improvement gateway config form (change html) * gateWayForm: new vadim verstka * GatewayForm: add valid config * GatewayForm: add valid config compile and add form to widgets library * GatewayForm: bug err yml Co-authored-by: Vladyslav <vprykhodko@thingsboard.io>
Showing
16 changed files
with
1712 additions
and
3 deletions
@@ -20,6 +20,26 @@ | @@ -20,6 +20,26 @@ | ||
20 | "dataKeySettingsSchema": "{}\n", | 20 | "dataKeySettingsSchema": "{}\n", |
21 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{},\"title\":\"Extensions table\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"18px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" | 21 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{},\"title\":\"Extensions table\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"18px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" |
22 | } | 22 | } |
23 | + }, | ||
24 | + { | ||
25 | + "alias": "new_config_form", | ||
26 | + "name": "Config form", | ||
27 | + "descriptor": { | ||
28 | + "type": "static", | ||
29 | + "sizeX": 7.5, | ||
30 | + "sizeY": 10.5, | ||
31 | + "resources": [ | ||
32 | + { | ||
33 | + "url": "" | ||
34 | + } | ||
35 | + ], | ||
36 | + "templateHtml": "<tb-gateway-form\n form-id=\"formId\"\n ctx=\"ctx\">\n</tb-gateway-form>\n", | ||
37 | + "templateCss": "#container {\n overflow: auto;\n height: 100%;\n margin: auto;\n}\n\n\n\n/*#configurations {*/\n/* display: flex;*/\n/* flex-direction: column;*/\n/* height: 100%;*/\n/* margin: 0px;*/\n/* padding: 0;*/\n/*}*/\n\n/*.configurationPointParent {*/\n/* display: flex;*/\n/* flex-direction: column;*/\n \n/*}*/\n\n/*.configurationPoint {*/\n/* display: flex;*/\n/* flex-direction: row;*/\n/* justify-content: space-between;*/\n/* margin: 5px;*/\n/*}*/\n\n/*.configurationPoint.select {*/\n/* margin: 0px;*/\n/* padding: 0;*/\n/* border: 0;*/\n/* height: 40px;*/\n\n/*}*/\n\n/*.configurationPoint.select.inputRow {*/\n/* margin: 0px;*/\n/* width: 100%;*/\n/* padding: 0;*/\n/* border: 0;*/\n/* height: 40px;*/\n/*}*/\n\n\n/*.error {*/\n/*color: red;*/\n/*}*/", | ||
38 | + "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.formId = \"form-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n self.ctx.$scope.$broadcast('gateway-form-resize', self.ctx.$scope.formId);\n}\n", | ||
39 | + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"GatewayConfigForm\",\n \"properties\": {\n \"gatewayTitle\": {\n \"title\": \"Gateway form title\",\n \"type\": \"string\",\n \"default\": \"Gateway Config Form\"\n }\n }\n },\n \"form\": [\n \"gatewayTitle\"\n ]\n}\n", | ||
40 | + "dataKeySettingsSchema": "{}\n", | ||
41 | + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"gatewayTitle\":\"Gateway Config Form\"},\"title\":\"Config form\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" | ||
42 | + } | ||
23 | } | 43 | } |
24 | ] | 44 | ] |
25 | -} | ||
45 | +} |
@@ -61,6 +61,7 @@ | @@ -61,6 +61,7 @@ | ||
61 | "js-beautify": "^1.10.0", | 61 | "js-beautify": "^1.10.0", |
62 | "json-schema-defaults": "^0.2.0", | 62 | "json-schema-defaults": "^0.2.0", |
63 | "jstree": "^3.3.8", | 63 | "jstree": "^3.3.8", |
64 | + "jszip": "^3.2.2", | ||
64 | "jstree-bootstrap-theme": "^1.0.1", | 65 | "jstree-bootstrap-theme": "^1.0.1", |
65 | "leaflet": "^1.5.1", | 66 | "leaflet": "^1.5.1", |
66 | "leaflet-polylinedecorator": "^1.6.0", | 67 | "leaflet-polylinedecorator": "^1.6.0", |
@@ -584,6 +584,32 @@ export default angular.module('thingsboard.types', []) | @@ -584,6 +584,32 @@ export default angular.module('thingsboard.types', []) | ||
584 | opc: "OPC UA", | 584 | opc: "OPC UA", |
585 | modbus: "MODBUS" | 585 | modbus: "MODBUS" |
586 | }, | 586 | }, |
587 | + gatewayConfigType: { | ||
588 | + mqtt: { | ||
589 | + value: "mqtt", | ||
590 | + name: "MQTT" | ||
591 | + }, | ||
592 | + modbus: { | ||
593 | + value: "modbus", | ||
594 | + name: "Modbus" | ||
595 | + }, | ||
596 | + opc_ua: { | ||
597 | + value: "opcua", | ||
598 | + name: "OPC-UA" | ||
599 | + }, | ||
600 | + ble: { | ||
601 | + value: "ble", | ||
602 | + name: "BLE" | ||
603 | + } | ||
604 | + }, | ||
605 | + gatewayLogLevel: { | ||
606 | + none: "NONE", | ||
607 | + critical: "CRITICAL", | ||
608 | + error: "ERROR", | ||
609 | + warning: "WARNING", | ||
610 | + info: "INFO", | ||
611 | + debug: "DEBUG" | ||
612 | + }, | ||
587 | extensionValueType: { | 613 | extensionValueType: { |
588 | string: 'value.string', | 614 | string: 'value.string', |
589 | long: 'value.long', | 615 | long: 'value.long', |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<md-dialog class="gateway-config-dialog"> | ||
19 | + <form name="theForm" ng-submit="vm.save()"> | ||
20 | + <md-toolbar> | ||
21 | + <div class="md-toolbar-tools"> | ||
22 | + <h2 translate translate-values='{ typeName: "{{vm.typeName | translate}}" }'> | ||
23 | + gateway.title-connectors-json | ||
24 | + </h2> | ||
25 | + <span flex></span> | ||
26 | + <md-button class="md-icon-button" | ||
27 | + ng-click="vm.cancel()" | ||
28 | + aria-label="{{'action.close'|translate}}"> | ||
29 | + <ng-md-icon icon="close" aria-label="Close"></ng-md-icon> | ||
30 | + </md-button> | ||
31 | + </div> | ||
32 | + </md-toolbar> | ||
33 | + <md-dialog-content> | ||
34 | + <div class="md-dialog-content"> | ||
35 | + <div tb-expand-fullscreen fullscreen-zindex="100" expand-button-id="expand-button" layout="column"> | ||
36 | + <div layout="row" layout-align="start center" class="tb-json-toolbar"> | ||
37 | + <label class="tb-title no-padding" translate>gateway.transformer-json-config</label> | ||
38 | + <span flex></span> | ||
39 | + <md-button ng-if="!readonly" | ||
40 | + aria-label="{{'gateway.tidy'|translate}}" | ||
41 | + class="tidy" | ||
42 | + ng-click="vm.beautifyJson()"> | ||
43 | + {{'gateway.tidy'|translate}} | ||
44 | + <md-tooltip md-direction="top">{{'gateway.tidy-tip' | translate }}</md-tooltip> | ||
45 | + </md-button> | ||
46 | + <md-button id="expand-button" aria-label="Fullscreen" | ||
47 | + class="md-icon-button tb-md-32 tb-fullscreen-button-style"></md-button> | ||
48 | + </div> | ||
49 | + <div layout="row" class="tb-json-panel" flex> | ||
50 | + <div flex class="tb-json-input" | ||
51 | + ui-ace="vm.configAreaOptions" | ||
52 | + ng-model="vm.config" | ||
53 | + name="config" | ||
54 | + ng-change='vm.validateConfig(vm.config, "config")' | ||
55 | + required> | ||
56 | + </div> | ||
57 | + </div> | ||
58 | + <div class="tb-error-messages" layout="column" flex ng-messages="theForm.config.$error" role="alert"> | ||
59 | + <div ng-message="required" flex class="tb-error-message" translate>gateway.json-required</div> | ||
60 | + <div ng-message="vm.config" class="tb-error-message" translate>gateway.json-parse</div> | ||
61 | + </div> | ||
62 | + </div> | ||
63 | + </div> | ||
64 | + </md-dialog-content> | ||
65 | + <md-dialog-actions layout="row" layout-align="end center" class="action-buttons"> | ||
66 | + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" | ||
67 | + class="md-raised md-primary"> | ||
68 | + {{'action.save'|translate}} | ||
69 | + </md-button> | ||
70 | + <md-button id="cancel-btn" ng-disabled="$root.loading" ng-click="vm.cancel()"> | ||
71 | + {{'action.cancel'|translate }} | ||
72 | + </md-button> | ||
73 | + </md-dialog-actions> | ||
74 | + </form> | ||
75 | +</md-dialog> |
1 | +/* | ||
2 | + * Copyright © 2016-2020 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 | +import './gateway-config-select.scss'; | ||
17 | + | ||
18 | +/* eslint-disable import/no-unresolved, import/default */ | ||
19 | + | ||
20 | +import gatewayAliasSelectTemplate from './gateway-config-select.tpl.html'; | ||
21 | + | ||
22 | +/* eslint-enable import/no-unresolved, import/default */ | ||
23 | + | ||
24 | + | ||
25 | +/* eslint-disable angular/angularelement */ | ||
26 | + | ||
27 | +export default angular.module('thingsboard.directives.gatewayConfigSelect', []) | ||
28 | + .directive('tbGatewayConfigSelect', GatewayConfigSelect) | ||
29 | + .name; | ||
30 | + | ||
31 | +/*@ngInject*/ | ||
32 | +function GatewayConfigSelect($compile, $templateCache, $mdConstant, $translate, $mdDialog) { | ||
33 | + | ||
34 | + var linker = function (scope, element, attrs, ngModelCtrl) { | ||
35 | + var template = $templateCache.get(gatewayAliasSelectTemplate); | ||
36 | + element.html(template); | ||
37 | + | ||
38 | + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; | ||
39 | + | ||
40 | + scope.ngModelCtrl = ngModelCtrl; | ||
41 | + scope.singleSelect = null; | ||
42 | + | ||
43 | + scope.updateValidity = function () { | ||
44 | + var value = ngModelCtrl.$viewValue; | ||
45 | + var valid = angular.isDefined(value) && value != null || !scope.tbRequired; | ||
46 | + ngModelCtrl.$setValidity('singleSelect', valid); | ||
47 | + }; | ||
48 | + | ||
49 | + scope.$watch('singleSelect', function () { | ||
50 | + scope.updateView(); | ||
51 | + }); | ||
52 | + | ||
53 | + scope.gatewayNameSearch = function (gatewaySearchText) { | ||
54 | + return gatewaySearchText ? scope.gatewayList.filter( | ||
55 | + scope.createFilterForGatewayName(gatewaySearchText)) : scope.gatewayList; | ||
56 | + }; | ||
57 | + | ||
58 | + scope.createFilterForGatewayName = function (query) { | ||
59 | + var lowercaseQuery = query.toLowerCase(); | ||
60 | + return function filterFn(device) { | ||
61 | + return (device.toLowerCase().indexOf(lowercaseQuery) === 0); | ||
62 | + }; | ||
63 | + }; | ||
64 | + | ||
65 | + scope.updateView = function () { | ||
66 | + ngModelCtrl.$setViewValue(scope.singleSelect); | ||
67 | + scope.updateValidity(); | ||
68 | + let deviceObj = {"name": scope.singleSelect, "type": "Gateway", "additionalInfo": { | ||
69 | + "gateway": true | ||
70 | + }}; | ||
71 | + scope.getAccessToken(deviceObj); | ||
72 | + }; | ||
73 | + | ||
74 | + ngModelCtrl.$render = function () { | ||
75 | + if (ngModelCtrl.$viewValue) { | ||
76 | + scope.singleSelect = ngModelCtrl.$viewValue; | ||
77 | + } | ||
78 | + }; | ||
79 | + | ||
80 | + scope.textIsEmpty = function (str) { | ||
81 | + return (!str || 0 === str.length); | ||
82 | + }; | ||
83 | + | ||
84 | + scope.gatewayNameEnter = function ($event) { | ||
85 | + if ($event.keyCode === $mdConstant.KEY_CODE.ENTER) { | ||
86 | + $event.preventDefault(); | ||
87 | + let indexRes = scope.gatewayList.findIndex((element) => element.key === scope.gatewaySearchText); | ||
88 | + if (indexRes === -1) { | ||
89 | + scope.createNewGatewayDialog($event, {name: scope.gatewaySearchText}); | ||
90 | + } | ||
91 | + } | ||
92 | + }; | ||
93 | + | ||
94 | + scope.createNewGatewayDialog = function ($event, deviceName) { | ||
95 | + if ($event) { | ||
96 | + $event.stopPropagation(); | ||
97 | + } | ||
98 | + var title = $translate.instant('gateway.create-new-gateway'); | ||
99 | + var content = $translate.instant('gateway.create-new-gateway-text', {gatewayName: deviceName.name}); | ||
100 | + var confirm = $mdDialog.confirm() | ||
101 | + .targetEvent($event) | ||
102 | + .title(title) | ||
103 | + .htmlContent(content) | ||
104 | + .ariaLabel(title) | ||
105 | + .cancel($translate.instant('action.no')) | ||
106 | + .ok($translate.instant('action.yes')); | ||
107 | + $mdDialog.show(confirm).then( | ||
108 | + () => { | ||
109 | + let deviceObj = {"name": deviceName.name, "type": "Gateway", "additionalInfo": { | ||
110 | + "gateway": true | ||
111 | + }}; | ||
112 | + scope.createDevice(deviceObj); | ||
113 | + }, | ||
114 | + () => { | ||
115 | + scope.gatewaySearchText = ""; | ||
116 | + } | ||
117 | + ); | ||
118 | + }; | ||
119 | + $compile(element.contents())(scope); | ||
120 | + }; | ||
121 | + | ||
122 | + return { | ||
123 | + restrict: "E", | ||
124 | + require: "^ngModel", | ||
125 | + link: linker, | ||
126 | + scope: { | ||
127 | + tbRequired: '=?', | ||
128 | + allowedEntityTypes: '=?', | ||
129 | + gatewayList: '=?', | ||
130 | + getAccessToken: '=', | ||
131 | + createDevice: '=', | ||
132 | + theForm: '=' | ||
133 | + } | ||
134 | + }; | ||
135 | +} | ||
136 | + | ||
137 | +/* eslint-enable angular/angularelement */ |
1 | +/** | ||
2 | + * Copyright © 2016-2020 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 | +.tb-gateway-autocomplete { | ||
17 | + .tb-not-found { | ||
18 | + line-height: 1.5; | ||
19 | + white-space: normal; | ||
20 | + | ||
21 | + .tb-no-gateway { | ||
22 | + line-height: 48px; | ||
23 | + } | ||
24 | + } | ||
25 | +} | ||
26 | + | ||
27 | +.tb-gateway-autocomplete-container.md-virtual-repeat-container.md-autocomplete-suggestions-container{ | ||
28 | + z-index: 70; | ||
29 | +} | ||
30 | + | ||
31 | +md-autocomplete{ | ||
32 | + md-input-container{ | ||
33 | + margin-bottom: 0; | ||
34 | + } | ||
35 | +} |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<section layout='column'> | ||
19 | + <md-autocomplete md-input-name="singleSelect" flex | ||
20 | + ng-required="tbRequired" | ||
21 | + md-no-cache="true" | ||
22 | + ng-model="singleSelect" | ||
23 | + md-selected-item="singleSelect" | ||
24 | + md-search-text="gatewaySearchText" | ||
25 | + md-items="item in gatewayNameSearch(gatewaySearchText)" | ||
26 | + md-item-text="item" | ||
27 | + tb-keydown="gatewayNameEnter($event)" | ||
28 | + tb-keypress="gatewayNameEnter($event)" | ||
29 | + md-min-length="0" | ||
30 | + md-clear-button="true" | ||
31 | + md-floating-label="{{ 'gateway.gateway-name' | translate }}" | ||
32 | + md-menu-class="tb-gateway-autocomplete" | ||
33 | + md-input-class="tb-test" | ||
34 | + md-menu-container-class="tb-gateway-autocomplete-container"> | ||
35 | + <md-item-template> | ||
36 | + <span md-highlight-text="gatewaySearchText" md-highlight-flags="^i">{{item}}</span> | ||
37 | + </md-item-template> | ||
38 | + <md-not-found> | ||
39 | + <div class="tb-not-found"> | ||
40 | + <div class="tb-no-gateway" ng-if="textIsEmpty(gatewaySearchText)"> | ||
41 | + <span translate>gateway.no-gateway-found</span> | ||
42 | + </div> | ||
43 | + <div ng-if="!textIsEmpty(gatewaySearchText)"> | ||
44 | + <span translate | ||
45 | + translate-values='{ item: "{{gatewaySearchText | truncate:true:6:'...'}}" }'>gateway.no-gateway-matching</span> | ||
46 | + <a translate ng-click="createNewGatewayDialog($event, {name: gatewaySearchText})">gateway.create-new-gateway</a> | ||
47 | + </div> | ||
48 | + </div> | ||
49 | + </md-not-found> | ||
50 | + <div ng-messages="theForm.singleSelect.$error"> | ||
51 | + <div ng-message="required" translate>Test</div> | ||
52 | + </div> | ||
53 | + </md-autocomplete> | ||
54 | +</section> |
1 | +/* | ||
2 | + * Copyright © 2016-2020 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 | +import './gateway-config.scss'; | ||
17 | + | ||
18 | +/* eslint-disable import/no-unresolved, import/default */ | ||
19 | + | ||
20 | +import gatewayTemplate from './gateway-config.tpl.html'; | ||
21 | +import gatewayDialogTemplate from './gateway-config-dialog.tpl.html'; | ||
22 | +import beautify from "js-beautify"; | ||
23 | + | ||
24 | +/* eslint-enable import/no-unresolved, import/default */ | ||
25 | +const js_beautify = beautify.js; | ||
26 | + | ||
27 | +export default angular.module('thingsboard.directives.gatewayConfig', []) | ||
28 | + .directive('tbGatewayConfig', GatewayConfig) | ||
29 | + .name; | ||
30 | + | ||
31 | +/*@ngInject*/ | ||
32 | +function GatewayConfig() { | ||
33 | + return { | ||
34 | + restrict: "E", | ||
35 | + scope: true, | ||
36 | + bindToController: { | ||
37 | + disabled: '=ngDisabled', | ||
38 | + titleText: '@?', | ||
39 | + keyPlaceholderText: '@?', | ||
40 | + valuePlaceholderText: '@?', | ||
41 | + noDataText: '@?', | ||
42 | + gatewayConfig: '=', | ||
43 | + changeAlignment: '=' | ||
44 | + }, | ||
45 | + controller: GatewayConfigController, | ||
46 | + controllerAs: 'vm', | ||
47 | + templateUrl: gatewayTemplate | ||
48 | + }; | ||
49 | +} | ||
50 | + | ||
51 | +/*@ngInject*/ | ||
52 | +function GatewayConfigController($scope, $document, $mdDialog, $mdUtil, $window, types, toast, $timeout, $compile, $translate) { //eslint-disable-line | ||
53 | + | ||
54 | + let vm = this; | ||
55 | + | ||
56 | + vm.kvList = []; | ||
57 | + vm.types = types; | ||
58 | + $scope.$watch('vm.gatewayConfig', () => { | ||
59 | + vm.stopWatchKvList(); | ||
60 | + vm.kvList.length = 0; | ||
61 | + if (vm.gatewayConfig) { | ||
62 | + for (var property in vm.gatewayConfig) { | ||
63 | + if (Object.prototype.hasOwnProperty.call(vm.gatewayConfig, property)) { | ||
64 | + vm.kvList.push( | ||
65 | + { | ||
66 | + enabled: vm.gatewayConfig[property].enabled, | ||
67 | + key: property + '', | ||
68 | + value: vm.gatewayConfig[property].connector + '', | ||
69 | + config: js_beautify(vm.gatewayConfig[property].config + '', {indent_size: 4}) | ||
70 | + } | ||
71 | + ); | ||
72 | + } | ||
73 | + } | ||
74 | + } | ||
75 | + $mdUtil.nextTick(() => { | ||
76 | + vm.watchKvList(); | ||
77 | + }); | ||
78 | + }); | ||
79 | + | ||
80 | + vm.watchKvList = () => { | ||
81 | + $scope.kvListWatcher = $scope.$watch('vm.kvList', () => { | ||
82 | + if (!vm.gatewayConfig) { | ||
83 | + return; | ||
84 | + } | ||
85 | + for (let property in vm.gatewayConfig) { | ||
86 | + if (Object.prototype.hasOwnProperty.call(vm.gatewayConfig, property)) { | ||
87 | + delete vm.gatewayConfig[property]; | ||
88 | + } | ||
89 | + } | ||
90 | + for (let i = 0; i < vm.kvList.length; i++) { | ||
91 | + let entry = vm.kvList[i]; | ||
92 | + if (entry.key && entry.value) { | ||
93 | + let connectorJSON = angular.toJson({ | ||
94 | + enabled: entry.enabled, | ||
95 | + connector: entry.value, | ||
96 | + config: angular.fromJson(entry.config) | ||
97 | + }); | ||
98 | + vm.gatewayConfig [entry.key] = angular.fromJson(connectorJSON); | ||
99 | + } | ||
100 | + } | ||
101 | + }, true); | ||
102 | + }; | ||
103 | + | ||
104 | + vm.stopWatchKvList = () => { | ||
105 | + if ($scope.kvListWatcher) { | ||
106 | + $scope.kvListWatcher(); | ||
107 | + $scope.kvListWatcher = null; | ||
108 | + } | ||
109 | + }; | ||
110 | + | ||
111 | + vm.removeKeyVal = (index) => { | ||
112 | + if (index > -1) { | ||
113 | + vm.kvList.splice(index, 1); | ||
114 | + } | ||
115 | + }; | ||
116 | + | ||
117 | + vm.addKeyVal = () => { | ||
118 | + if (!vm.kvList) { | ||
119 | + vm.kvList = []; | ||
120 | + } | ||
121 | + vm.kvList.push( | ||
122 | + { | ||
123 | + enabled: false, | ||
124 | + key: '', | ||
125 | + value: '', | ||
126 | + config: '{}' | ||
127 | + } | ||
128 | + ); | ||
129 | + } | ||
130 | + | ||
131 | + vm.openConfigDialog = ($event, index, config, typeName) => { | ||
132 | + if ($event) { | ||
133 | + $event.stopPropagation(); | ||
134 | + } | ||
135 | + $mdDialog.show({ | ||
136 | + controller: GatewayDialogController, | ||
137 | + controllerAs: 'vm', | ||
138 | + templateUrl: gatewayDialogTemplate, | ||
139 | + parent: angular.element($document[0].body), | ||
140 | + locals: { | ||
141 | + config: config, | ||
142 | + typeName: typeName | ||
143 | + }, | ||
144 | + targetEvent: $event, | ||
145 | + fullscreen: true, | ||
146 | + multiple: true, | ||
147 | + }).then(function (config) { | ||
148 | + if (config) { | ||
149 | + if (index > -1) { | ||
150 | + vm.kvList[index].config = config; | ||
151 | + } | ||
152 | + } | ||
153 | + }, function () { | ||
154 | + }); | ||
155 | + | ||
156 | + }; | ||
157 | + | ||
158 | + vm.configTypeChange = (keyVal) => { | ||
159 | + for (let prop in types.gatewayConfigType) { | ||
160 | + if (types.gatewayConfigType[prop].value === keyVal.value) { | ||
161 | + if (!keyVal.key) { | ||
162 | + keyVal.key = vm.configTypeChangeValid(types.gatewayConfigType[prop].name, 0); | ||
163 | + } | ||
164 | + } | ||
165 | + } | ||
166 | + vm.checkboxValid(keyVal); | ||
167 | + }; | ||
168 | + | ||
169 | + vm.keyValChange = (keyVal, indexKey) => { | ||
170 | + keyVal.key = vm.keyValChangeValid(keyVal.key, 0, indexKey); | ||
171 | + vm.checkboxValid(keyVal); | ||
172 | + }; | ||
173 | + | ||
174 | + vm.configTypeChangeValid = (name, index) => { | ||
175 | + let newKeyName = index ? name + index : name; | ||
176 | + let indexRes = vm.kvList.findIndex((element) => element.key === newKeyName); | ||
177 | + return indexRes === -1 ? newKeyName : vm.configTypeChangeValid(name, ++index); | ||
178 | + }; | ||
179 | + | ||
180 | + vm.keyValChangeValid = (name, index, indexKey) => { | ||
181 | + angular.forEach(vm.kvList, function (value, key) { | ||
182 | + let nameEq = (index === 0) ? name : name + index; | ||
183 | + if (key !== indexKey && value.key && value.key === nameEq) { | ||
184 | + index++; | ||
185 | + vm.keyValChangeValid(name, index, indexKey); | ||
186 | + } | ||
187 | + | ||
188 | + }); | ||
189 | + return (index === 0) ? name : name + index; | ||
190 | + }; | ||
191 | + | ||
192 | + vm.buttonValid = (config) => { | ||
193 | + return (angular.equals("{}", config)) ? "md-warn" : "md-primary"; | ||
194 | + }; | ||
195 | + | ||
196 | + vm.checkboxValid = (keyVal) => { | ||
197 | + if (!keyVal.key || angular.equals("", keyVal.key) | ||
198 | + || !keyVal.value || angular.equals("", keyVal.value) | ||
199 | + || angular.equals("{}", keyVal.config)) { | ||
200 | + return keyVal.enabled = false; | ||
201 | + } | ||
202 | + return true; | ||
203 | + }; | ||
204 | + vm.checkboxValidMouseover = ($event, keyVal) => { | ||
205 | + console.log($event, keyVal); //eslint-disable-line | ||
206 | + vm.checkboxValidClick ($event, keyVal); | ||
207 | + }; | ||
208 | + | ||
209 | + vm.checkboxValidClick = ($event, keyVal) => { | ||
210 | + if (!vm.checkboxValid(keyVal)) { | ||
211 | + let errTxt = ""; | ||
212 | + if (!keyVal.key || angular.equals("", keyVal.key)) { | ||
213 | + errTxt = $translate.instant('gateway.keyval-name-err'); | ||
214 | + } | ||
215 | + | ||
216 | + if (!keyVal.value || angular.equals("", keyVal.value)) { | ||
217 | + errTxt += '<div>' + $translate.instant('gateway.keyval-type-err') + '</div>'; | ||
218 | + } | ||
219 | + | ||
220 | + if (angular.equals("{}", keyVal.config)) { | ||
221 | + errTxt += '<div>' + $translate.instant('gateway.keyval-config-err') + '</div>'; | ||
222 | + } | ||
223 | + if (!angular.equals("", errTxt)) { | ||
224 | + displayTooltip($event, '<div class="tb-rule-node-tooltip tb-lib-tooltip">' + | ||
225 | + '<div id="tb-node-content" layout="column">' + | ||
226 | + '<div class="tb-node-title">' + $translate.instant('gateway.keyval-save-err') + '</div>' + | ||
227 | + '<div class="tb-node-details">' + errTxt + '</div>' + | ||
228 | + '</div>' + | ||
229 | + '</div>'); | ||
230 | + } | ||
231 | + } | ||
232 | + else { | ||
233 | + destroyTooltips(); | ||
234 | + } | ||
235 | + }; | ||
236 | + | ||
237 | + | ||
238 | + function displayTooltip(event, content) { | ||
239 | + destroyTooltips(); | ||
240 | + vm.tooltipTimeout = $timeout(() => { | ||
241 | + var element = angular.element(event.target); | ||
242 | + element.tooltipster( | ||
243 | + { | ||
244 | + theme: 'tooltipster-shadow', | ||
245 | + delay: 10, | ||
246 | + animation: 'grow', | ||
247 | + side: 'right' | ||
248 | + } | ||
249 | + ); | ||
250 | + var contentElement = angular.element(content); | ||
251 | + $compile(contentElement)($scope); | ||
252 | + var tooltip = element.tooltipster('instance'); | ||
253 | + tooltip.content(contentElement); | ||
254 | + tooltip.open(); | ||
255 | + }, 500); | ||
256 | + } | ||
257 | + | ||
258 | + function destroyTooltips() { | ||
259 | + if (vm.tooltipTimeout) { | ||
260 | + $timeout.cancel(vm.tooltipTimeout); | ||
261 | + vm.tooltipTimeout = null; | ||
262 | + } | ||
263 | + var instances = angular.element.tooltipster.instances(); | ||
264 | + instances.forEach((instance) => { | ||
265 | + if (!instance.isErrorTooltip) { | ||
266 | + instance.destroy(); | ||
267 | + } | ||
268 | + }); | ||
269 | + } | ||
270 | +} | ||
271 | + | ||
272 | +/*@ngInject*/ | ||
273 | +function GatewayDialogController($scope, $mdDialog, $document, $window, config, typeName) { | ||
274 | + let vm = this; | ||
275 | + vm.doc = $document[0]; | ||
276 | + vm.config = angular.copy(config); | ||
277 | + vm.typeName = "" + typeName; | ||
278 | + vm.configAreaOptions = { | ||
279 | + useWrapMode: false, | ||
280 | + mode: 'json', | ||
281 | + showGutter: true, | ||
282 | + showPrintMargin: true, | ||
283 | + theme: 'github', | ||
284 | + advanced: { | ||
285 | + enableSnippets: true, | ||
286 | + enableBasicAutocompletion: true, | ||
287 | + enableLiveAutocompletion: true | ||
288 | + }, | ||
289 | + onLoad: function (_ace) { | ||
290 | + _ace.$blockScrolling = 1; | ||
291 | + } | ||
292 | + }; | ||
293 | + | ||
294 | + vm.validateConfig = (model, editorName) => { | ||
295 | + if (model && model.length) { | ||
296 | + try { | ||
297 | + angular.fromJson(model); | ||
298 | + $scope.theForm[editorName].$setValidity('configJSON', true); | ||
299 | + } catch (e) { | ||
300 | + $scope.theForm[editorName].$setValidity('configJSON', false); | ||
301 | + } | ||
302 | + } | ||
303 | + }; | ||
304 | + | ||
305 | + vm.save = () => { | ||
306 | + $mdDialog.hide(vm.config); | ||
307 | + }; | ||
308 | + | ||
309 | + vm.cancel = () => { | ||
310 | + $mdDialog.hide(); | ||
311 | + }; | ||
312 | + | ||
313 | + vm.beautifyJson = () => { | ||
314 | + vm.config = js_beautify(vm.config, {indent_size: 4}); | ||
315 | + }; | ||
316 | +} | ||
317 | + |
1 | +/** | ||
2 | + * Copyright © 2016-2020 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 | +.gateway-config { | ||
17 | + span.no-data-found { | ||
18 | + position: relative; | ||
19 | + display: flex; | ||
20 | + height: 40px; | ||
21 | + text-transform: uppercase; | ||
22 | + | ||
23 | + &.disabled { | ||
24 | + color: rgba(0, 0, 0, .38); | ||
25 | + } | ||
26 | + } | ||
27 | + | ||
28 | + .gateway-config-row{ | ||
29 | + md-input-container{ | ||
30 | + margin-bottom: 0; | ||
31 | + } | ||
32 | + | ||
33 | + &.gateway-config-row-vertical { | ||
34 | + flex-direction: column; | ||
35 | + } | ||
36 | + } | ||
37 | + | ||
38 | + .action-buttons.gateway-config-row-vertical { | ||
39 | + flex-direction: column; | ||
40 | + justify-content: space-evenly; | ||
41 | + } | ||
42 | +} | ||
43 | + | ||
44 | +.gateway-config-dialog{ | ||
45 | + .md-button.tidy{ | ||
46 | + min-width: 32px; | ||
47 | + min-height: 15px; | ||
48 | + padding: 4px; | ||
49 | + margin: 0 5px 0 0; | ||
50 | + font-size: .8rem; | ||
51 | + line-height: 15px; | ||
52 | + color: #7b7b7b; | ||
53 | + background: rgba(220, 220, 220, .35); | ||
54 | + } | ||
55 | + | ||
56 | + .tb-json-toolbar{ | ||
57 | + height: 40px; | ||
58 | + } | ||
59 | + | ||
60 | + .tb-json-panel { | ||
61 | + height: calc(100% - 80px); | ||
62 | + margin-left: 15px; | ||
63 | + border: 1px solid #c0c0c0; | ||
64 | + | ||
65 | + .tb-json-input { | ||
66 | + width: 100%; | ||
67 | + min-width: 400px; | ||
68 | + height: 100%; | ||
69 | + | ||
70 | + &:not(.fill-height) { | ||
71 | + min-height: 200px; | ||
72 | + } | ||
73 | + } | ||
74 | + } | ||
75 | +} | ||
76 | + | ||
77 | +@media (max-width: 425px){ | ||
78 | + .gateway-config-dialog{ | ||
79 | + .tb-json-panel { | ||
80 | + .tb-json-input { | ||
81 | + min-width: 200px; | ||
82 | + } | ||
83 | + } | ||
84 | + } | ||
85 | +} |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<form name="gatewayConfig" flex layout="column" class="gateway-config"> | ||
19 | + <div layout="row" id="section-row" ng-repeat="keyVal in vm.kvList track by $index"> | ||
20 | + <div layout="column" layout-align="center center" class="gateway-config-row"> | ||
21 | + <md-input-container class="md-block"> | ||
22 | + <md-checkbox ng-model="keyVal.enabled" | ||
23 | + aria-label="{{ 'gateway.enabled' | translate }}" | ||
24 | + ng-change="vm.checkboxValid(keyVal)" | ||
25 | + ng-click="vm.checkboxValidClick($event, keyVal)" | ||
26 | + ng-mouseover="vm.checkboxValidMouseover($event, keyVal)"> | ||
27 | + </md-checkbox> | ||
28 | + <md-tooltip md-direction="top"> | ||
29 | + {{ 'gateway.enabled' | translate }} | ||
30 | + </md-tooltip> | ||
31 | + </md-input-container> | ||
32 | + </div> | ||
33 | + <div layout="row" flex class="gateway-config-row" | ||
34 | + ng-class="{'gateway-config-row-vertical': vm.changeAlignment}"> | ||
35 | + <md-input-container class="md-block" flex> | ||
36 | + <label>{{'gateway.connector-type' | translate }}</label> | ||
37 | + <md-select name="configType" ng-change="vm.configTypeChange(keyVal)" ng-model="keyVal.value" required> | ||
38 | + <md-option ng-repeat="configType in vm.types.gatewayConfigType" ng-value="configType.value"> | ||
39 | + {{configType.value}} | ||
40 | + </md-option> | ||
41 | + </md-select> | ||
42 | + <md-tooltip md-direction="top"> | ||
43 | + {{ 'gateway.connector-type' | translate }} | ||
44 | + </md-tooltip> | ||
45 | + </md-input-container> | ||
46 | + <md-input-container class="md-block" flex> | ||
47 | + <input placeholder="{{ (vm.keyPlaceholderText ? vm.keyPlaceholderText : 'gateway.name') | translate }}" | ||
48 | + ng-model-options="{ updateOn: 'blur' }" | ||
49 | + ng-change="vm.keyValChange(keyVal, $index)" name="key" ng-model="keyVal.key" required/> | ||
50 | + <div ng-messages="gatewayConfig.key.$error"> | ||
51 | + <div ng-message="required" translate>extension.field-required</div> | ||
52 | + </div> | ||
53 | + <md-tooltip md-direction="top"> | ||
54 | + {{ 'gateway.name' | translate }} | ||
55 | + </md-tooltip> | ||
56 | + </md-input-container> | ||
57 | + </div> | ||
58 | + <div layout="row" layout-align="end center" class="action-buttons" | ||
59 | + ng-class="{'gateway-config-row-vertical': vm.changeAlignment}"> | ||
60 | + <md-button class="md-icon-button md-fab md-mini" | ||
61 | + name="updateconf" | ||
62 | + ng-click="vm.openConfigDialog($event, $index, keyVal.config, keyVal.key)" | ||
63 | + aria-label="{{ 'gateway.update-config' | translate }}" | ||
64 | + ng-class="vm.buttonValid(keyVal.config)" required> | ||
65 | + <md-icon class="material-icons">settings_ethernet</md-icon> | ||
66 | + <md-tooltip md-direction="top"> | ||
67 | + {{ 'gateway.update-config' | translate }} | ||
68 | + </md-tooltip> | ||
69 | + </md-button> | ||
70 | + <md-button ng-show="!vm.disabled" ng-disabled="$root.loading" | ||
71 | + class="md-icon-button md-fab md-mini md-primary" | ||
72 | + ng-click="vm.removeKeyVal($index)" | ||
73 | + aria-label="{{ 'gateway.delete' | translate }}"> | ||
74 | + <md-icon class="material-icons">close</md-icon> | ||
75 | + <md-tooltip md-direction="top"> | ||
76 | + {{ 'gateway.delete' | translate }} | ||
77 | + </md-tooltip> | ||
78 | + </md-button> | ||
79 | + </div> | ||
80 | + </div> | ||
81 | + <span ng-show="!vm.kvList.length" | ||
82 | + layout-align="center center" ng-class="{'disabled': vm.disabled}" | ||
83 | + class="no-data-found" translate>{{vm.noDataText ? vm.noDataText : 'gateway.no-connectors'}}</span> | ||
84 | + <div> | ||
85 | + <md-button ng-show="!vm.disabled" ng-disabled="$root.loading" class="md-raised" | ||
86 | + ng-click="vm.addKeyVal()" | ||
87 | + aria-label="{{ 'gateway.add-connectors' | translate }}"> | ||
88 | + <md-tooltip md-direction="top"> | ||
89 | + {{ 'gateway.add-connectors' | translate }} | ||
90 | + </md-tooltip> | ||
91 | + <span translate>action.add</span> | ||
92 | + </md-button> | ||
93 | + </div> | ||
94 | +</form > |
1 | +/* | ||
2 | + * Copyright © 2016-2020 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 | +import './gateway-form.scss'; | ||
17 | +/* eslint-disable import/no-unresolved, import/default */ | ||
18 | + | ||
19 | +import gatewayFormTemplate from './gateway-form.tpl.html'; | ||
20 | + | ||
21 | +/* eslint-enable import/no-unresolved, import/default */ | ||
22 | + | ||
23 | +export default angular.module('thingsboard.directives.gatewayForm', []) | ||
24 | + .directive('tbGatewayForm', GatewayForm) | ||
25 | + .name; | ||
26 | + | ||
27 | +/*@ngInject*/ | ||
28 | +function GatewayForm() { | ||
29 | + return { | ||
30 | + restrict: "E", | ||
31 | + scope: true, | ||
32 | + bindToController: { | ||
33 | + disabled: '=ngDisabled', | ||
34 | + keyPlaceholderText: '@?', | ||
35 | + valuePlaceholderText: '@?', | ||
36 | + noDataText: '@?', | ||
37 | + formId: '=', | ||
38 | + ctx: '=', | ||
39 | + gatewayFormConfig: '=', | ||
40 | + theForm: '=' | ||
41 | + }, | ||
42 | + controller: GatewayFormController, | ||
43 | + controllerAs: 'vm', | ||
44 | + templateUrl: gatewayFormTemplate | ||
45 | + }; | ||
46 | +} | ||
47 | + | ||
48 | +/*@ngInject*/ | ||
49 | +function GatewayFormController($scope, $injector, $document, $mdExpansionPanel, toast, importExport, attributeService, deviceService, userService, $mdDialog, $mdUtil, types, $window, $q) { | ||
50 | + $scope.$mdExpansionPanel = $mdExpansionPanel; | ||
51 | + let vm = this; | ||
52 | + const attributeNameClinet = "current_configuration"; | ||
53 | + const attributeNameServer = "configuration_drafts"; | ||
54 | + const attributeNameShared = "configuration"; | ||
55 | + const attributeNameLogShared = "RemoteLoggingLevel"; | ||
56 | + vm.remoteLoggingConfig = '[loggers]}}keys=root, service, connector, converter, tb_connection, storage, extension}}[handlers]}}keys=consoleHandler, serviceHandler, connectorHandler, converterHandler, tb_connectionHandler, storageHandler, extensionHandler}}[formatters]}}keys=LogFormatter}}[logger_root]}}level=ERROR}}handlers=consoleHandler}}[logger_connector]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=connector}}[logger_storage]}}level={ERROR}}}handlers=storageHandler}}formatter=LogFormatter}}qualname=storage}}[logger_tb_connection]}}level={ERROR}}}handlers=tb_connectionHandler}}formatter=LogFormatter}}qualname=tb_connection}}[logger_service]}}level={ERROR}}}handlers=serviceHandler}}formatter=LogFormatter}}qualname=service}}[logger_converter]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=converter}}[logger_extension]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=extension}}[handler_consoleHandler]}}class=StreamHandler}}level={ERROR}}}formatter=LogFormatter}}args=(sys.stdout,)}}[handler_connectorHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}connector.log", "d", 1, 7,)}}[handler_storageHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}storage.log", "d", 1, 7,)}}[handler_serviceHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}service.log", "d", 1, 7,)}}[handler_converterHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}converter.log", "d", 1, 3,)}}[handler_extensionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}extension.log", "d", 1, 3,)}}[handler_tb_connectionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}tb_connection.log", "d", 1, 3,)}}[formatter_LogFormatter]}}format="%(asctime)s - %(levelname)s - [%(filename)s] - %(module)s - %(lineno)d - %(message)s" }}datefmt="%Y-%m-%d %H:%M:%S"'; | ||
57 | + vm.types = types; | ||
58 | + | ||
59 | + vm.configurations = { | ||
60 | + singleSelect: '', | ||
61 | + host: $document[0].domain, | ||
62 | + port: 1883, | ||
63 | + remoteConfiguration: true, | ||
64 | + accessToken: '', | ||
65 | + entityType: '', | ||
66 | + entityId: '', | ||
67 | + storageType: "memoryStorage", // "memoryStorage"; fileStorage | ||
68 | + readRecordsCount: 100, | ||
69 | + maxRecordsCount: 10000, | ||
70 | + dataFolderPath: './data/', | ||
71 | + maxFilesCount: 5, | ||
72 | + securityType: "accessToken", // "accessToken", "tls" | ||
73 | + caCertPath: '/etc/thingsboard-gateway/ca.pem', | ||
74 | + privateKeyPath: '/etc/thingsboard-gateway/privateKey.pem', | ||
75 | + certPath: '/etc/thingsboard-gateway/certificate.pem', | ||
76 | + connectors: {}, | ||
77 | + remoteLoggingLevel: "DEBUG", // level login | ||
78 | + remoteLoggingPathToLogs: './logs/' | ||
79 | + }; | ||
80 | + getGatewaysListByUser(true); | ||
81 | + | ||
82 | + vm.securityTypes = [{ | ||
83 | + name: 'Access Token', | ||
84 | + value: 'accessToken' | ||
85 | + }, { | ||
86 | + name: 'TLS', | ||
87 | + value: 'tls' | ||
88 | + }]; | ||
89 | + | ||
90 | + vm.storageTypes = [{ | ||
91 | + name: 'Memory storage', | ||
92 | + value: 'memoryStorage' | ||
93 | + }, { | ||
94 | + name: 'File storage', | ||
95 | + value: 'fileStorage' | ||
96 | + }]; | ||
97 | + | ||
98 | + $scope.$on('gateway-form-resize', function (event, formId) { | ||
99 | + if (vm.formId == formId) { | ||
100 | + updateWidgetDisplaying(); | ||
101 | + } | ||
102 | + }); | ||
103 | + | ||
104 | + function updateWidgetDisplaying() { | ||
105 | + if (vm.ctx && vm.ctx.$container) { | ||
106 | + vm.changeAlignment = (vm.ctx.$container[0].offsetWidth <= 425); | ||
107 | + } | ||
108 | + } | ||
109 | + | ||
110 | + updateWidgetDisplaying(); | ||
111 | + | ||
112 | + vm.getAccessToken = (deviceObj) => { | ||
113 | + if (deviceObj.name) { | ||
114 | + deviceService.findByName(deviceObj.name, {ignoreErrors: true}) | ||
115 | + .then( | ||
116 | + function (device) { | ||
117 | + getDeviceCredential(device.id.id); | ||
118 | + } | ||
119 | + ) | ||
120 | + } | ||
121 | + }; | ||
122 | + | ||
123 | + function getDeviceCredential(deviceId) { | ||
124 | + return deviceService.getDeviceCredentials(deviceId).then( | ||
125 | + (deviceCredentials) => { | ||
126 | + vm.configurations.accessToken = deviceCredentials.credentialsId; | ||
127 | + vm.configurations.entityType = deviceCredentials.deviceId.entityType; | ||
128 | + vm.configurations.entityId = deviceCredentials.deviceId.id; | ||
129 | + vm.getAttributeStart(); | ||
130 | + } | ||
131 | + ); | ||
132 | + } | ||
133 | + | ||
134 | + vm.createDevice = (deviceObj) => { | ||
135 | + deviceService.findByName(deviceObj.name, {ignoreErrors: true}) | ||
136 | + .then( | ||
137 | + function (device) { | ||
138 | + getDeviceCredential(device.id.id).then(() => { | ||
139 | + getGatewaysListByUser(); | ||
140 | + }); | ||
141 | + }, | ||
142 | + function () { | ||
143 | + deviceService.saveDevice(deviceObj).then( | ||
144 | + (device) => { | ||
145 | + deviceService.getDeviceCredentials(device.id.id).then( | ||
146 | + (data) => { | ||
147 | + vm.configurations.accessToken = data.credentialsId; | ||
148 | + vm.configurations.entityType = device.id.entityType; | ||
149 | + vm.configurations.entityId = device.id.id; | ||
150 | + vm.getAttributeStart(); | ||
151 | + getGatewaysListByUser(); | ||
152 | + } | ||
153 | + ); | ||
154 | + } | ||
155 | + ); | ||
156 | + }); | ||
157 | + }; | ||
158 | + | ||
159 | + vm.saveAttributeConfig = () => { | ||
160 | + vm.setAttribute(attributeNameShared, $window.btoa(angular.toJson(vm.getConfigAllByAttributeJSON())), types.attributesScope.shared.value); | ||
161 | + vm.setAttribute(attributeNameServer, $window.btoa(angular.toJson(vm.getConfigByAttributeTmpJSON())), types.attributesScope.server.value); | ||
162 | + vm.setAttribute(attributeNameLogShared, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value); | ||
163 | + }; | ||
164 | + | ||
165 | + vm.getAttributeStart = () => { | ||
166 | + let initResps = []; | ||
167 | + vm.configurations.connectors = {}; | ||
168 | + initResps.push(vm.getAttributeConfig(attributeNameClinet, types.attributesScope.client.value)); | ||
169 | + initResps.push(vm.getAttributeConfig(attributeNameServer, types.attributesScope.server.value)); | ||
170 | + initResps.push(vm.getAttributeConfig(attributeNameLogShared, types.attributesScope.shared.value)); | ||
171 | + $q.all(initResps).then((resp) => { | ||
172 | + vm.getAttributeInitFromClient(resp[0]); | ||
173 | + vm.getAttributeInitFromServer(resp[1]); | ||
174 | + vm.getAttributeInitFromShared(resp[2]); | ||
175 | + }, (err) => { | ||
176 | + console.log("getAttribute_error", err); //eslint-disable-line | ||
177 | + }); | ||
178 | + }; | ||
179 | + | ||
180 | + vm.getAttributeConfig = (attributeName, typeValue) => { | ||
181 | + let keys = [attributeName]; | ||
182 | + return attributeService.getEntityAttributesValues(vm.configurations.entityType, vm.configurations.entityId, typeValue, keys); | ||
183 | + }; | ||
184 | + | ||
185 | + vm.setAttribute = (attributeName, attributeConfig, typeValue) => { | ||
186 | + let attributes = [ | ||
187 | + { | ||
188 | + key: attributeName, | ||
189 | + value: attributeConfig | ||
190 | + } | ||
191 | + ]; | ||
192 | + attributeService.saveEntityAttributes(vm.configurations.entityType, vm.configurations.entityId, typeValue, attributes).then(() => { | ||
193 | + }, (err) => { | ||
194 | + console.log("setAttribute_", err); //eslint-disable-line | ||
195 | + }); | ||
196 | + }; | ||
197 | + | ||
198 | + vm.exportConfig = () => { | ||
199 | + let fileZip = {}; | ||
200 | + fileZip["tb_gateway.yaml"] = vm.getConfig(); | ||
201 | + vm.createConfigByExport(fileZip); | ||
202 | + vm.getLogsConfigByExport(fileZip); | ||
203 | + importExport.exportJSZip(fileZip, 'config'); | ||
204 | + vm.setAttribute(attributeNameLogShared, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value); | ||
205 | + }; | ||
206 | + | ||
207 | + vm.getConfig = () => { | ||
208 | + let config; | ||
209 | + config = 'thingsboard:\n'; | ||
210 | + config += ' host: ' + vm.configurations.host + '\n'; | ||
211 | + config += ' remoteConfiguration: ' + vm.configurations.remoteConfiguration + '\n'; | ||
212 | + config += ' port: ' + vm.configurations.port + '\n'; | ||
213 | + config += ' security:\n'; | ||
214 | + if (vm.configurations.securityType === 'accessToken') { | ||
215 | + config += ' access-token: ' + vm.configurations.accessToken + '\n'; | ||
216 | + } else if (vm.configurations.securityType === 'tls') { | ||
217 | + config += ' ca_cert: ' + vm.configurations.caCertPath + '\n'; | ||
218 | + config += ' privateKey: ' + vm.configurations.privateKeyPath + '\n'; | ||
219 | + config += ' cert: ' + vm.configurations.certPath + '\n'; | ||
220 | + } | ||
221 | + config += 'storage:\n'; | ||
222 | + if (vm.configurations.storageType === 'memoryStorage') { | ||
223 | + config += ' type: memory\n'; | ||
224 | + config += ' read_records_count: ' + vm.configurations.readRecordsCount + '\n'; | ||
225 | + config += ' max_records_count: ' + vm.configurations.maxRecordsCount + '\n'; | ||
226 | + } else if (vm.configurations.storageType === 'fileStorage') { | ||
227 | + config += ' type: file\n'; | ||
228 | + config += ' data_folder_path: ' + vm.configurations.dataFolderPath + '\n'; | ||
229 | + config += ' max_file_count: ' + vm.configurations.maxFilesCount + '\n'; | ||
230 | + config += ' max_read_records_count: ' + vm.configurations.readRecordsCount + '\n'; | ||
231 | + config += ' max_records_per_file: ' + vm.configurations.maxRecordsCount + '\n'; | ||
232 | + } | ||
233 | + config += 'connectors:\n'; | ||
234 | + for (let connector in vm.configurations.connectors) { | ||
235 | + if (vm.configurations.connectors[connector].enabled) { | ||
236 | + config += ' -\n'; | ||
237 | + config += ' name: ' + connector + ' Connector\n'; | ||
238 | + config += ' type: ' + vm.configurations.connectors[connector].connector + '\n'; | ||
239 | + config += ' configuration: ' + vm.validFileName(connector) + ".json" + '\n'; | ||
240 | + } | ||
241 | + } | ||
242 | + return config; | ||
243 | + }; | ||
244 | + | ||
245 | + vm.createConfigByExport = (fileZipAdd) => { | ||
246 | + for (let connector in vm.configurations.connectors) { | ||
247 | + if (vm.configurations.connectors[connector].enabled) { | ||
248 | + fileZipAdd[vm.validFileName(connector) + ".json"] = angular.toJson(vm.configurations.connectors[connector].config); | ||
249 | + } | ||
250 | + } | ||
251 | + }; | ||
252 | + | ||
253 | + vm.getLogsConfigByExport = (fileZipAdd) => { | ||
254 | + fileZipAdd["logs.conf"] = vm.getLogsConfig(); | ||
255 | + }; | ||
256 | + | ||
257 | + vm.getLogsConfig = () => { | ||
258 | + return vm.remoteLoggingConfig | ||
259 | + .replace(/{ERROR}/g, vm.configurations.remoteLoggingLevel) | ||
260 | + .replace(/{.\/logs\/}/g, vm.configurations.remoteLoggingPathToLogs); | ||
261 | + }; | ||
262 | + | ||
263 | + vm.getConfigAllByAttributeJSON = () => { | ||
264 | + let thingsBoardAll = {}; | ||
265 | + thingsBoardAll["thingsboard"] = vm.getConfigMainByAttributeJSON(); | ||
266 | + vm.getConfigByAttributeJSON(thingsBoardAll); | ||
267 | + return thingsBoardAll; | ||
268 | + }; | ||
269 | + | ||
270 | + vm.getConfigMainByAttributeJSON = () => { | ||
271 | + let configMain = {}; | ||
272 | + let thingsBoard = {}; | ||
273 | + thingsBoard.host = vm.configurations.host; | ||
274 | + thingsBoard.remoteConfiguration = vm.configurations.remoteConfiguration; | ||
275 | + thingsBoard.port = vm.configurations.port; | ||
276 | + let security = {}; | ||
277 | + if (vm.configurations.securityType === 'accessToken') { | ||
278 | + security.accessToken = (vm.configurations.accessToken) ? vm.configurations.accessToken : "" | ||
279 | + } else { | ||
280 | + security.caCert = vm.configurations.caCertPath; | ||
281 | + security.privateKey = vm.configurations.privateKeyPath; | ||
282 | + security.cert = vm.configurations.certPath; | ||
283 | + } | ||
284 | + thingsBoard.security = security; | ||
285 | + configMain.thingsboard = thingsBoard; | ||
286 | + | ||
287 | + let storage = {}; | ||
288 | + if (vm.configurations.storageType === 'memoryStorage') { | ||
289 | + storage.type = "memory"; | ||
290 | + storage.read_records_count = vm.configurations.readRecordsCount; | ||
291 | + storage.max_records_count = vm.configurations.maxRecordsCount; | ||
292 | + } else if (vm.configurations.storageType === 'fileStorage') { | ||
293 | + storage.type = "file"; | ||
294 | + storage.data_folder_path = vm.configurations.dataFolderPath; | ||
295 | + storage.max_file_count = vm.configurations.maxFilesCount; | ||
296 | + storage.max_read_records_count = vm.configurations.readRecordsCount; | ||
297 | + storage.max_records_per_file = vm.configurations.maxRecordsCount; | ||
298 | + } | ||
299 | + configMain.storage = storage; | ||
300 | + | ||
301 | + let conn = []; | ||
302 | + for (let connector in vm.configurations.connectors) { | ||
303 | + if (vm.configurations.connectors[connector].enabled) { | ||
304 | + let connect = {}; | ||
305 | + connect.configuration = vm.validFileName(connector) + ".json"; | ||
306 | + connect.name = connector; | ||
307 | + connect.type = vm.configurations.connectors[connector].connector; | ||
308 | + conn.push(connect); | ||
309 | + } | ||
310 | + } | ||
311 | + configMain.connectors = conn; | ||
312 | + | ||
313 | + configMain.logs = $window.btoa(vm.getLogsConfig()); | ||
314 | + | ||
315 | + return configMain; | ||
316 | + }; | ||
317 | + | ||
318 | + vm.getConfigByAttributeJSON = (thingsBoardBy) => { | ||
319 | + for (let connector in vm.configurations.connectors) { | ||
320 | + if (vm.configurations.connectors[connector].enabled) { | ||
321 | + let typeAr = vm.configurations.connectors[connector].connector; | ||
322 | + let objTypeAll = []; | ||
323 | + for (let conn in vm.configurations.connectors) { | ||
324 | + if (typeAr === vm.configurations.connectors[conn].connector && vm.configurations.connectors[conn].enabled) { | ||
325 | + let objType = {}; | ||
326 | + objType["name"] = conn; | ||
327 | + objType["config"] = vm.configurations.connectors[conn].config; | ||
328 | + objTypeAll.push(objType); | ||
329 | + } | ||
330 | + } | ||
331 | + if (objTypeAll.length > 0) { | ||
332 | + thingsBoardBy[typeAr] = objTypeAll; | ||
333 | + } | ||
334 | + } | ||
335 | + } | ||
336 | + }; | ||
337 | + | ||
338 | + vm.getConfigByAttributeTmpJSON = () => { | ||
339 | + let connects = {}; | ||
340 | + for (let connector in vm.configurations.connectors) { | ||
341 | + if (!vm.configurations.connectors[connector].enabled && Object.keys(vm.configurations.connectors[connector].config).length !== 0) { | ||
342 | + let conn = {}; | ||
343 | + conn["connector"] = vm.configurations.connectors[connector].connector; | ||
344 | + conn["config"] = vm.configurations.connectors[connector].config; | ||
345 | + connects[connector] = conn; | ||
346 | + } | ||
347 | + } | ||
348 | + return connects; | ||
349 | + }; | ||
350 | + | ||
351 | + function getGatewaysListByUser(firstInit) { | ||
352 | + vm.gateways = []; | ||
353 | + vm.currentUser = userService.getCurrentUser(); | ||
354 | + if (vm.currentUser.authority === 'TENANT_ADMIN') { | ||
355 | + deviceService.getTenantDevices({limit: 500}).then( | ||
356 | + (devices) => { | ||
357 | + if (devices.data.length > 0) { | ||
358 | + devices.data.forEach((device) => { | ||
359 | + if (device.additionalInfo !== null && device.additionalInfo.gateway === true) { | ||
360 | + vm.gateways.push(device.name); | ||
361 | + if (firstInit && vm.gateways.length && device.name === vm.gateways[0]) { | ||
362 | + vm.configurations.singleSelect = vm.gateways[0]; | ||
363 | + let deviceObj = { | ||
364 | + "name": vm.configurations.singleSelect, | ||
365 | + "type": "Gateway", | ||
366 | + "additionalInfo": { | ||
367 | + "gateway": true | ||
368 | + } | ||
369 | + }; | ||
370 | + vm.getAccessToken(deviceObj); | ||
371 | + } | ||
372 | + } | ||
373 | + }); | ||
374 | + } | ||
375 | + } | ||
376 | + ); | ||
377 | + } else if (vm.currentUser.authority === 'CUSTOMER_USER') { | ||
378 | + deviceService.getCustomerDevices(vm.currentUser.customerId, {limit: 500}).then( | ||
379 | + (devices) => { | ||
380 | + if (devices.data.length > 0) { | ||
381 | + devices.data.forEach((device) => { | ||
382 | + if (device.additionalInfo !== null && device.additionalInfo.gateway === true) { | ||
383 | + vm.gateways.push(device.name); | ||
384 | + if (firstInit && vm.gateways.length) { | ||
385 | + vm.configurations.singleSelect = vm.gateways[0]; | ||
386 | + let deviceObj = { | ||
387 | + "name": vm.configurations.singleSelect, | ||
388 | + "type": "Gateway", | ||
389 | + "additionalInfo": { | ||
390 | + "gateway": true | ||
391 | + } | ||
392 | + }; | ||
393 | + vm.getAccessToken(deviceObj); | ||
394 | + } | ||
395 | + } | ||
396 | + }); | ||
397 | + } | ||
398 | + } | ||
399 | + ); | ||
400 | + } | ||
401 | + } | ||
402 | + | ||
403 | + vm.getAttributeInitFromClient = (resp) => { | ||
404 | + if (resp.length > 0) { | ||
405 | + vm.configurations.connectors = {}; | ||
406 | + let attribute = angular.fromJson($window.atob(resp[0].value)); | ||
407 | + for (var type in attribute) { | ||
408 | + let keyVal = attribute[type]; | ||
409 | + if (type === "thingsboard") { | ||
410 | + if (keyVal !== null && Object.keys(keyVal).length > 0) { | ||
411 | + vm.setConfigMain(keyVal); | ||
412 | + } | ||
413 | + } else { | ||
414 | + for (let typeVal in keyVal) { | ||
415 | + let typeName = ''; | ||
416 | + if (Object.prototype.hasOwnProperty.call(keyVal[typeVal], 'name')) { | ||
417 | + typeName = 'name'; | ||
418 | + } | ||
419 | + let key = ""; | ||
420 | + key = (typeName === "") ? "No name" : ((typeName === 'name') ? keyVal[typeVal].name : keyVal[typeVal][typeName].name); | ||
421 | + let conn = {}; | ||
422 | + conn["enabled"] = true; | ||
423 | + conn["connector"] = type; | ||
424 | + conn["config"] = angular.toJson(keyVal[typeVal].config); | ||
425 | + vm.configurations.connectors[key] = conn; | ||
426 | + } | ||
427 | + } | ||
428 | + } | ||
429 | + } | ||
430 | + }; | ||
431 | + | ||
432 | + vm.getAttributeInitFromServer = (resp) => { | ||
433 | + if (resp.length > 0) { | ||
434 | + let attribute = angular.fromJson($window.atob(resp[0].value)); | ||
435 | + for (let key in attribute) { | ||
436 | + let conn = {}; | ||
437 | + conn["enabled"] = false; | ||
438 | + conn["connector"] = attribute[key].connector; | ||
439 | + conn["config"] = angular.toJson(attribute[key].config); | ||
440 | + vm.configurations.connectors[key] = conn; | ||
441 | + } | ||
442 | + } | ||
443 | + }; | ||
444 | + | ||
445 | + vm.getAttributeInitFromShared = (resp) => { | ||
446 | + if (resp.length > 0) { | ||
447 | + if (vm.types.gatewayLogLevel[resp[0].value.toLowerCase()]) { | ||
448 | + vm.configurations.remoteLoggingLevel = resp[0].value.toUpperCase(); | ||
449 | + } | ||
450 | + } else { | ||
451 | + vm.configurations.remoteLoggingLevel = vm.types.gatewayLogLevel.debug; | ||
452 | + } | ||
453 | + }; | ||
454 | + | ||
455 | + vm.setConfigMain = (keyVal) => { | ||
456 | + if (Object.prototype.hasOwnProperty.call(keyVal, 'thingsboard')) { | ||
457 | + vm.configurations.host = keyVal.thingsboard.host; | ||
458 | + vm.configurations.port = keyVal.thingsboard.port; | ||
459 | + vm.configurations.remoteConfiguration = keyVal.thingsboard.remoteConfiguration; | ||
460 | + if (Object.prototype.hasOwnProperty.call(keyVal.thingsboard.security, 'accessToken')) { | ||
461 | + vm.configurations.securityType = 'accessToken'; | ||
462 | + vm.configurations.accessToken = keyVal.thingsboard.security.accessToken; | ||
463 | + } else { | ||
464 | + vm.configurations.securityType = 'tls'; | ||
465 | + vm.configurations.caCertPath = keyVal.thingsboard.security.caCert; | ||
466 | + vm.configurations.privateKeyPath = keyVal.thingsboard.security.private_key; | ||
467 | + vm.configurations.certPath = keyVal.thingsboard.security.cert; | ||
468 | + } | ||
469 | + } | ||
470 | + if (Object.prototype.hasOwnProperty.call(keyVal, 'storage') && Object.prototype.hasOwnProperty.call(keyVal.storage, 'type')) { | ||
471 | + if (keyVal.storage.type === 'memory') { | ||
472 | + vm.configurations.storageType = 'memoryStorage'; | ||
473 | + vm.configurations.readRecordsCount = keyVal.storage.read_records_count; | ||
474 | + vm.configurations.maxRecordsCount = keyVal.storage.max_records_count; | ||
475 | + } else if (keyVal.storage.type === 'file') { | ||
476 | + vm.configurations.storageType = 'fileStorage'; | ||
477 | + vm.configurations.dataFolderPath = keyVal.storage.data_folder_path; | ||
478 | + vm.configurations.maxFilesCount = keyVal.storage.max_file_count; | ||
479 | + vm.configurations.readRecordsCount = keyVal.storage.read_records_count; | ||
480 | + vm.configurations.maxRecordsCount = keyVal.storage.max_records_count; | ||
481 | + } | ||
482 | + } | ||
483 | + }; | ||
484 | + | ||
485 | + vm.setSaveTypeConfig = (itemVal) => { | ||
486 | + vm.configurations.remoteConfiguration = itemVal.item; | ||
487 | + }; | ||
488 | + | ||
489 | + vm.validFileName = (fileName) => { | ||
490 | + let fileName1 = fileName.replace("_", ""); | ||
491 | + let fileName2 = fileName1.replace("-", ""); | ||
492 | + let fileName3 = fileName2.replace(/^\s+|\s+$/g, ''); | ||
493 | + let fileName4 = fileName3.toLowerCase(); | ||
494 | + return fileName4; | ||
495 | + }; | ||
496 | +} | ||
497 | + | ||
498 | + |
1 | +/** | ||
2 | + * Copyright © 2016-2020 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 | +.gateway-form{ | ||
17 | + padding: 5px 5px 0; | ||
18 | + | ||
19 | + .gateway-form-row{ | ||
20 | + md-input-container{ | ||
21 | + margin-bottom: 0; | ||
22 | + } | ||
23 | + | ||
24 | + &.gateway-config-row-vertical{ | ||
25 | + flex-direction: column; | ||
26 | + | ||
27 | + .md-select-container{ | ||
28 | + margin-bottom: 14px; | ||
29 | + } | ||
30 | + } | ||
31 | + } | ||
32 | + | ||
33 | + .form-action-buttons{ | ||
34 | + padding-top: 8px; | ||
35 | + } | ||
36 | +} | ||
37 | + | ||
38 | +.security-type { | ||
39 | + margin-top: 38px; | ||
40 | +} |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<form name="gatewayConfiguration" class="gateway-form"> | ||
19 | + <md-expansion-panel-group> | ||
20 | + <md-expansion-panel md-component-id="thingsboardPanelId"> | ||
21 | + <md-expansion-panel-collapsed> | ||
22 | + <div class="tb-panel-title">{{ 'gateway.thingsboard' | translate | uppercase }}</div> | ||
23 | + <span flex></span> | ||
24 | + <md-expansion-panel-icon></md-expansion-panel-icon> | ||
25 | + </md-expansion-panel-collapsed> | ||
26 | + <md-expansion-panel-expanded> | ||
27 | + <md-expansion-panel-header ng-click="$mdExpansionPanel('thingsboardPanelId').collapse()"> | ||
28 | + <div class="tb-panel-title">{{ 'gateway.thingsboard' | translate | uppercase }}</div> | ||
29 | + <span flex></span> | ||
30 | + <md-expansion-panel-icon></md-expansion-panel-icon> | ||
31 | + </md-expansion-panel-header> | ||
32 | + <md-expansion-panel-content> | ||
33 | + <tb-gateway-config-select tb-required="true" | ||
34 | + ng-model="vm.configurations.singleSelect" | ||
35 | + the-form="gatewayConfiguration" | ||
36 | + gateway-list="vm.gateways" | ||
37 | + get_access_token="vm.getAccessToken" | ||
38 | + create-device="vm.createDevice"> | ||
39 | + </tb-gateway-config-select> | ||
40 | + <md-input-container class="md-block"> | ||
41 | + <label>{{'gateway.security-type' | translate }}</label> | ||
42 | + <md-select name="securityType" ng-model="vm.configurations.securityType"> | ||
43 | + <md-option ng-repeat="securityType in vm.securityTypes" ng-value="securityType.value"> | ||
44 | + {{securityType.name}} | ||
45 | + </md-option> | ||
46 | + </md-select> | ||
47 | + </md-input-container> | ||
48 | + <div layout="row" class="gateway-form-row" | ||
49 | + ng-class="{'gateway-config-row-vertical': vm.changeAlignment}"> | ||
50 | + <md-input-container class="md-block" flex> | ||
51 | + <label>{{ 'gateway.thingsboard-host' | translate }}</label> | ||
52 | + <input type="text" name="host" ng-model="vm.configurations.host" required> | ||
53 | + <div ng-messages="gatewayConfiguration.host.$error"> | ||
54 | + <div ng-message="required" translate>extension.field-required</div> | ||
55 | + </div> | ||
56 | + </md-input-container> | ||
57 | + <md-input-container class="md-block" flex> | ||
58 | + <label>{{ 'gateway.thingsboard-port' | translate }}</label> | ||
59 | + <input type="number" min="1" max="65535" step="1" name="port" | ||
60 | + ng-model="vm.configurations.port" required> | ||
61 | + <div ng-messages="gatewayConfiguration.port.$error"> | ||
62 | + <div ng-message="required" translate>extension.field-required</div> | ||
63 | + <div ng-message="max" translate>max</div> | ||
64 | + <div ng-message="min" translate>min</div> | ||
65 | + </div> | ||
66 | + </md-input-container> | ||
67 | + </div> | ||
68 | + <div ng-if="vm.configurations.securityType=='tls'"> | ||
69 | + <md-input-container class="md-block security-type"> | ||
70 | + <label>{{'gateway.tls-path-ca-certificate' | translate }}</label> | ||
71 | + <input type="text" ng-model="vm.configurations.caCertPath" name="caCertPath"/> | ||
72 | + </md-input-container> | ||
73 | + <md-input-container class="md-block"> | ||
74 | + <label>{{'gateway.tls-path-private-key' | translate }}</label> | ||
75 | + <input type="text" ng-model="vm.configurations.privateKeyPath" name="privateKeyPath"/> | ||
76 | + </md-input-container> | ||
77 | + <md-input-container class="md-block"> | ||
78 | + <label>{{'gateway.tls-path-client-certificate' | translate }}</label> | ||
79 | + <input type="text" ng-model="vm.configurations.certPath" name="certPath"/> | ||
80 | + </md-input-container> | ||
81 | + </div> | ||
82 | + <md-checkbox ng-model="vm.configurations.remoteConfiguration" | ||
83 | + name="remoteConfiguration" | ||
84 | + ng-click="vm.setSaveTypeConfig({item: vm.configurations.remoteConfiguration})" | ||
85 | + aria-label="{{ 'gateway.remote-tip' | translate }}"> | ||
86 | + {{ 'gateway.remote' | translate }} | ||
87 | + <md-tooltip md-direction="right">{{'gateway.remote-tip' | translate }}</md-tooltip> | ||
88 | + </md-checkbox> | ||
89 | + <div layout="row" class="gateway-form-row" | ||
90 | + ng-class="{'gateway-config-row-vertical': vm.changeAlignment}"> | ||
91 | + <md-input-container class="md-block md-select-container" flex> | ||
92 | + <label>{{'gateway.remote-logging-level' | translate }}</label> | ||
93 | + <md-select name="loggingLevel" ng-model="vm.configurations.remoteLoggingLevel"> | ||
94 | + <md-option ng-repeat="loggingLevel in vm.types.gatewayLogLevel" | ||
95 | + ng-value="loggingLevel" ng-selected="$index === 5"> | ||
96 | + {{loggingLevel}} | ||
97 | + </md-option> | ||
98 | + </md-select> | ||
99 | + </md-input-container> | ||
100 | + <md-input-container class="md-block" flex> | ||
101 | + <label>{{'gateway.remote-logging-path-logs' | translate }}</label> | ||
102 | + <input type="text" ng-model="vm.configurations.remoteLoggingPathToLogs" | ||
103 | + name="remoteLoggingPathToLogs" required> | ||
104 | + <div ng-messages="gatewayConfiguration.remoteLoggingPathToLogs.$error"> | ||
105 | + <div ng-message="required" translate>extension.field-required</div> | ||
106 | + </div> | ||
107 | + </md-input-container> | ||
108 | + </div> | ||
109 | + </md-expansion-panel-content> | ||
110 | + </md-expansion-panel-expanded> | ||
111 | + </md-expansion-panel> | ||
112 | + <md-expansion-panel md-component-id="storagePanelId"> | ||
113 | + <md-expansion-panel-collapsed> | ||
114 | + <div class="tb-panel-title">{{ 'gateway.storage' | translate | uppercase }}</div> | ||
115 | + <span flex></span> | ||
116 | + <md-expansion-panel-icon></md-expansion-panel-icon> | ||
117 | + </md-expansion-panel-collapsed> | ||
118 | + <md-expansion-panel-expanded> | ||
119 | + <md-expansion-panel-header ng-click="$mdExpansionPanel('storagePanelId').collapse()"> | ||
120 | + <div class="tb-panel-title">{{ 'gateway.storage' | translate | uppercase }}</div> | ||
121 | + <span flex></span> | ||
122 | + <md-expansion-panel-icon></md-expansion-panel-icon> | ||
123 | + </md-expansion-panel-header> | ||
124 | + <md-expansion-panel-content> | ||
125 | + <md-input-container class="md-block" flex> | ||
126 | + <label>{{'gateway.storage-type' | translate }}</label> | ||
127 | + <md-select required ng-model="vm.configurations.storageType"> | ||
128 | + <md-option ng-repeat="storageType in vm.storageTypes" ng-value="storageType.value"> | ||
129 | + {{storageType.name}} | ||
130 | + </md-option> | ||
131 | + </md-select> | ||
132 | + </md-input-container> | ||
133 | + | ||
134 | + <div layout="row" class="gateway-form-row" | ||
135 | + ng-class="{'gateway-config-row-vertical': vm.changeAlignment}"> | ||
136 | + <md-input-container class="md-block" flex> | ||
137 | + <label>{{'gateway.storage-read-time' | translate }}</label> | ||
138 | + <input type="number" min="1" name="readRecordsCount" | ||
139 | + ng-model='vm.configurations.readRecordsCount' required/> | ||
140 | + <div ng-messages="gatewayConfiguration.readRecordsCount.$error"> | ||
141 | + <div ng-message="required" translate>extension.field-required</div> | ||
142 | + </div> | ||
143 | + </md-input-container> | ||
144 | + | ||
145 | + <md-input-container class="md-block" flex> | ||
146 | + <label>{{'gateway.storage-max-time' | translate }}</label> | ||
147 | + <input type="number" min="1" name="maxRecordsCount" | ||
148 | + ng-model='vm.configurations.maxRecordsCount' required/> | ||
149 | + <div ng-messages="gatewayConfiguration.maxRecordsCount.$error"> | ||
150 | + <div ng-message="required" translate>extension.field-required</div> | ||
151 | + </div> | ||
152 | + </md-input-container> | ||
153 | + </div> | ||
154 | + | ||
155 | + <div layout="row" class="gateway-form-row" | ||
156 | + ng-if="vm.configurations.storageType == 'fileStorage'" | ||
157 | + ng-class="{'gateway-config-row-vertical': vm.changeAlignment}"> | ||
158 | + <md-input-container class="md-block" flex> | ||
159 | + <label>{{'gateway.storage-max-files' | translate }}</label> | ||
160 | + <input type="number" min="1" name="maxFilesCount" ng-model='vm.configurations.maxFilesCount' | ||
161 | + required/> | ||
162 | + <div ng-messages="gatewayConfiguration.maxFilesCount.$error"> | ||
163 | + <div ng-message="required" translate>extension.field-required</div> | ||
164 | + </div> | ||
165 | + </md-input-container> | ||
166 | + | ||
167 | + <md-input-container class="md-block" flex> | ||
168 | + <label>{{'gateway.storage-data-path' | translate }}</label> | ||
169 | + <input type="text" name="dataFolderPath" ng-model='vm.configurations.dataFolderPath' | ||
170 | + required/> | ||
171 | + <div ng-messages="gatewayConfiguration.dataFolderPath.$error"> | ||
172 | + <div ng-message="required" translate>extension.field-required</div> | ||
173 | + </div> | ||
174 | + </md-input-container> | ||
175 | + </div> | ||
176 | + </md-expansion-panel-content> | ||
177 | + </md-expansion-panel-expanded> | ||
178 | + </md-expansion-panel> | ||
179 | + <md-expansion-panel md-component-id="connectorsPanelId"> | ||
180 | + <md-expansion-panel-collapsed> | ||
181 | + <div class="tb-panel-title">{{ 'gateway.connectors' | translate | uppercase }}</div> | ||
182 | + <span flex></span> | ||
183 | + <md-expansion-panel-icon></md-expansion-panel-icon> | ||
184 | + </md-expansion-panel-collapsed> | ||
185 | + <md-expansion-panel-expanded> | ||
186 | + <md-expansion-panel-header ng-click="$mdExpansionPanel('connectorsPanelId').collapse()"> | ||
187 | + <div class="tb-panel-title">{{ 'gateway.connectors' | translate | uppercase }}</div> | ||
188 | + <span flex></span> | ||
189 | + <md-expansion-panel-icon></md-expansion-panel-icon> | ||
190 | + </md-expansion-panel-header> | ||
191 | + <md-expansion-panel-content> | ||
192 | + <tb-gateway-config | ||
193 | + gateway-config="vm.configurations.connectors" | ||
194 | + change-alignment="vm.changeAlignment"> | ||
195 | + </tb-gateway-config> | ||
196 | + </md-expansion-panel-content> | ||
197 | + </md-expansion-panel-expanded> | ||
198 | + </md-expansion-panel> | ||
199 | + </md-expansion-panel-group> | ||
200 | + <section layout="row" layout-align="end center" class="form-action-buttons"> | ||
201 | + <md-button class="md-primary md-raised" | ||
202 | + ng-click="vm.exportConfig()" | ||
203 | + ng-if="!vm.configurations.remoteConfiguration" | ||
204 | + ng-disabled="gatewayConfiguration.$invalid || !gatewayConfiguration.$dirty" | ||
205 | + aria-label="{{ 'gateway.download-tip' | translate }}"> | ||
206 | + {{'action.download' | translate }} | ||
207 | + <md-tooltip>{{'gateway.download-tip' | translate }}</md-tooltip> | ||
208 | + </md-button> | ||
209 | + | ||
210 | + <md-button class="md-primary md-raised" | ||
211 | + ng-click="vm.saveAttributeConfig()" | ||
212 | + ng-if="vm.configurations.remoteConfiguration" | ||
213 | + ng-disabled="gatewayConfiguration.$invalid || !gatewayConfiguration.$dirty" | ||
214 | + aria-label="{{ 'gateway.save-tip' | translate }}"> | ||
215 | + {{'action.save' | translate }} | ||
216 | + <md-tooltip ng-if="vm.configurations.remoteConfiguration">{{'gateway.save-tip' | translate }}</md-tooltip> | ||
217 | + </md-button> | ||
218 | + </section> | ||
219 | +</form> |
@@ -18,6 +18,7 @@ | @@ -18,6 +18,7 @@ | ||
18 | import importDialogTemplate from './import-dialog.tpl.html'; | 18 | import importDialogTemplate from './import-dialog.tpl.html'; |
19 | import importDialogCSVTemplate from './import-dialog-csv.tpl.html'; | 19 | import importDialogCSVTemplate from './import-dialog-csv.tpl.html'; |
20 | import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; | 20 | import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; |
21 | +import * as JSZip from 'jszip'; | ||
21 | 22 | ||
22 | /* eslint-enable import/no-unresolved, import/default */ | 23 | /* eslint-enable import/no-unresolved, import/default */ |
23 | 24 | ||
@@ -28,6 +29,10 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; | @@ -28,6 +29,10 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; | ||
28 | export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, $rootScope, | 29 | export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, $rootScope, |
29 | dashboardUtils, entityService, dashboardService, ruleChainService, widgetService, toast, attributeService) { | 30 | dashboardUtils, entityService, dashboardService, ruleChainService, widgetService, toast, attributeService) { |
30 | 31 | ||
32 | + const JSZIP_TYPE = { | ||
33 | + mimeType: 'application/zip', | ||
34 | + extension: 'zip' | ||
35 | + }; | ||
31 | 36 | ||
32 | var service = { | 37 | var service = { |
33 | exportDashboard: exportDashboard, | 38 | exportDashboard: exportDashboard, |
@@ -40,6 +45,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -40,6 +45,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
40 | importWidgetType: importWidgetType, | 45 | importWidgetType: importWidgetType, |
41 | exportWidgetsBundle: exportWidgetsBundle, | 46 | exportWidgetsBundle: exportWidgetsBundle, |
42 | importWidgetsBundle: importWidgetsBundle, | 47 | importWidgetsBundle: importWidgetsBundle, |
48 | + exportJSZip: exportJSZip, | ||
43 | exportExtension: exportExtension, | 49 | exportExtension: exportExtension, |
44 | importExtension: importExtension, | 50 | importExtension: importExtension, |
45 | importEntities: importEntities, | 51 | importEntities: importEntities, |
@@ -851,7 +857,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -851,7 +857,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
851 | }); | 857 | }); |
852 | return $q.all(promises); | 858 | return $q.all(promises); |
853 | } | 859 | } |
854 | - | 860 | + |
855 | function createMultiEntity(arrayData, entityType, updateData, config) { | 861 | function createMultiEntity(arrayData, entityType, updateData, config) { |
856 | let partSize = 100; | 862 | let partSize = 100; |
857 | partSize = arrayData.length > partSize ? partSize : arrayData.length; | 863 | partSize = arrayData.length > partSize ? partSize : arrayData.length; |
@@ -982,6 +988,49 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -982,6 +988,49 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
982 | let dialogElement = element[0].getElementsByTagName('md-dialog'); | 988 | let dialogElement = element[0].getElementsByTagName('md-dialog'); |
983 | dialogElement[0].style.width = dialogElement[0].offsetWidth + 2 + "px"; | 989 | dialogElement[0].style.width = dialogElement[0].offsetWidth + 2 + "px"; |
984 | } | 990 | } |
991 | + | ||
992 | + /** | ||
993 | + * | ||
994 | + * @param data | ||
995 | + * @param filename | ||
996 | + * Warn data !!! Not object, if object, then object convert from object to format txt | ||
997 | + * Example: data = {keyNameFile1: valueFile1, | ||
998 | + * keyNameFile2: valueFile2...} | ||
999 | + * fileName - name file of the arhiv | ||
1000 | + */ | ||
1001 | + function exportJSZip(data, filename) { | ||
1002 | + let jsZip = new JSZip(); | ||
1003 | + for (let keyName in data) { | ||
1004 | + let valueData = data[keyName]; | ||
1005 | + jsZip.file(keyName, valueData); | ||
1006 | + } | ||
1007 | + jsZip.generateAsync({type: "Blob"}).then(function (content) { | ||
1008 | + downloadFile(content, filename, JSZIP_TYPE); | ||
1009 | + }); | ||
1010 | + } | ||
1011 | + | ||
1012 | + | ||
1013 | + function downloadFile(data, filename, fileType) { | ||
1014 | + console.log("downloadFile", data, filename, fileType); // eslint-disable-line | ||
1015 | + if (!filename) { | ||
1016 | + filename = 'download'; | ||
1017 | + } | ||
1018 | + filename += '.' + fileType.extension; | ||
1019 | + var blob = new Blob([data], {type: fileType.mimeType}); | ||
1020 | + // FOR IE: | ||
1021 | + if (window.navigator && window.navigator.msSaveOrOpenBlob) { | ||
1022 | + window.navigator.msSaveOrOpenBlob(blob, filename); | ||
1023 | + } else { | ||
1024 | + var e = document.createEvent('MouseEvents'), | ||
1025 | + a = document.createElement('a'); | ||
1026 | + a.download = filename; | ||
1027 | + a.href = window.URL.createObjectURL(blob); | ||
1028 | + a.dataset.downloadurl = [fileType.mimeType, a.download, a.href].join(':'); | ||
1029 | + e.initEvent('click', true, false, window, | ||
1030 | + 0, 0, 0, 0, 0, false, false, false, false, 0, null); | ||
1031 | + a.dispatchEvent(e); | ||
1032 | + } | ||
1033 | + } | ||
985 | } | 1034 | } |
986 | 1035 | ||
987 | /* eslint-enable no-undef, angular/window-service, angular/document-service */ | 1036 | /* eslint-enable no-undef, angular/window-service, angular/document-service */ |
@@ -30,6 +30,9 @@ import thingsboardSideMenu from '../components/side-menu.directive'; | @@ -30,6 +30,9 @@ import thingsboardSideMenu from '../components/side-menu.directive'; | ||
30 | import thingsboardNavTree from '../components/nav-tree.directive'; | 30 | import thingsboardNavTree from '../components/nav-tree.directive'; |
31 | import thingsboardDashboardAutocomplete from '../components/dashboard-autocomplete.directive'; | 31 | import thingsboardDashboardAutocomplete from '../components/dashboard-autocomplete.directive'; |
32 | import thingsboardKvMap from '../components/kv-map.directive'; | 32 | import thingsboardKvMap from '../components/kv-map.directive'; |
33 | +import thingsboardGatewayConfig from '../components/gateWay/gateway-config.directive'; | ||
34 | +import thingsboardGatewayConfigSelect from '../components/gateWay/gateway-config-select.directive'; | ||
35 | +import thingsboardGatewayForm from '../components/gateWay/gateway-form.directive'; | ||
33 | import thingsboardJsonObjectEdit from '../components/json-object-edit.directive'; | 36 | import thingsboardJsonObjectEdit from '../components/json-object-edit.directive'; |
34 | import thingsboardJsonContent from '../components/json-content.directive'; | 37 | import thingsboardJsonContent from '../components/json-content.directive'; |
35 | 38 | ||
@@ -93,6 +96,9 @@ export default angular.module('thingsboard.home', [ | @@ -93,6 +96,9 @@ export default angular.module('thingsboard.home', [ | ||
93 | thingsboardNavTree, | 96 | thingsboardNavTree, |
94 | thingsboardDashboardAutocomplete, | 97 | thingsboardDashboardAutocomplete, |
95 | thingsboardKvMap, | 98 | thingsboardKvMap, |
99 | + thingsboardGatewayConfig, | ||
100 | + thingsboardGatewayConfigSelect, | ||
101 | + thingsboardGatewayForm, | ||
96 | thingsboardJsonObjectEdit, | 102 | thingsboardJsonObjectEdit, |
97 | thingsboardJsonContent | 103 | thingsboardJsonContent |
98 | ]) | 104 | ]) |
@@ -50,7 +50,8 @@ | @@ -50,7 +50,8 @@ | ||
50 | "export": "Export", | 50 | "export": "Export", |
51 | "share-via": "Share via {{provider}}", | 51 | "share-via": "Share via {{provider}}", |
52 | "continue": "Continue", | 52 | "continue": "Continue", |
53 | - "discard-changes": "Discard Changes" | 53 | + "discard-changes": "Discard Changes", |
54 | + "download": "Download" | ||
54 | }, | 55 | }, |
55 | "aggregation": { | 56 | "aggregation": { |
56 | "aggregation": "Aggregation", | 57 | "aggregation": "Aggregation", |
@@ -1124,6 +1125,58 @@ | @@ -1124,6 +1125,58 @@ | ||
1124 | "function": { | 1125 | "function": { |
1125 | "function": "Function" | 1126 | "function": "Function" |
1126 | }, | 1127 | }, |
1128 | + "gateway": { | ||
1129 | + "key": "Key configuration", | ||
1130 | + "value": "Value configuration", | ||
1131 | + "remove-entry": "Remove configuration", | ||
1132 | + "add-entry": "Add configuration", | ||
1133 | + "no-data": "No configurations", | ||
1134 | + "gateway-required": "Gateway is required.", | ||
1135 | + "gateway-name": "Gateway name", | ||
1136 | + "create-new-gateway": "Create a new gateway", | ||
1137 | + "create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?", | ||
1138 | + "no-gateway-matching": " '{{item}}' not found.", | ||
1139 | + "thingsboard": "ThingsBoard", | ||
1140 | + "connectors": "Connectors configuration", | ||
1141 | + "thingsboard-host": "ThingsBoard Host", | ||
1142 | + "thingsboard-port": "ThingsBoard Port", | ||
1143 | + "security-type": "Security type", | ||
1144 | + "tls-path-ca-certificate": "Path to CA certificate on gateway:", | ||
1145 | + "tls-path-private-key": "Path to private key on gateway:", | ||
1146 | + "tls-path-client-certificate": "Path to client certificate on gateway:", | ||
1147 | + "storage": "Storage", | ||
1148 | + "storage-type": "Storage type", | ||
1149 | + "storage-read-time": "Read records per time:", | ||
1150 | + "storage-max-time": "Maximum records per time:", | ||
1151 | + "storage-max-files": "Maximum files:", | ||
1152 | + "storage-data-path": "Data folder path:", | ||
1153 | + "download-tip": "Download configuration file", | ||
1154 | + "save-tip": "Save configuration file", | ||
1155 | + "remote-tip": "Allow remote configuration", | ||
1156 | + "remote": "Remote configuration", | ||
1157 | + "remote-logging-level": "Logging level", | ||
1158 | + "remote-logging-path-logs": "Path to logs", | ||
1159 | + "connector-type": "Connector type", | ||
1160 | + "update-config": "Add/update config JSON", | ||
1161 | + "delete": "Delete configuration", | ||
1162 | + "title-connectors-json": "Connector {{typeName}} configuration", | ||
1163 | + "json-required": "Config json is required for gateway config.", | ||
1164 | + "json-parse": "Unable to parse config json for gateway config.", | ||
1165 | + "tidy": "Tidy", | ||
1166 | + "tidy-tip": "Tidy config JSON", | ||
1167 | + "transformer-json-config": "JSON for the config*", | ||
1168 | + "toggle-fullscreen": "Toggle fullscreen", | ||
1169 | + "add-connectors": "Add new connectors", | ||
1170 | + "no-connectors": "No connectors", | ||
1171 | + "enabled": "Enabled", | ||
1172 | + "name": "Name", | ||
1173 | + "no-gateway-found": "No gateway found.", | ||
1174 | + "gateway": "Gateway", | ||
1175 | + "keyval-save-err": "Save config error", | ||
1176 | + "keyval-name-err": "Please add <b>Name</b>", | ||
1177 | + "keyval-type-err": "Please add <b>Connector type</b>", | ||
1178 | + "keyval-config-err": "Please add <b>configuration JSON</b>" | ||
1179 | + }, | ||
1127 | "grid": { | 1180 | "grid": { |
1128 | "delete-item-title": "Are you sure you want to delete this item?", | 1181 | "delete-item-title": "Are you sure you want to delete this item?", |
1129 | "delete-item-text": "Be careful, after the confirmation this item and all related data will become unrecoverable.", | 1182 | "delete-item-text": "Be careful, after the confirmation this item and all related data will become unrecoverable.", |