Merge PR #124: Improved add device wizard

This commit is contained in:
Jenkins 2019-01-09 02:00:52 +01:00
commit b2cfc0ec15
21 changed files with 379 additions and 159 deletions

View File

@ -1,6 +1,7 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * *
* Copyright (C) 2017 Simon Stuerz <simon.stuerz@guh.io> * * Copyright (C) 2017 Simon Stuerz <simon.stuerz@guh.io> *
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
* * * *
* This file is part of nymea:app * * This file is part of nymea:app *
* * * *
@ -57,6 +58,10 @@ QVariant DeviceClasses::data(const QModelIndex &index, int role) const
return deviceClass->pluginId().toString(); return deviceClass->pluginId().toString();
case RoleVendorId: case RoleVendorId:
return deviceClass->vendorId().toString(); return deviceClass->vendorId().toString();
case RoleInterfaces:
return deviceClass->interfaces();
case RoleBaseInterface:
return deviceClass->baseInterface();
} }
return QVariant(); return QVariant();
} }
@ -68,6 +73,9 @@ int DeviceClasses::count() const
DeviceClass *DeviceClasses::get(int index) const DeviceClass *DeviceClasses::get(int index) const
{ {
if (index < 0 || index >= m_deviceClasses.count()) {
return nullptr;
}
return m_deviceClasses.at(index); return m_deviceClasses.at(index);
} }
@ -108,5 +116,7 @@ QHash<int, QByteArray> DeviceClasses::roleNames() const
roles[RoleDisplayName] = "displayName"; roles[RoleDisplayName] = "displayName";
roles[RolePluginId] = "pluginId"; roles[RolePluginId] = "pluginId";
roles[RoleVendorId] = "vendorId"; roles[RoleVendorId] = "vendorId";
roles[RoleInterfaces] = "interfaces";
roles[RoleBaseInterface] = "baseInterface";
return roles; return roles;
} }

View File

@ -1,6 +1,7 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * *
* Copyright (C) 2017 Simon Stuerz <simon.stuerz@guh.io> * * Copyright (C) 2017 Simon Stuerz <simon.stuerz@guh.io> *
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
* * * *
* This file is part of nymea:app * * This file is part of nymea:app *
* * * *
@ -37,10 +38,12 @@ public:
RoleName, RoleName,
RoleDisplayName, RoleDisplayName,
RolePluginId, RolePluginId,
RoleVendorId RoleVendorId,
RoleInterfaces,
RoleBaseInterface
}; };
explicit DeviceClasses(QObject *parent = 0); explicit DeviceClasses(QObject *parent = nullptr);
QList<DeviceClass *> deviceClasses(); QList<DeviceClass *> deviceClasses();

View File

@ -27,8 +27,10 @@
DeviceClassesProxy::DeviceClassesProxy(QObject *parent) : DeviceClassesProxy::DeviceClassesProxy(QObject *parent) :
QSortFilterProxyModel(parent) QSortFilterProxyModel(parent)
{ {
setSortRole(DeviceClasses::RoleDisplayName);
} }
QUuid DeviceClassesProxy::vendorId() const QUuid DeviceClassesProxy::vendorId() const
{ {
return m_vendorId; return m_vendorId;
@ -55,6 +57,35 @@ void DeviceClassesProxy::setDeviceClasses(DeviceClasses *deviceClasses)
m_deviceClasses = deviceClasses; m_deviceClasses = deviceClasses;
setSourceModel(deviceClasses); setSourceModel(deviceClasses);
emit deviceClassesChanged(); emit deviceClassesChanged();
sort(0);
}
QString DeviceClassesProxy::filterInterface() const
{
return m_filterInterface;
}
void DeviceClassesProxy::setFilterInterface(const QString &filterInterface)
{
if (m_filterInterface != filterInterface) {
m_filterInterface = filterInterface;
emit filterInterfaceChanged();
invalidateFilter();
}
}
bool DeviceClassesProxy::groupByInterface() const
{
return m_groupByInterface;
}
void DeviceClassesProxy::setGroupByInterface(bool groupByInterface)
{
if (m_groupByInterface != groupByInterface) {
m_groupByInterface = groupByInterface;
emit groupByInterfaceChanged();
invalidate();
}
} }
DeviceClass *DeviceClassesProxy::get(int index) const DeviceClass *DeviceClassesProxy::get(int index) const
@ -80,16 +111,27 @@ bool DeviceClassesProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sour
if (deviceClass->createMethods().count() == 1 && deviceClass->createMethods().contains("CreateMethodAuto")) if (deviceClass->createMethods().count() == 1 && deviceClass->createMethods().contains("CreateMethodAuto"))
return false; return false;
if (!m_vendorId.isNull() && deviceClass->vendorId() == m_vendorId) if (!m_vendorId.isNull() && deviceClass->vendorId() != m_vendorId)
return true; return false;
return false; if (!m_filterInterface.isEmpty() && !deviceClass->interfaces().contains(m_filterInterface)) {
return false;
}
return true;
} }
bool DeviceClassesProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const bool DeviceClassesProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const
{ {
QVariant leftName = sourceModel()->data(left); if (m_groupByInterface) {
QVariant rightName = sourceModel()->data(right); QString leftBaseInterface = sourceModel()->data(left, DeviceClasses::RoleBaseInterface).toString();
QString rightBaseInterface = sourceModel()->data(right, DeviceClasses::RoleBaseInterface).toString();
if (leftBaseInterface != rightBaseInterface) {
return QString::localeAwareCompare(leftBaseInterface, rightBaseInterface) < 0;
}
}
QString leftName = sourceModel()->data(left, DeviceClasses::RoleDisplayName).toString();
QString rightName = sourceModel()->data(right, DeviceClasses::RoleDisplayName).toString();
return QString::localeAwareCompare(leftName.toString(), rightName.toString()) < 0; return QString::localeAwareCompare(leftName, rightName) < 0;
} }

View File

@ -36,8 +36,12 @@ class DeviceClassesProxy : public QSortFilterProxyModel
Q_PROPERTY(QUuid vendorId READ vendorId WRITE setVendorId NOTIFY vendorIdChanged) Q_PROPERTY(QUuid vendorId READ vendorId WRITE setVendorId NOTIFY vendorIdChanged)
Q_PROPERTY(DeviceClasses *deviceClasses READ deviceClasses WRITE setDeviceClasses NOTIFY deviceClassesChanged) Q_PROPERTY(DeviceClasses *deviceClasses READ deviceClasses WRITE setDeviceClasses NOTIFY deviceClassesChanged)
Q_PROPERTY(QString filterInterface READ filterInterface WRITE setFilterInterface NOTIFY filterInterfaceChanged)
Q_PROPERTY(bool groupByInterface READ groupByInterface WRITE setGroupByInterface NOTIFY groupByInterfaceChanged)
public: public:
explicit DeviceClassesProxy(QObject *parent = 0); explicit DeviceClassesProxy(QObject *parent = nullptr);
QUuid vendorId() const; QUuid vendorId() const;
void setVendorId(const QUuid &vendorId); void setVendorId(const QUuid &vendorId);
@ -45,10 +49,22 @@ public:
DeviceClasses *deviceClasses(); DeviceClasses *deviceClasses();
void setDeviceClasses(DeviceClasses *deviceClasses); void setDeviceClasses(DeviceClasses *deviceClasses);
QString filterInterface() const;
void setFilterInterface(const QString &filterInterface);
bool groupByInterface() const;
void setGroupByInterface(bool groupByInterface);
Q_INVOKABLE DeviceClass *get(int index) const; Q_INVOKABLE DeviceClass *get(int index) const;
Q_INVOKABLE void resetFilter(); Q_INVOKABLE void resetFilter();
signals:
void vendorIdChanged();
void deviceClassesChanged();
void filterInterfaceChanged();
void groupByInterfaceChanged();
protected: protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE; bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE;
@ -56,11 +72,8 @@ protected:
private: private:
QUuid m_vendorId; QUuid m_vendorId;
DeviceClasses *m_deviceClasses; DeviceClasses *m_deviceClasses;
QString m_filterInterface;
signals: bool m_groupByInterface = false;
void vendorIdChanged();
void deviceClassesChanged();
}; };
#endif // DEVICECLASSFILERMODEL_H #endif // DEVICECLASSFILERMODEL_H

View File

@ -74,68 +74,12 @@ QVariant Devices::data(const QModelIndex &index, int role) const
return device->deviceClassId().toString(); return device->deviceClassId().toString();
case RoleSetupComplete: case RoleSetupComplete:
return device->setupComplete(); return device->setupComplete();
case RoleInterfaces: { case RoleInterfaces:
return device->deviceClass()->interfaces(); return device->deviceClass()->interfaces();
} case RoleBaseInterface:
case RoleBaseInterface: { return device->deviceClass()->baseInterface();
QStringList interfaces = device->deviceClass()->interfaces();
if (interfaces.contains("gateway")) {
return "gateway";
}
if (interfaces.contains("shutter")) {
return "shutter";
}
if (interfaces.contains("blind")) {
return "blind";
}
if (interfaces.contains("garagegate")) {
return "garagegate";
}
if (interfaces.contains("inputtrigger")) {
return "inputtrigger";
}
if (interfaces.contains("awning")) {
return "awning";
}
if (interfaces.contains("outputtrigger")) {
return "outputtrigger";
}
if (interfaces.contains("light")) {
return "light";
}
if (interfaces.contains("sensor")) {
return "sensor";
}
if (interfaces.contains("weather")) {
return "weather";
}
if (interfaces.contains("media")) {
return "media";
}
if (interfaces.contains("button")) {
return "button";
}
if (interfaces.contains("notifications")) {
return "notifications";
}
if (interfaces.contains("smartmeter")) {
return "smartmeter";
}
if (interfaces.contains("heating")) {
return "heating";
}
if (interfaces.contains("evcharger")) {
return "evcharger";
}
if (interfaces.contains("powersocket")) {
return "powersocket";
}
return "uncategorized";
}
} }
return QVariant(); return QVariant();
} }
void Devices::addDevice(Device *device) void Devices::addDevice(Device *device)

View File

@ -65,6 +65,20 @@ void InterfacesModel::setShownInterfaces(const QStringList &shownInterfaces)
} }
} }
bool InterfacesModel::onlyConfiguredDevices() const
{
return m_onlyConfiguredDevices;
}
void InterfacesModel::setOnlyConfiguredDevices(bool onlyConfigured)
{
if (m_onlyConfiguredDevices != onlyConfigured) {
m_onlyConfiguredDevices = onlyConfigured;
emit onlyConfiguredDevicesChanged();
syncInterfaces();
}
}
bool InterfacesModel::showUncategorized() const bool InterfacesModel::showUncategorized() const
{ {
return m_showUncategorized; return m_showUncategorized;
@ -79,30 +93,47 @@ void InterfacesModel::setShowUncategorized(bool showUncategorized)
} }
} }
QString InterfacesModel::get(int index) const
{
if (index < 0 || index > m_interfaces.count()) {
return QString();
}
return m_interfaces.at(index);
}
void InterfacesModel::syncInterfaces() void InterfacesModel::syncInterfaces()
{ {
if (!m_deviceManager) { if (!m_deviceManager) {
return; return;
} }
QStringList baseList;
if (m_onlyConfiguredDevices) {
for (int i = 0; i < m_deviceManager->devices()->rowCount(); i++) {
DeviceClass *dc = m_deviceManager->deviceClasses()->getDeviceClass(m_deviceManager->devices()->get(i)->deviceClassId());
baseList << dc->interfaces();
}
} else {
for (int i = 0; i < m_deviceManager->deviceClasses()->rowCount(); i++) {
DeviceClass *dc = m_deviceManager->deviceClasses()->get(i);
baseList << dc->interfaces();
}
}
baseList.removeDuplicates();
QStringList interfacesInSource; QStringList interfacesInSource;
for (int i = 0; i < m_deviceManager->devices()->rowCount(); i++) { bool isInShownIfaces = false;
DeviceClass *dc = m_deviceManager->deviceClasses()->getDeviceClass(m_deviceManager->devices()->get(i)->deviceClassId()); foreach (const QString &interface, baseList) {
// qDebug() << "device" <<dc->name() << "has interfaces" << dc->interfaces(); if (!m_shownInterfaces.contains(interface)) {
continue;
bool isInShownIfaces = false;
foreach (const QString &interface, dc->interfaces()) {
if (!m_shownInterfaces.contains(interface)) {
continue;
}
if (!interfacesInSource.contains(interface)) {
interfacesInSource.append(interface);
}
isInShownIfaces = true;
} }
if (!isInShownIfaces && !interfacesInSource.contains("uncategorized")) {
interfacesInSource.append("uncategorized"); if (!interfacesInSource.contains(interface)) {
interfacesInSource.append(interface);
} }
isInShownIfaces = true;
}
if (!isInShownIfaces && !interfacesInSource.contains("uncategorized")) {
interfacesInSource.append("uncategorized");
} }
QStringList interfacesToAdd = interfacesInSource; QStringList interfacesToAdd = interfacesInSource;
QStringList interfacesToRemove; QStringList interfacesToRemove;

View File

@ -14,6 +14,8 @@ class InterfacesModel : public QAbstractListModel
Q_PROPERTY(int count READ rowCount NOTIFY countChanged) Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
Q_PROPERTY(DeviceManager* deviceManager READ deviceManager WRITE setDeviceManager NOTIFY deviceManagerChanged) Q_PROPERTY(DeviceManager* deviceManager READ deviceManager WRITE setDeviceManager NOTIFY deviceManagerChanged)
Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged) Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged)
Q_PROPERTY(bool onlyConfiguredDevices READ onlyConfiguredDevices WRITE setOnlyConfiguredDevices NOTIFY onlyConfiguredDevicesChanged)
Q_PROPERTY(bool showUncategorized READ showUncategorized WRITE setShowUncategorized NOTIFY showUncategorizedChanged) Q_PROPERTY(bool showUncategorized READ showUncategorized WRITE setShowUncategorized NOTIFY showUncategorizedChanged)
public: public:
@ -34,13 +36,19 @@ public:
QStringList shownInterfaces() const; QStringList shownInterfaces() const;
void setShownInterfaces(const QStringList &shownInterfaces); void setShownInterfaces(const QStringList &shownInterfaces);
bool onlyConfiguredDevices() const;
void setOnlyConfiguredDevices(bool onlyConfigured);
bool showUncategorized() const; bool showUncategorized() const;
void setShowUncategorized(bool showUncategorized); void setShowUncategorized(bool showUncategorized);
Q_INVOKABLE QString get(int index) const;
signals: signals:
void countChanged(); void countChanged();
void deviceManagerChanged(); void deviceManagerChanged();
void shownInterfacesChanged(); void shownInterfacesChanged();
bool onlyConfiguredDevicesChanged();
void showUncategorizedChanged(); void showUncategorizedChanged();
private slots: private slots:
@ -52,6 +60,7 @@ private:
QStringList m_interfaces; QStringList m_interfaces;
QStringList m_shownInterfaces; QStringList m_shownInterfaces;
bool m_onlyConfiguredDevices = true;
bool m_showUncategorized = false; bool m_showUncategorized = false;
}; };

View File

@ -50,7 +50,7 @@ JsonTypes::JsonTypes(QObject *parent) :
Vendor *JsonTypes::unpackVendor(const QVariantMap &vendorMap) Vendor *JsonTypes::unpackVendor(const QVariantMap &vendorMap)
{ {
Vendor *v = new Vendor(vendorMap.value("id").toUuid(), vendorMap.value("name").toString()); Vendor *v = new Vendor(vendorMap.value("id").toString(), vendorMap.value("name").toString());
v->setDisplayName(vendorMap.value("displayName").toString()); v->setDisplayName(vendorMap.value("displayName").toString());
return v; return v;
} }

View File

@ -34,7 +34,7 @@ class VendorsProxy : public QSortFilterProxyModel
Q_PROPERTY(Vendors *vendors READ vendors WRITE setVendors NOTIFY vendorsChanged) Q_PROPERTY(Vendors *vendors READ vendors WRITE setVendors NOTIFY vendorsChanged)
public: public:
explicit VendorsProxy(QObject *parent = 0); explicit VendorsProxy(QObject *parent = nullptr);
Vendors *vendors(); Vendors *vendors();
void setVendors(Vendors *vendors); void setVendors(Vendors *vendors);

View File

@ -1,6 +1,7 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * *
* Copyright (C) 2017 Simon Stuerz <simon.stuerz@guh.io> * * Copyright (C) 2017 Simon Stuerz <simon.stuerz@guh.io> *
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
* * * *
* This file is part of nymea:app * * This file is part of nymea:app *
* * * *
@ -129,6 +130,63 @@ void DeviceClass::setInterfaces(const QStringList &interfaces)
m_interfaces = interfaces; m_interfaces = interfaces;
} }
QString DeviceClass::baseInterface() const
{
if (m_interfaces.contains("gateway")) {
return "gateway";
}
if (m_interfaces.contains("shutter")) {
return "shutter";
}
if (m_interfaces.contains("blind")) {
return "blind";
}
if (m_interfaces.contains("garagegate")) {
return "garagegate";
}
if (m_interfaces.contains("inputtrigger")) {
return "inputtrigger";
}
if (m_interfaces.contains("awning")) {
return "awning";
}
if (m_interfaces.contains("outputtrigger")) {
return "outputtrigger";
}
if (m_interfaces.contains("light")) {
return "light";
}
if (m_interfaces.contains("sensor")) {
return "sensor";
}
if (m_interfaces.contains("weather")) {
return "weather";
}
if (m_interfaces.contains("media")) {
return "media";
}
if (m_interfaces.contains("button")) {
return "button";
}
if (m_interfaces.contains("notifications")) {
return "notifications";
}
if (m_interfaces.contains("smartmeter")) {
return "smartmeter";
}
if (m_interfaces.contains("heating")) {
return "heating";
}
if (m_interfaces.contains("evcharger")) {
return "evcharger";
}
if (m_interfaces.contains("powersocket")) {
return "powersocket";
}
return "uncategorized";
}
ParamTypes *DeviceClass::paramTypes() const ParamTypes *DeviceClass::paramTypes() const
{ {
return m_paramTypes; return m_paramTypes;

View File

@ -1,6 +1,7 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * *
* Copyright (C) 2017 Simon Stuerz <simon.stuerz@guh.io> * * Copyright (C) 2017 Simon Stuerz <simon.stuerz@guh.io> *
* Copyright (C) 2019 Michael Zanetti <michael.zanetti@nymea.io> *
* * * *
* This file is part of nymea:app * * This file is part of nymea:app *
* * * *
@ -45,6 +46,7 @@ class DeviceClass : public QObject
Q_PROPERTY(SetupMethod setupMethod READ setupMethod CONSTANT) Q_PROPERTY(SetupMethod setupMethod READ setupMethod CONSTANT)
Q_PROPERTY(QStringList basicTags READ basicTagNames CONSTANT) Q_PROPERTY(QStringList basicTags READ basicTagNames CONSTANT)
Q_PROPERTY(QStringList interfaces READ interfaces CONSTANT) Q_PROPERTY(QStringList interfaces READ interfaces CONSTANT)
Q_PROPERTY(QString baseInterface READ baseInterface CONSTANT)
Q_PROPERTY(ParamTypes *paramTypes READ paramTypes NOTIFY paramTypesChanged) Q_PROPERTY(ParamTypes *paramTypes READ paramTypes NOTIFY paramTypesChanged)
Q_PROPERTY(ParamTypes *discoveryParamTypes READ discoveryParamTypes NOTIFY discoveryParamTypesChanged) Q_PROPERTY(ParamTypes *discoveryParamTypes READ discoveryParamTypes NOTIFY discoveryParamTypesChanged)
Q_PROPERTY(StateTypes *stateTypes READ stateTypes NOTIFY stateTypesChanged) Q_PROPERTY(StateTypes *stateTypes READ stateTypes NOTIFY stateTypesChanged)
@ -116,6 +118,8 @@ public:
QStringList interfaces() const; QStringList interfaces() const;
void setInterfaces(const QStringList &interfaces); void setInterfaces(const QStringList &interfaces);
QString baseInterface() const;
ParamTypes *paramTypes() const; ParamTypes *paramTypes() const;
void setParamTypes(ParamTypes *paramTypes); void setParamTypes(ParamTypes *paramTypes);

View File

@ -22,19 +22,19 @@
#include "vendor.h" #include "vendor.h"
Vendor::Vendor(const QUuid &id, const QString &name, QObject *parent) : Vendor::Vendor(const QString &id, const QString &name, QObject *parent) :
QObject(parent), QObject(parent),
m_id(id), m_id(id),
m_name(name) m_name(name)
{ {
} }
QUuid Vendor::id() const QString Vendor::id() const
{ {
return m_id; return m_id;
} }
void Vendor::setId(const QUuid &id) void Vendor::setId(const QString &id)
{ {
m_id = id; m_id = id;
} }

View File

@ -32,13 +32,13 @@ class Vendor : public QObject
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString displayName READ displayName CONSTANT) Q_PROPERTY(QString displayName READ displayName CONSTANT)
Q_PROPERTY(QUuid id READ id CONSTANT) Q_PROPERTY(QString id READ id CONSTANT)
public: public:
Vendor(const QUuid &id = QUuid(), const QString &name = QString(), QObject *parent = nullptr); Vendor(const QString &id = QString(), const QString &name = QString(), QObject *parent = nullptr);
QUuid id() const; QString id() const;
void setId(const QUuid &id); void setId(const QString &id);
QString name() const; QString name() const;
void setName(const QString &name); void setName(const QString &name);
@ -47,7 +47,7 @@ public:
void setDisplayName(const QString &displayName); void setDisplayName(const QString &displayName);
private: private:
QUuid m_id; QString m_id;
QString m_name; QString m_name;
QString m_displayName; QString m_displayName;
}; };

View File

@ -29,24 +29,14 @@ Vendors::Vendors(QObject *parent) :
{ {
} }
QList<Vendor *> Vendors::vendors() Vendor *Vendors::getVendor(const QString &vendorId) const
{
return m_vendors;
}
int Vendors::count() const
{
return m_vendors.count();
}
Vendor *Vendors::getVendor(const QUuid &vendorId) const
{ {
foreach (Vendor *vendor, m_vendors) { foreach (Vendor *vendor, m_vendors) {
if (vendor->id() == vendorId) { if (vendor->id() == vendorId) {
return vendor; return vendor;
} }
} }
return 0; return nullptr;
} }
int Vendors::rowCount(const QModelIndex &parent) const int Vendors::rowCount(const QModelIndex &parent) const
@ -67,7 +57,7 @@ QVariant Vendors::data(const QModelIndex &index, int role) const
case RoleDisplayName: case RoleDisplayName:
return vendor->displayName(); return vendor->displayName();
case RoleId: case RoleId:
return vendor->id().toString(); return vendor->id();
} }
return QVariant(); return QVariant();
} }
@ -79,15 +69,24 @@ void Vendors::addVendor(Vendor *vendor)
//qDebug() << "Vendors: loaded vendor" << vendor->name(); //qDebug() << "Vendors: loaded vendor" << vendor->name();
m_vendors.append(vendor); m_vendors.append(vendor);
endInsertRows(); endInsertRows();
emit countChanged();
} }
void Vendors::clearModel() void Vendors::clearModel()
{ {
beginResetModel(); beginResetModel();
qDebug() << "Vendors: delete all vendors";
qDeleteAll(m_vendors); qDeleteAll(m_vendors);
m_vendors.clear(); m_vendors.clear();
endResetModel(); endResetModel();
emit countChanged();
}
Vendor *Vendors::get(int index) const
{
if (index < 0 || index >= m_vendors.count()) {
return nullptr;
}
return m_vendors.at(index);
} }
QHash<int, QByteArray> Vendors::roleNames() const QHash<int, QByteArray> Vendors::roleNames() const

View File

@ -30,6 +30,8 @@
class Vendors : public QAbstractListModel class Vendors : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
public: public:
enum Role { enum Role {
RoleId, RoleId,
@ -37,20 +39,20 @@ public:
RoleDisplayName RoleDisplayName
}; };
explicit Vendors(QObject *parent = 0); explicit Vendors(QObject *parent = nullptr);
QList<Vendor *> vendors();
Q_INVOKABLE int count() const;
Q_INVOKABLE Vendor *getVendor(const QUuid &vendorId) const;
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 addVendor(Vendor *vendor); void addVendor(Vendor *vendor);
void clearModel(); void clearModel();
Q_INVOKABLE Vendor* get(int index) const;
Q_INVOKABLE Vendor *getVendor(const QString &vendorId) const;
signals:
void countChanged();
protected: protected:
QHash<int, QByteArray> roleNames() const; QHash<int, QByteArray> roleNames() const;

View File

@ -32,7 +32,6 @@
<file>ui/components/IconMenuItem.qml</file> <file>ui/components/IconMenuItem.qml</file>
<file>ui/components/Graph.qml</file> <file>ui/components/Graph.qml</file>
<file>ui/components/ErrorDialog.qml</file> <file>ui/components/ErrorDialog.qml</file>
<file>ui/components/InterfacesModels.qml</file>
<file>ui/components/ShutterControls.qml</file> <file>ui/components/ShutterControls.qml</file>
<file>ui/components/MeaDialog.qml</file> <file>ui/components/MeaDialog.qml</file>
<file>ui/components/MeaListItemDelegate.qml</file> <file>ui/components/MeaListItemDelegate.qml</file>
@ -147,5 +146,6 @@
<file>ui/devicepages/BoolSensorDevicePage.qml</file> <file>ui/devicepages/BoolSensorDevicePage.qml</file>
<file>ui/devicepages/PowersocketDevicePage.qml</file> <file>ui/devicepages/PowersocketDevicePage.qml</file>
<file>ui/devicelistpages/PowerSocketsDeviceListPage.qml</file> <file>ui/devicelistpages/PowerSocketsDeviceListPage.qml</file>
<file>ui/components/GroupedListView.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -60,7 +60,7 @@ Page {
Layout.fillWidth: true Layout.fillWidth: true
} }
ListView { GroupedListView {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
clip: true clip: true
@ -71,11 +71,6 @@ Page {
groupByInterface: true groupByInterface: true
nameFilter: filterInput.shown ? filterInput.text : "" nameFilter: filterInput.shown ? filterInput.text : ""
} }
section.property: "baseInterface"
section.criteria: ViewSection.FullString
section.delegate: ListSectionHeader {
text: app.interfaceToString(section)
}
delegate: ThingDelegate { delegate: ThingDelegate {
device: deviceProxy.get(index) device: deviceProxy.get(index)

View File

@ -1,6 +1,7 @@
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Controls 2.1 import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.1
import Nymea 1.0 import Nymea 1.0
import "components" import "components"
import "delegates" import "delegates"
@ -27,7 +28,7 @@ Page {
property DeviceClass deviceClass: null property DeviceClass deviceClass: null
property DeviceDescriptor deviceDescriptor: null property DeviceDescriptor deviceDescriptor: null
property var discoveryParams: [] property var discoveryParams: []
property string deviceName: null property string deviceName: ""
property int pairRequestId: 0 property int pairRequestId: 0
property var pairingTransactionId: null property var pairingTransactionId: null
property int addRequestId: 0 property int addRequestId: 0
@ -70,7 +71,12 @@ Page {
StackView { StackView {
id: internalPageStack id: internalPageStack
anchors.fill: parent anchors.fill: parent
initialItem: Page { Component.onCompleted: push(deviceClassesPage)
}
Component {
id: vendorsPage
Page {
ListView { ListView {
anchors.fill: parent anchors.fill: parent
model: VendorsProxy { model: VendorsProxy {
@ -92,18 +98,98 @@ Page {
Component { Component {
id: deviceClassesPage id: deviceClassesPage
Page { Page {
ListView { Pane {
anchors.fill: parent id: filterPane
anchors { left: parent.left; top: parent.top; right: parent.right }
Behavior on height { NumberAnimation { duration: 120; easing.type: Easing.InOutQuad } }
height: implicitHeight + app.margins * 2
Material.elevation: 1
z: 1
leftPadding: 0; rightPadding: 0; topPadding: 0; bottomPadding: 0
contentItem: Rectangle {
color: app.primaryColor
clip: true
GridLayout {
anchors.fill: parent
anchors.margins: app.margins
columnSpacing: app.margins
columns: 2
Label {
text: qsTr("Vendor")
}
ComboBox {
id: vendorFilterComboBox
Layout.fillWidth: true
textRole: "displayName"
model: ListModel {
id: vendorsFilterModel
ListElement { displayName: qsTr("All"); vendorId: "" }
Component.onCompleted: {
for (var i = 0; i < engine.deviceManager.vendors.count; i++) {
var vendor = engine.deviceManager.vendors.get(i);
append({displayName: vendor.displayName, vendorId: vendor.id})
}
}
}
}
Label {
text: qsTr("Type")
}
ComboBox {
id: typeFilterComboBox
Layout.fillWidth: true
textRole: "displayName"
InterfacesModel {
id: interfacesModel
deviceManager: engine.deviceManager
shownInterfaces: app.supportedInterfaces
onlyConfiguredDevices: false
showUncategorized: false
}
model: ListModel {
id: typeFilterModel
ListElement { interfaceName: ""; displayName: qsTr("All") }
Component.onCompleted: {
for (var i = 0; i < interfacesModel.count; i++) {
append({interfaceName: interfacesModel.get(i), displayName: app.interfaceToString(interfacesModel.get(i))});
}
}
}
}
}
}
}
GroupedListView {
anchors {
left: parent.left
top: filterPane.bottom
right: parent.right
bottom: parent.bottom
}
model: DeviceClassesProxy { model: DeviceClassesProxy {
id: deviceClassesProxy id: deviceClassesProxy
vendorId: d.vendorId ? d.vendorId : "" vendorId: vendorsFilterModel.get(vendorFilterComboBox.currentIndex).vendorId
deviceClasses: engine.deviceManager.deviceClasses deviceClasses: engine.deviceManager.deviceClasses
filterInterface: typeFilterModel.get(typeFilterComboBox.currentIndex).interfaceName
groupByInterface: true
} }
delegate: MeaListItemDelegate { delegate: MeaListItemDelegate {
id: deviceClassDelegate id: deviceClassDelegate
width: parent.width width: parent.width
text: model.displayName text: model.displayName
subText: engine.deviceManager.vendors.getVendor(model.vendorId).displayName
iconName: app.interfacesToIcon(deviceClass.interfaces) iconName: app.interfacesToIcon(deviceClass.interfaces)
prominentSubText: false
wrapTexts: false
property var deviceClass: deviceClassesProxy.get(index) property var deviceClass: deviceClassesProxy.get(index)
@ -219,23 +305,46 @@ Page {
property var deviceClass: null property var deviceClass: null
ListView { ColumnLayout {
anchors.fill: parent anchors.fill: parent
model: discovery
delegate: MeaListItemDelegate { ListView {
width: parent.width Layout.fillWidth: true
height: app.delegateHeight Layout.fillHeight: true
text: model.name model: discovery
subText: model.description delegate: MeaListItemDelegate {
iconName: app.interfacesToIcon(discoveryView.deviceClass.interfaces) width: parent.width
onClicked: { height: app.delegateHeight
d.deviceDescriptor = discovery.get(index); text: model.name
d.deviceName = model.name; subText: model.description
internalPageStack.push(paramsPage) iconName: app.interfacesToIcon(discoveryView.deviceClass.interfaces)
onClicked: {
d.deviceDescriptor = discovery.get(index);
d.deviceName = model.name;
internalPageStack.push(paramsPage)
}
} }
} }
Button {
id: retryButton
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
text: qsTr("Search again")
onClicked: discovery.discoverDevices(d.deviceClass.id, d.discoveryParams)
visible: !discovery.busy && discovery.count > 0
}
Button {
id: manualAddButton
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
visible: d.deviceClass.createMethods.indexOf("CreateMethodUser") >= 0
text: qsTr("Add thing manually")
onClicked: internalPageStack.push(paramsPage)
}
} }
ColumnLayout { ColumnLayout {
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width - app.margins * 2 width: parent.width - app.margins * 2
@ -249,7 +358,6 @@ Page {
} }
BusyIndicator { BusyIndicator {
running: visible running: visible
onRunningChanged: print("********* running changed", running)
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
} }

View File

@ -0,0 +1,15 @@
import QtQuick 2.9
import QtQuick.Controls 2.1
ListView {
ScrollBar.vertical: ScrollBar {}
clip: true
section.property: "baseInterface"
section.criteria: ViewSection.FullString
section.delegate: ListSectionHeader {
text: app.interfaceToString(section)
}
}

View File

@ -1,8 +0,0 @@
import QtQuick 2.0
Item {
property ListModel eventTemplateModel: ListModel {
ListElement { interfaceName: "battery"; stateName: "batteryLevel"; stateDisplayName: qsTr("Battery level"); eventDisplayName: qsTr("Battery level changed") }
ListElement { interfaceName: "battery"; stateName: "batteryCritical"; stateDisplayName: qsTr("Battery critical"); eventDisplayName: qsTr("Battery critical changed") }
}
}

View File

@ -64,16 +64,11 @@ Page {
ThinDivider { visible: root.allowSelectAny } ThinDivider { visible: root.allowSelectAny }
ListView { GroupedListView {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: root.selectInterface ? interfacesProxy : devicesProxy model: root.selectInterface ? interfacesProxy : devicesProxy
clip: true clip: true
section.property: "baseInterface"
section.criteria: ViewSection.FullString
section.delegate: ListSectionHeader {
text: app.interfaceToString(section)
}
delegate: MeaListItemDelegate { delegate: MeaListItemDelegate {
width: parent.width width: parent.width
text: root.selectInterface ? model.displayName : model.name text: root.selectInterface ? model.displayName : model.name