mirror of
https://github.com/SyncrowIOT/syncrow-app.git
synced 2025-07-15 17:47:28 +00:00
push automation details and update automation
This commit is contained in:
@ -1,6 +1,3 @@
|
||||
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
|
||||
@ -12,7 +9,6 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dar
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/navigation/routing_constants.dart';
|
||||
|
||||
|
||||
class SceneListview extends StatelessWidget {
|
||||
final List<ScenesModel> scenes;
|
||||
final String? loadingSceneId;
|
||||
@ -24,8 +20,7 @@ class SceneListview extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return
|
||||
ListView.builder(
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: scenes.length,
|
||||
@ -45,7 +40,8 @@ class SceneListview extends StatelessWidget {
|
||||
sceneName: scene.name,
|
||||
),
|
||||
);
|
||||
BlocProvider.of<CreateSceneBloc>(context).add(FetchSceneTasksEvent(sceneId: scene.id));
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(FetchSceneTasksEvent(sceneId: scene.id));
|
||||
},
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.4,
|
||||
@ -73,8 +69,7 @@ class SceneListview extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -276,8 +276,10 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
|
||||
? await SceneApi.updateScene(event.createSceneModel!, event.sceneId)
|
||||
: await SceneApi.createScene(event.createSceneModel!);
|
||||
} else if (event.createAutomationModel != null) {
|
||||
response =
|
||||
await SceneApi.createAutomation(event.createAutomationModel!);
|
||||
response = event.updateScene
|
||||
? await SceneApi.updateAutomation(
|
||||
event.createAutomationModel!, event.sceneId)
|
||||
: await SceneApi.createAutomation(event.createAutomationModel!);
|
||||
}
|
||||
|
||||
if (response['success'] == true) {
|
||||
@ -315,13 +317,31 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
|
||||
emit(CreateSceneLoading());
|
||||
|
||||
try {
|
||||
final response = await SceneApi.getSceneDetails(event.sceneId);
|
||||
final response = event.isAutomation
|
||||
? await SceneApi.getAutomationDetails(event.sceneId)
|
||||
: await SceneApi.getSceneDetails(event.sceneId);
|
||||
if (response.id.isNotEmpty) {
|
||||
tasksList = List<SceneStaticFunction>.from(getTaskListFunctionsFromApi(
|
||||
if (event.isAutomation) {
|
||||
automationTasksList = List<SceneStaticFunction>.from(
|
||||
getTaskListFunctionsFromApi(
|
||||
actions: [],
|
||||
isAutomation: true,
|
||||
conditions: response.conditions));
|
||||
tasksList = List<SceneStaticFunction>.from(
|
||||
getTaskListFunctionsFromApi(
|
||||
actions: response.actions, isAutomation: false));
|
||||
emit(AddSceneTask(
|
||||
automationTasksList: automationTasksList,
|
||||
tasksList: tasksList,
|
||||
));
|
||||
} else {
|
||||
tasksList = List<SceneStaticFunction>.from(
|
||||
getTaskListFunctionsFromApi(
|
||||
actions: response.actions, isAutomation: false));
|
||||
emit(AddSceneTask(
|
||||
tasksList: tasksList,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
emit(const CreateSceneError(message: 'Something went wrong'));
|
||||
}
|
||||
@ -420,7 +440,11 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState>
|
||||
conditionRule = 'and';
|
||||
}
|
||||
|
||||
emit(ConditionSelectedState(event.condition));
|
||||
emit(AddSceneTask(
|
||||
tasksList: tasksList,
|
||||
automationTasksList: automationTasksList,
|
||||
condition: conditionRule,
|
||||
));
|
||||
}
|
||||
|
||||
FutureOr<void> _sceneTypeEvent(
|
||||
|
@ -147,10 +147,13 @@ class ClearTempTaskListEvent extends CreateSceneEvent {
|
||||
|
||||
class FetchSceneTasksEvent extends CreateSceneEvent {
|
||||
final String sceneId;
|
||||
const FetchSceneTasksEvent({required this.sceneId});
|
||||
final bool isAutomation;
|
||||
|
||||
const FetchSceneTasksEvent(
|
||||
{this.isAutomation = false, required this.sceneId});
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
List<Object> get props => [sceneId, isAutomation];
|
||||
}
|
||||
|
||||
class DeleteSceneEvent extends CreateSceneEvent {
|
||||
|
@ -22,7 +22,9 @@ class CreateSceneError extends CreateSceneState {
|
||||
class AddSceneTask extends CreateSceneState {
|
||||
final List<SceneStaticFunction> tasksList;
|
||||
final List<SceneStaticFunction>? automationTasksList;
|
||||
const AddSceneTask({required this.tasksList, this.automationTasksList});
|
||||
final String? condition;
|
||||
const AddSceneTask(
|
||||
{required this.tasksList, this.automationTasksList, this.condition});
|
||||
|
||||
@override
|
||||
List<Object> get props => [tasksList];
|
||||
|
@ -11,16 +11,20 @@ part 'scene_state.dart';
|
||||
class SceneBloc extends Bloc<SceneEvent, SceneState> {
|
||||
SceneBloc() : super(SceneInitial()) {
|
||||
on<LoadScenes>(_onLoadScenes);
|
||||
on<LoadAutomation>(_onLoadAutomation);
|
||||
on<SceneTrigger>(_onSceneTrigger);
|
||||
}
|
||||
|
||||
List<ScenesModel> scenes = [];
|
||||
List<ScenesModel> automationList = [];
|
||||
|
||||
Future<void> _onLoadScenes(LoadScenes event, Emitter<SceneState> emit) async {
|
||||
emit(SceneLoading());
|
||||
|
||||
try {
|
||||
if (event.unitId.isNotEmpty) {
|
||||
final scenes = await SceneApi.getScenesByUnitId(event.unitId);
|
||||
emit(SceneLoaded(scenes));
|
||||
scenes = await SceneApi.getScenesByUnitId(event.unitId);
|
||||
emit(SceneLoaded(scenes, automationList));
|
||||
} else {
|
||||
const SceneError(message: '');
|
||||
}
|
||||
@ -29,16 +33,34 @@ class SceneBloc extends Bloc<SceneEvent, SceneState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSceneTrigger(SceneTrigger event, Emitter<SceneState> emit) async {
|
||||
Future<void> _onLoadAutomation(
|
||||
LoadAutomation event, Emitter<SceneState> emit) async {
|
||||
emit(SceneLoading());
|
||||
|
||||
try {
|
||||
if (event.unitId.isNotEmpty) {
|
||||
automationList = await SceneApi.getAutomationByUnitId(event.unitId);
|
||||
emit(SceneLoaded(scenes, automationList));
|
||||
} else {
|
||||
emit(const SceneError(message: 'Unit ID is empty'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(const SceneError(message: 'Something went wrong'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSceneTrigger(
|
||||
SceneTrigger event, Emitter<SceneState> emit) async {
|
||||
final currentState = state;
|
||||
if (currentState is SceneLoaded) {
|
||||
emit(SceneLoaded(currentState.scenes, loadingSceneId: event.sceneId));
|
||||
emit(SceneLoaded(currentState.scenes, currentState.automationList,
|
||||
loadingSceneId: event.sceneId));
|
||||
|
||||
try {
|
||||
final success = await SceneApi.triggerScene(event.sceneId);
|
||||
if (success) {
|
||||
emit(SceneTriggerSuccess(event.name));
|
||||
emit(SceneLoaded(currentState.scenes));
|
||||
emit(SceneLoaded(currentState.scenes, currentState.automationList));
|
||||
} else {
|
||||
emit(const SceneError(message: 'Something went wrong'));
|
||||
}
|
||||
|
@ -16,6 +16,15 @@ class LoadScenes extends SceneEvent {
|
||||
List<Object> get props => [unitId];
|
||||
}
|
||||
|
||||
class LoadAutomation extends SceneEvent {
|
||||
final String unitId;
|
||||
|
||||
const LoadAutomation(this.unitId);
|
||||
|
||||
@override
|
||||
List<Object> get props => [unitId];
|
||||
}
|
||||
|
||||
class SceneTrigger extends SceneEvent {
|
||||
final String sceneId;
|
||||
final String name;
|
||||
|
@ -13,12 +13,13 @@ class SceneLoading extends SceneState {}
|
||||
|
||||
class SceneLoaded extends SceneState {
|
||||
final List<ScenesModel> scenes;
|
||||
final List<ScenesModel> automationList;
|
||||
final String? loadingSceneId;
|
||||
|
||||
const SceneLoaded(this.scenes, {this.loadingSceneId});
|
||||
const SceneLoaded(this.scenes, this.automationList, {this.loadingSceneId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [scenes, loadingSceneId];
|
||||
List<Object?> get props => [scenes, loadingSceneId, automationList];
|
||||
}
|
||||
|
||||
class SceneError extends SceneState {
|
||||
|
@ -99,6 +99,16 @@ mixin SceneLogicHelper {
|
||||
));
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} else {
|
||||
if (isOnlyDelayOrDelayLast(actions)) {
|
||||
Navigator.pop(context);
|
||||
context.showCustomSnackbar(
|
||||
message: 'A single delay or delay-last operations are NOT allowed.',
|
||||
icon: const Icon(
|
||||
Icons.error,
|
||||
color: Colors.red,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Handle Scene Creation
|
||||
final createSceneModel = CreateSceneModel(
|
||||
@ -141,6 +151,7 @@ mixin SceneLogicHelper {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Widget getTheCorrectDialogBody(
|
||||
SceneStaticFunction taskItem, dynamic functionValue,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,9 +37,9 @@ class CreateAutomationModel {
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
Map<String, dynamic> toMap([String? automationId]) {
|
||||
return {
|
||||
'unitUuid': unitUuid,
|
||||
if (automationId == null) 'unitUuid': unitUuid,
|
||||
'automationName': automationName,
|
||||
'decisionExpr': decisionExpr,
|
||||
'effectiveTime': effectiveTime.toMap(),
|
||||
@ -61,7 +61,7 @@ class CreateAutomationModel {
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
String toJson([String? automationId]) => json.encode(toMap(automationId));
|
||||
|
||||
factory CreateAutomationModel.fromJson(String source) =>
|
||||
CreateAutomationModel.fromMap(json.decode(source));
|
||||
|
@ -6,6 +6,9 @@ class SceneDetailsModel {
|
||||
final String status;
|
||||
final String type;
|
||||
final List<Action> actions;
|
||||
final List<Condition>? conditions;
|
||||
final String? decisionExpr;
|
||||
final EffectiveTime? effectiveTime;
|
||||
|
||||
SceneDetailsModel({
|
||||
required this.id,
|
||||
@ -13,6 +16,9 @@ class SceneDetailsModel {
|
||||
required this.status,
|
||||
required this.type,
|
||||
required this.actions,
|
||||
this.conditions,
|
||||
this.decisionExpr,
|
||||
this.effectiveTime,
|
||||
});
|
||||
|
||||
factory SceneDetailsModel.fromRawJson(String str) =>
|
||||
@ -28,6 +34,14 @@ class SceneDetailsModel {
|
||||
type: json["type"],
|
||||
actions:
|
||||
List<Action>.from(json["actions"].map((x) => Action.fromJson(x))),
|
||||
conditions: json["conditions"] != null
|
||||
? List<Condition>.from(
|
||||
json["conditions"].map((x) => Condition.fromJson(x)))
|
||||
: null,
|
||||
decisionExpr: json["decisionExpr"],
|
||||
effectiveTime: json["effectiveTime"] != null
|
||||
? EffectiveTime.fromJson(json["effectiveTime"])
|
||||
: null,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
@ -36,6 +50,11 @@ class SceneDetailsModel {
|
||||
"status": status,
|
||||
"type": type,
|
||||
"actions": List<dynamic>.from(actions.map((x) => x.toJson())),
|
||||
"conditions": conditions != null
|
||||
? List<dynamic>.from(conditions!.map((x) => x.toJson()))
|
||||
: null,
|
||||
"decisionExpr": decisionExpr,
|
||||
"effectiveTime": effectiveTime?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -91,3 +110,93 @@ class ExecutorProperty {
|
||||
"delaySeconds": delaySeconds,
|
||||
};
|
||||
}
|
||||
|
||||
class Condition {
|
||||
final int code;
|
||||
final String entityId;
|
||||
final String entityType;
|
||||
final Expr expr;
|
||||
|
||||
Condition({
|
||||
required this.code,
|
||||
required this.entityId,
|
||||
required this.entityType,
|
||||
required this.expr,
|
||||
});
|
||||
|
||||
factory Condition.fromRawJson(String str) =>
|
||||
Condition.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Condition.fromJson(Map<String, dynamic> json) => Condition(
|
||||
code: json["code"],
|
||||
entityId: json["entityId"],
|
||||
entityType: json["entityType"],
|
||||
expr: Expr.fromJson(json["expr"]),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"code": code,
|
||||
"entityId": entityId,
|
||||
"entityType": entityType,
|
||||
"expr": expr.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
class Expr {
|
||||
final String comparator;
|
||||
final String statusCode;
|
||||
final dynamic statusValue;
|
||||
|
||||
Expr({
|
||||
required this.comparator,
|
||||
required this.statusCode,
|
||||
required this.statusValue,
|
||||
});
|
||||
|
||||
factory Expr.fromRawJson(String str) => Expr.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Expr.fromJson(Map<String, dynamic> json) => Expr(
|
||||
comparator: json["comparator"],
|
||||
statusCode: json["statusCode"],
|
||||
statusValue: json["statusValue"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"comparator": comparator,
|
||||
"statusCode": statusCode,
|
||||
"statusValue": statusValue,
|
||||
};
|
||||
}
|
||||
|
||||
class EffectiveTime {
|
||||
final String start;
|
||||
final String end;
|
||||
final String loops;
|
||||
|
||||
EffectiveTime({
|
||||
required this.start,
|
||||
required this.end,
|
||||
required this.loops,
|
||||
});
|
||||
|
||||
factory EffectiveTime.fromRawJson(String str) =>
|
||||
EffectiveTime.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory EffectiveTime.fromJson(Map<String, dynamic> json) => EffectiveTime(
|
||||
start: json["start"],
|
||||
end: json["end"],
|
||||
loops: json["loops"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"start": start,
|
||||
"end": end,
|
||||
"loops": loops,
|
||||
};
|
||||
}
|
||||
|
@ -37,9 +37,10 @@ enum Status { ENABLE }
|
||||
|
||||
final statusValues = EnumValues({"enable": Status.ENABLE});
|
||||
|
||||
enum Type { TAP_TO_RUN }
|
||||
enum Type { TAP_TO_RUN, automation }
|
||||
|
||||
final typeValues = EnumValues({"tap_to_run": Type.TAP_TO_RUN});
|
||||
final typeValues =
|
||||
EnumValues({"tap_to_run": Type.TAP_TO_RUN, "automation": Type.automation});
|
||||
|
||||
class EnumValues<T> {
|
||||
Map<String, T> map;
|
||||
|
@ -20,7 +20,7 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class DeviceFunctionsView extends StatelessWidget
|
||||
with SceneOperationsDataHelper, SceneLogicHelper {
|
||||
const DeviceFunctionsView({super.key});
|
||||
DeviceFunctionsView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -13,6 +13,8 @@ import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/navigation/navigate_to_route.dart';
|
||||
import 'package:syncrow_app/navigation/routing_constants.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
|
||||
@ -24,6 +26,7 @@ class SceneTasksView extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final sceneSettings = ModalRoute.of(context)!.settings.arguments
|
||||
as SceneSettingsRouteArguments;
|
||||
|
||||
return DefaultScaffold(
|
||||
title: sceneSettings.sceneName.isNotEmpty
|
||||
? sceneSettings.sceneName
|
||||
@ -107,8 +110,7 @@ class DeleteBottomSheetContent extends StatelessWidget {
|
||||
listener: (context, state) {
|
||||
if (state is DeleteSceneSuccess) {
|
||||
if (state.success) {
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
navigateToRoute(context, Routes.homeRoute);
|
||||
BlocProvider.of<SceneBloc>(context).add(
|
||||
LoadScenes(HomeCubit.getInstance().selectedSpace!.id!));
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
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/devices/view/widgets/scene_listview.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart';
|
||||
@ -9,7 +8,9 @@ import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_grid_
|
||||
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/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class SceneView extends StatelessWidget {
|
||||
final bool pageType;
|
||||
@ -19,7 +20,8 @@ class SceneView extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (BuildContext context) => SceneBloc()
|
||||
..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? '')),
|
||||
..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? ''))
|
||||
..add(LoadAutomation(HomeCubit.getInstance().selectedSpace?.id ?? '')),
|
||||
child: BlocBuilder<CreateSceneBloc, CreateSceneState>(
|
||||
builder: (context, state) {
|
||||
if (state is DeleteSceneSuccess) {
|
||||
@ -32,6 +34,8 @@ class SceneView extends StatelessWidget {
|
||||
if (state.success == true) {
|
||||
BlocProvider.of<SceneBloc>(context)
|
||||
.add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!));
|
||||
BlocProvider.of<SceneBloc>(context).add(
|
||||
LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!));
|
||||
}
|
||||
}
|
||||
return BlocListener<SceneBloc, SceneState>(
|
||||
@ -63,30 +67,70 @@ class SceneView extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
if (state is SceneLoaded) {
|
||||
if (state.scenes.isNotEmpty) {
|
||||
return pageType == false
|
||||
? Expanded(
|
||||
child: SceneGrid(
|
||||
scenes: state.scenes,
|
||||
loadingSceneId: state.loadingSceneId,
|
||||
),
|
||||
final scenes = state.scenes;
|
||||
final automationList = state.automationList;
|
||||
|
||||
return Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
ExpansionTile(
|
||||
tilePadding: const EdgeInsets.symmetric(
|
||||
horizontal: 6),
|
||||
initiallyExpanded: true,
|
||||
iconColor: ColorsManager.grayColor,
|
||||
title: const BodySmall(
|
||||
text: 'Tap to run routines'),
|
||||
children: [
|
||||
scenes.isNotEmpty
|
||||
? SceneGrid(
|
||||
scenes: scenes,
|
||||
loadingSceneId:
|
||||
state.loadingSceneId,
|
||||
disablePLayButton: false,
|
||||
)
|
||||
: Expanded(
|
||||
child: SceneListview(
|
||||
scenes: state.scenes,
|
||||
loadingSceneId: state.loadingSceneId,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return const Expanded(
|
||||
child: Center(
|
||||
: const Center(
|
||||
child: BodyMedium(
|
||||
text: 'No scenes have been added yet',
|
||||
text:
|
||||
'No scenes have been added yet',
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
ExpansionTile(
|
||||
initiallyExpanded: true,
|
||||
iconColor: ColorsManager.grayColor,
|
||||
tilePadding: const EdgeInsets.symmetric(
|
||||
horizontal: 6),
|
||||
title: const BodySmall(text: 'Automation'),
|
||||
children: [
|
||||
automationList.isNotEmpty
|
||||
? SceneGrid(
|
||||
scenes: automationList,
|
||||
loadingSceneId:
|
||||
state.loadingSceneId,
|
||||
disablePLayButton: true,
|
||||
)
|
||||
: const Center(
|
||||
child: BodyMedium(
|
||||
text:
|
||||
'No automations have been added yet',
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
|
@ -62,7 +62,7 @@ class _AlertDialogSliderStepsState extends State<AlertDialogSliderSteps> {
|
||||
double _normalizeValue(dynamic value) {
|
||||
if (widget.taskItem.code == "temp_set" ||
|
||||
widget.taskItem.code == "temp_current") {
|
||||
return (value as double) / 10;
|
||||
return (value) / 10;
|
||||
}
|
||||
return value.toDouble();
|
||||
}
|
||||
|
@ -83,8 +83,12 @@ class IFDefaultContainer extends StatelessWidget {
|
||||
BlocBuilder<CreateSceneBloc, CreateSceneState>(
|
||||
builder: (context, state) {
|
||||
String conditionText = "When any condition is met";
|
||||
if (state is ConditionSelectedState) {
|
||||
conditionText = state.condition;
|
||||
if (state is AddSceneTask) {
|
||||
if (state.condition == 'or') {
|
||||
conditionText = "When any condition is met";
|
||||
} else {
|
||||
conditionText = "When all conditions are met";
|
||||
}
|
||||
}
|
||||
return SizedBox(
|
||||
width: context.width * 0.6,
|
||||
|
@ -1,21 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/scene/model/scenes_model.dart';
|
||||
import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_item.dart';
|
||||
|
||||
class SceneGrid extends StatelessWidget {
|
||||
final List<ScenesModel> scenes;
|
||||
final List<dynamic> scenes;
|
||||
final String? loadingSceneId;
|
||||
final bool disablePLayButton;
|
||||
const SceneGrid({
|
||||
required this.scenes,
|
||||
required this.loadingSceneId,
|
||||
super.key,
|
||||
required this.disablePLayButton,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return
|
||||
GridView.builder(
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 12,
|
||||
@ -26,7 +27,10 @@ class SceneGrid extends StatelessWidget {
|
||||
itemBuilder: (context, index) {
|
||||
final scene = scenes[index];
|
||||
final isLoading = loadingSceneId == scene.id;
|
||||
return SceneItem(scene: scene, isLoading: isLoading);
|
||||
return SceneItem(
|
||||
scene: scene,
|
||||
isLoading: isLoading,
|
||||
disablePLayButton: disablePLayButton);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
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';
|
||||
@ -20,10 +19,10 @@ class SceneHeader extends StatelessWidget {
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
BodySmall(
|
||||
text: StringsManager.tapToRunRoutine,
|
||||
),
|
||||
// SizedBox(height: 20),
|
||||
// BodySmall(
|
||||
// text: StringsManager.tapToRunRoutine,
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -16,11 +16,13 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
class SceneItem extends StatelessWidget {
|
||||
final ScenesModel scene;
|
||||
final bool isLoading;
|
||||
final bool disablePLayButton;
|
||||
|
||||
const SceneItem({
|
||||
required this.scene,
|
||||
required this.isLoading,
|
||||
super.key,
|
||||
required this.disablePLayButton,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -31,16 +33,28 @@ class SceneItem extends StatelessWidget {
|
||||
context,
|
||||
Routes.sceneTasksRoute,
|
||||
arguments: SceneSettingsRouteArguments(
|
||||
sceneType: CreateSceneEnum.tabToRun.name,
|
||||
sceneType: disablePLayButton == false
|
||||
? CreateSceneEnum.tabToRun.name
|
||||
: CreateSceneEnum.deviceStatusChanges.name,
|
||||
sceneId: scene.id,
|
||||
sceneName: scene.name,
|
||||
),
|
||||
);
|
||||
if (disablePLayButton == false) {
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(FetchSceneTasksEvent(sceneId: scene.id));
|
||||
.add(const SceneTypeEvent(CreateSceneEnum.tabToRun));
|
||||
BlocProvider.of<CreateSceneBloc>(context).add(
|
||||
FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false));
|
||||
} else {
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(const SceneTypeEvent(CreateSceneEnum.deviceStatusChanges));
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: true));
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
@ -52,7 +66,9 @@ class SceneItem extends StatelessWidget {
|
||||
Assets.assetsIconsLogo,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
IconButton(
|
||||
Visibility(
|
||||
visible: disablePLayButton == false,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () {
|
||||
context
|
||||
@ -69,6 +85,7 @@ class SceneItem extends StatelessWidget {
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
@ -24,53 +24,66 @@ class Router {
|
||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
||||
switch (settings.name) {
|
||||
case Routes.splash:
|
||||
return MaterialPageRoute(builder: (_) => const SplashView(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const SplashView(), settings: settings);
|
||||
|
||||
// case Routes.devicesRoute:
|
||||
// return MaterialPageRoute(
|
||||
// builder: (_) => const DevicesView(), settings: settings);
|
||||
|
||||
case Routes.profileRoute:
|
||||
return MaterialPageRoute(builder: (_) => const ProfileView(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const ProfileView(), settings: settings);
|
||||
|
||||
case Routes.sceneRoute:
|
||||
return MaterialPageRoute(builder: (_) => const SceneView(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const SceneView(), settings: settings);
|
||||
|
||||
case Routes.layoutRoute:
|
||||
return MaterialPageRoute(builder: (_) => const LayoutPage(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const LayoutPage(), settings: settings);
|
||||
|
||||
case Routes.authLogin:
|
||||
return MaterialPageRoute(builder: (_) => const LoginView(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const LoginView(), settings: settings);
|
||||
|
||||
case Routes.otpRoute:
|
||||
return MaterialPageRoute(builder: (_) => const OtpView(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const OtpView(), settings: settings);
|
||||
|
||||
case Routes.authSignUp:
|
||||
return MaterialPageRoute(builder: (_) => const SignUpView(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const SignUpView(), settings: settings);
|
||||
|
||||
case Routes.dashboardRoute:
|
||||
return MaterialPageRoute(builder: (_) => const DashboardView(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const DashboardView(), settings: settings);
|
||||
|
||||
case Routes.homeRoute:
|
||||
return MaterialPageRoute(builder: (_) => const AppLayout(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const AppLayout(), settings: settings);
|
||||
|
||||
case Routes.menuRoute:
|
||||
return MaterialPageRoute(builder: (_) => const MenuView(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const MenuView(), settings: settings);
|
||||
|
||||
case Routes.createUnit:
|
||||
return MaterialPageRoute(builder: (_) => const CreateUnitView(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const CreateUnitView(), settings: settings);
|
||||
case Routes.sceneTasksRoute:
|
||||
return MaterialPageRoute(builder: (_) => const SceneTasksView(), settings: settings);
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const SceneTasksView(), settings: settings);
|
||||
case Routes.sceneControlDevicesRoute:
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (BuildContext context) => DeviceManagerBloc()..add(FetchAllDevices()),
|
||||
create: (BuildContext context) =>
|
||||
DeviceManagerBloc()..add(FetchAllDevices()),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (BuildContext context) =>
|
||||
TabBarBloc(context.read<DeviceManagerBloc>())
|
||||
create: (BuildContext context) => TabBarBloc(
|
||||
context.read<DeviceManagerBloc>())
|
||||
..add(const TabChanged(selectedIndex: 0, roomId: '-1')),
|
||||
),
|
||||
],
|
||||
@ -79,7 +92,7 @@ class Router {
|
||||
settings: settings);
|
||||
case Routes.deviceFunctionsRoute:
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => const DeviceFunctionsView(),
|
||||
builder: (_) => DeviceFunctionsView(),
|
||||
settings: settings,
|
||||
);
|
||||
default:
|
||||
|
@ -129,7 +129,7 @@ abstract class ApiEndpoints {
|
||||
|
||||
static const String assignDeviceToRoom = '$baseUrl/device/room';
|
||||
|
||||
/// Scene API ////////////////////
|
||||
/// Scene & Automation API ////////////////////
|
||||
/// POST
|
||||
static const String createScene = '$baseUrl/scene/tap-to-run';
|
||||
static const String triggerScene =
|
||||
@ -142,9 +142,16 @@ abstract class ApiEndpoints {
|
||||
|
||||
static const String getScene = '$baseUrl/scene/tap-to-run/details/{sceneId}';
|
||||
|
||||
static const String getUnitAutomation = '$baseUrl/automation/{unitUuid}';
|
||||
|
||||
static const String getAutomationDetails = '$baseUrl/automation/details/{automationId}';
|
||||
|
||||
/// PUT
|
||||
static const String updateScene = '$baseUrl/scene/tap-to-run/{sceneId}';
|
||||
|
||||
static const String updateAutomation =
|
||||
'$baseUrl/automation/{automationId}';
|
||||
|
||||
/// DELETE
|
||||
static const String deleteScene =
|
||||
'$baseUrl/scene/tap-to-run/{unitUuid}/{sceneId}';
|
||||
|
@ -8,6 +8,7 @@ import 'package:syncrow_app/services/api/http_service.dart';
|
||||
class SceneApi {
|
||||
static final HTTPService _httpService = HTTPService();
|
||||
|
||||
//create scene
|
||||
static Future<Map<String, dynamic>> createScene(
|
||||
CreateSceneModel createSceneModel) async {
|
||||
try {
|
||||
@ -25,6 +26,7 @@ class SceneApi {
|
||||
}
|
||||
}
|
||||
|
||||
// create automation
|
||||
static Future<Map<String, dynamic>> createAutomation(
|
||||
CreateAutomationModel createAutomationModel) async {
|
||||
try {
|
||||
@ -42,6 +44,8 @@ class SceneApi {
|
||||
}
|
||||
}
|
||||
|
||||
//get scene by unit id
|
||||
|
||||
static Future<List<ScenesModel>> getScenesByUnitId(String unitId) async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
@ -61,6 +65,27 @@ class SceneApi {
|
||||
}
|
||||
}
|
||||
|
||||
//getAutomation
|
||||
|
||||
static Future<List<ScenesModel>> getAutomationByUnitId(String unitId) async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getUnitAutomation.replaceAll('{unitUuid}', unitId),
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
List<ScenesModel> scenes = [];
|
||||
for (var scene in json) {
|
||||
scenes.add(ScenesModel.fromJson(scene));
|
||||
}
|
||||
return scenes;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> triggerScene(String sceneId) async {
|
||||
try {
|
||||
final response = await _httpService.post(
|
||||
@ -74,6 +99,22 @@ class SceneApi {
|
||||
}
|
||||
}
|
||||
|
||||
//automation details
|
||||
static Future<SceneDetailsModel> getAutomationDetails(
|
||||
String automationId) async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getAutomationDetails
|
||||
.replaceAll('{automationId}', automationId),
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) => SceneDetailsModel.fromJson(json),
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
//getScene
|
||||
|
||||
static Future<SceneDetailsModel> getSceneDetails(String sceneId) async {
|
||||
@ -106,6 +147,25 @@ class SceneApi {
|
||||
}
|
||||
}
|
||||
|
||||
//update automation
|
||||
static updateAutomation(
|
||||
CreateAutomationModel createAutomationModel, String automationId) async {
|
||||
try {
|
||||
final response = await _httpService.put(
|
||||
path: ApiEndpoints.updateAutomation
|
||||
.replaceAll('{automationId}', automationId),
|
||||
body: createAutomationModel
|
||||
.toJson(automationId.isNotEmpty == true ? automationId : null),
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
//delete Scene
|
||||
|
||||
static Future<bool> deleteScene(
|
||||
|
Reference in New Issue
Block a user