mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-15 10:25:23 +00:00
feat: implement date formatting function and enhance PowerClampService with space-based data retrieval
This commit is contained in:
14
libs/common/src/helper/date-format.ts
Normal file
14
libs/common/src/helper/date-format.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export function toDDMMYYYY(dateString?: string | null): string | null {
|
||||||
|
if (!dateString) return null;
|
||||||
|
|
||||||
|
// Ensure dateString is valid format YYYY-MM-DD
|
||||||
|
const regex = /^\d{4}-\d{2}-\d{2}$/;
|
||||||
|
if (!regex.test(dateString)) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid date format: ${dateString}. Expected format is YYYY-MM-DD`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [year, month, day] = dateString.split('-');
|
||||||
|
return `${day}-${month}-${year}`;
|
||||||
|
}
|
@ -46,16 +46,15 @@ export class PowerClampService {
|
|||||||
procedureFileName: string,
|
procedureFileName: string,
|
||||||
params: (string | number | null)[],
|
params: (string | number | null)[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const query = this.loadQuery(procedureFileName);
|
const query = this.loadQuery(
|
||||||
|
'fact_device_energy_consumed',
|
||||||
|
procedureFileName,
|
||||||
|
);
|
||||||
await this.dataSource.query(query, params);
|
await this.dataSource.query(query, params);
|
||||||
console.log(`Procedure ${procedureFileName} executed successfully.`);
|
console.log(`Procedure ${procedureFileName} executed successfully.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadQuery(fileName: string): string {
|
private loadQuery(folderName: string, fileName: string): string {
|
||||||
return this.sqlLoader.loadQuery(
|
return this.sqlLoader.loadQuery(folderName, fileName, SQL_PROCEDURES_PATH);
|
||||||
'fact_device_energy_consumed',
|
|
||||||
fileName,
|
|
||||||
SQL_PROCEDURES_PATH,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
WITH params AS (
|
WITH params AS (
|
||||||
SELECT
|
SELECT
|
||||||
TO_DATE(NULLIF($2, ''), 'DD-MM-YYYY') AS start_date,
|
TO_DATE(NULLIF($1, ''), 'DD-MM-YYYY') AS start_date,
|
||||||
TO_DATE(NULLIF($3, ''), 'DD-MM-YYYY') AS end_date,
|
TO_DATE(NULLIF($2, ''), 'DD-MM-YYYY') AS end_date,
|
||||||
string_to_array(NULLIF($4, ''), ',') AS device_ids
|
string_to_array(NULLIF($3, ''), ',') AS device_ids
|
||||||
)
|
)
|
||||||
|
|
||||||
SELECT TO_CHAR(A.date, 'MM-YYYY') AS month,
|
SELECT TO_CHAR(A.date, 'MM-YYYY') AS month,
|
||||||
|
@ -9,9 +9,15 @@ import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
|||||||
import { ControllerRoute } from '@app/common/constants/controller-route';
|
import { ControllerRoute } from '@app/common/constants/controller-route';
|
||||||
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
||||||
import { PowerClampService } from '../services/power-clamp.service';
|
import { PowerClampService } from '../services/power-clamp.service';
|
||||||
import { GetPowerClampDto } from '../dto/get-power-clamp.dto';
|
import {
|
||||||
|
GetPowerClampBySpaceDto,
|
||||||
|
GetPowerClampDto,
|
||||||
|
} from '../dto/get-power-clamp.dto';
|
||||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
import { PowerClampParamsDto } from '../dto/power-clamp-params.dto';
|
import {
|
||||||
|
PowerClampParamsDto,
|
||||||
|
SpaceParamsDto,
|
||||||
|
} from '../dto/power-clamp-params.dto';
|
||||||
|
|
||||||
@ApiTags('Power Clamp Module')
|
@ApiTags('Power Clamp Module')
|
||||||
@Controller({
|
@Controller({
|
||||||
@ -39,4 +45,22 @@ export class PowerClampController {
|
|||||||
): Promise<BaseResponseDto> {
|
): Promise<BaseResponseDto> {
|
||||||
return await this.powerClampService.getPowerClampData(params, query);
|
return await this.powerClampService.getPowerClampData(params, query);
|
||||||
}
|
}
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Get('historical/space/:spaceUuid')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ControllerRoute.PowerClamp.ACTIONS.GET_ENERGY_SUMMARY,
|
||||||
|
description: ControllerRoute.PowerClamp.ACTIONS.GET_ENERGY_DESCRIPTION,
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: 'spaceUuid',
|
||||||
|
description: 'UUID of the Space',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
async getPowerClampBySpaceData(
|
||||||
|
@Param() params: SpaceParamsDto,
|
||||||
|
@Query() query: GetPowerClampBySpaceDto,
|
||||||
|
): Promise<BaseResponseDto> {
|
||||||
|
return await this.powerClampService.getPowerClampBySpaceData(params, query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import { IsOptional, IsDateString, Matches } from 'class-validator';
|
import { IsOptional, IsDateString, Matches, IsNotEmpty } from 'class-validator';
|
||||||
|
|
||||||
export class GetPowerClampDto {
|
export class GetPowerClampDto {
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
@ -33,3 +33,21 @@ export class GetPowerClampDto {
|
|||||||
})
|
})
|
||||||
year?: string;
|
year?: string;
|
||||||
}
|
}
|
||||||
|
export class GetPowerClampBySpaceDto {
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Input date in ISO format (YYYY-MM-DD) to filter the data',
|
||||||
|
example: '2025-04-23',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@IsDateString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
public startDate: string;
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Input date in ISO format (YYYY-MM-DD) to filter the data',
|
||||||
|
example: '2025-05-23',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@IsDateString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
public endDate: string;
|
||||||
|
}
|
||||||
|
@ -4,3 +4,7 @@ export class PowerClampParamsDto {
|
|||||||
@IsUUID('4', { message: 'Invalid UUID format' })
|
@IsUUID('4', { message: 'Invalid UUID format' })
|
||||||
powerClampUuid: string;
|
powerClampUuid: string;
|
||||||
}
|
}
|
||||||
|
export class SpaceParamsDto {
|
||||||
|
@IsUUID('4', { message: 'Invalid UUID format' })
|
||||||
|
spaceUuid: string;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { PowerClampService } from './services/power-clamp.service';
|
import { PowerClampService as PowerClamp } from './services/power-clamp.service';
|
||||||
import { PowerClampController } from './controllers';
|
import { PowerClampController } from './controllers';
|
||||||
import {
|
import {
|
||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
@ -8,16 +8,108 @@ import {
|
|||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||||
|
import {
|
||||||
|
SpaceDeviceService,
|
||||||
|
SpaceLinkService,
|
||||||
|
SpaceService,
|
||||||
|
SubspaceDeviceService,
|
||||||
|
SubSpaceService,
|
||||||
|
ValidationService,
|
||||||
|
} from 'src/space/services';
|
||||||
|
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
||||||
|
import { DeviceService } from 'src/device/services';
|
||||||
|
import {
|
||||||
|
InviteSpaceRepository,
|
||||||
|
SpaceLinkRepository,
|
||||||
|
SpaceProductAllocationRepository,
|
||||||
|
SpaceRepository,
|
||||||
|
TagRepository,
|
||||||
|
} from '@app/common/modules/space';
|
||||||
|
import { CommunityService } from 'src/community/services';
|
||||||
|
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
||||||
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||||
|
import {
|
||||||
|
SpaceModelProductAllocationRepoitory,
|
||||||
|
SpaceModelRepository,
|
||||||
|
SubspaceModelProductAllocationRepoitory,
|
||||||
|
SubspaceModelRepository,
|
||||||
|
} from '@app/common/modules/space-model';
|
||||||
|
import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories';
|
||||||
|
import { ProductRepository } from '@app/common/modules/product/repositories';
|
||||||
|
import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service';
|
||||||
|
import { SceneService } from 'src/scene/services';
|
||||||
|
import { PowerClampService } from '@app/common/helper/services/power.clamp.service';
|
||||||
|
import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories';
|
||||||
|
import {
|
||||||
|
SceneIconRepository,
|
||||||
|
SceneRepository,
|
||||||
|
} from '@app/common/modules/scene/repositories';
|
||||||
|
import { AutomationRepository } from '@app/common/modules/automation/repositories';
|
||||||
|
import { TagService } from 'src/tags/services';
|
||||||
|
import {
|
||||||
|
SpaceModelService,
|
||||||
|
SubSpaceModelService,
|
||||||
|
} from 'src/space-model/services';
|
||||||
|
import { SpaceProductAllocationService } from 'src/space/services/space-product-allocation.service';
|
||||||
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import {
|
||||||
|
SubspaceProductAllocationRepository,
|
||||||
|
SubspaceRepository,
|
||||||
|
} from '@app/common/modules/space/repositories/subspace.repository';
|
||||||
|
import { SubspaceProductAllocationService } from 'src/space/services/subspace/subspace-product-allocation.service';
|
||||||
|
import { NewTagRepository } from '@app/common/modules/tag/repositories/tag-repository';
|
||||||
|
import { SpaceModelProductAllocationService } from 'src/space-model/services/space-model-product-allocation.service';
|
||||||
|
import { SubspaceModelProductAllocationService } from 'src/space-model/services/subspace/subspace-model-product-allocation.service';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule],
|
imports: [ConfigModule],
|
||||||
controllers: [PowerClampController],
|
controllers: [PowerClampController],
|
||||||
providers: [
|
providers: [
|
||||||
PowerClampService,
|
PowerClamp,
|
||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampHourlyRepository,
|
PowerClampHourlyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
DeviceRepository,
|
DeviceRepository,
|
||||||
|
SpaceDeviceService,
|
||||||
|
TuyaService,
|
||||||
|
ValidationService,
|
||||||
|
DeviceService,
|
||||||
|
SpaceRepository,
|
||||||
|
CommunityService,
|
||||||
|
ProjectRepository,
|
||||||
|
CommunityRepository,
|
||||||
|
SpaceModelRepository,
|
||||||
|
SceneDeviceRepository,
|
||||||
|
ProductRepository,
|
||||||
|
DeviceStatusFirebaseService,
|
||||||
|
SceneService,
|
||||||
|
SpaceService,
|
||||||
|
PowerClampService,
|
||||||
|
DeviceStatusLogRepository,
|
||||||
|
SceneIconRepository,
|
||||||
|
SceneRepository,
|
||||||
|
AutomationRepository,
|
||||||
|
InviteSpaceRepository,
|
||||||
|
SpaceLinkService,
|
||||||
|
SubSpaceService,
|
||||||
|
TagService,
|
||||||
|
SpaceModelService,
|
||||||
|
SpaceProductAllocationService,
|
||||||
|
SqlLoaderService,
|
||||||
|
SpaceLinkRepository,
|
||||||
|
SubspaceRepository,
|
||||||
|
SubspaceDeviceService,
|
||||||
|
SubspaceProductAllocationService,
|
||||||
|
NewTagRepository,
|
||||||
|
SubSpaceModelService,
|
||||||
|
SpaceModelProductAllocationService,
|
||||||
|
SpaceProductAllocationRepository,
|
||||||
|
SubspaceProductAllocationRepository,
|
||||||
|
TagRepository,
|
||||||
|
SubspaceModelRepository,
|
||||||
|
SubspaceModelProductAllocationService,
|
||||||
|
SpaceModelProductAllocationRepoitory,
|
||||||
|
SubspaceModelProductAllocationRepoitory,
|
||||||
],
|
],
|
||||||
exports: [PowerClampService],
|
exports: [PowerClamp],
|
||||||
})
|
})
|
||||||
export class PowerClampModule {}
|
export class PowerClampModule {}
|
||||||
|
@ -1,13 +1,24 @@
|
|||||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
import { GetPowerClampDto } from '../dto/get-power-clamp.dto';
|
import {
|
||||||
|
GetPowerClampBySpaceDto,
|
||||||
|
GetPowerClampDto,
|
||||||
|
} from '../dto/get-power-clamp.dto';
|
||||||
import {
|
import {
|
||||||
PowerClampDailyRepository,
|
PowerClampDailyRepository,
|
||||||
PowerClampHourlyRepository,
|
PowerClampHourlyRepository,
|
||||||
PowerClampMonthlyRepository,
|
PowerClampMonthlyRepository,
|
||||||
} from '@app/common/modules/power-clamp/repositories';
|
} from '@app/common/modules/power-clamp/repositories';
|
||||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||||
import { PowerClampParamsDto } from '../dto/power-clamp-params.dto';
|
import {
|
||||||
|
PowerClampParamsDto,
|
||||||
|
SpaceParamsDto,
|
||||||
|
} from '../dto/power-clamp-params.dto';
|
||||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||||
|
import { SpaceDeviceService } from 'src/space/services';
|
||||||
|
import { SqlLoaderService } from '@app/common/helper/services/sql-loader.service';
|
||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
import { SQL_PROCEDURES_PATH } from '@app/common/constants/sql-query-path';
|
||||||
|
import { toDDMMYYYY } from '@app/common/helper/date-format';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PowerClampService {
|
export class PowerClampService {
|
||||||
@ -16,8 +27,43 @@ export class PowerClampService {
|
|||||||
private readonly powerClampHourlyRepository: PowerClampHourlyRepository,
|
private readonly powerClampHourlyRepository: PowerClampHourlyRepository,
|
||||||
private readonly powerClampMonthlyRepository: PowerClampMonthlyRepository,
|
private readonly powerClampMonthlyRepository: PowerClampMonthlyRepository,
|
||||||
private readonly deviceRepository: DeviceRepository,
|
private readonly deviceRepository: DeviceRepository,
|
||||||
|
private readonly spaceDeviceService: SpaceDeviceService,
|
||||||
|
private readonly sqlLoader: SqlLoaderService,
|
||||||
|
private readonly dataSource: DataSource,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
async getPowerClampBySpaceData(
|
||||||
|
params: SpaceParamsDto,
|
||||||
|
query: GetPowerClampBySpaceDto,
|
||||||
|
) {
|
||||||
|
const { startDate, endDate } = query;
|
||||||
|
const { spaceUuid } = params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const devices =
|
||||||
|
await this.spaceDeviceService.getAllDevicesBySpace(spaceUuid);
|
||||||
|
|
||||||
|
const deviceUuids = devices.map((device) => device.uuid).join(',');
|
||||||
|
|
||||||
|
const formattedStartDate = toDDMMYYYY(startDate);
|
||||||
|
const formattedEndDate = toDDMMYYYY(endDate);
|
||||||
|
const data = await this.executeProcedure(
|
||||||
|
'fact_monthly_space_energy_consumed_procedure',
|
||||||
|
[formattedStartDate, formattedEndDate, deviceUuids],
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.buildResponse(
|
||||||
|
`Power clamp data for space ${spaceUuid} fetched successfully`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(
|
||||||
|
error.message || 'Error fetching power clamp data by space',
|
||||||
|
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getPowerClampData(
|
async getPowerClampData(
|
||||||
params: PowerClampParamsDto,
|
params: PowerClampParamsDto,
|
||||||
query: GetPowerClampDto,
|
query: GetPowerClampDto,
|
||||||
@ -99,4 +145,17 @@ export class PowerClampService {
|
|||||||
statusCode: HttpStatus.OK,
|
statusCode: HttpStatus.OK,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
private async executeProcedure(
|
||||||
|
procedureFileName: string,
|
||||||
|
params: (string | number | null)[],
|
||||||
|
): Promise<any[]> {
|
||||||
|
const query = this.loadQuery(
|
||||||
|
'fact_space_energy_consumed',
|
||||||
|
procedureFileName,
|
||||||
|
);
|
||||||
|
return await this.dataSource.query(query, params);
|
||||||
|
}
|
||||||
|
private loadQuery(folderName: string, fileName: string): string {
|
||||||
|
return this.sqlLoader.loadQuery(folderName, fileName, SQL_PROCEDURES_PATH);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
|
||||||
|
|
||||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
import {
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
Injectable,
|
||||||
|
NotFoundException,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { GetSpaceParam } from '../dtos';
|
import { GetSpaceParam } from '../dtos';
|
||||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||||
@ -9,6 +14,9 @@ import { ValidationService } from './space-validation.service';
|
|||||||
import { ProductType } from '@app/common/constants/product-type.enum';
|
import { ProductType } from '@app/common/constants/product-type.enum';
|
||||||
import { BatteryStatus } from '@app/common/constants/battery-status.enum';
|
import { BatteryStatus } from '@app/common/constants/battery-status.enum';
|
||||||
import { DeviceService } from 'src/device/services';
|
import { DeviceService } from 'src/device/services';
|
||||||
|
import { SpaceRepository } from '@app/common/modules/space';
|
||||||
|
import { DeviceEntity } from '@app/common/modules/device/entities';
|
||||||
|
import { SpaceEntity } from '@app/common/modules/space/entities/space.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SpaceDeviceService {
|
export class SpaceDeviceService {
|
||||||
@ -16,6 +24,7 @@ export class SpaceDeviceService {
|
|||||||
private readonly tuyaService: TuyaService,
|
private readonly tuyaService: TuyaService,
|
||||||
private readonly validationService: ValidationService,
|
private readonly validationService: ValidationService,
|
||||||
private readonly deviceService: DeviceService,
|
private readonly deviceService: DeviceService,
|
||||||
|
private readonly spaceRepository: SpaceRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async listDevicesInSpace(params: GetSpaceParam): Promise<BaseResponseDto> {
|
async listDevicesInSpace(params: GetSpaceParam): Promise<BaseResponseDto> {
|
||||||
@ -121,4 +130,37 @@ export class SpaceDeviceService {
|
|||||||
);
|
);
|
||||||
return batteryStatus ? batteryStatus.value : null;
|
return batteryStatus ? batteryStatus.value : null;
|
||||||
}
|
}
|
||||||
|
async getAllDevicesBySpace(spaceUuid: string): Promise<DeviceEntity[]> {
|
||||||
|
const space = await this.spaceRepository.findOne({
|
||||||
|
where: { uuid: spaceUuid },
|
||||||
|
relations: ['children', 'devices'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!space) {
|
||||||
|
throw new NotFoundException('Space not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const allDevices: DeviceEntity[] = [...space.devices];
|
||||||
|
|
||||||
|
// Recursive fetch function
|
||||||
|
const fetchChildren = async (parentSpace: SpaceEntity) => {
|
||||||
|
const children = await this.spaceRepository.find({
|
||||||
|
where: { parent: { uuid: parentSpace.uuid } },
|
||||||
|
relations: ['children', 'devices'],
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const child of children) {
|
||||||
|
allDevices.push(...child.devices);
|
||||||
|
|
||||||
|
if (child.children.length > 0) {
|
||||||
|
await fetchChildren(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start recursive fetch
|
||||||
|
await fetchChildren(space);
|
||||||
|
|
||||||
|
return allDevices;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user