Files
backend/libs/common/src/integrations/tuya/services/tuya.service.ts

288 lines
6.8 KiB
TypeScript

import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TuyaContext } from '@tuya/tuya-connector-nodejs';
import {
AddTuyaResponseInterface,
ConvertedAction,
TuyaResponseInterface,
} from '../interfaces';
@Injectable()
export class TuyaService {
private tuya: TuyaContext;
constructor(private readonly configService: ConfigService) {
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 createSpace({ name }: { name: string }) {
const path = '/v2.0/cloud/space/creation';
const body = { name };
const response = await this.tuya.request({
method: 'POST',
path,
body,
});
if (response.success) {
return response.result as string;
} else {
throw new HttpException(
'Error creating space in Tuya',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async getDeviceDetails(deviceId: string) {
const path = `/v1.1/iot-03/devices/${deviceId}`;
const response = await this.tuya.request({
method: 'GET',
path,
});
if (!response.success) {
throw new HttpException(
`Error fetching device details: ${response.msg}`,
HttpStatus.BAD_REQUEST,
);
}
return response.result;
}
async deleteSceneRule(sceneId: string, spaceId: string) {
const path = `/v2.0/cloud/scene/rule?ids=${sceneId}&space_id=${spaceId}`;
const response = await this.tuya.request({
method: 'DELETE',
path,
});
if (response.success) {
return response;
} else {
throw new HttpException(
`Error deleting scene rule: ${response.msg}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async getSceneRule(sceneId: string) {
const path = `/v2.0/cloud/scene/rule/${sceneId}`;
const response = await this.tuya.request({
method: 'GET',
path,
});
if (response.success) {
return response;
} else {
throw new HttpException(
`Error fetching scene rule: ${response.msg}`,
HttpStatus.BAD_REQUEST,
);
}
}
async addTapToRunScene(
spaceId: string,
sceneName: string,
actions: ConvertedAction[],
decisionExpr: string,
) {
const path = `/v2.0/cloud/scene/rule`;
const response = await this.tuya.request({
method: 'POST',
path,
body: {
space_id: spaceId,
name: sceneName,
type: 'scene',
decision_expr: decisionExpr,
actions: actions,
},
});
if (response.success) {
return response;
} else {
throw new HttpException(
`Error fetching scene rule: ${response.msg}`,
HttpStatus.BAD_REQUEST,
);
}
}
async updateTapToRunScene(
sceneTuyaUuid: string,
spaceId: string,
sceneName: string,
actions: ConvertedAction[],
decisionExpr: string,
) {
const path = `/v2.0/cloud/scene/rule/${sceneTuyaUuid}`;
const response = await this.tuya.request({
method: 'PUT',
path,
body: {
space_id: spaceId,
name: sceneName,
type: 'scene',
decision_expr: decisionExpr,
conditions: [],
actions: actions,
},
});
if (response.success) {
return response;
} else {
throw new HttpException(
`Error fetching scene rule: ${response.msg}`,
HttpStatus.BAD_REQUEST,
);
}
}
async triggerScene(sceneId: string): Promise<TuyaResponseInterface> {
const path = `/v2.0/cloud/scene/rule/${sceneId}/actions/trigger`;
const response: TuyaResponseInterface = await this.tuya.request({
method: 'POST',
path,
});
if (!response.success) {
throw new HttpException(
response.msg || 'Error triggering scene',
HttpStatus.BAD_REQUEST,
);
}
return response;
}
async createAutomation(
spaceId: string,
automationName: string,
effectiveTime: any,
decisionExpr: string,
conditions: any[],
actions: any[],
) {
const path = `/v2.0/cloud/scene/rule`;
const response: AddTuyaResponseInterface = await this.tuya.request({
method: 'POST',
path,
body: {
space_id: spaceId,
name: automationName,
effective_time: {
...effectiveTime,
timezone_id: 'Asia/Dubai',
},
type: 'automation',
decision_expr: decisionExpr,
conditions: conditions,
actions: actions,
},
});
if (!response.success) {
throw new HttpException(response.msg, HttpStatus.BAD_REQUEST);
}
return response;
}
async deleteAutomation(spaceId: string, automationUuid: string) {
const path = `/v2.0/cloud/scene/rule?ids=${automationUuid}&space_id=${spaceId}`;
const response = await this.tuya.request({
method: 'DELETE',
path,
});
if (!response.success) {
throw new HttpException(
'Failed to delete automation',
HttpStatus.NOT_FOUND,
);
}
return response;
}
async updateAutomation(
automationUuid: string,
spaceId: string,
automationName: string,
actions: ConvertedAction[],
conditions: any[],
decisionExpr: string,
effectiveTime: any,
) {
const path = `/v2.0/cloud/scene/rule/${automationUuid}`;
const response = await this.tuya.request({
method: 'PUT',
path,
body: {
space_id: spaceId,
name: automationName,
effective_time: {
...effectiveTime,
timezone_id: 'Asia/Dubai',
},
type: 'automation',
decision_expr: decisionExpr,
conditions: conditions,
actions: actions,
},
});
if (response.success) {
return response;
} else {
throw new HttpException(
`Error fetching automation rule: ${response.msg}`,
HttpStatus.BAD_REQUEST,
);
}
}
async updateAutomationState(
spaceId: string,
automationUuid: string,
isEnable: boolean,
) {
const path = `/v2.0/cloud/scene/rule/state?space_id=${spaceId}`;
try {
const response: TuyaResponseInterface = await this.tuya.request({
method: 'PUT',
path,
body: {
ids: automationUuid,
is_enable: isEnable,
},
});
if (!response.success) {
throw new HttpException('Automation not found', HttpStatus.NOT_FOUND);
}
return response;
} catch (error) {
throw new HttpException(
error.message || 'Failed to update automation state',
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}