WIP #523 and connection settings refactoring
This commit is contained in:
parent
96c8338890
commit
725ec9291d
@ -42,7 +42,7 @@ class HomeAssistant {
|
|||||||
return _instanceConfig["location_name"] ?? "Home";
|
return _instanceConfig["location_name"] ?? "Home";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String get userName => _userName ?? locationName;
|
String get userName => _userName ?? '';
|
||||||
String get userAvatarText => userName.length > 0 ? userName[0] : "";
|
String get userAvatarText => userName.length > 0 ? userName[0] : "";
|
||||||
bool get isNoEntities => entities == null || entities.isEmpty;
|
bool get isNoEntities => entities == null || entities.isEmpty;
|
||||||
bool get isNoViews => ui == null || ui.isEmpty;
|
bool get isNoViews => ui == null || ui.isEmpty;
|
||||||
|
@ -113,6 +113,7 @@ part 'pages/settings/integration_settings.part.dart';
|
|||||||
part 'pages/settings/app_settings.page.dart';
|
part 'pages/settings/app_settings.page.dart';
|
||||||
part 'pages/settings/lookandfeel_settings.part.dart';
|
part 'pages/settings/lookandfeel_settings.part.dart';
|
||||||
part 'pages/zha_page.dart';
|
part 'pages/zha_page.dart';
|
||||||
|
part 'pages/quick_start.page.dart';
|
||||||
part 'home_assistant.class.dart';
|
part 'home_assistant.class.dart';
|
||||||
part 'pages/entity.page.dart';
|
part 'pages/entity.page.dart';
|
||||||
part 'utils/mdi.class.dart';
|
part 'utils/mdi.class.dart';
|
||||||
@ -260,7 +261,7 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
routes: {
|
routes: {
|
||||||
"/": (context) => MainPage(title: 'HA Client'),
|
"/": (context) => MainPage(title: 'HA Client'),
|
||||||
"/app-settings": (context) => AppSettingsPage(),
|
"/app-settings": (context) => AppSettingsPage(),
|
||||||
"/connection-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.connectionSettings),
|
"/connection-settings": (context) => AppSettingsPage(),
|
||||||
"/integration-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.integrationSettings),
|
"/integration-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.integrationSettings),
|
||||||
"/putchase": (context) => PurchasePage(title: "Support app development"),
|
"/putchase": (context) => PurchasePage(title: "Support app development"),
|
||||||
"/play-media": (context) => PlayMediaPage(
|
"/play-media": (context) => PlayMediaPage(
|
||||||
@ -278,22 +279,23 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
"/whats-new": (context) => WhatsNewPage(),
|
"/whats-new": (context) => WhatsNewPage(),
|
||||||
|
"/quick-start": (context) => QuickStartPage(),
|
||||||
"/haclient_zha": (context) => ZhaPage(),
|
"/haclient_zha": (context) => ZhaPage(),
|
||||||
"/auth": (context) => new standaloneWebview.WebviewScaffold(
|
"/auth": (context) => new standaloneWebview.WebviewScaffold(
|
||||||
url: "${ConnectionManager().oauthUrl}",
|
url: "${ConnectionManager().oauthUrl}",
|
||||||
appBar: new AppBar(
|
appBar: new AppBar(
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: Icon(Icons.help),
|
icon: Icon(Icons.help),
|
||||||
onPressed: () => Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.app/docs#authentication")
|
onPressed: () => Launcher.launchURLInCustomTab(context: context, url: "https://ha-client.app/help/connection")
|
||||||
),
|
),
|
||||||
title: new Text("Login with HA"),
|
title: new Text("Login"),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
FlatButton(
|
FlatButton(
|
||||||
child: Text("Manual", style: Theme.of(context).textTheme.button.copyWith(
|
child: Text("Long-lived token", style: Theme.of(context).textTheme.button.copyWith(
|
||||||
decoration: TextDecoration.underline
|
decoration: TextDecoration.underline
|
||||||
)),
|
)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
eventBus.fire(ShowPageEvent(path: "/connection-settings", goBackFirst: true));
|
eventBus.fire(ShowTokenLoginPopupEvent(goBackFirst: true));
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -53,7 +53,10 @@ class ConnectionManager {
|
|||||||
httpWebHost =
|
httpWebHost =
|
||||||
"${prefs.getString('hassio-res-protocol')}://$_domain:$_port";
|
"${prefs.getString('hassio-res-protocol')}://$_domain:$_port";
|
||||||
Logger.d('$_domain$_port');
|
Logger.d('$_domain$_port');
|
||||||
if ((_domain == null) || (_port == null) ||
|
if (_domain == null && _port == null && webhookId == null && mobileAppDeviceName == null) {
|
||||||
|
completer.completeError(HACNotSetUpException());
|
||||||
|
stopInit = true;
|
||||||
|
} else if ((_domain == null) || (_port == null) ||
|
||||||
(_domain.isEmpty) || (_port.isEmpty)) {
|
(_domain.isEmpty) || (_port.isEmpty)) {
|
||||||
completer.completeError(HACException.checkConnectionSettings());
|
completer.completeError(HACException.checkConnectionSettings());
|
||||||
stopInit = true;
|
stopInit = true;
|
||||||
|
@ -17,6 +17,9 @@ class MobileAppIntegrationManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static String getDefaultDeviceName() {
|
static String getDefaultDeviceName() {
|
||||||
|
if (HomeAssistant().userName.isEmpty) {
|
||||||
|
return '${DeviceInfoManager().model}';
|
||||||
|
}
|
||||||
return '${HomeAssistant().userName}\'s ${DeviceInfoManager().model}';
|
return '${HomeAssistant().userName}\'s ${DeviceInfoManager().model}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ class HAClientTheme {
|
|||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
letterSpacing: 1,
|
letterSpacing: 1,
|
||||||
),
|
),
|
||||||
button: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
button: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||||
);
|
);
|
||||||
|
|
||||||
static const offEntityStates = [
|
static const offEntityStates = [
|
||||||
|
@ -20,6 +20,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
StreamSubscription _startAuthSubscription;
|
StreamSubscription _startAuthSubscription;
|
||||||
StreamSubscription _showPopupDialogSubscription;
|
StreamSubscription _showPopupDialogSubscription;
|
||||||
StreamSubscription _showPopupMessageSubscription;
|
StreamSubscription _showPopupMessageSubscription;
|
||||||
|
StreamSubscription _showTokenLoginPopupSubscription;
|
||||||
StreamSubscription _reloadUISubscription;
|
StreamSubscription _reloadUISubscription;
|
||||||
StreamSubscription _fullReloadSubscription;
|
StreamSubscription _fullReloadSubscription;
|
||||||
StreamSubscription _showPageSubscription;
|
StreamSubscription _showPageSubscription;
|
||||||
@ -105,7 +106,11 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
StartupUserMessagesManager().checkMessagesToShow();
|
StartupUserMessagesManager().checkMessagesToShow();
|
||||||
});
|
});
|
||||||
}, onError: (e) {
|
}, onError: (e) {
|
||||||
|
if (e is HACNotSetUpException) {
|
||||||
|
Navigator.of(context).pushReplacementNamed('/quick-start');
|
||||||
|
} else {
|
||||||
_setErrorState(e);
|
_setErrorState(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -201,6 +206,14 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (_showTokenLoginPopupSubscription == null) {
|
||||||
|
_showTokenLoginPopupSubscription = eventBus.on<ShowTokenLoginPopupEvent>().listen((event){
|
||||||
|
if (event.goBackFirst) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
_showTokenLoginDialog();
|
||||||
|
});
|
||||||
|
}
|
||||||
if (_serviceCallSubscription == null) {
|
if (_serviceCallSubscription == null) {
|
||||||
_serviceCallSubscription =
|
_serviceCallSubscription =
|
||||||
eventBus.on<NotifyServiceCallEvent>().listen((event) {
|
eventBus.on<NotifyServiceCallEvent>().listen((event) {
|
||||||
@ -302,6 +315,78 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final _tokenLoginFormKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
void _showTokenLoginDialog() {
|
||||||
|
// flutter defined function
|
||||||
|
showDialog(
|
||||||
|
barrierDismissible: false,
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
// return object of type Dialog
|
||||||
|
return SimpleDialog(
|
||||||
|
title: new Text('Login with long-lived token'),
|
||||||
|
children: <Widget>[
|
||||||
|
Form(
|
||||||
|
key: _tokenLoginFormKey,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
child: TextFormField(
|
||||||
|
onSaved: (newValue) {
|
||||||
|
final storage = new FlutterSecureStorage();
|
||||||
|
storage.write(key: "hacl_llt", value: newValue).then((_) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
eventBus.fire(SettingsChangedEvent(true));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Please enter long-lived token',
|
||||||
|
contentPadding: EdgeInsets.all(0),
|
||||||
|
hintStyle: Theme.of(context).textTheme.subhead.copyWith(
|
||||||
|
color: Theme.of(context).textTheme.overline.color
|
||||||
|
)
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value.isEmpty) {
|
||||||
|
return 'Long-lived token can\'t be emty';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
RaisedButton(
|
||||||
|
child: Text('Login', style: Theme.of(context).textTheme.button.copyWith(fontSize: 20)),
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
onPressed: () {
|
||||||
|
if (_tokenLoginFormKey.currentState.validate()) {
|
||||||
|
_tokenLoginFormKey.currentState.save();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Container(width: 10),
|
||||||
|
RaisedButton(
|
||||||
|
child: Text('Cancel', style: Theme.of(context).textTheme.button.copyWith(fontSize: 20)),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _notifyServiceCalled(String domain, String service, entityId) {
|
void _notifyServiceCalled(String domain, String service, entityId) {
|
||||||
_bottomInfoBarController.showInfoBottomBar(
|
_bottomInfoBarController.showInfoBottomBar(
|
||||||
message: "Calling $domain.$service",
|
message: "Calling $domain.$service",
|
||||||
@ -577,9 +662,15 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FlatButton(
|
FlatButton(
|
||||||
child: Text("Login with Home Assistant", style: Theme.of(context).textTheme.button),
|
child: Text("Login", style: Theme.of(context).textTheme.button),
|
||||||
color: Colors.blue,
|
color: Theme.of(context).primaryColor,
|
||||||
onPressed: () => _fullLoad(),
|
onPressed: () => _fullLoad(),
|
||||||
|
),
|
||||||
|
Container(height: 20,),
|
||||||
|
FlatButton(
|
||||||
|
child: Text("Login with long-lived token", style: Theme.of(context).textTheme.button),
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
onPressed: () => eventBus.fire(ShowTokenLoginPopupEvent(goBackFirst: false))
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -697,6 +788,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
//final flutterWebviewPlugin = new FlutterWebviewPlugin();
|
//final flutterWebviewPlugin = new FlutterWebviewPlugin();
|
||||||
//flutterWebviewPlugin.dispose();
|
//flutterWebviewPlugin.dispose();
|
||||||
_viewsTabController?.dispose();
|
_viewsTabController?.dispose();
|
||||||
|
_showTokenLoginPopupSubscription?.cancel();
|
||||||
_stateSubscription?.cancel();
|
_stateSubscription?.cancel();
|
||||||
_lovelaceSubscription?.cancel();
|
_lovelaceSubscription?.cancel();
|
||||||
_settingsSubscription?.cancel();
|
_settingsSubscription?.cancel();
|
||||||
|
53
lib/pages/quick_start.page.dart
Normal file
53
lib/pages/quick_start.page.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class QuickStartPage extends StatefulWidget {
|
||||||
|
QuickStartPage({Key key, this.title}) : super(key: key);
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_QuickStartPageState createState() => new _QuickStartPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _QuickStartPageState extends State<QuickStartPage> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: new AppBar(
|
||||||
|
leading: IconButton(
|
||||||
|
icon: Icon(Icons.close),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: Text('Quick start'),
|
||||||
|
actions: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.help),
|
||||||
|
onPressed: () {
|
||||||
|
Launcher.launchURLInCustomTab(
|
||||||
|
context: context,
|
||||||
|
url: 'https://ha-client.app/help'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: ConnectionSettingsPage(
|
||||||
|
quickStart: true,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
@ -35,12 +35,15 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMenu(BuildContext context) {
|
Widget _buildMenu(BuildContext context) {
|
||||||
return ListView(
|
List<Widget> items = [
|
||||||
children: <Widget>[
|
|
||||||
_buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:network'), 'Connection settings', AppSettingsSection.connectionSettings),
|
_buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:network'), 'Connection settings', AppSettingsSection.connectionSettings),
|
||||||
_buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:cellphone-android'), 'Integration settings', AppSettingsSection.integrationSettings),
|
|
||||||
_buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:brush'), 'Look and feel', AppSettingsSection.lookAndFeel),
|
_buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:brush'), 'Look and feel', AppSettingsSection.lookAndFeel),
|
||||||
],
|
];
|
||||||
|
if (ConnectionManager().webhookId != null) {
|
||||||
|
items.insert(1, _buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:cellphone-android'), 'Integration settings', AppSettingsSection.integrationSettings));
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
children: items,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,187 +1,194 @@
|
|||||||
part of '../../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class ConnectionSettingsPage extends StatefulWidget {
|
class ConnectionSettingsPage extends StatefulWidget {
|
||||||
ConnectionSettingsPage({Key key, this.title}) : super(key: key);
|
ConnectionSettingsPage({Key key, this.title, this.quickStart: false}) : super(key: key);
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
final bool quickStart;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ConnectionSettingsPageState createState() => new _ConnectionSettingsPageState();
|
_ConnectionSettingsPageState createState() => new _ConnectionSettingsPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
||||||
String _hassioDomain = "";
|
String _homeAssistantUrl = '';
|
||||||
String _newHassioDomain = "";
|
String _deviceName;
|
||||||
String _hassioPort = "";
|
bool _loaded = false;
|
||||||
String _newHassioPort = "";
|
bool _includeDeviceName = false;
|
||||||
String _socketProtocol = "wss";
|
|
||||||
String _newSocketProtocol = "wss";
|
|
||||||
String _longLivedToken = "";
|
|
||||||
String _newLongLivedToken = "";
|
|
||||||
|
|
||||||
String oauthUrl;
|
final _formKey = GlobalKey<FormState>();
|
||||||
bool useOAuth = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
if (!widget.quickStart) {
|
||||||
_loadSettings();
|
_loadSettings();
|
||||||
|
} else {
|
||||||
|
_deviceName = MobileAppIntegrationManager.getDefaultDeviceName();
|
||||||
|
_loaded = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_loadSettings() async {
|
_loadSettings() async {
|
||||||
|
Logger.d('Loading settings...');
|
||||||
|
_includeDeviceName = widget.quickStart || ConnectionManager().webhookId == null;
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
final storage = new FlutterSecureStorage();
|
String domain = prefs.getString('hassio-domain')?? '';
|
||||||
|
String port = prefs.getString('hassio-port') ?? '';
|
||||||
try {
|
String urlProtocol = prefs.getString('hassio-res-protocol') ?? 'https';
|
||||||
useOAuth = prefs.getBool("oauth-used") ?? true;
|
_homeAssistantUrl = '$urlProtocol://$domain:$port';
|
||||||
} catch (e) {
|
_deviceName = prefs.getString('app-integration-device-name') ?? MobileAppIntegrationManager.getDefaultDeviceName();
|
||||||
useOAuth = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!useOAuth) {
|
|
||||||
try {
|
|
||||||
_longLivedToken = _newLongLivedToken =
|
|
||||||
await storage.read(key: "hacl_llt");
|
|
||||||
} catch (e) {
|
|
||||||
_longLivedToken = _newLongLivedToken = "";
|
|
||||||
await storage.delete(key: "hacl_llt");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_hassioDomain = _newHassioDomain = prefs.getString("hassio-domain")?? "";
|
_loaded = true;
|
||||||
_hassioPort = _newHassioPort = prefs.getString("hassio-port") ?? "";
|
|
||||||
_socketProtocol = _newSocketProtocol = prefs.getString("hassio-protocol") ?? 'wss';
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _checkConfigChanged() {
|
|
||||||
return (
|
|
||||||
(_newHassioPort != _hassioPort) ||
|
|
||||||
(_newHassioDomain != _hassioDomain) ||
|
|
||||||
(_newSocketProtocol != _socketProtocol) ||
|
|
||||||
(_newLongLivedToken != _longLivedToken));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_saveSettings() async {
|
_saveSettings() async {
|
||||||
_newHassioDomain = _newHassioDomain.trim();
|
_homeAssistantUrl = _homeAssistantUrl.trim();
|
||||||
if (_newHassioDomain.startsWith("http") && _newHassioDomain.indexOf("//") > 0) {
|
String socketProtocol;
|
||||||
_newHassioDomain.startsWith("https") ? _newSocketProtocol = "wss" : _newSocketProtocol = "ws";
|
String domain;
|
||||||
_newHassioDomain = _newHassioDomain.split("//")[1];
|
String port;
|
||||||
|
if (_homeAssistantUrl.startsWith("http") && _homeAssistantUrl.indexOf("//") > 0) {
|
||||||
|
_homeAssistantUrl.startsWith("https") ? socketProtocol = "wss" : socketProtocol = "ws";
|
||||||
|
domain = _homeAssistantUrl.split("//")[1];
|
||||||
|
} else {
|
||||||
|
domain = _homeAssistantUrl;
|
||||||
}
|
}
|
||||||
_newHassioDomain = _newHassioDomain.split("/")[0];
|
domain = domain.split("/")[0];
|
||||||
if (_newHassioDomain.contains(":")) {
|
if (domain.contains(":")) {
|
||||||
List<String> domainAndPort = _newHassioDomain.split(":");
|
List<String> domainAndPort = domain.split(":");
|
||||||
_newHassioDomain = domainAndPort[0];
|
domain = domainAndPort[0];
|
||||||
_newHassioPort = domainAndPort[1];
|
port = domainAndPort[1];
|
||||||
}
|
}
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
final storage = new FlutterSecureStorage();
|
await prefs.setString("hassio-domain", domain);
|
||||||
if (_newLongLivedToken.isNotEmpty) {
|
if (port == null || port.isEmpty) {
|
||||||
_newLongLivedToken = _newLongLivedToken.trim();
|
port = socketProtocol == "wss" ? "443" : "80";
|
||||||
prefs.setBool("oauth-used", false);
|
|
||||||
await storage.write(key: "hacl_llt", value: _newLongLivedToken);
|
|
||||||
} else if (!useOAuth) {
|
|
||||||
await storage.delete(key: "hacl_llt");
|
|
||||||
}
|
|
||||||
prefs.setString("hassio-domain", _newHassioDomain);
|
|
||||||
if (_newHassioPort == null || _newHassioPort.isEmpty) {
|
|
||||||
_newHassioPort = _newSocketProtocol == "wss" ? "443" : "80";
|
|
||||||
} else {
|
} else {
|
||||||
_newHassioPort = _newHassioPort.trim();
|
port = port.trim();
|
||||||
|
}
|
||||||
|
await prefs.setString("hassio-port", port);
|
||||||
|
await prefs.setString("hassio-protocol", socketProtocol);
|
||||||
|
await prefs.setString("hassio-res-protocol", socketProtocol == "wss" ? "https" : "http");
|
||||||
|
if (_includeDeviceName) {
|
||||||
|
await prefs.setString('app-integration-device-name', _deviceName);
|
||||||
}
|
}
|
||||||
prefs.setString("hassio-port", _newHassioPort);
|
|
||||||
prefs.setString("hassio-protocol", _newSocketProtocol);
|
|
||||||
prefs.setString("hassio-res-protocol", _newSocketProtocol == "wss" ? "https" : "http");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListView(
|
if (!_loaded) {
|
||||||
scrollDirection: Axis.vertical,
|
return PageLoadingIndicator();
|
||||||
padding: const EdgeInsets.all(20.0),
|
}
|
||||||
children: <Widget>[
|
List<Widget> formChildren = <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"Connection settings",
|
"Home Assistant url:",
|
||||||
style: Theme.of(context).textTheme.headline,
|
style: Theme.of(context).textTheme.headline,
|
||||||
),
|
),
|
||||||
new Row(
|
TextFormField(
|
||||||
children: [
|
initialValue: _homeAssistantUrl,
|
||||||
Text("Use ssl (HTTPS)"),
|
decoration: InputDecoration(
|
||||||
Switch(
|
hintText: "Please enter url",
|
||||||
value: (_newSocketProtocol == "wss"),
|
contentPadding: EdgeInsets.all(0),
|
||||||
onChanged: (value) {
|
hintStyle: Theme.of(context).textTheme.subhead.copyWith(
|
||||||
setState(() {
|
color: Theme.of(context).textTheme.overline.color
|
||||||
_newSocketProtocol = value ? "wss" : "ws";
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
],
|
|
||||||
),
|
),
|
||||||
new TextField(
|
onSaved: (newValue) {
|
||||||
decoration: InputDecoration(
|
_homeAssistantUrl = newValue;
|
||||||
labelText: "Home Assistant domain or ip address"
|
},
|
||||||
),
|
validator: (value) {
|
||||||
controller: TextEditingController.fromValue(TextEditingValue(text: _newHassioDomain)),
|
if (value.isEmpty) {
|
||||||
onChanged: (value) {
|
return 'Url is required';
|
||||||
_newHassioDomain = value;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
new TextField(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: "Home Assistant port (default is 8123)"
|
|
||||||
),
|
|
||||||
controller: TextEditingController.fromValue(TextEditingValue(text: _newHassioPort)),
|
|
||||||
onChanged: (value) {
|
|
||||||
_newHassioPort = value;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
new Text(
|
|
||||||
"Try ports 80 and 443 if default is not working and you don't know why.",
|
|
||||||
style: Theme.of(context).textTheme.caption,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"Authentication settings",
|
|
||||||
style: Theme.of(context).textTheme.headline,
|
|
||||||
),
|
|
||||||
Container(height: 10.0,),
|
|
||||||
Text(
|
|
||||||
"You can leave this field blank to make app generate new long-lived token automatically by asking you to login to your Home Assistant. Use this field only if you still want to use manually generated long-lived token. Leave it blank if you don't understand what we are talking about.",
|
|
||||||
style: Theme.of(context).textTheme.body1.copyWith(
|
|
||||||
color: Colors.redAccent
|
|
||||||
),
|
|
||||||
),
|
|
||||||
new TextField(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: "Long-lived token"
|
|
||||||
),
|
|
||||||
controller: TextEditingController.fromValue(TextEditingValue(text: _newLongLivedToken)),
|
|
||||||
onChanged: (value) {
|
|
||||||
_newLongLivedToken = value;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: Sizes.rowPadding,
|
height: 10,
|
||||||
),
|
),
|
||||||
RaisedButton(
|
Text(
|
||||||
child: Text('Apply', style: Theme.of(context).textTheme.button),
|
"For example:",
|
||||||
color: Theme.of(context).primaryColorDark,
|
style: Theme.of(context).textTheme.body1,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"192.186.2.14:8123",
|
||||||
|
style: Theme.of(context).textTheme.subhead,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"http://myhome.duckdns.org:8123",
|
||||||
|
style: Theme.of(context).textTheme.subhead,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"https://efkmfrwk3r4fsfwrfrg5.ui.nabu.casa/",
|
||||||
|
style: Theme.of(context).textTheme.subhead,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_includeDeviceName) {
|
||||||
|
formChildren.addAll(<Widget>[
|
||||||
|
Container(
|
||||||
|
height: 30,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Device name:",
|
||||||
|
style: Theme.of(context).textTheme.headline,
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
initialValue: _deviceName,
|
||||||
|
onSaved: (newValue) {
|
||||||
|
_deviceName = newValue;
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Please enter device name',
|
||||||
|
contentPadding: EdgeInsets.all(0),
|
||||||
|
hintStyle: Theme.of(context).textTheme.subhead.copyWith(
|
||||||
|
color: Theme.of(context).textTheme.overline.color
|
||||||
|
)
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value.isEmpty) {
|
||||||
|
return 'Device name is required';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
formChildren.addAll(<Widget>[
|
||||||
|
Container(
|
||||||
|
height: 30,
|
||||||
|
),
|
||||||
|
ButtonTheme(
|
||||||
|
height: 60,
|
||||||
|
child: RaisedButton(
|
||||||
|
child: Text(widget.quickStart ? 'Engage' : 'Apply', style: Theme.of(context).textTheme.button.copyWith(fontSize: 20)),
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (_checkConfigChanged()) {
|
if (_formKey.currentState.validate()) {
|
||||||
Logger.d("Settings changed. Saving...");
|
_formKey.currentState.save();
|
||||||
_saveSettings().then((r) {
|
_saveSettings().then((r) {
|
||||||
|
if (widget.quickStart) {
|
||||||
|
Navigator.pushReplacementNamed(context, '/');
|
||||||
|
} else {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
eventBus.fire(SettingsChangedEvent(true));
|
eventBus.fire(SettingsChangedEvent(true));
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
Logger.d("Settings was not changed");
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: ListView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
children: formChildren,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,12 @@ class ShowPopupMessageEvent {
|
|||||||
ShowPopupMessageEvent({this.title, this.body, this.buttonText: "Ok", this.onButtonClick});
|
ShowPopupMessageEvent({this.title, this.body, this.buttonText: "Ok", this.onButtonClick});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ShowTokenLoginPopupEvent {
|
||||||
|
final bool goBackFirst;
|
||||||
|
|
||||||
|
ShowTokenLoginPopupEvent({this.goBackFirst: false});
|
||||||
|
}
|
||||||
|
|
||||||
class ShowEntityPageEvent {
|
class ShowEntityPageEvent {
|
||||||
final String entityId;
|
final String entityId;
|
||||||
|
|
||||||
|
@ -24,6 +24,13 @@ class HACException implements Exception {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HACNotSetUpException implements Exception {
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'HA Client is not set up';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class HAErrorAction {
|
class HAErrorAction {
|
||||||
final String title;
|
final String title;
|
||||||
final int type;
|
final int type;
|
||||||
|
Reference in New Issue
Block a user