it's working!
This commit is contained in:
parent
17ea41dd9b
commit
080a47932a
@ -22,11 +22,14 @@ AWSClient::AWSClient(QObject *parent) : QObject(parent)
|
||||
QSettings settings;
|
||||
settings.beginGroup("cloud");
|
||||
m_username = settings.value("username").toString();
|
||||
m_password = settings.value("password").toString();
|
||||
m_accessToken = settings.value("accessToken").toByteArray();
|
||||
m_accessTokenExpiry = settings.value("accessTokenExpiry").toDateTime();
|
||||
m_idToken = settings.value("idToken").toByteArray();
|
||||
m_refreshToken = settings.value("refreshToken").toByteArray();
|
||||
|
||||
m_identityId = settings.value("identityId").toByteArray();
|
||||
|
||||
m_accessKeyId = settings.value("accessKeyId").toByteArray();
|
||||
m_secretKey = settings.value("secretKey").toByteArray();
|
||||
m_sessionToken = settings.value("sessionToken").toByteArray();
|
||||
@ -38,6 +41,11 @@ bool AWSClient::isLoggedIn() const
|
||||
return !m_username.isEmpty() && !m_password.isEmpty();
|
||||
}
|
||||
|
||||
QString AWSClient::username() const
|
||||
{
|
||||
return m_username;
|
||||
}
|
||||
|
||||
void AWSClient::login(const QString &username, const QString &password)
|
||||
{
|
||||
m_username = username;
|
||||
@ -47,12 +55,6 @@ void AWSClient::login(const QString &username, const QString &password)
|
||||
// See: https://forums.aws.amazon.com/thread.jspa?threadID=287978
|
||||
m_password = password;
|
||||
|
||||
QSettings settings;
|
||||
settings.remove("cloud");
|
||||
settings.beginGroup("cloud");
|
||||
settings.setValue("username", username);
|
||||
settings.setValue("password", password);
|
||||
|
||||
QUrl url("https://cognito-idp.eu-west-1.amazonaws.com/");
|
||||
|
||||
QUrlQuery query;
|
||||
@ -85,6 +87,8 @@ void AWSClient::login(const QString &username, const QString &password)
|
||||
reply->deleteLater();
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Error logging in to aws:" << reply->error() << reply->errorString();
|
||||
m_username.clear();
|
||||
m_password.clear();
|
||||
return;
|
||||
}
|
||||
QByteArray data = reply->readAll();
|
||||
@ -92,6 +96,8 @@ void AWSClient::login(const QString &username, const QString &password)
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Failed to parse AWS login response" << error.errorString();
|
||||
m_username.clear();
|
||||
m_password.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -102,7 +108,11 @@ void AWSClient::login(const QString &username, const QString &password)
|
||||
m_refreshToken = authenticationResult.value("RefreshToken").toByteArray();
|
||||
|
||||
QSettings settings;
|
||||
settings.remove("cloud");
|
||||
|
||||
settings.beginGroup("cloud");
|
||||
settings.setValue("username", m_username);
|
||||
settings.setValue("password", m_password);
|
||||
settings.setValue("accessToken", m_accessToken);
|
||||
settings.setValue("accessTokenExpiry", m_accessTokenExpiry);
|
||||
settings.setValue("idToken", m_idToken);
|
||||
@ -116,6 +126,15 @@ void AWSClient::login(const QString &username, const QString &password)
|
||||
});
|
||||
}
|
||||
|
||||
void AWSClient::logout()
|
||||
{
|
||||
m_username.clear();
|
||||
m_password.clear();
|
||||
QSettings settings;
|
||||
settings.remove("cloud");
|
||||
emit isLoggedInChanged();
|
||||
}
|
||||
|
||||
void AWSClient::getId()
|
||||
{
|
||||
QUrl url("https://cognito-identity.eu-west-1.amazonaws.com/");
|
||||
@ -154,14 +173,22 @@ void AWSClient::getId()
|
||||
qWarning() << "Error parsing json reply for GetId" << error.errorString();
|
||||
return;
|
||||
}
|
||||
QByteArray identityId = jsonDoc.toVariant().toMap().value("IdentityId").toByteArray();
|
||||
m_identityId = jsonDoc.toVariant().toMap().value("IdentityId").toByteArray();
|
||||
QSettings settings;
|
||||
settings.beginGroup("cloud");
|
||||
settings.setValue("identityId", m_identityId);
|
||||
|
||||
qDebug() << "Received cognito identity id" << identityId;
|
||||
getCredentialsForIdentity(identityId);
|
||||
qDebug() << "Received cognito identity id" << m_identityId;
|
||||
getCredentialsForIdentity(m_identityId);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray AWSClient::idToken() const
|
||||
{
|
||||
return m_idToken;
|
||||
}
|
||||
|
||||
void AWSClient::getCredentialsForIdentity(const QString &identityId)
|
||||
{
|
||||
QUrl url("https://cognito-identity.eu-west-1.amazonaws.com/");
|
||||
@ -225,42 +252,34 @@ void AWSClient::getCredentialsForIdentity(const QString &identityId)
|
||||
|
||||
while (!m_callQueue.isEmpty()) {
|
||||
QueuedCall qc = m_callQueue.takeFirst();
|
||||
switch (qc.args.count()) {
|
||||
case 0:
|
||||
QMetaObject::invokeMethod(this, qc.method.toUtf8().data());
|
||||
break;
|
||||
case 1:
|
||||
QMetaObject::invokeMethod(this, qc.method.toUtf8().data(), Q_ARG(QString, qc.args.first()));
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT_X(false, "AWSClient", "Call queue handling does not yet support multiple arguments");
|
||||
break;
|
||||
if (qc.method == "fetchDevices") {
|
||||
fetchDevices();
|
||||
} else if (qc.method == "postToMQTT") {
|
||||
postToMQTT(qc.boxId, qc.callback);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool AWSClient::tokenExpired() const
|
||||
bool AWSClient::tokensExpired() const
|
||||
{
|
||||
qDebug() << "access token expirty:" << m_accessTokenExpiry.toString() << "session token expiry" << m_sessionTokenExpiry.toString() << "current:" << QDateTime::currentDateTime().toString();
|
||||
qDebug() << "access token expired:" << (m_accessTokenExpiry.addSecs(-10) < QDateTime::currentDateTime());
|
||||
return (m_accessTokenExpiry.addSecs(-10) < QDateTime::currentDateTime()) || (m_sessionTokenExpiry.addSecs(-10) < QDateTime::currentDateTime());
|
||||
}
|
||||
|
||||
void AWSClient::postToMQTT(const QString &token)
|
||||
bool AWSClient::postToMQTT(const QString &boxId, std::function<void(bool)> callback)
|
||||
{
|
||||
if (!isLoggedIn()) {
|
||||
qWarning() << "Cannot post to MQTT. Not logged in to AWS";
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (tokenExpired()) {
|
||||
qDebug() << "Cannot post to MQTT. Need to refresh the token first";
|
||||
if (tokensExpired()) {
|
||||
qDebug() << "Cannot post to MQTT. Need to refresh the tokens first";
|
||||
refreshAccessToken();
|
||||
m_callQueue.append(QueuedCall("postToMQTT", token));
|
||||
return;
|
||||
}
|
||||
m_callQueue.append(QueuedCall("postToMQTT", boxId, callback));
|
||||
return true; // So far it looks we're doing ok... let's return true
|
||||
}
|
||||
QString host = "a2addxakg5juii.iot.eu-west-1.amazonaws.com";
|
||||
QString topic = "850593e9-f2ab-4e89-913a-16f848d48867/eu-west-1:88c8b0f1-3f26-46cb-81f3-ccc37dcb543a/proxy";
|
||||
QString topic = QString("%1/%2/proxy").arg(boxId).arg(QString(m_identityId));
|
||||
|
||||
// This is somehow broken in AWS...
|
||||
// The Signature needs to be created with having the topic percentage-encoded twice
|
||||
@ -268,11 +287,11 @@ void AWSClient::postToMQTT(const QString &token)
|
||||
// Now one could think this is an issue in how the signature is made, but it can't really
|
||||
// be fixed there as this concerns only the actual topic, not /topics/
|
||||
// so we can't percentage-encode the whole path inside the signature helper...
|
||||
QString path = "/topics/" + topic.toUtf8().toPercentEncoding().toPercentEncoding() + "?qos=0";
|
||||
QString path1 = "/topics/" + topic.toUtf8().toPercentEncoding() + "?qos=0";
|
||||
QString path = "/topics/" + topic.toUtf8().toPercentEncoding().toPercentEncoding() + "?qos=1";
|
||||
QString path1 = "/topics/" + topic.toUtf8().toPercentEncoding() + "?qos=1";
|
||||
|
||||
QVariantMap params;
|
||||
params.insert("token", token);
|
||||
params.insert("token", m_idToken);
|
||||
params.insert("timestamp", QDateTime::currentDateTime().toSecsSinceEpoch());
|
||||
QByteArray payload = QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact);
|
||||
|
||||
@ -293,10 +312,27 @@ void AWSClient::postToMQTT(const QString &token)
|
||||
}
|
||||
qDebug() << "Payload:" << payload;
|
||||
QNetworkReply *reply = m_nam->post(request, payload);
|
||||
connect(reply, &QNetworkReply::finished, this, [reply]() {
|
||||
connect(reply, &QNetworkReply::finished, this, [reply, callback]() {
|
||||
reply->deleteLater();
|
||||
qDebug() << "post reply" << reply->readAll();
|
||||
QByteArray data = reply->readAll();
|
||||
qDebug() << "post reply" << data;
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Failed to parse 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();
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
callback(true);
|
||||
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AWSClient::fetchDevices()
|
||||
@ -305,8 +341,8 @@ void AWSClient::fetchDevices()
|
||||
qWarning() << "Not logged in at AWS. Can't fetch paired devices";
|
||||
return;
|
||||
}
|
||||
if (tokenExpired()) {
|
||||
qDebug() << "Need to refresh our tokens";
|
||||
if (tokensExpired()) {
|
||||
qDebug() << "Cannot fetch devices. Need to refresh our tokens";
|
||||
refreshAccessToken();
|
||||
m_callQueue.append(QueuedCall("fetchDevices"));
|
||||
return;
|
||||
@ -337,20 +373,11 @@ void AWSClient::fetchDevices()
|
||||
d.id = entry.toMap().value("deviceId").toString();
|
||||
d.name = entry.toMap().value("name").toString();
|
||||
d.online = entry.toMap().value("online").toBool();
|
||||
d.token = m_accessToken;
|
||||
ret.append(d);
|
||||
}
|
||||
emit devicesFetched(ret);
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
QByteArray AWSClient::accessToken() const
|
||||
{
|
||||
return m_accessToken;
|
||||
}
|
||||
|
||||
void AWSClient::refreshAccessToken()
|
||||
@ -359,12 +386,15 @@ void AWSClient::refreshAccessToken()
|
||||
qDebug() << "Cannot refresh tokens. Not logged in to AWS";
|
||||
return;
|
||||
}
|
||||
|
||||
// We should use REFRESH_TOKEN_AUTH to refresh our tokens but it's not working
|
||||
// https://forums.aws.amazon.com/thread.jspa?threadID=287978
|
||||
// Let's re-login instead with user & pass
|
||||
login(m_username, m_password);
|
||||
return;
|
||||
|
||||
|
||||
// We should use this to refresh our tokens but it's not working
|
||||
// https://forums.aws.amazon.com/thread.jspa?threadID=287978
|
||||
// Non-working block... Enable this if Amazon ever fixes their API...
|
||||
QUrl url("https://cognito-idp.eu-west-1.amazonaws.com/");
|
||||
|
||||
QUrlQuery query;
|
||||
|
||||
@ -12,27 +12,30 @@ public:
|
||||
QString id;
|
||||
QString name;
|
||||
bool online;
|
||||
QByteArray token;
|
||||
};
|
||||
|
||||
class AWSClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isLoggedIn READ isLoggedIn NOTIFY isLoggedInChanged)
|
||||
Q_PROPERTY(QString username READ username NOTIFY isLoggedInChanged)
|
||||
|
||||
public:
|
||||
explicit AWSClient(QObject *parent = nullptr);
|
||||
|
||||
bool isLoggedIn() const;
|
||||
QString username() const;
|
||||
|
||||
Q_INVOKABLE void login(const QString &username, const QString &password);
|
||||
Q_INVOKABLE void logout();
|
||||
|
||||
Q_INVOKABLE void fetchDevices();
|
||||
|
||||
Q_INVOKABLE void postToMQTT(const QString &token);
|
||||
Q_INVOKABLE bool postToMQTT(const QString &boxId, std::function<void(bool)> callback);
|
||||
Q_INVOKABLE void getId();
|
||||
|
||||
QByteArray accessToken() const;
|
||||
bool tokensExpired() const;
|
||||
QByteArray idToken() const;
|
||||
|
||||
signals:
|
||||
void isLoggedInChanged();
|
||||
@ -44,7 +47,6 @@ private:
|
||||
void getCredentialsForIdentity(const QString &identityId);
|
||||
void connectMQTT();
|
||||
|
||||
bool tokenExpired() const;
|
||||
|
||||
private:
|
||||
QNetworkAccessManager *m_nam = nullptr;
|
||||
@ -57,6 +59,8 @@ private:
|
||||
QByteArray m_idToken;
|
||||
QByteArray m_refreshToken;
|
||||
|
||||
QByteArray m_identityId;
|
||||
|
||||
QByteArray m_accessKeyId;
|
||||
QByteArray m_secretKey;
|
||||
QByteArray m_sessionToken;
|
||||
@ -65,9 +69,10 @@ private:
|
||||
class QueuedCall {
|
||||
public:
|
||||
QueuedCall(const QString &method): method(method) { }
|
||||
QueuedCall(const QString &method, const QString &arg1): method(method) { args.append(arg1); }
|
||||
QueuedCall(const QString &method, const QString &boxId, std::function<void(bool)> callback): method(method), boxId(boxId), callback(callback) {}
|
||||
QString method;
|
||||
QStringList args;
|
||||
QString boxId;
|
||||
std::function<void(bool)> callback;
|
||||
};
|
||||
|
||||
QList<QueuedCall> m_callQueue;
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "remoteproxyconnection.h"
|
||||
|
||||
#include <QUrlQuery>
|
||||
#include <QHostInfo>
|
||||
|
||||
using namespace remoteproxyclient;
|
||||
|
||||
@ -11,26 +12,33 @@ CloudTransport::CloudTransport(AWSClient *awsClient, QObject *parent):
|
||||
NymeaTransportInterface(parent),
|
||||
m_awsClient(awsClient)
|
||||
{
|
||||
m_remoteproxyConnection = new RemoteProxyConnection(QUuid::createUuid(), "nymea:app", RemoteProxyConnection::ConnectionTypeWebSocket, this);
|
||||
m_remoteproxyConnection = new RemoteProxyConnection(QUuid::createUuid(), "nymea:app", this);
|
||||
m_remoteproxyConnection->setInsecureConnection(true);
|
||||
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::connected, this,[this]() {
|
||||
qDebug() << "Connected to remote proxy";
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::remoteConnectionEstablished, this,[this]() {
|
||||
qDebug() << "CloudTransport: Remote connection established.";
|
||||
emit connected();
|
||||
});
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::stateChanged, this,[this](RemoteProxyConnection::State state) {
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::disconnected, this,[this]() {
|
||||
qDebug() << "CloudTransport: Disconnected.";
|
||||
emit disconnected();
|
||||
});
|
||||
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::stateChanged, this,[](RemoteProxyConnection::State state) {
|
||||
qDebug() << "Proxy state changed:" << state;
|
||||
if (state == RemoteProxyConnection::StateRemoteConnected) {
|
||||
emit connected();
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::ready, this,[this]() {
|
||||
qDebug() << "Proxy ready:";
|
||||
m_remoteproxyConnection->authenticate(m_token);
|
||||
qDebug() << "Proxy ready. Authenticating channel.";
|
||||
m_remoteproxyConnection->authenticate(m_awsClient->idToken());
|
||||
});
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::dataReady, this, [this](const QByteArray &data) {
|
||||
qDebug() << "Remote connection data received";
|
||||
emit dataReady(data);
|
||||
});
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::errorOccured, this, [this] (RemoteProxyConnection::Error error) {
|
||||
qDebug() << "Remote proxy Error:" << error;
|
||||
emit NymeaTransportInterface::error(QAbstractSocket::ConnectionRefusedError);
|
||||
});
|
||||
}
|
||||
|
||||
QStringList CloudTransport::supportedSchemes() const
|
||||
@ -44,20 +52,27 @@ bool CloudTransport::connect(const QUrl &url)
|
||||
qWarning() << "Not logged in to AWS, cannot connect";
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "Connecting to" << url;
|
||||
|
||||
QUrlQuery query(url.query());
|
||||
QString m_token = query.queryItemValue("token");
|
||||
m_awsClient->postToMQTT(m_token);
|
||||
bool postResult = m_awsClient->postToMQTT(url.host(), [this](bool success) {
|
||||
if (success) {
|
||||
m_remoteproxyConnection->connectServer(QHostAddress("34.244.242.103"), 443);
|
||||
}
|
||||
});
|
||||
|
||||
m_remoteproxyConnection->connectServer(QHostAddress("127.0.0.1"), 1212);
|
||||
if (!postResult) {
|
||||
qWarning() << "Failed to post to MQTT. Cannot continue";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CloudTransport::disconnect()
|
||||
{
|
||||
qDebug() << "should disconnect";
|
||||
qDebug() << "CloudTransport: Disconnecting from server.";
|
||||
m_remoteproxyConnection->disconnectServer();
|
||||
}
|
||||
|
||||
NymeaTransportInterface::ConnectionState CloudTransport::connectionState() const
|
||||
|
||||
@ -26,8 +26,6 @@ public:
|
||||
private:
|
||||
AWSClient *m_awsClient = nullptr;
|
||||
remoteproxyclient::RemoteProxyConnection *m_remoteproxyConnection = nullptr;
|
||||
|
||||
QString m_token;
|
||||
};
|
||||
|
||||
#endif // CLOUDTRANSPORT_H
|
||||
|
||||
@ -72,8 +72,6 @@ void NymeaDiscovery::cloudDevicesFetched(const QList<AWSDevice> &devices)
|
||||
QUrl url;
|
||||
url.setScheme("cloud");
|
||||
url.setHost(d.id);
|
||||
QUrlQuery query;
|
||||
query.addQueryItem("token", d.token);
|
||||
if (!device->connections()->find(url)) {
|
||||
Connection *conn = new Connection(url, Connection::BearerTypeCloud, true, d.id);
|
||||
device->connections()->addConnection(conn);
|
||||
|
||||
@ -10,9 +10,6 @@ include(../config.pri)
|
||||
DEFINES += WITH_ZEROCONF
|
||||
include(../QtZeroConf/qtzeroconf.pri)
|
||||
}
|
||||
DEFINES += QT_STATIC
|
||||
include(../qmqtt/src/mqtt/mqtt.pri)
|
||||
HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS
|
||||
|
||||
include(../nymea-remoteproxy/libnymea-remoteproxyclient/libnymea-remoteproxyclient.pri)
|
||||
|
||||
|
||||
@ -42,8 +42,7 @@ int main(int argc, char *argv[])
|
||||
application.setOrganizationName("nymea");
|
||||
|
||||
foreach (const QFileInfo &fi, QDir(":/ui/fonts/").entryInfoList()) {
|
||||
int id = QFontDatabase::addApplicationFont(fi.absoluteFilePath());
|
||||
// qDebug() << "Added font" << fi.absoluteFilePath() << QFontDatabase::applicationFontFamilies(id);
|
||||
QFontDatabase::addApplicationFont(fi.absoluteFilePath());
|
||||
}
|
||||
|
||||
QFont applicationFont;
|
||||
|
||||
@ -9,8 +9,6 @@ INCLUDEPATH += $$top_srcdir/libnymea-common \
|
||||
LIBS += -L$$top_builddir/libnymea-app-core/ -lnymea-app-core \
|
||||
-L$$top_builddir/libnymea-common/ -lnymea-common \
|
||||
|
||||
LIBS += -lssl -lcrypto
|
||||
|
||||
win32:Debug:LIBS += -L$$top_builddir/libnymea-app-core/debug \
|
||||
-L$$top_builddir/libnymea-common/debug
|
||||
win32:Release:LIBS += -L$$top_builddir/libnymea-app-core/release \
|
||||
|
||||
@ -12,43 +12,57 @@ Page {
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
visible: Engine.awsClient.isLoggedIn
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Logged in as %1").arg(Engine.awsClient.username)
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("Log out")
|
||||
onClicked: Engine.awsClient.logout();
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||
visible: !Engine.awsClient.isLoggedIn
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: "Username (e-mail)"
|
||||
}
|
||||
TextField {
|
||||
id: usernameTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
placeholderText: "john@dummy.com"
|
||||
inputMethodHints: Qt.ImhEmailCharactersOnly
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("Password")
|
||||
}
|
||||
TextField {
|
||||
id: passwordTextField
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
Layout.fillWidth: true
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
|
||||
text: qsTr("OK")
|
||||
enabled: usernameTextField.displayText.length > 0 && passwordTextField.displayText.length > 0
|
||||
onClicked: {
|
||||
Engine.awsClient.login(usernameTextField.text, passwordTextField.text);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "GetId"
|
||||
onClicked: Engine.awsClient.getId();
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "Post to MQTT"
|
||||
onClicked: Engine.awsClient.postToMQTT();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit a9e79bde9703c842a7ca7adead22d84bcb9d6f53
|
||||
Subproject commit 0a18897de79606543b0e7a68ac9b1990a5dd761c
|
||||
Reference in New Issue
Block a user