diff --git a/libnymea-app-core/discovery/discoverydevice.cpp b/libnymea-app-core/discovery/discoverydevice.cpp index f6d5a45c..d04a265d 100644 --- a/libnymea-app-core/discovery/discoverydevice.cpp +++ b/libnymea-app-core/discovery/discoverydevice.cpp @@ -121,6 +121,7 @@ void Connections::addConnection(Connection *connection) emit dataChanged(index(idx), index(idx), {RoleOnline}); }); endInsertRows(); + emit connectionAdded(connection); emit countChanged(); } @@ -134,6 +135,7 @@ void Connections::removeConnection(Connection *connection) beginRemoveRows(QModelIndex(), idx, idx); m_connections.takeAt(idx)->deleteLater(); endRemoveRows(); + emit connectionRemoved(connection); emit countChanged(); } @@ -157,6 +159,46 @@ Connection* Connections::get(int index) const return nullptr; } +Connection* Connections::bestMatch() const +{ + QList bearerPreference = {Connection::BearerTypeEthernet, Connection::BearerTypeWifi, Connection::BearerTypeCloud, Connection::BearerTypeBluetooth, Connection::BearerTypeUnknown}; + Connection *best = nullptr; + foreach (Connection *c, m_connections) { + if (!best) { + best = c; + continue; + } + uint oldBearerPriority = static_cast(bearerPreference.indexOf(best->bearerType())); + uint newBearerPriority = static_cast(bearerPreference.indexOf(c->bearerType())); + if (newBearerPriority < oldBearerPriority) { + // New one has better bearer, switch + best = c; + continue; + } + if (oldBearerPriority < newBearerPriority) { + // Discard new one as the existing is on a better bearer + continue; + } + + // Same bearer, prefer secure over insecure + if (!best->secure() && c->secure()) { + // New one is secure, old one not. switch + best = c; + continue; + } + if (best->secure() && !c->secure()) { + // Old one is secure, new one isn't, skip new one + continue; + } + + // both options are now on the same bearer and either secure or insecure, prefer nymearpc over websocket for less overhead + if (best->url().scheme().startsWith("ws") && c->url().scheme().startsWith("nymea")) { + best = c; + } + } + return best; +} + QHash Connections::roleNames() const { QHash roles; diff --git a/libnymea-app-core/discovery/discoverydevice.h b/libnymea-app-core/discovery/discoverydevice.h index 4895eaba..8394a48a 100644 --- a/libnymea-app-core/discovery/discoverydevice.h +++ b/libnymea-app-core/discovery/discoverydevice.h @@ -90,7 +90,11 @@ public: Q_INVOKABLE Connection* find(const QUrl &url) const; Q_INVOKABLE Connection* get(int index) const; + Connection *bestMatch() const; + signals: + void connectionAdded(Connection *connection); + void connectionRemoved(Connection *connection); void countChanged(); protected: diff --git a/libnymea-app-core/discovery/discoverymodel.cpp b/libnymea-app-core/discovery/discoverymodel.cpp index d730a316..25c7825d 100644 --- a/libnymea-app-core/discovery/discoverymodel.cpp +++ b/libnymea-app-core/discovery/discoverymodel.cpp @@ -61,6 +61,7 @@ void DiscoveryModel::addDevice(DiscoveryDevice *device) beginInsertRows(QModelIndex(), m_devices.count(), m_devices.count()); m_devices.append(device); endInsertRows(); + emit deviceAdded(device); emit countChanged(); } @@ -74,6 +75,7 @@ void DiscoveryModel::removeDevice(DiscoveryDevice *device) beginRemoveRows(QModelIndex(), idx, idx); m_devices.takeAt(idx); endRemoveRows(); + emit deviceRemoved(device); emit countChanged(); } diff --git a/libnymea-app-core/discovery/discoverymodel.h b/libnymea-app-core/discovery/discoverymodel.h index 47de7faa..bb677bd1 100644 --- a/libnymea-app-core/discovery/discoverymodel.h +++ b/libnymea-app-core/discovery/discoverymodel.h @@ -54,6 +54,8 @@ public: void clearModel(); signals: + void deviceAdded(DiscoveryDevice* device); + void deviceRemoved(DiscoveryDevice* device); void countChanged(); protected: diff --git a/libnymea-app-core/discovery/nymeadiscovery.cpp b/libnymea-app-core/discovery/nymeadiscovery.cpp index d2aed28f..26b90d31 100644 --- a/libnymea-app-core/discovery/nymeadiscovery.cpp +++ b/libnymea-app-core/discovery/nymeadiscovery.cpp @@ -7,10 +7,32 @@ #include #include #include +#include +#include NymeaDiscovery::NymeaDiscovery(QObject *parent) : QObject(parent) { m_discoveryModel = new DiscoveryModel(this); + connect(m_discoveryModel, &DiscoveryModel::deviceAdded, this, [this](DiscoveryDevice *device) { + if (device->uuid() != m_pendingHostResolution) { + return; + } + Connection *c = device->connections()->bestMatch(); + if (!c) { + qDebug() << "Host found but there isn't a valid candidate yet?"; + connect(device->connections(), &Connections::connectionAdded, this, [this, device](Connection *connection) { + if (device->uuid() == m_pendingHostResolution) { + qDebug() << "Host" << m_pendingHostResolution << "resolved to" << connection->url().toString(); + m_pendingHostResolution = QUuid(); + emit serverUuidResolved(connection->url().toString()); + } + }); + return; + } + qDebug() << "Host" << m_pendingHostResolution << "appeared! Best match is" << c->url(); + m_pendingHostResolution = QUuid(); + emit serverUuidResolved(c->url().toString()); + }); m_upnp = new UpnpDiscovery(m_discoveryModel, this); m_zeroConf = new ZeroconfDiscovery(m_discoveryModel, this); @@ -25,6 +47,20 @@ NymeaDiscovery::NymeaDiscovery(QObject *parent) : QObject(parent) m_awsClient->fetchDevices(); } }); + + + QNetworkConfigurationManager manager; + QList configs = manager.allConfigurations(QNetworkConfiguration::Active); + + foreach (const QNetworkConfiguration &config, configs) { + if (config.purpose() != QNetworkConfiguration::PublicPurpose) { + continue; + } + if (config.bearerType() != QNetworkConfiguration::BearerWLAN && config.bearerType() != QNetworkConfiguration::BearerEthernet) { + continue; + } + qDebug() << "Have Network configuration:" << config.name() << config.bearerTypeName() << config.purpose() << config.type(); + } } bool NymeaDiscovery::discovering() const @@ -102,6 +138,25 @@ void NymeaDiscovery::setAwsClient(AWSClient *awsClient) } } +void NymeaDiscovery::resolveServerUuid(const QUuid &uuid) +{ + // Do we already know this host? + DiscoveryDevice *dev = m_discoveryModel->find(uuid); + if (!dev) { + qDebug() << "Host" << uuid << "not known yet..."; + m_pendingHostResolution = uuid; + return; + } + Connection *c = dev->connections()->bestMatch(); + if (!c) { + qDebug() << "Host" << uuid << "is known but doesn't have a usable connection option yet."; + m_pendingHostResolution = uuid; + return; + } + qDebug() << "Host" << uuid << "is known. Best match is" << c->url(); + emit serverUuidResolved(c->url().toString()); +} + void NymeaDiscovery::syncCloudDevices() { for (int i = 0; i < m_awsClient->awsDevices()->rowCount(); i++) { diff --git a/libnymea-app-core/discovery/nymeadiscovery.h b/libnymea-app-core/discovery/nymeadiscovery.h index fd451aa4..0a88b994 100644 --- a/libnymea-app-core/discovery/nymeadiscovery.h +++ b/libnymea-app-core/discovery/nymeadiscovery.h @@ -3,6 +3,7 @@ #include #include +#include #include "connection/awsclient.h" @@ -31,10 +32,14 @@ public: AWSClient* awsClient() const; void setAwsClient(AWSClient *awsClient); + Q_INVOKABLE void resolveServerUuid(const QUuid &uuid); + signals: void discoveringChanged(); void awsClientChanged(); + void serverUuidResolved(const QString &url); + private slots: void syncCloudDevices(); @@ -49,6 +54,8 @@ private: QTimer m_cloudPollTimer; + QUuid m_pendingHostResolution; + }; #endif // NYMEADISCOVERY_H diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml index 227dfc9d..8ca953f6 100644 --- a/nymea-app/ui/Nymea.qml +++ b/nymea-app/ui/Nymea.qml @@ -52,6 +52,13 @@ ApplicationWindow { anchors.fill: parent } + NymeaDiscovery { + id: discovery + objectName: "discovery" + awsClient: AWSClient + discovering: pageStack.currentItem.objectName === "discoveryPage" + } + onClosing: { rootItem.handleCloseEvent(close) } diff --git a/nymea-app/ui/connection/ConnectPage.qml b/nymea-app/ui/connection/ConnectPage.qml index b8b5fde0..cd3c855a 100644 --- a/nymea-app/ui/connection/ConnectPage.qml +++ b/nymea-app/ui/connection/ConnectPage.qml @@ -11,16 +11,27 @@ Page { readonly property bool haveHosts: discovery.discoveryModel.count > 0 Component.onCompleted: { - print("completed connectPage for tab", connectionTabIndex, "last connected host:", tabSettings.lastConnectedHost) - if (tabSettings.lastConnectedHost.length > 0 && engine.connection.connect(tabSettings.lastConnectedHost)) { - var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml")) - page.cancel.connect(function() { - engine.connection.disconnect(); - pageStack.pop(root, StackView.Immediate); - pageStack.push(discoveryPage) - }) - } else { + print("completed connectPage. last connected host:", settings.lastConnectedHost) + if (settings.lastConnectedHost.length > 0) { + discovery.resolveServerUuid(settings.lastConnectedHost) + } + +// if (settings.lastConnectedHost.length > 0 && Engine.connection.connect(tabSettings.lastConnectedHost)) { +// var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml")) +// page.cancel.connect(function() { +// Engine.connection.disconnect(); +// pageStack.pop(root, StackView.Immediate); +// pageStack.push(discoveryPage) +// }) +// } else { pageStack.push(discoveryPage) +// } + } + + Connections { + target: discovery + onServerUuidResolved: { + connectToHost(url); } } @@ -34,12 +45,12 @@ Page { engine.connection.connect(url) } - NymeaDiscovery { - id: discovery - objectName: "discovery" - awsClient: AWSClient - discovering: pageStack.currentItem.objectName === "discoveryPage" - } +// NymeaDiscovery { +// id: discovery +// objectName: "discovery" +// awsClient: AWSClient +// discovering: pageStack.currentItem.objectName === "discoveryPage" +// } Connections { target: engine.connection