UI: gateway dashboard bug-fixes and improvements
This commit is contained in:
parent
214f9d178e
commit
355acfc30a
@ -136,7 +136,7 @@
|
||||
{
|
||||
"name": "active_connectors",
|
||||
"type": "attribute",
|
||||
"label": "Active Connectors",
|
||||
"label": "Enabled Connectors",
|
||||
"color": "#3f51b5",
|
||||
"settings": {
|
||||
"columnWidth": "20%",
|
||||
@ -248,7 +248,7 @@
|
||||
"type": "customPretty",
|
||||
"customHtml": "<form #addEntityForm=\"ngForm\" [formGroup]=\"addEntityFormGroup\"\r\n (ngSubmit)=\"save($event)\" class=\"add-entity-form\">\r\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\r\n <h2>Add gateway</h2>\r\n <span fxFlex></span>\r\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\r\n <mat-icon class=\"material-icons\">close</mat-icon>\r\n </button>\r\n </mat-toolbar>\r\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\r\n </mat-progress-bar>\r\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\r\n <div mat-dialog-content fxLayout=\"column\">\r\n <div fxLayout=\"row\" fxLayoutGap=\"8px\" fxLayout.xs=\"column\" fxLayoutGap.xs=\"0\">\r\n <mat-form-field fxFlex class=\"mat-block\">\r\n <mat-label>Name</mat-label>\r\n <input matInput formControlName=\"entityName\" required>\r\n <mat-error *ngIf=\"addEntityFormGroup.get('entityName').hasError('required')\">\r\n Gateway name is required.\r\n </mat-error>\r\n </mat-form-field>\r\n </div>\r\n <div fxLayout=\"row\" fxLayoutGap=\"8px\" fxLayout.xs=\"column\" fxLayoutGap.xs=\"0\">\r\n <tb-entity-subtype-autocomplete\r\n fxFlex\r\n class=\"mat-block\"\r\n formControlName=\"type\"\r\n [required]=\"true\"\r\n [entityType]=\"'DEVICE'\"\r\n ></tb-entity-subtype-autocomplete>\r\n </div>\r\n </div>\r\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\r\n <button mat-button color=\"primary\"\r\n type=\"button\"\r\n [disabled]=\"(isLoading$ | async)\"\r\n (click)=\"cancel()\" cdkFocusInitial>\r\n Cancel\r\n </button>\r\n <button mat-button mat-raised-button color=\"primary\"\r\n type=\"submit\"\r\n [disabled]=\"(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty\">\r\n Create\r\n </button>\r\n </div>\r\n</form>\r\n",
|
||||
"customCss": ".add-entity-form {\r\n min-width: 400px !important;\r\n}\r\n\r\n.add-entity-form .boolean-value-input {\r\n padding-left: 5px;\r\n}\r\n\r\n.add-entity-form .boolean-value-input .checkbox-label {\r\n margin-bottom: 8px;\r\n color: rgba(0,0,0,0.54);\r\n font-size: 12px;\r\n}\r\n\r\n.relations-list .header {\r\n padding-right: 5px;\r\n padding-bottom: 5px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .header .cell {\r\n padding-right: 5px;\r\n padding-left: 5px;\r\n font-size: 12px;\r\n font-weight: 700;\r\n color: rgba(0, 0, 0, .54);\r\n white-space: nowrap;\r\n}\r\n\r\n.relations-list .mat-form-field-infix {\r\n width: auto !important;\r\n}\r\n\r\n.relations-list .body {\r\n padding-right: 5px;\r\n padding-bottom: 15px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .body .row {\r\n padding-top: 5px;\r\n}\r\n\r\n.relations-list .body .cell {\r\n padding-right: 5px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .body .md-button {\r\n margin: 0;\r\n}\r\n\r\n",
|
||||
"customFunction": "let $injector = widgetContext.$scope.$injector;\r\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\r\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\r\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\r\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\r\nlet entityRelationService = $injector.get(widgetContext.servicesMap.get('entityRelationService'));\r\nlet userSettingsService = $injector.get(widgetContext.servicesMap.get('userSettingsService'));\r\n\r\nopenAddEntityDialog();\r\n\r\nfunction openAddEntityDialog() {\r\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\r\n}\r\n\r\nfunction AddEntityDialogController(instance) {\r\n let vm = instance;\r\n let userSettings;\r\n userSettingsService.loadUserSettings().subscribe(settings=> {\r\n userSettings = settings;\r\n if (!userSettings.createdGatewaysCount) userSettings.createdGatewaysCount = 0;\r\n });\r\n \r\n\r\n vm.addEntityFormGroup = vm.fb.group({\r\n entityName: ['', [vm.validators.required]],\r\n entityType: ['DEVICE'],\r\n entityLabel: [''],\r\n type: ['', [vm.validators.required]],\r\n });\r\n\r\n vm.cancel = function() {\r\n vm.dialogRef.close(null);\r\n };\r\n\r\n\r\n vm.save = function($event) {\r\n vm.addEntityFormGroup.markAsPristine();\r\n saveEntityObservable().subscribe(\r\n function (device) {\r\n widgetContext.updateAliases();\r\n userSettingsService.putUserSettings({ createdGatewaysCount: ++userSettings.createdGatewaysCount }).subscribe(_=>{\r\n });\r\n vm.dialogRef.close(null);\r\n openCommandDialog(device, $event);\r\n }\r\n );\r\n };\r\n \r\n function openCommandDialog(device, $event) {\r\n vm.device = device;\r\n let openCommandAction = widgetContext.actionsApi.getActionDescriptors(\"actionCellButton\").find(action => action.name == \"Docker commands\");\r\n widgetContext.actionsApi.handleWidgetAction($event, openCommandAction, device.id, device.name, {newDevice: true});\r\n setTimeout(function() {\r\n document.querySelector(\".dashboard-state-dialog .mat-mdc-button-touch-target\").addEventListener('click', goToConfigState);\r\n document.querySelector(\".dashboard-state-dialog .mat-mdc-button.mat-primary\").addEventListener('click', goToConfigState);\r\n }, 500);\r\n }\r\n\r\n \r\n function goToConfigState() {\r\n document.querySelector(\".dashboard-state-dialog .mat-mdc-button-touch-target\").removeEventListener('click', goToConfigState);\r\n document.querySelector(\".dashboard-state-dialog .mat-mdc-button.mat-primary\").removeEventListener('click', goToConfigState);\r\n const stateParams = {};\r\n stateParams.entityId = vm.device.id;\r\n stateParams.entityName = vm.device.name;\r\n const newStateParams = {\r\n targetEntityParamName: 'default',\r\n new_gateway: {\r\n entityId: vm.device.id,\r\n entityName: vm.device.name\r\n }\r\n }\r\n const params = {...stateParams, ...newStateParams};\r\n widgetContext.stateController.openState('gateway_details', params, false);\r\n }\r\n\r\n function saveEntityObservable() {\r\n const formValues = vm.addEntityFormGroup.value;\r\n let entity = {\r\n name: formValues.entityName,\r\n type: formValues.type,\r\n label: formValues.entityLabel,\r\n additionalInfo: {\r\n gateway: true\r\n }\r\n };\r\n return deviceService.saveDevice(entity);\r\n }\r\n}\r\n",
|
||||
"customFunction": "let $injector = widgetContext.$scope.$injector;\r\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\r\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\r\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\r\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\r\nlet entityRelationService = $injector.get(widgetContext.servicesMap.get('entityRelationService'));\r\nlet userSettingsService = $injector.get(widgetContext.servicesMap.get('userSettingsService'));\r\n\r\nopenAddEntityDialog();\r\n\r\nfunction openAddEntityDialog() {\r\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\r\n}\r\n\r\nfunction AddEntityDialogController(instance) {\r\n let vm = instance;\r\n let userSettings;\r\n userSettingsService.loadUserSettings().subscribe(settings=> {\r\n userSettings = settings;\r\n if (!userSettings.createdGatewaysCount) userSettings.createdGatewaysCount = 0;\r\n });\r\n \r\n\r\n vm.addEntityFormGroup = vm.fb.group({\r\n entityName: ['', [vm.validators.required]],\r\n entityType: ['DEVICE'],\r\n entityLabel: [''],\r\n type: ['', [vm.validators.required]],\r\n });\r\n\r\n vm.cancel = function() {\r\n vm.dialogRef.close(null);\r\n };\r\n\r\n\r\n vm.save = function($event) {\r\n vm.addEntityFormGroup.markAsPristine();\r\n saveEntityObservable().subscribe(\r\n function (device) {\r\n widgetContext.updateAliases();\r\n userSettingsService.putUserSettings({ createdGatewaysCount: ++userSettings.createdGatewaysCount }).subscribe(_=>{\r\n });\r\n vm.dialogRef.close(null);\r\n openCommandDialog(device, $event);\r\n }\r\n );\r\n };\r\n \r\n function openCommandDialog(device, $event) {\r\n vm.device = device;\r\n let openCommandAction = widgetContext.actionsApi.getActionDescriptors(\"actionCellButton\").find(action => action.name == \"Docker commands\");\r\n widgetContext.actionsApi.handleWidgetAction($event, openCommandAction, device.id, device.name, {newDevice: true});\r\n goToConfigState();\r\n }\r\n\r\n \r\n function goToConfigState() {\r\n const stateParams = {};\r\n stateParams.entityId = vm.device.id;\r\n stateParams.entityName = vm.device.name;\r\n const newStateParams = {\r\n targetEntityParamName: 'default',\r\n new_gateway: {\r\n entityId: vm.device.id,\r\n entityName: vm.device.name\r\n }\r\n }\r\n const params = {...stateParams, ...newStateParams};\r\n widgetContext.stateController.openState('gateway_details', params, false);\r\n }\r\n\r\n function saveEntityObservable() {\r\n const formValues = vm.addEntityFormGroup.value;\r\n let entity = {\r\n name: formValues.entityName,\r\n type: formValues.type,\r\n label: formValues.entityLabel,\r\n additionalInfo: {\r\n gateway: true\r\n }\r\n };\r\n return deviceService.saveDevice(entity);\r\n }\r\n}\r\n",
|
||||
"customResources": [],
|
||||
"openInSeparateDialog": false,
|
||||
"openInPopover": false,
|
||||
@ -567,7 +567,8 @@
|
||||
"padding": "8px",
|
||||
"settings": {
|
||||
"useMarkdownTextFunction": true,
|
||||
"markdownTextFunction": "var blockData = '';\nvar connectorsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Connectors\");\nvar logsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Logs\");\nfunction generateMatHeader(index) {\n if( index !== undefined && index > -1) {\n return `<mat-card-header class='tb-home-widget-link' (click)=\"ctx.actionsApi.handleWidgetAction($event, ctx.actionsApi.getActionDescriptors('elementClick')[${index}], ctx.datasources[0].entity.id)\">`\n } else {\n return \"<mat-card-header >\" \n }\n}\nfunction createDataBlock(value, label, dividerStyle, mobile, index) {\n blockData += `\n <mat-card style=\"flex-grow: 1; width: ${mobile? '100%': 'auto'}; min-height: ${mobile? 'auto': '57px'}\" class=\" ${dividerStyle}\">\n <div class=\"divider\"></div>\n <mat-divider vertical style=\"height:100%\"></mat-divider>\n ${generateMatHeader(index)}\n <mat-card-subtitle>${label}</mat-card-subtitle>\n </mat-card-header>\n <mat-card-content> ${value}</mat-card-content>\n </mat-card>`;\n}\ncreateDataBlock(data[0].Status, \"Status\", data[0].Status === \"Active\"? 'divider-green' : 'divider-red');\ncreateDataBlock(data[0].Name, \"Gateway Name\", '', ctx.isMobile);\ncreateDataBlock(data[0].Type, \"Gateway Type\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[1]?data[1].count:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[2]?data[2][\"count 2\"]:0)} </span>`\n , \"Devices <span class='tb-hint' style='padding-left: 0'>(Active | Inactive)</span>\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[0].active_connectors?JSON.parse(data[0].active_connectors).length:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[0].inactive_connectors?JSON.parse(data[0].inactive_connectors).length:0)} </span>`\n , \"Connectors <span class='tb-hint' style='padding-left: 0'>(Active | Inactive)</span>\", '', '', connectorsIndex);\ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"Errors\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red', '', logsIndex);\nreturn `<div fxLayout=\"row wrap\" fxLayoutGap=\"8px\" class=\"cards-container\">${blockData}</div>`;",
|
||||
"markdownTextPattern": "# Markdown/HTML card \\n - **Current entity**: **${entityName}**. \\n - **Current value**: **${Random}**.",
|
||||
"markdownTextFunction": "var blockData = '';\nvar connectorsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Connectors\");\nvar logsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Logs\");\nfunction generateMatHeader(index) {\n if( index !== undefined && index > -1) {\n return `<mat-card-header class='tb-home-widget-link' (click)=\"ctx.actionsApi.handleWidgetAction($event, ctx.actionsApi.getActionDescriptors('elementClick')[${index}], ctx.datasources[0].entity.id)\">`\n } else {\n return \"<mat-card-header >\" \n }\n}\nfunction createDataBlock(value, label, dividerStyle, mobile, index) {\n blockData += `\n <mat-card style=\"flex-grow: 1; width: ${mobile? '100%': 'auto'}; min-height: ${mobile? 'auto': '57px'}\" class=\" ${dividerStyle}\">\n <div class=\"divider\"></div>\n <mat-divider vertical style=\"height:100%\"></mat-divider>\n ${generateMatHeader(index)}\n <mat-card-subtitle>${label}</mat-card-subtitle>\n </mat-card-header>\n <mat-card-content> ${value}</mat-card-content>\n </mat-card>`;\n}\ncreateDataBlock(data[0].Status, \"Status\", data[0].Status === \"Active\"? 'divider-green' : 'divider-red');\ncreateDataBlock(data[0].Name, \"Gateway Name\", '', ctx.isMobile);\ncreateDataBlock(data[0].Type, \"Gateway Type\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[1]?data[1].count:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[2]?data[2][\"count 2\"]:0)} </span>`\n , \"Devices <span class='tb-hint' style='padding-left: 0'>(Active | Inactive)</span>\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[0].active_connectors?JSON.parse(data[0].active_connectors).length:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[0].inactive_connectors?JSON.parse(data[0].inactive_connectors).length:0)} </span>`\n , \"Connectors <span class='tb-hint' style='padding-left: 0'>(Enabled | Disabled)</span>\", '', '', connectorsIndex);\ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"Errors\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red', '', logsIndex);\nreturn `<div fxLayout=\"row wrap\" fxLayoutGap=\"8px\" class=\"cards-container\">${blockData}</div>`;",
|
||||
"applyDefaultMarkdownStyle": false,
|
||||
"markdownCss": ".divider {\n position: absolute;\n width: 3px;\n top: 8px;\n border-radius: 2px;\n bottom: 8px;\n border: 1px solid rgba(31, 70, 144, 1);\n background-color: rgba(31, 70, 144, 1);\n left: 10px;\n}\n.divider-green .divider {\n border: 1px solid rgb(25,128,56);\n background-color: rgb(25,128,56);\n}\n\n.divider-green .mat-mdc-card-content {\n color: rgb(25,128,56);\n}\n\n.divider-red .divider {\n border: 1px solid rgb(203,37,48);\n background-color: rgb(203,37,48);\n}\n\n.divider-red .mat-mdc-card-content {\n color: rgb(203,37,48);\n}\n\n.mdc-card {\n position: relative;\n padding-left: 10px;\n margin-bottom: 1px;\n}\n\n.mat-mdc-card-subtitle {\n font-weight: 400;\n font-size: 12px;\n}\n\n.mat-mdc-card-header {\n padding: 8px 16px 0;\n}\n\n.mat-mdc-card-content:last-child {\n padding-bottom: 8px;\n font-size: 16px;\n}\n\n.cards-container {\n height: calc(100% - 1px);\n justify-content: stretch;\n align-items: center;\n margin-bottom: 1px;\n}\n\n::ng-deep.tb-home-widget-link > div {\n flex-grow: 1;\n cursor: pointer;\n}\n\n .tb-home-widget-link {\n width: 100%;\n }\n\n .tb-home-widget-link:hover::after{\n color: inherit;\n }\n \n .tb-home-widget-link::after{\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px;\n}"
|
||||
},
|
||||
|
||||
@ -15,14 +15,13 @@
|
||||
///
|
||||
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef, forwardRef,
|
||||
ElementRef,
|
||||
forwardRef,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewContainerRef
|
||||
} from '@angular/core';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
@ -47,7 +46,8 @@ import {
|
||||
} from '@angular/forms';
|
||||
import {
|
||||
BrokerSecurityType,
|
||||
BrokerSecurityTypeTranslationsMap, noLeadTrailSpacesRegex,
|
||||
BrokerSecurityTypeTranslationsMap,
|
||||
noLeadTrailSpacesRegex
|
||||
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
@ -69,7 +69,7 @@ import { takeUntil } from 'rxjs/operators';
|
||||
}
|
||||
]
|
||||
})
|
||||
export class BrokerSecurityComponent extends PageComponent implements ControlValueAccessor, Validator, AfterViewInit, OnInit, OnDestroy {
|
||||
export class BrokerSecurityComponent extends PageComponent implements ControlValueAccessor, Validator, OnDestroy {
|
||||
|
||||
BrokerSecurityType = BrokerSecurityType;
|
||||
|
||||
@ -115,18 +115,12 @@ export class BrokerSecurityComponent extends PageComponent implements ControlVal
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
super.ngOnDestroy();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.propagateChange = fn;
|
||||
}
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
///
|
||||
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
@ -40,7 +39,8 @@ import { UtilsService } from '@core/services/utils.service';
|
||||
import { EntityService } from '@core/http/entity.service';
|
||||
import {
|
||||
ControlValueAccessor,
|
||||
FormBuilder, NG_VALIDATORS,
|
||||
FormBuilder,
|
||||
NG_VALIDATORS,
|
||||
NG_VALUE_ACCESSOR,
|
||||
UntypedFormGroup,
|
||||
ValidationErrors,
|
||||
@ -73,7 +73,7 @@ import { coerceBoolean } from '@shared/decorators/coercion';
|
||||
}
|
||||
]
|
||||
})
|
||||
export class DeviceInfoTableComponent extends PageComponent implements ControlValueAccessor, Validator, AfterViewInit, OnInit, OnDestroy {
|
||||
export class DeviceInfoTableComponent extends PageComponent implements ControlValueAccessor, Validator, OnInit, OnDestroy {
|
||||
|
||||
SourceTypeTranslationsMap = SourceTypeTranslationsMap;
|
||||
|
||||
@ -156,9 +156,6 @@ export class DeviceInfoTableComponent extends PageComponent implements ControlVa
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.propagateChange = fn;
|
||||
}
|
||||
|
||||
@ -15,26 +15,31 @@
|
||||
///
|
||||
|
||||
import {
|
||||
AfterViewInit,
|
||||
Directive,
|
||||
ElementRef,
|
||||
Inject,
|
||||
Input,
|
||||
OnDestroy,
|
||||
Renderer2
|
||||
} from '@angular/core';
|
||||
import { isEqual } from '@core/utils';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { WINDOW } from '@core/services/window.service';
|
||||
import { fromEvent, Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Directive({
|
||||
// eslint-disable-next-line @angular-eslint/directive-selector
|
||||
selector: '[tb-ellipsis-chip-list]'
|
||||
})
|
||||
export class EllipsisChipListDirective implements AfterViewInit, OnDestroy {
|
||||
export class EllipsisChipListDirective implements OnDestroy {
|
||||
|
||||
chipsValue: string[];
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
@Input('tb-ellipsis-chip-list')
|
||||
set chips(value: any[]) {
|
||||
set chips(value: string[]) {
|
||||
if (!isEqual(this.chipsValue, value)) {
|
||||
this.chipsValue = value;
|
||||
setTimeout(() => {
|
||||
@ -45,11 +50,15 @@ export class EllipsisChipListDirective implements AfterViewInit, OnDestroy {
|
||||
|
||||
constructor(private el: ElementRef,
|
||||
private renderer: Renderer2,
|
||||
private translate: TranslateService) {}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.adjustChips();
|
||||
window.addEventListener('resize', this.adjustChips.bind(this));
|
||||
private translate: TranslateService,
|
||||
@Inject(WINDOW) private window: Window) {
|
||||
this.renderer.setStyle(this.el.nativeElement, 'max-height', '48px');
|
||||
this.renderer.setStyle(this.el.nativeElement, 'overflow', 'auto');
|
||||
fromEvent(window, 'resize').pipe(
|
||||
takeUntil(this.destroy$)
|
||||
).subscribe(() => {
|
||||
this.adjustChips();
|
||||
});
|
||||
}
|
||||
|
||||
private adjustChips(): void {
|
||||
@ -58,17 +67,16 @@ export class EllipsisChipListDirective implements AfterViewInit, OnDestroy {
|
||||
const chipNodes = chipListElement.querySelectorAll('mat-chip:not(.ellipsis-chip)');
|
||||
const ellipsisChip = this.el.nativeElement.querySelector('.ellipsis-chip');
|
||||
this.renderer.setStyle(ellipsisChip,'display', 'inline-flex');
|
||||
ellipsisText.innerHTML = this.translate.instant('gateway.ellipsis-chips-text',
|
||||
{count: (this.chipsValue.length)});
|
||||
|
||||
const margin = parseFloat(window.getComputedStyle(ellipsisChip).marginLeft) | 0;
|
||||
const margin = parseFloat(this.window.getComputedStyle(ellipsisChip).marginLeft) || 0;
|
||||
const availableWidth = chipListElement.offsetWidth - (ellipsisChip.offsetWidth + margin);
|
||||
let usedWidth = 0;
|
||||
let visibleChipsCount = 0;
|
||||
|
||||
chipNodes.forEach((chip) => {
|
||||
this.renderer.setStyle(chip, 'display', 'inline-flex');
|
||||
});
|
||||
|
||||
chipNodes.forEach((chip) => {
|
||||
if ((usedWidth + (chip.offsetWidth + margin) <= availableWidth) && (visibleChipsCount < this.chipsValue.length)) {
|
||||
visibleChipsCount++;
|
||||
usedWidth += chip.offsetWidth + margin;
|
||||
@ -85,7 +93,8 @@ export class EllipsisChipListDirective implements AfterViewInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
window.removeEventListener('resize', this.adjustChips.bind(this));
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,10 @@
|
||||
<div class="tb-form-panel stroked">
|
||||
<div class="tb-form-panel-title" translate>gateway.platform-side</div>
|
||||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||
<div class="fixed-title-width tb-required" translate>gateway.key</div>
|
||||
<div class="fixed-title-width tb-required"
|
||||
tb-hint-tooltip-icon="{{ 'gateway.JSONPath-hint' | translate }}" translate>
|
||||
gateway.key
|
||||
</div>
|
||||
<div class="tb-flex no-gap">
|
||||
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
||||
<input matInput name="value" formControlName="key" placeholder="{{ 'gateway.set' | translate }}"/>
|
||||
@ -89,7 +92,10 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||
<div class="fixed-title-width tb-required" translate>gateway.value</div>
|
||||
<div class="fixed-title-width tb-required"
|
||||
tb-hint-tooltip-icon="{{ 'gateway.JSONPath-hint' | translate }}" translate>
|
||||
gateway.value
|
||||
</div>
|
||||
<mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic" class="tb-flex no-gap">
|
||||
<input matInput required formControlName="value"
|
||||
placeholder="{{ 'gateway.set' | translate }}"/>
|
||||
|
||||
@ -26,9 +26,6 @@
|
||||
<mat-icon class="material-icons">close</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar>
|
||||
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
||||
</mat-progress-bar>
|
||||
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
||||
<div mat-dialog-content>
|
||||
<div class="tb-form-panel no-border no-padding" fxLayout="column">
|
||||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||
@ -91,13 +88,12 @@
|
||||
<button mat-button color="primary"
|
||||
type="button"
|
||||
cdkFocusInitial
|
||||
[disabled]="(isLoading$ | async)"
|
||||
(click)="cancel()">
|
||||
{{ 'action.cancel' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button color="primary"
|
||||
(click)="add()"
|
||||
[disabled]="(isLoading$ | async) || connectorForm.invalid || !connectorForm.dirty">
|
||||
[disabled]="connectorForm.invalid || !connectorForm.dirty">
|
||||
{{ 'action.add' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, Inject, OnDestroy } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
@ -23,7 +23,9 @@ import { BaseData, HasId } from '@shared/models/base-data';
|
||||
import { DialogComponent } from '@shared/components/dialog.component';
|
||||
import { Router } from '@angular/router';
|
||||
import {
|
||||
AddConnectorConfigData,
|
||||
ConnectorType,
|
||||
CreatedConnectorConfigData,
|
||||
GatewayConnectorDefaultTypesTranslatesMap,
|
||||
GatewayLogLevel,
|
||||
getDefaultConfig,
|
||||
@ -38,7 +40,7 @@ import { ResourcesService } from '@core/services/resources.service';
|
||||
styleUrls: ['./add-connector-dialog.component.scss'],
|
||||
providers: [],
|
||||
})
|
||||
export class AddConnectorDialogComponent extends DialogComponent<AddConnectorDialogComponent, BaseData<HasId>> implements OnInit, OnDestroy {
|
||||
export class AddConnectorDialogComponent extends DialogComponent<AddConnectorDialogComponent, BaseData<HasId>> implements OnDestroy {
|
||||
|
||||
connectorForm: UntypedFormGroup;
|
||||
|
||||
@ -53,8 +55,8 @@ export class AddConnectorDialogComponent extends DialogComponent<AddConnectorDia
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
protected router: Router,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
public dialogRef: MatDialogRef<AddConnectorDialogComponent, any>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: AddConnectorConfigData,
|
||||
public dialogRef: MatDialogRef<AddConnectorDialogComponent, CreatedConnectorConfigData>,
|
||||
private fb: FormBuilder,
|
||||
private resourcesService: ResourcesService) {
|
||||
super(store, router, dialogRef);
|
||||
@ -67,9 +69,6 @@ export class AddConnectorDialogComponent extends DialogComponent<AddConnectorDia
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
|
||||
@ -139,7 +139,10 @@
|
||||
</div>
|
||||
<div class="tb-form-panel no-border no-padding" *ngIf="converterType === ConvertorTypeEnum.CUSTOM">
|
||||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||
<div class="fixed-title-width tb-required" translate>gateway.extension</div>
|
||||
<div class="fixed-title-width tb-required"
|
||||
tb-hint-tooltip-icon="{{ 'gateway.extension-hint' | translate }}" translate>
|
||||
gateway.extension
|
||||
</div>
|
||||
<div class="tb-flex no-gap">
|
||||
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
||||
<input matInput name="value" formControlName="extension" placeholder="{{ 'gateway.set' | translate }}"/>
|
||||
@ -237,6 +240,9 @@
|
||||
<ng-template [ngSwitchCase]="RequestTypeEnum.ATTRIBUTE_REQUEST">
|
||||
<div class="tb-form-panel stroked">
|
||||
<div class="tb-form-panel-title tb-required" translate>gateway.from-device-request-settings</div>
|
||||
<div class="tb-form-hint tb-primary-fill" translate>
|
||||
gateway.from-device-request-settings-hint
|
||||
</div>
|
||||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center" formGroupName="deviceInfo">
|
||||
<div class="fixed-title-width tb-flex no-flex align-center" translate>
|
||||
<div class="tb-required" translate>gateway.device-info.device-name-expression</div>
|
||||
@ -302,6 +308,9 @@
|
||||
</div>
|
||||
<div class="tb-form-panel stroked">
|
||||
<div class="tb-form-panel-title tb-required" translate>gateway.to-device-response-settings</div>
|
||||
<div class="tb-form-hint tb-primary-fill" translate>
|
||||
gateway.to-device-response-settings-hint
|
||||
</div>
|
||||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||
<div class="fixed-title-width tb-required" translate>gateway.response-value-expression</div>
|
||||
<div class="tb-flex no-gap">
|
||||
@ -359,7 +368,10 @@
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="RequestTypeEnum.ATTRIBUTE_UPDATE">
|
||||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||
<div class="fixed-title-width tb-required" translate>gateway.device-name-filter</div>
|
||||
<div class="fixed-title-width tb-required"
|
||||
tb-hint-tooltip-icon="{{ 'gateway.device-name-filter-hint' | translate }}" translate>
|
||||
gateway.device-name-filter
|
||||
</div>
|
||||
<div class="tb-flex no-gap">
|
||||
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
||||
<input matInput name="value" formControlName="deviceNameFilter" placeholder="{{ 'gateway.set' | translate }}"/>
|
||||
@ -368,7 +380,7 @@
|
||||
matTooltipClass="tb-error-tooltip"
|
||||
[matTooltip]="('gateway.device-name-filter-required') | translate"
|
||||
*ngIf="mappingForm.get('requestValue.attributeUpdates.deviceNameFilter').hasError('required') &&
|
||||
mappingForm.get('requestValue.attributeUpdates.deviceNameFilter').touched"
|
||||
mappingForm.get('requestValue.attributeUpdates.deviceNameFilter').touched"
|
||||
class="tb-error">
|
||||
warning
|
||||
</mat-icon>
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
|
||||
.mat-mdc-dialog-content {
|
||||
max-height: 670px;
|
||||
height: 670px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, Inject, OnDestroy, OnInit, Renderer2, ViewContainerRef } from '@angular/core';
|
||||
import { Component, Inject, OnDestroy, Renderer2, ViewContainerRef } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
@ -27,7 +27,6 @@ import {
|
||||
ConvertorTypeTranslationsMap,
|
||||
DataConversionTranslationsMap,
|
||||
DeviceInfoType,
|
||||
MappingDataKey,
|
||||
MappingHintTranslationsMap,
|
||||
MappingInfo,
|
||||
MappingKeysAddKeyTranslationsMap,
|
||||
@ -58,7 +57,7 @@ import { MappingDataKeysPanelComponent } from '@home/components/widget/lib/gatew
|
||||
styleUrls: ['./mapping-dialog.component.scss'],
|
||||
providers: [],
|
||||
})
|
||||
export class MappingDialogComponent extends DialogComponent<MappingDialogComponent, BaseData<HasId>> implements OnInit, OnDestroy {
|
||||
export class MappingDialogComponent extends DialogComponent<MappingDialogComponent, BaseData<HasId>> implements OnDestroy {
|
||||
|
||||
mappingForm: UntypedFormGroup;
|
||||
|
||||
@ -118,7 +117,7 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
|
||||
}
|
||||
}
|
||||
|
||||
get converterTelemetry(): Array<MappingDataKey> {
|
||||
get converterTelemetry(): Array<string> {
|
||||
if (this.converterType) {
|
||||
return this.mappingForm.get('converter').get(this.converterType).value.timeseries.map(value => value.key);
|
||||
}
|
||||
@ -128,7 +127,7 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
|
||||
return this.mappingForm.get('converter').get('type').value;
|
||||
}
|
||||
|
||||
get customKeys(): {[key: string]: any} {
|
||||
get customKeys(): Array<string> {
|
||||
return Object.keys(this.mappingForm.get('converter').get('custom').value.extensionConfig);
|
||||
}
|
||||
|
||||
@ -136,9 +135,6 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
|
||||
return this.mappingForm.get('requestType').value;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
|
||||
@ -82,6 +82,7 @@
|
||||
<span class="dot"
|
||||
matTooltip="{{ 'Errors: '+ getErrorsCount(attribute)}}"
|
||||
matTooltipPosition="above"
|
||||
(click)="connectorLogs(attribute, $event)"
|
||||
[class]="{'hasErrors': +getErrorsCount(attribute) > 0,
|
||||
'noErrors': +getErrorsCount(attribute) === 0 || getErrorsCount(attribute) === ''}"></span>
|
||||
</mat-cell>
|
||||
@ -127,7 +128,7 @@
|
||||
<mat-icon>private_connectivity</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button
|
||||
matTooltip="Delete connector"
|
||||
matTooltip="Logs"
|
||||
matTooltipPosition="above"
|
||||
(click)="connectorLogs(attribute, $event)">
|
||||
<mat-icon>list</mat-icon>
|
||||
@ -192,7 +193,7 @@
|
||||
<div class="tb-form-panel stroked">
|
||||
<div class="tb-form-panel-title" translate>gateway.logs-configuration</div>
|
||||
<div class="tb-form-row" fxLayoutAlign="space-between center">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="sendDataOnlyOnChange">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="enableRemoteLogging">
|
||||
<mat-label>
|
||||
{{ 'gateway.enable-remote-logging' | translate }}
|
||||
</mat-label>
|
||||
@ -245,13 +246,15 @@
|
||||
<div class="fixed-title-width tb-required" translate>gateway.port</div>
|
||||
<div class="tb-flex no-gap">
|
||||
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
||||
<input matInput type="number" min="0" name="value" formControlName="port" placeholder="{{ 'gateway.set' | translate }}"/>
|
||||
<input matInput type="number" min="{{portLimits.MIN}}" max="{{portLimits.MAX}}"
|
||||
name="value" formControlName="port" placeholder="{{ 'gateway.set' | translate }}"/>
|
||||
<mat-icon matSuffix
|
||||
matTooltipPosition="above"
|
||||
matTooltipClass="tb-error-tooltip"
|
||||
[matTooltip]="portErrorTooltip | translate"
|
||||
[matTooltip]="portErrorTooltip"
|
||||
*ngIf="(connectorForm.get('basicConfig.broker.port').hasError('required') ||
|
||||
connectorForm.get('basicConfig.broker.port').hasError('min')) &&
|
||||
connectorForm.get('basicConfig.broker.port').hasError('min') ||
|
||||
connectorForm.get('basicConfig.broker.port').hasError('max')) &&
|
||||
connectorForm.get('basicConfig.broker.port').touched"
|
||||
class="tb-error">
|
||||
warning
|
||||
|
||||
@ -56,7 +56,8 @@ import {
|
||||
GatewayLogLevel,
|
||||
MappingType,
|
||||
MqttVersions,
|
||||
noLeadTrailSpacesRegex
|
||||
noLeadTrailSpacesRegex,
|
||||
PortLimits
|
||||
} from './gateway-widget.models';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { AddConnectorDialogComponent } from '@home/components/widget/lib/gateway/dialog/add-connector-dialog.component';
|
||||
@ -110,6 +111,8 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
||||
|
||||
mappingTypes = MappingType;
|
||||
|
||||
portLimits = PortLimits;
|
||||
|
||||
mode: ConnectorConfigurationModes = this.connectorConfigurationModes.BASIC;
|
||||
|
||||
initialConnector: GatewayConnector;
|
||||
@ -180,9 +183,13 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
||||
|
||||
get portErrorTooltip(): string {
|
||||
if (this.connectorForm.get('basicConfig.broker.port').hasError('required')) {
|
||||
return 'gateway.port-required';
|
||||
} else if (this.connectorForm.get('basicConfig.broker.port').hasError('min')) {
|
||||
return 'gateway.only-natural-numbers';
|
||||
return this.translate.instant('gateway.port-required');
|
||||
} else if (
|
||||
this.connectorForm.get('basicConfig.broker.port').hasError('min') ||
|
||||
this.connectorForm.get('basicConfig.broker.port').hasError('max')
|
||||
) {
|
||||
return this.translate.instant('gateway.port-limits-error',
|
||||
{min: PortLimits.MIN, max: PortLimits.MAX});
|
||||
}
|
||||
return '';
|
||||
}
|
||||
@ -418,10 +425,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
this.confirmConnectorChange().subscribe((result) => {
|
||||
if (result) {
|
||||
const connector = attribute.value;
|
||||
if (connector?.name !== this.initialConnector?.name) {
|
||||
const connector = attribute.value;
|
||||
if (connector?.name !== this.initialConnector?.name) {
|
||||
this.confirmConnectorChange().subscribe((result) => {
|
||||
if (result) {
|
||||
if (this.connectorForm.disabled) {
|
||||
this.connectorForm.enable();
|
||||
}
|
||||
@ -447,8 +454,8 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
||||
this.connectorForm.patchValue(connector, {emitEvent: false});
|
||||
this.connectorForm.markAsPristine();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isSameConnector(attribute: AttributeData): boolean {
|
||||
@ -573,34 +580,36 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
return this.dialog.open<AddConnectorDialogComponent,
|
||||
AddConnectorConfigData>(AddConnectorDialogComponent, {
|
||||
disableClose: true,
|
||||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||
data: {
|
||||
dataSourceData: this.dataSource.data
|
||||
this.confirmConnectorChange().subscribe((changeConfirmed) => {
|
||||
if (changeConfirmed) {
|
||||
return this.dialog.open<AddConnectorDialogComponent,
|
||||
AddConnectorConfigData>(AddConnectorDialogComponent, {
|
||||
disableClose: true,
|
||||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||
data: {
|
||||
dataSourceData: this.dataSource.data
|
||||
}
|
||||
}).afterClosed().subscribe((value) => {
|
||||
if (value && changeConfirmed) {
|
||||
this.initialConnector = null;
|
||||
if (this.connectorForm.disabled) {
|
||||
this.connectorForm.enable();
|
||||
}
|
||||
if (!value.configurationJson) {
|
||||
value.configurationJson = {};
|
||||
}
|
||||
value.basicConfig = value.configurationJson;
|
||||
if (value.type === ConnectorType.MQTT) {
|
||||
this.addMQTTConfigControls();
|
||||
} else {
|
||||
this.connectorForm.setControl('basicConfig', this.fb.group({}), {emitEvent: false});
|
||||
}
|
||||
this.connectorForm.patchValue(value, {emitEvent: false});
|
||||
this.generate('basicConfig.broker.clientId');
|
||||
this.saveConnector();
|
||||
}
|
||||
});
|
||||
}
|
||||
}).afterClosed().subscribe((value) => {
|
||||
this.confirmConnectorChange().subscribe((changeConfirmed) => {
|
||||
if (value && changeConfirmed) {
|
||||
this.initialConnector = null;
|
||||
if (this.connectorForm.disabled) {
|
||||
this.connectorForm.enable();
|
||||
}
|
||||
if (!value.configurationJson) {
|
||||
value.configurationJson = {};
|
||||
}
|
||||
value.basicConfig = value.configurationJson;
|
||||
if (value.type === ConnectorType.MQTT) {
|
||||
this.addMQTTConfigControls();
|
||||
} else {
|
||||
this.connectorForm.setControl('basicConfig', this.fb.group({}), {emitEvent: false});
|
||||
}
|
||||
this.connectorForm.patchValue(value, {emitEvent: false});
|
||||
this.generate('basicConfig.broker.clientId');
|
||||
this.saveConnector();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@ -648,7 +657,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
||||
const brokerGroup = this.fb.group({
|
||||
name: ['', []],
|
||||
host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
port: [null, [Validators.required, Validators.min(0)]],
|
||||
port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]],
|
||||
version: [5, []],
|
||||
clientId: ['', [Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
maxNumberOfWorkers: [100, [Validators.required, Validators.min(1)]],
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
import { ResourcesService } from '@core/services/resources.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ValueTypeData } from '@shared/models/constants';
|
||||
import { Validators } from '@angular/forms';
|
||||
|
||||
export const noLeadTrailSpacesRegex: RegExp = /^(?! )[\S\s]*(?<! )$/;
|
||||
|
||||
@ -39,6 +40,11 @@ export enum GatewayLogLevel {
|
||||
DEBUG = 'DEBUG'
|
||||
}
|
||||
|
||||
export enum PortLimits {
|
||||
MIN = 1,
|
||||
MAX = 65535
|
||||
}
|
||||
|
||||
export const GatewayStatus = {
|
||||
...GatewayLogLevel,
|
||||
...DeviceGatewayStatus
|
||||
@ -314,6 +320,15 @@ export interface AddConnectorConfigData {
|
||||
dataSourceData: Array<any>
|
||||
}
|
||||
|
||||
export interface CreatedConnectorConfigData {
|
||||
type: ConnectorType,
|
||||
name: string,
|
||||
logLevel: GatewayLogLevel,
|
||||
useDefaults: boolean,
|
||||
sendDataOnlyOnChange: boolean,
|
||||
configurationJson?: {[key: string]: any}
|
||||
}
|
||||
|
||||
export interface MappingDataKey {
|
||||
key: string,
|
||||
value: any,
|
||||
|
||||
@ -94,6 +94,3 @@ For bytes converter, expression fields can use slices format only. A slice speci
|
||||
| AM123,mytype,12.2,45 | [:] | AM123,mytype,12.2,45 | Extracting all data |
|
||||
| AM123,mytype,12.2,45 | [18:] | 45 | Extracting humidity value |
|
||||
| AM123,mytype,12.2,45 | [13:17] | 12.2 | Extracting temperature value |
|
||||
|
||||
|
||||
For more information about MQTT connector configuration use the [official documentation](https://thingsboard.io/docs/iot-gateway/config/mqtt/?MqttConverterTypeConfig=json#subsection-security).
|
||||
|
||||
@ -2708,12 +2708,12 @@
|
||||
"add-entry": "Add configuration",
|
||||
"add-attribute": "Add attribute",
|
||||
"add-key": "Add key",
|
||||
"add-timeseries": "Add timeseries",
|
||||
"add-timeseries": "Add time series",
|
||||
"add-mapping": "Add mapping",
|
||||
"advanced": "Advanced",
|
||||
"attributes": "Attributes",
|
||||
"attribute-filter": "Attribute filter",
|
||||
"attribute-filter-hint": "Filter for incoming attribute name from ThingsBoard, supports regular expression.",
|
||||
"attribute-filter-hint": "Filter for incoming attribute name from platform, supports regular expression.",
|
||||
"attribute-filter-required": "Attribute filter required.",
|
||||
"attribute-name-expression": "Attribute name expression",
|
||||
"attribute-name-expression-required": "Attribute name expression required.",
|
||||
@ -2773,7 +2773,7 @@
|
||||
"device-profile-expression-required": "Device profile expression required."
|
||||
},
|
||||
"device-name-filter": "Device name filter",
|
||||
"device-name-filter-hint": "Regular expression for device name.",
|
||||
"device-name-filter-hint": "This field supports Regular expressions to filter incoming data by device name.",
|
||||
"device-name-filter-required": "Device name filter is required.",
|
||||
"details": "Details",
|
||||
"delete-mapping-title": "Delete mapping ?",
|
||||
@ -2797,9 +2797,9 @@
|
||||
"payload-type": "Payload type",
|
||||
"platform-side": "Platform side",
|
||||
"JSON": "JSON",
|
||||
"JSON-hint": "Converter for this payload type processes MQTT messages in JSON format. It uses JSON Path expressions to extract vital details such as device names, device profile names, attributes, and timeseries from the message. And regular expressions to get device details from topics.",
|
||||
"JSON-hint": "Converter for this payload type processes MQTT messages in JSON format. It uses JSON Path expressions to extract vital details such as device names, device profile names, attributes, and time series from the message. And regular expressions to get device details from topics.",
|
||||
"bytes": "Bytes",
|
||||
"bytes-hint": "Converter for this payload type designed for binary MQTT payloads, this converter directly interprets binary data to retrieve device names and device profile names, along with attributes and timeseries, using specific byte positions for data extraction.",
|
||||
"bytes-hint": "Converter for this payload type designed for binary MQTT payloads, this converter directly interprets binary data to retrieve device names and device profile names, along with attributes and time series, using specific byte positions for data extraction.",
|
||||
"custom": "Custom",
|
||||
"custom-hint": "This option allows you to use a custom converter for specific data tasks. You need to add your custom converter to the extension folder and enter its class name in the UI settings. Any keys you provide will be sent as configuration to your custom converter.",
|
||||
"client-cert-path": "Path to client certificate file",
|
||||
@ -2807,22 +2807,25 @@
|
||||
"client-id": "Client ID",
|
||||
"data-conversion": "Data conversion",
|
||||
"data-mapping": "Data mapping",
|
||||
"data-mapping-hint": "Data mapping provides the capability to parse and convert the data received from a MQTT client in incoming messages into specific attributes and timeseries data keys.",
|
||||
"data-mapping-hint": "Data mapping provides the capability to parse and convert the data received from a MQTT client in incoming messages into specific attributes and time series data keys.",
|
||||
"delete": "Delete configuration",
|
||||
"delete-attribute": "Delete attribute",
|
||||
"delete-key": "Delete key",
|
||||
"delete-timeseries": "Delete timeseries",
|
||||
"delete-timeseries": "Delete time series",
|
||||
"default": "Default",
|
||||
"download-tip": "Download configuration file",
|
||||
"drop-file": "Drop file here or",
|
||||
"extension": "Extension",
|
||||
"extension-hint": "Put your converter classname in the field. Custom converter with such class should be in extension/mqtt folder.",
|
||||
"extension-required": "Extension is required.",
|
||||
"extension-configuration": "Extension configuration",
|
||||
"extension-configuration-hint": "Configuration for convertor",
|
||||
"fill-connector-defaults": "Fill configuration with default values",
|
||||
"fill-connector-defaults-hint": "This property allows to fill connector configuration with default values on it's creation.",
|
||||
"from-device-request-settings": "Input request parsing",
|
||||
"to-device-response-settings": "Output request handling",
|
||||
"from-device-request-settings-hint": "These fields support JSONPath expressions to extract a name from incoming message.",
|
||||
"to-device-response-settings": "Output request processing",
|
||||
"to-device-response-settings-hint": "For these fields you can use the following variables and they will be replaced with actual values: ${deviceName}, ${attributeKey}, ${attributeValue}",
|
||||
"gateway": "Gateway",
|
||||
"gateway-exists": "Device with same name is already exists.",
|
||||
"gateway-name": "Gateway name",
|
||||
@ -2862,6 +2865,7 @@
|
||||
"host-required": "Host is required.",
|
||||
"json-parse": "Not valid JSON.",
|
||||
"json-required": "Field cannot be empty.",
|
||||
"JSONPath-hint": "This field supports constants and JSONPath expressions.",
|
||||
"logs": {
|
||||
"logs": "Logs",
|
||||
"days": "days",
|
||||
@ -2903,7 +2907,7 @@
|
||||
"no-data": "No configurations",
|
||||
"no-gateway-found": "No gateway found.",
|
||||
"no-gateway-matching": " '{{item}}' not found.",
|
||||
"no-timeseries": "No timeseries",
|
||||
"no-timeseries": "No time series",
|
||||
"no-keys": "No keys",
|
||||
"path-hint": "The path is local to the gateway file system",
|
||||
"path-logs": "Path to log files",
|
||||
@ -2913,6 +2917,7 @@
|
||||
"permit-without-calls": "Keep alive permit without calls",
|
||||
"port": "Port",
|
||||
"port-required": "Port is required.",
|
||||
"port-limits-error": "Port should be number from {{min}} to {{max}}.",
|
||||
"private-key-path": "Path to private key file",
|
||||
"path-to-private-key-required": "Path to private key file is required.",
|
||||
"raw": "Raw",
|
||||
@ -3044,7 +3049,6 @@
|
||||
"with-response": "With response",
|
||||
"without-response": "Without response",
|
||||
"other": "Other",
|
||||
"only-natural-numbers": "Only natural numbers allowed.",
|
||||
"save-tip": "Save configuration file",
|
||||
"security": "Security",
|
||||
"security-type": "Security type",
|
||||
@ -3057,7 +3061,7 @@
|
||||
},
|
||||
"select-connector": "Select connector to display config",
|
||||
"send-change-data": "Send data only on change",
|
||||
"send-change-data-hint": "The values will be saved to the database only if they are different from the corresponding values in the previous converted message. This functionality applies to both attributes and timeseries in the converter output.",
|
||||
"send-change-data-hint": "The values will be saved to the database only if they are different from the corresponding values in the previous converted message. This functionality applies to both attributes and time series in the converter output.",
|
||||
"server-port": "Server port",
|
||||
"set": "Set",
|
||||
"statistics": {
|
||||
@ -3126,13 +3130,13 @@
|
||||
"workers-settings": "Workers settings",
|
||||
"thingsboard": "ThingsBoard",
|
||||
"general": "General",
|
||||
"timeseries": "Timeseries",
|
||||
"timeseries": "Time series",
|
||||
"key": "Key",
|
||||
"keys": "Keys",
|
||||
"key-required": "Key is required.",
|
||||
"thingsboard-host": "ThingsBoard host",
|
||||
"thingsboard-host": "Platform host",
|
||||
"thingsboard-host-required": "Host is required.",
|
||||
"thingsboard-port": "ThingsBoard port",
|
||||
"thingsboard-port": "Platform port",
|
||||
"thingsboard-port-max": "Maximum port number is 65535.",
|
||||
"thingsboard-port-min": "Minimum port number is 1.",
|
||||
"thingsboard-port-pattern": "Port is not valid.",
|
||||
@ -3146,7 +3150,7 @@
|
||||
"tls-path-ca-certificate": "Path to CA certificate on gateway",
|
||||
"tls-path-client-certificate": "Path to client certificate on gateway",
|
||||
"method-filter": "Method filter",
|
||||
"method-filter-hint": "Regular expression for RPC method.",
|
||||
"method-filter-hint": "Regular expression to filter incoming RPC method from platform.",
|
||||
"method-filter-required": "Method filter is required.",
|
||||
"messages-ttl-check-in-hours": "Messages TTL check in hours",
|
||||
"messages-ttl-check-in-hours-required": "Messages TTL check in hours is required.",
|
||||
@ -3173,12 +3177,12 @@
|
||||
"hints": {
|
||||
"remote-configuration": "Enables remote configuration and management of the gateway",
|
||||
"remote-shell": "Enables remote control of the operating system with the gateway from the Remote Shell widget",
|
||||
"host": "Hostname or IP address of ThingsBoard server",
|
||||
"port": "Port of MQTT service on ThingsBoard server",
|
||||
"token": "Access token for the gateway from ThingsBoard server",
|
||||
"client-id": "MQTT client id for the gateway form ThingsBoard server",
|
||||
"username": "MQTT username for the gateway form ThingsBoard server",
|
||||
"password": "MQTT password for the gateway form ThingsBoard server",
|
||||
"host": "Hostname or IP address of platform server",
|
||||
"port": "Port of MQTT service on platform server",
|
||||
"token": "Access token for the gateway from platform server",
|
||||
"client-id": "MQTT client id for the gateway form platform server",
|
||||
"username": "MQTT username for the gateway form platform server",
|
||||
"password": "MQTT password for the gateway form platform server",
|
||||
"ca-cert": "Path to CA certificate file",
|
||||
"date-form": "Date format in log message",
|
||||
"data-folder": "Path to folder, that will contains data (Relative or Absolute)",
|
||||
@ -3187,10 +3191,10 @@
|
||||
"backup-count": "If backup count is > 0, when a rollover is done, no more than backup count files are kept - the oldest ones are deleted",
|
||||
"storage": "Provides configuration for saving incoming data before it is sent to the platform",
|
||||
"max-file-count": "Maximum count of file that will be created",
|
||||
"max-read-count": "Count of messages to get from storage and send to ThingsBoard",
|
||||
"max-read-count": "Count of messages to get from storage and send to platform",
|
||||
"max-records": "Maximum count of records that will be stored in one file",
|
||||
"read-record-count": "Count of messages to get from storage and send to ThingsBoard",
|
||||
"max-records-count": "Maximum count of data in storage before send to ThingsBoard",
|
||||
"read-record-count": "Count of messages to get from storage and send to platform",
|
||||
"max-records-count": "Maximum count of data in storage before send to platform",
|
||||
"ttl-check-hour": "How often will Gateway check data for obsolescence",
|
||||
"ttl-messages-day": "Maximum days that storage will save data",
|
||||
"commands": "Commands for collecting additional statistic",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user