From 5f18d1f4d5c93aeb526f03c1becf6feedc450f95 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:17:22 +0300 Subject: [PATCH 1/2] finshed get devices by room id --- src/app.module.ts | 2 + src/device/controllers/device.controller.ts | 91 +++++++ src/device/controllers/index.ts | 1 + src/device/device.module.ts | 11 + src/device/dtos/add.device.dto.ts | 36 +++ src/device/dtos/control.device.dto.ts | 20 ++ src/device/dtos/get.device.dto.ts | 20 ++ src/device/dtos/index.ts | 4 + src/device/dtos/rename.device.dto copy.ts | 20 ++ src/device/interfaces/get.device.interface.ts | 25 ++ src/device/services/device.service.ts | 242 ++++++++++++++++++ src/device/services/index.ts | 1 + 12 files changed, 473 insertions(+) create mode 100644 src/device/controllers/device.controller.ts create mode 100644 src/device/controllers/index.ts create mode 100644 src/device/device.module.ts create mode 100644 src/device/dtos/add.device.dto.ts create mode 100644 src/device/dtos/control.device.dto.ts create mode 100644 src/device/dtos/get.device.dto.ts create mode 100644 src/device/dtos/index.ts create mode 100644 src/device/dtos/rename.device.dto copy.ts create mode 100644 src/device/interfaces/get.device.interface.ts create mode 100644 src/device/services/device.service.ts create mode 100644 src/device/services/index.ts diff --git a/src/app.module.ts b/src/app.module.ts index b890f1a..0af9133 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -6,6 +6,7 @@ import { AuthenticationController } from './auth/controllers/authentication.cont import { UserModule } from './users/user.module'; import { HomeModule } from './home/home.module'; import { RoomModule } from './room/room.module'; +import { DeviceModule } from './device/device.module'; @Module({ imports: [ ConfigModule.forRoot({ @@ -15,6 +16,7 @@ import { RoomModule } from './room/room.module'; UserModule, HomeModule, RoomModule, + DeviceModule, ], controllers: [AuthenticationController], }) diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts new file mode 100644 index 0000000..ffedef3 --- /dev/null +++ b/src/device/controllers/device.controller.ts @@ -0,0 +1,91 @@ +import { DeviceService } from '../services/device.service'; +import { + Body, + Controller, + Get, + Post, + UseGuards, + Query, + Param, + Put, + Delete, +} from '@nestjs/common'; +import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; +import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; +import { AddDeviceDto } from '../dtos/add.device.dto'; +import { GetDeviceDto } from '../dtos/get.device.dto'; +import { ControlDeviceDto } from '../dtos/control.device.dto'; +import { RenameDeviceDto } from '../dtos/rename.device.dto copy'; + +@ApiTags('Device Module') +@Controller({ + version: '1', + path: 'device', +}) +export class DeviceController { + constructor(private readonly deviceService: DeviceService) {} + + // @ApiBearerAuth() + // @UseGuards(JwtAuthGuard) + @Get('room') + async getDevicesByRoomId(@Query() getDevicesDto: GetDeviceDto) { + try { + return await this.deviceService.getDevicesByRoomId(getDevicesDto); + } catch (err) { + throw new Error(err); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get(':deviceId') + async getDevicesByDeviceId(@Param('deviceId') deviceId: number) { + try { + return await this.deviceService.getDevicesByDeviceId(deviceId); + } catch (err) { + throw new Error(err); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post() + async addDevice(@Body() addDeviceDto: AddDeviceDto) { + try { + return await this.deviceService.addDevice(addDeviceDto); + } catch (err) { + throw new Error(err); + } + } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('control') + async controlDevice(@Body() controlDeviceDto: ControlDeviceDto) { + try { + return await this.deviceService.controlDevice(controlDeviceDto); + } catch (err) { + throw new Error(err); + } + } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Put('rename') + async renameDevice(@Body() renameDeviceDto: RenameDeviceDto) { + try { + return await this.deviceService.renameDevice(renameDeviceDto); + } catch (err) { + throw new Error(err); + } + } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Delete(':deviceId') + async deleteDevice(@Param('deviceId') deviceId: number) { + try { + return await this.deviceService.deleteDevice(deviceId); + } catch (err) { + throw new Error(err); + } + } +} diff --git a/src/device/controllers/index.ts b/src/device/controllers/index.ts new file mode 100644 index 0000000..8afa190 --- /dev/null +++ b/src/device/controllers/index.ts @@ -0,0 +1 @@ +export * from './device.controller'; diff --git a/src/device/device.module.ts b/src/device/device.module.ts new file mode 100644 index 0000000..8bac0cf --- /dev/null +++ b/src/device/device.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { DeviceService } from './services/device.service'; +import { DeviceController } from './controllers/device.controller'; +import { ConfigModule } from '@nestjs/config'; +@Module({ + imports: [ConfigModule], + controllers: [DeviceController], + providers: [DeviceService], + exports: [DeviceService], +}) +export class DeviceModule {} diff --git a/src/device/dtos/add.device.dto.ts b/src/device/dtos/add.device.dto.ts new file mode 100644 index 0000000..bdb7eb6 --- /dev/null +++ b/src/device/dtos/add.device.dto.ts @@ -0,0 +1,36 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString, IsNumberString } from 'class-validator'; + +export class AddDeviceDto { + @ApiProperty({ + description: 'deviceName', + required: true, + }) + @IsString() + @IsNotEmpty() + public deviceName: string; + + @ApiProperty({ + description: 'homeId', + required: true, + }) + @IsNumberString() + @IsNotEmpty() + public homeId: string; + + @ApiProperty({ + description: 'productId', + required: true, + }) + @IsString() + @IsNotEmpty() + public productId: string; + + @ApiProperty({ + description: 'The list of up to 20 device IDs, separated with commas (,)', + required: true, + }) + @IsString() + @IsNotEmpty() + public deviceIds: string; +} diff --git a/src/device/dtos/control.device.dto.ts b/src/device/dtos/control.device.dto.ts new file mode 100644 index 0000000..1164973 --- /dev/null +++ b/src/device/dtos/control.device.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsObject, IsNumberString } from 'class-validator'; + +export class ControlDeviceDto { + @ApiProperty({ + description: 'deviceId', + required: true, + }) + @IsNumberString() + @IsNotEmpty() + public deviceId: string; + + @ApiProperty({ + description: 'example {"switch_1":true,"add_ele":300}', + required: true, + }) + @IsObject() + @IsNotEmpty() + public properties: object; +} diff --git a/src/device/dtos/get.device.dto.ts b/src/device/dtos/get.device.dto.ts new file mode 100644 index 0000000..217ab46 --- /dev/null +++ b/src/device/dtos/get.device.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsNumberString } from 'class-validator'; + +export class GetDeviceDto { + @ApiProperty({ + description: 'roomId', + required: true, + }) + @IsNumberString() + @IsNotEmpty() + public roomId: string; + + @ApiProperty({ + description: 'pageSize', + required: true, + }) + @IsNumberString() + @IsNotEmpty() + public pageSize: number; +} diff --git a/src/device/dtos/index.ts b/src/device/dtos/index.ts new file mode 100644 index 0000000..3409fea --- /dev/null +++ b/src/device/dtos/index.ts @@ -0,0 +1,4 @@ +export * from './add.device.dto'; +export * from './control.device.dto'; +export * from './get.device.dto'; +export * from './rename.device.dto copy'; diff --git a/src/device/dtos/rename.device.dto copy.ts b/src/device/dtos/rename.device.dto copy.ts new file mode 100644 index 0000000..e32a6bb --- /dev/null +++ b/src/device/dtos/rename.device.dto copy.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString, IsNumberString } from 'class-validator'; + +export class RenameDeviceDto { + @ApiProperty({ + description: 'deviceId', + required: true, + }) + @IsNumberString() + @IsNotEmpty() + public deviceId: string; + + @ApiProperty({ + description: 'deviceName', + required: true, + }) + @IsString() + @IsNotEmpty() + public deviceName: string; +} diff --git a/src/device/interfaces/get.device.interface.ts b/src/device/interfaces/get.device.interface.ts new file mode 100644 index 0000000..75a2145 --- /dev/null +++ b/src/device/interfaces/get.device.interface.ts @@ -0,0 +1,25 @@ +export class GetDeviceDetailsInterface { + result: { + id: string; + name: string; + }; +} +export class GetDevicesInterface { + success: boolean; + msg: string; + result: []; +} + +export class addDeviceInterface { + success: boolean; + msg: string; + result: { + id: string; + }; +} + +export class controlDeviceInterface { + success: boolean; + result: boolean; + msg: string; +} diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts new file mode 100644 index 0000000..9a80bfb --- /dev/null +++ b/src/device/services/device.service.ts @@ -0,0 +1,242 @@ +import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { TuyaContext } from '@tuya/tuya-connector-nodejs'; +import { ConfigService } from '@nestjs/config'; +import { AddDeviceDto } from '../dtos/add.device.dto'; +import { + GetDeviceDetailsInterface, + GetDevicesInterface, + addDeviceInterface, + controlDeviceInterface, +} from '../interfaces/get.device.interface'; +import { GetDeviceDto } from '../dtos/get.device.dto'; +import { ControlDeviceDto } from '../dtos/control.device.dto'; +import { RenameDeviceDto } from '../dtos/rename.device.dto copy'; + +@Injectable() +export class DeviceService { + private tuya: TuyaContext; + constructor(private readonly configService: ConfigService) { + const accessKey = this.configService.get('auth-config.ACCESS_KEY'); + const secretKey = this.configService.get('auth-config.SECRET_KEY'); + // const clientId = this.configService.get('auth-config.CLIENT_ID'); + this.tuya = new TuyaContext({ + baseUrl: 'https://openapi.tuyaeu.com', + accessKey, + secretKey, + }); + } + + async getDevicesByRoomId(getDeviceDto: GetDeviceDto) { + try { + const response = await this.getDevicesTuya(getDeviceDto); + + return { + success: response.success, + devices: response.result, + msg: response.msg, + }; + } catch (error) { + throw new HttpException( + 'Error fetching devices', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async getDevicesTuya( + getDeviceDto: GetDeviceDto, + ): Promise { + try { + const path = `/v2.0/cloud/thing/space/device`; + const response = await this.tuya.request({ + method: 'GET', + path, + query: { + space_ids: getDeviceDto.roomId, + page_size: getDeviceDto.pageSize, + }, + }); + return response as unknown as GetDevicesInterface; + } catch (error) { + throw new HttpException( + 'Error fetching devices ', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async addDevice(addDeviceDto: AddDeviceDto) { + const response = await this.addDeviceTuya(addDeviceDto); + + if (response.success) { + return { + success: true, + deviceId: response.result.id, + }; + } else { + throw new HttpException( + response.msg || 'Unknown error', + HttpStatus.BAD_REQUEST, + ); + } + } + async addDeviceTuya(addDeviceDto: AddDeviceDto): Promise { + try { + const path = `/v2.0/cloud/thing/device`; + const response = await this.tuya.request({ + method: 'POST', + path, + body: { + space_id: addDeviceDto.homeId, + name: addDeviceDto.deviceName, + product_id: addDeviceDto.productId, + device_ids: addDeviceDto.deviceIds, + }, + }); + + return response as addDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error adding device', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async controlDevice(controlDeviceDto: ControlDeviceDto) { + const response = await this.controlDeviceTuya(controlDeviceDto); + + if (response.success) { + return response; + } else { + throw new HttpException( + response.msg || 'Unknown error', + HttpStatus.BAD_REQUEST, + ); + } + } + async controlDeviceTuya( + controlDeviceDto: ControlDeviceDto, + ): Promise { + try { + const path = `/v2.0/cloud/thing/device/properties`; + const response = await this.tuya.request({ + method: 'POST', + path, + body: { + device_id: controlDeviceDto.deviceId, + properties: controlDeviceDto.properties, + }, + }); + + return response as controlDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error control device', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async renameDevice(renameDeviceDto: RenameDeviceDto) { + const response = await this.renameDeviceTuya(renameDeviceDto); + + if (response.success) { + return { + success: response.success, + result: response.result, + msg: response.msg, + }; + } else { + throw new HttpException( + response.msg || 'Unknown error', + HttpStatus.BAD_REQUEST, + ); + } + } + async renameDeviceTuya( + renameDeviceDto: RenameDeviceDto, + ): Promise { + try { + const path = `/v2.0/cloud/thing/device/${renameDeviceDto.deviceId}/${renameDeviceDto.deviceName}`; + const response = await this.tuya.request({ + method: 'PUT', + path, + }); + + return response as controlDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error rename device', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async deleteDevice(deviceId: number) { + const response = await this.deleteDeviceTuya(deviceId); + + if (response.success) { + return { + success: response.success, + result: response.result, + msg: response.msg, + }; + } else { + throw new HttpException( + response.msg || 'Unknown error', + HttpStatus.BAD_REQUEST, + ); + } + } + async deleteDeviceTuya(deviceId: number): Promise { + try { + const path = `/v2.0/cloud/thing/device/${deviceId}`; + const response = await this.tuya.request({ + method: 'DELETE', + path, + }); + + return response as controlDeviceInterface; + } catch (error) { + throw new HttpException( + 'Error delete device', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async getDevicesByDeviceId(deviceId: number) { + try { + const response = await this.getDevicesByDeviceIdTuya(deviceId); + + return { + deviceId: response.result.id, + deviceName: response.result.name, + }; + } catch (error) { + throw new HttpException( + 'Error fetching device', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async getDevicesByDeviceIdTuya( + deviceId: number, + ): Promise { + try { + const path = `/v2.0/cloud/thing/device/${deviceId}`; + const response = await this.tuya.request({ + method: 'GET', + path, + }); + return response as GetDeviceDetailsInterface; + } catch (error) { + throw new HttpException( + 'Error fetching device ', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/device/services/index.ts b/src/device/services/index.ts new file mode 100644 index 0000000..9101752 --- /dev/null +++ b/src/device/services/index.ts @@ -0,0 +1 @@ +export * from './device.service'; From 1bd30ebb33ead5c7973f3686b1404b7045b03a86 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:57:04 +0300 Subject: [PATCH 2/2] finshed device endpoint --- src/device/controllers/device.controller.ts | 98 +++--- src/device/dtos/add.device.dto.ts | 37 ++- src/device/dtos/control.device.dto.ts | 16 +- src/device/dtos/get.device.dto.ts | 26 +- src/device/dtos/index.ts | 1 - src/device/dtos/rename.device.dto copy.ts | 20 -- src/device/interfaces/get.device.interface.ts | 33 +- src/device/services/device.service.ts | 296 ++++++++++++------ 8 files changed, 343 insertions(+), 184 deletions(-) delete mode 100644 src/device/dtos/rename.device.dto copy.ts diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index ffedef3..936b8e1 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -7,15 +7,18 @@ import { UseGuards, Query, Param, - Put, - Delete, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; -import { AddDeviceDto } from '../dtos/add.device.dto'; -import { GetDeviceDto } from '../dtos/get.device.dto'; +import { + AddDeviceInGroupDto, + AddDeviceInRoomDto, +} from '../dtos/add.device.dto'; +import { + GetDeviceByGroupIdDto, + GetDeviceByRoomIdDto, +} from '../dtos/get.device.dto'; import { ControlDeviceDto } from '../dtos/control.device.dto'; -import { RenameDeviceDto } from '../dtos/rename.device.dto copy'; @ApiTags('Device Module') @Controller({ @@ -25,12 +28,28 @@ import { RenameDeviceDto } from '../dtos/rename.device.dto copy'; export class DeviceController { constructor(private readonly deviceService: DeviceService) {} - // @ApiBearerAuth() - // @UseGuards(JwtAuthGuard) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) @Get('room') - async getDevicesByRoomId(@Query() getDevicesDto: GetDeviceDto) { + async getDevicesByRoomId( + @Query() getDeviceByRoomIdDto: GetDeviceByRoomIdDto, + ) { try { - return await this.deviceService.getDevicesByRoomId(getDevicesDto); + return await this.deviceService.getDevicesByRoomId(getDeviceByRoomIdDto); + } catch (err) { + throw new Error(err); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get('group') + async getDevicesByGroupId( + @Query() getDeviceByGroupIdDto: GetDeviceByGroupIdDto, + ) { + try { + return await this.deviceService.getDevicesByGroupId( + getDeviceByGroupIdDto, + ); } catch (err) { throw new Error(err); } @@ -38,7 +57,7 @@ export class DeviceController { @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Get(':deviceId') - async getDevicesByDeviceId(@Param('deviceId') deviceId: number) { + async getDevicesByDeviceId(@Param('deviceId') deviceId: string) { try { return await this.deviceService.getDevicesByDeviceId(deviceId); } catch (err) { @@ -47,15 +66,44 @@ export class DeviceController { } @ApiBearerAuth() @UseGuards(JwtAuthGuard) - @Post() - async addDevice(@Body() addDeviceDto: AddDeviceDto) { + @Get(':deviceId/functions') + async getDevicesInstructionByDeviceId(@Param('deviceId') deviceId: string) { try { - return await this.deviceService.addDevice(addDeviceDto); + return await this.deviceService.getDevicesInstructionByDeviceId(deviceId); + } catch (err) { + throw new Error(err); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get(':deviceId/functions/status') + async getDevicesInstructionStatus(@Param('deviceId') deviceId: string) { + try { + return await this.deviceService.getDevicesInstructionStatus(deviceId); + } catch (err) { + throw new Error(err); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('room') + async addDeviceInRoom(@Body() addDeviceInRoomDto: AddDeviceInRoomDto) { + try { + return await this.deviceService.addDeviceInRoom(addDeviceInRoomDto); + } catch (err) { + throw new Error(err); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('group') + async addDeviceInGroup(@Body() addDeviceInGroupDto: AddDeviceInGroupDto) { + try { + return await this.deviceService.addDeviceInGroup(addDeviceInGroupDto); } catch (err) { throw new Error(err); } } - @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Post('control') @@ -66,26 +114,4 @@ export class DeviceController { throw new Error(err); } } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Put('rename') - async renameDevice(@Body() renameDeviceDto: RenameDeviceDto) { - try { - return await this.deviceService.renameDevice(renameDeviceDto); - } catch (err) { - throw new Error(err); - } - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Delete(':deviceId') - async deleteDevice(@Param('deviceId') deviceId: number) { - try { - return await this.deviceService.deleteDevice(deviceId); - } catch (err) { - throw new Error(err); - } - } } diff --git a/src/device/dtos/add.device.dto.ts b/src/device/dtos/add.device.dto.ts index bdb7eb6..8b1dfe5 100644 --- a/src/device/dtos/add.device.dto.ts +++ b/src/device/dtos/add.device.dto.ts @@ -1,14 +1,31 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString, IsNumberString } from 'class-validator'; -export class AddDeviceDto { +export class AddDeviceInRoomDto { @ApiProperty({ - description: 'deviceName', + description: 'deviceId', required: true, }) @IsString() @IsNotEmpty() - public deviceName: string; + public deviceId: string; + + @ApiProperty({ + description: 'roomId', + required: true, + }) + @IsNumberString() + @IsNotEmpty() + public roomId: string; +} +export class AddDeviceInGroupDto { + @ApiProperty({ + description: 'deviceId', + required: true, + }) + @IsString() + @IsNotEmpty() + public deviceId: string; @ApiProperty({ description: 'homeId', @@ -19,18 +36,10 @@ export class AddDeviceDto { public homeId: string; @ApiProperty({ - description: 'productId', + description: 'groupId', required: true, }) - @IsString() + @IsNumberString() @IsNotEmpty() - public productId: string; - - @ApiProperty({ - description: 'The list of up to 20 device IDs, separated with commas (,)', - required: true, - }) - @IsString() - @IsNotEmpty() - public deviceIds: string; + public groupId: string; } diff --git a/src/device/dtos/control.device.dto.ts b/src/device/dtos/control.device.dto.ts index 1164973..660cdaf 100644 --- a/src/device/dtos/control.device.dto.ts +++ b/src/device/dtos/control.device.dto.ts @@ -1,20 +1,26 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsObject, IsNumberString } from 'class-validator'; +import { IsNotEmpty, IsString } from 'class-validator'; export class ControlDeviceDto { @ApiProperty({ description: 'deviceId', required: true, }) - @IsNumberString() + @IsString() @IsNotEmpty() public deviceId: string; @ApiProperty({ - description: 'example {"switch_1":true,"add_ele":300}', + description: 'code', required: true, }) - @IsObject() + @IsString() @IsNotEmpty() - public properties: object; + public code: string; + @ApiProperty({ + description: 'value', + required: true, + }) + @IsNotEmpty() + public value: any; } diff --git a/src/device/dtos/get.device.dto.ts b/src/device/dtos/get.device.dto.ts index 217ab46..d49a714 100644 --- a/src/device/dtos/get.device.dto.ts +++ b/src/device/dtos/get.device.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsNumberString } from 'class-validator'; -export class GetDeviceDto { +export class GetDeviceByRoomIdDto { @ApiProperty({ description: 'roomId', required: true, @@ -18,3 +18,27 @@ export class GetDeviceDto { @IsNotEmpty() public pageSize: number; } +export class GetDeviceByGroupIdDto { + @ApiProperty({ + description: 'groupId', + required: true, + }) + @IsNumberString() + @IsNotEmpty() + public groupId: string; + + @ApiProperty({ + description: 'pageSize', + required: true, + }) + @IsNumberString() + @IsNotEmpty() + public pageSize: number; + @ApiProperty({ + description: 'pageNo', + required: true, + }) + @IsNumberString() + @IsNotEmpty() + public pageNo: number; +} diff --git a/src/device/dtos/index.ts b/src/device/dtos/index.ts index 3409fea..1a0b6b3 100644 --- a/src/device/dtos/index.ts +++ b/src/device/dtos/index.ts @@ -1,4 +1,3 @@ export * from './add.device.dto'; export * from './control.device.dto'; export * from './get.device.dto'; -export * from './rename.device.dto copy'; diff --git a/src/device/dtos/rename.device.dto copy.ts b/src/device/dtos/rename.device.dto copy.ts deleted file mode 100644 index e32a6bb..0000000 --- a/src/device/dtos/rename.device.dto copy.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString, IsNumberString } from 'class-validator'; - -export class RenameDeviceDto { - @ApiProperty({ - description: 'deviceId', - required: true, - }) - @IsNumberString() - @IsNotEmpty() - public deviceId: string; - - @ApiProperty({ - description: 'deviceName', - required: true, - }) - @IsString() - @IsNotEmpty() - public deviceName: string; -} diff --git a/src/device/interfaces/get.device.interface.ts b/src/device/interfaces/get.device.interface.ts index 75a2145..e6f1361 100644 --- a/src/device/interfaces/get.device.interface.ts +++ b/src/device/interfaces/get.device.interface.ts @@ -1,25 +1,44 @@ export class GetDeviceDetailsInterface { - result: { - id: string; - name: string; - }; + result: object; + success: boolean; + msg: string; } -export class GetDevicesInterface { +export class GetDevicesByRoomIdInterface { success: boolean; msg: string; result: []; } -export class addDeviceInterface { +export class GetDevicesByGroupIdInterface { success: boolean; msg: string; result: { - id: string; + count: number; + data_list: []; }; } +export class addDeviceInRoomInterface { + success: boolean; + msg: string; + result: boolean; +} + export class controlDeviceInterface { success: boolean; result: boolean; msg: string; } +export class GetDeviceDetailsFunctionsInterface { + result: { + category: string; + functions: []; + }; + success: boolean; + msg: string; +} +export class GetDeviceDetailsFunctionsStatusInterface { + result: [{ id: string; status: [] }]; + success: boolean; + msg: string; +} diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 9a80bfb..ce13b30 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -1,16 +1,24 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; -import { AddDeviceDto } from '../dtos/add.device.dto'; import { + AddDeviceInGroupDto, + AddDeviceInRoomDto, +} from '../dtos/add.device.dto'; +import { + GetDeviceDetailsFunctionsInterface, + GetDeviceDetailsFunctionsStatusInterface, GetDeviceDetailsInterface, - GetDevicesInterface, - addDeviceInterface, + GetDevicesByGroupIdInterface, + GetDevicesByRoomIdInterface, + addDeviceInRoomInterface, controlDeviceInterface, } from '../interfaces/get.device.interface'; -import { GetDeviceDto } from '../dtos/get.device.dto'; +import { + GetDeviceByGroupIdDto, + GetDeviceByRoomIdDto, +} from '../dtos/get.device.dto'; import { ControlDeviceDto } from '../dtos/control.device.dto'; -import { RenameDeviceDto } from '../dtos/rename.device.dto copy'; @Injectable() export class DeviceService { @@ -26,9 +34,9 @@ export class DeviceService { }); } - async getDevicesByRoomId(getDeviceDto: GetDeviceDto) { + async getDevicesByRoomId(getDeviceByRoomIdDto: GetDeviceByRoomIdDto) { try { - const response = await this.getDevicesTuya(getDeviceDto); + const response = await this.getDevicesByRoomIdTuya(getDeviceByRoomIdDto); return { success: response.success, @@ -43,20 +51,62 @@ export class DeviceService { } } - async getDevicesTuya( - getDeviceDto: GetDeviceDto, - ): Promise { + async getDevicesByRoomIdTuya( + getDeviceByRoomIdDto: GetDeviceByRoomIdDto, + ): Promise { try { const path = `/v2.0/cloud/thing/space/device`; const response = await this.tuya.request({ method: 'GET', path, query: { - space_ids: getDeviceDto.roomId, - page_size: getDeviceDto.pageSize, + space_ids: getDeviceByRoomIdDto.roomId, + page_size: getDeviceByRoomIdDto.pageSize, }, }); - return response as unknown as GetDevicesInterface; + return response as GetDevicesByRoomIdInterface; + } catch (error) { + throw new HttpException( + 'Error fetching devices ', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getDevicesByGroupId(getDeviceByGroupIdDto: GetDeviceByGroupIdDto) { + try { + const response = await this.getDevicesByGroupIdTuya( + getDeviceByGroupIdDto, + ); + + return { + success: response.success, + devices: response.result.data_list, + count: response.result.count, + msg: response.msg, + }; + } catch (error) { + throw new HttpException( + 'Error fetching devices', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async getDevicesByGroupIdTuya( + getDeviceByGroupIdDto: GetDeviceByGroupIdDto, + ): Promise { + try { + const path = `/v2.0/cloud/thing/group`; + const response = await this.tuya.request({ + method: 'GET', + path, + query: { + space_id: getDeviceByGroupIdDto.groupId, + page_size: getDeviceByGroupIdDto.pageSize, + page_no: getDeviceByGroupIdDto.pageNo, + }, + }); + return response as GetDevicesByGroupIdInterface; } catch (error) { throw new HttpException( 'Error fetching devices ', @@ -65,13 +115,14 @@ export class DeviceService { } } - async addDevice(addDeviceDto: AddDeviceDto) { - const response = await this.addDeviceTuya(addDeviceDto); + async addDeviceInRoom(addDeviceInRoomDto: AddDeviceInRoomDto) { + const response = await this.addDeviceInRoomTuya(addDeviceInRoomDto); if (response.success) { return { - success: true, - deviceId: response.result.id, + success: response.success, + result: response.result, + msg: response.msg, }; } else { throw new HttpException( @@ -80,21 +131,58 @@ export class DeviceService { ); } } - async addDeviceTuya(addDeviceDto: AddDeviceDto): Promise { + async addDeviceInRoomTuya( + addDeviceInRoomDto: AddDeviceInRoomDto, + ): Promise { try { - const path = `/v2.0/cloud/thing/device`; + const path = `/v2.0/cloud/thing/${addDeviceInRoomDto.deviceId}/transfer`; const response = await this.tuya.request({ method: 'POST', path, body: { - space_id: addDeviceDto.homeId, - name: addDeviceDto.deviceName, - product_id: addDeviceDto.productId, - device_ids: addDeviceDto.deviceIds, + space_id: addDeviceInRoomDto.roomId, }, }); - return response as addDeviceInterface; + return response as addDeviceInRoomInterface; + } catch (error) { + throw new HttpException( + 'Error adding device', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async addDeviceInGroup(addDeviceInGroupDto: AddDeviceInGroupDto) { + const response = await this.addDeviceInGroupTuya(addDeviceInGroupDto); + + if (response.success) { + return { + success: response.success, + result: response.result, + msg: response.msg, + }; + } else { + throw new HttpException( + response.msg || 'Unknown error', + HttpStatus.BAD_REQUEST, + ); + } + } + async addDeviceInGroupTuya( + addDeviceInGroupDto: AddDeviceInGroupDto, + ): Promise { + try { + const path = `/v2.0/cloud/thing/group/${addDeviceInGroupDto.groupId}/device`; + const response = await this.tuya.request({ + method: 'PUT', + path, + body: { + space_id: addDeviceInGroupDto.homeId, + device_ids: addDeviceInGroupDto.deviceId, + }, + }); + + return response as addDeviceInRoomInterface; } catch (error) { throw new HttpException( 'Error adding device', @@ -119,13 +207,14 @@ export class DeviceService { controlDeviceDto: ControlDeviceDto, ): Promise { try { - const path = `/v2.0/cloud/thing/device/properties`; + const path = `/v1.0/iot-03/devices/${controlDeviceDto.deviceId}/commands`; const response = await this.tuya.request({ method: 'POST', path, body: { - device_id: controlDeviceDto.deviceId, - properties: controlDeviceDto.properties, + commands: [ + { code: controlDeviceDto.code, value: controlDeviceDto.value }, + ], }, }); @@ -138,81 +227,14 @@ export class DeviceService { } } - async renameDevice(renameDeviceDto: RenameDeviceDto) { - const response = await this.renameDeviceTuya(renameDeviceDto); - - if (response.success) { - return { - success: response.success, - result: response.result, - msg: response.msg, - }; - } else { - throw new HttpException( - response.msg || 'Unknown error', - HttpStatus.BAD_REQUEST, - ); - } - } - async renameDeviceTuya( - renameDeviceDto: RenameDeviceDto, - ): Promise { - try { - const path = `/v2.0/cloud/thing/device/${renameDeviceDto.deviceId}/${renameDeviceDto.deviceName}`; - const response = await this.tuya.request({ - method: 'PUT', - path, - }); - - return response as controlDeviceInterface; - } catch (error) { - throw new HttpException( - 'Error rename device', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async deleteDevice(deviceId: number) { - const response = await this.deleteDeviceTuya(deviceId); - - if (response.success) { - return { - success: response.success, - result: response.result, - msg: response.msg, - }; - } else { - throw new HttpException( - response.msg || 'Unknown error', - HttpStatus.BAD_REQUEST, - ); - } - } - async deleteDeviceTuya(deviceId: number): Promise { - try { - const path = `/v2.0/cloud/thing/device/${deviceId}`; - const response = await this.tuya.request({ - method: 'DELETE', - path, - }); - - return response as controlDeviceInterface; - } catch (error) { - throw new HttpException( - 'Error delete device', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async getDevicesByDeviceId(deviceId: number) { + async getDevicesByDeviceId(deviceId: string) { try { const response = await this.getDevicesByDeviceIdTuya(deviceId); return { - deviceId: response.result.id, - deviceName: response.result.name, + success: response.success, + result: response.result, + msg: response.msg, }; } catch (error) { throw new HttpException( @@ -223,10 +245,10 @@ export class DeviceService { } async getDevicesByDeviceIdTuya( - deviceId: number, + deviceId: string, ): Promise { try { - const path = `/v2.0/cloud/thing/device/${deviceId}`; + const path = `/v1.1/iot-03/devices/${deviceId}`; const response = await this.tuya.request({ method: 'GET', path, @@ -239,4 +261,78 @@ export class DeviceService { ); } } + async getDevicesInstructionByDeviceId(deviceId: string) { + try { + const response = await this.getDevicesInstructionByDeviceIdTuya(deviceId); + + return { + success: response.success, + result: { + category: response.result.category, + function: response.result.functions, + }, + msg: response.msg, + }; + } catch (error) { + throw new HttpException( + 'Error fetching device functions', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async getDevicesInstructionByDeviceIdTuya( + deviceId: string, + ): Promise { + try { + const path = `/v1.0/iot-03/devices/${deviceId}/functions`; + const response = await this.tuya.request({ + method: 'GET', + path, + }); + return response as GetDeviceDetailsFunctionsInterface; + } catch (error) { + throw new HttpException( + 'Error fetching device functions', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getDevicesInstructionStatus(deviceId: string) { + try { + const response = await this.getDevicesInstructionStatusTuya(deviceId); + + return { + result: response.result, + success: response.success, + msg: response.msg, + }; + } catch (error) { + throw new HttpException( + 'Error fetching device functions', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async getDevicesInstructionStatusTuya( + deviceId: string, + ): Promise { + try { + const path = `/v1.0/iot-03/devices/status`; + const response = await this.tuya.request({ + method: 'GET', + path, + query: { + device_ids: deviceId, + }, + }); + return response as unknown as GetDeviceDetailsFunctionsStatusInterface; + } catch (error) { + throw new HttpException( + 'Error fetching device functions', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } }