Resolves #124: Connection handling improvements
This commit is contained in:
parent
c975af4c79
commit
c2b88c8a12
@ -55,27 +55,23 @@ class HomeAssistant {
|
|||||||
} else {
|
} else {
|
||||||
_fetchCompleter = new Completer();
|
_fetchCompleter = new Completer();
|
||||||
_fetchTimer = Timer(fetchTimeout, () {
|
_fetchTimer = Timer(fetchTimeout, () {
|
||||||
closeConnection();
|
|
||||||
TheLogger.log("Error", "Data fetching timeout");
|
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) {
|
_connection().then((r) {
|
||||||
_getData();
|
_getData();
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
_finishFetching(e);
|
_completeFetching(e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return _fetchCompleter.future;
|
return _fetchCompleter.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
closeConnection() {
|
disconnect() async {
|
||||||
if (_socketSubscription != null) {
|
if ((_hassioChannel != null) && (_hassioChannel.closeCode == null) && (_hassioChannel.sink != null)) {
|
||||||
_socketSubscription.cancel();
|
await _hassioChannel.sink.close().timeout(Duration(seconds: 3),
|
||||||
}
|
onTimeout: () => TheLogger.log("Warning", "Socket sink closing timeout")
|
||||||
if (_hassioChannel != null) {
|
);
|
||||||
if (_hassioChannel.closeCode == null) {
|
|
||||||
_hassioChannel.sink?.close();
|
|
||||||
}
|
|
||||||
_hassioChannel = null;
|
_hassioChannel = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,30 +80,35 @@ class HomeAssistant {
|
|||||||
if ((_connectionCompleter != null) && (!_connectionCompleter.isCompleted)) {
|
if ((_connectionCompleter != null) && (!_connectionCompleter.isCompleted)) {
|
||||||
TheLogger.log("Debug","Previous connection is not complited");
|
TheLogger.log("Debug","Previous connection is not complited");
|
||||||
} else {
|
} else {
|
||||||
if ((_hassioChannel == null) || (_hassioChannel.sink == null) || (_hassioChannel.closeCode != null)) {
|
|
||||||
closeConnection();
|
|
||||||
TheLogger.log("Debug", "Socket connecting...");
|
|
||||||
_connectionCompleter = new Completer();
|
_connectionCompleter = new Completer();
|
||||||
|
if ((_hassioChannel == null) || (_hassioChannel.closeCode != null)) {
|
||||||
|
disconnect().then((_){
|
||||||
|
TheLogger.log("Debug", "Socket connecting...");
|
||||||
_connectionTimer = Timer(connectTimeout, () {
|
_connectionTimer = Timer(connectTimeout, () {
|
||||||
closeConnection();
|
|
||||||
TheLogger.log("Error", "Socket connection timeout");
|
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(
|
_hassioChannel = IOWebSocketChannel.connect(
|
||||||
_hassioAPIEndpoint, pingInterval: Duration(seconds: 30));
|
_hassioAPIEndpoint, pingInterval: Duration(seconds: 30));
|
||||||
_hassioChannel.stream.handleError((e) {
|
_socketSubscription = _hassioChannel.stream.listen(
|
||||||
TheLogger.log("Error", "Unhandled socket error: ${e.toString()}");
|
(message) => _handleMessage(_connectionCompleter, message),
|
||||||
});
|
cancelOnError: true,
|
||||||
if (_socketSubscription != null) _socketSubscription.cancel();
|
onDone: () {
|
||||||
_socketSubscription = _hassioChannel.stream.listen((message) =>
|
TheLogger.log("Debug","Socket stream closed. Disconnected.");
|
||||||
_handleMessage(_connectionCompleter, message));
|
disconnect();
|
||||||
_hassioChannel.sink.done.whenComplete(() {
|
},
|
||||||
TheLogger.log("Debug","Socket sink finished. Assuming it is closed.");
|
onError: (e) {
|
||||||
closeConnection();
|
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 {
|
} else {
|
||||||
//TheLogger.log("Debug","Socket looks connected...${_hassioChannel.protocol}, ${_hassioChannel.closeCode}, ${_hassioChannel.closeReason}");
|
//TheLogger.log("Debug","Socket looks connected...${_hassioChannel.protocol}, ${_hassioChannel.closeCode}, ${_hassioChannel.closeReason}");
|
||||||
_finishConnecting(null);
|
_completeConnecting(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _connectionCompleter.future;
|
return _connectionCompleter.future;
|
||||||
@ -117,40 +118,36 @@ class HomeAssistant {
|
|||||||
_getConfig().then((result) {
|
_getConfig().then((result) {
|
||||||
_getStates().then((result) {
|
_getStates().then((result) {
|
||||||
_getServices().then((result) {
|
_getServices().then((result) {
|
||||||
_finishFetching(null);
|
_completeFetching(null);
|
||||||
}).catchError((e) {
|
});
|
||||||
_finishFetching(e);
|
|
||||||
});
|
});
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
_finishFetching(e);
|
_completeFetching(e);
|
||||||
});
|
|
||||||
}).catchError((e) {
|
|
||||||
_finishFetching(e);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _finishFetching(error) {
|
void _completeFetching(error) {
|
||||||
_fetchTimer.cancel();
|
_fetchTimer.cancel();
|
||||||
_finishConnecting(error);
|
_completeConnecting(error);
|
||||||
|
if (!_fetchCompleter.isCompleted) {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
if (!_fetchCompleter.isCompleted)
|
|
||||||
_fetchCompleter.completeError(error);
|
_fetchCompleter.completeError(error);
|
||||||
} else {
|
} else {
|
||||||
if (!_fetchCompleter.isCompleted)
|
|
||||||
_fetchCompleter.complete();
|
_fetchCompleter.complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _finishConnecting(error) {
|
void _completeConnecting(error) {
|
||||||
_connectionTimer.cancel();
|
_connectionTimer.cancel();
|
||||||
|
if (!_connectionCompleter.isCompleted) {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
if (!_connectionCompleter.isCompleted)
|
|
||||||
_connectionCompleter.completeError(error);
|
_connectionCompleter.completeError(error);
|
||||||
} else {
|
} else {
|
||||||
if (!_connectionCompleter.isCompleted)
|
|
||||||
_connectionCompleter.complete();
|
_connectionCompleter.complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_handleMessage(Completer connectionCompleter, String message) {
|
_handleMessage(Completer connectionCompleter, String message) {
|
||||||
var data = json.decode(message);
|
var data = json.decode(message);
|
||||||
@ -158,10 +155,10 @@ class HomeAssistant {
|
|||||||
if (data["type"] == "auth_required") {
|
if (data["type"] == "auth_required") {
|
||||||
_sendAuthMessageRaw('{"type": "auth","$_hassioAuthType": "$_hassioPassword"}');
|
_sendAuthMessageRaw('{"type": "auth","$_hassioAuthType": "$_hassioPassword"}');
|
||||||
} else if (data["type"] == "auth_ok") {
|
} else if (data["type"] == "auth_ok") {
|
||||||
_finishConnecting(null);
|
_completeConnecting(null);
|
||||||
_sendSubscribe();
|
_sendSubscribe();
|
||||||
} else if (data["type"] == "auth_invalid") {
|
} else if (data["type"] == "auth_invalid") {
|
||||||
_finishFetching({"errorCode": 6, "errorMessage": "${data["message"]}"});
|
_completeFetching({"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);
|
||||||
|
@ -118,22 +118,20 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
_settingsSubscription = eventBus.on<SettingsChangedEvent>().listen((event) {
|
_settingsSubscription = eventBus.on<SettingsChangedEvent>().listen((event) {
|
||||||
TheLogger.log("Debug","Settings change event: reconnect=${event.reconnect}");
|
TheLogger.log("Debug","Settings change event: reconnect=${event.reconnect}");
|
||||||
if (event.reconnect) {
|
if (event.reconnect) {
|
||||||
_homeAssistant.closeConnection();
|
_homeAssistant.disconnect().then((_){
|
||||||
_initConnection().then((b){
|
_loadConnectionSettings().then((b){
|
||||||
setState(() {
|
_refreshData();
|
||||||
_homeAssistant.updateConnectionSettings(_apiEndpoint, _apiPassword, _authType);
|
|
||||||
_errorCodeToBeShown = 10;
|
|
||||||
_lastErrorMessage = "Connection settings was changed.";
|
|
||||||
});
|
|
||||||
}, onError: (_) {
|
}, onError: (_) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_lastErrorMessage = _;
|
_lastErrorMessage = _;
|
||||||
_errorCodeToBeShown = 5;
|
_errorCodeToBeShown = 5;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_initConnection().then((_){
|
_loadConnectionSettings().then((_){
|
||||||
_createConnection();
|
_createConnection();
|
||||||
}, onError: (_) {
|
}, onError: (_) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -151,7 +149,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_initConnection() async {
|
_loadConnectionSettings() 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');
|
||||||
@ -681,7 +679,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
if (_settingsSubscription != null) _settingsSubscription.cancel();
|
if (_settingsSubscription != null) _settingsSubscription.cancel();
|
||||||
if (_serviceCallSubscription != null) _serviceCallSubscription.cancel();
|
if (_serviceCallSubscription != null) _serviceCallSubscription.cancel();
|
||||||
if (_showEntityPageSubscription != null) _showEntityPageSubscription.cancel();
|
if (_showEntityPageSubscription != null) _showEntityPageSubscription.cancel();
|
||||||
_homeAssistant.closeConnection();
|
_homeAssistant.disconnect();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,11 @@ class ConnectionSettingsPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
||||||
String _hassioDomain = "";
|
String _hassioDomain = "";
|
||||||
String _hassioPort = "8123";
|
String _hassioPort = "";
|
||||||
String _hassioPassword = "";
|
String _hassioPassword = "";
|
||||||
String _socketProtocol = "wss";
|
String _socketProtocol = "wss";
|
||||||
String _authType = "access_token";
|
String _authType = "access_token";
|
||||||
bool _connectionSettingsChanged = false;
|
bool _edited = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -27,9 +27,9 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_hassioDomain = prefs.getString("hassio-domain");
|
_hassioDomain = prefs.getString("hassio-domain")?? "";
|
||||||
_hassioPort = prefs.getString("hassio-port") ?? '8123';
|
_hassioPort = prefs.getString("hassio-port") ?? "";
|
||||||
_hassioPassword = prefs.getString("hassio-password");
|
_hassioPassword = prefs.getString("hassio-password") ?? "";
|
||||||
_socketProtocol = prefs.getString("hassio-protocol") ?? 'wss';
|
_socketProtocol = prefs.getString("hassio-protocol") ?? 'wss';
|
||||||
_authType = prefs.getString("hassio-auth-type") ?? 'access_token';
|
_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-protocol", _socketProtocol);
|
||||||
prefs.setString("hassio-res-protocol", _socketProtocol == "wss" ? "https" : "http");
|
prefs.setString("hassio-res-protocol", _socketProtocol == "wss" ? "https" : "http");
|
||||||
prefs.setString("hassio-auth-type", _authType);
|
prefs.setString("hassio-auth-type", _authType);
|
||||||
_connectionSettingsChanged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -60,16 +59,12 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.check),
|
icon: Icon(Icons.check),
|
||||||
onPressed:(){
|
onPressed: _edited ? (){
|
||||||
if (_connectionSettingsChanged) {
|
|
||||||
_saveSettings().then((r){
|
_saveSettings().then((r){
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
eventBus.fire(SettingsChangedEvent(_connectionSettingsChanged));
|
eventBus.fire(SettingsChangedEvent(true));
|
||||||
});
|
});
|
||||||
} else {
|
} : null
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -84,8 +79,8 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_socketProtocol = value ? "wss" : "ws";
|
_socketProtocol = value ? "wss" : "ws";
|
||||||
|
_edited = true;
|
||||||
});
|
});
|
||||||
_saveSettings();
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -99,19 +94,18 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
),
|
),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_hassioDomain = value;
|
_hassioDomain = value;
|
||||||
_saveSettings();
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new TextField(
|
new TextField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Home Assistant port"
|
labelText: "Home Assistant port (default is 8123)"
|
||||||
),
|
),
|
||||||
controller: TextEditingController(
|
controller: TextEditingController(
|
||||||
text: _hassioPort
|
text: _hassioPort
|
||||||
),
|
),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_hassioPort = value;
|
_hassioPort = value;
|
||||||
_saveSettings();
|
//_saveSettings();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new Row(
|
new Row(
|
||||||
@ -122,8 +116,9 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_authType = value ? "access_token" : "api_password";
|
_authType = value ? "access_token" : "api_password";
|
||||||
|
_edited = true;
|
||||||
});
|
});
|
||||||
_saveSettings();
|
//_saveSettings();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -137,7 +132,7 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
),
|
),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_hassioPassword = value;
|
_hassioPassword = value;
|
||||||
_saveSettings();
|
//_saveSettings();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
Reference in New Issue
Block a user