Commit 743b97fe9fb8c8032cfd8bb2e609203b21e81249

Authored by Sergey Tarnavskiy
1 parent 9e1dbd4f

http form

... ... @@ -317,6 +317,21 @@ export default angular.module('thingsboard.types', [])
317 317 name: "event.type-stats"
318 318 }
319 319 },
  320 + extensionType: {
  321 + http: "HTTP",
  322 + mqtt: "MQTT",
  323 + opc: "OPC UA"
  324 + },
  325 + extensionValueType: {
  326 + string: 'value.string',
  327 + long: 'value.long',
  328 + double: 'value.double',
  329 + boolean: 'value.boolean'
  330 + },
  331 + extensionTransformerType: {
  332 + toDouble: 'extension.to-double',
  333 + custom: 'extension.custom'
  334 + },
320 335 latestTelemetry: {
321 336 value: "LATEST_TELEMETRY",
322 337 name: "attribute.scope-latest-telemetry",
... ...
... ... @@ -67,4 +67,11 @@
67 67 entity-type="{{vm.types.entityType.device}}">
68 68 </tb-relation-table>
69 69 </md-tab>
  70 + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.operatingItem().additionalInfo.gateway" md-on-select="vm.grid.triggerResize()" label="{{ 'extension.extensions' | translate }}">
  71 + <tb-extension-table flex
  72 + entity-id="vm.grid.operatingItem().id.id"
  73 + entity-type="{{vm.types.entityType.device}}">
  74 +
  75 + </tb-extension-table>
  76 + </md-tab>
70 77 </tb-grid>
... ...
  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 beautify from 'js-beautify';
  18 +
  19 +const js_beautify = beautify.js;
  20 +
  21 +/*@ngInject*/
  22 +export default function ExtensionDialogController($scope, $mdDialog, $translate, isAdd, allExtensions, entityId, entityType, extension, types, attributeService) {
  23 +
  24 + var vm = this;
  25 +
  26 + vm.types = types;
  27 + vm.isAdd = isAdd;
  28 + vm.entityType = entityType;
  29 + vm.entityId = entityId;
  30 + vm.allExtensions = allExtensions;
  31 +
  32 + vm.configuration = {};
  33 + vm.newExtension = {id:"",type:"",configuration:vm.configuration};
  34 +
  35 + if(!vm.isAdd) {
  36 + vm.newExtension = angular.copy(extension);
  37 + vm.configuration = vm.newExtension.configuration;
  38 + editTransformers(vm.newExtension);
  39 + }
  40 +
  41 + vm.cancel = cancel;
  42 + vm.save = save;
  43 +
  44 + function cancel() {
  45 + $mdDialog.cancel();
  46 + }
  47 + function save() {
  48 + saveTransformers();
  49 + if(vm.isAdd) {
  50 + vm.allExtensions.push(vm.newExtension);
  51 + } else {
  52 + var index = vm.allExtensions.indexOf(extension);
  53 + if(index > -1) {
  54 + vm.allExtensions[index] = vm.newExtension;
  55 + }
  56 + }
  57 +
  58 + var editedValue = angular.toJson(vm.allExtensions);
  59 +
  60 + attributeService.saveEntityAttributes(vm.entityType, vm.entityId, types.attributesScope.shared.value, [{key:"configuration", value:editedValue}]).then(
  61 + function success() {
  62 + $scope.theForm.$setPristine();
  63 + $mdDialog.hide();
  64 + }
  65 + );
  66 + }
  67 +
  68 + vm.validateId = function() {
  69 + var coincidenceArray = vm.allExtensions.filter(function(ext) {
  70 + return ext.id == vm.newExtension.id;
  71 + });
  72 + if(coincidenceArray.length) {
  73 + if(!vm.isAdd) {
  74 + if(coincidenceArray[0].id == extension.id) {
  75 + $scope.theForm.extensionId.$setValidity('uniqueIdValidation', true);
  76 + } else {
  77 + $scope.theForm.extensionId.$setValidity('uniqueIdValidation', false);
  78 + }
  79 + } else {
  80 + $scope.theForm.extensionId.$setValidity('uniqueIdValidation', false);
  81 + }
  82 + } else {
  83 + $scope.theForm.extensionId.$setValidity('uniqueIdValidation', true);
  84 + }
  85 + }
  86 +
  87 + function saveTransformers() {
  88 + var config = vm.newExtension.configuration.converterConfigurations;
  89 + if(vm.newExtension.type == types.extensionType.http) {
  90 + for(let i=0;i<config.length;i++) {
  91 + for(let j=0;j<config[i].converters.length;j++){
  92 + for(let k=0;k<config[i].converters[j].attributes.length;k++){
  93 + if(config[i].converters[j].attributes[k].transformerType == "toDouble"){
  94 + config[i].converters[j].attributes[k].transformer = {type: "intToDouble"};
  95 + }
  96 + delete config[i].converters[j].attributes[k].transformerType;
  97 + }
  98 + for(let l=0;l<config[i].converters[j].timeseries.length;l++) {
  99 + if(config[i].converters[j].timeseries[l].transformerType == "toDouble"){
  100 + config[i].converters[j].timeseries[l].transformer = {type: "intToDouble"};
  101 + }
  102 + delete config[i].converters[j].timeseries[l].transformerType;
  103 + }
  104 + }
  105 + }
  106 + }
  107 + }
  108 +
  109 + function editTransformers(extension) {
  110 + var config = extension.configuration.converterConfigurations;
  111 + if(extension.type == types.extensionType.http) {
  112 + for(let i=0;i<config.length;i++) {
  113 + for(let j=0;j<config[i].converters.length;j++){
  114 + for(let k=0;k<config[i].converters[j].attributes.length;k++){
  115 + if(config[i].converters[j].attributes[k].transformer){
  116 + if(config[i].converters[j].attributes[k].transformer.type == "intToDouble"){
  117 + config[i].converters[j].attributes[k].transformerType = "toDouble";
  118 + } else {
  119 + config[i].converters[j].attributes[k].transformerType = "custom";
  120 + config[i].converters[j].attributes[k].transformer = js_beautify(config[i].converters[j].attributes[k].transformer, {indent_size: 4});
  121 + }
  122 + }
  123 + }
  124 + for(let l=0;l<config[i].converters[j].timeseries.length;l++) {
  125 + if(config[i].converters[j].timeseries[l].transformer){
  126 + if(config[i].converters[j].timeseries[l].transformer.type == "intToDouble"){
  127 + config[i].converters[j].timeseries[l].transformerType = "toDouble";
  128 + } else {
  129 + config[i].converters[j].timeseries[l].transformerType = "custom";
  130 + config[i].converters[j].timeseries[l].transformer = js_beautify(config[i].converters[j].timeseries[l].transformer, {indent_size: 4});
  131 + }
  132 + }
  133 + }
  134 + }
  135 + }
  136 + }
  137 + }
  138 +}
  139 +
  140 +/*@ngInject*/
  141 +export function ParseToNull() {
  142 + var linker = function (scope, elem, attrs, ngModel) {
  143 + ngModel.$parsers.push(function(value) {
  144 + if(value === "") {
  145 + return null;
  146 + }
  147 + return value;
  148 + })
  149 + };
  150 + return {
  151 + restrict: "A",
  152 + link: linker,
  153 + require: "ngModel"
  154 + }
  155 +}
\ No newline at end of file
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 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 aria-label="{{ (vm.isAdd ? 'extension.add' : 'extension.edit' ) | translate }}" style="min-width: 1000px;">
  19 + <form name="theForm" ng-submit="vm.save()">
  20 + <md-toolbar>
  21 + <div class="md-toolbar-tools">
  22 + <h2 translate>{{ vm.isAdd ? 'extension.add' : 'extension.edit'}}</h2>
  23 + <span flex></span>
  24 + <md-button class="md-icon-button" ng-click="vm.cancel()">
  25 + <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
  26 + </md-button>
  27 + </div>
  28 + </md-toolbar>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  31 + <md-dialog-content>
  32 + <div class="md-dialog-content">
  33 + <md-content class="md-padding" layout="column">
  34 + <fieldset ng-disabled="loading">
  35 + <section flex layout="row">
  36 + <md-input-container flex="60" class="md-block">
  37 + <label translate>extension.extension-id</label>
  38 + <input required name="extensionId" ng-model="vm.newExtension.id" ng-change="vm.validateId()">
  39 + <div ng-messages="theForm.extensionId.$error">
  40 + <div translate ng-message="required">extension.id-required</div>
  41 + <div translate ng-message="uniqueIdValidation">extension.unique-id-required</div>
  42 + </div>
  43 + </md-input-container>
  44 + <md-input-container flex="40" class="md-block">
  45 + <label translate>extension.extension-type</label>
  46 + <md-select ng-disabled="!vm.isAdd" required name="extensionType" ng-model="vm.newExtension.type">
  47 + <md-option ng-repeat="(key,value) in vm.types.extensionType" ng-value="value">
  48 + {{value}}
  49 + </md-option>
  50 + </md-select>
  51 + <div ng-messages="theForm.extensionType.$error">
  52 + <div translate ng-message="required">extension.type-required</div>
  53 + </div>
  54 + </md-input-container>
  55 + </section>
  56 +
  57 + <div tb-extension-form-http config="vm.configuration" is-add="vm.isAdd" ng-if="vm.newExtension.type && vm.newExtension.type == vm.types.extensionType.http"></div>
  58 +
  59 + </fieldset>
  60 +
  61 + <!--<div>{{vm.newExtension}}</div>-->
  62 + </md-content>
  63 + </div>
  64 + </md-dialog-content>
  65 + <md-dialog-actions layout="row">
  66 + <span flex></span>
  67 + <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
  68 + class="md-raised md-primary">
  69 + {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
  70 + </md-button>
  71 + <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}
  72 + </md-button>
  73 + </md-dialog-actions>
  74 + </form>
  75 +</md-dialog>
\ No newline at end of file
... ...
  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 'angular-material-data-table/dist/md-data-table.min.css';
  18 +import './extension-table.scss';
  19 +
  20 +/* eslint-disable import/no-unresolved, import/default */
  21 +
  22 +import extensionTableTemplate from './extension-table.tpl.html';
  23 +import extensionDialogTemplate from './extension-dialog.tpl.html';
  24 +
  25 +/* eslint-enable import/no-unresolved, import/default */
  26 +
  27 +import ExtensionDialogController from './extension-dialog.controller'
  28 +
  29 +/*@ngInject*/
  30 +export default function ExtensionTableDirective() {
  31 + return {
  32 + restrict: "E",
  33 + scope: true,
  34 + bindToController: {
  35 + entityId: '=',
  36 + entityType: '@'
  37 + },
  38 + controller: ExtensionTableController,
  39 + controllerAs: 'vm',
  40 + templateUrl: extensionTableTemplate
  41 + };
  42 +}
  43 +
  44 +/*@ngInject*/
  45 +function ExtensionTableController($scope, $filter, $document, $translate, types, $mdDialog, attributeService) {
  46 +
  47 + let vm = this;
  48 +
  49 + vm.extensions = [];
  50 + vm.allExtensions = [];
  51 + vm.selectedExtensions = [];
  52 + vm.extensionsCount = 0;
  53 +
  54 + vm.query = {
  55 + order: 'id',
  56 + limit: 5,
  57 + page: 1,
  58 + search: null
  59 + };
  60 +
  61 + vm.enterFilterMode = enterFilterMode;
  62 + vm.exitFilterMode = exitFilterMode;
  63 + vm.onReorder = onReorder;
  64 + vm.onPaginate = onPaginate;
  65 + vm.addExtension = addExtension;
  66 + vm.editExtension = editExtension;
  67 + vm.deleteExtension = deleteExtension;
  68 + vm.deleteExtensions = deleteExtensions;
  69 + vm.reloadExtensions = reloadExtensions;
  70 + vm.updateExtensions = updateExtensions;
  71 +
  72 +
  73 + $scope.$watch("vm.entityId", function(newVal) {
  74 + if (newVal) {
  75 + reloadExtensions();
  76 + }
  77 + });
  78 +
  79 + $scope.$watch("vm.query.search", function(newVal, prevVal) {
  80 + if (!angular.equals(newVal, prevVal) && vm.query.search != null) {
  81 + updateExtensions();
  82 + }
  83 + });
  84 +
  85 + function enterFilterMode() {
  86 + vm.query.search = '';
  87 + }
  88 +
  89 + function exitFilterMode() {
  90 + vm.query.search = null;
  91 + updateExtensions();
  92 + }
  93 +
  94 + function onReorder() {
  95 + updateExtensions();
  96 + }
  97 +
  98 + function onPaginate() {
  99 + updateExtensions();
  100 + }
  101 +
  102 + function addExtension($event) {
  103 + if ($event) {
  104 + $event.stopPropagation();
  105 + }
  106 + openExtensionDialog($event);
  107 + }
  108 +
  109 + function editExtension($event, extension) {
  110 + if ($event) {
  111 + $event.stopPropagation();
  112 + }
  113 + openExtensionDialog($event, extension);
  114 + }
  115 +
  116 + function openExtensionDialog($event, extension) {
  117 + if ($event) {
  118 + $event.stopPropagation();
  119 + }
  120 + var isAdd = false;
  121 + if(!extension) {
  122 + isAdd = true;
  123 + }
  124 + $mdDialog.show({
  125 + controller: ExtensionDialogController,
  126 + controllerAs: 'vm',
  127 + templateUrl: extensionDialogTemplate,
  128 + parent: angular.element($document[0].body),
  129 + locals: { isAdd: isAdd,
  130 + allExtensions: vm.allExtensions,
  131 + entityId: vm.entityId,
  132 + entityType: vm.entityType,
  133 + extension: extension},
  134 + bindToController: true,
  135 + targetEvent: $event,
  136 + fullscreen: true,
  137 + skipHide: true
  138 + }).then(function() {
  139 + reloadExtensions();
  140 + }, function () {
  141 + });
  142 + }
  143 +
  144 + function deleteExtension($event, extension) {
  145 + if ($event) {
  146 + $event.stopPropagation();
  147 + }
  148 + if(extension) {
  149 + var title = $translate.instant('extension.delete-extension-title', {extensionId: extension.id});
  150 + var content = $translate.instant('extension.delete-extension-text');
  151 +
  152 + var confirm = $mdDialog.confirm()
  153 + .targetEvent($event)
  154 + .title(title)
  155 + .htmlContent(content)
  156 + .ariaLabel(title)
  157 + .cancel($translate.instant('action.no'))
  158 + .ok($translate.instant('action.yes'));
  159 + $mdDialog.show(confirm).then(function() {
  160 + var editedExtensions = vm.allExtensions.filter(function(ext) {
  161 + return ext.id !== extension.id;
  162 + });
  163 + var editedValue = angular.toJson(editedExtensions);
  164 + attributeService.saveEntityAttributes(vm.entityType, vm.entityId, types.attributesScope.shared.value, [{key:"configuration", value:editedValue}]).then(
  165 + function success() {
  166 + reloadExtensions();
  167 + }
  168 + );
  169 + });
  170 + }
  171 + }
  172 +
  173 + function deleteExtensions($event) {
  174 + if ($event) {
  175 + $event.stopPropagation();
  176 + }
  177 + if (vm.selectedExtensions && vm.selectedExtensions.length > 0) {
  178 + var title = $translate.instant('extension.delete-extensions-title', {count: vm.selectedExtensions.length}, 'messageformat');
  179 + var content = $translate.instant('extension.delete-extensions-text');
  180 +
  181 + var confirm = $mdDialog.confirm()
  182 + .targetEvent($event)
  183 + .title(title)
  184 + .htmlContent(content)
  185 + .ariaLabel(title)
  186 + .cancel($translate.instant('action.no'))
  187 + .ok($translate.instant('action.yes'));
  188 + $mdDialog.show(confirm).then(function () {
  189 + var editedExtensions = angular.copy(vm.allExtensions);
  190 + for (var i = 0; i < vm.selectedExtensions.length; i++) {
  191 + editedExtensions = editedExtensions.filter(function (ext) {
  192 + return ext.id !== vm.selectedExtensions[i].id;
  193 + });
  194 + }
  195 + var editedValue = angular.toJson(editedExtensions);
  196 + attributeService.saveEntityAttributes(vm.entityType, vm.entityId, types.attributesScope.shared.value, [{key:"configuration", value:editedValue}]).then(
  197 + function success() {
  198 + reloadExtensions();
  199 + }
  200 + );
  201 + });
  202 + }
  203 + }
  204 +
  205 + function reloadExtensions() {
  206 + vm.allExtensions.length = 0;
  207 + vm.extensions.length = 0;
  208 + vm.extensionsPromise = attributeService.getEntityAttributesValues(vm.entityType, vm.entityId, types.attributesScope.shared.value, ["configuration"]);
  209 + vm.extensionsPromise.then(
  210 + function success(data) {
  211 + vm.allExtensions = angular.fromJson(data[0].value);
  212 + vm.selectedExtensions = [];
  213 + updateExtensions();
  214 + vm.extensionsPromise = null;
  215 + },
  216 + function fail() {
  217 + vm.extensions = [];
  218 + vm.selectedExtensions = [];
  219 + updateExtensions();
  220 + vm.extensionsPromise = null;
  221 + }
  222 + );
  223 + }
  224 +
  225 + function updateExtensions() {
  226 + vm.selectedExtensions = [];
  227 + var result = $filter('orderBy')(vm.allExtensions, vm.query.order);
  228 + if (vm.query.search != null) {
  229 + result = $filter('filter')(result, function(extension) {
  230 + if(!vm.query.search || (extension.id.indexOf(vm.query.search) != -1) || (extension.type.indexOf(vm.query.search) != -1)) {
  231 + return true;
  232 + }
  233 + return false;
  234 + });
  235 + }
  236 + vm.extensionsCount = result.length;
  237 + var startIndex = vm.query.limit * (vm.query.page - 1);
  238 + vm.extensions = result.slice(startIndex, startIndex + vm.query.limit);
  239 + }
  240 +}
\ No newline at end of file
... ...
  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 +@import '../../scss/constants';
\ No newline at end of file
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 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 +
  19 +<md-content flex class="md-padding tb-absolute-fill tb-data-table" layout="column">
  20 + <div layout="column" class="md-whiteframe-z1">
  21 + <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedExtensions.length
  22 + && vm.query.search === null">
  23 + <div class="md-toolbar-tools">
  24 + <span translate>{{ 'extension.extensions' }}</span>
  25 + <span flex></span>
  26 + <md-button class="md-icon-button" ng-click="vm.addExtension($event)">
  27 + <md-icon>add</md-icon>
  28 + <md-tooltip md-direction="top">
  29 + {{ 'action.add' | translate }}
  30 + </md-tooltip>
  31 + </md-button>
  32 + <md-button class="md-icon-button" ng-click="vm.enterFilterMode()">
  33 + <md-icon>search</md-icon>
  34 + <md-tooltip md-direction="top">
  35 + {{ 'action.search' | translate }}
  36 + </md-tooltip>
  37 + </md-button>
  38 + <md-button class="md-icon-button" ng-click="vm.reloadExtensions()">
  39 + <md-icon>refresh</md-icon>
  40 + <md-tooltip md-direction="top">
  41 + {{ 'action.refresh' | translate }}
  42 + </md-tooltip>
  43 + </md-button>
  44 + </div>
  45 + </md-toolbar>
  46 + <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedExtensions.length
  47 + && vm.query.search != null"">
  48 + <div class="md-toolbar-tools">
  49 + <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}">
  50 + <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon>
  51 + <md-tooltip md-direction="top">
  52 + {{ 'action.search' | translate }}
  53 + </md-tooltip>
  54 + </md-button>
  55 + <md-input-container flex>
  56 + <label>&nbsp;</label>
  57 + <input ng-model="vm.query.search" placeholder="{{ 'common.enter-search' | translate }}"/>
  58 + </md-input-container>
  59 + <md-button class="md-icon-button" aria-label="{{ 'action.back' | translate }}" ng-click="vm.exitFilterMode()">
  60 + <md-icon aria-label="{{ 'action.close' | translate }}" class="material-icons">close</md-icon>
  61 + <md-tooltip md-direction="top">
  62 + {{ 'action.close' | translate }}
  63 + </md-tooltip>
  64 + </md-button>
  65 + </div>
  66 + </md-toolbar>
  67 + <md-toolbar class="md-table-toolbar alternate" ng-show="vm.selectedExtensions.length">
  68 + <div class="md-toolbar-tools">
  69 + <span translate
  70 + translate-values="{count: vm.selectedExtensions.length}"
  71 + translate-interpolation="messageformat">extension.selected-extensions</span>
  72 + <span flex></span>
  73 + <md-button class="md-icon-button" ng-click="vm.deleteExtensions($event)">
  74 + <md-icon>delete</md-icon>
  75 + <md-tooltip md-direction="top">
  76 + {{ 'action.delete' | translate }}
  77 + </md-tooltip>
  78 + </md-button>
  79 + </div>
  80 + </md-toolbar>
  81 + <md-table-container>
  82 + <table md-table md-row-select multiple="" ng-model="vm.selectedExtensions" md-progress="vm.extensionsDeferred.promise">
  83 + <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder">
  84 + <tr md-row>
  85 + <th md-column md-order-by="id"><span translate>extension.id</span></th>
  86 + <th md-column md-order-by="type"><span translate>extension.type</span></th>
  87 + <th md-column><span>&nbsp</span></th>
  88 + </tr>
  89 + </thead>
  90 + <tbody md-body>
  91 + <tr md-row md-select="extension" md-select-id="extension" md-auto-select ng-repeat="extension in vm.extensions">
  92 + <td md-cell>{{ extension.id }}</td>
  93 + <td md-cell>{{ extension.type }}</td>
  94 + <td md-cell class="tb-action-cell">
  95 + <md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" ng-click="vm.editExtension($event, extension)">
  96 + <md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon>
  97 + <md-tooltip md-direction="top">
  98 + {{ 'extension.edit' | translate }}
  99 + </md-tooltip>
  100 + </md-button>
  101 + <md-button class="md-icon-button" aria-label="{{ 'action.delete' | translate }}" ng-click="vm.deleteExtension($event, extension)"> <!-- add click-function -->
  102 + <md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">delete</md-icon>
  103 + <md-tooltip md-direction="top">
  104 + {{ 'extension.delete' | translate }}
  105 + </md-tooltip>
  106 + </md-button>
  107 + </td>
  108 + </tr>
  109 + </tbody>
  110 + </table>
  111 + </md-table-container>
  112 + <md-table-pagination md-limit="vm.query.limit" md-limit-options="[5, 10, 15]"
  113 + md-page="vm.query.page" md-total="{{vm.extensionsCount}}"
  114 + md-on-paginate="vm.onPaginate" md-page-select>
  115 + </md-table-pagination>
  116 + </div>
  117 + <div></div> <!-- div for testing values -->
  118 +</md-content>
\ No newline at end of file
... ...
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +import 'brace/ext/language_tools';
  18 +import 'brace/mode/json';
  19 +import 'brace/theme/github';
  20 +
  21 +import './extension-form.scss';
  22 +
  23 +/* eslint-disable angular/log */
  24 +
  25 +import extensionFormHttpTemplate from './extension-form-http.tpl.html';
  26 +
  27 +/* eslint-enable import/no-unresolved, import/default */
  28 +
  29 +/*@ngInject*/
  30 +export default function ExtensionFormHttpDirective($compile, $templateCache, $translate, types) {
  31 +
  32 + var linker = function(scope, element) {
  33 +
  34 + var template = $templateCache.get(extensionFormHttpTemplate);
  35 + element.html(template);
  36 +
  37 + scope.types = types;
  38 + scope.theForm = scope.$parent.theForm;
  39 +
  40 + scope.extensionCustomTransformerOptions = {
  41 + useWrapMode: false,
  42 + mode: 'json',
  43 + showGutter: true,
  44 + showPrintMargin: true,
  45 + theme: 'github',
  46 + advanced: {
  47 + enableSnippets: true,
  48 + enableBasicAutocompletion: true,
  49 + enableLiveAutocompletion: true
  50 + },
  51 + onLoad: aceOnLoad
  52 + };
  53 +
  54 + if(scope.isAdd) {
  55 + scope.converterConfigs = [];
  56 + scope.config.converterConfigurations = scope.converterConfigs;
  57 + } else {
  58 + scope.converterConfigs = scope.config.converterConfigurations;
  59 + }
  60 +
  61 + scope.updateValidity = function() {
  62 + var valid = scope.converterConfigs && scope.converterConfigs.length > 0;
  63 + scope.theForm.$setValidity('converterConfigs', valid);
  64 + if(scope.converterConfigs.length) {
  65 + for(let i=0;i<scope.converterConfigs.length;i++) {
  66 + if(!scope.converterConfigs[i].converters.length) {
  67 + scope.theForm.$setValidity('converters', false);
  68 + break;
  69 + } else {
  70 + scope.theForm.$setValidity('converters', true);
  71 + }
  72 + }
  73 + }
  74 + };
  75 +
  76 + scope.$watch('converterConfigs', function() {
  77 + scope.updateValidity();
  78 + }, true);
  79 +
  80 + scope.addConverterConfig = function() {
  81 + var newConverterConfig = {converterId:"", converters:[]};
  82 + scope.converterConfigs.push(newConverterConfig);
  83 + }
  84 +
  85 + scope.removeConverterConfig = function(config) {
  86 + var index = scope.converterConfigs.indexOf(config);
  87 + if (index > -1) {
  88 + scope.converterConfigs.splice(index, 1);
  89 + }
  90 + }
  91 +
  92 + scope.addConverter = function(converters) {
  93 + var newConverter = {deviceNameJsonExpression:"", deviceTypeJsonExpression:"", attributes:[], timeseries:[]};
  94 + converters.push(newConverter);
  95 + }
  96 +
  97 + scope.removeConverter = function(converter, converters) {
  98 + var index = converters.indexOf(converter);
  99 + if (index > -1) {
  100 + converters.splice(index, 1);
  101 + }
  102 + }
  103 +
  104 + scope.addAttribute = function(attributes) {
  105 + var newAttribute = {type:"", key:"", value:""};
  106 + attributes.push(newAttribute);
  107 + }
  108 +
  109 + scope.removeAttribute = function(attribute, attributes) {
  110 + var index = attributes.indexOf(attribute);
  111 + if (index > -1) {
  112 + attributes.splice(index, 1);
  113 + }
  114 + }
  115 +
  116 + scope.transformerTypeChange = function(attribute) {
  117 + attribute.transformer = "";
  118 + }
  119 +
  120 + function aceOnLoad(_ace) {
  121 + _ace.$blockScrolling = 1;
  122 + _ace.on("change", function(){
  123 + var aceValue = _ace.getSession().getDocument().getValue();
  124 + var valid = true;
  125 + if(!aceValue && !aceValue.length) {
  126 + valid = false;
  127 + } else {
  128 + try {
  129 + angular.fromJson(aceValue);
  130 + } catch(e) {
  131 + valid = false;
  132 + }
  133 + }
  134 + scope.theForm.$setValidity('transformerRequired', valid);
  135 + });
  136 + }
  137 +
  138 + $compile(element.contents())(scope);
  139 + }
  140 +
  141 + return {
  142 + restrict: "A",
  143 + link: linker,
  144 + scope: {
  145 + config: "=",
  146 + isAdd: "="
  147 + }
  148 + }
  149 +}
\ No newline at end of file
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 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-card class="extension-form">
  19 + <md-card-title>
  20 + <md-card-title-text>
  21 + <span translate class="md-headline">extension.configuration</span>
  22 + </md-card-title-text>
  23 + </md-card-title>
  24 + <md-card-content>
  25 + <v-accordion id="http-converter-configs-accordion" class="vAccordion--default">
  26 + <v-pane id="http-converters-pane" expanded="isAdd">
  27 + <v-pane-header>
  28 + {{ 'extension.converter-configurations' | translate }}
  29 + </v-pane-header>
  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">
  35 + <ol class="list-group">
  36 + <li class="list-group-item" ng-repeat="(configIndex,config) in converterConfigs">
  37 + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeConverterConfig(config)">
  38 + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon>
  39 + <md-tooltip md-direction="top">
  40 + {{ 'action.remove' | translate }}
  41 + </md-tooltip>
  42 + </md-button>
  43 + <md-card>
  44 + <md-card-content>
  45 +
  46 + <md-input-container class="md-block">
  47 + <label translate>extension.converter-id</label>
  48 + <input required name="httpConverterId_{{configIndex}}" ng-model="config.converterId">
  49 + <div ng-messages="theForm['httpConverterId_' + configIndex].$error">
  50 + <div translate ng-message="required">extension.converter-id-required</div>
  51 + </div>
  52 + </md-input-container>
  53 + <md-input-container class="md-block">
  54 + <label translate>extension.token</label>
  55 + <input name="httpToken" ng-model="config.token" parse-to-null>
  56 + </md-input-container>
  57 + <v-accordion id="http-converters-accordion" class="vAccordion--default">
  58 + <v-pane id="http-converters-pane">
  59 + <v-pane-header>
  60 + {{ 'extension.converters' | translate }}
  61 + </v-pane-header>
  62 + <v-pane-content>
  63 + <div ng-if="config.converters.length === 0">
  64 + <span translate layout-align="center center" class="tb-prompt">extension.add-converter-prompt</span>
  65 + </div>
  66 + <div ng-if="config.converters.length > 0">
  67 + <ol class="list-group">
  68 + <li class="list-group-item" ng-repeat="(converterIndex,converter) in config.converters">
  69 + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeConverter(converter, config.converters)">
  70 + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon>
  71 + <md-tooltip md-direction="top">
  72 + {{ 'action.remove' | translate }}
  73 + </md-tooltip>
  74 + </md-button>
  75 + <md-card>
  76 + <md-card-content>
  77 + <md-input-container class="md-block">
  78 + <label translate>extension.device-name-expression</label>
  79 + <input required name="httpDeviceNameExp_{{configIndex}}{{converterIndex}}" ng-model="converter.deviceNameJsonExpression">
  80 + <div ng-messages="theForm['httpDeviceNameExp_' + configIndex + converterIndex].$error">
  81 + <div translate ng-message="required">extension.device-name-expression-required</div>
  82 + </div>
  83 + </md-input-container>
  84 + <md-input-container class="md-block">
  85 + <label translate>extension.device-type-expression</label>
  86 + <input required name="httpDeviceTypeExp_{{configIndex}}{{converterIndex}}" ng-model="converter.deviceTypeJsonExpression">
  87 + <div ng-messages="theForm['httpDeviceTypeExp_' + configIndex + converterIndex].$error">
  88 + <div translate ng-message="required">extension.device-type-expression-required</div>
  89 + </div>
  90 + </md-input-container>
  91 +
  92 + <v-accordion id="http-attributes-accordion" class="vAccordion--default">
  93 + <v-pane id="http-attributes-pane">
  94 + <v-pane-header>
  95 + {{ 'extension.attributes' | translate }}
  96 + </v-pane-header>
  97 + <v-pane-content>
  98 + <div ng-if="converter.attributes.length > 0">
  99 + <ol class="list-group">
  100 + <li class="list-group-item" ng-repeat="(attributeIndex, attribute) in converter.attributes">
  101 + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeAttribute(attribute, converter.attributes)">
  102 + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon>
  103 + <md-tooltip md-direction="top">
  104 + {{ 'action.remove' | translate }}
  105 + </md-tooltip>
  106 + </md-button>
  107 + <md-card>
  108 + <md-card-content>
  109 + <section flex layout="row">
  110 + <md-input-container flex="60" class="md-block">
  111 + <label translate>extension.key</label>
  112 + <input required name="httpAttributeKey_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.key">
  113 + <div ng-messages="theForm['httpAttributeKey_' + configIndex + converterIndex + attributeIndex].$error">
  114 + <div translate ng-message="required">extension.required-key</div>
  115 + </div>
  116 + </md-input-container>
  117 + <md-input-container flex="40" class="md-block">
  118 + <label translate>extension.type</label>
  119 + <md-select required name="httpAttributeType_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.type">
  120 + <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType">
  121 + {{attrTypeValue | translate}}
  122 + </md-option>
  123 + </md-select>
  124 + <div ng-messages="theForm['httpAttributeType_' + configIndex + converterIndex + attributeIndex].$error">
  125 + <div translate ng-message="required">extension.required-type</div>
  126 + </div>
  127 + </md-input-container>
  128 + </section>
  129 + <section flex layout="row">
  130 + <md-input-container flex="60" class="md-block">
  131 + <label translate>extension.value</label>
  132 + <input required name="httpAttributeValue_{{configIndex}}{{converterIndex}}{{attributeIndex}}" ng-model="attribute.value">
  133 + <div ng-messages="theForm['httpAttributeValue_' + configIndex + converterIndex + attributeIndex].$error">
  134 + <div translate ng-message="required">extension.required-value</div>
  135 + </div>
  136 + </md-input-container>
  137 +
  138 +
  139 + <md-input-container flex="40" class="md-block">
  140 + <label translate>extension.transformer</label>
  141 + <md-select name="httpAttributeTransformer" ng-model="attribute.transformerType" ng-change="transformerTypeChange(attribute)">
  142 + <md-option ng-repeat="(transformerType, value) in types.extensionTransformerType" ng-value="transformerType">
  143 + {{value | translate}}
  144 + </md-option>
  145 + </md-select>
  146 + </md-input-container>
  147 + </section>
  148 +
  149 + <div ng-if='attribute.transformerType == "custom"'>
  150 + <div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>extension.transformer-json</div>
  151 + <div flex class="tb-extension-custom-transformer-panel">
  152 + <div flex class="tb-extension-custom-transformer"
  153 + ui-ace="extensionCustomTransformerOptions"
  154 + ng-model="attribute.transformer"
  155 + name="test_{{$index}}">
  156 + </div>
  157 + </div>
  158 + <div class="tb-error-messages" ng-messages="theForm['test_' + $index].$error" role="alert">
  159 + <div ng-message="transformerRequired" class="tb-error-message">Ну привет :)</div>
  160 + </div>
  161 + </div>
  162 +
  163 +
  164 + </md-card-content>
  165 + </md-card>
  166 + </li>
  167 + </ol>
  168 + </div>
  169 + <div flex layout="row" layout-align="start center">
  170 + <md-button class="md-primary md-raised"
  171 + ng-click="addAttribute(converter.attributes)" aria-label="{{ 'action.add' | translate }}">
  172 + <md-tooltip md-direction="top">
  173 + {{ 'extension.add-attribute' | translate }}
  174 + </md-tooltip>
  175 + <md-icon class="material-icons">add</md-icon>
  176 + <span translate>action.add</span>
  177 + </md-button>
  178 + </div>
  179 + </v-pane-content>
  180 + </v-pane>
  181 + </v-accordion>
  182 +
  183 +
  184 + <v-accordion id="http-timeseries-accordion" class="vAccordion--default">
  185 + <v-pane id="http-timeseries-pane">
  186 + <v-pane-header>
  187 + {{ 'extension.timeseries' | translate }}
  188 + </v-pane-header>
  189 + <v-pane-content>
  190 + <div ng-if="converter.timeseries.length > 0">
  191 + <ol class="list-group">
  192 + <li class="list-group-item" ng-repeat="(timeseriesIndex, timeseries) in converter.timeseries">
  193 + <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeAttribute(timeseries, converter.timeseries)">
  194 + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon>
  195 + <md-tooltip md-direction="top">
  196 + {{ 'action.remove' | translate }}
  197 + </md-tooltip>
  198 + </md-button>
  199 + <md-card>
  200 + <md-card-content>
  201 + <section flex layout="row">
  202 + <md-input-container flex="60" class="md-block">
  203 + <label translate>extension.key</label>
  204 + <input required name="httpTimeseriesKey_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.key">
  205 + <div ng-messages="theForm['httpTimeseriesKey_' + configIndex + converterIndex + timeseriesIndex].$error">
  206 + <div translate ng-message="required">extension.required-key</div>
  207 + </div>
  208 + </md-input-container>
  209 + <md-input-container flex="40" class="md-block">
  210 + <label translate>extension.type</label>
  211 + <md-select required name="httpTimeseriesType_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.type">
  212 + <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType" ng-value="attrType">
  213 + {{attrTypeValue | translate}}
  214 + </md-option>
  215 + </md-select>
  216 + <div ng-messages="theForm['httpTimeseriesType_' + configIndex + converterIndex + timeseriesIndex].$error">
  217 + <div translate ng-message="required">extension.required-type</div>
  218 + </div>
  219 + </md-input-container>
  220 + </section>
  221 + <section flex layout="row">
  222 + <md-input-container flex="60" class="md-block">
  223 + <label translate>extension.value</label>
  224 + <input required name="httpTimeseriesValue_{{configIndex}}{{converterIndex}}{{timeseriesIndex}}" ng-model="timeseries.value">
  225 + <div ng-messages="theForm['httpTimeseriesValue_' + configIndex + converterIndex + timeseriesIndex].$error">
  226 + <div translate ng-message="required">extension.required-value</div>
  227 + </div>
  228 + </md-input-container>
  229 +
  230 +
  231 + <md-input-container flex="40" class="md-block">
  232 + <label translate>extension.transformer</label>
  233 + <md-select name="httpTimeseriesTransformer" ng-model="timeseries.transformerType" ng-change="transformerTypeChange(timeseries)">
  234 + <md-option ng-repeat="(transformerType, value) in types.extensionTransformerType" ng-value="transformerType">
  235 + {{value | translate}}
  236 + </md-option>
  237 + </md-select>
  238 + </md-input-container>
  239 + </section>
  240 +
  241 + <div ng-if='timeseries.transformerType == "custom"'>
  242 + <div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>extension.transformer-json</div>
  243 + <div flex class="tb-extension-custom-transformer-panel">
  244 + <div flex class="tb-extension-custom-transformer"
  245 + ui-ace="extensionCustomTransformerOptions"
  246 + ng-model="timeseries.transformer">
  247 + </div>
  248 + </div>
  249 + </div>
  250 +
  251 +
  252 + </md-card-content>
  253 + </md-card>
  254 + </li>
  255 + </ol>
  256 + </div>
  257 + <div flex layout="row" layout-align="start center">
  258 + <md-button class="md-primary md-raised"
  259 + ng-click="addAttribute(converter.timeseries)" aria-label="{{ 'action.add' | translate }}">
  260 + <md-tooltip md-direction="top">
  261 + {{ 'extension.add-timeseries' | translate }}
  262 + </md-tooltip>
  263 + <md-icon class="material-icons">add</md-icon>
  264 + <span translate>action.add</span>
  265 + </md-button>
  266 + </div>
  267 + </v-pane-content>
  268 + </v-pane>
  269 + </v-accordion>
  270 + </md-card-content>
  271 + </md-card>
  272 + </li>
  273 + </ol>
  274 + </div>
  275 + <div flex layout="row" layout-align="start center">
  276 + <md-button class="md-primary md-raised"
  277 + ng-click="addConverter(config.converters)" aria-label="{{ 'action.add' | translate }}">
  278 + <md-tooltip md-direction="top">
  279 + {{ 'extension.add-converter' | translate }}
  280 + </md-tooltip>
  281 + <md-icon class="material-icons">add</md-icon>
  282 + <span translate>action.add</span>
  283 + </md-button>
  284 + </div>
  285 + </v-pane-content>
  286 + </v-pane>
  287 + </v-accordion>
  288 +
  289 + </md-card-content>
  290 + </md-card>
  291 + </li>
  292 + </ol>
  293 + </div>
  294 + <div flex layout="row" layout-align="start center">
  295 + <md-button class="md-primary md-raised"
  296 + ng-click="addConverterConfig()" aria-label="{{ 'action.add' | translate }}">
  297 + <md-tooltip md-direction="top">
  298 + {{ 'extension.add-config' | translate }}
  299 + </md-tooltip>
  300 + <md-icon class="material-icons">add</md-icon>
  301 + <span translate>action.add</span>
  302 + </md-button>
  303 + </div>
  304 + </v-pane-content>
  305 + </v-pane>
  306 + </v-accordion>
  307 + <!--{{config}}-->
  308 + </md-card-content>
  309 +</md-card>
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 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 +<div>MQTT</div>
\ No newline at end of file
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 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 +<div>OPC UA</div>
\ No newline at end of file
... ...
  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 +.extension-form {
  18 + li > .md-button {
  19 + color: rgba(0, 0, 0, 0.7);
  20 + margin: 0;
  21 + }
  22 + .vAccordion--default {
  23 + margin-top: 0;
  24 + padding-left: 3px;
  25 + }
  26 +}
  27 +
  28 +.tb-extension-custom-transformer-panel {
  29 + margin-left: 15px;
  30 + border: 1px solid #C0C0C0;
  31 + height: 100%;
  32 + .tb-extension-custom-transformer {
  33 + min-width: 600px;
  34 + min-height: 200px;
  35 + width: 100%;
  36 + height: 100%;
  37 + }
  38 + .ace_text-input {
  39 + position:absolute!important
  40 + }
  41 +}
\ No newline at end of file
... ...
  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 ExtensionTableDirective from './extension-table.directive';
  18 +import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive';
  19 +import {ParseToNull} from './extension-dialog.controller';
  20 +
  21 +export default angular.module('thingsboard.extension', [])
  22 + .directive('tbExtensionTable', ExtensionTableDirective)
  23 + .directive('tbExtensionFormHttp', ExtensionFormHttpDirective)
  24 + .directive('parseToNull', ParseToNull)
  25 + .name;
\ No newline at end of file
... ...
... ... @@ -35,6 +35,7 @@ import thingsboardUserMenu from './user-menu.directive';
35 35 import thingsboardEntity from '../entity';
36 36 import thingsboardEvent from '../event';
37 37 import thingsboardAlarm from '../alarm';
  38 +import thingsboardExtension from '../extension';
38 39 import thingsboardTenant from '../tenant';
39 40 import thingsboardCustomer from '../customer';
40 41 import thingsboardUser from '../user';
... ... @@ -66,6 +67,7 @@ export default angular.module('thingsboard.home', [
66 67 thingsboardEntity,
67 68 thingsboardEvent,
68 69 thingsboardAlarm,
  70 + thingsboardExtension,
69 71 thingsboardTenant,
70 72 thingsboardCustomer,
71 73 thingsboardUser,
... ...
... ... @@ -729,6 +729,51 @@ export default angular.module('thingsboard.locale', [])
729 729 "messages-processed": "Messages processed",
730 730 "errors-occurred": "Errors occurred"
731 731 },
  732 + "extension": {
  733 + "extensions": "Extensions",
  734 + "selected-extensions": "{ count, select, 1 {1 extension} other {# extensions} } selected",
  735 + "type": "Type",
  736 + "key": "Key",
  737 + "value": "Value",
  738 + "id": "Id",
  739 + "extension-id": "Extension id",
  740 + "extension-type": "Extension type",
  741 + "transformer-json": "JSON*",
  742 + "id-required": "Extension id is required.",
  743 + "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",
  749 + "add": "Add extension",
  750 + "edit": "Edit extension",
  751 + "delete-extension-title": "Are you sure you want to delete the extension '{{extensionId}}'?",
  752 + "delete-extension-text": "Be careful, after the confirmation the extension and all related data will become unrecoverable.",
  753 + "delete-extensions-title": "Are you sure you want to delete { count, select, 1 {1 extension} other {# extensions} }?",
  754 + "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.",
  755 + "converters": "Converters",
  756 + "converter-id": "Converter id",
  757 + "converter-id-required": "Converter id is required.",
  758 + "configuration": "Configuration",
  759 + "converter-configurations": "Converter configurations",
  760 + "token": "Security token",
  761 + "add-converter": "Add converter",
  762 + "add-converter-prompt": "Please add converter",
  763 + "add-config": "Add converter configuration",
  764 + "add-config-prompt": "Please add converter configuration",
  765 + "device-name-expression": "Device name expression",
  766 + "device-name-expression-required": "Device name expression is required.",
  767 + "device-type-expression": "Device type expression",
  768 + "device-type-expression-required": "Device type expression is required.",
  769 + "custom": "Custom",
  770 + "to-double": "To Double",
  771 + "transformer": "Transformer",
  772 + "attributes": "Attributes",
  773 + "add-attribute": "Add attribute",
  774 + "timeseries": "Timeseries",
  775 + "add-timeseries": "Add timeseries",
  776 + },
732 777 "fullscreen": {
733 778 "expand": "Expand to fullscreen",
734 779 "exit": "Exit fullscreen",
... ... @@ -1071,7 +1116,8 @@ export default angular.module('thingsboard.locale', [])
1071 1116 "boolean": "Boolean",
1072 1117 "boolean-value": "Boolean value",
1073 1118 "false": "False",
1074   - "true": "True"
  1119 + "true": "True",
  1120 + "long": "Long"
1075 1121 },
1076 1122 "widget": {
1077 1123 "widget-library": "Widgets Library",
... ...