Compare commits

..

29 Commits

Author SHA1 Message Date
391d6349cc Fixed the assets order 2024-11-05 15:54:48 +04:00
9c873fb0c5 fixed icon assets issue 2024-11-05 15:53:31 +04:00
e9615cd351 Fixed show in device bug 2024-11-05 12:52:11 +04:00
52f6be3db0 updated add to room, and verify to space 2024-11-05 12:27:27 +04:00
34536e6584 updated invitation code api 2024-11-05 11:01:49 +04:00
7d51ca1f12 Fixed bugs in automation 2024-11-05 00:52:38 +04:00
ff31c54e7b reformat 2024-11-04 17:59:55 +04:00
944de7b373 reformat 2024-11-04 17:45:29 +04:00
ab4f8c593d revert 2024-11-04 10:32:13 +04:00
977fd12721 podfile revert 2024-11-04 10:30:50 +04:00
e4a3d4c50b delete scene 2024-11-04 10:28:16 +04:00
29f82945a6 revert back 2024-11-04 10:19:36 +04:00
87a4a88417 fixed automation 2024-11-02 23:10:24 +04:00
dcccc4db3a create subspace 2024-10-31 11:30:35 +04:00
36e5df38ee Merge branch 'dev' of https://github.com/SyncrowIOT/syncrow-app into feat/refactor 2024-10-31 10:45:08 +04:00
7571e35a70 revert 2024-10-31 10:40:29 +04:00
995fd8d0a6 revert 2024-10-31 10:40:05 +04:00
b7b48006af revert config 2024-10-31 10:38:15 +04:00
ea202812cd revert back 2024-10-31 10:35:26 +04:00
79f224f481 revert back env 2024-10-31 10:34:39 +04:00
80d2651370 scene and automation has been updated 2024-10-31 10:19:38 +04:00
7071f50d49 auth_change 2024-10-31 08:35:32 +03:00
13a4bf25b3 added unit devices 2024-10-30 19:16:10 +04:00
f6a12f2e92 Merge pull request #62 from SyncrowIOT/password_changes
password_changes&_routine_changes
2024-10-30 14:23:11 +03:00
5c65bac076 removed logs 2024-10-30 12:27:31 +04:00
489789da0a fixed issues in space 2024-10-30 11:04:36 +04:00
d025da6daf fixed space 2024-10-30 09:56:57 +04:00
5a7ed3ec7c subspace model added 2024-10-29 18:44:41 +04:00
1558996891 models 2024-10-29 18:41:59 +04:00
48 changed files with 999 additions and 429 deletions

View File

@ -1,7 +1,7 @@
import UIKit
import Flutter
@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,

View File

@ -11,7 +11,7 @@ import 'package:syncrow_app/features/app_layout/view/widgets/app_bar_home_dropdo
import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/features/dashboard/view/dashboard_view.dart';
import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
import 'package:syncrow_app/features/devices/model/room_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/devices_view_body.dart';
import 'package:syncrow_app/features/menu/view/menu_view.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
@ -55,7 +55,8 @@ class HomeCubit extends Cubit<HomeState> {
Future fetchUserInfo() async {
try {
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await ProfileApi().fetchUserInfo(uuid);
emit(HomeUserInfoLoaded(user!)); // Emit state after fetching user info
} catch (e) {
@ -76,9 +77,12 @@ class HomeCubit extends Cubit<HomeState> {
selectedSpace = null;
selectedRoom = null;
pageIndex = 0;
OneSignal.User.pushSubscription.removeObserver((stateChanges) => oneSignalSubscriptionObserver);
OneSignal.Notifications.removePermissionObserver((permission) => oneSignalPermissionObserver);
OneSignal.Notifications.removeClickListener((event) => oneSignalClickListenerObserver);
OneSignal.User.pushSubscription
.removeObserver((stateChanges) => oneSignalSubscriptionObserver);
OneSignal.Notifications.removePermissionObserver(
(permission) => oneSignalPermissionObserver);
OneSignal.Notifications.removeClickListener(
(event) => oneSignalClickListenerObserver);
return super.close();
}
@ -88,7 +92,7 @@ class HomeCubit extends Cubit<HomeState> {
SpaceModel? selectedSpace;
RoomModel? selectedRoom;
SubSpaceModel? selectedRoom;
PageController devicesPageController = PageController();
@ -120,7 +124,9 @@ class HomeCubit extends Cubit<HomeState> {
return;
}
var userUuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? '';
var userUuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ??
'';
if (userUuid.isNotEmpty) {
await OneSignal.login(userUuid);
}
@ -128,21 +134,24 @@ class HomeCubit extends Cubit<HomeState> {
await OneSignal.User.pushSubscription.optIn();
//this function will be called once a user is subscribed
oneSignalSubscriptionObserver = OneSignal.User.pushSubscription.addObserver((state) async {
oneSignalSubscriptionObserver =
OneSignal.User.pushSubscription.addObserver((state) async {
if (state.current.optedIn) {
await _sendSubscriptionId();
}
});
// Send the player id when a user allows notifications
oneSignalPermissionObserver = OneSignal.Notifications.addPermissionObserver((state) async {
oneSignalPermissionObserver =
OneSignal.Notifications.addPermissionObserver((state) async {
await _sendSubscriptionId();
});
//check if the player id is sent, if not send it again
await _sendSubscriptionId();
oneSignalClickListenerObserver = OneSignal.Notifications.addClickListener((event) async {
oneSignalClickListenerObserver =
OneSignal.Notifications.addClickListener((event) async {
//Once the user clicks on the notification
});
} catch (err) {
@ -173,7 +182,7 @@ class HomeCubit extends Cubit<HomeState> {
if (index == 0) {
unselectRoom();
} else {
selectedRoom = selectedSpace!.rooms![index - 1];
selectedRoom = selectedSpace!.subspaces[index - 1];
emitSafe(RoomSelected(selectedRoom!));
}
}
@ -188,7 +197,7 @@ class HomeCubit extends Cubit<HomeState> {
if (index <= 0) {
unselectRoom();
} else {
selectedRoom = selectedSpace!.rooms![index - 1];
selectedRoom = selectedSpace!.subspaces[index - 1];
emitSafe(RoomSelected(selectedRoom!));
}
}
@ -211,9 +220,10 @@ class HomeCubit extends Cubit<HomeState> {
}
//////////////////////////////////////// API ////////////////////////////////////////
generateInvitation(String unitId) async {
generateInvitation(SpaceModel unit) async {
try {
final invitationCode = await SpacesAPI.generateInvitationCode(unitId);
final invitationCode =
await SpacesAPI.generateInvitationCode(unit.id, unit.community.uuid);
if (invitationCode.isNotEmpty) {
Share.share('The invitation code is $invitationCode');
CustomSnackBar.displaySnackBar(
@ -229,10 +239,12 @@ class HomeCubit extends Cubit<HomeState> {
Future<bool> joinAUnit(String code) async {
try {
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? '';
Map<String, String> body = {'userUuid': uuid, 'inviteCode': code};
var userUuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ??
'';
Map<String, String> body = {'inviteCode': code};
final success = await SpacesAPI.joinUnit(body);
final success = await SpacesAPI.joinUnit(userUuid, body);
if (success) {
await fetchUnitsByUserId();
CustomSnackBar.displaySnackBar('Done successfully');
@ -247,7 +259,7 @@ class HomeCubit extends Cubit<HomeState> {
fetchUnitsByUserId() async {
emitSafe(GetSpacesLoading());
try {
spaces = await SpacesAPI.getUnitsByUserId();
spaces = await SpacesAPI.getSpacesByUserId();
} catch (failure) {
emitSafe(GetSpacesError("No units found"));
return;
@ -265,13 +277,14 @@ class HomeCubit extends Cubit<HomeState> {
fetchRoomsByUnitId(SpaceModel space) async {
emitSafe(GetSpaceRoomsLoading());
try {
space.rooms = await SpacesAPI.getRoomsBySpaceId(space.id!);
space.subspaces =
await SpacesAPI.getSubSpaceBySpaceId(space.community.uuid, space.id);
} catch (failure) {
emitSafe(GetSpaceRoomsError(failure.toString()));
return;
}
if (space.rooms != null && space.rooms!.isNotEmpty) {
emitSafe(GetSpaceRoomsSuccess(space.rooms!));
if (space.subspaces != null && space.subspaces!.isNotEmpty) {
emitSafe(GetSpaceRoomsSuccess(space.subspaces!));
} else {
emitSafe(GetSpaceRoomsError("No rooms found"));
}
@ -347,7 +360,8 @@ class HomeCubit extends Cubit<HomeState> {
size: 32,
),
style: ButtonStyle(
foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor),
foregroundColor:
WidgetStateProperty.all(ColorsManager.textPrimaryColor),
),
onPressed: () {
Navigator.pushNamed(
@ -368,7 +382,8 @@ class HomeCubit extends Cubit<HomeState> {
NavigationService.navigatorKey.currentContext!
.read<SmartSceneSelectBloc>()
.add(const SmartSceneClearEvent());
BlocProvider.of<EffectPeriodBloc>(NavigationService.navigatorKey.currentState!.context)
BlocProvider.of<EffectPeriodBloc>(
NavigationService.navigatorKey.currentState!.context)
.add(ResetEffectivePeriod());
NavigationService.navigatorKey.currentContext!
.read<CreateSceneBloc>()
@ -381,7 +396,8 @@ class HomeCubit extends Cubit<HomeState> {
size: 28,
),
style: ButtonStyle(
foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor),
foregroundColor:
WidgetStateProperty.all(ColorsManager.textPrimaryColor),
),
onPressed: () {},
),
@ -414,7 +430,8 @@ class HomeCubit extends Cubit<HomeState> {
};
static var bottomNavItems = [
defaultBottomNavBarItem(icon: Assets.assetsIconsDashboard, label: 'Dashboard'),
defaultBottomNavBarItem(
icon: Assets.assetsIconsDashboard, label: 'Dashboard'),
// defaultBottomNavBarItem(icon: Assets.assetsIconslayout, label: 'Layout'),
defaultBottomNavBarItem(icon: Assets.assetsIconsDevices, label: 'Devices'),
defaultBottomNavBarItem(icon: Assets.assetsIconsRoutines, label: 'Routine'),
@ -440,7 +457,8 @@ class HomeCubit extends Cubit<HomeState> {
void updateDevice(String deviceId) async {
try {
final response = await DevicesAPI.firmwareDevice(deviceId: deviceId, firmwareVersion: '0');
final response = await DevicesAPI.firmwareDevice(
deviceId: deviceId, firmwareVersion: '0');
if (response['success'] ?? false) {
CustomSnackBar.displaySnackBar('No updates available');
}
@ -448,7 +466,8 @@ class HomeCubit extends Cubit<HomeState> {
}
}
BottomNavigationBarItem defaultBottomNavBarItem({required String icon, required String label}) {
BottomNavigationBarItem defaultBottomNavBarItem(
{required String icon, required String label}) {
return BottomNavigationBarItem(
icon: SvgPicture.asset(icon),
activeIcon: SvgPicture.asset(

View File

@ -33,7 +33,7 @@ class GetSpacesError extends HomeError {
class GetSpaceRoomsLoading extends HomeLoading {}
class GetSpaceRoomsSuccess extends HomeSuccess {
final List<RoomModel> rooms;
final List<SubSpaceModel> rooms;
GetSpaceRoomsSuccess(this.rooms);
}
@ -50,7 +50,7 @@ class SpaceSelected extends HomeState {
}
class RoomSelected extends HomeState {
final RoomModel room;
final SubSpaceModel room;
RoomSelected(this.room);
}

View File

@ -0,0 +1,27 @@
class Community {
final String uuid;
final String name;
final String description;
Community({
required this.uuid,
required this.name,
this.description = '',
});
factory Community.fromJson(Map<String, dynamic> json) {
return Community(
uuid: json['uuid'] ?? '',
name: json['name'] ?? 'Unnamed Community',
description: json['description'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'uuid': uuid,
'name': name,
'description': description,
};
}
}

View File

@ -1,37 +1,53 @@
import 'package:syncrow_app/features/devices/model/room_model.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
import 'package:syncrow_app/features/app_layout/model/community_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
class SpaceModel {
final String? id;
final String? name;
final SpaceType type;
late List<RoomModel>? rooms;
final String id;
final String name;
final Community community;
late List<SubSpaceModel> subspaces;
SpaceModel({
required this.type,
required this.id,
required this.name,
required this.rooms,
required this.community,
this.subspaces = const [], // Default to an empty list
});
/// Converts the instance into JSON format.
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'rooms': rooms,
'spaceName': name,
'community': community.toJson(),
'subspaces': subspaces.map((room) => room.toJson()).toList(),
};
}
/// Factory constructor to create an instance from JSON.
factory SpaceModel.fromJson(Map<String, dynamic> json) {
// Extract and log each part of space data
final id = json['uuid'] ?? '';
final name = json['spaceName'] ?? 'Unnamed Space';
final communityJson = json['community'] ?? {};
return SpaceModel(
id: json['uuid'],
name: json['name'],
type: spaceTypesMap[json['type']]!,
rooms: [],
id: id,
name: name,
community: Community.fromJson(
communityJson), // Ensure Community is created correctly
subspaces: (json['subspaces'] as List<dynamic>?)
?.map((item) => SubSpaceModel.fromJson(item))
.toList() ??
[],
);
}
/// Helper method to parse a list of SpaceModel from JSON.
static List<SpaceModel> fromJsonList(List<dynamic> jsonList) {
return jsonList.map((item) => SpaceModel.fromJson(item)).toList();
return jsonList.map((item) {
final spaceData = item['space']; // Extract the `space` object
return SpaceModel.fromJson(spaceData); // Pass to SpaceModel.fromJson
}).toList();
}
}

View File

@ -284,11 +284,13 @@ class AuthCubit extends Cubit<AuthState> {
await login();
}
emit(AuthOtpSuccess());
} else {
emit(AuthLoginError(message: 'Something went wrong'));
}
// else {
// emit(AuthLoginError(message: 'Something went wrong'));
// }
} catch (failure) {
emit(AuthLoginError(message: 'Something went wrong'));
emit(AuthErrorStatusWithoutMsg());
//emit(AuthLoginError(message: 'Something went wrong'));
return;
}
}

View File

@ -25,12 +25,14 @@ class DeviceManagerBloc extends Bloc<DeviceManagerEvent, DeviceManagerState> {
static List<DevicesCategoryModel>? allCategories;
Future<void> _onFetchAllDevices(FetchAllDevices event, Emitter<DeviceManagerState> emit) async {
Future<void> _onFetchAllDevices(
FetchAllDevices event, Emitter<DeviceManagerState> emit) async {
emit(state.copyWith(loading: true));
try {
final allDevices = await HomeManagementAPI.fetchDevicesByUnitId();
emit(state.copyWith(devices: allDevices, loading: false));
} catch (e) {
print(e);
emit(state.copyWith(error: e.toString(), loading: false));
}
}
@ -39,21 +41,27 @@ class DeviceManagerBloc extends Bloc<DeviceManagerEvent, DeviceManagerState> {
FetchDevicesByRoomId event, Emitter<DeviceManagerState> emit) async {
emit(state.copyWith(loading: true));
try {
final devices = await DevicesAPI.getDevicesByRoomId(event.roomId);
final devices = await DevicesAPI.getDevicesByRoomId(
communityUuid: event.unit.community.uuid,
spaceUuid: event.unit.id,
roomId: event.roomId,
);
emit(state.copyWith(devices: devices, loading: false));
} catch (e) {
emit(state.copyWith(error: e.toString(), loading: false));
}
}
void _onSelectCategory(SelectCategory event, Emitter<DeviceManagerState> emit) {
void _onSelectCategory(
SelectCategory event, Emitter<DeviceManagerState> emit) {
for (var i = 0; i < allCategories!.length; i++) {
allCategories![i].isSelected = i == event.index;
}
emit(state.copyWith(categoryChanged: true));
}
void _onUnselectAllCategories(UnselectAllCategories event, Emitter<DeviceManagerState> emit) {
void _onUnselectAllCategories(
UnselectAllCategories event, Emitter<DeviceManagerState> emit) {
for (var category in allCategories!) {
category.isSelected = false;
}
@ -97,7 +105,8 @@ class DeviceManagerBloc extends Bloc<DeviceManagerEvent, DeviceManagerState> {
_updateDevicesStatus(category, emit);
}
void _onTurnOnOffDevice(TurnOnOffDevice event, Emitter<DeviceManagerState> emit) {
void _onTurnOnOffDevice(
TurnOnOffDevice event, Emitter<DeviceManagerState> emit) {
var device = event.device;
device.isOnline = !device.isOnline!;
DevicesCategoryModel category = allCategories!.firstWhere((category) {
@ -120,7 +129,8 @@ class DeviceManagerBloc extends Bloc<DeviceManagerEvent, DeviceManagerState> {
emit(state.copyWith(categoryChanged: true)); // Set category changed state
}
void _updateDevicesStatus(DevicesCategoryModel category, Emitter<DeviceManagerState> emit) {
void _updateDevicesStatus(
DevicesCategoryModel category, Emitter<DeviceManagerState> emit) {
if (category.devices != null && category.devices!.isNotEmpty) {
bool? tempStatus = category.devices![0].isOnline;
for (var device in category.devices!) {
@ -140,7 +150,8 @@ class DeviceManagerBloc extends Bloc<DeviceManagerEvent, DeviceManagerState> {
try {
final deviceFunctions = await DevicesAPI.deviceFunctions(event.deviceId);
emit(state.copyWith(functionsLoading: false, deviceFunctions: deviceFunctions));
emit(state.copyWith(
functionsLoading: false, deviceFunctions: deviceFunctions));
} catch (e) {
emit(state.copyWith(functionsLoading: false, error: e.toString()));
}

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/devices/model/device_category_model.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
@ -15,8 +16,9 @@ class FetchAllDevices extends DeviceManagerEvent {}
class FetchDevicesByRoomId extends DeviceManagerEvent {
final String roomId;
final SpaceModel unit;
const FetchDevicesByRoomId(this.roomId);
const FetchDevicesByRoomId(this.roomId, this.unit);
@override
List<Object> get props => [roomId];

View File

@ -23,14 +23,22 @@ import 'package:syncrow_app/utils/resource_manager/constants.dart';
part 'devices_state.dart';
class DevicesCubit extends Cubit<DevicesState> {
Future<void> _initializeDevices(SpaceModel selectedSpace) async {
// Fetch devices for each room in the selected space
for (var room in selectedSpace.subspaces ?? []) {
await fetchDevicesByRoomId(selectedSpace, room.id!);
}
// Fetch groups based on the selected space ID
await fetchGroups(selectedSpace.id ?? '');
}
DevicesCubit._() : super(DevicesInitial()) {
if (HomeCubit.getInstance().selectedSpace != null &&
HomeCubit.getInstance().spaces!.isNotEmpty) {
// fetchGroups(HomeCubit.getInstance().selectedSpace!.id!);
for (var room in HomeCubit.getInstance().selectedSpace!.rooms!) {
fetchDevicesByRoomId(room.id!);
}
fetchGroups(HomeCubit.getInstance().selectedSpace?.id ?? '');
final selectedSpace = HomeCubit.getInstance().selectedSpace;
final spaces = HomeCubit.getInstance().spaces;
if (selectedSpace != null && spaces != null && spaces.isNotEmpty) {
_initializeDevices(selectedSpace);
}
}
@ -87,10 +95,10 @@ class DevicesCubit extends Cubit<DevicesState> {
return const DoorsListView();
case DeviceType.Curtain:
return const CurtainListView();
// case DeviceType.ThreeGang:
// return const ThreeGangSwitchesView();
// case DeviceType.Gateway:
// return const GateWayView();
// case DeviceType.ThreeGang:
// return const ThreeGangSwitchesView();
// case DeviceType.Gateway:
// return const GateWayView();
default:
return null;
}
@ -104,8 +112,8 @@ class DevicesCubit extends Cubit<DevicesState> {
List<DeviceModel> get allDevices {
List<DeviceModel> devices = [];
if (HomeCubit.getInstance().selectedSpace != null &&
HomeCubit.getInstance().selectedSpace!.rooms != null) {
for (var room in HomeCubit.getInstance().selectedSpace!.rooms!) {
HomeCubit.getInstance().selectedSpace!.subspaces != null) {
for (var room in HomeCubit.getInstance().selectedSpace!.subspaces!) {
if (room.devices != null) {
devices.addAll(room.devices!);
}
@ -275,7 +283,7 @@ class DevicesCubit extends Cubit<DevicesState> {
deviceId,
HomeCubit.getInstance()
.selectedSpace!
.rooms!
.subspaces!
.indexOf(HomeCubit.getInstance().selectedRoom!),
code: control.code);
});
@ -303,20 +311,26 @@ class DevicesCubit extends Cubit<DevicesState> {
}
}
fetchDevicesByRoomId(String? roomId) async {
fetchDevicesByRoomId(SpaceModel? unit, String? roomId) async {
if (roomId == null) return;
emitSafe(GetDevicesLoading());
int roomIndex =
HomeCubit.getInstance().selectedSpace!.rooms!.indexWhere((element) => element.id == roomId);
int roomIndex = HomeCubit.getInstance()
.selectedSpace!
.subspaces!
.indexWhere((element) => element.id == roomId);
try {
HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices =
await DevicesAPI.getDevicesByRoomId(roomId);
HomeCubit.getInstance().selectedSpace!.subspaces![roomIndex].devices =
await DevicesAPI.getDevicesByRoomId(
communityUuid: unit!.community.uuid,
spaceUuid: unit.id,
roomId: roomId);
} catch (e) {
emitSafe(GetDevicesError(e.toString()));
return;
}
final devices = HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices;
final devices =
HomeCubit.getInstance().selectedSpace!.subspaces![roomIndex].devices;
emitSafe(GetDevicesSuccess(devices));
//get status for each device
@ -333,7 +347,7 @@ class DevicesCubit extends Cubit<DevicesState> {
emitSafe(GetDeviceStatusLoading(code: code));
int deviceIndex = HomeCubit.getInstance()
.selectedSpace!
.rooms![roomIndex]
.subspaces![roomIndex]
.devices!
.indexWhere((element) => element.uuid == deviceUuid);
List<StatusModel> statuses = [];
@ -346,8 +360,11 @@ class DevicesCubit extends Cubit<DevicesState> {
emitSafe(GetDeviceStatusError(e.toString()));
return;
}
HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices![deviceIndex].status =
statuses;
HomeCubit.getInstance()
.selectedSpace!
.subspaces![roomIndex]
.devices![deviceIndex]
.status = statuses;
emitSafe(GetDeviceStatusSuccess(code: code));
}
@ -396,8 +413,6 @@ class DevicesCubit extends Cubit<DevicesState> {
// emitSafe(LightBrightnessChanged(value));
// }
// }
}
enum LightMode {

View File

@ -0,0 +1,35 @@
import 'package:syncrow_app/features/devices/model/device_model.dart';
class SubSpaceModel {
final String? id;
final String? name;
List<DeviceModel>? devices;
SubSpaceModel({
required this.id,
required this.name,
required this.devices,
});
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'devices': devices?.map((device) => device.toJson()).toList(),
};
}
factory SubSpaceModel.fromJson(Map<String, dynamic> json) {
List<DeviceModel> devices = [];
if (json['devices'] != null) {
for (var device in json['devices']) {
devices.add(DeviceModel.fromJson(device));
}
}
return SubSpaceModel(
id: json['uuid'],
name: json['subspaceName'],
devices: devices,
);
}
}

View File

@ -22,6 +22,8 @@ class ACsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("ACsView deviceModel UUID: ${deviceModel?.uuid}");
return BlocProvider(
create: (context) => ACsBloc(acId: deviceModel?.uuid ?? '')
..add(AcsInitial(allAcs: deviceModel != null ? false : true)),
@ -66,12 +68,14 @@ class ACsView extends StatelessWidget {
child: state is AcsLoadingState
? const Center(
child: DefaultContainer(
width: 50, height: 50, child: CircularProgressIndicator()),
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {
BlocProvider.of<ACsBloc>(context)
.add(AcsInitial(allAcs: deviceModel != null ? false : true));
BlocProvider.of<ACsBloc>(context).add(AcsInitial(
allAcs: deviceModel != null ? false : true));
},
child: Container(
padding: const EdgeInsets.only(top: 40),

View File

@ -69,6 +69,7 @@ class DevicesViewBody extends StatelessWidget {
Expanded(
child: PageView(
controller: HomeCubit.getInstance().devicesPageController,
onPageChanged: (index) {
HomeCubit.getInstance().devicesPageChanged(index);
@ -78,8 +79,8 @@ class DevicesViewBody extends StatelessWidget {
groupsList: DevicesCubit.getInstance().allCategories ?? [],
),
if (HomeCubit.getInstance().selectedSpace != null)
if (HomeCubit.getInstance().selectedSpace!.rooms != null)
...HomeCubit.getInstance().selectedSpace!.rooms!.map((room) {
if (HomeCubit.getInstance().selectedSpace!.subspaces != null)
...HomeCubit.getInstance().selectedSpace!.subspaces!.map((room) {
return RoomPage(
room: room,
);
@ -95,7 +96,7 @@ class DevicesViewBody extends StatelessWidget {
),
child: SmoothPageIndicator(
controller: HomeCubit.getInstance().devicesPageController,
count: HomeCubit.getInstance().selectedSpace!.rooms!.length + 1,
count: HomeCubit.getInstance().selectedSpace!.subspaces!.length + 1,
effect: const WormEffect(
paintStyle: PaintingStyle.stroke,
dotHeight: 8,

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/model/room_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/room_page_switch.dart';
@ -8,7 +9,7 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class RoomPage extends StatefulWidget {
const RoomPage({super.key, required this.room});
final RoomModel room;
final SubSpaceModel room;
@override
_RoomPageState createState() => _RoomPageState();

View File

@ -87,6 +87,7 @@ class RoomPageSwitch extends StatelessWidget {
///
/// The [device] parameter represents the device model.
void showDeviceInterface(DeviceModel device, BuildContext context) {
switch (device.productType) {
case DeviceType.AC:
Navigator.push(

View File

@ -39,14 +39,14 @@ class RoomsSlider extends StatelessWidget {
),
),
if (HomeCubit.getInstance().selectedSpace != null)
if (HomeCubit.getInstance().selectedSpace!.rooms != null)
...HomeCubit.getInstance().selectedSpace!.rooms!.map(
if (HomeCubit.getInstance().selectedSpace!.subspaces != null)
...HomeCubit.getInstance().selectedSpace!.subspaces!.map(
(room) => InkWell(
onTap: () {
HomeCubit.getInstance().roomSliderPageChanged(
HomeCubit.getInstance()
.selectedSpace!
.rooms!
.subspaces!
.indexOf(room));
},
child: TitleMedium(

View File

@ -40,10 +40,13 @@ class SceneListview extends StatelessWidget {
sceneName: scene.name,
),
);
context.read<SmartSceneSelectBloc>().add(const SmartSceneClearEvent());
context
.read<SmartSceneSelectBloc>()
.add(const SmartSceneClearEvent());
BlocProvider.of<CreateSceneBloc>(context)
.add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false));
BlocProvider.of<CreateSceneBloc>(context).add(
FetchSceneTasksEvent(
sceneId: scene.id, isAutomation: false));
/// the state to set the scene type must be after the fetch
BlocProvider.of<CreateSceneBloc>(context)
@ -56,14 +59,27 @@ class SceneListview extends StatelessWidget {
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.memory(
height: 32,
width: 32,
scene.iconInBytes,
fit: BoxFit.fill,
errorBuilder: (context, error, stackTrace) => Image.asset(
height: 32, width: 32, fit: BoxFit.fill, Assets.assetsIconsLogo),
),
child: scene.iconInBytes != null &&
scene.iconInBytes.isNotEmpty
? Image.memory(
scene.iconInBytes,
height: 32,
width: 32,
fit: BoxFit.fill,
errorBuilder: (context, error, stackTrace) =>
Image.asset(
Assets.assetsIconsLogo,
height: 32,
width: 32,
fit: BoxFit.fill,
),
)
: Image.asset(
Assets.assetsIconsLogo,
height: 32,
width: 32,
fit: BoxFit.fill,
),
),
Expanded(
child: BodyMedium(

View File

@ -61,7 +61,9 @@ class CreateUnitBloc extends Bloc<CreateUnitEvent, CreateUnitState> {
var storage = const FlutterSecureStorage();
var userId = await storage.read(key: UserModel.userUuidKey) ?? '';
Map<String, String> communityBody = {'communityName': event.communityName};
Map<String, String> communityBody = {
'communityName': event.communityName
};
final response = await HomeCreation.createCommunity(communityBody);
if (response['data']['uuid'] != '') {
// final result =
@ -75,15 +77,22 @@ class CreateUnitBloc extends Bloc<CreateUnitEvent, CreateUnitState> {
if (buildingId.isNotEmpty) {
final floorId = await _createFloor(
floorName: event.floorName, buildingId: buildingId, userId: userId);
floorName: event.floorName,
buildingId: buildingId,
userId: userId);
if (floorId.isNotEmpty) {
final unitId =
await _createUnit(unitName: event.unitName, floorId: floorId, userId: userId);
final unitId = await _createUnit(
unitName: event.unitName, floorId: floorId, userId: userId);
if (unitId.isNotEmpty && rooms.isNotEmpty) {
rooms.forEach((room) async {
await _createNewRoom(roomName: room, unitId: unitId, userId: userId);
await _createNewRoom(
roomName: room,
unitId: unitId,
userId: userId,
communityId: response['data']['uuid'],
);
});
}
}
@ -99,7 +108,8 @@ class CreateUnitBloc extends Bloc<CreateUnitEvent, CreateUnitState> {
}
}
Future<bool> _assignToCommunity({required String communityId, required String userId}) async {
Future<bool> _assignToCommunity(
{required String communityId, required String userId}) async {
try {
Map<String, String> body = {
'communityUuid': communityId,
@ -114,9 +124,14 @@ Future<bool> _assignToCommunity({required String communityId, required String us
}
Future<String> _createBuilding(
{required String buildingName, required String communityId, required String userId}) async {
{required String buildingName,
required String communityId,
required String userId}) async {
try {
Map<String, String> body = {'buildingName': buildingName, 'communityUuid': communityId};
Map<String, String> body = {
'buildingName': buildingName,
'communityUuid': communityId
};
final response = await HomeCreation.createBuilding(body);
// if (response['data']['uuid'] != '') {
// final result = await _assignToBuilding(buildingId: response['data']['uuid'], userId: userId);
@ -130,7 +145,8 @@ Future<String> _createBuilding(
}
}
Future<bool> _assignToBuilding({required String buildingId, required String userId}) async {
Future<bool> _assignToBuilding(
{required String buildingId, required String userId}) async {
try {
Map<String, String> body = {
'buildingUuid': buildingId,
@ -145,9 +161,14 @@ Future<bool> _assignToBuilding({required String buildingId, required String user
}
Future<String> _createFloor(
{required String floorName, required String buildingId, required String userId}) async {
{required String floorName,
required String buildingId,
required String userId}) async {
try {
Map<String, String> body = {'floorName': floorName, 'buildingUuid': buildingId};
Map<String, String> body = {
'floorName': floorName,
'buildingUuid': buildingId
};
final response = await HomeCreation.createFloor(body);
// if (response['data']['uuid'] != '') {
// final result = await _assignToFloor(buildingId: response['data']['uuid'], userId: userId);
@ -161,7 +182,8 @@ Future<String> _createFloor(
}
}
Future<bool> _assignToFloor({required String buildingId, required String userId}) async {
Future<bool> _assignToFloor(
{required String buildingId, required String userId}) async {
try {
Map<String, String> body = {
'floorUuid': buildingId,
@ -176,12 +198,15 @@ Future<bool> _assignToFloor({required String buildingId, required String userId}
}
Future<String> _createUnit(
{required String unitName, required String floorId, required String userId}) async {
{required String unitName,
required String floorId,
required String userId}) async {
try {
Map<String, String> body = {'unitName': unitName, 'floorUuid': floorId};
final response = await HomeCreation.createUnit(body);
if (response['data']['uuid'] != '') {
final result = await _assignToUnit(unitId: response['data']['uuid'], userId: userId);
final result =
await _assignToUnit(unitId: response['data']['uuid'], userId: userId);
return result ? response['data']['uuid'] : '';
} else {
@ -192,7 +217,8 @@ Future<String> _createUnit(
}
}
Future<bool> _assignToUnit({required String unitId, required String userId}) async {
Future<bool> _assignToUnit(
{required String unitId, required String userId}) async {
try {
Map<String, String> body = {
'unitUuid': unitId,
@ -207,10 +233,14 @@ Future<bool> _assignToUnit({required String unitId, required String userId}) asy
}
Future<String> _createNewRoom(
{required String roomName, required String unitId, required String userId}) async {
{required String roomName,
required String unitId,
required String userId,
required String communityId}) async {
try {
Map<String, String> body = {'roomName': roomName, 'unitUuid': unitId};
final response = await HomeCreation.createRoom(body);
Map<String, String> body = {'subspaceName': roomName};
final response = await HomeCreation.createRoom(
communityId: communityId, spaceId: unitId, body: body);
// if (response['data']['uuid'] != '') {
// final result = await _assignToRoom(roomId: response['data']['uuid'], userId: userId);
@ -223,7 +253,8 @@ Future<String> _createNewRoom(
}
}
Future<bool> _assignToRoom({required String roomId, required String userId}) async {
Future<bool> _assignToRoom(
{required String roomId, required String userId}) async {
try {
Map<String, String> body = {
'roomUuid': roomId,

View File

@ -17,12 +17,15 @@ class ManageUnitBloc extends Bloc<ManageUnitEvent, ManageUnitState> {
on<FetchDevicesByRoomIdEvent>(_fetchDevicesByRoomId);
on<AssignRoomEvent>(_assignDevice);
on<AddNewRoom>(_addNewRoom);
on<UnassignRoomEvent>(_unassignDevice);
}
void _fetchRoomsAndDevices(FetchRoomsEvent event, Emitter<ManageUnitState> emit) async {
void _fetchRoomsAndDevices(
FetchRoomsEvent event, Emitter<ManageUnitState> emit) async {
try {
emit(LoadingState());
final roomsList = await SpacesAPI.getRoomsBySpaceId(event.unitId);
final roomsList = await SpacesAPI.getSubSpaceBySpaceId(
event.unit.community.uuid, event.unit.id);
emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList));
} catch (e) {
emit(const ErrorState(message: 'Something went wrong'));
@ -30,11 +33,15 @@ class ManageUnitBloc extends Bloc<ManageUnitEvent, ManageUnitState> {
}
}
void _fetchDevicesByRoomId(FetchDevicesByRoomIdEvent event, Emitter<ManageUnitState> emit) async {
void _fetchDevicesByRoomId(
FetchDevicesByRoomIdEvent event, Emitter<ManageUnitState> emit) async {
try {
Map<String, bool> roomDevicesId = {};
emit(LoadingState());
final devicesList = await DevicesAPI.getDevicesByRoomId(event.roomId);
final devicesList = await DevicesAPI.getDevicesByRoomId(
communityUuid: event.unit.community.uuid,
spaceUuid: event.unit.id,
roomId: event.roomId);
allDevices = await HomeManagementAPI.fetchDevicesByUserId();
List<String> allDevicesIds = [];
@ -61,13 +68,18 @@ class ManageUnitBloc extends Bloc<ManageUnitEvent, ManageUnitState> {
}
}
void _assignDevice(AssignRoomEvent event, Emitter<ManageUnitState> emit) async {
void _assignDevice(
AssignRoomEvent event, Emitter<ManageUnitState> emit) async {
try {
Map<String, bool> roomDevicesId = {};
emit(LoadingState());
Map<String, String> body = {"deviceUuid": event.deviceId, "roomUuid": event.roomId};
await HomeManagementAPI.assignDeviceToRoom(body);
final devicesList = await DevicesAPI.getDevicesByRoomId(event.roomId);
await HomeManagementAPI.assignDeviceToRoom(
event.unit.community.uuid, event.unit.id, event.roomId, event.deviceId);
final devicesList = await DevicesAPI.getDevicesByRoomId(
communityUuid: event.unit.community.uuid,
spaceUuid: event.unit.id,
roomId: event.roomId);
List<String> allDevicesIds = [];
@ -93,13 +105,56 @@ class ManageUnitBloc extends Bloc<ManageUnitEvent, ManageUnitState> {
}
}
void _unassignDevice(
UnassignRoomEvent event, Emitter<ManageUnitState> emit) async {
try {
Map<String, bool> roomDevicesId = {};
emit(LoadingState());
await HomeManagementAPI.unAssignDeviceToRoom(
event.unit.community.uuid, event.unit.id, event.roomId, event.deviceId);
final devicesList = await DevicesAPI.getDevicesByRoomId(
communityUuid: event.unit.community.uuid,
spaceUuid: event.unit.id,
roomId: event.roomId);
List<String> allDevicesIds = [];
allDevices.forEach((element) {
allDevicesIds.add(element.uuid!);
});
devicesList.forEach((e) {
if (allDevicesIds.contains(e.uuid!)) {
roomDevicesId[e.uuid!] = true;
} else {
roomDevicesId[e.uuid!] = false;
}
});
emit(FetchDeviceByRoomIdState(
roomDevices: devicesList,
allDevices: allDevices,
roomDevicesId: roomDevicesId,
roomId: event.roomId));
} catch (e) {
emit(const ErrorState(message: 'Something went wrong'));
return;
}
}
_addNewRoom(AddNewRoom event, Emitter<ManageUnitState> emit) async {
Map<String, String> body = {'roomName': event.roomName, 'unitUuid': event.unitId};
Map<String, String> body = {'subspaceName': event.roomName};
try {
emit(LoadingState());
final response = await HomeCreation.createRoom(body);
final response = await HomeCreation.createRoom(
communityId: event.unit.community.uuid,
spaceId: event.unit.id,
body: body);
if (response['data']['uuid'] != '') {
final roomsList = await SpacesAPI.getRoomsBySpaceId(event.unitId);
final roomsList = await SpacesAPI.getSubSpaceBySpaceId(
event.unit.community.uuid, event.unit.id);
allDevices = await HomeManagementAPI.fetchDevicesByUserId();
emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList));
await HomeCubit.getInstance().fetchUnitsByUserId();

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
abstract class ManageUnitEvent extends Equatable {
const ManageUnitEvent();
@ -12,39 +13,54 @@ class InitialEvent extends ManageUnitEvent {}
class LoadingEvent extends ManageUnitEvent {}
class FetchRoomsEvent extends ManageUnitEvent {
final String unitId;
final SpaceModel unit;
const FetchRoomsEvent({required this.unitId});
const FetchRoomsEvent({required this.unit});
@override
List<Object> get props => [unitId];
List<Object> get props => [unit];
}
class FetchDevicesByRoomIdEvent extends ManageUnitEvent {
final String roomId;
final SpaceModel unit;
const FetchDevicesByRoomIdEvent({required this.roomId});
const FetchDevicesByRoomIdEvent({required this.roomId, required this.unit});
@override
List<Object> get props => [roomId];
List<Object> get props => [roomId, unit];
}
class AddNewRoom extends ManageUnitEvent {
final String roomName;
final String unitId;
final SpaceModel unit;
const AddNewRoom({required this.roomName, required this.unitId});
const AddNewRoom({required this.roomName, required this.unit});
@override
List<Object> get props => [roomName, unitId];
List<Object> get props => [roomName, unit];
}
class AssignRoomEvent extends ManageUnitEvent {
final String roomId;
final String deviceId;
final SpaceModel unit;
const AssignRoomEvent({required this.roomId, required this.deviceId});
const AssignRoomEvent(
{required this.roomId, required this.deviceId, required this.unit});
@override
List<Object> get props => [roomId];
List<Object> get props => [roomId, unit];
}
class UnassignRoomEvent extends ManageUnitEvent {
final String roomId;
final String deviceId;
final SpaceModel unit;
const UnassignRoomEvent(
{required this.roomId, required this.deviceId, required this.unit});
@override
List<Object> get props => [roomId, unit];
}

View File

@ -1,6 +1,7 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/room_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
abstract class ManageUnitState extends Equatable {
const ManageUnitState();
@ -14,7 +15,7 @@ class InitialState extends ManageUnitState {}
class LoadingState extends ManageUnitState {}
class FetchRoomsState extends ManageUnitState {
final List<RoomModel> roomsList;
final List<SubSpaceModel> roomsList;
final List<DeviceModel> devicesList;
const FetchRoomsState({required this.devicesList, required this.roomsList});

View File

@ -44,7 +44,8 @@ class JoinHomeView extends StatelessWidget {
controller: textEditingController,
decoration: InputDecoration(
hintText: 'Invitatoin code',
hintStyle: context.bodyMedium.copyWith(color: Colors.grey),
hintStyle:
context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
),
@ -52,10 +53,12 @@ class JoinHomeView extends StatelessWidget {
IconButton(
onPressed: () async {
if (textEditingController.text.isEmpty) {
CustomSnackBar.displaySnackBar('Please enter the invitation code');
CustomSnackBar.displaySnackBar(
'Please enter the invitation code');
return;
}
if (await HomeCubit.getInstance().joinAUnit(textEditingController.text)) {
if (await HomeCubit.getInstance()
.joinAUnit(textEditingController.text)) {
CustomSnackBar.displaySnackBar('Done successfully');
Navigator.of(context).pop();
} else {

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart';
@ -11,16 +12,24 @@ import 'package:syncrow_app/utils/helpers/snack_bar.dart';
class AssignDeviceView extends StatelessWidget {
final String unitId;
final String roomId;
const AssignDeviceView({super.key, required this.unitId, required this.roomId});
final SpaceModel unit;
const AssignDeviceView(
{super.key,
required this.unitId,
required this.roomId,
required this.unit});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ManageUnitBloc()..add(FetchDevicesByRoomIdEvent(roomId: roomId)),
child: BlocConsumer<ManageUnitBloc, ManageUnitState>(listener: (context, state) {
create: (context) => ManageUnitBloc()
..add(FetchDevicesByRoomIdEvent(roomId: roomId, unit: unit)),
child: BlocConsumer<ManageUnitBloc, ManageUnitState>(
listener: (context, state) {
if (state is FetchDeviceByRoomIdState) {
if (state.allDevices.isEmpty) {
CustomSnackBar.displaySnackBar('You do not have the permission to assign devices');
CustomSnackBar.displaySnackBar(
'You do not have the permission to assign devices');
Navigator.of(context).pop();
}
}
@ -34,7 +43,8 @@ class AssignDeviceView extends StatelessWidget {
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
@ -52,11 +62,14 @@ class AssignDeviceView extends StatelessWidget {
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
SvgPicture.asset(
state.allDevices[index].icon!,
@ -64,19 +77,42 @@ class AssignDeviceView extends StatelessWidget {
),
GestureDetector(
onTap: () {
if (state.roomDevicesId[
state.allDevices[index].uuid!] ??
false == false) {
BlocProvider.of<ManageUnitBloc>(context).add(
AssignRoomEvent(
deviceId:
state.allDevices[index].uuid ?? '',
bool isAssigned =
state.roomDevicesId[state
.allDevices[index]
.uuid!] ??
false;
if (isAssigned) {
BlocProvider.of<
ManageUnitBloc>(
context)
.add(UnassignRoomEvent(
deviceId: state
.allDevices[
index]
.uuid ??
'',
unit: unit,
roomId: roomId));
} else {
// Tick (assign) the device
BlocProvider.of<
ManageUnitBloc>(
context)
.add(AssignRoomEvent(
deviceId: state
.allDevices[
index]
.uuid ??
'',
unit: unit,
roomId: roomId));
}
},
child: SvgPicture.asset(
state.roomDevicesId[
state.allDevices[index].uuid!] ??
state.roomDevicesId[state
.allDevices[index]
.uuid!] ??
false
? Assets.blueCheckboxIcon
: Assets.emptyCheckboxIcon,

View File

@ -60,7 +60,7 @@ class HomeSettingsView extends StatelessWidget {
onTap: () {
Navigator.of(context).push(CustomPageRoute(
builder: (context) => RoomsView(
unitId: space?.id ?? '',
unit: space!,
)));
},
child: Container(
@ -112,7 +112,7 @@ class HomeSettingsView extends StatelessWidget {
padding: const EdgeInsets.only(bottom: 10),
child: GestureDetector(
onTap: () async {
await HomeCubit.getInstance().generateInvitation(space?.id ?? '');
await HomeCubit.getInstance().generateInvitation(space!);
},
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart';
@ -11,14 +12,14 @@ import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class RoomsView extends StatelessWidget {
final String unitId;
const RoomsView({super.key, required this.unitId});
final SpaceModel unit;
const RoomsView({super.key, required this.unit});
@override
Widget build(BuildContext context) {
TextEditingController textEditingController = TextEditingController();
return BlocProvider(
create: (context) => ManageUnitBloc()..add(FetchRoomsEvent(unitId: unitId)),
create: (context) => ManageUnitBloc()..add(FetchRoomsEvent(unit:unit )),
child: BlocConsumer<ManageUnitBloc, ManageUnitState>(
listener: (context, state) {},
builder: (context, state) {
@ -62,7 +63,8 @@ class RoomsView extends StatelessWidget {
MaterialPageRoute(
builder: (context) => AssignDeviceView(
roomId: state.roomsList[index].id ?? '',
unitId: unitId,
unitId: unit.id,
unit: unit,
)),
);
},
@ -100,14 +102,14 @@ class RoomsView extends StatelessWidget {
BlocProvider.of<ManageUnitBloc>(context).add(
AddNewRoom(
roomName: textEditingController.text,
unitId: unitId));
unit: unit));
textEditingController.clear();
},
child: const Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
BodyMedium(
text: 'Add Space',
text: 'Add Room',
fontColor: ColorsManager.primaryColor,
),
],

View File

@ -60,7 +60,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
String selectedIcon = '';
bool showInDeviceScreen = false;
FutureOr<void> _onAddSceneTask(AddTaskEvent event, Emitter<CreateSceneState> emit) {
FutureOr<void> _onAddSceneTask(
AddTaskEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading());
if (event.isAutomation == true) {
final copyList = List<SceneStaticFunction>.from(automationTempTasksList);
@ -95,7 +96,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
}
}
void addToTempTaskList(TempHoldSceneTasksEvent event, Emitter<CreateSceneState> emit) {
void addToTempTaskList(
TempHoldSceneTasksEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading());
bool updated = false;
@ -180,7 +182,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
));
}
void addToTempAutomationTaskList(TempHoldSceneTasksEvent event, Emitter<CreateSceneState> emit) {
void addToTempAutomationTaskList(
TempHoldSceneTasksEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading());
bool updated = false;
for (var element in automationTempTasksList) {
@ -202,8 +205,10 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
],
comparator: automationComparatorValues[element.code],
);
automationTempTasksList[automationTempTasksList.indexOf(element)] = updatedElement;
automationSelectedValues[updatedElement.code] = event.deviceControlModel.value;
automationTempTasksList[automationTempTasksList.indexOf(element)] =
updatedElement;
automationSelectedValues[updatedElement.code] =
event.deviceControlModel.value;
updated = true;
break;
}
@ -223,10 +228,12 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
icon: '',
),
],
comparator: automationComparatorValues[event.deviceControlModel.code] ?? '==',
comparator:
automationComparatorValues[event.deviceControlModel.code] ?? '==',
);
automationTempTasksList.add(newElement);
automationSelectedValues[newElement.code] = event.deviceControlModel.value;
automationSelectedValues[newElement.code] =
event.deviceControlModel.value;
}
emit(AddSceneTask(
tasksList: tasksList,
@ -235,7 +242,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
));
}
FutureOr<void> _selectedValue(SelectedValueEvent event, Emitter<CreateSceneState> emit) {
FutureOr<void> _selectedValue(
SelectedValueEvent event, Emitter<CreateSceneState> emit) {
if (event.isAutomation == true) {
automationSelectedValues[event.code] = event.value;
automationComparatorValues[event.code] = event.comparator ?? '==';
@ -272,7 +280,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
));
}
FutureOr<void> _removeTaskById(RemoveTaskByIdEvent event, Emitter<CreateSceneState> emit) {
FutureOr<void> _removeTaskById(
RemoveTaskByIdEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading());
if (event.isAutomation == true) {
for (var element in automationTasksList) {
@ -345,7 +354,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
: await SceneApi.createScene(event.createSceneModel!);
} else if (event.createAutomationModel != null) {
response = event.updateScene
? await SceneApi.updateAutomation(event.createAutomationModel!, event.sceneId)
? await SceneApi.updateAutomation(
event.createAutomationModel!, event.sceneId)
: await SceneApi.createAutomation(event.createAutomationModel!);
}
@ -359,12 +369,14 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
automationComparatorValues.clear();
selectedIcon = '';
showInDeviceScreen = false;
effectiveTime = EffectiveTime(start: '00:00', end: '23:59', loops: '1111111');
effectiveTime =
EffectiveTime(start: '00:00', end: '23:59', loops: '1111111');
sceneType = CreateSceneEnum.none;
conditionRule = 'or';
emit(const CreateSceneWithTasks(success: true));
CustomSnackBar.greenSnackBar(
event.updateScene ? 'Scene updated successfully' : 'Scene created successfully');
CustomSnackBar.greenSnackBar(event.updateScene
? 'Scene updated successfully'
: 'Scene created successfully');
} else {
emit(const CreateSceneError(message: 'Something went wrong'));
}
@ -378,7 +390,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
}
}
FutureOr<void> _clearTaskList(ClearTaskListEvent event, Emitter<CreateSceneState> emit) {
FutureOr<void> _clearTaskList(
ClearTaskListEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading());
automationTasksList.clear();
tasksList.clear();
@ -389,7 +402,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
));
}
FutureOr<void> _clearTabToRunSetting(ClearTabToRunSetting event, Emitter<CreateSceneState> emit) {
FutureOr<void> _clearTabToRunSetting(
ClearTabToRunSetting event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading());
selectedIcon = '';
showInDeviceScreen = false;
@ -416,7 +430,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
automationComparatorValues.clear();
selectedIcon = '';
showInDeviceScreen = false;
effectiveTime = EffectiveTime(start: '00:00', end: '23:59', loops: '1111111');
effectiveTime =
EffectiveTime(start: '00:00', end: '23:59', loops: '1111111');
sceneType = CreateSceneEnum.none;
conditionRule = 'or';
@ -425,10 +440,14 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
: await SceneApi.getSceneDetails(event.sceneId);
if (response.id.isNotEmpty) {
if (event.isAutomation) {
automationTasksList = List<SceneStaticFunction>.from(getTaskListFunctionsFromApi(
actions: [], isAutomation: true, conditions: response.conditions));
automationTasksList = List<SceneStaticFunction>.from(
getTaskListFunctionsFromApi(
actions: [],
isAutomation: true,
conditions: response.conditions));
tasksList = List<SceneStaticFunction>.from(
getTaskListFunctionsFromApi(actions: response.actions, isAutomation: false));
getTaskListFunctionsFromApi(
actions: response.actions, isAutomation: false));
conditionRule = response.decisionExpr ?? conditionRule;
@ -441,11 +460,13 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
: EffectiveTime(start: '00:00', end: '23:59', loops: '1111111');
// Set the days directly from the API response
BlocProvider.of<EffectPeriodBloc>(NavigationService.navigatorKey.currentContext!)
BlocProvider.of<EffectPeriodBloc>(
NavigationService.navigatorKey.currentContext!)
.add(SetDays(response.effectiveTime?.loops ?? '1111111'));
// Set Custom Time and reset days first
BlocProvider.of<EffectPeriodBloc>(NavigationService.navigatorKey.currentContext!)
BlocProvider.of<EffectPeriodBloc>(
NavigationService.navigatorKey.currentContext!)
.add(SetCustomTime(effectiveTime!.start, effectiveTime!.end));
emit(AddSceneTask(
@ -457,7 +478,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
showInDevice: showInDeviceScreen));
} else {
tasksList = List<SceneStaticFunction>.from(
getTaskListFunctionsFromApi(actions: response.actions, isAutomation: false));
getTaskListFunctionsFromApi(
actions: response.actions, isAutomation: false));
selectedIcon = response.icon!;
showInDeviceScreen = response.showInDevice!;
emit(AddSceneTask(
@ -475,7 +497,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
}
}
FutureOr<void> _fetchIconScene(SceneIconEvent event, Emitter<CreateSceneState> emit) async {
FutureOr<void> _fetchIconScene(
SceneIconEvent event, Emitter<CreateSceneState> emit) async {
emit(CreateSceneLoading());
try {
iconModelList = await SceneApi.getIcon();
@ -491,7 +514,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
}
}
FutureOr<void> _iconSelected(IconSelected event, Emitter<CreateSceneState> emit) async {
FutureOr<void> _iconSelected(
IconSelected event, Emitter<CreateSceneState> emit) async {
try {
if (event.confirmSelection) {
selectedIcon = event.iconId;
@ -531,7 +555,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
return days[index];
}
FutureOr<void> _clearTempTaskList(ClearTempTaskListEvent event, Emitter<CreateSceneState> emit) {
FutureOr<void> _clearTempTaskList(
ClearTempTaskListEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading());
if (event.isAutomation == true) {
automationTempTasksList.clear();
@ -575,13 +600,18 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
}
}
FutureOr<void> _deleteScene(DeleteSceneEvent event, Emitter<CreateSceneState> emit) async {
FutureOr<void> _deleteScene(
DeleteSceneEvent event, Emitter<CreateSceneState> emit) async {
emit(DeleteSceneLoading());
try {
final response = sceneType.name == CreateSceneEnum.deviceStatusChanges.name
? await SceneApi.deleteAutomation(automationId: event.sceneId, unitUuid: event.unitUuid)
: await SceneApi.deleteScene(sceneId: event.sceneId, unitUuid: event.unitUuid);
final response =
sceneType.name == CreateSceneEnum.deviceStatusChanges.name
? await SceneApi.deleteAutomation(
automationId: event.sceneId, unitUuid: event.unitUuid)
: await SceneApi.deleteScene(
sceneId: event.sceneId,
);
if (response == true) {
emit(const DeleteSceneSuccess(true));
} else {
@ -592,7 +622,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
}
}
FutureOr<void> _updateTaskValue(UpdateTaskEvent event, Emitter<CreateSceneState> emit) {
FutureOr<void> _updateTaskValue(
UpdateTaskEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading());
if (event.isAutomation == true) {
for (var i = 0; i < automationTasksList.length; i++) {
@ -628,7 +659,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
));
}
FutureOr<void> _selectConditionRule(SelectConditionEvent event, Emitter<CreateSceneState> emit) {
FutureOr<void> _selectConditionRule(
SelectConditionEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneInitial());
if (event.condition.contains('any')) {
conditionRule = 'or';
@ -643,7 +675,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
));
}
FutureOr<void> _sceneTypeEvent(SceneTypeEvent event, Emitter<CreateSceneState> emit) {
FutureOr<void> _sceneTypeEvent(
SceneTypeEvent event, Emitter<CreateSceneState> emit) {
// emit(CreateSceneInitial());
if (event.type == CreateSceneEnum.tabToRun) {

View File

@ -24,7 +24,9 @@ class SceneBloc extends Bloc<SceneEvent, SceneState> {
try {
if (event.unitId.isNotEmpty) {
scenes = await SceneApi.getScenesByUnitId(event.unitId, showInDevice: event.showInDevice);
scenes = await SceneApi.getScenesByUnitId(
event.unitId, event.unit.community.uuid,
showInDevice: event.showInDevice);
emit(SceneLoaded(scenes, automationList));
} else {
emit(const SceneError(message: 'Unit ID is empty'));
@ -34,7 +36,8 @@ class SceneBloc extends Bloc<SceneEvent, SceneState> {
}
}
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<SceneState> emit) async {
Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<SceneState> emit) async {
emit(SceneLoading());
try {
@ -49,7 +52,8 @@ class SceneBloc extends Bloc<SceneEvent, SceneState> {
}
}
Future<void> _onSceneTrigger(SceneTrigger event, Emitter<SceneState> emit) async {
Future<void> _onSceneTrigger(
SceneTrigger event, Emitter<SceneState> emit) async {
final currentState = state;
if (currentState is SceneLoaded) {
emit(SceneLoaded(
@ -76,8 +80,9 @@ class SceneBloc extends Bloc<SceneEvent, SceneState> {
UpdateAutomationStatus event, Emitter<SceneState> emit) async {
final currentState = state;
if (currentState is SceneLoaded) {
final newLoadingStates = Map<String, bool>.from(currentState.loadingStates)
..[event.automationId] = true;
final newLoadingStates =
Map<String, bool>.from(currentState.loadingStates)
..[event.automationId] = true;
emit(SceneLoaded(
currentState.scenes,
@ -86,11 +91,11 @@ class SceneBloc extends Bloc<SceneEvent, SceneState> {
));
try {
final success =
await SceneApi.updateAutomationStatus(event.automationId, event.automationStatusUpdate);
final success = await SceneApi.updateAutomationStatus(
event.automationId, event.automationStatusUpdate);
if (success) {
automationList =
await SceneApi.getAutomationByUnitId(event.automationStatusUpdate.unitUuid);
automationList = await SceneApi.getAutomationByUnitId(
event.automationStatusUpdate.spaceUuid);
newLoadingStates[event.automationId] = false;
emit(SceneLoaded(
currentState.scenes,

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/scene/model/update_automation.dart';
abstract class SceneEvent extends Equatable {
@ -11,8 +12,9 @@ abstract class SceneEvent extends Equatable {
class LoadScenes extends SceneEvent {
final String unitId;
final bool showInDevice;
final SpaceModel unit;
const LoadScenes(this.unitId, {this.showInDevice = false});
const LoadScenes(this.unitId, this.unit, {this.showInDevice = false});
@override
List<Object> get props => [unitId, showInDevice];

View File

@ -17,7 +17,7 @@ class TabBarBloc extends Bloc<TabBarEvent, TabBarState> {
if (event.roomId == "-1") {
deviceManagerBloc.add(FetchAllDevices());
} else {
deviceManagerBloc.add(FetchDevicesByRoomId(event.roomId));
deviceManagerBloc.add(FetchDevicesByRoomId(event.roomId,event.unit));
}
emit(TabSelected(
roomId: event.roomId, selectedTabIndex: event.selectedIndex));

View File

@ -1,3 +1,5 @@
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
abstract class TabBarEvent {
const TabBarEvent();
}
@ -5,5 +7,7 @@ abstract class TabBarEvent {
class TabChanged extends TabBarEvent {
final int selectedIndex;
final String roomId;
const TabChanged({required this.selectedIndex, required this.roomId});
final SpaceModel unit;
const TabChanged(
{required this.selectedIndex, required this.roomId, required this.unit});
}

View File

@ -124,7 +124,7 @@ mixin SceneLogicHelper {
));
} else {
final createSceneModel = CreateSceneModel(
unitUuid: HomeCubit.getInstance().selectedSpace!.id ?? '',
spaceUuid: HomeCubit.getInstance().selectedSpace!.id ?? '',
iconId: sceneBloc.selectedIcon,
showInDevice: sceneBloc.showInDeviceScreen,
sceneName: sceneName.text,

View File

@ -39,7 +39,7 @@ class CreateAutomationModel {
Map<String, dynamic> toMap([String? automationId]) {
return {
if (automationId == null) 'unitUuid': unitUuid,
if (automationId == null) 'spaceUuid': unitUuid,
'automationName': automationName,
'decisionExpr': decisionExpr,
'effectiveTime': effectiveTime.toMap(),
@ -50,7 +50,7 @@ class CreateAutomationModel {
factory CreateAutomationModel.fromMap(Map<String, dynamic> map) {
return CreateAutomationModel(
unitUuid: map['unitUuid'] ?? '',
unitUuid: map['spaceUuid'] ?? '',
automationName: map['automationName'] ?? '',
decisionExpr: map['decisionExpr'] ?? '',
effectiveTime: EffectiveTime.fromMap(map['effectiveTime']),

View File

@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
class CreateSceneModel {
String unitUuid;
String spaceUuid;
String iconId;
bool showInDevice;
String sceneName;
@ -11,7 +11,7 @@ class CreateSceneModel {
List<CreateSceneAction> actions;
CreateSceneModel({
required this.unitUuid,
required this.spaceUuid,
required this.iconId,
required this.showInDevice,
required this.sceneName,
@ -20,15 +20,16 @@ class CreateSceneModel {
});
CreateSceneModel copyWith({
String? unitUuid,
String? spaceUuid,
String? iconId,
bool? showInDevice,
String? sceneName,
String? decisionExpr,
List<CreateSceneAction>? actions,
bool? showInHomePage,
}) {
return CreateSceneModel(
unitUuid: unitUuid ?? this.unitUuid,
spaceUuid: spaceUuid ?? this.spaceUuid,
iconId: iconId ?? this.iconId,
showInDevice: showInDevice ?? this.showInDevice,
sceneName: sceneName ?? this.sceneName,
@ -39,7 +40,7 @@ class CreateSceneModel {
Map<String, dynamic> toMap([String? sceneId]) {
return {
if (sceneId == null) 'unitUuid': unitUuid,
if (sceneId == null) 'spaceUuid': spaceUuid,
if (iconId.isNotEmpty) 'iconUuid': iconId,
'showInHomePage': showInDevice,
'sceneName': sceneName,
@ -50,23 +51,24 @@ class CreateSceneModel {
factory CreateSceneModel.fromMap(Map<String, dynamic> map) {
return CreateSceneModel(
unitUuid: map['unitUuid'] ?? '',
iconId: map['iconUuid'] ?? '',
spaceUuid: map['spaceUuid'] ?? '',
showInDevice: map['showInHomePage'] ?? false,
iconId: map['iconUuid'] ?? '',
sceneName: map['sceneName'] ?? '',
decisionExpr: map['decisionExpr'] ?? '',
actions:
List<CreateSceneAction>.from(map['actions']?.map((x) => CreateSceneAction.fromMap(x))),
actions: List<CreateSceneAction>.from(
map['actions']?.map((x) => CreateSceneAction.fromMap(x))),
);
}
String toJson([String? sceneId]) => json.encode(toMap(sceneId));
factory CreateSceneModel.fromJson(String source) => CreateSceneModel.fromMap(json.decode(source));
factory CreateSceneModel.fromJson(String source) =>
CreateSceneModel.fromMap(json.decode(source));
@override
String toString() {
return 'CreateSceneModel(unitUuid: $unitUuid, sceneName: $sceneName, decisionExpr: $decisionExpr, actions: $actions)';
return 'CreateSceneModel(unitUuid: $spaceUuid, sceneName: $sceneName, decisionExpr: $decisionExpr, actions: $actions)';
}
@override
@ -74,7 +76,7 @@ class CreateSceneModel {
if (identical(this, other)) return true;
return other is CreateSceneModel &&
other.unitUuid == unitUuid &&
other.spaceUuid == spaceUuid &&
other.iconId == iconId &&
other.showInDevice == showInDevice &&
other.sceneName == sceneName &&
@ -84,7 +86,10 @@ class CreateSceneModel {
@override
int get hashCode {
return unitUuid.hashCode ^ sceneName.hashCode ^ decisionExpr.hashCode ^ actions.hashCode;
return spaceUuid.hashCode ^
sceneName.hashCode ^
decisionExpr.hashCode ^
actions.hashCode;
}
}
@ -130,7 +135,8 @@ class CreateSceneAction {
return CreateSceneAction(
entityId: map['entityId'] ?? '',
actionExecutor: map['actionExecutor'] ?? '',
executorProperty: CreateSceneExecutorProperty.fromMap(map['executorProperty']),
executorProperty:
CreateSceneExecutorProperty.fromMap(map['executorProperty']),
);
}
@ -154,7 +160,8 @@ class CreateSceneAction {
}
@override
int get hashCode => entityId.hashCode ^ actionExecutor.hashCode ^ executorProperty.hashCode;
int get hashCode =>
entityId.hashCode ^ actionExecutor.hashCode ^ executorProperty.hashCode;
}
class CreateSceneExecutorProperty {
@ -218,5 +225,6 @@ class CreateSceneExecutorProperty {
}
@override
int get hashCode => functionCode.hashCode ^ functionValue.hashCode ^ delaySeconds.hashCode;
int get hashCode =>
functionCode.hashCode ^ functionValue.hashCode ^ delaySeconds.hashCode;
}

View File

@ -25,28 +25,34 @@ class SceneDetailsModel {
this.effectiveTime,
});
factory SceneDetailsModel.fromRawJson(String str) => SceneDetailsModel.fromJson(json.decode(str));
factory SceneDetailsModel.fromRawJson(String str) =>
SceneDetailsModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory SceneDetailsModel.fromJson(Map<String, dynamic> json) => SceneDetailsModel(
id: json["id"],
name: json["name"],
status: json["status"],
type: json["type"],
actions: (json["actions"] as List)
.map((x) => Action.fromJson(x))
.where((x) => x != null)
.toList()
.cast<Action>(),
conditions: json["conditions"] != null
? (json["conditions"] as List).map((x) => Condition.fromJson(x)).toList()
: null,
decisionExpr: json["decisionExpr"],
effectiveTime:
json["effectiveTime"] != null ? EffectiveTime.fromJson(json["effectiveTime"]) : null,
icon: json["iconUuid"] != null ? json["iconUuid"] ?? '' : '',
showInDevice: json['showInHome'] != null ? json['showInHome'] ?? false : false);
factory SceneDetailsModel.fromJson(Map<String, dynamic> json) =>
SceneDetailsModel(
id: json["uuid"] ?? json["id"],
name: json["name"],
status: json["status"],
type: json["type"],
actions: (json["actions"] as List)
.map((x) => Action.fromJson(x))
.where((x) => x != null)
.toList()
.cast<Action>(),
conditions: json["conditions"] != null
? (json["conditions"] as List)
.map((x) => Condition.fromJson(x))
.toList()
: null,
decisionExpr: json["decisionExpr"],
effectiveTime: json["effectiveTime"] != null
? EffectiveTime.fromJson(json["effectiveTime"])
: null,
icon: json["iconUuid"] != null ? json["iconUuid"] ?? '' : '',
showInDevice:
json['showInHome'] != null ? json['showInHome'] ?? false : false);
Map<String, dynamic> toJson() => {
"id": id,
@ -54,8 +60,9 @@ class SceneDetailsModel {
"status": status,
"type": type,
"actions": List<dynamic>.from(actions.map((x) => x.toJson())),
"conditions":
conditions != null ? List<dynamic>.from(conditions!.map((x) => x.toJson())) : null,
"conditions": conditions != null
? List<dynamic>.from(conditions!.map((x) => x.toJson()))
: null,
"decisionExpr": decisionExpr,
"effectiveTime": effectiveTime?.toJson(),
};
@ -116,7 +123,8 @@ class ExecutorProperty {
this.delaySeconds,
});
factory ExecutorProperty.fromJson(Map<String, dynamic> json) => ExecutorProperty(
factory ExecutorProperty.fromJson(Map<String, dynamic> json) =>
ExecutorProperty(
functionCode: json["functionCode"] ?? '',
functionValue: json["functionValue"] ?? '',
delaySeconds: json["delaySeconds"] ?? 0,
@ -142,7 +150,8 @@ class Condition {
required this.expr,
});
factory Condition.fromRawJson(String str) => Condition.fromJson(json.decode(str));
factory Condition.fromRawJson(String str) =>
Condition.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
@ -200,7 +209,8 @@ class EffectiveTime {
required this.loops,
});
factory EffectiveTime.fromRawJson(String str) => EffectiveTime.fromJson(json.decode(str));
factory EffectiveTime.fromRawJson(String str) =>
EffectiveTime.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());

View File

@ -3,32 +3,40 @@ import 'dart:typed_data';
class ScenesModel {
final String id;
final String? sceneTuyaId;
final String name;
final String status;
final String type;
final String icon;
final String? icon;
ScenesModel(
{required this.id,
this.sceneTuyaId,
required this.name,
required this.status,
required this.type,
required this.icon});
this.icon});
factory ScenesModel.fromRawJson(String str) => ScenesModel.fromJson(json.decode(str));
factory ScenesModel.fromRawJson(String str) =>
ScenesModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
Uint8List get iconInBytes => base64Decode(icon);
factory ScenesModel.fromJson(Map<String, dynamic> json) => ScenesModel(
id: json["id"],
name: json["name"] ?? '',
status: json["status"] ?? '',
type: json["type"] ?? '',
icon: json["icon"] ?? '');
Uint8List get iconInBytes => base64Decode(icon ?? '');
factory ScenesModel.fromJson(Map<String, dynamic> json) {
return ScenesModel(
id: json["id"] ?? json["uuid"] ?? '', // Fallback to empty string if id is null
sceneTuyaId: json["sceneTuyaId"] as String?, // Nullable
name: json["name"] ?? '', // Fallback to empty string if name is null
status:
json["status"] ?? '', // Fallback to empty string if status is null
type: json["type"] ?? '', // Fallback to empty string if type is null
icon: json["icon"] as String?, // Nullable
);
}
Map<String, dynamic> toJson() => {
"id": id,
"sceneTuyaId": sceneTuyaId ?? '',
"name": name,
"status": status,
"type": type,

View File

@ -1,11 +1,11 @@
import 'dart:convert';
class AutomationStatusUpdate {
final String unitUuid;
final String spaceUuid;
final bool isEnable;
AutomationStatusUpdate({
required this.unitUuid,
required this.spaceUuid,
required this.isEnable,
});
@ -16,23 +16,23 @@ class AutomationStatusUpdate {
factory AutomationStatusUpdate.fromJson(Map<String, dynamic> json) =>
AutomationStatusUpdate(
unitUuid: json["unitUuid"],
spaceUuid: json["spaceUuid"],
isEnable: json["isEnable"],
);
Map<String, dynamic> toJson() => {
"unitUuid": unitUuid,
"spaceUuid": spaceUuid,
"isEnable": isEnable,
};
factory AutomationStatusUpdate.fromMap(Map<String, dynamic> map) =>
AutomationStatusUpdate(
unitUuid: map["unitUuid"],
spaceUuid: map["spaceUuid"],
isEnable: map["isEnable"],
);
Map<String, dynamic> toMap() => {
"unitUuid": unitUuid,
"spaceUuid": spaceUuid,
"isEnable": isEnable,
};
}

View File

@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
import 'package:syncrow_app/features/devices/model/room_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart';
import 'package:syncrow_app/features/scene/widgets/scene_devices/scene_devices_body.dart';
@ -26,20 +27,21 @@ class _SceneRoomsTabBarDevicesViewState
extends State<SceneRoomsTabBarDevicesView>
with SingleTickerProviderStateMixin {
late final TabController _tabController;
List<RoomModel>? rooms = [];
List<SubSpaceModel>? rooms = [];
late final SpaceModel selectedSpace;
@override
void initState() {
rooms = List.from(HomeCubit.getInstance().selectedSpace?.rooms ?? []);
selectedSpace = HomeCubit.getInstance().selectedSpace!;
rooms = List.from(HomeCubit.getInstance().selectedSpace?.subspaces ?? []);
if (rooms != null) {
if (rooms![0].id != '-1') {
rooms?.insert(
0,
RoomModel(
SubSpaceModel(
name: 'All Devices',
devices: DevicesCubit.getInstance().allDevices,
id: '-1',
type: SpaceType.Room,
),
);
}
@ -56,8 +58,10 @@ class _SceneRoomsTabBarDevicesViewState
final value = _tabController.index;
/// select tab
context.read<TabBarBloc>().add(
TabChanged(selectedIndex: value, roomId: rooms?[value].id ?? ''));
context.read<TabBarBloc>().add(TabChanged(
selectedIndex: value,
roomId: rooms?[value].id ?? '',
unit: selectedSpace));
return;
}
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/app_layout/model/community_model.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/scene_listview.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart';
@ -21,32 +23,47 @@ class SceneView extends StatelessWidget {
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => SceneBloc()
..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? '', showInDevice: pageType))
..add(LoadScenes(
HomeCubit.getInstance().selectedSpace?.id ?? '',
HomeCubit.getInstance().selectedSpace ??
SpaceModel(
id: '-1',
name: '',
community: Community(
uuid: '-1',
name: '',
)),
showInDevice: pageType))
..add(LoadAutomation(HomeCubit.getInstance().selectedSpace?.id ?? '')),
child: BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) {
if (state is DeleteSceneSuccess) {
if (state.success) {
BlocProvider.of<SceneBloc>(context).add(LoadScenes(
HomeCubit.getInstance().selectedSpace!.id!,HomeCubit.getInstance().selectedSpace!,
showInDevice: pageType));
BlocProvider.of<SceneBloc>(context).add(
LoadScenes(HomeCubit.getInstance().selectedSpace!.id!, showInDevice: pageType));
BlocProvider.of<SceneBloc>(context)
.add(LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!));
LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!));
}
}
if (state is CreateSceneWithTasks) {
if (state.success == true) {
BlocProvider.of<SceneBloc>(context).add(LoadScenes(
HomeCubit.getInstance().selectedSpace!.id!,HomeCubit.getInstance().selectedSpace!,
showInDevice: pageType));
BlocProvider.of<SceneBloc>(context).add(
LoadScenes(HomeCubit.getInstance().selectedSpace!.id!, showInDevice: pageType));
BlocProvider.of<SceneBloc>(context)
.add(LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!));
context.read<SmartSceneSelectBloc>().add(const SmartSceneClearEvent());
LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!));
context
.read<SmartSceneSelectBloc>()
.add(const SmartSceneClearEvent());
}
}
return BlocListener<SceneBloc, SceneState>(
listener: (context, state) {
if (state is SceneTriggerSuccess) {
context.showCustomSnackbar(
message: 'Scene ${state.sceneName} triggered successfully!');
message:
'Scene ${state.sceneName} triggered successfully!');
}
},
child: HomeCubit.getInstance().spaces?.isEmpty ?? true
@ -83,25 +100,30 @@ class SceneView extends StatelessWidget {
child: ListView(
children: [
Theme(
data: ThemeData()
.copyWith(dividerColor: Colors.transparent),
data: ThemeData().copyWith(
dividerColor: Colors.transparent),
child: ExpansionTile(
tilePadding: const EdgeInsets.symmetric(horizontal: 6),
tilePadding:
const EdgeInsets.symmetric(
horizontal: 6),
initiallyExpanded: true,
iconColor: ColorsManager.grayColor,
title: const BodyMedium(text: 'Tap to run routines'),
title: const BodyMedium(
text: 'Tap to run routines'),
children: [
scenes.isNotEmpty
? SceneGrid(
scenes: scenes,
loadingSceneId: state.loadingSceneId,
loadingSceneId:
state.loadingSceneId,
disablePlayButton: false,
loadingStates:
state.loadingStates, // Add this line
loadingStates: state
.loadingStates, // Add this line
)
: const Center(
child: BodyMedium(
text: 'No scenes have been added yet',
text:
'No scenes have been added yet',
),
),
const SizedBox(
@ -111,25 +133,30 @@ class SceneView extends StatelessWidget {
),
),
Theme(
data: ThemeData()
.copyWith(dividerColor: Colors.transparent),
data: ThemeData().copyWith(
dividerColor: Colors.transparent),
child: ExpansionTile(
initiallyExpanded: true,
iconColor: ColorsManager.grayColor,
tilePadding: const EdgeInsets.symmetric(horizontal: 6),
title: const BodyMedium(text: 'Automation'),
tilePadding:
const EdgeInsets.symmetric(
horizontal: 6),
title: const BodyMedium(
text: 'Automation'),
children: [
automationList.isNotEmpty
? SceneGrid(
scenes: automationList,
loadingSceneId: state.loadingSceneId,
loadingSceneId:
state.loadingSceneId,
disablePlayButton: true,
loadingStates:
state.loadingStates, // Add this line
loadingStates: state
.loadingStates, // Add this line
)
: const Center(
child: BodyMedium(
text: 'No automations have been added yet',
text:
'No automations have been added yet',
),
),
const SizedBox(

View File

@ -24,7 +24,7 @@ class DeleteRoutineButton extends StatelessWidget {
if (state.success) {
navigateToRoute(context, Routes.homeRoute);
BlocProvider.of<SceneBloc>(context)
.add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!));
.add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!,HomeCubit.getInstance().selectedSpace!));
BlocProvider.of<SceneBloc>(context).add(
LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!));
}

View File

@ -4,6 +4,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_state.dart';
import 'package:syncrow_app/features/devices/model/room_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart';
import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart';
@ -24,7 +25,7 @@ class SceneDevicesBody extends StatelessWidget {
}) : _tabController = tabController;
final TabController _tabController;
final List<RoomModel>? rooms;
final List<SubSpaceModel>? rooms;
@override
Widget build(BuildContext context) {
@ -76,7 +77,7 @@ class SceneDevicesBody extends StatelessWidget {
}
Widget _buildRoomTab(
RoomModel room, BuildContext context, bool isAutomationDeviceStatus) {
SubSpaceModel room, BuildContext context, bool isAutomationDeviceStatus) {
return BlocBuilder<DeviceManagerBloc, DeviceManagerState>(
builder: (context, state) {
if (state.loading && state.devices == null) {

View File

@ -38,8 +38,8 @@ class SceneItem extends StatelessWidget {
onTap: () {
context.read<SmartSceneSelectBloc>().add(const SmartSceneClearEvent());
if (disablePlayButton == false) {
BlocProvider.of<CreateSceneBloc>(context)
.add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false));
BlocProvider.of<CreateSceneBloc>(context).add(
FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false));
/// the state to set the scene type must be after the fetch
BlocProvider.of<CreateSceneBloc>(context)
@ -73,22 +73,32 @@ class SceneItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (!disablePlayButton)
if (!disablePlayButton && scene.iconInBytes != null && scene.iconInBytes.isNotEmpty)
Image.memory(
scene.iconInBytes,
height: 32,
width: 32,
fit: BoxFit.fill,
errorBuilder: (context, error, stackTrace) => Image.asset(
Assets.assetsIconsLogo,
height: 32,
width: 32,
fit: BoxFit.fill),
),
if (disablePlayButton || scene.iconInBytes == null || scene.iconInBytes.isEmpty)
SvgPicture.asset(
Assets.automationIcon,
height: 32,
width: 32,
scene.iconInBytes,
fit: BoxFit.fill,
errorBuilder: (context, error, stackTrace) =>
Image.asset(height: 32, width: 32, fit: BoxFit.fill, Assets.assetsIconsLogo),
),
if (disablePlayButton)
SvgPicture.asset(height: 32, width: 32, fit: BoxFit.fill, Assets.automationIcon),
disablePlayButton == false
? IconButton(
padding: EdgeInsets.zero,
onPressed: () {
context.read<SceneBloc>().add(SceneTrigger(scene.id, scene.name));
context
.read<SceneBloc>()
.add(SceneTrigger(scene.id, scene.name));
},
icon: isLoading
? const Center(
@ -110,11 +120,15 @@ class SceneItem extends StatelessWidget {
activeColor: ColorsManager.primaryColor,
value: scene.status == 'enable' ? true : false,
onChanged: (value) {
context.read<SceneBloc>().add(UpdateAutomationStatus(
automationStatusUpdate: AutomationStatusUpdate(
isEnable: value,
unitUuid: HomeCubit.getInstance().selectedSpace!.id!),
automationId: scene.id));
context.read<SceneBloc>().add(
UpdateAutomationStatus(
automationStatusUpdate:
AutomationStatusUpdate(
isEnable: value,
spaceUuid: HomeCubit.getInstance()
.selectedSpace!
.id!),
automationId: scene.id));
},
),
],

View File

@ -17,7 +17,7 @@ class SmartEnableTabRun extends StatelessWidget {
width: double.infinity,
child: BlocBuilder<SceneBloc, SceneState>(
bloc: context.read<SceneBloc>()
..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? '')),
..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? '',HomeCubit.getInstance().selectedSpace!)),
builder: (context, state) {
if (state is SceneLoading) {
return const Align(

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/model/community_model.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/app_layout/view/app_layout.dart';
import 'package:syncrow_app/features/auth/view/otp_view.dart';
import 'package:syncrow_app/features/auth/view/login_view.dart';
@ -84,9 +86,18 @@ class Router {
DeviceManagerBloc()..add(FetchAllDevices()),
),
BlocProvider(
create: (BuildContext context) => TabBarBloc(
context.read<DeviceManagerBloc>())
..add(const TabChanged(selectedIndex: 0, roomId: '-1')),
create: (BuildContext context) =>
TabBarBloc(context.read<DeviceManagerBloc>())
..add(TabChanged(
selectedIndex: 0,
roomId: '-1',
unit: SpaceModel(
id: '-1',
name: '',
community: Community(
uuid: '-1',
name: '',
)))),
),
],
child: const SceneRoomsTabBarDevicesView(),

View File

@ -53,17 +53,26 @@ abstract class ApiEndpoints {
//POST
static const String addUnit = '/unit';
static const String addUnitToUser = '/unit/user';
static const String verifyInvitationCode =
'/user/{userUuid}/spaces/verify-code';
//GET
static const String unitByUuid = '/unit/';
static const String unitChild = '/unit/child/';
static const String listSubspace =
'/communities/{communityUuid}/spaces/{spaceUuid}/subspaces';
static const String unitParent = '/unit/parent/{unitUuid}';
static const String unitUser = '/unit/user/';
static const String invitationCode = '/unit/{unitUuid}/invitation-code';
static const String verifyInvitationCode = '/unit/user/verify-code';
static const String invitationCode =
'/communities/{communityUuid}/spaces/{unitUuid}/invitation-code';
//PUT
static const String renameUnit = '/unit/{unitUuid}';
//Subspace Module
//POST
static const String addSubSpace =
'/communities/{communityUuid}/spaces/{spaceUuid}/subspaces';
///Room Module
//POST
static const String addRoom = '/room';
@ -75,6 +84,12 @@ abstract class ApiEndpoints {
//PUT
static const String renameRoom = '/room/{roomUuid}';
//SPACE Module
//GET
static const String userSpaces = '/user/{userUuid}/spaces';
static const String spaceDevices =
'/communities/{communityUuid}/spaces/{spaceUuid}/devices';
///Group Module
//POST
static const String addGroup = '/group';
@ -101,12 +116,15 @@ abstract class ApiEndpoints {
static const String openDoorLock = '/door-lock/open/{doorLockUuid}';
//GET
static const String deviceByRoom = '/device/room';
static const String deviceByRoom =
'/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices';
static const String deviceByUuid = '/device/{deviceUuid}';
static const String deviceFunctions = '/device/{deviceUuid}/functions';
static const String gatewayApi = '/device/gateway/{gatewayUuid}/devices';
static const String deviceFunctionsStatus = '/device/{deviceUuid}/functions/status';
static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status';
static const String deviceFunctionsStatus =
'/device/{deviceUuid}/functions/status';
static const String powerClamp =
'/device/{powerClampUuid}/power-clamp/status';
///Device Permission Module
//POST
@ -116,18 +134,20 @@ abstract class ApiEndpoints {
//PUT
static const String editDevicePermission = '/device-permission/edit/{userId}';
static const String assignDeviceToRoom = '/device/room';
static const String assignDeviceToRoom =
'/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices/{deviceUuid}';
/// Scene & Automation API ////////////////////
/// POST
static const String createScene = '/scene/tap-to-run';
static const String triggerScene = '/scene/tap-to-run/trigger/{sceneId}';
static const String triggerScene = '/scene/tap-to-run/{sceneId}/trigger';
static const String createAutomation = '/automation';
/// GET
static const String getUnitScenes = '/scene/tap-to-run/{unitUuid}';
static const String getUnitScenes =
'/communities/{communityUuid}/spaces/{spaceUuid}/scenes';
static const String getScene = '/scene/tap-to-run/details/{sceneId}';
static const String getScene = '/scene/tap-to-run/{sceneId}';
static const String getIconScene = '/scene/icon';
static const String getUnitAutomation = '/automation/{unitUuid}';
@ -144,10 +164,9 @@ abstract class ApiEndpoints {
'/automation/status/{automationId}';
/// DELETE
static const String deleteScene = '/scene/tap-to-run/{unitUuid}/{sceneId}';
static const String deleteScene = '/scene/tap-to-run/{sceneId}';
static const String deleteAutomation =
'/automation/{unitUuid}/{automationId}';
static const String deleteAutomation = '/automation/{automationId}';
//////////////////////Door Lock //////////////////////
//online

View File

@ -65,6 +65,7 @@ class DevicesAPI {
showServerMessage: false,
expectedResponseModel: (json) => DevicesCategoryModel.fromJsonList(json),
);
return response;
}
@ -142,23 +143,41 @@ class DevicesAPI {
return response;
}
static Future<List<DeviceModel>> getDevicesByRoomId(String roomId) async {
final response = await _httpService.get(
path: ApiEndpoints.deviceByRoom,
queryParameters: {"roomUuid": roomId},
showServerMessage: false,
expectedResponseModel: (json) {
if (json == null || json.isEmpty || json == []) {
return <DeviceModel>[];
}
List<DeviceModel> devices = [];
for (var device in json) {
devices.add(DeviceModel.fromJson(device));
}
return devices;
},
);
return response;
static Future<List<DeviceModel>> getDevicesByRoomId({
required String communityUuid,
required String spaceUuid,
required String roomId,
}) async {
try {
final String path = ApiEndpoints.deviceByRoom
.replaceAll('{communityUuid}', communityUuid)
.replaceAll('{spaceUuid}', spaceUuid)
.replaceAll('{subSpaceUuid}', roomId);
final response = await _httpService.get(
path: path,
showServerMessage: false,
expectedResponseModel: (json) {
final data = json['data'];
if (data == null || data.isEmpty) {
return <DeviceModel>[];
}
if (json == null || json.isEmpty || json == []) {
return <DeviceModel>[];
}
return data
.map<DeviceModel>((device) => DeviceModel.fromJson(device))
.toList();
},
);
return response;
} catch (e) {
// Log the error if needed
// Return an empty list in case of error
return <DeviceModel>[];
}
}
static Future<List<DeviceModel>> getDevicesByGatewayId(

View File

@ -4,7 +4,8 @@ import 'package:syncrow_app/services/api/http_service.dart';
class HomeCreation {
static final HTTPService _httpService = HTTPService();
static Future<Map<String, dynamic>> createCommunity(Map<String, String> body) async {
static Future<Map<String, dynamic>> createCommunity(
Map<String, String> body) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addCommunity,
@ -20,7 +21,8 @@ class HomeCreation {
}
}
static Future<Map<String, dynamic>> assignUserToCommunity(Map<String, String> body) async {
static Future<Map<String, dynamic>> assignUserToCommunity(
Map<String, String> body) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addCommunityToUser,
@ -36,7 +38,8 @@ class HomeCreation {
}
}
static Future<Map<String, dynamic>> createBuilding(Map<String, String> body) async {
static Future<Map<String, dynamic>> createBuilding(
Map<String, String> body) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addBuilding,
@ -52,7 +55,8 @@ class HomeCreation {
}
}
static Future<Map<String, dynamic>> assignUserToBuilding(Map<String, String> body) async {
static Future<Map<String, dynamic>> assignUserToBuilding(
Map<String, String> body) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addBuildingToUser,
@ -68,7 +72,8 @@ class HomeCreation {
}
}
static Future<Map<String, dynamic>> createFloor(Map<String, String> body) async {
static Future<Map<String, dynamic>> createFloor(
Map<String, String> body) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addFloor,
@ -84,7 +89,8 @@ class HomeCreation {
}
}
static Future<Map<String, dynamic>> assignUserToFloor(Map<String, String> body) async {
static Future<Map<String, dynamic>> assignUserToFloor(
Map<String, String> body) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addBuildingToUser,
@ -100,7 +106,8 @@ class HomeCreation {
}
}
static Future<Map<String, dynamic>> createUnit(Map<String, String> body) async {
static Future<Map<String, dynamic>> createUnit(
Map<String, String> body) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addUnit,
@ -116,7 +123,8 @@ class HomeCreation {
}
}
static Future<Map<String, dynamic>> assignUserToUnit(Map<String, String> body) async {
static Future<Map<String, dynamic>> assignUserToUnit(
Map<String, String> body) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addUnitToUser,
@ -132,10 +140,17 @@ class HomeCreation {
}
}
static Future<Map<String, dynamic>> createRoom(Map<String, String> body) async {
static Future<Map<String, dynamic>> createRoom({
required String communityId,
required String spaceId,
required Map<String, String> body,
}) async {
try {
final fullPath = ApiEndpoints.addSubSpace
.replaceAll('{communityUuid}', communityId)
.replaceAll('{spaceUuid}', spaceId);
final response = await _httpService.post(
path: ApiEndpoints.addRoom,
path: fullPath,
body: body,
showServerMessage: false,
expectedResponseModel: (json) {
@ -148,7 +163,8 @@ class HomeCreation {
}
}
static Future<Map<String, dynamic>> assignUserToRoom(Map<String, String> body) async {
static Future<Map<String, dynamic>> assignUserToRoom(
Map<String, String> body) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addRoomToUser,

View File

@ -28,24 +28,46 @@ class HomeManagementAPI {
static Future<List<DeviceModel>> fetchDevicesByUnitId() async {
List<DeviceModel> list = [];
await _httpService.get(
path: ApiEndpoints.getDevicesByUnitId.replaceAll(
"{unitUuid}", HomeCubit.getInstance().selectedSpace?.id ?? ''),
try {
// Retrieve selected space details
final selectedSpace = HomeCubit.getInstance().selectedSpace;
final communityUuid = selectedSpace?.community?.uuid ?? '';
final spaceUuid = selectedSpace?.id ?? '';
// Ensure both placeholders are replaced
final path = ApiEndpoints.spaceDevices
.replaceAll('{communityUuid}', communityUuid)
.replaceAll('{spaceUuid}', spaceUuid);
await _httpService.get(
path: path,
showServerMessage: false,
expectedResponseModel: (json) {
json.forEach((value) {
list.add(DeviceModel.fromJson(value));
});
});
if (json['data'] != null) {
json['data'].forEach((value) {
list.add(DeviceModel.fromJson(value));
});
}
},
);
// Debugging: Log successful fetch
} catch (e) {
// Log the error for debugging
}
return list;
}
static Future<Map<String, dynamic>> assignDeviceToRoom(
Map<String, String> body) async {
static Future<Map<String, dynamic>> assignDeviceToRoom(String communityId,
String spaceId, String subSpaceId, String deviceId) async {
try {
final response = await _httpService.put(
path: ApiEndpoints.assignDeviceToRoom,
body: body,
final response = await _httpService.post(
path: ApiEndpoints.assignDeviceToRoom
.replaceAll('{communityUuid}', communityId)
.replaceAll('{spaceUuid}', spaceId)
.replaceAll('{subSpaceUuid}', subSpaceId)
.replaceAll('{deviceUuid}', deviceId),
expectedResponseModel: (json) {
return json;
},
@ -55,4 +77,24 @@ class HomeManagementAPI {
rethrow;
}
}
static Future<Map<String, dynamic>> unAssignDeviceToRoom(String communityId,
String spaceId, String subSpaceId, String deviceId) async {
try {
final response = await _httpService.delete(
path: ApiEndpoints.assignDeviceToRoom
.replaceAll('{communityUuid}', communityId)
.replaceAll('{spaceUuid}', spaceId)
.replaceAll('{subSpaceUuid}', subSpaceId)
.replaceAll('{deviceUuid}', deviceId),
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
}

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:syncrow_app/features/scene/model/create_automation_model.dart';
import 'package:syncrow_app/features/scene/model/create_scene_model.dart';
import 'package:syncrow_app/features/scene/model/icon_model.dart';
@ -11,7 +13,8 @@ class SceneApi {
static final HTTPService _httpService = HTTPService();
//create scene
static Future<Map<String, dynamic>> createScene(CreateSceneModel createSceneModel) async {
static Future<Map<String, dynamic>> createScene(
CreateSceneModel createSceneModel) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.createScene,
@ -47,15 +50,21 @@ class SceneApi {
//get scene by unit id
static Future<List<ScenesModel>> getScenesByUnitId(String unitId, {showInDevice = false}) async {
static Future<List<ScenesModel>> getScenesByUnitId(
String unitId, String communityId,
{showInDevice = false}) async {
try {
final response = await _httpService.get(
path: ApiEndpoints.getUnitScenes.replaceAll('{unitUuid}', unitId),
path: ApiEndpoints.getUnitScenes
.replaceAll('{spaceUuid}', unitId)
.replaceAll('{communityUuid}', communityId),
queryParameters: {'showInHomePage': showInDevice},
showServerMessage: false,
expectedResponseModel: (json) {
final scenesJson = json['data'] as List;
List<ScenesModel> scenes = [];
for (var scene in json) {
for (var scene in scenesJson) {
scenes.add(ScenesModel.fromJson(scene));
}
return scenes;
@ -102,10 +111,12 @@ class SceneApi {
}
//automation details
static Future<SceneDetailsModel> getAutomationDetails(String automationId) async {
static Future<SceneDetailsModel> getAutomationDetails(
String automationId) async {
try {
final response = await _httpService.get(
path: ApiEndpoints.getAutomationDetails.replaceAll('{automationId}', automationId),
path: ApiEndpoints.getAutomationDetails
.replaceAll('{automationId}', automationId),
showServerMessage: false,
expectedResponseModel: (json) => SceneDetailsModel.fromJson(json),
);
@ -116,11 +127,12 @@ class SceneApi {
}
//updateAutomationStatus
static Future<bool> updateAutomationStatus(
String automationId, AutomationStatusUpdate createAutomationEnable) async {
static Future<bool> updateAutomationStatus(String automationId,
AutomationStatusUpdate createAutomationEnable) async {
try {
final response = await _httpService.put(
path: ApiEndpoints.updateAutomationStatus.replaceAll('{automationId}', automationId),
path: ApiEndpoints.updateAutomationStatus
.replaceAll('{automationId}', automationId),
body: createAutomationEnable.toMap(),
expectedResponseModel: (json) => json['success'],
);
@ -135,7 +147,13 @@ class SceneApi {
final response = await _httpService.get(
path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId),
showServerMessage: false,
expectedResponseModel: (json) => SceneDetailsModel.fromJson(json),
expectedResponseModel: (json) {
if (json != null && json['data'] != null) {
return SceneDetailsModel.fromJson(json['data']);
} else {
throw Exception('Data field is null');
}
},
);
return response;
} catch (e) {
@ -163,7 +181,8 @@ class SceneApi {
try {
final response = await _httpService.put(
path: ApiEndpoints.updateScene.replaceAll('{sceneId}', sceneId),
body: createSceneModel.toJson(sceneId.isNotEmpty == true ? sceneId : null),
body: createSceneModel
.toJson(sceneId.isNotEmpty == true ? sceneId : null),
expectedResponseModel: (json) {
return json;
},
@ -175,11 +194,14 @@ class SceneApi {
}
//update automation
static updateAutomation(CreateAutomationModel createAutomationModel, String automationId) async {
static updateAutomation(
CreateAutomationModel createAutomationModel, String automationId) async {
try {
final response = await _httpService.put(
path: ApiEndpoints.updateAutomation.replaceAll('{automationId}', automationId),
body: createAutomationModel.toJson(automationId.isNotEmpty == true ? automationId : null),
path: ApiEndpoints.updateAutomation
.replaceAll('{automationId}', automationId),
body: createAutomationModel
.toJson(automationId.isNotEmpty == true ? automationId : null),
expectedResponseModel: (json) {
return json;
},
@ -192,12 +214,10 @@ class SceneApi {
//delete Scene
static Future<bool> deleteScene({required String unitUuid, required String sceneId}) async {
static Future<bool> deleteScene({required String sceneId}) async {
try {
final response = await _httpService.delete(
path: ApiEndpoints.deleteScene
.replaceAll('{sceneId}', sceneId)
.replaceAll('{unitUuid}', unitUuid),
path: ApiEndpoints.deleteScene.replaceAll('{sceneId}', sceneId),
showServerMessage: false,
expectedResponseModel: (json) => json['statusCode'] == 200,
);
@ -213,8 +233,7 @@ class SceneApi {
try {
final response = await _httpService.delete(
path: ApiEndpoints.deleteAutomation
.replaceAll('{automationId}', automationId)
.replaceAll('{unitUuid}', unitUuid),
.replaceAll('{automationId}', automationId),
showServerMessage: false,
expectedResponseModel: (json) => json['statusCode'] == 200,
);

View File

@ -1,55 +1,89 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/features/devices/model/room_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/services/api/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart';
class SpacesAPI {
static final HTTPService _httpService = HTTPService();
static Future<List<SpaceModel>> getUnitsByUserId() async {
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
final response = await _httpService.get(
path: "${ApiEndpoints.unitUser}$uuid",
showServerMessage: false,
expectedResponseModel: (json) => SpaceModel.fromJsonList(json),
);
return response;
static Future<List<SpaceModel>> getSpacesByUserId() async {
try {
var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
if (uuid == null) throw Exception("User UUID is missing");
final path = ApiEndpoints.userSpaces.replaceFirst('{userUuid}', uuid);
final response = await _httpService.get(
path: path,
showServerMessage: false,
expectedResponseModel: (json) {
return SpaceModel.fromJsonList(json['data']);
},
);
return response;
} catch (error) {
rethrow; // Rethrow the error to be caught by `fetchUnitsByUserId`
}
}
static Future<List<RoomModel>> getRoomsBySpaceId(String unitId) async {
static Future<List<SubSpaceModel>> getSubSpaceBySpaceId(
String communityId, String spaceId) async {
try {
// Construct the API path
final path = ApiEndpoints.listSubspace
.replaceFirst('{communityUuid}', communityId)
.replaceFirst('{spaceUuid}', spaceId);
final response = await _httpService.get(
path: path,
queryParameters: {"page": 1, "pageSize": 10},
showServerMessage: false,
expectedResponseModel: (json) {
List<SubSpaceModel> rooms = [];
if (json['data'] != null) {
for (var subspace in json['data']) {
rooms.add(SubSpaceModel.fromJson(subspace));
}
} else {
print("Warning: 'data' key is missing or null in response JSON.");
}
return rooms;
},
);
return response;
} catch (error, stackTrace) {
return []; // Return an empty list if there's an error
}
}
static Future<String> generateInvitationCode(
String unitId, String communityId) async {
final response = await _httpService.get(
path: "${ApiEndpoints.unitChild}$unitId",
queryParameters: {"page": 1, "pageSize": 10},
path: ApiEndpoints.invitationCode
.replaceAll('{unitUuid}', unitId)
.replaceAll('{communityUuid}', communityId),
showServerMessage: false,
expectedResponseModel: (json) {
List<RoomModel> rooms = [];
for (var room in json['children']) {
rooms.add(RoomModel.fromJson(room));
if (json != null && json['data'] != null) {
return json['data']['invitationCode'];
} else {
throw Exception('Data field is null');
}
return rooms;
},
);
return response;
}
static Future<String> generateInvitationCode(
String unitId,
) async {
final response = await _httpService.get(
path: ApiEndpoints.invitationCode.replaceAll('{unitUuid}', unitId),
showServerMessage: false,
expectedResponseModel: (json) => json['invitationCode'],
);
return response;
}
static Future<bool> joinUnit(
String userId,
Map<String, String> body,
) async {
final response = await _httpService.post(
path: ApiEndpoints.verifyInvitationCode,
path: ApiEndpoints.verifyInvitationCode.replaceAll('{userUuid}', userId),
showServerMessage: false,
body: body,
expectedResponseModel: (json) => json['success'],