connected rest of the devices and functions and trigger api

This commit is contained in:
ashrafzarkanisala
2024-06-29 18:24:33 +03:00
parent 320afb0ca8
commit 043e8c1e2d
24 changed files with 630 additions and 162 deletions
@@ -11,38 +11,37 @@ part 'scene_state.dart';
class SceneBloc extends Bloc<SceneEvent, SceneState> {
SceneBloc() : super(SceneInitial()) {
on<LoadScenes>(_onLoadScenes);
on<SceneTrigger>(_sceneTrigger);
on<SceneTrigger>(_onSceneTrigger);
}
FutureOr<void> _onLoadScenes(
LoadScenes event, Emitter<SceneState> emit) async {
Future<void> _onLoadScenes(LoadScenes event, Emitter<SceneState> emit) async {
emit(SceneLoading());
try {
final scenes = await SceneApi.getScenesByUnitId(event.unitId);
if (scenes.isNotEmpty) {
emit(SceneLoaded(scenes));
} else {
emit(SceneTriggerError(message: 'Something went wrong'));
}
emit(SceneLoaded(scenes));
} catch (e) {
emit(SceneError(message: e.toString()));
}
}
FutureOr<void> _sceneTrigger(
Future<void> _onSceneTrigger(
SceneTrigger event, Emitter<SceneState> emit) async {
emit(SceneTriggerLoading());
final currentState = state;
if (currentState is SceneLoaded) {
emit(SceneLoaded(currentState.scenes, loadingSceneId: event.sceneId));
try {
final success = await SceneApi.triggerScene(event.sceneId);
if (success) {
emit(SceneTriggerState(success: true));
} else {
emit(SceneTriggerError(message: 'Something went wrong'));
try {
final success = await SceneApi.triggerScene(event.sceneId);
if (success) {
emit(SceneTriggerSuccess(event.name));
emit(SceneLoaded(currentState.scenes));
} else {
emit(const SceneError(message: 'Something went wrong'));
}
} catch (e) {
emit(SceneError(message: e.toString()));
}
} catch (e) {
emit(SceneTriggerError(message: e.toString()));
}
}
}
@@ -1,6 +1,8 @@
import 'package:equatable/equatable.dart';
abstract class SceneEvent extends Equatable {
const SceneEvent();
@override
List<Object> get props => [];
}
@@ -8,7 +10,7 @@ abstract class SceneEvent extends Equatable {
class LoadScenes extends SceneEvent {
final String unitId;
LoadScenes(this.unitId);
const LoadScenes(this.unitId);
@override
List<Object> get props => [unitId];
@@ -16,8 +18,9 @@ class LoadScenes extends SceneEvent {
class SceneTrigger extends SceneEvent {
final String sceneId;
final String name;
SceneTrigger(this.sceneId);
const SceneTrigger(this.sceneId, this.name);
@override
List<Object> get props => [sceneId];
@@ -1,8 +1,10 @@
part of 'scene_bloc.dart';
abstract class SceneState extends Equatable {
const SceneState();
@override
List<Object> get props => [];
List<Object?> get props => [];
}
class SceneInitial extends SceneState {}
@@ -11,38 +13,28 @@ class SceneLoading extends SceneState {}
class SceneLoaded extends SceneState {
final List<SceneModel> scenes;
final String? loadingSceneId;
SceneLoaded(this.scenes);
const SceneLoaded(this.scenes, {this.loadingSceneId});
@override
List<Object> get props => [scenes];
List<Object?> get props => [scenes, loadingSceneId];
}
class SceneError extends SceneState {
final String message;
SceneError({required this.message});
const SceneError({required this.message});
@override
List<Object> get props => [message];
}
class SceneTriggerState extends SceneState {
final bool success;
class SceneTriggerSuccess extends SceneState {
final String sceneName;
SceneTriggerState({required this.success});
const SceneTriggerSuccess(this.sceneName);
@override
List<Object> get props => [success];
}
class SceneTriggerLoading extends SceneState {}
class SceneTriggerError extends SceneState {
final String message;
SceneTriggerError({required this.message});
@override
List<Object> get props => [message];
List<Object> get props => [sceneName];
}
@@ -145,14 +145,182 @@ List<SceneStaticFunction> doorLockFunctions(
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [];
return [
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsIconsDoorLock,
operationName: 'Set Door lock Normal Open',
code: 'normal_open_switch',
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
];
}
List<SceneStaticFunction> wallSensorFunctions(
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [];
return [
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsFarDetection,
operationName: 'Far Detection',
code: 'far_detection',
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 75,
description: '75cm',
iconValue: '75',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 150,
description: '150cm',
iconValue: '150',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 225,
description: '225cm',
iconValue: '225',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 300,
description: '300cm',
iconValue: '300',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 375,
description: '375cm',
iconValue: '375',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 450,
description: '450cm',
iconValue: '450',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 525,
description: '525cm',
iconValue: '525',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 600,
description: '600cm',
iconValue: '600',
),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsMotionDetection,
operationName: 'Motion Detection Sensitivity',
code: 'motion_sensitivity_value',
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon,
value: 1,
description: 1.toString(),
),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon,
value: 2,
description: 2.toString(),
),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon,
value: 3,
description: 3.toString(),
),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon,
value: 4,
description: 4.toString(),
),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon,
value: 5,
description: 5.toString(),
),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsMotionlessDetection,
operationName: 'Motionless Detection Sensitivity',
code: 'motionless_sensitivity',
operationalValues: [
SceneOperationalValue(
iconValue: '1',
icon: Assets.assetsFarDetectionFunction,
value: 1,
description: '1',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 2,
description: '2',
iconValue: '2',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 3,
description: '3',
iconValue: '3',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 4,
description: '4',
iconValue: '4',
),
SceneOperationalValue(
icon: Assets.assetsFarDetectionFunction,
value: 5,
description: '5',
iconValue: '5',
),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsIndicator,
operationName: 'Indicator',
code: 'indicator',
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsNobodyTime,
operationName: 'Nobody Time',
code: 'presence_time',
operationalValues: [
SceneOperationalValue(icon: '', value: 0),
],
),
];
}
List<SceneStaticFunction> lightBulbFunctions(
@@ -166,7 +334,53 @@ List<SceneStaticFunction> gatewayFunctions(
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [];
return [
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsSwitchAlarmSound,
operationName: 'Switch Alarm Sound',
code: 'switch_alarm_sound',
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsMasterState,
operationName: 'Master State',
code: 'master_state',
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower,
description: "Alarm",
value: 'alarm',
),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "Normal",
value: 'normal',
),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsFactoryReset,
operationName: 'Factory Reset',
code: 'factory_reset',
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsSceneRefresh, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsResetOff, description: "OFF", value: false),
],
),
];
}
List<SceneStaticFunction> threeGangFunctions(
@@ -101,27 +101,36 @@ class SceneOperationalValue {
final String icon;
final dynamic value;
final String? description;
final String? iconValue;
SceneOperationalValue({
required this.icon,
required this.value,
this.description,
this.iconValue,
});
SceneOperationalValue copyWith({
String? icon,
dynamic value,
String? description,
String? iconValue,
}) {
return SceneOperationalValue(
icon: icon ?? this.icon,
value: value ?? this.value,
description: description ?? this.description,
iconValue: iconValue ?? this.iconValue,
);
}
Map<String, dynamic> toMap() {
return {'icon': icon, 'value': value, 'description': description};
return {
'icon': icon,
'value': value,
'description': description,
'iconValue': iconValue
};
}
factory SceneOperationalValue.fromMap(Map<String, dynamic> map) {
@@ -129,6 +138,7 @@ class SceneOperationalValue {
icon: map['icon'] ?? '',
value: map['value'],
description: map['description'],
iconValue: map['iconValue'] ?? '',
);
}
@@ -139,7 +149,7 @@ class SceneOperationalValue {
@override
String toString() =>
'StaticFunctionOperationHelper(icon: $icon, value: $value, description: $description)';
'StaticFunctionOperationHelper(icon: $icon, value: $value, description: $description, iconValue: $iconValue)';
@override
bool operator ==(Object other) {
@@ -148,9 +158,15 @@ class SceneOperationalValue {
return other is SceneOperationalValue &&
other.icon == icon &&
other.description == description &&
other.iconValue == iconValue &&
other.value == value;
}
@override
int get hashCode => icon.hashCode ^ value.hashCode ^ description.hashCode;
int get hashCode =>
icon.hashCode ^
value.hashCode ^
description.hashCode ^
iconValue.hashCode ^
description.hashCode;
}
@@ -97,7 +97,7 @@ class DeviceFunctionsView extends StatelessWidget
BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) {
return SceneListTile(
iconsSize: 32,
iconsSize: 22,
minLeadingWidth: 20,
assetPath: functions[index].icon,
titleString: functions[index].operationName,
@@ -118,7 +118,12 @@ class DeviceFunctionsView extends StatelessWidget
alertBody: functions[index].code == 'temp_set'
? AlertDialogTemperatureBody(
index: index, functions: functions)
: functions[index].code.contains('countdown')
: (functions[index]
.code
.contains('countdown') ||
functions[index]
.code
.contains('presence_time'))
? AlertDialogCountdown(
durationValue: functions[index]
.operationalValues
+44 -112
View File
@@ -3,14 +3,11 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart';
import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_grid_view.dart';
import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_header.dart';
import 'package:syncrow_app/features/shared_widgets/create_unit.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
import 'package:syncrow_app/utils/context_extension.dart';
class SceneView extends StatelessWidget {
const SceneView({super.key});
@@ -20,113 +17,48 @@ class SceneView extends StatelessWidget {
return BlocProvider(
create: (BuildContext context) => SceneBloc()
..add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!)),
child: HomeCubit.getInstance().spaces?.isEmpty ?? true
? const CreateUnitWidget()
: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const TitleMedium(
text: StringsManager.routine,
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
const BodySmall(
text: StringsManager.tapToRunRoutine,
),
const SizedBox(height: 8),
BlocBuilder<SceneBloc, SceneState>(
builder: (context, state) {
if (state is SceneLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (state is SceneError) {
return Center(
child: Text(state.message),
);
}
if (state is SceneLoaded) {
return Expanded(
child: GridView.builder(
shrinkWrap: true,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 1.5,
child: BlocListener<SceneBloc, SceneState>(
listener: (context, state) {
if (state is SceneTriggerSuccess) {
context.showCustomSnackbar(
message: 'Scene ${state.sceneName} triggered successfully!');
}
},
child: HomeCubit.getInstance().spaces?.isEmpty ?? true
? const CreateUnitWidget()
: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SceneHeader(),
const SizedBox(height: 8),
BlocBuilder<SceneBloc, SceneState>(
builder: (context, state) {
if (state is SceneLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (state is SceneError) {
return Center(
child: Text(state.message),
);
}
if (state is SceneLoaded) {
return Expanded(
child: SceneGrid(
scenes: state.scenes,
loadingSceneId: state.loadingSceneId,
),
itemCount: state.scenes.length,
itemBuilder: (context, index) {
final scene = state.scenes[index];
return DefaultContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Image.asset(
height: 32,
width: 32,
Assets.assetsIconsLogo,
fit: BoxFit.fill,
),
BlocBuilder<SceneBloc, SceneState>(
builder: (context, state) {
return IconButton(
padding: EdgeInsets.zero,
onPressed: () {
// context
// .read<SceneBloc>()
// .add(SceneTrigger(scene.id));
},
icon: state is SceneTriggerLoading
? const Center(
child:
CircularProgressIndicator())
: const Icon(
Icons.play_circle,
size: 40,
color:
ColorsManager.greyColor,
),
);
},
)
],
),
const SizedBox(
height: 10,
),
BodyMedium(
text: scene.name,
maxLines: 1,
overflow: TextOverflow.fade,
fontWeight: FontWeight.bold,
fontSize: 16,
)
],
),
);
},
),
);
}
);
}
return const SizedBox();
},
),
],
),
return const SizedBox();
},
),
],
),
),
);
}
}
@@ -36,6 +36,7 @@ class _AlertDialogFunctionsOperationsBodyState
minLeadingWidth: 15,
padding: const EdgeInsets.symmetric(horizontal: 16),
assetPath: operation.icon,
assetHeaderValue: operation.iconValue,
titleString: operation.description.toString(),
textAlign: TextAlign.start,
trailingWidget: Radio(
@@ -22,6 +22,7 @@ class SceneListTile extends StatelessWidget {
this.subtitleString,
this.subtitleWidget,
this.iconsSize,
this.assetHeaderValue,
});
final String? assetPath;
final String? titleString;
@@ -36,6 +37,7 @@ class SceneListTile extends StatelessWidget {
final double? minLeadingWidth;
final Widget? titleWidget;
final double? iconsSize;
final String? assetHeaderValue;
@override
Widget build(BuildContext context) {
@@ -50,7 +52,7 @@ class SceneListTile extends StatelessWidget {
width: iconsSize,
height: assetHeight ?? 35,
alignment: Alignment.center,
fit: BoxFit.contain,
fit: BoxFit.fitWidth,
),
)
: null),
@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/scene/model/scene_model.dart';
import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_item.dart';
class SceneGrid extends StatelessWidget {
final List<SceneModel> scenes;
final String? loadingSceneId;
const SceneGrid({
required this.scenes,
required this.loadingSceneId,
super.key,
});
@override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 1.5,
),
itemCount: scenes.length,
itemBuilder: (context, index) {
final scene = scenes[index];
final isLoading = loadingSceneId == scene.id;
return SceneItem(scene: scene, isLoading: isLoading);
},
);
}
}
@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
class SceneHeader extends StatelessWidget {
const SceneHeader({super.key});
@override
Widget build(BuildContext context) {
return const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TitleMedium(
text: StringsManager.routine,
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 20),
BodySmall(
text: StringsManager.tapToRunRoutine,
),
],
);
}
}
@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart';
import 'package:syncrow_app/features/scene/model/scene_model.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SceneItem extends StatelessWidget {
final SceneModel scene;
final bool isLoading;
const SceneItem({
required this.scene,
required this.isLoading,
super.key,
});
@override
Widget build(BuildContext context) {
return DefaultContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset(
height: 32,
width: 32,
Assets.assetsIconsLogo,
fit: BoxFit.fill,
),
IconButton(
padding: EdgeInsets.zero,
onPressed: () {
context
.read<SceneBloc>()
.add(SceneTrigger(scene.id, scene.name));
},
icon: isLoading
? const Center(
child: CircularProgressIndicator(),
)
: const Icon(
Icons.play_circle,
size: 40,
color: ColorsManager.greyColor,
),
),
],
),
const SizedBox(height: 10),
BodyMedium(
text: scene.name,
maxLines: 1,
overflow: TextOverflow.fade,
fontWeight: FontWeight.bold,
fontSize: 16,
),
],
),
);
}
}
+51 -1
View File
@@ -853,8 +853,58 @@ class Assets {
static const String assetsSceneRefresh =
"assets/icons/functions_icons/scene_refresh.svg";
//assets/icons/functions_icons/light_countdown.svg
//assets/icons/functions_icons/light_countdown.svg
static const String assetsLightCountdown =
"assets/icons/functions_icons/light_countdown.svg";
//assets/icons/functions_icons/far_detection.svg
static const String assetsFarDetection =
"assets/icons/functions_icons/far_detection.svg";
//assets/icons/functions_icons/far_detection_function.svg
static const String assetsFarDetectionFunction =
"assets/icons/functions_icons/far_detection_function.svg";
//assets/icons/functions_icons/indicator.svg
static const String assetsIndicator =
"assets/icons/functions_icons/indicator.svg";
//assets/icons/functions_icons/motion_detection.svg
static const String assetsMotionDetection =
"assets/icons/functions_icons/motion_detection.svg";
//assets/icons/functions_icons/motionless_detection.svg
static const String assetsMotionlessDetection =
"assets/icons/functions_icons/motionless_detection.svg";
//assets/icons/functions_icons/nobody_time.svg
static const String assetsNobodyTime =
"assets/icons/functions_icons/nobody_time.svg";
//assets/icons/functions_icons/factory_reset.svg
static const String assetsFactoryReset =
"assets/icons/functions_icons/factory_reset.svg";
//assets/icons/functions_icons/master_state.svg
static const String assetsMasterState =
"assets/icons/functions_icons/master_state.svg";
//assets/icons/functions_icons/switch_alarm_sound.svg
static const String assetsSwitchAlarmSound =
"assets/icons/functions_icons/switch_alarm_sound.svg";
//assets/icons/functions_icons/reset_off.svg
static const String assetsResetOff =
"assets/icons/functions_icons/reset_off.svg";
}
+1 -1
View File
@@ -45,7 +45,7 @@ class SceneApi {
static Future<bool> triggerScene(String sceneId) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.triggerScene.replaceAll('{sceneUuid}', sceneId),
path: ApiEndpoints.triggerScene.replaceAll('{sceneId}', sceneId),
showServerMessage: false,
expectedResponseModel: (json) => json['success'],
);