WIP: App settings refactoring
This commit is contained in:
@ -16,15 +16,25 @@ class AppSettings {
|
||||
String displayHostname;
|
||||
String webSocketAPIEndpoint;
|
||||
String httpWebHost;
|
||||
String _token;
|
||||
String _tempToken;
|
||||
String longLivedToken;
|
||||
String tempToken;
|
||||
String oauthUrl;
|
||||
String webhookId;
|
||||
double haVersion;
|
||||
bool scrollBadges;
|
||||
int appIntegrationVersion;
|
||||
AppTheme appTheme;
|
||||
final int defaultLocationUpdateIntervalMinutes = 20;
|
||||
Duration locationUpdateInterval;
|
||||
bool locationTrackingEnabled = false;
|
||||
|
||||
bool get isAuthenticated => _token != null;
|
||||
bool get isAuthenticated => longLivedToken != null;
|
||||
bool get isTempAuthenticated => tempToken != null;
|
||||
|
||||
loadAppTheme() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
appTheme = AppTheme.values[prefs.getInt('app-theme') ?? AppTheme.defaultTheme.index];
|
||||
}
|
||||
|
||||
Future load(bool quick) async {
|
||||
if (!quick) {
|
||||
@ -36,13 +46,16 @@ class AppSettings {
|
||||
appIntegrationVersion = prefs.getInt('app-integration-version') ?? 0;
|
||||
scrollBadges = prefs.getBool('scroll-badges') ?? true;
|
||||
displayHostname = "$_domain:$_port";
|
||||
_webSocketAPIEndpoint =
|
||||
webSocketAPIEndpoint =
|
||||
"${prefs.getString('hassio-protocol')}://$_domain:$_port/api/websocket";
|
||||
httpWebHost =
|
||||
"${prefs.getString('hassio-res-protocol')}://$_domain:$_port";
|
||||
locationUpdateInterval = Duration(minutes: prefs.getInt("location-interval") ??
|
||||
defaultLocationUpdateIntervalMinutes);
|
||||
locationTrackingEnabled = prefs.getBool("location-enabled") ?? false;
|
||||
try {
|
||||
final storage = new FlutterSecureStorage();
|
||||
_token = await storage.read(key: "hacl_llt");
|
||||
longLivedToken = await storage.read(key: "hacl_llt");
|
||||
Logger.d("Long-lived token read successful");
|
||||
oauthUrl = "$httpWebHost/auth/authorize?client_id=${Uri.encodeComponent(
|
||||
'https://ha-client.app')}&redirect_uri=${Uri
|
||||
@ -54,15 +67,58 @@ class AppSettings {
|
||||
}
|
||||
}
|
||||
|
||||
Future save(Map<String, dynamic> settings) async {
|
||||
if (settings != null && settings.isNotEmpty) {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
settings.forEach((k,v) async {
|
||||
if (v is String) {
|
||||
await prefs.setString(k, v);
|
||||
} else if (v is bool) {
|
||||
await prefs.setBool(k ,v);
|
||||
} else if (v is int) {
|
||||
await prefs.setInt(k ,v);
|
||||
} else if (v is double) {
|
||||
await prefs.setDouble(k, v);
|
||||
} else {
|
||||
Logger.e('Unknown setting type: <$k, $v>');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future startAuth() {
|
||||
return AuthManager().start(
|
||||
oauthUrl: oauthUrl
|
||||
).then((token) {
|
||||
Logger.d("Token from AuthManager recived");
|
||||
_tempToken = token;
|
||||
tempToken = token;
|
||||
});
|
||||
}
|
||||
|
||||
Future clearTokens() async {
|
||||
longLivedToken = null;
|
||||
tempToken = null;
|
||||
try {
|
||||
final storage = new FlutterSecureStorage();
|
||||
await storage.delete(key: "hacl_llt");
|
||||
} catch(e, stacktrace) {
|
||||
Logger.e("Error clearing tokens: $e", stacktrace: stacktrace);
|
||||
}
|
||||
}
|
||||
|
||||
Future saveLongLivedToken(token) async {
|
||||
longLivedToken = token;
|
||||
tempToken = null;
|
||||
try {
|
||||
final storage = new FlutterSecureStorage();
|
||||
await storage.write(key: "hacl_llt", value: "$longLivedToken");
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool("oauth-used", true);
|
||||
} catch(e, stacktrace) {
|
||||
Logger.e("Error saving long-lived token: $e", stacktrace: stacktrace);
|
||||
}
|
||||
}
|
||||
|
||||
bool isNotConfigured() {
|
||||
return _domain == null && _port == null && webhookId == null && mobileAppDeviceName == null;
|
||||
}
|
||||
|
@ -212,16 +212,16 @@ class ConnectionManager {
|
||||
Logger.d( "[Sending] ==> auth request");
|
||||
sendSocketMessage(
|
||||
type: "auth",
|
||||
additionalData: {"access_token": "$_token"},
|
||||
additionalData: {"access_token": "${AppSettings().longLivedToken}"},
|
||||
auth: true
|
||||
).then((_) {
|
||||
completer.complete();
|
||||
}).catchError((e) => completer.completeError(e));
|
||||
} else if (_tempToken != null) {
|
||||
} else if (AppSettings().isTempAuthenticated != null) {
|
||||
Logger.d("We have temp token. Loging in...");
|
||||
sendSocketMessage(
|
||||
type: "auth",
|
||||
additionalData: {"access_token": "$_tempToken"},
|
||||
additionalData: {"access_token": "${AppSettings().tempToken}"},
|
||||
auth: true
|
||||
).then((_) {
|
||||
Logger.d("Requesting long-lived token...");
|
||||
@ -239,35 +239,17 @@ class ConnectionManager {
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future logout() {
|
||||
Future logout() async {
|
||||
Logger.d("Logging out");
|
||||
Completer completer = Completer();
|
||||
_disconnect().whenComplete(() {
|
||||
_token = null;
|
||||
_tempToken = null;
|
||||
final storage = new FlutterSecureStorage();
|
||||
storage.delete(key: "hacl_llt").whenComplete((){
|
||||
completer.complete();
|
||||
});
|
||||
});
|
||||
return completer.future;
|
||||
await _disconnect();
|
||||
await AppSettings().clearTokens();
|
||||
}
|
||||
|
||||
Future _getLongLivedToken() {
|
||||
Completer completer = Completer();
|
||||
sendSocketMessage(type: "auth/long_lived_access_token", additionalData: {"client_name": "HA Client app ${DateTime.now().millisecondsSinceEpoch}", "lifespan": 365}).then((data) {
|
||||
Logger.d("Got long-lived token.");
|
||||
_token = data;
|
||||
_tempToken = null;
|
||||
final storage = new FlutterSecureStorage();
|
||||
storage.write(key: "hacl_llt", value: "$_token").then((_) {
|
||||
SharedPreferences.getInstance().then((prefs) {
|
||||
prefs.setBool("oauth-used", true);
|
||||
completer.complete();
|
||||
});
|
||||
}).catchError((e) {
|
||||
throw e;
|
||||
});
|
||||
AppSettings().saveLongLivedToken(data);
|
||||
}).catchError((e) {
|
||||
completer.completeError(HACException("Authentication error: $e", actions: [HAErrorAction.reload(title: "Retry"), HAErrorAction.loginAgain(title: "Relogin")]));
|
||||
});
|
||||
@ -339,11 +321,11 @@ class ConnectionManager {
|
||||
DateTime now = DateTime.now();
|
||||
//String endTime = formatDate(now, [yyyy, '-', mm, '-', dd, 'T', HH, ':', nn, ':', ss, z]);
|
||||
String startTime = formatDate(now.subtract(Duration(hours: 24)), [yyyy, '-', mm, '-', dd, 'T', HH, ':', nn, ':', ss, z]);
|
||||
String url = "$httpWebHost/api/history/period/$startTime?&filter_entity_id=$entityId";
|
||||
String url = "${AppSettings().httpWebHost}/api/history/period/$startTime?&filter_entity_id=$entityId";
|
||||
Logger.d("[Sending] ==> HTTP /api/history/period/$startTime?&filter_entity_id=$entityId");
|
||||
http.Response historyResponse;
|
||||
historyResponse = await http.get(url, headers: {
|
||||
"authorization": "Bearer $_token",
|
||||
"authorization": "Bearer ${AppSettings().longLivedToken}",
|
||||
"Content-Type": "application/json"
|
||||
});
|
||||
var history = json.decode(historyResponse.body);
|
||||
@ -357,14 +339,14 @@ class ConnectionManager {
|
||||
|
||||
Future sendHTTPPost({String endPoint, String data, String contentType: "application/json", bool includeAuthHeader: true}) async {
|
||||
Completer completer = Completer();
|
||||
String url = "$httpWebHost$endPoint";
|
||||
String url = "${AppSettings().httpWebHost}$endPoint";
|
||||
Logger.d("[Sending] ==> HTTP $endPoint");
|
||||
Map<String, String> headers = {};
|
||||
if (contentType != null) {
|
||||
headers["Content-Type"] = contentType;
|
||||
}
|
||||
if (includeAuthHeader) {
|
||||
headers["authorization"] = "Bearer $_token";
|
||||
headers["authorization"] = "Bearer ${AppSettings().longLivedToken}";
|
||||
}
|
||||
http.post(
|
||||
url,
|
||||
|
@ -13,68 +13,53 @@ class LocationManager {
|
||||
init();
|
||||
}
|
||||
|
||||
final int defaultUpdateIntervalMinutes = 20;
|
||||
final String backgroundTaskId = "haclocationtask0";
|
||||
final String backgroundTaskTag = "haclocation";
|
||||
Duration _updateInterval;
|
||||
bool _isRunning;
|
||||
|
||||
void init() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
await prefs.reload();
|
||||
_updateInterval = Duration(minutes: prefs.getInt("location-interval") ??
|
||||
defaultUpdateIntervalMinutes);
|
||||
_isRunning = prefs.getBool("location-enabled") ?? false;
|
||||
if (_isRunning) {
|
||||
if (AppSettings().locationTrackingEnabled) {
|
||||
await _startLocationService();
|
||||
}
|
||||
}
|
||||
|
||||
setSettings(bool enabled, int interval) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
if (interval != _updateInterval.inMinutes) {
|
||||
prefs.setInt("location-interval", interval);
|
||||
_updateInterval = Duration(minutes: interval);
|
||||
if (_isRunning) {
|
||||
Logger.d("Stopping location tracking...");
|
||||
_isRunning = false;
|
||||
await _stopLocationService();
|
||||
}
|
||||
if (interval != AppSettings().locationUpdateInterval.inMinutes) {
|
||||
await _stopLocationService();
|
||||
}
|
||||
if (enabled && !_isRunning) {
|
||||
AppSettings().save({
|
||||
'location-interval': interval,
|
||||
'location-enabled': enabled
|
||||
});
|
||||
AppSettings().locationUpdateInterval = Duration(minutes: interval);
|
||||
AppSettings().locationTrackingEnabled = enabled;
|
||||
if (enabled && !AppSettings().locationTrackingEnabled) {
|
||||
Logger.d("Starting location tracking");
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool("location-enabled", enabled);
|
||||
_isRunning = true;
|
||||
await _startLocationService();
|
||||
} else if (!enabled && _isRunning) {
|
||||
} else if (!enabled && AppSettings().locationTrackingEnabled) {
|
||||
Logger.d("Stopping location tracking...");
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool("location-enabled", enabled);
|
||||
_isRunning = false;
|
||||
await _stopLocationService();
|
||||
}
|
||||
}
|
||||
|
||||
_startLocationService() async {
|
||||
String webhookId = ConnectionManager().webhookId;
|
||||
String httpWebHost = ConnectionManager().httpWebHost;
|
||||
String webhookId = AppSettings().webhookId;
|
||||
String httpWebHost = AppSettings().httpWebHost;
|
||||
if (webhookId != null && webhookId.isNotEmpty) {
|
||||
Duration interval;
|
||||
int delayFactor;
|
||||
int taskCount;
|
||||
Logger.d("Starting location update for every ${_updateInterval
|
||||
Logger.d("Starting location update for every ${AppSettings().locationUpdateInterval
|
||||
.inMinutes} minutes...");
|
||||
if (_updateInterval.inMinutes == 10) {
|
||||
if (AppSettings().locationUpdateInterval.inMinutes == 10) {
|
||||
interval = Duration(minutes: 20);
|
||||
taskCount = 2;
|
||||
delayFactor = 10;
|
||||
} else if (_updateInterval.inMinutes == 5) {
|
||||
} else if (AppSettings().locationUpdateInterval.inMinutes == 5) {
|
||||
interval = Duration(minutes: 15);
|
||||
taskCount = 3;
|
||||
delayFactor = 5;
|
||||
} else {
|
||||
interval = _updateInterval;
|
||||
interval = AppSettings().locationUpdateInterval;
|
||||
taskCount = 1;
|
||||
delayFactor = 0;
|
||||
}
|
||||
@ -112,8 +97,8 @@ class LocationManager {
|
||||
Logger.d("[Foreground location] Started");
|
||||
Geolocator geolocator = Geolocator();
|
||||
var battery = Battery();
|
||||
String webhookId = ConnectionManager().webhookId;
|
||||
String httpWebHost = ConnectionManager().httpWebHost;
|
||||
String webhookId = AppSettings().webhookId;
|
||||
String httpWebHost = AppSettings().httpWebHost;
|
||||
if (webhookId != null && webhookId.isNotEmpty) {
|
||||
Logger.d("[Foreground location] Getting battery level...");
|
||||
int batteryLevel = await battery.batteryLevel;
|
||||
|
@ -25,9 +25,9 @@ class MobileAppIntegrationManager {
|
||||
|
||||
static Future checkAppRegistration() {
|
||||
Completer completer = Completer();
|
||||
_appRegistrationData["device_name"] = ConnectionManager().mobileAppDeviceName ?? getDefaultDeviceName();
|
||||
_appRegistrationData["device_name"] = AppSettings().mobileAppDeviceName ?? getDefaultDeviceName();
|
||||
(_appRegistrationData["app_data"] as Map)["push_token"] = "${HomeAssistant().fcmToken}";
|
||||
if (ConnectionManager().webhookId == null) {
|
||||
if (AppSettings().webhookId == null) {
|
||||
Logger.d("Mobile app was not registered yet. Registering...");
|
||||
var registrationData = Map.from(_appRegistrationData);
|
||||
registrationData.addAll({
|
||||
@ -36,7 +36,7 @@ class MobileAppIntegrationManager {
|
||||
"os_name": DeviceInfoManager().osName,
|
||||
"supports_encryption": false,
|
||||
});
|
||||
if (ConnectionManager().haVersion >= 104) {
|
||||
if (AppSettings().haVersion >= 104) {
|
||||
registrationData.addAll({
|
||||
"device_id": "${DeviceInfoManager().unicDeviceId}"
|
||||
});
|
||||
@ -48,12 +48,12 @@ class MobileAppIntegrationManager {
|
||||
).then((response) {
|
||||
Logger.d("Processing registration responce...");
|
||||
var responseObject = json.decode(response);
|
||||
ConnectionManager().webhookId = responseObject["webhook_id"];
|
||||
ConnectionManager().appIntegrationVersion = INTEGRATION_VERSION;
|
||||
SharedPreferences.getInstance().then((prefs) {
|
||||
prefs.setString("app-webhook-id", responseObject["webhook_id"]);
|
||||
prefs.setInt('app-integration-version', INTEGRATION_VERSION);
|
||||
|
||||
AppSettings().webhookId = responseObject["webhook_id"];
|
||||
AppSettings().appIntegrationVersion = INTEGRATION_VERSION;
|
||||
AppSettings().save({
|
||||
'app-webhook-id': responseObject["webhook_id"],
|
||||
'app-integration-version': INTEGRATION_VERSION
|
||||
}).then((prefs) {
|
||||
completer.complete();
|
||||
eventBus.fire(ShowPopupEvent(
|
||||
popup: Popup(
|
||||
@ -84,7 +84,7 @@ class MobileAppIntegrationManager {
|
||||
"data": _appRegistrationData
|
||||
};
|
||||
ConnectionManager().sendHTTPPost(
|
||||
endPoint: "/api/webhook/${ConnectionManager().webhookId}",
|
||||
endPoint: "/api/webhook/${AppSettings().webhookId}",
|
||||
includeAuthHeader: false,
|
||||
data: json.encode(updateData)
|
||||
).then((response) {
|
||||
@ -98,7 +98,7 @@ class MobileAppIntegrationManager {
|
||||
Logger.w("No registration data in response. MobileApp integration was removed or broken");
|
||||
_askToRegisterApp();
|
||||
} else {
|
||||
if (INTEGRATION_VERSION > ConnectionManager().appIntegrationVersion) {
|
||||
if (INTEGRATION_VERSION > AppSettings().appIntegrationVersion) {
|
||||
Logger.d('App registration needs to be updated');
|
||||
_askToRemoveAndRegisterApp();
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user