Resolves #124: Connection handling improvements

This commit is contained in:
Yegor Vialov 2018-10-06 16:01:38 +03:00
parent c975af4c79
commit c2b88c8a12
3 changed files with 70 additions and 80 deletions

View File

@ -55,27 +55,23 @@ class HomeAssistant {
} else {
_fetchCompleter = new Completer();
_fetchTimer = Timer(fetchTimeout, () {
closeConnection();
TheLogger.log("Error", "Data fetching timeout");
_finishFetching({"errorCode" : 9,"errorMessage": "Couldn't get data from server"});
_completeFetching({"errorCode" : 9,"errorMessage": "Couldn't get data from server"});
});
_connection().then((r) {
_getData();
}).catchError((e) {
_finishFetching(e);
_completeFetching(e);
});
}
return _fetchCompleter.future;
}
closeConnection() {
if (_socketSubscription != null) {
_socketSubscription.cancel();
}
if (_hassioChannel != null) {
if (_hassioChannel.closeCode == null) {
_hassioChannel.sink?.close();
}
disconnect() async {
if ((_hassioChannel != null) && (_hassioChannel.closeCode == null) && (_hassioChannel.sink != null)) {
await _hassioChannel.sink.close().timeout(Duration(seconds: 3),
onTimeout: () => TheLogger.log("Warning", "Socket sink closing timeout")
);
_hassioChannel = null;
}
}
@ -84,30 +80,35 @@ class HomeAssistant {
if ((_connectionCompleter != null) && (!_connectionCompleter.isCompleted)) {
TheLogger.log("Debug","Previous connection is not complited");
} else {
if ((_hassioChannel == null) || (_hassioChannel.sink == null) || (_hassioChannel.closeCode != null)) {
closeConnection();
TheLogger.log("Debug", "Socket connecting...");
_connectionCompleter = new Completer();
if ((_hassioChannel == null) || (_hassioChannel.closeCode != null)) {
disconnect().then((_){
TheLogger.log("Debug", "Socket connecting...");
_connectionTimer = Timer(connectTimeout, () {
closeConnection();
TheLogger.log("Error", "Socket connection timeout");
_finishConnecting({"errorCode" : 1,"errorMessage": "Couldn't connect to Home Assistant. Looks like a network issues"});
_completeConnecting({"errorCode" : 1,"errorMessage": "Couldn't connect to Home Assistant. Check network connection or connection settings."});
});
if (_socketSubscription != null) {
_socketSubscription.cancel();
}
_hassioChannel = IOWebSocketChannel.connect(
_hassioAPIEndpoint, pingInterval: Duration(seconds: 30));
_hassioChannel.stream.handleError((e) {
TheLogger.log("Error", "Unhandled socket error: ${e.toString()}");
});
if (_socketSubscription != null) _socketSubscription.cancel();
_socketSubscription = _hassioChannel.stream.listen((message) =>
_handleMessage(_connectionCompleter, message));
_hassioChannel.sink.done.whenComplete(() {
TheLogger.log("Debug","Socket sink finished. Assuming it is closed.");
closeConnection();
_socketSubscription = _hassioChannel.stream.listen(
(message) => _handleMessage(_connectionCompleter, message),
cancelOnError: true,
onDone: () {
TheLogger.log("Debug","Socket stream closed. Disconnected.");
disconnect();
},
onError: (e) {
TheLogger.log("Error","Socket stream Error: $e");
disconnect().then((_) => _completeConnecting({"errorCode" : 1,"errorMessage": "Couldn't connect to Home Assistant. Check network connection or connection settings."}));
}
);
});
} else {
//TheLogger.log("Debug","Socket looks connected...${_hassioChannel.protocol}, ${_hassioChannel.closeCode}, ${_hassioChannel.closeReason}");
_finishConnecting(null);
_completeConnecting(null);
}
}
return _connectionCompleter.future;
@ -117,40 +118,36 @@ class HomeAssistant {
_getConfig().then((result) {
_getStates().then((result) {
_getServices().then((result) {
_finishFetching(null);
}).catchError((e) {
_finishFetching(e);
_completeFetching(null);
});
});
}).catchError((e) {
_finishFetching(e);
});
}).catchError((e) {
_finishFetching(e);
_completeFetching(e);
});
}
void _finishFetching(error) {
void _completeFetching(error) {
_fetchTimer.cancel();
_finishConnecting(error);
_completeConnecting(error);
if (!_fetchCompleter.isCompleted) {
if (error != null) {
if (!_fetchCompleter.isCompleted)
_fetchCompleter.completeError(error);
} else {
if (!_fetchCompleter.isCompleted)
_fetchCompleter.complete();
}
}
}
void _finishConnecting(error) {
void _completeConnecting(error) {
_connectionTimer.cancel();
if (!_connectionCompleter.isCompleted) {
if (error != null) {
if (!_connectionCompleter.isCompleted)
_connectionCompleter.completeError(error);
} else {
if (!_connectionCompleter.isCompleted)
_connectionCompleter.complete();
}
}
}
_handleMessage(Completer connectionCompleter, String message) {
var data = json.decode(message);
@ -158,10 +155,10 @@ class HomeAssistant {
if (data["type"] == "auth_required") {
_sendAuthMessageRaw('{"type": "auth","$_hassioAuthType": "$_hassioPassword"}');
} else if (data["type"] == "auth_ok") {
_finishConnecting(null);
_completeConnecting(null);
_sendSubscribe();
} else if (data["type"] == "auth_invalid") {
_finishFetching({"errorCode": 6, "errorMessage": "${data["message"]}"});
_completeFetching({"errorCode": 6, "errorMessage": "${data["message"]}"});
} else if (data["type"] == "result") {
if (data["id"] == _configMessageId) {
_parseConfig(data);

View File

@ -118,22 +118,20 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
_settingsSubscription = eventBus.on<SettingsChangedEvent>().listen((event) {
TheLogger.log("Debug","Settings change event: reconnect=${event.reconnect}");
if (event.reconnect) {
_homeAssistant.closeConnection();
_initConnection().then((b){
setState(() {
_homeAssistant.updateConnectionSettings(_apiEndpoint, _apiPassword, _authType);
_errorCodeToBeShown = 10;
_lastErrorMessage = "Connection settings was changed.";
});
_homeAssistant.disconnect().then((_){
_loadConnectionSettings().then((b){
_refreshData();
}, onError: (_) {
setState(() {
_lastErrorMessage = _;
_errorCodeToBeShown = 5;
});
}
);
});
}
});
_initConnection().then((_){
_loadConnectionSettings().then((_){
_createConnection();
}, onError: (_) {
setState(() {
@ -151,7 +149,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
}
}
_initConnection() async {
_loadConnectionSettings() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String domain = prefs.getString('hassio-domain');
String port = prefs.getString('hassio-port');
@ -681,7 +679,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
if (_settingsSubscription != null) _settingsSubscription.cancel();
if (_serviceCallSubscription != null) _serviceCallSubscription.cancel();
if (_showEntityPageSubscription != null) _showEntityPageSubscription.cancel();
_homeAssistant.closeConnection();
_homeAssistant.disconnect();
super.dispose();
}
}

View File

@ -11,11 +11,11 @@ class ConnectionSettingsPage extends StatefulWidget {
class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
String _hassioDomain = "";
String _hassioPort = "8123";
String _hassioPort = "";
String _hassioPassword = "";
String _socketProtocol = "wss";
String _authType = "access_token";
bool _connectionSettingsChanged = false;
bool _edited = false;
@override
void initState() {
@ -27,9 +27,9 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_hassioDomain = prefs.getString("hassio-domain");
_hassioPort = prefs.getString("hassio-port") ?? '8123';
_hassioPassword = prefs.getString("hassio-password");
_hassioDomain = prefs.getString("hassio-domain")?? "";
_hassioPort = prefs.getString("hassio-port") ?? "";
_hassioPassword = prefs.getString("hassio-password") ?? "";
_socketProtocol = prefs.getString("hassio-protocol") ?? 'wss';
_authType = prefs.getString("hassio-auth-type") ?? 'access_token';
});
@ -46,7 +46,6 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
prefs.setString("hassio-protocol", _socketProtocol);
prefs.setString("hassio-res-protocol", _socketProtocol == "wss" ? "https" : "http");
prefs.setString("hassio-auth-type", _authType);
_connectionSettingsChanged = true;
}
@override
@ -60,16 +59,12 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
actions: <Widget>[
IconButton(
icon: Icon(Icons.check),
onPressed:(){
if (_connectionSettingsChanged) {
onPressed: _edited ? (){
_saveSettings().then((r){
Navigator.pop(context);
eventBus.fire(SettingsChangedEvent(_connectionSettingsChanged));
eventBus.fire(SettingsChangedEvent(true));
});
} else {
Navigator.pop(context);
}
}
} : null
)
],
),
@ -84,8 +79,8 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
onChanged: (value) {
setState(() {
_socketProtocol = value ? "wss" : "ws";
_edited = true;
});
_saveSettings();
},
)
],
@ -99,19 +94,18 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
),
onChanged: (value) {
_hassioDomain = value;
_saveSettings();
},
),
new TextField(
decoration: InputDecoration(
labelText: "Home Assistant port"
labelText: "Home Assistant port (default is 8123)"
),
controller: TextEditingController(
text: _hassioPort
),
onChanged: (value) {
_hassioPort = value;
_saveSettings();
//_saveSettings();
},
),
new Row(
@ -122,8 +116,9 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
onChanged: (value) {
setState(() {
_authType = value ? "access_token" : "api_password";
_edited = true;
});
_saveSettings();
//_saveSettings();
},
)
],
@ -137,7 +132,7 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
),
onChanged: (value) {
_hassioPassword = value;
_saveSettings();
//_saveSettings();
},
)
],