Move AppLogController into libnymea-app
This commit is contained in:
parent
f71c1fc219
commit
f36b36f8da
380
libnymea-app/applogcontroller.cpp
Normal file
380
libnymea-app/applogcontroller.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <QStandardPaths>
|
||||
#include <QDebug>
|
||||
#include <QSettings>
|
||||
#include <QGuiApplication>
|
||||
#include <QDir>
|
||||
#include <QMutexLocker>
|
||||
|
||||
#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<LogLevel>(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<LogLevel, QString> 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<QString, AppLogController::LogLevel> map = {
|
||||
{"C", AppLogController::LogLevelCritical},
|
||||
{"W", AppLogController::LogLevelWarning},
|
||||
{"I", AppLogController::LogLevelInfo},
|
||||
{"D", AppLogController::LogLevelDebug}
|
||||
};
|
||||
while (!f.atEnd()) {
|
||||
QByteArray line = f.readLine().trimmed();
|
||||
QList<QByteArray> 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<int, QByteArray> LogMessages::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> 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<int, QByteArray> LoggingCategories::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles.insert(RoleName, "name");
|
||||
roles.insert(RoleLevel, "logLevel");
|
||||
return roles;
|
||||
}
|
||||
|
||||
@ -37,60 +37,120 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QMutex>
|
||||
|
||||
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<int, QByteArray> 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<QString, LogLevel> m_logLevels;
|
||||
|
||||
QFile m_logFile;
|
||||
QStringList m_buffer;
|
||||
QList<Type> 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<int, QByteArray> roleNames() const override;
|
||||
|
||||
void append(const QString &category, const QString &message, AppLogController::LogLevel level);
|
||||
|
||||
private:
|
||||
QList<LogMessage> 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<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
AppLogController *m_controller = nullptr;
|
||||
};
|
||||
|
||||
#endif // APPLOGCONTROLLER_H
|
||||
@ -41,9 +41,12 @@
|
||||
#include <QPointer>
|
||||
|
||||
#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<QByteArray> 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::function<void(const Q
|
||||
request.setRawHeader("X-api-deviceId", fixedUuid.toUtf8());
|
||||
request.setRawHeader("X-api-serialId", "69696969");
|
||||
QNetworkReply *reply = m_nam->get(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<void (bool)> 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<QUuid> 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);
|
||||
|
||||
@ -35,6 +35,10 @@
|
||||
#include <QXmlStreamReader>
|
||||
#include <QNetworkInterface>
|
||||
|
||||
#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<QSslError> &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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,9 @@
|
||||
#include <QUuid>
|
||||
|
||||
#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
|
||||
|
||||
@ -44,6 +44,9 @@
|
||||
#include <QGuiApplication>
|
||||
|
||||
#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*>(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<QSslError> &errors)
|
||||
{
|
||||
NymeaTransportInterface *transport = qobject_cast<NymeaTransportInterface*>(sender());
|
||||
|
||||
qDebug() << "SSL errors for url:" << transport->url();
|
||||
qCDebug(dcNymeaConnection()) << "SSL errors for url:" << transport->url();
|
||||
QList<QSslError> 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<NymeaTransportInterface*>(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<NymeaTransportInterface*>(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<QNetworkConfiguration> 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<QNetworkConfiguration> 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;
|
||||
|
||||
@ -126,6 +126,7 @@
|
||||
#include "zigbee/zigbeeadaptersproxy.h"
|
||||
#include "zigbee/zigbeenetwork.h"
|
||||
#include "zigbee/zigbeenetworks.h"
|
||||
#include "applogcontroller.h"
|
||||
|
||||
#include <QtQml/qqml.h>
|
||||
|
||||
@ -158,6 +159,9 @@ void registerQmlTypes() {
|
||||
|
||||
qmlRegisterType<Engine>(uri, 1, 0, "Engine");
|
||||
|
||||
qmlRegisterSingletonType<AppLogController>("Nymea", 1, 0, "AppLogController", AppLogController::appLogControllerProvider);
|
||||
qmlRegisterType<LogMessages>("Nymea", 1, 0, "LogMessages");
|
||||
|
||||
qmlRegisterUncreatableType<ThingManager>(uri, 1, 0, "ThingManager", "Can't create this in QML. Get it from the Engine.");
|
||||
qmlRegisterUncreatableType<JsonRpcClient>(uri, 1, 0, "JsonRpcClient", "Can't create this in QML. Get it from the Engine.");
|
||||
qmlRegisterUncreatableType<NymeaConnection>(uri, 1, 0, "NymeaConnection", "Can't create this in QML. Get it from the Engine.");
|
||||
|
||||
@ -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 \
|
||||
|
||||
6
libnymea-app/logging.cpp
Normal file
6
libnymea-app/logging.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "logging.h"
|
||||
|
||||
QStringList& nymeaLoggingCategories() {
|
||||
static QStringList _nymeaLoggingCategories;
|
||||
return _nymeaLoggingCategories;
|
||||
}
|
||||
20
libnymea-app/logging.h
Normal file
20
libnymea-app/logging.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef LOGGING_H
|
||||
#define LOGGING_H
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
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
|
||||
@ -41,6 +41,9 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#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());
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <QStandardPaths>
|
||||
#include <QDebug>
|
||||
#include <QSettings>
|
||||
#include <QClipboard>
|
||||
#include <QGuiApplication>
|
||||
#include <QDir>
|
||||
#include <QMutexLocker>
|
||||
|
||||
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<int, QByteArray> AppLogController::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> 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;
|
||||
|
||||
}
|
||||
@ -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<NfcThingActionWriter>("Nymea", 1, 0, "NfcThingActionWriter");
|
||||
|
||||
qmlRegisterSingletonType<PushNotifications>("Nymea", 1, 0, "PushNotifications", PushNotifications::pushNotificationsProvider);
|
||||
qmlRegisterSingletonType<AppLogController>("Nymea", 1, 0, "AppLogController", AppLogController::appLogControllerProvider);
|
||||
qmlRegisterSingletonType(QUrl("qrc:///ui/utils/NymeaUtils.qml"), "Nymea", 1, 0, "NymeaUtils" );
|
||||
|
||||
engine->rootContext()->setContextProperty("appVersion", APP_VERSION);
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -32,6 +32,8 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
|
||||
#if defined Q_OS_ANDROID
|
||||
#include <QtAndroidExtras/QtAndroid>
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<void>("shareFile", "(Ljava/lang/String;)V",
|
||||
QAndroidJniObject::fromString(fileName).object<jstring>()
|
||||
);
|
||||
}
|
||||
|
||||
void PlatformHelperAndroid::permissionRequestFinished(const QtAndroid::PermissionResultMap &result)
|
||||
|
||||
@ -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 &);
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -36,32 +36,35 @@
|
||||
#include <QSettings>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -232,5 +232,6 @@
|
||||
<file>ui/customviews/ThermostatController.qml</file>
|
||||
<file>ui/devicepages/ThermostatDevicePage.qml</file>
|
||||
<file>ui/components/BigThingTile.qml</file>
|
||||
<file>ui/appsettings/LoggingCategories.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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")
|
||||
|
||||
130
nymea-app/ui/appsettings/LoggingCategories.qml
Normal file
130
nymea-app/ui/appsettings/LoggingCategories.qml
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,7 +41,6 @@ import Nymea 1.0
|
||||
|
||||
ItemDelegate {
|
||||
id: root
|
||||
implicitWidth: 200
|
||||
|
||||
property string subText
|
||||
property bool progressive: true
|
||||
|
||||
@ -158,6 +158,10 @@ MainPageTile {
|
||||
rightMargin: app.margins / 2
|
||||
}
|
||||
sourceComponent: {
|
||||
if (!root.iface) {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch (iface.name) {
|
||||
case "sensor":
|
||||
case "weather":
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
pragma Singleton
|
||||
import QtQuick 2.9
|
||||
import Nymea 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
@ -142,6 +142,16 @@
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="io.guh.nymeaapp.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
|
||||
|
||||
3
packaging/android/res/xml/file_paths.xml
Normal file
3
packaging/android/res/xml/file_paths.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<cache-path name="cache" path="." />
|
||||
</paths>
|
||||
@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user