add support for tagging
rework the main page completely by using the new features available for tagging
This commit is contained in:
parent
37c94e083b
commit
113963a77b
@ -22,11 +22,12 @@
|
||||
|
||||
#include "devicesproxy.h"
|
||||
#include "engine.h"
|
||||
#include "tagsmanager.h"
|
||||
|
||||
DevicesProxy::DevicesProxy(QObject *parent) :
|
||||
QSortFilterProxyModel(parent)
|
||||
{
|
||||
|
||||
connect(Engine::instance()->tagsManager()->tags(), &Tags::countChanged, this, &DevicesProxy::invalidateFilter);
|
||||
}
|
||||
|
||||
Devices *DevicesProxy::devices() const
|
||||
@ -47,31 +48,46 @@ void DevicesProxy::setDevices(Devices *devices)
|
||||
}
|
||||
}
|
||||
|
||||
DeviceClass::BasicTag DevicesProxy::filterTag() const
|
||||
QString DevicesProxy::filterTagId() const
|
||||
{
|
||||
return m_filterTag;
|
||||
return m_filterTagId;
|
||||
}
|
||||
|
||||
void DevicesProxy::setFilterTag(DeviceClass::BasicTag filterTag)
|
||||
void DevicesProxy::setFilterTagId(const QString &filterTag)
|
||||
{
|
||||
if (m_filterTag != filterTag) {
|
||||
m_filterTag = filterTag;
|
||||
emit filterTagChanged();
|
||||
if (m_filterTagId != filterTagId()) {
|
||||
m_filterTagId = filterTag;
|
||||
emit filterTagIdChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString DevicesProxy::filterInterface() const
|
||||
QStringList DevicesProxy::shownInterfaces() const
|
||||
{
|
||||
return m_filterInterface;
|
||||
return m_shownInterfaces;
|
||||
}
|
||||
|
||||
void DevicesProxy::setFilterInterface(const QString &filterInterface)
|
||||
void DevicesProxy::setShownInterfaces(const QStringList &shownInterfaces)
|
||||
{
|
||||
if (m_filterInterface != filterInterface) {
|
||||
m_filterInterface = filterInterface;
|
||||
emit filterInterfaceChanged();
|
||||
if (m_shownInterfaces != shownInterfaces) {
|
||||
m_shownInterfaces = shownInterfaces;
|
||||
emit shownInterfacesChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList DevicesProxy::hiddenInterfaces() const
|
||||
{
|
||||
return m_hiddenInterfaces;
|
||||
}
|
||||
|
||||
void DevicesProxy::setHiddenInterfaces(const QStringList &hiddenInterfaces)
|
||||
{
|
||||
if (m_hiddenInterfaces != hiddenInterfaces) {
|
||||
m_hiddenInterfaces = hiddenInterfaces;
|
||||
emit hiddenInterfacesChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
}
|
||||
@ -92,17 +108,33 @@ bool DevicesProxy::lessThan(const QModelIndex &left, const QModelIndex &right) c
|
||||
|
||||
bool DevicesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
if (m_filterTag != DeviceClass::BasicTagNone) {
|
||||
QList<DeviceClass::BasicTag> tags = Engine::instance()->deviceManager()->deviceClasses()->getDeviceClass(m_devices->get(source_row)->deviceClassId())->basicTags();
|
||||
if (!tags.contains(m_filterTag)) {
|
||||
Device *device = m_devices->get(source_row);
|
||||
if (!m_filterTagId.isEmpty()) {
|
||||
if (!Engine::instance()->tagsManager()->tags()->findDeviceTag(device->id().toString(), m_filterTagId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_filterInterface.isEmpty()) {
|
||||
if (!m_shownInterfaces.isEmpty()) {
|
||||
QStringList interfaces = Engine::instance()->deviceManager()->deviceClasses()->getDeviceClass(m_devices->get(source_row)->deviceClassId())->interfaces();
|
||||
if (!interfaces.contains(m_filterInterface)) {
|
||||
bool foundMatch = false;
|
||||
foreach (const QString &filterInterface, m_shownInterfaces) {
|
||||
if (interfaces.contains(filterInterface)) {
|
||||
foundMatch = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!foundMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_hiddenInterfaces.isEmpty()) {
|
||||
QStringList interfaces = Engine::instance()->deviceManager()->deviceClasses()->getDeviceClass(m_devices->get(source_row)->deviceClassId())->interfaces();
|
||||
foreach (const QString &filterInterface, m_hiddenInterfaces) {
|
||||
if (interfaces.contains(filterInterface)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
|
||||
}
|
||||
|
||||
@ -34,8 +34,9 @@ class DevicesProxy : public QSortFilterProxyModel
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
Q_PROPERTY(Devices *devices READ devices WRITE setDevices NOTIFY devicesChanged)
|
||||
Q_PROPERTY(DeviceClass::BasicTag filterTag READ filterTag WRITE setFilterTag NOTIFY filterTagChanged)
|
||||
Q_PROPERTY(QString filterInterface READ filterInterface WRITE setFilterInterface NOTIFY filterInterfaceChanged)
|
||||
Q_PROPERTY(QString filterTagId READ filterTagId WRITE setFilterTagId NOTIFY filterTagIdChanged)
|
||||
Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged)
|
||||
Q_PROPERTY(QStringList hiddenInterfaces READ hiddenInterfaces WRITE setHiddenInterfaces NOTIFY hiddenInterfacesChanged)
|
||||
|
||||
public:
|
||||
explicit DevicesProxy(QObject *parent = 0);
|
||||
@ -43,24 +44,29 @@ public:
|
||||
Devices *devices() const;
|
||||
void setDevices(Devices *devices);
|
||||
|
||||
DeviceClass::BasicTag filterTag() const;
|
||||
void setFilterTag(DeviceClass::BasicTag filterTag);
|
||||
QString filterTagId() const;
|
||||
void setFilterTagId(const QString &filterTag);
|
||||
|
||||
QString filterInterface() const;
|
||||
void setFilterInterface(const QString &filterInterface);
|
||||
QStringList shownInterfaces() const;
|
||||
void setShownInterfaces(const QStringList &shownInterfaces);
|
||||
|
||||
QStringList hiddenInterfaces() const;
|
||||
void setHiddenInterfaces(const QStringList &hiddenInterfaces);
|
||||
|
||||
Q_INVOKABLE Device *get(int index) const;
|
||||
|
||||
signals:
|
||||
void devicesChanged();
|
||||
void filterTagChanged();
|
||||
void filterInterfaceChanged();
|
||||
void filterTagIdChanged();
|
||||
void shownInterfacesChanged();
|
||||
void hiddenInterfacesChanged();
|
||||
void countChanged();
|
||||
|
||||
private:
|
||||
Devices *m_devices = nullptr;
|
||||
DeviceClass::BasicTag m_filterTag = DeviceClass::BasicTagNone;
|
||||
QString m_filterInterface;
|
||||
QString m_filterTagId;
|
||||
QStringList m_shownInterfaces;
|
||||
QStringList m_hiddenInterfaces;
|
||||
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE;
|
||||
|
||||
@ -44,7 +44,7 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
|
||||
if (!entry.name().startsWith("nymea") || entry.ip().isNull()) {
|
||||
return;
|
||||
}
|
||||
qDebug() << "zeroconf service discovered" << entry << entry.txt() << entry.type();
|
||||
// qDebug() << "zeroconf service discovered" << entry << entry.txt() << entry.type();
|
||||
|
||||
QString uuid;
|
||||
bool sslEnabled = false;
|
||||
@ -65,14 +65,14 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
|
||||
version = txtRecord.second;
|
||||
}
|
||||
}
|
||||
qDebug() << "avahi service entry added" << serverName << uuid << sslEnabled;
|
||||
// qDebug() << "avahi service entry added" << serverName << uuid << sslEnabled;
|
||||
|
||||
|
||||
DiscoveryDevice* device = m_discoveryModel->find(uuid);
|
||||
if (!device) {
|
||||
device = new DiscoveryDevice(m_discoveryModel);
|
||||
device->setUuid(uuid);
|
||||
qDebug() << "Adding new host to model";
|
||||
// qDebug() << "Adding new host to model";
|
||||
m_discoveryModel->addDevice(device);
|
||||
}
|
||||
device->setHostAddress(entry.ip());
|
||||
@ -80,7 +80,7 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
|
||||
device->setVersion(version);
|
||||
PortConfig *portConfig = device->portConfigs()->find(entry.port());
|
||||
if (!portConfig) {
|
||||
qDebug() << "Adding new port config";
|
||||
// qDebug() << "Adding new port config";
|
||||
portConfig = new PortConfig(entry.port());
|
||||
device->portConfigs()->insert(portConfig);
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#include "tcpsocketinterface.h"
|
||||
#include "rulemanager.h"
|
||||
#include "logmanager.h"
|
||||
#include "tagsmanager.h"
|
||||
#include "basicconfiguration.h"
|
||||
|
||||
Engine* Engine::s_instance = 0;
|
||||
@ -45,6 +46,11 @@ RuleManager *Engine::ruleManager() const
|
||||
return m_ruleManager;
|
||||
}
|
||||
|
||||
TagsManager *Engine::tagsManager() const
|
||||
{
|
||||
return m_tagsManager;
|
||||
}
|
||||
|
||||
JsonRpcClient *Engine::jsonRpcClient() const
|
||||
{
|
||||
return m_jsonRpcClient;
|
||||
@ -77,11 +83,14 @@ Engine::Engine(QObject *parent) :
|
||||
m_deviceManager(new DeviceManager(m_jsonRpcClient, this)),
|
||||
m_ruleManager(new RuleManager(m_jsonRpcClient, this)),
|
||||
m_logManager(new LogManager(m_jsonRpcClient, this)),
|
||||
m_tagsManager(new TagsManager(m_jsonRpcClient, this)),
|
||||
m_basicConfiguration(new BasicConfiguration(m_jsonRpcClient, this)),
|
||||
m_bluetoothDiscovery(new BluetoothDiscovery(this))
|
||||
{
|
||||
connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, &Engine::onConnectedChanged);
|
||||
connect(m_jsonRpcClient, &JsonRpcClient::authenticationRequiredChanged, this, &Engine::onConnectedChanged);
|
||||
|
||||
connect(m_deviceManager, &DeviceManager::fetchingDataChanged, this, &Engine::onDeviceManagerFetchingChanged);
|
||||
}
|
||||
|
||||
void Engine::onConnectedChanged()
|
||||
@ -98,3 +107,10 @@ void Engine::onConnectedChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::onDeviceManagerFetchingChanged()
|
||||
{
|
||||
if (!m_deviceManager->fetchingData()) {
|
||||
m_tagsManager->init();
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
|
||||
class RuleManager;
|
||||
class LogManager;
|
||||
class TagsManager;
|
||||
class BasicConfiguration;
|
||||
|
||||
class Engine : public QObject
|
||||
@ -38,6 +39,7 @@ class Engine : public QObject
|
||||
Q_PROPERTY(NymeaConnection* connection READ connection CONSTANT)
|
||||
Q_PROPERTY(DeviceManager* deviceManager READ deviceManager CONSTANT)
|
||||
Q_PROPERTY(RuleManager* ruleManager READ ruleManager CONSTANT)
|
||||
Q_PROPERTY(TagsManager* tagsManager READ tagsManager CONSTANT)
|
||||
Q_PROPERTY(JsonRpcClient* jsonRpcClient READ jsonRpcClient CONSTANT)
|
||||
Q_PROPERTY(BasicConfiguration* basicConfiguration READ basicConfiguration CONSTANT)
|
||||
Q_PROPERTY(BluetoothDiscovery* bluetoothDiscovery READ bluetoothDiscovery CONSTANT)
|
||||
@ -51,6 +53,7 @@ public:
|
||||
NymeaConnection *connection() const;
|
||||
DeviceManager *deviceManager() const;
|
||||
RuleManager *ruleManager() const;
|
||||
TagsManager *tagsManager() const;
|
||||
JsonRpcClient *jsonRpcClient() const;
|
||||
LogManager *logManager() const;
|
||||
BasicConfiguration *basicConfiguration() const;
|
||||
@ -65,11 +68,13 @@ private:
|
||||
DeviceManager *m_deviceManager;
|
||||
RuleManager *m_ruleManager;
|
||||
LogManager *m_logManager;
|
||||
TagsManager *m_tagsManager;
|
||||
BasicConfiguration *m_basicConfiguration;
|
||||
BluetoothDiscovery *m_bluetoothDiscovery;
|
||||
|
||||
private slots:
|
||||
void onConnectedChanged();
|
||||
void onDeviceManagerFetchingChanged();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -61,6 +61,20 @@ void InterfacesModel::setShownInterfaces(const QStringList &shownInterfaces)
|
||||
}
|
||||
}
|
||||
|
||||
bool InterfacesModel::showUncategorized() const
|
||||
{
|
||||
return m_showUncategorized;
|
||||
}
|
||||
|
||||
void InterfacesModel::setShowUncategorized(bool showUncategorized)
|
||||
{
|
||||
if (m_showUncategorized != showUncategorized) {
|
||||
m_showUncategorized = showUncategorized;
|
||||
emit showUncategorizedChanged();
|
||||
syncInterfaces();
|
||||
}
|
||||
}
|
||||
|
||||
void InterfacesModel::syncInterfaces()
|
||||
{
|
||||
if (!m_devices) {
|
||||
@ -72,6 +86,7 @@ void InterfacesModel::syncInterfaces()
|
||||
DeviceClass *dc = Engine::instance()->deviceManager()->deviceClasses()->getDeviceClass(m_devices->get(i)->deviceClassId());
|
||||
// qDebug() << "device" <<dc->name() << "has interfaces" << dc->interfaces();
|
||||
|
||||
bool isInShownIfaces = false;
|
||||
foreach (const QString &interface, dc->interfaces()) {
|
||||
if (!m_shownInterfaces.contains(interface)) {
|
||||
continue;
|
||||
@ -80,6 +95,10 @@ void InterfacesModel::syncInterfaces()
|
||||
if (!interfacesInSource.contains(interface)) {
|
||||
interfacesInSource.append(interface);
|
||||
}
|
||||
isInShownIfaces = true;
|
||||
}
|
||||
if (!isInShownIfaces && !interfacesInSource.contains("uncategorized")) {
|
||||
interfacesInSource.append("uncategorized");
|
||||
}
|
||||
}
|
||||
QStringList interfacesToAdd = interfacesInSource;
|
||||
@ -113,3 +132,38 @@ void InterfacesModel::rowsChanged(const QModelIndex &index, int first, int last)
|
||||
|
||||
syncInterfaces();
|
||||
}
|
||||
|
||||
InterfacesSortModel::InterfacesSortModel(QObject *parent):
|
||||
QSortFilterProxyModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
InterfacesModel *InterfacesSortModel::interfacesModel() const
|
||||
{
|
||||
return m_interfacesModel;
|
||||
}
|
||||
|
||||
void InterfacesSortModel::setInterfacesModel(InterfacesModel *interfacesModel)
|
||||
{
|
||||
if (m_interfacesModel != interfacesModel) {
|
||||
m_interfacesModel = interfacesModel;
|
||||
setSourceModel(interfacesModel);
|
||||
setSortRole(Devices::RoleName);
|
||||
sort(0);
|
||||
emit interfacesModelChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool InterfacesSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
QVariant leftName = sourceModel()->data(left, InterfacesModel::RoleName);
|
||||
QVariant rightName = sourceModel()->data(right, InterfacesModel::RoleName);
|
||||
|
||||
if (leftName == "uncategorized") {
|
||||
return false;
|
||||
}
|
||||
if (rightName == "uncategorized") {
|
||||
return true;
|
||||
}
|
||||
return m_interfacesModel->shownInterfaces().indexOf(leftName.toString()) < m_interfacesModel->shownInterfaces().indexOf(rightName.toString());
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ class InterfacesModel : public QAbstractListModel
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
Q_PROPERTY(Devices* devices READ devices WRITE setDevices NOTIFY devicesChanged)
|
||||
Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged)
|
||||
Q_PROPERTY(bool showUncategorized READ showUncategorized WRITE setShowUncategorized NOTIFY showUncategorizedChanged)
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
@ -31,10 +32,14 @@ public:
|
||||
QStringList shownInterfaces() const;
|
||||
void setShownInterfaces(const QStringList &shownInterfaces);
|
||||
|
||||
bool showUncategorized() const;
|
||||
void setShowUncategorized(bool showUncategorized);
|
||||
|
||||
signals:
|
||||
void countChanged();
|
||||
void devicesChanged();
|
||||
void shownInterfacesChanged();
|
||||
void showUncategorizedChanged();
|
||||
|
||||
private slots:
|
||||
void syncInterfaces();
|
||||
@ -45,6 +50,27 @@ private:
|
||||
QStringList m_interfaces;
|
||||
|
||||
QStringList m_shownInterfaces;
|
||||
bool m_showUncategorized = false;
|
||||
};
|
||||
|
||||
class InterfacesSortModel: public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(InterfacesModel* interfacesModel READ interfacesModel WRITE setInterfacesModel NOTIFY interfacesModelChanged)
|
||||
|
||||
public:
|
||||
InterfacesSortModel(QObject *parent = nullptr);
|
||||
|
||||
InterfacesModel* interfacesModel() const;
|
||||
void setInterfacesModel(InterfacesModel* interfacesModel);
|
||||
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE;
|
||||
|
||||
signals:
|
||||
void interfacesModelChanged();
|
||||
|
||||
private:
|
||||
InterfacesModel* m_interfacesModel = nullptr;
|
||||
};
|
||||
|
||||
#endif // INTERFACESMODEL_H
|
||||
|
||||
@ -239,6 +239,7 @@ QVariantMap JsonTypes::packRule(Rule *rule)
|
||||
}
|
||||
ret.insert("name", rule->name());
|
||||
ret.insert("enabled", rule->enabled());
|
||||
ret.insert("executable", rule->executable());
|
||||
|
||||
if (rule->actions()->rowCount() > 0) {
|
||||
ret.insert("actions", packRuleActions(rule->actions()));
|
||||
|
||||
@ -37,6 +37,9 @@
|
||||
#include "models/interfacesproxy.h"
|
||||
#include "basicconfiguration.h"
|
||||
#include "wifisetup/networkmanagercontroler.h"
|
||||
#include "tagsmanager.h"
|
||||
#include "models/tagsproxymodel.h"
|
||||
#include "types/tag.h"
|
||||
|
||||
static QObject* interfacesModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
{
|
||||
@ -87,6 +90,7 @@ void registerQmlTypes() {
|
||||
qmlRegisterUncreatableType<Devices>(uri, 1, 0, "Devices", "Can't create this in QML. Get it from the DeviceManager.");
|
||||
qmlRegisterType<DevicesProxy>(uri, 1, 0, "DevicesProxy");
|
||||
qmlRegisterType<InterfacesModel>(uri, 1, 0, "InterfacesModel");
|
||||
qmlRegisterType<InterfacesSortModel>(uri, 1, 0, "InterfacesSortModel");
|
||||
|
||||
qmlRegisterUncreatableType<DeviceClass>(uri, 1, 0, "DeviceClass", "Can't create this in QML. Get it from the DeviceClasses.");
|
||||
qmlRegisterUncreatableType<DeviceClasses>(uri, 1, 0, "DeviceClasses", "Can't create this in QML. Get it from the DeviceManager.");
|
||||
@ -141,6 +145,11 @@ void registerQmlTypes() {
|
||||
qmlRegisterType<ValueLogsProxyModel>(uri, 1, 0, "ValueLogsProxyModel");
|
||||
qmlRegisterUncreatableType<LogEntry>(uri, 1, 0, "LogEntry", "Get them from LogsModel");
|
||||
|
||||
qmlRegisterUncreatableType<TagsManager>(uri, 1, 0, "TagsManager", "Get it from Engine");
|
||||
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<NetworkManagerControler>(uri, 1, 0, "NetworkManagerControler");
|
||||
qmlRegisterUncreatableType<BluetoothDiscovery>(uri, 1, 0, "BluetoothDiscovery", "Can't create this in QML. Get it from the Engine instance.");
|
||||
qmlRegisterUncreatableType<BluetoothDeviceInfo>(uri, 1, 0, "BluetoothDeviceInfo", "Can't create this in QML. Get it from the DeviceInfos.");
|
||||
|
||||
@ -12,7 +12,7 @@ include(../config.pri)
|
||||
}
|
||||
|
||||
QT -= gui
|
||||
QT += websockets bluetooth
|
||||
QT += network websockets bluetooth
|
||||
|
||||
INCLUDEPATH += $$top_srcdir/libnymea-common $$top_srcdir/QtZeroConf
|
||||
|
||||
@ -57,7 +57,9 @@ SOURCES += \
|
||||
wifisetup/wirelesssetupmanager.cpp \
|
||||
wifisetup/networkmanagercontroler.cpp \
|
||||
models/logsmodelng.cpp \
|
||||
models/interfacesproxy.cpp
|
||||
models/interfacesproxy.cpp \
|
||||
tagsmanager.cpp \
|
||||
models/tagsproxymodel.cpp
|
||||
|
||||
HEADERS += \
|
||||
engine.h \
|
||||
@ -101,7 +103,9 @@ HEADERS += \
|
||||
wifisetup/networkmanagercontroler.h \
|
||||
libnymea-app-core.h \
|
||||
models/logsmodelng.h \
|
||||
models/interfacesproxy.h
|
||||
models/interfacesproxy.h \
|
||||
tagsmanager.h \
|
||||
models/tagsproxymodel.h
|
||||
|
||||
unix {
|
||||
target.path = /usr/lib
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
RulesFilterModel::RulesFilterModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
|
||||
setSortRole(Rules::RoleName);
|
||||
}
|
||||
|
||||
Rules *RulesFilterModel::rules() const
|
||||
@ -27,6 +27,7 @@ void RulesFilterModel::setRules(Rules *rules)
|
||||
emit rulesChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
sort(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +46,21 @@ void RulesFilterModel::setFilterDeviceId(const QString &filterDeviceId)
|
||||
}
|
||||
}
|
||||
|
||||
bool RulesFilterModel::filterExecutable() const
|
||||
{
|
||||
return m_filterExecutable;
|
||||
}
|
||||
|
||||
void RulesFilterModel::setFilterExecutable(bool filterExecutable)
|
||||
{
|
||||
if (m_filterExecutable != filterExecutable) {
|
||||
m_filterExecutable = filterExecutable;
|
||||
emit filterExecutableChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
Rule *RulesFilterModel::get(int index) const
|
||||
{
|
||||
return m_rules->get(mapToSource(this->index(index, 0)).row());
|
||||
@ -53,9 +69,12 @@ Rule *RulesFilterModel::get(int index) const
|
||||
bool RulesFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
Q_UNUSED(source_parent)
|
||||
Rule* rule = m_rules->get(source_row);
|
||||
if (m_filterExecutable && !rule->executable()) {
|
||||
return false;
|
||||
}
|
||||
bool found = true;
|
||||
if (!m_filterDeviceId.isNull()) {
|
||||
Rule* rule = m_rules->get(source_row);
|
||||
found = false;
|
||||
for (int i = 0; i < rule->eventDescriptors()->rowCount(); i++) {
|
||||
EventDescriptor *ed = rule->eventDescriptors()->get(i);
|
||||
|
||||
@ -13,6 +13,7 @@ class RulesFilterModel : public QSortFilterProxyModel
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
Q_PROPERTY(Rules* rules READ rules WRITE setRules NOTIFY rulesChanged)
|
||||
Q_PROPERTY(QString filterDeviceId READ filterDeviceId WRITE setFilterDeviceId NOTIFY filterDeviceIdChanged)
|
||||
Q_PROPERTY(bool filterExecutable READ filterExecutable WRITE setFilterExecutable NOTIFY filterExecutableChanged)
|
||||
|
||||
public:
|
||||
explicit RulesFilterModel(QObject *parent = nullptr);
|
||||
@ -23,11 +24,15 @@ public:
|
||||
QString filterDeviceId() const;
|
||||
void setFilterDeviceId(const QString &filterDeviceId);
|
||||
|
||||
bool filterExecutable() const;
|
||||
void setFilterExecutable(bool filterExecutable);
|
||||
|
||||
Q_INVOKABLE Rule* get(int index) const;
|
||||
|
||||
signals:
|
||||
void rulesChanged();
|
||||
void filterDeviceIdChanged();
|
||||
void filterExecutableChanged();
|
||||
void countChanged();
|
||||
|
||||
protected:
|
||||
@ -36,6 +41,7 @@ protected:
|
||||
private:
|
||||
Rules *m_rules = nullptr;
|
||||
QString m_filterDeviceId;
|
||||
bool m_filterExecutable = false;
|
||||
};
|
||||
|
||||
#endif // RULESFILTERMODEL_H
|
||||
|
||||
111
libnymea-app-core/models/tagsproxymodel.cpp
Normal file
111
libnymea-app-core/models/tagsproxymodel.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
#include "tagsproxymodel.h"
|
||||
#include "engine.h"
|
||||
#include "tagsmanager.h"
|
||||
#include "types/tag.h"
|
||||
|
||||
TagsProxyModel::TagsProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
setSourceModel(Engine::instance()->tagsManager()->tags());
|
||||
connect(Engine::instance()->tagsManager()->tags(), &Tags::countChanged, this, &TagsProxyModel::countChanged, Qt::QueuedConnection);
|
||||
setSortRole(Tags::RoleValue);
|
||||
sort(0);
|
||||
}
|
||||
|
||||
QString TagsProxyModel::filterTagId() const
|
||||
{
|
||||
return m_filterTagId;
|
||||
}
|
||||
|
||||
void TagsProxyModel::setFilterTagId(const QString &filterTagId)
|
||||
{
|
||||
if (m_filterTagId != filterTagId) {
|
||||
m_filterTagId = filterTagId;
|
||||
emit filterTagIdChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString TagsProxyModel::filterDeviceId() const
|
||||
{
|
||||
return m_filterDeviceId;
|
||||
}
|
||||
|
||||
void TagsProxyModel::setFilterDeviceId(const QString &filterDeviceId)
|
||||
{
|
||||
if (m_filterDeviceId != filterDeviceId) {
|
||||
m_filterDeviceId = filterDeviceId;
|
||||
emit filterDeviceIdChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString TagsProxyModel::filterRuleId() const
|
||||
{
|
||||
return m_filterRuleId;
|
||||
}
|
||||
|
||||
void TagsProxyModel::setFilterRuleId(const QString &filterRuleId)
|
||||
{
|
||||
if (m_filterRuleId != filterRuleId) {
|
||||
m_filterRuleId = filterRuleId;
|
||||
emit filterRuleIdChanged();
|
||||
invalidateFilter();
|
||||
emit countChanged();
|
||||
}
|
||||
}
|
||||
|
||||
Tag *TagsProxyModel::get(int index) const
|
||||
{
|
||||
if (index < 0 || index > rowCount()) {
|
||||
return nullptr;
|
||||
}
|
||||
return Engine::instance()->tagsManager()->tags()->get(mapToSource(this->index(index, 0)).row());
|
||||
}
|
||||
|
||||
Tag *TagsProxyModel::findTag(const QString &tagId) const
|
||||
{
|
||||
for (int i = 0; i < rowCount(); i++) {
|
||||
Tag *tag = Engine::instance()->tagsManager()->tags()->get(mapToSource(index(i, 0)).row());
|
||||
if (tag->tagId() == tagId) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TagsProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
Q_UNUSED(source_parent)
|
||||
Tag *tag = Engine::instance()->tagsManager()->tags()->get(source_row);
|
||||
if (!m_filterTagId.isEmpty()) {
|
||||
if (tag->tagId() != m_filterTagId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_filterDeviceId.isEmpty()) {
|
||||
if (tag->deviceId() != m_filterDeviceId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_filterRuleId.isEmpty()) {
|
||||
if (tag->ruleId() != m_filterRuleId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TagsProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||
{
|
||||
QString leftValue = Engine::instance()->tagsManager()->tags()->get(source_left.row())->value();
|
||||
QString rightValue = Engine::instance()->tagsManager()->tags()->get(source_right.row())->value();
|
||||
bool okLeft, okRight;;
|
||||
qlonglong leftAsNumber = leftValue.toLongLong(&okLeft);
|
||||
qlonglong rightAsNumber = rightValue.toLongLong(&okRight);
|
||||
if (okLeft && okRight) {
|
||||
return leftAsNumber < rightAsNumber;
|
||||
}
|
||||
return leftValue < rightValue;
|
||||
}
|
||||
47
libnymea-app-core/models/tagsproxymodel.h
Normal file
47
libnymea-app-core/models/tagsproxymodel.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef TAGSPROXYMODEL_H
|
||||
#define TAGSPROXYMODEL_H
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class Tag;
|
||||
|
||||
class TagsProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
Q_PROPERTY(QString filterTagId READ filterTagId WRITE setFilterTagId NOTIFY filterTagIdChanged)
|
||||
Q_PROPERTY(QString filterDeviceId READ filterDeviceId WRITE setFilterDeviceId NOTIFY filterDeviceIdChanged)
|
||||
Q_PROPERTY(QString filterRuleId READ filterRuleId WRITE setFilterRuleId NOTIFY filterRuleIdChanged)
|
||||
|
||||
public:
|
||||
explicit TagsProxyModel(QObject *parent = nullptr);
|
||||
|
||||
QString filterTagId() const;
|
||||
void setFilterTagId(const QString &filterTagId);
|
||||
|
||||
QString filterDeviceId() const;
|
||||
void setFilterDeviceId(const QString &filterDeviceId);
|
||||
|
||||
QString filterRuleId() const;
|
||||
void setFilterRuleId(const QString &filterRuleId);
|
||||
|
||||
Q_INVOKABLE Tag* get(int index) const;
|
||||
Q_INVOKABLE Tag* findTag(const QString &tagId) const;
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
|
||||
|
||||
signals:
|
||||
void filterTagIdChanged();
|
||||
void filterDeviceIdChanged();
|
||||
void filterRuleIdChanged();
|
||||
void countChanged();
|
||||
|
||||
private:
|
||||
QString m_filterTagId;
|
||||
QString m_filterDeviceId;
|
||||
QString m_filterRuleId;
|
||||
};
|
||||
|
||||
#endif // TAGSPROXYMODEL_H
|
||||
@ -4,6 +4,7 @@
|
||||
#include <QDebug>
|
||||
#include <QSslKey>
|
||||
#include <QSettings>
|
||||
#include <QMetaEnum>
|
||||
|
||||
#include "nymeainterface.h"
|
||||
#include "tcpsocketinterface.h"
|
||||
@ -133,8 +134,8 @@ void NymeaConnection::onSslErrors(const QList<QSslError> &errors)
|
||||
|
||||
void NymeaConnection::onError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
qWarning() << "Socket error" << error;
|
||||
emit connectionError();
|
||||
QMetaEnum errorEnum = QMetaEnum::fromType<QAbstractSocket::SocketError>();
|
||||
emit connectionError(errorEnum.valueToKey(error));
|
||||
}
|
||||
|
||||
void NymeaConnection::onConnected()
|
||||
|
||||
@ -34,7 +34,7 @@ public:
|
||||
signals:
|
||||
void verifyConnectionCertificate(const QString &url, const QStringList &issuerInfo, const QByteArray &fingerprint);
|
||||
void connectedChanged(bool connected);
|
||||
void connectionError();
|
||||
void connectionError(const QString &error);
|
||||
void dataAvailable(const QByteArray &data);
|
||||
|
||||
private slots:
|
||||
|
||||
@ -80,9 +80,16 @@ void RuleManager::editRule(Rule *rule)
|
||||
|
||||
}
|
||||
|
||||
void RuleManager::executeActions(const QString &ruleId)
|
||||
{
|
||||
QVariantMap params;
|
||||
params.insert("ruleId", ruleId);
|
||||
m_jsonClient->sendCommand("Rules.ExecuteActions", params, this, "onExecuteRuleActionsReply");
|
||||
}
|
||||
|
||||
void RuleManager::handleRulesNotification(const QVariantMap ¶ms)
|
||||
{
|
||||
// qDebug() << "rules notification received" << params;
|
||||
qDebug() << "rules notification received" << params;
|
||||
if (params.value("notification").toString() == "Rules.RuleAdded") {
|
||||
QVariantMap ruleMap = params.value("params").toMap().value("rule").toMap();
|
||||
m_rules->insert(parseRule(ruleMap));
|
||||
@ -124,11 +131,13 @@ void RuleManager::getRulesReply(const QVariantMap ¶ms)
|
||||
QString name = ruleDescriptionVariant.toMap().value("name").toString();
|
||||
bool enabled = ruleDescriptionVariant.toMap().value("enabled").toBool();
|
||||
bool active = ruleDescriptionVariant.toMap().value("active").toBool();
|
||||
bool executable = ruleDescriptionVariant.toMap().value("executable").toBool();
|
||||
|
||||
Rule *rule = new Rule(ruleId, m_rules);
|
||||
rule->setName(name);
|
||||
rule->setEnabled(enabled);
|
||||
rule->setActive(active);
|
||||
rule->setExecutable(executable);
|
||||
m_rules->insert(rule);
|
||||
|
||||
QVariantMap requestParams;
|
||||
@ -156,7 +165,7 @@ void RuleManager::getRuleDetailsReply(const QVariantMap ¶ms)
|
||||
void RuleManager::onAddRuleReply(const QVariantMap ¶ms)
|
||||
{
|
||||
qDebug() << "Add rule reply" << params;
|
||||
emit addRuleReply(params.value("params").toMap().value("ruleError").toString());
|
||||
emit addRuleReply(params.value("params").toMap().value("ruleError").toString(), params.value("params").toMap().value("ruleId").toString());
|
||||
}
|
||||
|
||||
void RuleManager::removeRuleReply(const QVariantMap ¶ms)
|
||||
@ -171,16 +180,23 @@ void RuleManager::onEditRuleReply(const QVariantMap ¶ms)
|
||||
emit editRuleReply(params.value("params").toMap().value("ruleError").toString());
|
||||
}
|
||||
|
||||
void RuleManager::onExecuteRuleActionsReply(const QVariantMap ¶ms)
|
||||
{
|
||||
qDebug() << "Execute rule actions reply:" << params;
|
||||
}
|
||||
|
||||
Rule *RuleManager::parseRule(const QVariantMap &ruleMap)
|
||||
{
|
||||
QUuid ruleId = ruleMap.value("id").toUuid();
|
||||
QString name = ruleMap.value("name").toString();
|
||||
bool enabled = ruleMap.value("enabled").toBool();
|
||||
bool active = ruleMap.value("active").toBool();
|
||||
bool executable = ruleMap.value("executable").toBool();
|
||||
Rule* rule = new Rule(ruleId);
|
||||
rule->setName(name);
|
||||
rule->setEnabled(enabled);
|
||||
rule->setActive(active);
|
||||
rule->setExecutable(executable);
|
||||
parseEventDescriptors(ruleMap.value("eventDescriptors").toList(), rule);
|
||||
parseRuleActions(ruleMap.value("actions").toList(), rule);
|
||||
parseRuleExitActions(ruleMap.value("exitActions").toList(), rule);
|
||||
|
||||
@ -31,6 +31,7 @@ public:
|
||||
Q_INVOKABLE void addRule(Rule *rule);
|
||||
Q_INVOKABLE void removeRule(const QUuid &ruleId);
|
||||
Q_INVOKABLE void editRule(Rule *rule);
|
||||
Q_INVOKABLE void executeActions(const QString &ruleId);
|
||||
|
||||
private slots:
|
||||
void handleRulesNotification(const QVariantMap ¶ms);
|
||||
@ -39,6 +40,7 @@ private slots:
|
||||
void onAddRuleReply(const QVariantMap ¶ms);
|
||||
void removeRuleReply(const QVariantMap ¶ms);
|
||||
void onEditRuleReply(const QVariantMap ¶ms);
|
||||
void onExecuteRuleActionsReply(const QVariantMap ¶ms);
|
||||
|
||||
private:
|
||||
Rule *parseRule(const QVariantMap &ruleMap);
|
||||
@ -50,7 +52,7 @@ private:
|
||||
void parseTimeDescriptor(const QVariantMap &timeDescriptor, Rule *rule);
|
||||
|
||||
signals:
|
||||
void addRuleReply(const QString &ruleError);
|
||||
void addRuleReply(const QString &ruleError, const QString &ruleId);
|
||||
void editRuleReply(const QString &ruleError);
|
||||
|
||||
private:
|
||||
|
||||
151
libnymea-app-core/tagsmanager.cpp
Normal file
151
libnymea-app-core/tagsmanager.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
#include "tagsmanager.h"
|
||||
#include "types/tag.h"
|
||||
#include "engine.h"
|
||||
|
||||
TagsManager::TagsManager(JsonRpcClient *jsonClient, QObject *parent):
|
||||
JsonHandler(parent),
|
||||
m_jsonClient(jsonClient),
|
||||
m_tags(new Tags(this))
|
||||
{
|
||||
jsonClient->registerNotificationHandler(this, "handleTagsNotification");
|
||||
}
|
||||
|
||||
QString TagsManager::nameSpace() const
|
||||
{
|
||||
return "Tags";
|
||||
}
|
||||
|
||||
void TagsManager::init()
|
||||
{
|
||||
m_tags->clear();
|
||||
m_jsonClient->sendCommand("Tags.GetTags", this, "getTagsReply");
|
||||
}
|
||||
|
||||
Tags *TagsManager::tags() const
|
||||
{
|
||||
return m_tags;
|
||||
}
|
||||
|
||||
void TagsManager::tagDevice(const QString &deviceId, const QString &tagId, const QString &value)
|
||||
{
|
||||
QVariantMap params;
|
||||
QVariantMap tag;
|
||||
tag.insert("deviceId", deviceId);
|
||||
tag.insert("appId", "nymea:app");
|
||||
tag.insert("tagId", tagId);
|
||||
tag.insert("value", value);
|
||||
params.insert("tag", tag);
|
||||
m_jsonClient->sendCommand("Tags.AddTag", params, this, "addTagReply");
|
||||
}
|
||||
|
||||
void TagsManager::untagDevice(const QString &deviceId, const QString &tagId)
|
||||
{
|
||||
QVariantMap params;
|
||||
QVariantMap tag;
|
||||
tag.insert("deviceId", deviceId);
|
||||
tag.insert("appId", "nymea:app");
|
||||
tag.insert("tagId", tagId);
|
||||
params.insert("tag", tag);
|
||||
m_jsonClient->sendCommand("Tags.RemoveTag", params, this, "removeTagReply");
|
||||
}
|
||||
|
||||
void TagsManager::tagRule(const QString &ruleId, const QString &tagId, const QString &value)
|
||||
{
|
||||
QVariantMap params;
|
||||
QVariantMap tag;
|
||||
tag.insert("ruleId", ruleId);
|
||||
tag.insert("appId", "nymea:app");
|
||||
tag.insert("tagId", tagId);
|
||||
tag.insert("value", value);
|
||||
params.insert("tag", tag);
|
||||
m_jsonClient->sendCommand("Tags.AddTag", params, this, "addTagReply");
|
||||
}
|
||||
|
||||
void TagsManager::untagRule(const QString &ruleId, const QString &tagId)
|
||||
{
|
||||
QVariantMap params;
|
||||
QVariantMap tag;
|
||||
tag.insert("ruleId", ruleId);
|
||||
tag.insert("appId", "nymea:app");
|
||||
tag.insert("tagId", tagId);
|
||||
params.insert("tag", tag);
|
||||
m_jsonClient->sendCommand("Tags.RemoveTag", params, this, "removeTagReply");
|
||||
}
|
||||
|
||||
void TagsManager::handleTagsNotification(const QVariantMap ¶ms)
|
||||
{
|
||||
qDebug() << "Have tags notification" << params;
|
||||
|
||||
QVariantMap tagMap = params.value("params").toMap().value("tag").toMap();
|
||||
if (tagMap.value("appId").toString() != "nymea:app") {
|
||||
return; // not for us
|
||||
}
|
||||
|
||||
QString notification = params.value("notification").toString();
|
||||
if (notification == "Tags.TagAdded") {
|
||||
addTagInternal(tagMap);
|
||||
|
||||
} 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() &&
|
||||
tagMap.value("tagId").toString() == tag->tagId()) {
|
||||
m_tags->removeTag(tag);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (notification == "Tags.TagValueChanged") {
|
||||
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() &&
|
||||
tagMap.value("tagId").toString() == tag->tagId()) {
|
||||
qDebug() << "Found tag";
|
||||
tag->setValue(tagMap.value("value").toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TagsManager::getTagsReply(const QVariantMap ¶ms)
|
||||
{
|
||||
qDebug() << "Have tags" << params;
|
||||
foreach (const QVariant &tagVariant, params.value("params").toMap().value("tags").toList()) {
|
||||
addTagInternal(tagVariant.toMap());
|
||||
}
|
||||
emit tagsChanged();
|
||||
}
|
||||
|
||||
void TagsManager::addTagReply(const QVariantMap ¶ms)
|
||||
{
|
||||
qDebug() << "AddTag reply" << params;
|
||||
}
|
||||
|
||||
void TagsManager::removeTagReply(const QVariantMap ¶ms)
|
||||
{
|
||||
qDebug() << "RemoveTag reply" << params;
|
||||
}
|
||||
|
||||
void TagsManager::addTagInternal(const QVariantMap &tagMap)
|
||||
{
|
||||
QString deviceId = tagMap.value("deviceId").toString();
|
||||
QString ruleId = tagMap.value("ruleId").toString();
|
||||
QString tagId = tagMap.value("tagId").toString();
|
||||
QString value = tagMap.value("value").toString();
|
||||
Tag *tag = nullptr;
|
||||
if (!deviceId.isEmpty()) {
|
||||
tag = new Tag(tagId, value);
|
||||
tag->setDeviceId(deviceId);
|
||||
} else if (!ruleId.isEmpty()) {
|
||||
tag = new Tag(tagId, value);
|
||||
tag->setRuleId(ruleId);
|
||||
} else {
|
||||
qWarning() << "Invalid tag. Neither deviceId nor ruleId are set. Skipping...";
|
||||
tag->deleteLater();
|
||||
return;
|
||||
}
|
||||
qDebug() << "adding tag" << tag->tagId() << tag->value();
|
||||
m_tags->addTag(tag);
|
||||
}
|
||||
44
libnymea-app-core/tagsmanager.h
Normal file
44
libnymea-app-core/tagsmanager.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef TAGSMANAGER_H
|
||||
#define TAGSMANAGER_H
|
||||
|
||||
#include "jsonrpc/jsonhandler.h"
|
||||
#include "jsonrpc/jsonrpcclient.h"
|
||||
|
||||
#include "types/tags.h"
|
||||
|
||||
class TagsManager : public JsonHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Tags* tags READ tags NOTIFY tagsChanged)
|
||||
|
||||
public:
|
||||
explicit TagsManager(JsonRpcClient *jsonClient, QObject *parent = nullptr);
|
||||
QString nameSpace() const override;
|
||||
|
||||
void init();
|
||||
|
||||
Tags* tags() const;
|
||||
|
||||
Q_INVOKABLE void tagDevice(const QString &deviceId, const QString &tagId, const QString &value);
|
||||
Q_INVOKABLE void untagDevice(const QString &deviceId, const QString &tagId);
|
||||
Q_INVOKABLE void tagRule(const QString &ruleId, const QString &tagId, const QString &value);
|
||||
Q_INVOKABLE void untagRule(const QString &ruleId, const QString &tagId);
|
||||
|
||||
signals:
|
||||
void tagsChanged();
|
||||
|
||||
private slots:
|
||||
void handleTagsNotification(const QVariantMap ¶ms);
|
||||
void getTagsReply(const QVariantMap ¶ms);
|
||||
void addTagReply(const QVariantMap ¶ms);
|
||||
void removeTagReply(const QVariantMap ¶ms);
|
||||
|
||||
private:
|
||||
void addTagInternal(const QVariantMap &tagMap);
|
||||
|
||||
JsonRpcClient *m_jsonClient = nullptr;
|
||||
|
||||
Tags *m_tags = nullptr;
|
||||
};
|
||||
|
||||
#endif // TAGSMANAGER_H
|
||||
@ -52,7 +52,9 @@ HEADERS += \
|
||||
types/calendaritem.h \
|
||||
types/timeeventitems.h \
|
||||
types/calendaritems.h \
|
||||
types/repeatingoption.h
|
||||
types/repeatingoption.h \
|
||||
types/tag.h \
|
||||
types/tags.h
|
||||
|
||||
SOURCES += \
|
||||
types/vendor.cpp \
|
||||
@ -95,7 +97,9 @@ SOURCES += \
|
||||
types/calendaritem.cpp \
|
||||
types/timeeventitems.cpp \
|
||||
types/calendaritems.cpp \
|
||||
types/repeatingoption.cpp
|
||||
types/repeatingoption.cpp \
|
||||
types/tag.cpp \
|
||||
types/tags.cpp
|
||||
|
||||
# install header file with relative subdirectory
|
||||
for(header, HEADERS) {
|
||||
|
||||
@ -24,7 +24,7 @@ Rule::Rule(const QUuid &id, QObject *parent) :
|
||||
m_exitActions(new RuleActions(this)),
|
||||
m_timeDescriptor(new TimeDescriptor(this))
|
||||
{
|
||||
qDebug() << "### Creating rule" << this;
|
||||
// qDebug() << "### Creating rule" << this;
|
||||
}
|
||||
|
||||
Rule::~Rule()
|
||||
@ -76,6 +76,19 @@ void Rule::setActive(bool active)
|
||||
}
|
||||
}
|
||||
|
||||
bool Rule::executable() const
|
||||
{
|
||||
return m_executable;
|
||||
}
|
||||
|
||||
void Rule::setExecutable(bool executable)
|
||||
{
|
||||
if (m_executable != executable) {
|
||||
m_executable = executable;
|
||||
emit executableChanged();
|
||||
}
|
||||
}
|
||||
|
||||
EventDescriptors *Rule::eventDescriptors() const
|
||||
{
|
||||
return m_eventDescriptors;
|
||||
@ -123,6 +136,7 @@ Rule *Rule::clone() const
|
||||
Rule *ret = new Rule(this->id());
|
||||
ret->setName(this->name());
|
||||
ret->setEnabled(this->enabled());
|
||||
ret->setExecutable(this->executable());
|
||||
for (int i = 0; i < this->eventDescriptors()->rowCount(); i++) {
|
||||
ret->eventDescriptors()->addEventDescriptor(this->eventDescriptors()->get(i)->clone());
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ class Rule : public QObject
|
||||
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
|
||||
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
|
||||
Q_PROPERTY(bool active READ active NOTIFY activeChanged)
|
||||
Q_PROPERTY(bool executable READ executable WRITE setExecutable NOTIFY executableChanged)
|
||||
Q_PROPERTY(EventDescriptors* eventDescriptors READ eventDescriptors CONSTANT)
|
||||
Q_PROPERTY(StateEvaluator* stateEvaluator READ stateEvaluator WRITE setStateEvaluator NOTIFY stateEvaluatorChanged)
|
||||
Q_PROPERTY(RuleActions* actions READ actions CONSTANT)
|
||||
@ -36,6 +37,9 @@ public:
|
||||
bool active() const;
|
||||
void setActive(bool active);
|
||||
|
||||
bool executable() const;
|
||||
void setExecutable(bool executable);
|
||||
|
||||
EventDescriptors* eventDescriptors() const;
|
||||
StateEvaluator *stateEvaluator() const;
|
||||
RuleActions* actions() const;
|
||||
@ -52,6 +56,7 @@ signals:
|
||||
void nameChanged();
|
||||
void enabledChanged();
|
||||
void activeChanged();
|
||||
void executableChanged();
|
||||
void stateEvaluatorChanged();
|
||||
|
||||
private:
|
||||
@ -59,6 +64,7 @@ private:
|
||||
QString m_name;
|
||||
bool m_enabled = true;
|
||||
bool m_active = false;
|
||||
bool m_executable = false;
|
||||
EventDescriptors *m_eventDescriptors = nullptr;
|
||||
StateEvaluator *m_stateEvaluator = nullptr;
|
||||
RuleActions *m_actions = nullptr;
|
||||
|
||||
@ -34,6 +34,8 @@ QVariant Rules::data(const QModelIndex &index, int role) const
|
||||
return m_list.at(index.row())->enabled();
|
||||
case RoleActive:
|
||||
return m_list.at(index.row())->active();
|
||||
case RoleExecutable:
|
||||
return m_list.at(index.row())->executable();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
@ -45,6 +47,7 @@ QHash<int, QByteArray> Rules::roleNames() const
|
||||
roles.insert(RoleId, "id");
|
||||
roles.insert(RoleEnabled, "enabled");
|
||||
roles.insert(RoleActive, "active");
|
||||
roles.insert(RoleExecutable, "executable");
|
||||
return roles;
|
||||
}
|
||||
|
||||
@ -56,6 +59,7 @@ void Rules::insert(Rule *rule)
|
||||
connect(rule, &Rule::enabledChanged, this, &Rules::ruleChanged);
|
||||
connect(rule, &Rule::activeChanged, this, &Rules::ruleChanged);
|
||||
connect(rule, &Rule::nameChanged, this, &Rules::ruleChanged);
|
||||
connect(rule, &Rule::executableChanged, this, &Rules::ruleChanged);
|
||||
endInsertRows();
|
||||
emit countChanged();
|
||||
}
|
||||
@ -103,5 +107,5 @@ void Rules::ruleChanged()
|
||||
return;
|
||||
}
|
||||
QModelIndex modelIndex = index(idx);
|
||||
emit dataChanged(modelIndex, modelIndex, {RoleActive, RoleEnabled, RoleName});
|
||||
emit dataChanged(modelIndex, modelIndex, {RoleActive, RoleEnabled, RoleName, RoleExecutable});
|
||||
}
|
||||
|
||||
@ -14,7 +14,8 @@ public:
|
||||
RoleName,
|
||||
RoleId,
|
||||
RoleEnabled,
|
||||
RoleActive
|
||||
RoleActive,
|
||||
RoleExecutable
|
||||
};
|
||||
explicit Rules(QObject *parent = nullptr);
|
||||
|
||||
|
||||
50
libnymea-common/types/tag.cpp
Normal file
50
libnymea-common/types/tag.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "tag.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
Tag::Tag(const QString &tagId, const QString &value, QObject *parent):
|
||||
QObject(parent),
|
||||
m_tagId(tagId),
|
||||
m_value(value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString Tag::deviceId() const
|
||||
{
|
||||
return m_deviceId;
|
||||
}
|
||||
|
||||
void Tag::setDeviceId(const QString &deviceId)
|
||||
{
|
||||
m_deviceId = deviceId;
|
||||
}
|
||||
|
||||
QString Tag::ruleId() const
|
||||
{
|
||||
return m_ruleId;
|
||||
}
|
||||
|
||||
void Tag::setRuleId(const QString &ruleId)
|
||||
{
|
||||
m_ruleId = ruleId;
|
||||
}
|
||||
|
||||
QString Tag::tagId() const
|
||||
{
|
||||
return m_tagId;
|
||||
}
|
||||
|
||||
QString Tag::value() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void Tag::setValue(const QString &value)
|
||||
{
|
||||
if (m_value != value) {
|
||||
m_value = value;
|
||||
qDebug() << "tags value changed" << m_deviceId << m_tagId << value;
|
||||
emit valueChanged();
|
||||
}
|
||||
}
|
||||
38
libnymea-common/types/tag.h
Normal file
38
libnymea-common/types/tag.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef TAG_H
|
||||
#define TAG_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class Tag : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString deviceId READ deviceId CONSTANT)
|
||||
Q_PROPERTY(QString 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);
|
||||
|
||||
QString ruleId() const;
|
||||
void setRuleId(const QString &ruleId);
|
||||
|
||||
QString tagId() const;
|
||||
|
||||
QString value() const;
|
||||
void setValue(const QString &value);
|
||||
|
||||
signals:
|
||||
void valueChanged();
|
||||
|
||||
private:
|
||||
QString m_deviceId;
|
||||
QString m_ruleId;
|
||||
QString m_tagId;
|
||||
QString m_value;
|
||||
};
|
||||
|
||||
#endif // TAG_H
|
||||
106
libnymea-common/types/tags.cpp
Normal file
106
libnymea-common/types/tags.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "tags.h"
|
||||
#include "tag.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
Tags::Tags(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int Tags::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_list.count();
|
||||
}
|
||||
|
||||
QVariant Tags::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case RoleDeviceId:
|
||||
return m_list.at(index.row())->deviceId();
|
||||
case RoleRuleId:
|
||||
return m_list.at(index.row())->ruleId();
|
||||
case RoleTagId:
|
||||
return m_list.at(index.row())->tagId();
|
||||
case RoleValue:
|
||||
return m_list.at(index.row())->value();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> Tags::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles.insert(RoleDeviceId, "deviceId");
|
||||
roles.insert(RoleRuleId, "ruleId");
|
||||
roles.insert(RoleTagId, "tagId");
|
||||
roles.insert(RoleValue, "value");
|
||||
return roles;
|
||||
}
|
||||
|
||||
void Tags::addTag(Tag *tag)
|
||||
{
|
||||
tag->setParent(this);
|
||||
connect(tag, &Tag::valueChanged, this, &Tags::tagValueChanged);
|
||||
beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
|
||||
m_list.append(tag);
|
||||
endInsertRows();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
void Tags::removeTag(Tag *tag)
|
||||
{
|
||||
int idx = m_list.indexOf(tag);
|
||||
if (idx < 0) {
|
||||
qWarning() << "Don't know this tag. Can't remove";
|
||||
return;
|
||||
}
|
||||
beginRemoveRows(QModelIndex(), idx, idx);
|
||||
m_list.removeAt(idx);
|
||||
endRemoveRows();
|
||||
tag->deleteLater();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
Tag *Tags::get(int index) const
|
||||
{
|
||||
return m_list.at(index);
|
||||
}
|
||||
|
||||
Tag *Tags::findDeviceTag(const QString &deviceId, const QString &tagId) const
|
||||
{
|
||||
foreach (Tag *tag, m_list) {
|
||||
if (tag->deviceId() == deviceId && tag->tagId() == tagId) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Tag *Tags::findRuleTag(const QString &ruleId, const QString &tagId) const
|
||||
{
|
||||
foreach (Tag *tag, m_list) {
|
||||
if (tag->ruleId() == ruleId && tag->tagId() == tagId) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Tags::clear()
|
||||
{
|
||||
beginResetModel();
|
||||
qDeleteAll(m_list);
|
||||
m_list.clear();
|
||||
endResetModel();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
void Tags::tagValueChanged()
|
||||
{
|
||||
qDebug() << "Tag value in mode changed";
|
||||
Tag *tag = static_cast<Tag*>(sender());
|
||||
int idx = m_list.indexOf(tag);
|
||||
emit dataChanged(index(idx, 0), index(idx, 0), {RoleValue});
|
||||
}
|
||||
47
libnymea-common/types/tags.h
Normal file
47
libnymea-common/types/tags.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef TAGS_H
|
||||
#define TAGS_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
class Tag;
|
||||
|
||||
class Tags: public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
public:
|
||||
enum Roles {
|
||||
RoleDeviceId,
|
||||
RoleRuleId,
|
||||
RoleTagId,
|
||||
RoleValue
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit Tags(QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
void addTag(Tag *tag);
|
||||
void removeTag(Tag *tag);
|
||||
|
||||
Tag* get(int index) const;
|
||||
|
||||
Q_INVOKABLE Tag* findDeviceTag(const QString &deviceId, const QString &tagId) const;
|
||||
Q_INVOKABLE Tag* findRuleTag(const QString &ruleId, const QString &tagId) const;
|
||||
|
||||
void clear();
|
||||
|
||||
signals:
|
||||
void countChanged();
|
||||
|
||||
private slots:
|
||||
void tagValueChanged();
|
||||
|
||||
private:
|
||||
QList<Tag*> m_list;
|
||||
};
|
||||
|
||||
#endif // TAGS_H
|
||||
@ -6,6 +6,8 @@ SUBDIRS = libnymea-common libnymea-app-core nymea-app
|
||||
libnymea-app-core.depends = libnymea-common
|
||||
nymea-app.depends = libnymea-app-core
|
||||
|
||||
#QML_IMPORT_PATH=/home/micha/Develop/Qt/5.11.0/gcc_64/qml/
|
||||
|
||||
withtests: {
|
||||
SUBDIRS += tests
|
||||
tests.depends = libnymea-app-core
|
||||
|
||||
@ -2,7 +2,7 @@ TEMPLATE=app
|
||||
TARGET=nymea-app
|
||||
include(../config.pri)
|
||||
|
||||
QT += qml quick quickcontrols2 svg websockets bluetooth
|
||||
QT += network qml quick quickcontrols2 svg websockets bluetooth
|
||||
|
||||
INCLUDEPATH += $$top_srcdir/libnymea-common \
|
||||
$$top_srcdir/libnymea-app-core
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<qresource prefix="/">
|
||||
<file>ui/Nymea.qml</file>
|
||||
<file>ui/ConnectPage.qml</file>
|
||||
<file>ui/DevicesPage.qml</file>
|
||||
<file>ui/mainviews/DevicesPage.qml</file>
|
||||
<file>ui/NewDeviceWizard.qml</file>
|
||||
<file>ui/SettingsPage.qml</file>
|
||||
<file>ui/components/GuhHeader.qml</file>
|
||||
@ -203,5 +203,14 @@
|
||||
<file>ui/images/rpi-setup.svg</file>
|
||||
<file>ui/images/eye.svg</file>
|
||||
<file>ui/images/private-browsing.svg</file>
|
||||
<file>ui/images/starred.svg</file>
|
||||
<file>ui/images/non-starred.svg</file>
|
||||
<file>ui/images/slideshow.svg</file>
|
||||
<file>ui/components/MainPageTabButton.qml</file>
|
||||
<file>ui/mainviews/ScenesView.qml</file>
|
||||
<file>ui/mainviews/FavoritesView.qml</file>
|
||||
<file>ui/mainviews/DevicesPageDelegate.qml</file>
|
||||
<file>ui/components/AutoSizeMenu.qml</file>
|
||||
<file>ui/components/EmptyViewPlaceholder.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -28,6 +28,20 @@ Page {
|
||||
popup.open();
|
||||
}
|
||||
onConnectionError: {
|
||||
var errorMessage;
|
||||
switch (error) {
|
||||
case "ConnectionRefusedError":
|
||||
errorMessage = qsTr("The host has rejected our connection. This probably means that %1 stopped running. Did you unplug your %1 box?").arg(app.systemName);
|
||||
break;
|
||||
case "SslInvalidUserDataError":
|
||||
case "SslHandshakeFailedError":
|
||||
// silently ignore. They'll be handled by the SSL logic
|
||||
return;
|
||||
}
|
||||
var comp = Qt.createComponent(Qt.resolvedUrl("components/ErrorDialog.qml"))
|
||||
var popup = comp.createObject(app, {text: errorMessage})
|
||||
popup.open()
|
||||
|
||||
pageStack.pop(root)
|
||||
pageStack.push(discoveryPage)
|
||||
}
|
||||
|
||||
@ -1,284 +0,0 @@
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
import Nymea 1.0
|
||||
import "components"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property alias count: interfacesGridView.count
|
||||
property alias model: interfacesGridView.model
|
||||
|
||||
GridView {
|
||||
id: interfacesGridView
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
|
||||
readonly property int minTileWidth: 180
|
||||
readonly property int minTileHeight: 240
|
||||
readonly property int tilesPerRow: root.width / minTileWidth
|
||||
|
||||
model: InterfacesModel {
|
||||
id: interfacesModel
|
||||
devices: Engine.deviceManager.devices
|
||||
}
|
||||
cellWidth: width / tilesPerRow
|
||||
cellHeight: Math.max(cellWidth, minTileHeight)
|
||||
delegate: Item {
|
||||
width: interfacesGridView.cellWidth
|
||||
height: interfacesGridView.cellHeight
|
||||
Pane {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
Material.elevation: 1
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -app.iconSize
|
||||
spacing: app.margins
|
||||
ColorIcon {
|
||||
height: app.iconSize * 2
|
||||
width: height
|
||||
color: app.guhAccent
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
name: interfaceToIcon(model.name)
|
||||
}
|
||||
|
||||
Label {
|
||||
text: interfaceToString(model.name).toUpperCase()
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
var page;
|
||||
switch (model.name) {
|
||||
case "light":
|
||||
page = "LightsDeviceListPage.qml"
|
||||
break;
|
||||
default:
|
||||
page = "GenericDeviceListPage.qml"
|
||||
}
|
||||
|
||||
pageStack.push(Qt.resolvedUrl("devicelistpages/" + page), {filterInterface: model.name})
|
||||
}
|
||||
}
|
||||
|
||||
DevicesProxy {
|
||||
id: devicesProxy
|
||||
devices: Engine.deviceManager.devices
|
||||
filterInterface: model.name
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: inlineControlPane
|
||||
anchors { left: parent.left; bottom: parent.bottom; right: parent.right; margins: app.margins / 2 }
|
||||
height: app.iconSize + app.margins * 2
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
// color: app.guhAccent
|
||||
color: "black"
|
||||
opacity: .05
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: inlineControlLoader
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: app.margins
|
||||
rightMargin: app.margins
|
||||
topMargin: app.margins / 2
|
||||
bottomMargin: app.margins / 2
|
||||
}
|
||||
sourceComponent: {
|
||||
switch (model.name) {
|
||||
case "sensor":
|
||||
case "weather":
|
||||
return labelComponent;
|
||||
|
||||
case "light":
|
||||
case "media":
|
||||
case "garagegate":
|
||||
case "shutter":
|
||||
case "blind":
|
||||
return buttonComponent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: buttonComponent
|
||||
MouseArea {
|
||||
onClicked: {
|
||||
switch (model.name) {
|
||||
case "light":
|
||||
if (devicesProxy.count == 1) {
|
||||
var device = devicesProxy.get(0);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("power")
|
||||
var actionType = deviceClass.actionTypes.findByName("power")
|
||||
var params = [];
|
||||
var param1 = {};
|
||||
param1["paramTypeId"] = actionType.paramTypes.get(0).id;
|
||||
param1["value"] = !device.states.getState(stateType.id).value;
|
||||
params.push(param1)
|
||||
Engine.deviceManager.executeAction(device.id, actionType.id, params)
|
||||
} else {
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("power");
|
||||
|
||||
var params = [];
|
||||
var param1 = {};
|
||||
param1["paramTypeId"] = actionType.paramTypes.get(0).id;
|
||||
param1["value"] = false;
|
||||
params.push(param1)
|
||||
Engine.deviceManager.executeAction(device.id, actionType.id, params)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "media":
|
||||
var device = devicesProxy.get(0)
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("playbackStatus");
|
||||
var state = device.states.getState(stateType.id)
|
||||
|
||||
var actionName
|
||||
switch (state.value) {
|
||||
case "PLAYING":
|
||||
actionName = "pause";
|
||||
break;
|
||||
case "PAUSED":
|
||||
actionName = "play";
|
||||
break;
|
||||
}
|
||||
var actionTypeId = deviceClass.actionTypes.findByName(actionName).id;
|
||||
|
||||
print("executing", device, device.id, actionTypeId, actionName, deviceClass.actionTypes)
|
||||
|
||||
Engine.deviceManager.executeAction(device.id, actionTypeId)
|
||||
case "garagegate":
|
||||
case "shutter":
|
||||
case "blind":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("close");
|
||||
Engine.deviceManager.executeAction(device.id, actionType.id)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
id: label
|
||||
Layout.fillWidth: true
|
||||
text: {
|
||||
switch (model.name) {
|
||||
case "media":
|
||||
return devicesProxy.get(0).name;
|
||||
case "light":
|
||||
var count = 0;
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("power")
|
||||
if (device.states.getState(stateType.id).value === true) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count === 0 ? qsTr("All off") : qsTr("%1 on").arg(count)
|
||||
case "garagegate":
|
||||
var count = 0;
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("state");
|
||||
if (device.states.getState(stateType.id).value !== "closed") {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count === 0 ? qsTr("All closed") : qsTr("%1 open").arg(count)
|
||||
case "shutter":
|
||||
return qsTr("%1 installed").arg(devicesProxy.count)
|
||||
}
|
||||
console.warn("Unhandled interface", model.name)
|
||||
}
|
||||
font.pixelSize: app.smallFont
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
ColorIcon {
|
||||
id: icon
|
||||
width: app.largeFont
|
||||
height: width
|
||||
color: app.guhAccent
|
||||
anchors.right: parent.right
|
||||
name: {
|
||||
switch (model.name) {
|
||||
case "media":
|
||||
var device = devicesProxy.get(0)
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("playbackStatus");
|
||||
var state = device.states.getState(stateType.id)
|
||||
return state.value === "PLAYING" ? "../images/media-playback-pause.svg" :
|
||||
state.value === "PAUSED" ? "../images/media-playback-start.svg" :
|
||||
""
|
||||
case "light":
|
||||
return "../images/system-shutdown.svg"
|
||||
case "garagegate":
|
||||
case "shutter":
|
||||
case "blind":
|
||||
return "../images/down.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: labelComponent
|
||||
ColumnLayout {
|
||||
property var device: devicesProxy.get(0)
|
||||
property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
|
||||
property var state: deviceClass ? device.states.getState(deviceClass.stateTypes.findByName("temperature").id) : null
|
||||
|
||||
Label {
|
||||
text: parent.device.name
|
||||
font.pixelSize: app.smallFont
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Label {
|
||||
font.pixelSize: app.largeFont
|
||||
color: app.guhAccent
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: {
|
||||
if (devicesProxy.count > 0) {
|
||||
var stateName;
|
||||
// switch (model.name) {
|
||||
// case "sensor":
|
||||
// }
|
||||
return parent.state.value + "°C";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,42 +33,14 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
EmptyViewPlaceholder {
|
||||
anchors { left: parent.left; right: parent.right; margins: app.margins }
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: app.margins * 2
|
||||
visible: Engine.deviceManager.devices.count === 0 && !Engine.deviceManager.fetchingData
|
||||
Label {
|
||||
text: qsTr("There are no things set up yet.")
|
||||
font.pixelSize: app.largeFont
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: app.guhAccent
|
||||
}
|
||||
Label {
|
||||
text: qsTr("In order for your %1 box to be useful, go ahead and add some things.").arg(app.systemName)
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 400
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
Image {
|
||||
source: "qrc:/styles/%1/logo.svg".arg(styleController.currentStyle)
|
||||
Layout.preferredWidth: app.iconSize * 5
|
||||
Layout.preferredHeight: width
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
sourceSize.width: app.iconSize * 5
|
||||
sourceSize.height: app.iconSize * 5
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 400
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Add a thing")
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("NewDeviceWizard.qml"))
|
||||
}
|
||||
title: qsTr("There are no things set up yet.")
|
||||
text: qsTr("In order for your %1 box to be useful, go ahead and add some things.").arg(app.systemName)
|
||||
imageSource: "qrc:/styles/%1/logo.svg".arg(styleController.currentStyle)
|
||||
buttonText: qsTr("Add a thing")
|
||||
onButtonClicked: pageStack.push(Qt.resolvedUrl("NewDeviceWizard.qml"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -43,6 +43,9 @@ Page {
|
||||
onAddRuleReply: {
|
||||
d.editRulePage.busy = false;
|
||||
if (ruleError == "RuleErrorNoError") {
|
||||
print("should tag rule now:", d.editRulePage.rule.id, d.editRulePage.ruleIcon, d.editRulePage.ruleColor)
|
||||
Engine.tagsManager.tagRule(ruleId, "color", d.editRulePage.ruleColor)
|
||||
Engine.tagsManager.tagRule(ruleId, "icon", d.editRulePage.ruleIcon)
|
||||
pageStack.pop();
|
||||
} else {
|
||||
var popup = errorDialog.createObject(app, {errorCode: ruleError })
|
||||
@ -53,6 +56,9 @@ Page {
|
||||
onEditRuleReply: {
|
||||
d.editRulePage.busy = false;
|
||||
if (ruleError == "RuleErrorNoError") {
|
||||
print("should tag rule now:", d.editRulePage.ruleIcon, d.editRulePage.ruleColor)
|
||||
Engine.tagsManager.tagRule(d.editRulePage.rule.id, "color", d.editRulePage.ruleColor)
|
||||
Engine.tagsManager.tagRule(d.editRulePage.rule.id, "icon", d.editRulePage.ruleIcon)
|
||||
pageStack.pop();
|
||||
} else {
|
||||
var popup = errorDialog.createObject(app, {errorCode: ruleError })
|
||||
@ -64,19 +70,32 @@ Page {
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
|
||||
model: Engine.ruleManager.rules
|
||||
model: RulesFilterModel {
|
||||
id: rulesProxy
|
||||
rules: Engine.ruleManager.rules
|
||||
}
|
||||
delegate: MeaListItemDelegate {
|
||||
id: ruleDelegate
|
||||
width: parent.width
|
||||
iconName: "../images/magic.svg"
|
||||
iconColor: !model.enabled ? "red" : (model.active ? app.guhAccent : "grey")
|
||||
iconName: "../images/" + (model.executable ? (iconTag ? iconTag.value : "slideshow") : "magic") + ".svg"
|
||||
iconColor: model.executable ? (colorTag ? colorTag.value : app.guhAccent) : !model.enabled ? "red" : (model.active ? app.guhAccent : "grey")
|
||||
text: model.name
|
||||
canDelete: true
|
||||
|
||||
property var colorTag: model.executable ? Engine.tagsManager.tags.findRuleTag(model.id, "color") : null
|
||||
property var iconTag: model.executable ? Engine.tagsManager.tags.findRuleTag(model.id, "icon") : null
|
||||
Connections {
|
||||
target: Engine.tagsManager.tags
|
||||
onCountChanged: {
|
||||
colorTag = Engine.tagsManager.tags.findRuleTag(model.id, "color")
|
||||
iconTag = Engine.tagsManager.tags.findRuleTag(model.id, "icon")
|
||||
}
|
||||
}
|
||||
|
||||
onDeleteClicked: Engine.ruleManager.removeRule(model.id)
|
||||
|
||||
onClicked: {
|
||||
var newRule = Engine.ruleManager.rules.get(index).clone();
|
||||
var newRule = rulesProxy.get(index).clone();
|
||||
d.editRulePage = pageStack.push(Qt.resolvedUrl("magic/EditRulePage.qml"), {rule: newRule})
|
||||
d.editRulePage.StackView.onRemoved.connect(function() {
|
||||
newRule.destroy();
|
||||
@ -92,37 +111,16 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
EmptyViewPlaceholder {
|
||||
anchors { left: parent.left; right: parent.right; margins: app.margins }
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: app.margins * 2
|
||||
visible: Engine.ruleManager.rules.count === 0
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("There is no magic set up yet.")
|
||||
wrapMode: Text.WordWrap
|
||||
color: app.guhAccent
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Add some using the wizard stick!")
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
AbstractButton {
|
||||
Layout.preferredHeight: app.iconSize * 4
|
||||
Layout.preferredWidth: height
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
ColorIcon {
|
||||
anchors.fill: parent
|
||||
name: "../images/magic.svg"
|
||||
}
|
||||
|
||||
onClicked: addRule()
|
||||
}
|
||||
visible: Engine.ruleManager.rules.count === 0
|
||||
title: qsTr("There is no magic set up yet.")
|
||||
text: qsTr("Use magic to make your things smart! In a few easy steps you'll have your things wired up and work for you.")
|
||||
imageSource: "images/magic.svg"
|
||||
buttonText: qsTr("Add some magic")
|
||||
onImageClicked: addRule()
|
||||
onButtonClicked: addRule()
|
||||
}
|
||||
|
||||
Component {
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
import Nymea 1.0
|
||||
import "components"
|
||||
import "delegates"
|
||||
import "mainviews"
|
||||
|
||||
Page {
|
||||
id: root
|
||||
|
||||
header: GuhHeader {
|
||||
text: qsTr("My things")
|
||||
text: swipeView.currentItem.title
|
||||
backButtonVisible: false
|
||||
menuButtonVisible: true
|
||||
onMenuPressed: mainMenu.open()
|
||||
@ -36,72 +37,134 @@ Page {
|
||||
// }
|
||||
// }
|
||||
|
||||
Menu {
|
||||
AutoSizeMenu {
|
||||
id: mainMenu
|
||||
width: implicitWidth + app.margins
|
||||
IconMenuItem {
|
||||
iconSource: "../images/share.svg"
|
||||
text: qsTr("Configure things")
|
||||
width: parent.width
|
||||
onTriggered: pageStack.push(Qt.resolvedUrl("EditDevicesPage.qml"))
|
||||
}
|
||||
// MenuSeparator {}
|
||||
IconMenuItem {
|
||||
iconSource: "../images/magic.svg"
|
||||
text: qsTr("Magic")
|
||||
width: parent.width
|
||||
onTriggered: pageStack.push(Qt.resolvedUrl("MagicPage.qml"))
|
||||
}
|
||||
MenuSeparator {}
|
||||
MenuSeparator { width: parent.width }
|
||||
IconMenuItem {
|
||||
iconSource: "../images/settings.svg"
|
||||
text: qsTr("System settings")
|
||||
width: parent.width
|
||||
onTriggered: pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
|
||||
}
|
||||
MenuSeparator {}
|
||||
MenuSeparator { width: parent.width }
|
||||
IconMenuItem {
|
||||
iconSource: "../images/stock_application.svg"
|
||||
text: qsTr("App settings")
|
||||
width: parent.width
|
||||
onTriggered: pageStack.push(Qt.resolvedUrl("AppSettingsPage.qml"))
|
||||
}
|
||||
}
|
||||
|
||||
InterfacesModel {
|
||||
id: page1Model
|
||||
devices: Engine.deviceManager.devices
|
||||
shownInterfaces: ["light", "weather", "sensor", "media", "garagegate", "shutter", "garagegate"]
|
||||
property var view: null
|
||||
onCountChanged: buildView()
|
||||
}
|
||||
InterfacesModel {
|
||||
id: page2Model
|
||||
devices: Engine.deviceManager.devices
|
||||
shownInterfaces: ["gateway", "button", "notifications", "inputtrigger", "outputtrigger"]
|
||||
property var view: null
|
||||
onCountChanged: buildView()
|
||||
}
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Component {
|
||||
id: devicePageComponent
|
||||
DevicesPage {
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
visible: count > 0
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Component {
|
||||
id: allDevicesComponent
|
||||
ListView {
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
model: DevicesProxy {
|
||||
id: devicesProxy
|
||||
devices: Engine.deviceManager.devices
|
||||
}
|
||||
delegate: ThingDelegate {
|
||||
interfaces: model.interfaces
|
||||
name: model.name
|
||||
onClicked: {
|
||||
pageStack.push(Qt.resolvedUrl("devicepages/GenericDevicePage.qml"), {device: devicesProxy.get(index)})
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
clip: true
|
||||
anchors.fill: parent
|
||||
currentIndex: settings.currentMainViewIndex
|
||||
onCurrentIndexChanged: settings.currentMainViewIndex = currentIndex
|
||||
opacity: Engine.deviceManager.fetchingData ? 0 : 1
|
||||
Behavior on opacity { NumberAnimation { duration: 300 } }
|
||||
|
||||
Component.onCompleted: {
|
||||
if (Engine.jsonRpcClient.ensureServerVersion(1.6)) {
|
||||
swipeView.insertItem(0, favoritesViewComponent.createObject(swipeView))
|
||||
} else if (settings.currentMainViewIndex === 2) {
|
||||
settings.currentMainViewIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: favoritesViewComponent
|
||||
FavoritesView {
|
||||
id: favoritesView
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
property string title: qsTr("My favorites")
|
||||
|
||||
EmptyViewPlaceholder {
|
||||
anchors { left: parent.left; right: parent.right; margins: app.margins }
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: favoritesView.count === 0 && !Engine.deviceManager.fetchingData
|
||||
title: qsTr("There are no favorite things yet.")
|
||||
text: Engine.deviceManager.devices.count === 0 ?
|
||||
qsTr("It appears there are no things set up either yet. In order to use favorites you need to add some things first.") :
|
||||
qsTr("Favorites allow you to keep track of your most important things when you have lots of them. Watch out for the star when interacting with things and use it to mark them as your favorites.")
|
||||
imageSource: "images/starred.svg"
|
||||
buttonVisible: Engine.deviceManager.devices.count === 0
|
||||
buttonText: qsTr("Add a thing")
|
||||
onButtonClicked: pageStack.push(Qt.resolvedUrl("NewDeviceWizard.qml"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DevicesPage {
|
||||
property string title: qsTr("My things");
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
model: InterfacesSortModel {
|
||||
interfacesModel: InterfacesModel {
|
||||
devices: Engine.deviceManager.devices
|
||||
shownInterfaces: app.supportedInterfaces
|
||||
}
|
||||
}
|
||||
|
||||
EmptyViewPlaceholder {
|
||||
anchors { left: parent.left; right: parent.right; margins: app.margins }
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: Engine.deviceManager.devices.count === 0 && !Engine.deviceManager.fetchingData
|
||||
title: qsTr("Welcome to %1!").arg(app.systemName)
|
||||
// Have that split in 2 because we need those strings separated in EditDevicesPage too and don't want translators to do them twice
|
||||
text: qsTr("There are no things set up yet.") + "\n" + qsTr("In order for your %1 box to be useful, go ahead and add some things.").arg(app.systemName)
|
||||
imageSource: "qrc:/styles/%1/logo.svg".arg(styleController.currentStyle)
|
||||
buttonText: qsTr("Add a thing")
|
||||
onButtonClicked: pageStack.push(Qt.resolvedUrl("NewDeviceWizard.qml"))
|
||||
}
|
||||
}
|
||||
|
||||
ScenesView {
|
||||
id: scenesView
|
||||
property string title: qsTr("My scenes");
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
|
||||
EmptyViewPlaceholder {
|
||||
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")
|
||||
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.")
|
||||
imageSource: "images/slideshow.svg"
|
||||
buttonText: Engine.deviceManager.devices.count === 0 ? qsTr("Add a thing") : qsTr("Add a scene")
|
||||
onButtonClicked: {
|
||||
if (Engine.deviceManager.devices.count === 0) {
|
||||
pageStack.push(Qt.resolvedUrl("NewDeviceWizard.qml"))
|
||||
} else {
|
||||
var page = pageStack.push(Qt.resolvedUrl("MagicPage.qml"))
|
||||
page.addRule()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,91 +184,36 @@ Page {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; right: parent.right; margins: app.margins }
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: app.margins * 2
|
||||
visible: Engine.deviceManager.devices.count === 0 && !Engine.deviceManager.fetchingData
|
||||
Label {
|
||||
text: qsTr("Welcome to %1!").arg(app.systemName)
|
||||
font.pixelSize: app.largeFont
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: app.guhAccent
|
||||
TabBar {
|
||||
id: tabBar
|
||||
Layout.fillWidth: true
|
||||
Material.elevation: 3
|
||||
currentIndex: settings.currentMainViewIndex
|
||||
position: TabBar.Footer
|
||||
Layout.preferredHeight: 70
|
||||
// FIXME: All this can go away when we require Controls 2.3 (Qt 5.10) or greater as TabBar got a major rework there.
|
||||
// Ideally we'd just list the 3 items and set visible to false if the server version isn't good enough but TabBar
|
||||
// has troubles dealing with that. For now, let's manually fill it and use a timer to initialize the currentIndex.
|
||||
Component.onCompleted: {
|
||||
var pi = 0;
|
||||
if (Engine.jsonRpcClient.ensureServerVersion(1.6)) {
|
||||
tabEntryComponent.createObject(tabBar, {text: qsTr("Favorites"), iconSource: "../images/starred.svg", pageIndex: pi++})
|
||||
}
|
||||
Label {
|
||||
text: qsTr("There are no things set up yet. In order for your %1 box to be useful, go ahead and add some things.").arg(app.systemName)
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 400
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
Image {
|
||||
source: "qrc:/styles/%1/logo.svg".arg(styleController.currentStyle)
|
||||
Layout.preferredWidth: app.iconSize * 5
|
||||
Layout.preferredHeight: width
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
sourceSize.width: app.iconSize * 5
|
||||
sourceSize.height: app.iconSize * 5
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 400
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Add a thing")
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("NewDeviceWizard.qml"))
|
||||
tabEntryComponent.createObject(tabBar, {text: qsTr("Things"), iconSource: "../images/share.svg", pageIndex: pi++})
|
||||
tabEntryComponent.createObject(tabBar, {text: qsTr("Scenes"), iconSource: "../images/slideshow.svg", pageIndex: pi++})
|
||||
initTimer.start()
|
||||
}
|
||||
Timer { id: initTimer; interval: 1; repeat: false; onTriggered: tabBar.currentIndex = Qt.binding(function() {return settings.currentMainViewIndex;})}
|
||||
|
||||
Component {
|
||||
id: tabEntryComponent
|
||||
MainPageTabButton {
|
||||
property int pageIndex: 0
|
||||
onClicked: settings.currentMainViewIndex = pageIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildView() {
|
||||
var shownViews = []
|
||||
if (page1Model.count > 0) {
|
||||
shownViews.push(0)
|
||||
}
|
||||
if (page2Model.count > 0) {
|
||||
shownViews.push(1)
|
||||
}
|
||||
shownViews.push(2)
|
||||
|
||||
if (swipeView.count === shownViews.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (swipeView.count > 0) {
|
||||
swipeView.removeItem(0)
|
||||
}
|
||||
if (shownViews.indexOf(0) >= 0) {
|
||||
swipeView.addItem(devicePageComponent.createObject(swipeView, {model: page1Model}))
|
||||
}
|
||||
if (shownViews.indexOf(1) >= 0) {
|
||||
swipeView.addItem(devicePageComponent.createObject(swipeView, {model: page2Model}))
|
||||
}
|
||||
swipeView.addItem(allDevicesComponent.createObject(swipeView))
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
currentIndex: pageIndicator.currentIndex
|
||||
clip: true
|
||||
}
|
||||
|
||||
PageIndicator {
|
||||
id: pageIndicator
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
count: swipeView.count
|
||||
currentIndex: swipeView.currentIndex
|
||||
interactive: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ ApplicationWindow {
|
||||
property bool darkTheme: false
|
||||
property string graphStyle: "bars"
|
||||
property string style: "light"
|
||||
property int currentMainViewIndex: 0
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
@ -103,6 +104,12 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround flickering on pageStack animations when the white background shines through
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Material.background
|
||||
}
|
||||
|
||||
StackView {
|
||||
id: pageStack
|
||||
objectName: "pageStack"
|
||||
@ -138,6 +145,7 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
property var supportedInterfaces: ["light", "weather", "sensor", "media", "garagegate", "shutter", "garagegate", "button", "notifications", "inputtrigger", "outputtrigger", "gateway"]
|
||||
function interfaceToString(name) {
|
||||
switch(name) {
|
||||
case "light":
|
||||
@ -168,6 +176,8 @@ ApplicationWindow {
|
||||
return qsTr("Blinds");
|
||||
case "garagegate":
|
||||
return qsTr("Garage gates");
|
||||
case "uncategorized":
|
||||
return qsTr("Uncategorized")
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,6 +232,8 @@ ApplicationWindow {
|
||||
return Qt.resolvedUrl("images/shutter-10.svg")
|
||||
case "battery":
|
||||
return Qt.resolvedUrl("images/battery/battery-050.svg")
|
||||
case "uncategorized":
|
||||
return Qt.resolvedUrl("images/select-none.svg")
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@ -236,6 +248,28 @@ ApplicationWindow {
|
||||
return "grey";
|
||||
}
|
||||
|
||||
function interfaceListToDevicePage(interfaceList) {
|
||||
var page;
|
||||
if (interfaceList.indexOf("media") >= 0) {
|
||||
page = "MediaDevicePage.qml";
|
||||
} else if (interfaceList.indexOf("button") >= 0) {
|
||||
page = "ButtonDevicePage.qml";
|
||||
} else if (interfaceList.indexOf("weather") >= 0) {
|
||||
page = "WeatherDevicePage.qml";
|
||||
} else if (interfaceList.indexOf("sensor") >= 0) {
|
||||
page = "SensorDevicePage.qml";
|
||||
} else if (interfaceList.indexOf("inputtrigger") >= 0) {
|
||||
page = "InputTriggerDevicePage.qml";
|
||||
} else if (interfaceList.indexOf("shutter") >= 0 ) {
|
||||
page = "ShutterDevicePage.qml";
|
||||
} else if (interfaceList.indexOf("garagegate") >= 0 ) {
|
||||
page = "GarageGateDevicePage.qml";
|
||||
} else {
|
||||
page = "GenericDevicePage.qml";
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
Component {
|
||||
id: invalidVersionComponent
|
||||
Popup {
|
||||
|
||||
17
nymea-app/ui/components/AutoSizeMenu.qml
Normal file
17
nymea-app/ui/components/AutoSizeMenu.qml
Normal file
@ -0,0 +1,17 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
Menu {
|
||||
function calculateWidth() {
|
||||
var result = 0;
|
||||
var i = 0;
|
||||
while (itemAt(i) !== null) {
|
||||
result = Math.max(itemAt(i).contentItem.implicitWidth + app.margins * 2, result);
|
||||
i++;
|
||||
}
|
||||
width = Math.min(parent.width, result + app.margins * 2);
|
||||
|
||||
}
|
||||
onAboutToShow: calculateWidth()
|
||||
|
||||
}
|
||||
53
nymea-app/ui/components/EmptyViewPlaceholder.qml
Normal file
53
nymea-app/ui/components/EmptyViewPlaceholder.qml
Normal file
@ -0,0 +1,53 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: app.margins * 2
|
||||
|
||||
property alias title: titleLabel.text
|
||||
property alias text: textLabel.text
|
||||
property alias imageSource: image.source
|
||||
property alias buttonText: button.text
|
||||
property alias buttonVisible: button.visible
|
||||
|
||||
signal imageClicked();
|
||||
signal buttonClicked();
|
||||
|
||||
Label {
|
||||
id: titleLabel
|
||||
font.pixelSize: app.largeFont
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: app.guhAccent
|
||||
}
|
||||
Label {
|
||||
id: textLabel
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 400
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
Image {
|
||||
id: image
|
||||
Layout.preferredWidth: app.iconSize * 5
|
||||
Layout.preferredHeight: width
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
sourceSize.width: app.iconSize * 5
|
||||
sourceSize.height: app.iconSize * 5
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.imageClicked();
|
||||
}
|
||||
}
|
||||
Button {
|
||||
id: button
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 400
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: root.buttonClicked();
|
||||
}
|
||||
}
|
||||
27
nymea-app/ui/components/MainPageTabButton.qml
Normal file
27
nymea-app/ui/components/MainPageTabButton.qml
Normal file
@ -0,0 +1,27 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
TabButton {
|
||||
id: root
|
||||
property string iconSource
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
ColorIcon {
|
||||
Layout.preferredWidth: app.iconSize
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
name: root.iconSource
|
||||
color: root.checked ? app.guhAccent : keyColor
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: root.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: app.smallFont
|
||||
color: root.checked ? app.guhAccent : Material.foreground
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,8 @@ import "../delegates"
|
||||
|
||||
Page {
|
||||
id: subPage
|
||||
property alias filterInterface: devicesProxy.filterInterface
|
||||
property alias shownInterfaces: devicesProxy.shownInterfaces
|
||||
property alias hiddenInterfaces: devicesProxy.hiddenInterfaces
|
||||
|
||||
Component.onCompleted: {
|
||||
if (devicesProxy.count == 1) {
|
||||
@ -17,9 +18,12 @@ Page {
|
||||
|
||||
header: GuhHeader {
|
||||
text: {
|
||||
if (subPage.filterInterface.length > 0) {
|
||||
if (subPage.shownInterfaces.length === 1) {
|
||||
return qsTr("My %1 things").arg(interfaceToString(subPage.filterInterface))
|
||||
} else if (subPage.shownInterfaces.length > 1) {
|
||||
return qsTr("My things")
|
||||
}
|
||||
|
||||
return qsTr("All my things")
|
||||
}
|
||||
|
||||
@ -32,24 +36,7 @@ Page {
|
||||
function enterPage(index, replace) {
|
||||
var device = devicesProxy.get(index);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var page;
|
||||
if (deviceClass.interfaces.indexOf("media") >= 0) {
|
||||
page = "MediaDevicePage.qml";
|
||||
} else if (deviceClass.interfaces.indexOf("button") >= 0) {
|
||||
page = "ButtonDevicePage.qml";
|
||||
} else if (deviceClass.interfaces.indexOf("weather") >= 0) {
|
||||
page = "WeatherDevicePage.qml";
|
||||
} else if (deviceClass.interfaces.indexOf("sensor") >= 0) {
|
||||
page = "SensorDevicePage.qml";
|
||||
} else if (deviceClass.interfaces.indexOf("inputtrigger") >= 0) {
|
||||
page = "InputTriggerDevicePage.qml";
|
||||
} else if (deviceClass.interfaces.indexOf("shutter") >= 0 ) {
|
||||
page = "ShutterDevicePage.qml";
|
||||
} else if (deviceClass.interfaces.indexOf("garagegate") >= 0 ) {
|
||||
page = "GarageGateDevicePage.qml";
|
||||
} else {
|
||||
page = "GenericDevicePage.qml";
|
||||
}
|
||||
var page = app.interfaceListToDevicePage(deviceClass.interfaces);
|
||||
if (replace) {
|
||||
pageStack.replace(Qt.resolvedUrl("../devicepages/" + page), {device: devicesProxy.get(index)})
|
||||
} else {
|
||||
|
||||
@ -5,7 +5,7 @@ import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Page {
|
||||
property alias filterInterface: devicesProxy.filterInterface
|
||||
property alias shownInterfaces: devicesProxy.shownInterfaces
|
||||
header: GuhHeader {
|
||||
text: qsTr("Lights")
|
||||
onBackPressed: pageStack.pop()
|
||||
@ -48,6 +48,7 @@ Page {
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: itemDelegate
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
property var device: devicesProxy.get(index);
|
||||
@ -67,9 +68,9 @@ Page {
|
||||
ThrottledSlider {
|
||||
id: inlineSlider
|
||||
visible: model.interfaces.indexOf("dimmablelight") >= 0 && parent.width > 350
|
||||
property var stateType: deviceClass.stateTypes.findByName("brightness");
|
||||
property var actionType: deviceClass.actionTypes.findByName("brightness");
|
||||
property var actionState: device.states.getState(stateType.id)
|
||||
property var stateType: itemDelegate.deviceClass.stateTypes.findByName("brightness");
|
||||
property var actionType: itemDelegate.deviceClass.actionTypes.findByName("brightness");
|
||||
property var actionState: itemDelegate.device.states.getState(stateType.id)
|
||||
from: 0; to: 100
|
||||
value: actionState.value
|
||||
onMoved: {
|
||||
@ -82,9 +83,9 @@ Page {
|
||||
}
|
||||
}
|
||||
Switch {
|
||||
property var stateType: deviceClass.stateTypes.findByName("power");
|
||||
property var actionType: deviceClass.actionTypes.findByName("power");
|
||||
property var actionState: device.states.getState(stateType.id)
|
||||
property var stateType: itemDelegate.deviceClass.stateTypes.findByName("power");
|
||||
property var actionType: itemDelegate.deviceClass.actionTypes.findByName("power");
|
||||
property var actionState: itemDelegate.device.states.getState(stateType.id)
|
||||
checked: actionState.value === true
|
||||
onClicked: {
|
||||
var params = [];
|
||||
|
||||
@ -16,13 +16,55 @@ Page {
|
||||
onBackPressed: pageStack.pop()
|
||||
|
||||
HeaderButton {
|
||||
imageSource: "../images/magic.svg"
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("../magic/DeviceRulesPage.qml"), {device: root.device})
|
||||
imageSource: "../images/navigation-menu.svg"
|
||||
onClicked: thingMenu.open();
|
||||
}
|
||||
}
|
||||
|
||||
TagsProxyModel {
|
||||
id: favoritesProxy
|
||||
filterDeviceId: root.device.id
|
||||
filterTagId: "favorites"
|
||||
}
|
||||
|
||||
AutoSizeMenu {
|
||||
id: thingMenu
|
||||
x: parent.width - width
|
||||
|
||||
Component.onCompleted: {
|
||||
thingMenu.addItem(menuEntryComponent.createObject(thingMenu, {text: qsTr("Magic"), iconSource: "../images/magic.svg", functionName: "openDeviceMagicPage"}))
|
||||
|
||||
thingMenu.addItem(menuEntryComponent.createObject(thingMenu, {text: qsTr("Thing details"), iconSource: "../images/info.svg", functionName: "openDeviceInfoPage"}))
|
||||
if (Engine.jsonRpcClient.ensureServerVersion(1.6)) {
|
||||
thingMenu.addItem(menuEntryComponent.createObject(thingMenu,
|
||||
{
|
||||
text: Qt.binding(function() { return favoritesProxy.count === 0 ? qsTr("Mark as favorite") : qsTr("Remove from favorites")}),
|
||||
iconSource: Qt.binding(function() { return favoritesProxy.count === 0 ? "../images/starred.svg" : "../images/non-starred.svg"}),
|
||||
functionName: "toggleFavorite"
|
||||
}))
|
||||
}
|
||||
}
|
||||
function openDeviceMagicPage() {
|
||||
pageStack.push(Qt.resolvedUrl("../magic/DeviceRulesPage.qml"), {device: root.device})
|
||||
}
|
||||
function openDeviceInfoPage() {
|
||||
pageStack.push(Qt.resolvedUrl("GenericDeviceStateDetailsPage.qml"), {device: root.device})
|
||||
}
|
||||
function toggleFavorite() {
|
||||
if (favoritesProxy.count === 0) {
|
||||
Engine.tagsManager.tagDevice(root.device.id, "favorites", 100000)
|
||||
} else {
|
||||
Engine.tagsManager.untagDevice(root.device.id, "favorites")
|
||||
}
|
||||
}
|
||||
|
||||
HeaderButton {
|
||||
imageSource: "../images/info.svg"
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("GenericDeviceStateDetailsPage.qml"), {device: root.device})
|
||||
Component {
|
||||
id: menuEntryComponent
|
||||
IconMenuItem {
|
||||
width: parent.width
|
||||
property string functionName: ""
|
||||
onTriggered: thingMenu[functionName]()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,22 +5,8 @@ import Nymea 1.0
|
||||
import "../components"
|
||||
import "../customviews"
|
||||
|
||||
Page {
|
||||
DevicePageBase {
|
||||
id: root
|
||||
property var device: null
|
||||
readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
|
||||
|
||||
|
||||
header: GuhHeader {
|
||||
text: device.name
|
||||
onBackPressed: pageStack.pop()
|
||||
|
||||
HeaderButton {
|
||||
imageSource: "../images/info.svg"
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("GenericDeviceStateDetailsPage.qml"), {device: root.device})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
|
||||
@ -1,301 +0,0 @@
|
||||
<?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.92.2 (5c3e80d, 2017-08-06)"
|
||||
viewBox="0 0 96 96.000001"
|
||||
sodipodi:docname="magic.svg">
|
||||
<defs
|
||||
id="defs4876" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8284272"
|
||||
inkscape:cx="72.011163"
|
||||
inkscape:cy="30.205758"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g4780"
|
||||
showgrid="false"
|
||||
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="false"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1698"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="44"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5451"
|
||||
empspacing="8"
|
||||
snapvisiblegridlinesonly="true"
|
||||
enabled="true" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="8,-8.0000001"
|
||||
id="guide4063"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="4,-8.0000001"
|
||||
id="guide4065"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,88.000001"
|
||||
id="guide4067"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,92.000001"
|
||||
id="guide4069"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="104,4"
|
||||
id="guide4071"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-5,8.0000001"
|
||||
id="guide4073"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="92,-8.0000001"
|
||||
id="guide4075"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="88,-8.0000001"
|
||||
id="guide4077"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,84.000001"
|
||||
id="guide4074"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="12,-8.0000001"
|
||||
id="guide4076"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-5,12"
|
||||
id="guide4078"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="84,-9.0000001"
|
||||
id="guide4080"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="48,-8.0000001"
|
||||
orientation="1,0"
|
||||
id="guide4170"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="-8,48"
|
||||
orientation="0,1"
|
||||
id="guide4172"
|
||||
inkscape:locked="false" />
|
||||
</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 />
|
||||
</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)" />
|
||||
<g
|
||||
id="g1894"
|
||||
transform="matrix(0.8660254,0.49980225,-0.50019783,0.8660254,249.00681,-142.21407)">
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#808080;stroke-width:3.00094485;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 30.308594 7.9980469 C 29.732649 7.9577855 29.137407 8.0829062 28.597656 8.3945312 L 23.402344 11.394531 C 21.96301 12.225531 21.473687 14.052853 22.304688 15.492188 L 40.070312 46.263672 C 42.564163 45.825188 45.083421 45.27706 47.589844 44.605469 C 49.160416 44.184634 50.68081 43.717115 52.173828 43.230469 L 32.695312 9.4921875 C 32.175937 8.5926036 31.268502 8.0651491 30.308594 7.9980469 z M 52.939453 44.556641 C 51.325707 45.088407 49.681399 45.5984 47.978516 46.054688 C 45.608831 46.68964 43.228725 47.217784 40.869141 47.648438 L 42.587891 50.625 C 45.343918 50.105213 48.154312 49.47698 51.005859 48.712891 C 52.265992 48.375244 53.496223 48.01012 54.71875 47.638672 L 52.939453 44.556641 z M 55.492188 48.976562 C 54.146813 49.389444 52.784982 49.789545 51.394531 50.162109 C 48.681709 50.88902 46.00943 51.484656 43.378906 51.994141 L 63.304688 86.507812 C 64.135688 87.947147 65.96301 88.436469 67.402344 87.605469 L 72.597656 84.605469 C 74.03699 83.774469 74.526313 81.947147 73.695312 80.507812 L 55.492188 48.976562 z "
|
||||
transform="matrix(-0.50019783,-0.8660254,-0.86636805,0.5,455.57862,410.93144)"
|
||||
id="rect1888" />
|
||||
<path
|
||||
style="fill:#808080;fill-opacity:1;stroke:none;stroke-width:3.00094485;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 38.195312,19.019531 -10.390624,6 12.265624,21.244141 c 2.493851,-0.438484 5.013109,-0.986612 7.519532,-1.658203 1.570572,-0.420835 3.090966,-0.888354 4.583984,-1.375 z m 17.296876,29.957031 c -1.345375,0.412882 -2.707206,0.812983 -4.097657,1.185547 -2.712822,0.726911 -5.385101,1.322547 -8.015625,1.832032 l 14.425782,24.986328 10.390624,-6 z"
|
||||
id="rect1890"
|
||||
transform="matrix(-0.50019783,-0.8660254,-0.86636805,0.5,455.57862,410.93144)"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccc" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#808080;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1903"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="12"
|
||||
sodipodi:cy="63.807842"
|
||||
sodipodi:r1="11.632512"
|
||||
sodipodi:r2="5.8162556"
|
||||
sodipodi:arg1="1.3326172"
|
||||
sodipodi:arg2="1.9609357"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 14.7445,75.111959 -4.9565227,-5.924918 -7.6907342,0.72414 4.1032818,-6.544833 -3.0652651,-7.090552 7.4924902,1.879988 5.796296,-5.106341 0.527332,7.70673 6.647573,3.934659 -7.166581,2.883033 z"
|
||||
transform="matrix(0,-1,-1.0003957,0,433.33151,433.80227)"
|
||||
inkscape:transform-center-x="-0.84809303"
|
||||
inkscape:transform-center-y="0.27286362" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#808080;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1905"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="14.86737"
|
||||
sodipodi:cy="34.318844"
|
||||
sodipodi:r1="6.986371"
|
||||
sodipodi:r2="3.4931855"
|
||||
sodipodi:arg1="0.97303794"
|
||||
sodipodi:arg2="1.6013565"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 18.799236,40.093773 -4.038602,-2.283375 -4.170535,2.032424 0.923622,-4.54654 -3.2217163,-3.338361 4.6094313,-0.526542 2.179405,-4.095644 1.925164,4.22112 4.568662,0.807113 -3.419615,3.135337 z"
|
||||
transform="matrix(0,-1,-1.0003957,0,435.29822,435.88751)"
|
||||
inkscape:transform-center-x="-0.065962241"
|
||||
inkscape:transform-center-y="-0.6040865" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#808080;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1907"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="69.667763"
|
||||
sodipodi:cy="48"
|
||||
sodipodi:r1="7.9908891"
|
||||
sodipodi:r2="3.9954443"
|
||||
sodipodi:arg1="0.86871607"
|
||||
sodipodi:arg2="1.4970346"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 74.828338,54.101047 -4.866132,-2.116467 -4.502178,2.808746 0.509162,-5.28199 -4.062526,-3.413877 5.180811,-1.147982 1.9914,-4.918638 2.692755,4.572498 5.293279,0.373992 -3.516597,3.973941 z"
|
||||
transform="matrix(0,-1,-1.0003957,0,423.1292,432.66883)"
|
||||
inkscape:transform-center-x="0.18198021"
|
||||
inkscape:transform-center-y="-0.58791341" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#808080;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1909"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="8"
|
||||
sodipodi:cy="11.999999"
|
||||
sodipodi:r1="7.0902119"
|
||||
sodipodi:r2="3.5451059"
|
||||
sodipodi:arg1="0.97138318"
|
||||
sodipodi:arg2="1.5997017"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 12,17.854152 7.8975417,15.543624 3.6684378,17.613258 4.598151,12.997596 1.3229474,9.6150309 6,9.0729227 8.2049168,4.912749 l 1.9608642,4.2806207 4.637917,0.8114363 -3.465172,3.187677 z"
|
||||
transform="matrix(0,-1,-1.0003957,0,435.19471,439.528)"
|
||||
inkscape:transform-center-x="-0.06331877"
|
||||
inkscape:transform-center-y="-0.61654618" />
|
||||
<path
|
||||
style="fill:#808080;fill-opacity:1;stroke:none;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 67.513672 10.917969 C 64.985843 10.916324 62.314284 11.045287 59.537109 11.310547 C 56.933931 11.559183 54.230913 11.939606 51.466797 12.429688 L 54.220703 15.048828 L 54.150391 14.304688 C 58.555851 13.883874 62.636049 13.890175 66.193359 14.306641 C 69.750679 14.723066 72.787297 15.544697 75.117188 16.794922 C 77.447077 18.045147 79.094458 19.763523 79.673828 21.923828 C 80.253028 24.084403 79.685229 26.394797 78.292969 28.642578 C 76.900689 30.890369 74.681094 33.122225 71.808594 35.261719 C 66.063594 39.540696 57.688946 43.452777 47.978516 46.054688 C 38.262796 48.657997 28.345412 49.532279 20.576172 48.769531 C 16.691562 48.388152 13.343288 47.602974 10.798828 46.375 C 8.2543681 45.147036 6.4596556 43.424899 5.9785156 41.189453 C 5.5911256 39.389645 6.2567856 37.391465 7.3847656 35.351562 C 8.5127356 33.31166 10.146742 31.211204 11.919922 29.259766 C 13.049542 28.016587 14.196023 27.034758 15.345703 25.978516 C 14.198123 26.64999 13.202239 27.096549 11.849609 28.130859 C 8.3114594 30.836369 5.6074594 33.583556 3.9746094 36.195312 C 2.3417594 38.807069 1.78282 41.231781 2.375 43.441406 C 2.96752 45.653121 4.6644394 47.502934 7.3808594 48.960938 C 10.097289 50.418941 13.808222 51.447488 18.226562 51.964844 C 27.063252 52.999564 38.717849 52.005538 51.005859 48.712891 C 63.293889 45.420373 73.884061 40.453139 81.019531 35.138672 C 84.587271 32.481433 87.286616 29.736177 88.910156 27.115234 C 90.533686 24.494281 91.079048 22.043686 90.486328 19.832031 C 89.893818 17.620326 88.196889 15.770503 85.480469 14.3125 C 82.764049 12.854487 79.053106 11.827912 74.634766 11.310547 C 72.425596 11.051864 70.041501 10.919613 67.513672 10.917969 z "
|
||||
id="path1945"
|
||||
transform="matrix(0,-1,-1.0003957,0,438.00245,441.36222)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#808080;fill-opacity:1;stroke:#ffffff;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1970"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="46.3125"
|
||||
sodipodi:cy="5.5"
|
||||
sodipodi:r1="13.101168"
|
||||
sodipodi:r2="6.5505843"
|
||||
sodipodi:arg1="0.51914607"
|
||||
sodipodi:arg2="1.1474646"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 57.687499,11.999999 -8.684017,-0.527665 -5.35778,6.854543 -2.18167,-8.4220481 -8.174703,-2.9773817 7.335671,-4.6774469 0.305536,-8.6946667 6.715363,5.53122727 8.363535,-2.39621797 -3.185348,8.0959334 z"
|
||||
transform="matrix(0,-1,-1.0003957,0,430.32036,431.20152)"
|
||||
inkscape:transform-center-x="0.82409001"
|
||||
inkscape:transform-center-y="0.44110773" />
|
||||
<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:normal;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;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;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;vector-effect:none;fill:#ff3dff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.50029671;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 416.48828,349.14453 c -2.09199,-0.0241 -4.26,0.70397 -6.40039,2.0293 -2.85386,1.76711 -5.7029,4.5987 -8.43945,8.27148 -5.47311,7.34556 -10.49257,18.07774 -13.82813,30.52149 -3.33566,12.44372 -4.35859,24.24678 -3.29297,33.34375 0.53281,4.54848 1.58618,8.42211 3.17383,11.3789 1.58765,2.9568 3.75008,5.02172 6.45508,5.7461 2.70697,0.72518 5.5914,-0.002 8.43164,-1.77735 2.84024,-1.77499 5.68531,-4.60085 8.46484,-8.23437 3.85709,-5.04215 5.06812,-9.08101 5.65235,-10.48633 h -0.002 c 5.9e-4,-0.001 0.003,-0.004 0.004,-0.006 l -1.38867,-0.56836 c -5e-4,0.001 -0.001,0.005 -0.002,0.006 -2.1e-4,5e-4 -0.002,0.001 -0.002,0.002 -0.25972,0.64229 -1.2251,2.1002 -2.58203,3.73438 -1.35774,1.63516 -3.11592,3.50639 -5.00976,5.22656 -1.89385,1.72018 -3.92855,3.2907 -5.8125,4.33203 -1.88396,1.04133 -3.599,1.50968 -4.79688,1.25196 -1.6406,-0.35298 -3.0406,-1.70314 -4.15234,-4.00586 -1.11175,-2.30272 -1.88397,-5.50482 -2.25391,-9.27149 -0.73988,-7.53333 0.11212,-17.31777 2.67188,-26.86719 2.5612,-9.55471 6.42665,-17.78736 10.55078,-23.32226 2.06206,-2.76745 4.19024,-4.85677 6.20703,-6.10547 2.01678,-1.2487 3.87589,-1.65428 5.54492,-1.20703 1.66882,0.44738 3.07556,1.72879 4.19727,3.81836 1.1217,2.08956 1.91896,4.96147 2.32031,8.38867 0.40135,3.42719 0.41248,7.40815 0,11.72461 l 1.49219,0.14258 c 0.42098,-4.40546 0.41463,-8.48566 -0.002,-12.04297 -0.41659,-3.55732 -1.23756,-6.59394 -2.48828,-8.92383 -1.25072,-2.32989 -2.9697,-3.97727 -5.13086,-4.55664 -2.16143,-0.5792 -4.47398,-0.0114 -6.72265,1.38086 -2.24868,1.39228 -4.48076,3.61187 -6.6211,6.48437 -4.28067,5.745 -8.19393,14.11965 -10.79687,23.83008 -2.60434,9.71572 -3.47985,19.63311 -2.7168,27.40235 0.38153,3.88461 1.16803,7.23288 2.39649,9.77734 1.22845,2.54446 2.95117,4.33917 5.1875,4.82031 1.80052,0.38739 3.79913,-0.27827 5.83984,-1.40625 2.04071,-1.12797 4.14154,-2.76197 6.09375,-4.53515 1.24367,-1.12962 2.22654,-2.27611 3.2832,-3.42579 -0.67174,1.14758 -1.11762,2.14347 -2.15234,3.4961 -2.70658,3.53815 -5.45557,6.24215 -8.06836,7.875 -2.61279,1.63285 -5.0395,2.19179 -7.25,1.59961 -2.21259,-0.59252 -4.06291,-2.28944 -5.52149,-5.00586 -1.45858,-2.71643 -2.48634,-6.42736 -3.0039,-10.8457 -1.03513,-8.83669 -0.042,-20.49129 3.25195,-32.7793 3.29382,-12.28803 8.26351,-22.8782 13.58008,-30.01367 2.65829,-3.56774 5.40536,-6.26709 8.02734,-7.89063 2.62199,-1.62353 5.07263,-2.16889 7.28516,-1.57617 2.21258,0.59251 4.0629,2.28944 5.52148,5.00586 1.45859,2.71642 2.48634,6.42736 3.00391,10.8457 0.51757,4.41834 0.53073,9.54331 0,15.09766 -0.26536,2.77718 -0.66654,5.66077 -1.20703,8.61914 l 1.47656,0.26953 c 0.54791,-2.99895 0.95499,-5.92441 1.22461,-8.74609 0.53923,-5.64335 0.52891,-10.86754 -0.004,-15.41602 -0.53281,-4.54849 -1.58422,-8.42406 -3.17187,-11.38086 -1.58765,-2.95679 -3.75007,-5.01976 -6.45508,-5.74414 -0.67623,-0.18116 -1.36517,-0.2732 -2.0625,-0.28125 z"
|
||||
id="path943"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path958"
|
||||
d="m 416.48828,349.14453 c -2.09199,-0.0241 -4.26,0.70397 -6.40039,2.0293 -2.85386,1.76711 -5.7029,4.5987 -8.43945,8.27148 -5.47311,7.34556 -10.49257,18.07774 -13.82813,30.52149 -3.33566,12.44372 -4.35859,24.24678 -3.29297,33.34375 0.53281,4.54848 1.58618,8.42211 3.17383,11.3789 1.58765,2.9568 3.75008,5.02172 6.45508,5.7461 2.70697,0.72518 5.5914,-0.002 8.43164,-1.77735 2.84024,-1.77499 5.68531,-4.60085 8.46484,-8.23437 3.85709,-5.04215 5.06812,-9.08101 5.65235,-10.48633 h -0.002 c 5.9e-4,-0.001 0.003,-0.004 0.004,-0.006 l -1.38867,-0.56836 c -5e-4,0.001 -0.001,0.005 -0.002,0.006 -2.1e-4,5e-4 -0.002,0.001 -0.002,0.002 -0.25972,0.64229 -1.2251,2.1002 -2.58203,3.73438 -1.35774,1.63516 -3.11592,3.50639 -5.00976,5.22656 -1.89385,1.72018 -3.92855,3.2907 -5.8125,4.33203 -1.88396,1.04133 -3.599,1.50968 -4.79688,1.25196 -1.6406,-0.35298 -3.0406,-1.70314 -4.15234,-4.00586 -1.11175,-2.30272 -1.88397,-5.50482 -2.25391,-9.27149 -0.73988,-7.53333 0.11212,-17.31777 2.67188,-26.86719 2.5612,-9.55471 6.42665,-17.78736 10.55078,-23.32226 2.06206,-2.76745 4.19024,-4.85677 6.20703,-6.10547 2.01678,-1.2487 3.87589,-1.65428 5.54492,-1.20703 1.66882,0.44738 3.07556,1.72879 4.19727,3.81836 1.1217,2.08956 1.91896,4.96147 2.32031,8.38867 0.40135,3.42719 0.41248,7.40815 0,11.72461 l 1.49219,0.14258 c 0.42098,-4.40546 0.41463,-8.48566 -0.002,-12.04297 -0.41659,-3.55732 -1.23756,-6.59394 -2.48828,-8.92383 -1.25072,-2.32989 -2.9697,-3.97727 -5.13086,-4.55664 -2.16143,-0.5792 -4.47398,-0.0114 -6.72265,1.38086 -2.24868,1.39228 -4.48076,3.61187 -6.6211,6.48437 -4.28067,5.745 -8.19393,14.11965 -10.79687,23.83008 -2.60434,9.71572 -3.47985,19.63311 -2.7168,27.40235 0.38153,3.88461 1.16803,7.23288 2.39649,9.77734 1.22845,2.54446 2.95117,4.33917 5.1875,4.82031 1.80052,0.38739 3.79913,-0.27827 5.83984,-1.40625 2.04071,-1.12797 4.14154,-2.76197 6.09375,-4.53515 1.24367,-1.12962 2.22654,-2.27611 3.2832,-3.42579 -0.67174,1.14758 -1.11762,2.14347 -2.15234,3.4961 -2.70658,3.53815 -5.45557,6.24215 -8.06836,7.875 -2.61279,1.63285 -5.0395,2.19179 -7.25,1.59961 -2.21259,-0.59252 -4.06291,-2.28944 -5.52149,-5.00586 -1.45858,-2.71643 -2.48634,-6.42736 -3.0039,-10.8457 -1.03513,-8.83669 -0.042,-20.49129 3.25195,-32.7793 3.29382,-12.28803 8.26351,-22.8782 13.58008,-30.01367 2.65829,-3.56774 5.40536,-6.26709 8.02734,-7.89063 2.62199,-1.62353 5.07263,-2.16889 7.28516,-1.57617 2.21258,0.59251 4.0629,2.28944 5.52148,5.00586 1.45859,2.71642 2.48634,6.42736 3.00391,10.8457 0.51757,4.41834 0.53073,9.54331 0,15.09766 -0.26536,2.77718 -0.66654,5.66077 -1.20703,8.61914 l 1.47656,0.26953 c 0.54791,-2.99895 0.95499,-5.92441 1.22461,-8.74609 0.53923,-5.64335 0.52891,-10.86754 -0.004,-15.41602 -0.53281,-4.54849 -1.58422,-8.42406 -3.17187,-11.38086 -1.58765,-2.95679 -3.75007,-5.01976 -6.45508,-5.74414 -0.67623,-0.18116 -1.36517,-0.2732 -2.0625,-0.28125 z"
|
||||
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:normal;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;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;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;vector-effect:none;fill:#003dff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.50029671;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 22 KiB |
160
nymea-app/ui/images/non-starred.svg
Normal file
160
nymea-app/ui/images/non-starred.svg
Normal file
@ -0,0 +1,160 @@
|
||||
<?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="non-starred.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.6199993"
|
||||
inkscape:cx="-4.3416438"
|
||||
inkscape:cy="69.76867"
|
||||
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="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="1,0"
|
||||
position="84,-8.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:guide
|
||||
position="92,-8.0000001"
|
||||
orientation="1,0"
|
||||
id="guide4760" />
|
||||
</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;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;enable-background:accumulate"
|
||||
d="M 48.021484 8 C 48.021484 8 48.023438 8.0019531 48.023438 8.0019531 C 48.023438 8.0019531 48.025391 8 48.025391 8 L 48.021484 8 z M 48.023438 8.0019531 C 48.020658 8.0024345 48.017186 8.0030924 48.015625 8.0078125 C 48.008625 8.0088121 48.007859 8.0126262 48.005859 8.015625 C 47.999859 8.0186238 47.999047 8.0243449 47.998047 8.0273438 C 47.992047 8.0303426 47.991281 8.0292047 47.988281 8.0332031 C 42.927951 18.893186 39.018172 29.962354 36.794922 36.744141 C 29.481092 36.750138 17.434385 37.027378 5.953125 38.517578 C 5.952125 38.521577 5.9510781 38.522392 5.9550781 38.525391 C 5.9540781 38.532388 5.9598906 38.536164 5.9628906 38.539062 C 5.9638906 38.54606 5.96575 38.548782 5.96875 38.550781 C 5.96975 38.557778 5.9706562 38.555595 5.9726562 38.558594 C 14.737316 46.727322 24.057332 53.865858 29.820312 58.076172 C 27.565103 65.033519 24.105319 76.578677 21.974609 87.958984 C 21.977609 87.960984 21.979375 87.961983 21.984375 87.958984 C 21.990375 87.961983 21.993194 87.956078 21.996094 87.955078 C 22.003094 87.956078 22.007866 87.954125 22.009766 87.953125 C 22.016766 87.954125 22.016531 87.954125 22.019531 87.953125 C 32.497441 82.141414 42.168135 75.48841 47.953125 71.308594 C 53.873275 75.603464 63.783426 82.459396 73.947266 88.001953 C 73.950266 87.999954 73.950219 87.995332 73.949219 87.990234 C 73.953219 87.986236 73.951172 87.979461 73.951172 87.976562 C 73.954172 87.970565 73.953125 87.966743 73.953125 87.964844 C 73.956125 87.958846 73.958984 87.957223 73.958984 87.953125 C 71.669554 76.192038 68.326252 64.939966 66.138672 58.146484 C 72.052872 53.843157 81.633422 46.537088 90.044922 38.583984 C 90.043922 38.580986 90.043109 38.579125 90.037109 38.578125 C 90.034109 38.572127 90.028391 38.571312 90.025391 38.570312 C 90.021391 38.566314 90.013419 38.565453 90.011719 38.564453 C 90.007719 38.560455 90.008906 38.556687 90.003906 38.554688 C 78.111466 37.097764 66.377304 36.800063 59.240234 36.78125 C 56.975133 29.827029 52.986923 18.457674 48.023438 8.0019531 z M 48.021484 18.292969 C 51.110304 25.766972 53.823267 33.067625 55.435547 38.017578 L 56.332031 40.773438 L 59.228516 40.78125 C 64.336616 40.794715 72.076045 41.124943 80.265625 41.757812 C 74.107185 47.009426 67.995863 51.846816 63.783203 54.912109 L 61.441406 56.617188 L 62.330078 59.371094 C 63.895448 64.2323 65.971861 71.694193 67.900391 79.677734 C 61.005081 75.444679 54.517604 71.128064 50.302734 68.070312 L 47.957031 66.371094 L 45.607422 68.066406 C 41.469542 71.056143 35.017477 75.335426 28.023438 79.634766 C 29.919048 71.768427 32.01918 64.262584 33.625 59.308594 L 34.517578 56.556641 L 32.179688 54.847656 C 28.056207 51.835128 21.990151 47.021389 15.738281 41.697266 C 23.804281 41.069284 31.589045 40.746188 36.796875 40.742188 L 39.693359 40.740234 L 40.595703 37.988281 C 42.186103 33.136911 44.890674 25.882296 48.021484 18.292969 z "
|
||||
transform="matrix(0,-1,-1.0003957,0,438.00245,441.36222)"
|
||||
id="path4170" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.4 KiB |
167
nymea-app/ui/images/slideshow.svg
Normal file
167
nymea-app/ui/images/slideshow.svg
Normal file
@ -0,0 +1,167 @@
|
||||
<?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="slideshow.svg">
|
||||
<defs
|
||||
id="defs4876" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4.6116859"
|
||||
inkscape:cx="-47.395774"
|
||||
inkscape:cy="41.875405"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g4778"
|
||||
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: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">
|
||||
<path
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.31292856;marker:none;enable-background:accumulate"
|
||||
d="m 158.25437,1028.3622 43.76848,0 c 0,0 -10.33241,-23.0909 -21.88306,-38.89082 -11.55061,15.79992 -21.88542,38.89082 -21.88542,38.89082 z"
|
||||
id="path4188"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
y="956.36218"
|
||||
x="137.99754"
|
||||
height="96"
|
||||
width="96.037987"
|
||||
id="rect4782"
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate" />
|
||||
<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;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:round;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 146.00195,1044.3633 2,0 66.02539,0 0,-68.00197 -68.02539,0 0,68.00197 z m 4,-4.002 0,-59.99802 60.02539,0 0,59.99802 -60.02539,0 z"
|
||||
id="rect4177"
|
||||
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;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:square;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 166.00977,964.36133 0,14 0,2.00195 4,0 0,-2.00195 0,-9.99805 52.02148,0 0,53.99802 0,2.002 4,0 0,-2.002 0,-57.99997 -60.02148,0 z"
|
||||
id="path4182"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.1 KiB |
160
nymea-app/ui/images/starred.svg
Normal file
160
nymea-app/ui/images/starred.svg
Normal file
@ -0,0 +1,160 @@
|
||||
<?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="starred.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.6199993"
|
||||
inkscape:cx="-4.3416438"
|
||||
inkscape:cy="69.76867"
|
||||
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="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="1,0"
|
||||
position="84,-8.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:guide
|
||||
position="92,-8.0000001"
|
||||
orientation="1,0"
|
||||
id="guide4760" />
|
||||
</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;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;enable-background:accumulate"
|
||||
d="m 429.99929,393.33773 c 0,0.004 -9.5e-4,0.007 -0.007,0.009 -0.001,0.007 -0.006,0.007 -0.009,0.009 -0.003,0.006 -0.008,0.008 -0.011,0.009 -0.003,0.006 -0.003,0.006 -0.007,0.009 -10.86428,5.06033 -21.9364,8.97057 -28.72087,11.19382 -0.006,7.31383 -0.28348,19.36 -1.77427,30.84126 -0.004,0.001 -0.006,0.002 -0.009,-0.002 -0.007,10e-4 -0.01,-0.004 -0.0129,-0.007 -0.007,-0.001 -0.009,-0.004 -0.011,-0.007 -0.007,-0.001 -0.006,-0.002 -0.009,-0.004 -8.17196,-8.76466 -15.31373,-18.08322 -19.52571,-23.8462 -6.9601,2.25521 -18.50812,5.71446 -29.89293,7.84517 -0.002,-0.003 -0.003,-0.004 0,-0.009 -0.003,-0.006 0.001,-0.01 0.002,-0.0129 -0.001,-0.007 0.001,-0.011 0.002,-0.0129 -0.001,-0.007 -0.001,-0.008 0,-0.011 5.81401,-10.47791 12.47128,-20.14765 16.65275,-25.93264 -4.29657,-5.92015 -11.1555,-15.83137 -16.70025,-25.99521 0.002,-0.003 0.006,-0.003 0.0111,-0.002 0.004,-0.004 0.01,-0.002 0.0129,-0.002 0.006,-0.003 0.011,-0.002 0.0129,-0.002 0.006,-0.003 0.007,-0.004 0.0111,-0.004 11.76574,2.28943 23.02338,5.63169 29.81955,7.81927 4.30503,-5.9142 11.61263,-15.49511 19.56888,-23.90661 0.003,0.001 0.006,0.003 0.007,0.009 0.006,0.003 0.006,0.008 0.007,0.011 0.004,0.004 0.006,0.0112 0.007,0.0129 0.004,0.004 0.007,0.004 0.009,0.009 1.4575,11.89244 1.75546,23.62651 1.77428,30.76358 6.95749,2.26527 18.3316,6.25356 28.7921,11.21756 z"
|
||||
id="path4170"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
@ -12,10 +12,14 @@ Page {
|
||||
|
||||
readonly property bool isEventBased: rule.eventDescriptors.count > 0 || rule.timeDescriptor.timeEventItems.count > 0
|
||||
readonly property bool isStateBased: (rule.stateEvaluator !== null || rule.timeDescriptor.calendarItems.count > 0) && !isEventBased
|
||||
readonly property bool actionsVisible: !isEmpty
|
||||
readonly property bool actionsVisible: true
|
||||
readonly property bool exitActionsVisible: actionsVisible && isStateBased
|
||||
readonly property bool hasActions: rule.actions.count > 0
|
||||
readonly property bool hasExitActions: rule.exitActions.count > 0
|
||||
readonly property bool isEmpty: !isEventBased && !isStateBased
|
||||
readonly property bool isEmpty: !isEventBased && !isStateBased && !hasActions
|
||||
|
||||
property string ruleIcon: Engine.tagsManager.tags.findRuleTag(rule.id, "icon").value
|
||||
property string ruleColor: Engine.tagsManager.tags.findRuleTag(rule.id, "color").value
|
||||
|
||||
signal accept();
|
||||
signal cancel();
|
||||
@ -223,6 +227,7 @@ Page {
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
Layout.topMargin: app.margins
|
||||
visible: !root.isEmpty
|
||||
|
||||
property bool showDetails: false
|
||||
|
||||
@ -268,6 +273,101 @@ Page {
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: ruleSettings.showDetails ? implicitHeight : 0
|
||||
opacity: ruleSettings.showDetails ? 1 : 0
|
||||
Behavior on Layout.preferredHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad} }
|
||||
Behavior on opacity { NumberAnimation {duration: 200; easing.type: Easing.InOutQuad } }
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("This is a scene" + root.ruleColor, root.ruleIcon)
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
checked: root.rule.executable
|
||||
onClicked: {
|
||||
root.rule.executable = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: colorsGrid
|
||||
Layout.fillWidth: true
|
||||
columns: (root.width / 10 < app.iconSize + app.margins) ? 5 : 10
|
||||
columnSpacing: app.margins
|
||||
rowSpacing: app.margins
|
||||
Layout.preferredHeight: opacity > 0 ? implicitHeight : 0
|
||||
opacity: Engine.jsonRpcClient.ensureServerVersion(1.6) && ruleSettings.showDetails && root.rule.executable ? 1 : 0
|
||||
Behavior on Layout.preferredHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad} }
|
||||
Behavior on opacity { NumberAnimation {duration: 200; easing.type: Easing.InOutQuad } }
|
||||
|
||||
Repeater {
|
||||
model: ["red", "orange", "yellow", "lime", "green", "aqua", "skyblue", "blue", "magenta", "purple"]
|
||||
|
||||
delegate: Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: app.iconSize + app.margins
|
||||
Rectangle {
|
||||
height: parent.height
|
||||
width: height
|
||||
color: "transparent"
|
||||
border.width: 2
|
||||
border.color: modelData === root.ruleColor ? app.guhAccent : "transparent"
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.ruleColor = modelData
|
||||
}
|
||||
}
|
||||
|
||||
ColorIcon {
|
||||
height: app.iconSize
|
||||
width: app.iconSize
|
||||
color: modelData
|
||||
name: "../images/" + (root.ruleIcon ? root.ruleIcon : "slideshow") + ".svg"
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
model: ["torch-on", "torch-off", "alarm-clock", "media-preview-start", "network-secure", "notification", "sensors", "shutter-10", "mail-mark-important", "eye"]
|
||||
|
||||
delegate: Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: app.iconSize + app.margins
|
||||
Rectangle {
|
||||
height: parent.height
|
||||
width: height
|
||||
color: "transparent"
|
||||
border.width: 2
|
||||
border.color: modelData === root.ruleIcon ? app.guhAccent : "transparent"
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.ruleIcon = modelData
|
||||
}
|
||||
}
|
||||
|
||||
ColorIcon {
|
||||
height: app.iconSize
|
||||
width: app.iconSize
|
||||
color: root.ruleColor
|
||||
name: "../images/" + modelData + ".svg"
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider { visible: !root.isStateBased }
|
||||
@ -277,7 +377,7 @@ Page {
|
||||
Layout.margins: app.margins
|
||||
font.pixelSize: app.mediumFont
|
||||
wrapMode: Text.WordWrap
|
||||
text: eventsRepeater.count === 0 && timeEventRepeater.count === 0 ?
|
||||
text: eventsRepeater.count === 0 && timeEventRepeater.count === 0 && actionsRepeater.count === 0 ?
|
||||
qsTr("Execute actions when something happens.") :
|
||||
qsTr("When any of these events happen...")
|
||||
visible: !root.isStateBased
|
||||
@ -425,9 +525,10 @@ Page {
|
||||
ThinDivider { visible: root.actionsVisible }
|
||||
|
||||
Label {
|
||||
text: root.isStateBased ?
|
||||
(root.rule.stateEvaluator === 0 ? qsTr("...come true, execute those actions:") : qsTr("...comes true, execute those actions:")) :
|
||||
qsTr("...execute those actions:")
|
||||
text: root.isEmpty ? qsTr("Create a scene.") :
|
||||
root.isStateBased ?
|
||||
(root.rule.stateEvaluator === 0 ? qsTr("...come true, execute those actions:") : qsTr("...comes true, execute those actions:")) :
|
||||
qsTr("...execute those actions:")
|
||||
font.pixelSize: app.mediumFont
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
@ -436,6 +537,17 @@ Page {
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: app.smallFont
|
||||
font.italic: true
|
||||
text: qsTr("Just pick some actions which will be executed when the scene is activated. Scenes are like any other magic except they can also be activated manually.")
|
||||
visible: root.isEmpty
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: actionsRepeater
|
||||
model: root.actionsVisible ? root.rule.actions : null
|
||||
@ -449,8 +561,12 @@ Page {
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: actionsRepeater.count == 0 ? qsTr("Add an action...") : qsTr("Add another action...")
|
||||
text: root.isEmpty ? qsTr("Configure...") :
|
||||
actionsRepeater.count == 0 ? qsTr("Add an action...") : qsTr("Add another action...")
|
||||
onClicked: {
|
||||
if (root.isEmpty) {
|
||||
root.rule.executable = true;
|
||||
}
|
||||
var page = pageStack.push(ruleActionQuestionPageComponent, {exitAction: false});
|
||||
}
|
||||
visible: root.actionsVisible
|
||||
|
||||
@ -37,7 +37,7 @@ Page {
|
||||
Component.onCompleted: {
|
||||
actualModel.clear()
|
||||
for (var i = 0; i < actionModel.count; i++) {
|
||||
ifaceFilterModel.filterInterface = actionModel.get(i).interfaceName;
|
||||
ifaceFilterModel.shownInterfaces = [actionModel.get(i).interfaceName];
|
||||
if (actionModel.get(i).interfaceName === "" || ifaceFilterModel.count > 0) {
|
||||
actualModel.append(actionModel.get(i))
|
||||
}
|
||||
@ -253,7 +253,7 @@ Page {
|
||||
model: DevicesProxy {
|
||||
id: lightsModel
|
||||
devices: Engine.deviceManager.devices
|
||||
filterInterface: "light"
|
||||
shownInterfaces: ["light"]
|
||||
}
|
||||
delegate: CheckDelegate {
|
||||
width: parent.width
|
||||
@ -338,7 +338,7 @@ Page {
|
||||
model: DevicesProxy {
|
||||
id: notificationsModel
|
||||
devices: Engine.deviceManager.devices
|
||||
filterInterface: "notifications"
|
||||
shownInterfaces: ["notifications"]
|
||||
}
|
||||
delegate: CheckDelegate {
|
||||
width: parent.width
|
||||
|
||||
33
nymea-app/ui/mainviews/DevicesPage.qml
Normal file
33
nymea-app/ui/mainviews/DevicesPage.qml
Normal file
@ -0,0 +1,33 @@
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property alias count: interfacesGridView.count
|
||||
property alias model: interfacesGridView.model
|
||||
|
||||
GridView {
|
||||
id: interfacesGridView
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
|
||||
readonly property int minTileWidth: 180
|
||||
readonly property int minTileHeight: 240
|
||||
readonly property int tilesPerRow: root.width / minTileWidth
|
||||
|
||||
model: InterfacesModel {
|
||||
id: interfacesModel
|
||||
devices: Engine.deviceManager.devices
|
||||
}
|
||||
cellWidth: width / tilesPerRow
|
||||
cellHeight: Math.max(cellWidth, minTileHeight)
|
||||
delegate: DevicesPageDelegate {
|
||||
width: interfacesGridView.cellWidth
|
||||
height: interfacesGridView.cellHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
264
nymea-app/ui/mainviews/DevicesPageDelegate.qml
Normal file
264
nymea-app/ui/mainviews/DevicesPageDelegate.qml
Normal file
@ -0,0 +1,264 @@
|
||||
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"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
Pane {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
Material.elevation: 1
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -app.iconSize
|
||||
spacing: app.margins
|
||||
ColorIcon {
|
||||
height: app.iconSize * 2
|
||||
width: height
|
||||
color: app.guhAccent
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
name: interfaceToIcon(model.name)
|
||||
}
|
||||
|
||||
Label {
|
||||
text: interfaceToString(model.name).toUpperCase()
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
var page;
|
||||
switch (model.name) {
|
||||
case "light":
|
||||
page = "LightsDeviceListPage.qml"
|
||||
break;
|
||||
default:
|
||||
page = "GenericDeviceListPage.qml"
|
||||
}
|
||||
if (model.name === "uncategorized") {
|
||||
pageStack.push(Qt.resolvedUrl("../devicelistpages/" + page), {hiddenInterfaces: app.supportedInterfaces})
|
||||
} else {
|
||||
pageStack.push(Qt.resolvedUrl("../devicelistpages/" + page), {shownInterfaces: [model.name]})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DevicesProxy {
|
||||
id: devicesProxy
|
||||
devices: Engine.deviceManager.devices
|
||||
shownInterfaces: [model.name]
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: inlineControlPane
|
||||
anchors { left: parent.left; bottom: parent.bottom; right: parent.right; margins: app.margins / 2 }
|
||||
height: app.iconSize + app.margins * 2
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
// color: app.guhAccent
|
||||
color: "black"
|
||||
opacity: .05
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: inlineControlLoader
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: app.margins
|
||||
rightMargin: app.margins
|
||||
topMargin: app.margins / 2
|
||||
bottomMargin: app.margins / 2
|
||||
}
|
||||
sourceComponent: {
|
||||
switch (model.name) {
|
||||
case "sensor":
|
||||
case "weather":
|
||||
return labelComponent;
|
||||
|
||||
case "light":
|
||||
case "media":
|
||||
case "garagegate":
|
||||
case "shutter":
|
||||
case "blind":
|
||||
return buttonComponent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: buttonComponent
|
||||
MouseArea {
|
||||
onClicked: {
|
||||
switch (model.name) {
|
||||
case "light":
|
||||
if (devicesProxy.count == 1) {
|
||||
var device = devicesProxy.get(0);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("power")
|
||||
var actionType = deviceClass.actionTypes.findByName("power")
|
||||
var params = [];
|
||||
var param1 = {};
|
||||
param1["paramTypeId"] = actionType.paramTypes.get(0).id;
|
||||
param1["value"] = !device.states.getState(stateType.id).value;
|
||||
params.push(param1)
|
||||
Engine.deviceManager.executeAction(device.id, actionType.id, params)
|
||||
} else {
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("power");
|
||||
|
||||
var params = [];
|
||||
var param1 = {};
|
||||
param1["paramTypeId"] = actionType.paramTypes.get(0).id;
|
||||
param1["value"] = false;
|
||||
params.push(param1)
|
||||
Engine.deviceManager.executeAction(device.id, actionType.id, params)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "media":
|
||||
var device = devicesProxy.get(0)
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("playbackStatus");
|
||||
var state = device.states.getState(stateType.id)
|
||||
|
||||
var actionName
|
||||
switch (state.value) {
|
||||
case "PLAYING":
|
||||
actionName = "pause";
|
||||
break;
|
||||
case "PAUSED":
|
||||
actionName = "play";
|
||||
break;
|
||||
}
|
||||
var actionTypeId = deviceClass.actionTypes.findByName(actionName).id;
|
||||
|
||||
print("executing", device, device.id, actionTypeId, actionName, deviceClass.actionTypes)
|
||||
|
||||
Engine.deviceManager.executeAction(device.id, actionTypeId)
|
||||
case "garagegate":
|
||||
case "shutter":
|
||||
case "blind":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("close");
|
||||
Engine.deviceManager.executeAction(device.id, actionType.id)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
id: label
|
||||
Layout.fillWidth: true
|
||||
text: {
|
||||
switch (model.name) {
|
||||
case "media":
|
||||
return devicesProxy.get(0).name;
|
||||
case "light":
|
||||
var count = 0;
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("power")
|
||||
if (device.states.getState(stateType.id).value === true) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count === 0 ? qsTr("All off") : qsTr("%1 on").arg(count)
|
||||
case "garagegate":
|
||||
var count = 0;
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("state");
|
||||
if (device.states.getState(stateType.id).value !== "closed") {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count === 0 ? qsTr("All closed") : qsTr("%1 open").arg(count)
|
||||
case "shutter":
|
||||
return qsTr("%1 installed").arg(devicesProxy.count)
|
||||
}
|
||||
console.warn("Unhandled interface", model.name)
|
||||
}
|
||||
font.pixelSize: app.smallFont
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
ColorIcon {
|
||||
id: icon
|
||||
width: app.largeFont
|
||||
height: width
|
||||
color: app.guhAccent
|
||||
Layout.alignment: Qt.AlignRight
|
||||
name: {
|
||||
switch (model.name) {
|
||||
case "media":
|
||||
var device = devicesProxy.get(0)
|
||||
var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("playbackStatus");
|
||||
var state = device.states.getState(stateType.id)
|
||||
return state.value === "PLAYING" ? "../images/media-playback-pause.svg" :
|
||||
state.value === "PAUSED" ? "../images/media-playback-start.svg" :
|
||||
""
|
||||
case "light":
|
||||
return "../images/system-shutdown.svg"
|
||||
case "garagegate":
|
||||
case "shutter":
|
||||
case "blind":
|
||||
return "../images/down.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: labelComponent
|
||||
ColumnLayout {
|
||||
property var device: devicesProxy.get(0)
|
||||
property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
|
||||
property var state: deviceClass ? device.states.getState(deviceClass.stateTypes.findByName("temperature").id) : null
|
||||
|
||||
Label {
|
||||
text: parent.device.name
|
||||
font.pixelSize: app.smallFont
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Label {
|
||||
font.pixelSize: app.largeFont
|
||||
color: app.guhAccent
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: {
|
||||
if (devicesProxy.count > 0) {
|
||||
var stateName;
|
||||
// switch (model.name) {
|
||||
// case "sensor":
|
||||
// }
|
||||
return parent.state.value + "°C";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
146
nymea-app/ui/mainviews/FavoritesView.qml
Normal file
146
nymea-app/ui/mainviews/FavoritesView.qml
Normal file
@ -0,0 +1,146 @@
|
||||
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"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property int count: tagsProxy.count
|
||||
|
||||
TagsProxyModel {
|
||||
id: tagsProxy
|
||||
filterTagId: "favorites"
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: gridView
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
readonly property int minTileWidth: 180
|
||||
readonly property int minTileHeight: 240
|
||||
readonly property int tilesPerRow: root.width / minTileWidth
|
||||
cellWidth: gridView.width / tilesPerRow
|
||||
cellHeight: cellWidth
|
||||
|
||||
model: tagsProxy
|
||||
delegate: favoritesDelegateComponent
|
||||
MouseArea {
|
||||
id: dndArea
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
|
||||
property int from: -1
|
||||
property int to: -1
|
||||
property int index: gridView.indexAt(mouseX, mouseY) // Item underneath cursor
|
||||
property var dndDelegate: null
|
||||
|
||||
property int dx
|
||||
property int dy
|
||||
|
||||
onPressAndHold: {
|
||||
//currentId = icons.get(newIndex = index).gridId
|
||||
preventStealing = true;
|
||||
from = index;
|
||||
print("pressandHold on", index)
|
||||
var tag = tagsProxy.get(index);
|
||||
var originalDelegate = gridView.itemAt(mouseX, mouseY)
|
||||
dndDelegate = favoritesDelegateComponent.createObject(dndArea, {deviceId: tag.deviceId, ruleId: tag.ruleId, x: originalDelegate.x, y: originalDelegate.y})
|
||||
dx = mouseX - originalDelegate.x;
|
||||
dy = mouseY - originalDelegate.y;
|
||||
}
|
||||
onReleased: {
|
||||
preventStealing = false;
|
||||
from = -1;
|
||||
dndDelegate.destroy();
|
||||
}
|
||||
|
||||
onPositionChanged: {
|
||||
if (dndDelegate) {
|
||||
dndDelegate.x = mouseX - dx
|
||||
dndDelegate.y = mouseY - dy
|
||||
}
|
||||
|
||||
if (dndArea.from >= 0 && to != index && from != index) {
|
||||
to = index;
|
||||
print("should move", from, "to", to)
|
||||
for (var i = 0; i < tagsProxy.count; i++) {
|
||||
if (i < Math.min(from, to) || i > Math.max(from, to)) {
|
||||
// outside the range... don't touch
|
||||
continue;
|
||||
}
|
||||
var newIdx;
|
||||
if (i == from) {
|
||||
newIdx = to;
|
||||
} else {
|
||||
if (from < to) {
|
||||
// item is moved down the list
|
||||
newIdx = i - 1;
|
||||
} else {
|
||||
newIdx = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
var tag = tagsProxy.get(i);
|
||||
Engine.tagsManager.tagDevice(tag.deviceId, tag.tagId, newIdx);
|
||||
}
|
||||
from = index;
|
||||
}
|
||||
// tagsProxy.move(newIndex, newIndex = index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: favoritesDelegateComponent
|
||||
Item {
|
||||
id: delegateRoot
|
||||
property string deviceId: model.deviceId
|
||||
property string ruleId: model.ruleId
|
||||
readonly property var device: Engine.deviceManager.devices.getDevice(deviceId)
|
||||
readonly property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
|
||||
|
||||
visible: index !== undefined && index !== dndArea.from
|
||||
width: gridView.cellWidth
|
||||
height: gridView.cellHeight
|
||||
|
||||
Pane {
|
||||
id: pane
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
Material.elevation: 1
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - app.margins * 2
|
||||
spacing: app.margins
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredWidth: app.iconSize * 2
|
||||
Layout.preferredHeight: width
|
||||
name: app.interfacesToIcon(delegateRoot.deviceClass.interfaces)
|
||||
color: app.guhAccent
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: delegateRoot.device.name
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
pageStack.push(Qt.resolvedUrl("../devicepages/" + app.interfaceListToDevicePage(delegateRoot.deviceClass.interfaces)), {device: delegateRoot.device})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
79
nymea-app/ui/mainviews/ScenesView.qml
Normal file
79
nymea-app/ui/mainviews/ScenesView.qml
Normal file
@ -0,0 +1,79 @@
|
||||
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"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property int count: interfacesGridView.count
|
||||
|
||||
GridView {
|
||||
id: interfacesGridView
|
||||
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: RulesFilterModel {
|
||||
rules: Engine.ruleManager.rules
|
||||
filterExecutable: true
|
||||
}
|
||||
cellWidth: width / tilesPerRow
|
||||
cellHeight: Math.max(cellWidth, minTileHeight)
|
||||
delegate: Item {
|
||||
id: scenesDelegate
|
||||
width: interfacesGridView.cellWidth
|
||||
height: interfacesGridView.cellHeight
|
||||
|
||||
property var colorTag: Engine.tagsManager.tags.findRuleTag(model.id, "color")
|
||||
property var iconTag: Engine.tagsManager.tags.findRuleTag(model.id, "icon")
|
||||
Connections {
|
||||
target: Engine.tagsManager.tags
|
||||
onCountChanged: {
|
||||
colorTag = Engine.tagsManager.tags.findRuleTag(model.id, "color")
|
||||
iconTag = Engine.tagsManager.tags.findRuleTag(model.id, "icon")
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
Material.elevation: 1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
Engine.ruleManager.executeActions(model.id)
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
spacing: app.margins
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize * 2
|
||||
Layout.preferredWidth: height
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
name: scenesDelegate.iconTag ? "../images/" + scenesDelegate.iconTag.value + ".svg" : "../images/slideshow.svg";
|
||||
color: scenesDelegate.colorTag ? scenesDelegate.colorTag.value : app.guhAccent;
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: model.name
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user