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

View File

@ -33,7 +33,7 @@ class GetSpacesError extends HomeError {
class GetSpaceRoomsLoading extends HomeLoading {} class GetSpaceRoomsLoading extends HomeLoading {}
class GetSpaceRoomsSuccess extends HomeSuccess { class GetSpaceRoomsSuccess extends HomeSuccess {
final List<RoomModel> rooms; final List<SubSpaceModel> rooms;
GetSpaceRoomsSuccess(this.rooms); GetSpaceRoomsSuccess(this.rooms);
} }
@ -50,7 +50,7 @@ class SpaceSelected extends HomeState {
} }
class RoomSelected extends HomeState { class RoomSelected extends HomeState {
final RoomModel room; final SubSpaceModel room;
RoomSelected(this.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/features/app_layout/model/community_model.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart'; import 'package:syncrow_app/features/devices/model/subspace_model.dart';
class SpaceModel { class SpaceModel {
final String? id; final String id;
final String? name; final String name;
final SpaceType type; final Community community;
late List<RoomModel>? rooms; late List<SubSpaceModel> subspaces;
SpaceModel({ SpaceModel({
required this.type,
required this.id, required this.id,
required this.name, 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() { Map<String, dynamic> toJson() {
return { return {
'id': id, 'id': id,
'name': name, 'spaceName': name,
'rooms': rooms, '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) { 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( return SpaceModel(
id: json['uuid'], id: id,
name: json['name'], name: name,
type: spaceTypesMap[json['type']]!, community: Community.fromJson(
rooms: [], 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) { 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(); await login();
} }
emit(AuthOtpSuccess()); emit(AuthOtpSuccess());
} else {
emit(AuthLoginError(message: 'Something went wrong'));
} }
// else {
// emit(AuthLoginError(message: 'Something went wrong'));
// }
} catch (failure) { } catch (failure) {
emit(AuthLoginError(message: 'Something went wrong')); emit(AuthErrorStatusWithoutMsg());
//emit(AuthLoginError(message: 'Something went wrong'));
return; return;
} }
} }

View File

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

View File

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

View File

@ -23,14 +23,22 @@ import 'package:syncrow_app/utils/resource_manager/constants.dart';
part 'devices_state.dart'; part 'devices_state.dart';
class DevicesCubit extends Cubit<DevicesState> { 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()) { DevicesCubit._() : super(DevicesInitial()) {
if (HomeCubit.getInstance().selectedSpace != null && final selectedSpace = HomeCubit.getInstance().selectedSpace;
HomeCubit.getInstance().spaces!.isNotEmpty) { final spaces = HomeCubit.getInstance().spaces;
// fetchGroups(HomeCubit.getInstance().selectedSpace!.id!);
for (var room in HomeCubit.getInstance().selectedSpace!.rooms!) { if (selectedSpace != null && spaces != null && spaces.isNotEmpty) {
fetchDevicesByRoomId(room.id!); _initializeDevices(selectedSpace);
}
fetchGroups(HomeCubit.getInstance().selectedSpace?.id ?? '');
} }
} }
@ -87,10 +95,10 @@ class DevicesCubit extends Cubit<DevicesState> {
return const DoorsListView(); return const DoorsListView();
case DeviceType.Curtain: case DeviceType.Curtain:
return const CurtainListView(); return const CurtainListView();
// case DeviceType.ThreeGang: // case DeviceType.ThreeGang:
// return const ThreeGangSwitchesView(); // return const ThreeGangSwitchesView();
// case DeviceType.Gateway: // case DeviceType.Gateway:
// return const GateWayView(); // return const GateWayView();
default: default:
return null; return null;
} }
@ -104,8 +112,8 @@ class DevicesCubit extends Cubit<DevicesState> {
List<DeviceModel> get allDevices { List<DeviceModel> get allDevices {
List<DeviceModel> devices = []; List<DeviceModel> devices = [];
if (HomeCubit.getInstance().selectedSpace != null && if (HomeCubit.getInstance().selectedSpace != null &&
HomeCubit.getInstance().selectedSpace!.rooms != null) { HomeCubit.getInstance().selectedSpace!.subspaces != null) {
for (var room in HomeCubit.getInstance().selectedSpace!.rooms!) { for (var room in HomeCubit.getInstance().selectedSpace!.subspaces!) {
if (room.devices != null) { if (room.devices != null) {
devices.addAll(room.devices!); devices.addAll(room.devices!);
} }
@ -275,7 +283,7 @@ class DevicesCubit extends Cubit<DevicesState> {
deviceId, deviceId,
HomeCubit.getInstance() HomeCubit.getInstance()
.selectedSpace! .selectedSpace!
.rooms! .subspaces!
.indexOf(HomeCubit.getInstance().selectedRoom!), .indexOf(HomeCubit.getInstance().selectedRoom!),
code: control.code); 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; if (roomId == null) return;
emitSafe(GetDevicesLoading()); emitSafe(GetDevicesLoading());
int roomIndex = int roomIndex = HomeCubit.getInstance()
HomeCubit.getInstance().selectedSpace!.rooms!.indexWhere((element) => element.id == roomId); .selectedSpace!
.subspaces!
.indexWhere((element) => element.id == roomId);
try { try {
HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices = HomeCubit.getInstance().selectedSpace!.subspaces![roomIndex].devices =
await DevicesAPI.getDevicesByRoomId(roomId); await DevicesAPI.getDevicesByRoomId(
communityUuid: unit!.community.uuid,
spaceUuid: unit.id,
roomId: roomId);
} catch (e) { } catch (e) {
emitSafe(GetDevicesError(e.toString())); emitSafe(GetDevicesError(e.toString()));
return; return;
} }
final devices = HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices; final devices =
HomeCubit.getInstance().selectedSpace!.subspaces![roomIndex].devices;
emitSafe(GetDevicesSuccess(devices)); emitSafe(GetDevicesSuccess(devices));
//get status for each device //get status for each device
@ -333,7 +347,7 @@ class DevicesCubit extends Cubit<DevicesState> {
emitSafe(GetDeviceStatusLoading(code: code)); emitSafe(GetDeviceStatusLoading(code: code));
int deviceIndex = HomeCubit.getInstance() int deviceIndex = HomeCubit.getInstance()
.selectedSpace! .selectedSpace!
.rooms![roomIndex] .subspaces![roomIndex]
.devices! .devices!
.indexWhere((element) => element.uuid == deviceUuid); .indexWhere((element) => element.uuid == deviceUuid);
List<StatusModel> statuses = []; List<StatusModel> statuses = [];
@ -346,8 +360,11 @@ class DevicesCubit extends Cubit<DevicesState> {
emitSafe(GetDeviceStatusError(e.toString())); emitSafe(GetDeviceStatusError(e.toString()));
return; return;
} }
HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices![deviceIndex].status = HomeCubit.getInstance()
statuses; .selectedSpace!
.subspaces![roomIndex]
.devices![deviceIndex]
.status = statuses;
emitSafe(GetDeviceStatusSuccess(code: code)); emitSafe(GetDeviceStatusSuccess(code: code));
} }
@ -396,8 +413,6 @@ class DevicesCubit extends Cubit<DevicesState> {
// emitSafe(LightBrightnessChanged(value)); // emitSafe(LightBrightnessChanged(value));
// } // }
// } // }
} }
enum LightMode { 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
print("ACsView deviceModel UUID: ${deviceModel?.uuid}");
return BlocProvider( return BlocProvider(
create: (context) => ACsBloc(acId: deviceModel?.uuid ?? '') create: (context) => ACsBloc(acId: deviceModel?.uuid ?? '')
..add(AcsInitial(allAcs: deviceModel != null ? false : true)), ..add(AcsInitial(allAcs: deviceModel != null ? false : true)),
@ -66,12 +68,14 @@ class ACsView extends StatelessWidget {
child: state is AcsLoadingState child: state is AcsLoadingState
? const Center( ? const Center(
child: DefaultContainer( child: DefaultContainer(
width: 50, height: 50, child: CircularProgressIndicator()), width: 50,
height: 50,
child: CircularProgressIndicator()),
) )
: RefreshIndicator( : RefreshIndicator(
onRefresh: () async { onRefresh: () async {
BlocProvider.of<ACsBloc>(context) BlocProvider.of<ACsBloc>(context).add(AcsInitial(
.add(AcsInitial(allAcs: deviceModel != null ? false : true)); allAcs: deviceModel != null ? false : true));
}, },
child: Container( child: Container(
padding: const EdgeInsets.only(top: 40), padding: const EdgeInsets.only(top: 40),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,12 +17,15 @@ class ManageUnitBloc extends Bloc<ManageUnitEvent, ManageUnitState> {
on<FetchDevicesByRoomIdEvent>(_fetchDevicesByRoomId); on<FetchDevicesByRoomIdEvent>(_fetchDevicesByRoomId);
on<AssignRoomEvent>(_assignDevice); on<AssignRoomEvent>(_assignDevice);
on<AddNewRoom>(_addNewRoom); on<AddNewRoom>(_addNewRoom);
on<UnassignRoomEvent>(_unassignDevice);
} }
void _fetchRoomsAndDevices(FetchRoomsEvent event, Emitter<ManageUnitState> emit) async { void _fetchRoomsAndDevices(
FetchRoomsEvent event, Emitter<ManageUnitState> emit) async {
try { try {
emit(LoadingState()); 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)); emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList));
} catch (e) { } catch (e) {
emit(const ErrorState(message: 'Something went wrong')); 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 { try {
Map<String, bool> roomDevicesId = {}; Map<String, bool> roomDevicesId = {};
emit(LoadingState()); 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(); allDevices = await HomeManagementAPI.fetchDevicesByUserId();
List<String> allDevicesIds = []; 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 { try {
Map<String, bool> roomDevicesId = {}; Map<String, bool> roomDevicesId = {};
emit(LoadingState()); emit(LoadingState());
Map<String, String> body = {"deviceUuid": event.deviceId, "roomUuid": event.roomId};
await HomeManagementAPI.assignDeviceToRoom(body); await HomeManagementAPI.assignDeviceToRoom(
final devicesList = await DevicesAPI.getDevicesByRoomId(event.roomId); 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 = []; 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 { _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 { try {
emit(LoadingState()); 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'] != '') { 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(); allDevices = await HomeManagementAPI.fetchDevicesByUserId();
emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList)); emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList));
await HomeCubit.getInstance().fetchUnitsByUserId(); await HomeCubit.getInstance().fetchUnitsByUserId();

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
abstract class ManageUnitEvent extends Equatable { abstract class ManageUnitEvent extends Equatable {
const ManageUnitEvent(); const ManageUnitEvent();
@ -12,39 +13,54 @@ class InitialEvent extends ManageUnitEvent {}
class LoadingEvent extends ManageUnitEvent {} class LoadingEvent extends ManageUnitEvent {}
class FetchRoomsEvent extends ManageUnitEvent { class FetchRoomsEvent extends ManageUnitEvent {
final String unitId; final SpaceModel unit;
const FetchRoomsEvent({required this.unitId}); const FetchRoomsEvent({required this.unit});
@override @override
List<Object> get props => [unitId]; List<Object> get props => [unit];
} }
class FetchDevicesByRoomIdEvent extends ManageUnitEvent { class FetchDevicesByRoomIdEvent extends ManageUnitEvent {
final String roomId; final String roomId;
final SpaceModel unit;
const FetchDevicesByRoomIdEvent({required this.roomId}); const FetchDevicesByRoomIdEvent({required this.roomId, required this.unit});
@override @override
List<Object> get props => [roomId]; List<Object> get props => [roomId, unit];
} }
class AddNewRoom extends ManageUnitEvent { class AddNewRoom extends ManageUnitEvent {
final String roomName; 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 @override
List<Object> get props => [roomName, unitId]; List<Object> get props => [roomName, unit];
} }
class AssignRoomEvent extends ManageUnitEvent { class AssignRoomEvent extends ManageUnitEvent {
final String roomId; final String roomId;
final String deviceId; 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 @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:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_model.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/room_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
abstract class ManageUnitState extends Equatable { abstract class ManageUnitState extends Equatable {
const ManageUnitState(); const ManageUnitState();
@ -14,7 +15,7 @@ class InitialState extends ManageUnitState {}
class LoadingState extends ManageUnitState {} class LoadingState extends ManageUnitState {}
class FetchRoomsState extends ManageUnitState { class FetchRoomsState extends ManageUnitState {
final List<RoomModel> roomsList; final List<SubSpaceModel> roomsList;
final List<DeviceModel> devicesList; final List<DeviceModel> devicesList;
const FetchRoomsState({required this.devicesList, required this.roomsList}); const FetchRoomsState({required this.devicesList, required this.roomsList});

View File

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

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.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_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_event.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_state.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 { class AssignDeviceView extends StatelessWidget {
final String unitId; final String unitId;
final String roomId; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => ManageUnitBloc()..add(FetchDevicesByRoomIdEvent(roomId: roomId)), create: (context) => ManageUnitBloc()
child: BlocConsumer<ManageUnitBloc, ManageUnitState>(listener: (context, state) { ..add(FetchDevicesByRoomIdEvent(roomId: roomId, unit: unit)),
child: BlocConsumer<ManageUnitBloc, ManageUnitState>(
listener: (context, state) {
if (state is FetchDeviceByRoomIdState) { if (state is FetchDeviceByRoomIdState) {
if (state.allDevices.isEmpty) { 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(); Navigator.of(context).pop();
} }
} }
@ -34,7 +43,8 @@ class AssignDeviceView extends StatelessWidget {
width: MediaQuery.sizeOf(context).width, width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height, height: MediaQuery.sizeOf(context).height,
child: GridView.builder( child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
crossAxisSpacing: 10.0, crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0, mainAxisSpacing: 10.0,
@ -52,11 +62,14 @@ class AssignDeviceView extends StatelessWidget {
), ),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
mainAxisAlignment: MainAxisAlignment.spaceBetween, CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
SvgPicture.asset( SvgPicture.asset(
state.allDevices[index].icon!, state.allDevices[index].icon!,
@ -64,19 +77,42 @@ class AssignDeviceView extends StatelessWidget {
), ),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
if (state.roomDevicesId[ bool isAssigned =
state.allDevices[index].uuid!] ?? state.roomDevicesId[state
false == false) { .allDevices[index]
BlocProvider.of<ManageUnitBloc>(context).add( .uuid!] ??
AssignRoomEvent( false;
deviceId: if (isAssigned) {
state.allDevices[index].uuid ?? '', 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)); roomId: roomId));
} }
}, },
child: SvgPicture.asset( child: SvgPicture.asset(
state.roomDevicesId[ state.roomDevicesId[state
state.allDevices[index].uuid!] ?? .allDevices[index]
.uuid!] ??
false false
? Assets.blueCheckboxIcon ? Assets.blueCheckboxIcon
: Assets.emptyCheckboxIcon, : Assets.emptyCheckboxIcon,

View File

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

View File

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

View File

@ -60,7 +60,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
String selectedIcon = ''; String selectedIcon = '';
bool showInDeviceScreen = false; bool showInDeviceScreen = false;
FutureOr<void> _onAddSceneTask(AddTaskEvent event, Emitter<CreateSceneState> emit) { FutureOr<void> _onAddSceneTask(
AddTaskEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading()); emit(CreateSceneLoading());
if (event.isAutomation == true) { if (event.isAutomation == true) {
final copyList = List<SceneStaticFunction>.from(automationTempTasksList); 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()); emit(CreateSceneLoading());
bool updated = false; 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()); emit(CreateSceneLoading());
bool updated = false; bool updated = false;
for (var element in automationTempTasksList) { for (var element in automationTempTasksList) {
@ -202,8 +205,10 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
], ],
comparator: automationComparatorValues[element.code], comparator: automationComparatorValues[element.code],
); );
automationTempTasksList[automationTempTasksList.indexOf(element)] = updatedElement; automationTempTasksList[automationTempTasksList.indexOf(element)] =
automationSelectedValues[updatedElement.code] = event.deviceControlModel.value; updatedElement;
automationSelectedValues[updatedElement.code] =
event.deviceControlModel.value;
updated = true; updated = true;
break; break;
} }
@ -223,10 +228,12 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
icon: '', icon: '',
), ),
], ],
comparator: automationComparatorValues[event.deviceControlModel.code] ?? '==', comparator:
automationComparatorValues[event.deviceControlModel.code] ?? '==',
); );
automationTempTasksList.add(newElement); automationTempTasksList.add(newElement);
automationSelectedValues[newElement.code] = event.deviceControlModel.value; automationSelectedValues[newElement.code] =
event.deviceControlModel.value;
} }
emit(AddSceneTask( emit(AddSceneTask(
tasksList: tasksList, 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) { if (event.isAutomation == true) {
automationSelectedValues[event.code] = event.value; automationSelectedValues[event.code] = event.value;
automationComparatorValues[event.code] = event.comparator ?? '=='; 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()); emit(CreateSceneLoading());
if (event.isAutomation == true) { if (event.isAutomation == true) {
for (var element in automationTasksList) { for (var element in automationTasksList) {
@ -345,7 +354,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
: await SceneApi.createScene(event.createSceneModel!); : await SceneApi.createScene(event.createSceneModel!);
} else if (event.createAutomationModel != null) { } else if (event.createAutomationModel != null) {
response = event.updateScene response = event.updateScene
? await SceneApi.updateAutomation(event.createAutomationModel!, event.sceneId) ? await SceneApi.updateAutomation(
event.createAutomationModel!, event.sceneId)
: await SceneApi.createAutomation(event.createAutomationModel!); : await SceneApi.createAutomation(event.createAutomationModel!);
} }
@ -359,12 +369,14 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
automationComparatorValues.clear(); automationComparatorValues.clear();
selectedIcon = ''; selectedIcon = '';
showInDeviceScreen = false; 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; sceneType = CreateSceneEnum.none;
conditionRule = 'or'; conditionRule = 'or';
emit(const CreateSceneWithTasks(success: true)); emit(const CreateSceneWithTasks(success: true));
CustomSnackBar.greenSnackBar( CustomSnackBar.greenSnackBar(event.updateScene
event.updateScene ? 'Scene updated successfully' : 'Scene created successfully'); ? 'Scene updated successfully'
: 'Scene created successfully');
} else { } else {
emit(const CreateSceneError(message: 'Something went wrong')); 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()); emit(CreateSceneLoading());
automationTasksList.clear(); automationTasksList.clear();
tasksList.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()); emit(CreateSceneLoading());
selectedIcon = ''; selectedIcon = '';
showInDeviceScreen = false; showInDeviceScreen = false;
@ -416,7 +430,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
automationComparatorValues.clear(); automationComparatorValues.clear();
selectedIcon = ''; selectedIcon = '';
showInDeviceScreen = false; 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; sceneType = CreateSceneEnum.none;
conditionRule = 'or'; conditionRule = 'or';
@ -425,10 +440,14 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
: await SceneApi.getSceneDetails(event.sceneId); : await SceneApi.getSceneDetails(event.sceneId);
if (response.id.isNotEmpty) { if (response.id.isNotEmpty) {
if (event.isAutomation) { if (event.isAutomation) {
automationTasksList = List<SceneStaticFunction>.from(getTaskListFunctionsFromApi( automationTasksList = List<SceneStaticFunction>.from(
actions: [], isAutomation: true, conditions: response.conditions)); getTaskListFunctionsFromApi(
actions: [],
isAutomation: true,
conditions: response.conditions));
tasksList = List<SceneStaticFunction>.from( tasksList = List<SceneStaticFunction>.from(
getTaskListFunctionsFromApi(actions: response.actions, isAutomation: false)); getTaskListFunctionsFromApi(
actions: response.actions, isAutomation: false));
conditionRule = response.decisionExpr ?? conditionRule; conditionRule = response.decisionExpr ?? conditionRule;
@ -441,11 +460,13 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
: EffectiveTime(start: '00:00', end: '23:59', loops: '1111111'); : EffectiveTime(start: '00:00', end: '23:59', loops: '1111111');
// Set the days directly from the API response // 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')); .add(SetDays(response.effectiveTime?.loops ?? '1111111'));
// Set Custom Time and reset days first // 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)); .add(SetCustomTime(effectiveTime!.start, effectiveTime!.end));
emit(AddSceneTask( emit(AddSceneTask(
@ -457,7 +478,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
showInDevice: showInDeviceScreen)); showInDevice: showInDeviceScreen));
} else { } else {
tasksList = List<SceneStaticFunction>.from( tasksList = List<SceneStaticFunction>.from(
getTaskListFunctionsFromApi(actions: response.actions, isAutomation: false)); getTaskListFunctionsFromApi(
actions: response.actions, isAutomation: false));
selectedIcon = response.icon!; selectedIcon = response.icon!;
showInDeviceScreen = response.showInDevice!; showInDeviceScreen = response.showInDevice!;
emit(AddSceneTask( 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()); emit(CreateSceneLoading());
try { try {
iconModelList = await SceneApi.getIcon(); 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 { try {
if (event.confirmSelection) { if (event.confirmSelection) {
selectedIcon = event.iconId; selectedIcon = event.iconId;
@ -531,7 +555,8 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
return days[index]; return days[index];
} }
FutureOr<void> _clearTempTaskList(ClearTempTaskListEvent event, Emitter<CreateSceneState> emit) { FutureOr<void> _clearTempTaskList(
ClearTempTaskListEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading()); emit(CreateSceneLoading());
if (event.isAutomation == true) { if (event.isAutomation == true) {
automationTempTasksList.clear(); 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()); emit(DeleteSceneLoading());
try { try {
final response = sceneType.name == CreateSceneEnum.deviceStatusChanges.name final response =
? await SceneApi.deleteAutomation(automationId: event.sceneId, unitUuid: event.unitUuid) sceneType.name == CreateSceneEnum.deviceStatusChanges.name
: await SceneApi.deleteScene(sceneId: event.sceneId, unitUuid: event.unitUuid); ? await SceneApi.deleteAutomation(
automationId: event.sceneId, unitUuid: event.unitUuid)
: await SceneApi.deleteScene(
sceneId: event.sceneId,
);
if (response == true) { if (response == true) {
emit(const DeleteSceneSuccess(true)); emit(const DeleteSceneSuccess(true));
} else { } 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()); emit(CreateSceneLoading());
if (event.isAutomation == true) { if (event.isAutomation == true) {
for (var i = 0; i < automationTasksList.length; i++) { 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()); emit(CreateSceneInitial());
if (event.condition.contains('any')) { if (event.condition.contains('any')) {
conditionRule = 'or'; 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()); // emit(CreateSceneInitial());
if (event.type == CreateSceneEnum.tabToRun) { if (event.type == CreateSceneEnum.tabToRun) {

View File

@ -24,7 +24,9 @@ class SceneBloc extends Bloc<SceneEvent, SceneState> {
try { try {
if (event.unitId.isNotEmpty) { 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)); emit(SceneLoaded(scenes, automationList));
} else { } else {
emit(const SceneError(message: 'Unit ID is empty')); 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()); emit(SceneLoading());
try { 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; final currentState = state;
if (currentState is SceneLoaded) { if (currentState is SceneLoaded) {
emit(SceneLoaded( emit(SceneLoaded(
@ -76,8 +80,9 @@ class SceneBloc extends Bloc<SceneEvent, SceneState> {
UpdateAutomationStatus event, Emitter<SceneState> emit) async { UpdateAutomationStatus event, Emitter<SceneState> emit) async {
final currentState = state; final currentState = state;
if (currentState is SceneLoaded) { if (currentState is SceneLoaded) {
final newLoadingStates = Map<String, bool>.from(currentState.loadingStates) final newLoadingStates =
..[event.automationId] = true; Map<String, bool>.from(currentState.loadingStates)
..[event.automationId] = true;
emit(SceneLoaded( emit(SceneLoaded(
currentState.scenes, currentState.scenes,
@ -86,11 +91,11 @@ class SceneBloc extends Bloc<SceneEvent, SceneState> {
)); ));
try { try {
final success = final success = await SceneApi.updateAutomationStatus(
await SceneApi.updateAutomationStatus(event.automationId, event.automationStatusUpdate); event.automationId, event.automationStatusUpdate);
if (success) { if (success) {
automationList = automationList = await SceneApi.getAutomationByUnitId(
await SceneApi.getAutomationByUnitId(event.automationStatusUpdate.unitUuid); event.automationStatusUpdate.spaceUuid);
newLoadingStates[event.automationId] = false; newLoadingStates[event.automationId] = false;
emit(SceneLoaded( emit(SceneLoaded(
currentState.scenes, currentState.scenes,

View File

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

View File

@ -17,7 +17,7 @@ class TabBarBloc extends Bloc<TabBarEvent, TabBarState> {
if (event.roomId == "-1") { if (event.roomId == "-1") {
deviceManagerBloc.add(FetchAllDevices()); deviceManagerBloc.add(FetchAllDevices());
} else { } else {
deviceManagerBloc.add(FetchDevicesByRoomId(event.roomId)); deviceManagerBloc.add(FetchDevicesByRoomId(event.roomId,event.unit));
} }
emit(TabSelected( emit(TabSelected(
roomId: event.roomId, selectedTabIndex: event.selectedIndex)); 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 { abstract class TabBarEvent {
const TabBarEvent(); const TabBarEvent();
} }
@ -5,5 +7,7 @@ abstract class TabBarEvent {
class TabChanged extends TabBarEvent { class TabChanged extends TabBarEvent {
final int selectedIndex; final int selectedIndex;
final String roomId; 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 { } else {
final createSceneModel = CreateSceneModel( final createSceneModel = CreateSceneModel(
unitUuid: HomeCubit.getInstance().selectedSpace!.id ?? '', spaceUuid: HomeCubit.getInstance().selectedSpace!.id ?? '',
iconId: sceneBloc.selectedIcon, iconId: sceneBloc.selectedIcon,
showInDevice: sceneBloc.showInDeviceScreen, showInDevice: sceneBloc.showInDeviceScreen,
sceneName: sceneName.text, sceneName: sceneName.text,

View File

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

View File

@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
class CreateSceneModel { class CreateSceneModel {
String unitUuid; String spaceUuid;
String iconId; String iconId;
bool showInDevice; bool showInDevice;
String sceneName; String sceneName;
@ -11,7 +11,7 @@ class CreateSceneModel {
List<CreateSceneAction> actions; List<CreateSceneAction> actions;
CreateSceneModel({ CreateSceneModel({
required this.unitUuid, required this.spaceUuid,
required this.iconId, required this.iconId,
required this.showInDevice, required this.showInDevice,
required this.sceneName, required this.sceneName,
@ -20,15 +20,16 @@ class CreateSceneModel {
}); });
CreateSceneModel copyWith({ CreateSceneModel copyWith({
String? unitUuid, String? spaceUuid,
String? iconId, String? iconId,
bool? showInDevice, bool? showInDevice,
String? sceneName, String? sceneName,
String? decisionExpr, String? decisionExpr,
List<CreateSceneAction>? actions, List<CreateSceneAction>? actions,
bool? showInHomePage,
}) { }) {
return CreateSceneModel( return CreateSceneModel(
unitUuid: unitUuid ?? this.unitUuid, spaceUuid: spaceUuid ?? this.spaceUuid,
iconId: iconId ?? this.iconId, iconId: iconId ?? this.iconId,
showInDevice: showInDevice ?? this.showInDevice, showInDevice: showInDevice ?? this.showInDevice,
sceneName: sceneName ?? this.sceneName, sceneName: sceneName ?? this.sceneName,
@ -39,7 +40,7 @@ class CreateSceneModel {
Map<String, dynamic> toMap([String? sceneId]) { Map<String, dynamic> toMap([String? sceneId]) {
return { return {
if (sceneId == null) 'unitUuid': unitUuid, if (sceneId == null) 'spaceUuid': spaceUuid,
if (iconId.isNotEmpty) 'iconUuid': iconId, if (iconId.isNotEmpty) 'iconUuid': iconId,
'showInHomePage': showInDevice, 'showInHomePage': showInDevice,
'sceneName': sceneName, 'sceneName': sceneName,
@ -50,23 +51,24 @@ class CreateSceneModel {
factory CreateSceneModel.fromMap(Map<String, dynamic> map) { factory CreateSceneModel.fromMap(Map<String, dynamic> map) {
return CreateSceneModel( return CreateSceneModel(
unitUuid: map['unitUuid'] ?? '', spaceUuid: map['spaceUuid'] ?? '',
iconId: map['iconUuid'] ?? '',
showInDevice: map['showInHomePage'] ?? false, showInDevice: map['showInHomePage'] ?? false,
iconId: map['iconUuid'] ?? '',
sceneName: map['sceneName'] ?? '', sceneName: map['sceneName'] ?? '',
decisionExpr: map['decisionExpr'] ?? '', decisionExpr: map['decisionExpr'] ?? '',
actions: actions: List<CreateSceneAction>.from(
List<CreateSceneAction>.from(map['actions']?.map((x) => CreateSceneAction.fromMap(x))), map['actions']?.map((x) => CreateSceneAction.fromMap(x))),
); );
} }
String toJson([String? sceneId]) => json.encode(toMap(sceneId)); 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 @override
String toString() { String toString() {
return 'CreateSceneModel(unitUuid: $unitUuid, sceneName: $sceneName, decisionExpr: $decisionExpr, actions: $actions)'; return 'CreateSceneModel(unitUuid: $spaceUuid, sceneName: $sceneName, decisionExpr: $decisionExpr, actions: $actions)';
} }
@override @override
@ -74,7 +76,7 @@ class CreateSceneModel {
if (identical(this, other)) return true; if (identical(this, other)) return true;
return other is CreateSceneModel && return other is CreateSceneModel &&
other.unitUuid == unitUuid && other.spaceUuid == spaceUuid &&
other.iconId == iconId && other.iconId == iconId &&
other.showInDevice == showInDevice && other.showInDevice == showInDevice &&
other.sceneName == sceneName && other.sceneName == sceneName &&
@ -84,7 +86,10 @@ class CreateSceneModel {
@override @override
int get hashCode { 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( return CreateSceneAction(
entityId: map['entityId'] ?? '', entityId: map['entityId'] ?? '',
actionExecutor: map['actionExecutor'] ?? '', actionExecutor: map['actionExecutor'] ?? '',
executorProperty: CreateSceneExecutorProperty.fromMap(map['executorProperty']), executorProperty:
CreateSceneExecutorProperty.fromMap(map['executorProperty']),
); );
} }
@ -154,7 +160,8 @@ class CreateSceneAction {
} }
@override @override
int get hashCode => entityId.hashCode ^ actionExecutor.hashCode ^ executorProperty.hashCode; int get hashCode =>
entityId.hashCode ^ actionExecutor.hashCode ^ executorProperty.hashCode;
} }
class CreateSceneExecutorProperty { class CreateSceneExecutorProperty {
@ -218,5 +225,6 @@ class CreateSceneExecutorProperty {
} }
@override @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, 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()); String toRawJson() => json.encode(toJson());
factory SceneDetailsModel.fromJson(Map<String, dynamic> json) => SceneDetailsModel( factory SceneDetailsModel.fromJson(Map<String, dynamic> json) =>
id: json["id"], SceneDetailsModel(
name: json["name"], id: json["uuid"] ?? json["id"],
status: json["status"], name: json["name"],
type: json["type"], status: json["status"],
actions: (json["actions"] as List) type: json["type"],
.map((x) => Action.fromJson(x)) actions: (json["actions"] as List)
.where((x) => x != null) .map((x) => Action.fromJson(x))
.toList() .where((x) => x != null)
.cast<Action>(), .toList()
conditions: json["conditions"] != null .cast<Action>(),
? (json["conditions"] as List).map((x) => Condition.fromJson(x)).toList() conditions: json["conditions"] != null
: null, ? (json["conditions"] as List)
decisionExpr: json["decisionExpr"], .map((x) => Condition.fromJson(x))
effectiveTime: .toList()
json["effectiveTime"] != null ? EffectiveTime.fromJson(json["effectiveTime"]) : null, : null,
icon: json["iconUuid"] != null ? json["iconUuid"] ?? '' : '', decisionExpr: json["decisionExpr"],
showInDevice: json['showInHome'] != null ? json['showInHome'] ?? false : false); 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() => { Map<String, dynamic> toJson() => {
"id": id, "id": id,
@ -54,8 +60,9 @@ class SceneDetailsModel {
"status": status, "status": status,
"type": type, "type": type,
"actions": List<dynamic>.from(actions.map((x) => x.toJson())), "actions": List<dynamic>.from(actions.map((x) => x.toJson())),
"conditions": "conditions": conditions != null
conditions != null ? List<dynamic>.from(conditions!.map((x) => x.toJson())) : null, ? List<dynamic>.from(conditions!.map((x) => x.toJson()))
: null,
"decisionExpr": decisionExpr, "decisionExpr": decisionExpr,
"effectiveTime": effectiveTime?.toJson(), "effectiveTime": effectiveTime?.toJson(),
}; };
@ -116,7 +123,8 @@ class ExecutorProperty {
this.delaySeconds, this.delaySeconds,
}); });
factory ExecutorProperty.fromJson(Map<String, dynamic> json) => ExecutorProperty( factory ExecutorProperty.fromJson(Map<String, dynamic> json) =>
ExecutorProperty(
functionCode: json["functionCode"] ?? '', functionCode: json["functionCode"] ?? '',
functionValue: json["functionValue"] ?? '', functionValue: json["functionValue"] ?? '',
delaySeconds: json["delaySeconds"] ?? 0, delaySeconds: json["delaySeconds"] ?? 0,
@ -142,7 +150,8 @@ class Condition {
required this.expr, 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()); String toRawJson() => json.encode(toJson());
@ -200,7 +209,8 @@ class EffectiveTime {
required this.loops, 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()); String toRawJson() => json.encode(toJson());

View File

@ -3,32 +3,40 @@ import 'dart:typed_data';
class ScenesModel { class ScenesModel {
final String id; final String id;
final String? sceneTuyaId;
final String name; final String name;
final String status; final String status;
final String type; final String type;
final String icon; final String? icon;
ScenesModel( ScenesModel(
{required this.id, {required this.id,
this.sceneTuyaId,
required this.name, required this.name,
required this.status, required this.status,
required this.type, 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()); String toRawJson() => json.encode(toJson());
Uint8List get iconInBytes => base64Decode(icon); 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"] ?? '');
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() => { Map<String, dynamic> toJson() => {
"id": id, "id": id,
"sceneTuyaId": sceneTuyaId ?? '',
"name": name, "name": name,
"status": status, "status": status,
"type": type, "type": type,

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@ class DeleteRoutineButton extends StatelessWidget {
if (state.success) { if (state.success) {
navigateToRoute(context, Routes.homeRoute); navigateToRoute(context, Routes.homeRoute);
BlocProvider.of<SceneBloc>(context) BlocProvider.of<SceneBloc>(context)
.add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!)); .add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!,HomeCubit.getInstance().selectedSpace!));
BlocProvider.of<SceneBloc>(context).add( BlocProvider.of<SceneBloc>(context).add(
LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!)); 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_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_state.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/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_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.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'; import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart';
@ -24,7 +25,7 @@ class SceneDevicesBody extends StatelessWidget {
}) : _tabController = tabController; }) : _tabController = tabController;
final TabController _tabController; final TabController _tabController;
final List<RoomModel>? rooms; final List<SubSpaceModel>? rooms;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -76,7 +77,7 @@ class SceneDevicesBody extends StatelessWidget {
} }
Widget _buildRoomTab( Widget _buildRoomTab(
RoomModel room, BuildContext context, bool isAutomationDeviceStatus) { SubSpaceModel room, BuildContext context, bool isAutomationDeviceStatus) {
return BlocBuilder<DeviceManagerBloc, DeviceManagerState>( return BlocBuilder<DeviceManagerBloc, DeviceManagerState>(
builder: (context, state) { builder: (context, state) {
if (state.loading && state.devices == null) { if (state.loading && state.devices == null) {

View File

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

View File

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

View File

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

View File

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

View File

@ -65,6 +65,7 @@ class DevicesAPI {
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) => DevicesCategoryModel.fromJsonList(json), expectedResponseModel: (json) => DevicesCategoryModel.fromJsonList(json),
); );
return response; return response;
} }
@ -142,23 +143,41 @@ class DevicesAPI {
return response; return response;
} }
static Future<List<DeviceModel>> getDevicesByRoomId(String roomId) async { static Future<List<DeviceModel>> getDevicesByRoomId({
final response = await _httpService.get( required String communityUuid,
path: ApiEndpoints.deviceByRoom, required String spaceUuid,
queryParameters: {"roomUuid": roomId}, required String roomId,
showServerMessage: false, }) async {
expectedResponseModel: (json) { try {
if (json == null || json.isEmpty || json == []) { final String path = ApiEndpoints.deviceByRoom
return <DeviceModel>[]; .replaceAll('{communityUuid}', communityUuid)
} .replaceAll('{spaceUuid}', spaceUuid)
List<DeviceModel> devices = []; .replaceAll('{subSpaceUuid}', roomId);
for (var device in json) {
devices.add(DeviceModel.fromJson(device)); final response = await _httpService.get(
} path: path,
return devices; showServerMessage: false,
}, expectedResponseModel: (json) {
); final data = json['data'];
return response;
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( static Future<List<DeviceModel>> getDevicesByGatewayId(

View File

@ -4,7 +4,8 @@ import 'package:syncrow_app/services/api/http_service.dart';
class HomeCreation { class HomeCreation {
static final HTTPService _httpService = HTTPService(); 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 { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.addCommunity, 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 { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.addCommunityToUser, 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 { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.addBuilding, 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 { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.addBuildingToUser, 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 { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.addFloor, 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 { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.addBuildingToUser, 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 { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.addUnit, 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 { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.addUnitToUser, 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 { try {
final fullPath = ApiEndpoints.addSubSpace
.replaceAll('{communityUuid}', communityId)
.replaceAll('{spaceUuid}', spaceId);
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.addRoom, path: fullPath,
body: body, body: body,
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { 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 { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.addRoomToUser, path: ApiEndpoints.addRoomToUser,

View File

@ -28,24 +28,46 @@ class HomeManagementAPI {
static Future<List<DeviceModel>> fetchDevicesByUnitId() async { static Future<List<DeviceModel>> fetchDevicesByUnitId() async {
List<DeviceModel> list = []; List<DeviceModel> list = [];
await _httpService.get(
path: ApiEndpoints.getDevicesByUnitId.replaceAll( try {
"{unitUuid}", HomeCubit.getInstance().selectedSpace?.id ?? ''), // 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, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
json.forEach((value) { if (json['data'] != null) {
list.add(DeviceModel.fromJson(value)); json['data'].forEach((value) {
}); list.add(DeviceModel.fromJson(value));
}); });
}
},
);
// Debugging: Log successful fetch
} catch (e) {
// Log the error for debugging
}
return list; return list;
} }
static Future<Map<String, dynamic>> assignDeviceToRoom( static Future<Map<String, dynamic>> assignDeviceToRoom(String communityId,
Map<String, String> body) async { String spaceId, String subSpaceId, String deviceId) async {
try { try {
final response = await _httpService.put( final response = await _httpService.post(
path: ApiEndpoints.assignDeviceToRoom, path: ApiEndpoints.assignDeviceToRoom
body: body, .replaceAll('{communityUuid}', communityId)
.replaceAll('{spaceUuid}', spaceId)
.replaceAll('{subSpaceUuid}', subSpaceId)
.replaceAll('{deviceUuid}', deviceId),
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;
}, },
@ -55,4 +77,24 @@ class HomeManagementAPI {
rethrow; 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_automation_model.dart';
import 'package:syncrow_app/features/scene/model/create_scene_model.dart'; import 'package:syncrow_app/features/scene/model/create_scene_model.dart';
import 'package:syncrow_app/features/scene/model/icon_model.dart'; import 'package:syncrow_app/features/scene/model/icon_model.dart';
@ -11,7 +13,8 @@ class SceneApi {
static final HTTPService _httpService = HTTPService(); static final HTTPService _httpService = HTTPService();
//create scene //create scene
static Future<Map<String, dynamic>> createScene(CreateSceneModel createSceneModel) async { static Future<Map<String, dynamic>> createScene(
CreateSceneModel createSceneModel) async {
try { try {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.createScene, path: ApiEndpoints.createScene,
@ -47,15 +50,21 @@ class SceneApi {
//get scene by unit id //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 { try {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.getUnitScenes.replaceAll('{unitUuid}', unitId), path: ApiEndpoints.getUnitScenes
.replaceAll('{spaceUuid}', unitId)
.replaceAll('{communityUuid}', communityId),
queryParameters: {'showInHomePage': showInDevice}, queryParameters: {'showInHomePage': showInDevice},
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
final scenesJson = json['data'] as List;
List<ScenesModel> scenes = []; List<ScenesModel> scenes = [];
for (var scene in json) { for (var scene in scenesJson) {
scenes.add(ScenesModel.fromJson(scene)); scenes.add(ScenesModel.fromJson(scene));
} }
return scenes; return scenes;
@ -102,10 +111,12 @@ class SceneApi {
} }
//automation details //automation details
static Future<SceneDetailsModel> getAutomationDetails(String automationId) async { static Future<SceneDetailsModel> getAutomationDetails(
String automationId) async {
try { try {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.getAutomationDetails.replaceAll('{automationId}', automationId), path: ApiEndpoints.getAutomationDetails
.replaceAll('{automationId}', automationId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) => SceneDetailsModel.fromJson(json), expectedResponseModel: (json) => SceneDetailsModel.fromJson(json),
); );
@ -116,11 +127,12 @@ class SceneApi {
} }
//updateAutomationStatus //updateAutomationStatus
static Future<bool> updateAutomationStatus( static Future<bool> updateAutomationStatus(String automationId,
String automationId, AutomationStatusUpdate createAutomationEnable) async { AutomationStatusUpdate createAutomationEnable) async {
try { try {
final response = await _httpService.put( final response = await _httpService.put(
path: ApiEndpoints.updateAutomationStatus.replaceAll('{automationId}', automationId), path: ApiEndpoints.updateAutomationStatus
.replaceAll('{automationId}', automationId),
body: createAutomationEnable.toMap(), body: createAutomationEnable.toMap(),
expectedResponseModel: (json) => json['success'], expectedResponseModel: (json) => json['success'],
); );
@ -135,7 +147,13 @@ class SceneApi {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId), path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId),
showServerMessage: false, 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; return response;
} catch (e) { } catch (e) {
@ -163,7 +181,8 @@ class SceneApi {
try { try {
final response = await _httpService.put( final response = await _httpService.put(
path: ApiEndpoints.updateScene.replaceAll('{sceneId}', sceneId), path: ApiEndpoints.updateScene.replaceAll('{sceneId}', sceneId),
body: createSceneModel.toJson(sceneId.isNotEmpty == true ? sceneId : null), body: createSceneModel
.toJson(sceneId.isNotEmpty == true ? sceneId : null),
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;
}, },
@ -175,11 +194,14 @@ class SceneApi {
} }
//update automation //update automation
static updateAutomation(CreateAutomationModel createAutomationModel, String automationId) async { static updateAutomation(
CreateAutomationModel createAutomationModel, String automationId) async {
try { try {
final response = await _httpService.put( final response = await _httpService.put(
path: ApiEndpoints.updateAutomation.replaceAll('{automationId}', automationId), path: ApiEndpoints.updateAutomation
body: createAutomationModel.toJson(automationId.isNotEmpty == true ? automationId : null), .replaceAll('{automationId}', automationId),
body: createAutomationModel
.toJson(automationId.isNotEmpty == true ? automationId : null),
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;
}, },
@ -192,12 +214,10 @@ class SceneApi {
//delete Scene //delete Scene
static Future<bool> deleteScene({required String unitUuid, required String sceneId}) async { static Future<bool> deleteScene({required String sceneId}) async {
try { try {
final response = await _httpService.delete( final response = await _httpService.delete(
path: ApiEndpoints.deleteScene path: ApiEndpoints.deleteScene.replaceAll('{sceneId}', sceneId),
.replaceAll('{sceneId}', sceneId)
.replaceAll('{unitUuid}', unitUuid),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) => json['statusCode'] == 200, expectedResponseModel: (json) => json['statusCode'] == 200,
); );
@ -213,8 +233,7 @@ class SceneApi {
try { try {
final response = await _httpService.delete( final response = await _httpService.delete(
path: ApiEndpoints.deleteAutomation path: ApiEndpoints.deleteAutomation
.replaceAll('{automationId}', automationId) .replaceAll('{automationId}', automationId),
.replaceAll('{unitUuid}', unitUuid),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) => json['statusCode'] == 200, expectedResponseModel: (json) => json['statusCode'] == 200,
); );

View File

@ -1,55 +1,89 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; 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/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/auth/model/user_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/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart'; import 'package:syncrow_app/services/api/http_service.dart';
class SpacesAPI { class SpacesAPI {
static final HTTPService _httpService = HTTPService(); static final HTTPService _httpService = HTTPService();
static Future<List<SpaceModel>> getUnitsByUserId() async { static Future<List<SpaceModel>> getSpacesByUserId() async {
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); try {
final response = await _httpService.get( var uuid =
path: "${ApiEndpoints.unitUser}$uuid", await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
showServerMessage: false, if (uuid == null) throw Exception("User UUID is missing");
expectedResponseModel: (json) => SpaceModel.fromJsonList(json),
); final path = ApiEndpoints.userSpaces.replaceFirst('{userUuid}', uuid);
return response; 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( final response = await _httpService.get(
path: "${ApiEndpoints.unitChild}$unitId", path: ApiEndpoints.invitationCode
queryParameters: {"page": 1, "pageSize": 10}, .replaceAll('{unitUuid}', unitId)
.replaceAll('{communityUuid}', communityId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
List<RoomModel> rooms = []; if (json != null && json['data'] != null) {
for (var room in json['children']) { return json['data']['invitationCode'];
rooms.add(RoomModel.fromJson(room)); } else {
throw Exception('Data field is null');
} }
return rooms;
}, },
); );
return response; 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( static Future<bool> joinUnit(
String userId,
Map<String, String> body, Map<String, String> body,
) async { ) async {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.verifyInvitationCode, path: ApiEndpoints.verifyInvitationCode.replaceAll('{userUuid}', userId),
showServerMessage: false, showServerMessage: false,
body: body, body: body,
expectedResponseModel: (json) => json['success'], expectedResponseModel: (json) => json['success'],