Browse Source

UI: Add new resource type text

pull/13350/head
ArtemDzhereleiko 9 months ago
committed by Vladyslav_Prykhodko
parent
commit
b9a9348eac
  1. 8
      ui-ngx/src/app/core/http/entity.service.ts
  2. 8
      ui-ngx/src/app/core/http/resource.service.ts
  3. 6
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  4. 54
      ui-ngx/src/app/modules/home/components/resources/resources-dialog.component.html
  5. 24
      ui-ngx/src/app/modules/home/components/resources/resources-dialog.component.scss
  6. 113
      ui-ngx/src/app/modules/home/components/resources/resources-dialog.component.ts
  7. 2
      ui-ngx/src/app/modules/home/components/resources/resources-library.component.html
  8. 18
      ui-ngx/src/app/modules/home/components/resources/resources-library.component.ts
  9. 10
      ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html
  10. 24
      ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts
  11. 2
      ui-ngx/src/app/modules/home/pages/admin/admin.module.ts
  12. 2
      ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts
  13. 2
      ui-ngx/src/app/modules/home/pages/admin/resource/resources-table-header.component.ts
  14. 11
      ui-ngx/src/app/shared/components/entity/entity-list.component.html
  15. 29
      ui-ngx/src/app/shared/components/entity/entity-list.component.ts
  16. 12
      ui-ngx/src/app/shared/models/resource.models.ts
  17. 6
      ui-ngx/src/assets/locale/locale.constant-en_US.json

8
ui-ngx/src/app/core/http/entity.service.ts

@ -100,6 +100,7 @@ import { OAuth2Service } from '@core/http/oauth2.service';
import { MobileAppService } from '@core/http/mobile-app.service';
import { PlatformType } from '@shared/models/oauth2.models';
import { AiModelService } from '@core/http/ai-model.service';
import { ResourceType } from "@shared/models/resource.models";
@Injectable({
providedIn: 'root'
@ -297,6 +298,11 @@ export class EntityService {
(id) => this.ruleChainService.getRuleChain(id, config),
entityIds);
break;
case EntityType.TB_RESOURCE:
observable = this.getEntitiesByIdsObservable(
(id) => this.resourceService.getResource(id, config),
entityIds);
break;
}
return observable;
}
@ -472,7 +478,7 @@ export class EntityService {
break;
case EntityType.TB_RESOURCE:
pageLink.sortOrder.property = 'title';
entitiesObservable = this.resourceService.getTenantResources(pageLink, config);
entitiesObservable = this.resourceService.getTenantResources(pageLink, subType as ResourceType, config);
break;
case EntityType.QUEUE_STATS:
pageLink.sortOrder.property = 'createdTime';

8
ui-ngx/src/app/core/http/resource.service.ts

@ -47,8 +47,12 @@ export class ResourceService {
return this.http.get<PageData<ResourceInfo>>(url, defaultHttpOptionsFromConfig(config));
}
public getTenantResources(pageLink: PageLink, config?: RequestConfig): Observable<PageData<ResourceInfo>> {
return this.http.get<PageData<ResourceInfo>>(`/api/resource/tenant${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
public getTenantResources(pageLink: PageLink, resourceType?: ResourceType, config?: RequestConfig): Observable<PageData<ResourceInfo>> {
let url = `/api/resource${pageLink.toQuery()}`;
if (isNotEmptyStr(resourceType)) {
url += `&resourceType=${resourceType}`;
}
return this.http.get<PageData<ResourceInfo>>(url, defaultHttpOptionsFromConfig(config));
}
public getResource(resourceId: string, config?: RequestConfig): Observable<Resource> {

6
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -205,6 +205,8 @@ import {
} from '@home/components/calculated-fields/components/test-arguments/calculated-field-test-arguments.component';
import { CheckConnectivityDialogComponent } from '@home/components/ai-model/check-connectivity-dialog.component';
import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-dialog.component';
import { ResourcesDialogComponent } from "@home/components/resources/resources-dialog.component";
import { ResourcesLibraryComponent } from "@home/components/resources/resources-library.component";
@NgModule({
declarations:
@ -358,6 +360,8 @@ import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-dialo
CalculatedFieldTestArgumentsComponent,
CheckConnectivityDialogComponent,
AIModelDialogComponent,
ResourcesDialogComponent,
ResourcesLibraryComponent,
],
imports: [
CommonModule,
@ -505,6 +509,8 @@ import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-dialo
CalculatedFieldTestArgumentsComponent,
CheckConnectivityDialogComponent,
AIModelDialogComponent,
ResourcesDialogComponent,
ResourcesLibraryComponent,
],
providers: [
WidgetComponentService,

54
ui-ngx/src/app/modules/home/components/resources/resources-dialog.component.html

@ -0,0 +1,54 @@
<!--
Copyright © 2016-2025 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<form (ngSubmit)="save()" style="width: 600px;">
<mat-toolbar color="primary">
<h2>{{ 'resource.add' | translate }}</h2>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
<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>
<tb-resources-library #resourcesComponent
[standalone]="true"
[entity]="resources"
[defaultResourceType]="ResourceType.TEXT"
[resourceTypes]="[ResourceType.TEXT]"
[isEdit]="true">
</tb-resources-library>
</div>
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
cdkFocusInitial
[disabled]="(isLoading$ | async)"
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || resourcesComponent.entityForm?.invalid || !resourcesComponent.entityForm?.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
</div>
</form>

24
ui-ngx/src/app/modules/home/components/resources/resources-dialog.component.scss

@ -0,0 +1,24 @@
/**
* Copyright © 2016-2025 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host ::ng-deep {
.mat-mdc-dialog-content {
display: flex;
flex-direction: column;
height: 100%;
padding: 0 !important;
}
}

113
ui-ngx/src/app/modules/home/components/resources/resources-dialog.component.ts

@ -0,0 +1,113 @@
///
/// Copyright © 2016-2025 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { AfterViewInit, Component, Inject, SkipSelf, ViewChild } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormGroupDirective, NgForm, UntypedFormControl } from '@angular/forms';
import { EntityType } from '@shared/models/entity-type.models';
import { map } from 'rxjs/operators';
import { ResourcesLibraryComponent } from "@home/components/resources/resources-library.component";
import { ErrorStateMatcher } from "@angular/material/core";
import { Resource, ResourceType } from "@shared/models/resource.models";
import { ResourceService } from "@core/http/resource.service";
export interface ResourcesDialogData {
resources?: Resource;
isAdd?: boolean;
}
@Component({
selector: 'tb-resources-dialog',
templateUrl: './resources-dialog.component.html',
providers: [{provide: ErrorStateMatcher, useExisting: ResourcesDialogComponent}],
styleUrls: ['./resources-dialog.component.scss']
})
export class ResourcesDialogComponent extends DialogComponent<ResourcesDialogComponent, Resource> implements ErrorStateMatcher, AfterViewInit {
readonly entityType = EntityType;
ResourceType = ResourceType;
isAdd = false;
submitted = false;
resources: Resource;
@ViewChild('resourcesComponent', {static: true}) resourcesComponent: ResourcesLibraryComponent;
constructor(protected store: Store<AppState>,
protected router: Router,
protected dialogRef: MatDialogRef<ResourcesDialogComponent, Resource>,
@Inject(MAT_DIALOG_DATA) public data: ResourcesDialogData,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
private resourceService: ResourceService) {
super(store, router, dialogRef);
if (this.data.isAdd) {
this.isAdd = true;
}
if (this.data.resources) {
this.resources = this.data.resources;
}
}
ngAfterViewInit(): void {
if (this.isAdd) {
setTimeout(() => {
this.resourcesComponent.entityForm.markAsDirty();
}, 0);
}
}
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
const customErrorState = !!(control && control.invalid && this.submitted);
return originalErrorState || customErrorState;
}
cancel(): void {
this.dialogRef.close(null);
}
save(): void {
this.submitted = true;
if (this.resourcesComponent.entityForm.valid) {
const resource = {...this.resourcesComponent.entityFormValue()};
if (Array.isArray(resource.data)) {
const resources = [];
resource.data.forEach((data, index) => {
resources.push({
resourceType: resource.resourceType,
data,
fileName: resource.fileName[index],
title: resource.title
});
});
this.resourceService.saveResources(resources, {resendRequest: true}).pipe(
map((response) => response[0])
).subscribe(result => this.dialogRef.close(result));
} else {
this.resourceService.saveResource(resource).subscribe(result => this.dialogRef.close(result));
}
}
}
}

2
ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html → ui-ngx/src/app/modules/home/components/resources/resources-library.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<div class="tb-details-buttons xs:flex xs:flex-col">
<div class="tb-details-buttons xs:flex xs:flex-col" *ngIf="!standalone">
<button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'open')"

18
ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.ts → ui-ngx/src/app/modules/home/components/resources/resources-library.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { ChangeDetectorRef, Component, Inject, Input, OnDestroy, OnInit, Optional } from '@angular/core';
import { Subject } from 'rxjs';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@ -40,8 +40,16 @@ import { getCurrentAuthState } from '@core/auth/auth.selectors';
})
export class ResourcesLibraryComponent extends EntityComponent<Resource> implements OnInit, OnDestroy {
@Input()
standalone = false;
@Input()
resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS, ResourceType.TEXT];
@Input()
defaultResourceType = ResourceType.LWM2M_MODEL;
readonly resourceType = ResourceType;
readonly resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS];
readonly resourceTypesTranslationMap = ResourceTypeTranslationMap;
readonly maxResourceSize = getCurrentAuthState(this.store).maxResourceSize;
@ -49,8 +57,8 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
@Inject('entity') protected entityValue: Resource,
@Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<Resource>,
@Optional() @Inject('entity') protected entityValue: Resource,
@Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<Resource>,
public fb: FormBuilder,
protected cd: ChangeDetectorRef) {
super(store, fb, entityValue, entitiesTableConfigValue, cd);
@ -138,7 +146,7 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
private observeResourceTypeChange(): void {
this.entityForm.get('resourceType').valueChanges.pipe(
startWith(ResourceType.LWM2M_MODEL),
startWith(this.defaultResourceType || ResourceType.LWM2M_MODEL),
takeUntil(this.destroy$)
).subscribe((type: ResourceType) => this.onResourceTypeChange(type));
}

10
ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html

@ -70,6 +70,16 @@
{{ 'rule-node-config.ai.user-prompt-blank' | translate }}
</mat-error>
</mat-form-field>
<tb-entity-list
class="flex-1"
allowCreateNew
placeholderText="{{ 'rule-node-config.ai.ai-resources' | translate }}"
[inlineField]="true"
[entityType]="EntityType.TB_RESOURCE"
[subType]="ResourceType.TEXT"
(createNew)="createAiResources($event, 'resourceIds')"
formControlName="resourceIds">
</tb-entity-list>
</div>
</mat-expansion-panel>
</div>

24
ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts

@ -24,6 +24,8 @@ import { AiModel, AiRuleNodeResponseFormatTypeOnlyText, ResponseFormat } from '@
import { deepTrim } from '@core/utils';
import { TranslateService } from '@ngx-translate/core';
import { jsonRequired } from '@shared/components/json-object-edit.component';
import { Resource, ResourceType } from "@shared/models/resource.models";
import { ResourcesDialogComponent, ResourcesDialogData } from "@home/components/resources/resources-dialog.component";
@Component({
selector: 'tb-external-node-ai-config',
@ -38,6 +40,9 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent {
responseFormat = ResponseFormat;
EntityType = EntityType;
ResourceType = ResourceType;
constructor(private fb: UntypedFormBuilder,
private translate: TranslateService,
private dialog: MatDialog) {
@ -53,6 +58,7 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent {
modelId: [configuration?.modelId ?? null, [Validators.required]],
systemPrompt: [configuration?.systemPrompt ?? '', [Validators.maxLength(500_000), Validators.pattern(/.*\S.*/)]],
userPrompt: [configuration?.userPrompt ?? '', [Validators.required, Validators.maxLength(500_000), Validators.pattern(/.*\S.*/)]],
resourceIds: [configuration?.resourceIds ?? []],
responseFormat: this.fb.group({
type: [configuration?.responseFormat?.type ?? ResponseFormat.JSON, []],
schema: [configuration?.responseFormat?.schema ?? null, [jsonRequired]],
@ -116,5 +122,23 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent {
this.aiConfigForm.get(formControl).markAsDirty();
}
});
};
createAiResources(name: string, formControl: string) {
this.dialog.open<ResourcesDialogComponent, ResourcesDialogData, Resource>(ResourcesDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
resources: {title: name, resourceType: ResourceType.TEXT},
isAdd: true
}
}).afterClosed()
.subscribe((resource) => {
if (resource) {
const resourceIds = [...(this.aiConfigForm.get(formControl).value || []), resource.id.id];
this.aiConfigForm.get(formControl).patchValue(resourceIds);
this.aiConfigForm.get(formControl).markAsDirty();
}
});
}
}

2
ui-ngx/src/app/modules/home/pages/admin/admin.module.ts

@ -26,7 +26,6 @@ import { HomeComponentsModule } from '@modules/home/components/home-components.m
import { SmsProviderComponent } from '@home/pages/admin/sms-provider.component';
import { SendTestSmsDialogComponent } from '@home/pages/admin/send-test-sms-dialog.component';
import { HomeSettingsComponent } from '@home/pages/admin/home-settings.component';
import { ResourcesLibraryComponent } from '@home/pages/admin/resource/resources-library.component';
import { ResourceTabsComponent } from '@home/pages/admin/resource/resource-tabs.component';
import { ResourcesTableHeaderComponent } from '@home/pages/admin/resource/resources-table-header.component';
import { QueueComponent } from '@home/pages/admin/queue/queue.component';
@ -49,7 +48,6 @@ import { ResourceLibraryTabsComponent } from '@home/pages/admin/resource/resourc
SendTestSmsDialogComponent,
SecuritySettingsComponent,
HomeSettingsComponent,
ResourcesLibraryComponent,
ResourceTabsComponent,
ResourceLibraryTabsComponent,
ResourcesTableHeaderComponent,

2
ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts

@ -32,7 +32,7 @@ import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Authority } from '@shared/models/authority.enum';
import { ResourcesLibraryComponent } from '@home/pages/admin/resource/resources-library.component';
import { ResourcesLibraryComponent } from '@home/components/resources/resources-library.component';
import { PageLink } from '@shared/models/page/page-link';
import { EntityAction } from '@home/models/entity/entity-component.models';
import { map } from 'rxjs/operators';

2
ui-ngx/src/app/modules/home/pages/admin/resource/resources-table-header.component.ts

@ -28,7 +28,7 @@ import { PageLink } from '@shared/models/page/page-link';
})
export class ResourcesTableHeaderComponent extends EntityTableHeaderComponent<Resource, PageLink, ResourceInfo> {
readonly resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS];
readonly resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS, ResourceType.TEXT];
readonly resourceTypesTranslationMap = ResourceTypeTranslationMap;
constructor(protected store: Store<AppState>) {

11
ui-ngx/src/app/shared/components/entity/entity-list.component.html

@ -40,6 +40,12 @@
[matAutocompleteConnectedTo]="origin"
[matAutocomplete]="entityAutocomplete"
[matChipInputFor]="chipList">
<button mat-button color="primary" matSuffix
type="button"
*ngIf="allowCreateNew && !disabled"
(click)="createNewEntity($event)">
<span style="white-space: nowrap">{{ 'entity.create-new' | translate }}</span>
</button>
</mat-chip-grid>
<mat-autocomplete #entityAutocomplete="matAutocomplete"
class="tb-autocomplete"
@ -56,6 +62,11 @@
<span>
{{ 'entity.no-entities-matching' | translate: {entity: searchText} }}
</span>
@if (allowCreateNew) {
<span>
<a translate (click)="createNewEntity($event, searchText)">entity.create-new-key</a>
</span>
}
</ng-template>
</div>
</mat-option>

29
ui-ngx/src/app/shared/components/entity/entity-list.component.ts

@ -14,7 +14,18 @@
/// limitations under the License.
///
import { Component, ElementRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
import {
Component,
ElementRef,
EventEmitter,
forwardRef,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
ViewChild
} from '@angular/core';
import {
ControlValueAccessor,
NG_VALIDATORS,
@ -93,6 +104,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
}
@Input()
@coerceBoolean()
disabled: boolean;
@Input()
@ -109,6 +121,13 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
@coerceBoolean()
inlineField: boolean;
@Input()
@coerceBoolean()
allowCreateNew: boolean;
@Output()
createNew = new EventEmitter<string>();
@ViewChild('entityInput') entityInput: ElementRef<HTMLInputElement>;
@ViewChild('entityAutocomplete') matAutocomplete: MatAutocomplete;
@ViewChild('chipList', {static: true}) chipList: MatChipGrid;
@ -136,6 +155,11 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
this.entityListFormGroup.get('entities').updateValueAndValidity();
}
createNewEntity($event: Event, searchText?: string) {
$event.stopPropagation();
this.createNew.emit(searchText);
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
@ -201,6 +225,9 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
this.modelValue = null;
}
this.dirty = true;
if (this.entityInput) {
this.entityInput.nativeElement.value = '';
}
}
validate(): ValidationErrors | null {

12
ui-ngx/src/app/shared/models/resource.models.ts

@ -24,7 +24,8 @@ export enum ResourceType {
LWM2M_MODEL = 'LWM2M_MODEL',
PKCS_12 = 'PKCS_12',
JKS = 'JKS',
JS_MODULE = 'JS_MODULE'
JS_MODULE = 'JS_MODULE',
TEXT = 'TEXT',
}
export enum ResourceSubType {
@ -57,7 +58,8 @@ export const ResourceTypeTranslationMap = new Map<ResourceType, string>(
[ResourceType.LWM2M_MODEL, 'resource.type.lwm2m-model'],
[ResourceType.PKCS_12, 'resource.type.pkcs-12'],
[ResourceType.JKS, 'resource.type.jks'],
[ResourceType.JS_MODULE, 'resource.type.js-module']
[ResourceType.JS_MODULE, 'resource.type.js-module'],
[ResourceType.TEXT, 'resource.type.text'],
]
);
@ -76,8 +78,8 @@ export interface TbResourceInfo<D> extends Omit<BaseData<TbResourceId>, 'name' |
title?: string;
resourceType: ResourceType;
resourceSubType?: ResourceSubType;
fileName: string;
public: boolean;
fileName?: string;
public?: boolean;
publicResourceKey?: string;
readonly link?: string;
readonly publicLink?: string;
@ -87,7 +89,7 @@ export interface TbResourceInfo<D> extends Omit<BaseData<TbResourceId>, 'name' |
export type ResourceInfo = TbResourceInfo<any>;
export interface Resource extends ResourceInfo {
data: string;
data?: string;
name?: string;
}

6
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -4488,7 +4488,8 @@
"jks": "JKS",
"js-module": "JS module",
"lwm2m-model": "LWM2M model",
"pkcs-12": "PKCS #12"
"pkcs-12": "PKCS #12",
"text": "Text"
},
"resource-sub-type": "Sub-type",
"sub-type": {
@ -5467,7 +5468,8 @@
"timeout-required": "Timeout is required",
"timeout-validation": "Must be from 1 second to 10 minutes.",
"force-acknowledgement": "Force acknowledgement",
"force-acknowledgement-hint": "If enabled, the incoming message is acknowledged immediately. The model's response is then enqueued as a separate, new message."
"force-acknowledgement-hint": "If enabled, the incoming message is acknowledged immediately. The model's response is then enqueued as a separate, new message.",
"ai-resources": "AI resources"
}
},
"timezone": {

Loading…
Cancel
Save