This commit is contained in:
Faris Armoush
2025-04-29 10:39:54 +03:00
201 changed files with 11547 additions and 2722 deletions

View File

@ -6,13 +6,23 @@ import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart';
class AccessMangApi {
AccessMangApi() {
_validateEndpoints();
}
void _validateEndpoints() {
if (!ApiEndpoints.getDevices.contains('{projectId}')) {
throw Exception("Endpoint 'getDevices' must contain '{projectId}' placeholder.");
}
}
Future<List<PasswordModel>> fetchVisitorPassword(String projectId) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.visitorPassword.replaceAll('{projectId}', projectId),
path: ApiEndpoints.visitorPassword,
showServerMessage: true,
expectedResponseModel: (json) {
List<dynamic> jsonData = json;
List<dynamic> jsonData = json['data'] ?? [];
List<PasswordModel> passwordList = jsonData.map((jsonItem) {
return PasswordModel.fromJson(jsonItem);
}).toList();
@ -25,17 +35,22 @@ class AccessMangApi {
}
}
Future fetchDevices(String projectId) async {
Future fetchDoorLockDeviceList(String projectId) async {
try {
// The endpoint structure is already validated during initialization.
final response = await HTTPService().get(
path: ApiEndpoints.getDevices.replaceAll('{projectId}', projectId),
queryParameters: {
'deviceType': 'DOOR_LOCK',
},
showServerMessage: true,
expectedResponseModel: (json) {
List<dynamic> jsonData = json;
List<DeviceModel> passwordList = jsonData.map((jsonItem) {
List<dynamic> jsonData = json['data'] ?? [];
List<DeviceModel> deviceList = jsonData.map((jsonItem) {
return DeviceModel.fromJson(jsonItem);
}).toList();
return passwordList;
return deviceList;
},
);
return response;
@ -52,14 +67,15 @@ class AccessMangApi {
String? invalidTime,
List<String>? devicesUuid}) async {
final response = await HTTPService().post(
path: ApiEndpoints.sendOnlineOneTime,
path: ApiEndpoints.visitorPassword,
body: jsonEncode({
"email": email,
"passwordName": passwordName,
"password": password,
"devicesUuid": devicesUuid,
"effectiveTime": effectiveTime,
"invalidTime": invalidTime
"invalidTime": invalidTime,
"operationType": "ONLINE_ONE_TIME",
}),
showServerMessage: true,
expectedResponseModel: (json) {
@ -84,13 +100,13 @@ class AccessMangApi {
"password": password,
"effectiveTime": effectiveTime,
"invalidTime": invalidTime,
"operationType": "ONLINE_MULTIPLE_TIME",
};
if (scheduleList != null) {
body["scheduleList"] =
scheduleList.map((schedule) => schedule.toJson()).toList();
body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList();
}
final response = await HTTPService().post(
path: ApiEndpoints.sendOnlineMultipleTime,
path: ApiEndpoints.visitorPassword,
body: jsonEncode(body),
showServerMessage: true,
expectedResponseModel: (json) {
@ -105,8 +121,9 @@ class AccessMangApi {
Future postOffLineOneTime(
{String? email, String? passwordName, List<String>? devicesUuid}) async {
final response = await HTTPService().post(
path: ApiEndpoints.sendOffLineOneTime,
path: ApiEndpoints.visitorPassword,
body: jsonEncode({
"operationType": "OFFLINE_ONE_TIME",
"email": email,
"passwordName": passwordName,
"devicesUuid": devicesUuid
@ -126,13 +143,14 @@ class AccessMangApi {
String? invalidTime,
List<String>? devicesUuid}) async {
final response = await HTTPService().post(
path: ApiEndpoints.sendOffLineMultipleTime,
path: ApiEndpoints.visitorPassword,
body: jsonEncode({
"email": email,
"devicesUuid": devicesUuid,
"passwordName": passwordName,
"effectiveTime": effectiveTime,
"invalidTime": invalidTime,
"operationType": "OFFLINE_MULTIPLE_TIME",
}),
showServerMessage: true,
expectedResponseModel: (json) {

View File

@ -0,0 +1,95 @@
import 'dart:developer';
import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart';
abstract interface class BatchControlDevicesService {
Future<bool> batchControlDevices({
required List<String> uuids,
required String code,
required Object value,
});
}
final class RemoteBatchControlDevicesService implements BatchControlDevicesService {
@override
Future<bool> batchControlDevices({
required List<String> uuids,
required String code,
required Object value,
}) async {
try {
final body = {
'devicesUuid': uuids,
'code': code,
'value': value,
'operationType': 'COMMAND',
};
final response = await HTTPService().post(
path: ApiEndpoints.deviceBatchControl,
body: body,
showServerMessage: true,
expectedResponseModel: (json) => (json['success'] as bool?) ?? false,
);
return response;
} catch (e) {
log('Error fetching $e', name: 'BatchControlDevicesService');
return false;
}
}
}
final class DebouncedBatchControlDevicesService
implements BatchControlDevicesService {
final BatchControlDevicesService decoratee;
final Duration debounceDuration;
DebouncedBatchControlDevicesService({
required this.decoratee,
this.debounceDuration = const Duration(milliseconds: 1500),
});
final _pendingRequests = <(List<String> uuids, String code, Object value)>[];
var _isProcessing = false;
@override
Future<bool> batchControlDevices({
required List<String> uuids,
required String code,
required Object value,
}) async {
_pendingRequests.add((uuids, code, value));
if (_isProcessing) return false;
_isProcessing = true;
await Future.delayed(debounceDuration);
final groupedRequests =
<String, (List<String> uuids, String code, Object value)>{};
for (final request in _pendingRequests) {
final (_, requestCode, requestValue) = request;
groupedRequests[requestCode] = request;
}
_pendingRequests.clear();
try {
var allSuccessful = true;
for (final request in groupedRequests.values) {
final (lastRequestUuids, lastRequestCode, lastRequestValue) = request;
final success = await decoratee.batchControlDevices(
uuids: lastRequestUuids,
code: lastRequestCode,
value: lastRequestValue,
);
if (!success) allSuccessful = false;
}
return allSuccessful;
} finally {
_isProcessing = false;
}
}
}

View File

@ -0,0 +1,84 @@
import 'dart:developer';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart';
abstract interface class ControlDeviceService {
Future<bool> controlDevice({
required String deviceUuid,
required Status status,
});
}
final class RemoteControlDeviceService implements ControlDeviceService {
@override
Future<bool> controlDevice({
required String deviceUuid,
required Status status,
}) async {
try {
final response = await HTTPService().post(
path: ApiEndpoints.deviceControl.replaceAll('{uuid}', deviceUuid),
body: status.toMap(),
showServerMessage: true,
expectedResponseModel: (json) {
return (json['success'] as bool?) ?? false;
},
);
return response;
} catch (e) {
log('Error fetching $e', name: 'ControlDeviceService');
return false;
}
}
}
final class DebouncedControlDeviceService implements ControlDeviceService {
final ControlDeviceService decoratee;
final Duration debounceDuration;
DebouncedControlDeviceService({
required this.decoratee,
this.debounceDuration = const Duration(milliseconds: 1500),
});
final _pendingRequests = <(String deviceUuid, Status status)>[];
var _isProcessing = false;
@override
Future<bool> controlDevice({
required String deviceUuid,
required Status status,
}) async {
_pendingRequests.add((deviceUuid, status));
if (_isProcessing) return false;
_isProcessing = true;
await Future.delayed(debounceDuration);
final groupedRequests = <String, (String deviceUuid, Status status)>{};
for (final request in _pendingRequests) {
final (_, requestStatus) = request;
groupedRequests[requestStatus.code] = request;
}
_pendingRequests.clear();
try {
var allSuccessful = true;
for (final request in groupedRequests.values) {
final (lastRequestDeviceUuid, lastRequestStatus) = request;
final success = await decoratee.controlDevice(
deviceUuid: lastRequestDeviceUuid,
status: lastRequestStatus,
);
if (!success) allSuccessful = false;
}
return allSuccessful;
} finally {
_isProcessing = false;
}
}
}

View File

@ -23,8 +23,7 @@ class DevicesManagementApi {
: ApiEndpoints.getAllDevices.replaceAll('{projectId}', projectId),
showServerMessage: true,
expectedResponseModel: (json) {
List<dynamic> jsonData =
communityId.isNotEmpty && spaceId.isNotEmpty ? json['data'] : json;
List<dynamic> jsonData = json['data'];
List<AllDevicesModel> devicesList = jsonData.map((jsonItem) {
return AllDevicesModel.fromJson(jsonItem);
}).toList();
@ -33,7 +32,7 @@ class DevicesManagementApi {
);
return response;
} catch (e) {
debugPrint('Error fetching $e');
debugPrint('Error fetching device $e');
return [];
}
}
@ -44,7 +43,7 @@ class DevicesManagementApi {
path: ApiEndpoints.getDeviceStatus.replaceAll('{uuid}', uuid),
showServerMessage: true,
expectedResponseModel: (json) {
return DeviceStatus.fromJson(json);
return DeviceStatus.fromJson(json['data']);
},
);
return response;
@ -61,7 +60,7 @@ class DevicesManagementApi {
Future getPowerClampInfo(String deviceId) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.powerClamp.replaceAll('{powerClampUuid}', deviceId),
path: ApiEndpoints.getDeviceStatus.replaceAll('{uuid}', deviceId),
showServerMessage: true,
expectedResponseModel: (json) {
return json;
@ -98,6 +97,7 @@ class DevicesManagementApi {
'devicesUuid': uuids,
'code': code,
'value': value,
'operationType': 'COMMAND',
};
final response = await HTTPService().post(
@ -105,7 +105,7 @@ class DevicesManagementApi {
body: body,
showServerMessage: true,
expectedResponseModel: (json) {
return (json['successResults'] as List).isNotEmpty;
return json['success'] ?? false;
},
);
@ -125,7 +125,7 @@ class DevicesManagementApi {
if (json == null || json.isEmpty || json == []) {
return devices;
}
for (var device in json['devices']) {
for (var device in json['data']['devices']) {
devices.add(DeviceModel.fromJson(device));
}
return devices;
@ -153,7 +153,7 @@ class DevicesManagementApi {
path: ApiEndpoints.getDeviceLogs.replaceAll('{uuid}', uuid).replaceAll('{code}', code),
showServerMessage: false,
expectedResponseModel: (json) {
return DeviceReport.fromJson(json);
return DeviceReport.fromJson(json['data']);
},
);
return response;
@ -169,7 +169,7 @@ class DevicesManagementApi {
.replaceAll('{endTime}', to ?? ''),
showServerMessage: false,
expectedResponseModel: (json) {
return DeviceReport.fromJson(json);
return DeviceReport.fromJson(json['data']);
},
);
return response;
@ -185,7 +185,7 @@ class DevicesManagementApi {
queryParameters: queryParameters,
showServerMessage: true,
expectedResponseModel: (json) {
return DeviceStatus.fromJson(json['status']);
return DeviceStatus.fromJson(json['data']['status']);
},
);
return response;
@ -321,13 +321,14 @@ class DevicesManagementApi {
Future<bool> factoryReset(FactoryResetModel factoryReset, String uuid) async {
try {
final response = await HTTPService().post(
path: ApiEndpoints.factoryReset.replaceAll('{deviceUuid}', uuid),
path: ApiEndpoints.factoryReset,
body: factoryReset.toMap(),
showServerMessage: true,
expectedResponseModel: (json) {
return json['success'] ?? false;
},
);
return response;
} catch (e) {
debugPrint('Error fetching $e');

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_scene_model.dart';
import 'package:syncrow_web/pages/routines/models/icon_model.dart';
@ -6,7 +7,6 @@ import 'package:syncrow_web/pages/routines/models/routine_details_model.dart';
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
class SceneApi {
static final HTTPService _httpService = HTTPService();
@ -35,10 +35,11 @@ class SceneApi {
//
// create automation
static Future<Map<String, dynamic>> createAutomation(
CreateAutomationModel createAutomationModel) async {
CreateAutomationModel createAutomationModel, String projectId) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.createAutomation,
path:
ApiEndpoints.createAutomation.replaceAll('{projectId}', projectId),
body: createAutomationModel.toMap(),
showServerMessage: false,
expectedResponseModel: (json) {
@ -99,11 +100,14 @@ class SceneApi {
//getAutomation
static Future<List<ScenesModel>> getAutomation(String spaceId) async {
static Future<List<ScenesModel>> getAutomation(
String spaceId, String communityId, String projectId) async {
try {
final response = await _httpService.get(
path:
ApiEndpoints.getSpaceAutomation.replaceAll('{spaceUuid}', spaceId),
path: ApiEndpoints.getSpaceAutomation
.replaceAll('{spaceUuid}', spaceId)
.replaceAll('{communityId}', communityId)
.replaceAll('{projectId}', projectId),
showServerMessage: false,
expectedResponseModel: (json) {
List<ScenesModel> scenes = [];
@ -134,11 +138,12 @@ class SceneApi {
//automation details
static Future<RoutineDetailsModel> getAutomationDetails(
String automationId) async {
String automationId, String projectId) async {
try {
final response = await _httpService.get(
path: ApiEndpoints.getAutomationDetails
.replaceAll('{automationId}', automationId),
.replaceAll('{automationId}', automationId)
.replaceAll('{projectId}', projectId),
showServerMessage: false,
expectedResponseModel: (json) => RoutineDetailsModel.fromMap(json),
);
@ -166,12 +171,13 @@ class SceneApi {
}
//update automation
static updateAutomation(
CreateAutomationModel createAutomationModel, String automationId) async {
static updateAutomation(CreateAutomationModel createAutomationModel,
String automationId, String projectId) async {
try {
final response = await _httpService.put(
path: ApiEndpoints.updateAutomation
.replaceAll('{automationId}', automationId),
.replaceAll('{automationId}', automationId)
.replaceAll('{projectId}', projectId),
body: createAutomationModel
.toJson(automationId.isNotEmpty == true ? automationId : null),
expectedResponseModel: (json) {
@ -218,12 +224,14 @@ class SceneApi {
// delete automation
static Future<bool> deleteAutomation(
{required String unitUuid, required String automationId}) async {
{required String unitUuid,
required String automationId,
required String projectId}) async {
try {
final response = await _httpService.delete(
path: ApiEndpoints.deleteAutomation
.replaceAll('{automationId}', automationId)
.replaceAll('{unitUuid}', unitUuid),
.replaceAll('{projectId}', projectId),
showServerMessage: false,
expectedResponseModel: (json) => json['statusCode'] == 200,
);
@ -232,4 +240,59 @@ class SceneApi {
rethrow;
}
}
static Future<bool> updateAutomationStatus(String automationId,
AutomationStatusUpdate createAutomationEnable, String projectId) async {
try {
final response = await _httpService.patch(
path: ApiEndpoints.updateAutomationStatus
.replaceAll('{automationId}', automationId)
.replaceAll('{projectId}', projectId),
body: createAutomationEnable.toMap(),
expectedResponseModel: (json) => json['success'],
);
return response;
} catch (e) {
rethrow;
}
}
static Future<bool> triggerScene(String sceneId) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.triggerScene.replaceAll('{sceneId}', sceneId),
showServerMessage: false,
expectedResponseModel: (json) => json['success'],
);
return response;
} catch (e) {
rethrow;
}
}
static Future<List<ScenesModel>> getAutomationByUnitId(
String unitId,
String communityId,
String projectId,
) async {
try {
final response = await _httpService.get(
path: ApiEndpoints.getUnitAutomation
.replaceAll('{unitUuid}', unitId)
.replaceAll('{communityId}', communityId)
.replaceAll('{projectId}', projectId),
showServerMessage: false,
expectedResponseModel: (json) {
List<ScenesModel> scenes = [];
for (var scene in json) {
scenes.add(ScenesModel.fromJson(scene));
}
return scenes;
},
);
return response;
} catch (e) {
rethrow;
}
}
}

View File

@ -1,29 +1,28 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/space_tree/model/pagination_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_response_model.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
class CommunitySpaceManagementApi {
// Community Management APIs
Future<List<CommunityModel>> fetchCommunities(String projectId,
{int page = 1}) async {
Future<List<CommunityModel>> fetchCommunities(String projectId, {int page = 1}) async {
try {
List<CommunityModel> allCommunities = [];
bool hasNext = true;
while (hasNext) {
await HTTPService().get(
path: ApiEndpoints.getCommunityList
.replaceAll('{projectId}', projectId),
queryParameters: {'page': page},
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
queryParameters: {
'page': page,
},
expectedResponseModel: (json) {
try {
List<dynamic> jsonData = json['data'] ?? [];
@ -49,11 +48,42 @@ class CommunitySpaceManagementApi {
}
}
Future<PaginationModel> fetchCommunitiesAndSpaces(
{required String projectId, int page = 1, String search = ''}) async {
PaginationModel paginationModel = const PaginationModel.emptyConstructor();
try {
bool hasNext = false;
await HTTPService().get(
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
queryParameters: {'page': page, 'includeSpaces': true, 'size': 25, 'search': search},
expectedResponseModel: (json) {
try {
List<dynamic> jsonData = json['data'] ?? [];
hasNext = json['hasNext'] ?? false;
int currentPage = json['page'] ?? 1;
List<CommunityModel> communityList = jsonData.map((jsonItem) {
return CommunityModel.fromJson(jsonItem);
}).toList();
page = currentPage + 1;
paginationModel = PaginationModel(
pageNum: page, hasNext: hasNext, size: 25, communities: communityList);
return paginationModel;
} catch (_) {
hasNext = false;
return [];
}
},
);
} catch (_) {}
return paginationModel;
}
Future<CommunityModel?> getCommunityById(String communityId) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.getCommunityById
.replaceAll('{communityId}', communityId),
path: ApiEndpoints.getCommunityById.replaceAll('{communityId}', communityId),
expectedResponseModel: (json) {
return CommunityModel.fromJson(json['data']);
},
@ -65,8 +95,7 @@ class CommunitySpaceManagementApi {
}
}
Future<CommunityModel?> createCommunity(
String name, String description, String projectId) async {
Future<CommunityModel?> createCommunity(String name, String description, String projectId) async {
try {
final response = await HTTPService().post(
path: ApiEndpoints.createCommunity.replaceAll('{projectId}', projectId),
@ -85,8 +114,7 @@ class CommunitySpaceManagementApi {
}
}
Future<bool> updateCommunity(
String communityId, String name, String projectId) async {
Future<bool> updateCommunity(String communityId, String name, String projectId) async {
try {
final response = await HTTPService().put(
path: ApiEndpoints.updateCommunity
@ -123,8 +151,7 @@ class CommunitySpaceManagementApi {
}
}
Future<SpacesResponse> fetchSpaces(
String communityId, String projectId) async {
Future<SpacesResponse> fetchSpaces(String communityId, String projectId) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.listSpaces
@ -150,8 +177,7 @@ class CommunitySpaceManagementApi {
}
}
Future<SpaceModel?> getSpace(
String communityId, String spaceId, String projectId) async {
Future<SpaceModel?> getSpace(String communityId, String spaceId, String projectId) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.getSpace
@ -255,6 +281,7 @@ class CommunitySpaceManagementApi {
return json['success'] ?? false;
},
);
return response;
} catch (e) {
debugPrint('Error updating space: $e');
@ -262,8 +289,7 @@ class CommunitySpaceManagementApi {
}
}
Future<bool> deleteSpace(
String communityId, String spaceId, String projectId) async {
Future<bool> deleteSpace(String communityId, String spaceId, String projectId) async {
try {
final response = await HTTPService().delete(
path: ApiEndpoints.deleteSpace
@ -281,17 +307,15 @@ class CommunitySpaceManagementApi {
}
}
Future<List<SpaceModel>> getSpaceHierarchy(
String communityId, String projectId) async {
Future<List<SpaceModel>> getSpaceHierarchy(String communityId, String projectId) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.getSpaceHierarchy
.replaceAll('{communityId}', communityId)
.replaceAll('{projectId}', projectId),
expectedResponseModel: (json) {
final spaceModels = (json['data'] as List)
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
.toList();
final spaceModels =
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
return spaceModels;
},
@ -302,4 +326,23 @@ class CommunitySpaceManagementApi {
return [];
}
}
Future<List<SpaceModel>> getSpaceOnlyWithDevices({String? communityId, String? projectId}) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.spaceOnlyWithDevices
.replaceAll('{communityId}', communityId!)
.replaceAll('{projectId}', projectId!),
expectedResponseModel: (json) {
final spaceModels =
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
return spaceModels;
},
);
return response;
} catch (e) {
debugPrint('Error fetching space hierarchy: $e');
return [];
}
}
}