[#43] Handling bad configuration. Advanced connection error handling
This commit is contained in:
		| @@ -6,6 +6,12 @@ class StateChangedEvent { | |||||||
|   StateChangedEvent(this.entityId); |   StateChangedEvent(this.entityId); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | class SettingsChangedEvent { | ||||||
|  |   bool reconnect; | ||||||
|  |  | ||||||
|  |   SettingsChangedEvent(this.reconnect); | ||||||
|  | } | ||||||
|  |  | ||||||
| class HassioDataModel { | class HassioDataModel { | ||||||
|   String _hassioAPIEndpoint; |   String _hassioAPIEndpoint; | ||||||
|   String _hassioPassword; |   String _hassioPassword; | ||||||
| @@ -56,7 +62,7 @@ class HassioDataModel { | |||||||
|  |  | ||||||
|   closeConnection() { |   closeConnection() { | ||||||
|     if (_hassioChannel?.closeCode == null) { |     if (_hassioChannel?.closeCode == null) { | ||||||
|       _hassioChannel.sink?.close(); |       _hassioChannel?.sink?.close(); | ||||||
|     } |     } | ||||||
|     _hassioChannel = null; |     _hassioChannel = null; | ||||||
|   } |   } | ||||||
| @@ -111,7 +117,7 @@ class HassioDataModel { | |||||||
|       _sendSubscribe(); |       _sendSubscribe(); | ||||||
|       connectionCompleter.complete(); |       connectionCompleter.complete(); | ||||||
|     } else if (data["type"] == "auth_invalid") { |     } else if (data["type"] == "auth_invalid") { | ||||||
|       connectionCompleter.completeError({message: "Auth error: ${data["message"]}"}); |       connectionCompleter.completeError({"errorCode": 6, "errorMessage": "${data["message"]}"}); | ||||||
|     } else if (data["type"] == "result") { |     } else if (data["type"] == "result") { | ||||||
|       if (data["id"] == _configMessageId) { |       if (data["id"] == _configMessageId) { | ||||||
|         _parseConfig(data); |         _parseConfig(data); | ||||||
|   | |||||||
							
								
								
									
										111
									
								
								lib/main.dart
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								lib/main.dart
									
									
									
									
									
								
							| @@ -51,8 +51,10 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver { | |||||||
|   Map _instanceConfig; |   Map _instanceConfig; | ||||||
|   int _uiViewsCount = 0; |   int _uiViewsCount = 0; | ||||||
|   String _instanceHost; |   String _instanceHost; | ||||||
|   int _fetchErrorCode = 0; |   int _errorCodeToBeShown = 0; | ||||||
|   bool loading = true; |   String _lastErrorMessage = ""; | ||||||
|  |   StreamSubscription _stateSubscription; | ||||||
|  |   bool _isLoading = true; | ||||||
|   Map _stateIconColors = { |   Map _stateIconColors = { | ||||||
|     "on": Colors.amber, |     "on": Colors.amber, | ||||||
|     "off": Colors.blueGrey, |     "off": Colors.blueGrey, | ||||||
| @@ -65,7 +67,14 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver { | |||||||
|   void initState() { |   void initState() { | ||||||
|     super.initState(); |     super.initState(); | ||||||
|     WidgetsBinding.instance.addObserver(this); |     WidgetsBinding.instance.addObserver(this); | ||||||
|     _init(); |     eventBus.on<SettingsChangedEvent>().listen((event) { | ||||||
|  |       debugPrint("Settings change event: reconnect=${event.reconnect}"); | ||||||
|  |       setState(() { | ||||||
|  |         _errorCodeToBeShown = 0; | ||||||
|  |       }); | ||||||
|  |       _initConnection(); | ||||||
|  |     }); | ||||||
|  |     _initConnection(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
| @@ -76,16 +85,29 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   _init() async { |   _initConnection() async { | ||||||
|     SharedPreferences prefs = await SharedPreferences.getInstance(); |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|     String domain = prefs.getString('hassio-domain'); |     String domain = prefs.getString('hassio-domain'); | ||||||
|     String port = prefs.getString('hassio-port'); |     String port = prefs.getString('hassio-port'); | ||||||
|     _instanceHost = "$domain:$port"; |     _instanceHost = "$domain:$port"; | ||||||
|     String _hassioAPIEndpoint = "${prefs.getString('hassio-protocol')}://$domain:$port/api/websocket"; |     String apiEndpoint = "${prefs.getString('hassio-protocol')}://$domain:$port/api/websocket"; | ||||||
|     String _hassioPassword = prefs.getString('hassio-password'); |     String apiPassword = prefs.getString('hassio-password'); | ||||||
|     _dataModel = HassioDataModel(_hassioAPIEndpoint, _hassioPassword); |     if ((domain == null) || (port == null) || (apiEndpoint == null) || (apiPassword == null) || | ||||||
|  |         (domain.length == 0) || (port.length == 0) || (apiEndpoint.length == 0) || (apiPassword.length == 0)) { | ||||||
|  |       setState(() { | ||||||
|  |         _errorCodeToBeShown = 5; | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       if (_dataModel != null) _dataModel.closeConnection(); | ||||||
|  |       _createConnection(apiEndpoint, apiPassword); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   _createConnection(String apiEndpoint, String apiPassword) { | ||||||
|  |     _dataModel = HassioDataModel(apiEndpoint, apiPassword); | ||||||
|     _refreshData(); |     _refreshData(); | ||||||
|     eventBus.on<StateChangedEvent>().listen((event) { |     if (_stateSubscription != null) _stateSubscription.cancel(); | ||||||
|  |     _stateSubscription = eventBus.on<StateChangedEvent>().listen((event) { | ||||||
|       debugPrint("State change event for ${event.entityId}"); |       debugPrint("State change event for ${event.entityId}"); | ||||||
|       setState(() { |       setState(() { | ||||||
|         _entitiesData = _dataModel.entities; |         _entitiesData = _dataModel.entities; | ||||||
| @@ -95,9 +117,9 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver { | |||||||
|  |  | ||||||
|   _refreshData() async { |   _refreshData() async { | ||||||
|     setState(() { |     setState(() { | ||||||
|       loading = true; |       _isLoading = true; | ||||||
|     }); |     }); | ||||||
|     _fetchErrorCode = 0; |     _errorCodeToBeShown = 0; | ||||||
|     if (_dataModel != null) { |     if (_dataModel != null) { | ||||||
|       await _dataModel.fetch().then((result) { |       await _dataModel.fetch().then((result) { | ||||||
|         setState(() { |         setState(() { | ||||||
| @@ -105,12 +127,13 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver { | |||||||
|           _entitiesData = _dataModel.entities; |           _entitiesData = _dataModel.entities; | ||||||
|           _uiStructure = _dataModel.uiStructure; |           _uiStructure = _dataModel.uiStructure; | ||||||
|           _uiViewsCount = _uiStructure.length; |           _uiViewsCount = _uiStructure.length; | ||||||
|           loading = false; |           _isLoading = false; | ||||||
|         }); |         }); | ||||||
|       }).catchError((e) { |       }).catchError((e) { | ||||||
|         setState(() { |         setState(() { | ||||||
|           _fetchErrorCode = e["errorCode"] != null ? e["errorCode"] : 2; |           _errorCodeToBeShown = e["errorCode"] != null ? e["errorCode"] : 99; | ||||||
|           loading = false; |           _lastErrorMessage = e["errorMessage"] ?? "Unknown error"; | ||||||
|  |           _isLoading = false; | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
| @@ -259,7 +282,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver { | |||||||
|     Row titleRow = Row( |     Row titleRow = Row( | ||||||
|       children: [Text(_instanceConfig != null ? _instanceConfig["location_name"] : "")], |       children: [Text(_instanceConfig != null ? _instanceConfig["location_name"] : "")], | ||||||
|     ); |     ); | ||||||
|     if (loading) { |     if (_isLoading) { | ||||||
|       titleRow.children.add(Padding( |       titleRow.children.add(Padding( | ||||||
|         child: JumpingDotsProgressIndicator( |         child: JumpingDotsProgressIndicator( | ||||||
|           fontSize: 26.0, |           fontSize: 26.0, | ||||||
| @@ -297,35 +320,61 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver { | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   _getErrorMessageByCode(int code, bool short) { |  | ||||||
|     String message = short ? "Unknown error" : "Unknown error"; |  | ||||||
|     switch (code) { |  | ||||||
|       case 1: { |  | ||||||
|         message = short ? "Unable to connect" : "Unable to connect\n Please check your internet connection and Home Assistant instance state"; |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return message; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _checkShowInfo(BuildContext context) { |   _checkShowInfo(BuildContext context) { | ||||||
|     if (_fetchErrorCode > 0) { |     if (_errorCodeToBeShown > 0) { | ||||||
|       String text = _getErrorMessageByCode(_fetchErrorCode, true); |       String message = _lastErrorMessage; | ||||||
|       SnackBarAction action; |       SnackBarAction action; | ||||||
|       switch (_fetchErrorCode) { |       switch (_errorCodeToBeShown) { | ||||||
|         case 1: { |         case 1: { | ||||||
|             action = SnackBarAction( |             action = SnackBarAction( | ||||||
|                 label: "Retry", |                 label: "Retry", | ||||||
|                 onPressed: _refreshData, |                 onPressed: () { | ||||||
|  |                   _scaffoldKey?.currentState?.hideCurrentSnackBar(); | ||||||
|  |                   _refreshData(); | ||||||
|  |                 }, | ||||||
|             ); |             ); | ||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|  |  | ||||||
|  |         case 5: { | ||||||
|  |           message = "Check connection settings"; | ||||||
|  |           action = SnackBarAction( | ||||||
|  |             label: "Open", | ||||||
|  |             onPressed: () { | ||||||
|  |               _scaffoldKey?.currentState?.hideCurrentSnackBar(); | ||||||
|  |               Navigator.pushNamed(context, '/connection-settings'); | ||||||
|  |             }, | ||||||
|  |           ); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case 6: { | ||||||
|  |           action = SnackBarAction( | ||||||
|  |             label: "Settings", | ||||||
|  |             onPressed: () { | ||||||
|  |               _scaffoldKey?.currentState?.hideCurrentSnackBar(); | ||||||
|  |               Navigator.pushNamed(context, '/connection-settings'); | ||||||
|  |             }, | ||||||
|  |           ); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case 7: { | ||||||
|  |           action = SnackBarAction( | ||||||
|  |             label: "Retry", | ||||||
|  |             onPressed: () { | ||||||
|  |               _scaffoldKey?.currentState?.hideCurrentSnackBar(); | ||||||
|  |               _refreshData(); | ||||||
|  |             }, | ||||||
|  |           ); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|       Timer(Duration(seconds: 1), () { |       Timer(Duration(seconds: 1), () { | ||||||
|         _scaffoldKey.currentState.hideCurrentSnackBar(); |         _scaffoldKey.currentState.hideCurrentSnackBar(); | ||||||
|         _scaffoldKey.currentState.showSnackBar( |         _scaffoldKey.currentState.showSnackBar( | ||||||
|             SnackBar( |             SnackBar( | ||||||
|                 content: Text("$text"), |                 content: Text("$message (code: $_errorCodeToBeShown)"), | ||||||
|                 action: action, |                 action: action, | ||||||
|                 duration: Duration(hours: 1), |                 duration: Duration(hours: 1), | ||||||
|             ) |             ) | ||||||
| @@ -363,7 +412,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver { | |||||||
|               Icon( |               Icon( | ||||||
|                 _createMDIfromCode(MaterialDesignIcons.getCustomIconByName("mdi:home-assistant")), |                 _createMDIfromCode(MaterialDesignIcons.getCustomIconByName("mdi:home-assistant")), | ||||||
|                 size: 100.0, |                 size: 100.0, | ||||||
|                 color: _fetchErrorCode == 0 ? Colors.blue : Colors.redAccent, |                 color: _errorCodeToBeShown == 0 ? Colors.blue : Colors.redAccent, | ||||||
|               ), |               ), | ||||||
|             ] |             ] | ||||||
|           ), |           ), | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ class ConnectionSettingsPage extends StatefulWidget { | |||||||
|  |  | ||||||
| class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> { | class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> { | ||||||
|   String _hassioDomain = ""; |   String _hassioDomain = ""; | ||||||
|   String _hassioPort = ""; |   String _hassioPort = "8123"; | ||||||
|   String _hassioPassword = ""; |   String _hassioPassword = ""; | ||||||
|   String _socketProtocol = "wss"; |   String _socketProtocol = "wss"; | ||||||
|  |  | ||||||
| @@ -26,20 +26,18 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> { | |||||||
|  |  | ||||||
|     setState(() { |     setState(() { | ||||||
|       _hassioDomain = prefs.getString("hassio-domain"); |       _hassioDomain = prefs.getString("hassio-domain"); | ||||||
|       _hassioPort = prefs.getString("hassio-port"); |       _hassioPort = prefs.getString("hassio-port") ?? '8123'; | ||||||
|       _hassioPassword = prefs.getString("hassio-password"); |       _hassioPassword = prefs.getString("hassio-password"); | ||||||
|       _socketProtocol = prefs.getString("hassio-protocol"); |       _socketProtocol = prefs.getString("hassio-protocol") ?? 'wss'; | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   _saveSettings() async { |   _saveSettings() async { | ||||||
|     debugPrint("Saving settings...."); |  | ||||||
|     SharedPreferences prefs = await SharedPreferences.getInstance(); |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|     prefs.setString("hassio-domain", _hassioDomain); |     prefs.setString("hassio-domain", _hassioDomain); | ||||||
|     prefs.setString("hassio-port", _hassioPort); |     prefs.setString("hassio-port", _hassioPort); | ||||||
|     prefs.setString("hassio-password", _hassioPassword); |     prefs.setString("hassio-password", _hassioPassword); | ||||||
|     prefs.setString("hassio-protocol", _socketProtocol); |     prefs.setString("hassio-protocol", _socketProtocol); | ||||||
|     debugPrint("Done saving settings...."); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
| @@ -47,8 +45,10 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> { | |||||||
|     return new Scaffold( |     return new Scaffold( | ||||||
|       appBar: new AppBar( |       appBar: new AppBar( | ||||||
|         leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){ |         leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){ | ||||||
|           _saveSettings(); |           _saveSettings().then((r){ | ||||||
|           Navigator.pop(context); |             Navigator.pop(context); | ||||||
|  |           }); | ||||||
|  |           eventBus.fire(SettingsChangedEvent(true)); | ||||||
|         }), |         }), | ||||||
|         // Here we take the value from the MyHomePage object that was created by |         // Here we take the value from the MyHomePage object that was created by | ||||||
|         // the App.build method, and use it to set our appbar title. |         // the App.build method, and use it to set our appbar title. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user