rework the generic device page

This commit is contained in:
Michael Zanetti 2018-12-10 00:29:14 +01:00
parent eaaf7300cd
commit f1d29034b0
26 changed files with 1126 additions and 343 deletions

View File

@ -239,7 +239,7 @@ void DeviceManager::getConfiguredDevicesResponse(const QVariantMap &params)
value.convert(QVariant::Int); value.convert(QVariant::Int);
} }
device->setStateValue(stateTypeId, value); device->setStateValue(stateTypeId, value);
qDebug() << "Set device state value:" << device->stateValue(stateTypeId) << value; // qDebug() << "Set device state value:" << device->stateValue(stateTypeId) << value;
} }
devices()->addDevice(device); devices()->addDevice(device);
} }
@ -297,7 +297,7 @@ void DeviceManager::editDeviceResponse(const QVariantMap &params)
void DeviceManager::executeActionResponse(const QVariantMap &params) void DeviceManager::executeActionResponse(const QVariantMap &params)
{ {
qDebug() << "Execute Action response" << params; qDebug() << "Execute Action response" << params;
emit executeActionReply(params.value("params").toMap()); emit executeActionReply(params);
} }
void DeviceManager::savePluginConfig(const QUuid &pluginId) void DeviceManager::savePluginConfig(const QUuid &pluginId)
@ -366,7 +366,7 @@ void DeviceManager::editDevice(const QUuid &deviceId, const QString &name)
m_jsonClient->sendCommand("Devices.EditDevice", params, this, "editDeviceResponse"); m_jsonClient->sendCommand("Devices.EditDevice", params, this, "editDeviceResponse");
} }
void DeviceManager::executeAction(const QUuid &deviceId, const QUuid &actionTypeId, const QVariantList &params) int DeviceManager::executeAction(const QUuid &deviceId, const QUuid &actionTypeId, const QVariantList &params)
{ {
qDebug() << "JsonRpc: execute action " << deviceId.toString() << actionTypeId.toString() << params; qDebug() << "JsonRpc: execute action " << deviceId.toString() << actionTypeId.toString() << params;
QVariantMap p; QVariantMap p;
@ -377,5 +377,5 @@ void DeviceManager::executeAction(const QUuid &deviceId, const QUuid &actionType
} }
qDebug() << "Params:" << p; qDebug() << "Params:" << p;
m_jsonClient->sendCommand("Actions.ExecuteAction", p, this, "executeActionResponse"); return m_jsonClient->sendCommand("Actions.ExecuteAction", p, this, "executeActionResponse");
} }

View File

@ -69,7 +69,7 @@ public:
Q_INVOKABLE void confirmPairing(const QUuid &pairingTransactionId, const QString &secret = QString()); Q_INVOKABLE void confirmPairing(const QUuid &pairingTransactionId, const QString &secret = QString());
Q_INVOKABLE void removeDevice(const QUuid &deviceId, RemovePolicy policy = RemovePolicyNone); Q_INVOKABLE void removeDevice(const QUuid &deviceId, RemovePolicy policy = RemovePolicyNone);
Q_INVOKABLE void editDevice(const QUuid &deviceId, const QString &name); Q_INVOKABLE void editDevice(const QUuid &deviceId, const QString &name);
Q_INVOKABLE void executeAction(const QUuid &deviceId, const QUuid &actionTypeId, const QVariantList &params = QVariantList()); Q_INVOKABLE int executeAction(const QUuid &deviceId, const QUuid &actionTypeId, const QVariantList &params = QVariantList());
private: private:
Q_INVOKABLE void notificationReceived(const QVariantMap &data); Q_INVOKABLE void notificationReceived(const QVariantMap &data);
@ -98,6 +98,7 @@ signals:
void editDeviceReply(const QVariantMap &params); void editDeviceReply(const QVariantMap &params);
void executeActionReply(const QVariantMap &params); void executeActionReply(const QVariantMap &params);
void fetchingDataChanged(); void fetchingDataChanged();
void notificationReceived(const QString &deviceId, const QString &eventTypeId, const QVariantList &params);
private: private:
Vendors *m_vendors; Vendors *m_vendors;

View File

@ -91,7 +91,7 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
version = txtRecord.second; version = txtRecord.second;
} }
} }
// qDebug() << "avahi service entry added" << serverName << uuid << sslEnabled; qDebug() << "avahi service entry added" << serverName << uuid << sslEnabled;
DiscoveryDevice* device = m_discoveryModel->find(uuid); DiscoveryDevice* device = m_discoveryModel->find(uuid);

View File

@ -56,15 +56,15 @@ void JsonRpcClient::registerNotificationHandler(JsonHandler *handler, const QStr
m_notificationHandlers.insert(handler->nameSpace(), qMakePair<JsonHandler*, QString>(handler, method)); m_notificationHandlers.insert(handler->nameSpace(), qMakePair<JsonHandler*, QString>(handler, method));
} }
void JsonRpcClient::sendCommand(const QString &method, const QVariantMap &params, QObject *caller, const QString &callbackMethod) int JsonRpcClient::sendCommand(const QString &method, const QVariantMap &params, QObject *caller, const QString &callbackMethod)
{ {
JsonRpcReply *reply = createReply(method, params, caller, callbackMethod); JsonRpcReply *reply = createReply(method, params, caller, callbackMethod);
m_replies.insert(reply->commandId(), reply); m_replies.insert(reply->commandId(), reply);
sendRequest(reply->requestMap()); sendRequest(reply->requestMap());
return reply->commandId();
} }
void JsonRpcClient::sendCommand(const QString &method, QObject *caller, const QString &callbackMethod) int JsonRpcClient::sendCommand(const QString &method, QObject *caller, const QString &callbackMethod)
{ {
return sendCommand(method, QVariantMap(), caller, callbackMethod); return sendCommand(method, QVariantMap(), caller, callbackMethod);
} }

View File

@ -60,8 +60,8 @@ public:
void registerNotificationHandler(JsonHandler *handler, const QString &method); void registerNotificationHandler(JsonHandler *handler, const QString &method);
void sendCommand(const QString &method, const QVariantMap &params, QObject *caller = nullptr, const QString &callbackMethod = QString()); int sendCommand(const QString &method, const QVariantMap &params, QObject *caller = nullptr, const QString &callbackMethod = QString());
void sendCommand(const QString &method, QObject *caller = nullptr, const QString &callbackMethod = QString()); int sendCommand(const QString &method, QObject *caller = nullptr, const QString &callbackMethod = QString());
void setConnection(NymeaConnection *connection); void setConnection(NymeaConnection *connection);
bool connected() const; bool connected() const;

View File

@ -55,6 +55,7 @@
#include "ruletemplates/ruleactiontemplate.h" #include "ruletemplates/ruleactiontemplate.h"
#include "ruletemplates/ruleactionparamtemplate.h" #include "ruletemplates/ruleactionparamtemplate.h"
#include "connection/awsclient.h" #include "connection/awsclient.h"
#include "models/devicemodel.h"
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
@ -114,6 +115,8 @@ void registerQmlTypes() {
qmlRegisterType<DeviceClassesProxy>(uri, 1, 0, "DeviceClassesProxy"); qmlRegisterType<DeviceClassesProxy>(uri, 1, 0, "DeviceClassesProxy");
qmlRegisterType<DeviceDiscovery>(uri, 1, 0, "DeviceDiscovery"); qmlRegisterType<DeviceDiscovery>(uri, 1, 0, "DeviceDiscovery");
qmlRegisterType<DeviceModel>(uri, 1, 0, "DeviceModel");
qmlRegisterUncreatableType<RuleManager>(uri, 1, 0, "RuleManager", "Get it from the Engine"); qmlRegisterUncreatableType<RuleManager>(uri, 1, 0, "RuleManager", "Get it from the Engine");
qmlRegisterUncreatableType<Rules>(uri, 1, 0, "Rules", "Get it from RuleManager"); qmlRegisterUncreatableType<Rules>(uri, 1, 0, "Rules", "Get it from RuleManager");
qmlRegisterUncreatableType<Rule>(uri, 1, 0, "Rule", "Get it from Rules"); qmlRegisterUncreatableType<Rule>(uri, 1, 0, "Rule", "Get it from Rules");

View File

@ -83,7 +83,8 @@ SOURCES += \
configuration/nymeaconfiguration.cpp \ configuration/nymeaconfiguration.cpp \
models/mqttpolicies.cpp \ models/mqttpolicies.cpp \
configuration/mqttpolicy.cpp \ configuration/mqttpolicy.cpp \
configuration/mqttpolicies.cpp configuration/mqttpolicies.cpp \
models/devicemodel.cpp
HEADERS += \ HEADERS += \
engine.h \ engine.h \
@ -145,7 +146,8 @@ HEADERS += \
configuration/serverconfigurations.h \ configuration/serverconfigurations.h \
configuration/nymeaconfiguration.h \ configuration/nymeaconfiguration.h \
configuration/mqttpolicy.h \ configuration/mqttpolicy.h \
configuration/mqttpolicies.h configuration/mqttpolicies.h \
models/devicemodel.h
unix { unix {
target.path = /usr/lib target.path = /usr/lib

View File

@ -0,0 +1,164 @@
#include "devicemodel.h"
#include "types/statetype.h"
DeviceModel::DeviceModel(QObject *parent) : QAbstractListModel(parent)
{
}
int DeviceModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_list.count();
}
QVariant DeviceModel::data(const QModelIndex &index, int role) const
{
if (role == RoleId) {
return m_list.at(index.row());
}
if (role == RoleType) {
StateType* stateType = m_device->deviceClass()->stateTypes()->getStateType(m_list.at(index.row()));
if (stateType) {
return TypeStateType;
}
ActionType* actionType = m_device->deviceClass()->actionTypes()->getActionType(m_list.at(index.row()));
if (actionType) {
return TypeActionType;
}
EventType* eventType = m_device->deviceClass()->eventTypes()->getEventType(m_list.at(index.row()));
if (eventType) {
return TypeEventType;
}
}
if (role == RoleDisplayName) {
StateType* stateType = m_device->deviceClass()->stateTypes()->getStateType(m_list.at(index.row()));
if (stateType) {
return stateType->displayName();
}
ActionType* actionType = m_device->deviceClass()->actionTypes()->getActionType(m_list.at(index.row()));
if (actionType) {
return actionType->displayName();
}
EventType* eventType = m_device->deviceClass()->eventTypes()->getEventType(m_list.at(index.row()));
if (eventType) {
return eventType->displayName();
}
}
if (role == RoleWritable) {
ActionType* actionType = m_device->deviceClass()->actionTypes()->getActionType(m_list.at(index.row()));
return actionType != nullptr;
}
return QVariant();
}
QHash<int, QByteArray> DeviceModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles.insert(RoleId, "id");
roles.insert(RoleType, "type");
roles.insert(RoleDisplayName, "displayName");
roles.insert(RoleWritable, "writable");
return roles;
}
QVariant DeviceModel::getData(int index, int role) const
{
if (index < 0 || index >= m_list.count()) {
return QVariant();
}
return data(this->index(index), role);
}
Device *DeviceModel::device() const
{
return m_device;
}
void DeviceModel::setDevice(Device *device)
{
if (m_device != device) {
m_device = device;
emit deviceChanged();
updateList();
}
}
bool DeviceModel::showStates() const
{
return m_showStates;
}
void DeviceModel::setShowStates(bool showStates)
{
if (m_showStates != showStates) {
m_showStates = showStates;
emit showStatesChanged();
updateList();
}
}
bool DeviceModel::showActions() const
{
return m_showActions;
}
void DeviceModel::setShowActions(bool showActions)
{
if (m_showActions != showActions) {
m_showActions = showActions;
emit showActionsChanged();
updateList();
}
}
bool DeviceModel::showEvents() const
{
return m_showEvents;
}
void DeviceModel::setShowEvents(bool showEvents)
{
if (m_showEvents != showEvents) {
m_showEvents = showEvents;
emit showEventsChanged();
updateList();
}
}
void DeviceModel::updateList()
{
if (!m_device) {
beginResetModel();
m_list.clear();
endResetModel();
emit countChanged();
return;
}
beginResetModel();
m_list.clear();
if (m_showStates) {
for (int i = 0; i < m_device->deviceClass()->stateTypes()->rowCount(); i++) {
m_list.append(m_device->deviceClass()->stateTypes()->get(i)->id());
}
}
if (m_showActions) {
for (int i = 0; i < m_device->deviceClass()->actionTypes()->rowCount(); i++) {
if (!m_list.contains(m_device->deviceClass()->actionTypes()->get(i)->id())) {
m_list.append(m_device->deviceClass()->actionTypes()->get(i)->id());
}
}
}
if (m_showEvents) {
for (int i = 0; i < m_device->deviceClass()->eventTypes()->rowCount(); i++) {
if (!m_list.contains(m_device->deviceClass()->eventTypes()->get(i)->id())) {
m_list.append(m_device->deviceClass()->eventTypes()->get(i)->id());
}
}
}
endResetModel();
emit countChanged();
}

View File

@ -0,0 +1,77 @@
#ifndef DEVICEMODEL_H
#define DEVICEMODEL_H
#include <QObject>
#include "types/device.h"
#include "types/deviceclass.h"
class DeviceModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
Q_PROPERTY(Device* device READ device WRITE setDevice NOTIFY deviceChanged)
Q_PROPERTY(bool showStates READ showStates WRITE setShowStates NOTIFY showStatesChanged)
Q_PROPERTY(bool showActions READ showActions WRITE setShowActions NOTIFY showActionsChanged)
Q_PROPERTY(bool showEvents READ showEvents WRITE setShowEvents NOTIFY showEventsChanged)
public:
enum Roles {
RoleId,
RoleType,
RoleDisplayName,
RoleWritable
};
Q_ENUM(Roles)
enum Type {
TypeStateType,
TypeActionType,
TypeEventType
};
Q_ENUM(Type)
explicit DeviceModel(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;
Q_INVOKABLE QVariant getData(int index, int role) const;
Device* device() const;
void setDevice(Device *device);
bool showStates() const;
void setShowStates(bool showStates);
bool showActions() const;
void setShowActions(bool showActions);
bool showEvents() const;
void setShowEvents(bool showEvents);
signals:
void deviceChanged();
void countChanged();
bool showStatesChanged();
bool showActionsChanged();
bool showEventsChanged();
private:
void updateList();
private:
Device *m_device = nullptr;
bool m_showStates = true;
bool m_showActions = true;
bool m_showEvents = true;
QList<QString> m_list;
};
#endif // DEVICEMODEL_H

View File

@ -104,6 +104,11 @@ void LogsModelNg::setTypeIds(const QStringList &typeIds)
if (m_typeIds != typeIds) { if (m_typeIds != typeIds) {
m_typeIds = typeIds; m_typeIds = typeIds;
emit typeIdsChanged(); emit typeIdsChanged();
beginResetModel();
qDeleteAll(m_list);
m_list.clear();
endResetModel();
fetchMore();
} }
} }

View File

@ -35,7 +35,7 @@ class State : public QObject
Q_PROPERTY(QVariant value READ value NOTIFY valueChanged) Q_PROPERTY(QVariant value READ value NOTIFY valueChanged)
public: public:
explicit State(const QUuid &deviceId, const QUuid &stateTypeId, const QVariant &value, QObject *parent = 0); explicit State(const QUuid &deviceId, const QUuid &stateTypeId, const QVariant &value, QObject *parent = nullptr);
QUuid deviceId() const; QUuid deviceId() const;
QUuid stateTypeId() const; QUuid stateTypeId() const;

View File

@ -36,7 +36,9 @@ QList<StateType *> StateTypes::stateTypes()
StateType *StateTypes::get(int index) const StateType *StateTypes::get(int index) const
{ {
qDebug() << "returning" << m_stateTypes.at(index); if (index < 0 || index >= m_stateTypes.count()) {
return nullptr;
}
return m_stateTypes.at(index); return m_stateTypes.at(index);
} }

View File

@ -60,7 +60,6 @@
<file>ui/customviews/ExtendedVolumeController.qml</file> <file>ui/customviews/ExtendedVolumeController.qml</file>
<file>ui/devicepages/MediaDevicePage.qml</file> <file>ui/devicepages/MediaDevicePage.qml</file>
<file>ui/devicepages/ButtonDevicePage.qml</file> <file>ui/devicepages/ButtonDevicePage.qml</file>
<file>ui/devicepages/GenericDeviceStateDetailsPage.qml</file>
<file>ui/devicepages/GenericDevicePage.qml</file> <file>ui/devicepages/GenericDevicePage.qml</file>
<file>ui/devicepages/WeatherDevicePagePre110.qml</file> <file>ui/devicepages/WeatherDevicePagePre110.qml</file>
<file>ui/devicepages/WeatherDevicePagePost110.qml</file> <file>ui/devicepages/WeatherDevicePagePost110.qml</file>
@ -137,5 +136,7 @@
<file>ui/system/MqttBrokerSettingsPage.qml</file> <file>ui/system/MqttBrokerSettingsPage.qml</file>
<file>ui/system/ServerConfigurationDialog.qml</file> <file>ui/system/ServerConfigurationDialog.qml</file>
<file>ui/system/MqttPolicyPage.qml</file> <file>ui/system/MqttPolicyPage.qml</file>
<file>ui/devicepages/DeviceLogPage.qml</file>
<file>ui/components/Led.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -123,7 +123,7 @@ Page {
print("should setup", deviceClass.name, deviceClass.setupMethod, deviceClass.createMethods, deviceClass["discoveryParamTypes"].count) print("should setup", deviceClass.name, deviceClass.setupMethod, deviceClass.createMethods, deviceClass["discoveryParamTypes"].count)
} }
swipe.enabled: true// deviceClass.createMethods.indexOf("CreateMethodUser") !== -1 swipe.enabled: deviceClass.createMethods.indexOf("CreateMethodUser") !== -1
swipe.right: MouseArea { swipe.right: MouseArea {
height: deviceClassDelegate.height height: deviceClassDelegate.height
width: height width: height

View File

@ -74,9 +74,10 @@ Item {
} }
Engine { Engine {
id: engine id: engineObject
} }
property alias _engine: engine readonly property Engine engine: engineObject
readonly property Engine _engine: engineObject // In case a child cannot use "engine"
property int connectionTabIndex: index property int connectionTabIndex: index
onConnectionTabIndexChanged: tabSettings.lastConnectedHost = engine.connection.url onConnectionTabIndexChanged: tabSettings.lastConnectedHost = engine.connection.url

View File

@ -4,13 +4,28 @@ Item {
id: root id: root
property color color: actionState property color color: actionState
property Component touchDelegate: null
property bool pressed: mouseArea.pressed property bool pressed: mouseArea.pressed
property bool hovered: mouseArea.containsMouse property bool hovered: mouseArea.containsMouse
property variant lights
property bool active: true property bool active: true
property Component touchDelegate: Rectangle {
height: 15
width: height
radius: height / 2
color: app.accentColor
Rectangle {
color: root.hovered || root.pressed ? "#11000000" : "transparent"
anchors.centerIn: parent
height: 30
width: height
radius: width / 2
Behavior on color { ColorAnimation { duration: 200 } }
}
}
function calculateXy(color) { function calculateXy(color) {
if (!color.hasOwnProperty("r")) { if (!color.hasOwnProperty("r")) {
// Qt 4.8's color doesn't have r,g,b properties // Qt 4.8's color doesn't have r,g,b properties
@ -189,7 +204,7 @@ Item {
x: item ? Math.max(0, Math.min(point.x - width * .5, parent.width - item.width)) : 0 x: item ? Math.max(0, Math.min(point.x - width * .5, parent.width - item.width)) : 0
y: item ? Math.max(0, Math.min(point.y - height * .5, parent.height - item.height)) : 0 y: item ? Math.max(0, Math.min(point.y - height * .5, parent.height - item.height)) : 0
sourceComponent: root.touchDelegate sourceComponent: root.touchDelegate
visible: mouseArea.draggedItem != touchDelegateLoader && root.active visible: mouseArea.draggedItem !== touchDelegateLoader && root.active
// Behavior on x { // Behavior on x {
// enabled: !mouseArea.pressed // enabled: !mouseArea.pressed
// NumberAnimation {} // NumberAnimation {}
@ -199,6 +214,6 @@ Item {
Loader { Loader {
id: dndItem id: dndItem
sourceComponent: root.touchDelegate sourceComponent: root.touchDelegate
visible: mouseArea.draggedItem != undefined && root.active visible: mouseArea.draggedItem !== undefined && root.active
} }
} }

View File

@ -5,13 +5,14 @@ import QtQuick.Layouts 1.3
MenuItem { MenuItem {
id: root id: root
property alias iconSource: icon.name property alias iconSource: icon.name
implicitWidth: 200
contentItem: RowLayout { contentItem: RowLayout {
spacing: app.margins spacing: app.margins
ColorIcon { ColorIcon {
id: icon id: icon
height: parent.height Layout.preferredHeight: app.iconSize
width: height Layout.preferredWidth: height
} }
Label { Label {
id: label id: label

View File

@ -0,0 +1,18 @@
import QtQuick 2.9
Item {
id: root
implicitHeight: app.iconSize * .8
implicitWidth: height
property bool on: false
Rectangle {
height: Math.min(parent.height, parent.height)
width: height
radius: width / 2
color: root.on ? "lightgreen" : "lightgray"
border.width: 1
border.color: app.foregroundColor
}
}

View File

@ -11,8 +11,8 @@ Item {
id: root id: root
implicitHeight: width * .6 implicitHeight: width * .6
property var device: null property Device device: null
property var stateType: null property StateType stateType: null
property int roundTo: 2 property int roundTo: 2
readonly property var valueState: device.states.getState(stateType.id) readonly property var valueState: device.states.getState(stateType.id)
readonly property var deviceClass: engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); readonly property var deviceClass: engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
@ -55,12 +55,20 @@ Item {
color: root.color color: root.color
} }
Led {
visible: root.stateType.type.toLowerCase() === "bool"
on: root.valueState.value === true
}
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
text: 1.0 * Math.round(root.valueState.value * Math.pow(10, root.roundTo)) / Math.pow(10, root.roundTo) + " " + root.stateType.unitString text: root.stateType.type.toLowerCase() === "bool"
? root.stateType.displayName
: 1.0 * Math.round(root.valueState.value * Math.pow(10, root.roundTo)) / Math.pow(10, root.roundTo) + " " + root.stateType.unitString
font.pixelSize: app.largeFont font.pixelSize: app.largeFont
} }
HeaderButton { HeaderButton {
imageSource: "../images/zoom-out.svg" imageSource: "../images/zoom-out.svg"
onClicked: { onClicked: {
@ -96,11 +104,19 @@ Item {
ValueAxis { ValueAxis {
id: yAxis id: yAxis
max: logsModelNg.maxValue + Math.abs(logsModelNg.maxValue * .05) max: Math.ceil(logsModelNg.maxValue + Math.abs(logsModelNg.maxValue * .05))
min: logsModelNg.minValue - Math.abs(logsModelNg.minValue * .05) min: Math.floor(logsModelNg.minValue - Math.abs(logsModelNg.minValue * .05))
labelsFont.pixelSize: app.smallFont labelsFont.pixelSize: app.smallFont
labelFormat: {
switch (root.stateType.type.toLowerCase()) {
case "bool":
return "x";
default:
return "%d";
}
}
labelsColor: app.foregroundColor labelsColor: app.foregroundColor
tickCount: chartView.height / 40 tickCount: root.stateType.type.toLowerCase() === "bool" ? 2 : chartView.height / 40
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .2) color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .2)
gridLineColor: color gridLineColor: color
} }

View File

@ -8,7 +8,7 @@ import "../components"
ItemDelegate { ItemDelegate {
id: root id: root
property ActionType actionType: null property var actionType: null
property var actionState: null property var actionState: null
signal executeAction(var params) signal executeAction(var params)
@ -294,23 +294,6 @@ ItemDelegate {
root.executeAction(params) root.executeAction(params)
} }
} }
touchDelegate: Rectangle {
height: 15
width: height
radius: height / 2
color: Material.accent
Rectangle {
color: colorPicker.hovered || colorPicker.pressed ? "#11000000" : "transparent"
anchors.centerIn: parent
height: 30
width: height
radius: width / 2
Behavior on color { ColorAnimation { duration: 200 } }
}
}
} }
} }

View File

@ -143,14 +143,20 @@ ItemDelegate {
Component { Component {
id: spinnerComponent id: spinnerComponent
SpinBox { SpinBox {
value: root.param.value value: root.param.value ? root.param.value : 0
from: root.paramType.minValue from: root.paramType.minValue ? root.paramType.minValue : -999999999
to: root.paramType.maxValue to: root.paramType.maxValue ? root.paramType.maxValue : 999999999
editable: true editable: true
width: 150
onValueModified: root.param.value = value onValueModified: root.param.value = value
textFromValue: function(value) { textFromValue: function(value) {
return value return value
} }
Component.onCompleted: {
if (root.value === undefined) {
root.value = value
}
}
} }
} }
@ -173,6 +179,11 @@ ItemDelegate {
root.param.value = root.paramType.allowedValues[index] root.param.value = root.paramType.allowedValues[index]
print("setting value to", root.param.value) print("setting value to", root.param.value)
} }
Component.onCompleted: {
if (root.value === undefined) {
root.value = model[0]
}
}
} }
} }

View File

@ -78,9 +78,7 @@ DeviceListPageBase {
Rectangle { Rectangle {
anchors { left: parent.left; top: parent.top; bottom: parent.bottom } anchors { left: parent.left; top: parent.top; bottom: parent.bottom }
width: app.margins / 2 width: app.margins / 2
color: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false ? color: itemDelegate.colorStateType ? itemDelegate.colorState.value : "#00000000"
"red"
: itemDelegate.colorStateType ? itemDelegate.colorState.value : "#00000000"
} }
contentItem: ColumnLayout { contentItem: ColumnLayout {
@ -105,22 +103,12 @@ DeviceListPageBase {
// source: icon // source: icon
// } // }
Glow {
anchors.fill: icon
radius: 1
samples: 17
color: app.backgroundColor
source: icon
}
ColorIcon { ColorIcon {
id: icon id: icon
anchors.fill: parent anchors.fill: parent
color: app.accentColor color: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false
// anchors.margins: app.margins / 4 ? "red"
// color: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false ? : app.accentColor
// "red"
// : itemDelegate.colorStateType ? itemDelegate.colorState.value : "#00000000"
name: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false ? name: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false ?
"../images/dialog-warning-symbolic.svg" "../images/dialog-warning-symbolic.svg"
: itemDelegate.powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg" : itemDelegate.powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg"

View File

@ -0,0 +1,291 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2
import QtQuick.Layouts 1.1
import Nymea 1.0
import "../components"
import "../customviews"
Page {
id: root
property Device device: null
property var filterTypeIds: []
header: GuhHeader {
text: qsTr("History for %1").arg(root.device.name)
onBackPressed: pageStack.pop()
HeaderButton {
imageSource: "../images/filters.svg"
color: logsModelNg.filterEnabled ? app.accentColor : keyColor
onClicked: logsModelNg.filterEnabled = !logsModelNg.filterEnabled
visible: root.filterTypeIds.length === 0
}
}
LogsModelNg {
id: logsModelNg
engine: _engine
deviceId: root.device.id
typeIds: root.filterTypeIds.length > 0
? root.filterTypeIds
: filterEnabled
? [filterDeviceModel.getData(filterComboBox.currentIndex, DeviceModel.RoleId)]
: []
live: true
property bool filterEnabled: false
}
DeviceModel {
id: filterDeviceModel
device: root.device
}
Pane {
id: filterPane
anchors { left: parent.left; top: parent.top; right: parent.right }
Behavior on height { NumberAnimation { duration: 120; easing.type: Easing.InOutQuad } }
height: logsModelNg.filterEnabled ? implicitHeight + app.margins * 2 : 0
Material.elevation: 1
leftPadding: 0; rightPadding: 0; topPadding: 0; bottomPadding: 0
contentItem: Rectangle {
color: app.primaryColor
clip: true
RowLayout {
anchors.fill: parent
anchors.margins: app.margins
spacing: app.margins
Label {
text: qsTr("Filter by")
}
ComboBox {
id: filterComboBox
Layout.fillWidth: true
textRole: "displayName"
model: filterDeviceModel
}
}
}
}
Loader {
id: graphLoader
anchors {
left: parent.left
top: filterPane.bottom
right: parent.right
}
readonly property StateType stateType: root.device.deviceClass.stateTypes.getStateType(root.filterTypeIds[0])
readonly property bool canShowGraph: {
if (stateType === null) {
return false
}
if (stateType.unit === Types.UnitUnixTime) {
return false;
}
switch (stateType.type) {
case "Int":
case "Double":
return true;
case "Bool":
return engine.jsonRpcClient.ensureServerVersion("1.10")
}
print("not showing graph for", root.stateType.type)
return false;
}
Component.onCompleted: {
if (root.filterTypeIds.length === 0) {
return;
}
if (!canShowGraph) {
return;
}
var source;
if (engine.jsonRpcClient.ensureServerVersion("1.10")) {
source = Qt.resolvedUrl("../customviews/GenericTypeGraph.qml");
} else {
source = Qt.resolvedUrl("../customviews/GenericTypeGraphPre110.qml");
}
setSource(source, {device: root.device, stateType: stateType})
}
}
ListView {
anchors { left: parent.left; top: graphLoader.bottom; right: parent.right; bottom: parent.bottom }
clip: true
model: logsModelNg
ScrollBar.vertical: ScrollBar {}
BusyIndicator {
anchors.centerIn: parent
visible: logsModelNg.busy
}
delegate: ItemDelegate {
id: entryDelegate
width: parent.width
property StateType stateType: model.source === LogEntry.LoggingSourceStates ? root.device.deviceClass.stateTypes.getStateType(model.typeId) : null
property EventType eventType: model.source === LogEntry.LoggingSourceEvents || model.source === LogEntry.LoggingSourceStates ? root.device.deviceClass.eventTypes.getEventType(model.typeId) : null
property ActionType actionType: model.source === LogEntry.LoggingSourceActions ? root.device.deviceClass.actionTypes.getActionType(model.typeId) : null
contentItem: RowLayout {
ColorIcon {
Layout.preferredWidth: app.iconSize
Layout.preferredHeight: width
Layout.alignment: Qt.AlignVCenter
color: {
switch (model.source) {
case LogEntry.LoggingSourceStates:
case LogEntry.LoggingSourceSystem:
case LogEntry.LoggingSourceActions:
case LogEntry.LoggingSourceEvents:
return app.accentColor
case LogEntry.LoggingSourceRules:
if (model.loggingEventType === LogEntry.LoggingEventTypeActiveChange) {
return model.value === true ? "green" : keyColor
}
return app.accentColor
}
}
name: {
switch (model.source) {
case LogEntry.LoggingSourceStates:
return "../images/state.svg"
case LogEntry.LoggingSourceSystem:
return "../images/system-shutdown.svg"
case LogEntry.LoggingSourceActions:
return "../images/action.svg"
case LogEntry.LoggingSourceEvents:
return "../images/event.svg"
case LogEntry.LoggingSourceRules:
return "../images/magic.svg"
}
}
}
ColumnLayout {
RowLayout {
Label {
text: {
switch (model.source) {
case LogEntry.LoggingSourceStates:
// return entryDelegate.stateType.displayName
case LogEntry.LoggingSourceEvents:
return entryDelegate.eventType.displayName
case LogEntry.LoggingSourceActions:
return entryDelegate.actionType.displayName
}
}
Layout.fillWidth: true
elide: Text.ElideRight
}
Label {
text: Qt.formatDateTime(model.timestamp,"dd.MM.yy hh:mm:ss")
elide: Text.ElideRight
font.pixelSize: app.smallFont
enabled: false
}
}
RowLayout {
Loader {
id: valueLoader
Layout.fillWidth: true
sourceComponent: {
switch (model.source) {
case LogEntry.LoggingSourceStates:
switch (entryDelegate.stateType.type.toLowerCase()) {
case "bool":
return boolComponent;
case "color":
return colorComponent
default:
if (entryDelegate.stateType.unit == Types.UnitUnixTime) {
return dateTimeComponent
}
return labelComponent
}
case LogEntry.LoggingSourceActions:
break;
case LogEntry.LoggingSourceEvents:
break;
}
return labelComponent
}
Binding { target: valueLoader.item; property: "value"; value: model.value }
Binding {
target: entryDelegate.stateType && valueLoader.item.hasOwnProperty("unitString") ? valueLoader.item : null;
property: "unitString"
value: entryDelegate.stateType ? entryDelegate.stateType.unitString : ""
}
}
}
}
}
}
}
Component {
id: labelComponent
Label {
property var value
property string unitString
text: value + " " + unitString
font.pixelSize: app.smallFont
elide: Text.ElideRight
}
}
Component {
id: dateTimeComponent
Label {
property var value
text: Qt.formatDateTime(new Date(value * 1000), Qt.DefaultLocaleShortDate)
}
}
Component {
id: boolComponent
Item {
id: boolLed
property var value
Led {
implicitHeight: app.smallFont
on: boolLed.value === "true"
}
}
}
Component {
id: colorComponent
Item {
property var value
implicitHeight: app.smallFont
Rectangle {
height: parent.height
width: height * 2
color: parent.value
// radius: width / 2
border.color: app.foregroundColor
border.width: 1
}
}
}
}

View File

@ -6,8 +6,11 @@ import "../components"
Page { Page {
id: root id: root
property var device: null property Device device: null
readonly property var deviceClass: engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) readonly property DeviceClass deviceClass: engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
property bool showLogsButton: true
property bool showDetailsButton: true
default property alias data: contentItem.data default property alias data: contentItem.data
@ -35,7 +38,13 @@ Page {
Component.onCompleted: { Component.onCompleted: {
thingMenu.addItem(menuEntryComponent.createObject(thingMenu, {text: qsTr("Magic"), iconSource: "../images/magic.svg", functionName: "openDeviceMagicPage"})) 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 (root.showDetailsButton) {
thingMenu.addItem(menuEntryComponent.createObject(thingMenu, {text: qsTr("Thing details"), iconSource: "../images/configure.svg", functionName: "openGenericDevicePage"}))
}
if (root.showLogsButton) {
thingMenu.addItem(menuEntryComponent.createObject(thingMenu, {text: qsTr("Thing logs"), iconSource: "../images/logs.svg", functionName: "openDeviceLogPage"}))
}
if (engine.jsonRpcClient.ensureServerVersion(1.6)) { if (engine.jsonRpcClient.ensureServerVersion(1.6)) {
thingMenu.addItem(menuEntryComponent.createObject(thingMenu, thingMenu.addItem(menuEntryComponent.createObject(thingMenu,
{ {
@ -48,8 +57,8 @@ Page {
function openDeviceMagicPage() { function openDeviceMagicPage() {
pageStack.push(Qt.resolvedUrl("../magic/DeviceRulesPage.qml"), {device: root.device}) pageStack.push(Qt.resolvedUrl("../magic/DeviceRulesPage.qml"), {device: root.device})
} }
function openDeviceInfoPage() { function openGenericDevicePage() {
pageStack.push(Qt.resolvedUrl("GenericDeviceStateDetailsPage.qml"), {device: root.device}) pageStack.push(Qt.resolvedUrl("GenericDevicePage.qml"), {device: root.device})
} }
function toggleFavorite() { function toggleFavorite() {
if (favoritesProxy.count === 0) { if (favoritesProxy.count === 0) {
@ -58,6 +67,9 @@ Page {
engine.tagsManager.untagDevice(root.device.id, "favorites") engine.tagsManager.untagDevice(root.device.id, "favorites")
} }
} }
function openDeviceLogPage() {
pageStack.push(Qt.resolvedUrl("DeviceLogPage.qml"), {device: root.device });
}
Component { Component {
id: menuEntryComponent id: menuEntryComponent

View File

@ -1,134 +1,487 @@
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Controls 2.1 import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import Nymea 1.0 import Nymea 1.0
import "../components" import "../components"
import "../delegates"
DevicePageBase { DevicePageBase {
id: root id: root
showDetailsButton: false
Flickable { function executeAction(actionTypeId, params) {
return engine.deviceManager.executeAction(root.device.id, actionTypeId, params)
}
ListView {
id: flickable id: flickable
anchors.fill: parent anchors.fill: parent
contentHeight: contentColumn.height
contentWidth: parent.width
clip: true clip: true
property var d: root.device section.property: "type"
property var dc: root.deviceClass section.delegate: ListSectionHeader {
text: {
switch (parseInt(section)) {
case DeviceModel.TypeStateType:
return qsTr("States")
case DeviceModel.TypeActionType:
return qsTr("Actions")
case DeviceModel.TypeEventType:
return qsTr("Events")
}
}
}
ColumnLayout { model: DeviceModel {
id: contentColumn device: root.device
}
delegate: SwipeDelegate {
id: delegate
width: parent.width width: parent.width
// spacing: app.margins
Repeater { readonly property StateType stateType: model.type === DeviceModel.TypeStateType ? root.deviceClass.stateTypes.getStateType(model.id) : null
id: interfaceViewsRepeater readonly property ActionType actionType: model.writable ? root.deviceClass.actionTypes.getActionType(model.id) : null
property bool unhandledInterface: false readonly property EventType eventType: model.type === DeviceModel.TypeEventType ? root.deviceClass.eventTypes.getEventType(model.id) : null
// model: deviceClass.interfaces Layout.fillWidth: true
delegate: Loader { topPadding: model.type === DeviceModel.TypeActionType ? app.margins / 2 : 0
id: stateViewLoader bottomPadding: 0
Layout.fillWidth: true contentItem: Loader {
source: { id: inlineLoader
var src = ""; sourceComponent: {
var options = [] switch (model.type) {
switch (modelData) { case DeviceModel.TypeStateType:
case "weather": return stateComponent;
src = "WeatherView.qml"; case DeviceModel.TypeActionType:
break; return actionComponent;
case "mediacontroller": case DeviceModel.TypeEventType:
src = "MediaControllerView.qml" return eventComponent;
break; }
case "extendedvolumecontroller": }
src = "ExtendedVolumeController.qml"
break; Binding {
case "temperaturesensor": target: inlineLoader.item
case "humiditysensor": when: model.type === DeviceModel.TypeStateType
case "moisturesensor": property: "stateType"
case "lightsensor": value: delegate.stateType
case "conductivitysensor": }
case "pressuresensor": Binding {
case "noisesensor": target: inlineLoader.item
case "co2sensor": when: model.type === DeviceModel.TypeActionType
src = "SensorView.qml" property: "actionType"
options.interfaceName = modelData; value: delegate.actionType
break; }
case "notifications": Binding {
src = "NotificationsView.qml" target: inlineLoader.item
break; when: model.type === DeviceModel.TypeEventType
case "battery": property: "eventType"
case "connectable": value: delegate.eventType
// Handled by our base class }
break; }
case "sensor":
case "media": onClicked: pageStack.push(Qt.resolvedUrl("DeviceLogPage.qml"), {device: root.device, filterTypeIds: [model.id]})
// Ignore interfaces without any states/actions
break; swipe.right: RowLayout {
default: height: delegate.height
print("WARNING: Unhandled interface", modelData) anchors.right: parent.right
interfaceViewsRepeater.unhandledInterface = true MouseArea {
Layout.fillHeight: true
Layout.preferredWidth: height
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/logs.svg"
}
onClicked: {
pageStack.push(Qt.resolvedUrl("DeviceLogPage.qml"), {device: root.device, filterTypeIds: [model.id]})
}
}
}
}
}
Component {
id: stateComponent
RowLayout {
id: stateDelegate
property StateType stateType: null
readonly property State deviceState: stateType ? root.device.states.getState(stateType.id) : null
readonly property bool writable: root.deviceClass.actionTypes.getActionType(stateType.id) !== null
Label {
Layout.fillWidth: true
text: stateDelegate.stateType.displayName
elide: Text.ElideRight
}
Loader {
id: stateDelegateLoader
sourceComponent: {
switch (stateType.type.toLowerCase()) {
case "string":
if (stateDelegate.writable) {
if (stateDelegate.stateType.allowedValues !== undefined) {
return comboBoxComponent
}
return textFieldComponent;
} else {
return labelComponent;
}
case "stringlist":
return listComponent;
case "bool":
if (stateDelegate.writable) {
return switchComponent;
} else {
return ledComponent;
}
case "int":
case "double":
if (stateDelegate.stateType.unit === Types.UnitUnixTime) {
return dateTimeComponent;
} }
return Qt.resolvedUrl("../customviews/" + src); if (stateDelegate.writable) {
return spinBoxComponent;
}
return numberLabelComponent;
case "color":
return colorComponent;
default:
print("Unhandled state type", stateType.displayName, stateType.type)
} }
Binding {
target: stateViewLoader.item ? stateViewLoader.item : null print("GenericDevicePage: unhandled entry", stateDelegate.stateType.displayName)
property: "device" }
value: device }
Label {
visible: stateDelegate.stateType.unit !== Types.UnitUnixTime && stateDelegate.stateType.unitString.length > 0
text: stateDelegate.stateType.unitString
}
Binding {
target: stateDelegateLoader.item
property: "value"
value: root.device.states.getState(stateDelegate.stateType.id).value
}
Binding {
target: stateDelegateLoader.item && stateDelegateLoader.item.hasOwnProperty("possibleValues") ? stateDelegateLoader.item : null
property: "possibleValues"
value: stateDelegate.stateType.allowedValues
}
Binding {
target: stateDelegateLoader.item && stateDelegateLoader.item.hasOwnProperty("from") ? stateDelegateLoader.item : null
property: "from"
value: stateDelegate.stateType.minValue !== undefined ? stateDelegate.stateType.minValue : -999999999999
}
Binding {
target: stateDelegateLoader.item && stateDelegateLoader.item.hasOwnProperty("to") ? stateDelegateLoader.item : null
property: "to"
value: stateDelegate.stateType.maxValue !== undefined ? stateDelegate.stateType.maxValue : 999999999999
}
Binding {
target: stateDelegateLoader.item && stateDelegateLoader.item.hasOwnProperty("actionTypeId") ? stateDelegateLoader.item : null
property: "actionTypeId"
value: stateDelegate.stateType.id
}
Connections {
target: stateDelegateLoader.item && stateDelegateLoader.item.hasOwnProperty("changed") ? stateDelegateLoader.item : null
onChanged: {
var params = []
var param1 = {}
param1["paramTypeId"] = stateDelegate.stateType.id
param1["value"] = value;
params.push(param1)
root.executeAction(stateDelegate.stateType.id, params);
}
}
}
}
Component {
id: actionComponent
RowLayout {
id: actionDelegate
property ActionType actionType: null
property int pendingActionId: -1
property bool lastSuccess: false
Connections {
target: engine.deviceManager
onExecuteActionReply: {
if (params["id"] === actionDelegate.pendingActionId) {
print("blubb!")
pendingTimer.start();
actionDelegate.lastSuccess = params["params"]["deviceError"] === "DeviceErrorNoError"
actionDelegate.pendingActionId = -1
} }
Binding { }
target: stateViewLoader.item ? stateViewLoader.item : null }
property: "deviceClass" Timer { id: pendingTimer; interval: 1000; repeat: false; running: false }
value: deviceClass
Button {
text: actionType.displayName
Layout.fillWidth: true
onClicked: {
if (actionDelegate.actionType.paramTypes.count === 0) {
actionDelegate.pendingActionId = root.executeAction(actionDelegate.actionType.id, [])
} else {
var dialog = paramsDialogComponent.createObject(root, { actionType: actionDelegate.actionType })
dialog.open()
} }
Binding { }
target: stateViewLoader.item ? stateViewLoader.item : null
property: "interfaceName" Component {
value: modelData id: paramsDialogComponent
Dialog {
id: paramsDialog
modal: true
width: parent.width - app.margins * 2
x: (parent.width - width) / 2
y: (parent.height - height) / 2
padding: 0
property ActionType actionType: null
contentItem: ColumnLayout {
Repeater {
id: paramsRepeater
model: paramsDialog.actionType.paramTypes
delegate: ParamDelegate {
Layout.fillWidth: true
paramType: paramsDialog.actionType.paramTypes.get(index)
}
}
RowLayout {
Layout.margins: app.margins
spacing: app.margins
Button {
text: qsTr("Cancel")
Layout.fillWidth: true
onClicked: paramsDialog.close()
}
Button {
text: qsTr("OK")
Layout.fillWidth: true
onClicked: {
var params = []
for (var i = 0; i < paramsRepeater.count; i++) {
var param = {}
param["paramTypeId"] = paramsRepeater.itemAt(i).paramType.id
param["value"] = paramsRepeater.itemAt(i).value
params.push(param)
}
actionDelegate.pendingActionId = root.executeAction(paramsDialog.actionType.id, params);
paramsDialog.close();
}
}
}
}
} }
} }
} }
Item {
Layout.preferredHeight: preferredSize
Layout.preferredWidth: preferredSize
property int preferredSize: actionDelegate.pendingActionId !== -1 || pendingTimer.running ? app.iconSize : 0
Behavior on preferredSize { NumberAnimation { duration: 100 } }
Repeater { BusyIndicator {
model: interfaceViewsRepeater.count == 0 || interfaceViewsRepeater.unhandledInterface ? deviceClass.actionTypes : null anchors.fill: parent
delegate: ItemDelegate { visible: actionDelegate.pendingActionId !== -1
Layout.fillWidth: true }
Layout.preferredHeight: delegateLoader.height
Loader { ColorIcon {
id: delegateLoader anchors.fill: parent
width: parent.width visible: actionDelegate.pendingActionId === -1
property var actionType: deviceClass.actionTypes.get(index) name: actionDelegate.lastSuccess ? "../images/tick.svg" : "../images/close.svg"
property var actionValue: device.hasState(actionType.id) ? device.states.getState(actionType.id).value : null color: actionDelegate.lastSuccess ? "green" : "red"
source: Qt.resolvedUrl("../delegates/ActionDelegate.qml") }
}
}
}
Binding { Component {
target: delegateLoader.item ? delegateLoader.item : null id: eventComponent
property: "actionType" RowLayout {
value: delegateLoader.actionType id: eventComponentItem
} property EventType eventType: null
Binding {
target: delegateLoader.item ? delegateLoader.item : null
property: "actionState" Label {
value: delegateLoader.actionValue Layout.fillWidth: true
text: eventComponentItem.eventType.displayName
}
Rectangle {
id: flashlight
Layout.preferredHeight: app.iconSize * .8
Layout.preferredWidth: height
color: "lightgray"
radius: width / 2
border.color: app.foregroundColor
border.width: 1
SequentialAnimation on color {
id: flashlightAnimation
running: false
ColorAnimation { to: "lightgreen"; duration: 100 }
ColorAnimation { to: "lightgray"; duration: 500 }
}
}
LogsModelNg {
engine: _engine
live: true
deviceId: root.device.id
typeIds: eventComponentItem.eventType.id
onCountChanged: {
flashlightAnimation.start()
}
}
}
}
Component {
id: ledComponent
Led {
property bool value
on: value === true
}
}
Component {
id: labelComponent
Label {
property var value
text: value
}
}
Component {
id: numberLabelComponent
Label {
property var value
text: Math.round(value * 100) / 100
}
}
Component {
id: textFieldComponent
TextField {
property var value
text: value
}
}
Component {
id: listComponent
Label {
property var value
text: value.join(", ")
}
}
Component {
id: checkBoxComponent
CheckBox {
property var value
checked: value === true
enabled: false
}
}
Component {
id: switchComponent
Switch {
property var value
signal changed(var value)
checked: value === true
onClicked: {
changed(checked)
}
}
}
Component {
id: spinBoxComponent
SpinBox {
width: 150
signal changed(var value)
stepSize: (to - from) / 10
editable: true
onValueModified: {
changed(value)
}
}
}
Component {
id: comboBoxComponent
ComboBox {
property var value
property var possibleValues
signal changed(var value)
model: possibleValues
onActivated: changed(model[index])
}
}
Component {
id: colorComponent
Item {
id: colorComponentItem
implicitWidth: app.iconSize * 2
implicitHeight: app.iconSize
property var value
signal changed(var value)
Pane {
anchors.fill: parent
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
Material.elevation: 1
contentItem: Rectangle {
color: colorComponentItem.value
MouseArea {
anchors.fill: parent
onClicked: {
var pos = colorComponentItem.mapToItem(root, 0, colorComponentItem.height)
print("opening", colorComponentItem.value)
var colorPicker = colorPickerComponent.createObject(root, {preferredY: pos.y, colorValue: colorComponentItem.value })
colorPicker.open()
} }
}
}
}
property int commandId: 0 Component {
Connections { id: colorPickerComponent
target: delegateLoader.item ? delegateLoader.item : null Dialog {
onExecuteAction: { id: colorPickerDialog
engine.deviceManager.executeAction(root.device.id, model.id, params) modal: true
} x: (parent.width - width) / 2
} y: Math.min(preferredY, parent.height - height)
Connections { width: parent.width - app.margins * 2
target: engine.jsonRpcClient height: 200
onResponseReceived: { padding: app.margins
if (commandId == delegateLoader.commandId) { property var colorValue
print("response:", response["error"]) property int preferredY: 0
} contentItem: ColorPicker {
color: colorPickerDialog.colorValue
property var lastSentTime: new Date()
onColorChanged: {
var currentTime = new Date();
if (pressed && currentTime - lastSentTime > 200) {
colorComponentItem.changed(color);
} }
} }
} }
@ -136,4 +489,12 @@ DevicePageBase {
} }
} }
} }
Component {
id: dateTimeComponent
Label {
property var value
text: Qt.formatDateTime(new Date(value * 1000), Qt.DefaultLocaleShortDate)
}
}
} }

View File

@ -1,169 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import Nymea 1.0
import "../components"
Page {
id: root
property var device
readonly property var deviceClass: engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
property bool readOnly: true
header: GuhHeader {
text: "Details for " + root.device.name
onBackPressed: pageStack.pop()
}
Flickable {
anchors.fill: parent
contentHeight: statesColumn.height + app.margins*2
ColumnLayout {
id: statesColumn
anchors { left: parent.left; top: parent.top; right: parent.right; margins: app.margins }
spacing: app.margins
Repeater {
model: deviceClass.stateTypes
delegate: RowLayout {
width: parent.width
height: app.largeFont
property var stateType: deviceClass.stateTypes.get(index)
Label {
id: stateLabel
Layout.preferredWidth: parent.width / 2
text: displayName
elide: Text.ElideRight
}
Loader {
id: placeHolder
Layout.fillWidth: true
sourceComponent: {
var writable = deviceClass.actionTypes.getActionType(id) !== null;
if (root.readOnly || !writable) {
return labelComponent;
}
switch (stateType.type) {
case "Bool":
return boolComponent;
case "Int":
case "Double":
if (stateType.minValue !== undefined && stateType.maxValue !== undefined) {
return sliderComponent;
}
return textFieldComponent;
case "String":
return textFieldComponent;
case "Color":
return colorPreviewComponent;
}
console.warn("DeviceStateDetailsPage: Type delegate not implemented", stateType.type)
return null;
}
}
ColorIcon {
Layout.fillHeight: true
Layout.preferredWidth: height
name: "../images/info.svg"
MouseArea {
anchors.fill: parent
anchors.margins: -app.margins / 2
onClicked: {
pageStack.push(Qt.resolvedUrl("StateLogPage.qml"),
{device: root.device, stateType: stateType})
}
}
}
Binding {
target: placeHolder.item
when: placeHolder.item
property: "value"
value: device.states.getState(id).value
}
// Binding {
// target: placeHolder.item
// when: placeHolder.item
// property: "enabled"
// value: deviceClass.actionTypes.getActionType(id) !== null
// }
Binding {
target: placeHolder.item
when: placeHolder.item
property: "stateTypeId"
value: id
}
// Label {
// id: valueLable
// Layout.fillWidth: true
// text: device.states.getState(id).value + " " + deviceClass.stateTypes.getStateType(id).unitString
// }
}
}
}
}
Component {
id: labelComponent
Label {
property var value: ""
property var stateTypeId: null
property var unitString: deviceClass.stateTypes.getStateType(stateTypeId).unitString
text: unitString === "datetime" ? Qt.formatDateTime(new Date(value * 1000), Qt.DefaultLocaleShortDate) : value + " " + unitString
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
}
Component {
id: textFieldComponent
TextField {
property var value: ""
property var stateTypeId: null
text: value
onEditingFinished: {
executeAction(stateTypeId, text)
}
}
}
Component {
id: boolComponent
Switch {
property var value: false
property var stateTypeId: null
checked: value
onClicked: executeAction(stateTypeId, checked)
}
}
Component {
id: colorPreviewComponent
Rectangle {
property var value: "blue"
property var stateTypeId: null
color: value
implicitHeight: app.mediumFont
implicitWidth: height
radius: height / 4
}
}
function executeAction(stateTypeId, value) {
var paramList = []
var muteParam = {}
muteParam["paramTypeId"] = stateTypeId;
muteParam["value"] = value;
paramList.push(muteParam)
engine.deviceManager.executeAction(root.device.id, stateTypeId, paramList)
}
}