mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-15 18:27:05 +00:00
Merge pull request #97 from SyncrowIOT/SP-599-be-implement-scheduling-for-the-light-switch
Sp 599 be implement scheduling for the light switch
This commit is contained in:
@ -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',
|
||||
}
|
||||
|
23
libs/common/src/helper/convertTimestampToDubaiTime.ts
Normal file
23
libs/common/src/helper/convertTimestampToDubaiTime.ts
Normal file
@ -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}`,
|
||||
};
|
||||
}
|
27
libs/common/src/helper/getScheduleStatus.ts
Normal file
27
libs/common/src/helper/getScheduleStatus.ts
Normal file
@ -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;
|
||||
}
|
182
package-lock.json
generated
182
package-lock.json
generated
@ -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"
|
||||
}
|
||||
|
@ -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: [
|
||||
|
1
src/schedule/controllers/index.ts
Normal file
1
src/schedule/controllers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './schedule.controller';
|
121
src/schedule/controllers/schedule.controller.ts
Normal file
121
src/schedule/controllers/schedule.controller.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { ScheduleService } from '../services/schedule.service';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Param,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
UseGuards,
|
||||
Put,
|
||||
Delete,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import {
|
||||
AddScheduleDto,
|
||||
EnableScheduleDto,
|
||||
GetScheduleDeviceDto,
|
||||
} from '../dtos/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')
|
||||
async getDeviceScheduleByCategory(
|
||||
@Param('deviceUuid') deviceUuid: string,
|
||||
@Query() query: GetScheduleDeviceDto,
|
||||
) {
|
||||
try {
|
||||
return await this.scheduleService.getDeviceScheduleByCategory(
|
||||
deviceUuid,
|
||||
query.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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
1
src/schedule/dtos/index.ts
Normal file
1
src/schedule/dtos/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './schedule.dto';
|
93
src/schedule/dtos/schedule.dto.ts
Normal file
93
src/schedule/dtos/schedule.dto.ts
Normal file
@ -0,0 +1,93 @@
|
||||
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;
|
||||
}
|
||||
export class GetScheduleDeviceDto {
|
||||
@ApiProperty({
|
||||
description: 'category',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public category: string;
|
||||
}
|
10
src/schedule/interfaces/get.schedule.interface.ts
Normal file
10
src/schedule/interfaces/get.schedule.interface.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export interface getDeviceScheduleInterface {
|
||||
success: boolean;
|
||||
result: [];
|
||||
msg: string;
|
||||
}
|
||||
export interface addScheduleDeviceInterface {
|
||||
success: boolean;
|
||||
result: boolean;
|
||||
msg: string;
|
||||
}
|
13
src/schedule/schedule.module.ts
Normal file
13
src/schedule/schedule.module.ts
Normal file
@ -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 {}
|
1
src/schedule/services/index.ts
Normal file
1
src/schedule/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './schedule.service';
|
281
src/schedule/services/schedule.service.ts
Normal file
281
src/schedule/services/schedule.service.ts
Normal file
@ -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/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<string>('auth-config.ACCESS_KEY');
|
||||
const secretKey = this.configService.get<string>('auth-config.SECRET_KEY');
|
||||
const tuyaEuUrl = this.configService.get<string>('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<addScheduleDeviceInterface> {
|
||||
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<addScheduleDeviceInterface> {
|
||||
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<addScheduleDeviceInterface> {
|
||||
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<getDeviceScheduleInterface> {
|
||||
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'] }),
|
||||
});
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user