[#43] Handling bad configuration. Advanced connection error handling
This commit is contained in:
parent
e5fbc40c66
commit
eceece5a90
@ -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