Merge PR #276: Grouping
This commit is contained in:
commit
2955690e02
@ -23,6 +23,7 @@
|
||||
#include "devicesproxy.h"
|
||||
#include "engine.h"
|
||||
#include "tagsmanager.h"
|
||||
#include "types/tag.h"
|
||||
|
||||
DevicesProxy::DevicesProxy(QObject *parent) :
|
||||
QSortFilterProxyModel(parent)
|
||||
@ -93,7 +94,7 @@ QString DevicesProxy::filterTagId() const
|
||||
|
||||
void DevicesProxy::setFilterTagId(const QString &filterTag)
|
||||
{
|
||||
if (m_filterTagId != filterTagId()) {
|
||||
if (m_filterTagId != filterTag) {
|
||||
m_filterTagId = filterTag;
|
||||
emit filterTagIdChanged();
|
||||
invalidateFilter();
|
||||
@ -101,6 +102,21 @@ void DevicesProxy::setFilterTagId(const QString &filterTag)
|
||||
}
|
||||
}
|
||||
|
||||
QString DevicesProxy::filterTagValue() const
|
||||
{
|
||||
return m_filterTagValue;
|
||||
}
|
||||
|
||||
void DevicesProxy::setFilterTagValue(const QString &tagValue)
|
||||
{
|
||||
if (m_filterTagValue != tagValue) {
|
||||
m_filterTagValue = tagValue;
|
||||
emit filterTagValueChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString DevicesProxy::filterDeviceClassId() const
|
||||
{
|
||||
return m_filterDeviceClassId;
|
||||
@ -112,6 +128,22 @@ void DevicesProxy::setFilterDeviceClassId(const QString &filterDeviceClassId)
|
||||
m_filterDeviceClassId = filterDeviceClassId;
|
||||
emit filterDeviceClassIdChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString DevicesProxy::filterDeviceId() const
|
||||
{
|
||||
return m_filterDeviceId;
|
||||
}
|
||||
|
||||
void DevicesProxy::setFilterDeviceId(const QString &filterDeviceId)
|
||||
{
|
||||
if (m_filterDeviceId != filterDeviceId) {
|
||||
m_filterDeviceId = filterDeviceId;
|
||||
emit filterDeviceIdChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +188,7 @@ void DevicesProxy::setNameFilter(const QString &nameFilter)
|
||||
m_nameFilter = nameFilter;
|
||||
emit nameFilterChanged();
|
||||
invalidateFilter();
|
||||
countChanged();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,6 +233,7 @@ void DevicesProxy::setGroupByInterface(bool groupByInterface)
|
||||
m_groupByInterface = groupByInterface;
|
||||
emit groupByInterfaceChanged();
|
||||
invalidate();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,12 +287,21 @@ bool DevicesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_pa
|
||||
{
|
||||
Device *device = getInternal(source_row);
|
||||
if (!m_filterTagId.isEmpty()) {
|
||||
if (!m_engine->tagsManager()->tags()->findDeviceTag(device->id().toString(), m_filterTagId)) {
|
||||
Tag *tag = m_engine->tagsManager()->tags()->findDeviceTag(device->id().toString(), m_filterTagId);
|
||||
if (!tag) {
|
||||
return false;
|
||||
}
|
||||
if (!m_filterTagValue.isEmpty() && tag->value() != m_filterTagValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_filterDeviceClassId.isEmpty()) {
|
||||
if (device->deviceClassId() != m_filterDeviceClassId) {
|
||||
if (device->deviceClassId() != QUuid(m_filterDeviceClassId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_filterDeviceId.isEmpty()) {
|
||||
if (device->id() != QUuid(m_filterDeviceId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +38,9 @@ class DevicesProxy : public QSortFilterProxyModel
|
||||
Q_PROPERTY(Engine* engine READ engine WRITE setEngine NOTIFY engineChanged)
|
||||
Q_PROPERTY(DevicesProxy *parentProxy READ parentProxy WRITE setParentProxy NOTIFY parentProxyChanged)
|
||||
Q_PROPERTY(QString filterTagId READ filterTagId WRITE setFilterTagId NOTIFY filterTagIdChanged)
|
||||
Q_PROPERTY(QString filterTagValue READ filterTagValue WRITE setFilterTagValue NOTIFY filterTagValueChanged)
|
||||
Q_PROPERTY(QString filterDeviceClassId READ filterDeviceClassId WRITE setFilterDeviceClassId NOTIFY filterDeviceClassIdChanged)
|
||||
Q_PROPERTY(QString filterDeviceId READ filterDeviceId WRITE setFilterDeviceId NOTIFY filterDeviceIdChanged)
|
||||
Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged)
|
||||
Q_PROPERTY(QStringList hiddenInterfaces READ hiddenInterfaces WRITE setHiddenInterfaces NOTIFY hiddenInterfacesChanged)
|
||||
Q_PROPERTY(QString nameFilter READ nameFilter WRITE setNameFilter NOTIFY nameFilterChanged)
|
||||
@ -63,9 +65,15 @@ public:
|
||||
QString filterTagId() const;
|
||||
void setFilterTagId(const QString &filterTag);
|
||||
|
||||
QString filterTagValue() const;
|
||||
void setFilterTagValue(const QString &tagValue);
|
||||
|
||||
QString filterDeviceClassId() const;
|
||||
void setFilterDeviceClassId(const QString &filterDeviceClassId);
|
||||
|
||||
QString filterDeviceId() const;
|
||||
void setFilterDeviceId(const QString &filterDeviceId);
|
||||
|
||||
QStringList shownInterfaces() const;
|
||||
void setShownInterfaces(const QStringList &shownInterfaces);
|
||||
|
||||
@ -91,7 +99,9 @@ signals:
|
||||
void engineChanged();
|
||||
void parentProxyChanged();
|
||||
void filterTagIdChanged();
|
||||
void filterTagValueChanged();
|
||||
void filterDeviceClassIdChanged();
|
||||
void filterDeviceIdChanged();
|
||||
void shownInterfacesChanged();
|
||||
void hiddenInterfacesChanged();
|
||||
void nameFilterChanged();
|
||||
@ -106,7 +116,9 @@ private:
|
||||
Engine *m_engine = nullptr;
|
||||
DevicesProxy *m_parentProxy = nullptr;
|
||||
QString m_filterTagId;
|
||||
QString m_filterTagValue;
|
||||
QString m_filterDeviceClassId;
|
||||
QString m_filterDeviceId;
|
||||
QStringList m_shownInterfaces;
|
||||
QStringList m_hiddenInterfaces;
|
||||
QString m_nameFilter;
|
||||
|
||||
@ -137,6 +137,7 @@ void Engine::onConnectedChanged()
|
||||
qDebug() << "Engine: connected changed:" << m_jsonRpcClient->connected();
|
||||
m_deviceManager->clear();
|
||||
m_ruleManager->clear();
|
||||
m_tagsManager->clear();
|
||||
if (m_jsonRpcClient->connected()) {
|
||||
qDebug() << "Engine: inital setup required:" << m_jsonRpcClient->initialSetupRequired() << "auth required:" << m_jsonRpcClient->authenticationRequired();
|
||||
if (!m_jsonRpcClient->initialSetupRequired() && !m_jsonRpcClient->authenticationRequired()) {
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
#include "models/wirelessaccesspointsproxy.h"
|
||||
#include "tagsmanager.h"
|
||||
#include "models/tagsproxymodel.h"
|
||||
#include "models/taglistmodel.h"
|
||||
#include "types/tag.h"
|
||||
#include "ruletemplates/ruletemplates.h"
|
||||
#include "ruletemplates/ruletemplate.h"
|
||||
@ -189,6 +190,7 @@ void registerQmlTypes() {
|
||||
qmlRegisterUncreatableType<Tags>(uri, 1, 0, "Tags", "Get it from TagsManager");
|
||||
qmlRegisterUncreatableType<Tag>(uri, 1, 0, "Tag", "Get it from Tags");
|
||||
qmlRegisterType<TagsProxyModel>(uri, 1, 0, "TagsProxyModel");
|
||||
qmlRegisterType<TagListModel>(uri, 1, 0, "TagListModel");
|
||||
|
||||
qmlRegisterType<NetworkManagerController>(uri, 1, 0, "NetworkManagerController");
|
||||
qmlRegisterType<BluetoothDiscovery>(uri, 1, 0, "BluetoothDiscovery");
|
||||
|
||||
@ -47,6 +47,7 @@ SOURCES += \
|
||||
deviceclassesproxy.cpp \
|
||||
devicediscovery.cpp \
|
||||
models/packagesfiltermodel.cpp \
|
||||
models/taglistmodel.cpp \
|
||||
vendorsproxy.cpp \
|
||||
pluginsproxy.cpp \
|
||||
interfacesmodel.cpp \
|
||||
@ -109,6 +110,7 @@ HEADERS += \
|
||||
deviceclassesproxy.h \
|
||||
devicediscovery.h \
|
||||
models/packagesfiltermodel.h \
|
||||
models/taglistmodel.h \
|
||||
vendorsproxy.h \
|
||||
pluginsproxy.h \
|
||||
interfacesmodel.h \
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "types/device.h"
|
||||
|
||||
#include "devices.h"
|
||||
#include "devicesproxy.h"
|
||||
|
||||
InterfacesProxy::InterfacesProxy(QObject *parent): QSortFilterProxyModel(parent)
|
||||
{
|
||||
@ -23,6 +24,7 @@ void InterfacesProxy::setShowEvents(bool showEvents)
|
||||
m_showEvents = showEvents;
|
||||
emit showEventsChanged();
|
||||
invalidateFilter();
|
||||
void countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +39,7 @@ void InterfacesProxy::setShowActions(bool showActions)
|
||||
m_showActions = showActions;
|
||||
emit showActionsChanged();
|
||||
invalidateFilter();
|
||||
void countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,6 +54,7 @@ void InterfacesProxy::setShowStates(bool showStates)
|
||||
m_showStates = showStates;
|
||||
emit showStatesChanged();
|
||||
invalidateFilter();
|
||||
void countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +62,6 @@ bool InterfacesProxy::filterAcceptsRow(int source_row, const QModelIndex &source
|
||||
{
|
||||
Q_UNUSED(source_parent)
|
||||
QString interfaceName = m_interfaces->get(source_row)->name();
|
||||
qDebug() << "filterAcceptsRow" << interfaceName << m_shownInterfaces;
|
||||
if (!m_shownInterfaces.isEmpty()) {
|
||||
if (!m_shownInterfaces.contains(interfaceName)) {
|
||||
return false;
|
||||
@ -83,6 +86,24 @@ bool InterfacesProxy::filterAcceptsRow(int source_row, const QModelIndex &source
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (m_devicesProxyFilter != nullptr) {
|
||||
// TODO: This could be improved *a lot* by caching interfaces in the devices model...
|
||||
bool found = false;
|
||||
for (int i = 0; i < m_devicesProxyFilter->rowCount(); i++) {
|
||||
Device *d = m_devicesProxyFilter->get(i);
|
||||
if (!d->deviceClass()) {
|
||||
qWarning() << "Cannot find DeviceClass for device:" << d->id() << d->name();
|
||||
return false;
|
||||
}
|
||||
if (d->deviceClass()->interfaces().contains(interfaceName)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Interface* iface = m_interfaces->get(source_row);
|
||||
if (m_showEvents) {
|
||||
|
||||
@ -4,15 +4,18 @@
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class Devices;
|
||||
class DevicesProxy;
|
||||
class Interface;
|
||||
class Interfaces;
|
||||
|
||||
class InterfacesProxy: public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
|
||||
Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged)
|
||||
Q_PROPERTY(Devices* devicesFilter READ devicesFilter WRITE setDevicesFilter NOTIFY devicesFilterChanged)
|
||||
Q_PROPERTY(DevicesProxy* devicesProxyFilter READ devicesProxyFilter WRITE setDevicesProxyFilter NOTIFY devicesProxyFilterChanged)
|
||||
Q_PROPERTY(bool showEvents READ showEvents WRITE setShowEvents NOTIFY showEventsChanged)
|
||||
Q_PROPERTY(bool showActions READ showActions WRITE setShowActions NOTIFY showActionsChanged)
|
||||
Q_PROPERTY(bool showStates READ showStates WRITE setShowStates NOTIFY showStatesChanged)
|
||||
@ -26,6 +29,9 @@ public:
|
||||
Devices* devicesFilter() const { return m_devicesFilter; }
|
||||
void setDevicesFilter(Devices *devices) { m_devicesFilter = devices; emit devicesFilterChanged(); invalidateFilter(); }
|
||||
|
||||
DevicesProxy* devicesProxyFilter() const { return m_devicesProxyFilter; }
|
||||
void setDevicesProxyFilter(DevicesProxy *devicesProxy) { m_devicesProxyFilter = devicesProxy; emit devicesProxyFilterChanged(); invalidateFilter(); }
|
||||
|
||||
bool showEvents() const;
|
||||
void setShowEvents(bool showEvents);
|
||||
|
||||
@ -42,14 +48,18 @@ public:
|
||||
signals:
|
||||
void shownInterfacesChanged();
|
||||
void devicesFilterChanged();
|
||||
void devicesProxyFilterChanged();
|
||||
void showEventsChanged();
|
||||
void showActionsChanged();
|
||||
void showStatesChanged();
|
||||
|
||||
void countChanged();
|
||||
|
||||
private:
|
||||
Interfaces *m_interfaces = nullptr;
|
||||
QStringList m_shownInterfaces;
|
||||
Devices* m_devicesFilter = nullptr;
|
||||
DevicesProxy* m_devicesProxyFilter = nullptr;
|
||||
bool m_showEvents = false;
|
||||
bool m_showActions = false;
|
||||
bool m_showStates = false;
|
||||
|
||||
100
libnymea-app-core/models/taglistmodel.cpp
Normal file
100
libnymea-app-core/models/taglistmodel.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include "taglistmodel.h"
|
||||
#include "tagsproxymodel.h"
|
||||
#include "types/tag.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
TagListModel::TagListModel(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TagsProxyModel *TagListModel::tagsProxy() const
|
||||
{
|
||||
return m_tagsProxy;
|
||||
}
|
||||
|
||||
void TagListModel::setTagsProxy(TagsProxyModel *tagsProxy)
|
||||
{
|
||||
if (m_tagsProxy != tagsProxy) {
|
||||
m_tagsProxy = tagsProxy;
|
||||
emit tagsProxyChanged();
|
||||
|
||||
connect(tagsProxy, &TagsProxyModel::countChanged, this, &TagListModel::update);
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
int TagListModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_list.count();
|
||||
}
|
||||
|
||||
QVariant TagListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case RoleTagId:
|
||||
return m_list.at(index.row())->tagId();
|
||||
case RoleValue:
|
||||
return m_list.at(index.row())->value();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> TagListModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles.insert(RoleTagId, "tagId");
|
||||
roles.insert(RoleValue, "value");
|
||||
return roles;
|
||||
}
|
||||
|
||||
bool TagListModel::containsId(const QString &tagId)
|
||||
{
|
||||
foreach (Tag* t, m_list) {
|
||||
if (t->tagId() == tagId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TagListModel::containsValue(const QString &tagValue)
|
||||
{
|
||||
foreach (Tag* t, m_list) {
|
||||
if (t->value() == tagValue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TagListModel::update()
|
||||
{
|
||||
beginResetModel();
|
||||
qDeleteAll(m_list);
|
||||
m_list.clear();
|
||||
|
||||
for (int i = 0; i < m_tagsProxy->rowCount(); i++) {
|
||||
Tag *tag = m_tagsProxy->get(i);
|
||||
|
||||
bool found = false;
|
||||
foreach (Tag* existingTag, m_list) {
|
||||
if (tag->tagId() == existingTag->tagId() && tag->value() == existingTag->value()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
Tag *t = new Tag(tag->tagId(), tag->value(), this);
|
||||
m_list.append(t);
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Model populated" << m_list.count() << this;
|
||||
endResetModel();
|
||||
emit countChanged();
|
||||
}
|
||||
44
libnymea-app-core/models/taglistmodel.h
Normal file
44
libnymea-app-core/models/taglistmodel.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef TAGLISTMODEL_H
|
||||
#define TAGLISTMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
class TagsProxyModel;
|
||||
class Tag;
|
||||
|
||||
class TagListModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
Q_PROPERTY(TagsProxyModel* tagsProxy READ tagsProxy WRITE setTagsProxy NOTIFY tagsProxyChanged)
|
||||
public:
|
||||
enum Roles {
|
||||
RoleTagId,
|
||||
RoleValue
|
||||
};
|
||||
explicit TagListModel(QObject *parent = nullptr);
|
||||
|
||||
TagsProxyModel* tagsProxy() const;
|
||||
void setTagsProxy(TagsProxyModel* tagsProxy);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
Q_INVOKABLE bool containsId(const QString &tagId);
|
||||
Q_INVOKABLE bool containsValue(const QString &tagValue);
|
||||
|
||||
signals:
|
||||
void countChanged();
|
||||
void tagsProxyChanged();
|
||||
|
||||
private slots:
|
||||
void update();
|
||||
|
||||
private:
|
||||
TagsProxyModel *m_tagsProxy = nullptr;
|
||||
|
||||
QList<Tag*> m_list;
|
||||
};
|
||||
|
||||
#endif // TAGLISTMODEL_H
|
||||
@ -94,17 +94,18 @@ bool TagsProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_
|
||||
Q_UNUSED(source_parent)
|
||||
Tag *tag = m_tags->get(source_row);
|
||||
if (!m_filterTagId.isEmpty()) {
|
||||
if (tag->tagId() != m_filterTagId) {
|
||||
QRegExp exp(m_filterTagId);
|
||||
if (!exp.exactMatch(tag->tagId())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_filterDeviceId.isEmpty()) {
|
||||
if (tag->deviceId() != m_filterDeviceId) {
|
||||
if (QUuid(tag->deviceId()) != QUuid(m_filterDeviceId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_filterRuleId.isEmpty()) {
|
||||
if (tag->ruleId() != m_filterRuleId) {
|
||||
if (QUuid(tag->ruleId()) != QUuid(m_filterRuleId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -115,7 +116,7 @@ bool TagsProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex
|
||||
{
|
||||
QString leftValue = m_tags->get(source_left.row())->value();
|
||||
QString rightValue = m_tags->get(source_right.row())->value();
|
||||
bool okLeft, okRight;;
|
||||
bool okLeft, okRight;
|
||||
qlonglong leftAsNumber = leftValue.toLongLong(&okLeft);
|
||||
qlonglong rightAsNumber = rightValue.toLongLong(&okRight);
|
||||
if (okLeft && okRight) {
|
||||
|
||||
@ -42,6 +42,7 @@ signals:
|
||||
void filterTagIdChanged();
|
||||
void filterDeviceIdChanged();
|
||||
void filterRuleIdChanged();
|
||||
void groupSameTagsChanged();
|
||||
void countChanged();
|
||||
|
||||
private:
|
||||
|
||||
@ -17,10 +17,22 @@ QString TagsManager::nameSpace() const
|
||||
|
||||
void TagsManager::init()
|
||||
{
|
||||
m_busy = true;
|
||||
emit busyChanged();
|
||||
m_tags->clear();
|
||||
m_jsonClient->sendCommand("Tags.GetTags", this, "getTagsReply");
|
||||
}
|
||||
|
||||
void TagsManager::clear()
|
||||
{
|
||||
m_tags->clear();
|
||||
}
|
||||
|
||||
bool TagsManager::busy() const
|
||||
{
|
||||
return m_busy;
|
||||
}
|
||||
|
||||
Tags *TagsManager::tags() const
|
||||
{
|
||||
return m_tags;
|
||||
@ -83,13 +95,16 @@ void TagsManager::handleTagsNotification(const QVariantMap ¶ms)
|
||||
|
||||
QString notification = params.value("notification").toString();
|
||||
if (notification == "Tags.TagAdded") {
|
||||
addTagInternal(tagMap);
|
||||
Tag *tag = unpackTag(tagMap);
|
||||
if (tag) {
|
||||
m_tags->addTag(tag);
|
||||
}
|
||||
|
||||
} else if (notification == "Tags.TagRemoved") {
|
||||
for (int i = 0; i < m_tags->rowCount(); i++) {
|
||||
Tag* tag = m_tags->get(i);
|
||||
if (tagMap.value("deviceId").toString() == tag->deviceId() &&
|
||||
tagMap.value("ruleId").toString() == tag->ruleId() &&
|
||||
if (tagMap.value("deviceId").toUuid() == tag->deviceId() &&
|
||||
tagMap.value("ruleId").toUuid() == tag->ruleId() &&
|
||||
tagMap.value("tagId").toString() == tag->tagId()) {
|
||||
m_tags->removeTag(tag);
|
||||
return;
|
||||
@ -99,8 +114,8 @@ void TagsManager::handleTagsNotification(const QVariantMap ¶ms)
|
||||
qDebug() << "tag value changed";
|
||||
for (int i = 0; i < m_tags->rowCount(); i++) {
|
||||
Tag* tag = m_tags->get(i);
|
||||
if (tagMap.value("deviceId").toString() == tag->deviceId() &&
|
||||
tagMap.value("ruleId").toString() == tag->ruleId() &&
|
||||
if (tagMap.value("deviceId").toUuid() == tag->deviceId() &&
|
||||
tagMap.value("ruleId").toUuid() == tag->ruleId() &&
|
||||
tagMap.value("tagId").toString() == tag->tagId()) {
|
||||
qDebug() << "Found tag";
|
||||
tag->setValue(tagMap.value("value").toString());
|
||||
@ -111,10 +126,17 @@ void TagsManager::handleTagsNotification(const QVariantMap ¶ms)
|
||||
|
||||
void TagsManager::getTagsReply(const QVariantMap ¶ms)
|
||||
{
|
||||
QList<Tag*> tags;
|
||||
foreach (const QVariant &tagVariant, params.value("params").toMap().value("tags").toList()) {
|
||||
addTagInternal(tagVariant.toMap());
|
||||
Tag *tag = unpackTag(tagVariant.toMap());
|
||||
if (tag) {
|
||||
tags.append(tag);
|
||||
}
|
||||
}
|
||||
emit tagsChanged();
|
||||
m_tags->addTags(tags);
|
||||
|
||||
m_busy = false;
|
||||
emit busyChanged();
|
||||
}
|
||||
|
||||
void TagsManager::addTagReply(const QVariantMap ¶ms)
|
||||
@ -127,7 +149,7 @@ void TagsManager::removeTagReply(const QVariantMap ¶ms)
|
||||
qDebug() << "RemoveTag reply" << params;
|
||||
}
|
||||
|
||||
void TagsManager::addTagInternal(const QVariantMap &tagMap)
|
||||
Tag* TagsManager::unpackTag(const QVariantMap &tagMap)
|
||||
{
|
||||
QString deviceId = tagMap.value("deviceId").toString();
|
||||
QString ruleId = tagMap.value("ruleId").toString();
|
||||
@ -143,8 +165,8 @@ void TagsManager::addTagInternal(const QVariantMap &tagMap)
|
||||
} else {
|
||||
qWarning() << "Invalid tag. Neither deviceId nor ruleId are set. Skipping...";
|
||||
tag->deleteLater();
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
// qDebug() << "adding tag" << tag->tagId() << tag->value();
|
||||
m_tags->addTag(tag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
@ -9,13 +9,16 @@
|
||||
class TagsManager : public JsonHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Tags* tags READ tags NOTIFY tagsChanged)
|
||||
Q_PROPERTY(Tags* tags READ tags CONSTANT)
|
||||
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
|
||||
|
||||
public:
|
||||
explicit TagsManager(JsonRpcClient *jsonClient, QObject *parent = nullptr);
|
||||
QString nameSpace() const override;
|
||||
|
||||
void init();
|
||||
void clear();
|
||||
bool busy() const;
|
||||
|
||||
Tags* tags() const;
|
||||
|
||||
@ -25,7 +28,7 @@ public:
|
||||
Q_INVOKABLE void untagRule(const QString &ruleId, const QString &tagId);
|
||||
|
||||
signals:
|
||||
void tagsChanged();
|
||||
void busyChanged();
|
||||
|
||||
private slots:
|
||||
void handleTagsNotification(const QVariantMap ¶ms);
|
||||
@ -34,11 +37,12 @@ private slots:
|
||||
void removeTagReply(const QVariantMap ¶ms);
|
||||
|
||||
private:
|
||||
void addTagInternal(const QVariantMap &tagMap);
|
||||
Tag *unpackTag(const QVariantMap &tagMap);
|
||||
|
||||
JsonRpcClient *m_jsonClient = nullptr;
|
||||
|
||||
Tags *m_tags = nullptr;
|
||||
bool m_busy = false;
|
||||
};
|
||||
|
||||
#endif // TAGSMANAGER_H
|
||||
|
||||
@ -89,6 +89,11 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
|
||||
tr("Daylight"),
|
||||
tr("Daylight changed"));
|
||||
|
||||
addInterface("lightsensor", tr("Light intensity sensors"));
|
||||
addStateType("lightsensor", "lightIntensity", QVariant::Bool, false,
|
||||
tr("Light intensity"),
|
||||
tr("Light intensity changed"));
|
||||
|
||||
addInterface("evcharger", tr("EV charger"));
|
||||
addStateType("evcharger", "power", QVariant::Bool, true,
|
||||
tr("Charging"),
|
||||
@ -115,6 +120,13 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
|
||||
tr("Playback status"),
|
||||
tr("Playback status changed"),
|
||||
tr("Set playback status"));
|
||||
|
||||
addInterface("mediacontroller", tr("Media controllers"));
|
||||
addActionType("mediacontroller", "play", tr("Start playback"), new ParamTypes());
|
||||
addActionType("mediacontroller", "stop", tr("Stop playback"), new ParamTypes());
|
||||
addActionType("mediacontroller", "pause", tr("Pause playback"), new ParamTypes());
|
||||
addActionType("mediacontroller", "skipBack", tr("Skip back"), new ParamTypes());
|
||||
addActionType("mediacontroller", "skipNext", tr("Skip next"), new ParamTypes());
|
||||
}
|
||||
|
||||
int Interfaces::rowCount(const QModelIndex &parent) const
|
||||
@ -144,6 +156,9 @@ QHash<int, QByteArray> Interfaces::roleNames() const
|
||||
|
||||
Interface *Interfaces::get(int index) const
|
||||
{
|
||||
if (index < 0 || index >= m_list.count()) {
|
||||
return nullptr;
|
||||
}
|
||||
return m_list.at(index);
|
||||
}
|
||||
|
||||
|
||||
@ -10,22 +10,22 @@ Tag::Tag(const QString &tagId, const QString &value, QObject *parent):
|
||||
|
||||
}
|
||||
|
||||
QString Tag::deviceId() const
|
||||
QUuid Tag::deviceId() const
|
||||
{
|
||||
return m_deviceId;
|
||||
}
|
||||
|
||||
void Tag::setDeviceId(const QString &deviceId)
|
||||
void Tag::setDeviceId(const QUuid &deviceId)
|
||||
{
|
||||
m_deviceId = deviceId;
|
||||
}
|
||||
|
||||
QString Tag::ruleId() const
|
||||
QUuid Tag::ruleId() const
|
||||
{
|
||||
return m_ruleId;
|
||||
}
|
||||
|
||||
void Tag::setRuleId(const QString &ruleId)
|
||||
void Tag::setRuleId(const QUuid &ruleId)
|
||||
{
|
||||
m_ruleId = ruleId;
|
||||
}
|
||||
|
||||
@ -2,23 +2,24 @@
|
||||
#define TAG_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUuid>
|
||||
|
||||
class Tag : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString deviceId READ deviceId CONSTANT)
|
||||
Q_PROPERTY(QString ruleId READ ruleId CONSTANT)
|
||||
Q_PROPERTY(QUuid deviceId READ deviceId CONSTANT)
|
||||
Q_PROPERTY(QUuid ruleId READ ruleId CONSTANT)
|
||||
Q_PROPERTY(QString tagId READ tagId CONSTANT)
|
||||
Q_PROPERTY(QString value READ value NOTIFY valueChanged)
|
||||
|
||||
public:
|
||||
explicit Tag(const QString &tagId, const QString &value, QObject *parent = nullptr);
|
||||
|
||||
QString deviceId() const;
|
||||
void setDeviceId(const QString &deviceId);
|
||||
QUuid deviceId() const;
|
||||
void setDeviceId(const QUuid &deviceId);
|
||||
|
||||
QString ruleId() const;
|
||||
void setRuleId(const QString &ruleId);
|
||||
QUuid ruleId() const;
|
||||
void setRuleId(const QUuid &ruleId);
|
||||
|
||||
QString tagId() const;
|
||||
|
||||
@ -29,8 +30,8 @@ signals:
|
||||
void valueChanged();
|
||||
|
||||
private:
|
||||
QString m_deviceId;
|
||||
QString m_ruleId;
|
||||
QUuid m_deviceId;
|
||||
QUuid m_ruleId;
|
||||
QString m_tagId;
|
||||
QString m_value;
|
||||
};
|
||||
|
||||
@ -46,6 +46,19 @@ void Tags::addTag(Tag *tag)
|
||||
beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
|
||||
m_list.append(tag);
|
||||
endInsertRows();
|
||||
qDebug() << "tags count changed";
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
void Tags::addTags(QList<Tag *> tags)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), m_list.count(), m_list.count() + tags.count() - 1);
|
||||
foreach (Tag *tag, tags) {
|
||||
tag->setParent(this);
|
||||
connect(tag, &Tag::valueChanged, this, &Tags::tagValueChanged);
|
||||
}
|
||||
m_list.append(tags);
|
||||
endInsertRows();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
@ -68,7 +81,7 @@ Tag *Tags::get(int index) const
|
||||
return m_list.at(index);
|
||||
}
|
||||
|
||||
Tag *Tags::findDeviceTag(const QString &deviceId, const QString &tagId) const
|
||||
Tag *Tags::findDeviceTag(const QUuid &deviceId, const QString &tagId) const
|
||||
{
|
||||
foreach (Tag *tag, m_list) {
|
||||
if (tag->deviceId() == deviceId && tag->tagId() == tagId) {
|
||||
|
||||
@ -25,11 +25,12 @@ public:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
void addTag(Tag *tag);
|
||||
void addTags(QList<Tag*> tags);
|
||||
void removeTag(Tag *tag);
|
||||
|
||||
Tag* get(int index) const;
|
||||
|
||||
Q_INVOKABLE Tag* findDeviceTag(const QString &deviceId, const QString &tagId) const;
|
||||
Q_INVOKABLE Tag* findDeviceTag(const QUuid &deviceId, const QString &tagId) const;
|
||||
Q_INVOKABLE Tag* findRuleTag(const QString &ruleId, const QString &tagId) const;
|
||||
|
||||
void clear();
|
||||
|
||||
@ -212,5 +212,6 @@
|
||||
<file>ui/images/browser/MediaBrowserIconNapster.svg</file>
|
||||
<file>ui/images/browser/MediaBrowserIconSoundCloud.svg</file>
|
||||
<file>ui/images/browser/MediaBrowserIconDeezer.svg</file>
|
||||
<file>ui/images/view-grid-symbolic.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -197,5 +197,8 @@
|
||||
<file>ui/magic/NewMagicPage.qml</file>
|
||||
<file>ui/components/WebViewWrapper.qml</file>
|
||||
<file>ui/magic/NewScenePage.qml</file>
|
||||
<file>ui/mainviews/GroupsView.qml</file>
|
||||
<file>ui/grouping/GroupPage.qml</file>
|
||||
<file>ui/delegates/ThingTile.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -1339,6 +1339,22 @@
|
||||
<source>Logs</source>
|
||||
<translation>Protokoll</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Grouping</source>
|
||||
<translation>Gruppen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New group</source>
|
||||
<translation>Neue Gruppe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OK</source>
|
||||
<translation>OK</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Groups for %1</source>
|
||||
<translation>Gruppen für %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DeviceRulesPage</name>
|
||||
@ -2204,6 +2220,13 @@ Bitte versuche es erneut.</translation>
|
||||
<translation>Entschuldigung, es gibt noch nicht genügend Daten um hier ein Diagramm anzuzeigen!</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GroupsView</name>
|
||||
<message>
|
||||
<source>Lighting</source>
|
||||
<translation>Beleuchtung</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HeatingDevicePage</name>
|
||||
<message>
|
||||
@ -2515,6 +2538,42 @@ Bitte versuche es erneut.</translation>
|
||||
<source>Set playback status</source>
|
||||
<translation>Setze Wiedergabestatus</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity sensors</source>
|
||||
<translation>Lichtstärkesensoren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity</source>
|
||||
<translation>Lichtstärke</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity changed</source>
|
||||
<translation>Lichtstärke geändert</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Media controllers</source>
|
||||
<translation>Mediensteuerungen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Start playback</source>
|
||||
<translation>Wiedergabe starten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stop playback</source>
|
||||
<translation>Wiedergabe stoppen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pause playback</source>
|
||||
<translation>Wiedergabe pausieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip back</source>
|
||||
<translation>Zurück springen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip next</source>
|
||||
<translation>Weiter springen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>InterfacesModels</name>
|
||||
@ -2893,7 +2952,7 @@ Bitte versuche es erneut.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no scenes set up yet</source>
|
||||
<translation>Es sind noch keine Szenen eingerichet</translation>
|
||||
<translation type="vanished">Es sind noch keine Szenen eingerichet</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>It appears there are no things set up either yet. In order to use scenes you need to add some things first.</source>
|
||||
@ -2950,6 +3009,26 @@ Bitte versuche es erneut.</translation>
|
||||
<source>In order for your %1 system to be useful, go ahead and add some things.</source>
|
||||
<translation>Damit dein %1 System sich nützlich machen kann, füge einige "Things" hinzu.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Groups</source>
|
||||
<translation>Gruppen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no scenes set up yet.</source>
|
||||
<translation>Es sind noch keine Szenen eingerichtet.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>My groups</source>
|
||||
<translation>Meine Gruppen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no groups set up yet.</source>
|
||||
<translation>Es sind noch keine Gruppen eingerichtet.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Grouping things can be useful to control multiple devices at once, for example an entire room. Watch out for the group symbol when interacting with things and use it to add them to groups.</source>
|
||||
<translation>"Things" in Gruppen zu verwalten ist hilfreich um mehrere Geräte gleichzeitig zu schalten, z.B. einen ganzen Raum. Halte Ausschau nach dem Gruppensymbol wenn du mit "Things" interagierst und benutze es um sie Gruppen hinzuzufügen.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManualConnectPage</name>
|
||||
|
||||
@ -903,6 +903,22 @@
|
||||
<source>Logs</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Grouping</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New group</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OK</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Groups for %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DeviceRulesPage</name>
|
||||
@ -1648,6 +1664,13 @@ Please try again.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GroupsView</name>
|
||||
<message>
|
||||
<source>Lighting</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HeatingDevicePage</name>
|
||||
<message>
|
||||
@ -1944,6 +1967,42 @@ Please try again.</source>
|
||||
<source>Set playback status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity sensors</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity changed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Media controllers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Start playback</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stop playback</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pause playback</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip back</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip next</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>LightsDeviceListPage</name>
|
||||
@ -2225,10 +2284,6 @@ Please try again.</source>
|
||||
<source>My scenes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no scenes set up yet</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>It appears there are no things set up either yet. In order to use scenes you need to add some things first.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -2276,6 +2331,26 @@ Please try again.</source>
|
||||
<source>In order for your %1 system to be useful, go ahead and add some things.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Groups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no scenes set up yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>My groups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no groups set up yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Grouping things can be useful to control multiple devices at once, for example an entire room. Watch out for the group symbol when interacting with things and use it to add them to groups.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManualConnectPage</name>
|
||||
|
||||
@ -903,6 +903,22 @@
|
||||
<source>Logs</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Grouping</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New group</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OK</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Groups for %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DeviceRulesPage</name>
|
||||
@ -1648,6 +1664,13 @@ Please try again.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GroupsView</name>
|
||||
<message>
|
||||
<source>Lighting</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HeatingDevicePage</name>
|
||||
<message>
|
||||
@ -1944,6 +1967,42 @@ Please try again.</source>
|
||||
<source>Set playback status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity sensors</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity changed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Media controllers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Start playback</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stop playback</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pause playback</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip back</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip next</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>LightsDeviceListPage</name>
|
||||
@ -2225,10 +2284,6 @@ Please try again.</source>
|
||||
<source>My scenes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no scenes set up yet</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>It appears there are no things set up either yet. In order to use scenes you need to add some things first.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -2276,6 +2331,26 @@ Please try again.</source>
|
||||
<source>In order for your %1 system to be useful, go ahead and add some things.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Groups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no scenes set up yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>My groups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no groups set up yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Grouping things can be useful to control multiple devices at once, for example an entire room. Watch out for the group symbol when interacting with things and use it to add them to groups.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManualConnectPage</name>
|
||||
|
||||
@ -932,6 +932,22 @@
|
||||
<source>Logs</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Grouping</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New group</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OK</source>
|
||||
<translation type="unfinished">확인</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Groups for %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DeviceRulesPage</name>
|
||||
@ -1684,6 +1700,13 @@ Please try again.</source>
|
||||
<translation>죄송하지만, 그래프를 표시할 충분한 데이터가 없습니다!</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GroupsView</name>
|
||||
<message>
|
||||
<source>Lighting</source>
|
||||
<translation type="unfinished">전등(조명)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HeatingDevicePage</name>
|
||||
<message>
|
||||
@ -1984,6 +2007,42 @@ Please try again.</source>
|
||||
<source>Set playback status</source>
|
||||
<translation>재생 상태 설정</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity sensors</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Light intensity changed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Media controllers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Start playback</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stop playback</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pause playback</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip back</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip next</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>LightsDeviceListPage</name>
|
||||
@ -2267,7 +2326,7 @@ Please try again.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no scenes set up yet</source>
|
||||
<translation>아직 설정된 시나리오(연출)가 없습니다</translation>
|
||||
<translation type="vanished">아직 설정된 시나리오(연출)가 없습니다</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>It appears there are no things set up either yet. In order to use scenes you need to add some things first.</source>
|
||||
@ -2315,6 +2374,26 @@ Please try again.</source>
|
||||
<source>In order for your %1 system to be useful, go ahead and add some things.</source>
|
||||
<translation>%1 시스템이 유용하려면 먼저 몇 가지를 추가하십시오.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Groups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no scenes set up yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>My groups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no groups set up yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Grouping things can be useful to control multiple devices at once, for example an entire room. Watch out for the group symbol when interacting with things and use it to add them to groups.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManualConnectPage</name>
|
||||
|
||||
@ -79,6 +79,10 @@ Page {
|
||||
}
|
||||
tabEntryComponent.createObject(tabBar, {text: qsTr("Things"), iconSource: "../images/share.svg", pageIndex: pi++})
|
||||
tabEntryComponent.createObject(tabBar, {text: qsTr("Scenes"), iconSource: "../images/slideshow.svg", pageIndex: pi++})
|
||||
if (engine.jsonRpcClient.ensureServerVersion(1.6)) {
|
||||
tabEntryComponent.createObject(tabBar, {text: qsTr("Groups"), iconSource: "../images/view-grid-symbolic.svg", pageIndex: pi++})
|
||||
}
|
||||
|
||||
root.tabsReady = true
|
||||
}
|
||||
|
||||
@ -265,7 +269,7 @@ Page {
|
||||
anchors { left: parent.left; right: parent.right; margins: app.margins }
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: scenesView.count === 0 && !engine.deviceManager.fetchingData
|
||||
title: qsTr("There are no scenes set up yet")
|
||||
title: qsTr("There are no scenes set up yet.")
|
||||
text: engine.deviceManager.devices.count === 0 ?
|
||||
qsTr("It appears there are no things set up either yet. In order to use scenes you need to add some things first.") :
|
||||
qsTr("Scenes provide a useful way to control your things with just one click.")
|
||||
@ -292,6 +296,25 @@ Page {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GroupsView {
|
||||
id: groupsView
|
||||
property string title: qsTr("My groups");
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
|
||||
EmptyViewPlaceholder {
|
||||
anchors { left: parent.left; right: parent.right; margins: app.margins }
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: groupsView.count == 0
|
||||
title: qsTr("There are no groups set up yet.")
|
||||
text: qsTr("Grouping things can be useful to control multiple devices at once, for example an entire room. Watch out for the group symbol when interacting with things and use it to add them to groups.")
|
||||
imageSource: "images/view-grid-symbolic.svg"
|
||||
buttonVisible: false
|
||||
// buttonText: qsTr("Create a group")
|
||||
// onButtonClicked: pageStack.push(Qt.resolvedUrl("thingconfiguration/NewThingPage.qml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
@ -86,7 +86,7 @@ Item {
|
||||
Component.onCompleted: ready = true
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.margins
|
||||
anchors.margins: parent ? parent.margins : 0
|
||||
source: ready && width > 0 && height > 0 && icon.name ? icon.name : ""
|
||||
sourceSize {
|
||||
width: width
|
||||
@ -100,7 +100,7 @@ Item {
|
||||
id: colorizedImage
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.margins
|
||||
anchors.margins: parent ? parent.margins : 0
|
||||
visible: active && image.status == Image.Ready
|
||||
|
||||
// Whether or not a color has been set.
|
||||
|
||||
376
nymea-app/ui/delegates/ThingTile.qml
Normal file
376
nymea-app/ui/delegates/ThingTile.qml
Normal file
@ -0,0 +1,376 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
MainPageTile {
|
||||
id: root
|
||||
text: device.name.toUpperCase()
|
||||
iconName: app.interfacesToIcon(deviceClass.interfaces)
|
||||
iconColor: app.accentColor
|
||||
batteryCritical: batteryCriticalState && batteryCriticalState.value === true
|
||||
disconnected: connectedState && connectedState.value === false
|
||||
|
||||
backgroundImage: artworkState && artworkState.value.length > 0 ? artworkState.value : ""
|
||||
|
||||
property Device device: null
|
||||
readonly property DeviceClass deviceClass: device ? engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
|
||||
readonly property State connectedState: deviceClass.interfaces.indexOf("connectable") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("connected").id) : null
|
||||
readonly property State batteryCriticalState: deviceClass.interfaces.indexOf("battery") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("batteryCritical").id) : null
|
||||
readonly property State artworkState: deviceClass.interfaces.indexOf("mediametadataprovider") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("artwork").id) : null
|
||||
|
||||
contentItem: Loader {
|
||||
id: loader
|
||||
anchors.fill: parent
|
||||
sourceComponent: {
|
||||
if (root.deviceClass.interfaces.indexOf("closable") >= 0) {
|
||||
return closableComponent;
|
||||
}
|
||||
if (root.deviceClass.interfaces.indexOf("power") >= 0) {
|
||||
return lightsComponent;
|
||||
}
|
||||
if (root.deviceClass.interfaces.indexOf("sensor") >= 0) {
|
||||
return sensorsComponent;
|
||||
}
|
||||
if (root.deviceClass.interfaces.indexOf("weather") >= 0) {
|
||||
return sensorsComponent;
|
||||
}
|
||||
if (root.deviceClass.interfaces.indexOf("smartmeter") >= 0) {
|
||||
return sensorsComponent;
|
||||
}
|
||||
if (root.deviceClass.interfaces.indexOf("mediacontroller") >= 0) {
|
||||
return mediaComponent;
|
||||
}
|
||||
}
|
||||
Binding { target: loader.item ? loader.item : null; property: "deviceClass"; value: root.deviceClass }
|
||||
Binding { target: loader.item ? loader.item : null; property: "device"; value: root.device }
|
||||
}
|
||||
|
||||
Component {
|
||||
id: lightsComponent
|
||||
RowLayout {
|
||||
property var device: null
|
||||
property var deviceClass: null
|
||||
|
||||
readonly property var powerStateType: deviceClass.stateTypes.findByName("power");
|
||||
readonly property var powerState: device.states.getState(powerStateType.id)
|
||||
|
||||
readonly property var brightnessStateType: deviceClass.stateTypes.findByName("brightness");
|
||||
readonly property var brightnessState: brightnessStateType ? device.states.getState(brightnessStateType.id) : null
|
||||
|
||||
ThrottledSlider {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
opacity: deviceClass.interfaces.indexOf("dimmablelight") >= 0 ? 1 : 0
|
||||
enabled: opacity > 0
|
||||
from: 0
|
||||
to: 100
|
||||
value: brightnessState ? brightnessState.value : 0
|
||||
onMoved: {
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("brightness");
|
||||
var params = [];
|
||||
var powerParam = {}
|
||||
powerParam["paramTypeId"] = actionType.paramTypes.get(0).id;
|
||||
powerParam["value"] = value;
|
||||
params.push(powerParam)
|
||||
engine.deviceManager.executeAction(device.id, actionType.id, params);
|
||||
}
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.rightMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
padding: 0; topPadding: 0; bottomPadding: 0
|
||||
|
||||
contentItem: ColorIcon {
|
||||
name: deviceClass.interfaces.indexOf("light") >= 0
|
||||
? (powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg")
|
||||
: app.interfacesToIcon(deviceClass.interfaces)
|
||||
color: powerState.value === true ? app.accentColor : keyColor
|
||||
}
|
||||
onClicked: {
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("power");
|
||||
var params = [];
|
||||
var powerParam = {}
|
||||
powerParam["paramTypeId"] = actionType.paramTypes.get(0).id;
|
||||
powerParam["value"] = !powerState.value;
|
||||
params.push(powerParam)
|
||||
engine.deviceManager.executeAction(device.id, actionType.id, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: sensorsComponent
|
||||
RowLayout {
|
||||
id: sensorsRoot
|
||||
property var device: null
|
||||
property var deviceClass: null
|
||||
spacing: 0
|
||||
|
||||
property var shownInterfaces: []
|
||||
property int currentStateIndex: -1
|
||||
property var currentStateType: deviceClass ? deviceClass.stateTypes.findByName(shownInterfaces[currentStateIndex].state) : null
|
||||
property var currentState: currentStateType ? device.states.getState(currentStateType.id) : null
|
||||
|
||||
onDeviceClassChanged: {
|
||||
if (deviceClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tmp = []
|
||||
if (deviceClass.interfaces.indexOf("temperaturesensor") >= 0) {
|
||||
tmp.push({iface: "temperaturesensor", state: "temperature"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("humiditysensor") >= 0) {
|
||||
tmp.push({iface: "humiditysensor", state: "humidity"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("moisturesensor") >= 0) {
|
||||
tmp.push({iface: "moisturesensor", state: "moisture"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("pressuresensor") >= 0) {
|
||||
tmp.push({iface: "pressuresensor", state: "pressure"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("lightsensor") >= 0) {
|
||||
tmp.push({iface: "lightsensor", state: "lightIntensity"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("conductivitysensor") >= 0) {
|
||||
tmp.push({iface: "conductivitysensor", state: "conductivity"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("noisesensor") >= 0) {
|
||||
tmp.push({iface: "noisesensor", state: "noise"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("co2sensor") >= 0) {
|
||||
tmp.push({iface: "co2sensor", state: "co2"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("smartmeterconsumer") >= 0) {
|
||||
tmp.push({iface: "smartmeterconsumer", state: "totalEnergyConsumed"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("smartmeterproducer") >= 0) {
|
||||
tmp.push({iface: "smartmeterproducer", state: "totalEnergyProduced"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("daylightsensor") >= 0) {
|
||||
tmp.push({iface: "daylightsensor", state: "daylight"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("presencesensor") >= 0) {
|
||||
tmp.push({iface: "presencesensor", state: "isPresent"});
|
||||
}
|
||||
|
||||
if (deviceClass.interfaces.indexOf("weather") >= 0) {
|
||||
tmp.push({iface: "temperaturesensor", state: "temperature"});
|
||||
tmp.push({iface: "humiditysensor", state: "humidity"});
|
||||
tmp.push({iface: "pressuresensor", state: "pressure"});
|
||||
}
|
||||
|
||||
shownInterfaces = tmp
|
||||
currentStateIndex = 0
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.leftMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
padding: 0; topPadding: 0; bottomPadding: 0
|
||||
visible: sensorsRoot.shownInterfaces.length > 1
|
||||
contentItem: ColorIcon {
|
||||
name: "../images/back.svg"
|
||||
}
|
||||
onClicked: {
|
||||
var newIndex = sensorsRoot.currentStateIndex - 1;
|
||||
if (newIndex < 0) newIndex = sensorsRoot.shownInterfaces.length - 1
|
||||
sensorsRoot.currentStateIndex = newIndex;
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
ColorIcon {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: app.interfaceToColor(sensorsRoot.shownInterfaces[sensorsRoot.currentStateIndex].iface)
|
||||
name: app.interfaceToIcon(sensorsRoot.shownInterfaces[sensorsRoot.currentStateIndex].iface)
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
visible: sensorsRoot.currentStateType.type.toLowerCase() !== "bool"
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: sensorsRoot.currentStateType.unitString
|
||||
font.pixelSize: app.smallFont
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: sensorsRoot.currentState.value// + " " + sensorsRoot.currentStateType.unitString
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
Led {
|
||||
state: sensorsRoot.currentState.value === true ? "on" : "off"
|
||||
visible: sensorsRoot.currentStateType.type.toLowerCase() === "bool"
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.rightMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
padding: 0; topPadding: 0; bottomPadding: 0
|
||||
visible: sensorsRoot.shownInterfaces.length > 1
|
||||
contentItem: ColorIcon {
|
||||
name: "../images/next.svg"
|
||||
}
|
||||
onClicked: {
|
||||
var newIndex = sensorsRoot.currentStateIndex + 1;
|
||||
if (newIndex >= sensorsRoot.shownInterfaces.length) newIndex = 0;
|
||||
sensorsRoot.currentStateIndex = newIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: closableComponent
|
||||
RowLayout {
|
||||
property var device: null
|
||||
property var deviceClass: null
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.leftMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
padding: 0; topPadding: 0; bottomPadding: 0
|
||||
contentItem: ColorIcon {
|
||||
name: "../images/up.svg"
|
||||
color: app.accentColor
|
||||
}
|
||||
onClicked: {
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("open");
|
||||
engine.deviceManager.executeAction(device.id, actionType.id);
|
||||
}
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: closableSlider
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: deviceClass.interfaces.indexOf("extendedclosable") >= 0
|
||||
readonly property var percentageStateType: deviceClass.stateTypes.findByName("percentage");
|
||||
readonly property var percentateState: percentageStateType ? device.states.getState(percentageStateType.id) : null
|
||||
from: 0
|
||||
to: 100
|
||||
value: percentateState ? percentateState.value : 0
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: !closableSlider.visible
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.rightMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
padding: 0; topPadding: 0; bottomPadding: 0
|
||||
contentItem: ColorIcon {
|
||||
name: "../images/down.svg"
|
||||
color: app.accentColor
|
||||
}
|
||||
onClicked: {
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("close");
|
||||
engine.deviceManager.executeAction(device.id, actionType.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: mediaComponent
|
||||
RowLayout {
|
||||
id: mediaRoot
|
||||
|
||||
property Device device: null
|
||||
property DeviceClass deviceClass: null
|
||||
|
||||
readonly property State playbackState: device.states.getState(deviceClass.stateTypes.findByName("playbackStatus").id)
|
||||
|
||||
function executeAction(actionName, params) {
|
||||
var actionTypeId = deviceClass.actionTypes.findByName(actionName).id;
|
||||
engine.deviceManager.executeAction(device.id, actionTypeId, params)
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize * .9
|
||||
Layout.preferredWidth: height
|
||||
imageSource: "../images/media-skip-backward.svg"
|
||||
longpressImageSource: "../images/media-seek-backward.svg"
|
||||
repeat: true
|
||||
|
||||
onClicked: {
|
||||
mediaRoot.executeAction("skipBack")
|
||||
}
|
||||
onLongpressed: {
|
||||
mediaRoot.executeAction("fastRewind")
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize * 1.3
|
||||
Layout.preferredWidth: height
|
||||
imageSource: mediaRoot.playbackState.value === "Playing" ? "../images/media-playback-pause.svg" : "../images/media-playback-start.svg"
|
||||
longpressImageSource: "../images/media-playback-stop.svg"
|
||||
longpressEnabled: mediaRoot.playbackState.value !== "Stopped"
|
||||
|
||||
onClicked: {
|
||||
if (mediaRoot.playbackState.value === "Playing") {
|
||||
mediaRoot.executeAction("pause")
|
||||
} else {
|
||||
mediaRoot.executeAction("play")
|
||||
}
|
||||
}
|
||||
|
||||
onLongpressed: {
|
||||
mediaRoot.executeAction("stop")
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize * .9
|
||||
Layout.preferredWidth: height
|
||||
imageSource: "../images/media-skip-forward.svg"
|
||||
longpressImageSource: "../images/media-seek-forward.svg"
|
||||
repeat: true
|
||||
onClicked: {
|
||||
mediaRoot.executeAction("skipNext")
|
||||
}
|
||||
onLongpressed: {
|
||||
mediaRoot.executeAction("fastForward")
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,6 +70,13 @@ Page {
|
||||
iconSource: Qt.binding(function() { return favoritesProxy.count === 0 ? "../images/starred.svg" : "../images/non-starred.svg"}),
|
||||
functionName: "toggleFavorite"
|
||||
}))
|
||||
|
||||
thingMenu.addItem(menuEntryComponent.createObject(thingMenu,
|
||||
{
|
||||
text: qsTr("Grouping"),
|
||||
iconSource: "../images/view-grid-symbolic.svg",
|
||||
functionName: "addToGroup"
|
||||
}))
|
||||
}
|
||||
}
|
||||
function openDeviceMagicPage() {
|
||||
@ -85,6 +92,11 @@ Page {
|
||||
engine.tagsManager.untagDevice(root.device.id, "favorites")
|
||||
}
|
||||
}
|
||||
function addToGroup() {
|
||||
var dialog = addToGroupDialog.createObject(root)
|
||||
dialog.open();
|
||||
}
|
||||
|
||||
function openThingSettingsPage() {
|
||||
pageStack.push(Qt.resolvedUrl("../thingconfiguration/ConfigureThingPage.qml"), {device: root.device})
|
||||
}
|
||||
@ -101,6 +113,70 @@ Page {
|
||||
onTriggered: thingMenu[functionName]()
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: addToGroupDialog
|
||||
MeaDialog {
|
||||
title: qsTr("Groups for %1").arg(root.device.name)
|
||||
headerIcon: "../images/view-grid-symbolic.svg"
|
||||
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
spacing: app.margins
|
||||
TextField {
|
||||
id: newGroupdTextField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("New group")
|
||||
}
|
||||
Button {
|
||||
text: qsTr("OK")
|
||||
enabled: newGroupdTextField.displayText.length > 0 && !groupTags.containsId("group-" + newGroupdTextField.displayText)
|
||||
onClicked: {
|
||||
engine.tagsManager.tagDevice(root.device.id, "group-" + newGroupdTextField.text, 1000)
|
||||
newGroupdTextField.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
height: 200
|
||||
clip: true
|
||||
ScrollIndicator.vertical: ScrollIndicator {}
|
||||
|
||||
model: TagListModel {
|
||||
id: groupTags
|
||||
tagsProxy: TagsProxyModel {
|
||||
tags: engine.tagsManager.tags
|
||||
filterTagId: "group-.*"
|
||||
}
|
||||
}
|
||||
|
||||
delegate: CheckDelegate {
|
||||
width: parent.width
|
||||
text: model.tagId.substring(6)
|
||||
checked: innerProxy.count > 0
|
||||
onClicked: {
|
||||
if (innerProxy.count == 0) {
|
||||
engine.tagsManager.tagDevice(root.device.id, model.tagId, 1000)
|
||||
} else {
|
||||
engine.tagsManager.untagDevice(root.device.id, model.tagId, model.value)
|
||||
}
|
||||
}
|
||||
|
||||
DevicesProxy {
|
||||
id: innerProxy
|
||||
engine: _engine
|
||||
filterTagId: model.tagId
|
||||
filterDeviceId: root.device.id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
||||
56
nymea-app/ui/grouping/GroupPage.qml
Normal file
56
nymea-app/ui/grouping/GroupPage.qml
Normal file
@ -0,0 +1,56 @@
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
import "../delegates"
|
||||
|
||||
Page {
|
||||
id: root
|
||||
header: NymeaHeader {
|
||||
text: root.groupTag.substring(6)
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
|
||||
property string groupTag
|
||||
|
||||
|
||||
DevicesProxy {
|
||||
id: devicesInGroup
|
||||
engine: _engine
|
||||
filterTagId: root.groupTag
|
||||
}
|
||||
|
||||
InterfacesProxy {
|
||||
id: interfacesInGroup
|
||||
devicesProxyFilter: devicesInGroup
|
||||
showStates: true
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: gridView
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
|
||||
model: devicesInGroup
|
||||
|
||||
readonly property int minTileWidth: 180
|
||||
readonly property int minTileHeight: 240
|
||||
readonly property int tilesPerRow: root.width / minTileWidth
|
||||
|
||||
cellWidth: gridView.width / tilesPerRow
|
||||
cellHeight: cellWidth
|
||||
|
||||
delegate: ThingTile {
|
||||
width: gridView.cellWidth
|
||||
height: gridView.cellHeight
|
||||
|
||||
device: devicesInGroup.get(index)
|
||||
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("../devicepages/" + app.interfaceListToDevicePage(deviceClass.interfaces)), {device: device})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
179
nymea-app/ui/images/view-grid-symbolic.svg
Normal file
179
nymea-app/ui/images/view-grid-symbolic.svg
Normal file
@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg4874"
|
||||
version="1.1"
|
||||
inkscape:version="0.91+devel r"
|
||||
viewBox="0 0 96 96.000001"
|
||||
sodipodi:docname="view-grid-symbolic.svg">
|
||||
<defs
|
||||
id="defs4876" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6199994"
|
||||
inkscape:cx="-33.300718"
|
||||
inkscape:cy="62.909239"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g4780"
|
||||
showgrid="true"
|
||||
showborder="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-center="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-global="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5451"
|
||||
empspacing="8" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="8,-8.0000001"
|
||||
id="guide4063" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="4,-8.0000001"
|
||||
id="guide4065" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,88.000001"
|
||||
id="guide4067" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,92.000001"
|
||||
id="guide4069" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="104,4"
|
||||
id="guide4071" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-5,8.0000001"
|
||||
id="guide4073" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="92,-8.0000001"
|
||||
id="guide4075" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="88,-8.0000001"
|
||||
id="guide4077" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,84.000001"
|
||||
id="guide4074" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="12,-8.0000001"
|
||||
id="guide4076" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-5,12"
|
||||
id="guide4078" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="84,-9.0000001"
|
||||
id="guide4080" />
|
||||
<sodipodi:guide
|
||||
position="48,-8.0000001"
|
||||
orientation="1,0"
|
||||
id="guide4170" />
|
||||
<sodipodi:guide
|
||||
position="-8,48"
|
||||
orientation="0,1"
|
||||
id="guide4172" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4879">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(67.857146,-78.50504)">
|
||||
<g
|
||||
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
|
||||
id="g4845"
|
||||
style="display:inline">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="next01.png"
|
||||
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
|
||||
id="g4778"
|
||||
inkscape:label="Layer 1">
|
||||
<g
|
||||
transform="matrix(-1,0,0,1,575.99999,611)"
|
||||
id="g4780"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"
|
||||
id="rect4782"
|
||||
width="96.037987"
|
||||
height="96"
|
||||
x="-438.00244"
|
||||
y="345.36221"
|
||||
transform="scale(-1,1)" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079107;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 429.99805,433.36328 -2,0 -34.01367,0 0,-36.00195 36.01367,0 0,36.00195 z m -4,-4.00195 0,-27.99805 -28.01172,0 0,27.99805 28.01172,0 z"
|
||||
id="rect4201"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 429.99805,389.36133 -2,0 -34.01367,0 0,-36 36.01367,0 0,36 z m -4,-4 0,-27.99805 -28.01172,0 0,27.99805 28.01172,0 z"
|
||||
id="rect4203"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 385.98242,433.36328 -2,0 -34.01562,0 0,-36.00195 36.01562,0 0,36.00195 z m -4.00195,-4.00195 0,-27.99805 -28.01172,0 0,27.99805 28.01172,0 z"
|
||||
id="rect4205"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 385.98242,389.36133 -2,0 -34.01562,0 0,-36 36.01562,0 0,36 z m -4.00195,-4 0,-27.99805 -28.01172,0 0,27.99805 28.01172,0 z"
|
||||
id="rect4207"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
@ -5,11 +5,15 @@ import QtQuick.Layouts 1.2
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
MouseArea {
|
||||
id: root
|
||||
property alias count: interfacesGridView.count
|
||||
property alias model: interfacesGridView.model
|
||||
|
||||
// Prevent scroll events to swipe left/right in case they fall through the grid
|
||||
preventStealing: true
|
||||
onWheel: wheel.accepted = true
|
||||
|
||||
GridView {
|
||||
id: interfacesGridView
|
||||
anchors.fill: parent
|
||||
|
||||
@ -4,13 +4,18 @@ import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
import "../delegates"
|
||||
|
||||
Item {
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
property bool editMode: false
|
||||
readonly property int count: tagsProxy.count
|
||||
|
||||
// Prevent scroll events to swipe left/right in case they fall through the grid
|
||||
preventStealing: true
|
||||
onWheel: wheel.accepted = true
|
||||
|
||||
TagsProxyModel {
|
||||
id: tagsProxy
|
||||
tags: engine.tagsManager.tags
|
||||
@ -29,57 +34,16 @@ Item {
|
||||
cellHeight: cellWidth
|
||||
|
||||
model: tagsProxy
|
||||
delegate: MainPageTile {
|
||||
delegate: ThingTile {
|
||||
id: delegateRoot
|
||||
width: gridView.cellWidth
|
||||
height: gridView.cellHeight
|
||||
text: device.name.toUpperCase()
|
||||
iconName: app.interfacesToIcon(deviceClass.interfaces)
|
||||
iconColor: app.accentColor
|
||||
visible: !fakeDragItem.visible || fakeDragItem.deviceId !== device.id
|
||||
batteryCritical: batteryCriticalState && batteryCriticalState.value === true
|
||||
disconnected: connectedState && connectedState.value === false
|
||||
|
||||
property var modelIndex: index
|
||||
|
||||
property string deviceId: model.deviceId
|
||||
property string ruleId: model.ruleId
|
||||
readonly property Device device: engine.deviceManager.devices.getDevice(deviceId)
|
||||
readonly property DeviceClass deviceClass: device ? engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
|
||||
readonly property State connectedState: deviceClass.interfaces.indexOf("connectable") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("connected").id) : null
|
||||
readonly property State batteryCriticalState: deviceClass.interfaces.indexOf("battery") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("batteryCritical").id) : null
|
||||
device: engine.deviceManager.devices.getDevice(deviceId)
|
||||
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("../devicepages/" + app.interfaceListToDevicePage(deviceClass.interfaces)), {device: device})
|
||||
|
||||
onPressAndHold: root.editMode = true
|
||||
|
||||
contentItem: Loader {
|
||||
id: loader
|
||||
anchors.fill: parent
|
||||
sourceComponent: {
|
||||
if (delegateRoot.deviceClass.interfaces.indexOf("closable") >= 0) {
|
||||
return closableComponent;
|
||||
}
|
||||
if (delegateRoot.deviceClass.interfaces.indexOf("power") >= 0) {
|
||||
return lightsComponent;
|
||||
}
|
||||
if (delegateRoot.deviceClass.interfaces.indexOf("sensor") >= 0) {
|
||||
return sensorsComponent;
|
||||
}
|
||||
if (delegateRoot.deviceClass.interfaces.indexOf("weather") >= 0) {
|
||||
return sensorsComponent;
|
||||
}
|
||||
if (delegateRoot.deviceClass.interfaces.indexOf("smartmeter") >= 0) {
|
||||
return sensorsComponent;
|
||||
}
|
||||
if (delegateRoot.deviceClass.interfaces.indexOf("mediacontroller") >= 0) {
|
||||
return mediaComponent;
|
||||
}
|
||||
}
|
||||
Binding { target: loader.item ? loader.item : null; property: "deviceClass"; value: delegateRoot.deviceClass }
|
||||
Binding { target: loader.item ? loader.item : null; property: "device"; value: delegateRoot.device }
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
loops: Animation.Infinite
|
||||
running: root.editMode
|
||||
@ -174,330 +138,4 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: lightsComponent
|
||||
RowLayout {
|
||||
property var device: null
|
||||
property var deviceClass: null
|
||||
|
||||
readonly property var powerStateType: deviceClass.stateTypes.findByName("power");
|
||||
readonly property var powerState: device.states.getState(powerStateType.id)
|
||||
|
||||
readonly property var brightnessStateType: deviceClass.stateTypes.findByName("brightness");
|
||||
readonly property var brightnessState: brightnessStateType ? device.states.getState(brightnessStateType.id) : null
|
||||
|
||||
ThrottledSlider {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
opacity: deviceClass.interfaces.indexOf("dimmablelight") >= 0 ? 1 : 0
|
||||
enabled: opacity > 0
|
||||
from: 0
|
||||
to: 100
|
||||
value: brightnessState ? brightnessState.value : 0
|
||||
onMoved: {
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("brightness");
|
||||
var params = [];
|
||||
var powerParam = {}
|
||||
powerParam["paramTypeId"] = actionType.paramTypes.get(0).id;
|
||||
powerParam["value"] = value;
|
||||
params.push(powerParam)
|
||||
engine.deviceManager.executeAction(device.id, actionType.id, params);
|
||||
}
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.rightMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
padding: 0; topPadding: 0; bottomPadding: 0
|
||||
|
||||
contentItem: ColorIcon {
|
||||
name: deviceClass.interfaces.indexOf("light") >= 0
|
||||
? (powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg")
|
||||
: app.interfacesToIcon(deviceClass.interfaces)
|
||||
color: powerState.value === true ? app.accentColor : keyColor
|
||||
}
|
||||
onClicked: {
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("power");
|
||||
var params = [];
|
||||
var powerParam = {}
|
||||
powerParam["paramTypeId"] = actionType.paramTypes.get(0).id;
|
||||
powerParam["value"] = !powerState.value;
|
||||
params.push(powerParam)
|
||||
engine.deviceManager.executeAction(device.id, actionType.id, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: sensorsComponent
|
||||
RowLayout {
|
||||
id: sensorsRoot
|
||||
property var device: null
|
||||
property var deviceClass: null
|
||||
spacing: 0
|
||||
|
||||
property var shownInterfaces: []
|
||||
property int currentStateIndex: -1
|
||||
property var currentStateType: deviceClass ? deviceClass.stateTypes.findByName(shownInterfaces[currentStateIndex].state) : null
|
||||
property var currentState: currentStateType ? device.states.getState(currentStateType.id) : null
|
||||
|
||||
onDeviceClassChanged: {
|
||||
if (deviceClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tmp = []
|
||||
if (deviceClass.interfaces.indexOf("temperaturesensor") >= 0) {
|
||||
tmp.push({iface: "temperaturesensor", state: "temperature"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("humiditysensor") >= 0) {
|
||||
tmp.push({iface: "humiditysensor", state: "humidity"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("moisturesensor") >= 0) {
|
||||
tmp.push({iface: "moisturesensor", state: "moisture"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("pressuresensor") >= 0) {
|
||||
tmp.push({iface: "pressuresensor", state: "pressure"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("lightsensor") >= 0) {
|
||||
tmp.push({iface: "lightsensor", state: "lightIntensity"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("conductivitysensor") >= 0) {
|
||||
tmp.push({iface: "conductivitysensor", state: "conductivity"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("noisesensor") >= 0) {
|
||||
tmp.push({iface: "noisesensor", state: "noise"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("co2sensor") >= 0) {
|
||||
tmp.push({iface: "co2sensor", state: "co2"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("smartmeterconsumer") >= 0) {
|
||||
tmp.push({iface: "smartmeterconsumer", state: "totalEnergyConsumed"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("smartmeterproducer") >= 0) {
|
||||
tmp.push({iface: "smartmeterproducer", state: "totalEnergyProduced"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("daylightsensor") >= 0) {
|
||||
tmp.push({iface: "daylightsensor", state: "daylight"});
|
||||
}
|
||||
if (deviceClass.interfaces.indexOf("presencesensor") >= 0) {
|
||||
tmp.push({iface: "presencesensor", state: "isPresent"});
|
||||
}
|
||||
|
||||
if (deviceClass.interfaces.indexOf("weather") >= 0) {
|
||||
tmp.push({iface: "temperaturesensor", state: "temperature"});
|
||||
tmp.push({iface: "humiditysensor", state: "humidity"});
|
||||
tmp.push({iface: "pressuresensor", state: "pressure"});
|
||||
}
|
||||
|
||||
shownInterfaces = tmp
|
||||
currentStateIndex = 0
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.leftMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
padding: 0; topPadding: 0; bottomPadding: 0
|
||||
visible: sensorsRoot.shownInterfaces.length > 1
|
||||
contentItem: ColorIcon {
|
||||
name: "../images/back.svg"
|
||||
}
|
||||
onClicked: {
|
||||
var newIndex = sensorsRoot.currentStateIndex - 1;
|
||||
if (newIndex < 0) newIndex = sensorsRoot.shownInterfaces.length - 1
|
||||
sensorsRoot.currentStateIndex = newIndex;
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
ColorIcon {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: app.interfaceToColor(sensorsRoot.shownInterfaces[sensorsRoot.currentStateIndex].iface)
|
||||
name: app.interfaceToIcon(sensorsRoot.shownInterfaces[sensorsRoot.currentStateIndex].iface)
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
visible: sensorsRoot.currentStateType.type.toLowerCase() !== "bool"
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: sensorsRoot.currentStateType.unitString
|
||||
font.pixelSize: app.smallFont
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: sensorsRoot.currentState.value// + " " + sensorsRoot.currentStateType.unitString
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
Led {
|
||||
state: sensorsRoot.currentState.value === true ? "on" : "off"
|
||||
visible: sensorsRoot.currentStateType.type.toLowerCase() === "bool"
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.rightMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
padding: 0; topPadding: 0; bottomPadding: 0
|
||||
visible: sensorsRoot.shownInterfaces.length > 1
|
||||
contentItem: ColorIcon {
|
||||
name: "../images/next.svg"
|
||||
}
|
||||
onClicked: {
|
||||
var newIndex = sensorsRoot.currentStateIndex + 1;
|
||||
if (newIndex >= sensorsRoot.shownInterfaces.length) newIndex = 0;
|
||||
sensorsRoot.currentStateIndex = newIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: closableComponent
|
||||
RowLayout {
|
||||
property var device: null
|
||||
property var deviceClass: null
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.leftMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
padding: 0; topPadding: 0; bottomPadding: 0
|
||||
contentItem: ColorIcon {
|
||||
name: "../images/up.svg"
|
||||
color: app.accentColor
|
||||
}
|
||||
onClicked: {
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("open");
|
||||
engine.deviceManager.executeAction(device.id, actionType.id);
|
||||
}
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: closableSlider
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: deviceClass.interfaces.indexOf("extendedclosable") >= 0
|
||||
readonly property var percentageStateType: deviceClass.stateTypes.findByName("percentage");
|
||||
readonly property var percentateState: device.states.getState(percentageStateType.id)
|
||||
from: 0
|
||||
to: 100
|
||||
value: percentateState.value
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: !closableSlider.visible
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: width
|
||||
Layout.rightMargin: app.margins / 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
padding: 0; topPadding: 0; bottomPadding: 0
|
||||
contentItem: ColorIcon {
|
||||
name: "../images/down.svg"
|
||||
color: app.accentColor
|
||||
}
|
||||
onClicked: {
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("close");
|
||||
engine.deviceManager.executeAction(device.id, actionType.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: mediaComponent
|
||||
RowLayout {
|
||||
id: mediaRoot
|
||||
|
||||
property Device device: null
|
||||
property DeviceClass deviceClass: null
|
||||
|
||||
readonly property State playbackState: device.states.getState(deviceClass.stateTypes.findByName("playbackStatus").id)
|
||||
|
||||
function executeAction(actionName, params) {
|
||||
var actionTypeId = deviceClass.actionTypes.findByName(actionName).id;
|
||||
engine.deviceManager.executeAction(device.id, actionTypeId, params)
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize * .9
|
||||
Layout.preferredWidth: height
|
||||
imageSource: "../images/media-skip-backward.svg"
|
||||
longpressImageSource: "../images/media-seek-backward.svg"
|
||||
repeat: true
|
||||
|
||||
onClicked: {
|
||||
mediaRoot.executeAction("skipBack")
|
||||
}
|
||||
onLongpressed: {
|
||||
mediaRoot.executeAction("fastRewind")
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize * 1.3
|
||||
Layout.preferredWidth: height
|
||||
imageSource: mediaRoot.playbackState.value === "Playing" ? "../images/media-playback-pause.svg" : "../images/media-playback-start.svg"
|
||||
longpressImageSource: "../images/media-playback-stop.svg"
|
||||
longpressEnabled: mediaRoot.playbackState.value !== "Stopped"
|
||||
|
||||
onClicked: {
|
||||
if (mediaRoot.playbackState.value === "Playing") {
|
||||
mediaRoot.executeAction("pause")
|
||||
} else {
|
||||
mediaRoot.executeAction("play")
|
||||
}
|
||||
}
|
||||
|
||||
onLongpressed: {
|
||||
mediaRoot.executeAction("stop")
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize * .9
|
||||
Layout.preferredWidth: height
|
||||
imageSource: "../images/media-skip-forward.svg"
|
||||
longpressImageSource: "../images/media-seek-forward.svg"
|
||||
repeat: true
|
||||
onClicked: {
|
||||
mediaRoot.executeAction("skipNext")
|
||||
}
|
||||
onLongpressed: {
|
||||
mediaRoot.executeAction("fastForward")
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
393
nymea-app/ui/mainviews/GroupsView.qml
Normal file
393
nymea-app/ui/mainviews/GroupsView.qml
Normal file
@ -0,0 +1,393 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import "../components"
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
preventStealing: true
|
||||
onWheel: wheel.accepted = true
|
||||
|
||||
readonly property int count: groupsGridView.count
|
||||
|
||||
GridView {
|
||||
id: groupsGridView
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
|
||||
|
||||
readonly property int minTileWidth: 180
|
||||
readonly property int minTileHeight: 180
|
||||
readonly property int tilesPerRow: root.width / minTileWidth
|
||||
|
||||
model: TagListModel {
|
||||
tagsProxy: TagsProxyModel {
|
||||
tags: engine.tagsManager.tags
|
||||
filterTagId: "group-.*"
|
||||
}
|
||||
}
|
||||
cellWidth: width / tilesPerRow
|
||||
cellHeight: cellWidth
|
||||
|
||||
delegate: Item {
|
||||
id: groupDelegate
|
||||
width: groupsGridView.cellWidth
|
||||
height: groupsGridView.cellHeight
|
||||
|
||||
Pane {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
Material.elevation: 2
|
||||
padding: 0
|
||||
|
||||
DevicesProxy {
|
||||
id: devicesInGroup
|
||||
engine: _engine
|
||||
filterTagId: model.tagId
|
||||
filterTagValue: model.value
|
||||
}
|
||||
|
||||
InterfacesProxy {
|
||||
id: controlsInGroup
|
||||
shownInterfaces: ["light", "simpleclosable", "mediacontroller"]
|
||||
devicesProxyFilter: devicesInGroup
|
||||
showStates: true
|
||||
showActions: true
|
||||
}
|
||||
InterfacesProxy {
|
||||
id: sensorsInGroup
|
||||
shownInterfaces: ["temperaturesensor", "lightsensor", "presencesensor"]
|
||||
devicesProxyFilter: devicesInGroup
|
||||
showStates: true
|
||||
}
|
||||
|
||||
contentItem: ItemDelegate {
|
||||
leftPadding: 0
|
||||
topPadding: 0
|
||||
rightPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
onClicked: {
|
||||
pageStack.push(Qt.resolvedUrl("../grouping/GroupPage.qml"), {groupTag: model.tagId})
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors { leftMargin: app.margins; rightMargin: app.margins }
|
||||
text: model.tagId.substring(6)
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColorIcon {
|
||||
anchors.centerIn: parent
|
||||
height: app.iconSize * 2
|
||||
width: height
|
||||
visible: controlsInGroup.count == 0
|
||||
color: app.accentColor
|
||||
name: "../images/view-grid-symbolic.svg"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Repeater {
|
||||
model: Math.min(controlsInGroup.count, parent.height / 50)
|
||||
delegate: Loader {
|
||||
id: controlLoader
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins / 2
|
||||
Layout.rightMargin: app.margins / 2
|
||||
property string interfaceName: controlsInGroup.get(index).name
|
||||
sourceComponent: {
|
||||
switch (interfaceName) {
|
||||
case "simpleclosable":
|
||||
return closableDelegate
|
||||
case "light":
|
||||
return lightDelegate
|
||||
case "mediacontroller":
|
||||
return mediaControllerDelegate
|
||||
}
|
||||
}
|
||||
Binding {
|
||||
target: controlLoader.item
|
||||
property: "devices"
|
||||
value: devicesInGroup
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: app.iconSize * 1.2
|
||||
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, 0.05)
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Repeater {
|
||||
model: sensorsInGroup
|
||||
delegate: Row {
|
||||
height: parent.height
|
||||
|
||||
ColorIcon {
|
||||
height: app.iconSize * .8
|
||||
width: height
|
||||
name: app.interfaceToIcon(model.name)
|
||||
color: app.interfaceToColor(model.name)
|
||||
}
|
||||
DevicesProxy {
|
||||
id: innerProxy
|
||||
engine: _engine
|
||||
parentProxy: devicesInGroup
|
||||
shownInterfaces: [model.name]
|
||||
}
|
||||
|
||||
Led {
|
||||
visible: ["presencesensor"].indexOf(model.name) >= 0
|
||||
state: {
|
||||
var stateName = null
|
||||
switch (model.name) {
|
||||
case "presencesensor":
|
||||
stateName = "isPresent"
|
||||
break;
|
||||
}
|
||||
if (!stateName) {
|
||||
return "off";
|
||||
}
|
||||
var ret = false;
|
||||
for (var i = 0; i < innerProxy.count; i++) {
|
||||
ret |= innerProxy.get(i).states.getState(innerProxy.get(i).deviceClass.stateTypes.findByName(stateName).id).value
|
||||
}
|
||||
return ret ? "on" : "off";
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
height: parent.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: {
|
||||
var stateName = null;
|
||||
switch (model.name) {
|
||||
case "temperaturesensor":
|
||||
stateName = "temperature";
|
||||
break;
|
||||
case "lightsensor":
|
||||
stateName = "lightIntensity"
|
||||
break;
|
||||
}
|
||||
if (!stateName) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var ret = 0
|
||||
for (var i = 0; i < innerProxy.count; i++) {
|
||||
ret += innerProxy.get(i).states.getState(innerProxy.get(i).deviceClass.stateTypes.findByName(stateName).id).value
|
||||
}
|
||||
return (ret / innerProxy.count).toFixed(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: lightDelegate
|
||||
RowLayout {
|
||||
property var devices
|
||||
|
||||
DevicesProxy {
|
||||
id: lights
|
||||
engine: _engine
|
||||
parentProxy: devices
|
||||
shownInterfaces: ["light"]
|
||||
}
|
||||
|
||||
DevicesProxy {
|
||||
id: dimmableLights
|
||||
engine: _engine
|
||||
parentProxy: devices
|
||||
shownInterfaces: ["dimmablelight"]
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Lighting")
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: slider.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
visible: dimmableLights.count == 0
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: slider
|
||||
from: 0
|
||||
to: 100
|
||||
visible: dimmableLights.count > 0
|
||||
value: {
|
||||
var median = 0
|
||||
var count = 0;
|
||||
for (var i = 0; i < dimmableLights.count; i++) {
|
||||
var device = dimmableLights.get(i);
|
||||
var brightnessId = device.deviceClass.stateTypes.findByName("brightness").id
|
||||
median += device.states.getState(brightnessId).value
|
||||
count++
|
||||
}
|
||||
return median / count;
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
onPressedChanged: {
|
||||
for (var i = 0; i < dimmableLights.count; i++) {
|
||||
var device = dimmableLights.get(i);
|
||||
var brightnessId = device.deviceClass.actionTypes.findByName("brightness").id
|
||||
engine.deviceManager.executeAction(device.id, brightnessId, [{paramTypeId: brightnessId, value: value}]);
|
||||
}
|
||||
}
|
||||
}
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
name: isOn ? "../images/light-on.svg" : "../images/light-off.svg"
|
||||
color: isOn ? app.accentColor : keyColor
|
||||
|
||||
property bool isOn: {
|
||||
for (var i = 0; i < lights.count; i++) {
|
||||
var device = lights.get(i)
|
||||
var powerId = device.deviceClass.stateTypes.findByName("power").id
|
||||
if (device.states.getState(powerId).value === true) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
for (var i = 0; i < lights.count; i++) {
|
||||
var device = lights.get(i)
|
||||
var powerId = device.deviceClass.stateTypes.findByName("power").id
|
||||
engine.deviceManager.executeAction(device.id, powerId, [{paramTypeId: powerId, value: !parent.isOn}])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: closableDelegate
|
||||
|
||||
RowLayout {
|
||||
|
||||
property var devices: null
|
||||
DevicesProxy {
|
||||
id: simpleClosables
|
||||
engine: _engine
|
||||
parentProxy: devices
|
||||
shownInterfaces: ["simpleclosable"]
|
||||
}
|
||||
DevicesProxy {
|
||||
id: closables
|
||||
engine: _engine
|
||||
parentProxy: devices
|
||||
shownInterfaces: ["closable"]
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: app.iconSize
|
||||
|
||||
ColorIcon {
|
||||
height: parent.height
|
||||
width: height
|
||||
anchors.centerIn: parent
|
||||
name: Qt.resolvedUrl("../images/up.svg")
|
||||
}
|
||||
onClicked: {
|
||||
for (var i = 0; i < simpleClosables.count; i++) {
|
||||
var device = simpleClosables.get(i)
|
||||
var openId = device.deviceClass.actionTypes.findByName("open").id
|
||||
engine.deviceManager.executeAction(device.id, openId)
|
||||
}
|
||||
}
|
||||
}
|
||||
ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: app.iconSize
|
||||
visible: closables.count > 0
|
||||
|
||||
ColorIcon {
|
||||
height: parent.height
|
||||
width: height
|
||||
anchors.centerIn: parent
|
||||
name: Qt.resolvedUrl("../images/media-playback-stop.svg")
|
||||
}
|
||||
onClicked: {
|
||||
for (var i = 0; i < closables.count; i++) {
|
||||
var device = closables.get(i)
|
||||
var openId = device.deviceClass.actionTypes.findByName("stop").id
|
||||
engine.deviceManager.executeAction(device.id, openId)
|
||||
}
|
||||
}
|
||||
}
|
||||
ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: app.iconSize
|
||||
|
||||
ColorIcon {
|
||||
height: parent.height
|
||||
width: height
|
||||
anchors.centerIn: parent
|
||||
name: Qt.resolvedUrl("../images/down.svg")
|
||||
}
|
||||
onClicked: {
|
||||
for (var i = 0; i < simpleClosables.count; i++) {
|
||||
var device = simpleClosables.get(i)
|
||||
var closeId = device.deviceClass.actionTypes.findByName("close").id
|
||||
engine.deviceManager.executeAction(device.id, closeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: mediaControllerDelegate
|
||||
MediaControls {
|
||||
property var devices: null
|
||||
DevicesProxy {
|
||||
id: mediaControllers
|
||||
engine: _engine
|
||||
parentProxy: devices
|
||||
shownInterfaces: ["mediacontroller"]
|
||||
}
|
||||
|
||||
// involve count in the statement to make the binding re-evaluate when the group is changed
|
||||
device: mediaControllers.count > 0 ? mediaControllers.get(0) : null
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,11 +5,15 @@ import Nymea 1.0
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
readonly property int count: interfacesGridView.count
|
||||
|
||||
// Prevent scroll events to swipe left/right in case they fall through the grid
|
||||
preventStealing: true
|
||||
onWheel: wheel.accepted = true
|
||||
|
||||
GridView {
|
||||
id: interfacesGridView
|
||||
anchors.fill: parent
|
||||
|
||||
Reference in New Issue
Block a user