intermediate commit.

Working pretty well now. No cleanup done. some broken menu entries related to
connect.

TBC
This commit is contained in:
Michael Zanetti 2019-02-05 13:52:08 +01:00
parent 4d7400d1c7
commit 22dd3fe27d
40 changed files with 1053 additions and 950 deletions

View File

@ -866,25 +866,25 @@ bool AWSClient::postToMQTT(const QString &boxId, const QString &timestamp, std::
request.setUrl("https://" + m_configs.value(m_usedConfig).mqttEndpoint + path1); request.setUrl("https://" + m_configs.value(m_usedConfig).mqttEndpoint + path1);
qDebug() << "Posting to MQTT:" << request.url().toString(); qDebug() << "Posting to MQTT:" << request.url().toString();
qDebug() << "HEADERS:"; // qDebug() << "HEADERS:";
foreach (const QByteArray &headerName, request.rawHeaderList()) { // foreach (const QByteArray &headerName, request.rawHeaderList()) {
qDebug() << headerName << ":" << request.rawHeader(headerName); // qDebug() << headerName << ":" << request.rawHeader(headerName);
} // }
qDebug() << "Payload:" << payload; // qDebug() << "Payload:" << payload;
QNetworkReply *reply = m_nam->post(request, payload); QNetworkReply *reply = m_nam->post(request, payload);
connect(reply, &QNetworkReply::finished, this, [reply, callback]() { connect(reply, &QNetworkReply::finished, this, [reply, callback]() {
reply->deleteLater(); reply->deleteLater();
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
qDebug() << "post reply" << data; // qDebug() << "MQTT post reply" << data;
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
qWarning() << "Network reply error" << reply->error() << reply->errorString(); qWarning() << "MQTT Network reply error" << reply->error() << reply->errorString();
callback(false); callback(false);
return; return;
} }
QJsonParseError error; QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) { if (error.error != QJsonParseError::NoError) {
qWarning() << "Failed to parse reply" << error.error << error.errorString() << data; qWarning() << "Failed to parse MQTT reply" << error.error << error.errorString() << data;
callback(false); callback(false);
return; return;
} }

View File

@ -43,6 +43,7 @@ bool BluetoothTransport::connect(const QUrl &url)
qWarning() << "BluetoothInterface: Cannot connect. Invalid scheme in url" << url.toString(); qWarning() << "BluetoothInterface: Cannot connect. Invalid scheme in url" << url.toString();
return false; return false;
} }
m_url = url;
QUrlQuery query(url); QUrlQuery query(url);
QString macAddressString = query.queryItemValue("mac"); QString macAddressString = query.queryItemValue("mac");
@ -54,6 +55,11 @@ bool BluetoothTransport::connect(const QUrl &url)
return true; return true;
} }
QUrl BluetoothTransport::url() const
{
return m_url;
}
void BluetoothTransport::disconnect() void BluetoothTransport::disconnect()
{ {
m_socket->close(); m_socket->close();

View File

@ -24,6 +24,7 @@
#define BLUETOOTHTRANSPORT_H #define BLUETOOTHTRANSPORT_H
#include <QObject> #include <QObject>
#include <QUrl>
#include <QBluetoothSocket> #include <QBluetoothSocket>
#include "nymeatransportinterface.h" #include "nymeatransportinterface.h"
@ -42,11 +43,13 @@ public:
explicit BluetoothTransport(QObject *parent = nullptr); explicit BluetoothTransport(QObject *parent = nullptr);
bool connect(const QUrl &url) override; bool connect(const QUrl &url) override;
QUrl url() const override;
void disconnect() override; void disconnect() override;
ConnectionState connectionState() const override; ConnectionState connectionState() const override;
void sendData(const QByteArray &data) override; void sendData(const QByteArray &data) override;
private: private:
QUrl m_url;
QBluetoothSocket *m_socket = nullptr; QBluetoothSocket *m_socket = nullptr;
QBluetoothServiceInfo m_service; QBluetoothServiceInfo m_service;

View File

@ -49,6 +49,7 @@ bool CloudTransport::connect(const QUrl &url)
} }
qDebug() << "Connecting to" << url; qDebug() << "Connecting to" << url;
m_url = url;
m_timestamp = QDateTime::currentDateTime(); m_timestamp = QDateTime::currentDateTime();
bool postResult = m_awsClient->postToMQTT(url.host(), QString::number(m_timestamp.toMSecsSinceEpoch()), [this](bool success) { bool postResult = m_awsClient->postToMQTT(url.host(), QString::number(m_timestamp.toMSecsSinceEpoch()), [this](bool success) {
@ -68,6 +69,11 @@ bool CloudTransport::connect(const QUrl &url)
return true; return true;
} }
QUrl CloudTransport::url() const
{
return m_url;
}
void CloudTransport::disconnect() void CloudTransport::disconnect()
{ {
qDebug() << "CloudTransport: Disconnecting from server."; qDebug() << "CloudTransport: Disconnecting from server.";

View File

@ -4,6 +4,7 @@
#include "nymeatransportinterface.h" #include "nymeatransportinterface.h"
#include <QObject> #include <QObject>
#include <QUrl>
class AWSClient; class AWSClient;
namespace remoteproxyclient { namespace remoteproxyclient {
@ -25,12 +26,14 @@ public:
explicit CloudTransport(AWSClient *awsClient, QObject *parent = nullptr); explicit CloudTransport(AWSClient *awsClient, QObject *parent = nullptr);
bool connect(const QUrl &url) override; bool connect(const QUrl &url) override;
QUrl url() const override;
void disconnect() override; void disconnect() override;
ConnectionState connectionState() const override; ConnectionState connectionState() const override;
void sendData(const QByteArray &data) override; void sendData(const QByteArray &data) override;
void ignoreSslErrors(const QList<QSslError> &errors) override; void ignoreSslErrors(const QList<QSslError> &errors) override;
private: private:
QUrl m_url;
AWSClient *m_awsClient = nullptr; AWSClient *m_awsClient = nullptr;
remoteproxyclient::RemoteProxyConnection *m_remoteproxyConnection = nullptr; remoteproxyclient::RemoteProxyConnection *m_remoteproxyConnection = nullptr;
QDateTime m_timestamp; QDateTime m_timestamp;

View File

@ -1,13 +1,13 @@
#include "bluetoothservicediscovery.h" #include "bluetoothservicediscovery.h"
#include "discoverymodel.h" #include "../nymeahosts.h"
#include "discoverydevice.h" #include "../nymeahost.h"
#include <QTimer> #include <QTimer>
BluetoothServiceDiscovery::BluetoothServiceDiscovery(DiscoveryModel *discoveryModel, QObject *parent) : BluetoothServiceDiscovery::BluetoothServiceDiscovery(NymeaHosts *nymeaHosts, QObject *parent) :
QObject(parent), QObject(parent),
m_discoveryModel(discoveryModel) m_nymeaHosts(nymeaHosts)
{ {
m_nymeaServiceUuid = QBluetoothUuid(QUuid("997936b5-d2cd-4c57-b41b-c6048320cd2b")); m_nymeaServiceUuid = QBluetoothUuid(QUuid("997936b5-d2cd-4c57-b41b-c6048320cd2b"));
@ -29,7 +29,7 @@ bool BluetoothServiceDiscovery::available() const
if (!m_localDevice) if (!m_localDevice)
return false; return false;
return m_localDevice->isValid() && !m_localDevice->hostMode() != QBluetoothLocalDevice::HostPoweredOff; return m_localDevice->isValid() && m_localDevice->hostMode() != QBluetoothLocalDevice::HostPoweredOff;
} }
void BluetoothServiceDiscovery::discover() void BluetoothServiceDiscovery::discover()
@ -101,15 +101,15 @@ void BluetoothServiceDiscovery::onServiceDiscovered(const QBluetoothServiceInfo
if (serviceInfo.serviceClassUuids().first() == QBluetoothUuid(QUuid("997936b5-d2cd-4c57-b41b-c6048320cd2b"))) { if (serviceInfo.serviceClassUuids().first() == QBluetoothUuid(QUuid("997936b5-d2cd-4c57-b41b-c6048320cd2b"))) {
qDebug() << "BluetoothServiceDiscovery: Found nymea rfcom service!"; qDebug() << "BluetoothServiceDiscovery: Found nymea rfcom service!";
// DiscoveryDevice* device = m_discoveryModel->find(serviceInfo.device().address()); // NymeaHost* host = m_nymeaHosts->find(serviceInfo.device().address());
// if (!device) { // if (!host) {
// device = new DiscoveryDevice(DiscoveryDevice::DeviceTypeBluetooth, this); // host = new DiscoveryDevice(DiscoveryDevice::DeviceTypeBluetooth, this);
// qDebug() << "BluetoothServiceDiscovery: Adding new bluetooth host to model"; // qDebug() << "BluetoothServiceDiscovery: Adding new bluetooth host to model";
// device->setName(QString("%1 (%2)").arg(serviceInfo.serviceName()).arg(serviceInfo.device().name())); // host->setName(QString("%1 (%2)").arg(serviceInfo.serviceName()).arg(serviceInfo.device().name()));
//// device->setBluetoothAddress(serviceInfo.device().address()); //// device->setBluetoothAddress(serviceInfo.device().address());
// PortConfig pc; // PortConfig pc;
// m_discoveryModel->addDevice(device); // m_nymeaHosts->addHost(device);
// } // }
} }
} }

View File

@ -6,13 +6,13 @@
#include <QBluetoothLocalDevice> #include <QBluetoothLocalDevice>
#include <QBluetoothServiceDiscoveryAgent> #include <QBluetoothServiceDiscoveryAgent>
class DiscoveryModel; class NymeaHosts;
class BluetoothServiceDiscovery : public QObject class BluetoothServiceDiscovery : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit BluetoothServiceDiscovery(DiscoveryModel *discoveryModel, QObject *parent = nullptr); explicit BluetoothServiceDiscovery(NymeaHosts *nymeaHosts, QObject *parent = nullptr);
bool discovering() const; bool discovering() const;
bool available() const; bool available() const;
@ -21,7 +21,7 @@ public:
Q_INVOKABLE void stopDiscovery(); Q_INVOKABLE void stopDiscovery();
private: private:
DiscoveryModel *m_discoveryModel = nullptr; NymeaHosts *m_nymeaHosts = nullptr;
QBluetoothLocalDevice *m_localDevice = nullptr; QBluetoothLocalDevice *m_localDevice = nullptr;
QBluetoothServiceDiscoveryAgent *m_serviceDiscovery = nullptr; QBluetoothServiceDiscoveryAgent *m_serviceDiscovery = nullptr;
QBluetoothUuid m_nymeaServiceUuid; QBluetoothUuid m_nymeaServiceUuid;

View File

@ -0,0 +1,227 @@
#include "nymeadiscovery.h"
#include "upnpdiscovery.h"
#include "zeroconfdiscovery.h"
#include "bluetoothservicediscovery.h"
#include "connection/awsclient.h"
#include "../nymeahost.h"
#include <QUuid>
#include <QBluetoothUuid>
#include <QUrlQuery>
#include <QSettings>
#include <QNetworkConfigurationManager>
#include <QNetworkSession>
NymeaDiscovery::NymeaDiscovery(QObject *parent) : QObject(parent)
{
m_nymeaHosts = new NymeaHosts(this);
loadFromDisk();
m_upnp = new UpnpDiscovery(m_nymeaHosts, this);
m_zeroConf = new ZeroconfDiscovery(m_nymeaHosts, this);
#ifndef Q_OS_IOS
m_bluetooth = new BluetoothServiceDiscovery(m_nymeaHosts, this);
#endif
m_cloudPollTimer.setInterval(5000);
connect(&m_cloudPollTimer, &QTimer::timeout, this, [this](){
if (m_awsClient && m_awsClient->isLoggedIn()) {
m_awsClient->fetchDevices();
}
});
}
NymeaDiscovery::~NymeaDiscovery()
{
}
bool NymeaDiscovery::discovering() const
{
return m_discovering;
}
void NymeaDiscovery::setDiscovering(bool discovering)
{
if (m_discovering == discovering)
return;
m_discovering = discovering;
// If we have zeroconf skip upnp. ZeroConf will not do an active discovery and if it's available it'll always have good data
if (!m_zeroConf->available()) {
if (discovering) {
m_upnp->discover();
} else {
m_upnp->stopDiscovery();
}
}
if (discovering) {
// If there's no Zeroconf, use UPnP instead
if (!m_zeroConf->available()) {
m_upnp->discover();
}
// Always start Bluetooth discovery if HW is available
if (m_bluetooth) {
m_bluetooth->discover();
}
// start polling cloud
m_cloudPollTimer.start();
// If we're logged in, poll right away
if (m_awsClient && m_awsClient->isLoggedIn()) {
m_awsClient->fetchDevices();
}
} else {
if (!m_zeroConf->available()) {
m_upnp->stopDiscovery();
}
if (m_bluetooth) {
m_bluetooth->stopDiscovery();
}
m_cloudPollTimer.stop();
}
emit discoveringChanged();
}
NymeaHosts *NymeaDiscovery::nymeaHosts() const
{
return m_nymeaHosts;
}
AWSClient *NymeaDiscovery::awsClient() const
{
return m_awsClient;
}
void NymeaDiscovery::setAwsClient(AWSClient *awsClient)
{
if (m_awsClient != awsClient) {
m_awsClient = awsClient;
emit awsClientChanged();
}
if (m_awsClient) {
m_awsClient->fetchDevices();
connect(m_awsClient, &AWSClient::devicesFetched, this, &NymeaDiscovery::syncCloudDevices);
}
}
void NymeaDiscovery::cacheHost(NymeaHost *host)
{
QSettings settings;
settings.beginGroup("HostCache");
settings.remove(host->uuid().toString());
settings.beginGroup(host->uuid().toString());
settings.setValue("name", host->name());
QList<Connection*> connections;
Connection *remoteConnection = host->connections()->bestMatch(Connection::BearerTypeCloud);
if (remoteConnection) {
connections.append(remoteConnection);
}
Connection *lanConnection = host->connections()->bestMatch(Connection::BearerTypeWifi | Connection::BearerTypeEthernet);
if (lanConnection) {
connections.append(lanConnection);
}
Connection *btConnection = host->connections()->bestMatch(Connection::BearerTypeBluetooth);
if (btConnection) {
connections.append(btConnection);
}
int i = 0;
foreach (Connection *connection, connections) {
settings.beginGroup(QString::number(i++));
settings.setValue("url", connection->url());
settings.setValue("bearerType", connection->bearerType());
settings.value("secure", connection->secure());
settings.setValue("displayName", connection->displayName());
settings.endGroup();
}
settings.endGroup();
}
void NymeaDiscovery::syncCloudDevices()
{
for (int i = 0; i < m_awsClient->awsDevices()->rowCount(); i++) {
AWSDevice *d = m_awsClient->awsDevices()->get(i);
NymeaHost *host = m_nymeaHosts->find(d->id());
if (!host) {
host = new NymeaHost();
host->setUuid(d->id());
host->setName(d->name());
qDebug() << "CloudDiscovery: Adding new host:" << host->name() << host->uuid().toString();
m_nymeaHosts->addHost(host);
}
QUrl url;
url.setScheme("cloud");
url.setHost(d->id());
Connection *conn = host->connections()->find(url);
if (!conn) {
conn = new Connection(url, Connection::BearerTypeCloud, true, d->id());
qDebug() << "CloudDiscovery: Adding new connection to host:" << host->name() << conn->url().toString();
host->connections()->addConnection(conn);
}
conn->setOnline(d->online());
}
QList<NymeaHost*> hostsToRemove;
for (int i = 0; i < m_nymeaHosts->rowCount(); i++) {
NymeaHost *host = m_nymeaHosts->get(i);
for (int j = 0; j < host->connections()->rowCount(); j++) {
if (host->connections()->get(j)->bearerType() == Connection::BearerTypeCloud) {
if (m_awsClient->awsDevices()->getDevice(host->uuid().toString()) == nullptr) {
host->connections()->removeConnection(j);
break;
}
}
}
if (host->connections()->rowCount() == 0) {
hostsToRemove.append(host);
}
}
while (!hostsToRemove.isEmpty()) {
m_nymeaHosts->removeHost(hostsToRemove.takeFirst());
}
}
void NymeaDiscovery::loadFromDisk()
{
QSettings settings;
settings.beginGroup("HostCache");
foreach (const QString &serverUuid, settings.childGroups()) {
settings.beginGroup(serverUuid);
NymeaHost* host = m_nymeaHosts->find(QUuid(serverUuid));
if (!host) {
host = new NymeaHost(m_nymeaHosts);
host->setName(settings.value("name").toString());
host->setUuid(QUuid(serverUuid));
m_nymeaHosts->addHost(host);
}
qDebug() << "Loaded Host from cache" << host->name() << host->uuid();
foreach (const QString &group, settings.childGroups()) {
settings.beginGroup(group);
QString url = settings.value("url").toString();
Connection* connection = host->connections()->find(url);
if (!connection) {
Connection::BearerType bearerType = static_cast<Connection::BearerType>(settings.value("bearerType").toInt());
bool secure = settings.value("secure").toBool();
QString displayName = settings.value("displayName").toString();
connection = new Connection(url, bearerType, secure, displayName, host);
host->connections()->addConnection(connection);
qDebug() << "|- Connection:" << group << connection->url() << connection->bearerType() << "secure:" << connection->secure();
}
settings.endGroup();
}
settings.endGroup();
}
}
void NymeaDiscovery::updateActiveBearers()
{
}

View File

@ -6,8 +6,9 @@
#include <QUuid> #include <QUuid>
#include "connection/awsclient.h" #include "connection/awsclient.h"
#include "connection/nymeahost.h"
class DiscoveryModel; class NymeaHosts;
class UpnpDiscovery; class UpnpDiscovery;
class ZeroconfDiscovery; class ZeroconfDiscovery;
class BluetoothServiceDiscovery; class BluetoothServiceDiscovery;
@ -17,22 +18,23 @@ class NymeaDiscovery : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool discovering READ discovering WRITE setDiscovering NOTIFY discoveringChanged) Q_PROPERTY(bool discovering READ discovering WRITE setDiscovering NOTIFY discoveringChanged)
Q_PROPERTY(DiscoveryModel *discoveryModel READ discoveryModel CONSTANT)
Q_PROPERTY(AWSClient* awsClient READ awsClient WRITE setAwsClient NOTIFY awsClientChanged) Q_PROPERTY(AWSClient* awsClient READ awsClient WRITE setAwsClient NOTIFY awsClientChanged)
Q_PROPERTY(NymeaHosts* nymeaHosts READ nymeaHosts CONSTANT)
public: public:
explicit NymeaDiscovery(QObject *parent = nullptr); explicit NymeaDiscovery(QObject *parent = nullptr);
~NymeaDiscovery();
bool discovering() const; bool discovering() const;
void setDiscovering(bool discovering); void setDiscovering(bool discovering);
DiscoveryModel *discoveryModel() const; NymeaHosts *nymeaHosts() const;
AWSClient* awsClient() const; AWSClient* awsClient() const;
void setAwsClient(AWSClient *awsClient); void setAwsClient(AWSClient *awsClient);
Q_INVOKABLE void resolveServerUuid(const QUuid &uuid); Q_INVOKABLE void cacheHost(NymeaHost* host);
signals: signals:
void discoveringChanged(); void discoveringChanged();
@ -43,14 +45,19 @@ signals:
private slots: private slots:
void syncCloudDevices(); void syncCloudDevices();
void loadFromDisk();
void updateActiveBearers();
private: private:
bool m_discovering = false; bool m_discovering = false;
DiscoveryModel *m_discoveryModel = nullptr; NymeaHosts *m_nymeaHosts = nullptr;
AWSClient *m_awsClient = nullptr;
UpnpDiscovery *m_upnp = nullptr; UpnpDiscovery *m_upnp = nullptr;
ZeroconfDiscovery *m_zeroConf = nullptr; ZeroconfDiscovery *m_zeroConf = nullptr;
BluetoothServiceDiscovery *m_bluetooth = nullptr; BluetoothServiceDiscovery *m_bluetooth = nullptr;
AWSClient *m_awsClient = nullptr;
QTimer m_cloudPollTimer; QTimer m_cloudPollTimer;

View File

@ -25,9 +25,9 @@
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QNetworkInterface> #include <QNetworkInterface>
UpnpDiscovery::UpnpDiscovery(DiscoveryModel *discoveryModel, QObject *parent) : UpnpDiscovery::UpnpDiscovery(NymeaHosts *nymeaHosts, QObject *parent) :
QObject(parent), QObject(parent),
m_discoveryModel(discoveryModel) m_nymeaHosts(nymeaHosts)
{ {
m_networkAccessManager = new QNetworkAccessManager(this); m_networkAccessManager = new QNetworkAccessManager(this);
connect(m_networkAccessManager, &QNetworkAccessManager::finished, this, &UpnpDiscovery::networkReplyFinished); connect(m_networkAccessManager, &QNetworkAccessManager::finished, this, &UpnpDiscovery::networkReplyFinished);
@ -240,12 +240,12 @@ void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
// qDebug() << "discovered device" << uuid << name << discoveredAddress << version << connections << data; // qDebug() << "discovered device" << uuid << name << discoveredAddress << version << connections << data;
DiscoveryDevice* device = m_discoveryModel->find(uuid); NymeaHost* device = m_nymeaHosts->find(uuid);
if (!device) { if (!device) {
device = new DiscoveryDevice(m_discoveryModel); device = new NymeaHost(m_nymeaHosts);
device->setUuid(uuid); device->setUuid(uuid);
qDebug() << "UPnP: Adding new host to model"; qDebug() << "UPnP: Adding new host to model";
m_discoveryModel->addDevice(device); m_nymeaHosts->addHost(device);
} }
device->setName(name); device->setName(name);
device->setVersion(version); device->setVersion(version);

View File

@ -27,14 +27,14 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QTimer> #include <QTimer>
#include "discoverydevice.h" #include "../nymeahost.h"
#include "discoverymodel.h" #include "../nymeahosts.h"
class UpnpDiscovery : public QObject class UpnpDiscovery : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit UpnpDiscovery(DiscoveryModel *discoveryModel, QObject *parent = 0); explicit UpnpDiscovery(NymeaHosts *nymeaHosts, QObject *parent = nullptr);
bool discovering() const; bool discovering() const;
@ -49,7 +49,7 @@ private:
QTimer m_repeatTimer; QTimer m_repeatTimer;
DiscoveryModel *m_discoveryModel; NymeaHosts *m_nymeaHosts;
QHash<QNetworkReply *, QHostAddress> m_runningReplies; QHash<QNetworkReply *, QHostAddress> m_runningReplies;
QList<QUrl> m_foundDevices; QList<QUrl> m_foundDevices;
@ -57,7 +57,7 @@ private:
signals: signals:
void discoveringChanged(); void discoveringChanged();
void availableChanged(); void availableChanged();
void discoveryModelChanged(); void nymeaHostsChanged();
private slots: private slots:
void writeDiscoveryPacket(); void writeDiscoveryPacket();

View File

@ -2,11 +2,11 @@
#include <QUuid> #include <QUuid>
#include "discoverydevice.h" #include "../nymeahost.h"
ZeroconfDiscovery::ZeroconfDiscovery(DiscoveryModel *discoveryModel, QObject *parent) : ZeroconfDiscovery::ZeroconfDiscovery(NymeaHosts *nymeaHosts, QObject *parent) :
QObject(parent), QObject(parent),
m_discoveryModel(discoveryModel) m_nymeaHosts(nymeaHosts)
{ {
#ifdef WITH_ZEROCONF #ifdef WITH_ZEROCONF
// NOTE: There seem to be too many issues in QtZeroConf and IPv6. // NOTE: There seem to be too many issues in QtZeroConf and IPv6.
@ -70,6 +70,16 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
return; return;
} }
// Workaround a bug in deeper layers (I believe it's avahi, but could be QtZeroConf too):
// Sometimes the ip() field contains an IPv6 address. In that case the entry is likely garbage as
// it does not mean the host necessarily exports the services on IPv6.
bool isIPv4;
entry.ip().toIPv4Address(&isIPv4);
if (!isIPv4) {
qDebug() << "Skipping invalid Avahi entry: IPv4:" << entry.ip();
return;
}
// qDebug() << "zeroconf service discovered" << entry.type() << entry.name() << " IP:" << entry.ip() << "IPv6:" << entry.ipv6() << entry.txt(); // qDebug() << "zeroconf service discovered" << entry.type() << entry.name() << " IP:" << entry.ip() << "IPv6:" << entry.ipv6() << entry.txt();
QString uuid; QString uuid;
@ -91,18 +101,18 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
version = txtRecord.second; version = txtRecord.second;
} }
} }
qDebug() << "avahi service entry added" << serverName << uuid << sslEnabled; // qDebug() << "avahi service entry added" << serverName << uuid << sslEnabled;
DiscoveryDevice* device = m_discoveryModel->find(uuid); NymeaHost* host = m_nymeaHosts->find(uuid);
if (!device) { if (!host) {
device = new DiscoveryDevice(m_discoveryModel); host = new NymeaHost(m_nymeaHosts);
device->setUuid(uuid); host->setUuid(uuid);
qDebug() << "ZeroConf: Adding new host:" << serverName << uuid; qDebug() << "ZeroConf: Adding new host:" << serverName << uuid;
m_discoveryModel->addDevice(device); m_nymeaHosts->addHost(host);
} }
device->setName(serverName); host->setName(serverName);
device->setVersion(version); host->setVersion(version);
QUrl url; QUrl url;
// NOTE: On linux this is "_jsonrpc._tcp" while on apple systems this is "_jsonrpc._tcp." // NOTE: On linux this is "_jsonrpc._tcp" while on apple systems this is "_jsonrpc._tcp."
if (entry.type().startsWith("_jsonrpc._tcp")) { if (entry.type().startsWith("_jsonrpc._tcp")) {
@ -112,12 +122,12 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
} }
url.setHost(!entry.ip().isNull() ? entry.ip().toString() : entry.ipv6().toString()); url.setHost(!entry.ip().isNull() ? entry.ip().toString() : entry.ipv6().toString());
url.setPort(entry.port()); url.setPort(entry.port());
if (!device->connections()->find(url)){ if (!host->connections()->find(url)){
qDebug() << "Zeroconf: Adding new connection to host:" << device->name() << url.toString(); qDebug() << "Zeroconf: Adding new connection to host:" << host->name() << url.toString();
QString displayName = QString("%1:%2").arg(url.host()).arg(url.port()); QString displayName = QString("%1:%2").arg(url.host()).arg(url.port());
Connection *connection = new Connection(url, Connection::BearerTypeWifi, sslEnabled, displayName); Connection *connection = new Connection(url, Connection::BearerTypeWifi, sslEnabled, displayName);
connection->setOnline(true); connection->setOnline(true);
device->connections()->addConnection(connection); host->connections()->addConnection(connection);
} }
} }
@ -149,8 +159,8 @@ void ZeroconfDiscovery::serviceEntryRemoved(const QZeroConfService &entry)
// qDebug() << "Zeroconf: Service entry removed" << entry.name(); // qDebug() << "Zeroconf: Service entry removed" << entry.name();
DiscoveryDevice* device = m_discoveryModel->find(uuid); NymeaHost* host = m_nymeaHosts->find(uuid);
if (!device) { if (!host) {
// Nothing to do... // Nothing to do...
return; return;
} }
@ -163,19 +173,19 @@ void ZeroconfDiscovery::serviceEntryRemoved(const QZeroConfService &entry)
} }
url.setHost(!entry.ip().isNull() ? entry.ip().toString() : entry.ipv6().toString()); url.setHost(!entry.ip().isNull() ? entry.ip().toString() : entry.ipv6().toString());
url.setPort(entry.port()); url.setPort(entry.port());
Connection *connection = device->connections()->find(url); Connection *connection = host->connections()->find(url);
if (!connection){ if (!connection){
// Connection url not found... // Connection url not found...
return; return;
} }
// Ok, now we need to remove it // Ok, now we need to remove it
device->connections()->removeConnection(connection); host->connections()->removeConnection(connection);
// And if there aren't any connections left, remove the entire device // And if there aren't any connections left, remove the entire device
if (device->connections()->rowCount() == 0) { if (host->connections()->rowCount() == 0) {
qDebug() << "Zeroconf: Removing connection from host:" << device->name() << url.toString(); qDebug() << "Zeroconf: Removing connection from host:" << host->name() << url.toString();
m_discoveryModel->removeDevice(device); m_nymeaHosts->removeHost(host);
} }
} }
#endif #endif

View File

@ -5,7 +5,7 @@
#include "qzeroconf.h" #include "qzeroconf.h"
#endif #endif
#include "discoverymodel.h" #include "../nymeahosts.h"
#include <QObject> #include <QObject>
@ -14,14 +14,14 @@ class ZeroconfDiscovery : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit ZeroconfDiscovery(DiscoveryModel *discoveryModel, QObject *parent = nullptr); explicit ZeroconfDiscovery(NymeaHosts *nymeaHosts, QObject *parent = nullptr);
~ZeroconfDiscovery(); ~ZeroconfDiscovery();
bool available() const; bool available() const;
bool discovering() const; bool discovering() const;
private: private:
DiscoveryModel *m_discoveryModel; NymeaHosts *m_nymeaHosts;
#ifdef WITH_ZEROCONF #ifdef WITH_ZEROCONF
QZeroConf *m_zeroconfJsonRPC = nullptr; QZeroConf *m_zeroconfJsonRPC = nullptr;

View File

@ -1,4 +1,5 @@
#include "nymeaconnection.h" #include "nymeaconnection.h"
#include "nymeahost.h"
#include <QUrl> #include <QUrl>
#include <QDebug> #include <QDebug>
@ -14,51 +15,18 @@
NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent) NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent)
{ {
} m_networkConfigManager = new QNetworkConfigurationManager(this);
bool NymeaConnection::connect(const QString &url) QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationAdded, this, [this](const QNetworkConfiguration &config){
{ // qDebug() << "Network configuration added:" << config.name() << config.bearerTypeName() << config.purpose();
if (connected()) { updateActiveBearers();
qWarning() << "Already connected. Cannot connect multiple times"; });
return false; QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationRemoved, this, [this](const QNetworkConfiguration &config){
} // qDebug() << "Network configuration removed:" << config.name() << config.bearerTypeName() << config.purpose();
updateActiveBearers();
});
m_currentUrl = QUrl(url); updateActiveBearers();
emit currentUrlChanged();
if (!m_transports.contains(m_currentUrl.scheme())) {
qWarning() << "Cannot connect to urls of scheme" << m_currentUrl.scheme() << "Supported schemes are" << m_transports.keys();
return false;
}
// Create a new transport
m_currentTransport = m_transports.value(m_currentUrl.scheme())->createTransport();
QObject::connect(m_currentTransport, &NymeaTransportInterface::sslErrors, this, &NymeaConnection::onSslErrors);
QObject::connect(m_currentTransport, &NymeaTransportInterface::error, this, &NymeaConnection::onError);
QObject::connect(m_currentTransport, &NymeaTransportInterface::connected, this, &NymeaConnection::onConnected);
QObject::connect(m_currentTransport, &NymeaTransportInterface::disconnected, this, &NymeaConnection::onDisconnected);
QObject::connect(m_currentTransport, &NymeaTransportInterface::dataReady, this, &NymeaConnection::dataAvailable);
// Load any certificate we might have for this url
QByteArray pem;
if (loadPem(m_currentUrl, pem)) {
qDebug() << "Loaded SSL certificate for" << m_currentUrl.host();
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(QSslError::HostNameMismatch);
expectedSslErrors.append(QSslError(QSslError::SelfSignedCertificate, QSslCertificate(pem)));
m_currentTransport->ignoreSslErrors(expectedSslErrors);
}
qDebug() << "Connecting to:" << m_currentUrl;
return m_currentTransport->connect(m_currentUrl);
}
void NymeaConnection::disconnect()
{
if (!m_currentTransport || m_currentTransport->connectionState() == NymeaTransportInterface::ConnectionStateDisconnected) {
qWarning() << "not connected, cannot disconnect";
return;
}
m_currentTransport->disconnect();
} }
void NymeaConnection::acceptCertificate(const QString &url, const QByteArray &pem) void NymeaConnection::acceptCertificate(const QString &url, const QByteArray &pem)
@ -84,30 +52,56 @@ bool NymeaConnection::isTrusted(const QString &url)
return false; return false;
} }
Connection::BearerTypes NymeaConnection::availableBearerTypes() const
{
return m_availableBearerTypes;
}
bool NymeaConnection::connected() bool NymeaConnection::connected()
{ {
return m_currentTransport && m_currentTransport->connectionState() == NymeaTransportInterface::ConnectionStateConnected; return m_currentHost && m_currentTransport && m_currentTransport->connectionState() == NymeaTransportInterface::ConnectionStateConnected;
} }
QString NymeaConnection::url() const NymeaHost *NymeaConnection::currentHost() const
{ {
return m_currentUrl.toString(); return m_currentHost;
} }
QString NymeaConnection::hostAddress() const void NymeaConnection::setCurrentHost(NymeaHost *host)
{ {
return m_currentUrl.host(); if (m_currentHost == host) {
return;
}
if (m_currentTransport) {
m_currentTransport = nullptr;
emit currentConnectionChanged();
emit connectedChanged(false);
}
while (!m_transportCandidates.isEmpty()) {
NymeaTransportInterface *transport = m_transportCandidates.keys().first();
m_transportCandidates.remove(transport);
transport->deleteLater();
}
if (m_currentHost) {
m_currentHost = nullptr;
}
m_currentHost = host;
emit currentHostChanged();
if (m_currentHost) {
connectInternal(m_currentHost);
}
} }
int NymeaConnection::port() const Connection *NymeaConnection::currentConnection() const
{ {
return m_currentUrl.port(); if (!m_currentHost || !m_currentTransport) {
} return nullptr;
}
QString NymeaConnection::bluetoothAddress() const return m_transportCandidates.value(m_currentTransport);
{
QUrlQuery query(m_currentUrl);
return query.queryItemValue("mac");
} }
void NymeaConnection::sendData(const QByteArray &data) void NymeaConnection::sendData(const QByteArray &data)
@ -122,15 +116,16 @@ void NymeaConnection::sendData(const QByteArray &data)
void NymeaConnection::onSslErrors(const QList<QSslError> &errors) void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
{ {
qDebug() << "Connection: SSL errors:" << errors; NymeaTransportInterface *transport = qobject_cast<NymeaTransportInterface*>(sender());
qDebug() << "SSL errors for url:" << transport->url();
QList<QSslError> ignoredErrors; QList<QSslError> ignoredErrors;
foreach (const QSslError &error, errors) { foreach (const QSslError &error, errors) {
qDebug() << error.errorString();
if (error.error() == QSslError::HostNameMismatch) { if (error.error() == QSslError::HostNameMismatch) {
qDebug() << "Ignoring host mismatch on certificate."; qDebug() << "Ignoring host mismatch on certificate.";
ignoredErrors.append(error); ignoredErrors.append(error);
} else if (error.error() == QSslError::SelfSignedCertificate || error.error() == QSslError::CertificateUntrusted) { } else if (error.error() == QSslError::SelfSignedCertificate || error.error() == QSslError::CertificateUntrusted) {
qDebug() << "have a self signed certificate." << error.certificate();
// Check our cert DB // Check our cert DB
QByteArray pem; QByteArray pem;
@ -140,7 +135,7 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
// However, we want to emit verifyConnectionCertificate in any case here. // However, we want to emit verifyConnectionCertificate in any case here.
QSettings settings; QSettings settings;
settings.beginGroup("acceptedCertificates"); settings.beginGroup("acceptedCertificates");
QByteArray storedFingerPrint = settings.value(m_currentUrl.host()).toByteArray(); QByteArray storedFingerPrint = settings.value(transport->url().host()).toByteArray();
settings.endGroup(); settings.endGroup();
QByteArray certificateFingerprint; QByteArray certificateFingerprint;
@ -158,15 +153,18 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
ignoredErrors.append(error); ignoredErrors.append(error);
// Update the config to use the new system: // Update the config to use the new system:
storePem(m_currentUrl, error.certificate().toPem()); storePem(transport->url(), error.certificate().toPem());
// Check new style PEM storage // Check new style PEM storage
} else if (loadPem(m_currentUrl, pem) && pem == error.certificate().toPem()) { } else if (loadPem(transport->url(), pem) && pem == error.certificate().toPem()) {
qDebug() << "Found a SSL certificate for this host. Ignoring error."; qDebug() << "Found a SSL certificate for this host. Ignoring error.";
ignoredErrors.append(error); ignoredErrors.append(error);
// Ok... nothing found... Pop up the message // Ok... nothing found... Pop up the message
} else { } else {
qDebug() << "Host presents an unknown self signed certificate:" << error.certificate();
qDebug() << "Asking user for confirmation.";
QStringList info; QStringList info;
info << tr("Common Name:") << error.certificate().issuerInfo(QSslCertificate::CommonName); info << tr("Common Name:") << error.certificate().issuerInfo(QSslCertificate::CommonName);
info << tr("Oragnisation:") <<error.certificate().issuerInfo(QSslCertificate::Organization); info << tr("Oragnisation:") <<error.certificate().issuerInfo(QSslCertificate::Organization);
@ -177,7 +175,7 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
// info << tr("Name Qualifier:")<< error.certificate().issuerInfo(QSslCertificate::DistinguishedNameQualifier); // info << tr("Name Qualifier:")<< error.certificate().issuerInfo(QSslCertificate::DistinguishedNameQualifier);
// info << tr("Email:")<< error.certificate().issuerInfo(QSslCertificate::EmailAddress); // info << tr("Email:")<< error.certificate().issuerInfo(QSslCertificate::EmailAddress);
emit verifyConnectionCertificate(m_currentUrl.toString(), info, certificateFingerprint, error.certificate().toPem()); emit verifyConnectionCertificate(transport->url().toString(), info, certificateFingerprint, error.certificate().toPem());
} }
} else { } else {
// Reject the connection on all other errors... // Reject the connection on all other errors...
@ -187,38 +185,141 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
if (ignoredErrors == errors) { if (ignoredErrors == errors) {
// Note, due to a workaround in the WebSocketTransport we must not call this // Note, due to a workaround in the WebSocketTransport we must not call this
// unless we've handled all the errors or the websocket will ignore unhandled errors too... // unless we've handled all the errors or the websocket will ignore unhandled errors too...
m_currentTransport->ignoreSslErrors(ignoredErrors); transport->ignoreSslErrors(ignoredErrors);
} }
} }
void NymeaConnection::onError(QAbstractSocket::SocketError error) void NymeaConnection::onError(QAbstractSocket::SocketError error)
{ {
QMetaEnum errorEnum = QMetaEnum::fromType<QAbstractSocket::SocketError>(); QMetaEnum errorEnum = QMetaEnum::fromType<QAbstractSocket::SocketError>();
emit connectionError(errorEnum.valueToKey(error)); QString errorString = errorEnum.valueToKey(error);
NymeaTransportInterface* transport = qobject_cast<NymeaTransportInterface*>(sender());
if (transport == m_currentTransport) {
qDebug() << "Current transport failed:" << error;
// The current transport failed, forward the error
emit connectionError(errorString);
return;
}
if (!m_currentTransport) {
// We're trying to connect and one of the transports failed...
qDebug() << "A transport error happened for" << transport->url() << error;
if (m_transportCandidates.contains(transport)) {
m_transportCandidates.remove(transport);
transport->deleteLater();
}
if (m_transportCandidates.isEmpty()) {
emit connectionError(errorString);
}
}
} }
void NymeaConnection::onConnected() void NymeaConnection::onConnected()
{ {
if (m_currentTransport != sender()) { NymeaTransportInterface* newTransport = qobject_cast<NymeaTransportInterface*>(sender());
qWarning() << "NymeaConnection: An inactive transport is emitting signals... ignoring."; if (!m_currentTransport) {
m_currentTransport = newTransport;
qDebug() << "NymeaConnection: Connected to" << m_currentHost->name() << "via" << m_currentTransport->url();
emit connectedChanged(true);
return;
}
if (m_currentTransport != newTransport) {
qDebug() << "Alternative connection established:" << newTransport->url();
Connection *existingConnection = m_transportCandidates.value(m_currentTransport);
Connection *alternativeConnection = m_transportCandidates.value(newTransport);
if (alternativeConnection->priority() > existingConnection->priority()) {
qDebug() << "New connection has higher priority! Roaming from" << existingConnection->url() << existingConnection->priority() << "to" << alternativeConnection->url() << alternativeConnection->priority();
// m_transportCandidates.remove(m_currentTransport);
// m_currentTransport->deleteLater();
m_currentTransport = newTransport;
} else {
qDebug() << "Connection" << alternativeConnection->url() << alternativeConnection->priority() << "has lower priority than existing" << existingConnection->url() << existingConnection->priority();
m_transportCandidates.remove(newTransport);
newTransport->deleteLater();
}
return; return;
} }
qDebug() << "NymeaConnection: connected.";
emit connectedChanged(true);
} }
void NymeaConnection::onDisconnected() void NymeaConnection::onDisconnected()
{ {
if (m_currentTransport != sender()) { NymeaTransportInterface* t = qobject_cast<NymeaTransportInterface*>(sender());
qWarning() << "NymeaConnection: An inactive transport is emitting signals... ignoring."; if (m_currentTransport != t) {
qWarning() << "NymeaConnection: An inactive transport for url" << t->url() << "disconnected... Cleaning up...";
if (m_transportCandidates.contains(t)) {
m_transportCandidates.remove(t);
}
t->deleteLater();
return; return;
} }
m_transportCandidates.remove(m_currentTransport);
m_currentTransport->deleteLater(); m_currentTransport->deleteLater();
m_currentTransport = nullptr; m_currentTransport = nullptr;
emit currentConnectionChanged();
qDebug() << "NymeaConnection: disconnected."; qDebug() << "NymeaConnection: disconnected.";
emit connectedChanged(false); emit connectedChanged(false);
connectInternal(m_currentHost);
} }
void NymeaConnection::updateActiveBearers()
{
Connection::BearerTypes availableBearerTypes;
QList<QNetworkConfiguration> configs = m_networkConfigManager->allConfigurations(QNetworkConfiguration::Active);
// qDebug() << "Network configuations:" << configs.count();
foreach (const QNetworkConfiguration &config, configs) {
// qDebug() << "Candidate network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName();
availableBearerTypes.setFlag(qBearerTypeToNymeaBearerType(config.bearerType()));
}
// qDebug() << "Available bearers:" << availableBearerTypes;
if (m_availableBearerTypes != availableBearerTypes) {
qDebug() << "Available Bearer Types changed:" << availableBearerTypes;
m_availableBearerTypes = availableBearerTypes;
emit availableBearerTypesChanged();
}
if (!m_currentHost) {
// No host set... Nothing to do...
return;
}
if (!m_currentTransport) {
// There's a host but no connection. Try connecting now...
connectInternal(m_currentHost);
}
}
Connection::BearerType NymeaConnection::qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const
{
switch (type) {
case QNetworkConfiguration::BearerWLAN:
return Connection::BearerTypeWifi;
case QNetworkConfiguration::BearerEthernet:
return Connection::BearerTypeEthernet;
case QNetworkConfiguration::Bearer2G:
case QNetworkConfiguration::BearerCDMA2000:
case QNetworkConfiguration::BearerWCDMA:
case QNetworkConfiguration::BearerHSPA:
case QNetworkConfiguration::BearerWiMAX:
case QNetworkConfiguration::BearerEVDO:
case QNetworkConfiguration::BearerLTE:
case QNetworkConfiguration::Bearer3G:
case QNetworkConfiguration::Bearer4G:
return Connection::BearerTypeCloud;
case QNetworkConfiguration::BearerBluetooth:
return Connection::BearerTypeBluetooth;
default:
qWarning() << "Unhandled Bearer Type Family:" << type;
}
return Connection::BearerTypeNone;
}
bool NymeaConnection::storePem(const QUrl &host, const QByteArray &pem) bool NymeaConnection::storePem(const QUrl &host, const QByteArray &pem)
{ {
QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/sslcerts/"); QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/sslcerts/");
@ -249,6 +350,70 @@ bool NymeaConnection::loadPem(const QUrl &host, QByteArray &pem)
void NymeaConnection::registerTransport(NymeaTransportInterfaceFactory *transportFactory) void NymeaConnection::registerTransport(NymeaTransportInterfaceFactory *transportFactory)
{ {
foreach (const QString &scheme, transportFactory->supportedSchemes()) { foreach (const QString &scheme, transportFactory->supportedSchemes()) {
m_transports[scheme] = transportFactory; m_transportFactories[scheme] = transportFactory;
} }
} }
void NymeaConnection::connect(NymeaHost *nymeaHost)
{
setCurrentHost(nymeaHost);
}
void NymeaConnection::connectInternal(NymeaHost *host)
{
if (m_availableBearerTypes.testFlag(Connection::BearerTypeWifi) || m_availableBearerTypes.testFlag(Connection::BearerTypeEthernet)) {
Connection* lanConnection = host->connections()->bestMatch(Connection::BearerTypeWifi | Connection::BearerTypeEthernet);
if (lanConnection) {
qDebug() << "Best candidate LAN connection:" << lanConnection->url();
connectInternal(lanConnection);
}
}
if (m_availableBearerTypes.testFlag(Connection::BearerTypeCloud)) {
Connection* wanConnection = host->connections()->bestMatch(Connection::BearerTypeCloud);
if (wanConnection) {
qDebug() << "Best candidate WAN connection:" << wanConnection->url();
connectInternal(wanConnection);
}
}
}
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();
return false;
}
if (m_transportCandidates.values().contains(connection)) {
qDebug() << "Already have a connection (or connection attempt) for" << connection->url();
return false;
}
// Create a new transport
NymeaTransportInterface* newTransport = m_transportFactories.value(connection->url().scheme())->createTransport();
QObject::connect(newTransport, &NymeaTransportInterface::sslErrors, this, &NymeaConnection::onSslErrors);
QObject::connect(newTransport, &NymeaTransportInterface::error, this, &NymeaConnection::onError);
QObject::connect(newTransport, &NymeaTransportInterface::connected, this, &NymeaConnection::onConnected);
QObject::connect(newTransport, &NymeaTransportInterface::disconnected, this, &NymeaConnection::onDisconnected);
QObject::connect(newTransport, &NymeaTransportInterface::dataReady, this, &NymeaConnection::dataAvailable);
// Load any certificate we might have for this url
QByteArray pem;
if (loadPem(connection->url(), pem)) {
qDebug() << "Loaded SSL certificate for" << connection->url().host();
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(QSslError::HostNameMismatch);
expectedSslErrors.append(QSslError(QSslError::SelfSignedCertificate, QSslCertificate(pem)));
newTransport->ignoreSslErrors(expectedSslErrors);
}
m_transportCandidates.insert(newTransport, connection);
qDebug() << "Connecting to:" << connection->url();
return newTransport->connect(connection->url());
}
void NymeaConnection::disconnect()
{
setCurrentHost(nullptr);
}

View File

@ -6,6 +6,10 @@
#include <QSslError> #include <QSslError>
#include <QAbstractSocket> #include <QAbstractSocket>
#include <QUrl> #include <QUrl>
#include <QNetworkConfigurationManager>
#include "nymeahost.h"
class NymeaTransportInterface; class NymeaTransportInterface;
class NymeaTransportInterfaceFactory; class NymeaTransportInterfaceFactory;
@ -14,34 +18,37 @@ class NymeaConnection : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged) Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
Q_PROPERTY(QString url READ url NOTIFY currentUrlChanged) Q_PROPERTY(NymeaHost* currentHost READ currentHost WRITE setCurrentHost NOTIFY currentHostChanged)
Q_PROPERTY(QString hostAddress READ hostAddress NOTIFY currentUrlChanged) Q_PROPERTY(Connection* currentConnection READ currentConnection NOTIFY currentConnectionChanged)
Q_PROPERTY(int port READ port NOTIFY currentUrlChanged) Q_PROPERTY(Connection::BearerTypes availableBearerTypes READ availableBearerTypes NOTIFY availableBearerTypesChanged)
Q_PROPERTY(QString bluetoothAddress READ bluetoothAddress NOTIFY currentUrlChanged)
public: public:
explicit NymeaConnection(QObject *parent = nullptr); explicit NymeaConnection(QObject *parent = nullptr);
void registerTransport(NymeaTransportInterfaceFactory *transportFactory); void registerTransport(NymeaTransportInterfaceFactory *transportFactory);
Q_INVOKABLE bool connect(const QString &url); Q_INVOKABLE void connect(NymeaHost* nymeaHost);
Q_INVOKABLE void disconnect(); Q_INVOKABLE void disconnect();
Q_INVOKABLE void acceptCertificate(const QString &url, const QByteArray &pem); Q_INVOKABLE void acceptCertificate(const QString &url, const QByteArray &pem);
Q_INVOKABLE bool isTrusted(const QString &url); Q_INVOKABLE bool isTrusted(const QString &url);
Connection::BearerTypes availableBearerTypes() const;
bool connected(); bool connected();
QString url() const; NymeaHost* currentHost() const;
QString hostAddress() const; void setCurrentHost(NymeaHost *host);
int port() const;
QString bluetoothAddress() const; Connection* currentConnection() const;
void sendData(const QByteArray &data); void sendData(const QByteArray &data);
signals: signals:
void currentUrlChanged(); void availableBearerTypesChanged();
void verifyConnectionCertificate(const QString &url, const QStringList &issuerInfo, const QByteArray &fingerprint, const QByteArray &pem); void verifyConnectionCertificate(const QString &url, const QStringList &issuerInfo, const QByteArray &fingerprint, const QByteArray &pem);
void currentHostChanged();
void connectedChanged(bool connected); void connectedChanged(bool connected);
void currentConnectionChanged();
void connectionError(const QString &error); void connectionError(const QString &error);
void dataAvailable(const QByteArray &data); void dataAvailable(const QByteArray &data);
@ -51,14 +58,24 @@ private slots:
void onConnected(); void onConnected();
void onDisconnected(); void onDisconnected();
void updateActiveBearers();
private: private:
bool storePem(const QUrl &host, const QByteArray &pem); bool storePem(const QUrl &host, const QByteArray &pem);
bool loadPem(const QUrl &host, QByteArray &pem); bool loadPem(const QUrl &host, QByteArray &pem);
void connectInternal(NymeaHost *host);
bool connectInternal(Connection *connection);
Connection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const;
private: private:
QHash<QString, NymeaTransportInterfaceFactory*> m_transports; QNetworkConfigurationManager *m_networkConfigManager = nullptr;
Connection::BearerTypes m_availableBearerTypes = Connection::BearerTypeNone;
QHash<QString, NymeaTransportInterfaceFactory*> m_transportFactories;
QHash<NymeaTransportInterface*, Connection*> m_transportCandidates;
NymeaTransportInterface *m_currentTransport = nullptr; NymeaTransportInterface *m_currentTransport = nullptr;
QUrl m_currentUrl; NymeaHost *m_currentHost = nullptr;
}; };
#endif // NYMEACONNECTION_H #endif // NYMEACONNECTION_H

View File

@ -18,32 +18,41 @@
* * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "discoverydevice.h" #include "nymeahost.h"
#include <QUrl> #include <QUrl>
DiscoveryDevice::DiscoveryDevice(QObject *parent): NymeaHost::NymeaHost(QObject *parent):
QObject(parent), QObject(parent),
m_connections(new Connections(this)) m_connections(new Connections(this))
{ {
connect(m_connections, &Connections::dataChanged, this, [this](const QModelIndex &, const QModelIndex &, const QVector<int>){
emit connectionChanged();
});
connect(m_connections, &Connections::connectionAdded, this, [this](Connection*){
emit connectionChanged();
});
connect(m_connections, &Connections::connectionRemoved, this, [this](Connection*){
emit connectionChanged();
});
} }
QUuid DiscoveryDevice::uuid() const QUuid NymeaHost::uuid() const
{ {
return m_uuid; return m_uuid;
} }
void DiscoveryDevice::setUuid(const QUuid &uuid) void NymeaHost::setUuid(const QUuid &uuid)
{ {
m_uuid = uuid; m_uuid = uuid;
} }
QString DiscoveryDevice::name() const QString NymeaHost::name() const
{ {
return m_name; return m_name;
} }
void DiscoveryDevice::setName(const QString &name) void NymeaHost::setName(const QString &name)
{ {
if (m_name != name) { if (m_name != name) {
m_name = name; m_name = name;
@ -51,12 +60,12 @@ void DiscoveryDevice::setName(const QString &name)
} }
} }
QString DiscoveryDevice::version() const QString NymeaHost::version() const
{ {
return m_version; return m_version;
} }
void DiscoveryDevice::setVersion(const QString &version) void NymeaHost::setVersion(const QString &version)
{ {
if (m_version != version) { if (m_version != version) {
m_version = version; m_version = version;
@ -64,7 +73,7 @@ void DiscoveryDevice::setVersion(const QString &version)
} }
} }
Connections* DiscoveryDevice::connections() const Connections* NymeaHost::connections() const
{ {
return m_connections; return m_connections;
} }
@ -159,40 +168,21 @@ Connection* Connections::get(int index) const
return nullptr; return nullptr;
} }
Connection* Connections::bestMatch() const Connection *Connections::bestMatch(Connection::BearerTypes bearerTypes) const
{ {
QList<Connection::BearerType> bearerPreference = {Connection::BearerTypeEthernet, Connection::BearerTypeWifi, Connection::BearerTypeCloud, Connection::BearerTypeBluetooth, Connection::BearerTypeUnknown}; QList<Connection::BearerType> bearerPreference = {Connection::BearerTypeEthernet, Connection::BearerTypeWifi, Connection::BearerTypeCloud, Connection::BearerTypeBluetooth, Connection::BearerTypeNone};
Connection *best = nullptr; Connection *best = nullptr;
// qDebug() << "Bestmatch" << m_connections.count();
foreach (Connection *c, m_connections) { foreach (Connection *c, m_connections) {
// qDebug() << "have connection:" << bearerTypes << c->url() << bearerTypes.testFlag(c->bearerType());
if (!bearerTypes.testFlag(c->bearerType())) {
continue;
}
if (!best) { if (!best) {
best = c; best = c;
continue; continue;
} }
uint oldBearerPriority = static_cast<uint>(bearerPreference.indexOf(best->bearerType())); if (c->priority() > best->priority()) {
uint newBearerPriority = static_cast<uint>(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; best = c;
} }
} }
@ -252,3 +242,31 @@ void Connection::setOnline(bool online)
emit onlineChanged(); emit onlineChanged();
} }
} }
int Connection::priority() const
{
int prio = 0;
switch(m_bearerType) {
case BearerTypeEthernet:
prio += 400;
break;
case BearerTypeWifi:
prio += 300;
break;
case BearerTypeBluetooth:
prio += 200;
break;
case BearerTypeCloud:
prio += 100;
break;
default:
prio += 0;
}
if (m_secure) {
prio += 10;
}
// if (m_url.scheme().startsWith("nymea")) {
// prio += 5;
// }
return prio;
}

View File

@ -18,8 +18,8 @@
* * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DISCOVERYDEVICE_H #ifndef NYMEAHOST_H
#define DISCOVERYDEVICE_H #define NYMEAHOST_H
#include <QObject> #include <QObject>
#include <QUuid> #include <QUuid>
@ -36,15 +36,19 @@ class Connection: public QObject {
Q_PROPERTY(bool secure READ secure CONSTANT) Q_PROPERTY(bool secure READ secure CONSTANT)
Q_PROPERTY(QString displayName READ displayName CONSTANT) Q_PROPERTY(QString displayName READ displayName CONSTANT)
Q_PROPERTY(bool online READ online NOTIFY onlineChanged) Q_PROPERTY(bool online READ online NOTIFY onlineChanged)
Q_PROPERTY(int priority READ priority NOTIFY priorityChanged)
public: public:
enum BearerType { enum BearerType {
BearerTypeUnknown, BearerTypeNone = 0x00,
BearerTypeWifi, BearerTypeWifi = 0x01,
BearerTypeEthernet, BearerTypeEthernet = 0x02,
BearerTypeBluetooth, BearerTypeBluetooth = 0x04,
BearerTypeCloud BearerTypeCloud = 0x08,
BearerTypeAll = 0xFF
}; };
Q_ENUM(BearerType) Q_ENUM(BearerType)
Q_DECLARE_FLAGS(BearerTypes, BearerType)
Connection(const QUrl &url, BearerType bearerType, bool secure, const QString &displayName, QObject *parent = nullptr); Connection(const QUrl &url, BearerType bearerType, bool secure, const QString &displayName, QObject *parent = nullptr);
@ -54,13 +58,15 @@ public:
QString displayName() const; QString displayName() const;
bool online() const; bool online() const;
void setOnline(bool online); void setOnline(bool online);
int priority() const;
signals: signals:
void onlineChanged(); void onlineChanged();
void priorityChanged();
private: private:
QUrl m_url; QUrl m_url;
BearerType m_bearerType = BearerTypeUnknown; BearerType m_bearerType = BearerTypeNone;
bool m_secure = false; bool m_secure = false;
QString m_displayName; QString m_displayName;
bool m_online = false; bool m_online = false;
@ -89,23 +95,22 @@ public:
Q_INVOKABLE Connection* find(const QUrl &url) const; Q_INVOKABLE Connection* find(const QUrl &url) const;
Q_INVOKABLE Connection* get(int index) const; Q_INVOKABLE Connection* get(int index) const;
Q_INVOKABLE Connection* bestMatch(Connection::BearerTypes bearerTypes = Connection::BearerTypeAll) const;
Connection *bestMatch() const;
signals: signals:
void countChanged();
void connectionAdded(Connection *connection); void connectionAdded(Connection *connection);
void connectionRemoved(Connection *connection); void connectionRemoved(Connection *connection);
void countChanged();
protected: protected:
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
private: private:
QList<Connection*> m_connections; QList<Connection*> m_connections;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(Connection::BearerTypes)
class DiscoveryDevice: public QObject class NymeaHost: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QUuid uuid READ uuid CONSTANT) Q_PROPERTY(QUuid uuid READ uuid CONSTANT)
@ -114,7 +119,7 @@ class DiscoveryDevice: public QObject
Q_PROPERTY(Connections* connections READ connections CONSTANT) Q_PROPERTY(Connections* connections READ connections CONSTANT)
public: public:
explicit DiscoveryDevice(QObject *parent = nullptr); explicit NymeaHost(QObject *parent = nullptr);
QUuid uuid() const; QUuid uuid() const;
void setUuid(const QUuid &uuid); void setUuid(const QUuid &uuid);
@ -130,6 +135,7 @@ public:
signals: signals:
void nameChanged(); void nameChanged();
void versionChanged(); void versionChanged();
void connectionChanged();
private: private:
QUuid m_uuid; QUuid m_uuid;
@ -138,4 +144,4 @@ private:
Connections *m_connections = nullptr; Connections *m_connections = nullptr;
}; };
#endif // DISCOVERYDEVICE_H #endif // NYMEAHOST_H

View File

@ -0,0 +1,204 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <stuerz.simon@gmail.com> *
* *
* This file is part of nymea:app. *
* *
* nymea:app is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* nymea:app 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 nymea:app. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "nymeahosts.h"
#include "connection/discovery/nymeadiscovery.h"
#include "nymeahost.h"
#include "connection/nymeaconnection.h"
NymeaHosts::NymeaHosts(QObject *parent) :
QAbstractListModel(parent)
{
}
int NymeaHosts::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_hosts.count();
}
QVariant NymeaHosts::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_hosts.count())
return QVariant();
NymeaHost *host = m_hosts.at(index.row());
switch (role) {
case UuidRole:
return host->uuid();
case NameRole:
return host->name();
case VersionRole:
return host->version();
}
return QVariant();
}
void NymeaHosts::addHost(NymeaHost *host)
{
for (int i = 0; i < m_hosts.count(); i++) {
if (m_hosts.at(i)->uuid() == host->uuid()) {
qWarning() << "Host already added. Update existing host instead.";
return;
}
}
host->setParent(this);
connect(host, &NymeaHost::connectionChanged, this, &NymeaHosts::hostChanged);
beginInsertRows(QModelIndex(), m_hosts.count(), m_hosts.count());
m_hosts.append(host);
endInsertRows();
emit hostAdded(host);
emit countChanged();
}
void NymeaHosts::removeHost(NymeaHost *host)
{
int idx = m_hosts.indexOf(host);
if (idx == -1) {
qWarning() << "Cannot remove NymeaHost" << host << "as its nit in the model";
return;
}
beginRemoveRows(QModelIndex(), idx, idx);
m_hosts.takeAt(idx);
endRemoveRows();
emit hostRemoved(host);
emit countChanged();
}
NymeaHost *NymeaHosts::get(int index) const
{
if (index < 0 || index >= m_hosts.count()) {
return nullptr;
}
return m_hosts.at(index);
}
NymeaHost *NymeaHosts::find(const QUuid &uuid)
{
foreach (NymeaHost *dev, m_hosts) {
if (dev->uuid() == uuid) {
return dev;
}
}
return nullptr;
}
void NymeaHosts::clearModel()
{
beginResetModel();
m_hosts.clear();
endResetModel();
emit countChanged();
}
QHash<int, QByteArray> NymeaHosts::roleNames() const
{
QHash<int, QByteArray> roles;
roles[UuidRole] = "uuid";
roles[NameRole] = "name";
roles[VersionRole] = "version";
return roles;
}
NymeaHostsFilterModel::NymeaHostsFilterModel(QObject *parent):
QSortFilterProxyModel(parent)
{
}
NymeaDiscovery *NymeaHostsFilterModel::discovery() const
{
return m_nymeaDiscovery;
}
void NymeaHostsFilterModel::setDiscovery(NymeaDiscovery *discovery)
{
if (m_nymeaDiscovery != discovery) {
m_nymeaDiscovery = discovery;
setSourceModel(discovery->nymeaHosts());
emit discoveryChanged();
connect(discovery->nymeaHosts(), &NymeaHosts::hostChanged, this, [this](){
// qDebug() << "Host Changed!";
invalidateFilter();
emit countChanged();
});
emit countChanged();
}
}
NymeaConnection *NymeaHostsFilterModel::nymeaConnection() const
{
return m_nymeaConnection;
}
void NymeaHostsFilterModel::setNymeaConnection(NymeaConnection *nymeaConnection)
{
if (m_nymeaConnection != nymeaConnection) {
m_nymeaConnection = nymeaConnection;
emit nymeaConnectionChanged();
connect(m_nymeaConnection, &NymeaConnection::availableBearerTypesChanged, this, [this](){
qDebug() << "Bearer Types Changed!";
invalidateFilter();
emit countChanged();
});
invalidateFilter();
emit countChanged();
}
}
bool NymeaHostsFilterModel::showUnreachableBearers() const
{
return m_showUneachableBearers;
}
void NymeaHostsFilterModel::setShowUnreachableBearers(bool showUnreachableBearers)
{
if (m_showUneachableBearers != showUnreachableBearers) {
m_showUneachableBearers = showUnreachableBearers;
emit showUnreachableBearersChanged();
invalidateFilter();
emit countChanged();
}
}
bool NymeaHostsFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
Q_UNUSED(sourceParent)
NymeaHost *host = m_nymeaDiscovery->nymeaHosts()->get(sourceRow);
if (m_nymeaConnection && !m_showUneachableBearers) {
bool hasReachableConnection = false;
for (int i = 0; i < host->connections()->rowCount(); i++) {
// qDebug() << "checking host for available bearer" << host->name() << host->connections()->get(i)->url();
if (m_nymeaConnection->availableBearerTypes().testFlag(host->connections()->get(i)->bearerType())) {
hasReachableConnection = true;
break;
}
}
if (!hasReachableConnection) {
return false;
}
}
return true;
}

View File

@ -18,51 +18,91 @@
* * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DISCOVERYMODEL_H #ifndef NYMEAHOSTS_H
#define DISCOVERYMODEL_H #define NYMEAHOSTS_H
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QList> #include <QList>
#include <QBluetoothAddress> #include <QBluetoothAddress>
#include <QSortFilterProxyModel>
class DiscoveryDevice; class NymeaHost;
class NymeaDiscovery;
class NymeaConnection;
class DiscoveryModel : public QAbstractListModel class NymeaHosts : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged) Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
public: public:
enum DeviceRole { enum HostRole {
DeviceTypeRole,
UuidRole, UuidRole,
NameRole, NameRole,
VersionRole VersionRole
}; };
Q_ENUM(DeviceRole) Q_ENUM(HostRole)
explicit DiscoveryModel(QObject *parent = nullptr); explicit NymeaHosts(QObject *parent = nullptr);
int rowCount(const QModelIndex & parent = QModelIndex()) const; int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
void addDevice(DiscoveryDevice *device); void addHost(NymeaHost *host);
void removeDevice(DiscoveryDevice *device); void removeHost(NymeaHost *host);
Q_INVOKABLE DiscoveryDevice *get(int index) const; Q_INVOKABLE NymeaHost *get(int index) const;
Q_INVOKABLE DiscoveryDevice *find(const QUuid &uuid); Q_INVOKABLE NymeaHost *find(const QUuid &uuid);
void clearModel(); void clearModel();
signals: signals:
void deviceAdded(DiscoveryDevice* device); void hostAdded(NymeaHost* host);
void deviceRemoved(DiscoveryDevice* device); void hostRemoved(NymeaHost* host);
void countChanged(); void countChanged();
void hostChanged();
protected: protected:
QHash<int, QByteArray> roleNames() const; QHash<int, QByteArray> roleNames() const;
private: private:
QList<DiscoveryDevice *> m_devices; QList<NymeaHost*> m_hosts;
}; };
#endif // DISCOVERYMODEL_H class NymeaHostsFilterModel: public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
Q_PROPERTY(NymeaDiscovery* discovery READ discovery WRITE setDiscovery NOTIFY discoveryChanged)
Q_PROPERTY(NymeaConnection* nymeaConnection READ nymeaConnection WRITE setNymeaConnection NOTIFY nymeaConnectionChanged)
Q_PROPERTY(bool showUnreachableBearers READ showUnreachableBearers WRITE setShowUnreachableBearers NOTIFY showUnreachableBearersChanged)
public:
NymeaHostsFilterModel(QObject *parent = nullptr);
NymeaDiscovery* discovery() const;
void setDiscovery(NymeaDiscovery *discovery);
NymeaConnection* nymeaConnection() const;
void setNymeaConnection(NymeaConnection* nymeaConnection);
bool showUnreachableBearers() const;
void setShowUnreachableBearers(bool showUnreachableBearers);
signals:
void countChanged();
void discoveryChanged();
void nymeaConnectionChanged();
void showUnreachableBearersChanged();
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
private:
NymeaDiscovery *m_nymeaDiscovery = nullptr;
NymeaConnection *m_nymeaConnection = nullptr;
bool m_showUneachableBearers = false;
};
#endif // NYMEAHOSTS_H

View File

@ -53,6 +53,7 @@ public:
virtual ~NymeaTransportInterface() = default; virtual ~NymeaTransportInterface() = default;
virtual bool connect(const QUrl &url) = 0; virtual bool connect(const QUrl &url) = 0;
virtual QUrl url() const = 0;
virtual void disconnect() = 0; virtual void disconnect() = 0;
virtual ConnectionState connectionState() const = 0; virtual ConnectionState connectionState() const = 0;
virtual void sendData(const QByteArray &data) = 0; virtual void sendData(const QByteArray &data) = 0;

View File

@ -5,7 +5,6 @@
TcpSocketTransport::TcpSocketTransport(QObject *parent) : NymeaTransportInterface(parent) TcpSocketTransport::TcpSocketTransport(QObject *parent) : NymeaTransportInterface(parent)
{ {
QObject::connect(&m_socket, &QSslSocket::connected, this, &TcpSocketTransport::onConnected); QObject::connect(&m_socket, &QSslSocket::connected, this, &TcpSocketTransport::onConnected);
QObject::connect(&m_socket, &QSslSocket::disconnected, this, &TcpSocketTransport::disconnected);
QObject::connect(&m_socket, &QSslSocket::encrypted, this, &TcpSocketTransport::onEncrypted); QObject::connect(&m_socket, &QSslSocket::encrypted, this, &TcpSocketTransport::onEncrypted);
typedef void (QSslSocket:: *sslErrorsSignal)(const QList<QSslError> &); typedef void (QSslSocket:: *sslErrorsSignal)(const QList<QSslError> &);
QObject::connect(&m_socket, static_cast<sslErrorsSignal>(&QSslSocket::sslErrors), this, &TcpSocketTransport::sslErrors); QObject::connect(&m_socket, static_cast<sslErrorsSignal>(&QSslSocket::sslErrors), this, &TcpSocketTransport::sslErrors);
@ -58,6 +57,11 @@ bool TcpSocketTransport::connect(const QUrl &url)
return false; return false;
} }
QUrl TcpSocketTransport::url() const
{
return m_url;
}
NymeaTransportInterface::ConnectionState TcpSocketTransport::connectionState() const NymeaTransportInterface::ConnectionState TcpSocketTransport::connectionState() const
{ {
switch (m_socket.state()) { switch (m_socket.state()) {
@ -91,6 +95,9 @@ void TcpSocketTransport::socketReadyRead()
void TcpSocketTransport::onSocketStateChanged(const QAbstractSocket::SocketState &state) void TcpSocketTransport::onSocketStateChanged(const QAbstractSocket::SocketState &state)
{ {
qDebug() << "Socket state changed -->" << state; qDebug() << "Socket state changed -->" << state;
if (state == QAbstractSocket::UnconnectedState) {
emit disconnected();
}
} }
NymeaTransportInterface *TcpSocketTransportFactory::createTransport(QObject *parent) const NymeaTransportInterface *TcpSocketTransportFactory::createTransport(QObject *parent) const

View File

@ -21,6 +21,7 @@ public:
explicit TcpSocketTransport(QObject *parent = nullptr); explicit TcpSocketTransport(QObject *parent = nullptr);
bool connect(const QUrl &url) override; bool connect(const QUrl &url) override;
QUrl url() const override;
ConnectionState connectionState() const override; ConnectionState connectionState() const override;
void disconnect() override; void disconnect() override;
void sendData(const QByteArray &data) override; void sendData(const QByteArray &data) override;

View File

@ -42,10 +42,16 @@ WebsocketTransport::WebsocketTransport(QObject *parent) :
bool WebsocketTransport::connect(const QUrl &url) bool WebsocketTransport::connect(const QUrl &url)
{ {
m_url = url;
m_socket->open(QUrl(url)); m_socket->open(QUrl(url));
return true; return true;
} }
QUrl WebsocketTransport::url() const
{
return m_url;
}
NymeaTransportInterface::ConnectionState WebsocketTransport::connectionState() const NymeaTransportInterface::ConnectionState WebsocketTransport::connectionState() const
{ {
switch (m_socket->state()) { switch (m_socket->state()) {

View File

@ -40,12 +40,14 @@ public:
explicit WebsocketTransport(QObject *parent = nullptr); explicit WebsocketTransport(QObject *parent = nullptr);
bool connect(const QUrl &url) override; bool connect(const QUrl &url) override;
QUrl url() const override;
ConnectionState connectionState() const override; ConnectionState connectionState() const override;
void disconnect() override; void disconnect() override;
void sendData(const QByteArray &data) override; void sendData(const QByteArray &data) override;
void ignoreSslErrors(const QList<QSslError> &errors) override; void ignoreSslErrors(const QList<QSslError> &errors) override;
private: private:
QUrl m_url;
QWebSocket *m_socket; QWebSocket *m_socket;
private slots: private slots:

View File

@ -1,115 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <stuerz.simon@gmail.com> *
* *
* This file is part of nymea:app. *
* *
* nymea:app is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* nymea:app 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 nymea:app. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "discoverymodel.h"
#include "discoverydevice.h"
DiscoveryModel::DiscoveryModel(QObject *parent) :
QAbstractListModel(parent)
{
}
int DiscoveryModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_devices.count();
}
QVariant DiscoveryModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_devices.count())
return QVariant();
DiscoveryDevice *device = m_devices.at(index.row());
switch (role) {
case UuidRole:
return device->uuid();
case NameRole:
return device->name();
case VersionRole:
return device->version();
}
return QVariant();
}
void DiscoveryModel::addDevice(DiscoveryDevice *device)
{
for (int i = 0; i < m_devices.count(); i++) {
if (m_devices.at(i)->uuid() == device->uuid()) {
qWarning() << "Device already added. Update existing device instead.";
return;
}
}
device->setParent(this);
beginInsertRows(QModelIndex(), m_devices.count(), m_devices.count());
m_devices.append(device);
endInsertRows();
emit deviceAdded(device);
emit countChanged();
}
void DiscoveryModel::removeDevice(DiscoveryDevice *device)
{
int idx = m_devices.indexOf(device);
if (idx == -1) {
qWarning() << "Cannot remove DiscoveryDevice" << device << "as its nit in the model";
return;
}
beginRemoveRows(QModelIndex(), idx, idx);
m_devices.takeAt(idx);
endRemoveRows();
emit deviceRemoved(device);
emit countChanged();
}
DiscoveryDevice *DiscoveryModel::get(int index) const
{
if (index < 0 || index >= m_devices.count()) {
return nullptr;
}
return m_devices.at(index);
}
DiscoveryDevice *DiscoveryModel::find(const QUuid &uuid)
{
foreach (DiscoveryDevice *dev, m_devices) {
if (dev->uuid() == uuid) {
return dev;
}
}
return nullptr;
}
void DiscoveryModel::clearModel()
{
beginResetModel();
m_devices.clear();
endResetModel();
emit countChanged();
}
QHash<int, QByteArray> DiscoveryModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[UuidRole] = "uuid";
roles[NameRole] = "name";
roles[VersionRole] = "version";
return roles;
}

View File

@ -1,204 +0,0 @@
#include "nymeadiscovery.h"
#include "upnpdiscovery.h"
#include "zeroconfdiscovery.h"
#include "bluetoothservicediscovery.h"
#include "connection/awsclient.h"
#include <QUuid>
#include <QBluetoothUuid>
#include <QUrlQuery>
#include <QNetworkConfigurationManager>
#include <QNetworkSession>
NymeaDiscovery::NymeaDiscovery(QObject *parent) : QObject(parent)
{
m_discoveryModel = new DiscoveryModel(this);
connect(m_discoveryModel, &DiscoveryModel::deviceAdded, this, [this](DiscoveryDevice *device) {
if (!m_pendingHostResolutions.contains(device->uuid())) {
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 (m_pendingHostResolutions.contains(device->uuid())) {
qDebug() << "Host" << device->uuid() << "resolved to" << connection->url().toString();
m_pendingHostResolutions.removeAll(device->uuid());
emit serverUuidResolved(device->uuid(), connection->url().toString());
}
});
return;
}
qDebug() << "Host" << device->uuid() << "appeared! Best match is" << c->url();
m_pendingHostResolutions.removeAll(device->uuid());
emit serverUuidResolved(device->uuid(), c->url().toString());
});
m_upnp = new UpnpDiscovery(m_discoveryModel, this);
m_zeroConf = new ZeroconfDiscovery(m_discoveryModel, this);
#ifndef Q_OS_IOS
m_bluetooth = new BluetoothServiceDiscovery(m_discoveryModel, this);
#endif
m_cloudPollTimer.setInterval(5000);
connect(&m_cloudPollTimer, &QTimer::timeout, this, [this](){
if (m_awsClient && m_awsClient->isLoggedIn()) {
m_awsClient->fetchDevices();
}
});
QNetworkConfigurationManager manager;
QList<QNetworkConfiguration> 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
{
return m_discovering;
}
void NymeaDiscovery::setDiscovering(bool discovering)
{
if (m_discovering == discovering)
return;
m_discovering = discovering;
// If we have zeroconf skip upnp. ZeroConf will not do an active discovery and if it's available it'll always have good data
if (!m_zeroConf->available()) {
if (discovering) {
m_upnp->discover();
} else {
m_upnp->stopDiscovery();
}
}
if (discovering) {
// If there's no Zeroconf, use UPnP instead
if (!m_zeroConf->available()) {
m_upnp->discover();
}
// Always start Bluetooth discovery if HW is available
if (m_bluetooth) {
m_bluetooth->discover();
}
// start polling cloud
m_cloudPollTimer.start();
// If we're logged in, poll right away
if (m_awsClient && m_awsClient->isLoggedIn()) {
syncCloudDevices();
m_awsClient->fetchDevices();
}
} else {
if (!m_zeroConf->available()) {
m_upnp->stopDiscovery();
}
if (m_bluetooth) {
m_bluetooth->stopDiscovery();
}
m_cloudPollTimer.stop();
}
emit discoveringChanged();
}
DiscoveryModel *NymeaDiscovery::discoveryModel() const
{
return m_discoveryModel;
}
AWSClient *NymeaDiscovery::awsClient() const
{
return m_awsClient;
}
void NymeaDiscovery::setAwsClient(AWSClient *awsClient)
{
if (m_awsClient != awsClient) {
m_awsClient = awsClient;
emit awsClientChanged();
}
if (m_awsClient) {
m_awsClient->fetchDevices();
connect(m_awsClient, &AWSClient::devicesFetched, this, &NymeaDiscovery::syncCloudDevices);
syncCloudDevices();
}
}
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_pendingHostResolutions.append(uuid);
return;
}
Connection *c = dev->connections()->bestMatch();
if (!c) {
qDebug() << "Host" << uuid << "is known but doesn't have a usable connection option yet.";
m_pendingHostResolutions.append(uuid);
return;
}
qDebug() << "Host" << uuid << "is known. Best match is" << c->url();
emit serverUuidResolved(uuid, c->url().toString());
}
void NymeaDiscovery::syncCloudDevices()
{
for (int i = 0; i < m_awsClient->awsDevices()->rowCount(); i++) {
AWSDevice *d = m_awsClient->awsDevices()->get(i);
DiscoveryDevice *device = m_discoveryModel->find(d->id());
if (!device) {
device = new DiscoveryDevice();
device->setUuid(d->id());
device->setName(d->name());
qDebug() << "CloudDiscovery: Adding new host:" << device->name() << device->uuid().toString();
m_discoveryModel->addDevice(device);
}
QUrl url;
url.setScheme("cloud");
url.setHost(d->id());
Connection *conn = device->connections()->find(url);
if (!conn) {
conn = new Connection(url, Connection::BearerTypeCloud, true, d->id());
qDebug() << "CloudDiscovery: Adding new connection to host:" << device->name() << conn->url().toString();
device->connections()->addConnection(conn);
}
conn->setOnline(d->online());
}
QList<DiscoveryDevice*> devicesToRemove;
for (int i = 0; i < m_discoveryModel->rowCount(); i++) {
DiscoveryDevice *device = m_discoveryModel->get(i);
for (int j = 0; j < device->connections()->rowCount(); j++) {
if (device->connections()->get(j)->bearerType() == Connection::BearerTypeCloud) {
if (m_awsClient->awsDevices()->getDevice(device->uuid().toString()) == nullptr) {
device->connections()->removeConnection(j);
break;
}
}
}
if (device->connections()->rowCount() == 0) {
devicesToRemove.append(device);
}
}
while (!devicesToRemove.isEmpty()) {
m_discoveryModel->removeDevice(devicesToRemove.takeFirst());
}
}

View File

@ -1,68 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <stuerz.simon@gmail.com> *
* *
* This file is part of nymea:app. *
* *
* nymea:app is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* nymea:app 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 nymea:app. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "nymeahost.h"
NymeaHost::NymeaHost(QObject *parent) :
QObject(parent)
{
}
QString NymeaHost::name() const
{
return m_name;
}
void NymeaHost::setName(const QString &name)
{
m_name = name;
}
QString NymeaHost::webSocketUrl() const
{
return m_webSocketUrl;
}
void NymeaHost::setWebSocketUrl(const QString &webSocketUrl)
{
m_webSocketUrl = webSocketUrl;
}
QString NymeaHost::hostAddress() const
{
return m_hostAddress;
}
void NymeaHost::setHostAddress(const QString &hostAddress)
{
m_hostAddress = hostAddress;
}
QUuid NymeaHost::uuid() const
{
return m_uuid;
}
void NymeaHost::setUuid(const QUuid &uuid)
{
m_uuid = uuid;
}

View File

@ -1,54 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <stuerz.simon@gmail.com> *
* *
* This file is part of nymea:app. *
* *
* nymea:app is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* nymea:app 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 nymea:app. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef NYMEAHOST_H
#define NYMEAHOST_H
#include <QUuid>
#include <QObject>
#include <QHostAddress>
class NymeaHost : public QObject
{
Q_OBJECT
public:
explicit NymeaHost(QObject *parent = 0);
QString name() const;
void setName(const QString &name);
QString webSocketUrl() const;
void setWebSocketUrl(const QString &webSocketUrl);
QString hostAddress() const;
void setHostAddress(const QString &hostAddress);
QUuid uuid() const;
void setUuid(const QUuid &uuid);
private:
QString m_name;
QString m_webSocketUrl;
QString m_hostAddress;
QUuid m_uuid;
};
#endif // NYMEAHOST_H

View File

@ -1,150 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <stuerz.simon@gmail.com> *
* *
* This file is part of nymea:app. *
* *
* nymea:app is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* nymea:app 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 nymea:app. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "nymeahosts.h"
#include "nymeahost.h"
#include <QUuid>
#include <QDebug>
#include <QSettings>
NymeaHosts::NymeaHosts(QObject *parent) :
QAbstractListModel(parent)
{
beginResetModel();
QSettings settings;
qDebug() << "Connections: loading connections " << settings.fileName();
settings.beginGroup("Connections");
foreach (const QString &uuid, settings.childGroups()) {
settings.beginGroup(uuid);
NymeaHost *host = new NymeaHost(this);
host->setName(settings.value("name").toString());
host->setHostAddress(settings.value("hostAddress").toString());
host->setWebSocketUrl(settings.value("webSocketUrl").toString());
host->setUuid(QUuid(uuid));
qDebug() << " " << host->webSocketUrl();
m_hosts.append(host);
settings.endGroup();
}
settings.endGroup();
endResetModel();
}
NymeaHost *NymeaHosts::get(const QString &webSocketUrl)
{
foreach (NymeaHost *host, m_hosts) {
if (host->webSocketUrl() == webSocketUrl) {
return host;
}
}
return nullptr;
}
QList<NymeaHost *> NymeaHosts::hosts()
{
return m_hosts;
}
int NymeaHosts::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_hosts.count();
}
QVariant NymeaHosts::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_hosts.count())
return QVariant();
NymeaHost *host = m_hosts.at(index.row());
if (role == NameRole) {
return host->name();
} else if (role == HostAddressRole) {
return host->hostAddress();
} else if (role == WebSocketUrlRole) {
return host->webSocketUrl();
}
return QVariant();
}
void NymeaHosts::addHost(const QString &name, const QString &hostAddress, const QString &webSocketUrl)
{
// check if we allready have added this connection
foreach (NymeaHost *host, m_hosts) {
if (host->webSocketUrl() == webSocketUrl) {
return;
}
}
NymeaHost *host = new NymeaHost(this);
host->setName(name);
host->setHostAddress(hostAddress);
host->setWebSocketUrl(webSocketUrl);
host->setUuid(QUuid::createUuid());
qDebug() << "NymeaHosts: add connection" << host->webSocketUrl();
beginInsertRows(QModelIndex(), m_hosts.count(), m_hosts.count());
m_hosts.append(host);
endInsertRows();
QSettings settings;
settings.beginGroup("Connections");
settings.beginGroup(host->uuid().toString());
settings.setValue("name", name);
settings.setValue("hostAddress", hostAddress);
settings.setValue("webSocketUrl", webSocketUrl);
settings.endGroup();
settings.endGroup();
qDebug() << "Connections: saved connection" << settings.fileName();
}
void NymeaHosts::removeHost(NymeaHost *host)
{
int index = m_hosts.indexOf(host);
beginRemoveRows(QModelIndex(), index, index);
qDebug() << "Connections: removed connection" << host->webSocketUrl();
m_hosts.removeAt(index);
QSettings settings;
settings.beginGroup("Connections");
settings.remove(host->uuid().toString());
settings.endGroup();
host->deleteLater();
endRemoveRows();
}
void NymeaHosts::clearModel()
{
beginResetModel();
qDebug() << "NymeaHosts: delete all hosts";
qDeleteAll(m_hosts);
m_hosts.clear();
endResetModel();
}
QHash<int, QByteArray> NymeaHosts::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[HostAddressRole] = "hostAddress";
roles[WebSocketUrlRole] = "webSocketUrl";
return roles;
}

View File

@ -1,59 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <stuerz.simon@gmail.com> *
* *
* This file is part of nymea:app. *
* *
* nymea:app is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* nymea:app 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 nymea:app. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef NYMEAHOSTS_H
#define NYMEAHOSTS_H
#include <QAbstractListModel>
class NymeaHost;
class NymeaHosts : public QAbstractListModel
{
Q_OBJECT
public:
enum ConnectionRole {
NameRole = Qt::DisplayRole,
HostAddressRole,
WebSocketUrlRole
};
explicit NymeaHosts(QObject *parent = 0);
Q_INVOKABLE NymeaHost *get(const QString &webSocketUrl);
QList<NymeaHost*> hosts();
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
void addHost(const QString &name, const QString &hostAddress, const QString &webSocketUrl);
Q_INVOKABLE void removeHost(NymeaHost *host);
void clearModel();
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<NymeaHost*> m_hosts;
};
#endif // NYMEAHOSTS_H

View File

@ -2,14 +2,14 @@
#define LIBNYMEAAPPCORE_H #define LIBNYMEAAPPCORE_H
#include "engine.h" #include "engine.h"
#include "connection/nymeahosts.h"
#include "connection/nymeahost.h"
#include "connection/discovery/nymeadiscovery.h"
#include "vendorsproxy.h" #include "vendorsproxy.h"
#include "deviceclassesproxy.h" #include "deviceclassesproxy.h"
#include "devicesproxy.h" #include "devicesproxy.h"
#include "pluginsproxy.h" #include "pluginsproxy.h"
#include "devicediscovery.h" #include "devicediscovery.h"
#include "discovery/nymeadiscovery.h"
#include "discovery/discoverymodel.h"
#include "discovery/discoverydevice.h"
#include "interfacesmodel.h" #include "interfacesmodel.h"
#include "rulemanager.h" #include "rulemanager.h"
#include "models/rulesfiltermodel.h" #include "models/rulesfiltermodel.h"
@ -159,9 +159,10 @@ void registerQmlTypes() {
qmlRegisterUncreatableType<MqttPolicies>(uri, 1, 0, "MqttPolicies", "Get it from NymeaConfiguration"); qmlRegisterUncreatableType<MqttPolicies>(uri, 1, 0, "MqttPolicies", "Get it from NymeaConfiguration");
qmlRegisterType<NymeaDiscovery>(uri, 1, 0, "NymeaDiscovery"); qmlRegisterType<NymeaDiscovery>(uri, 1, 0, "NymeaDiscovery");
qmlRegisterUncreatableType<DiscoveryModel>(uri, 1, 0, "DiscoveryModel", "Get it from NymeaDiscovery"); qmlRegisterUncreatableType<NymeaHosts>(uri, 1, 0, "NymeaHosts", "Get it from NymeaDiscovery");
qmlRegisterUncreatableType<DiscoveryDevice>(uri, 1, 0, "DiscoveryDevice", "Get it from DiscoveryModel"); qmlRegisterType<NymeaHostsFilterModel>(uri, 1, 0, "NymeaHostsFilterModel");
qmlRegisterUncreatableType<Connection>(uri, 1, 0, "Connection", "Get it from DiscoveryDevice"); qmlRegisterUncreatableType<NymeaHost>(uri, 1, 0, "NymeaHost", "Get it from NymeaHosts");
qmlRegisterUncreatableType<Connection>(uri, 1, 0, "Connection", "Get it from NymeaHost");
qmlRegisterType<LogsModel>(uri, 1, 0, "LogsModel"); qmlRegisterType<LogsModel>(uri, 1, 0, "LogsModel");
qmlRegisterType<LogsModelNg>(uri, 1, 0, "LogsModelNg"); qmlRegisterType<LogsModelNg>(uri, 1, 0, "LogsModelNg");

View File

@ -25,19 +25,22 @@ INCLUDEPATH += $$top_srcdir/libnymea-common \
SOURCES += \ SOURCES += \
engine.cpp \ engine.cpp \
connection/nymeahost.cpp \
connection/nymeahosts.cpp \
connection/nymeaconnection.cpp \ connection/nymeaconnection.cpp \
connection/nymeatransportinterface.cpp \ connection/nymeatransportinterface.cpp \
connection/websockettransport.cpp \ connection/websockettransport.cpp \
connection/tcpsockettransport.cpp \ connection/tcpsockettransport.cpp \
connection/bluetoothtransport.cpp \ connection/bluetoothtransport.cpp \
connection/awsclient.cpp \ connection/awsclient.cpp \
connection/discovery/nymeadiscovery.cpp \
connection/discovery/upnpdiscovery.cpp \
connection/discovery/zeroconfdiscovery.cpp \
connection/discovery/bluetoothservicediscovery.cpp \
devicemanager.cpp \ devicemanager.cpp \
jsonrpc/jsontypes.cpp \ jsonrpc/jsontypes.cpp \
jsonrpc/jsonrpcclient.cpp \ jsonrpc/jsonrpcclient.cpp \
jsonrpc/jsonhandler.cpp \ jsonrpc/jsonhandler.cpp \
discovery/nymeahost.cpp \
discovery/nymeahosts.cpp \
discovery/upnpdiscovery.cpp \
devices.cpp \ devices.cpp \
devicesproxy.cpp \ devicesproxy.cpp \
deviceclasses.cpp \ deviceclasses.cpp \
@ -46,14 +49,10 @@ SOURCES += \
vendorsproxy.cpp \ vendorsproxy.cpp \
pluginsproxy.cpp \ pluginsproxy.cpp \
interfacesmodel.cpp \ interfacesmodel.cpp \
discovery/zeroconfdiscovery.cpp \
discovery/discoverydevice.cpp \
discovery/discoverymodel.cpp \
rulemanager.cpp \ rulemanager.cpp \
models/rulesfiltermodel.cpp \ models/rulesfiltermodel.cpp \
models/logsmodel.cpp \ models/logsmodel.cpp \
models/valuelogsproxymodel.cpp \ models/valuelogsproxymodel.cpp \
discovery/nymeadiscovery.cpp \
logmanager.cpp \ logmanager.cpp \
wifisetup/bluetoothdevice.cpp \ wifisetup/bluetoothdevice.cpp \
wifisetup/bluetoothdeviceinfo.cpp \ wifisetup/bluetoothdeviceinfo.cpp \
@ -74,7 +73,6 @@ SOURCES += \
ruletemplates/ruleactiontemplate.cpp \ ruletemplates/ruleactiontemplate.cpp \
ruletemplates/stateevaluatortemplate.cpp \ ruletemplates/stateevaluatortemplate.cpp \
ruletemplates/statedescriptortemplate.cpp \ ruletemplates/statedescriptortemplate.cpp \
discovery/bluetoothservicediscovery.cpp \
connection/cloudtransport.cpp \ connection/cloudtransport.cpp \
connection/sigv4utils.cpp \ connection/sigv4utils.cpp \
ruletemplates/ruleactionparamtemplate.cpp \ ruletemplates/ruleactionparamtemplate.cpp \
@ -88,6 +86,8 @@ SOURCES += \
HEADERS += \ HEADERS += \
engine.h \ engine.h \
connection/nymeahost.h \
connection/nymeahosts.h \
connection/nymeaconnection.h \ connection/nymeaconnection.h \
connection/nymeatransportinterface.h \ connection/nymeatransportinterface.h \
connection/websockettransport.h \ connection/websockettransport.h \
@ -95,13 +95,14 @@ HEADERS += \
connection/bluetoothtransport.h \ connection/bluetoothtransport.h \
connection/awsclient.h \ connection/awsclient.h \
connection/sigv4utils.h \ connection/sigv4utils.h \
connection/discovery/nymeadiscovery.h \
connection/discovery/upnpdiscovery.h \
connection/discovery/zeroconfdiscovery.h \
connection/discovery/bluetoothservicediscovery.h \
devicemanager.h \ devicemanager.h \
jsonrpc/jsontypes.h \ jsonrpc/jsontypes.h \
jsonrpc/jsonrpcclient.h \ jsonrpc/jsonrpcclient.h \
jsonrpc/jsonhandler.h \ jsonrpc/jsonhandler.h \
discovery/nymeahost.h \
discovery/nymeahosts.h \
discovery/upnpdiscovery.h \
devices.h \ devices.h \
devicesproxy.h \ devicesproxy.h \
deviceclasses.h \ deviceclasses.h \
@ -110,14 +111,10 @@ HEADERS += \
vendorsproxy.h \ vendorsproxy.h \
pluginsproxy.h \ pluginsproxy.h \
interfacesmodel.h \ interfacesmodel.h \
discovery/zeroconfdiscovery.h \
discovery/discoverydevice.h \
discovery/discoverymodel.h \
rulemanager.h \ rulemanager.h \
models/rulesfiltermodel.h \ models/rulesfiltermodel.h \
models/logsmodel.h \ models/logsmodel.h \
models/valuelogsproxymodel.h \ models/valuelogsproxymodel.h \
discovery/nymeadiscovery.h \
logmanager.h \ logmanager.h \
wifisetup/bluetoothdevice.h \ wifisetup/bluetoothdevice.h \
wifisetup/bluetoothdeviceinfo.h \ wifisetup/bluetoothdeviceinfo.h \
@ -139,7 +136,6 @@ HEADERS += \
ruletemplates/ruleactiontemplate.h \ ruletemplates/ruleactiontemplate.h \
ruletemplates/stateevaluatortemplate.h \ ruletemplates/stateevaluatortemplate.h \
ruletemplates/statedescriptortemplate.h \ ruletemplates/statedescriptortemplate.h \
discovery/bluetoothservicediscovery.h \
connection/cloudtransport.h \ connection/cloudtransport.h \
ruletemplates/ruleactionparamtemplate.h \ ruletemplates/ruleactionparamtemplate.h \
configuration/serverconfiguration.h \ configuration/serverconfiguration.h \

View File

@ -57,6 +57,12 @@ QObject *platformHelperProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QLoggingCategory::setFilterRules("RemoteProxyClientJsonRpcTraffic.debug=false\n"
"RemoteProxyClientJsonRpc.debug=false\n"
"RemoteProxyClientWebSocket.debug=false\n"
"RemoteProxyClientConnection.debug=false\n"
"RemoteProxyClientConnectionTraffic.debug=false\n"
);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication application(argc, argv); QApplication application(argc, argv);
application.setApplicationName("nymea-app"); application.setApplicationName("nymea-app");

View File

@ -59,10 +59,10 @@ void PlatformHelperAndroid::vibrate(PlatformHelper::HapticsFeedback feedbackType
int duration; int duration;
switch (feedbackType) { switch (feedbackType) {
case HapticsFeedbackSelection: case HapticsFeedbackSelection:
duration = 15; duration = 20;
break; break;
case HapticsFeedbackImpact: case HapticsFeedbackImpact:
duration = 25; duration = 30;
break; break;
case HapticsFeedbackNotification: case HapticsFeedbackNotification:
duration = 500; duration = 500;

View File

@ -57,6 +57,7 @@ ApplicationWindow {
awsClient: AWSClient awsClient: AWSClient
// discovering: pageStack.currentItem.objectName === "discoveryPage" // discovering: pageStack.currentItem.objectName === "discoveryPage"
} }
property alias _discovery: discovery
onClosing: { onClosing: {
rootItem.handleCloseEvent(close) rootItem.handleCloseEvent(close)

View File

@ -5,6 +5,7 @@ import QtQuick.Layouts 1.3
import Qt.labs.settings 1.0 import Qt.labs.settings 1.0
import Nymea 1.0 import Nymea 1.0
import "components" import "components"
import "connection"
Item { Item {
id: root id: root
@ -74,7 +75,7 @@ Item {
height: swipeView.height height: swipeView.height
width: swipeView.width width: swipeView.width
objectName: "pageStack" objectName: "pageStack"
initialItem: Page {} initialItem: ConnectPage {}
property var tabSettings: Settings { property var tabSettings: Settings {
category: "tabSettings" + index category: "tabSettings" + index
@ -88,7 +89,7 @@ Item {
readonly property Engine engine: engineObject readonly property Engine engine: engineObject
readonly property Engine _engine: engineObject // In case a child cannot use "engine" readonly property Engine _engine: engineObject // In case a child cannot use "engine"
property int connectionTabIndex: index property int connectionTabIndex: index
onConnectionTabIndexChanged: tabSettings.lastConnectedHost = engine.connection.url // onConnectionTabIndexChanged: tabSettings.lastConnectedHost = engine.connection.url
Binding { Binding {
target: AWSClient target: AWSClient
@ -97,20 +98,26 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml"), StackView.Immediate) // pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml"), StackView.Immediate)
setupPushNotifications(); // setupPushNotifications();
} }
function init() { function init() {
print("calling init. Auth required:", engine.jsonRpcClient.authenticationRequired, "initial setup required:", engine.jsonRpcClient.initialSetupRequired, "jsonrpc connected:", engine.jsonRpcClient.connected) print("calling init. Auth required:", engine.jsonRpcClient.authenticationRequired, "initial setup required:", engine.jsonRpcClient.initialSetupRequired, "jsonrpc connected:", engine.jsonRpcClient.connected, "Current host:", engine.connection.currentHost)
pageStack.clear() pageStack.clear()
if (!engine.connection.connected) { if (!engine.connection.currentHost) {
pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml"))
PlatformHelper.hideSplashScreen(); PlatformHelper.hideSplashScreen();
return; return;
} }
if (engine.jsonRpcClient.connected) {
pageStack.push(Qt.resolvedUrl("MainPage.qml"))
PlatformHelper.hideSplashScreen();
return;
}
if (engine.jsonRpcClient.authenticationRequired || engine.jsonRpcClient.initialSetupRequired) { if (engine.jsonRpcClient.authenticationRequired || engine.jsonRpcClient.initialSetupRequired) {
PlatformHelper.hideSplashScreen();
if (engine.jsonRpcClient.pushButtonAuthAvailable) { if (engine.jsonRpcClient.pushButtonAuthAvailable) {
print("opening push button auth") print("opening push button auth")
var page = pageStack.push(Qt.resolvedUrl("PushButtonAuthPage.qml")) var page = pageStack.push(Qt.resolvedUrl("PushButtonAuthPage.qml"))
@ -127,10 +134,8 @@ Item {
init(); init();
}) })
} }
} else {
pageStack.push(Qt.resolvedUrl("MainPage.qml"))
} }
PlatformHelper.hideSplashScreen(); pageStack.push(Qt.resolvedUrl("connection/ConnectingPage.qml"))
} }
function handleCloseEvent(close) { function handleCloseEvent(close) {
@ -169,11 +174,19 @@ Item {
} }
} }
Connections {
target: engine.connection
onCurrentHostChanged: {
init();
}
}
Connections { Connections {
target: engine.jsonRpcClient target: engine.jsonRpcClient
onConnectedChanged: { onConnectedChanged: {
print("json client connected changed", engine.jsonRpcClient.connected) print("json client connected changed", engine.jsonRpcClient.connected)
if (engine.jsonRpcClient.connected) { if (engine.jsonRpcClient.connected) {
discovery.cacheHost(engine.connection.currentHost)
tabSettings.lastConnectedHost = engine.jsonRpcClient.serverUuid tabSettings.lastConnectedHost = engine.jsonRpcClient.serverUuid
} }
init(); init();

View File

@ -37,7 +37,7 @@ Page {
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
elide: Text.ElideMiddle elide: Text.ElideMiddle
text: engine.connection.url text: engine.connection.currentConnection.url
} }
Button { Button {
text: qsTr("Disconnect") text: qsTr("Disconnect")

View File

@ -8,12 +8,18 @@ import "../components"
Page { Page {
id: root id: root
readonly property bool haveHosts: discovery.discoveryModel.count > 0 readonly property bool haveHosts: hostsProxy.count > 0
Component.onCompleted: { Component.onCompleted: {
print("completed connectPage. last connected host:", tabSettings.lastConnectedHost) print("Ready to connect")
if (tabSettings.lastConnectedHost.length > 0) { if (tabSettings.lastConnectedHost.length > 0) {
discovery.resolveServerUuid(tabSettings.lastConnectedHost) print("Last connected host was", tabSettings.lastConnectedHost)
var cachedHost = discovery.nymeaHosts.find(tabSettings.lastConnectedHost);
if (cachedHost) {
engine.connection.currentHost = cachedHost
} else {
print("Warning: There is a last connected host but UUID is unknown to discovery...")
}
} else { } else {
PlatformHelper.hideSplashScreen(); PlatformHelper.hideSplashScreen();
} }
@ -30,16 +36,6 @@ Page {
// } // }
} }
Connections {
target: discovery
onServerUuidResolved: {
print("** resolved", uuid, tabSettings.lastConnectedHost)
if (uuid == tabSettings.lastConnectedHost) {
print("yesss")
connectToHost(url, true);
}
}
}
function connectToHost(url, noAnimations) { function connectToHost(url, noAnimations) {
var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml"), noAnimations ? StackView.Immediate : StackView.PushTransition) var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml"), noAnimations ? StackView.Immediate : StackView.PushTransition)
@ -51,12 +47,23 @@ Page {
engine.connection.connect(url) engine.connection.connect(url)
} }
// NymeaDiscovery { function connectToHost2(host, noAnimations) {
// id: discovery var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml"), noAnimations ? StackView.Immediate : StackView.PushTransition)
// objectName: "discovery" page.cancel.connect(function() {
// awsClient: AWSClient engine.connection.disconnect()
// discovering: pageStack.currentItem.objectName === "discoveryPage" pageStack.pop(root, StackView.Immediate);
// } pageStack.push(discoveryPage)
})
print("Connecting to host", host)
engine.connection.connect(host)
}
NymeaHostsFilterModel {
id: hostsProxy
discovery: _discovery
showUnreachableBearers: false
nymeaConnection: engine.connection
}
Connections { Connections {
target: engine.connection target: engine.connection
@ -169,7 +176,7 @@ Page {
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
text: root.haveHosts ? text: root.haveHosts ?
qsTr("There are %1 %2 boxes in your network! Which one would you like to use?").arg(discovery.discoveryModel.count).arg(app.systemName) qsTr("There are %1 %2 boxes in your network! Which one would you like to use?").arg(discovery.nymeaHosts.count).arg(app.systemName)
: startupTimer.running ? qsTr("We haven't found any %1 boxes in your network yet.").arg(app.systemName) : startupTimer.running ? qsTr("We haven't found any %1 boxes in your network yet.").arg(app.systemName)
: qsTr("There doesn't seem to be a %1 box installed in your network. Please make sure your %1 box is correctly set up and connected.").arg(app.systemName) : qsTr("There doesn't seem to be a %1 box installed in your network. Please make sure your %1 box is correctly set up and connected.").arg(app.systemName)
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
@ -181,27 +188,27 @@ Page {
ListView { ListView {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: discovery.discoveryModel model: hostsProxy
clip: true clip: true
delegate: MeaListItemDelegate { delegate: MeaListItemDelegate {
id: discoveryDeviceDelegate id: nymeaHostDelegate
width: parent.width width: parent.width
height: app.delegateHeight height: app.delegateHeight
objectName: "discoveryDelegate" + index objectName: "discoveryDelegate" + index
property var discoveryDevice: discovery.discoveryModel.get(index) property var nymeaHost: discovery.nymeaHosts.get(index)
property string defaultConnectionIndex: { property string defaultConnectionIndex: {
var usedConfigIndex = 0; var usedConfigIndex = 0;
for (var i = 1; i < discoveryDevice.connections.count; i++) { for (var i = 1; i < nymeaHost.connections.count; i++) {
var oldConfig = discoveryDevice.connections.get(usedConfigIndex); var oldConfig = nymeaHost.connections.get(usedConfigIndex);
var newConfig = discoveryDevice.connections.get(i); var newConfig = nymeaHost.connections.get(i);
// Preference of bearerType // Preference of bearerType
var bearerPreference = [Connection.BearerTypeEthernet, Connection.BearerTypeWifi, Connection.BearerTypeBluetooth, Connection.BearerTypeCloud] var bearerPreference = [Connection.BearerTypeEthernet, Connection.BearerTypeWifi, Connection.BearerTypeBluetooth, Connection.BearerTypeCloud]
var oldBearerPriority = bearerPreference.indexOf(oldConfig.bearerType); var oldBearerPriority = bearerPreference.indexOf(oldConfig.bearerType);
var newBearerPriority = bearerPreference.indexOf(newConfig.bearerType); var newBearerPriority = bearerPreference.indexOf(newConfig.bearerType);
if (newBearerPriority < oldBearerPriority) { if (newBearerPriority < oldBearerPriority) {
print(discoveryDevice.name, "switching to preferred index", i, "of bearer type", newConfig.bearerType, "from", oldConfig.bearerType, "new prio:", newBearerPriority, "old:", oldBearerPriority) print(nymeaHost.name, "switching to preferred index", i, "of bearer type", newConfig.bearerType, "from", oldConfig.bearerType, "new prio:", newBearerPriority, "old:", oldBearerPriority)
usedConfigIndex = i; usedConfigIndex = i;
continue; continue;
} }
@ -227,7 +234,7 @@ Page {
} }
iconName: { iconName: {
switch (discoveryDevice.connections.get(defaultConnectionIndex).bearerType) { switch (nymeaHost.connections.get(defaultConnectionIndex).bearerType) {
case Connection.BearerTypeWifi: case Connection.BearerTypeWifi:
return "../images/network-wifi-symbolic.svg"; return "../images/network-wifi-symbolic.svg";
case Connection.BearerTypeEthernet: case Connection.BearerTypeEthernet:
@ -241,21 +248,21 @@ Page {
} }
text: model.name text: model.name
subText: discoveryDevice.connections.get(defaultConnectionIndex).url subText: nymeaHost.connections.get(defaultConnectionIndex).url
wrapTexts: false wrapTexts: false
prominentSubText: false prominentSubText: false
progressive: false progressive: false
property bool isSecure: discoveryDevice.connections.get(defaultConnectionIndex).secure property bool isSecure: nymeaHost.connections.get(defaultConnectionIndex).secure
property bool isTrusted: engine.connection.isTrusted(discoveryDeviceDelegate.discoveryDevice.connections.get(defaultConnectionIndex).url) property bool isTrusted: engine.connection.isTrusted(nymeaHostDelegate.nymeaHost.connections.get(defaultConnectionIndex).url)
property bool isOnline: discoveryDevice.connections.get(defaultConnectionIndex).online property bool isOnline: nymeaHost.connections.get(defaultConnectionIndex).online
tertiaryIconName: isSecure ? "../images/network-secure.svg" : "" tertiaryIconName: isSecure ? "../images/network-secure.svg" : ""
tertiaryIconColor: isTrusted ? app.accentColor : Material.foreground tertiaryIconColor: isTrusted ? app.accentColor : Material.foreground
secondaryIconName: !isOnline ? "../images/cloud-error.svg" : "" secondaryIconName: !isOnline ? "../images/cloud-error.svg" : ""
secondaryIconColor: "red" secondaryIconColor: "red"
swipe.enabled: discoveryDeviceDelegate.discoveryDevice.deviceType === DiscoveryDevice.DeviceTypeNetwork swipe.enabled: nymeaHostDelegate.nymeaHost.deviceType === NymeaHost.DeviceTypeNetwork
onClicked: { onClicked: {
root.connectToHost(discoveryDeviceDelegate.discoveryDevice.connections.get(defaultConnectionIndex).url) root.connectToHost2(nymeaHostDelegate.nymeaHost)
} }
swipe.right: MouseArea { swipe.right: MouseArea {
@ -268,9 +275,9 @@ Page {
name: "../images/info.svg" name: "../images/info.svg"
} }
onClicked: { onClicked: {
if (model.deviceType === DiscoveryDevice.DeviceTypeNetwork) { if (model.deviceType === NymeaHost.DeviceTypeNetwork) {
swipe.close() swipe.close()
var popup = infoDialog.createObject(app,{discoveryDevice: discovery.discoveryModel.get(index)}) var popup = infoDialog.createObject(app,{nymeaHost: discovery.nymeaHosts.get(index)})
popup.open() popup.open()
} }
} }
@ -300,14 +307,14 @@ Page {
Layout.leftMargin: app.margins Layout.leftMargin: app.margins
Layout.rightMargin: app.margins Layout.rightMargin: app.margins
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
visible: discovery.discoveryModel.count === 0 visible: discovery.nymeaHosts.count === 0
text: qsTr("Do you have a %1 box but it's not connected to your network yet? Use the wireless setup to connect it!").arg(app.systemName) text: qsTr("Do you have a %1 box but it's not connected to your network yet? Use the wireless setup to connect it!").arg(app.systemName)
} }
Button { Button {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: app.margins Layout.leftMargin: app.margins
Layout.rightMargin: app.margins Layout.rightMargin: app.margins
visible: discovery.discoveryModel.count === 0 visible: discovery.nymeaHosts.count === 0
text: qsTr("Start wireless setup") text: qsTr("Start wireless setup")
onClicked: pageStack.push(Qt.resolvedUrl("wifisetup/BluetoothDiscoveryPage.qml"), {nymeaDiscovery: discovery}) onClicked: pageStack.push(Qt.resolvedUrl("wifisetup/BluetoothDiscoveryPage.qml"), {nymeaDiscovery: discovery})
} }
@ -325,7 +332,7 @@ Page {
Layout.leftMargin: app.margins Layout.leftMargin: app.margins
Layout.rightMargin: app.margins Layout.rightMargin: app.margins
Layout.bottomMargin: app.margins Layout.bottomMargin: app.margins
visible: discovery.discoveryModel.count === 0 visible: discovery.nymeaHosts.count === 0
text: qsTr("Demo mode (online)") text: qsTr("Demo mode (online)")
onClicked: { onClicked: {
root.connectToHost("nymea://nymea.nymea.io:2222") root.connectToHost("nymea://nymea.nymea.io:2222")
@ -472,7 +479,7 @@ Page {
standardButtons: Dialog.Ok standardButtons: Dialog.Ok
property var discoveryDevice: null property var nymeaHost: null
header: Item { header: Item {
implicitHeight: headerRow.height + app.margins * 2 implicitHeight: headerRow.height + app.margins * 2
@ -508,7 +515,7 @@ Page {
text: "Name:" text: "Name:"
} }
Label { Label {
text: dialog.discoveryDevice.name text: dialog.nymeaHost.name
Layout.fillWidth: true Layout.fillWidth: true
elide: Text.ElideRight elide: Text.ElideRight
} }
@ -516,7 +523,7 @@ Page {
text: "UUID:" text: "UUID:"
} }
Label { Label {
text: dialog.discoveryDevice.uuid text: dialog.nymeaHost.uuid
Layout.fillWidth: true Layout.fillWidth: true
elide: Text.ElideRight elide: Text.ElideRight
} }
@ -524,7 +531,7 @@ Page {
text: "Version:" text: "Version:"
} }
Label { Label {
text: dialog.discoveryDevice.version text: dialog.nymeaHost.version
Layout.fillWidth: true Layout.fillWidth: true
elide: Text.ElideRight elide: Text.ElideRight
} }
@ -544,7 +551,7 @@ Page {
id: contentColumn id: contentColumn
width: parent.width width: parent.width
Repeater { Repeater {
model: dialog.discoveryDevice.connections model: dialog.nymeaHost.connections
delegate: MeaListItemDelegate { delegate: MeaListItemDelegate {
Layout.fillWidth: true Layout.fillWidth: true
wrapTexts: false wrapTexts: false
@ -573,7 +580,7 @@ Page {
secondaryIconColor: "red" secondaryIconColor: "red"
onClicked: { onClicked: {
root.connectToHost(dialog.discoveryDevice.connections.get(index).url) root.connectToHost2(dialog.nymeaHost.connections.get(index))
dialog.close() dialog.close()
} }
} }

View File

@ -28,7 +28,7 @@ Page {
} }
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
text: engine.connection.url text: engine.connection.currentHost.uuid
font.pixelSize: app.smallFont font.pixelSize: app.smallFont
wrapMode: Text.WrapAtWordBoundaryOrAnywhere wrapMode: Text.WrapAtWordBoundaryOrAnywhere
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter

View File

@ -52,7 +52,7 @@ Page {
} }
Connections { Connections {
target: root.nymeaDiscovery.discoveryModel target: root.nymeadiscovery.nymeaHosts
onCountChanged: updateConnectButton(); onCountChanged: updateConnectButton();
} }
@ -63,14 +63,14 @@ Page {
} }
// FIXME: We should rather look for the UUID here, but nymea-networkmanager doesn't support getting us the nymea uuid (yet) // FIXME: We should rather look for the UUID here, but nymea-networkmanager doesn't support getting us the nymea uuid (yet)
for (var i = 0; i < root.nymeaDiscovery.discoveryModel.count; i++) { for (var i = 0; i < root.nymeadiscovery.nymeaHosts.count; i++) {
for (var j = 0; j < root.nymeaDiscovery.discoveryModel.get(i).connections.count; j++) { for (var j = 0; j < root.nymeadiscovery.nymeaHosts.get(i).connections.count; j++) {
if (root.nymeaDiscovery.discoveryModel.get(i).connections.get(j).url.toString().indexOf(root.networkManagerController.manager.currentConnection.hostAddress) >= 0) { if (root.nymeadiscovery.nymeaHosts.get(i).connections.get(j).url.toString().indexOf(root.networkManagerController.manager.currentConnection.hostAddress) >= 0) {
connectButton.url = root.nymeaDiscovery.discoveryModel.get(i).connections.get(j).url connectButton.url = root.nymeadiscovery.nymeaHosts.get(i).connections.get(j).url
return; return;
} }
} }
root.nymeaDiscovery.discoveryModel.get(i).connections.countChanged.connect(function() { root.nymeadiscovery.nymeaHosts.get(i).connections.countChanged.connect(function() {
updateConnectButton(); updateConnectButton();
}) })
} }