Resolves #338 OAuth with Home Assistant
This commit is contained in:
parent
6a03105d01
commit
67d7bb45f5
@ -3,6 +3,7 @@ part of 'main.dart';
|
|||||||
class HomeAssistant {
|
class HomeAssistant {
|
||||||
String _webSocketAPIEndpoint;
|
String _webSocketAPIEndpoint;
|
||||||
String httpWebHost;
|
String httpWebHost;
|
||||||
|
String oauthUrl;
|
||||||
//String _password;
|
//String _password;
|
||||||
String _token;
|
String _token;
|
||||||
String _tempToken;
|
String _tempToken;
|
||||||
@ -68,6 +69,7 @@ class HomeAssistant {
|
|||||||
throw("Check connection settings");
|
throw("Check connection settings");
|
||||||
} else {
|
} else {
|
||||||
isSettingsLoaded = true;
|
isSettingsLoaded = true;
|
||||||
|
oauthUrl = "$httpWebHost/auth/authorize?client_id=${Uri.encodeComponent('http://ha-client.homemade.systems/')}&redirect_uri=${Uri.encodeComponent('http://ha-client.homemade.systems/service/auth_callback.html')}";
|
||||||
entities = EntityCollection(httpWebHost);
|
entities = EntityCollection(httpWebHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,6 +87,7 @@ class HomeAssistant {
|
|||||||
} else {
|
} else {
|
||||||
Logger.d("Fetching...");
|
Logger.d("Fetching...");
|
||||||
_fetchCompleter = new Completer();
|
_fetchCompleter = new Completer();
|
||||||
|
_fetchTimer?.cancel();
|
||||||
_fetchTimer = Timer(fetchTimeout, () {
|
_fetchTimer = Timer(fetchTimeout, () {
|
||||||
Logger.e( "Data fetching timeout");
|
Logger.e( "Data fetching timeout");
|
||||||
disconnect().then((_) {
|
disconnect().then((_) {
|
||||||
@ -104,13 +107,12 @@ class HomeAssistant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
disconnect() async {
|
disconnect() async {
|
||||||
if ((_hassioChannel != null) && (_hassioChannel.closeCode == null) && (_hassioChannel.sink != null)) {
|
Logger.d( "Socket disconnecting...");
|
||||||
await _hassioChannel.sink.close().timeout(Duration(seconds: 3),
|
await _socketSubscription?.cancel();
|
||||||
|
await _hassioChannel?.sink?.close()?.timeout(Duration(seconds: 3),
|
||||||
onTimeout: () => Logger.d( "Socket sink closed")
|
onTimeout: () => Logger.d( "Socket sink closed")
|
||||||
);
|
);
|
||||||
await _socketSubscription.cancel();
|
_hassioChannel = null;
|
||||||
_hassioChannel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +121,7 @@ class HomeAssistant {
|
|||||||
Logger.d("Previous connection is not complited");
|
Logger.d("Previous connection is not complited");
|
||||||
} else {
|
} else {
|
||||||
if ((_hassioChannel == null) || (_hassioChannel.closeCode != null)) {
|
if ((_hassioChannel == null) || (_hassioChannel.closeCode != null)) {
|
||||||
|
_connectionTimer?.cancel();
|
||||||
_connectionCompleter = new Completer();
|
_connectionCompleter = new Completer();
|
||||||
autoReconnect = false;
|
autoReconnect = false;
|
||||||
disconnect().then((_){
|
disconnect().then((_){
|
||||||
@ -127,9 +130,7 @@ class HomeAssistant {
|
|||||||
Logger.e( "Socket connection timeout");
|
Logger.e( "Socket connection timeout");
|
||||||
_handleSocketError(null);
|
_handleSocketError(null);
|
||||||
});
|
});
|
||||||
if (_socketSubscription != null) {
|
_socketSubscription?.cancel();
|
||||||
_socketSubscription.cancel();
|
|
||||||
}
|
|
||||||
_hassioChannel = IOWebSocketChannel.connect(
|
_hassioChannel = IOWebSocketChannel.connect(
|
||||||
_webSocketAPIEndpoint, pingInterval: Duration(seconds: 30));
|
_webSocketAPIEndpoint, pingInterval: Duration(seconds: 30));
|
||||||
_socketSubscription = _hassioChannel.stream.listen(
|
_socketSubscription = _hassioChannel.stream.listen(
|
||||||
@ -199,7 +200,7 @@ class HomeAssistant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _completeFetching(error) {
|
void _completeFetching(error) {
|
||||||
_fetchTimer.cancel();
|
_fetchTimer?.cancel();
|
||||||
_completeConnecting(error);
|
_completeConnecting(error);
|
||||||
if (!_fetchCompleter.isCompleted) {
|
if (!_fetchCompleter.isCompleted) {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
@ -213,7 +214,7 @@ class HomeAssistant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _completeConnecting(error) {
|
void _completeConnecting(error) {
|
||||||
_connectionTimer.cancel();
|
_connectionTimer?.cancel();
|
||||||
if (!_connectionCompleter.isCompleted) {
|
if (!_connectionCompleter.isCompleted) {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
_connectionCompleter.completeError(error);
|
_connectionCompleter.completeError(error);
|
||||||
@ -241,10 +242,10 @@ class HomeAssistant {
|
|||||||
_sendSubscribe();
|
_sendSubscribe();
|
||||||
} else if (data["type"] == "auth_invalid") {
|
} else if (data["type"] == "auth_invalid") {
|
||||||
Logger.d("[Received] <== ${data.toString()}");
|
Logger.d("[Received] <== ${data.toString()}");
|
||||||
//TODO remove token and login again
|
_logout();
|
||||||
_completeConnecting({"errorCode": 6, "errorMessage": "${data["message"]}"});
|
_completeConnecting({"errorCode": 62, "errorMessage": "${data["message"]}"});
|
||||||
} else if (data["type"] == "result") {
|
} else if (data["type"] == "result") {
|
||||||
Logger.d("[Received] <== ${data.toString()}");
|
Logger.d("[Received] <== id: ${data['id']}, success: ${data['success']}");
|
||||||
_messageResolver[data["id"]]?.complete(data);
|
_messageResolver[data["id"]]?.complete(data);
|
||||||
_messageResolver.remove(data["id"]);
|
_messageResolver.remove(data["id"]);
|
||||||
} else if (data["type"] == "event") {
|
} else if (data["type"] == "event") {
|
||||||
@ -261,6 +262,12 @@ class HomeAssistant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _logout() {
|
||||||
|
_token = null;
|
||||||
|
_tempToken = null;
|
||||||
|
SharedPreferences.getInstance().then((prefs) => prefs.remove("hassio-token"));
|
||||||
|
}
|
||||||
|
|
||||||
void _sendSubscribe() {
|
void _sendSubscribe() {
|
||||||
_incrementMessageId();
|
_incrementMessageId();
|
||||||
_subscriptionMessageId = _currentMessageId;
|
_subscriptionMessageId = _currentMessageId;
|
||||||
@ -276,18 +283,19 @@ class HomeAssistant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future _getLongLivedToken() async {
|
Future _getLongLivedToken() async {
|
||||||
await _sendSocketMessage(type: "auth/long_lived_access_token", additionalData: {"client_name": "HA Client 3", "client_icon": null, "lifespan": 365}).then((data) {
|
await _sendSocketMessage(type: "auth/long_lived_access_token", additionalData: {"client_name": "HA Client app", "lifespan": 365}).then((data) {
|
||||||
if (data['success']) {
|
if (data['success']) {
|
||||||
Logger.d("Got long-lived token: ${data['result']}");
|
Logger.d("Got long-lived token: ${data['result']}");
|
||||||
_token = data['result'];
|
_token = data['result'];
|
||||||
//TODO save token
|
_tempToken = null;
|
||||||
|
SharedPreferences.getInstance().then((prefs) => prefs.setString("hassio-token", _token));
|
||||||
} else {
|
} else {
|
||||||
|
_logout();
|
||||||
Logger.e("Error getting long-lived token: ${data['error'].toString()}");
|
Logger.e("Error getting long-lived token: ${data['error'].toString()}");
|
||||||
//TODO DO DO something here
|
|
||||||
}
|
}
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
Logger.e("Error getting long-lived token: ${e.toString()}");
|
Logger.e("Error getting long-lived token: ${e.toString()}");
|
||||||
//TODO DO DO something here
|
_logout();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +345,6 @@ class HomeAssistant {
|
|||||||
Logger.d( "No long leaved token. Need to authenticate.");
|
Logger.d( "No long leaved token. Need to authenticate.");
|
||||||
final flutterWebviewPlugin = new FlutterWebviewPlugin();
|
final flutterWebviewPlugin = new FlutterWebviewPlugin();
|
||||||
flutterWebviewPlugin.onUrlChanged.listen((String url) {
|
flutterWebviewPlugin.onUrlChanged.listen((String url) {
|
||||||
Logger.d("Launched url: $url");
|
|
||||||
if (url.startsWith("http://ha-client.homemade.systems/service/auth_callback.html")) {
|
if (url.startsWith("http://ha-client.homemade.systems/service/auth_callback.html")) {
|
||||||
String authCode = url.split("=")[1];
|
String authCode = url.split("=")[1];
|
||||||
Logger.d("We have auth code. Getting temporary access token...");
|
Logger.d("We have auth code. Getting temporary access token...");
|
||||||
@ -354,27 +361,33 @@ class HomeAssistant {
|
|||||||
Logger.d("Firing event to reload UI");
|
Logger.d("Firing event to reload UI");
|
||||||
eventBus.fire(ReloadUIEvent());
|
eventBus.fire(ReloadUIEvent());
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
//TODO DO DO something here
|
_logout();
|
||||||
|
disconnect();
|
||||||
|
flutterWebviewPlugin.close();
|
||||||
|
_completeFetching({"errorCode": 61, "errorMessage": "Error getting temp token"});
|
||||||
Logger.e("Error getting temp token: ${e.toString()}");
|
Logger.e("Error getting temp token: ${e.toString()}");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
disconnect().then((_){
|
disconnect();
|
||||||
//TODO create special error code to show "Login" in message
|
_completeFetching({"errorCode": 60, "errorMessage": "Not authenticated"});
|
||||||
_completeConnecting({"errorCode": 6, "errorMessage": "Not authenticated"});
|
_requestOAuth();
|
||||||
});
|
|
||||||
String oauthUrl = "$httpWebHost/auth/authorize?client_id=${Uri.encodeComponent('http://ha-client.homemade.systems/')}&redirect_uri=${Uri.encodeComponent('http://ha-client.homemade.systems/service/auth_callback.html')}";
|
|
||||||
Logger.d("OAuth url: $oauthUrl");
|
|
||||||
eventBus.fire(StartAuthEvent(oauthUrl));
|
|
||||||
} else if (_tempToken != null) {
|
} else if (_tempToken != null) {
|
||||||
Logger.d("We have temp token. Login...");
|
Logger.d("We have temp token. Login...");
|
||||||
_hassioChannel.sink.add('{"type": "auth","access_token": "$_tempToken"}');
|
_hassioChannel.sink.add('{"type": "auth","access_token": "$_tempToken"}');
|
||||||
} else {
|
} else {
|
||||||
Logger.e("General login error");
|
Logger.e("General login error");
|
||||||
//TODO DO DO something here
|
_logout();
|
||||||
|
disconnect();
|
||||||
|
_completeFetching({"errorCode": 61, "errorMessage": "General login error"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _requestOAuth() {
|
||||||
|
Logger.d("OAuth url: $oauthUrl");
|
||||||
|
eventBus.fire(StartAuthEvent(oauthUrl));
|
||||||
|
}
|
||||||
|
|
||||||
Future _sendSocketMessage({String type, Map additionalData, bool noId: false}) {
|
Future _sendSocketMessage({String type, Map additionalData, bool noId: false}) {
|
||||||
Completer _completer = Completer();
|
Completer _completer = Completer();
|
||||||
Map dataObject = {"type": "$type"};
|
Map dataObject = {"type": "$type"};
|
||||||
|
@ -104,8 +104,6 @@ EventBus eventBus = new EventBus();
|
|||||||
const String appName = "HA Client";
|
const String appName = "HA Client";
|
||||||
const appVersion = "0.5.2";
|
const appVersion = "0.5.2";
|
||||||
|
|
||||||
//String homeAssistantWebHost;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
FlutterError.onError = (errorDetails) {
|
FlutterError.onError = (errorDetails) {
|
||||||
Logger.e( "${errorDetails.exception}");
|
Logger.e( "${errorDetails.exception}");
|
||||||
@ -158,12 +156,7 @@ class MainPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MainPageState extends State<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin {
|
class _MainPageState extends State<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin {
|
||||||
//HomeAssistant _homeAssistant;
|
|
||||||
//Map _instanceConfig;
|
|
||||||
//String _webSocketApiEndpoint;
|
|
||||||
//String _password;
|
|
||||||
//int _uiViewsCount = 0;
|
|
||||||
//String _instanceHost;
|
|
||||||
StreamSubscription _stateSubscription;
|
StreamSubscription _stateSubscription;
|
||||||
StreamSubscription _settingsSubscription;
|
StreamSubscription _settingsSubscription;
|
||||||
StreamSubscription _serviceCallSubscription;
|
StreamSubscription _serviceCallSubscription;
|
||||||
@ -171,11 +164,9 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
StreamSubscription _showErrorSubscription;
|
StreamSubscription _showErrorSubscription;
|
||||||
StreamSubscription _startAuthSubscription;
|
StreamSubscription _startAuthSubscription;
|
||||||
StreamSubscription _reloadUISubscription;
|
StreamSubscription _reloadUISubscription;
|
||||||
//bool _settingsLoaded = false;
|
|
||||||
bool _accountMenuExpanded = false;
|
bool _accountMenuExpanded = false;
|
||||||
//bool _useLovelaceUI;
|
|
||||||
int _previousViewCount;
|
int _previousViewCount;
|
||||||
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
//final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -213,23 +204,6 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*_loadConnectionSettings() async {
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
String domain = prefs.getString('hassio-domain');
|
|
||||||
String port = prefs.getString('hassio-port');
|
|
||||||
_instanceHost = "$domain:$port";
|
|
||||||
_webSocketApiEndpoint = "${prefs.getString('hassio-protocol')}://$domain:$port/api/websocket";
|
|
||||||
homeAssistantWebHost = "${prefs.getString('hassio-res-protocol')}://$domain:$port";
|
|
||||||
_password = prefs.getString('hassio-password');
|
|
||||||
_useLovelaceUI = prefs.getBool('use-lovelace') ?? true;
|
|
||||||
if ((domain == null) || (port == null) || (_password == null) ||
|
|
||||||
(domain.length == 0) || (port.length == 0) || (_password.length == 0)) {
|
|
||||||
throw("Check connection settings");
|
|
||||||
} else {
|
|
||||||
_settingsLoaded = true;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
_subscribe() {
|
_subscribe() {
|
||||||
if (_stateSubscription == null) {
|
if (_stateSubscription == null) {
|
||||||
_stateSubscription = eventBus.on<StateChangedEvent>().listen((event) {
|
_stateSubscription = eventBus.on<StateChangedEvent>().listen((event) {
|
||||||
@ -269,20 +243,12 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
|
|
||||||
if (_startAuthSubscription == null) {
|
if (_startAuthSubscription == null) {
|
||||||
_startAuthSubscription = eventBus.on<StartAuthEvent>().listen((event){
|
_startAuthSubscription = eventBus.on<StartAuthEvent>().listen((event){
|
||||||
Navigator.push(
|
_showOAuth();
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => WebviewScaffold(
|
|
||||||
url: "${event.oauthUrl}",
|
|
||||||
appBar: new AppBar(
|
|
||||||
title: new Text("Login"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*_firebaseMessaging.getToken().then((String token) {
|
/*_firebaseMessaging.getToken().then((String token) {
|
||||||
//Logger.d("FCM token: $token");
|
//Logger.d("FCM token: $token");
|
||||||
widget.homeAssistant.sendHTTPPost(
|
widget.homeAssistant.sendHTTPPost(
|
||||||
@ -303,6 +269,20 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
);*/
|
);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showOAuth() {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => WebviewScaffold(
|
||||||
|
url: "${widget.homeAssistant.oauthUrl}",
|
||||||
|
appBar: new AppBar(
|
||||||
|
title: new Text("Login"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_refreshData() async {
|
_refreshData() async {
|
||||||
//widget.homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _useLovelaceUI);
|
//widget.homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _useLovelaceUI);
|
||||||
_hideBottomBar();
|
_hideBottomBar();
|
||||||
@ -539,12 +519,31 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 6: {
|
case 60: {
|
||||||
_bottomBarAction = FlatButton(
|
_bottomBarAction = FlatButton(
|
||||||
child: Text("Settings", style: textStyle),
|
child: Text("Login", style: textStyle),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
//_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
_refreshData();
|
||||||
Navigator.pushNamed(context, '/connection-settings');
|
},
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 61: {
|
||||||
|
_bottomBarAction = FlatButton(
|
||||||
|
child: Text("Try again", style: textStyle),
|
||||||
|
onPressed: () {
|
||||||
|
_refreshData();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 62: {
|
||||||
|
_bottomBarAction = FlatButton(
|
||||||
|
child: Text("Login again", style: textStyle),
|
||||||
|
onPressed: () {
|
||||||
|
_refreshData();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -14,8 +14,6 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
String _newHassioDomain = "";
|
String _newHassioDomain = "";
|
||||||
String _hassioPort = "";
|
String _hassioPort = "";
|
||||||
String _newHassioPort = "";
|
String _newHassioPort = "";
|
||||||
String _hassioPassword = "";
|
|
||||||
String _newHassioPassword = "";
|
|
||||||
String _socketProtocol = "wss";
|
String _socketProtocol = "wss";
|
||||||
String _newSocketProtocol = "wss";
|
String _newSocketProtocol = "wss";
|
||||||
bool _useLovelace = true;
|
bool _useLovelace = true;
|
||||||
@ -36,7 +34,6 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_hassioDomain = _newHassioDomain = prefs.getString("hassio-domain")?? "";
|
_hassioDomain = _newHassioDomain = prefs.getString("hassio-domain")?? "";
|
||||||
_hassioPort = _newHassioPort = prefs.getString("hassio-port") ?? "";
|
_hassioPort = _newHassioPort = prefs.getString("hassio-port") ?? "";
|
||||||
_hassioPassword = _newHassioPassword = prefs.getString("hassio-password") ?? "";
|
|
||||||
_socketProtocol = _newSocketProtocol = prefs.getString("hassio-protocol") ?? 'wss';
|
_socketProtocol = _newSocketProtocol = prefs.getString("hassio-protocol") ?? 'wss';
|
||||||
try {
|
try {
|
||||||
_useLovelace = _newUseLovelace = prefs.getBool("use-lovelace") ?? true;
|
_useLovelace = _newUseLovelace = prefs.getBool("use-lovelace") ?? true;
|
||||||
@ -47,7 +44,7 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _checkConfigChanged() {
|
bool _checkConfigChanged() {
|
||||||
return ((_newHassioPassword != _hassioPassword) ||
|
return (
|
||||||
(_newHassioPort != _hassioPort) ||
|
(_newHassioPort != _hassioPort) ||
|
||||||
(_newHassioDomain != _hassioDomain) ||
|
(_newHassioDomain != _hassioDomain) ||
|
||||||
(_newSocketProtocol != _socketProtocol) ||
|
(_newSocketProtocol != _socketProtocol) ||
|
||||||
@ -62,7 +59,6 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
prefs.setString("hassio-domain", _newHassioDomain);
|
prefs.setString("hassio-domain", _newHassioDomain);
|
||||||
prefs.setString("hassio-port", _newHassioPort);
|
prefs.setString("hassio-port", _newHassioPort);
|
||||||
prefs.setString("hassio-password", _newHassioPassword);
|
|
||||||
prefs.setString("hassio-protocol", _newSocketProtocol);
|
prefs.setString("hassio-protocol", _newSocketProtocol);
|
||||||
prefs.setString("hassio-res-protocol", _newSocketProtocol == "wss" ? "https" : "http");
|
prefs.setString("hassio-res-protocol", _newSocketProtocol == "wss" ? "https" : "http");
|
||||||
prefs.setBool("use-lovelace", _newUseLovelace);
|
prefs.setBool("use-lovelace", _newUseLovelace);
|
||||||
@ -152,21 +148,6 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
"Try ports 80 and 443 if default is not working and you don't know why.",
|
"Try ports 80 and 443 if default is not working and you don't know why.",
|
||||||
style: TextStyle(color: Colors.grey),
|
style: TextStyle(color: Colors.grey),
|
||||||
),
|
),
|
||||||
new TextField(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: "Access token"
|
|
||||||
),
|
|
||||||
controller: new TextEditingController.fromValue(
|
|
||||||
new TextEditingValue(
|
|
||||||
text: _newHassioPassword,
|
|
||||||
selection:
|
|
||||||
new TextSelection.collapsed(offset: _newHassioPassword.length)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
onChanged: (value) {
|
|
||||||
_newHassioPassword = value;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 20.0),
|
padding: EdgeInsets.only(top: 20.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
Reference in New Issue
Block a user