Merge pull request #12467 from maxunbearable/task/5416-mobile-action-device-provisioning

Added device provision mobile action
This commit is contained in:
Igor Kulikov 2025-03-13 18:44:58 +02:00 committed by GitHub
commit a90cdd38e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 86 additions and 7 deletions

View File

@ -36,6 +36,17 @@
</mat-form-field> </mat-form-field>
</div> </div>
<ng-container [formGroup]="mobileActionTypeFormGroup" [ngSwitch]="mobileActionFormGroup.get('type').value"> <ng-container [formGroup]="mobileActionTypeFormGroup" [ngSwitch]="mobileActionFormGroup.get('type').value">
<ng-template [ngSwitchCase]="mobileActionType.deviceProvision">
<tb-js-func
formControlName="handleProvisionSuccessFunction"
functionName="handleProvisionSuccess"
withModules
[functionArgs]="['deviceName', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']"
[globalVariables]="functionScopeVariables"
[editorCompleter]="customActionEditorCompleter"
hideBrackets
></tb-js-func>
</ng-template>
<ng-template [ngSwitchCase]="mobileActionFormGroup.get('type').value === mobileActionType.mapDirection || <ng-template [ngSwitchCase]="mobileActionFormGroup.get('type').value === mobileActionType.mapDirection ||
mobileActionFormGroup.get('type').value === mobileActionType.mapLocation ? mobileActionFormGroup.get('type').value === mobileActionType.mapLocation ?
mobileActionFormGroup.get('type').value : ''"> mobileActionFormGroup.get('type').value : ''">

View File

@ -27,7 +27,7 @@ import {
WidgetActionType, WidgetActionType,
WidgetMobileActionDescriptor, WidgetMobileActionDescriptor,
WidgetMobileActionType, WidgetMobileActionType,
widgetMobileActionTypeTranslationMap widgetMobileActionTypeTranslationMap,
} from '@shared/models/widget.models'; } from '@shared/models/widget.models';
import { CustomActionEditorCompleter } from '@home/components/widget/lib/settings/common/action/custom-action.models'; import { CustomActionEditorCompleter } from '@home/components/widget/lib/settings/common/action/custom-action.models';
import { import {
@ -38,7 +38,8 @@ import {
getDefaultProcessImageFunction, getDefaultProcessImageFunction,
getDefaultProcessLaunchResultFunction, getDefaultProcessLaunchResultFunction,
getDefaultProcessLocationFunction, getDefaultProcessLocationFunction,
getDefaultProcessQrCodeFunction getDefaultProcessQrCodeFunction,
getDefaultProvisionSuccessFunction
} from '@home/components/widget/lib/settings/common/action/mobile-action-editor.models'; } from '@home/components/widget/lib/settings/common/action/mobile-action-editor.models';
import { WidgetService } from '@core/http/widget.service'; import { WidgetService } from '@core/http/widget.service';
import { TbFunction } from '@shared/models/js-function.models'; import { TbFunction } from '@shared/models/js-function.models';
@ -254,6 +255,18 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
this.fb.control(processLocationFunction, [Validators.required]) this.fb.control(processLocationFunction, [Validators.required])
); );
break; break;
case WidgetMobileActionType.deviceProvision:
let handleProvisionSuccessFunction = action?.handleProvisionSuccessFunction;
if (changed) {
const defaultProvisionSuccessFunction = getDefaultProvisionSuccessFunction();
if (defaultProvisionSuccessFunction !== handleProvisionSuccessFunction) {
handleProvisionSuccessFunction = defaultProvisionSuccessFunction;
}
}
this.mobileActionTypeFormGroup.addControl(
'handleProvisionSuccessFunction',
this.fb.control(handleProvisionSuccessFunction, [Validators.required])
);
} }
} }
this.mobileActionTypeFormGroup.valueChanges.pipe( this.mobileActionTypeFormGroup.valueChanges.pipe(

View File

@ -138,6 +138,18 @@ const processLocationFunction: TbFunction =
' }, 100);\n' + ' }, 100);\n' +
'}'; '}';
const provisionSuccessFunction: TbFunction =
'// Function body to handle device provision success. \n' +
'// - deviceName - name of device that was successfully provisioned.\n' +
'\n' +
'showDeviceProvisionSuccess(deviceName);\n' +
'\n' +
'function showDeviceProvisionSuccess(deviceName) {\n' +
' setTimeout(function() {\n' +
' widgetContext.showSuccessToast(`Device ` + deviceName + ` was successfully provisioned`).subscribe();\n' +
' }, 100);\n' +
'}\n';
const handleEmptyResultFunctionTemplate: TbFunction = const handleEmptyResultFunctionTemplate: TbFunction =
'// Optional function body to handle empty result. \n' + '// Optional function body to handle empty result. \n' +
'// Usually this happens when user cancels the action (for ex. by pressing phone back button). \n\n' + '// Usually this happens when user cancels the action (for ex. by pressing phone back button). \n\n' +
@ -145,7 +157,7 @@ const handleEmptyResultFunctionTemplate: TbFunction =
'\n' + '\n' +
'function showEmptyResultDialog(message) {\n' + 'function showEmptyResultDialog(message) {\n' +
' setTimeout(function() {\n' + ' setTimeout(function() {\n' +
' widgetContext.dialogs.alert(\'Empty result\', message).subscribe();\n' + ' widgetContext.showInfoToast(message).subscribe();\n' +
' }, 100);\n' + ' }, 100);\n' +
'}\n'; '}\n';
@ -241,6 +253,8 @@ export const getDefaultProcessQrCodeFunction = () => processQrCodeFunction;
export const getDefaultProcessLocationFunction = () => processLocationFunction; export const getDefaultProcessLocationFunction = () => processLocationFunction;
export const getDefaultProvisionSuccessFunction = () => provisionSuccessFunction;
export const getDefaultGetLocationFunction = () => getLocationFunctionTemplate; export const getDefaultGetLocationFunction = () => getLocationFunctionTemplate;
export const getDefaultGetPhoneNumberFunction = () => getPhoneNumberFunctionTemplate; export const getDefaultGetPhoneNumberFunction = () => getPhoneNumberFunctionTemplate;
@ -272,6 +286,9 @@ export const getDefaultHandleEmptyResultFunction = (type: WidgetMobileActionType
case WidgetMobileActionType.takeScreenshot: case WidgetMobileActionType.takeScreenshot:
message = 'Take screenshot action was cancelled!'; message = 'Take screenshot action was cancelled!';
break; break;
case WidgetMobileActionType.deviceProvision:
message = 'Device provision was not invoked!';
break;
} }
return handleEmptyResultFunctionTemplate.replace('--MESSAGE--', message); return handleEmptyResultFunctionTemplate.replace('--MESSAGE--', message);
}; };
@ -303,6 +320,9 @@ export const getDefaultHandleErrorFunction = (type: WidgetMobileActionType): TbF
case WidgetMobileActionType.takeScreenshot: case WidgetMobileActionType.takeScreenshot:
title = 'Failed to take screenshot'; title = 'Failed to take screenshot';
break; break;
case WidgetMobileActionType.deviceProvision:
title = 'Failed to make device provision';
break;
} }
return handleErrorFunctionTemplate.replace('--TITLE--', title); return handleErrorFunctionTemplate.replace('--TITLE--', title);
}; };

View File

@ -1177,6 +1177,7 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges,
case WidgetMobileActionType.scanQrCode: case WidgetMobileActionType.scanQrCode:
case WidgetMobileActionType.getLocation: case WidgetMobileActionType.getLocation:
case WidgetMobileActionType.takeScreenshot: case WidgetMobileActionType.takeScreenshot:
case WidgetMobileActionType.deviceProvision:
argsObservable = of([]); argsObservable = of([]);
break; break;
case WidgetMobileActionType.mapDirection: case WidgetMobileActionType.mapDirection:
@ -1266,6 +1267,26 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges,
); );
} }
break; break;
case WidgetMobileActionType.deviceProvision:
const deviceName = actionResult.deviceName;
if (isNotEmptyTbFunction(mobileAction.handleProvisionSuccessFunction)) {
compileTbFunction(this.http, mobileAction.handleProvisionSuccessFunction, 'deviceName', '$event', 'widgetContext', 'entityId',
'entityName', 'additionalParams', 'entityLabel').subscribe(
{
next: (compiled) => {
try {
compiled.execute(deviceName, $event, this.widgetContext, entityId, entityName, additionalParams, entityLabel);
} catch (e) {
console.error(e);
}
},
error: (err) => {
console.error(err);
}
}
);
}
break;
case WidgetMobileActionType.scanQrCode: case WidgetMobileActionType.scanQrCode:
const code = actionResult.code; const code = actionResult.code;
const format = actionResult.format; const format = actionResult.format;

View File

@ -47,6 +47,7 @@ import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-con
import { TbFunction } from '@shared/models/js-function.models'; import { TbFunction } from '@shared/models/js-function.models';
import { FormProperty, jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; import { FormProperty, jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Device } from '@shared/models/device.models';
export enum widgetType { export enum widgetType {
timeseries = 'timeseries', timeseries = 'timeseries',
@ -589,7 +590,8 @@ export enum WidgetMobileActionType {
scanQrCode = 'scanQrCode', scanQrCode = 'scanQrCode',
makePhoneCall = 'makePhoneCall', makePhoneCall = 'makePhoneCall',
getLocation = 'getLocation', getLocation = 'getLocation',
takeScreenshot = 'takeScreenshot' takeScreenshot = 'takeScreenshot',
deviceProvision = 'deviceProvision',
} }
export enum MapItemType { export enum MapItemType {
@ -625,7 +627,8 @@ export const widgetMobileActionTypeTranslationMap = new Map<WidgetMobileActionTy
[ WidgetMobileActionType.scanQrCode, 'widget-action.mobile.scan-qr-code' ], [ WidgetMobileActionType.scanQrCode, 'widget-action.mobile.scan-qr-code' ],
[ WidgetMobileActionType.makePhoneCall, 'widget-action.mobile.make-phone-call' ], [ WidgetMobileActionType.makePhoneCall, 'widget-action.mobile.make-phone-call' ],
[ WidgetMobileActionType.getLocation, 'widget-action.mobile.get-location' ], [ WidgetMobileActionType.getLocation, 'widget-action.mobile.get-location' ],
[ WidgetMobileActionType.takeScreenshot, 'widget-action.mobile.take-screenshot' ] [ WidgetMobileActionType.takeScreenshot, 'widget-action.mobile.take-screenshot' ],
[ WidgetMobileActionType.deviceProvision, 'widget-action.mobile.device-provision' ]
] ]
); );
@ -656,10 +659,15 @@ export interface MobileLocationResult {
longitude: number; longitude: number;
} }
export interface MobileDeviceProvisionResult {
deviceName: string;
}
export type MobileActionResult = MobileLaunchResult & export type MobileActionResult = MobileLaunchResult &
MobileImageResult & MobileImageResult &
MobileQrCodeResult & MobileQrCodeResult &
MobileLocationResult; MobileLocationResult &
MobileDeviceProvisionResult;
export interface WidgetMobileActionResult<T extends MobileActionResult> { export interface WidgetMobileActionResult<T extends MobileActionResult> {
result?: T; result?: T;
@ -668,6 +676,10 @@ export interface WidgetMobileActionResult<T extends MobileActionResult> {
hasError: boolean; hasError: boolean;
} }
export interface ProvisionSuccessDescriptor {
handleProvisionSuccessFunction: TbFunction;
}
export interface ProcessImageDescriptor { export interface ProcessImageDescriptor {
processImageFunction: TbFunction; processImageFunction: TbFunction;
} }
@ -696,7 +708,8 @@ export type WidgetMobileActionDescriptors = ProcessImageDescriptor &
LaunchMapDescriptor & LaunchMapDescriptor &
ScanQrCodeDescriptor & ScanQrCodeDescriptor &
MakePhoneCallDescriptor & MakePhoneCallDescriptor &
GetLocationDescriptor; GetLocationDescriptor &
ProvisionSuccessDescriptor;
export interface WidgetMobileActionDescriptor extends WidgetMobileActionDescriptors { export interface WidgetMobileActionDescriptor extends WidgetMobileActionDescriptors {
type: WidgetMobileActionType; type: WidgetMobileActionType;

View File

@ -6569,6 +6569,7 @@
"URL": "URL", "URL": "URL",
"url-required": "URL is required.", "url-required": "URL is required.",
"mobile": { "mobile": {
"device-provision": "Device provision",
"action-type": "Mobile action type", "action-type": "Mobile action type",
"select-action-type": "Select mobile action type", "select-action-type": "Select mobile action type",
"action-type-required": "Mobile action type is required", "action-type-required": "Mobile action type is required",