From eb916b79b44ce20ef93cfe9001d3de43f5d3b4f8 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 16 Nov 2024 23:05:06 -0600 Subject: [PATCH] finished 4scene configration endpoints --- libs/common/src/common.module.ts | 10 +- libs/common/src/constants/four-scene.enum.ts | 6 + libs/common/src/database/database.module.ts | 2 + .../modules/device/entities/device.entity.ts | 14 +- .../src/modules/scene-device/dtos/index.ts | 1 + .../scene-device/dtos/scene-device.dto.ts | 20 +++ .../modules/scene-device/entities/index.ts | 1 + .../entities/scene-device.entity.ts | 50 ++++++ .../scene-device/repositories/index.ts | 1 + .../repositories/scene-device.repository.ts | 10 ++ .../scene-device.repository.module.ts | 11 ++ .../modules/scene/entities/scene.entity.ts | 4 + src/automation/automation.module.ts | 10 ++ src/automation/services/automation.service.ts | 21 +++ src/device/controllers/device.controller.ts | 38 ++++- src/device/device.module.ts | 14 ++ src/device/dtos/add.device.dto.ts | 27 +++- src/device/dtos/control.device.dto.ts | 9 ++ src/device/services/device.service.ts | 149 +++++++++++++++++- src/door-lock/door.lock.module.ts | 12 ++ src/group/services/group.service.ts | 1 - src/scene/scene.module.ts | 2 + src/scene/services/scene.service.ts | 17 +- src/space/space.module.ts | 2 + .../visitor-password.module.ts | 12 ++ 25 files changed, 429 insertions(+), 15 deletions(-) create mode 100644 libs/common/src/constants/four-scene.enum.ts create mode 100644 libs/common/src/modules/scene-device/dtos/index.ts create mode 100644 libs/common/src/modules/scene-device/dtos/scene-device.dto.ts create mode 100644 libs/common/src/modules/scene-device/entities/index.ts create mode 100644 libs/common/src/modules/scene-device/entities/scene-device.entity.ts create mode 100644 libs/common/src/modules/scene-device/repositories/index.ts create mode 100644 libs/common/src/modules/scene-device/repositories/scene-device.repository.ts create mode 100644 libs/common/src/modules/scene-device/scene-device.repository.module.ts diff --git a/libs/common/src/common.module.ts b/libs/common/src/common.module.ts index cbe733d..de0780a 100644 --- a/libs/common/src/common.module.ts +++ b/libs/common/src/common.module.ts @@ -8,8 +8,15 @@ import config from './config'; import { EmailService } from './util/email.service'; import { ErrorMessageService } from 'src/error-message/error-message.service'; import { TuyaService } from './integrations/tuya/services/tuya.service'; +import { SceneDeviceRepository } from './modules/scene-device/repositories'; @Module({ - providers: [CommonService, EmailService, ErrorMessageService, TuyaService], + providers: [ + CommonService, + EmailService, + ErrorMessageService, + TuyaService, + SceneDeviceRepository, + ], exports: [ CommonService, TuyaService, @@ -17,6 +24,7 @@ import { TuyaService } from './integrations/tuya/services/tuya.service'; AuthModule, EmailService, ErrorMessageService, + SceneDeviceRepository, ], imports: [ ConfigModule.forRoot({ diff --git a/libs/common/src/constants/four-scene.enum.ts b/libs/common/src/constants/four-scene.enum.ts new file mode 100644 index 0000000..7f43d70 --- /dev/null +++ b/libs/common/src/constants/four-scene.enum.ts @@ -0,0 +1,6 @@ +export enum FourSceneSwitchesEnum { + Scene_1 = 'scene_1', + Scene_2 = 'scene_2', + Scene_3 = 'scene_3', + Scene_4 = 'scene_4', +} diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 44e69c9..868ef5a 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -22,6 +22,7 @@ import { VisitorPasswordEntity } from '../modules/visitor-password/entities'; import { CommunityEntity } from '../modules/community/entities'; import { DeviceStatusLogEntity } from '../modules/device-status-log/entities'; import { SceneEntity, SceneIconEntity } from '../modules/scene/entities'; +import { SceneDeviceEntity } from '../modules/scene-device/entities'; @Module({ imports: [ @@ -60,6 +61,7 @@ import { SceneEntity, SceneIconEntity } from '../modules/scene/entities'; DeviceStatusLogEntity, SceneEntity, SceneIconEntity, + SceneDeviceEntity, ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index 727e290..9a75950 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -1,4 +1,12 @@ -import { Column, Entity, ManyToOne, OneToMany, Unique, Index, JoinColumn } from 'typeorm'; +import { + Column, + Entity, + ManyToOne, + OneToMany, + Unique, + Index, + JoinColumn, +} from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceDto, DeviceUserPermissionDto } from '../dtos/device.dto'; import { SpaceEntity, SubspaceEntity } from '../../space/entities'; @@ -6,6 +14,7 @@ import { ProductEntity } from '../../product/entities'; import { UserEntity } from '../../user/entities'; import { DeviceNotificationDto } from '../dtos'; import { PermissionTypeEntity } from '../../permission/entities'; +import { SceneDeviceEntity } from '../../scene-device/entities'; @Entity({ name: 'device' }) @Unique(['deviceTuyaUuid']) @@ -62,6 +71,9 @@ export class DeviceEntity extends AbstractEntity { @Column({ nullable: false }) uuid: string; + @OneToMany(() => SceneDeviceEntity, (sceneDevice) => sceneDevice.device, {}) + sceneDevices: SceneDeviceEntity[]; + constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/scene-device/dtos/index.ts b/libs/common/src/modules/scene-device/dtos/index.ts new file mode 100644 index 0000000..96f03a1 --- /dev/null +++ b/libs/common/src/modules/scene-device/dtos/index.ts @@ -0,0 +1 @@ +export * from './scene-device.dto'; diff --git a/libs/common/src/modules/scene-device/dtos/scene-device.dto.ts b/libs/common/src/modules/scene-device/dtos/scene-device.dto.ts new file mode 100644 index 0000000..fd7e26a --- /dev/null +++ b/libs/common/src/modules/scene-device/dtos/scene-device.dto.ts @@ -0,0 +1,20 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class SceneDeviceDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public deviceUuid: string; + @IsString() + @IsNotEmpty() + public sceneUuid: string; + @IsString() + @IsNotEmpty() + public switchName: string; + @IsString() + @IsNotEmpty() + public automationTuyaUuid: string; +} diff --git a/libs/common/src/modules/scene-device/entities/index.ts b/libs/common/src/modules/scene-device/entities/index.ts new file mode 100644 index 0000000..337a094 --- /dev/null +++ b/libs/common/src/modules/scene-device/entities/index.ts @@ -0,0 +1 @@ +export * from './scene-device.entity'; diff --git a/libs/common/src/modules/scene-device/entities/scene-device.entity.ts b/libs/common/src/modules/scene-device/entities/scene-device.entity.ts new file mode 100644 index 0000000..537a627 --- /dev/null +++ b/libs/common/src/modules/scene-device/entities/scene-device.entity.ts @@ -0,0 +1,50 @@ +import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; +import { SceneDeviceDto } from '../dtos'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { FourSceneSwitchesEnum } from '@app/common/constants/four-scene.enum'; +import { DeviceEntity } from '../../device/entities'; +import { SceneEntity } from '../../scene/entities'; + +@Entity({ name: 'scene-device' }) +export class SceneDeviceEntity extends AbstractEntity { + @Column({ + type: 'uuid', + default: () => 'gen_random_uuid()', + nullable: false, + }) + public uuid: string; + + @ManyToOne(() => DeviceEntity, (device) => device.sceneDevices, { + nullable: false, + }) + @JoinColumn({ name: 'device_uuid' }) + device: DeviceEntity; + + @Column({ + nullable: false, + }) + sceneUuid: string; + + @Column({ + nullable: false, + type: 'enum', + enum: FourSceneSwitchesEnum, + }) + switchName: FourSceneSwitchesEnum; + + @Column({ + nullable: false, + }) + automationTuyaUuid: string; + + @ManyToOne(() => SceneEntity, (scene) => scene.sceneDevices, { + nullable: false, + }) + @JoinColumn({ name: 'scene_uuid' }) + scene: SceneEntity; + + constructor(partial: Partial) { + super(); + Object.assign(this, partial); + } +} diff --git a/libs/common/src/modules/scene-device/repositories/index.ts b/libs/common/src/modules/scene-device/repositories/index.ts new file mode 100644 index 0000000..f10ccb5 --- /dev/null +++ b/libs/common/src/modules/scene-device/repositories/index.ts @@ -0,0 +1 @@ +export * from './scene-device.repository'; diff --git a/libs/common/src/modules/scene-device/repositories/scene-device.repository.ts b/libs/common/src/modules/scene-device/repositories/scene-device.repository.ts new file mode 100644 index 0000000..1d7915c --- /dev/null +++ b/libs/common/src/modules/scene-device/repositories/scene-device.repository.ts @@ -0,0 +1,10 @@ +import { DataSource, Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { SceneDeviceEntity } from '../entities'; + +@Injectable() +export class SceneDeviceRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SceneDeviceEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/scene-device/scene-device.repository.module.ts b/libs/common/src/modules/scene-device/scene-device.repository.module.ts new file mode 100644 index 0000000..96fa8a7 --- /dev/null +++ b/libs/common/src/modules/scene-device/scene-device.repository.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SceneDeviceEntity } from './entities'; + +@Module({ + providers: [], + exports: [], + controllers: [], + imports: [TypeOrmModule.forFeature([SceneDeviceEntity])], +}) +export class SceneDeviceRepositoryModule {} diff --git a/libs/common/src/modules/scene/entities/scene.entity.ts b/libs/common/src/modules/scene/entities/scene.entity.ts index 1e04fd7..58a880a 100644 --- a/libs/common/src/modules/scene/entities/scene.entity.ts +++ b/libs/common/src/modules/scene/entities/scene.entity.ts @@ -2,6 +2,7 @@ import { Column, Entity, ManyToOne, OneToMany } from 'typeorm'; import { SceneDto, SceneIconDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SceneIconType } from '@app/common/constants/secne-icon-type.enum'; +import { SceneDeviceEntity } from '../../scene-device/entities'; // Define SceneIconEntity before SceneEntity @Entity({ name: 'scene-icon' }) @@ -59,6 +60,9 @@ export class SceneEntity extends AbstractEntity { }) sceneIcon: SceneIconEntity; + @OneToMany(() => SceneDeviceEntity, (sceneDevice) => sceneDevice.scene) + sceneDevices: SceneDeviceEntity[]; + constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/src/automation/automation.module.ts b/src/automation/automation.module.ts index 3a3d7db..0a05f0e 100644 --- a/src/automation/automation.module.ts +++ b/src/automation/automation.module.ts @@ -9,6 +9,12 @@ import { DeviceRepository } from '@app/common/modules/device/repositories'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { DeviceStatusFirebaseModule } from '@app/common/firebase/devices-status/devices-status.module'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; +import { SceneService } from 'src/scene/services'; +import { + SceneIconRepository, + SceneRepository, +} from '@app/common/modules/scene/repositories'; +import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusFirebaseModule], @@ -20,6 +26,10 @@ import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service DeviceService, DeviceRepository, ProductRepository, + SceneService, + SceneIconRepository, + SceneRepository, + SceneDeviceRepository, ], exports: [AutomationService], }) diff --git a/src/automation/services/automation.service.ts b/src/automation/services/automation.service.ts index 468a4ae..27d1b6a 100644 --- a/src/automation/services/automation.service.ts +++ b/src/automation/services/automation.service.ts @@ -30,6 +30,7 @@ import { } from '@app/common/constants/automation.enum'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; import { ConvertedAction } from '@app/common/integrations/tuya/interfaces'; +import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; @Injectable() export class AutomationService { @@ -39,6 +40,7 @@ export class AutomationService { private readonly spaceRepository: SpaceRepository, private readonly deviceService: DeviceService, private readonly tuyaService: TuyaService, + private readonly sceneDeviceRepository: SceneDeviceRepository, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); const secretKey = this.configService.get('auth-config.SECRET_KEY'); @@ -304,6 +306,16 @@ export class AutomationService { HttpStatus.BAD_REQUEST, ); } + const existingSceneDevice = await this.sceneDeviceRepository.findOne({ + where: { automationTuyaUuid: automationUuid }, + }); + + if (existingSceneDevice) { + await this.sceneDeviceRepository.delete({ + automationTuyaUuid: automationUuid, + }); + } + const response = this.tuyaService.deleteAutomation( automation.spaceId, automationUuid, @@ -323,6 +335,15 @@ export class AutomationService { async delete(tuyaSpaceId: string, automationUuid: string) { try { + const existingSceneDevice = await this.sceneDeviceRepository.findOne({ + where: { automationTuyaUuid: automationUuid }, + }); + + if (existingSceneDevice) { + await this.sceneDeviceRepository.delete({ + automationTuyaUuid: automationUuid, + }); + } const response = await this.tuyaService.deleteAutomation( tuyaSpaceId, automationUuid, diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index 5a2fba5..15bf622 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -12,13 +12,18 @@ import { Put, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { AddDeviceDto, UpdateDeviceInSpaceDto } from '../dtos/add.device.dto'; +import { + AddDeviceDto, + AddSceneToFourSceneDeviceDto, + UpdateDeviceInSpaceDto, +} from '../dtos/add.device.dto'; import { GetDeviceLogsDto } from '../dtos/get.device.dto'; import { ControlDeviceDto, BatchControlDevicesDto, BatchStatusDevicesDto, BatchFactoryResetDevicesDto, + GetSceneFourSceneDeviceDto, } from '../dtos/control.device.dto'; import { CheckRoomGuard } from 'src/guards/room.guard'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; @@ -184,4 +189,35 @@ export class DeviceController { powerClampUuid, ); } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('four-scene/:deviceUuid') + async addSceneToFourSceneDevice( + @Param('deviceUuid') deviceUuid: string, + @Body() addSceneToFourSceneDeviceDto: AddSceneToFourSceneDeviceDto, + ) { + const device = await this.deviceService.addSceneToFourSceneDevice( + deviceUuid, + addSceneToFourSceneDeviceDto, + ); + + return { + statusCode: HttpStatus.CREATED, + success: true, + message: `scene added successfully to device ${deviceUuid}`, + data: device, + }; + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get('four-scene/:deviceUuid') + async getSceneFourSceneDevice( + @Param('deviceUuid') deviceUuid: string, + @Query() getSceneFourSceneDeviceDto: GetSceneFourSceneDeviceDto, + ) { + return await this.deviceService.getSceneFourSceneDevice( + deviceUuid, + getSceneFourSceneDeviceDto, + ); + } } diff --git a/src/device/device.module.ts b/src/device/device.module.ts index 5d17318..39b1be6 100644 --- a/src/device/device.module.ts +++ b/src/device/device.module.ts @@ -11,9 +11,18 @@ import { SpaceRepository } from '@app/common/modules/space/repositories'; import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories'; import { UserRepository } from '@app/common/modules/user/repositories'; import { DeviceStatusFirebaseModule } from '@app/common/firebase/devices-status/devices-status.module'; +import { SpaceModule } from 'src/space/space.module'; +import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; +import { SceneService } from 'src/scene/services'; +import { + SceneIconRepository, + SceneRepository, +} from '@app/common/modules/scene/repositories'; +import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; @Module({ imports: [ ConfigModule, + SpaceModule, ProductRepositoryModule, DeviceRepositoryModule, DeviceStatusFirebaseModule, @@ -27,6 +36,11 @@ import { DeviceStatusFirebaseModule } from '@app/common/firebase/devices-status/ SpaceRepository, DeviceRepository, UserRepository, + TuyaService, + SceneService, + SceneIconRepository, + SceneRepository, + SceneDeviceRepository, ], exports: [DeviceService], }) diff --git a/src/device/dtos/add.device.dto.ts b/src/device/dtos/add.device.dto.ts index bff522f..b5884a8 100644 --- a/src/device/dtos/add.device.dto.ts +++ b/src/device/dtos/add.device.dto.ts @@ -1,5 +1,6 @@ +import { FourSceneSwitchesEnum } from '@app/common/constants/four-scene.enum'; import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; export class AddDeviceDto { @ApiProperty({ @@ -35,3 +36,27 @@ export class UpdateDeviceInSpaceDto { @IsNotEmpty() public spaceUuid: string; } +export class AddSceneToFourSceneDeviceDto { + @ApiProperty({ + description: 'switchName', + required: true, + }) + @IsEnum(FourSceneSwitchesEnum) + @IsNotEmpty() + switchName: FourSceneSwitchesEnum; + + @ApiProperty({ + description: 'sceneUuid', + required: true, + }) + @IsString() + @IsNotEmpty() + public sceneUuid: string; + @ApiProperty({ + description: 'spaceUuid', + required: true, + }) + @IsString() + @IsNotEmpty() + public spaceUuid: string; +} diff --git a/src/device/dtos/control.device.dto.ts b/src/device/dtos/control.device.dto.ts index d917a96..7a1e900 100644 --- a/src/device/dtos/control.device.dto.ts +++ b/src/device/dtos/control.device.dto.ts @@ -54,3 +54,12 @@ export class BatchFactoryResetDevicesDto { @IsNotEmpty() public devicesUuid: [string]; } +export class GetSceneFourSceneDeviceDto { + @ApiProperty({ + description: 'switchName', + required: true, + }) + @IsString() + @IsNotEmpty() + public switchName: string; +} diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 09b905c..a402973 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -5,10 +5,16 @@ import { HttpStatus, NotFoundException, BadRequestException, + forwardRef, + Inject, } from '@nestjs/common'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; -import { AddDeviceDto, UpdateDeviceInSpaceDto } from '../dtos/add.device.dto'; +import { + AddDeviceDto, + AddSceneToFourSceneDeviceDto, + UpdateDeviceInSpaceDto, +} from '../dtos/add.device.dto'; import { DeviceInstructionResponse, GetDeviceDetailsFunctionsInterface, @@ -28,6 +34,7 @@ import { BatchFactoryResetDevicesDto, BatchStatusDevicesDto, ControlDeviceDto, + GetSceneFourSceneDeviceDto, } from '../dtos/control.device.dto'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { DeviceRepository } from '@app/common/modules/device/repositories'; @@ -40,6 +47,11 @@ import { DeviceStatuses } from '@app/common/constants/device-status.enum'; import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; import { BatteryStatus } from '@app/common/constants/battery-status.enum'; import { SpaceEntity } from '@app/common/modules/space/entities'; +import { SceneService } from 'src/scene/services'; +import { AddAutomationDto } from 'src/automation/dtos'; +import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; +import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; +import { FourSceneSwitchesEnum } from '@app/common/constants/four-scene.enum'; @Injectable() export class DeviceService { @@ -47,9 +59,13 @@ export class DeviceService { constructor( private readonly configService: ConfigService, private readonly deviceRepository: DeviceRepository, + private readonly sceneDeviceRepository: SceneDeviceRepository, private readonly productRepository: ProductRepository, private readonly deviceStatusFirebaseService: DeviceStatusFirebaseService, private readonly spaceRepository: SpaceRepository, + @Inject(forwardRef(() => SceneService)) + private readonly sceneService: SceneService, + private readonly tuyaService: TuyaService, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); const secretKey = this.configService.get('auth-config.SECRET_KEY'); @@ -630,7 +646,6 @@ export class DeviceService { status: deviceStatus.result[0].status, }; } catch (error) { - console.log(error); throw new HttpException( 'Error fetching device functions status', HttpStatus.INTERNAL_SERVER_ERROR, @@ -1136,4 +1151,134 @@ export class DeviceService { return descendants; } + async addSceneToFourSceneDevice( + deviceUuid: string, + addSceneToFourSceneDeviceDto: AddSceneToFourSceneDeviceDto, + ) { + try { + const { spaceUuid, sceneUuid, switchName } = addSceneToFourSceneDeviceDto; + + if (!spaceUuid || !sceneUuid || !switchName) { + throw new BadRequestException('Missing required fields in DTO'); + } + + const [sceneData, spaceData, deviceData] = await Promise.all([ + this.sceneService.findScene(sceneUuid), + this.sceneService.getSpaceByUuid(spaceUuid), + this.getDeviceByDeviceUuid(deviceUuid), + ]); + + // Generate a shorter automation name (e.g., "Auto_ABC123_169") + const shortUuid = deviceUuid.slice(0, 6); // First 6 characters of the UUID + const timestamp = Math.floor(Date.now() / 1000); // Current timestamp in seconds + const automationName = `Auto_${shortUuid}_${timestamp}`; + + const addAutomationData: AddAutomationDto = { + spaceUuid: spaceData.spaceTuyaUuid, + automationName, + decisionExpr: 'and', + effectiveTime: { + start: '00:00', + end: '23:59', + loops: '1111111', + }, + conditions: [ + { + code: 1, + entityId: deviceData.deviceTuyaUuid, + entityType: 'device_report', + expr: { + comparator: '==', + statusCode: switchName, + statusValue: 'scene', + }, + }, + ], + actions: [ + { + actionExecutor: 'rule_trigger', + entityId: sceneData.sceneTuyaUuid, + }, + ], + }; + + // Create automation + const automation = await this.tuyaService.createAutomation( + addAutomationData.spaceUuid, + addAutomationData.automationName, + addAutomationData.effectiveTime, + addAutomationData.decisionExpr, + addAutomationData.conditions, + addAutomationData.actions, + ); + + if (automation.success) { + const existingSceneDevice = await this.sceneDeviceRepository.findOne({ + where: { + device: { uuid: deviceUuid }, + scene: { uuid: sceneUuid }, + switchName: switchName, + }, + relations: ['scene', 'device'], + }); + + if (existingSceneDevice) { + await this.tuyaService.deleteAutomation( + spaceData.spaceTuyaUuid, + existingSceneDevice.automationTuyaUuid, + ); + + existingSceneDevice.automationTuyaUuid = automation.result.id; + existingSceneDevice.scene = sceneData; + existingSceneDevice.device = deviceData; + existingSceneDevice.switchName = switchName; + + return await this.sceneDeviceRepository.save(existingSceneDevice); + } else { + const sceneDevice = await this.sceneDeviceRepository.save({ + scene: sceneData, + device: deviceData, + automationTuyaUuid: automation.result.id, + switchName: switchName, + }); + return sceneDevice; + } + } + } catch (err) { + const errorMessage = err.message || 'Error creating automation'; + const errorStatus = err.status || HttpStatus.INTERNAL_SERVER_ERROR; + throw new HttpException(errorMessage, errorStatus); + } + } + async getSceneFourSceneDevice( + deviceUuid: string, + getSceneFourSceneDeviceDto: GetSceneFourSceneDeviceDto, + ) { + try { + const sceneDevice = await this.sceneDeviceRepository.findOne({ + where: { + device: { uuid: deviceUuid }, + switchName: + getSceneFourSceneDeviceDto.switchName as FourSceneSwitchesEnum, // Cast the string to the enum + }, + relations: ['device', 'scene'], + }); + if (sceneDevice.uuid) { + const SceneDetails = await this.sceneService.getSceneByUuid( + sceneDevice.scene.uuid, + ); + return { + switchName: sceneDevice.switchName, + createdAt: sceneDevice.createdAt, + updatedAt: sceneDevice.updatedAt, + deviceUuid: sceneDevice.device.uuid, + scene: { + ...SceneDetails.data, + }, + }; + } + } catch (error) { + throw new HttpException('Scene device not found', HttpStatus.NOT_FOUND); + } + } } diff --git a/src/door-lock/door.lock.module.ts b/src/door-lock/door.lock.module.ts index d5f070e..9bbc0d5 100644 --- a/src/door-lock/door.lock.module.ts +++ b/src/door-lock/door.lock.module.ts @@ -11,6 +11,13 @@ import { ProductRepository } from '@app/common/modules/product/repositories'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; +import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; +import { SceneService } from 'src/scene/services'; +import { + SceneIconRepository, + SceneRepository, +} from '@app/common/modules/scene/repositories'; +import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule], controllers: [DoorLockController], @@ -24,6 +31,11 @@ import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log DeviceStatusFirebaseService, SpaceRepository, DeviceStatusLogRepository, + TuyaService, + SceneService, + SceneIconRepository, + SceneRepository, + SceneDeviceRepository, ], exports: [DoorLockService], }) diff --git a/src/group/services/group.service.ts b/src/group/services/group.service.ts index 9759232..049e82d 100644 --- a/src/group/services/group.service.ts +++ b/src/group/services/group.service.ts @@ -120,7 +120,6 @@ export class GroupService { throw new HttpException('No devices found', HttpStatus.NOT_FOUND); return devices.flat(); // Flatten the array since flatMap was used } catch (error) { - console.log(error); throw new HttpException( 'This space does not have any devices for the specified group name', HttpStatus.NOT_FOUND, diff --git a/src/scene/scene.module.ts b/src/scene/scene.module.ts index 680de5f..9da0156 100644 --- a/src/scene/scene.module.ts +++ b/src/scene/scene.module.ts @@ -13,6 +13,7 @@ import { SceneRepository, } from '@app/common/modules/scene/repositories'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; +import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusFirebaseModule], @@ -26,6 +27,7 @@ import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service ProductRepository, SceneIconRepository, SceneRepository, + SceneDeviceRepository, ], exports: [SceneService], }) diff --git a/src/scene/services/scene.service.ts b/src/scene/services/scene.service.ts index 08d05be..9c5791b 100644 --- a/src/scene/services/scene.service.ts +++ b/src/scene/services/scene.service.ts @@ -3,6 +3,8 @@ import { HttpException, HttpStatus, BadRequestException, + forwardRef, + Inject, } from '@nestjs/common'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { @@ -14,7 +16,6 @@ import { UpdateSceneTapToRunDto, } from '../dtos'; import { convertKeysToSnakeCase } from '@app/common/helper/snakeCaseConverter'; -import { DeviceService } from 'src/device/services'; import { AddTapToRunSceneInterface, DeleteTapToRunSceneInterface, @@ -37,6 +38,7 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { HttpStatusCode } from 'axios'; import { ConvertedAction } from '@app/common/integrations/tuya/interfaces'; +import { DeviceService } from 'src/device/services'; @Injectable() export class SceneService { @@ -44,8 +46,9 @@ export class SceneService { private readonly spaceRepository: SpaceRepository, private readonly sceneIconRepository: SceneIconRepository, private readonly sceneRepository: SceneRepository, - private readonly deviceService: DeviceService, private readonly tuyaService: TuyaService, + @Inject(forwardRef(() => DeviceService)) + private readonly deviceService: DeviceService, ) {} async createScene( @@ -178,7 +181,10 @@ export class SceneService { }); if (!scenesData.length) { - return []; + throw new HttpException( + `No scenes found for space UUID ${spaceUuid} with showInHomePage ${showInHomePage} `, + HttpStatus.NOT_FOUND, + ); } const scenes = await Promise.all( @@ -438,7 +444,6 @@ export class SceneService { if (err instanceof BadRequestException) { throw err; } else { - console.log(err); throw new HttpException( `An error occurred while retrieving scene details for ${scene.uuid}`, HttpStatus.INTERNAL_SERVER_ERROR, @@ -454,9 +459,6 @@ export class SceneService { const space = await this.getSpaceByUuid(scene.spaceUuid); await this.delete(scene.sceneTuyaUuid, space.spaceTuyaUuid); - - await this.sceneRepository.remove(scene); - return new SuccessResponseDto({ message: `Scene with ID ${sceneUuid} deleted successfully`, }); @@ -564,7 +566,6 @@ export class SceneService { }; } catch (err) { if (err instanceof BadRequestException) { - console.log(err); throw err; } else { throw new HttpException( diff --git a/src/space/space.module.ts b/src/space/space.module.ts index cf6b766..c840bd9 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -37,6 +37,7 @@ import { import { DeviceService } from 'src/device/services'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; +import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; @Module({ imports: [ConfigModule, SpaceRepositoryModule], @@ -69,6 +70,7 @@ import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log DeviceService, DeviceStatusFirebaseService, DeviceStatusLogRepository, + SceneDeviceRepository, ], exports: [SpaceService], }) diff --git a/src/vistor-password/visitor-password.module.ts b/src/vistor-password/visitor-password.module.ts index 990e1e7..e768ad5 100644 --- a/src/vistor-password/visitor-password.module.ts +++ b/src/vistor-password/visitor-password.module.ts @@ -13,6 +13,13 @@ import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status import { SpaceRepository } from '@app/common/modules/space/repositories'; import { VisitorPasswordRepository } from '@app/common/modules/visitor-password/repositories'; import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; +import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; +import { SceneService } from 'src/scene/services'; +import { + SceneIconRepository, + SceneRepository, +} from '@app/common/modules/scene/repositories'; +import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule, DoorLockModule], controllers: [VisitorPasswordController], @@ -27,6 +34,11 @@ import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log DeviceRepository, VisitorPasswordRepository, DeviceStatusLogRepository, + TuyaService, + SceneService, + SceneIconRepository, + SceneRepository, + SceneDeviceRepository, ], exports: [VisitorPasswordService], })