From 0df4af175f7f16d002771ec64fdcbd402fb14ddb Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 15 Sep 2024 18:02:37 +0300 Subject: [PATCH 1/3] add gangs types --- .../common/src/constants/product-type.enum.ts | 4 +- package-lock.json | 182 +----------------- 2 files changed, 13 insertions(+), 173 deletions(-) diff --git a/libs/common/src/constants/product-type.enum.ts b/libs/common/src/constants/product-type.enum.ts index f0d24ad..bff76e3 100644 --- a/libs/common/src/constants/product-type.enum.ts +++ b/libs/common/src/constants/product-type.enum.ts @@ -4,5 +4,7 @@ export enum ProductType { CPS = 'CPS', DL = 'DL', WPS = 'WPS', - TH_G = '3G', + THREE_G = '3G', + TWO_G = '2G', + ONE_G = '1G', } diff --git a/package-lock.json b/package-lock.json index db7b28d..64662e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2258,6 +2258,8 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", + "optional": true, + "peer": true, "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -2490,17 +2492,6 @@ } } }, - "node_modules/@nestjs/mongoose": { - "version": "10.0.10", - "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-10.0.10.tgz", - "integrity": "sha512-3Ff60ock8nwlAJC823TG91Qy+Qc6av+ddIb6n6wlFsTK0akDF/aTcagX8cF8uI8mWxCWjEwEsgv99vo6p0yJ+w==", - "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", - "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", - "mongoose": "^6.0.2 || ^7.0.0 || ^8.0.0", - "rxjs": "^7.0.0" - } - }, "node_modules/@nestjs/passport": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", @@ -3138,7 +3129,9 @@ "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "optional": true, + "peer": true }, "node_modules/@types/whatwg-url": { "version": "8.2.2", @@ -4131,14 +4124,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/bson": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", - "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", - "engines": { - "node": ">=16.20.1" - } - }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -7666,14 +7651,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/kareem": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", - "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7911,7 +7888,9 @@ "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true, + "peer": true }, "node_modules/merge-descriptors": { "version": "1.0.1", @@ -8146,125 +8125,6 @@ "node": ">=14.20.1" } }, - "node_modules/mongoose": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.5.1.tgz", - "integrity": "sha512-OhVcwVl91A1G6+XpjDcpkGP7l7ikZkxa0DylX7NT/lcEqAjggzSdqDxb48A+xsDxqNAr0ntSJ1yiE3+KJTOd5Q==", - "dependencies": { - "bson": "^6.7.0", - "kareem": "2.6.3", - "mongodb": "6.7.0", - "mpath": "0.9.0", - "mquery": "5.0.0", - "ms": "2.1.3", - "sift": "17.1.3" - }, - "engines": { - "node": ">=16.20.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" - } - }, - "node_modules/mongoose/node_modules/@types/whatwg-url": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", - "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", - "dependencies": { - "@types/webidl-conversions": "*" - } - }, - "node_modules/mongoose/node_modules/mongodb": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.7.0.tgz", - "integrity": "sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==", - "dependencies": { - "@mongodb-js/saslprep": "^1.1.5", - "bson": "^6.7.0", - "mongodb-connection-string-url": "^3.0.0" - }, - "engines": { - "node": ">=16.20.1" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } - } - }, - "node_modules/mongoose/node_modules/mongodb-connection-string-url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", - "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", - "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^13.0.0" - } - }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/mongoose/node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "dependencies": { - "punycode": "^2.3.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/mongoose/node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "engines": { - "node": ">=12" - } - }, - "node_modules/mongoose/node_modules/whatwg-url": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", - "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", - "dependencies": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -8304,25 +8164,6 @@ "node": ">= 0.8" } }, - "node_modules/mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", - "dependencies": { - "debug": "4.x" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -9956,11 +9797,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sift": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", - "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" - }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -10045,6 +9881,8 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "optional": true, + "peer": true, "dependencies": { "memory-pager": "^1.0.2" } From d962d4a7b3fd0676e2e3bf45df4b960f8c16d710 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 15 Sep 2024 18:03:30 +0300 Subject: [PATCH 2/3] finished schedule module --- .../src/helper/convertTimestampToDubaiTime.ts | 23 ++ libs/common/src/helper/getScheduleStatus.ts | 27 ++ src/app.module.ts | 2 + src/schedule/controllers/index.ts | 1 + .../controllers/schedule.controller.ts | 116 ++++++++ src/schedule/dtos/add.schedule.dto.ts | 84 ++++++ src/schedule/dtos/index.ts | 1 + .../interfaces/get.schedule.interface.ts | 10 + src/schedule/schedule.module.ts | 13 + src/schedule/services/index.ts | 1 + src/schedule/services/schedule.service.ts | 281 ++++++++++++++++++ 11 files changed, 559 insertions(+) create mode 100644 libs/common/src/helper/convertTimestampToDubaiTime.ts create mode 100644 libs/common/src/helper/getScheduleStatus.ts create mode 100644 src/schedule/controllers/index.ts create mode 100644 src/schedule/controllers/schedule.controller.ts create mode 100644 src/schedule/dtos/add.schedule.dto.ts create mode 100644 src/schedule/dtos/index.ts create mode 100644 src/schedule/interfaces/get.schedule.interface.ts create mode 100644 src/schedule/schedule.module.ts create mode 100644 src/schedule/services/index.ts create mode 100644 src/schedule/services/schedule.service.ts diff --git a/libs/common/src/helper/convertTimestampToDubaiTime.ts b/libs/common/src/helper/convertTimestampToDubaiTime.ts new file mode 100644 index 0000000..ade7c29 --- /dev/null +++ b/libs/common/src/helper/convertTimestampToDubaiTime.ts @@ -0,0 +1,23 @@ +export function convertTimestampToDubaiTime(timestamp) { + // Convert timestamp to milliseconds + const date = new Date(timestamp * 1000); + + // Convert to Dubai time (UTC+4) + const dubaiTimeOffset = 4 * 60; // 4 hours in minutes + const dubaiTime = new Date(date.getTime() + dubaiTimeOffset * 60 * 1000); + + // Format the date as YYYYMMDD + const year = dubaiTime.getUTCFullYear(); + const month = String(dubaiTime.getUTCMonth() + 1).padStart(2, '0'); // Months are zero-based + const day = String(dubaiTime.getUTCDate()).padStart(2, '0'); + + // Format the time as HH:MM (24-hour format) + const hours = String(dubaiTime.getUTCHours()).padStart(2, '0'); + const minutes = String(dubaiTime.getUTCMinutes()).padStart(2, '0'); + + // Return formatted date and time + return { + date: `${year}${month}${day}`, + time: `${hours}:${minutes}`, + }; +} diff --git a/libs/common/src/helper/getScheduleStatus.ts b/libs/common/src/helper/getScheduleStatus.ts new file mode 100644 index 0000000..e77496c --- /dev/null +++ b/libs/common/src/helper/getScheduleStatus.ts @@ -0,0 +1,27 @@ +export function getScheduleStatus(daysEnabled: string[]): string { + const daysMap: string[] = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + + const schedule: string[] = Array(7).fill('0'); + + daysEnabled.forEach((day) => { + const index: number = daysMap.indexOf(day); + if (index !== -1) { + schedule[index] = '1'; + } + }); + + return schedule.join(''); +} +export function getEnabledDays(schedule: string): string[] { + const daysMap: string[] = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + const enabledDays: string[] = []; + + // Iterate through the schedule string + for (let i = 0; i < schedule.length; i++) { + if (schedule[i] === '1') { + enabledDays.push(daysMap[i]); + } + } + + return enabledDays; +} diff --git a/src/app.module.ts b/src/app.module.ts index 2970185..29d07ac 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -24,6 +24,7 @@ import { AutomationModule } from './automation/automation.module'; import { RegionModule } from './region/region.module'; import { TimeZoneModule } from './timezone/timezone.module'; import { VisitorPasswordModule } from './vistor-password/visitor-password.module'; +import { ScheduleModule } from './schedule/schedule.module'; @Module({ imports: [ ConfigModule.forRoot({ @@ -50,6 +51,7 @@ import { VisitorPasswordModule } from './vistor-password/visitor-password.module RegionModule, TimeZoneModule, VisitorPasswordModule, + ScheduleModule, ], controllers: [AuthenticationController], providers: [ diff --git a/src/schedule/controllers/index.ts b/src/schedule/controllers/index.ts new file mode 100644 index 0000000..edd3a1d --- /dev/null +++ b/src/schedule/controllers/index.ts @@ -0,0 +1 @@ +export * from './schedule.controller'; diff --git a/src/schedule/controllers/schedule.controller.ts b/src/schedule/controllers/schedule.controller.ts new file mode 100644 index 0000000..94ad414 --- /dev/null +++ b/src/schedule/controllers/schedule.controller.ts @@ -0,0 +1,116 @@ +import { ScheduleService } from '../services/schedule.service'; +import { + Body, + Controller, + Get, + Post, + Param, + HttpException, + HttpStatus, + UseGuards, + Put, + Delete, +} from '@nestjs/common'; +import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; +import { AddScheduleDto, EnableScheduleDto } from '../dtos/add.schedule.dto'; + +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; + +@ApiTags('Schedule Module') +@Controller({ + version: '1', + path: 'schedule', +}) +export class ScheduleController { + constructor(private readonly scheduleService: ScheduleService) {} + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post(':deviceUuid') + async addDeviceSchedule( + @Param('deviceUuid') deviceUuid: string, + @Body() addScheduleDto: AddScheduleDto, + ) { + try { + const device = await this.scheduleService.addDeviceSchedule( + deviceUuid, + addScheduleDto, + ); + + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'schedule added successfully', + data: device, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get(':deviceUuid/category/:category') + async getDeviceScheduleByCategory( + @Param('deviceUuid') deviceUuid: string, + @Param('category') category: string, + ) { + try { + return await this.scheduleService.getDeviceScheduleByCategory( + deviceUuid, + category, + ); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Delete(':deviceUuid/:scheduleId') + async deleteDeviceSchedule( + @Param('deviceUuid') deviceUuid: string, + @Param('scheduleId') scheduleId: string, + ) { + try { + await this.scheduleService.deleteDeviceSchedule(deviceUuid, scheduleId); + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'schedule deleted successfully', + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Put('enable/:deviceUuid') + async enableDeviceSchedule( + @Param('deviceUuid') deviceUuid: string, + @Body() enableScheduleDto: EnableScheduleDto, + ) { + try { + await this.scheduleService.enableDeviceSchedule( + deviceUuid, + enableScheduleDto, + ); + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'schedule updated successfully', + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/schedule/dtos/add.schedule.dto.ts b/src/schedule/dtos/add.schedule.dto.ts new file mode 100644 index 0000000..c51a027 --- /dev/null +++ b/src/schedule/dtos/add.schedule.dto.ts @@ -0,0 +1,84 @@ +import { WorkingDays } from '@app/common/constants/working-days'; +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { + IsArray, + IsBoolean, + IsEnum, + IsNotEmpty, + IsString, + ValidateNested, +} from 'class-validator'; +export class FunctionDto { + @ApiProperty({ + description: 'code', + required: true, + }) + @IsString() + @IsNotEmpty() + public code: string; + + @ApiProperty({ + description: 'value', + required: true, + }) + @IsNotEmpty() + public value: any; +} + +// Update the main DTO class +export class AddScheduleDto { + @ApiProperty({ + description: 'category', + required: true, + }) + @IsString() + @IsNotEmpty() + public category: string; + + @ApiProperty({ + description: 'time', + required: true, + }) + @IsString() + @IsNotEmpty() + public time: string; + + @ApiProperty({ + description: 'function', + required: true, + type: FunctionDto, + }) + @ValidateNested() + @Type(() => FunctionDto) + public function: FunctionDto; + + @ApiProperty({ + description: 'days', + enum: WorkingDays, + isArray: true, + required: true, + }) + @IsArray() + @IsEnum(WorkingDays, { each: true }) + @IsNotEmpty() + public days: WorkingDays[]; +} + +export class EnableScheduleDto { + @ApiProperty({ + description: 'scheduleId', + required: true, + }) + @IsString() + @IsNotEmpty() + public scheduleId: string; + + @ApiProperty({ + description: 'enable', + required: true, + }) + @IsBoolean() + @IsNotEmpty() + public enable: boolean; +} diff --git a/src/schedule/dtos/index.ts b/src/schedule/dtos/index.ts new file mode 100644 index 0000000..e71549f --- /dev/null +++ b/src/schedule/dtos/index.ts @@ -0,0 +1 @@ +export * from './add.schedule.dto'; diff --git a/src/schedule/interfaces/get.schedule.interface.ts b/src/schedule/interfaces/get.schedule.interface.ts new file mode 100644 index 0000000..40c2011 --- /dev/null +++ b/src/schedule/interfaces/get.schedule.interface.ts @@ -0,0 +1,10 @@ +export interface getDeviceScheduleInterface { + success: boolean; + result: []; + msg: string; +} +export interface addScheduleDeviceInterface { + success: boolean; + result: boolean; + msg: string; +} diff --git a/src/schedule/schedule.module.ts b/src/schedule/schedule.module.ts new file mode 100644 index 0000000..f0f0a64 --- /dev/null +++ b/src/schedule/schedule.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { ScheduleService } from './services/schedule.service'; +import { ScheduleController } from './controllers/schedule.controller'; +import { ConfigModule } from '@nestjs/config'; +import { DeviceRepositoryModule } from '@app/common/modules/device'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +@Module({ + imports: [ConfigModule, DeviceRepositoryModule], + controllers: [ScheduleController], + providers: [ScheduleService, DeviceRepository], + exports: [ScheduleService], +}) +export class ScheduleModule {} diff --git a/src/schedule/services/index.ts b/src/schedule/services/index.ts new file mode 100644 index 0000000..ad5aed0 --- /dev/null +++ b/src/schedule/services/index.ts @@ -0,0 +1 @@ +export * from './schedule.service'; diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts new file mode 100644 index 0000000..f5a6417 --- /dev/null +++ b/src/schedule/services/schedule.service.ts @@ -0,0 +1,281 @@ +import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { TuyaContext } from '@tuya/tuya-connector-nodejs'; +import { ConfigService } from '@nestjs/config'; +import { AddScheduleDto, EnableScheduleDto } from '../dtos/add.schedule.dto'; +import { + addScheduleDeviceInterface, + getDeviceScheduleInterface, +} from '../interfaces/get.schedule.interface'; + +import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { ProductType } from '@app/common/constants/product-type.enum'; +import { convertTimestampToDubaiTime } from '@app/common/helper/convertTimestampToDubaiTime'; +import { + getEnabledDays, + getScheduleStatus, +} from '@app/common/helper/getScheduleStatus'; + +@Injectable() +export class ScheduleService { + private tuya: TuyaContext; + constructor( + private readonly configService: ConfigService, + private readonly deviceRepository: DeviceRepository, + ) { + const accessKey = this.configService.get('auth-config.ACCESS_KEY'); + const secretKey = this.configService.get('auth-config.SECRET_KEY'); + const tuyaEuUrl = this.configService.get('tuya-config.TUYA_EU_URL'); + this.tuya = new TuyaContext({ + baseUrl: tuyaEuUrl, + accessKey, + secretKey, + }); + } + + async enableDeviceSchedule( + deviceUuid: string, + enableScheduleDto: EnableScheduleDto, + ) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + // Corrected condition for supported device types + if ( + deviceDetails.productDevice.prodType !== ProductType.THREE_G && + deviceDetails.productDevice.prodType !== ProductType.ONE_G && + deviceDetails.productDevice.prodType !== ProductType.TWO_G + ) { + throw new HttpException( + 'This device is not supported for schedule', + HttpStatus.BAD_REQUEST, + ); + } + return await this.enableScheduleDeviceInTuya( + deviceDetails.deviceTuyaUuid, + enableScheduleDto, + ); + } catch (error) { + throw new HttpException( + error.message || 'Error While Updating Schedule', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async enableScheduleDeviceInTuya( + deviceId: string, + enableScheduleDto: EnableScheduleDto, + ): Promise { + try { + const path = `/v2.0/cloud/timer/device/${deviceId}/state`; + const response = await this.tuya.request({ + method: 'PUT', + path, + body: { + enable: enableScheduleDto.enable, + timer_id: enableScheduleDto.scheduleId, + }, + }); + + return response as addScheduleDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error while updating schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async deleteDeviceSchedule(deviceUuid: string, scheduleId: string) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + // Corrected condition for supported device types + if ( + deviceDetails.productDevice.prodType !== ProductType.THREE_G && + deviceDetails.productDevice.prodType !== ProductType.ONE_G && + deviceDetails.productDevice.prodType !== ProductType.TWO_G + ) { + throw new HttpException( + 'This device is not supported for schedule', + HttpStatus.BAD_REQUEST, + ); + } + return await this.deleteScheduleDeviceInTuya( + deviceDetails.deviceTuyaUuid, + scheduleId, + ); + } catch (error) { + throw new HttpException( + error.message || 'Error While Deleting Schedule', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async deleteScheduleDeviceInTuya( + deviceId: string, + scheduleId: string, + ): Promise { + try { + const path = `/v2.0/cloud/timer/device/${deviceId}/batch?timer_ids=${scheduleId}`; + const response = await this.tuya.request({ + method: 'DELETE', + path, + }); + + return response as addScheduleDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error while deleting schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async addDeviceSchedule(deviceUuid: string, addScheduleDto: AddScheduleDto) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + + // Corrected condition for supported device types + if ( + deviceDetails.productDevice.prodType !== ProductType.THREE_G && + deviceDetails.productDevice.prodType !== ProductType.ONE_G && + deviceDetails.productDevice.prodType !== ProductType.TWO_G + ) { + throw new HttpException( + 'This device is not supported for schedule', + HttpStatus.BAD_REQUEST, + ); + } + await this.addScheduleDeviceInTuya( + deviceDetails.deviceTuyaUuid, + addScheduleDto, + ); + } catch (error) { + throw new HttpException( + error.message || 'Error While Adding Schedule', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async addScheduleDeviceInTuya( + deviceId: string, + addScheduleDto: AddScheduleDto, + ): Promise { + try { + const convertedTime = convertTimestampToDubaiTime(addScheduleDto.time); + const loops = getScheduleStatus(addScheduleDto.days); + + const path = `/v2.0/cloud/timer/device/${deviceId}`; + const response = await this.tuya.request({ + method: 'POST', + path, + body: { + time: convertedTime.time, + timezone_id: 'Asia/Dubai', + loops: `${loops}`, + functions: [ + { + code: addScheduleDto.function.code, + value: addScheduleDto.function.value, + }, + ], + category: `category_${addScheduleDto.category}`, + }, + }); + + return response as addScheduleDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error adding schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getDeviceScheduleByCategory(deviceUuid: string, category: string) { + try { + const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); + + if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { + throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + } + // Corrected condition for supported device types + if ( + deviceDetails.productDevice.prodType !== ProductType.THREE_G && + deviceDetails.productDevice.prodType !== ProductType.ONE_G && + deviceDetails.productDevice.prodType !== ProductType.TWO_G + ) { + throw new HttpException( + 'This device is not supported for schedule', + HttpStatus.BAD_REQUEST, + ); + } + const schedules = await this.getScheduleDeviceInTuya( + deviceDetails.deviceTuyaUuid, + category, + ); + const result = schedules.result.map((schedule: any) => { + return { + category: schedule.category.replace('category_', ''), + enable: schedule.enable, + function: { + code: schedule.functions[0].code, + value: schedule.functions[0].value, + }, + time: schedule.time, + schedule_id: schedule.timer_id, + timezone_id: schedule.timezone_id, + days: getEnabledDays(schedule.loops), + }; + }); + return convertKeysToCamelCase(result); + } catch (error) { + throw new HttpException( + error.message || 'Error While Adding Schedule', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getScheduleDeviceInTuya( + deviceId: string, + category: string, + ): Promise { + try { + const path = `/v2.0/cloud/timer/device/${deviceId}?category=category_${category}`; + const response = await this.tuya.request({ + method: 'GET', + path, + }); + + return response as getDeviceScheduleInterface; + } catch (error) { + console.error('Error fetching device schedule from Tuya:', error); + + throw new HttpException( + 'Error fetching device schedule from Tuya', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getDeviceByDeviceUuid( + deviceUuid: string, + withProductDevice: boolean = true, + ) { + return await this.deviceRepository.findOne({ + where: { + uuid: deviceUuid, + }, + ...(withProductDevice && { relations: ['productDevice'] }), + }); + } +} From 5b6dcc1440b6feac371fc6ca49853cc2b3bd77ae Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 15 Sep 2024 18:09:34 +0300 Subject: [PATCH 3/3] edit category from param to query --- src/schedule/controllers/schedule.controller.ts | 13 +++++++++---- src/schedule/dtos/index.ts | 2 +- .../dtos/{add.schedule.dto.ts => schedule.dto.ts} | 9 +++++++++ src/schedule/services/schedule.service.ts | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) rename src/schedule/dtos/{add.schedule.dto.ts => schedule.dto.ts} (90%) diff --git a/src/schedule/controllers/schedule.controller.ts b/src/schedule/controllers/schedule.controller.ts index 94ad414..c14c6fe 100644 --- a/src/schedule/controllers/schedule.controller.ts +++ b/src/schedule/controllers/schedule.controller.ts @@ -10,9 +10,14 @@ import { UseGuards, Put, Delete, + Query, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { AddScheduleDto, EnableScheduleDto } from '../dtos/add.schedule.dto'; +import { + AddScheduleDto, + EnableScheduleDto, + GetScheduleDeviceDto, +} from '../dtos/schedule.dto'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; @@ -51,15 +56,15 @@ export class ScheduleController { } @ApiBearerAuth() @UseGuards(JwtAuthGuard) - @Get(':deviceUuid/category/:category') + @Get(':deviceUuid') async getDeviceScheduleByCategory( @Param('deviceUuid') deviceUuid: string, - @Param('category') category: string, + @Query() query: GetScheduleDeviceDto, ) { try { return await this.scheduleService.getDeviceScheduleByCategory( deviceUuid, - category, + query.category, ); } catch (error) { throw new HttpException( diff --git a/src/schedule/dtos/index.ts b/src/schedule/dtos/index.ts index e71549f..8398723 100644 --- a/src/schedule/dtos/index.ts +++ b/src/schedule/dtos/index.ts @@ -1 +1 @@ -export * from './add.schedule.dto'; +export * from './schedule.dto'; diff --git a/src/schedule/dtos/add.schedule.dto.ts b/src/schedule/dtos/schedule.dto.ts similarity index 90% rename from src/schedule/dtos/add.schedule.dto.ts rename to src/schedule/dtos/schedule.dto.ts index c51a027..550519c 100644 --- a/src/schedule/dtos/add.schedule.dto.ts +++ b/src/schedule/dtos/schedule.dto.ts @@ -82,3 +82,12 @@ export class EnableScheduleDto { @IsNotEmpty() public enable: boolean; } +export class GetScheduleDeviceDto { + @ApiProperty({ + description: 'category', + required: true, + }) + @IsString() + @IsNotEmpty() + public category: string; +} diff --git a/src/schedule/services/schedule.service.ts b/src/schedule/services/schedule.service.ts index f5a6417..1f9e224 100644 --- a/src/schedule/services/schedule.service.ts +++ b/src/schedule/services/schedule.service.ts @@ -1,7 +1,7 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; -import { AddScheduleDto, EnableScheduleDto } from '../dtos/add.schedule.dto'; +import { AddScheduleDto, EnableScheduleDto } from '../dtos/schedule.dto'; import { addScheduleDeviceInterface, getDeviceScheduleInterface,