getting close
This commit is contained in:
parent
080a47932a
commit
e9390dcb5a
@ -6,6 +6,7 @@
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonDocument>
|
||||
#include <QSettings>
|
||||
#include <QUuid>
|
||||
|
||||
#include "qmqtt.h"
|
||||
#include "sigv4utils.h"
|
||||
@ -15,7 +16,38 @@ static QByteArray region = "eu-west-1";
|
||||
//static QByteArray service = "iotdevicegateway";
|
||||
static QByteArray service = "iotdata";
|
||||
|
||||
AWSClient::AWSClient(QObject *parent) : QObject(parent)
|
||||
static QByteArray rootCA = "-----BEGIN CERTIFICATE-----\n\
|
||||
MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\n\
|
||||
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\n\
|
||||
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\n\
|
||||
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\n\
|
||||
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\n\
|
||||
aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL\n\
|
||||
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW\n\
|
||||
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln\n\
|
||||
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp\n\
|
||||
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y\n\
|
||||
aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1\n\
|
||||
nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex\n\
|
||||
t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz\n\
|
||||
SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG\n\
|
||||
BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+\n\
|
||||
rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/\n\
|
||||
NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E\n\
|
||||
BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH\n\
|
||||
BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy\n\
|
||||
aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv\n\
|
||||
MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE\n\
|
||||
p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y\n\
|
||||
5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK\n\
|
||||
WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ\n\
|
||||
4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N\n\
|
||||
hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq\n\
|
||||
-----END CERTIFICATE-----\n\
|
||||
";
|
||||
|
||||
AWSClient::AWSClient(QObject *parent) : QObject(parent),
|
||||
m_devices(new AWSDevices(this))
|
||||
{
|
||||
m_nam = new QNetworkAccessManager(this);
|
||||
|
||||
@ -46,6 +78,16 @@ QString AWSClient::username() const
|
||||
return m_username;
|
||||
}
|
||||
|
||||
QByteArray AWSClient::userId() const
|
||||
{
|
||||
return m_identityId;
|
||||
}
|
||||
|
||||
AWSDevices *AWSClient::awsDevices() const
|
||||
{
|
||||
return m_devices;
|
||||
}
|
||||
|
||||
void AWSClient::login(const QString &username, const QString &password)
|
||||
{
|
||||
m_username = username;
|
||||
@ -189,6 +231,46 @@ QByteArray AWSClient::idToken() const
|
||||
return m_idToken;
|
||||
}
|
||||
|
||||
QString AWSClient::cognitoIdentityId() const
|
||||
{
|
||||
return m_identityId;
|
||||
}
|
||||
|
||||
void AWSClient::fetchCertificate(const QString &uuid, std::function<void(const QByteArray &, const QByteArray &, const QByteArray &, const QByteArray &, const QString &)> callback)
|
||||
{
|
||||
QString fixedUuid = uuid;
|
||||
fixedUuid.remove(QRegExp("[{}]"));
|
||||
QNetworkRequest request(QUrl("https://testproductionservice-cloud.guh.io/certificatews/certificate"));
|
||||
request.setRawHeader("X-api-key", "BJMq4h19dB5yjVKwagTvk9u72FqLecEoWPJIkyDj");
|
||||
request.setRawHeader("X-api-vendorId", "testVendor001");
|
||||
request.setRawHeader("X-api-deviceId", fixedUuid.toUtf8());
|
||||
request.setRawHeader("X-api-serialId", "69696969");
|
||||
QNetworkReply *reply = m_nam->get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, [reply, callback]() {
|
||||
reply->deleteLater();
|
||||
QByteArray data = reply->readAll();
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Error deploying certificate" << data;
|
||||
return;
|
||||
}
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "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;
|
||||
callback(rootCA, certificate, publicKey, privateKey, "a2addxakg5juii.iot.eu-west-1.amazonaws.com");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void AWSClient::getCredentialsForIdentity(const QString &identityId)
|
||||
{
|
||||
QUrl url("https://cognito-identity.eu-west-1.amazonaws.com/");
|
||||
@ -367,15 +449,21 @@ void AWSClient::fetchDevices()
|
||||
qWarning() << "Failed to parse JSON from server" << error.errorString() << qUtf8Printable(data);
|
||||
return;
|
||||
}
|
||||
QList<AWSDevice> ret;
|
||||
foreach (const QVariant &entry, jsonDoc.toVariant().toMap().value("devices").toList()) {
|
||||
AWSDevice d;
|
||||
d.id = entry.toMap().value("deviceId").toString();
|
||||
d.name = entry.toMap().value("name").toString();
|
||||
d.online = entry.toMap().value("online").toBool();
|
||||
ret.append(d);
|
||||
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;
|
||||
|
||||
AWSDevice *d = m_devices->getDevice(deviceId);
|
||||
if (!d) {
|
||||
d = new AWSDevice(deviceId, name);
|
||||
m_devices->insert(d);
|
||||
}
|
||||
d->setOnline(online);
|
||||
}
|
||||
emit devicesFetched(ret);
|
||||
|
||||
emit devicesFetched();
|
||||
|
||||
});
|
||||
}
|
||||
@ -455,3 +543,96 @@ void AWSClient::refreshAccessToken()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
AWSDevices::AWSDevices(QObject *parent):
|
||||
QAbstractListModel(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int AWSDevices::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_list.count();
|
||||
}
|
||||
|
||||
QVariant AWSDevices::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case RoleName:
|
||||
return m_list.at(index.row())->name();
|
||||
case RoleId:
|
||||
return m_list.at(index.row())->id();
|
||||
case RoleOnline:
|
||||
return m_list.at(index.row())->online();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> AWSDevices::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles.insert(RoleName, "name");
|
||||
roles.insert(RoleId, "id");
|
||||
roles.insert(RoleOnline, "online");
|
||||
return roles;
|
||||
}
|
||||
|
||||
AWSDevice *AWSDevices::getDevice(const QString &uuid) const
|
||||
{
|
||||
for (int i = 0; i < m_list.count(); i++) {
|
||||
if (m_list.at(i)->id() == uuid) {
|
||||
return m_list.at(i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AWSDevice *AWSDevices::get(int index) const
|
||||
{
|
||||
if (index < 0 || index >= m_list.count()) {
|
||||
return nullptr;
|
||||
}
|
||||
return m_list.at(index);
|
||||
}
|
||||
|
||||
void AWSDevices::insert(AWSDevice *device)
|
||||
{
|
||||
device->setParent(this);
|
||||
beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
|
||||
m_list.append(device);
|
||||
endInsertRows();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
AWSDevice::AWSDevice(const QString &id, const QString &name, bool online, QObject *parent):
|
||||
QObject (parent),
|
||||
m_id(id),
|
||||
m_name(name),
|
||||
m_online(online)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString AWSDevice::id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
QString AWSDevice::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
bool AWSDevice::online() const
|
||||
{
|
||||
return m_online;
|
||||
}
|
||||
|
||||
void AWSDevice::setOnline(bool online)
|
||||
{
|
||||
if (m_online != online) {
|
||||
m_online = online;
|
||||
emit onlineChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,14 +4,52 @@
|
||||
#include <QObject>
|
||||
#include <QNetworkRequest>
|
||||
#include <QDate>
|
||||
#include <QAbstractListModel>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class AWSDevice {
|
||||
class AWSDevice: public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString id READ id CONSTANT)
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(bool online READ online NOTIFY onlineChanged)
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
bool online;
|
||||
AWSDevice(const QString &id, const QString &name, bool online = false, QObject *parent = nullptr);
|
||||
QString id() const;
|
||||
QString name() const;
|
||||
bool online() const;
|
||||
void setOnline(bool online);
|
||||
|
||||
signals:
|
||||
void onlineChanged();
|
||||
|
||||
private:
|
||||
QString m_id;
|
||||
QString m_name;
|
||||
bool m_online;
|
||||
};
|
||||
|
||||
class AWSDevices: public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
public:
|
||||
enum Roles {
|
||||
RoleName,
|
||||
RoleId,
|
||||
RoleOnline
|
||||
};
|
||||
AWSDevices(QObject *parent = nullptr);
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
Q_INVOKABLE AWSDevice* getDevice(const QString &uuid) const;
|
||||
Q_INVOKABLE AWSDevice* get(int index) const;
|
||||
void insert(AWSDevice *device);
|
||||
signals:
|
||||
void countChanged();
|
||||
private:
|
||||
QList<AWSDevice*> m_list;
|
||||
};
|
||||
|
||||
class AWSClient : public QObject
|
||||
@ -19,12 +57,17 @@ class AWSClient : public QObject
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isLoggedIn READ isLoggedIn NOTIFY isLoggedInChanged)
|
||||
Q_PROPERTY(QString username READ username NOTIFY isLoggedInChanged)
|
||||
Q_PROPERTY(QByteArray userId READ userId NOTIFY isLoggedInChanged)
|
||||
Q_PROPERTY(QByteArray idToken READ idToken NOTIFY isLoggedInChanged)
|
||||
Q_PROPERTY(AWSDevices* awsDevices READ awsDevices CONSTANT)
|
||||
|
||||
public:
|
||||
explicit AWSClient(QObject *parent = nullptr);
|
||||
|
||||
bool isLoggedIn() const;
|
||||
QString username() const;
|
||||
QByteArray userId() const;
|
||||
AWSDevices* awsDevices() const;
|
||||
|
||||
Q_INVOKABLE void login(const QString &username, const QString &password);
|
||||
Q_INVOKABLE void logout();
|
||||
@ -36,11 +79,13 @@ public:
|
||||
|
||||
bool tokensExpired() const;
|
||||
QByteArray idToken() const;
|
||||
QString cognitoIdentityId() const;
|
||||
|
||||
void fetchCertificate(const QString &uuid, std::function<void(const QByteArray &rootCA, const QByteArray &certificate, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint)> callback);
|
||||
|
||||
signals:
|
||||
void isLoggedInChanged();
|
||||
|
||||
void devicesFetched(QList<AWSDevice> devices);
|
||||
void devicesFetched();
|
||||
|
||||
private:
|
||||
void refreshAccessToken();
|
||||
@ -76,6 +121,8 @@ private:
|
||||
};
|
||||
|
||||
QList<QueuedCall> m_callQueue;
|
||||
|
||||
AWSDevices *m_devices;
|
||||
};
|
||||
|
||||
#endif // AWSCLIENT_H
|
||||
|
||||
@ -13,7 +13,6 @@ CloudTransport::CloudTransport(AWSClient *awsClient, QObject *parent):
|
||||
m_awsClient(awsClient)
|
||||
{
|
||||
m_remoteproxyConnection = new RemoteProxyConnection(QUuid::createUuid(), "nymea:app", this);
|
||||
m_remoteproxyConnection->setInsecureConnection(true);
|
||||
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::remoteConnectionEstablished, this,[this]() {
|
||||
qDebug() << "CloudTransport: Remote connection established.";
|
||||
@ -37,8 +36,9 @@ CloudTransport::CloudTransport(AWSClient *awsClient, QObject *parent):
|
||||
});
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::errorOccured, this, [this] (RemoteProxyConnection::Error error) {
|
||||
qDebug() << "Remote proxy Error:" << error;
|
||||
emit NymeaTransportInterface::error(QAbstractSocket::ConnectionRefusedError);
|
||||
// emit NymeaTransportInterface::error(QAbstractSocket::ConnectionRefusedError);
|
||||
});
|
||||
QObject::connect(m_remoteproxyConnection, &RemoteProxyConnection::sslErrors, this, &CloudTransport::sslErrors);
|
||||
}
|
||||
|
||||
QStringList CloudTransport::supportedSchemes() const
|
||||
@ -57,7 +57,8 @@ bool CloudTransport::connect(const QUrl &url)
|
||||
|
||||
bool postResult = m_awsClient->postToMQTT(url.host(), [this](bool success) {
|
||||
if (success) {
|
||||
m_remoteproxyConnection->connectServer(QHostAddress("34.244.242.103"), 443);
|
||||
m_remoteproxyConnection->connectServer(QUrl("wss://dev-remoteproxy.nymea.io"));
|
||||
// m_remoteproxyConnection->connectServer(QUrl("wss://127.0.0.1:1212"));
|
||||
}
|
||||
});
|
||||
|
||||
@ -81,6 +82,7 @@ NymeaTransportInterface::ConnectionState CloudTransport::connectionState() const
|
||||
case RemoteProxyConnection::StateRemoteConnected:
|
||||
return NymeaTransportInterface::ConnectionStateConnected;
|
||||
case RemoteProxyConnection::StateInitializing:
|
||||
case RemoteProxyConnection::StateHostLookup:
|
||||
case RemoteProxyConnection::StateConnecting:
|
||||
case RemoteProxyConnection::StateConnected:
|
||||
case RemoteProxyConnection::StateAuthenticating:
|
||||
@ -98,3 +100,9 @@ void CloudTransport::sendData(const QByteArray &data)
|
||||
qDebug() << "should send" << data;
|
||||
m_remoteproxyConnection->sendData(data);
|
||||
}
|
||||
|
||||
void CloudTransport::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
qDebug() << "Ignoring SSL errors" << errors;
|
||||
m_remoteproxyConnection->ignoreSslErrors(errors);
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ public:
|
||||
ConnectionState connectionState() const override;
|
||||
void sendData(const QByteArray &data) override;
|
||||
|
||||
void ignoreSslErrors(const QList<QSslError> &errors) override;
|
||||
private:
|
||||
AWSClient *m_awsClient = nullptr;
|
||||
remoteproxyclient::RemoteProxyConnection *m_remoteproxyConnection = nullptr;
|
||||
|
||||
@ -162,3 +162,16 @@ QString Connection::displayName() const
|
||||
{
|
||||
return m_displayName;
|
||||
}
|
||||
|
||||
bool Connection::online() const
|
||||
{
|
||||
return m_online;
|
||||
}
|
||||
|
||||
void Connection::setOnline(bool online)
|
||||
{
|
||||
if (m_online != online) {
|
||||
m_online = online;
|
||||
emit onlineChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,7 @@ class Connection: public QObject {
|
||||
Q_PROPERTY(BearerType bearerType READ bearerType CONSTANT)
|
||||
Q_PROPERTY(bool secure READ secure CONSTANT)
|
||||
Q_PROPERTY(QString displayName READ displayName CONSTANT)
|
||||
Q_PROPERTY(bool online READ online NOTIFY onlineChanged)
|
||||
public:
|
||||
enum BearerType {
|
||||
BearerTypeUnknown,
|
||||
@ -51,12 +52,18 @@ public:
|
||||
BearerType bearerType() const;
|
||||
bool secure() const;
|
||||
QString displayName() const;
|
||||
bool online() const;
|
||||
void setOnline(bool online);
|
||||
|
||||
signals:
|
||||
void onlineChanged();
|
||||
|
||||
private:
|
||||
QUrl m_url;
|
||||
BearerType m_bearerType = BearerTypeUnknown;
|
||||
bool m_secure = false;
|
||||
QString m_displayName;
|
||||
bool m_online = false;
|
||||
};
|
||||
|
||||
class Connections: public QAbstractListModel
|
||||
|
||||
@ -40,7 +40,7 @@ public:
|
||||
};
|
||||
Q_ENUM(DeviceRole)
|
||||
|
||||
explicit DiscoveryModel(QObject *parent = 0);
|
||||
explicit DiscoveryModel(QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex & parent = QModelIndex()) const;
|
||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
|
||||
|
||||
@ -20,7 +20,7 @@ NymeaDiscovery::NymeaDiscovery(QObject *parent) : QObject(parent)
|
||||
m_bluetooth = new BluetoothServiceDiscovery(m_discoveryModel, this);
|
||||
#endif
|
||||
|
||||
connect(Engine::instance()->awsClient(), &AWSClient::devicesFetched, this, &NymeaDiscovery::cloudDevicesFetched);
|
||||
connect(Engine::instance()->awsClient()->awsDevices(), &AWSDevices::countChanged, this, &NymeaDiscovery::syncCloudDevices);
|
||||
}
|
||||
|
||||
bool NymeaDiscovery::discovering() const
|
||||
@ -41,6 +41,7 @@ void NymeaDiscovery::setDiscovering(bool discovering)
|
||||
m_bluetooth->discover();
|
||||
}
|
||||
if (Engine::instance()->awsClient()->isLoggedIn()) {
|
||||
syncCloudDevices();
|
||||
Engine::instance()->awsClient()->fetchDevices();
|
||||
}
|
||||
} else {
|
||||
@ -58,23 +59,26 @@ DiscoveryModel *NymeaDiscovery::discoveryModel() const
|
||||
return m_discoveryModel;
|
||||
}
|
||||
|
||||
void NymeaDiscovery::cloudDevicesFetched(const QList<AWSDevice> &devices)
|
||||
void NymeaDiscovery::syncCloudDevices()
|
||||
{
|
||||
qDebug() << "Cloud devices fetched";
|
||||
foreach (const AWSDevice &d, devices) {
|
||||
DiscoveryDevice *device = m_discoveryModel->find(d.id);
|
||||
for (int i = 0; i < Engine::instance()->awsClient()->awsDevices()->rowCount(); i++) {
|
||||
AWSDevice *d = Engine::instance()->awsClient()->awsDevices()->get(i);
|
||||
DiscoveryDevice *device = m_discoveryModel->find(d->id());
|
||||
if (!device) {
|
||||
device = new DiscoveryDevice();
|
||||
device->setUuid(d.id);
|
||||
device->setName(d.name);
|
||||
device->setUuid(d->id());
|
||||
device->setName(d->name());
|
||||
m_discoveryModel->addDevice(device);
|
||||
}
|
||||
QUrl url;
|
||||
url.setScheme("cloud");
|
||||
url.setHost(d.id);
|
||||
url.setHost(d->id());
|
||||
if (!device->connections()->find(url)) {
|
||||
Connection *conn = new Connection(url, Connection::BearerTypeCloud, true, d.id);
|
||||
Connection *conn = new Connection(url, Connection::BearerTypeCloud, true, d->id());
|
||||
conn->setOnline(d->online());
|
||||
device->connections()->addConnection(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ signals:
|
||||
void discoveringChanged();
|
||||
|
||||
private slots:
|
||||
void cloudDevicesFetched(const QList<AWSDevice> &devices);
|
||||
void syncCloudDevices();
|
||||
|
||||
private:
|
||||
bool m_discovering = false;
|
||||
|
||||
@ -81,6 +81,22 @@ AWSClient *Engine::awsClient() const
|
||||
return m_aws;
|
||||
}
|
||||
|
||||
void Engine::deployCertificate()
|
||||
{
|
||||
if (!m_jsonRpcClient->connected()) {
|
||||
qWarning() << "JSONRPC not connected. Cannot deploy certificate";
|
||||
return;
|
||||
}
|
||||
if (!m_aws->isLoggedIn()) {
|
||||
qWarning() << "Not logged in at AWS. Cannot deploy certificate";
|
||||
return;
|
||||
}
|
||||
m_aws->fetchCertificate(m_jsonRpcClient->serverUuid(), [this](const QByteArray &rootCA, const QByteArray &certificate, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint){
|
||||
qDebug() << "Certificate received" << certificate << publicKey << privateKey;
|
||||
m_jsonRpcClient->deployCertificate(rootCA, certificate, publicKey, privateKey, endpoint);
|
||||
});
|
||||
}
|
||||
|
||||
NymeaConnection *Engine::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
@ -107,6 +123,22 @@ Engine::Engine(QObject *parent) :
|
||||
connect(m_jsonRpcClient, &JsonRpcClient::authenticationRequiredChanged, this, &Engine::onConnectedChanged);
|
||||
|
||||
connect(m_deviceManager, &DeviceManager::fetchingDataChanged, this, &Engine::onDeviceManagerFetchingChanged);
|
||||
|
||||
connect(m_aws, &AWSClient::devicesFetched, this, [this]() {
|
||||
if (m_jsonRpcClient->connected() && m_jsonRpcClient->cloudConnectionState() == JsonRpcClient::CloudConnectionStateConnected) {
|
||||
if (m_aws->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) {
|
||||
m_jsonRpcClient->setupRemoteAccess(m_aws->idToken(), m_aws->cognitoIdentityId());
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, [this]() {
|
||||
if (m_jsonRpcClient->connected() && m_jsonRpcClient->cloudConnectionState() == JsonRpcClient::CloudConnectionStateConnected) {
|
||||
if (m_aws->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) {
|
||||
m_jsonRpcClient->setupRemoteAccess(m_aws->idToken(), m_aws->cognitoIdentityId());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void Engine::onConnectedChanged()
|
||||
|
||||
@ -62,6 +62,8 @@ public:
|
||||
BluetoothDiscovery *bluetoothDiscovery() const;
|
||||
AWSClient* awsClient() const;
|
||||
|
||||
Q_INVOKABLE void deployCertificate();
|
||||
|
||||
private:
|
||||
explicit Engine(QObject *parent = nullptr);
|
||||
static Engine *s_instance;
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include <QUuid>
|
||||
#include <QSettings>
|
||||
#include <QVersionNumber>
|
||||
#include <QMetaEnum>
|
||||
|
||||
JsonRpcClient::JsonRpcClient(NymeaConnection *connection, QObject *parent) :
|
||||
JsonHandler(parent),
|
||||
@ -120,8 +121,9 @@ void JsonRpcClient::notificationReceived(const QVariantMap &data)
|
||||
}
|
||||
|
||||
if (data.value("notification").toString() == "JSONRPC.CloudConnectedChanged") {
|
||||
m_cloudConnected = data.value("params").toMap().value("connected").toBool();
|
||||
emit cloudConnectedChanged();
|
||||
QMetaEnum connectionStateEnum = QMetaEnum::fromType<CloudConnectionState>();
|
||||
m_cloudConnectionState = static_cast<CloudConnectionState>(connectionStateEnum.keyToValue(data.value("params").toMap().value("connectionState").toByteArray().data()));
|
||||
emit cloudConnectionStateChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -131,8 +133,19 @@ void JsonRpcClient::notificationReceived(const QVariantMap &data)
|
||||
void JsonRpcClient::isCloudConnectedReply(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "Cloud is connected" << data;
|
||||
m_cloudConnected = data.value("params").toMap().value("connected").toBool();
|
||||
emit cloudConnectedChanged();
|
||||
QMetaEnum connectionStateEnum = QMetaEnum::fromType<CloudConnectionState>();
|
||||
m_cloudConnectionState = static_cast<CloudConnectionState>(connectionStateEnum.keyToValue(data.value("params").toMap().value("connectionState").toByteArray().data()));
|
||||
emit cloudConnectionStateChanged();
|
||||
}
|
||||
|
||||
void JsonRpcClient::setupRemoteAccessReply(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "Setup Remote Access reply" << data;
|
||||
}
|
||||
|
||||
void JsonRpcClient::deployCertificateReply(const QVariantMap &data)
|
||||
{
|
||||
qDebug() << "deploy certificate reply:" << data;
|
||||
}
|
||||
|
||||
bool JsonRpcClient::connected() const
|
||||
@ -155,9 +168,21 @@ bool JsonRpcClient::pushButtonAuthAvailable() const
|
||||
return m_pushButtonAuthAvailable;
|
||||
}
|
||||
|
||||
bool JsonRpcClient::cloudConnected() const
|
||||
JsonRpcClient::CloudConnectionState JsonRpcClient::cloudConnectionState() const
|
||||
{
|
||||
return m_cloudConnected;
|
||||
return m_cloudConnectionState;
|
||||
}
|
||||
|
||||
void JsonRpcClient::deployCertificate(const QByteArray &rootCA, const QByteArray &certificate, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint)
|
||||
{
|
||||
QVariantMap params;
|
||||
params.insert("rootCA", rootCA);
|
||||
params.insert("certificatePEM", certificate);
|
||||
params.insert("publicKey", publicKey);
|
||||
params.insert("privateKey", privateKey);
|
||||
params.insert("endpoint", endpoint);
|
||||
|
||||
sendCommand("JSONRPC.SetupCloudConnection", params, this, "deployCertificateReply");
|
||||
}
|
||||
|
||||
QString JsonRpcClient::serverVersion() const
|
||||
@ -209,6 +234,14 @@ int JsonRpcClient::requestPushButtonAuth(const QString &deviceName)
|
||||
return reply->commandId();
|
||||
}
|
||||
|
||||
void JsonRpcClient::setupRemoteAccess(const QString &idToken, const QString &userId)
|
||||
{
|
||||
QVariantMap params;
|
||||
params.insert("idToken", idToken);
|
||||
params.insert("userId", userId);
|
||||
sendCommand("JSONRPC.SetupRemoteAccess", params, this, "setupRemoteAccessReply");
|
||||
}
|
||||
|
||||
bool JsonRpcClient::ensureServerVersion(const QString &jsonRpcVersion)
|
||||
{
|
||||
return QVersionNumber(m_jsonRpcVersion) >= QVersionNumber::fromString(jsonRpcVersion);
|
||||
|
||||
@ -40,13 +40,21 @@ class JsonRpcClient : public JsonHandler
|
||||
Q_PROPERTY(bool initialSetupRequired READ initialSetupRequired NOTIFY initialSetupRequiredChanged)
|
||||
Q_PROPERTY(bool authenticationRequired READ authenticationRequired NOTIFY authenticationRequiredChanged)
|
||||
Q_PROPERTY(bool pushButtonAuthAvailable READ pushButtonAuthAvailable NOTIFY pushButtonAuthAvailableChanged)
|
||||
Q_PROPERTY(bool cloudConnected READ cloudConnected NOTIFY cloudConnectedChanged)
|
||||
Q_PROPERTY(CloudConnectionState cloudConnectionState READ cloudConnectionState NOTIFY cloudConnectionStateChanged)
|
||||
Q_PROPERTY(QString serverVersion READ serverVersion NOTIFY handshakeReceived)
|
||||
Q_PROPERTY(QString jsonRpcVersion READ jsonRpcVersion NOTIFY handshakeReceived)
|
||||
Q_PROPERTY(QString serverUuid READ serverUuid NOTIFY handshakeReceived)
|
||||
|
||||
public:
|
||||
explicit JsonRpcClient(NymeaConnection *connection, QObject *parent = 0);
|
||||
enum CloudConnectionState {
|
||||
CloudConnectionStateDisabled,
|
||||
CloudConnectionStateUnconfigured,
|
||||
CloudConnectionStateConnecting,
|
||||
CloudConnectionStateConnected
|
||||
};
|
||||
Q_ENUM(CloudConnectionState)
|
||||
|
||||
explicit JsonRpcClient(NymeaConnection *connection, QObject *parent = nullptr);
|
||||
|
||||
QString nameSpace() const override;
|
||||
|
||||
@ -60,7 +68,8 @@ public:
|
||||
bool initialSetupRequired() const;
|
||||
bool authenticationRequired() const;
|
||||
bool pushButtonAuthAvailable() const;
|
||||
bool cloudConnected() const;
|
||||
CloudConnectionState cloudConnectionState() const;
|
||||
void deployCertificate(const QByteArray &rootCA, const QByteArray &certificate, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint);
|
||||
|
||||
QString serverVersion() const;
|
||||
QString jsonRpcVersion() const;
|
||||
@ -70,6 +79,7 @@ public:
|
||||
Q_INVOKABLE int createUser(const QString &username, const QString &password);
|
||||
Q_INVOKABLE int authenticate(const QString &username, const QString &password, const QString &deviceName);
|
||||
Q_INVOKABLE int requestPushButtonAuth(const QString &deviceName);
|
||||
Q_INVOKABLE void setupRemoteAccess(const QString &idToken, const QString &userId);
|
||||
|
||||
Q_INVOKABLE bool ensureServerVersion(const QString &jsonRpcVersion);
|
||||
|
||||
@ -84,7 +94,7 @@ signals:
|
||||
void authenticationFailed();
|
||||
void pushButtonAuthFailed();
|
||||
void createUserFailed(const QString &error);
|
||||
void cloudConnectedChanged();
|
||||
void cloudConnectionStateChanged();
|
||||
|
||||
void responseReceived(const int &commandId, const QVariantMap &response);
|
||||
|
||||
@ -105,7 +115,7 @@ private:
|
||||
bool m_initialSetupRequired = false;
|
||||
bool m_authenticationRequired = false;
|
||||
bool m_pushButtonAuthAvailable = false;
|
||||
bool m_cloudConnected = false;
|
||||
CloudConnectionState m_cloudConnectionState = CloudConnectionStateDisabled;
|
||||
int m_pendingPushButtonTransaction = -1;
|
||||
QString m_serverUuid;
|
||||
QVersionNumber m_jsonRpcVersion;
|
||||
@ -124,6 +134,8 @@ private:
|
||||
Q_INVOKABLE void setNotificationsEnabledResponse(const QVariantMap ¶ms);
|
||||
Q_INVOKABLE void notificationReceived(const QVariantMap &data);
|
||||
Q_INVOKABLE void isCloudConnectedReply(const QVariantMap &data);
|
||||
Q_INVOKABLE void setupRemoteAccessReply(const QVariantMap &data);
|
||||
Q_INVOKABLE void deployCertificateReply(const QVariantMap &data);
|
||||
|
||||
void sendRequest(const QVariantMap &request);
|
||||
|
||||
|
||||
@ -174,6 +174,7 @@ void registerQmlTypes() {
|
||||
qmlRegisterUncreatableType<WirelessAccessPointsProxy>(uri, 1, 0, "WirelessAccessPoints", "Can't create this in QML. Get it from the Engine instance.");
|
||||
|
||||
qmlRegisterUncreatableType<AWSClient>(uri, 1, 0, "AWSClient", "Can't create this in QML. Get it from Engine");
|
||||
qmlRegisterUncreatableType<AWSDevice>(uri, 1, 0, "AWSDevice", "Can't create this in QML. Get it from AWSClient");
|
||||
|
||||
qmlRegisterType<RuleTemplates>(uri, 1, 0, "RuleTemplates");
|
||||
qmlRegisterType<RuleTemplatesFilterModel>(uri, 1, 0, "RuleTemplatesFilterModel");
|
||||
|
||||
@ -243,5 +243,7 @@
|
||||
<file>ui/images/cloud.svg</file>
|
||||
<file>ui/system/CloudSettingsPage.qml</file>
|
||||
<file>ui/connection/CloudLoginPage.qml</file>
|
||||
<file>ui/images/cloud-offline.svg</file>
|
||||
<file>ui/images/cloud-error.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -110,6 +110,12 @@ Page {
|
||||
}
|
||||
}
|
||||
ThinDivider {}
|
||||
MeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Cloud login")
|
||||
iconName: "../images/cloud.svg"
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("connection/CloudLoginPage.qml"))
|
||||
}
|
||||
MeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("About %1").arg(app.appName)
|
||||
|
||||
@ -369,6 +369,5 @@ ApplicationWindow {
|
||||
|
||||
KeyboardLoader {
|
||||
anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,16 +16,44 @@ Page {
|
||||
visible: Engine.awsClient.isLoggedIn
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
Layout.topMargin: app.margins
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: 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();
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: app.margins
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
text: Engine.awsClient.awsDevices.count === 0 ?
|
||||
qsTr("There are no boxes connected to your cloud yet.") :
|
||||
qsTr("There (are|is) %1 boxe(s) connected to your cloud", "", Engine.awsClient.awsDevices.count)
|
||||
}
|
||||
Repeater {
|
||||
model: Engine.awsClient.awsDevices
|
||||
delegate: MeaListItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: model.name
|
||||
subText: model.uuid
|
||||
progressive: false
|
||||
prominentSubText: false
|
||||
iconName: "../images/cloud.svg"
|
||||
secondaryIconName: model.online ? "../images/cloud.svg" : "../images/cloud-offline.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
@ -86,11 +86,19 @@ Page {
|
||||
model: ListModel {
|
||||
ListElement { iconSource: "../images/network-vpn.svg"; text: qsTr("Manual connection"); page: "ManualConnectPage.qml" }
|
||||
ListElement { iconSource: "../images/bluetooth.svg"; text: qsTr("Wireless setup"); page: "BluetoothDiscoveryPage.qml"; }
|
||||
ListElement { iconSource: "../images/cloud.svg"; text: qsTr("Cloud login"); page: "CloudLoginPage.qml" }
|
||||
ListElement { iconSource: "../images/private-browsing.svg"; text: qsTr("Demo mode"); page: "" }
|
||||
ListElement { iconSource: "../images/stock_application.svg"; text: qsTr("App settings"); page: "../AppSettingsPage.qml" }
|
||||
}
|
||||
onClicked: {
|
||||
pageStack.push(model.get(index).page);
|
||||
if (index === 2) {
|
||||
Engine.connection.connect("nymea://nymea.nymea.io:2222")
|
||||
var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml"))
|
||||
page.cancel.connect(function() {
|
||||
Engine.connection.disconnect()
|
||||
})
|
||||
} else {
|
||||
pageStack.push(model.get(index).page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,23 +269,32 @@ Page {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
visible: discovery.discoveryModel.count === 0
|
||||
// visible: discovery.discoveryModel.count === 0
|
||||
text: qsTr("Start wireless setup")
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("connection/BluetoothDiscoveryPage.qml"))
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("BluetoothDiscoveryPage.qml"))
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
text: qsTr("Cloud login")
|
||||
visible: !Engine.awsClient.isLoggedIn
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("CloudLoginPage.qml"))
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Layout.bottomMargin: app.margins
|
||||
visible: discovery.discoveryModel.count === 0
|
||||
// visible: discovery.discoveryModel.count === 0
|
||||
text: qsTr("Demo mode (online)")
|
||||
onClicked: {
|
||||
Engine.connection.connect("nymea://nymea.nymea.io:2222")
|
||||
var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml"))
|
||||
page.cancel.connect(function() {
|
||||
Engine.connection.disconnect()
|
||||
})
|
||||
Engine.connection.connect("nymea://nymea.nymea.io:2222")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
154
nymea-app/ui/images/cloud-error.svg
Normal file
154
nymea-app/ui/images/cloud-error.svg
Normal file
@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="90"
|
||||
height="90"
|
||||
id="svg6138"
|
||||
version="1.1"
|
||||
inkscape:version="0.91+devel r"
|
||||
viewBox="0 0 90 90.000001"
|
||||
sodipodi:docname="sync-error.svg">
|
||||
<defs
|
||||
id="defs6140" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.6077032"
|
||||
inkscape:cx="-148.24923"
|
||||
inkscape:cy="56.003297"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g6253"
|
||||
showgrid="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-others="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-center="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid6700"
|
||||
empspacing="6" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="62,87"
|
||||
id="guide4084" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="63,84"
|
||||
id="guide4086" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="63,81"
|
||||
id="guide4088" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="3,70"
|
||||
id="guide4090" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="6,66"
|
||||
id="guide4092" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="9,59"
|
||||
id="guide4094" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="87,63"
|
||||
id="guide4096" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="84,64"
|
||||
id="guide4098" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="81,55"
|
||||
id="guide4100" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="60,3"
|
||||
id="guide4102" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="61,6"
|
||||
id="guide4104" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="62,9"
|
||||
id="guide4106" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata6143">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-283.57144,-358.79068)">
|
||||
<g
|
||||
id="g6253"
|
||||
inkscape:export-filename="planemode01.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
transform="matrix(-1,0,0,1,547.57143,-1341.5715)">
|
||||
<rect
|
||||
style="fill:none;stroke:none"
|
||||
id="rect6257"
|
||||
width="90"
|
||||
height="90"
|
||||
x="174"
|
||||
y="1700.3622" />
|
||||
<path
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;display:inline;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.99999988"
|
||||
d="m 221.99999,1732.3622 0,8.5799 c 0,2.5213 -0.0933,4.8561 -0.2793,7.0086 -0.18602,2.1215 -0.43607,4.2591 -0.74609,6.4114 l -3.99805,0 c -0.27903,-2.1523 -0.51126,-4.2899 -0.69726,-6.4114 -0.18602,-2.1217 -0.2793,-4.4565 -0.2793,-7.0086 l 0,-8.5799 6,0 z"
|
||||
id="path4185"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;display:inline;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.99999994"
|
||||
d="m 218.99999,1758.3622 c 1.10006,0 2.04956,0.3867 2.84961,1.1582 0.76673,0.738 1.15039,1.6912 1.15039,2.8653 0,1.174 -0.38366,2.1311 -1.15039,2.8691 -0.80005,0.738 -1.74955,1.1074 -2.84961,1.1074 -1.13341,0 -2.08291,-0.3694 -2.84961,-1.1074 -0.76673,-0.738 -1.15039,-1.6951 -1.15039,-2.8691 0,-1.1741 0.38366,-2.1273 1.15039,-2.8653 0.7667,-0.7715 1.7162,-1.1582 2.84961,-1.1582 z"
|
||||
id="path4942" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 212.5,1714.3613 c -11.29815,0 -20.5,9.2019 -20.5,20.5 l 0,0.01 0,0.01 c 0.002,0.2497 0.0454,0.4967 0.0566,0.7461 -10.15029,1.2279 -18.0494,9.7961 -18.05664,20.2363 0,11.2982 9.20185,20.5 20.5,20.5 l 49,0 c 11.29815,0 20.5,-9.2018 20.5,-20.5 l 0,-0 c -0.0116,-8.9301 -5.844,-16.696 -14.21094,-19.4063 0.0644,-0.5317 0.21041,-1.0527 0.21094,-1.5898 l 0,-0 c 0,-7.4321 -6.06785,-13.5 -13.5,-13.5 l -0.002,0 -0.002,0 c -2.55665,0 -4.93658,0.9271 -7.07421,2.2754 -3.76777,-5.6712 -10.02107,-9.2653 -16.91993,-9.2754 l -0.002,0 z m -0.002,4 0.002,0 c 6.1355,0.01 11.75025,3.4089 14.59766,8.8418 l 1.18945,2.2676 1.91211,-1.7031 c 1.73553,-1.5457 3.97683,-2.4018 6.30078,-2.4063 5.27039,0 9.5,4.2296 9.5,9.5 -0.001,0.8309 -0.11279,1.6591 -0.33008,2.4629 l -0.54297,2.0117 2.03125,0.461 c 7.51333,1.7082 12.82999,8.3597 12.8418,16.0644 0,7e-4 0,0 0,0 0,6e-4 0,0 0,0 -0.002,9.1346 -7.36493,16.4961 -16.5,16.4961 l -49,0 c -9.13573,0 -16.49893,-7.3625 -16.5,-16.498 l 0,-0 c 0.007,-9.023 7.2045,-16.3348 16.22656,-16.4843 l 2.27539,-0.037 -0.33007,-2.25 c -0.10801,-0.7397 -0.16511,-1.4866 -0.17188,-2.2343 0.003,-9.133 7.36456,-16.4931 16.49805,-16.4942 z"
|
||||
id="path4154"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.5 KiB |
144
nymea-app/ui/images/cloud-offline.svg
Normal file
144
nymea-app/ui/images/cloud-offline.svg
Normal file
@ -0,0 +1,144 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="90"
|
||||
height="90"
|
||||
id="svg6138"
|
||||
version="1.1"
|
||||
inkscape:version="0.91+devel r"
|
||||
viewBox="0 0 90 90.000001"
|
||||
sodipodi:docname="sync-offline.svg">
|
||||
<defs
|
||||
id="defs6140" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.0931703"
|
||||
inkscape:cx="-81.39135"
|
||||
inkscape:cy="23.804422"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g6253"
|
||||
showgrid="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-others="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-center="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid6700"
|
||||
empspacing="6" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="62,87"
|
||||
id="guide4084" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="63,84"
|
||||
id="guide4086" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="63,81"
|
||||
id="guide4088" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="3,70"
|
||||
id="guide4090" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="6,66"
|
||||
id="guide4092" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="9,59"
|
||||
id="guide4094" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="87,63"
|
||||
id="guide4096" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="84,64"
|
||||
id="guide4098" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="81,55"
|
||||
id="guide4100" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="60,3"
|
||||
id="guide4102" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="61,6"
|
||||
id="guide4104" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="62,9"
|
||||
id="guide4106" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata6143">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-283.57144,-358.79068)">
|
||||
<g
|
||||
id="g6253"
|
||||
inkscape:export-filename="planemode01.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
transform="matrix(-1,0,0,1,547.57143,-1341.5715)">
|
||||
<rect
|
||||
style="fill:none;stroke:none"
|
||||
id="rect6257"
|
||||
width="90"
|
||||
height="90"
|
||||
x="174"
|
||||
y="1700.3622" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.5;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 212.5,1714.3613 c -11.29815,0 -20.5,9.2019 -20.5,20.5 l 0,0.01 0,0.01 c 0.002,0.2497 0.0454,0.4967 0.0566,0.7461 -10.15029,1.2279 -18.0494,9.7961 -18.05664,20.2363 0,11.2982 9.20185,20.5 20.5,20.5 l 49,0 c 11.29815,0 20.5,-9.2018 20.5,-20.5 l 0,-0 c -0.0116,-8.9301 -5.844,-16.696 -14.21094,-19.4063 0.0644,-0.5317 0.21041,-1.0527 0.21094,-1.5898 l 0,-0 c 0,-7.4321 -6.06785,-13.5 -13.5,-13.5 l -0.002,0 -0.002,0 c -2.55665,0 -4.93658,0.9271 -7.07421,2.2754 -3.76777,-5.6712 -10.02107,-9.2653 -16.91993,-9.2754 l -0.002,0 z m -0.002,4 0.002,0 c 6.1355,0.01 11.75025,3.4089 14.59766,8.8418 l 1.18945,2.2676 1.91211,-1.7031 c 1.73553,-1.5457 3.97683,-2.4018 6.30078,-2.4063 5.27039,0 9.5,4.2296 9.5,9.5 -0.001,0.8309 -0.11279,1.6591 -0.33008,2.4629 l -0.54297,2.0117 2.03125,0.461 c 7.51333,1.7082 12.82999,8.3597 12.8418,16.0644 0,7e-4 0,0 0,0 0,6e-4 0,0 0,0 -0.002,9.1346 -7.36493,16.4961 -16.5,16.4961 l -49,0 c -9.13573,0 -16.49893,-7.3625 -16.5,-16.498 l 0,-0 c 0.007,-9.023 7.2045,-16.3348 16.22656,-16.4843 l 2.27539,-0.037 -0.33007,-2.25 c -0.10801,-0.7397 -0.16511,-1.4866 -0.17188,-2.2343 0.003,-9.133 7.36456,-16.4931 16.49805,-16.4942 z"
|
||||
id="path4154"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.0 KiB |
@ -11,12 +11,35 @@ Page {
|
||||
onBackPressed: pageStack.pop();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Engine.basicConfiguration
|
||||
onCloudEnabledChanged: {
|
||||
if (Engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateUnconfigured) {
|
||||
Engine.deployCertificate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Engine.jsonRpcClient
|
||||
onCloudConnectionStateChanged: {
|
||||
if (Engine.awsClient.isLoggedIn && Engine.awsClient.awsDevices.getDevice(Engine.jsonRpcClient.serverUuid) === null) {
|
||||
print("Pairing user and box...")
|
||||
Engine.jsonRpcClient.setupRemoteAccess(Engine.awsClient.idToken, Engine.awsClient.userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: Engine.jsonRpcClient.cloudConnected
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Layout.topMargin: app.margins
|
||||
text: qsTr("You can connect a nymea:box to a nymea:cloud in order to access it from anywhere")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
SwitchDelegate {
|
||||
@ -27,5 +50,45 @@ Page {
|
||||
Engine.basicConfiguration.cloudEnabled = checked;
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
visible: Engine.basicConfiguration.cloudEnabled
|
||||
|
||||
BusyIndicator {
|
||||
visible: Engine.jsonRpcClient.cloudConnectionState == JsonRpcClient.CloudConnectionStateUnconfigured ||
|
||||
Engine.jsonRpcClient.cloudConnectionState == JsonRpcClient.CloudConnectionStateConnecting
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: {
|
||||
switch (Engine.jsonRpcClient.cloudConnectionState) {
|
||||
case JsonRpcClient.CloudConnectionStateDisabled:
|
||||
return ""
|
||||
case JsonRpcClient.CloudConnectionStateUnconfigured:
|
||||
return qsTr("Configuring the box to connect to nymea:cloud...");
|
||||
case JsonRpcClient.CloudConnectionStateConnecting:
|
||||
return qsTr("Connecting the box to nymea:cloud...");
|
||||
case JsonRpcClient.CloudConnectionStateConnected:
|
||||
return qsTr("The box is connected to nymea:cloud.");
|
||||
}
|
||||
return Engine.jsonRpcClient.cloudConnectionState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Label {
|
||||
// Layout.fillWidth: true
|
||||
// Layout.leftMargin: app.margins
|
||||
// Layout.rightMargin: app.margins
|
||||
// visible: Engine.basicConfiguration.cloudEnabled && Engine.awsClient.isLoggedIn
|
||||
// text: Engine.awsClient.awsDevices.getDevice(Engine.jsonRpcClient.serverUuid) !== null ?
|
||||
// qsTr("This box is connected to a nymea:cloud.") :
|
||||
// qsTr("Connecting to nymea:cloud...")
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 0a18897de79606543b0e7a68ac9b1990a5dd761c
|
||||
Subproject commit f6e2d9b3b208362159dd22a4eb11527db25d8760
|
||||
Reference in New Issue
Block a user