diff --git a/libnymea-app-core/devices.cpp b/libnymea-app-core/devices.cpp index 9100f159..e7056cc2 100644 --- a/libnymea-app-core/devices.cpp +++ b/libnymea-app-core/devices.cpp @@ -102,6 +102,11 @@ void Devices::addDevice(Device *device) if (idx < 0) return; emit dataChanged(index(idx), index(idx), {RoleSetupComplete}); }); + connect(device->states(), &States::dataChanged, this, [device, this]() { + int idx = m_devices.indexOf(device); + if (idx < 0) return; + emit dataChanged(index(idx), index(idx)); + }); emit countChanged(); } diff --git a/libnymea-app-core/devicesproxy.cpp b/libnymea-app-core/devicesproxy.cpp index 3ae851ed..77dbdce1 100644 --- a/libnymea-app-core/devicesproxy.cpp +++ b/libnymea-app-core/devicesproxy.cpp @@ -30,19 +30,23 @@ DevicesProxy::DevicesProxy(QObject *parent) : connect(Engine::instance()->tagsManager()->tags(), &Tags::countChanged, this, &DevicesProxy::invalidateFilter); } -Devices *DevicesProxy::devices() const +QAbstractItemModel *DevicesProxy::devices() const { return m_devices; } -void DevicesProxy::setDevices(Devices *devices) +void DevicesProxy::setDevices(QAbstractItemModel *devices) { if (m_devices != devices) { m_devices = devices; setSourceModel(devices); setSortRole(Devices::RoleName); sort(0); - connect(devices, &Devices::countChanged, this, &DevicesProxy::countChanged); + connect(devices, SIGNAL(countChanged()), this, SIGNAL(countChanged())); + connect(devices, &QAbstractItemModel::dataChanged, this, [this]() { + invalidateFilter(); + emit countChanged(); + }); emit devicesChanged(); emit countChanged(); } @@ -93,9 +97,52 @@ void DevicesProxy::setHiddenInterfaces(const QStringList &hiddenInterfaces) } } +bool DevicesProxy::filterBatteryCritical() const +{ + return m_filterBatteryCritical; +} + +void DevicesProxy::setFilterBatteryCritical(bool filterBatteryCritical) +{ + if (m_filterBatteryCritical != filterBatteryCritical) { + m_filterBatteryCritical = filterBatteryCritical; + emit filterBatteryCriticalChanged(); + invalidateFilter(); + emit countChanged(); + } +} + +bool DevicesProxy::filterDisconnected() const +{ + return m_filterDisconnected; +} + +void DevicesProxy::setFilterDisconnected(bool filterDisconnected) +{ + if (m_filterDisconnected != filterDisconnected) { + m_filterDisconnected = filterDisconnected; + emit filterDisconnectedChanged(); + invalidateFilter(); + emit countChanged(); + } +} + Device *DevicesProxy::get(int index) const { - return m_devices->get(mapToSource(this->index(index, 0)).row()); + return getInternal(mapToSource(this->index(index, 0)).row()); +} + +Device *DevicesProxy::getInternal(int source_index) const +{ + Devices* d = qobject_cast(m_devices); + if (d) { + return d->get(source_index); + } + DevicesProxy *dp = qobject_cast(m_devices); + if (dp) { + return dp->get(source_index); + } + return nullptr; } bool DevicesProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const @@ -108,17 +155,17 @@ bool DevicesProxy::lessThan(const QModelIndex &left, const QModelIndex &right) c bool DevicesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { - Device *device = m_devices->get(source_row); + Device *device = getInternal(source_row); if (!m_filterTagId.isEmpty()) { if (!Engine::instance()->tagsManager()->tags()->findDeviceTag(device->id().toString(), m_filterTagId)) { return false; } } + DeviceClass *deviceClass = Engine::instance()->deviceManager()->deviceClasses()->getDeviceClass(device->deviceClassId()); if (!m_shownInterfaces.isEmpty()) { - QStringList interfaces = Engine::instance()->deviceManager()->deviceClasses()->getDeviceClass(m_devices->get(source_row)->deviceClassId())->interfaces(); bool foundMatch = false; foreach (const QString &filterInterface, m_shownInterfaces) { - if (interfaces.contains(filterInterface)) { + if (deviceClass->interfaces().contains(filterInterface)) { foundMatch = true; continue; } @@ -129,12 +176,23 @@ bool DevicesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_pa } if (!m_hiddenInterfaces.isEmpty()) { - QStringList interfaces = Engine::instance()->deviceManager()->deviceClasses()->getDeviceClass(m_devices->get(source_row)->deviceClassId())->interfaces(); foreach (const QString &filterInterface, m_hiddenInterfaces) { - if (interfaces.contains(filterInterface)) { + if (deviceClass->interfaces().contains(filterInterface)) { return false; } } } + + if (m_filterBatteryCritical) { + if (!deviceClass->interfaces().contains("battery") || device->stateValue(deviceClass->stateTypes()->findByName("batteryCritical")->id()).toBool() == false) { + return false; + } + } + + if (m_filterDisconnected) { + if (!deviceClass->interfaces().contains("connectable") || device->stateValue(deviceClass->stateTypes()->findByName("connected")->id()).toBool() == true) { + return false; + } + } return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } diff --git a/libnymea-app-core/devicesproxy.h b/libnymea-app-core/devicesproxy.h index fcd41c5c..fd9beab2 100644 --- a/libnymea-app-core/devicesproxy.h +++ b/libnymea-app-core/devicesproxy.h @@ -33,16 +33,22 @@ class DevicesProxy : public QSortFilterProxyModel { Q_OBJECT Q_PROPERTY(int count READ rowCount NOTIFY countChanged) - Q_PROPERTY(Devices *devices READ devices WRITE setDevices NOTIFY devicesChanged) + Q_PROPERTY(QAbstractItemModel *devices READ devices WRITE setDevices NOTIFY devicesChanged) Q_PROPERTY(QString filterTagId READ filterTagId WRITE setFilterTagId NOTIFY filterTagIdChanged) Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged) Q_PROPERTY(QStringList hiddenInterfaces READ hiddenInterfaces WRITE setHiddenInterfaces NOTIFY hiddenInterfacesChanged) + // Setting this to true will imply filtering for "battery" interface + Q_PROPERTY(bool filterBatteryCritical READ filterBatteryCritical WRITE setFilterBatteryCritical NOTIFY filterBatteryCriticalChanged) + + // Setting this to true will imply filtering for "connectable" interface + Q_PROPERTY(bool filterDisconnected READ filterDisconnected WRITE setFilterDisconnected NOTIFY filterDisconnectedChanged) + public: explicit DevicesProxy(QObject *parent = 0); - Devices *devices() const; - void setDevices(Devices *devices); + QAbstractItemModel *devices() const; + void setDevices(QAbstractItemModel *devices); QString filterTagId() const; void setFilterTagId(const QString &filterTag); @@ -53,6 +59,12 @@ public: QStringList hiddenInterfaces() const; void setHiddenInterfaces(const QStringList &hiddenInterfaces); + bool filterBatteryCritical() const; + void setFilterBatteryCritical(bool filterBatteryCritical); + + bool filterDisconnected() const; + void setFilterDisconnected(bool filterDisconnected); + Q_INVOKABLE Device *get(int index) const; signals: @@ -60,14 +72,21 @@ signals: void filterTagIdChanged(); void shownInterfacesChanged(); void hiddenInterfacesChanged(); + void filterBatteryCriticalChanged(); + void filterDisconnectedChanged(); void countChanged(); private: - Devices *m_devices = nullptr; + Device *getInternal(int source_index) const; + + QAbstractItemModel *m_devices = nullptr; QString m_filterTagId; QStringList m_shownInterfaces; QStringList m_hiddenInterfaces; + bool m_filterBatteryCritical = false; + bool m_filterDisconnected = false; + protected: bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; diff --git a/libnymea-app-core/rulemanager.cpp b/libnymea-app-core/rulemanager.cpp index e6635fcc..33cbe50c 100644 --- a/libnymea-app-core/rulemanager.cpp +++ b/libnymea-app-core/rulemanager.cpp @@ -222,7 +222,7 @@ void RuleManager::parseEventDescriptors(const QVariantList &eventDescriptorList, paramDescriptor->setOperatorType((ParamDescriptor::ValueOperator)operatorEnum.keyToValue(paramDescriptorVariant.toMap().value("operator").toString().toLocal8Bit())); eventDescriptor->paramDescriptors()->addParamDescriptor(paramDescriptor); } - qDebug() << "Adding eventdescriptor" << eventDescriptor->deviceId() << eventDescriptor->eventTypeId(); +// qDebug() << "Adding eventdescriptor" << eventDescriptor->deviceId() << eventDescriptor->eventTypeId(); rule->eventDescriptors()->addEventDescriptor(eventDescriptor); } } diff --git a/libnymea-app-core/tagsmanager.cpp b/libnymea-app-core/tagsmanager.cpp index d7b34583..e859f824 100644 --- a/libnymea-app-core/tagsmanager.cpp +++ b/libnymea-app-core/tagsmanager.cpp @@ -145,6 +145,6 @@ void TagsManager::addTagInternal(const QVariantMap &tagMap) tag->deleteLater(); return; } - qDebug() << "adding tag" << tag->tagId() << tag->value(); +// qDebug() << "adding tag" << tag->tagId() << tag->value(); m_tags->addTag(tag); } diff --git a/libnymea-common/types/states.cpp b/libnymea-common/types/states.cpp index 4bc09872..b255ca9f 100644 --- a/libnymea-common/types/states.cpp +++ b/libnymea-common/types/states.cpp @@ -79,6 +79,11 @@ void States::addState(State *state) beginInsertRows(QModelIndex(), m_states.count(), m_states.count()); //qDebug() << "States: loaded state" << state->stateTypeId(); m_states.append(state); + connect(state, &State::valueChanged, this, [state, this]() { + int idx = m_states.indexOf(state); + if (idx < 0) return; + emit dataChanged(index(idx), index(idx), {ValueRole}); + }); endInsertRows(); } diff --git a/nymea-app/ui/EditDevicesPage.qml b/nymea-app/ui/EditDevicesPage.qml index d5a6efa9..5b464a49 100644 --- a/nymea-app/ui/EditDevicesPage.qml +++ b/nymea-app/ui/EditDevicesPage.qml @@ -49,8 +49,7 @@ Page { anchors.fill: parent model: Engine.deviceManager.devices delegate: ThingDelegate { - interfaces:model.interfaces - name: model.name + device: Engine.deviceManager.devices.get(index) canDelete: true onClicked: { pageStack.push(Qt.resolvedUrl("devicepages/ConfigureThingPage.qml"), {device: Engine.deviceManager.devices.get(index)}) diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml index 553a6225..f54c3e9e 100644 --- a/nymea-app/ui/Nymea.qml +++ b/nymea-app/ui/Nymea.qml @@ -160,6 +160,8 @@ ApplicationWindow { return qsTr("Temperature"); case "humiditysensor": return qsTr("Humidity"); + case "pressuresensor": + return qsTr("Pressure"); case "inputtrigger": return qsTr("Incoming Events"); case "outputtrigger": @@ -208,6 +210,8 @@ ApplicationWindow { return Qt.resolvedUrl("images/sensors/light.svg") case "conductivitysensor": return Qt.resolvedUrl("images/sensors/conductivity.svg") + case "pressuresensor": + return Qt.resolvedUrl("images/sensors/pressure.svg") case "media": case "mediacontroller": return Qt.resolvedUrl("images/mediaplayer-app-symbolic.svg") @@ -265,8 +269,9 @@ ApplicationWindow { case "lightsensor": return "orange"; case "conductivitysensor": - return "green" - + return "green"; + case "pressuresensor": + return "grey"; } return "grey"; } diff --git a/nymea-app/ui/components/MeaListItemDelegate.qml b/nymea-app/ui/components/MeaListItemDelegate.qml index 7a90bf64..4f055f65 100644 --- a/nymea-app/ui/components/MeaListItemDelegate.qml +++ b/nymea-app/ui/components/MeaListItemDelegate.qml @@ -13,6 +13,9 @@ SwipeDelegate { property int iconSize: app.iconSize property color iconColor: app.guhAccent + property bool batteryCritical: false + property bool disconnected: false + signal deleteClicked() contentItem: RowLayout { @@ -51,6 +54,21 @@ SwipeDelegate { } } + ColorIcon { + Layout.preferredHeight: app.iconSize * .5 + Layout.preferredWidth: height + name: "../images/battery/battery-010.svg" + visible: root.batteryCritical + } + + ColorIcon { + Layout.preferredHeight: app.iconSize * .5 + Layout.preferredWidth: height + name: "../images/dialog-warning-symbolic.svg" + visible: root.disconnected + color: "red" + } + ColorIcon { Layout.preferredHeight: app.iconSize * .6 Layout.preferredWidth: height diff --git a/nymea-app/ui/delegates/ThingDelegate.qml b/nymea-app/ui/delegates/ThingDelegate.qml index 8d88d359..c5b4d904 100644 --- a/nymea-app/ui/delegates/ThingDelegate.qml +++ b/nymea-app/ui/delegates/ThingDelegate.qml @@ -2,14 +2,19 @@ import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.2 import "../components" +import Nymea 1.0 MeaListItemDelegate { id: root width: parent.width - iconName: app.interfacesToIcon(root.interfaces) - text: root.name + iconName: deviceClass ? app.interfacesToIcon(deviceClass.interfaces) : "" + text: device.name progressive: true + batteryCritical: deviceClass && deviceClass.interfaces.indexOf("battery") >= 0 ? device.stateValue(deviceClass.stateTypes.findByName("batteryCritical").id) === true : false + disconnected: deviceClass && deviceClass.interfaces.indexOf("connectable") >= 0 ? device.stateValue(deviceClass.stateTypes.findByName("connected").id) === false : false + + property var device: null + + readonly property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null - property var interfaces: [] - property string name: "" } diff --git a/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml b/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml index 4274b3c4..4d2756a0 100644 --- a/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml +++ b/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml @@ -51,8 +51,7 @@ Page { } delegate: ThingDelegate { width: parent.width - name: model.name - interfaces: Engine.deviceManager.deviceClasses.getDeviceClass(model.deviceClassId).interfaces + device: Engine.deviceManager.devices.getDevice(model.id); onClicked: { enterPage(index, false) } diff --git a/nymea-app/ui/mainviews/DevicesPageDelegate.qml b/nymea-app/ui/mainviews/DevicesPageDelegate.qml index 321bdee0..8dd61738 100644 --- a/nymea-app/ui/mainviews/DevicesPageDelegate.qml +++ b/nymea-app/ui/mainviews/DevicesPageDelegate.qml @@ -62,6 +62,35 @@ Item { } } + Row { + id: quickAlertPane + anchors { top: parent.top; right: parent.right; margins: app.margins } + DevicesProxy { + id: devicesSubProxyConnectables + devices: devicesProxy + filterDisconnected: true + } + ColorIcon { + height: app.iconSize / 2 + width: height + name: "../images/dialog-warning-symbolic.svg" + color: "red" + visible: devicesSubProxyConnectables.count > 0 + } + DevicesProxy { + id: devicesSubProxyBattery + devices: devicesProxy + filterBatteryCritical: true + } + ColorIcon { + height: app.iconSize / 2 + width: height + name: "../images/battery/battery-010.svg" + visible: devicesSubProxyBattery.count > 0 + } + } + + Item { id: inlineControlPane anchors { left: parent.left; bottom: parent.bottom; right: parent.right; margins: app.margins / 2 } diff --git a/nymea-app/ui/mainviews/FavoritesView.qml b/nymea-app/ui/mainviews/FavoritesView.qml index 9436c80f..02982c2c 100644 --- a/nymea-app/ui/mainviews/FavoritesView.qml +++ b/nymea-app/ui/mainviews/FavoritesView.qml @@ -138,6 +138,26 @@ Item { } } + Row { + id: quickAlertPane + anchors { top: parent.top; right: parent.right; margins: app.margins } + ColorIcon { + height: app.iconSize / 2 + width: height + name: "../images/dialog-warning-symbolic.svg" + color: "red" + property var connectedState: delegateRoot.deviceClass.interfaces.indexOf("connectable") >= 0 ? delegateRoot.device.states.getState(delegateRoot.deviceClass.stateTypes.findByName("connected").id) : null + visible: connectedState !== null && connectedState.value === false + } + ColorIcon { + height: app.iconSize / 2 + width: height + name: "../images/battery/battery-010.svg" + property var batteryCriticalState: delegateRoot.deviceClass.interfaces.indexOf("battery") >= 0 ? delegateRoot.device.states.getState(delegateRoot.deviceClass.stateTypes.findByName("batteryCritical").id) : null + visible: batteryCriticalState !== null && batteryCriticalState.value === true + } + } + MouseArea { anchors.fill: parent onClicked: {