// ignore_for_file: constant_identifier_names, unused_import import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/devices/model/device_category_model.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/model/status_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_list_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/lights_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/three_gang/three_gang_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/doors_list_view.dart'; import 'package:syncrow_app/services/api/devices_api.dart'; import 'package:syncrow_app/services/api/home_management_api.dart'; import 'package:syncrow_app/services/api/network_exception.dart'; import 'package:syncrow_app/services/api/spaces_api.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; part 'devices_state.dart'; class DevicesCubit extends Cubit { Future _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); await fetchAllDevices(selectedSpace); } DevicesCubit._() : super(DevicesInitial()) { final selectedSpace = HomeCubit.getInstance().selectedSpace; final spaces = HomeCubit.getInstance().spaces; if (selectedSpace != null && spaces != null && spaces.isNotEmpty) { _initializeDevices(selectedSpace); } } static DevicesCubit? _instance; static DevicesCubit getInstance() { return _instance ??= DevicesCubit._(); } @override Future close() { _instance = null; return super.close(); } void emitSafe(DevicesState newState) { final cubit = this; if (!cubit.isClosed) { cubit.emit(newState); } } static DevicesCubit get(context) => BlocProvider.of(context); List? allCategories; selectCategory(int index) { for (var i = 0; i < allCategories!.length; i++) { if (i == index) { allCategories![i].isSelected = true; } else { allCategories![i].isSelected = false; } } emitSafe(DevicesCategoryChanged()); } unselectAllCategories() { for (var category in allCategories!) { category.isSelected = false; } emitSafe(DevicesCategoryChanged()); } Widget? get chosenCategoryView { if (allCategories != null) { for (var category in allCategories!) { if (category.isSelected) { switch (category.type) { case DeviceType.AC: return const ACsView(); case DeviceType.LightBulb: return const LightsView(); case DeviceType.DoorLock: return const DoorsListView(); case DeviceType.Curtain: return const CurtainListView(); // case DeviceType.ThreeGang: // return const ThreeGangSwitchesView(); // case DeviceType.Gateway: // return const GateWayView(); default: return null; } } } } return null; } // Getter to retrieve all devices from HomeCubit // DevicesCategoryModel? get chosenCategory { // for (var category in allCategories!) { // if (category.isSelected) { // return category; // } // } // return null; // } selectDevice(DeviceModel device) { for (var category in allCategories!) { if (category.devices != null) { for (var device in category.devices!) { if (device.isSelected) { category.isSelected = false; emitSafe(DeviceSelected()); return; } } } } device.isSelected = !device.isSelected; emitSafe(DeviceSelected()); } DeviceModel? getSelectedDevice() { for (var category in allCategories!) { if (category.devices != null) { for (var device in category.devices!) { if (device.isSelected) { return device; } } } } return null; } changeCategorySwitchValue(DevicesCategoryModel category) { if (category.devicesStatus != null) { category.devicesStatus = !category.devicesStatus!; if (category.devices != null) { for (var device in category.devices!) { device.isOnline = category.devicesStatus; } } } else { category.devicesStatus = true; if (category.devices != null) { for (var device in category.devices!) { device.isOnline = true; } } } updateDevicesStatus(category); emitSafe(CategorySwitchChanged()); } turnOnOffDevice(DeviceModel device) { device.isOnline = !device.isOnline!; DevicesCategoryModel category = allCategories!.firstWhere((category) { if (category.devices != null) { return category.devices!.contains(device); } else { return false; } }); updateDevicesStatus(category); emitSafe(DeviceSwitchChanged()); } updateDevicesStatus(DevicesCategoryModel category) { if (category.devices != null) { if (category.devices!.isNotEmpty) { bool? tempStatus = category.devices![0].isOnline; for (var ac in category.devices!) { //check if there any ac have a different status than the initial ==> turn off the universal switch if (ac.isOnline != tempStatus) { category.devicesStatus = null; emitSafe(DeviceSwitchChanged()); return; } category.devicesStatus = tempStatus; emitSafe(DeviceSwitchChanged()); } } else { category.devicesStatus = null; emitSafe(DeviceSwitchChanged()); } } } turnAllDevicesOff(DevicesCategoryModel category) { if (category.devices != null) { if (category.devices!.isNotEmpty) { for (var device in category.devices!) { device.isOnline = false; } changeCategorySwitchValue(category); updateDevicesStatus(category); emitSafe(CategorySwitchChanged()); } } } turnAllDevicesOn(DevicesCategoryModel category) { if (category.devices != null) { if (category.devices!.isNotEmpty) { for (var device in category.devices!) { device.isOnline = true; } changeCategorySwitchValue(category); updateDevicesStatus(category); emitSafe(CategorySwitchChanged()); } } } areAllDevicesOff(DevicesCategoryModel category) { if (category.devices != null) { for (var device in category.devices!) { if (device.isOnline ?? false) { category.devicesStatus = false; emitSafe(CategorySwitchChanged()); return; } } } } clearCategoriesSelection(BuildContext context) { for (var category in allCategories!) { category.isSelected = false; if (category.devices != null) { for (var device in category.devices!) { device.isSelected = false; } } } Navigator.popUntil(context, (route) => route.isFirst); emitSafe(DevicesCategoryChanged()); } ///////////////////////// API CALLS ////////////////////////// // deviceControl(DeviceControlModel control, String deviceId) async { // emitSafe(DeviceControlLoading( // code: control.code, // )); // try { // var response = await DevicesAPI.controlDevice(control, deviceId); // if (response['success'] ?? false) { // emitSafe(DeviceControlSuccess(code: control.code)); // //this delay is to give tuya server time to update the status // // Future.delayed(const Duration(milliseconds: 400), () { // fetchDevicesStatues( // deviceId, // HomeCubit.getInstance() // .selectedSpace! // .subspaces // .indexOf(HomeCubit.getInstance().selectedRoom!), // code: control.code); // // }); // } else { // emitSafe(DeviceControlError('Failed to control the device')); // } // } catch (failure) { // emitSafe(DeviceControlError(failure.toString())); // return; // } // } fetchGroups(String spaceId) async { emitSafe(GetDevicesLoading()); try { allCategories = await DevicesAPI.fetchGroups(spaceId); } catch (e) { emitSafe(DevicesCategoriesError(e.toString())); return; } if (allCategories!.isNotEmpty && allCategories != null) { emitSafe(DevicesCategoriesSuccess()); } else { emitSafe(DevicesCategoriesError('No Groups found')); } } fetchDevicesByRoomId(SpaceModel? unit, String? roomId) async { if (roomId == null) return; emitSafe(GetDevicesLoading()); int roomIndex = HomeCubit.getInstance() .selectedSpace! .subspaces .indexWhere((element) => element.id == roomId); try { HomeCubit.getInstance().selectedSpace!.subspaces[roomIndex].devices = await DevicesAPI.getDevicesByRoomId( communityUuid: unit!.community.uuid, spaceUuid: unit.id, roomId: roomId); } catch (e) { emitSafe(GetDevicesError(e.toString())); return; } final devices = HomeCubit.getInstance().selectedSpace!.subspaces[roomIndex].devices; emitSafe(GetDevicesSuccess(devices)); //get status for each device //TODO get devices status per page via page controller instead of getting all devices status at once // List devices = HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices!; // if (devices.isNotEmpty) { // for (var device in devices) { // fetchDevicesStatues(device.uuid!, roomIndex); // } // } } fetchDevicesStatues(String deviceUuid, int roomIndex, {String? code}) async { emitSafe(GetDeviceStatusLoading(code: code)); int deviceIndex = HomeCubit.getInstance() .selectedSpace! .subspaces[roomIndex] .devices! .indexWhere((element) => element.uuid == deviceUuid); List statuses = []; try { var response = await DevicesAPI.getDeviceStatus(deviceUuid); for (var status in response['status']) { statuses.add(StatusModel.fromJson(status)); } } catch (e) { emitSafe(GetDeviceStatusError(e.toString())); return; } HomeCubit.getInstance() .selectedSpace! .subspaces[roomIndex] .devices![deviceIndex] .status = statuses; emitSafe(GetDeviceStatusSuccess(code: code)); } ///Lights onHorizontalDragUpdate(DeviceModel light, double dx, double screenWidth) { double newBrightness = (dx / (screenWidth - 15) * 100); if (newBrightness > 100) { newBrightness = 100; } else if (newBrightness < 0) { newBrightness = 0; } // setBrightness(light, newBrightness); } Map lightModes = { 0: LightMode.Doze, 1: LightMode.Relax, 2: LightMode.Reading, 3: LightMode.Energizing, }; // setLightingMode(DeviceModel light, LightMode mode) { // light.lightingMode = // lightModes.entries.firstWhere((element) => element.value == mode).key; // emitSafe(LightModeChanged(mode)); // } // // toggleLight(DeviceModel light) { // light.isOnline != null ? light.isOnline = !light.isOnline! : light.isOnline = true; // emitSafe(LightToggled(light)); // } // // setColor(DeviceModel light, int color) { // light.color = color; // emitSafe(LightColorChanged(color)); // } // // int getBrightness(DeviceModel light) { // return light.brightness.toInt(); // } // setBrightness(DeviceModel light, double value) { // value = (value / 5).ceil() * 5; // if (value != light.brightness) { // light.brightness = value; // emitSafe(LightBrightnessChanged(value)); // } // } // List _fetchedDevices = []; // List get allDevices => _fetchedDevices; List allDevices = []; Future fetchAllDevices(SpaceModel? unit) async { emitSafe(GetDevicesLoading()); try { final devices = await DevicesAPI.getAllDevices( communityUuid: unit!.community.uuid, spaceUuid: unit.id, ); allDevices = devices; emitSafe(GetDevicesSuccess(allDevices)); } catch (e) { emitSafe(GetDevicesError(e.toString())); } } bool isDeviceOn(DeviceModel device) { final switchStatuses = device.status.where( (s) => (s.code?.startsWith('switch') ?? false), ); final anySwitchFalse = switchStatuses.any((s) => s.value == false); return !anySwitchFalse; } void updateDeviceStatus(String deviceId, bool newToggleStatus) { emitSafe(GetDevicesLoading()); // Create a fresh copy of the device list. final updatedDevices = List.from(allDevices); for (int i = 0; i < updatedDevices.length; i++) { final device = updatedDevices[i]; if (device.uuid == deviceId) { updatedDevices[i] = DeviceModel( activeTime: device.activeTime, localKey: device.localKey, model: device.model, name: device.name, icon: device.icon, categoryName: device.categoryName, type: device.type, isOnline: device.isOnline, status: device.status, // Make sure to keep the same status list productName: device.productName, timeZone: device.timeZone, updateTime: device.updateTime, uuid: device.uuid, productUuid: device.productUuid, productType: device.productType, subspace: device.subspace, toggleStatus: newToggleStatus, ); break; } } emit(GetDevicesSuccess(updatedDevices)); } Future threeGangToggle( DeviceControlModel control, String deviceUuid) async { emit(SwitchControlLoading(code: control.code)); try { final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid); if (deviceIndex == -1) { throw Exception('Device not found'); } final device = allDevices[deviceIndex]; final switches = ['switch_1', 'switch_2', 'switch_3']; for (final switchCode in switches) { final statusIndex = device.status.indexWhere((s) => s.code == switchCode); if (statusIndex != -1) { final toggledValue = control.value; final controlRequest = DeviceControlModel( code: switchCode, value: toggledValue, deviceId: deviceUuid); final response = await DevicesAPI.controlDevice(controlRequest, deviceUuid); if (response['success'] != true) { throw Exception('Failed to toggle $switchCode'); } device.status[statusIndex].value = toggledValue; } } final anySwitchOff = device.status.any( (s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false), ); device.toggleStatus = !anySwitchOff; allDevices[deviceIndex] = device; emit(DeviceControlSuccess(code: control.code)); } catch (failure) { emit(DeviceControlError(failure.toString())); } } Future towGangToggle( DeviceControlModel control, String deviceUuid) async { emit(SwitchControlLoading(code: control.code)); try { final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid); if (deviceIndex == -1) { throw Exception('Device not found'); } final device = allDevices[deviceIndex]; final switches = [ 'switch_1', 'switch_2', ]; for (final switchCode in switches) { final statusIndex = device.status.indexWhere((s) => s.code == switchCode); if (statusIndex != -1) { final toggledValue = control.value; final controlRequest = DeviceControlModel( code: switchCode, value: toggledValue, deviceId: deviceUuid); final response = await DevicesAPI.controlDevice(controlRequest, deviceUuid); device.status[statusIndex].value = response['result']; } } final anySwitchOff = device.status.any( (s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false), ); device.toggleStatus = !anySwitchOff; allDevices[deviceIndex] = device; emit(DeviceControlSuccess(code: control.code)); } catch (failure) { emit(DeviceControlError(failure.toString())); } } Future towGTGangToggle( DeviceControlModel control, String deviceUuid) async { emit(SwitchControlLoading(code: control.code)); try { final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid); if (deviceIndex == -1) { throw Exception('Device not found'); } final device = allDevices[deviceIndex]; final switches = [ 'switch_1', 'switch_2', ]; for (final switchCode in switches) { final statusIndex = device.status.indexWhere((s) => s.code == switchCode); if (statusIndex != -1) { final toggledValue = control.value; final controlRequest = DeviceControlModel( code: switchCode, value: toggledValue, deviceId: deviceUuid); final response = await DevicesAPI.controlDevice(controlRequest, deviceUuid); device.status[statusIndex].value = response['result']; } } final anySwitchOff = device.status.any( (s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false), ); device.toggleStatus = !anySwitchOff; allDevices[deviceIndex] = device; emit(DeviceControlSuccess(code: control.code)); } catch (failure) { emit(DeviceControlError(failure.toString())); } } Future oneGangToggle( DeviceControlModel control, String deviceUuid) async { emit(SwitchControlLoading(code: control.code)); try { final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid); if (deviceIndex == -1) { throw Exception('Device not found'); } final device = allDevices[deviceIndex]; final statusIndex = device.status.indexWhere((s) => s.code == 'switch_1'); if (statusIndex != -1) { final toggledValue = control.value; final controlRequest = DeviceControlModel( code: 'switch_1', value: toggledValue, deviceId: deviceUuid); final response = await DevicesAPI.controlDevice(controlRequest, deviceUuid); print(response); if (response['result'] != true) { throw Exception('Failed to toggle switch_1'); } device.status[statusIndex].value = response['result']; } final anySwitchOff = device.status.any( (s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false), ); device.toggleStatus = !anySwitchOff; allDevices[deviceIndex] = device; emit(DeviceControlSuccess(code: control.code)); } catch (failure) { emit(DeviceControlError(failure.toString())); } } Future oneGTGangToggle( DeviceControlModel control, String deviceUuid) async { emit(SwitchControlLoading(code: control.code)); try { final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid); if (deviceIndex == -1) { throw Exception('Device not found'); } final device = allDevices[deviceIndex]; final statusIndex = device.status.indexWhere((s) => s.code == 'switch_1'); if (statusIndex != -1) { final currentValue = device.status[statusIndex].value ?? false; final toggledValue = !currentValue; final controlRequest = DeviceControlModel( code: 'switch_1', value: toggledValue, deviceId: deviceUuid); final response = await DevicesAPI.controlDevice(controlRequest, deviceUuid); if (response['result'] != true) { throw Exception('Failed to toggle switch_1'); } device.status[statusIndex].value = response['result']; } final anySwitchOff = device.status.any( (s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false), ); device.toggleStatus = !anySwitchOff; allDevices[deviceIndex] = device; emit(DeviceControlSuccess(code: control.code)); } catch (failure) { emit(DeviceControlError(failure.toString())); } } Future deviceControl( DeviceControlModel control, String deviceUuid) async { emit(SwitchControlLoading(code: control.code)); try { final response = await DevicesAPI.controlDevice(control, deviceUuid); if (response['success'] == true) { final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid); if (deviceIndex != -1) { final device = allDevices[deviceIndex]; final statusIndex = device.status.indexWhere((s) => s.code == control.code); if (statusIndex != -1) { device.status[statusIndex].value = control.value; } final anySwitchOff = device.status.any( (s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false), ); device.toggleStatus = !anySwitchOff; allDevices[deviceIndex] = device; } emit(DeviceControlSuccess(code: control.code)); } else { emit(DeviceControlError('Failed to control the device')); } } catch (failure) { emit(DeviceControlError(failure.toString())); } } Future changeCurtainSwitch( DeviceControlModel control, String deviceUuid) async { emit(SwitchControlLoading(code: control.code)); try { final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid); if (deviceIndex == -1) { throw Exception('Device not found'); } final device = allDevices[deviceIndex]; final isOpen = control.value == "open"; final newValue = isOpen ? 0 : 100; final response = await DevicesAPI.deviceBatchController( code: 'percent_control', devicesUuid: [deviceUuid], value: newValue, ); if (response['success'] == true) { final statusIndex = device.status.indexWhere((s) => s.code == 'percent_control'); if (statusIndex != -1) { device.status[statusIndex].value = newValue; } allDevices[deviceIndex] = device; emit(DeviceControlSuccess(code: control.code)); } else { emit(DeviceControlError('Failed to toggle curtain.')); } } catch (error) { emit(DeviceControlError(error.toString())); } } Future changeGarageSwitch( DeviceControlModel control, String deviceUuid) async { emit(SwitchControlLoading(code: control.code)); try { final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid); if (deviceIndex == -1) { throw Exception('Device not found'); } final device = allDevices[deviceIndex]; final isOpen = control.value == "open"; final newValue = isOpen ? 0 : 100; final response = await DevicesAPI.deviceBatchController( code: 'percent_control', devicesUuid: [deviceUuid], value: newValue, ); if (response['success'] == true) { final statusIndex = device.status.indexWhere((s) => s.code == 'percent_control'); if (statusIndex != -1) { device.status[statusIndex].value = newValue; } allDevices[deviceIndex] = device; emit(DeviceControlSuccess(code: control.code)); } else { emit(DeviceControlError('Failed to toggle curtain.')); } } catch (error) { emit(DeviceControlError(error.toString())); } } } enum LightMode { Doze, Relax, Reading, Energizing, }