Added support for Home Assistant servers

This commit is contained in:
Juan Gilsanz Polo 2022-10-16 23:59:56 +02:00
parent c848591356
commit b1e257ddd3
9 changed files with 165 additions and 15 deletions

View file

@ -408,5 +408,7 @@
"logsConfigNotUpdated": "Logs settings couldn't be updated",
"deletingLogs": "Clearing logs...",
"logsCleared": "Logs cleared successfully",
"logsNotCleared": "Logs could not be cleared"
"logsNotCleared": "Logs could not be cleared",
"runningHomeAssistant": "Running on Home Assistant",
"serverError": "Server error"
}

View file

@ -408,5 +408,7 @@
"logsConfigNotUpdated": "La configuración de registros no ha podido ser actualizada",
"deletingLogs": "Borrando registros...",
"logsCleared": "Registros borrados correctamente",
"logsNotCleared": "No se han podido borrar los registros"
"logsNotCleared": "No se han podido borrar los registros",
"runningHomeAssistant": "Ejecutando en Home Assistant",
"serverError": "Error del servidor"
}

View file

@ -22,4 +22,9 @@ class AppLog {
'resBody': resBody.toString()
};
}
@override
String toString() {
return toMap().toString();
}
}

View file

@ -9,6 +9,7 @@ class Server {
String password;
bool defaultServer;
String authToken;
bool runningOnHa;
Server({
required this.id,
@ -20,6 +21,7 @@ class Server {
required this.user,
required this.password,
required this.defaultServer,
required this.authToken
required this.authToken,
required this.runningOnHa,
});
}

View file

@ -362,7 +362,7 @@ class ServersProvider with ChangeNotifier {
try {
return await _dbInstance!.transaction((txn) async {
await txn.rawInsert(
'INSERT INTO servers (id, name, connectionMethod, domain, path, port, user, password, defaultServer, authToken) VALUES ("${server.id}", "${server.name}", "${server.connectionMethod}", "${server.domain}", ${server.path != null ? "${server.path}" : null}, ${server.port}, "${server.user}", "${server.password}", 0, "${server.authToken}")',
'INSERT INTO servers (id, name, connectionMethod, domain, path, port, user, password, defaultServer, authToken, runningOnHa) VALUES ("${server.id}", "${server.name}", "${server.connectionMethod}", "${server.domain}", ${server.path != null ? "${server.path}" : null}, ${server.port}, "${server.user}", "${server.password}", 0, "${server.authToken}", ${convertFromBoolToInt(server.runningOnHa)})',
);
return true;
});
@ -375,7 +375,7 @@ class ServersProvider with ChangeNotifier {
try {
return await _dbInstance!.transaction((txn) async {
await txn.rawUpdate(
'UPDATE servers SET name = "${server.name}", connectionMethod = "${server.connectionMethod}", domain = "${server.domain}", path = ${server.path != null ? "${server.path}" : null}, port = ${server.port}, user = "${server.user}", password = "${server.password}", authToken = "${server.authToken}" WHERE id = "${server.id}"',
'UPDATE servers SET name = "${server.name}", connectionMethod = "${server.connectionMethod}", domain = "${server.domain}", path = ${server.path != null ? "${server.path}" : null}, port = ${server.port}, user = "${server.user}", password = "${server.password}", authToken = "${server.authToken}", runningOnHa = ${convertFromBoolToInt(server.runningOnHa)} WHERE id = "${server.id}"',
);
return true;
});
@ -426,7 +426,8 @@ class ServersProvider with ChangeNotifier {
user: server['user'],
password: server['password'],
defaultServer: convertFromIntToBool(server['defaultServer'])!,
authToken: server['authToken']
authToken: server['authToken'],
runningOnHa: convertFromIntToBool(server['runningOnHa'])!,
);
_serversList.add(serverObj);
if (convertFromIntToBool(server['defaultServer']) == true) {

View file

@ -26,11 +26,22 @@ Future<Map<String, dynamic>> loadDb() async {
});
}
Future upgradeDbToV4(Database db) async {
await db.execute("ALTER TABLE servers ADD COLUMN runningOnHa INTEGER");
await db.execute("UPDATE servers SET runningOnHa = 0");
await db.transaction((txn) async{
await txn.rawQuery(
'SELECT * FROM servers',
);
});
}
Database db = await openDatabase(
'adguard_home_manager.db',
version: 3,
version: 4,
onCreate: (Database db, int version) async {
await db.execute("CREATE TABLE servers (id TEXT PRIMARY KEY, name TEXT, connectionMethod TEXT, domain TEXT, path TEXT, port INTEGER, user TEXT, password TEXT, defaultServer INTEGER, authToken TEXT)");
await db.execute("CREATE TABLE servers (id TEXT PRIMARY KEY, name TEXT, connectionMethod TEXT, domain TEXT, path TEXT, port INTEGER, user TEXT, password TEXT, defaultServer INTEGER, authToken TEXT, runningOnHa INTEGER)");
await db.execute("CREATE TABLE appConfig (theme NUMERIC, overrideSslCheck NUMERIC, hideZeroValues NUMERIC)");
await db.execute("INSERT INTO appConfig (theme, overrideSslCheck, hideZeroValues) VALUES (0, 0, 0)");
},
@ -38,9 +49,14 @@ Future<Map<String, dynamic>> loadDb() async {
if (oldVersion == 1) {
await upgradeDbToV2(db);
await upgradeDbToV3(db);
await upgradeDbToV4(db);
}
if (oldVersion == 2) {
await upgradeDbToV3(db);
await upgradeDbToV4(db);
}
if (oldVersion == 3) {
await upgradeDbToV4(db);
}
},
onOpen: (Database db) async {

View file

@ -29,7 +29,7 @@ Future<Map<String, dynamic>> apiRequest({
HttpClient httpClient = HttpClient();
if (method == 'get') {
HttpClientRequest request = await httpClient.getUrl(Uri.parse("${server.connectionMethod}://${server.domain}${server.path ?? ""}${server.port != null ? ':${server.port}' : ""}/control$urlPath"));
request.headers.set('authorization', 'Basic ${server.authToken}');
request.headers.set('Authorization', 'Basic ${server.authToken}');
HttpClientResponse response = overrideTimeout == true
? await request.close()
: await request.close().timeout(const Duration(seconds: 10));
@ -54,7 +54,7 @@ Future<Map<String, dynamic>> apiRequest({
}
else if (method == 'post') {
HttpClientRequest request = await httpClient.postUrl(Uri.parse("${server.connectionMethod}://${server.domain}${server.path ?? ""}${server.port != null ? ':${server.port}' : ""}/control$urlPath"));
request.headers.set('authorization', 'Basic ${server.authToken}');
request.headers.set('Authorization', 'Basic ${server.authToken}');
request.headers.set('content-type', 'application/json');
request.add(utf8.encode(json.encode(body)));
HttpClientResponse response = overrideTimeout == true
@ -165,6 +165,18 @@ Future login(Server server) async {
)
};
}
else if (result['statusCode'] == 500) {
return {
'result': 'server_error',
'log': AppLog(
type: 'login',
dateTime: DateTime.now(),
message: 'server_error',
statusCode: result['statusCode'].toString(),
resBody: result['body']
)
};
}
else {
return {
'result': 'error',
@ -183,6 +195,49 @@ Future login(Server server) async {
}
}
Future loginHA(Server server) async {
print(server.authToken);
final result = await apiRequest(
server: server,
method: 'get',
urlPath: '/status',
type: 'login_ha'
);
if (result['hasResponse'] == true) {
if (result['statusCode'] == 200) {
return {'result': 'success'};
}
else if (result['statusCode'] == 401) {
return {
'result': 'invalid_username_password',
'log': AppLog(
type: 'login',
dateTime: DateTime.now(),
message: 'invalid_username_password',
statusCode: result['statusCode'].toString(),
resBody: result['body']
)
};
}
else {
return {
'result': 'error',
'log': AppLog(
type: 'login_ha',
dateTime: DateTime.now(),
message: 'error_code_not_expected',
statusCode: result['statusCode'].toString(),
resBody: result['body']
)
};
}
}
else {
return result;
}
}
Future getServerStatus(Server server) async {
final result = await Future.wait([
apiRequest(server: server, method: 'get', urlPath: '/stats', type: 'server_status'),

View file

@ -51,6 +51,8 @@ class _AddServerModalState extends State<AddServerModal> {
bool defaultServer = false;
bool homeAssistant = false;
bool allDataValid = false;
bool isConnecting = false;
@ -254,11 +256,19 @@ class _AddServerModalState extends State<AddServerModal> {
user: userController.text,
password: passwordController.text,
defaultServer: defaultServer,
authToken: ''
authToken: homeAssistant == true
? encodeBase64UserPass(userController.text, passwordController.text)
: '',
runningOnHa: homeAssistant
);
setState(() => isConnecting = true);
final result = await login(serverObj);
final result = homeAssistant == true
? await loginHA(serverObj)
: await login(serverObj);
setState(() => isConnecting = false);
if (result['result'] == 'success') {
serverObj.authToken = encodeBase64UserPass(serverObj.user, serverObj.password);
final serverCreated = await serversProvider.createServer(serverObj);
@ -320,6 +330,15 @@ class _AddServerModalState extends State<AddServerModal> {
)
);
}
else if (result['result'] == 'server_error') {
appConfigProvider.addLog(result['log']);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.serverError),
backgroundColor: Colors.red,
)
);
}
else {
appConfigProvider.addLog(result['log']);
ScaffoldMessenger.of(context).showSnackBar(
@ -341,9 +360,16 @@ class _AddServerModalState extends State<AddServerModal> {
user: userController.text,
password: passwordController.text,
defaultServer: defaultServer,
authToken: ''
authToken: homeAssistant == true
? encodeBase64UserPass(userController.text, passwordController.text)
: '',
runningOnHa: homeAssistant
);
final result = await login(serverObj);
final result = homeAssistant == true
? await loginHA(serverObj)
: await login(serverObj);
if (result['result'] == 'success') {
serverObj.authToken = encodeBase64UserPass(serverObj.user, serverObj.password);
final serverSaved = await serversProvider.editServer(serverObj);
@ -395,6 +421,15 @@ class _AddServerModalState extends State<AddServerModal> {
)
);
}
else if (result['result'] == 'server_error') {
appConfigProvider.addLog(result['log']);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.serverError),
backgroundColor: Colors.red,
)
);
}
else {
appConfigProvider.addLog(result['log']);
ScaffoldMessenger.of(context).showSnackBar(
@ -566,6 +601,36 @@ class _AddServerModalState extends State<AddServerModal> {
),
),
const SizedBox(height: 20),
Material(
color: Colors.transparent,
child: InkWell(
onTap: widget.server == null
? () => setState(() => homeAssistant = !homeAssistant)
: null,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppLocalizations.of(context)!.runningHomeAssistant,
style: const TextStyle(
fontSize: 15,
),
),
Switch(
value: homeAssistant,
onChanged: widget.server == null
? (value) => setState(() => homeAssistant = value)
: null,
activeColor: Theme.of(context).primaryColor,
)
],
),
),
),
),
const SizedBox(height: 20),
],
),
),

View file

@ -60,7 +60,9 @@ class ServersList extends StatelessWidget {
final ProcessModal process = ProcessModal(context: context);
process.open(AppLocalizations.of(context)!.connecting);
final result = await login(server);
final result = server.runningOnHa == true
? await loginHA(server)
: await login(server);
if (result['result'] == 'success') {
serversProvider.setSelectedServer(server);