diff --git a/assets/icons/edit_name_sos_icon.svg b/assets/icons/edit_name_sos_icon.svg
new file mode 100644
index 0000000..c9e6a9e
--- /dev/null
+++ b/assets/icons/edit_name_sos_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_bloc.dart b/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_bloc.dart
index 6fbfb69..68c33ad 100644
--- a/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_bloc.dart
+++ b/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_bloc.dart
@@ -8,7 +8,7 @@ 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_report_model.dart';
import 'package:syncrow_app/features/devices/model/group_devices_model.dart';
-import 'package:syncrow_app/features/devices/model/scene_switch_model.dart';
+import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/sex_scene_question_model.dart';
import 'package:syncrow_app/features/devices/model/six_scene_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
@@ -68,7 +68,7 @@ class SixSceneBloc extends Bloc {
scene_id_group_id: '',
switch_backlight: false);
- SceneSwitch deviceInfo = SceneSwitch(
+ DeviceInfoModel deviceInfo = DeviceInfoModel(
activeTime: 0,
category: "",
categoryName: "",
@@ -175,7 +175,7 @@ class SixSceneBloc extends Bloc {
try {
emit(SixSceneLoadingState());
var response = await DevicesAPI.getDeviceInfo(sixSceneId);
- deviceInfo = SceneSwitch.fromJson(response);
+ deviceInfo = DeviceInfoModel.fromJson(response);
deviceName = deviceInfo.name;
emit(LoadingDeviceInfo(deviceInfo: deviceInfo));
} catch (e) {
diff --git a/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart b/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart
index d5480f5..318d37a 100644
--- a/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart
+++ b/lib/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart
@@ -1,7 +1,7 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/group_devices_model.dart';
-import 'package:syncrow_app/features/devices/model/scene_switch_model.dart';
+import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/sex_scene_question_model.dart';
import 'package:syncrow_app/features/devices/model/six_scene_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
@@ -121,7 +121,7 @@ class SceneSelectionUpdatedState extends SixSceneState {
}
class LoadingDeviceInfo extends SixSceneState {
- final SceneSwitch deviceInfo;
+ final DeviceInfoModel deviceInfo;
const LoadingDeviceInfo({required this.deviceInfo});
@override
diff --git a/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart b/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart
index 469515f..4987e7a 100644
--- a/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart
+++ b/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart
@@ -8,10 +8,10 @@ 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_report_model.dart';
import 'package:syncrow_app/features/devices/model/four_scene_model.dart';
-import 'package:syncrow_app/features/devices/model/four_scene_question_model.dart';
+import 'package:syncrow_app/features/devices/model/question_model.dart';
import 'package:syncrow_app/features/devices/model/four_scene_switch_model.dart';
import 'package:syncrow_app/features/devices/model/group_devices_model.dart';
-import 'package:syncrow_app/features/devices/model/scene_switch_model.dart';
+import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/scene/model/scenes_model.dart';
@@ -68,7 +68,7 @@ class FourSceneBloc extends Bloc {
scene_id_group_id: '',
switch_backlight: false);
- SceneSwitch deviceInfo = SceneSwitch(
+ DeviceInfoModel deviceInfo = DeviceInfoModel(
activeTime: 0,
category: "",
categoryName: "",
@@ -285,7 +285,7 @@ class FourSceneBloc extends Bloc {
try {
emit(FourSceneLoadingState());
var response = await DevicesAPI.getDeviceInfo(fourSceneId);
- deviceInfo = SceneSwitch.fromJson(response);
+ deviceInfo = DeviceInfoModel.fromJson(response);
deviceName = deviceInfo.name;
emit(LoadingDeviceInfo(deviceInfo: deviceInfo));
} catch (e) {
@@ -295,7 +295,7 @@ class FourSceneBloc extends Bloc {
void _onSearchFaq(SearchFaqEvent event, Emitter emit) {
emit(FourSceneLoadingState());
- List _faqQuestions = faqQuestions.where((question) {
+ List _faqQuestions = faqQuestions.where((question) {
return question.question
.toLowerCase()
.contains(event.query.toLowerCase());
@@ -339,31 +339,31 @@ class FourSceneBloc extends Bloc {
DeviceReport recordGroups =
DeviceReport(startTime: '0', endTime: '0', data: []);
- final List faqQuestions = [
- FourSceneQuestionModel(
+ final List faqQuestions = [
+ QuestionModel(
id: 1,
question: 'How does an SOS emergency button work?',
answer:
'The SOS emergency button sends an alert to your contacts when pressed.',
),
- FourSceneQuestionModel(
+ QuestionModel(
id: 2,
question: 'How long will an SOS alarm persist?',
answer:
'The SOS alarm will persist until it is manually turned off or after a set time.',
),
- FourSceneQuestionModel(
+ QuestionModel(
id: 3,
question: 'What should I do if the SOS button is unresponsive?',
answer: 'Try restarting the device. If it persists, contact support.',
),
- FourSceneQuestionModel(
+ QuestionModel(
id: 4,
question: 'Can I use the SOS feature without a network connection?',
answer:
'No, a network connection is required to send the alert to your contacts.',
),
- FourSceneQuestionModel(
+ QuestionModel(
id: 5,
question: 'How often should I check the SOS battery?',
answer:
diff --git a/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart b/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart
index 66c888d..08c849c 100644
--- a/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart
+++ b/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart
@@ -1,9 +1,9 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/four_scene_model.dart';
-import 'package:syncrow_app/features/devices/model/four_scene_question_model.dart';
+import 'package:syncrow_app/features/devices/model/question_model.dart';
import 'package:syncrow_app/features/devices/model/group_devices_model.dart';
-import 'package:syncrow_app/features/devices/model/scene_switch_model.dart';
+import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/scene/model/scenes_model.dart';
@@ -58,13 +58,13 @@ class NameEditingState extends FourSceneState {
}
class FaqLoadedState extends FourSceneState {
- final List filteredFaqQuestions;
+ final List filteredFaqQuestions;
FaqLoadedState({this.filteredFaqQuestions = const []});
}
class FaqSearchState extends FourSceneState {
- final List filteredFaqQuestions;
+ final List filteredFaqQuestions;
const FaqSearchState({this.filteredFaqQuestions = const []});
}
@@ -116,7 +116,7 @@ class OptionSelectedState extends FourSceneState {
}
class LoadingDeviceInfo extends FourSceneState {
- final SceneSwitch deviceInfo;
+ final DeviceInfoModel deviceInfo;
const LoadingDeviceInfo({required this.deviceInfo});
@override
diff --git a/lib/features/devices/bloc/sos_bloc/sos_bloc.dart b/lib/features/devices/bloc/sos_bloc/sos_bloc.dart
new file mode 100644
index 0000000..02d385e
--- /dev/null
+++ b/lib/features/devices/bloc/sos_bloc/sos_bloc.dart
@@ -0,0 +1,452 @@
+import 'dart:async';
+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/community_model.dart';
+import 'package:syncrow_app/features/app_layout/model/space_model.dart';
+import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
+import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
+import 'package:syncrow_app/features/devices/model/device_control_model.dart';
+import 'package:syncrow_app/features/devices/model/device_info_model.dart';
+import 'package:syncrow_app/features/devices/model/device_model.dart';
+import 'package:syncrow_app/features/devices/model/device_report_model.dart';
+import 'package:syncrow_app/features/devices/model/question_model.dart';
+import 'package:syncrow_app/features/devices/model/sos_model.dart';
+import 'package:syncrow_app/features/devices/model/status_model.dart';
+import 'package:syncrow_app/features/devices/model/subspace_model.dart';
+import 'package:syncrow_app/navigation/routing_constants.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/spaces_api.dart';
+import 'package:syncrow_app/utils/helpers/snack_bar.dart';
+
+class SosBloc extends Bloc {
+ final String sosId;
+ SosBloc({
+ required this.sosId,
+ }) : super(const SosState()) {
+ on(_fetchStatus);
+ on(fetchLogsForLastMonth);
+ on(_toggleLowBattery);
+ on(_toggleClosingReminder);
+ on(_changeName);
+ on(_onSearchFaq);
+ on(_onSosInitial);
+ on(_fetchRoomsAndDevices);
+ on(_onOptionSelected);
+ on(_onSaveSelection);
+ on(_assignDevice);
+ on(fetchDeviceInfo);
+ // on(_unassignDevice);
+ // on(_toggleWaterLeakAlarm);
+ }
+
+ final TextEditingController nameController =
+ TextEditingController(text: '${'firstName'}');
+ bool isSaving = false;
+ bool editName = false;
+ final FocusNode focusNode = FocusNode();
+ Timer? _timer;
+ bool enableAlarm = false;
+ bool closingReminder = false;
+ bool waterAlarm = false;
+ SosModel deviceStatus =
+ SosModel(sosContactState: 'normal', batteryPercentage: 0);
+
+ void _fetchStatus(SosInitial event, Emitter emit) async {
+ emit(SosLoadingState());
+ try {
+ var response = await DevicesAPI.getDeviceStatus(sosId);
+ List statusModelList = [];
+ for (var status in response['status']) {
+ statusModelList.add(StatusModel.fromJson(status));
+ }
+ deviceStatus = SosModel.fromJson(
+ statusModelList,
+ );
+ emit(UpdateState(sensor: deviceStatus));
+
+ Future.delayed(const Duration(milliseconds: 500));
+ // _listenToChanges();
+ } catch (e) {
+ emit(SosFailedState(errorMessage: e.toString()));
+ return;
+ }
+ }
+
+ DeviceInfoModel deviceInfo = DeviceInfoModel(
+ activeTime: 0,
+ category: "",
+ categoryName: "",
+ createTime: 0,
+ gatewayId: "",
+ icon: "",
+ ip: "",
+ lat: "",
+ localKey: "",
+ lon: "",
+ model: "",
+ name: "",
+ nodeId: "",
+ online: false,
+ ownerId: "",
+ productName: "",
+ sub: false,
+ timeZone: "",
+ updateTime: 0,
+ uuid: "",
+ productUuid: "",
+ productType: "",
+ permissionType: "",
+ macAddress: "",
+ subspace: Subspace(
+ uuid: "",
+ createdAt: "",
+ updatedAt: "",
+ subspaceName: "",
+ ),
+ );
+
+ void _onSearchFaq(SearchFaqEvent event, Emitter emit) {
+ emit(SosLoadingState());
+ List _faqQuestions = faqQuestions.where((question) {
+ return question.question
+ .toLowerCase()
+ .contains(event.query.toLowerCase());
+ }).toList();
+ emit(FaqSearchState(filteredFaqQuestions: _faqQuestions));
+ }
+
+ void _changeName(ChangeNameEvent event, Emitter emit) {
+ emit(SosLoadingState());
+ editName = event.value!;
+ if (editName) {
+ Future.delayed(const Duration(milliseconds: 500), () {
+ focusNode.requestFocus();
+ });
+ } else {
+ focusNode.unfocus();
+ }
+ emit(NameEditingState(editName: editName));
+ }
+
+ void _toggleLowBattery(
+ ToggleEnableAlarmEvent event, Emitter emit) async {
+ emit(LoadingNewSate(sosSensor: deviceStatus));
+ try {
+ enableAlarm = event.isLowBatteryEnabled;
+ emit(UpdateState(sensor: deviceStatus));
+ await DevicesAPI.controlDevice(
+ DeviceControlModel(
+ deviceId: sosId,
+ code: 'low_battery',
+ value: enableAlarm,
+ ),
+ sosId,
+ );
+ } catch (e) {
+ emit(SosFailedState(errorMessage: e.toString()));
+ }
+ }
+
+ void _toggleClosingReminder(
+ ToggleClosingReminderEvent event, Emitter emit) async {
+ emit(LoadingNewSate(sosSensor: deviceStatus));
+ try {
+ closingReminder = event.isClosingReminderEnabled;
+ emit(UpdateState(sensor: deviceStatus));
+
+ // API call to update the state, if necessary
+ // await DevicesAPI.controlDevice(
+ // DeviceControlModel(
+ // deviceId: sosId,
+ // code: 'closing_reminder',
+ // value: closingReminder,
+ // ),
+ // sosId,
+ // );
+ } catch (e) {
+ emit(SosFailedState(errorMessage: e.toString()));
+ }
+ }
+
+ DeviceReport recordGroups =
+ DeviceReport(startTime: '0', endTime: '0', data: []);
+
+ Future fetchLogsForLastMonth(
+ ReportLogsInitial event, Emitter emit) async {
+ DateTime now = DateTime.now();
+ DateTime lastMonth = DateTime(now.year, now.month - 1, now.day);
+ int startTime = lastMonth.millisecondsSinceEpoch;
+ int endTime = now.millisecondsSinceEpoch;
+ try {
+ emit(SosLoadingState());
+ var response = await DevicesAPI.getReportLogs(
+ startTime: startTime.toString(),
+ endTime: endTime.toString(),
+ deviceUuid: sosId,
+ code: 'sos',
+ );
+ recordGroups = response;
+ emit(UpdateState(sensor: deviceStatus));
+ } on DioException catch (e) {
+ final errorData = e.response!.data;
+ String errorMessage = errorData['message'];
+ emit(SosFailedState(errorMessage: e.toString()));
+ }
+ }
+
+ static String deviceName = '';
+
+ fetchDeviceInfo(SosInitialDeviseInfo event, Emitter emit) async {
+ try {
+ emit(SosLoadingState());
+ var response = await DevicesAPI.getDeviceInfo(sosId);
+ deviceInfo = DeviceInfoModel.fromJson(response);
+ deviceName = deviceInfo.name;
+ emit(LoadingSosDeviceInfo(deviceInfo: deviceInfo));
+ } catch (e) {
+ emit(SosFailedState(errorMessage: e.toString()));
+ }
+ }
+
+ // Demo list of FAQ questions using the QuestionModel class
+ final List faqQuestions = [
+ QuestionModel(
+ id: 1,
+ question: 'How does an SOS emergency button work?',
+ answer:
+ 'The SOS emergency button sends an alert to your contacts when pressed.',
+ ),
+ QuestionModel(
+ id: 2,
+ question: 'How long will an SOS alarm persist?',
+ answer:
+ 'The SOS alarm will persist until it is manually turned off or after a set time.',
+ ),
+ QuestionModel(
+ id: 3,
+ question: 'What should I do if the SOS button is unresponsive?',
+ answer: 'Try restarting the device. If it persists, contact support.',
+ ),
+ QuestionModel(
+ id: 4,
+ question: 'Can I use the SOS feature without a network connection?',
+ answer:
+ 'No, a network connection is required to send the alert to your contacts.',
+ ),
+ QuestionModel(
+ id: 5,
+ question: 'How often should I check the SOS battery?',
+ answer:
+ 'Check the SOS battery at least once a month to ensure it is operational.',
+ ),
+ ];
+ Future _onSosInitial(
+ SosInitialQuestion event, Emitter emit) async {
+ emit(SosLoadingState());
+ // SosModel sosModel = await fetchSosData(sosId); // Define this function as needed
+ emit(FaqLoadedState(filteredFaqQuestions: faqQuestions));
+ }
+
+ List allDevices = [];
+ List roomsList = [];
+
+ void _fetchRoomsAndDevices(
+ FetchRoomsEvent event, Emitter emit) async {
+ try {
+ emit(SosLoadingState());
+ roomsList = await SpacesAPI.getSubSpaceBySpaceId(
+ event.unit.community.uuid, event.unit.id);
+ emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList));
+ } catch (e) {
+ emit(const SosFailedState(errorMessage: 'Something went wrong'));
+ return;
+ }
+ }
+
+ String roomId = '';
+ SpaceModel unit =
+ SpaceModel(id: '', name: '', community: Community(uuid: '', name: ''));
+
+ String _selectedOption = '';
+ bool _hasSelectionChanged = false;
+
+ void _onOptionSelected(SelectOptionEvent event, Emitter emit) {
+ emit(SosLoadingState());
+ _selectedOption = event.selectedOption;
+ _hasSelectionChanged = true;
+ emit(OptionSelectedState(
+ selectedOption: _selectedOption,
+ hasSelectionChanged: _hasSelectionChanged));
+ }
+
+ void _onSaveSelection(SaveSelectionEvent event, Emitter emit) {
+ if (_hasSelectionChanged) {
+ _hasSelectionChanged = false;
+ add(AssignRoomEvent(roomId: roomId, unit: unit, context: event.context));
+ emit(SaveSelectionSuccessState());
+
+ // Navigator.pushNamedAndRemoveUntil(
+ // event.context, Routes.homeRoute, (route) => false);
+ var cubit = HomeCubit.getInstance();
+ cubit.updatePageIndex(1);
+ Navigator.pushReplacementNamed(event.context, Routes.homeRoute);
+ }
+ }
+
+ void _assignDevice(AssignRoomEvent event, Emitter emit) async {
+ try {
+ // Map roomDevicesId = {};
+ emit(SosLoadingState());
+
+ await HomeManagementAPI.assignDeviceToRoom(
+ event.unit.community.uuid, event.unit.id, event.roomId, sosId);
+ final devicesList = await DevicesAPI.getDevicesByRoomId(
+ communityUuid: event.unit.community.uuid,
+ spaceUuid: event.unit.id,
+ roomId: event.roomId);
+
+ List allDevicesIds = [];
+
+ allDevices.forEach((element) {
+ allDevicesIds.add(element.uuid!);
+ });
+ emit(SaveSelectionSuccessState());
+
+ // 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 SosFailedState(errorMessage: 'Something went wrong'));
+ return;
+ }
+ }
+
+ void _unassignDevice(UnassignRoomEvent event, Emitter emit) async {
+ try {
+ Map roomDevicesId = {};
+ emit(SosLoadingState());
+ await HomeManagementAPI.unAssignDeviceToRoom(
+ event.unit.community.uuid, event.unit.id, event.roomId, sosId);
+ final devicesList = await DevicesAPI.getDevicesByRoomId(
+ communityUuid: event.unit.community.uuid,
+ spaceUuid: event.unit.id,
+ roomId: event.roomId);
+
+ List 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 SosFailedState(errorMessage: 'Something went wrong'));
+ return;
+ }
+ }
+
+ Map> devicesByRoom = {};
+
+// Your existing function
+ void _fetchDevicesByRoomId(
+ FetchDevicesByRoomIdEvent event, Emitter emit) async {
+ try {
+ Map roomDevicesId = {};
+ emit(SosLoadingState());
+
+ // Fetch devices for the specified room
+ final devicesList = await DevicesAPI.getDevicesByRoomId(
+ communityUuid: event.unit.community.uuid,
+ spaceUuid: event.unit.id,
+ roomId: event.roomId);
+
+ // Fetch all devices accessible by the user
+ allDevices = await HomeManagementAPI.fetchDevicesByUserId();
+
+ // Map all accessible device IDs
+ List allDevicesIds = [];
+ allDevices.forEach((element) {
+ allDevicesIds.add(element.uuid!);
+ });
+
+ // Mark devices as accessible/inaccessible for the room and add to devicesByRoom map
+ devicesList.forEach((e) {
+ roomDevicesId[e.uuid!] = allDevicesIds.contains(e.uuid!);
+ });
+
+ // Update the devicesByRoom map with the devices in the current room
+ devicesByRoom[event.roomId] = devicesList;
+
+ // emit(FetchDeviceByRoomIdState(
+ // roomDevices: devicesList,
+ // allDevices: allDevices,
+ // roomDevicesId: roomDevicesId,
+ // roomId: event.roomId));
+ } catch (e) {
+ emit(const SosFailedState(errorMessage: 'Something went wrong'));
+ return;
+ }
+ }
+
+ Future saveName(SaveNameEvent event, Emitter emit) async {
+ if (_validateInputs()) return;
+ try {
+ add(const ChangeNameEvent(value: false));
+ isSaving = true;
+ emit(SosLoadingState());
+ var response = await DevicesAPI.putDeviceName(
+ deviceId: sosId, deviceName: nameController.text);
+ add(SosInitialDeviseInfo());
+ CustomSnackBar.displaySnackBar('Save Successfully');
+ emit(SaveState());
+ } catch (e) {
+ emit(SosFailedState(errorMessage: e.toString()));
+ } finally {
+ isSaving = false;
+ }
+ }
+
+ bool _validateInputs() {
+ final nameError = fullNameValidator(nameController.text);
+ if (nameError != null) {
+ CustomSnackBar.displaySnackBar(nameError);
+ return true;
+ }
+ return false;
+ }
+
+ String? fullNameValidator(String? value) {
+ if (value == null) return 'name is required';
+ final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim();
+ if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) {
+ return 'name must be between 2 and 30 characters long';
+ }
+ if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) {
+ return 'Only alphanumeric characters, space, dash and single quote are allowed';
+ }
+ return null;
+ }
+}
diff --git a/lib/features/devices/bloc/sos_bloc/sos_event.dart b/lib/features/devices/bloc/sos_bloc/sos_event.dart
new file mode 100644
index 0000000..8a882a9
--- /dev/null
+++ b/lib/features/devices/bloc/sos_bloc/sos_event.dart
@@ -0,0 +1,196 @@
+import 'package:equatable/equatable.dart';
+import 'package:flutter/cupertino.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_info_model.dart';
+
+abstract class SosEvent extends Equatable {
+ const SosEvent();
+
+ @override
+ List