diff --git a/libnymea-app/applogcontroller.cpp b/libnymea-app/applogcontroller.cpp
new file mode 100644
index 00000000..b5e605a7
--- /dev/null
+++ b/libnymea-app/applogcontroller.cpp
@@ -0,0 +1,380 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU General Public License as published by the Free Software
+* Foundation, GNU version 3. This project is distributed in the hope that it
+* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+* Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along with
+* this project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "applogcontroller.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "logging.h"
+
+QtMessageHandler AppLogController::s_oldLogMessageHandler = nullptr;
+
+
+AppLogController::LogLevel AppLogController::qtMsgTypeToLogLevel(QtMsgType msgType)
+{
+ switch (msgType) {
+ case QtDebugMsg:
+ return LogLevelDebug;
+ case QtInfoMsg:
+ return LogLevelInfo;
+ case QtWarningMsg:
+ return LogLevelWarning;
+ default:
+ return LogLevelCritical;
+ }
+}
+
+QtMsgType AppLogController::logLevelToQtMsgType(AppLogController::LogLevel logLevel)
+{
+ switch (logLevel) {
+ case LogLevelDebug:
+ return QtDebugMsg;
+ case LogLevelInfo:
+ return QtInfoMsg;
+ case LogLevelWarning:
+ return QtWarningMsg;
+ default:
+ return QtCriticalMsg;
+ }
+}
+
+QObject *AppLogController::appLogControllerProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(scriptEngine)
+ return instance();
+}
+
+AppLogController *AppLogController::instance()
+{
+ static AppLogController* thiz = nullptr;
+ if (!thiz) {
+ thiz = new AppLogController();
+ }
+ return thiz;
+}
+
+AppLogController::AppLogController(QObject *parent) : QObject(parent)
+{
+ m_loggingCategories = new LoggingCategories(this);
+
+ QSettings settings;
+ settings.beginGroup("LoggingLevels");
+ foreach (const QString &category, nymeaLoggingCategories()) {
+ m_logLevels[category] = static_cast(settings.value(category, LogLevelInfo).toInt());
+ }
+ settings.endGroup();
+ updateFilters();
+
+ // Finally, install the logMessageHandler
+ s_oldLogMessageHandler = qInstallMessageHandler(&logMessageHandler);
+
+ if (enabled()) {
+ openLogFile();
+ }
+}
+
+bool AppLogController::enabled() const
+{
+ QSettings settings;
+ return settings.value("AppLoggingEnabled", false).toBool();
+}
+
+void AppLogController::setEnabled(bool enabled)
+{
+ if (enabled == this->enabled()) {
+ return;
+ }
+
+ if (enabled) {
+ openLogFile();
+ } else {
+ m_logFile.close();
+ }
+ QSettings settings;
+ settings.setValue("AppLoggingEnabled", enabled);
+ emit enabledChanged();
+}
+
+LoggingCategories *AppLogController::loggingCategories() const
+{
+ return m_loggingCategories;
+}
+
+AppLogController::LogLevel AppLogController::logLevel(const QString &category) const
+{
+ return m_logLevels.value(category);
+}
+
+void AppLogController::setLogLevel(const QString &category, AppLogController::LogLevel logLevel)
+{
+ m_logLevels[category] = logLevel;
+
+ QSettings settings;
+ settings.beginGroup("LoggingLevels");
+ settings.setValue(category, logLevel);
+ settings.endGroup();
+
+ emit categoryChanged(category, logLevel);
+}
+
+QString AppLogController::logPath() const
+{
+ return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/logs/";
+}
+
+QString AppLogController::currentLogFile() const
+{
+ return logPath() + "/" + QGuiApplication::applicationName() + ".log";
+}
+
+QStringList AppLogController::logFiles() const
+{
+ QDir dir(logPath());
+ QStringList files;
+ foreach (const QString &file, dir.entryList({QGuiApplication::applicationName() + ".log*"})) {
+ files.append(logPath() + "/" + file);
+ }
+ return files;
+}
+
+QString AppLogController::exportLogs()
+{
+ QFile f(logPath() + "/" + QGuiApplication::applicationName() + "-logs.txt");
+ if (!f.open(QFile::WriteOnly)) {
+ return QString();
+ }
+ foreach (const QString &logFile, logFiles()) {
+ QFile l(logFile);
+ if (!l.open(QFile::ReadOnly)) {
+ continue;
+ }
+ f.write("\n******** App start ********\n");
+ f.write(logFile.toUtf8() + "\n");
+ f.write(l.readAll());
+ }
+ f.close();
+ return f.fileName();
+}
+
+void AppLogController::logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
+{
+ s_oldLogMessageHandler(type, context, message);
+ QMetaObject::invokeMethod(instance(), "append", Q_ARG(QString, context.category), Q_ARG(QString, message), Q_ARG(AppLogController::LogLevel, qtMsgTypeToLogLevel(type)));
+}
+
+void AppLogController::append(const QString &category, const QString &message, LogLevel level)
+{
+ if (m_logLevels.value(category) < level) {
+ return;
+ }
+
+ if (m_logFile.isOpen()) {
+ QHash t = {
+ {LogLevelDebug, "D"},
+ {LogLevelInfo, "I"},
+ {LogLevelWarning, "W"},
+ {LogLevelCritical, "C"}
+ };
+ QString line = QString("%0: %1: %2\n").arg(t.value(level), category, message);
+ m_logFile.write(line.toUtf8());
+ m_logFile.flush();
+ }
+
+ emit messageAdded(category, message, level);
+}
+
+void AppLogController::updateFilters()
+{
+ QStringList loggingRules = {"*.warn=false", "*.info=false", "*.debug=false"};
+
+ // Load the rules from nymead.conf file and append them to the rules
+ foreach (const QString &category, nymeaLoggingCategories()) {
+ LogLevel level = m_logLevels.value(category, LogLevelWarning);
+ loggingRules << QString("%1.debug=%2").arg(category).arg(level >= LogLevelDebug ? "true" : "false");
+ loggingRules << QString("%1.info=%2").arg(category).arg(level >= LogLevelInfo ? "true" : "false");
+ loggingRules << QString("%1.warn=%2").arg(category).arg(level >= LogLevelWarning ? "true" : "false");
+ }
+ QLoggingCategory::setFilterRules(loggingRules.join('\n'));
+}
+
+void AppLogController::openLogFile()
+{
+ // Make sure log dir exists
+ if (!QDir().mkpath(logPath())) {
+ qWarning() << "Cannot create cache location. Logging will not work.";
+ }
+
+ // Rotate old log files, keeping the last 5
+ for (int i = 4; i > 0; i--) {
+ if (QFile::exists(currentLogFile() + "." + QString::number(i))) {
+ if (QFile::exists(currentLogFile() + "." + QString::number(i + 1))) {
+ QFile::remove(currentLogFile() + "." + QString::number(i + 1));
+ }
+ QFile::rename(currentLogFile() + "." + QString::number(i), currentLogFile() + "." + QString::number(i + 1));
+ }
+ }
+ if (QFile::exists(currentLogFile())) {
+ QFile::rename(currentLogFile(), currentLogFile() + ".1");
+ }
+
+ m_logFile.setFileName(currentLogFile());
+ if (!m_logFile.open(QFile::ReadWrite | QFile::Truncate)) {
+ qWarning() << "Cannot open logfile for writing.";
+ } else {
+ qDebug() << "App log opened at" << m_logFile.fileName();
+ }
+}
+
+LogMessages::LogMessages(QObject *parent):
+ QAbstractListModel(parent)
+{
+ QFile f(AppLogController::instance()->currentLogFile());
+ if (!f.open(QFile::ReadOnly)) {
+ return;
+ }
+ QHash map = {
+ {"C", AppLogController::LogLevelCritical},
+ {"W", AppLogController::LogLevelWarning},
+ {"I", AppLogController::LogLevelInfo},
+ {"D", AppLogController::LogLevelDebug}
+ };
+ while (!f.atEnd()) {
+ QByteArray line = f.readLine().trimmed();
+ QList parts = line.split(':');
+ if (parts.length() < 2) {
+ continue;
+ }
+ LogMessage message;
+ message.level = map.value(parts.takeFirst());
+ message.category = parts.takeFirst();
+ message.message = parts.join(":");
+ m_messages.append(message);
+ }
+ connect(AppLogController::instance(), &AppLogController::messageAdded, this, [=](const QString &category, const QString &message, AppLogController::LogLevel level){
+ beginInsertRows(QModelIndex(), m_messages.count(), m_messages.count());
+ LogMessage msg;
+ msg.category = category;
+ msg.message = message;
+ msg.level = level;
+ m_messages.append(msg);
+ endInsertRows();
+ });
+}
+
+int LogMessages::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return m_messages.count();
+}
+
+QVariant LogMessages::data(const QModelIndex &index, int role) const
+{
+ switch (role) {
+ case RoleCategory:
+ return m_messages.at(index.row()).category;
+ case RoleMessage:
+ return m_messages.at(index.row()).message;
+ case RoleLevel:
+ return m_messages.at(index.row()).level;
+ case RoleText:
+ return m_messages.at(index.row()).category + ": " + m_messages.at(index.row()).message;
+ }
+ return QVariant();
+}
+
+QHash LogMessages::roleNames() const
+{
+ QHash roles;
+ roles.insert(RoleCategory, "category");
+ roles.insert(RoleMessage, "message");
+ roles.insert(RoleLevel, "level");
+ roles.insert(RoleText, "text");
+ return roles;
+}
+
+void LogMessages::append(const QString &category, const QString &message, AppLogController::LogLevel level)
+{
+ beginInsertRows(QModelIndex(), m_messages.count(), m_messages.count());
+ LogMessage msg;
+ msg.category = category;
+ msg.message = message;
+ msg.level = level;
+ m_messages.append(msg);
+ endInsertRows();
+
+ int maxEntries = 1024;
+ if (m_messages.size() > maxEntries) {
+ beginRemoveRows(QModelIndex(), 0, 0);
+ m_messages.removeFirst();
+ endRemoveRows();
+ }
+}
+
+LoggingCategories::LoggingCategories(AppLogController *parent):
+ QAbstractListModel(parent),
+ m_controller(parent)
+{
+ connect(m_controller, &AppLogController::categoryChanged, this, [=](const QString &category) {
+ QModelIndex idx = index(nymeaLoggingCategories().indexOf(category));
+ emit dataChanged(idx, idx, {RoleLevel});
+ });
+}
+
+int LoggingCategories::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return nymeaLoggingCategories().count();
+}
+
+QVariant LoggingCategories::data(const QModelIndex &index, int role) const
+{
+ switch (role) {
+ case RoleName:
+ return nymeaLoggingCategories().at(index.row());
+ case RoleLevel:
+ return m_controller->logLevel(nymeaLoggingCategories().at(index.row()));
+ }
+ return QVariant();
+}
+
+QHash LoggingCategories::roleNames() const
+{
+ QHash roles;
+ roles.insert(RoleName, "name");
+ roles.insert(RoleLevel, "logLevel");
+ return roles;
+}
+
diff --git a/nymea-app/applogcontroller.h b/libnymea-app/applogcontroller.h
similarity index 50%
rename from nymea-app/applogcontroller.h
rename to libnymea-app/applogcontroller.h
index a9f4c02b..3a46e1fc 100644
--- a/nymea-app/applogcontroller.h
+++ b/libnymea-app/applogcontroller.h
@@ -37,60 +37,120 @@
#include
#include
-class AppLogController : public QAbstractListModel
+class LogMessages;
+class LoggingCategories;
+
+class AppLogController : public QObject
{
Q_OBJECT
- Q_PROPERTY(bool canWriteLogs READ canWriteLogs CONSTANT)
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
- Q_PROPERTY(QString logFile READ logFile CONSTANT)
+
+ Q_PROPERTY(LoggingCategories* loggingCategories READ loggingCategories CONSTANT)
public:
- enum Type {
- TypeInfo,
- TypeWarning
+ // Note: QtMsgType is sorted in a way that we can't compare for >= etc
+ enum LogLevel {
+ LogLevelCritical = 0,
+ LogLevelWarning = 1,
+ LogLevelInfo = 2,
+ LogLevelDebug = 3
};
- Q_ENUM(Type)
-
- enum Roles {
- RoleText,
- RoleType
- };
- Q_ENUM(Roles)
+ Q_ENUM(LogLevel)
+ static LogLevel qtMsgTypeToLogLevel(QtMsgType msgType);
+ static QtMsgType logLevelToQtMsgType(LogLevel logLevel);
static QObject* appLogControllerProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
static AppLogController* instance();
- bool canWriteLogs() const;
-
bool enabled() const;
void setEnabled(bool enabled);
- int rowCount(const QModelIndex &parent) const override;
- QVariant data(const QModelIndex &parent, int role) const override;
- QHash roleNames() const override;
+ LoggingCategories* loggingCategories() const;
+ LogLevel logLevel(const QString &category) const;
+ Q_INVOKABLE void setLogLevel(const QString &category, LogLevel logLevel);
+ Q_INVOKABLE QString logPath() const;
+ Q_INVOKABLE QString currentLogFile() const;
+ Q_INVOKABLE QStringList logFiles() const;
- Q_INVOKABLE void toClipboard();
-
- QString logFile() const;
+ Q_INVOKABLE QString exportLogs();
signals:
void enabledChanged();
+ void logToModelChanged();
+
+ void categoryChanged(const QString &category, LogLevel level);
+ void messageAdded(const QString &category, const QString &message, LogLevel level);
private:
explicit AppLogController(QObject *parent = nullptr);
+
static QtMessageHandler s_oldLogMessageHandler;
static void logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
- void append(const QString &message, Type type = TypeInfo);
+ Q_INVOKABLE void append(const QString &category, const QString &message, AppLogController::LogLevel level);
- void activate();
- void deactivate();
+ void updateFilters();
+ void openLogFile();
+
+ QHash m_logLevels;
- QFile m_logFile;
- QStringList m_buffer;
- QList m_types;
QMutex m_mutex;
+ QFile m_logFile;
+ LoggingCategories *m_loggingCategories = nullptr;
+};
+Q_DECLARE_METATYPE(AppLogController::LogLevel)
+
+class LogMessages: public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ enum Roles {
+ RoleCategory,
+ RoleMessage,
+ RoleLevel,
+ RoleText
+ };
+ Q_ENUM(Roles)
+
+ struct LogMessage {
+ QString category;
+ QString message;
+ AppLogController::LogLevel level;
+ };
+
+ LogMessages(QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QHash roleNames() const override;
+
+ void append(const QString &category, const QString &message, AppLogController::LogLevel level);
+
+private:
+ QList m_messages;
+};
+
+class LoggingCategories: public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ enum Roles {
+ RoleName,
+ RoleLevel
+ };
+ Q_ENUM(Roles)
+
+ LoggingCategories(AppLogController *parent);
+
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QHash roleNames() const override;
+
+private:
+ AppLogController *m_controller = nullptr;
};
#endif // APPLOGCONTROLLER_H
diff --git a/libnymea-app/connection/awsclient.cpp b/libnymea-app/connection/awsclient.cpp
index a1120691..7085bda3 100644
--- a/libnymea-app/connection/awsclient.cpp
+++ b/libnymea-app/connection/awsclient.cpp
@@ -41,9 +41,12 @@
#include
#include "sigv4utils.h"
+#include "logging.h"
AWSClient* AWSClient::s_instance = nullptr;
+NYMEA_LOGGING_CATEGORY(dcCloud, "Cloud")
+
// This is Symantec's root CA certificate and most platforms should
// have this in their certificate storage already, but as we can't
// be certain about the core's setup, let's deploy it ourselves.
@@ -187,11 +190,11 @@ bool AWSClient::confirmationPending() const
void AWSClient::login(const QString &username, const QString &password)
{
if (m_usedConfig.isEmpty()) {
- qDebug() << "AWS config not set. Not logging in.";
+ qCInfo(dcCloud()) << "AWS config not set. Not logging in.";
return;
}
if (m_loginInProgress) {
- qWarning() << "Login already pending...";
+ qCDebug(dcCloud()) << "Login already pending...";
return;
}
m_loginInProgress = true;
@@ -229,7 +232,7 @@ void AWSClient::login(const QString &username, const QString &password)
QJsonDocument jsonDoc = QJsonDocument::fromVariant(params);
QByteArray payload = jsonDoc.toJson(QJsonDocument::Compact);
- qDebug() << "Logging in to AWS as user:" << username << "with config" << m_usedConfig;
+ qCInfo(dcCloud()) << "Logging in to AWS as user:" << username << "with config" << m_usedConfig;
QNetworkReply *reply = m_nam->post(request, payload);
connect(reply, &QNetworkReply::finished, this, [this, reply, username, password]() {
@@ -237,18 +240,18 @@ void AWSClient::login(const QString &username, const QString &password)
m_loginInProgress = false;
if (reply->error() != QNetworkReply::NoError) {
if (reply->error() == QNetworkReply::HostNotFoundError) {
- qDebug() << "Error logging in to aws due to network connection.";
+ qCWarning(dcCloud()) << "Error logging in to aws due to network connection.";
emit loginResult(LoginErrorNetworkError);
return;
}
if (reply->error() == QNetworkReply::ProtocolInvalidOperationError) {
- qWarning() << "Looks like a wrong password.";
+ qCWarning(dcCloud()) << "Looks like a wrong password.";
m_username.clear();
m_password.clear();
emit loginResult(LoginErrorInvalidUserOrPass);
return;
}
- qWarning() << "Error logging in to aws. Error:" << reply->error() << reply->errorString();
+ qCWarning(dcCloud()) << "Error logging in to aws. Error:" << reply->error() << reply->errorString();
emit loginResult(LoginErrorUnknownError);
return;
}
@@ -256,7 +259,7 @@ void AWSClient::login(const QString &username, const QString &password)
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
- qWarning() << "Failed to parse AWS login response" << error.errorString();
+ qCWarning(dcCloud()) << "Failed to parse AWS login response" << error.errorString();
m_username.clear();
m_password.clear();
emit loginResult(LoginErrorUnknownError);
@@ -272,7 +275,7 @@ void AWSClient::login(const QString &username, const QString &password)
// qDebug() << "AWS ID token" << m_idToken;
QList jwtParts = m_idToken.split('.');
if (jwtParts.count() != 3) {
- qWarning() << "Error: JWT token doesn't have 3 parts. Cannot retrieve AWS Cognito ID.";
+ qCWarning(dcCloud()) << "Error: JWT token doesn't have 3 parts. Cannot retrieve AWS Cognito ID.";
return;
}
// qDebug() << "decoded header:" << QByteArray::fromBase64(jwtParts.at(0));
@@ -331,13 +334,13 @@ void AWSClient::signup(const QString &username, const QString &password)
QJsonDocument jsonDoc = QJsonDocument::fromVariant(params);
QByteArray payload = jsonDoc.toJson(QJsonDocument::Compact);
- qDebug() << "Signing up to AWS as user:" << username << payload;
+ qCInfo(dcCloud()) << "Signing up to AWS as user:" << username << payload;
QNetworkReply *reply = m_nam->post(request, payload);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
QByteArray data = reply->readAll();
reply->deleteLater();
- qDebug() << "AWS signup reply:" << data;
+ qCDebug(dcCloud()) << "AWS signup reply:" << data;
if (reply->error() == QNetworkReply::ProtocolInvalidOperationError) {
emit signupResult(LoginErrorInvalidUserOrPass);
@@ -345,7 +348,7 @@ void AWSClient::signup(const QString &username, const QString &password)
}
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "Error signing up to aws:" << reply->error() << reply->errorString();
+ qCWarning(dcCloud()) << "Error signing up to aws:" << reply->error() << reply->errorString();
m_username.clear();
m_password.clear();
emit signupResult(LoginErrorUnknownError);
@@ -382,13 +385,13 @@ void AWSClient::confirmRegistration(const QString &code)
QJsonDocument jsonDoc = QJsonDocument::fromVariant(params);
QByteArray payload = jsonDoc.toJson(QJsonDocument::Compact);
- qDebug() << "Confirming registration for user:" << m_username;
+ qCInfo(dcCloud()) << "Confirming registration for user:" << m_username;
QNetworkReply *reply = m_nam->post(request, payload);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
QByteArray data = reply->readAll();
reply->deleteLater();
- qDebug() << "AWS signup reply:" << data;
+ qCDebug(dcCloud()) << "AWS signup reply:" << data;
if (reply->error() == QNetworkReply::ProtocolInvalidOperationError) {
QJsonParseError error;
@@ -405,7 +408,7 @@ void AWSClient::confirmRegistration(const QString &code)
}
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "Error confirming registration:" << reply->error() << reply->errorString();
+ qCWarning(dcCloud()) << "Error confirming registration:" << reply->error() << reply->errorString();
emit confirmationResult(LoginErrorUnknownError);
return;
}
@@ -440,7 +443,7 @@ void AWSClient::forgotPassword(const QString &username)
QJsonDocument jsonDoc = QJsonDocument::fromVariant(params);
QByteArray payload = jsonDoc.toJson(QJsonDocument::Compact);
- qDebug() << "Forgot password for user:" << username << payload;
+ qCInfo(dcCloud()) << "Forgot password for user:" << username << payload;
QNetworkReply *reply = m_nam->post(request, payload);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
@@ -456,12 +459,12 @@ void AWSClient::forgotPassword(const QString &username)
}
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "Error calling ForgotPassword:" << reply->error() << reply->errorString() << data;
+ qCWarning(dcCloud()) << "Error calling ForgotPassword:" << reply->error() << reply->errorString() << data;
emit forgotPasswordResult(LoginErrorUnknownError);
return;
}
- qDebug() << "AWS forgotPassword success:" << data;
+ qCInfo(dcCloud()) << "AWS forgotPassword success:" << data;
emit forgotPasswordResult(LoginErrorNoError);
});
@@ -491,7 +494,8 @@ void AWSClient::confirmForgotPassword(const QString &username, const QString &co
QJsonDocument jsonDoc = QJsonDocument::fromVariant(params);
QByteArray payload = jsonDoc.toJson(QJsonDocument::Compact);
- qDebug() << "ConfirmForgotPassword for user:" << username << payload;
+ qCInfo(dcCloud()) << "Resetting password for user:" << username;
+ qCDebug(dcCloud()) << "Reset password payload:" << payload;
QNetworkReply *reply = m_nam->post(request, payload);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
@@ -499,12 +503,12 @@ void AWSClient::confirmForgotPassword(const QString &username, const QString &co
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "Error calling ConfirmForgotPassword:" << reply->error() << reply->errorString() << data;
+ qCWarning(dcCloud()) << "Error calling ConfirmForgotPassword:" << reply->error() << reply->errorString() << data;
emit confirmForgotPasswordResult(LoginErrorUnknownError);
return;
}
- qDebug() << "AWS ConfirmForgotPassword success:" << data;
+ qCInfo(dcCloud()) << "Password reset successfully.";
emit confirmForgotPasswordResult(LoginErrorNoError);
});
@@ -513,26 +517,26 @@ void AWSClient::confirmForgotPassword(const QString &username, const QString &co
void AWSClient::deleteAccount()
{
if (!isLoggedIn()) {
- qWarning() << "Not logged in at AWS. Can't delete account";
+ qCWarning(dcCloud()) << "Not logged in at AWS. Can't delete account";
return;
}
if (tokensExpired()) {
- qDebug() << "Cannot unpair device. Need to refresh our tokens";
+ qCInfo(dcCloud()) << "Cannot unpair device. Need to refresh our tokens";
refreshAccessToken();
QueuedCall::enqueue(m_callQueue, QueuedCall("deleteAccount"));
return;
}
- qDebug() << "Deleting account";
+ qCInfo(dcCloud()) << "Deleting account";
QUrl url(QString("https://%1/users/profiles/%2").arg(m_configs.value(m_usedConfig).apiEndpoint).arg(m_userId));
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("x-api-idToken", m_idToken);
- qDebug() << "DELETE" << url.toString();
- qDebug() << "HEADERS:";
+ qCDebug(dcCloud()) << "DELETE" << url.toString();
+ qCDebug(dcCloud()) << "HEADERS:";
foreach (const QByteArray &hdr, request.rawHeaderList()) {
- qDebug() << hdr << ":" << request.rawHeader(hdr);
+ qCDebug(dcCloud()) << hdr << ":" << request.rawHeader(hdr);
}
QNetworkReply *reply = m_nam->deleteResource(request);
@@ -540,36 +544,36 @@ void AWSClient::deleteAccount()
reply->deleteLater();
QByteArray data = reply->readAll();
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "Error deleting cloud user account:" << reply->error() << reply->errorString() << qUtf8Printable(data);
+ qCWarning(dcCloud()) << "Error deleting cloud user account:" << reply->error() << reply->errorString() << qUtf8Printable(data);
emit deleteAccountResult(LoginErrorUnknownError);
return;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
- qWarning() << "Failed to parse JSON from server" << error.errorString() << qUtf8Printable(data);
+ qCWarning(dcCloud()) << "Failed to parse JSON from server" << error.errorString() << qUtf8Printable(data);
emit deleteAccountResult(LoginErrorUnknownError);
return;
}
emit deleteAccountResult(LoginErrorNoError);
logout();
- qDebug() << "Account deleted" << data;
+ qCInfo(dcCloud()) << "Account deleted" << data;
});
}
void AWSClient::unpairDevice(const QString &coreId)
{
if (!isLoggedIn()) {
- qWarning() << "Not logged in at AWS. Can't unpair device";
+ qCWarning(dcCloud()) << "Not logged in at AWS. Can't unpair device";
return;
}
if (tokensExpired()) {
- qDebug() << "Cannot unpair device. Need to refresh our tokens";
+ qCInfo(dcCloud()) << "Cannot unpair device. Need to refresh our tokens";
refreshAccessToken();
QueuedCall::enqueue(m_callQueue, QueuedCall("unpairDevice", coreId));
return;
}
- qDebug() << "unpairing device";
+ qCInfo(dcCloud()) << "Unpairing device" << coreId << "from user" << m_username;
QUrl url(QString("https://%1/users/devices/%2").arg(m_configs.value(m_usedConfig).apiEndpoint).arg(coreId));
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
@@ -591,7 +595,7 @@ void AWSClient::unpairDevice(const QString &coreId)
qWarning() << "Failed to parse JSON from server" << error.errorString() << qUtf8Printable(data);
return;
}
- qDebug() << "Device unpaired" << data;
+ qCInfo(dcCloud()) << "Device" << coreId << "unpaired from user" << m_username;
m_devices->remove(coreId);
});
@@ -628,19 +632,19 @@ void AWSClient::getId()
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "Error calling GetId" << reply->error() << reply->errorString();
+ qCWarning(dcCloud()) << "Error calling GetId" << reply->error() << reply->errorString();
return;
}
QByteArray data = reply->readAll();
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
- qWarning() << "Error parsing json reply for GetId" << error.errorString();
+ qCWarning(dcCloud()) << "Error parsing json reply for GetId" << error.errorString();
return;
}
m_identityId = jsonDoc.toVariant().toMap().value("IdentityId").toByteArray();
-// qDebug() << "Received cognito identity id" << m_identityId;// << qUtf8Printable(data);
+ qCDebug(dcCloud()) << "Received cognito identity id" << m_identityId;// << qUtf8Printable(data);
getCredentialsForIdentity(m_identityId);
});
@@ -649,11 +653,11 @@ void AWSClient::getId()
void AWSClient::registerPushNotificationEndpoint(const QString ®istrationId, const QString &deviceDisplayName, const QString mobileDeviceId, const QString &mobileDeviceManufacturer, const QString &mobileDeviceModel)
{
if (!isLoggedIn()) {
- qWarning() << "Not logged in at AWS. Can't register push endpoint";
+ qCWarning(dcCloud()) << "Not logged in at AWS. Can't register push endpoint";
return;
}
if (tokensExpired()) {
- qDebug() << "Cannot register push endpoint. Need to refresh our tokens";
+ qCInfo(dcCloud()) << "Cannot register push endpoint. Need to refresh our tokens";
QueuedCall::enqueue(m_callQueue, QueuedCall("registerPushNotificationEndpoint", registrationId, deviceDisplayName, mobileDeviceId, mobileDeviceManufacturer, mobileDeviceModel));
refreshAccessToken();
return;
@@ -681,7 +685,7 @@ void AWSClient::registerPushNotificationEndpoint(const QString ®istrationId,
QJsonDocument jsonDoc = QJsonDocument::fromVariant(payload);
- qDebug() << "Registering push notification endpoint" << qUtf8Printable(QJsonDocument::fromVariant(payload).toJson());
+ qCInfo(dcCloud()) << "Registering push notification endpoint" << qUtf8Printable(QJsonDocument::fromVariant(payload).toJson());
// qDebug() << "POST" << url.toString();
// qDebug() << "HEADERS:";
// foreach (const QByteArray &hdr, request.rawHeaderList()) {
@@ -694,10 +698,10 @@ void AWSClient::registerPushNotificationEndpoint(const QString ®istrationId,
reply->deleteLater();
QByteArray data = reply->readAll();
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "Error registering push notification endpoint:" << reply->error() << reply->errorString() << qUtf8Printable(data);
+ qCWarning(dcCloud()) << "Error registering push notification endpoint:" << reply->error() << reply->errorString() << qUtf8Printable(data);
return;
}
- qDebug() << "Push notification endpoint registered" << data;
+ qCInfo(dcCloud()) << "Push notification endpoint registered" << data;
});
}
@@ -722,27 +726,27 @@ void AWSClient::fetchCertificate(const QString &uuid, std::functionget(request);
- qDebug() << "Fetching certificate for vendor:" << m_configs.value(m_usedConfig).certificateVendorId << "device id:" << fixedUuid;
+ qCInfo(dcCloud()) << "Fetching certificate for vendor:" << m_configs.value(m_usedConfig).certificateVendorId << "device id:" << fixedUuid;
connect(reply, &QNetworkReply::finished, this, [this, reply, callback]() {
reply->deleteLater();
QByteArray data = reply->readAll();
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "Error deploying certificate" << data;
+ qCWarning(dcCloud()) << "Error deploying certificate" << data;
return;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
- qWarning() << "Error parsing certificate json" << data;
+ qCWarning(dcCloud()) << "Error parsing certificate json" << data;
return;
}
QByteArray certificate = jsonDoc.toVariant().toMap().value("certificatePem").toByteArray();
QByteArray publicKey = jsonDoc.toVariant().toMap().value("keyPair").toMap().value("PublicKey").toByteArray();
QByteArray privateKey = jsonDoc.toVariant().toMap().value("keyPair").toMap().value("PrivateKey").toByteArray();
- qDebug() << "Certificate received" << certificate;
- qDebug() << "Public key" << publicKey;
- qDebug() << "Private key" << privateKey;
+ qCDebug(dcCloud()) << "Certificate received" << certificate;
+ qCDebug(dcCloud()) << "Public key" << publicKey;
+ qCDebug(dcCloud()) << "Private key" << privateKey;
callback(rootCA, certificate, publicKey, privateKey, m_configs.value(m_usedConfig).mqttEndpoint);
});
@@ -769,10 +773,10 @@ void AWSClient::setConfig(const QString &config)
if (m_usedConfig != fixedConfig) {
if (!m_configs.contains(fixedConfig)) {
- qWarning() << "AWS: Config" << fixedConfig << "not known. Not switching AWS config";
+ qCWarning(dcCloud()) << "AWS: Config" << fixedConfig << "not known. Not switching AWS config";
return;
}
- qDebug() << "Setting AWS configuration to" << fixedConfig;
+ qCInfo(dcCloud()) << "Setting AWS configuration to" << fixedConfig;
m_usedConfig = fixedConfig;
emit configChanged();
}
@@ -814,7 +818,7 @@ void AWSClient::getCredentialsForIdentity(const QString &identityId)
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "Error calling GetCredentialsForIdentity" << reply->errorString();
+ qCWarning(dcCloud()) << "Error calling GetCredentialsForIdentity" << reply->errorString();
emit loginResult(LoginErrorUnknownError);
return;
}
@@ -822,7 +826,7 @@ void AWSClient::getCredentialsForIdentity(const QString &identityId)
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
- qWarning() << "Error parsing JSON reply from GetCredentialsForIdentity" << error.errorString();
+ qCWarning(dcCloud()) << "Error parsing JSON reply from GetCredentialsForIdentity" << error.errorString();
emit loginResult(LoginErrorUnknownError);
return;
}
@@ -832,14 +836,11 @@ void AWSClient::getCredentialsForIdentity(const QString &identityId)
m_secretKey = credentialsMap.value("SecretKey").toByteArray();
m_sessionToken = credentialsMap.value("SessionToken").toByteArray();
m_sessionTokenExpiry = QDateTime::fromSecsSinceEpoch(credentialsMap.value("Expiration").toLongLong());
- qDebug() << "AWS Credentials for Identity received.";// << data;
- qDebug() << "AWS login successful. Userid:" << m_userId;
+ qCInfo(dcCloud()) << "AWS login successful. Userid:" << m_userId;
QSettings settings;
- qDebug() << "settings has:" << settings.childGroups();
-
bool newLogin = !settings.childGroups().contains("cloud");
settings.remove("cloud");
@@ -860,7 +861,7 @@ void AWSClient::getCredentialsForIdentity(const QString &identityId)
emit loginResult(LoginErrorNoError);
if (newLogin) {
- qDebug() << "new login!";
+ qCInfo(dcCloud()) << "New login!";
emit isLoggedInChanged();
}
@@ -890,11 +891,11 @@ bool AWSClient::tokensExpired() const
bool AWSClient::postToMQTT(const QString &coreId, const QString &nonce, QObject* sender, std::function callback)
{
if (!isLoggedIn()) {
- qWarning() << "Cannot post to MQTT. Not logged in to AWS";
+ qCWarning(dcCloud()) << "Cannot post to MQTT. Not logged in to AWS";
return false;
}
if (tokensExpired()) {
- qDebug() << "Cannot post to MQTT. Need to refresh the tokens first";
+ qCDebug(dcCloud()) << "Cannot post to MQTT. Need to refresh the tokens first";
refreshAccessToken();
QueuedCall::enqueue(m_callQueue, QueuedCall("postToMQTT", coreId, nonce, sender, callback));
return true; // So far it looks we're doing ok... let's return true
@@ -929,7 +930,7 @@ bool AWSClient::postToMQTT(const QString &coreId, const QString &nonce, QObject*
// Workaround MQTT broker url weirdness as described above
request.setUrl("https://" + m_configs.value(m_usedConfig).mqttEndpoint + path1);
- qDebug() << "Posting to MQTT:" << request.url().toString();
+ qCDebug(dcCloud()) << "Posting to MQTT:" << request.url().toString();
// qDebug() << "HEADERS:";
// foreach (const QByteArray &headerName, request.rawHeaderList()) {
// qDebug() << headerName << ":" << request.rawHeader(headerName);
@@ -948,23 +949,23 @@ bool AWSClient::postToMQTT(const QString &coreId, const QString &nonce, QObject*
QByteArray data = reply->readAll();
// qDebug() << "MQTT post reply" << data;
if (senderWatcher.isNull()) {
- qDebug() << "Request object disappeared. Discarding MQTT reply...";
+ qCDebug(dcCloud()) << "Request object disappeared. Discarding MQTT reply...";
return;
}
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "MQTT Network reply error" << reply->error() << reply->errorString();
+ qCWarning(dcCloud()) << "MQTT Network reply error" << reply->error() << reply->errorString();
callback(false);
return;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
- qWarning() << "Failed to parse MQTT reply" << error.error << error.errorString() << data;
+ qCWarning(dcCloud()) << "Failed to parse MQTT reply" << error.error << error.errorString() << data;
callback(false);
return;
}
if (jsonDoc.toVariant().toMap().value("message").toString() != "OK") {
- qWarning() << "Something went wrong posting to MQTT:" << jsonDoc.toVariant().toMap().value("message").toString();
+ qCWarning(dcCloud()) << "Something went wrong posting to MQTT:" << jsonDoc.toVariant().toMap().value("message").toString();
callback(false);
return;
}
@@ -976,18 +977,22 @@ bool AWSClient::postToMQTT(const QString &coreId, const QString &nonce, QObject*
void AWSClient::fetchDevices()
{
+ if (m_usedConfig.isEmpty()) {
+ qCWarning(dcCloud()) << "Cloud environment not set. Not fetching cloud devices";
+ return;
+ }
if (!isLoggedIn()) {
- qWarning() << "Not logged in at AWS. Can't fetch paired devices";
+ qCWarning(dcCloud()) << "Not logged in at AWS. Can't fetch paired devices";
return;
}
if (tokensExpired()) {
- qDebug() << "Cannot fetch devices. Need to refresh our tokens";
+ qCDebug(dcCloud()) << "Cannot fetch devices. Need to refresh our tokens";
refreshAccessToken();
QueuedCall::enqueue(m_callQueue, QueuedCall("fetchDevices"));
return;
}
-// qDebug() << "Fetching cloud devices";
QUrl url(QString("https://%1/users/devices").arg(m_configs.value(m_usedConfig).apiEndpoint));
+ qCDebug(dcCloud()) << "Fetching cloud devices" << url.toString();
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("x-api-idToken", m_idToken);
@@ -999,13 +1004,13 @@ void AWSClient::fetchDevices()
m_devices->setBusy(false);
QByteArray data = reply->readAll();
if (reply->error() != QNetworkReply::NoError) {
- qWarning() << "Error fetching cloud devices:" << reply->error() << reply->errorString() << qUtf8Printable(data);
+ qCWarning(dcCloud()) << "Error fetching cloud devices:" << reply->error() << reply->errorString() << qUtf8Printable(data);
return;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
- qWarning() << "Failed to parse JSON from server" << error.errorString() << qUtf8Printable(data);
+ qCWarning(dcCloud()) << "Failed to parse JSON from server" << error.errorString() << qUtf8Printable(data);
return;
}
QList actualDevices;
@@ -1013,7 +1018,7 @@ void AWSClient::fetchDevices()
QString deviceId = entry.toMap().value("deviceId").toString();
QString name = entry.toMap().value("name").toString();
bool online = entry.toMap().value("online").toBool();
- qDebug() << "Have cloud device:" << deviceId << name << "online:" << online;
+ qCDebug(dcCloud()) << "Have cloud device:" << deviceId << name << "online:" << online;
AWSDevice *d = m_devices->getDevice(deviceId);
if (!d) {
@@ -1044,7 +1049,7 @@ void AWSClient::fetchDevices()
void AWSClient::refreshAccessToken()
{
if (!isLoggedIn()) {
- qDebug() << "Cannot refresh tokens. Not logged in to AWS";
+ qCWarning(dcCloud()) << "Cannot refresh tokens. Not logged in to AWS";
return;
}
@@ -1111,7 +1116,7 @@ void AWSClient::refreshAccessToken()
// settings.setValue("idToken", m_idToken);
// settings.setValue("refreshToken", m_refreshToken);
- qDebug() << "AWS login successful" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
+ qCInfo(dcCloud()) << "AWS login successful" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
emit isLoggedInChanged();
});
@@ -1217,7 +1222,7 @@ void AWSDevices::remove(const QString &uuid)
}
}
if (idx == -1) {
- qWarning() << "Cannot remove AWS with id" << uuid << "as there is no such device";
+ qCWarning(dcCloud()) << "Cannot remove AWS with id" << uuid << "as there is no such device";
return;
}
beginRemoveRows(QModelIndex(), idx, idx);
diff --git a/libnymea-app/connection/discovery/upnpdiscovery.cpp b/libnymea-app/connection/discovery/upnpdiscovery.cpp
index 1de798ca..eba09ba6 100644
--- a/libnymea-app/connection/discovery/upnpdiscovery.cpp
+++ b/libnymea-app/connection/discovery/upnpdiscovery.cpp
@@ -35,6 +35,10 @@
#include
#include
+#include "logging.h"
+
+NYMEA_LOGGING_CATEGORY(dcUPnP, "UPnP")
+
UpnpDiscovery::UpnpDiscovery(NymeaHosts *nymeaHosts, QObject *parent) :
QObject(parent),
m_nymeaHosts(nymeaHosts)
@@ -61,10 +65,10 @@ UpnpDiscovery::UpnpDiscovery(NymeaHosts *nymeaHosts, QObject *parent) :
}
if (port == 65535 || socket->state() != QUdpSocket::BoundState) {
socket->deleteLater();
- qWarning() << "UPnP: Discovery could not bind to interface" << netAddressEntry.ip();
+ qCWarning(dcUPnP()) << "Discovery could not bind to interface" << netAddressEntry.ip();
continue;
}
- qDebug() << "UPnP: Discovering on" << netAddressEntry.ip() << port;
+ qCInfo(dcUPnP()) << "Discovering on" << netAddressEntry.ip() << port;
m_sockets.append(socket);
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));
connect(socket, &QUdpSocket::readyRead, this, &UpnpDiscovery::readData);
@@ -86,11 +90,11 @@ bool UpnpDiscovery::available() const
void UpnpDiscovery::discover()
{
if (!available()) {
- qWarning() << "UPnP: UPnP not available. Discovery not started.";
+ qCWarning(dcUPnP()) << "UPnP not available. Discovery not started.";
return;
}
- qDebug() << "UPNP: Discovery started...";
+ qCInfo(dcUPnP()) << "Discovery started...";
m_repeatTimer.start();
m_foundDevices.clear();
writeDiscoveryPacket();
@@ -99,7 +103,7 @@ void UpnpDiscovery::discover()
void UpnpDiscovery::stopDiscovery()
{
- qDebug() << "UPNP: Discovery stopped.";
+ qCInfo(dcUPnP()) << "Discovery stopped.";
m_repeatTimer.stop();
emit discoveringChanged();
}
@@ -112,13 +116,13 @@ void UpnpDiscovery::writeDiscoveryPacket()
"MX:2\r\n"
"ST: ssdp:all\r\n\r\n");
-// qDebug() << "sending discovery package";
+ qCDebug(dcUPnP()) << "sending discovery package";
foreach (QUdpSocket* socket, m_sockets) {
qint64 ret = socket->writeDatagram(ssdpSearchMessage, QHostAddress("239.255.255.250"), 1900);
if (ret != ssdpSearchMessage.length()) {
- qWarning() << "UPnP: Error sending SSDP query on socket" << socket->localAddress();
+ // Leaving a debug message because this happens on many platforms and spams logs.
+ qCDebug(dcUPnP()) << "Error sending SSDP query on socket" << socket->localAddress();
}
-
}
}
@@ -140,7 +144,7 @@ void UpnpDiscovery::readData()
data.resize(socket->pendingDatagramSize());
socket->readDatagram(data.data(), data.size(), &hostAddress, &port);
-// qDebug() << "Received UPnP datagram:" << data;
+ qCDebug(dcUPnP()) << "Received UPnP datagram:" << data;
// if the data contains the HTTP OK header...
if (data.contains("HTTP/1.1 200 OK")) {
@@ -168,7 +172,7 @@ void UpnpDiscovery::readData()
if (!m_foundDevices.contains(location) && isNymea) {
m_foundDevices.append(location);
- // qDebug() << "Getting server data from:" << location;
+ qCDebug(dcUPnP()) << "Getting server data from:" << location;
QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(location));
connect(reply, &QNetworkReply::sslErrors, [reply](const QList &errors){
reply->ignoreSslErrors(errors);
@@ -186,7 +190,7 @@ void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (reply->error() != QNetworkReply::NoError || status != 200) {
- qWarning() << "UPnP: Error fetching discovery data:" << status << reply->error() << reply->errorString();
+ qCWarning(dcUPnP()) << "UPnP: Error fetching discovery data:" << status << reply->error() << reply->errorString();
return;
}
@@ -244,13 +248,13 @@ void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
}
}
- qDebug() << "UPnP: Discovered device" << name << discoveredAddress << version << connections /*<< data*/;
+ qCDebug(dcUPnP()) << "Discovered device" << name << discoveredAddress << version << connections /*<< data*/;
NymeaHost* device = m_nymeaHosts->find(uuid);
if (!device) {
device = new NymeaHost(m_nymeaHosts);
device->setUuid(uuid);
- qDebug() << "UPnP: Adding new host to model";
+ qCInfo(dcUPnP()) << "Adding new host to model" << device->name() << device->uuid();
m_nymeaHosts->addHost(device);
}
device->setName(name);
@@ -258,7 +262,7 @@ void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
foreach (const QUrl &url, connections) {
Connection *connection = device->connections()->find(url);
if (!connection) {
- qDebug() << "UPnP: Adding new connection to host:" << device->name() << url;
+ qCInfo(dcUPnP()) << "Adding new connection to host:" << device->name() << url;
bool sslEnabled = url.scheme() == "nymeas" || url.scheme() == "wss";
QString displayName = QString("%1:%2").arg(url.host()).arg(url.port());
Connection::BearerType bearerType = QHostAddress(url.host()).isLoopback() ? Connection::BearerTypeLoopback : Connection::BearerTypeLan;
@@ -266,7 +270,7 @@ void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
connection->setOnline(true);
device->connections()->addConnection(connection);
} else {
- qDebug() << "UPnP: Setting connection online:" << device->name() << url.toString();
+ qCInfo(dcUPnP()) << "Setting connection online:" << device->name() << url.toString();
connection->setOnline(true);
}
}
diff --git a/libnymea-app/connection/discovery/zeroconfdiscovery.cpp b/libnymea-app/connection/discovery/zeroconfdiscovery.cpp
index d2816d11..9f0ca5c7 100644
--- a/libnymea-app/connection/discovery/zeroconfdiscovery.cpp
+++ b/libnymea-app/connection/discovery/zeroconfdiscovery.cpp
@@ -33,6 +33,9 @@
#include
#include "../nymeahost.h"
+#include "logging.h"
+
+NYMEA_LOGGING_CATEGORY(dcZeroConf, "ZeroConf")
ZeroconfDiscovery::ZeroconfDiscovery(NymeaHosts *nymeaHosts, QObject *parent) :
QObject(parent),
@@ -50,9 +53,9 @@ ZeroconfDiscovery::ZeroconfDiscovery(NymeaHosts *nymeaHosts, QObject *parent) :
if (m_zeroconfJsonRPC->isValid()) {
m_zeroconfJsonRPC->startBrowser("_jsonrpc._tcp", QAbstractSocket::IPv4Protocol);
- qDebug() << "ZeroConf: Created service browser for _jsonrpc._tcp:" << m_zeroconfJsonRPC->browserExists();
+ qCInfo(dcZeroConf()) << "Created service browser for _jsonrpc._tcp:" << m_zeroconfJsonRPC->browserExists();
} else {
- qWarning() << "Zeroconf init failed for _jsonprc._tcp";
+ qCWarning(dcZeroConf()) << "Failed to initialize service broeser for _jsonprc._tcp";
}
m_zeroconfWebSocket = new QZeroConf(this);
@@ -61,19 +64,19 @@ ZeroconfDiscovery::ZeroconfDiscovery(NymeaHosts *nymeaHosts, QObject *parent) :
connect(m_zeroconfWebSocket, &QZeroConf::serviceRemoved, this, &ZeroconfDiscovery::serviceEntryRemoved);
if (m_zeroconfWebSocket->isValid()) {
m_zeroconfWebSocket->startBrowser("_ws._tcp", QAbstractSocket::IPv4Protocol);
- qDebug() << "ZeroConf: Created service browser for _ws._tcp:" << m_zeroconfWebSocket->browserExists();
+ qCInfo(dcZeroConf()) << "Created service browser for _ws._tcp:" << m_zeroconfWebSocket->browserExists();
} else {
- qWarning() << "Zeroconf init failed for _ws._tcp";
+ qCWarning(dcZeroConf()) << "Failed to initialize service browserr for _ws._tcp";
}
#else
- qDebug() << "Zeroconf support not compiled in. Zeroconf will not be available.";
+ qCInfo(dcZeroConf()) << "Zeroconf support not compiled in. Zeroconf will not be available.";
#endif
}
ZeroconfDiscovery::~ZeroconfDiscovery()
{
- qDebug() << "ZeroConf: Shutting down service browsers";
+ qCInfo(dcZeroConf()) << "Shutting down service browsers";
}
bool ZeroconfDiscovery::available() const
@@ -95,21 +98,21 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
{
if (!entry->name().startsWith("nymea")) {
// Skip non-nymea services altogether
- qDebug() << "Skipping Avahi entry:" << entry << entry->ip() << entry->txt() << entry->type();
+ qCDebug(dcZeroConf()) << "Skipping service entry:" << entry << entry->ip() << entry->txt() << entry->type();
return;
}
if (entry->ip().isNull()) {
// Skip entries that don't have an ip address at all for some reason
- qDebug() << "Skipping Avahi entry:" << entry << entry->ip() << entry->txt() << entry->type();
+ qCDebug(dcZeroConf()) << "Skipping service entry:" << entry << entry->ip() << entry->txt() << entry->type();
return;
}
if (entry->ip().toString().startsWith("fe80")) {
// Skip link-local-IPv6 results
- qDebug() << "Skipping Avahi entry:" << entry << entry->ip() << entry->txt() << entry->type();
+ qCDebug(dcZeroConf()) << "Skipping service entry:" << entry << entry->ip() << entry->txt() << entry->type();
return;
}
-// qDebug() << "zeroconf service discovered" << entry->type() << entry->name() << " IP:" << entry->ip().toString() << entry->txt();
+ qCDebug(dcZeroConf()) << "Service discovered" << entry->type() << entry->name() << " IP:" << entry->ip().toString() << entry->txt();
QString uuid;
bool sslEnabled = false;
@@ -130,14 +133,14 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
version = txtRecord.second;
}
}
-// qDebug() << "avahi service entry added" << serverName << uuid << sslEnabled;
+ qCDebug(dcZeroConf()) << "Service entry added" << serverName << uuid << sslEnabled;
NymeaHost* host = m_nymeaHosts->find(uuid);
if (!host) {
host = new NymeaHost(m_nymeaHosts);
host->setUuid(uuid);
- qDebug() << "ZeroConf: Adding new host:" << serverName << uuid;
+ qCInfo(dcZeroConf()) << "Adding new host:" << serverName << uuid;
m_nymeaHosts->addHost(host);
}
host->setName(serverName);
@@ -153,14 +156,14 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
url.setPort(entry->port());
Connection *connection = host->connections()->find(url);
if (!connection) {
- qDebug() << "Zeroconf: Adding new connection to host:" << host->name() << url.toString();
+ qCInfo(dcZeroConf()) << "Adding new connection to host:" << host->name() << url.toString();
QString displayName = QString("%1:%2").arg(url.host()).arg(url.port());
Connection::BearerType bearerType = QHostAddress(url.host()).isLoopback() ? Connection::BearerTypeLoopback : Connection::BearerTypeLan;
connection = new Connection(url, bearerType, sslEnabled, displayName);
connection->setOnline(true);
host->connections()->addConnection(connection);
} else {
- qDebug() << "Zeroconf: Setting connection online:" << host->name() << url.toString();
+ qCInfo(dcZeroConf()) << "Setting connection online:" << host->name() << url.toString();
connection->setOnline(true);
}
}
@@ -191,7 +194,7 @@ void ZeroconfDiscovery::serviceEntryRemoved(const QZeroConfService &entry)
}
}
-// qDebug() << "Zeroconf: Service entry removed" << entry->name();
+ qCDebug(dcZeroConf()) << "Service entry removed" << entry->name();
NymeaHost* host = m_nymeaHosts->find(uuid);
if (!host) {
@@ -213,7 +216,7 @@ void ZeroconfDiscovery::serviceEntryRemoved(const QZeroConfService &entry)
return;
}
- qDebug() << "Zeroconf: Setting connection offline:" << host->name() << url.toString();
+ qCInfo(dcZeroConf()) << "Setting connection offline:" << host->name() << url.toString();
connection->setOnline(false);
}
#endif
diff --git a/libnymea-app/connection/nymeaconnection.cpp b/libnymea-app/connection/nymeaconnection.cpp
index 4593c812..3a97ced9 100644
--- a/libnymea-app/connection/nymeaconnection.cpp
+++ b/libnymea-app/connection/nymeaconnection.cpp
@@ -44,6 +44,9 @@
#include
#include "nymeatransportinterface.h"
+#include "logging.h"
+
+NYMEA_LOGGING_CATEGORY(dcNymeaConnection, "NymeaConnection")
NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent)
{
@@ -51,18 +54,18 @@ NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent)
QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationAdded, this, [this](const QNetworkConfiguration &config){
Q_UNUSED(config)
-// qDebug() << "Network configuration added:" << config.name() << config.bearerTypeName() << config.purpose();
+ qCDebug(dcNymeaConnection()) << "Network configuration added:" << config.name() << config.bearerTypeName() << config.purpose();
updateActiveBearers();
});
QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationRemoved, this, [this](const QNetworkConfiguration &config){
Q_UNUSED(config)
-// qDebug() << "Network configuration removed:" << config.name() << config.bearerTypeName() << config.purpose();
+ qCDebug(dcNymeaConnection()) << "Network configuration removed:" << config.name() << config.bearerTypeName() << config.purpose();
updateActiveBearers();
});
QGuiApplication *app = static_cast(QGuiApplication::instance());
- QObject::connect(app, &QGuiApplication::applicationStateChanged, this, [this](Qt::ApplicationState /*state*/) {
-// qDebug() << "Application state changed to:" << state;
+ QObject::connect(app, &QGuiApplication::applicationStateChanged, this, [this](Qt::ApplicationState state) {
+ qCDebug(dcNymeaConnection()) << "Application state changed to:" << state;
updateActiveBearers();
});
@@ -116,11 +119,11 @@ void NymeaConnection::setCurrentHost(NymeaHost *host)
emit currentHostChanged();
if (!m_currentHost) {
- qDebug() << "No current host.";
+ qCInfo(dcNymeaConnection()) << "Current host cleared. Not connecting.";
return;
}
- qDebug() << "Nymea host is" << m_currentHost->name() << m_currentHost->uuid();
+ qCInfo(dcNymeaConnection()) << "Nymea host set to:" << m_currentHost->name() << m_currentHost->uuid();
connect(m_currentHost, &NymeaHost::connectionChanged, this, &NymeaConnection::hostConnectionsUpdated);
@@ -132,9 +135,6 @@ void NymeaConnection::setCurrentHost(NymeaHost *host)
Connection *NymeaConnection::currentConnection() const
{
- qDebug() << "Current connection:" << m_currentHost << m_currentTransport << m_transportCandidates.count();
- qDebug() << m_transportCandidates.keys();
- qDebug() << m_transportCandidates.value(m_currentTransport);
if (!m_currentHost || !m_currentTransport) {
return nullptr;
}
@@ -143,11 +143,10 @@ Connection *NymeaConnection::currentConnection() const
void NymeaConnection::sendData(const QByteArray &data)
{
-// qDebug() << "sending data:" << data;
if (connected()) {
m_currentTransport->sendData(data);
} else {
- qWarning() << "Connection: Not connected. Cannot send.";
+ qCWarning(dcNymeaConnection()) << "Connection: Not connected. Cannot send.";
}
}
@@ -155,19 +154,19 @@ void NymeaConnection::onSslErrors(const QList &errors)
{
NymeaTransportInterface *transport = qobject_cast(sender());
- qDebug() << "SSL errors for url:" << transport->url();
+ qCDebug(dcNymeaConnection()) << "SSL errors for url:" << transport->url();
QList ignoredErrors;
foreach (const QSslError &error, errors) {
qDebug() << error.errorString();
if (error.error() == QSslError::HostNameMismatch) {
- qDebug() << "Ignoring host mismatch on certificate.";
+ qCInfo(dcNymeaConnection()) << "Ignoring host mismatch on certificate.";
ignoredErrors.append(error);
} else if (error.error() == QSslError::SelfSignedCertificate || error.error() == QSslError::CertificateUntrusted) {
- qDebug() << "Ignoring self signed certificate.";
+ qCInfo(dcNymeaConnection()) << "Ignoring self signed certificate.";
ignoredErrors.append(error);
} else {
// Reject the connection on all other errors...
- qDebug() << "SSL Error:" << error.errorString() << error.certificate();
+ qCritical(dcNymeaConnection()) << "SSL Error:" << error.errorString() << error.certificate();
}
}
if (ignoredErrors == errors) {
@@ -213,7 +212,7 @@ void NymeaConnection::onError(QAbstractSocket::SocketError error)
}
if (transport == m_currentTransport) {
- qDebug() << "Current transport failed:" << error;
+ qCCritical(dcNymeaConnection()) << "Current transport failed:" << error;
// The current transport failed, forward the error
m_connectionStatus = errorStatus;
emit connectionStatusChanged();
@@ -226,9 +225,9 @@ void NymeaConnection::onError(QAbstractSocket::SocketError error)
m_transportCandidates.remove(transport);
transport->deleteLater();
}
- qDebug() << "A transport error happened for" << transport->url() << error << "(Still trying on" << m_transportCandidates.count() << "connections)";
+ qCWarning(dcNymeaConnection()) << "A transport error happened for" << transport->url() << error << "(Still trying on" << m_transportCandidates.count() << "connections)";
foreach (Connection *c, m_transportCandidates) {
- qDebug() << "Connection candidate:" << c->url();
+ qCDebug(dcNymeaConnection()) << "Connection candidate:" << c->url();
}
if (m_transportCandidates.isEmpty()) {
m_connectionStatus = errorStatus;
@@ -237,6 +236,7 @@ void NymeaConnection::onError(QAbstractSocket::SocketError error)
if (m_connectionStatus != ConnectionStatusSslUntrusted) {
QTimer::singleShot(1000, this, [this](){
if (m_currentHost) {
+ qCInfo(dcNymeaConnection()) << "Reconnecting...";
connectInternal(m_currentHost);
}
});
@@ -250,20 +250,18 @@ void NymeaConnection::onConnected()
NymeaTransportInterface* newTransport = qobject_cast(sender());
if (!m_currentTransport) {
m_currentTransport = newTransport;
- qDebug() << "NymeaConnection: Connected to" << m_currentHost->name() << "via" << m_currentTransport->url() << m_currentTransport->isEncrypted();
+ qCInfo(dcNymeaConnection()) << "Connected to" << m_currentHost->name() << "via" << m_currentTransport->url() << m_currentTransport->isEncrypted();
emit currentConnectionChanged();
emit connectedChanged(true);
return;
}
if (m_currentTransport != newTransport) {
- qDebug() << "Alternative connection established:" << newTransport->url();
-
// In theory, we could roam from one connection to another.
// However, in practice it turns out there are too many issues for this to be reliable
// So lets just tear down any alternative connection that comes up again.
- qDebug() << "Dropping alternative connection again...";
+ qCInfo(dcNymeaConnection()) << "Dropping successfully established alternative connection to" << newTransport->url() << "again...";
m_transportCandidates.remove(newTransport);
newTransport->deleteLater();
@@ -288,23 +286,24 @@ void NymeaConnection::onDisconnected()
{
NymeaTransportInterface* t = qobject_cast(sender());
if (m_currentTransport != t) {
- qWarning() << "NymeaConnection: An inactive transport for url" << t->url() << "disconnected... Cleaning up...";
+ qCDebug(dcNymeaConnection()) << "An inactive transport for url" << t->url() << "disconnected... Cleaning up...";
if (m_transportCandidates.contains(t)) {
m_transportCandidates.remove(t);
}
t->deleteLater();
+ qCInfo(dcNymeaConnection()) << "Current transport:" << m_currentTransport << "Remaining connections:" << m_transportCandidates.count() << "Current host:" << m_currentHost;
+
if (!m_currentTransport && m_transportCandidates.isEmpty()) {
- qDebug() << "Last connection dropped.";
+ qCWarning(dcNymeaConnection()) << "Last connection dropped.";
QTimer::singleShot(1000, this, [this](){
if (m_currentHost && m_connectionStatus != ConnectionStatusSslUntrusted) {
- qDebug() << "Trying to reconnect..";
+ qCInfo(dcNymeaConnection()) << "Trying to reconnect..";
connectInternal(m_currentHost);
}
});
}
- qDebug() << "Current transport:" << m_currentTransport << "Remaining connections:" << m_transportCandidates.count() << "Current host:" << m_currentHost;
return;
}
m_transportCandidates.remove(m_currentTransport);
@@ -313,7 +312,7 @@ void NymeaConnection::onDisconnected()
foreach (NymeaTransportInterface *candidate, m_transportCandidates.keys()) {
if (candidate->connectionState() == NymeaTransportInterface::ConnectionStateConnected) {
- qDebug() << "Alternative connection is still up. Roaming to:" << candidate->url();
+ qCInfo(dcNymeaConnection()) << "Alternative connection is still up. Roaming to:" << candidate->url();
m_currentTransport = candidate;
break;
}
@@ -322,7 +321,7 @@ void NymeaConnection::onDisconnected()
emit currentConnectionChanged();
if (!m_currentTransport) {
- qDebug() << "NymeaConnection: disconnected.";
+ qCInfo(dcNymeaConnection()) << "Disconnected.";
emit connectedChanged(false);
}
@@ -334,6 +333,7 @@ void NymeaConnection::onDisconnected()
if (m_connectionStatus != ConnectionStatusSslUntrusted) {
QTimer::singleShot(1000, this, [this](){
if (m_currentHost) {
+ qCInfo(dcNymeaConnection()) << "Trying to reconnect after disconnect...";
connectInternal(m_currentHost);
}
});
@@ -344,9 +344,9 @@ void NymeaConnection::updateActiveBearers()
{
NymeaConnection::BearerTypes availableBearerTypes;
QList configs = m_networkConfigManager->allConfigurations(QNetworkConfiguration::Active);
-// qDebug() << "Network configuations:" << configs.count();
+ qCDebug(dcNymeaConnection()) << "Network configuations:" << configs.count();
foreach (const QNetworkConfiguration &config, configs) {
-// qDebug() << "Active network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName();
+ qCDebug(dcNymeaConnection()) << "Active network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName();
// NOTE: iOS doesn't correctly report bearer types. It'll be Unknown all the time. Let's hardcode it to WiFi for that...
#if defined(Q_OS_IOS)
@@ -357,27 +357,27 @@ void NymeaConnection::updateActiveBearers()
}
if (availableBearerTypes == NymeaConnection::BearerTypeNone) {
// This is just debug info... On some platform bearer management seems a bit broken, so let's get some infos right away...
- qDebug() << "No active bearer available. Inactive bearers are:";
+ qCDebug(dcNymeaConnection()) << "No active bearer available. Inactive bearers are:";
QList configs = m_networkConfigManager->allConfigurations();
foreach (const QNetworkConfiguration &config, configs) {
- qDebug() << "Inactive network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName();
+ qCDebug(dcNymeaConnection()) << "Inactive network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName();
}
- qWarning() << "Updating network manager";
+ qCDebug(dcNymeaConnection()) << "Updating network manager";
m_networkConfigManager->updateConfigurations();
}
if (m_availableBearerTypes != availableBearerTypes) {
-// qDebug() << "Available Bearer Types changed:" << availableBearerTypes;
+ qCInfo(dcNymeaConnection()) << "Available Bearer Types changed to:" << availableBearerTypes;
m_availableBearerTypes = availableBearerTypes;
emit availableBearerTypesChanged();
} else {
-// qDebug() << "Available Bearer Types:" << availableBearerTypes;
+ qCDebug(dcNymeaConnection()) << "Available Bearer Types:" << availableBearerTypes;
}
if (!m_currentHost) {
// No host set... Nothing to do...
- qDebug() << "No current host... Nothing to do...";
+ qCInfo(dcNymeaConnection()) << "No current host... Nothing to do...";
return;
}
@@ -392,14 +392,17 @@ void NymeaConnection::updateActiveBearers()
if (!m_currentTransport) {
// There's a host but no connection. Try connecting now...
- qDebug() << "There's a host but no connection. Trying to connect now...";
+ qCInfo(dcNymeaConnection()) << "There's a host but no connection. Trying to connect now...";
connectInternal(m_currentHost);
}
}
void NymeaConnection::hostConnectionsUpdated()
{
- connectInternal(m_currentHost);
+ if (!m_currentTransport) {
+ qCInfo(dcNymeaConnection()) << "Possible connections for host" << m_currentHost->name() << "updated.";
+ connectInternal(m_currentHost);
+ }
}
void NymeaConnection::registerTransport(NymeaTransportInterfaceFactory *transportFactory)
@@ -418,10 +421,10 @@ void NymeaConnection::connectToHost(NymeaHost *nymeaHost, Connection *connection
m_preferredConnection = nullptr;
if (connection) {
if (nymeaHost->connections()->find(connection->url())) {
- qDebug() << "Setting preferred connection to" << connection->url();
+ qCInfo(dcNymeaConnection()) << "Setting preferred connection to" << connection->url();
m_preferredConnection = connection;
} else {
- qWarning() << "Connection" << connection << "is not a candidate for" << nymeaHost->name() << "Not setting preferred connection.";
+ qCWarning(dcNymeaConnection()) << "Connection" << connection << "is not a candidate for" << nymeaHost->name() << "Not setting preferred connection.";
}
}
@@ -432,47 +435,48 @@ void NymeaConnection::connectInternal(NymeaHost *host)
{
if (m_preferredConnection) {
if (isConnectionBearerAvailable(m_preferredConnection->bearerType())) {
- qDebug() << "Preferred connection is set. Using" << m_preferredConnection->url();
+ qCInfo(dcNymeaConnection()) << "Preferred connection is set. Using" << m_preferredConnection->url();
connectInternal(m_preferredConnection);
return;
}
- qDebug() << "Preferred connection set but no bearer available for it.";
+ qCWarning(dcNymeaConnection()) << "Preferred connection set but no bearer available for it.";
}
Connection *loopbackConnection = host->connections()->bestMatch(Connection::BearerTypeLoopback);
if (loopbackConnection) {
- qDebug() << "Best candidate Loopback connection:" << loopbackConnection->url();
+ qCDebug(dcNymeaConnection()) << "Best candidate Loopback connection:" << loopbackConnection->url();
connectInternal(loopbackConnection);
} else if (m_availableBearerTypes.testFlag(NymeaConnection::BearerTypeWiFi)
|| m_availableBearerTypes.testFlag(NymeaConnection::BearerTypeEthernet)) {
Connection* lanConnection = host->connections()->bestMatch(Connection::BearerTypeLan | Connection::BearerTypeWan);
if (lanConnection) {
- qDebug() << "Best candidate LAN/WAN connection:" << lanConnection->url();
+ qCDebug(dcNymeaConnection()) << "Best candidate LAN/WAN connection:" << lanConnection->url();
connectInternal(lanConnection);
} else {
- qDebug() << "No available LAN/WAN connection to" << host->name();
+ qCDebug(dcNymeaConnection()) << "No available LAN/WAN connection to" << host->name();
}
} else if (m_availableBearerTypes.testFlag(NymeaConnection::BearerTypeMobileData)) {
Connection* wanConnection = host->connections()->bestMatch(Connection::BearerTypeWan);
if (wanConnection) {
- qDebug() << "Best candidate WAN connection:" << wanConnection->url();
+ qCDebug(dcNymeaConnection()) << "Best candidate WAN connection:" << wanConnection->url();
connectInternal(wanConnection);
} else {
- qDebug() << "No available WAN connection to" << host->name();
+ qCDebug(dcNymeaConnection()) << "No available WAN connection to" << host->name();
}
}
Connection* cloudConnection = host->connections()->bestMatch(Connection::BearerTypeCloud);
if (cloudConnection) {
- qDebug() << "Best candidate Cloud connection:" << cloudConnection->url();
+ qCDebug(dcNymeaConnection()) << "Best candidate Cloud connection:" << cloudConnection->url();
connectInternal(cloudConnection);
} else {
- qDebug() << "No available Cloud connection to" << host->name();
+ qCDebug(dcNymeaConnection()) << "No available Cloud connection to" << host->name();
}
if (m_transportCandidates.isEmpty()) {
+ qCWarning(dcNymeaConnection()) << "No available bearers available for host:" << host->name() << host->uuid();
m_connectionStatus = ConnectionStatusNoBearerAvailable;
} else {
m_connectionStatus = ConnectionStatusConnecting;
@@ -483,12 +487,12 @@ void NymeaConnection::connectInternal(NymeaHost *host)
bool NymeaConnection::connectInternal(Connection *connection)
{
if (!m_transportFactories.contains(connection->url().scheme())) {
- qWarning() << "Cannot connect to urls of scheme" << connection->url().scheme() << "Supported schemes are" << m_transportFactories.keys();
+ qCCritical(dcNymeaConnection()) << "Cannot connect to urls of scheme" << connection->url().scheme() << "Supported schemes are" << m_transportFactories.keys();
return false;
}
if (m_transportCandidates.values().contains(connection)) {
- qDebug() << "Already have a connection (or connection attempt) for" << connection->url();
+ qCInfo(dcNymeaConnection()) << "Already have a connection (or connection attempt) for" << connection->url();
return false;
}
@@ -511,7 +515,7 @@ bool NymeaConnection::connectInternal(Connection *connection)
// }
m_transportCandidates.insert(newTransport, connection);
- qDebug() << "Connecting to:" << connection->url() << newTransport << m_transportCandidates.value(newTransport);
+ qCInfo(dcNymeaConnection()) << "Connecting to:" << connection->url() << newTransport << m_transportCandidates.value(newTransport);
return newTransport->connect(connection->url());
}
@@ -519,6 +523,7 @@ NymeaConnection::BearerType NymeaConnection::qBearerTypeToNymeaBearerType(QNetwo
{
switch (type) {
case QNetworkConfiguration::BearerUnknown:
+ // Unable to determine the connection type. Assume it's something we can establish any connection type on
return BearerTypeAll;
case QNetworkConfiguration::BearerEthernet:
return BearerTypeEthernet;
diff --git a/libnymea-app/libnymea-app-core.h b/libnymea-app/libnymea-app-core.h
index 8de2204d..903e9a5f 100644
--- a/libnymea-app/libnymea-app-core.h
+++ b/libnymea-app/libnymea-app-core.h
@@ -126,6 +126,7 @@
#include "zigbee/zigbeeadaptersproxy.h"
#include "zigbee/zigbeenetwork.h"
#include "zigbee/zigbeenetworks.h"
+#include "applogcontroller.h"
#include
@@ -158,6 +159,9 @@ void registerQmlTypes() {
qmlRegisterType(uri, 1, 0, "Engine");
+ qmlRegisterSingletonType("Nymea", 1, 0, "AppLogController", AppLogController::appLogControllerProvider);
+ qmlRegisterType("Nymea", 1, 0, "LogMessages");
+
qmlRegisterUncreatableType(uri, 1, 0, "ThingManager", "Can't create this in QML. Get it from the Engine.");
qmlRegisterUncreatableType(uri, 1, 0, "JsonRpcClient", "Can't create this in QML. Get it from the Engine.");
qmlRegisterUncreatableType(uri, 1, 0, "NymeaConnection", "Can't create this in QML. Get it from the Engine.");
diff --git a/libnymea-app/libnymea-app.pri b/libnymea-app/libnymea-app.pri
index 5b867965..5116640e 100644
--- a/libnymea-app/libnymea-app.pri
+++ b/libnymea-app/libnymea-app.pri
@@ -20,7 +20,9 @@ INCLUDEPATH += \
$$top_srcdir/QtZeroConf
SOURCES += \
- $$PWD/wifisetup/btwifisetup.cpp \
+ $${PWD}/logging.cpp \
+ $${PWD}/applogcontroller.cpp \
+ $${PWD}/wifisetup/btwifisetup.cpp \
$${PWD}/configuration/networkmanager.cpp \
$${PWD}/engine.cpp \
$${PWD}/models/barseriesadapter.cpp \
@@ -163,7 +165,9 @@ SOURCES += \
HEADERS += \
- $$PWD/wifisetup/btwifisetup.h \
+ $${PWD}/logging.h \
+ $${PWD}/applogcontroller.h \
+ $${PWD}/wifisetup/btwifisetup.h \
$${PWD}/configuration/networkmanager.h \
$${PWD}/engine.h \
$${PWD}/models/barseriesadapter.h \
diff --git a/libnymea-app/logging.cpp b/libnymea-app/logging.cpp
new file mode 100644
index 00000000..0d1b3e90
--- /dev/null
+++ b/libnymea-app/logging.cpp
@@ -0,0 +1,6 @@
+#include "logging.h"
+
+QStringList& nymeaLoggingCategories() {
+ static QStringList _nymeaLoggingCategories;
+ return _nymeaLoggingCategories;
+}
diff --git a/libnymea-app/logging.h b/libnymea-app/logging.h
new file mode 100644
index 00000000..988f7565
--- /dev/null
+++ b/libnymea-app/logging.h
@@ -0,0 +1,20 @@
+#ifndef LOGGING_H
+#define LOGGING_H
+
+#include
+
+QStringList& nymeaLoggingCategories();
+
+#define NYMEA_LOGGING_CATEGORY(name, string) \
+ class NymeaLoggingCategory##name: public QLoggingCategory { \
+ public: \
+ NymeaLoggingCategory##name(): QLoggingCategory(string) { nymeaLoggingCategories().append(string); } \
+ }; \
+ static NymeaLoggingCategory##name s_##name; \
+ const QLoggingCategory &name() \
+ { \
+ return s_##name; \
+ } \
+
+
+#endif // LOGGING_H
diff --git a/libnymea-app/thingmanager.cpp b/libnymea-app/thingmanager.cpp
index 4ceeced5..b73942a3 100644
--- a/libnymea-app/thingmanager.cpp
+++ b/libnymea-app/thingmanager.cpp
@@ -41,6 +41,9 @@
#include
#include
+#include "logging.h"
+NYMEA_LOGGING_CATEGORY(dcThingManager, "ThingManager")
+
ThingManager::ThingManager(JsonRpcClient* jsonclient, QObject *parent) :
JsonHandler(parent),
m_vendors(new Vendors(this)),
@@ -122,7 +125,9 @@ void ThingManager::notificationReceived(const QVariantMap &data)
if (notification == "Integrations.StateChanged") {
Thing *thing = m_things->getThing(data.value("params").toMap().value("thingId").toUuid());
if (!thing) {
- qWarning() << "Thing state change notification received for an unknown thing";
+ if (!m_fetchingData) {
+ qCWarning(dcThingManager()) << "Thing state change notification received for an unknown thing";
+ }
return;
}
QUuid stateTypeId = data.value("params").toMap().value("stateTypeId").toUuid();
@@ -188,7 +193,9 @@ void ThingManager::notificationReceived(const QVariantMap &data)
Thing *thing = m_things->getThing(thingId);
if (!thing) {
- qWarning() << "received an event from a thing we don't know..." << thingId << qUtf8Printable(QJsonDocument::fromVariant(data).toJson());
+ if (!m_fetchingData) {
+ qCWarning(dcThingManager()) << "received an event from a thing we don't know..." << thingId << qUtf8Printable(QJsonDocument::fromVariant(data).toJson());
+ }
return;
}
// qDebug() << "Event received" << thingId.toString() << eventTypeId.toString() << qUtf8Printable(QJsonDocument::fromVariant(event).toJson());
diff --git a/nymea-app/applogcontroller.cpp b/nymea-app/applogcontroller.cpp
deleted file mode 100644
index c842815e..00000000
--- a/nymea-app/applogcontroller.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-*
-* Copyright 2013 - 2020, nymea GmbH
-* Contact: contact@nymea.io
-*
-* This file is part of nymea.
-* This project including source code and documentation is protected by
-* copyright law, and remains the property of nymea GmbH. All rights, including
-* reproduction, publication, editing and translation, are reserved. The use of
-* this project is subject to the terms of a license agreement to be concluded
-* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
-* under https://nymea.io/license
-*
-* GNU General Public License Usage
-* Alternatively, this project may be redistributed and/or modified under the
-* terms of the GNU General Public License as published by the Free Software
-* Foundation, GNU version 3. This project is distributed in the hope that it
-* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-* Public License for more details.
-*
-* You should have received a copy of the GNU General Public License along with
-* this project. If not, see .
-*
-* For any further details and any questions please contact us under
-* contact@nymea.io or see our FAQ/Licensing Information on
-* https://nymea.io/license/faq
-*
-* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#include "applogcontroller.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-QtMessageHandler AppLogController::s_oldLogMessageHandler = nullptr;
-
-
-QObject *AppLogController::appLogControllerProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
-{
- Q_UNUSED(engine)
- Q_UNUSED(scriptEngine)
- return instance();
-}
-
-AppLogController *AppLogController::instance()
-{
- static AppLogController* thiz = nullptr;
- if (!thiz) {
- thiz = new AppLogController();
- }
- return thiz;
-}
-
-AppLogController::AppLogController(QObject *parent) : QAbstractListModel(parent)
-{
-
- QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
- QString fileName = path + "/nymea-app.log";
- m_logFile.setFileName(fileName);
-
- QByteArray oldContent;
- if (QFile::exists(fileName)) {
- if (QFile::exists(fileName + ".old")) {
- QFile::remove(fileName + ".old");
- }
- QFile::rename(fileName, fileName + ".old");
- QFile oldFile(fileName + ".old");
- if (oldFile.open(QFile::ReadOnly)) {
- oldFile.seek(qMax((long long)0, oldFile.size() - 1024 * 1024));
- oldContent = oldFile.readAll();
- oldFile.close();
-
- m_buffer.append(QString(oldContent).split('\n'));
- for (int i = 0; i < m_buffer.count(); i++) {
- m_types.append(TypeInfo);
- }
- m_types.append(TypeWarning);
- m_buffer.append("**** App restart ****");
-
- oldContent.append("\n\n**** App restart ****\n\n");
- }
- }
- QDir dir(path);
- if (!dir.exists()) {
- if (!dir.mkpath(path)) {
- qWarning() << "Cannot create cache location. Logging will not work.";
- return;
- }
- }
-
- if (!m_logFile.open(QFile::ReadWrite | QFile::Truncate)) {
- qWarning() << "Cannot open logfile for writing.";
- return;
- }
- qDebug() << "App log opened at" << fileName;
- m_logFile.write(oldContent);
-
-
- if (enabled()) {
- activate();
- }
-}
-
-bool AppLogController::canWriteLogs() const
-{
- return m_logFile.isOpen();
-}
-
-bool AppLogController::enabled() const
-{
- QSettings settings;
- return settings.value("AppLoggingEnabled", false).toBool();
-}
-
-void AppLogController::setEnabled(bool enabled)
-{
- if (enabled == this->enabled()) {
- return;
- }
-
- if (enabled) {
- if (!canWriteLogs()) {
- qWarning() << "Cannot write log file. Not enabling logging.";
- return;
- }
- activate();
- } else {
- deactivate();
- }
- QSettings settings;
- settings.setValue("AppLoggingEnabled", enabled);
-
- emit enabledChanged();
-
-}
-
-int AppLogController::rowCount(const QModelIndex &parent) const
-{
- Q_UNUSED(parent)
- return m_buffer.count();
-}
-
-QVariant AppLogController::data(const QModelIndex &index, int role) const
-{
- switch (role) {
- case RoleText:
- return m_buffer.at(index.row());
- case RoleType:
- return m_types.at(index.row());
- }
- return QVariant();
-}
-
-QHash AppLogController::roleNames() const
-{
- QHash roles;
- roles.insert(RoleText, "text");
- roles.insert(RoleType, "type");
- return roles;
-}
-
-void AppLogController::toClipboard()
-{
- m_logFile.seek(0);
- QByteArray completeLog = m_logFile.readAll();
- QGuiApplication::clipboard()->setText(completeLog);
-}
-
-QString AppLogController::logFile() const
-{
- return m_logFile.fileName();
-}
-
-void AppLogController::logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
-{
- s_oldLogMessageHandler(type, context, message);
- instance()->append(message, type == QtWarningMsg ? TypeWarning : TypeInfo);
-}
-
-void AppLogController::append(const QString &message, AppLogController::Type type)
-{
- QMutexLocker locker(&m_mutex);
- QString finalMessage = message + "\n";
- m_logFile.write(finalMessage.toUtf8());
- m_logFile.flush();
-
- beginInsertRows(QModelIndex(), m_buffer.count(), m_buffer.count());
- m_buffer.append(message);
- m_types.append(type);
- endInsertRows();
-
- int maxEntries = 1024;
- if (m_buffer.size() > maxEntries) {
- beginRemoveRows(QModelIndex(), 0, 0);
- m_buffer.removeFirst();
- m_types.removeFirst();
- endRemoveRows();
- }
-}
-
-void AppLogController::activate()
-{
- qDebug() << "Activating log file writing to" << m_logFile.fileName();
-
- s_oldLogMessageHandler = qInstallMessageHandler(&logMessageHandler);
-}
-
-void AppLogController::deactivate()
-{
- qInstallMessageHandler(s_oldLogMessageHandler);
- s_oldLogMessageHandler = nullptr;
-
-}
diff --git a/nymea-app/main.cpp b/nymea-app/main.cpp
index 6b271538..fd342d19 100644
--- a/nymea-app/main.cpp
+++ b/nymea-app/main.cpp
@@ -42,12 +42,15 @@
#include "stylecontroller.h"
#include "pushnotifications.h"
-#include "applogcontroller.h"
#include "ruletemplates/messages.h"
#include "nfchelper.h"
#include "nfcthingactionwriter.h"
#include "platformhelper.h"
+#include "logging.h"
+
+NYMEA_LOGGING_CATEGORY(dcApplication, "Application")
+NYMEA_LOGGING_CATEGORY(qml, "qml")
int main(int argc, char *argv[])
{
@@ -94,15 +97,15 @@ int main(int argc, char *argv[])
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
application.installTranslator(&qtTranslator);
- qDebug() << "nymea:app" << APP_VERSION << "running on" << QSysInfo::machineHostName() << QSysInfo::prettyProductName() << QSysInfo::productType() << QSysInfo::productVersion();
- qDebug() << "Locale info:" << QLocale() << QLocale().name() << QLocale().language() << QLocale().system();
+ qCInfo(dcApplication()) << "nymea:app" << APP_VERSION << "running on" << QSysInfo::machineHostName() << QSysInfo::prettyProductName() << QSysInfo::productType() << QSysInfo::productVersion();
+ qCInfo(dcApplication()) << "Locale info:" << QLocale() << QLocale().name() << QLocale().language() << QLocale().system();
QTranslator appTranslator;
bool translationResult = appTranslator.load("nymea-app-" + QLocale().name(), ":/translations/");
if (translationResult) {
- qDebug() << "Loaded translation for locale" << QLocale();
+ qCDebug(dcApplication()) << "Loaded translation for locale" << QLocale();
} else {
- qWarning() << "Failed to load translations for locale" << QLocale();
+ qCInfo(dcApplication()) << "Failed to load translations for locale" << QLocale();
}
application.installTranslator(&appTranslator);
@@ -116,7 +119,7 @@ int main(int argc, char *argv[])
#else
StyleController styleController(parser.value(defaultStyleOption));
if (parser.isSet(styleOption)) {
- qDebug() << "Setting style to" << parser.value(styleOption);
+ qCInfo(dcApplication()) << "Setting style to" << parser.value(styleOption);
styleController.lockToStyle(parser.value(styleOption));
}
#endif
@@ -128,7 +131,7 @@ int main(int argc, char *argv[])
QFontDatabase::addApplicationFont(fi.absoluteFilePath());
}
foreach (const QFileInfo &fi, QDir(":/styles/" + styleController.currentStyle() + "/fonts/").entryInfoList()) {
- qDebug() << "Adding style font:" << fi.absoluteFilePath();
+ qCDebug(dcApplication()) << "Adding style font:" << fi.absoluteFilePath();
QFontDatabase::addApplicationFont(fi.absoluteFilePath());
}
@@ -141,7 +144,6 @@ int main(int argc, char *argv[])
qmlRegisterType("Nymea", 1, 0, "NfcThingActionWriter");
qmlRegisterSingletonType("Nymea", 1, 0, "PushNotifications", PushNotifications::pushNotificationsProvider);
- qmlRegisterSingletonType("Nymea", 1, 0, "AppLogController", AppLogController::appLogControllerProvider);
qmlRegisterSingletonType(QUrl("qrc:///ui/utils/NymeaUtils.qml"), "Nymea", 1, 0, "NymeaUtils" );
engine->rootContext()->setContextProperty("appVersion", APP_VERSION);
diff --git a/nymea-app/nymea-app.pro b/nymea-app/nymea-app.pro
index 1fdd2c71..b5306293 100644
--- a/nymea-app/nymea-app.pro
+++ b/nymea-app/nymea-app.pro
@@ -24,7 +24,6 @@ HEADERS += \
pushnotifications.h \
platformhelper.h \
platformintegration/generic/platformhelpergeneric.h \
- applogcontroller.h \
ruletemplates/messages.h
SOURCES += main.cpp \
@@ -37,7 +36,6 @@ SOURCES += main.cpp \
pushnotifications.cpp \
platformhelper.cpp \
platformintegration/generic/platformhelpergeneric.cpp \
- applogcontroller.cpp
RESOURCES += resources.qrc \
ruletemplates.qrc \
diff --git a/nymea-app/platformhelper.cpp b/nymea-app/platformhelper.cpp
index ea76316b..584f4e2f 100644
--- a/nymea-app/platformhelper.cpp
+++ b/nymea-app/platformhelper.cpp
@@ -32,6 +32,8 @@
#include
#include
+#include
+#include
#if defined Q_OS_ANDROID
#include
@@ -44,6 +46,9 @@
#include "platformintegration/generic/platformhelpergeneric.h"
#endif
+#include "logging.h"
+NYMEA_LOGGING_CATEGORY(dcPlatformIntegration, "PlatformIntegration")
+
PlatformHelper* PlatformHelper::s_instance = nullptr;
PlatformHelper::PlatformHelper(QObject *parent) : QObject(parent)
@@ -183,6 +188,11 @@ QString PlatformHelper::fromClipBoard()
return QApplication::clipboard()->text();
}
+void PlatformHelper::shareFile(const QString &fileName)
+{
+ QDesktopServices::openUrl(QUrl(fileName));
+}
+
QObject *PlatformHelper::platformHelperProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
diff --git a/nymea-app/platformhelper.h b/nymea-app/platformhelper.h
index 72e18ccd..90bfc9bf 100644
--- a/nymea-app/platformhelper.h
+++ b/nymea-app/platformhelper.h
@@ -91,6 +91,8 @@ public:
Q_INVOKABLE virtual void toClipBoard(const QString &text);
Q_INVOKABLE virtual QString fromClipBoard();
+ Q_INVOKABLE virtual void shareFile(const QString &fileName);
+
static QObject *platformHelperProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
signals:
void permissionsRequestFinished();
diff --git a/nymea-app/platformintegration/android/java/io/guh/nymeaapp/NymeaAppActivity.java b/nymea-app/platformintegration/android/java/io/guh/nymeaapp/NymeaAppActivity.java
index f63a0463..92e24bf2 100644
--- a/nymea-app/platformintegration/android/java/io/guh/nymeaapp/NymeaAppActivity.java
+++ b/nymea-app/platformintegration/android/java/io/guh/nymeaapp/NymeaAppActivity.java
@@ -1,4 +1,7 @@
package io.guh.nymeaapp;
+
+import java.io.File;
+
import android.util.Log;
import android.content.Intent;
import android.content.Context;
@@ -7,6 +10,8 @@ import android.os.Build;
import android.telephony.TelephonyManager;
import android.provider.Settings.Secure;
import android.os.Vibrator;
+import android.net.Uri;
+import android.support.v4.content.FileProvider;
public class NymeaAppActivity extends org.qtproject.qt5.android.bindings.QtActivity
{
@@ -48,4 +53,17 @@ public class NymeaAppActivity extends org.qtproject.qt5.android.bindings.QtActiv
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(duration);
}
+
+ public void shareFile(String fileName) {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.setType("text/plain");
+ Uri uri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".fileprovider", new File(fileName));
+ sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ if (sendIntent.resolveActivity(getPackageManager()) != null) {
+ startActivity(sendIntent);
+ } else {
+ Log.d(TAG, "Intent not resolved");
+ }
+ }
}
diff --git a/nymea-app/platformintegration/android/platformhelperandroid.cpp b/nymea-app/platformintegration/android/platformhelperandroid.cpp
index 697f1cf4..e058da4c 100644
--- a/nymea-app/platformintegration/android/platformhelperandroid.cpp
+++ b/nymea-app/platformintegration/android/platformhelperandroid.cpp
@@ -175,7 +175,14 @@ void PlatformHelperAndroid::setBottomPanelColor(const QColor &color)
PlatformHelper::setBottomPanelColor(color);
if (QtAndroid::androidSdkVersion() < 21)
- return;
+ return;
+}
+
+void PlatformHelperAndroid::shareFile(const QString &fileName)
+{
+ QtAndroid::androidActivity().callMethod("shareFile", "(Ljava/lang/String;)V",
+ QAndroidJniObject::fromString(fileName).object()
+ );
}
void PlatformHelperAndroid::permissionRequestFinished(const QtAndroid::PermissionResultMap &result)
diff --git a/nymea-app/platformintegration/android/platformhelperandroid.h b/nymea-app/platformintegration/android/platformhelperandroid.h
index fed19a80..62980518 100644
--- a/nymea-app/platformintegration/android/platformhelperandroid.h
+++ b/nymea-app/platformintegration/android/platformhelperandroid.h
@@ -62,6 +62,8 @@ public:
void setTopPanelTheme(Theme theme);
void setBottomPanelColor(const QColor &color) override;
+ void shareFile(const QString &fileName) override;
+
private:
static void permissionRequestFinished(const QtAndroid::PermissionResultMap &);
};
diff --git a/nymea-app/platformintegration/generic/platformhelpergeneric.cpp b/nymea-app/platformintegration/generic/platformhelpergeneric.cpp
index 4bc2d306..93e7e717 100644
--- a/nymea-app/platformintegration/generic/platformhelpergeneric.cpp
+++ b/nymea-app/platformintegration/generic/platformhelpergeneric.cpp
@@ -30,6 +30,10 @@
#include "platformhelpergeneric.h"
+#include "logging.h"
+
+Q_DECLARE_LOGGING_CATEGORY(dcPlatformIntergration)
+
PlatformHelperGeneric::PlatformHelperGeneric(QObject *parent) : PlatformHelper(parent)
{
m_piHelper = new ScreenHelper(this);
diff --git a/nymea-app/platformintegration/generic/screenhelper.cpp b/nymea-app/platformintegration/generic/screenhelper.cpp
index 383160c8..2e99c0de 100644
--- a/nymea-app/platformintegration/generic/screenhelper.cpp
+++ b/nymea-app/platformintegration/generic/screenhelper.cpp
@@ -36,32 +36,35 @@
#include
#include
#include
+#include
+
+Q_DECLARE_LOGGING_CATEGORY(dcPlatformIntegration)
ScreenHelper::ScreenHelper(QObject *parent) : QObject(parent)
{
// Try generic backlight
QDir backlightDir("/sys/class/backlight");
foreach (const QFileInfo &fi, backlightDir.entryInfoList({"*_backlight"}, QDir::Dirs)) {
- qDebug() << "Checking backlight directory:" << fi.absoluteFilePath();
+ qCDebug(dcPlatformIntegration()) << "Checking backlight directory:" << fi.absoluteFilePath();
m_powerFile.setFileName(fi.absoluteFilePath() + "/bl_power");
m_brightnessFile.setFileName(fi.absoluteFilePath() + "/brightness");
if (!m_powerFile.open(QFile::ReadWrite | QFile::Text)) {
- qWarning() << "Cannot open" << m_powerFile.fileName() << "for writing";
+ qCDebug(dcPlatformIntegration()) << "Cannot open" << m_powerFile.fileName() << "for writing";
continue;
}
if (!m_brightnessFile.open(QFile::ReadWrite | QFile::Text)) {
- qWarning() << "Cannot open" << m_brightnessFile.fileName() << "for writing";
+ qCDebug(dcPlatformIntegration()) << "Cannot open" << m_brightnessFile.fileName() << "for writing";
continue;
}
QFile maxBrightnessFile(fi.absoluteFilePath() + "/max_brightness");
if (!maxBrightnessFile.open(QFile::ReadOnly)) {
- qWarning() << "Cannot open" << m_brightnessFile.fileName() << "for reading";
+ qCDebug(dcPlatformIntegration()) << "Cannot open" << m_brightnessFile.fileName() << "for reading";
continue;
}
bool ok;
m_maxBrightness = maxBrightnessFile.readLine().trimmed().toInt(&ok);
if (!ok) {
- qWarning() << "Error reading max brightness value from" << maxBrightnessFile.fileName();
+ qCDebug(dcPlatformIntegration()) << "Error reading max brightness value from" << maxBrightnessFile.fileName();
m_maxBrightness = -1;
continue;
}
@@ -70,15 +73,15 @@ ScreenHelper::ScreenHelper(QObject *parent) : QObject(parent)
}
if (!m_powerFile.isOpen() || !m_brightnessFile.isOpen()) {
- qWarning() << "No backlight support on this platform";
+ qCInfo(dcPlatformIntegration()) << "No backlight support on this platform";
return;
}
- qDebug() << "Backlight control enabled on" << m_powerFile.fileName();
+ qCInfo(dcPlatformIntegration()) << "Backlight control enabled on" << m_powerFile.fileName();
bool ok;
int currentBrightness = m_brightnessFile.readLine().trimmed().toInt(&ok);
m_currentBrightness = currentBrightness * 100 / m_maxBrightness;
- qDebug().nospace() << "Brigness: Absolute: " << currentBrightness << "/" << m_maxBrightness << " Percentage:" << m_currentBrightness;
+ qCInfo(dcPlatformIntegration()).nospace() << "Brigness: Absolute: " << currentBrightness << "/" << m_maxBrightness << " Percentage:" << m_currentBrightness;
screenOn();
@@ -190,20 +193,20 @@ bool ScreenHelper::eventFilter(QObject *watched, QEvent *event)
void ScreenHelper::screenOn()
{
- qDebug() << "Turning screen on";
+ qCInfo(dcPlatformIntegration()) << "Turning screen on";
int ret = m_powerFile.write("0\n");
m_powerFile.flush();
if (ret < 0) {
- qWarning() << "Failed to power on screen";
+ qCWarning(dcPlatformIntegration()) << "Failed to power on screen";
}
}
void ScreenHelper::screenOff()
{
- qDebug() << "Turning screen off";
+ qCInfo(dcPlatformIntegration()) << "Turning screen off";
int ret = m_powerFile.write("1\n");
m_powerFile.flush();
if (ret < 0) {
- qWarning() << "Failed to power off screen";
+ qCWarning(dcPlatformIntegration()) << "Failed to power off screen";
}
}
diff --git a/nymea-app/platformintegration/ios/platformhelperios.h b/nymea-app/platformintegration/ios/platformhelperios.h
index 05f30dab..ec431e47 100644
--- a/nymea-app/platformintegration/ios/platformhelperios.h
+++ b/nymea-app/platformintegration/ios/platformhelperios.h
@@ -57,6 +57,8 @@ public:
void setTopPanelColor(const QColor &color) override;
void setBottomPanelColor(const QColor &color) override;
+ void shareFile(const QString &fileName) override;
+
private:
// defined in platformhelperios.mm
QString readKeyChainEntry(const QString &service, const QString &key);
diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc
index 57b89867..a95d5b6c 100644
--- a/nymea-app/resources.qrc
+++ b/nymea-app/resources.qrc
@@ -232,5 +232,6 @@
ui/customviews/ThermostatController.qml
ui/devicepages/ThermostatDevicePage.qml
ui/components/BigThingTile.qml
+ ui/appsettings/LoggingCategories.qml
diff --git a/nymea-app/ui/MainMenu.qml b/nymea-app/ui/MainMenu.qml
index fba2901a..31d3f2fd 100644
--- a/nymea-app/ui/MainMenu.qml
+++ b/nymea-app/ui/MainMenu.qml
@@ -53,7 +53,7 @@ Drawer {
}
Label {
Layout.fillWidth: true
- text: root.currentEngine.jsonRpcClient.currentConnection.url
+ text: root.currentEngine.jsonRpcClient.currentConnection ? root.currentEngine.jsonRpcClient.currentConnection.url : ""
font.pixelSize: app.extraSmallFont
enabled: false
}
diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml
index 1e01358b..e5be9b25 100644
--- a/nymea-app/ui/Nymea.qml
+++ b/nymea-app/ui/Nymea.qml
@@ -110,6 +110,12 @@ ApplicationWindow {
value: settings.units === "metric" ? Types.UnitSystemMetric : Types.UnitSystemImperial
}
+ Binding {
+ target: AWSClient
+ property: "config"
+ value: "cloudEnvironment" in app ? app.cloudEnvironment : settings.cloudEnvironment
+ }
+
property alias mainMenu: m
MainMenu {
id: m
diff --git a/nymea-app/ui/RootItem.qml b/nymea-app/ui/RootItem.qml
index 1c6c2522..c2deef0b 100644
--- a/nymea-app/ui/RootItem.qml
+++ b/nymea-app/ui/RootItem.qml
@@ -157,12 +157,6 @@ Item {
property int connectionTabIndex: index
// onConnectionTabIndexChanged: tabSettings.lastConnectedHost = engine.jsonRpcClient.url
- Binding {
- target: AWSClient
- property: "config"
- value: "cloudEnvironment" in app ? app.cloudEnvironment : settings.cloudEnvironment
- }
-
Binding {
target: nymeaDiscovery
property: "discovering"
@@ -469,6 +463,7 @@ Item {
}
RowLayout {
+ Layout.fillWidth: true
visible: settings.showConnectionTabs
spacing: 0
@@ -477,6 +472,7 @@ Item {
Layout.fillWidth: true
Material.elevation: 2
position: TabBar.Footer
+ property int tabWidth: Math.max(150, root.width / tabModel.count)
Repeater {
model: tabModel.count
@@ -486,7 +482,7 @@ Item {
property var engine: mainRepeater.itemAt(index)._engine
property string serverName: engine.nymeaConfiguration.serverName
Material.elevation: index
- width: Math.max(150, tabbar.width / tabModel.count)
+ width: tabbar.tabWidth
Rectangle {
anchors.fill: parent
diff --git a/nymea-app/ui/StyleBase.qml b/nymea-app/ui/StyleBase.qml
index ef40cfb4..9bbb7d3d 100644
--- a/nymea-app/ui/StyleBase.qml
+++ b/nymea-app/ui/StyleBase.qml
@@ -17,10 +17,39 @@ Item {
property color tileOverlayIconColor: iconColor
property int tileRadius: 6
+ readonly property int smallMargins: 8
+ readonly property int margins: 16
+
+ readonly property int smallDelegateHeight: 50
+ readonly property int delegateHeight: 60
// Note: Font files need to be provided in a "fonts" folder in the style
property string fontFamily: "Ubuntu"
+
+ // Fonts
+ readonly property font extraSmallFont: Qt.font({
+ family: "Ubuntu",
+ pixelSize: 10
+ })
+ readonly property font smallFont: Qt.font({
+ family: "Ubuntu",
+ pixelSize: 13
+ })
+ readonly property font font: Qt.font({
+ family: "Ubuntu",
+ pixelSize: 16
+ })
+ readonly property font largeFont: Qt.font({
+ family: "Ubuntu",
+ pixelSize: 20
+ })
+ readonly property font hugeFont: Qt.font({
+ family: "Ubuntu",
+ pixelSize: 40
+ })
+
+
// Icon/graph colors for various interfaces
property var interfaceColors: {
"temperaturesensor": "red",
diff --git a/nymea-app/ui/appsettings/AppLogPage.qml b/nymea-app/ui/appsettings/AppLogPage.qml
index 9a3e4aaa..023b3f60 100644
--- a/nymea-app/ui/appsettings/AppLogPage.qml
+++ b/nymea-app/ui/appsettings/AppLogPage.qml
@@ -36,12 +36,15 @@ import "../components"
Page {
header: NymeaHeader {
- text: qsTr("App log")
+ text: qsTr("Application logs")
backButtonVisible: true
onBackPressed: pageStack.pop()
HeaderButton {
- imageSource: "../images/edit-copy.svg"
- onClicked: AppLogController.toClipboard()
+ imageSource: "../images/state-out.svg"
+ onClicked: {
+ var exportedFile = AppLogController.exportLogs()
+ PlatformHelper.shareFile(exportedFile)
+ }
}
}
@@ -51,14 +54,29 @@ Page {
ScrollBar.vertical: ScrollBar {}
- model: AppLogController
- delegate: Text {
+ model: LogMessages {
+
+ }
+
+ delegate: Label {
width: listView.width
maximumLineCount: 2
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
text: model.text
- color: model.type === AppLogController.TypeWarning ? "red" : Style.foregroundColor
- font.pixelSize: app.smallFont
+ color: {
+ switch (model.level) {
+ case AppLogController.LogLevelCritical:
+ return "red";
+ case AppLogController.LogLevelWarning:
+ return "orange";
+ case AppLogController.LogLevelInfo:
+ return Style.foregroundColor;
+ case AppLogController.LogLevelDebug:
+ return Qt.tint(Style.foregroundColor, Qt.rgba(Style.backgroundColor.r, Style.backgroundColor.g, Style.backgroundColor.b, .4))
+
+ }
+ }
+ font: Style.smallFont
}
}
}
diff --git a/nymea-app/ui/appsettings/DeveloperOptionsPage.qml b/nymea-app/ui/appsettings/DeveloperOptionsPage.qml
index 6e56c392..fe8571e2 100644
--- a/nymea-app/ui/appsettings/DeveloperOptionsPage.qml
+++ b/nymea-app/ui/appsettings/DeveloperOptionsPage.qml
@@ -42,21 +42,25 @@ SettingsPageBase {
text: qsTr("Logging")
}
- CheckDelegate {
- text: qsTr("Enable app logging")
- enabled: AppLogController.canWriteLogs
+ SwitchDelegate {
+ text: qsTr("Application logs enabled")
checked: AppLogController.enabled
- onCheckedChanged: AppLogController.enabled = checked;
+ onCheckedChanged: AppLogController.enabled = checked
Layout.fillWidth: true
}
- NymeaSwipeDelegate {
+ NymeaItemDelegate {
Layout.fillWidth: true
- text: qsTr("View log")
+ text: qsTr("View live log")
onClicked: pageStack.push(Qt.resolvedUrl("../appsettings/AppLogPage.qml"))
- enabled: AppLogController.enabled
+ visible: AppLogController.enabled
}
+ NymeaItemDelegate {
+ Layout.fillWidth: true
+ text: qsTr("Configure logging categories")
+ onClicked: pageStack.push(Qt.resolvedUrl("../appsettings/LoggingCategories.qml"))
+ }
SettingsPageSectionHeader {
text: qsTr("Advanced options")
diff --git a/nymea-app/ui/appsettings/LoggingCategories.qml b/nymea-app/ui/appsettings/LoggingCategories.qml
new file mode 100644
index 00000000..549f218d
--- /dev/null
+++ b/nymea-app/ui/appsettings/LoggingCategories.qml
@@ -0,0 +1,130 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU General Public License as published by the Free Software
+* Foundation, GNU version 3. This project is distributed in the hope that it
+* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+* Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along with
+* this project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.3
+import Nymea 1.0
+import "../components"
+
+SettingsPageBase {
+ header: NymeaHeader {
+ text: qsTr("Logging categories")
+ backButtonVisible: true
+ onBackPressed: pageStack.pop()
+ }
+
+ RowLayout {
+ Layout.margins: Style.margins
+ Item {
+ Layout.fillWidth: true
+ }
+ Label {
+ Layout.preferredWidth: Style.smallDelegateHeight
+ horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Critical")
+ elide: Text.ElideRight
+ font: Style.smallFont
+ }
+ Label {
+ Layout.preferredWidth: Style.smallDelegateHeight
+ horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Warning")
+ elide: Text.ElideRight
+ font: Style.smallFont
+ }
+ Label {
+ Layout.preferredWidth: Style.smallDelegateHeight
+ horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Info")
+ elide: Text.ElideRight
+ font: Style.smallFont
+ }
+ Label {
+ Layout.preferredWidth: Style.smallDelegateHeight
+ horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Debug")
+ elide: Text.ElideRight
+ font: Style.smallFont
+ }
+ }
+ ThinDivider {}
+
+ Repeater {
+ model: AppLogController.loggingCategories
+ delegate: ItemDelegate {
+ Layout.fillWidth: true
+ Layout.preferredHeight: Style.smallDelegateHeight
+ contentItem: RowLayout {
+ height: parent.height
+ Label {
+ Layout.fillWidth: true
+ text: model.name
+ elide: Text.ElideRight
+ }
+ RadioButton {
+ Layout.fillHeight: true
+ Layout.preferredWidth: Style.smallDelegateHeight
+ checked: model.logLevel === AppLogController.LogLevelCritical
+ onClicked: AppLogController.setLogLevel(model.name, AppLogController.LogLevelCritical)
+ }
+ RadioButton {
+ Layout.fillHeight: true
+ Layout.preferredWidth: Style.smallDelegateHeight
+ checked: model.logLevel === AppLogController.LogLevelWarning
+ onClicked: AppLogController.setLogLevel(model.name, AppLogController.LogLevelWarning)
+ }
+ RadioButton {
+ Layout.fillHeight: true
+ Layout.preferredWidth: Style.smallDelegateHeight
+ checked: model.logLevel === AppLogController.LogLevelInfo
+ onClicked: AppLogController.setLogLevel(model.name, AppLogController.LogLevelInfo)
+ }
+ RadioButton {
+ Layout.fillHeight: true
+ Layout.preferredWidth: Style.smallDelegateHeight
+ checked: model.logLevel === AppLogController.LogLevelDebug
+ onClicked: AppLogController.setLogLevel(model.name, AppLogController.LogLevelDebug)
+ }
+
+// Slider {
+// from: 0
+// to: 3
+// stepSize: 1
+// Layout.preferredWidth: 200
+// value: model.logLevel
+// onMoved: {
+// AppLogController.setLogLevel(model.name, value)
+// }
+// }
+ }
+ }
+ }
+}
diff --git a/nymea-app/ui/components/NymeaItemDelegate.qml b/nymea-app/ui/components/NymeaItemDelegate.qml
index 555d55d3..fbac58d1 100644
--- a/nymea-app/ui/components/NymeaItemDelegate.qml
+++ b/nymea-app/ui/components/NymeaItemDelegate.qml
@@ -41,7 +41,6 @@ import Nymea 1.0
ItemDelegate {
id: root
- implicitWidth: 200
property string subText
property bool progressive: true
diff --git a/nymea-app/ui/delegates/InterfaceTile.qml b/nymea-app/ui/delegates/InterfaceTile.qml
index ce09a64b..53e858e4 100644
--- a/nymea-app/ui/delegates/InterfaceTile.qml
+++ b/nymea-app/ui/delegates/InterfaceTile.qml
@@ -158,6 +158,10 @@ MainPageTile {
rightMargin: app.margins / 2
}
sourceComponent: {
+ if (!root.iface) {
+ return ""
+ }
+
switch (iface.name) {
case "sensor":
case "weather":
diff --git a/nymea-app/ui/mainviews/EnergyView.qml b/nymea-app/ui/mainviews/EnergyView.qml
index 29fcbe9e..013a5439 100644
--- a/nymea-app/ui/mainviews/EnergyView.qml
+++ b/nymea-app/ui/mainviews/EnergyView.qml
@@ -182,9 +182,9 @@ MainViewBase {
ValueAxis {
id: yAxis
- readonly property XYSeriesAdapter adapter: consumersRepeater.itemAt(consumersRepeater.count - 1).adapter;
- max: Math.ceil(Math.max(adapter.maxValue * 0.95, adapter.maxValue * 1.05))
- min: Math.floor(Math.min(adapter.minValue * 0.95, adapter.minValue * 1.05))
+ readonly property XYSeriesAdapter adapter: consumersRepeater.count > 0 ? consumersRepeater.itemAt(consumersRepeater.count - 1).adapter : null
+ max: adapter ? Math.ceil(Math.max(adapter.maxValue * 0.95, adapter.maxValue * 1.05)) : 1
+ min: adapter ? Math.floor(Math.min(adapter.minValue * 0.95, adapter.minValue * 1.05)) : 0
// This seems to crash occationally
// onMinChanged: applyNiceNumbers();
// onMaxChanged: applyNiceNumbers();
diff --git a/nymea-app/ui/utils/NymeaUtils.qml b/nymea-app/ui/utils/NymeaUtils.qml
index a2a8d033..bea9988f 100644
--- a/nymea-app/ui/utils/NymeaUtils.qml
+++ b/nymea-app/ui/utils/NymeaUtils.qml
@@ -1,6 +1,5 @@
pragma Singleton
import QtQuick 2.9
-import Nymea 1.0
Item {
id: root
diff --git a/packaging/android/AndroidManifest.xml b/packaging/android/AndroidManifest.xml
index 5fea09f0..75bb562f 100644
--- a/packaging/android/AndroidManifest.xml
+++ b/packaging/android/AndroidManifest.xml
@@ -142,6 +142,16 @@
+
+
+
+
diff --git a/packaging/android/res/xml/file_paths.xml b/packaging/android/res/xml/file_paths.xml
new file mode 100644
index 00000000..ed6c087b
--- /dev/null
+++ b/packaging/android/res/xml/file_paths.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/packaging/ios/platformhelperios.mm b/packaging/ios/platformhelperios.mm
index 1c851f48..22d6fb6a 100644
--- a/packaging/ios/platformhelperios.mm
+++ b/packaging/ios/platformhelperios.mm
@@ -128,3 +128,11 @@ void PlatformHelperIOS::setBottomPanelColorInternal(const QColor &color)
app.windows.firstObject.backgroundColor = [UIColor colorWithRed:color.redF() green:color.greenF() blue:color.blueF() alpha:color.alphaF()];
}
+void PlatformHelperIOS::shareFile(const QString &fileName)
+{
+ UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:@[[NSURL fileURLWithPath:fileName.toNSString()]] applicationActivities:nil];
+ UIViewController *qtController = [[UIApplication sharedApplication].keyWindow rootViewController];
+ [qtController presentViewController:activityController animated:YES completion:nil];
+}
+
+