moar work!

This commit is contained in:
Michael Zanetti 2017-11-10 15:11:49 +01:00
parent 509ce837e4
commit 834c7a43c5
35 changed files with 876 additions and 116 deletions

View File

@ -83,7 +83,8 @@ void DeviceManager::addDevice(const QUuid &deviceClassId, const QString &name, c
void DeviceManager::notificationReceived(const QVariantMap &data)
{
if (data.value("notification").toString() == "Devices.StateChanged") {
QString notification = data.value("notification").toString();
if (notification == "Devices.StateChanged") {
qDebug() << "Device state changed" << data.value("params");
Device *dev = m_devices->getDevice(data.value("params").toMap().value("deviceId").toUuid());
if (!dev) {
@ -91,20 +92,33 @@ void DeviceManager::notificationReceived(const QVariantMap &data)
return;
}
dev->setStateValue(data.value("params").toMap().value("stateTypeId").toUuid(), data.value("params").toMap().value("value"));
} else if (notification == "Devices.DeviceAdded") {
Device *dev = JsonTypes::unpackDevice(data.value("params").toMap().value("device").toMap(), m_devices);
if (!dev) {
qWarning() << "Cannot parse json device:" << data;
return;
}
m_devices->addDevice(dev);
} else if (notification == "Devices.DeviceRemoved") {
QUuid deviceId = data.value("params").toMap().value("deviceId").toUuid();
qDebug() << "JsonRpc: Notification: Device removed" << deviceId.toString();
Device *device = m_devices->getDevice(deviceId);
m_devices->removeDevice(device);
device->deleteLater();
} else {
qWarning() << "DeviceManager unhandled device notification received" << data;
qWarning() << "DeviceManager unhandled device notification received" << notification;
}
}
void DeviceManager::getVendorsResponse(const QVariantMap &params)
{
qDebug() << "Got GetSupportedVendors response" << params;
// qDebug() << "Got GetSupportedVendors response" << params;
if (params.value("params").toMap().keys().contains("vendors")) {
QVariantList vendorList = params.value("params").toMap().value("vendors").toList();
foreach (QVariant vendorVariant, vendorList) {
Vendor *vendor = JsonTypes::unpackVendor(vendorVariant.toMap(), Engine::instance()->deviceManager()->vendors());
m_vendors->addVendor(vendor);
qDebug() << "Added Vendor:" << vendor->name();
// qDebug() << "Added Vendor:" << vendor->name();
}
}
@ -166,6 +180,10 @@ void DeviceManager::getConfiguredDevicesResponse(const QVariantMap &params)
QVariant value = stateMap.toMap().value("value");
if (st->type() == "Bool") {
value.convert(QVariant::Bool);
} else if (st->type() == "Double") {
value.convert(QVariant::Double);
} else if (st->type() == "Int") {
value.convert(QVariant::Int);
}
device->setStateValue(stateTypeId, value);
}
@ -189,11 +207,8 @@ void DeviceManager::addDeviceResponse(const QVariantMap &params)
void DeviceManager::removeDeviceResponse(const QVariantMap &params)
{
QUuid deviceId = params.value("params").toMap().value("deviceId").toUuid();
qDebug() << "JsonRpc: Notification: Device removed" << deviceId.toString();
Device *device = m_devices->getDevice(deviceId);
m_devices->removeDevice(device);
device->deleteLater();
qDebug() << "Device removed response" << params;
emit removeDeviceReply(params.value("params").toMap());
}
void DeviceManager::pairDeviceResponse(const QVariantMap &params)

View File

@ -73,6 +73,7 @@ signals:
void pairDeviceReply(const QVariantMap &params);
void confirmPairingReply(const QVariantMap &params);
void addDeviceReply(const QVariantMap &params);
void removeDeviceReply(const QVariantMap &params);
private:
Vendors *m_vendors;

View File

@ -109,7 +109,7 @@ DeviceClass *JsonTypes::unpackDeviceClass(const QVariantMap &deviceClassMap, QOb
Param *JsonTypes::unpackParam(const QVariantMap &paramMap, QObject *parent)
{
return new Param(paramMap.value("name").toString(), paramMap.value("value"), parent);
return new Param(paramMap.value("paramTypeId").toString(), paramMap.value("value"), parent);
}
ParamType *JsonTypes::unpackParamType(const QVariantMap &paramTypeMap, QObject *parent)
@ -117,6 +117,7 @@ ParamType *JsonTypes::unpackParamType(const QVariantMap &paramTypeMap, QObject *
ParamType *paramType = new ParamType(parent);
paramType->setId(paramTypeMap.value("id").toString());
paramType->setName(paramTypeMap.value("name").toString());
paramType->setDisplayName(paramTypeMap.value("displayName").toString());
paramType->setType(paramTypeMap.value("type").toString());
paramType->setIndex(paramTypeMap.value("index").toInt());
paramType->setDefaultValue(paramTypeMap.value("defaultValue"));

View File

@ -18,7 +18,7 @@
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <QGuiApplication>
#include <QApplication>
#include <QCommandLineParser>
#include <QtQml/QQmlContext>
#include <QQmlApplicationEngine>
@ -46,7 +46,7 @@
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication application(argc, argv);
QApplication application(argc, argv);
application.setApplicationName("guh-control");
application.setOrganizationName("guh");

View File

@ -4,7 +4,8 @@
ValueLogsProxyModel::ValueLogsProxyModel(QObject *parent) : LogsModel(parent)
{
m_minimumValue = QVariant(0);
m_maximumValue = QVariant(0);
}
void ValueLogsProxyModel::update()
@ -40,8 +41,6 @@ QVariant ValueLogsProxyModel::maximumValue() const
void ValueLogsProxyModel::logsReply(const QVariantMap &data)
{
m_busy = false;
emit busyChanged();
qDebug() << "logs reply";
@ -84,7 +83,9 @@ void ValueLogsProxyModel::logsReply(const QVariantMap &data)
continue;
}
QList<QVariant> tmp = entries[slot];
tmp.append(entryMap.value("value"));
QVariant value = entryMap.value("value");
value.convert(QVariant::Double);
tmp.append(value);
entries[slot] = tmp;
// qDebug() << "adding value to slot" << slot << entryMap.value("value") << QDateTime::fromMSecsSinceEpoch(entryMap.value("timestamp").toLongLong());
}
@ -114,15 +115,16 @@ void ValueLogsProxyModel::logsReply(const QVariantMap &data)
continue;
}
LogEntry *entry = new LogEntry(startTime().addSecs(stepSize * i).addSecs(stepSize * .5), avg, this);
// qDebug() << "filling slot" << i << "average:" << avg << entry->timestamp();
m_list.append(entry);
// qDebug() << "**" << m_minimumValue << entry->value();
if (m_minimumValue.isNull() || entry->value() < m_minimumValue) {
m_minimumValue = qRound(entry->value().toDouble());
}
if (m_maximumValue.isNull() || entry->value() > m_maximumValue) {
m_maximumValue = qRound(entry->value().toDouble());
}
qDebug() << "filling slot" << i << "average:" << avg << entry->timestamp().toString() << "min:" << m_minimumValue << "max:" << m_maximumValue;
}
@ -133,4 +135,7 @@ void ValueLogsProxyModel::logsReply(const QVariantMap &data)
emit countChanged();
qDebug() << "min" << minimumValue() << "max" << maximumValue();
m_busy = false;
emit busyChanged();
}

View File

@ -101,5 +101,11 @@
<file>ui/images/battery/battery-090.svg</file>
<file>ui/images/battery/battery-100.svg</file>
<file>ui/images/dialog-warning-symbolic.svg</file>
<file>ui/images/magic.svg</file>
<file>ui/EditDevicesPage.qml</file>
<file>ui/devicepages/ConfigureThingPage.qml</file>
<file>ui/magic/DeviceRulesPage.qml</file>
<file>ui/magic/SelectEventPage.qml</file>
<file>ui/magic/NewThingMagicPage.qml</file>
</qresource>
</RCC>

View File

@ -0,0 +1,38 @@
import QtQuick 2.4
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import "components"
import Guh 1.0
Page {
id: root
header: GuhHeader {
text: "Configure Things"
onBackPressed: pageStack.pop()
}
ListView {
anchors.fill: parent
model: Engine.deviceManager.devices
delegate: ItemDelegate {
width: parent.width
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
height: app.iconSize
width: height
name: app.interfaceToIcon(model.interfaces[0])
color: app.guhAccent
}
Label {
Layout.fillWidth: true
text: model.name
}
}
onClicked: {
pageStack.push(Qt.resolvedUrl("devicepages/ConfigureThingPage.qml"), {device: Engine.deviceManager.devices.get(index)})
}
}
}
}

View File

@ -7,10 +7,10 @@ Page {
id: root
header: GuhHeader {
text: "Magic"
backButtonVisible: false
onBackPressed: pageStack.pop()
HeaderButton {
imageSource: "images/add.svg"
imageSource: Qt.resolvedUrl("images/add.svg")
onClicked: pageStack.push(Qt.resolvedUrl("magic/NewRulePage.qml"))
}
}

View File

@ -21,17 +21,26 @@ Page {
IconMenuItem {
iconSource: "../images/share.svg"
text: "Configure things"
onTriggered: pageStack.push(Qt.resolvedUrl("EditDevicesPage.qml"))
}
IconMenuItem {
iconSource: "../images/add.svg"
text: "Add a new thing..."
text: "Add a new thing"
onTriggered: pageStack.push(Qt.resolvedUrl("NewDeviceWizard.qml"))
}
MenuSeparator {}
IconMenuItem {
iconSource: "../images/magic.svg"
text: "Magic"
onTriggered: pageStack.push(Qt.resolvedUrl("MagicPage.qml"))
}
MenuSeparator {}
IconMenuItem {
iconSource: "../images/settings.svg"
text: "App settings"
onTriggered: pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
}
MenuSeparator {}
IconMenuItem {
iconSource: "../images/info.svg"
text: "System information"

View File

@ -16,7 +16,7 @@ Page {
}
HeaderButton {
imageSource: "images/close.svg"
imageSource: "../images/close.svg"
onClicked: pageStack.pop();
}
}

View File

@ -42,6 +42,7 @@ ToolBar {
Layout.fillHeight: true
verticalAlignment: Text.AlignVCenter
font.pixelSize: app.largeFont
elide: Text.ElideRight
color: "#333"
text: root.text.toUpperCase()
}

View File

@ -2,17 +2,16 @@ import QtQuick 2.5
import QtQuick.Controls 2.1
ToolButton {
property alias imageSource: image.source
property alias imageSource: image.name
property alias color: image.color
contentItem: Item {
height: 20
width: 20
Image {
ColorIcon {
id: image
anchors.fill: parent
anchors.margins: app.margins / 2
sourceSize.height: height
sourceSize.width: width
}
}
}

View File

@ -1,6 +1,8 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.3
//import QtCharts 2.1
import "../components"
import Guh 1.0
@ -22,11 +24,25 @@ CustomViewBase {
Component.onCompleted: updateTimer.start();
onAverageChanged: updateTimer.start()
onStartTimeChanged: updateTimer.start();
onBusyChanged: {
if (!busy) {
// lineSeries1.clear()
// splineSeries.clear()
// print("---", axisX.min, axisX.max)
// for (var i = 0; i < logsModel.count; i++) {
// print("adding", logsModel.get(i).timestamp, logsModel.get(i).value)
// lineSeries1.append(logsModel.get(i).timestamp, logsModel.get(i).value)
// splineSeries.append(logsModel.get(i).timestamp, logsModel.get(i).value)
// }
}
}
}
Timer {
id: updateTimer
interval: 0
interval: 10
repeat: false
onTriggered: {
print("updating:", logsModel.startTime)
@ -91,5 +107,70 @@ CustomViewBase {
model: logsModel
color: app.interfaceToColor(root.interfaceName)
}
// ChartView {
// id: chartView
// Layout.fillWidth: true
// Layout.preferredHeight: 300
// animationOptions: ChartView.SeriesAnimations
// theme: ChartView.ChartThemeQt
// backgroundColor: Material.background
// property bool openGL: false
// property bool openGLSupported: true
// onOpenGLChanged: {
// if (openGLSupported) {
// series("signal 1").useOpenGL = openGL;
// series("signal 2").useOpenGL = openGL;
// }
// }
// Component.onCompleted: {
// if (!series("signal 1").useOpenGL) {
// openGLSupported = false
// openGL = false
// }
// }
// ValueAxis {
// id: axisY1
// min: logsModel.minimumValue - 1
// max: logsModel.maximumValue + 1
// }
// DateTimeAxis {
// id: axisX
// min: logsModel.startTime
// max: logsModel.endTime
// format: {
// switch (logsModel.average) {
// case ValueLogsProxyModel.AverageMinute:
// case ValueLogsProxyModel.AverageHourly:
// case ValueLogsProxyModel.AverageQuarterHour:
// return "hh:mm"
// }
// return "ddd<br> dd.MM.<br>hh:mm"
// }
// }
// AreaSeries {
// axisX: axisX
// axisY: axisY1
// borderWidth: 0
// name: app.interfaceToString(interfaceName)
// borderColor: app.interfaceToColor(interfaceName)
// color: Qt.rgba(borderColor.r, borderColor.g, borderColor.b, .4)
// useOpenGL: chartView.openGL
// upperSeries: LineSeries {
// id: lineSeries1
// }
// }
// SplineSeries {
// id: splineSeries
// axisX: axisX
// axisY: axisY1
// width: 3
// color: app.interfaceToColor(interfaceName)
// }
// }
}
}

View File

@ -100,9 +100,7 @@ Page {
}
onClicked: {
if (deviceClass.interfaces.indexOf("colorlight") >= 0) {
pageStack.push(Qt.resolvedUrl("../devicepages/ColorLightDevicePage.qml"), {device: devicesProxy.get(index)})
}
pageStack.push(Qt.resolvedUrl("../devicepages/ColorLightDevicePage.qml"), {device: devicesProxy.get(index)})
}
}
}

View File

@ -5,21 +5,8 @@ import Guh 1.0
import "../components"
import "../actiondelegates"
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 {
anchors.fill: parent
@ -96,8 +83,9 @@ Page {
Layout.fillWidth: true
Layout.margins: app.margins
property var actionType: root.deviceClass.actionTypes.findByName("colorTemperature")
property var ctState: root.device.states.getState(actionType.id)
ct: ctState.value
property var ctState: actionType ? root.device.states.getState(actionType.id) : null
ct: ctState ? ctState.value : 0
visible: root.deviceClass.interfaces.indexOf("colorlight") >= 0
height: 80
@ -132,13 +120,19 @@ Page {
Layout.fillWidth: true
Layout.fillHeight: true
actionType: root.deviceClass.actionTypes.findByName("color")
actionState: root.device.states.getState(actionType.id).value
actionState: actionType ? root.device.states.getState(actionType.id).value : null
visible: root.deviceClass.interfaces.indexOf("colorlight") >= 0
onExecuteAction: {
Engine.deviceManager.executeAction(root.device.id, actionType.id, params)
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}

View File

@ -0,0 +1,53 @@
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import Guh 1.0
import "../components"
Page {
id: root
property var device: null
readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
header: GuhHeader {
text: root.device.name
onBackPressed: pageStack.pop()
HeaderButton {
imageSource: "../images/delete.svg"
color: "red"
onClicked: {
Engine.deviceManager.removeDevice(root.device.id)
}
}
}
Connections {
target: Engine.deviceManager
onRemoveDeviceReply: {
if (params.deviceError === "DeviceErrorNoError") {
pageStack.pop();
return;
}
print("Remove device error!", params)
}
}
ListView {
anchors.fill: parent
model: root.device.params
delegate: ItemDelegate {
width: parent.width
contentItem: RowLayout {
Label {
text: root.deviceClass.paramTypes.getParamType(model.id).displayName
}
Label {
Layout.fillWidth: true
text: model.value
horizontalAlignment: Text.AlignRight
}
}
}
}
}

View File

@ -11,6 +11,21 @@ Page {
default property alias data: contentItem.data
header: GuhHeader {
text: device.name
onBackPressed: pageStack.pop()
HeaderButton {
imageSource: "../images/magic.svg"
onClicked: pageStack.push(Qt.resolvedUrl("../magic/DeviceRulesPage.qml"), {device: root.device})
}
HeaderButton {
imageSource: "../images/info.svg"
onClicked: pageStack.push(Qt.resolvedUrl("GenericDeviceStateDetailsPage.qml"), {device: root.device})
}
}
Pane {
id: infoPane
visible: batteryState !== null || (connectedState !== null && connectedState.value === false)

View File

@ -7,16 +7,6 @@ import "../components"
DevicePageBase {
id: root
header: GuhHeader {
text: device.name
onBackPressed: pageStack.pop()
HeaderButton {
imageSource: "../images/info.svg"
onClicked: pageStack.push(Qt.resolvedUrl("GenericDeviceStateDetailsPage.qml"), {device: root.device})
}
}
Flickable {
id: flickable
anchors.fill: parent

View File

@ -5,26 +5,33 @@ import Guh 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})
Flickable {
anchors.fill: parent
clip: true
contentHeight: content.implicitHeight
ColumnLayout {
id: content
width: parent.width
WeatherView {
Layout.fillWidth: true
device: root.device
deviceClass: root.deviceClass
}
SensorView {
Layout.fillWidth: true
device: root.device
deviceClass: root.deviceClass
interfaceName: "temperaturesensor"
}
SensorView {
Layout.fillWidth: true
device: root.device
deviceClass: root.deviceClass
interfaceName: "humiditysensor"
}
}
}
WeatherView {
anchors.fill: parent
device: root.device
deviceClass: root.deviceClass
}
}

View File

@ -0,0 +1,296 @@
<?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="5.6568543"
inkscape:cx="115.91572"
inkscape:cy="46.722217"
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="true"
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" />
<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></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)">
<rect
style="fill:none;fill-opacity:1;stroke:#808080;stroke-width:3.00153852;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect1888"
width="12"
height="88.034821"
x="-399.36221"
y="-434.00089"
transform="matrix(0,-1,-1,0,0,0)"
rx="3"
ry="3.0011871" />
<rect
style="fill:#808080;fill-opacity:1;stroke:none;stroke-width:3.00153852;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect1890"
width="12"
height="60.023739"
x="-399.36221"
y="-419.99533"
transform="matrix(0,-1,-1,0,0,0)" />
</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:#ffffff;stroke-width:1.50029671;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 422.9456,387.13916 c 0.41673,-4.36096 0.4085,-8.39102 -4.7e-4,-11.88327 -0.81794,-6.98452 -3.23883,-11.8178 -7.06881,-12.84455 -7.66091,-2.05289 -18.05798,11.90079 -23.22212,31.16593 -5.16409,19.26514 -3.45309,39.40357 4.30075,41.07181 5.99681,1.29022 17.70837,-11.65794 19.05693,-14.99875 -0.63403,1.52511 -1.7672,5.36696 -5.55437,10.31771 -5.48611,7.17168 -11.19038,11.06022 -16.10785,9.74286 -9.83518,-2.63379 -12.43389,-24.81834 -5.80431,-49.55007 6.62937,-24.73177 19.97658,-42.64531 29.81151,-40.0106 4.91759,1.31689 8.02607,7.52147 9.07645,16.4883 0.52519,4.48341 0.53585,9.65739 8.7e-4,15.25624 -0.26749,2.79943 -0.67139,5.70507 -1.21559,8.68373"
id="path1945"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccscscccssc" />
<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" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,75 @@
import QtQuick 2.8
import QtQuick.Controls 2.1
import "../components"
import Guh 1.0
Page {
id: root
property var device: null
header: GuhHeader {
text: qsTr("Magic involving %1").arg(root.device.name)
onBackPressed: pageStack.pop()
HeaderButton {
imageSource: "../images/add.svg"
visible: rulesListView.count > 0
onClicked: addRule()
}
}
function addRule() {
pageStack.push(Qt.resolvedUrl("NewThingMagicPage.qml"), {device: root.device, text: "Add magic"})
}
ListView {
id: rulesListView
anchors.fill: parent
model: RulesFilterModel {
id: rulesFilterModel
rules: Engine.ruleManager.rules
filterEventDeviceId: root.device.id
}
delegate: ItemDelegate {
text: model.name
}
}
Column {
anchors.centerIn: parent
width: parent.width - app.margins * 2
spacing: app.margins * 2
visible: rulesListView.count == 0
Label {
width: parent.width
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
text: qsTr("There's no magic involving %1.").arg(root.device.name)
font.pixelSize: app.largeFont
}
Label {
width: parent.width
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
text: qsTr("Add some using the wizard stick!")
font.pixelSize: app.largeFont
}
AbstractButton {
height: app.iconSize * 4
width: height
anchors.horizontalCenter: parent.horizontalCenter
ColorIcon {
anchors.fill: parent
name: "../images/magic.svg"
}
onClicked: addRule()
}
}
}

View File

@ -4,6 +4,9 @@ import "../components"
Page {
id: root
property var device: null
header: GuhHeader {
text: "New rule"
onBackPressed: pageStack.pop()

View File

@ -0,0 +1,75 @@
import QtQuick 2.4
import QtQuick.Controls 2.1
import "../components"
import Guh 1.0
Page {
id: root
property alias text: header.text
property var device: null
readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
header: GuhHeader {
id: header
onBackPressed: pageStack.pop()
}
ListModel {
id: eventModel
ListElement { interfaceName: "temperaturesensor"; text: "When it's freezing..."; identifier: "freeze"}
ListElement { interfaceName: "battery"; text: "When the device runs out of battery..."; identifier: "lowBattery"}
ListElement { interfaceName: "weather"; text: "When it starts raining..."; identifier: "rain" }
}
ListModel {
id: actionModel
ListElement { interfaceName: "light"; text: "Switch light when..."; identifier: "switchLight"}
ListElement { interfaceName: "dimmablelight"; text: "Dim light when..."; identifier: "dimLight"}
ListElement { interfaceName: "colorlight"; text: "Set light color when..."; identifier: "colorLight" }
ListElement { interfaceName: "mediacontroller"; text: "Pause playback when..."; identifier: "pausePlayback" }
ListElement { interfaceName: "mediacontroller"; text: "Resume playback when..."; identifier: "resumePlayback" }
ListElement { interfaceName: "extendedvolumecontroller"; text: "Set volume..."; identifier: "setVolume" }
ListElement { interfaceName: "extendedvolumecontroller"; text: "Mute when..."; identifier: "mute" }
ListElement { interfaceName: "extendedvolumecontroller"; text: "Unmute when..."; identifier: "unmute" }
ListElement { interfaceName: "notifications"; text: "Notify me when..."; identifier: "notify" }
}
function entrySelected(identifier) {
switch (identifier) {
case "freeze":
pageStack.push(Qt.resolvedUrl("SelectActionPage.qml"), {device: root.device })
}
}
onDeviceClassChanged: {
actualModel.clear()
print("device supports interfaces", deviceClass.interfaces)
for (var i = 0; i < eventModel.count; i++) {
print("event is for interface", eventModel.get(i).interfaceName)
if (deviceClass.interfaces.indexOf(eventModel.get(i).interfaceName) >= 0) {
actualModel.append(eventModel.get(i))
}
}
print("huh")
for (var i = 0; i < actionModel.count; i++) {
print("action is for interface", actionModel.get(i).interfaceName)
if (deviceClass.interfaces.indexOf(actionModel.get(i).interfaceName) >= 0) {
actualModel.append(actionModel.get(i))
}
}
}
ListView {
anchors.fill: parent
model: ListModel {
id: actualModel
}
delegate: ItemDelegate {
width: parent.width
text: model.text
onClicked: root.entrySelected(model.identifier)
}
}
}

View File

@ -21,6 +21,45 @@ Page {
onBackPressed: pageStack.pop()
}
ListModel {
id: actionModel
ListElement { interfaceName: "light"; text: "Switch lights..."; identifier: "switchLights" }
ListElement { interfaceName: "mediacontroller"; text: "Control media playback..."; identifier: "controlMedia" }
ListElement { interfaceName: "extendedvolumecontroller"; text: "Mute media playback..."; identifier: "muteMedia" }
ListElement { interfaceName: "notifications"; text: "Notify me..."; identifier: "notify" }
ListElement { interfaceName: ""; text: "Manually configure an action..."; identifier: "manualAction" }
}
DevicesProxy {
id: ifaceFilterModel
devices: Engine.deviceManager.devices
}
Component.onCompleted: {
actualModel.clear()
for (var i = 0; i < actionModel.count; i++) {
ifaceFilterModel.filterInterface = actionModel.get(i).interfaceName;
if (actionModel.get(i).interfaceName === "" || ifaceFilterModel.count > 0) {
actualModel.append(actionModel.get(i))
}
}
}
function actionSelected(identifier) {
switch (identifier) {
case "switchLights":
pageStack.push(switchLightsCompoent)
break;
case "controlMedia":
break;
case "muteMedia":
break;
case "manualAction":
pageStack.push(selectDeviceComponent)
break;
}
}
ColumnLayout {
anchors.fill: parent
@ -35,26 +74,13 @@ Page {
Layout.fillWidth: true
Layout.fillHeight: true
model: ListModel {
ListElement { name: "switchLights"; text: "Switch lights..." }
ListElement { name: "controlMedia"; text: "Control media playback..." }
ListElement { name: "manualAction"; text: "Manually configure an action..." }
id: actualModel
}
delegate: ItemDelegate {
width: parent.width
text: model.text
onClicked: {
switch (model.name) {
case "switchLights":
pageStack.push(switchLightsCompoent)
break;
case "controlMedia":
break;
case "muteMedia":
break;
case "manualAction":
pageStack.push(selectDeviceComponent)
break;
}
root.actionSelected(model.identifier)
}
}
}

View File

@ -0,0 +1,46 @@
import QtQuick 2.4
import QtQuick.Controls 2.1
import "../components"
import Guh 1.0
Page {
id: root
property alias text: header.text
property var device: null
readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
header: GuhHeader {
id: header
onBackPressed: pageStack.pop()
}
ListModel {
id: eventModel
ListElement { interfaceName: "temperaturesensor"; text: "When it's freezing..."; event: "freeze"}
ListElement { interfaceName: "battery"; text: "When the device runs out of battery..."; event: "lowBattery"}
ListElement { interfaceName: "weather"; text: "When it starts raining..."; event: "rain" }
}
onDeviceClassChanged: {
actualModel.clear()
print("device supports interfaces", deviceClass.interfaces)
for (var i = 0; i < eventModel.count; i++) {
print("event is for interface", eventModel.get(i).interfaceName)
if (deviceClass.interfaces.indexOf(eventModel.get(i).interfaceName) >= 0) {
actualModel.append(eventModel.get(i))
}
}
}
ListView {
anchors.fill: parent
model: ListModel {
id: actualModel
}
delegate: ItemDelegate {
text: model.text
}
}
}

View File

@ -117,18 +117,28 @@ ApplicationWindow {
return "Gateways"
case "notifications":
return "Notifications"
case "temperaturesensor":
return "Temperature";
case "humiditysensor":
return "Humidity";
}
}
function interfaceToIcon(name) {
switch (name) {
case "light":
case "colorlight":
case "dimmablelight":
return Qt.resolvedUrl("images/torch-on.svg")
case "sensor":
return Qt.resolvedUrl("images/sensors.svg")
case "media":
case "mediacontroller":
return Qt.resolvedUrl("images/mediaplayer-app-symbolic.svg")
case "button":
case "longpressbutton":
case "multibutton":
case "longpressmultibutton":
return Qt.resolvedUrl("images/system-shutdown.svg")
case "weather":
return Qt.resolvedUrl("images/weather-app-symbolic.svg")

View File

@ -10,6 +10,7 @@ class LogEntry : public QObject
Q_OBJECT
Q_PROPERTY(QVariant value READ value CONSTANT)
Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT)
Q_PROPERTY(QString timeString READ timeString CONSTANT)
Q_PROPERTY(QString dayString READ dayString CONSTANT)
Q_PROPERTY(QString dateString READ dateString CONSTANT)

View File

@ -22,22 +22,22 @@
#include "param.h"
Param::Param(const QString &name, const QVariant &value, QObject *parent) :
Param::Param(const QString &id, const QVariant &value, QObject *parent) :
QObject(parent),
m_name(name),
m_id(id),
m_value(value)
{
}
QString Param::name() const
QString Param::id() const
{
return m_name;
return m_id;
}
void Param::setName(const QString &name)
void Param::setId(const QString &id)
{
m_name = name;
emit nameChanged();
m_id = id;
emit idChanged();
}
QVariant Param::value() const

View File

@ -30,24 +30,24 @@
class Param : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged)
Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged)
public:
Param(const QString &name = QString(), const QVariant &value = QVariant(), QObject *parent = 0);
Param(const QString &id = QString(), const QVariant &value = QVariant(), QObject *parent = 0);
QString name() const;
void setName(const QString &name);
QString id() const;
void setId(const QString &id);
QVariant value() const;
void setValue(const QVariant &value);
private:
QString m_name;
QString m_id;
QVariant m_value;
signals:
void nameChanged();
void idChanged();
void valueChanged();
};

View File

@ -44,10 +44,10 @@ Param *Params::get(int index) const
return m_params.at(index);
}
Param *Params::getParam(QString name) const
Param *Params::getParam(QString paramTypeId) const
{
foreach (Param *param, m_params) {
if (param->name() == name) {
if (param->id() == paramTypeId) {
return param;
}
}
@ -71,9 +71,9 @@ QVariant Params::data(const QModelIndex &index, int role) const
return QVariant();
Param *param = m_params.at(index.row());
if (role == NameRole) {
return param->name();
} else if (role == ValueRole) {
if (role == RoleId) {
return param->id();
} else if (role == RoleValue) {
return param->value();
}
return QVariant();
@ -97,7 +97,7 @@ void Params::clearModel()
QHash<int, QByteArray> Params::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[ValueRole] = "value";
roles[RoleId] = "id";
roles[RoleValue] = "value";
return roles;
}

View File

@ -31,9 +31,9 @@ class Params : public QAbstractListModel
{
Q_OBJECT
public:
enum ParamRole {
NameRole = Qt::DisplayRole,
ValueRole
enum RoleId {
RoleId,
RoleValue
};
explicit Params(QObject *parent = 0);
@ -42,7 +42,7 @@ public:
Q_INVOKABLE int count() const;
Q_INVOKABLE Param *get(int index) const;
Q_INVOKABLE Param *getParam(QString name) const;
Q_INVOKABLE Param *getParam(QString paramTypeId) const;
Q_INVOKABLE int paramCount() const;

View File

@ -57,6 +57,16 @@ void ParamType::setName(const QString &name)
m_name = name;
}
QString ParamType::displayName() const
{
return m_displayName;
}
void ParamType::setDisplayName(const QString &displayName)
{
m_displayName = displayName;
}
QString ParamType::type() const
{
return m_type;

View File

@ -35,6 +35,7 @@ class ParamType : public QObject
Q_OBJECT
Q_PROPERTY(QUuid id READ id CONSTANT)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString displayName READ displayName CONSTANT)
Q_PROPERTY(QString type READ type CONSTANT)
Q_PROPERTY(int index READ index CONSTANT)
Q_PROPERTY(QVariant defaultValue READ defaultValue CONSTANT)
@ -55,6 +56,9 @@ public:
QString name() const;
void setName(const QString &name);
QString displayName() const;
void setDisplayName(const QString &displayName);
QString type() const;
void setType(const QString &type);
@ -88,6 +92,7 @@ public:
private:
QUuid m_id;
QString m_name;
QString m_displayName;
QString m_type;
int m_index;
QVariant m_defaultValue;

View File

@ -37,10 +37,10 @@ ParamType *ParamTypes::get(int index) const
return m_paramTypes.at(index);
}
ParamType *ParamTypes::getParamType(const QString &name) const
ParamType *ParamTypes::getParamType(const QString &id) const
{
foreach (ParamType *paramType, m_paramTypes) {
if (paramType->name() == name) {
if (paramType->id() == id) {
return paramType;
}
}

View File

@ -49,7 +49,7 @@ public:
QList<ParamType *> paramTypes();
Q_INVOKABLE ParamType *get(int index) const;
Q_INVOKABLE ParamType *getParamType(const QString &name) const;
Q_INVOKABLE ParamType *getParamType(const QString &id) const;
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;