+ [class.no-padding-bottom]="gatewayConfigGroup.get('thingsboard.checkingDeviceActivity.checkDeviceInactivity').value">
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss
index 803654734f..d15c05a902 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss
@@ -45,6 +45,14 @@
.saving-period {
flex: 1;
}
+
+ .statistics-container {
+ width: 50%;
+
+ .command-container {
+ width: 100%;
+ }
+ }
}
:host ::ng-deep {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts
index bef0c2cc74..ed64c96799 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts
@@ -48,6 +48,7 @@ import {
StorageTypes,
StorageTypesTranslationMap
} from './gateway-widget.models';
+import { deepTrim } from '@core/utils';
@Component({
selector: 'tb-gateway-configuration',
@@ -91,7 +92,7 @@ export class GatewayConfigurationComponent implements OnInit {
ngOnInit() {
this.gatewayConfigGroup = this.fb.group({
thingsboard: this.fb.group({
- host: [window.location.hostname, [Validators.required]],
+ host: [window.location.hostname, [Validators.required, Validators.pattern(/^[^\s]+$/)]],
port: [1883, [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern(/^-?[0-9]+$/)]],
remoteShell: [false, []],
remoteConfiguration: [true, []],
@@ -107,30 +108,30 @@ export class GatewayConfigurationComponent implements OnInit {
handleDeviceRenaming: [true, []],
checkingDeviceActivity: this.fb.group({
checkDeviceInactivity: [false, []],
- inactivityTimeoutSeconds: [200, [Validators.min(1)]],
- inactivityCheckPeriodSeconds: [500, [Validators.min(1)]]
+ inactivityTimeoutSeconds: [200, [Validators.min(1), Validators.pattern(/^[^.\s]+$/)]],
+ inactivityCheckPeriodSeconds: [500, [Validators.min(1), Validators.pattern(/^[^.\s]+$/)]]
}),
security: this.fb.group({
type: [SecurityTypes.ACCESS_TOKEN, [Validators.required]],
- accessToken: [null, [Validators.required]],
- clientId: [null, []],
- username: [null, []],
- password: [null, []],
+ accessToken: [null, [Validators.required, Validators.pattern(/^[^.\s]+$/)]],
+ clientId: [null, [Validators.pattern(/^[^.\s]+$/)]],
+ username: [null, [Validators.pattern(/^[^.\s]+$/)]],
+ password: [null, [Validators.pattern(/^[^.\s]+$/)]],
caCert: [null, []],
cert: [null, []],
privateKey: [null, []],
}),
- qos: [1, [Validators.min(0), Validators.max(1), Validators.required]]
+ qos: [1, [Validators.min(0), Validators.max(1), Validators.required, Validators.pattern(/^[^.\s]+$/)]]
}),
storage: this.fb.group({
type: [StorageTypes.MEMORY, [Validators.required]],
- read_records_count: [100, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]],
- max_records_count: [100000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]],
- data_folder_path: ['./data/', []],
+ read_records_count: [100, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required, Validators.pattern(/^[^.\s]+$/)]],
+ max_records_count: [100000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required, Validators.pattern(/^[^.\s]+$/)]],
+ data_folder_path: ['./data/', [Validators.pattern(/^[^\s]+$/)]],
max_file_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
max_read_records_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
max_records_per_file: [10000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
- data_file_path: ['./data/data.db', []],
+ data_file_path: ['./data/data.db', [Validators.pattern(/^[^\s]+$/)]],
messages_ttl_check_in_hours: [1, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
messages_ttl_in_days: [7, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
@@ -147,9 +148,9 @@ export class GatewayConfigurationComponent implements OnInit {
}),
connectors: this.fb.array([]),
logs: this.fb.group({
- dateFormat: ['%Y-%m-%d %H:%M:%S', [Validators.required]],
+ dateFormat: ['%Y-%m-%d %H:%M:%S', [Validators.required, Validators.pattern(/^[^\s].*[^\s]$/)]],
logFormat: ['%(asctime)s - |%(levelname)s| - [%(filename)s] - %(module)s - %(funcName)s - %(lineno)d - %(message)s',
- [Validators.required]],
+ [Validators.required, Validators.pattern(/^[^\s].*[^\s]$/)]],
type: ['remote', [Validators.required]],
remote: this.fb.group({
enabled: [false],
@@ -204,7 +205,7 @@ export class GatewayConfigurationComponent implements OnInit {
securityGroup.get('type').valueChanges.subscribe(type => {
this.removeAllSecurityValidators();
if (type === SecurityTypes.ACCESS_TOKEN) {
- securityGroup.get('accessToken').addValidators([Validators.required]);
+ securityGroup.get('accessToken').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]);
securityGroup.get('accessToken').updateValueAndValidity();
} else if (type === SecurityTypes.TLS_PRIVATE_KEY) {
securityGroup.get('caCert').addValidators([Validators.required]);
@@ -214,7 +215,7 @@ export class GatewayConfigurationComponent implements OnInit {
securityGroup.get('cert').addValidators([Validators.required]);
securityGroup.get('cert').updateValueAndValidity();
} else if (type === SecurityTypes.TLS_ACCESS_TOKEN) {
- securityGroup.get('accessToken').addValidators([Validators.required]);
+ securityGroup.get('accessToken').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]);
securityGroup.get('accessToken').updateValueAndValidity();
securityGroup.get('caCert').addValidators([Validators.required]);
securityGroup.get('caCert').updateValueAndValidity();
@@ -239,7 +240,7 @@ export class GatewayConfigurationComponent implements OnInit {
storageGroup.get('read_records_count').updateValueAndValidity({emitEvent: false});
storageGroup.get('max_records_count').updateValueAndValidity({emitEvent: false});
} else if (type === StorageTypes.FILE) {
- storageGroup.get('data_folder_path').addValidators([Validators.required]);
+ storageGroup.get('data_folder_path').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]);
storageGroup.get('max_file_count').addValidators(
[Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]);
storageGroup.get('max_read_records_count').addValidators(
@@ -251,7 +252,7 @@ export class GatewayConfigurationComponent implements OnInit {
storageGroup.get('max_read_records_count').updateValueAndValidity({emitEvent: false});
storageGroup.get('max_records_per_file').updateValueAndValidity({emitEvent: false});
} else if (type === StorageTypes.SQLITE) {
- storageGroup.get('data_file_path').addValidators([Validators.required]);
+ storageGroup.get('data_file_path').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]);
storageGroup.get('messages_ttl_check_in_hours').addValidators(
[Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]);
storageGroup.get('messages_ttl_in_days').addValidators(
@@ -342,8 +343,8 @@ export class GatewayConfigurationComponent implements OnInit {
this.initialCredentials = credentials;
if (credentials.credentialsType === DeviceCredentialsType.ACCESS_TOKEN || security.type === SecurityTypes.TLS_ACCESS_TOKEN) {
this.gatewayConfigGroup.get('thingsboard.security.type').setValue(security.type === SecurityTypes.TLS_ACCESS_TOKEN
- ? SecurityTypes.TLS_ACCESS_TOKEN
- : SecurityTypes.ACCESS_TOKEN);
+ ? SecurityTypes.TLS_ACCESS_TOKEN
+ : SecurityTypes.ACCESS_TOKEN);
this.gatewayConfigGroup.get('thingsboard.security.accessToken').setValue(credentials.credentialsId);
if(security.type === SecurityTypes.TLS_ACCESS_TOKEN) {
this.gatewayConfigGroup.get('thingsboard.security.caCert').setValue(security.caCert);
@@ -407,9 +408,9 @@ export class GatewayConfigurationComponent implements OnInit {
addCommand(command: any = {}): void {
const commandsFormArray = this.commandFormArray();
const commandFormGroup = this.fb.group({
- attributeOnGateway: [command.attributeOnGateway || null, [Validators.required]],
- command: [command.command || null, [Validators.required]],
- timeout: [command.timeout || null, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
+ attributeOnGateway: [command.attributeOnGateway || null, [Validators.required, Validators.pattern(/^[^.\s]+$/)]],
+ command: [command.command || null, [Validators.required, Validators.pattern(/^[^.\s]+$/)]],
+ timeout: [command.timeout || null, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.pattern(/^[^.\s]+$/)]],
});
commandsFormArray.push(commandFormGroup);
}
@@ -542,7 +543,7 @@ export class GatewayConfigurationComponent implements OnInit {
}
saveConfig(): void {
- const value = this.removeEmpty(this.gatewayConfigGroup.value);
+ const value = deepTrim(this.removeEmpty(this.gatewayConfigGroup.value));
value.thingsboard.statistics.commands = Object.values(value.thingsboard.statistics.commands);
const attributes = [];
attributes.push({
@@ -609,8 +610,8 @@ export class GatewayConfigurationComponent implements OnInit {
credentialsValue.password = securityConfig.password;
}
newCredentials = {
- credentialsType,
- credentialsValue: JSON.stringify(credentialsValue)
+ credentialsType,
+ credentialsValue: JSON.stringify(credentialsValue)
};
}
} else if (securityConfig.type === SecurityTypes.ACCESS_TOKEN || securityConfig.type === SecurityTypes.TLS_ACCESS_TOKEN) {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html
index 973b3e02b7..e195dcece9 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html
@@ -25,13 +25,13 @@
matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()"
matSortDisableClear>
- {{ 'widgets.gateway.created-time' | translate }}
+ {{ 'widgets.gateway.created-time' | translate }}
{{ attribute.ts | date:'yyyy-MM-dd HH:mm:ss' }}
- {{ 'widgets.gateway.status' | translate }}
+ {{ 'widgets.gateway.status' | translate }}
{{ attribute.status }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts
index 2e1deb900a..503f5643bf 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts
@@ -50,6 +50,7 @@ export class GatewayStatisticsComponent implements AfterViewInit {
public general: boolean;
public isNumericData = false;
+ public dataTypeDefined: boolean = false;
public chartInited: boolean;
private flot: TbFlot;
private flotCtx: WidgetContext;
@@ -155,6 +156,7 @@ export class GatewayStatisticsComponent implements AfterViewInit {
subscriptionInfo[0].timeseries = [{name: attr, label: attr}];
this.subscriptionInfo = subscriptionInfo;
this.changeSubscription(subscriptionInfo);
+ this.ctx.defaultSubscription.unsubscribe();
}
private createGeneralChartsSubscription(gateway: BaseData, attrData: string[]) {
@@ -175,8 +177,8 @@ export class GatewayStatisticsComponent implements AfterViewInit {
subscriptionInfo[0].timeseries.push({name: dataKey.name, label: dataKey.label});
});
- this.subscriptionInfo = subscriptionInfo;
this.changeSubscription(subscriptionInfo);
+ this.ctx.defaultSubscription.unsubscribe();
}
private init = () => {
@@ -252,7 +254,10 @@ export class GatewayStatisticsComponent implements AfterViewInit {
return;
}
this.dataSource.data = this.subscription.data.length ? this.subscription.data[0].data : [];
- this.isNumericData = this.dataSource.data.every(data => !isNaN(+data[1]));
+ if (this.dataSource.data.length && !this.dataTypeDefined) {
+ this.dataTypeDefined = true;
+ this.isNumericData = this.dataSource.data.every(data => !isNaN(+data[1]));
+ }
}
@@ -263,6 +268,7 @@ export class GatewayStatisticsComponent implements AfterViewInit {
if (this.ctx.datasources[0].entity) {
this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.timeseries, subscriptionInfo, this.subscriptionOptions,
false, true).subscribe(subscription => {
+ this.dataTypeDefined = false;
this.subscription = subscription;
this.isDataOnlyNumbers();
this.legendData = this.subscription.legendData;
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts
index 5d3ea09447..c543ee99fb 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts
@@ -41,14 +41,14 @@ export class GatewayLogsSettingsComponent extends WidgetSettingsComponent {
protected defaultSettings(): WidgetSettings {
return {
isConnectorLog: false,
- connectorLogState: 'default'
+ connectorLogState: ''
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.gatewayLogSettingForm = this.fb.group({
isConnectorLog: [false, []],
- connectorLogState: ['default', Validators.required]
+ connectorLogState: ['', Validators.required]
});
}
diff --git a/ui-ngx/src/app/shared/components/file-input.component.scss b/ui-ngx/src/app/shared/components/file-input.component.scss
index 45ed704336..8577744995 100644
--- a/ui-ngx/src/app/shared/components/file-input.component.scss
+++ b/ui-ngx/src/app/shared/components/file-input.component.scss
@@ -83,6 +83,12 @@ $previewSize: 100px !default;
}
}
}
+
+ .input-hint {
+ cursor:pointer;
+ transform: translate(5px,-3px);
+ position: absolute;
+ }
}
:host ::ng-deep {
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json
index 7397f82993..053c1713bc 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -2683,7 +2683,7 @@
"rpc-command-result": "Result",
"rpc-command-edit-params": "Edit parameters",
"select-connector": "Select connector",
- "gateway-configuration": "Gateway Configuration",
+ "gateway-configuration": "General Configuration",
"docker-label": "In order to run ThingsBoard IoT gateway in docker with credentials for this device you can use the following commands.",
"copy-command": "Copy docker command",
"create-new-gateway": "Create a new gateway",
@@ -5329,8 +5329,8 @@
"events-title": "Gateway events form title",
"events-filter": "Events filter",
"event-key-contains": "Event key contains...",
- "is-connector": "Is Connector",
- "state-param-name": "State parameter connector name",
+ "is-connector": "Show for the connector",
+ "state-param-name": "Connector state parameter key",
"status": "Status",
"message": "Message",
"created-time": "Created time"