WIP #523 and connection settings refactoring
This commit is contained in:
		| @@ -42,7 +42,7 @@ class HomeAssistant { | |||||||
|       return _instanceConfig["location_name"] ?? "Home"; |       return _instanceConfig["location_name"] ?? "Home"; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   String get userName => _userName ?? locationName; |   String get userName => _userName ?? ''; | ||||||
|   String get userAvatarText => userName.length > 0 ? userName[0] : ""; |   String get userAvatarText => userName.length > 0 ? userName[0] : ""; | ||||||
|   bool get isNoEntities => entities == null || entities.isEmpty; |   bool get isNoEntities => entities == null || entities.isEmpty; | ||||||
|   bool get isNoViews => ui == null || ui.isEmpty; |   bool get isNoViews => ui == null || ui.isEmpty; | ||||||
|   | |||||||
| @@ -113,6 +113,7 @@ part 'pages/settings/integration_settings.part.dart'; | |||||||
| part 'pages/settings/app_settings.page.dart'; | part 'pages/settings/app_settings.page.dart'; | ||||||
| part 'pages/settings/lookandfeel_settings.part.dart'; | part 'pages/settings/lookandfeel_settings.part.dart'; | ||||||
| part 'pages/zha_page.dart'; | part 'pages/zha_page.dart'; | ||||||
|  | part 'pages/quick_start.page.dart'; | ||||||
| part 'home_assistant.class.dart'; | part 'home_assistant.class.dart'; | ||||||
| part 'pages/entity.page.dart'; | part 'pages/entity.page.dart'; | ||||||
| part 'utils/mdi.class.dart'; | part 'utils/mdi.class.dart'; | ||||||
| @@ -260,7 +261,7 @@ class _HAClientAppState extends State<HAClientApp> { | |||||||
|       routes: { |       routes: { | ||||||
|         "/": (context) => MainPage(title: 'HA Client'), |         "/": (context) => MainPage(title: 'HA Client'), | ||||||
|         "/app-settings": (context) => AppSettingsPage(), |         "/app-settings": (context) => AppSettingsPage(), | ||||||
|         "/connection-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.connectionSettings), |         "/connection-settings": (context) => AppSettingsPage(), | ||||||
|         "/integration-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.integrationSettings), |         "/integration-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.integrationSettings), | ||||||
|         "/putchase": (context) => PurchasePage(title: "Support app development"), |         "/putchase": (context) => PurchasePage(title: "Support app development"), | ||||||
|         "/play-media": (context) => PlayMediaPage( |         "/play-media": (context) => PlayMediaPage( | ||||||
| @@ -278,22 +279,23 @@ class _HAClientAppState extends State<HAClientApp> { | |||||||
|           ), |           ), | ||||||
|         ), |         ), | ||||||
|         "/whats-new": (context) => WhatsNewPage(), |         "/whats-new": (context) => WhatsNewPage(), | ||||||
|  |         "/quick-start": (context) => QuickStartPage(), | ||||||
|         "/haclient_zha": (context) => ZhaPage(), |         "/haclient_zha": (context) => ZhaPage(), | ||||||
|         "/auth": (context) => new standaloneWebview.WebviewScaffold( |         "/auth": (context) => new standaloneWebview.WebviewScaffold( | ||||||
|           url: "${ConnectionManager().oauthUrl}", |           url: "${ConnectionManager().oauthUrl}", | ||||||
|           appBar: new AppBar( |           appBar: new AppBar( | ||||||
|             leading: IconButton( |             leading: IconButton( | ||||||
|                 icon: Icon(Icons.help), |               icon: Icon(Icons.help), | ||||||
|                 onPressed: () => Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.app/docs#authentication") |               onPressed: () => Launcher.launchURLInCustomTab(context: context, url: "https://ha-client.app/help/connection") | ||||||
|             ), |             ), | ||||||
|             title: new Text("Login with HA"), |             title: new Text("Login"), | ||||||
|             actions: <Widget>[ |             actions: <Widget>[ | ||||||
|               FlatButton( |               FlatButton( | ||||||
|                 child: Text("Manual", style: Theme.of(context).textTheme.button.copyWith( |                 child: Text("Long-lived token", style: Theme.of(context).textTheme.button.copyWith( | ||||||
|                   decoration: TextDecoration.underline |                   decoration: TextDecoration.underline | ||||||
|                 )), |                 )), | ||||||
|                 onPressed: () { |                 onPressed: () { | ||||||
|                   eventBus.fire(ShowPageEvent(path: "/connection-settings", goBackFirst: true)); |                   eventBus.fire(ShowTokenLoginPopupEvent(goBackFirst: true)); | ||||||
|                 }, |                 }, | ||||||
|               ) |               ) | ||||||
|             ], |             ], | ||||||
|   | |||||||
| @@ -53,7 +53,10 @@ class ConnectionManager { | |||||||
|       httpWebHost = |       httpWebHost = | ||||||
|       "${prefs.getString('hassio-res-protocol')}://$_domain:$_port"; |       "${prefs.getString('hassio-res-protocol')}://$_domain:$_port"; | ||||||
|       Logger.d('$_domain$_port'); |       Logger.d('$_domain$_port'); | ||||||
|       if ((_domain == null) || (_port == null) || |       if (_domain == null && _port == null && webhookId == null && mobileAppDeviceName == null) { | ||||||
|  |         completer.completeError(HACNotSetUpException()); | ||||||
|  |         stopInit = true; | ||||||
|  |       } else if ((_domain == null) || (_port == null) || | ||||||
|           (_domain.isEmpty) || (_port.isEmpty)) { |           (_domain.isEmpty) || (_port.isEmpty)) { | ||||||
|         completer.completeError(HACException.checkConnectionSettings()); |         completer.completeError(HACException.checkConnectionSettings()); | ||||||
|         stopInit = true; |         stopInit = true; | ||||||
|   | |||||||
| @@ -17,6 +17,9 @@ class MobileAppIntegrationManager { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   static String getDefaultDeviceName() { |   static String getDefaultDeviceName() { | ||||||
|  |     if (HomeAssistant().userName.isEmpty) { | ||||||
|  |       return '${DeviceInfoManager().model}';   | ||||||
|  |     } | ||||||
|     return '${HomeAssistant().userName}\'s ${DeviceInfoManager().model}'; |     return '${HomeAssistant().userName}\'s ${DeviceInfoManager().model}'; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ class HAClientTheme { | |||||||
|       fontWeight: FontWeight.normal, |       fontWeight: FontWeight.normal, | ||||||
|       letterSpacing: 1, |       letterSpacing: 1, | ||||||
|     ), |     ), | ||||||
|     button: TextStyle(fontSize: 14, fontWeight: FontWeight.w500), |     button: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   static const offEntityStates = [ |   static const offEntityStates = [ | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker | |||||||
|   StreamSubscription _startAuthSubscription; |   StreamSubscription _startAuthSubscription; | ||||||
|   StreamSubscription _showPopupDialogSubscription; |   StreamSubscription _showPopupDialogSubscription; | ||||||
|   StreamSubscription _showPopupMessageSubscription; |   StreamSubscription _showPopupMessageSubscription; | ||||||
|  |   StreamSubscription _showTokenLoginPopupSubscription; | ||||||
|   StreamSubscription _reloadUISubscription; |   StreamSubscription _reloadUISubscription; | ||||||
|   StreamSubscription _fullReloadSubscription; |   StreamSubscription _fullReloadSubscription; | ||||||
|   StreamSubscription _showPageSubscription; |   StreamSubscription _showPageSubscription; | ||||||
| @@ -105,7 +106,11 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker | |||||||
|           StartupUserMessagesManager().checkMessagesToShow(); |           StartupUserMessagesManager().checkMessagesToShow(); | ||||||
|         }); |         }); | ||||||
|       }, onError: (e) { |       }, onError: (e) { | ||||||
|         _setErrorState(e); |         if (e is HACNotSetUpException) { | ||||||
|  |           Navigator.of(context).pushReplacementNamed('/quick-start'); | ||||||
|  |         } else { | ||||||
|  |           _setErrorState(e); | ||||||
|  |         } | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| @@ -201,6 +206,14 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker | |||||||
|         ); |         ); | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|  |     if (_showTokenLoginPopupSubscription == null) { | ||||||
|  |       _showTokenLoginPopupSubscription = eventBus.on<ShowTokenLoginPopupEvent>().listen((event){ | ||||||
|  |         if (event.goBackFirst) { | ||||||
|  |           Navigator.of(context).pop(); | ||||||
|  |         } | ||||||
|  |         _showTokenLoginDialog(); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|     if (_serviceCallSubscription == null) { |     if (_serviceCallSubscription == null) { | ||||||
|       _serviceCallSubscription = |       _serviceCallSubscription = | ||||||
|           eventBus.on<NotifyServiceCallEvent>().listen((event) { |           eventBus.on<NotifyServiceCallEvent>().listen((event) { | ||||||
| @@ -302,6 +315,78 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   final _tokenLoginFormKey = GlobalKey<FormState>(); | ||||||
|  |  | ||||||
|  |   void _showTokenLoginDialog() { | ||||||
|  |     // flutter defined function | ||||||
|  |     showDialog( | ||||||
|  |       barrierDismissible: false, | ||||||
|  |       context: context, | ||||||
|  |       builder: (BuildContext context) { | ||||||
|  |         // return object of type Dialog | ||||||
|  |         return SimpleDialog( | ||||||
|  |           title: new Text('Login with long-lived token'), | ||||||
|  |           children: <Widget>[ | ||||||
|  |             Form( | ||||||
|  |               key: _tokenLoginFormKey, | ||||||
|  |               child: Column( | ||||||
|  |                 mainAxisSize: MainAxisSize.min, | ||||||
|  |                 children: <Widget>[ | ||||||
|  |                   Padding( | ||||||
|  |                     padding: EdgeInsets.all(20), | ||||||
|  |                       child: TextFormField( | ||||||
|  |                       onSaved: (newValue) { | ||||||
|  |                         final storage = new FlutterSecureStorage(); | ||||||
|  |                         storage.write(key: "hacl_llt", value: newValue).then((_) { | ||||||
|  |                           Navigator.of(context).pop(); | ||||||
|  |                           eventBus.fire(SettingsChangedEvent(true)); | ||||||
|  |                         }); | ||||||
|  |                       }, | ||||||
|  |                       decoration: InputDecoration( | ||||||
|  |                         hintText: 'Please enter long-lived token', | ||||||
|  |                         contentPadding: EdgeInsets.all(0), | ||||||
|  |                         hintStyle: Theme.of(context).textTheme.subhead.copyWith( | ||||||
|  |                           color: Theme.of(context).textTheme.overline.color | ||||||
|  |                         ) | ||||||
|  |                       ), | ||||||
|  |                       validator: (value) { | ||||||
|  |                         if (value.isEmpty) { | ||||||
|  |                           return 'Long-lived token can\'t be emty'; | ||||||
|  |                         } | ||||||
|  |                         return null; | ||||||
|  |                       }, | ||||||
|  |                     ) | ||||||
|  |                   ), | ||||||
|  |                   Row( | ||||||
|  |                     mainAxisSize: MainAxisSize.min, | ||||||
|  |                     children: <Widget>[ | ||||||
|  |                       RaisedButton( | ||||||
|  |                         child: Text('Login', style: Theme.of(context).textTheme.button.copyWith(fontSize: 20)), | ||||||
|  |                         color: Theme.of(context).primaryColor, | ||||||
|  |                         onPressed: () { | ||||||
|  |                           if (_tokenLoginFormKey.currentState.validate()) { | ||||||
|  |                             _tokenLoginFormKey.currentState.save(); | ||||||
|  |                           } | ||||||
|  |                         }, | ||||||
|  |                       ), | ||||||
|  |                       Container(width: 10), | ||||||
|  |                       RaisedButton( | ||||||
|  |                         child: Text('Cancel', style: Theme.of(context).textTheme.button.copyWith(fontSize: 20)), | ||||||
|  |                         onPressed: () { | ||||||
|  |                           Navigator.of(context).pop(); | ||||||
|  |                         }, | ||||||
|  |                       ) | ||||||
|  |                     ], | ||||||
|  |                   ) | ||||||
|  |                 ], | ||||||
|  |               ), | ||||||
|  |             ) | ||||||
|  |           ], | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   void _notifyServiceCalled(String domain, String service, entityId) { |   void _notifyServiceCalled(String domain, String service, entityId) { | ||||||
|     _bottomInfoBarController.showInfoBottomBar( |     _bottomInfoBarController.showInfoBottomBar( | ||||||
|         message: "Calling $domain.$service", |         message: "Calling $domain.$service", | ||||||
| @@ -577,9 +662,15 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker | |||||||
|                 mainAxisAlignment: MainAxisAlignment.center, |                 mainAxisAlignment: MainAxisAlignment.center, | ||||||
|                 children: <Widget>[ |                 children: <Widget>[ | ||||||
|                   FlatButton( |                   FlatButton( | ||||||
|                     child: Text("Login with Home Assistant", style: Theme.of(context).textTheme.button), |                     child: Text("Login", style: Theme.of(context).textTheme.button), | ||||||
|                     color: Colors.blue, |                     color: Theme.of(context).primaryColor, | ||||||
|                     onPressed: () => _fullLoad(), |                     onPressed: () => _fullLoad(), | ||||||
|  |                   ), | ||||||
|  |                   Container(height: 20,), | ||||||
|  |                   FlatButton( | ||||||
|  |                     child: Text("Login with long-lived token", style: Theme.of(context).textTheme.button), | ||||||
|  |                     color: Theme.of(context).primaryColor, | ||||||
|  |                     onPressed: () => eventBus.fire(ShowTokenLoginPopupEvent(goBackFirst: false)) | ||||||
|                   ) |                   ) | ||||||
|                 ] |                 ] | ||||||
|             ) |             ) | ||||||
| @@ -697,6 +788,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker | |||||||
|     //final flutterWebviewPlugin = new FlutterWebviewPlugin(); |     //final flutterWebviewPlugin = new FlutterWebviewPlugin(); | ||||||
|     //flutterWebviewPlugin.dispose(); |     //flutterWebviewPlugin.dispose(); | ||||||
|     _viewsTabController?.dispose(); |     _viewsTabController?.dispose(); | ||||||
|  |     _showTokenLoginPopupSubscription?.cancel(); | ||||||
|     _stateSubscription?.cancel(); |     _stateSubscription?.cancel(); | ||||||
|     _lovelaceSubscription?.cancel(); |     _lovelaceSubscription?.cancel(); | ||||||
|     _settingsSubscription?.cancel(); |     _settingsSubscription?.cancel(); | ||||||
|   | |||||||
							
								
								
									
										53
									
								
								lib/pages/quick_start.page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								lib/pages/quick_start.page.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | part of '../main.dart'; | ||||||
|  |  | ||||||
|  | class QuickStartPage extends StatefulWidget { | ||||||
|  |   QuickStartPage({Key key, this.title}) : super(key: key); | ||||||
|  |  | ||||||
|  |   final String title; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   _QuickStartPageState createState() => new _QuickStartPageState(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class _QuickStartPageState extends State<QuickStartPage> { | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     super.initState(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Scaffold( | ||||||
|  |       appBar: new AppBar( | ||||||
|  |         leading: IconButton( | ||||||
|  |           icon: Icon(Icons.close), | ||||||
|  |           onPressed: () { | ||||||
|  |             Navigator.of(context).pop(); | ||||||
|  |           }, | ||||||
|  |         ), | ||||||
|  |         title: Text('Quick start'), | ||||||
|  |         actions: <Widget>[ | ||||||
|  |           IconButton( | ||||||
|  |             icon: Icon(Icons.help), | ||||||
|  |             onPressed: () { | ||||||
|  |               Launcher.launchURLInCustomTab( | ||||||
|  |                 context: context, | ||||||
|  |                 url: 'https://ha-client.app/help' | ||||||
|  |               ); | ||||||
|  |             }, | ||||||
|  |           ) | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |       body: ConnectionSettingsPage( | ||||||
|  |         quickStart: true, | ||||||
|  |       ) | ||||||
|  |     ); | ||||||
|  |      | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void dispose() { | ||||||
|  |     super.dispose(); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -35,12 +35,15 @@ class _AppSettingsPageState extends State<AppSettingsPage> { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   Widget _buildMenu(BuildContext context) { |   Widget _buildMenu(BuildContext context) { | ||||||
|  |     List<Widget> items = [ | ||||||
|  |       _buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:network'), 'Connection settings', AppSettingsSection.connectionSettings), | ||||||
|  |       _buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:brush'), 'Look and feel', AppSettingsSection.lookAndFeel), | ||||||
|  |     ]; | ||||||
|  |     if (ConnectionManager().webhookId != null) { | ||||||
|  |       items.insert(1, _buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:cellphone-android'), 'Integration settings', AppSettingsSection.integrationSettings)); | ||||||
|  |     } | ||||||
|     return ListView( |     return ListView( | ||||||
|       children: <Widget>[ |       children: items, | ||||||
|         _buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:network'), 'Connection settings', AppSettingsSection.connectionSettings), |  | ||||||
|         _buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:cellphone-android'), 'Integration settings', AppSettingsSection.integrationSettings), |  | ||||||
|         _buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:brush'), 'Look and feel', AppSettingsSection.lookAndFeel), |  | ||||||
|       ], |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,187 +1,194 @@ | |||||||
| part of '../../main.dart'; | part of '../../main.dart'; | ||||||
|  |  | ||||||
| class ConnectionSettingsPage extends StatefulWidget { | class ConnectionSettingsPage extends StatefulWidget { | ||||||
|   ConnectionSettingsPage({Key key, this.title}) : super(key: key); |   ConnectionSettingsPage({Key key, this.title, this.quickStart: false}) : super(key: key); | ||||||
|  |  | ||||||
|   final String title; |   final String title; | ||||||
|  |   final bool quickStart; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   _ConnectionSettingsPageState createState() => new _ConnectionSettingsPageState(); |   _ConnectionSettingsPageState createState() => new _ConnectionSettingsPageState(); | ||||||
| } | } | ||||||
|  |  | ||||||
| class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> { | class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> { | ||||||
|   String _hassioDomain = ""; |   String _homeAssistantUrl = ''; | ||||||
|   String _newHassioDomain = ""; |   String _deviceName; | ||||||
|   String _hassioPort = ""; |   bool _loaded = false; | ||||||
|   String _newHassioPort = ""; |   bool _includeDeviceName = false; | ||||||
|   String _socketProtocol = "wss"; |  | ||||||
|   String _newSocketProtocol = "wss"; |  | ||||||
|   String _longLivedToken = ""; |  | ||||||
|   String _newLongLivedToken = ""; |  | ||||||
|  |  | ||||||
|   String oauthUrl; |   final _formKey = GlobalKey<FormState>(); | ||||||
|   bool useOAuth = false; |  | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   void initState() { |   void initState() { | ||||||
|     super.initState(); |     super.initState(); | ||||||
|     _loadSettings(); |     if (!widget.quickStart) { | ||||||
|  |       _loadSettings(); | ||||||
|  |     } else { | ||||||
|  |       _deviceName = MobileAppIntegrationManager.getDefaultDeviceName(); | ||||||
|  |       _loaded = true; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   _loadSettings() async { |   _loadSettings() async { | ||||||
|  |     Logger.d('Loading settings...'); | ||||||
|  |     _includeDeviceName = widget.quickStart || ConnectionManager().webhookId == null; | ||||||
|     SharedPreferences prefs = await SharedPreferences.getInstance(); |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|     final storage = new FlutterSecureStorage(); |     String domain = prefs.getString('hassio-domain')?? ''; | ||||||
|  |     String port = prefs.getString('hassio-port') ?? ''; | ||||||
|     try { |     String urlProtocol = prefs.getString('hassio-res-protocol') ?? 'https'; | ||||||
|       useOAuth = prefs.getBool("oauth-used") ?? true; |     _homeAssistantUrl = '$urlProtocol://$domain:$port'; | ||||||
|     } catch (e) { |     _deviceName = prefs.getString('app-integration-device-name') ?? MobileAppIntegrationManager.getDefaultDeviceName(); | ||||||
|       useOAuth = true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!useOAuth) { |  | ||||||
|       try { |  | ||||||
|         _longLivedToken = _newLongLivedToken = |  | ||||||
|         await storage.read(key: "hacl_llt"); |  | ||||||
|       } catch (e) { |  | ||||||
|         _longLivedToken = _newLongLivedToken = ""; |  | ||||||
|         await storage.delete(key: "hacl_llt"); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     setState(() { |     setState(() { | ||||||
|       _hassioDomain = _newHassioDomain = prefs.getString("hassio-domain")?? ""; |       _loaded = true; | ||||||
|       _hassioPort = _newHassioPort = prefs.getString("hassio-port") ?? ""; |  | ||||||
|       _socketProtocol = _newSocketProtocol = prefs.getString("hassio-protocol") ?? 'wss'; |  | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool _checkConfigChanged() { |  | ||||||
|     return ( |  | ||||||
|       (_newHassioPort != _hassioPort) || |  | ||||||
|       (_newHassioDomain != _hassioDomain) || |  | ||||||
|       (_newSocketProtocol != _socketProtocol) || |  | ||||||
|       (_newLongLivedToken != _longLivedToken)); |  | ||||||
|  |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _saveSettings() async { |   _saveSettings() async { | ||||||
|     _newHassioDomain = _newHassioDomain.trim(); |     _homeAssistantUrl = _homeAssistantUrl.trim(); | ||||||
|     if (_newHassioDomain.startsWith("http") && _newHassioDomain.indexOf("//") > 0) { |     String socketProtocol; | ||||||
|       _newHassioDomain.startsWith("https") ? _newSocketProtocol = "wss" : _newSocketProtocol = "ws"; |     String domain; | ||||||
|       _newHassioDomain = _newHassioDomain.split("//")[1]; |     String port; | ||||||
|  |     if (_homeAssistantUrl.startsWith("http") && _homeAssistantUrl.indexOf("//") > 0) { | ||||||
|  |       _homeAssistantUrl.startsWith("https") ? socketProtocol = "wss" : socketProtocol = "ws"; | ||||||
|  |       domain = _homeAssistantUrl.split("//")[1]; | ||||||
|  |     } else { | ||||||
|  |       domain = _homeAssistantUrl; | ||||||
|     } |     } | ||||||
|     _newHassioDomain = _newHassioDomain.split("/")[0]; |     domain = domain.split("/")[0]; | ||||||
|     if (_newHassioDomain.contains(":")) { |     if (domain.contains(":")) { | ||||||
|       List<String> domainAndPort = _newHassioDomain.split(":"); |       List<String> domainAndPort = domain.split(":"); | ||||||
|       _newHassioDomain = domainAndPort[0]; |       domain = domainAndPort[0]; | ||||||
|       _newHassioPort = domainAndPort[1]; |       port = domainAndPort[1]; | ||||||
|     } |     } | ||||||
|     SharedPreferences prefs = await SharedPreferences.getInstance(); |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|     final storage = new FlutterSecureStorage(); |     await prefs.setString("hassio-domain", domain); | ||||||
|     if (_newLongLivedToken.isNotEmpty) { |     if (port == null || port.isEmpty) { | ||||||
|       _newLongLivedToken = _newLongLivedToken.trim(); |       port = socketProtocol == "wss" ? "443" : "80"; | ||||||
|       prefs.setBool("oauth-used", false); |  | ||||||
|       await storage.write(key: "hacl_llt", value: _newLongLivedToken); |  | ||||||
|     } else if (!useOAuth) { |  | ||||||
|       await storage.delete(key: "hacl_llt"); |  | ||||||
|     } |  | ||||||
|     prefs.setString("hassio-domain", _newHassioDomain); |  | ||||||
|     if (_newHassioPort == null || _newHassioPort.isEmpty) { |  | ||||||
|       _newHassioPort = _newSocketProtocol == "wss" ? "443" : "80"; |  | ||||||
|     } else { |     } else { | ||||||
|       _newHassioPort = _newHassioPort.trim(); |       port = port.trim(); | ||||||
|  |     } | ||||||
|  |     await prefs.setString("hassio-port", port); | ||||||
|  |     await prefs.setString("hassio-protocol", socketProtocol); | ||||||
|  |     await prefs.setString("hassio-res-protocol", socketProtocol == "wss" ? "https" : "http"); | ||||||
|  |     if (_includeDeviceName) { | ||||||
|  |       await prefs.setString('app-integration-device-name', _deviceName); | ||||||
|     } |     } | ||||||
|     prefs.setString("hassio-port", _newHassioPort); |  | ||||||
|     prefs.setString("hassio-protocol", _newSocketProtocol); |  | ||||||
|     prefs.setString("hassio-res-protocol", _newSocketProtocol == "wss" ? "https" : "http"); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return ListView( |     if (!_loaded) { | ||||||
|       scrollDirection: Axis.vertical, |       return PageLoadingIndicator(); | ||||||
|       padding: const EdgeInsets.all(20.0), |     } | ||||||
|       children: <Widget>[ |     List<Widget> formChildren = <Widget>[ | ||||||
|  |       Text( | ||||||
|  |           "Home Assistant url:", | ||||||
|  |           style: Theme.of(context).textTheme.headline, | ||||||
|  |       ), | ||||||
|  |       TextFormField( | ||||||
|  |         initialValue: _homeAssistantUrl, | ||||||
|  |         decoration: InputDecoration( | ||||||
|  |           hintText: "Please enter url", | ||||||
|  |           contentPadding: EdgeInsets.all(0), | ||||||
|  |           hintStyle: Theme.of(context).textTheme.subhead.copyWith( | ||||||
|  |             color: Theme.of(context).textTheme.overline.color | ||||||
|  |           ) | ||||||
|  |         ), | ||||||
|  |         onSaved: (newValue) { | ||||||
|  |           _homeAssistantUrl = newValue; | ||||||
|  |         }, | ||||||
|  |         validator: (value) { | ||||||
|  |           if (value.isEmpty) { | ||||||
|  |             return 'Url is required'; | ||||||
|  |           } | ||||||
|  |           return null; | ||||||
|  |         }, | ||||||
|  |       ), | ||||||
|  |       Container( | ||||||
|  |         height: 10, | ||||||
|  |       ), | ||||||
|  |       Text( | ||||||
|  |           "For example:", | ||||||
|  |           style: Theme.of(context).textTheme.body1, | ||||||
|  |       ), | ||||||
|  |       Text( | ||||||
|  |           "192.186.2.14:8123", | ||||||
|  |           style: Theme.of(context).textTheme.subhead, | ||||||
|  |       ), | ||||||
|  |       Text( | ||||||
|  |           "http://myhome.duckdns.org:8123", | ||||||
|  |           style: Theme.of(context).textTheme.subhead, | ||||||
|  |       ), | ||||||
|  |       Text( | ||||||
|  |           "https://efkmfrwk3r4fsfwrfrg5.ui.nabu.casa/", | ||||||
|  |           style: Theme.of(context).textTheme.subhead, | ||||||
|  |       ), | ||||||
|  |     ]; | ||||||
|  |  | ||||||
|  |     if (_includeDeviceName) { | ||||||
|  |       formChildren.addAll(<Widget>[ | ||||||
|  |         Container( | ||||||
|  |           height: 30, | ||||||
|  |         ), | ||||||
|         Text( |         Text( | ||||||
|             "Connection settings", |             "Device name:", | ||||||
|             style: Theme.of(context).textTheme.headline, |             style: Theme.of(context).textTheme.headline, | ||||||
|         ), |         ), | ||||||
|         new Row( |         TextFormField( | ||||||
|           children: [ |           initialValue: _deviceName, | ||||||
|             Text("Use ssl (HTTPS)"), |           onSaved: (newValue) { | ||||||
|             Switch( |             _deviceName = newValue; | ||||||
|               value: (_newSocketProtocol == "wss"), |           }, | ||||||
|               onChanged: (value) { |           decoration: InputDecoration( | ||||||
|                 setState(() { |             hintText: 'Please enter device name', | ||||||
|                   _newSocketProtocol = value ? "wss" : "ws"; |             contentPadding: EdgeInsets.all(0), | ||||||
|                 }); |             hintStyle: Theme.of(context).textTheme.subhead.copyWith( | ||||||
|               }, |               color: Theme.of(context).textTheme.overline.color | ||||||
|             ) |             ) | ||||||
|           ], |  | ||||||
|         ), |  | ||||||
|         new TextField( |  | ||||||
|           decoration: InputDecoration( |  | ||||||
|             labelText: "Home Assistant domain or ip address" |  | ||||||
|           ), |           ), | ||||||
|           controller: TextEditingController.fromValue(TextEditingValue(text: _newHassioDomain)), |           validator: (value) { | ||||||
|           onChanged: (value) { |             if (value.isEmpty) { | ||||||
|             _newHassioDomain = value; |               return 'Device name is required'; | ||||||
|           } |  | ||||||
|         ), |  | ||||||
|         new TextField( |  | ||||||
|           decoration: InputDecoration( |  | ||||||
|             labelText: "Home Assistant port (default is 8123)" |  | ||||||
|           ), |  | ||||||
|           controller: TextEditingController.fromValue(TextEditingValue(text: _newHassioPort)), |  | ||||||
|           onChanged: (value) { |  | ||||||
|             _newHassioPort = value; |  | ||||||
|           } |  | ||||||
|         ), |  | ||||||
|         new Text( |  | ||||||
|           "Try ports 80 and 443 if default is not working and you don't know why.", |  | ||||||
|           style: Theme.of(context).textTheme.caption, |  | ||||||
|         ), |  | ||||||
|         Text( |  | ||||||
|           "Authentication settings", |  | ||||||
|           style: Theme.of(context).textTheme.headline, |  | ||||||
|         ), |  | ||||||
|         Container(height: 10.0,), |  | ||||||
|         Text( |  | ||||||
|           "You can leave this field blank to make app generate new long-lived token automatically by asking you to login to your Home Assistant. Use this field only if you still want to use manually generated long-lived token. Leave it blank if you don't understand what we are talking about.", |  | ||||||
|           style: Theme.of(context).textTheme.body1.copyWith( |  | ||||||
|             color: Colors.redAccent |  | ||||||
|           ), |  | ||||||
|         ), |  | ||||||
|         new TextField( |  | ||||||
|             decoration: InputDecoration( |  | ||||||
|                 labelText: "Long-lived token" |  | ||||||
|             ), |  | ||||||
|             controller: TextEditingController.fromValue(TextEditingValue(text: _newLongLivedToken)), |  | ||||||
|             onChanged: (value) { |  | ||||||
|               _newLongLivedToken = value; |  | ||||||
|             } |             } | ||||||
|  |             return null; | ||||||
|  |           }, | ||||||
|         ), |         ), | ||||||
|         Container( |       ]); | ||||||
|           height: Sizes.rowPadding, |     } | ||||||
|         ), |  | ||||||
|         RaisedButton( |     formChildren.addAll(<Widget>[ | ||||||
|           child: Text('Apply', style: Theme.of(context).textTheme.button), |       Container( | ||||||
|           color: Theme.of(context).primaryColorDark, |         height: 30, | ||||||
|  |       ), | ||||||
|  |       ButtonTheme( | ||||||
|  |         height: 60, | ||||||
|  |         child: RaisedButton( | ||||||
|  |           child: Text(widget.quickStart ? 'Engage' : 'Apply', style: Theme.of(context).textTheme.button.copyWith(fontSize: 20)), | ||||||
|  |           color: Theme.of(context).primaryColor, | ||||||
|           onPressed: () { |           onPressed: () { | ||||||
|             if (_checkConfigChanged()) { |             if (_formKey.currentState.validate()) { | ||||||
|               Logger.d("Settings changed. Saving..."); |               _formKey.currentState.save(); | ||||||
|               _saveSettings().then((r) { |               _saveSettings().then((r) { | ||||||
|                 Navigator.pop(context); |                 if (widget.quickStart) { | ||||||
|  |                   Navigator.pushReplacementNamed(context, '/'); | ||||||
|  |                 } else { | ||||||
|  |                   Navigator.pop(context); | ||||||
|  |                 } | ||||||
|                 eventBus.fire(SettingsChangedEvent(true)); |                 eventBus.fire(SettingsChangedEvent(true)); | ||||||
|               }); |               }); | ||||||
|             } else { |  | ||||||
|               Logger.d("Settings was not changed"); |  | ||||||
|               Navigator.pop(context); |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|         ) |         ) | ||||||
|       ], |       ) | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     return Form( | ||||||
|  |       key: _formKey, | ||||||
|  |       child: ListView( | ||||||
|  |         scrollDirection: Axis.vertical, | ||||||
|  |         padding: const EdgeInsets.all(20.0), | ||||||
|  |         children: formChildren, | ||||||
|  |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -76,6 +76,12 @@ class ShowPopupMessageEvent { | |||||||
|   ShowPopupMessageEvent({this.title, this.body, this.buttonText: "Ok", this.onButtonClick}); |   ShowPopupMessageEvent({this.title, this.body, this.buttonText: "Ok", this.onButtonClick}); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | class ShowTokenLoginPopupEvent {     | ||||||
|  |   final bool goBackFirst; | ||||||
|  |  | ||||||
|  |   ShowTokenLoginPopupEvent({this.goBackFirst: false}); | ||||||
|  | } | ||||||
|  |  | ||||||
| class ShowEntityPageEvent { | class ShowEntityPageEvent { | ||||||
|   final String entityId; |   final String entityId; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,6 +24,13 @@ class HACException implements Exception { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | class HACNotSetUpException implements Exception { | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return 'HA Client is not set up'; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| class HAErrorAction { | class HAErrorAction { | ||||||
|   final String title; |   final String title; | ||||||
|   final int type; |   final int type; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user