diff --git a/README.md b/README.md index de05da5..83b2546 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ flutter clean flutter pub get +flutter packages pub get + flutter pub run build_runner build --delete-conflicting-outputs Le fichier est dans le projet. diff --git a/android/app/build.gradle b/android/app/build.gradle index 59b6864..94e78f6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -24,9 +24,10 @@ if (flutterVersionName == null) { apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" +apply plugin: 'com.google.gms.google-services' android { - compileSdkVersion 32 + compileSdkVersion 33 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -110,4 +111,14 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + // Import the Firebase BoM + implementation platform('com.google.firebase:firebase-bom:32.2.0') + + // TODO: Add the dependencies for Firebase products you want to use + // When using the BoM, don't specify versions in Firebase dependencies + implementation 'com.google.firebase:firebase-analytics' + implementation 'com.google.firebase:firebase-messaging' + + // Add the dependencies for any other desired Firebase products + // https://firebase.google.com/docs/android/setup#available-libraries } diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..849b610 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,39 @@ +{ + "project_info": { + "project_number": "246480640287", + "project_id": "unov---myhomie", + "storage_bucket": "unov---myhomie.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:246480640287:android:55411924cff9636eb9382a", + "android_client_info": { + "package_name": "be.unov.myhomie" + } + }, + "oauth_client": [ + { + "client_id": "246480640287-iu9np24gr3oaab8gs0c5o8n5at1e3it1.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyD_3Acf-JR2Uw4lCmCR_oKh6U-AKcuYj4o" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "246480640287-iu9np24gr3oaab8gs0c5o8n5at1e3it1.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 9441ef4..59f94ba 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="be.unov.myhomie"> diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 15019a0..4e5c213 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,9 +1,9 @@ + package="be.unov.myhomie"> + + + + + diff --git a/android/app/src/main/kotlin/be/myhomie/myhomie_app/MainActivity.kt b/android/app/src/main/kotlin/be/myhomie/myhomie_app/MainActivity.kt index 5952bfd..bc524f3 100644 --- a/android/app/src/main/kotlin/be/myhomie/myhomie_app/MainActivity.kt +++ b/android/app/src/main/kotlin/be/myhomie/myhomie_app/MainActivity.kt @@ -1,4 +1,4 @@ -package be.myhomie.myhomie_app +package be.unov.myhomie import io.flutter.embedding.android.FlutterActivity diff --git a/android/app/src/main/res/drawable/logo.png b/android/app/src/main/res/drawable/logo.png new file mode 100644 index 0000000..c6f093a Binary files /dev/null and b/android/app/src/main/res/drawable/logo.png differ diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f341415 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #6025b6 + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..44a2fa5 --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,15 @@ + + + MyHomie + + + + + + + + main + \ No newline at end of file diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml index 9441ef4..59f94ba 100644 --- a/android/app/src/profile/AndroidManifest.xml +++ b/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="be.unov.myhomie"> diff --git a/android/app/version.properties b/android/app/version.properties index b171b88..8a5e2cd 100644 --- a/android/app/version.properties +++ b/android/app/version.properties @@ -1,5 +1,5 @@ -#Fri Mar 17 18:48:37 CET 2023 -VERSION_BUILD=19 +#Fri Jul 28 17:03:08 CEST 2023 +VERSION_BUILD=32 VERSION_MAJOR=1 VERSION_MINOR=0 VERSION_PATCH=0 diff --git a/android/build.gradle b/android/build.gradle index 714549c..ed2d527 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,6 +8,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.google.gms:google-services:4.3.15' } } diff --git a/lib/Components/Alarms/getCurrentAlarmModeIcon.dart b/lib/Components/Alarms/getCurrentAlarmModeIcon.dart new file mode 100644 index 0000000..4d1cf56 --- /dev/null +++ b/lib/Components/Alarms/getCurrentAlarmModeIcon.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:mycore_api/api.dart'; + +IconData getAlarmModeIcon(AlarmModeDTO alarmModeDTO) { + switch(alarmModeDTO.type) { + case AlarmType.desarmed: + return Icons.lock_open; + case AlarmType.absent: + return Icons.person_off; + case AlarmType.home: + return Icons.home_outlined; + case AlarmType.geolocalized: + return Icons.location_on; + case AlarmType.programmed: + return Icons.schedule; + case AlarmType.custom: + return Icons.tune; + default: + return Icons.device_unknown; + } +} \ No newline at end of file diff --git a/lib/Components/Custom_Navigation_Bar/CustomAppBar.dart b/lib/Components/Custom_Navigation_Bar/CustomAppBar.dart new file mode 100644 index 0000000..c9b2a47 --- /dev/null +++ b/lib/Components/Custom_Navigation_Bar/CustomAppBar.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:myhomie_app/Models/homieContext.dart'; +import 'package:myhomie_app/app_context.dart'; +import 'package:provider/provider.dart'; + +class CustomAppBar extends StatefulWidget implements PreferredSizeWidget { + CustomAppBar({Key? key, required this.title, this.titleIcon, this.isTextSizeButton}); + + final String title; + final IconData? titleIcon; + bool? isTextSizeButton = false; + final double _preferredHeight = 50; + + @override + State createState() => _CustomAppBarState(); + + @override + Size get preferredSize => Size.fromHeight(_preferredHeight); +} + +class _CustomAppBarState extends State { + @override + Widget build(BuildContext context) { + final appContext = Provider.of(context); + HomieAppContext homieAppContext = appContext.getContext(); + + final notchInset = MediaQuery.of(context).padding; + return AppBar( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(widget.title), + Padding( + padding: const EdgeInsets.all(8.0), + child: widget.titleIcon != null ? Icon(widget.titleIcon) : null, + ), + ], + ), + centerTitle: true, + /*leading: widget.isHomeButton ? IconButton( + icon: const Icon(Icons.home), + onPressed: () { + // Set new State + setState(() { + visitAppContext.configuration = null; + visitAppContext.isScanningBeacons = false; + //Navigator.of(context).pop(); + Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute( + builder: (context) => const HomePage(), + ),(route) => false); + }); + } + ) : null,*/ + actions: [ + ], + flexibleSpace: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerRight, + end: Alignment.centerLeft, + colors: [ + /*Color(0xFFDD79C2), + Color(0xFFB65FBE), + Color(0xFF9146BA), + Color(0xFF7633B8), + Color(0xFF6528B6), + Color(0xFF6025B6)*/ + Color(0xFF306bac), + Color(0xFF308aae), + Color(0xFF309cb0), + ], + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Components/Custom_Navigation_Bar/custom_bottom_navigation_bar.dart b/lib/Components/Custom_Navigation_Bar/custom_bottom_navigation_bar.dart index 8e4419a..fe39a1c 100644 --- a/lib/Components/Custom_Navigation_Bar/custom_bottom_navigation_bar.dart +++ b/lib/Components/Custom_Navigation_Bar/custom_bottom_navigation_bar.dart @@ -67,8 +67,8 @@ class _CustomBottomNavigationBarState extends State { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - CustomNavItem(setPage: setPage, icon: NavItemIcon.energy, id: 3), - CustomNavItem(setPage: setPage, icon: NavItemIcon.profile, id: 4), + CustomNavItem(setPage: setPage, icon: NavItemIcon.devices, id: 3), + CustomNavItem(setPage: setPage, icon: NavItemIcon.energy, id: 4), ], ), ), diff --git a/lib/Components/Custom_Navigation_Bar/custom_nav_item.dart b/lib/Components/Custom_Navigation_Bar/custom_nav_item.dart index c9dad62..3ce9471 100644 --- a/lib/Components/Custom_Navigation_Bar/custom_nav_item.dart +++ b/lib/Components/Custom_Navigation_Bar/custom_nav_item.dart @@ -54,22 +54,28 @@ class CustomNavItem extends StatelessWidget { size: 25, color: Colors.white, ); - case 'energy': + case 'devices': return Icon( - Icons.power, + Icons.devices_other, size: 25, color: Colors.white, ); - case 'profile': //Todo show user image ? + case 'energy': + return Icon( + Icons.bolt, + size: 25, + color: Colors.white, + ); + /*case 'profile': //Todo show user image ? return Icon( Icons.person, size: 25, color: Colors.white, - ); + );*/ } //return new FlareActor("assets/animations/bottom_navigation/"+ icon.toString().split(".").last +".flr", alignment:Alignment.center, fit:BoxFit.contain, animation: index == icon.index ? "Selected" : "Unselected"); //icon.index } } -enum NavItemIcon { home, security, automations, energy, profile } \ No newline at end of file +enum NavItemIcon { home, security, automations, devices, energy, profile } \ No newline at end of file diff --git a/lib/Components/check_input_container.dart b/lib/Components/check_input_container.dart new file mode 100644 index 0000000..0fa5cd7 --- /dev/null +++ b/lib/Components/check_input_container.dart @@ -0,0 +1,86 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; + +import '../constants.dart'; + +class CheckInputContainer extends StatefulWidget { + final bool isChecked; + final IconData? icon; + final String label; + final ValueChanged onChanged; + final double fontSize; + const CheckInputContainer({ + Key? key, + required this.isChecked, + this.icon, + required this.label, + required this.onChanged, + this.fontSize = 20 + }) : super(key: key); + + @override + _CheckInputContainerState createState() => _CheckInputContainerState(); +} + +class _CheckInputContainerState extends State { + bool? isChecked; + + @override + void initState() { + setState(() { + isChecked = widget.isChecked; + }); + super.initState(); + } + @override + Widget build(BuildContext context) { + + return Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + children: [ + if(widget.icon != null) + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Icon( + widget.icon, + color: kMainColor, + size: 25.0, + ), + ), + if(widget.label != null) + AutoSizeText( + widget.label, + style: new TextStyle(fontSize: widget.fontSize, fontWeight: FontWeight.w300), + maxLines: 2, + maxFontSize: widget.fontSize, + textAlign: TextAlign.center, + ), + ], + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + width: 50, + height: 50, + child: Checkbox( + shape: CircleBorder(), + value: isChecked, + checkColor: Colors.white, + activeColor: kMainColor, + onChanged: (bool? value) { + setState(() { + isChecked = value; + }); + widget.onChanged(value!); + }, + ), + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/Components/custom_dialog.dart b/lib/Components/custom_dialog.dart new file mode 100644 index 0000000..1cf7d1b --- /dev/null +++ b/lib/Components/custom_dialog.dart @@ -0,0 +1,138 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:myhomie_app/Components/Buttons/rounded_button.dart'; +import 'package:myhomie_app/constants.dart'; + +class Consts { + Consts._(); + + static const double padding = 16.0; + static const double avatarRadius = 70.0; +} + +class CustomDialog extends StatelessWidget { + String title, description, buttonText; + String? iconSvg; + + Function onPress; + + CustomDialog({ + required this.title, + required this.description, + required this.buttonText, + this.iconSvg, + required this.onPress + }); + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(Consts.padding), + ), + elevation: 0.0, + backgroundColor: Colors.transparent, + child: dialogContent(context), + ); + } + + dialogContent(BuildContext context) { + Size size = MediaQuery.of(context).size; + return Stack( + children: [ + Container( + padding: EdgeInsets.only( + top: Consts.avatarRadius + Consts.padding, + bottom: Consts.padding, + left: Consts.padding, + right: Consts.padding, + ), + margin: EdgeInsets.only(top: Consts.avatarRadius), + decoration: new BoxDecoration( + color: Colors.white, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(Consts.padding), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 10.0, + offset: const Offset(0.0, 10.0), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, // To make the card compact + children: [ + SizedBox(height: 16.0), + Text( + description, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.0, + ), + ), + SizedBox(height: 22.0), + Align( + alignment: Alignment.bottomRight, + child: RoundedButton( + press: () { + onPress(); + Navigator.of(context).pop(); // To close the dialog + }, + text: buttonText, + ), + ), + ], + ), + ), + Positioned( + left: Consts.padding, + right: Consts.padding, + child: Container( + width: size.width *0.18, + height: size.height *0.18, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color(0xFFDD79C2), + Color(0xFFB65FBE), + Color(0xFF9146BA), + Color(0xFF7633B8), + Color(0xFF6528B6), + Color(0xFF6025B6) + ], + ), + //borderRadius: BorderRadius.circular(10.0), + ), + child: Center( + child: Stack( + children: [ + if (iconSvg != null) + SvgPicture.asset( + iconSvg!, + height: 65, + color: Colors.white, + ) + else + Container( + width: size.width*0.3, + height: size.width*0.23, + child: Center( + child: Text( + title, + style: kHeadingTextStyle, + textAlign: TextAlign.center, + overflow: TextOverflow.clip + ), + ), + ) + ])) + ), + ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/Components/rounded_input_field.dart b/lib/Components/rounded_input_field.dart index 592e1c0..3b6a69d 100644 --- a/lib/Components/rounded_input_field.dart +++ b/lib/Components/rounded_input_field.dart @@ -9,6 +9,7 @@ class RoundedInputField extends StatelessWidget { final String? initialValue; final Color color, textColor, iconColor; final int? maxLength; + final double? fontSize; const RoundedInputField({ Key? key, this.hintText, @@ -19,6 +20,7 @@ class RoundedInputField extends StatelessWidget { this.iconColor = kTitleTextColor, // TODO this.onChanged, this.maxLength, // 50 + this.fontSize = 20, }) : super(key: key); @override @@ -30,7 +32,7 @@ class RoundedInputField extends StatelessWidget { initialValue: initialValue, cursorColor: textColor, maxLength: maxLength, - style: TextStyle(fontSize: 20, color: textColor), + style: TextStyle(fontSize: fontSize, color: textColor), decoration: InputDecoration( icon: icon != null ? Icon( icon, diff --git a/lib/Components/string_input_container.dart b/lib/Components/string_input_container.dart new file mode 100644 index 0000000..ceec9f6 --- /dev/null +++ b/lib/Components/string_input_container.dart @@ -0,0 +1,65 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:myhomie_app/Components/rounded_input_field.dart'; +import 'package:myhomie_app/constants.dart'; + +class StringInputContainer extends StatelessWidget { + final Color color; + final Color textColor; + final String label; + final String? initialValue; + final ValueChanged? onChanged; + final bool isUrl; + final bool isSmall; + final int maxLength; + final double fontSize; + final double fontSizeText; + const StringInputContainer({ + Key? key, + this.color = kMainColor, + this.textColor = Colors.black, + required this.label, + this.initialValue, + this.onChanged, + this.isUrl = false, + this.isSmall = false, + this.maxLength = 50, + this.fontSize = 25, + this.fontSizeText = 20, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + return Container( + child: Row( + children: [ + Align( + alignment: AlignmentDirectional.centerStart, + child: AutoSizeText( + label, + style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.w300), + maxLines: 2, + maxFontSize: fontSize, + textAlign: TextAlign.center, + ), + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + width: isUrl ? size.width *0.6 : isSmall ? size.width *0.1 : size.width *0.65, + child: RoundedInputField( + color: color, + textColor: textColor, + fontSize: fontSizeText, + initialValue: initialValue, + onChanged: onChanged, + maxLength: maxLength, + ), + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/Helpers/NotificationManager.dart b/lib/Helpers/NotificationManager.dart new file mode 100644 index 0000000..f886795 --- /dev/null +++ b/lib/Helpers/NotificationManager.dart @@ -0,0 +1,46 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:myhomie_app/Components/custom_dialog.dart'; +import 'package:myhomie_app/Helpers/PushNotificationService.dart'; + +class NotificationManager { + + static BuildContext? _context; + static String defaultButtonLabel = "Merci !"; + + static init({required BuildContext context}) { + _context = context; + } + + static handleDataMsg(Map data){ + } + + static handleNotificationMsg(PushNotificationMessage message) { + //We can add showDialog key in future + /*if (data.containsKey('showDialog')) { + // Handle data message with dialog + _showDialog(data); + }*/ + _showDialog(message: message); + } + + + static _showDialog({required PushNotificationMessage message}) { + var buttonLabel = message.data.labelButton == null ? defaultButtonLabel : message.data.labelButton; + + showDialog( + context: _context!, + builder: (BuildContext context) => CustomDialog( + title: message.title, + description: message.body, + buttonText: buttonLabel!, + onPress: () => { + /*if (message.data.type != null) { + PageSwitcher(message.data.type) + }*/ + } + ), + ); + } + +} \ No newline at end of file diff --git a/lib/Helpers/PushNotificationService.dart b/lib/Helpers/PushNotificationService.dart new file mode 100644 index 0000000..d808bfc --- /dev/null +++ b/lib/Helpers/PushNotificationService.dart @@ -0,0 +1,168 @@ +import 'dart:io'; + +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:myhomie_app/Helpers/NotificationManager.dart'; +import 'package:myhomie_app/Models/homieContext.dart'; + +import '../constants.dart'; + +class PushNotificationService { + //final FirebaseMessaging _fcm; + + PushNotificationService(); //this._fcm + + goToPage(Map message) { + var notification = PushNotificationMessage( + title: message['notification']['title'], + body: message['notification']['body'], + data: DataNotification( + collapseKey: message['data']['title'], + type: message['data']['type']) + ); + + //PageSwitcher(notification.data.type); + + Fluttertoast.showToast( + msg: 'onResume or onLaunch '+ notification.title + ' : ' + notification.body, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + timeInSecForIosWeb: 1, + backgroundColor: kMainColor, + textColor: Colors.white, + fontSize: 16.0 + ); + } + + Future initialise(HomieAppContext? homieAppContext, {required BuildContext context}) async { + if (Platform.isIOS) { + //_fcm.requestPermission(); // IosNotificationSettings + FirebaseMessaging.instance.requestPermission(); + } + + //_fcm.subscribeToTopic("main"); + + FirebaseMessaging.instance.subscribeToTopic("main"); + + if(homieAppContext != null) { + if (homieAppContext.userId != null) { + print('MyHomie USER ID for fcm notification = ' + homieAppContext.userId.toString()); + FirebaseMessaging.instance.subscribeToTopic(homieAppContext.userId!); + } + } + + // If you want to test the push notification locally, + // you need to get the token and input to the Firebase console + // https://console.firebase.google.com/project/YOUR_PROJECT_ID/notification/compose + String? token = await FirebaseMessaging.instance.getToken(); + print("FirebaseMessaging token: $token"); + + FirebaseMessaging.instance.getInitialMessage().then( + (value) { + debugPrint("COUCOUCOUC $value"); + }, + ); + + FirebaseMessaging.onMessage.listen((data) { + var notification; + print("onMessage: $data"); + print(data); + + if (Platform.isAndroid) { + notification = PushNotificationMessage( + title: data.notification!.title!, + body: data.notification!.body!, + data: DataNotification( + //labelButton: message['data']['labelButton'], + type: data.messageType!) + ); + } + /*if (Platform.isIOS) { + notification = PushNotificationMessage( + title: message['aps']['alert']['title'], + body: message['aps']['alert']['body'], + data: DataNotification( + labelButton: message['labelButton'], + type: message['type']) + ); + }*/ + + NotificationManager.handleNotificationMsg(notification); + // show notification UI here + }); + + FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { + print('A new onMessageOpenedApp event was published!'); + + /*Navigator.pushNamed( + context, + '/message', + arguments: MessageArguments(message, true), + );*/ + }); + + /*_fcm.configure( + onMessage: (Map message) async { + + + }, + onLaunch: (Map message) async { + print("onLaunch: $message"); + this.goToPage(message); + + }, + onResume: (Map message) async { + print("onResume: $message"); + this.goToPage(message); + }, + );*/ + } +} + +class PushNotificationMessage { + final String title; + final String body; + final DataNotification data; + PushNotificationMessage({ + required this.title, + required this.body, + required this.data + }); +} + +class DataNotification { + final String? collapseKey; + final String type; + final String? labelButton; + DataNotification({ + this.collapseKey, + required this.type, + this.labelButton + }); +} + +/*PageSwitcher(String type) { + switch(type) { + case "home": + pageController.jumpToPage(NavItemIcon.home.index); + break; + case "advices": + // In case of new advice, need to reload advices list automatically + pageController.jumpToPage(NavItemIcon.advices.index); + break; + case "scan": + pageController.jumpToPage(NavItemIcon.scan.index); + break; + case "shop": + pageController.jumpToPage(NavItemIcon.wallet.index); + break; + case "profile": + pageController.jumpToPage(NavItemIcon.profile.index); + break; + default: + pageController.jumpToPage(NavItemIcon.home.index); + break; + } +}*/ \ No newline at end of file diff --git a/lib/Screens/Main/Automations/automationDetailPage.dart b/lib/Screens/Main/Automations/automationDetailPage.dart new file mode 100644 index 0000000..f1fb5b5 --- /dev/null +++ b/lib/Screens/Main/Automations/automationDetailPage.dart @@ -0,0 +1,213 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:mycore_api/api.dart'; + +import 'package:mycore_api/api.dart' as API; +import 'package:myhomie_app/Components/Alarms/getCurrentAlarmModeIcon.dart'; +import 'package:myhomie_app/Components/Custom_Navigation_Bar/CustomAppBar.dart'; +import 'package:myhomie_app/Components/check_input_container.dart'; +import 'package:myhomie_app/Components/loading_common.dart'; +import 'package:myhomie_app/Components/string_input_container.dart'; +import 'package:myhomie_app/Models/homieContext.dart'; +import 'package:myhomie_app/app_context.dart'; +import 'package:myhomie_app/constants.dart'; +import 'package:provider/provider.dart'; + +class AutomationDetailPage extends StatefulWidget { + const AutomationDetailPage({Key? key, required this.automationDTO}) : super(key: key); + + final API.AutomationDTO automationDTO; + + @override + State createState() => _AutomationDetailPageState(); +} + +class _AutomationDetailPageState extends State { + final GlobalKey _scaffoldKey = GlobalKey(); + AutomationDetailDTO? automationDetailDTO; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final appContext = Provider.of(context); + Size size = MediaQuery.of(context).size; + //final notchInset = MediaQuery.of(context).padding; + + HomieAppContext homieAppContext = appContext.getContext(); + + debugPrint(widget.automationDTO.id!); + + return Scaffold( + key: _scaffoldKey, + appBar: CustomAppBar( + title: widget.automationDTO.name!, + //titleIcon: getAlarmModeIcon(widget.alarmMode), + isTextSizeButton: true, + ), + body: FutureBuilder( + future: homieAppContext.clientAPI.automationApi!.automationGetDetail(widget.automationDTO.id!), + builder: (context, AsyncSnapshot snapshot) { + if(snapshot.data != null ) { + automationDetailDTO = snapshot.data; + + return SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: size.width *1.1, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: StringInputContainer( + label: "Nom:", + color: kMainColor, + textColor: Colors.white, + initialValue: automationDetailDTO!.name, + onChanged: (String value) { + setState(() { + automationDetailDTO!.name = value; + // TODO SAVE or not + }); + }, + ), + ), + ), + /*Padding( + padding: const EdgeInsets.all(8.0), + child: CheckInputContainer( + icon: Icons.notifications_active, + label: "Notification :", + fontSize: 20, + isChecked: alarmModeDetailDTO!.notification!, + onChanged: (bool value) { + alarmModeDetailDTO!.notification = value; + }, + ), + ),*/ + ExpansionTile( + title: Text( + "Triggers", + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold + ), + ), + children: [ + for(var trigger in automationDetailDTO!.triggers!) + if(automationDetailDTO!.devices!.where((d) => d.id == trigger.deviceId).isNotEmpty) + ExpansionTile( + title: Text(automationDetailDTO!.devices!.firstWhere((d) => d.id == trigger.deviceId).name!), + children: [ + ListTile( + title: Text("name: "+trigger.stateName! + " - " + "value: "+ trigger.stateValue!), + ) + ], + ), + ListTile( + title: Text("Ajouter +", style: TextStyle(), textAlign: TextAlign.center), + tileColor: Colors.lightGreen, + onTap: () { + // Show popup ajout + debugPrint("Add device to trigger popup"); + }, + ) + ], + ), + ExpansionTile( + title: Text( + "Actions", + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold + ), + ), + children: [ + for(var action in automationDetailDTO!.actions!) + actionTile(action), + ListTile( + title: Text("Ajouter +", style: TextStyle(), textAlign: TextAlign.center), + tileColor: Colors.lightGreen, + onTap: () { + // Show popup ajout + debugPrint("Add device to trigger popup"); + }, + ) + ], + ), + + Text("Actions"), + for(var device in automationDetailDTO!.devices!.where((d) => automationDetailDTO!.actions!.map((t)=> t.deviceId).contains(d.id))) + Text(device.name!), + Text(automationDetailDTO!.actions!.toString()), + Text("Devices"), + Text(automationDetailDTO!.devices!.toString()), + ], + ), + ); + } else { + return const LoadingCommon(); + } + } + ) + ); + } + + actionTile(API.Action action) { + switch(action.type) { + case ActionType.DELAY: + return ListTile( + title: Text(action.type.toString()), + ); + case ActionType.DEVICE: + return ExpansionTile( + title: Text(action.type.toString()), + children: [ + if(automationDetailDTO!.devices!.where((d) => d.id == action.deviceId).isNotEmpty) + ExpansionTile( + title: Text(automationDetailDTO!.devices!.firstWhere((d) => d.id == action.deviceId).name!), + children: [ + ListTile( + title: Text(action.states.toString()) + ), + ], + ) + ], + ); + case ActionType.GROUP: + return ListTile( + title: Text(action.type.toString()), + ); + case ActionType.HTTP: + return ListTile( + title: Text(action.type.toString()), + ); + case ActionType.MQTT: + return ExpansionTile( + title: Text(action.type.toString()), + children: [ + ListTile( + title: Text(action.rawRequest!), + ) + ], + ); + case ActionType.zIGBEE2MQTT: + return ListTile( + title: Text(action.type.toString()), + ); + } + + } +} diff --git a/lib/Screens/Main/Automations/automations.dart b/lib/Screens/Main/Automations/automations.dart index ec5c31f..61472fa 100644 --- a/lib/Screens/Main/Automations/automations.dart +++ b/lib/Screens/Main/Automations/automations.dart @@ -1,5 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:mycore_api/api.dart'; +import 'package:myhomie_app/Components/loading_common.dart'; +import 'package:myhomie_app/Models/homieContext.dart'; +import 'package:myhomie_app/Screens/Main/Automations/automationDetailPage.dart'; import 'package:myhomie_app/app_context.dart'; +import 'package:myhomie_app/constants.dart'; import 'package:provider/provider.dart'; class AutomationsScreen extends StatefulWidget { @@ -11,7 +16,7 @@ class AutomationsScreen extends StatefulWidget { class _AutomationsScreenState extends State { bool isLoading = true; - //List messages; + List? allAutomations; @override void initState() { @@ -22,47 +27,103 @@ class _AutomationsScreenState extends State { Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; final appContext = Provider.of(context); + HomieAppContext homieAppContext = appContext.getContext(); - return interfaceElements(); - - /*if(appContext.getContext().feed != null) { - return interfaceElements(); - } else { - return FutureBuilder( - future: Message.getMessages(this.messages, appContext, false, true), - builder: (context, AsyncSnapshot> snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return interfaceElements(); - } else if (snapshot.connectionState == ConnectionState.none) { - print('ConnectionState.none'); - return Text("No data"); - } else { - return Container(height: size.height * 0.2, child: Loading()); - } - } - ); - }*/ - } - - interfaceElements() { - Size size = MediaQuery.of(context).size; - final appContext = Provider.of(context); - return RefreshIndicator( - color: Theme.of(context).primaryColor, - displacement: 20, - onRefresh: () async { - print("TODO refresh"); - //await Message.getMessages(this.messages, appContext, true, true); - }, + return SingleChildScrollView( child: Container( - height: size.height * 0.8, - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text("TODO Automations") - ], - ), + height: size.height * 0.9, + child: FutureBuilder( + future: homieAppContext.clientAPI.automationApi!.automationGetAll(homieAppContext.homeId!), + builder: (context, AsyncSnapshot?> snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + allAutomations = snapshot.data; + return Padding( + padding: const EdgeInsets.all(8.0), + child: RefreshIndicator( + color: Theme.of(context).primaryColor, + displacement: 20, + onRefresh: () async { + print("TODO refresh"); + setState(() {}); + return Future(() => null); + //await Message.getMessages(this.messages, appContext, true, true); + }, + child: ListView.builder( + shrinkWrap: true, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: InkWell( + onTap: () { + debugPrint(allAutomations![index].toString()); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AutomationDetailPage( + automationDTO: allAutomations![index], + ), + ), + ); + }, + child: Container( + height: size.height * 0.1, + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(20.0), + border: Border.all( + color: kBackgroundSecondGrey.withOpacity(0.35), + width: 0.2, + ), + boxShadow: [ + BoxShadow( + color: kMainColor.withOpacity(0.35), + //spreadRadius: 0.15, + blurRadius: 5, + offset: const Offset(0, 10), // changes position of shadow + ), + ], + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text(allAutomations![index].name!), + Switch( + // thumb color (round icon) + activeColor: kMainColor, + activeTrackColor: Colors.grey.shade400, + inactiveThumbColor: Colors.blueGrey.shade600, + inactiveTrackColor: Colors.grey.shade400, + splashRadius: 50.0, + // boolean variable value + value: allAutomations![index].active!, + // changes the state of the switch + onChanged: (bool value) { + + /*setState(() { + allAutomations![index].active = value; + // TODO save + });*/ + } + ), + ], + ), + ), + ), + ); + }, + itemCount: allAutomations!.length + ), + ), + ); + } else if (snapshot.connectionState == ConnectionState.none) { + print('ConnectionState.none'); + return Text("No data"); + } else { + return Container(height: size.height * 0.2, child: LoadingCommon()); + } + } ), ), ); diff --git a/lib/Screens/Main/Devices/devices.dart b/lib/Screens/Main/Devices/devices.dart new file mode 100644 index 0000000..8a89412 --- /dev/null +++ b/lib/Screens/Main/Devices/devices.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:myhomie_app/app_context.dart'; +import 'package:provider/provider.dart'; + +class DevicesScreen extends StatefulWidget { + DevicesScreen({Key? key}) : super(key: key); + + @override + _DevicesScreenState createState() => _DevicesScreenState(); +} + +class _DevicesScreenState extends State { + bool isLoading = true; + //List messages; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + final appContext = Provider.of(context); + + return interfaceElements(); + + /*if(appContext.getContext().feed != null) { + return interfaceElements(); + } else { + return FutureBuilder( + future: Message.getMessages(this.messages, appContext, false, true), + builder: (context, AsyncSnapshot> snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return interfaceElements(); + } else if (snapshot.connectionState == ConnectionState.none) { + print('ConnectionState.none'); + return Text("No data"); + } else { + return Container(height: size.height * 0.2, child: Loading()); + } + } + ); + }*/ + } + + interfaceElements() { + Size size = MediaQuery.of(context).size; + final appContext = Provider.of(context); + return RefreshIndicator( + color: Theme.of(context).primaryColor, + displacement: 20, + onRefresh: () async { + print("TODO refresh"); + //await Message.getMessages(this.messages, appContext, true, true); + }, + child: Container( + height: size.height * 0.8, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text("TODO Devices") + ], + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Screens/Main/Home/home.dart b/lib/Screens/Main/Home/home.dart index a0dbb7a..8184cd8 100644 --- a/lib/Screens/Main/Home/home.dart +++ b/lib/Screens/Main/Home/home.dart @@ -3,6 +3,7 @@ import 'package:mycore_api/api.dart'; import 'package:myhomie_app/Components/loading.dart'; import 'package:myhomie_app/Components/loading_common.dart'; import 'package:myhomie_app/Models/homieContext.dart'; +import 'package:myhomie_app/Screens/Main/Home/roomDetailPage.dart'; import 'package:myhomie_app/app_context.dart'; import 'package:myhomie_app/constants.dart'; import 'package:provider/provider.dart'; @@ -86,10 +87,19 @@ class _HomeScreenState extends State { itemBuilder: (BuildContext context, int index) { return InkWell( onTap: () { - setState(() { + debugPrint(roomsMaindetails[index].toString()); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => RoomDetailPage( + roomMainDetailDTO: roomsMaindetails[index] + ) + ), + ); + /*setState(() { //selectedSection = menuDTO.sections[index]; print("Hero to room detail"); - }); + });*/ }, child: Container( decoration: boxDecorationRoom(roomsMaindetails[index], false), diff --git a/lib/Screens/Main/Home/roomDetailPage.dart b/lib/Screens/Main/Home/roomDetailPage.dart new file mode 100644 index 0000000..aa9ac89 --- /dev/null +++ b/lib/Screens/Main/Home/roomDetailPage.dart @@ -0,0 +1,139 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:mycore_api/api.dart'; + +import 'package:mycore_api/api.dart' as API; +import 'package:myhomie_app/Components/Alarms/getCurrentAlarmModeIcon.dart'; +import 'package:myhomie_app/Components/Custom_Navigation_Bar/CustomAppBar.dart'; +import 'package:myhomie_app/Components/check_input_container.dart'; +import 'package:myhomie_app/Components/loading_common.dart'; +import 'package:myhomie_app/Components/string_input_container.dart'; +import 'package:myhomie_app/Models/homieContext.dart'; +import 'package:myhomie_app/app_context.dart'; +import 'package:myhomie_app/constants.dart'; +import 'package:provider/provider.dart'; + +class RoomDetailPage extends StatefulWidget { + const RoomDetailPage({Key? key, required this.roomMainDetailDTO}) : super(key: key); + + final API.RoomMainDetailDTO roomMainDetailDTO; + + @override + State createState() => _RoomDetailPagePageState(); +} + +class _RoomDetailPagePageState extends State { + final GlobalKey _scaffoldKey = GlobalKey(); + API.RoomDetailDTO? roomDetailDTO; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final appContext = Provider.of(context); + Size size = MediaQuery.of(context).size; + //final notchInset = MediaQuery.of(context).padding; + + HomieAppContext homieAppContext = appContext.getContext(); + + debugPrint(widget.roomMainDetailDTO.id!); + + return Scaffold( + key: _scaffoldKey, + appBar: CustomAppBar( + title: widget.roomMainDetailDTO.name!, + //titleIcon: getAlarmModeIcon(widget.alarmMode), + isTextSizeButton: true, + ), + body: FutureBuilder( + future: homieAppContext.clientAPI.roomApi!.roomGetDetail(widget.roomMainDetailDTO.id!), + builder: (context, AsyncSnapshot snapshot) { + if(snapshot.data != null ) { + roomDetailDTO = snapshot.data; + + return SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: size.width *1.1, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: StringInputContainer( + label: "Nom:", + color: kMainColor, + textColor: Colors.white, + initialValue: roomDetailDTO!.name, + onChanged: (String value) { + setState(() { + roomDetailDTO!.name = value; + // TODO SAVE or not + }); + }, + ), + ), + ), + /*Padding( + padding: const EdgeInsets.all(8.0), + child: CheckInputContainer( + icon: Icons.notifications_active, + label: "Notification :", + fontSize: 20, + isChecked: alarmModeDetailDTO!.notification!, + onChanged: (bool value) { + alarmModeDetailDTO!.notification = value; + }, + ), + ),*/ + ExpansionTile( + title: Text( + "Devices", + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold + ), + ), + children: [ + for(var device in roomDetailDTO!.devices!) + ExpansionTile( + title: Text(device.name!), + children: [ + ListTile( + title: Text("type: "+ (device.type != null ? device.type!.value : 'no type') ), + ), + ListTile( + title: Text("status: "+ device.status.toString()), + ), + ListTile( + title: Text("last update date: "+ device.lastStateDate!.toLocal().toString()), + ), + ListTile( + title: Text("lastState: "+ (device.lastState != null ? device.lastState! : 'no data')), + ) + ], + ), + ], + ), + ], + ), + ); + } else { + return const LoadingCommon(); + } + } + ) + ); + } +} diff --git a/lib/Screens/Main/MainPage.dart b/lib/Screens/Main/MainPage.dart index b85046c..38b99d1 100644 --- a/lib/Screens/Main/MainPage.dart +++ b/lib/Screens/Main/MainPage.dart @@ -7,6 +7,7 @@ import 'package:myhomie_app/Components/Custom_Navigation_Bar/custom_nav_item.dar import 'package:myhomie_app/Helpers/MQTTHelper.dart'; import 'package:myhomie_app/Screens/Debug/DebugPage.dart'; import 'package:myhomie_app/Screens/Main/Automations/automations.dart'; +import 'package:myhomie_app/Screens/Main/Devices/devices.dart'; import 'package:myhomie_app/app_context.dart'; import 'package:myhomie_app/constants.dart'; import 'package:provider/provider.dart'; @@ -62,8 +63,9 @@ class _MainPageState extends State { HomeScreen(), SecurityScreen(), AutomationsScreen(), + DevicesScreen(), EnergyScreen(), - ProfileScreen(), + //ProfileScreen(), ], ), drawer: Drawer( diff --git a/lib/Screens/Main/Security/alarmDetailPage.dart b/lib/Screens/Main/Security/alarmDetailPage.dart new file mode 100644 index 0000000..97b4eea --- /dev/null +++ b/lib/Screens/Main/Security/alarmDetailPage.dart @@ -0,0 +1,211 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:mycore_api/api.dart'; + +import 'package:mycore_api/api.dart' as API; +import 'package:myhomie_app/Components/Alarms/getCurrentAlarmModeIcon.dart'; +import 'package:myhomie_app/Components/Custom_Navigation_Bar/CustomAppBar.dart'; +import 'package:myhomie_app/Components/check_input_container.dart'; +import 'package:myhomie_app/Components/loading_common.dart'; +import 'package:myhomie_app/Components/string_input_container.dart'; +import 'package:myhomie_app/Models/homieContext.dart'; +import 'package:myhomie_app/app_context.dart'; +import 'package:myhomie_app/constants.dart'; +import 'package:provider/provider.dart'; + +class AlarmDetailPage extends StatefulWidget { + const AlarmDetailPage({Key? key, required this.alarmMode}) : super(key: key); + + final AlarmModeDTO alarmMode; + + @override + State createState() => _AlarmDetailPageState(); +} + +class _AlarmDetailPageState extends State { + final GlobalKey _scaffoldKey = GlobalKey(); + AlarmModeDetailDTO? alarmModeDetailDTO; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final appContext = Provider.of(context); + Size size = MediaQuery.of(context).size; + //final notchInset = MediaQuery.of(context).padding; + + HomieAppContext homieAppContext = appContext.getContext(); + + return Scaffold( + key: _scaffoldKey, + appBar: CustomAppBar( + title: widget.alarmMode.name!, + titleIcon: getAlarmModeIcon(widget.alarmMode), + isTextSizeButton: true, + ), + body: FutureBuilder( + future: homieAppContext.clientAPI.alarmApi!.alarmGetDetail(widget.alarmMode.id!), + builder: (context, AsyncSnapshot snapshot) { + if(snapshot.data != null ) { + alarmModeDetailDTO = snapshot.data; + + + return SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if(!alarmModeDetailDTO!.isDefault!) + SizedBox( + width: size.width *1.1, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: StringInputContainer( + label: "Nom:", + color: kMainColor, + textColor: Colors.white, + initialValue: alarmModeDetailDTO!.name, + onChanged: (String value) { + setState(() { + alarmModeDetailDTO!.name = value; + // TODO SAVE or not + }); + }, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: CheckInputContainer( + icon: Icons.notifications_active, + label: "Notification :", + fontSize: 20, + isChecked: alarmModeDetailDTO!.notification!, + onChanged: (bool value) { + alarmModeDetailDTO!.notification = value; + }, + ), + ), + ExpansionTile( + title: Text( + "Triggers", + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold + ), + ), + children: [ + for(var trigger in alarmModeDetailDTO!.triggers!) + if(alarmModeDetailDTO!.devices!.where((d) => d.id == trigger.deviceId).isNotEmpty) + ExpansionTile( + title: Text(alarmModeDetailDTO!.devices!.firstWhere((d) => d.id == trigger.deviceId).name!), + children: [ + ListTile( + title: Text("name: "+trigger.stateName! + " - " + "value: "+ trigger.stateValue!), + ) + ], + ), + ListTile( + title: Text("Ajouter +", style: TextStyle(), textAlign: TextAlign.center), + tileColor: Colors.lightGreen, + onTap: () { + // Show popup ajout + debugPrint("Add device to trigger popup"); + }, + ) + ], + ), + ExpansionTile( + title: Text( + "Actions", + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold + ), + ), + children: [ + for(var action in alarmModeDetailDTO!.actions!) + actionTile(action), + ListTile( + title: Text("Ajouter +", style: TextStyle(), textAlign: TextAlign.center), + tileColor: Colors.lightGreen, + onTap: () { + // Show popup ajout + debugPrint("Add device to trigger popup"); + }, + ) + ], + ), + + Text("Actions"), + for(var device in alarmModeDetailDTO!.devices!.where((d) => alarmModeDetailDTO!.actions!.map((t)=> t.deviceId).contains(d.id))) + Text(device.name!), + Text(alarmModeDetailDTO!.actions!.toString()), + ], + ), + ); + } else { + return const LoadingCommon(); + } + } + ) + ); + } + + actionTile(API.Action action) { + switch(action.type) { + case ActionType.DELAY: + return ListTile( + title: Text(action.type.toString()), + ); + case ActionType.DEVICE: + return ExpansionTile( + title: Text(action.type.toString()), + children: [ + if(alarmModeDetailDTO!.devices!.where((d) => d.id == action.deviceId).isNotEmpty) + ExpansionTile( + title: Text(alarmModeDetailDTO!.devices!.firstWhere((d) => d.id == action.deviceId).name!), + children: [ + ListTile( + title: Text(action.states.toString()) + ), + ], + ) + ], + ); + case ActionType.GROUP: + return ListTile( + title: Text(action.type.toString()), + ); + case ActionType.HTTP: + return ListTile( + title: Text(action.type.toString()), + ); + case ActionType.MQTT: + return ExpansionTile( + title: Text(action.type.toString()), + children: [ + ListTile( + title: Text(action.rawRequest!), + ) + ], + ); + case ActionType.zIGBEE2MQTT: + return ListTile( + title: Text(action.type.toString()), + ); + } + + } +} diff --git a/lib/Screens/Main/Security/changeAlarmMode.dart b/lib/Screens/Main/Security/changeAlarmMode.dart new file mode 100644 index 0000000..00e316c --- /dev/null +++ b/lib/Screens/Main/Security/changeAlarmMode.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:mycore_api/api.dart'; +import 'package:myhomie_app/Components/Alarms/getCurrentAlarmModeIcon.dart'; +import 'package:myhomie_app/Models/homieContext.dart'; +import 'package:myhomie_app/constants.dart'; + +Future showChangeAlarmModePopup(List allAlarmModes, HomieAppContext homieAppContext, BuildContext context, Size size) { + return showDialog( + builder: (BuildContext dialogContext) => AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(10.0)) + ), + content: SingleChildScrollView( + child: Column( + children: [ + SizedBox( + height: size.height * 0.5, + width: size.width * 0.95, + child: Center( + child: Padding( + padding: const EdgeInsets.only(left:8.0, right: 8.0, bottom: 8.0, top: 8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("Changer le mode d'alarme courante"), + ), + SingleChildScrollView( + child: Container( + height: size.height*0.4, + //color: Colors.orange, + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + for(var alarmMode in allAlarmModes) + SizedBox( + width: size.width * 0.5, + height: size.height * 0.05, + child: InkWell( + onTap: () { + Navigator.pop(dialogContext, alarmMode.id); + }, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(20.0), + border: Border.all( + color: kBackgroundSecondGrey.withOpacity(0.35), + width: 0.2, + ), + boxShadow: [ + BoxShadow( + color: kMainColor.withOpacity(0.35), + //spreadRadius: 0.15, + blurRadius: 5, + offset: const Offset(0, 10), // changes position of shadow + ), + ], + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text(alarmMode.name!), + Icon(getAlarmModeIcon(alarmMode)), + ], + ), + ), + ), + ) + ], + ) + ), + ) + ], + ), + ), + ), + ), + ], + ), + ), + contentPadding: EdgeInsets.zero, + ), context: context + ); +} diff --git a/lib/Screens/Main/Security/security.dart b/lib/Screens/Main/Security/security.dart index 2bd2185..11e2460 100644 --- a/lib/Screens/Main/Security/security.dart +++ b/lib/Screens/Main/Security/security.dart @@ -1,5 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:mycore_api/api.dart'; +import 'package:myhomie_app/Components/Alarms/getCurrentAlarmModeIcon.dart'; +import 'package:myhomie_app/Components/loading_common.dart'; +import 'package:myhomie_app/Models/homieContext.dart'; +import 'package:myhomie_app/Screens/Main/Security/alarmDetailPage.dart'; +import 'package:myhomie_app/Screens/Main/Security/changeAlarmMode.dart'; import 'package:myhomie_app/app_context.dart'; +import 'package:myhomie_app/constants.dart'; import 'package:provider/provider.dart'; class SecurityScreen extends StatefulWidget { @@ -11,7 +18,8 @@ class SecurityScreen extends StatefulWidget { class _SecurityScreenState extends State { bool isLoading = true; - //List messages; + List? allAlarmModes; + AlarmModeDTO? currentAlarmMode; @override void initState() { @@ -22,31 +30,8 @@ class _SecurityScreenState extends State { Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; final appContext = Provider.of(context); + HomieAppContext homieAppContext = appContext.getContext(); - return interfaceElements(); - - /*if(appContext.getContext().feed != null) { - return interfaceElements(); - } else { - return FutureBuilder( - future: Message.getMessages(this.messages, appContext, false, true), - builder: (context, AsyncSnapshot> snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return interfaceElements(); - } else if (snapshot.connectionState == ConnectionState.none) { - print('ConnectionState.none'); - return Text("No data"); - } else { - return Container(height: size.height * 0.2, child: Loading()); - } - } - ); - }*/ - } - - interfaceElements() { - Size size = MediaQuery.of(context).size; - final appContext = Provider.of(context); return RefreshIndicator( color: Theme.of(context).primaryColor, displacement: 20, @@ -55,16 +40,98 @@ class _SecurityScreenState extends State { //await Message.getMessages(this.messages, appContext, true, true); }, child: Container( - height: size.height * 0.8, - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text("TODO Security") - ], - ), + height: size.height * 0.842, + //child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + FutureBuilder( + future: homieAppContext.clientAPI.alarmApi!.alarmGetAll(homieAppContext.homeId!), + builder: (context, AsyncSnapshot?> snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + allAlarmModes = snapshot.data; + currentAlarmMode = allAlarmModes!.where((am) => am.activated!).first; + return Padding( + padding: const EdgeInsets.all(8.0), + child: Center( + child: Container( + width: size.width * 0.6, + height: size.height * 0.1, + decoration: alarmDecoration(currentAlarmMode!), + child: InkWell( + onTap: () async { + var result = await showChangeAlarmModePopup(allAlarmModes!, homieAppContext, context, size); + if(result != null && currentAlarmMode!.id != result) { + // update current alarmMode + try{ + var resultId = await homieAppContext.clientAPI.alarmApi!.alarmActivate(result); + if(resultId != null) { + //allAlarmModes!.firstWhere((alarmMode) => alarmMode.id == resultId); + setState(() { + }); + } + } catch(e) { + debugPrint("An error occured during alarm mode activation - " + e.toString()); + } + } + }, + onLongPress: () { + if(currentAlarmMode!.type! != AlarmType.desarmed) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AlarmDetailPage( + alarmMode: currentAlarmMode!, + ), + ), + ); + } + }, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text(currentAlarmMode!.name!), + Icon(getAlarmModeIcon(currentAlarmMode!)), + ], + ), + ), + ), + ), + ); + } else if (snapshot.connectionState == ConnectionState.none) { + print('ConnectionState.none'); + return Text("No data"); + } else { + return Container(height: size.height * 0.2, child: LoadingCommon()); + } + } + ), + Text("TODO Cameras"), + ], ), + //), ), ); } +} + +alarmDecoration(AlarmModeDTO alarmModeDTO) { + return BoxDecoration( + color: alarmModeDTO.activated! && alarmModeDTO.type == AlarmType.desarmed ? Colors.white : Colors.red.withOpacity(0.6), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(20.0), + border: Border.all( + color: kBackgroundSecondGrey.withOpacity(0.35), + width: 0.2, + ), + boxShadow: [ + BoxShadow( + color: kMainColor.withOpacity(0.35), + //spreadRadius: 0.15, + blurRadius: 5, + offset: const Offset(0, 15), // changes position of shadow + ), + ], + ); } \ No newline at end of file diff --git a/lib/Screens/Main/index.dart b/lib/Screens/Main/index.dart index 7153ba2..59b3e99 100644 --- a/lib/Screens/Main/index.dart +++ b/lib/Screens/Main/index.dart @@ -22,6 +22,8 @@ class Index with ChangeNotifier { return "Security"; case NavItemIcon.automations: return "Automations"; + case NavItemIcon.devices: + return "Devices"; case NavItemIcon.energy: return "Energy"; case NavItemIcon.profile: diff --git a/lib/api/swagger.yaml b/lib/api/swagger.yaml index eb3b7f6..3c91f93 100644 --- a/lib/api/swagger.yaml +++ b/lib/api/swagger.yaml @@ -4186,6 +4186,11 @@ components: nullable: true items: $ref: '#/components/schemas/Trigger' + actions: + type: array + nullable: true + items: + $ref: '#/components/schemas/Action' devices: type: array nullable: true @@ -4442,6 +4447,11 @@ components: nullable: true items: type: string + devices: + type: array + nullable: true + items: + $ref: '#/components/schemas/DeviceDetailDTO' Condition: type: object additionalProperties: false diff --git a/lib/main.dart b/lib/main.dart index bc5b177..e3b4f76 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,6 @@ +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; +import 'package:myhomie_app/Helpers/PushNotificationService.dart'; import 'package:myhomie_app/client.dart'; import 'package:provider/provider.dart'; @@ -25,6 +27,7 @@ void main() async { print(localContext); } else { print("NO LOCAL DB !"); + localContext = HomieAppContext(); } initialRoute = isLogged ? '/home' : '/login'; @@ -47,10 +50,13 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { + //FirebaseMessaging _firebaseMessaging = FirebaseMessaging(); //HomieAppContext homieAppContext; @override Widget build(BuildContext context) { + final pushNotificationService = PushNotificationService(); + pushNotificationService.initialise(widget.homieAppContext, context: context); return ChangeNotifierProvider( create: (_) => AppContext(widget.homieAppContext), child: MaterialApp( diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8370e57..2e8332f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,12 @@ import FlutterMacOS import Foundation +import firebase_core +import firebase_messaging import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/mycore_api/doc/AlarmModeDetailDTO.md b/mycore_api/doc/AlarmModeDetailDTO.md index 24c9c83..9527dfd 100644 --- a/mycore_api/doc/AlarmModeDetailDTO.md +++ b/mycore_api/doc/AlarmModeDetailDTO.md @@ -18,6 +18,7 @@ Name | Type | Description | Notes **createdDate** | [**DateTime**](DateTime.md) | | [optional] **updatedDate** | [**DateTime**](DateTime.md) | | [optional] **triggers** | [**List**](Trigger.md) | | [optional] [default to const []] +**actions** | [**List**](Action.md) | | [optional] [default to const []] **devices** | [**List**](DeviceDetailDTO.md) | | [optional] [default to const []] **programmedMode** | [**OneOfProgrammedMode**](OneOfProgrammedMode.md) | | [optional] **geolocalizedMode** | [**OneOfGeolocalizedMode**](OneOfGeolocalizedMode.md) | | [optional] diff --git a/mycore_api/doc/AlarmModeDetailDTOAllOf.md b/mycore_api/doc/AlarmModeDetailDTOAllOf.md index 968ce3d..019a8ba 100644 --- a/mycore_api/doc/AlarmModeDetailDTOAllOf.md +++ b/mycore_api/doc/AlarmModeDetailDTOAllOf.md @@ -9,6 +9,7 @@ import 'package:mycore_api/api.dart'; Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **triggers** | [**List**](Trigger.md) | | [optional] [default to const []] +**actions** | [**List**](Action.md) | | [optional] [default to const []] **devices** | [**List**](DeviceDetailDTO.md) | | [optional] [default to const []] **programmedMode** | [**OneOfProgrammedMode**](OneOfProgrammedMode.md) | | [optional] **geolocalizedMode** | [**OneOfGeolocalizedMode**](OneOfGeolocalizedMode.md) | | [optional] diff --git a/mycore_api/doc/AutomationDetailDTO.md b/mycore_api/doc/AutomationDetailDTO.md index 1622177..abfd2aa 100644 --- a/mycore_api/doc/AutomationDetailDTO.md +++ b/mycore_api/doc/AutomationDetailDTO.md @@ -18,6 +18,7 @@ Name | Type | Description | Notes **conditions** | [**List**](Condition.md) | | [optional] [default to const []] **actions** | [**List**](Action.md) | | [optional] [default to const []] **devicesIds** | **List** | | [optional] [default to const []] +**devices** | [**List**](DeviceDetailDTO.md) | | [optional] [default to const []] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/mycore_api/doc/AutomationDetailDTOAllOf.md b/mycore_api/doc/AutomationDetailDTOAllOf.md index 142695a..36fdca7 100644 --- a/mycore_api/doc/AutomationDetailDTOAllOf.md +++ b/mycore_api/doc/AutomationDetailDTOAllOf.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **conditions** | [**List**](Condition.md) | | [optional] [default to const []] **actions** | [**List**](Action.md) | | [optional] [default to const []] **devicesIds** | **List** | | [optional] [default to const []] +**devices** | [**List**](DeviceDetailDTO.md) | | [optional] [default to const []] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/mycore_api/lib/model/alarm_mode_detail_dto.dart b/mycore_api/lib/model/alarm_mode_detail_dto.dart index fee886f..a2d3923 100644 --- a/mycore_api/lib/model/alarm_mode_detail_dto.dart +++ b/mycore_api/lib/model/alarm_mode_detail_dto.dart @@ -23,6 +23,7 @@ class AlarmModeDetailDTO { this.createdDate, this.updatedDate, this.triggers = const [], + this.actions = const [], this.devices = const [], this.programmedMode, this.geolocalizedMode, @@ -84,6 +85,8 @@ class AlarmModeDetailDTO { List? triggers; + List? actions; + List? devices; ProgrammedMode? programmedMode; @@ -102,6 +105,7 @@ class AlarmModeDetailDTO { other.createdDate == createdDate && other.updatedDate == updatedDate && other.triggers == triggers && + other.actions == actions && other.devices == devices && other.programmedMode == programmedMode && other.geolocalizedMode == geolocalizedMode; @@ -119,12 +123,13 @@ class AlarmModeDetailDTO { (createdDate == null ? 0 : createdDate!.hashCode) + (updatedDate == null ? 0 : updatedDate!.hashCode) + (triggers == null ? 0 : triggers!.hashCode) + + (actions == null ? 0 : actions!.hashCode) + (devices == null ? 0 : devices!.hashCode) + (programmedMode == null ? 0 : programmedMode!.hashCode) + (geolocalizedMode == null ? 0 : geolocalizedMode!.hashCode); @override - String toString() => 'AlarmModeDetailDTO[id=$id, homeId=$homeId, name=$name, type=$type, activated=$activated, isDefault=$isDefault, notification=$notification, createdDate=$createdDate, updatedDate=$updatedDate, triggers=$triggers, devices=$devices, programmedMode=$programmedMode, geolocalizedMode=$geolocalizedMode]'; + String toString() => 'AlarmModeDetailDTO[id=$id, homeId=$homeId, name=$name, type=$type, activated=$activated, isDefault=$isDefault, notification=$notification, createdDate=$createdDate, updatedDate=$updatedDate, triggers=$triggers, actions=$actions, devices=$devices, programmedMode=$programmedMode, geolocalizedMode=$geolocalizedMode]'; Map toJson() { final _json = {}; @@ -158,6 +163,9 @@ class AlarmModeDetailDTO { if (triggers != null) { _json[r'triggers'] = triggers; } + if (actions != null) { + _json[r'actions'] = actions; + } if (devices != null) { _json[r'devices'] = devices; } @@ -199,6 +207,7 @@ class AlarmModeDetailDTO { createdDate: mapDateTime(json, r'createdDate', ''), updatedDate: mapDateTime(json, r'updatedDate', ''), triggers: Trigger.listFromJson(json[r'triggers']) ?? const [], + actions: Action.listFromJson(json[r'actions']) ?? const [], devices: DeviceDetailDTO.listFromJson(json[r'devices']) ?? const [], programmedMode: ProgrammedMode.fromJson(json[r'programmedMode']), geolocalizedMode: GeolocalizedMode.fromJson(json[r'geolocalizedMode']), diff --git a/mycore_api/lib/model/alarm_mode_detail_dto_all_of.dart b/mycore_api/lib/model/alarm_mode_detail_dto_all_of.dart index cbd7d7f..b90f3e6 100644 --- a/mycore_api/lib/model/alarm_mode_detail_dto_all_of.dart +++ b/mycore_api/lib/model/alarm_mode_detail_dto_all_of.dart @@ -14,6 +14,7 @@ class AlarmModeDetailDTOAllOf { /// Returns a new [AlarmModeDetailDTOAllOf] instance. AlarmModeDetailDTOAllOf({ this.triggers = const [], + this.actions = const [], this.devices = const [], this.programmedMode, this.geolocalizedMode, @@ -21,6 +22,8 @@ class AlarmModeDetailDTOAllOf { List? triggers; + List? actions; + List? devices; ProgrammedMode? programmedMode; @@ -30,6 +33,7 @@ class AlarmModeDetailDTOAllOf { @override bool operator ==(Object other) => identical(this, other) || other is AlarmModeDetailDTOAllOf && other.triggers == triggers && + other.actions == actions && other.devices == devices && other.programmedMode == programmedMode && other.geolocalizedMode == geolocalizedMode; @@ -38,18 +42,22 @@ class AlarmModeDetailDTOAllOf { int get hashCode => // ignore: unnecessary_parenthesis (triggers == null ? 0 : triggers!.hashCode) + + (actions == null ? 0 : actions!.hashCode) + (devices == null ? 0 : devices!.hashCode) + (programmedMode == null ? 0 : programmedMode!.hashCode) + (geolocalizedMode == null ? 0 : geolocalizedMode!.hashCode); @override - String toString() => 'AlarmModeDetailDTOAllOf[triggers=$triggers, devices=$devices, programmedMode=$programmedMode, geolocalizedMode=$geolocalizedMode]'; + String toString() => 'AlarmModeDetailDTOAllOf[triggers=$triggers, actions=$actions, devices=$devices, programmedMode=$programmedMode, geolocalizedMode=$geolocalizedMode]'; Map toJson() { final _json = {}; if (triggers != null) { _json[r'triggers'] = triggers; } + if (actions != null) { + _json[r'actions'] = actions; + } if (devices != null) { _json[r'devices'] = devices; } @@ -82,6 +90,7 @@ class AlarmModeDetailDTOAllOf { return AlarmModeDetailDTOAllOf( triggers: Trigger.listFromJson(json[r'triggers']) ?? const [], + actions: Action.listFromJson(json[r'actions']) ?? const [], devices: DeviceDetailDTO.listFromJson(json[r'devices']) ?? const [], programmedMode: ProgrammedMode.fromJson(json[r'programmedMode']), geolocalizedMode: GeolocalizedMode.fromJson(json[r'geolocalizedMode']), diff --git a/mycore_api/lib/model/automation_detail_dto.dart b/mycore_api/lib/model/automation_detail_dto.dart index 08f5bd1..964cb8a 100644 --- a/mycore_api/lib/model/automation_detail_dto.dart +++ b/mycore_api/lib/model/automation_detail_dto.dart @@ -23,6 +23,7 @@ class AutomationDetailDTO { this.conditions = const [], this.actions = const [], this.devicesIds = const [], + this.devices = const [], }); String? id; @@ -63,6 +64,8 @@ class AutomationDetailDTO { List? devicesIds; + List? devices; + @override bool operator ==(Object other) => identical(this, other) || other is AutomationDetailDTO && other.id == id && @@ -74,7 +77,8 @@ class AutomationDetailDTO { other.triggers == triggers && other.conditions == conditions && other.actions == actions && - other.devicesIds == devicesIds; + other.devicesIds == devicesIds && + other.devices == devices; @override int get hashCode => @@ -88,10 +92,11 @@ class AutomationDetailDTO { (triggers == null ? 0 : triggers!.hashCode) + (conditions == null ? 0 : conditions!.hashCode) + (actions == null ? 0 : actions!.hashCode) + - (devicesIds == null ? 0 : devicesIds!.hashCode); + (devicesIds == null ? 0 : devicesIds!.hashCode) + + (devices == null ? 0 : devices!.hashCode); @override - String toString() => 'AutomationDetailDTO[id=$id, name=$name, active=$active, homeId=$homeId, createdDate=$createdDate, updatedDate=$updatedDate, triggers=$triggers, conditions=$conditions, actions=$actions, devicesIds=$devicesIds]'; + String toString() => 'AutomationDetailDTO[id=$id, name=$name, active=$active, homeId=$homeId, createdDate=$createdDate, updatedDate=$updatedDate, triggers=$triggers, conditions=$conditions, actions=$actions, devicesIds=$devicesIds, devices=$devices]'; Map toJson() { final _json = {}; @@ -125,6 +130,9 @@ class AutomationDetailDTO { if (devicesIds != null) { _json[r'devicesIds'] = devicesIds; } + if (devices != null) { + _json[r'devices'] = devices; + } return _json; } @@ -159,6 +167,7 @@ class AutomationDetailDTO { devicesIds: json[r'devicesIds'] is List ? (json[r'devicesIds'] as List).cast() : const [], + devices: DeviceDetailDTO.listFromJson(json[r'devices']) ?? const [], ); } return null; diff --git a/mycore_api/lib/model/automation_detail_dto_all_of.dart b/mycore_api/lib/model/automation_detail_dto_all_of.dart index 50fad0f..dc5094e 100644 --- a/mycore_api/lib/model/automation_detail_dto_all_of.dart +++ b/mycore_api/lib/model/automation_detail_dto_all_of.dart @@ -17,6 +17,7 @@ class AutomationDetailDTOAllOf { this.conditions = const [], this.actions = const [], this.devicesIds = const [], + this.devices = const [], }); List? triggers; @@ -27,12 +28,15 @@ class AutomationDetailDTOAllOf { List? devicesIds; + List? devices; + @override bool operator ==(Object other) => identical(this, other) || other is AutomationDetailDTOAllOf && other.triggers == triggers && other.conditions == conditions && other.actions == actions && - other.devicesIds == devicesIds; + other.devicesIds == devicesIds && + other.devices == devices; @override int get hashCode => @@ -40,10 +44,11 @@ class AutomationDetailDTOAllOf { (triggers == null ? 0 : triggers!.hashCode) + (conditions == null ? 0 : conditions!.hashCode) + (actions == null ? 0 : actions!.hashCode) + - (devicesIds == null ? 0 : devicesIds!.hashCode); + (devicesIds == null ? 0 : devicesIds!.hashCode) + + (devices == null ? 0 : devices!.hashCode); @override - String toString() => 'AutomationDetailDTOAllOf[triggers=$triggers, conditions=$conditions, actions=$actions, devicesIds=$devicesIds]'; + String toString() => 'AutomationDetailDTOAllOf[triggers=$triggers, conditions=$conditions, actions=$actions, devicesIds=$devicesIds, devices=$devices]'; Map toJson() { final _json = {}; @@ -59,6 +64,9 @@ class AutomationDetailDTOAllOf { if (devicesIds != null) { _json[r'devicesIds'] = devicesIds; } + if (devices != null) { + _json[r'devices'] = devices; + } return _json; } @@ -87,6 +95,7 @@ class AutomationDetailDTOAllOf { devicesIds: json[r'devicesIds'] is List ? (json[r'devicesIds'] as List).cast() : const [], + devices: DeviceDetailDTO.listFromJson(json[r'devices']) ?? const [], ); } return null; diff --git a/pubspec.lock b/pubspec.lock index a084fd9..17fbfec 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "48.0.0" + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: "5dce45a06d386358334eb1689108db6455d90ceb0d75848d5f4819283d4ee2b8" + url: "https://pub.dev" + source: hosted + version: "1.3.4" analyzer: dependency: transitive description: @@ -33,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" + source: hosted + version: "3.0.0" boolean_selector: dependency: transitive description: @@ -209,6 +225,54 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" + firebase_core: + dependency: transitive + description: + name: firebase_core + sha256: "2e9324f719e90200dc7d3c4f5d2abc26052f9f2b995d3b6626c47a0dfe1c8192" + url: "https://pub.dev" + source: hosted + version: "2.15.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: b63e3be6c96ef5c33bdec1aab23c91eb00696f6452f0519401d640938c94cba2 + url: "https://pub.dev" + source: hosted + version: "4.8.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: "0fd5c4b228de29b55fac38aed0d9e42514b3d3bd47675de52bf7f8fccaf922fa" + url: "https://pub.dev" + source: hosted + version: "2.6.0" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + sha256: "8ac91d83a028eef050de770f1dc98421e215714d245f34de7b154d436676fbd0" + url: "https://pub.dev" + source: hosted + version: "14.6.5" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + sha256: b2995e3640efb646e9ebf0e2fa50dea84895f0746a31d7e3af0e5e009a533a1a + url: "https://pub.dev" + source: hosted + version: "4.5.4" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + sha256: "5d8446a28339124a2cb4f57a6ca454a3aca7d0c5c0cdfa5707afb192f7c830a7" + url: "https://pub.dev" + source: hosted + version: "3.5.4" fixnum: dependency: transitive description: @@ -455,6 +519,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" pool: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ff9b530..3be9d18 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,8 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.0 + auto_size_text: ^3.0.0 + firebase_messaging: ^14.6.5 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..1a82e7d 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + FirebaseCorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..fa8a39b 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + firebase_core ) list(APPEND FLUTTER_FFI_PLUGIN_LIST