mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-16 02:36:19 +00:00
Merge pull request #249 from SyncrowIOT/origin/SP-1162-BE-Missing-Spaces-name-in-Device-Table-When-Fetching-from-Community-in-the-device-management-screen
added battery and space details
This commit is contained in:
@ -18,4 +18,5 @@ export enum ProductType {
|
||||
PC = 'PC',
|
||||
FOUR_S = '4S',
|
||||
SIX_S = '6S',
|
||||
SOS = 'SOS',
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||
import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter';
|
||||
|
||||
import { ValidationService } from './space-validation.service';
|
||||
import { ProductType } from '@app/common/constants/product-type.enum';
|
||||
import { BatteryStatus } from '@app/common/constants/battery-status.enum';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceDeviceService {
|
||||
@ -18,54 +20,35 @@ export class SpaceDeviceService {
|
||||
|
||||
async listDevicesInSpace(params: GetSpaceParam): Promise<BaseResponseDto> {
|
||||
const { spaceUuid, communityUuid, projectUuid } = params;
|
||||
try {
|
||||
const space =
|
||||
await this.validationService.validateSpaceWithinCommunityAndProject(
|
||||
communityUuid,
|
||||
projectUuid,
|
||||
spaceUuid,
|
||||
);
|
||||
|
||||
if (!space.devices || space.devices.length === 0) {
|
||||
try {
|
||||
// Validate community, project, and fetch space including devices in a single query
|
||||
const space = await this.validationService.fetchSpaceDevices(spaceUuid);
|
||||
|
||||
if (!space || !space.devices?.length) {
|
||||
throw new HttpException(
|
||||
'The space does not contain any devices.',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
const safeFetch = async (device: any) => {
|
||||
try {
|
||||
const tuyaDetails = await this.getDeviceDetailsByDeviceIdTuya(
|
||||
device.deviceTuyaUuid,
|
||||
);
|
||||
const tuyaDeviceStatus =
|
||||
await this.tuyaService.getDevicesInstructionStatusTuya(
|
||||
device.deviceTuyaUuid,
|
||||
);
|
||||
return {
|
||||
uuid: device.uuid,
|
||||
deviceTuyaUuid: device.deviceTuyaUuid,
|
||||
productUuid: device.productDevice.uuid,
|
||||
productType: device.productDevice.prodType,
|
||||
isActive: device.isActive,
|
||||
updatedAt: device.updatedAt,
|
||||
deviceTag: device.tag,
|
||||
subspace: device.subspace,
|
||||
...tuyaDetails,
|
||||
status: tuyaDeviceStatus.result[0].status,
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Skipping device with deviceTuyaUuid: ${device.deviceTuyaUuid} due to error. ${error}`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const detailedDevices = await Promise.all(space.devices.map(safeFetch));
|
||||
// Fetch space hierarchy **once** and reverse it to get an ordered hierarchy
|
||||
const spaceHierarchy =
|
||||
await this.validationService.getFullSpaceHierarchy(space);
|
||||
const orderedHierarchy = spaceHierarchy.reverse();
|
||||
|
||||
// Fetch Tuya details for each device in parallel using Promise.allSettled
|
||||
const deviceDetailsPromises = space.devices.map((device) =>
|
||||
this.fetchDeviceDetails(device, orderedHierarchy),
|
||||
);
|
||||
|
||||
const detailedDevices = (await Promise.allSettled(deviceDetailsPromises))
|
||||
.filter((result) => result.status === 'fulfilled' && result.value)
|
||||
.map((result) => (result as PromiseFulfilledResult<any>).value);
|
||||
|
||||
return new SuccessResponseDto({
|
||||
data: detailedDevices.filter(Boolean),
|
||||
message: 'Successfully retrieved list of devices',
|
||||
data: detailedDevices,
|
||||
message: 'Successfully retrieved list of devices.',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error listing devices in space:', error);
|
||||
@ -76,6 +59,70 @@ export class SpaceDeviceService {
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchDeviceDetails(device: any, orderedHierarchy: any[]) {
|
||||
try {
|
||||
// Fetch Tuya details in parallel
|
||||
const [tuyaDetails, tuyaDeviceStatusResponse] = await Promise.all([
|
||||
this.getDeviceDetailsByDeviceIdTuya(device.deviceTuyaUuid),
|
||||
this.tuyaService.getDevicesInstructionStatusTuya(device.deviceTuyaUuid),
|
||||
]);
|
||||
|
||||
const tuyaStatusList =
|
||||
tuyaDeviceStatusResponse?.result?.[0]?.status || [];
|
||||
|
||||
return {
|
||||
spaces: orderedHierarchy.map((space) => ({
|
||||
uuid: space.uuid,
|
||||
spaceName: space.spaceName,
|
||||
})),
|
||||
uuid: device.uuid,
|
||||
deviceTuyaUuid: device.deviceTuyaUuid,
|
||||
productUuid: device.productDevice?.uuid || null,
|
||||
productType: device.productDevice?.prodType || null,
|
||||
isActive: device.isActive,
|
||||
updatedAt: device.updatedAt,
|
||||
deviceTag: device.tag,
|
||||
subspace: device.subspace,
|
||||
...tuyaDetails,
|
||||
...(this.extractBatteryStatus(
|
||||
device.productDevice?.prodType,
|
||||
tuyaStatusList,
|
||||
) !== null && {
|
||||
battery: this.extractBatteryStatus(
|
||||
device.productDevice?.prodType,
|
||||
tuyaStatusList,
|
||||
),
|
||||
}),
|
||||
status: tuyaStatusList,
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Skipping device ${device.deviceTuyaUuid} due to error: ${error.message}`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private extractBatteryStatus(
|
||||
deviceType: string,
|
||||
tuyaStatus: any[],
|
||||
): number | null {
|
||||
const batteryCodes = {
|
||||
[ProductType.DL]: BatteryStatus.RESIDUAL_ELECTRICITY,
|
||||
[ProductType.DS]: BatteryStatus.BATTERY_PERCENTAGE,
|
||||
[ProductType.WL]: BatteryStatus.BATTERY_PERCENTAGE,
|
||||
[ProductType.SOS]: BatteryStatus.BATTERY_PERCENTAGE,
|
||||
};
|
||||
|
||||
const batteryCode = batteryCodes[deviceType];
|
||||
if (!batteryCode) return null;
|
||||
|
||||
const batteryStatus = tuyaStatus.find(
|
||||
(status) => status.code === batteryCode,
|
||||
);
|
||||
return batteryStatus ? batteryStatus.value : null;
|
||||
}
|
||||
|
||||
private async getDeviceDetailsByDeviceIdTuya(
|
||||
deviceId: string,
|
||||
): Promise<GetDeviceDetailsInterface> {
|
||||
|
@ -104,6 +104,26 @@ export class ValidationService {
|
||||
return space;
|
||||
}
|
||||
|
||||
async fetchSpaceDevices(spaceUuid: string): Promise<SpaceEntity> {
|
||||
const space = await this.spaceRepository.findOne({
|
||||
where: { uuid: spaceUuid, disabled: false },
|
||||
relations: [
|
||||
'devices',
|
||||
'devices.productDevice',
|
||||
'devices.tag',
|
||||
'devices.subspace',
|
||||
],
|
||||
});
|
||||
|
||||
if (!space) {
|
||||
throw new HttpException(
|
||||
`Space with UUID ${spaceUuid} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
||||
async validateSpaceModel(spaceModelUuid: string): Promise<SpaceModelEntity> {
|
||||
const spaceModel = await this.spaceModelRepository.findOne({
|
||||
where: { uuid: spaceModelUuid },
|
||||
@ -125,4 +145,81 @@ export class ValidationService {
|
||||
|
||||
return spaceModel;
|
||||
}
|
||||
|
||||
async getFullSpaceHierarchy(
|
||||
space: SpaceEntity,
|
||||
): Promise<{ uuid: string; spaceName: string }[]> {
|
||||
try {
|
||||
// Fetch only the relevant spaces, starting with the target space
|
||||
const targetSpace = await this.spaceRepository.findOne({
|
||||
where: { uuid: space.uuid },
|
||||
relations: ['parent', 'children'],
|
||||
});
|
||||
|
||||
// Fetch only the ancestors of the target space
|
||||
const ancestors = await this.fetchAncestors(targetSpace);
|
||||
|
||||
// Optionally, fetch descendants if required
|
||||
const descendants = await this.fetchDescendants(targetSpace);
|
||||
|
||||
const fullHierarchy = [...ancestors, targetSpace, ...descendants].map(
|
||||
(space) => ({
|
||||
uuid: space.uuid,
|
||||
spaceName: space.spaceName,
|
||||
}),
|
||||
);
|
||||
|
||||
return fullHierarchy;
|
||||
} catch (error) {
|
||||
console.error('Error fetching space hierarchy:', error.message);
|
||||
throw new HttpException(
|
||||
'Error fetching space hierarchy',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchAncestors(space: SpaceEntity): Promise<SpaceEntity[]> {
|
||||
const ancestors: SpaceEntity[] = [];
|
||||
|
||||
let currentSpace = space;
|
||||
while (currentSpace && currentSpace.parent) {
|
||||
// Fetch the parent space
|
||||
const parent = await this.spaceRepository.findOne({
|
||||
where: { uuid: currentSpace.parent.uuid },
|
||||
relations: ['parent'], // To continue fetching upwards
|
||||
});
|
||||
|
||||
if (parent) {
|
||||
ancestors.push(parent);
|
||||
currentSpace = parent;
|
||||
} else {
|
||||
currentSpace = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the ancestors in reverse order to have the root at the start
|
||||
return ancestors.reverse();
|
||||
}
|
||||
|
||||
private async fetchDescendants(space: SpaceEntity): Promise<SpaceEntity[]> {
|
||||
const descendants: SpaceEntity[] = [];
|
||||
|
||||
// Fetch the immediate children of the current space
|
||||
const children = await this.spaceRepository.find({
|
||||
where: { parent: { uuid: space.uuid } },
|
||||
relations: ['children'], // To continue fetching downwards
|
||||
});
|
||||
|
||||
for (const child of children) {
|
||||
// Add the child to the descendants list
|
||||
descendants.push(child);
|
||||
|
||||
// Recursively fetch the child's descendants
|
||||
const childDescendants = await this.fetchDescendants(child);
|
||||
descendants.push(...childDescendants);
|
||||
}
|
||||
|
||||
return descendants;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user