More work on all sorts of things pages

pull/666/head
Michael Zanetti 2021-09-20 16:57:59 +02:00
parent 554575698b
commit 1661dd4eba
28 changed files with 1158 additions and 992 deletions

View File

@ -144,9 +144,13 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
addInterface("heating", tr("Heating"));
addStateType("heating", "power", QVariant::Bool, true, tr("Heating enabled"), tr("Heating enabled changed"), tr("Enable heating"));
addStateType("heating", "percentage", QVariant::Int, true, tr("Percentage"), tr("Percentage changed"), tr("Set percentage"), 0, 100);
addInterface("cooling", tr("Cooling"));
addStateType("cooling", "power", QVariant::Bool, true, tr("Cooling enabled"), tr("Cooling enabled changed"), tr("Enable cooling"));
addStateType("cooling", "percentage", QVariant::Int, true, tr("Percentage"), tr("Percentage changed"), tr("Set percentage"), 0, 100);
addInterface("extendedheating", tr("Heatings"), {"heating"});
addStateType("extendedheating", "percentage", QVariant::Int, true, tr("Percentage"), tr("Percentage changed"), tr("Set percentage"), 0, 100);
addInterface("media", tr("Media"));

View File

@ -32,7 +32,8 @@
#include "thingclass.h"
#include "thingmanager.h"
#include <QDebug>
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(dcThingManager)
Thing::Thing(ThingManager *thingManager, ThingClass *thingClass, const QUuid &parentId, QObject *parent) :
QObject(parent),
@ -220,6 +221,10 @@ void Thing::setStateValue(const QUuid &stateTypeId, const QVariant &value)
int Thing::executeAction(const QString &actionName, const QVariantList &params)
{
ActionType *actionType = m_thingClass->actionTypes()->findByName(actionName);
if (!actionType) {
qCWarning(dcThingManager) << "No such action name" << actionName << "in thing class" << m_thingClass->name();
return -1;
}
QVariantList finalParams;
foreach (const QVariant &paramVariant, params) {

View File

@ -254,5 +254,8 @@
<file>ui/system/ZigbeeNetworkPage.qml</file>
<file>ui/components/ConnectionInfoDialog.qml</file>
<file>ui/components/ButtonControls.qml</file>
<file>ui/components/CircleBackground.qml</file>
<file>ui/devicepages/CoolingThingPage.qml</file>
<file>ui/devicepages/EvChargerThingPage.qml</file>
</qresource>
</RCC>

View File

@ -149,6 +149,7 @@ ApplicationWindow {
"powersocket",
"thermostat",
"heating",
"cooling",
"smartlock",
"doorbell",
"irrigation",

View File

@ -80,7 +80,8 @@ Item {
"smartmeterconsumer": "orange",
"smartmeterproducer": "lightgreen",
"energymeter": "deepskyblue",
"heating" : "gainsboro",
"heating" : "crimson",
"cooling": "dodgerBlue",
"thermostat": "dodgerblue",
"irrigation": "lightblue",
"windspeedsensor": "blue",
@ -88,7 +89,9 @@ Item {
"watersensor": "aqua",
"phsensor": "green",
"o2sensor": "lightblue",
"orpsensor": "yellow"
"orpsensor": "yellow",
"powersocket": "aquamarine",
"evcharger": "limegreen"
}
property var stateColors: {
@ -97,7 +100,10 @@ Item {
"currentPower": "deepskyblue",
}
property color red: "#952727"
property color red: "indianred"
property color green: "mediumseagreen"
property color yellow: "gold"
property color white: "white"
property color gray: "gray"
property color darkGray: "darkGray"

View File

@ -40,7 +40,7 @@ Item {
property Thing thing: null
readonly property StateType colorTemperatureStateType: root.thing.thingClass.stateTypes.findByName("brightness")
readonly property StateType brightnessStateType: root.thing.thingClass.stateTypes.findByName("brightness")
property int value: thing.stateByName("brightness").value
@ -49,7 +49,7 @@ Item {
ActionQueue {
id: actionQueue
thing: root.thing
stateType: root.colorTemperatureStateType
stateName: "brightness"
}
Rectangle {
@ -94,8 +94,8 @@ Item {
anchors.fill: parent
anchors.margins: -Style.smallMargins
onPositionChanged: {
var minCt = root.colorTemperatureStateType.minValue;
var maxCt = root.colorTemperatureStateType.maxValue
var minCt = root.brightnessStateType.minValue;
var maxCt = root.brightnessStateType.maxValue
var ct;
if (root.orientation == Qt.Horizontal) {
ct = Math.min(maxCt, Math.max(minCt, (mouseX * (maxCt - minCt) / (width - dragHandle.width)) + minCt))

View File

@ -0,0 +1,98 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, GNU version 3. This project is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtGraphicalEffects 1.0
import Nymea 1.0
import "../utils"
Item {
id: root
implicitHeight: 400
implicitWidth: 400
property alias iconSource: icon.name
property color onColor: Style.accentColor
property bool on: false
readonly property Item contentItem: background
signal clicked()
Rectangle {
id: background
anchors.centerIn: parent
height: Math.min(400, Math.min(parent.height, parent.width))
width: height
radius: width / 2
color: Style.tileBackgroundColor
}
MouseArea {
anchors.fill: background
onClicked: root.clicked()
}
ColorIcon {
id: icon
anchors.centerIn: background
size: Style.hugeIconSize
color: root.on ? root.onColor : Style.iconColor
Behavior on color { ColorAnimation { duration: Style.animationDuration } }
}
RadialGradient {
id: gradient
anchors.fill: background
visible: false
gradient: Gradient{
GradientStop { position: .45; color: "transparent" }
GradientStop { position: .5; color: root.onColor }
}
}
OpacityMask {
id: opacityMask
opacity: root.on ? 1 : 0
anchors.fill: gradient
source: gradient
maskSource: background
Behavior on opacity { NumberAnimation { duration: Style.animationDuration } }
}
Item {
id: contentContainer
anchors.fill: background
}
}

View File

@ -32,7 +32,7 @@ Item {
ConicalGradient {
id: gradient
anchors.centerIn: parent
width: Math.min(parent.width, parent.height)
width: Math.min(400, Math.min(parent.width, parent.height))
height: width
visible: false
gradient: Gradient{
@ -176,7 +176,7 @@ Item {
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.fill: gradient
onPositionChanged: {
var angle = calculateAngle(mouseX, mouseY)

View File

@ -25,7 +25,7 @@ Item {
Rectangle {
id: background
width: Math.min(parent.width, parent.height)
width: Math.min(400, Math.min(parent.width, parent.height))
anchors.centerIn: parent
height: width
radius: width / 2
@ -35,14 +35,6 @@ Item {
GradientStop { position: 0.5; color: "#ffffea" }
GradientStop { position: 1.0; color: "#ffd649" }
}
}
Desaturate {
anchors.fill: background
source: background
desaturation: root.powerState.value === true ? 0 : 1
Behavior on desaturation { NumberAnimation { duration: Style.animationDuration } }
}
Rectangle {
id: dragHandle
@ -53,13 +45,21 @@ Item {
color: Style.backgroundColor
border.color: Style.foregroundColor
border.width: 2
x: (parent.width - width) / 2
y: parent.height * valuePercentage - (height / 2)
x: (background.width - width) / 2
y: (background.height - height) * valuePercentage
}
}
Desaturate {
anchors.fill: background
source: background
desaturation: root.powerState.value === true ? 0 : 1
Behavior on desaturation { NumberAnimation { duration: Style.animationDuration } }
}
MouseArea {
anchors.fill: parent
anchors.fill: background
onPositionChanged: {
var minCt = root.colorTemperatureStateType.minValue;
var maxCt = root.colorTemperatureStateType.maxValue
@ -68,7 +68,7 @@ Item {
// ct = Math.min(maxCt, Math.max(minCt, (mouseX * (maxCt - minCt) / (width - dragHandle.width)) + minCt))
// } else {
// ct : y = max : height
ct = mouseY * (maxCt - minCt) / height + minCt
ct = mouseY * (maxCt - minCt) / (height) + minCt
ct = Math.min(maxCt, ct)
ct = Math.max(minCt, ct)
// ct = Math.min(maxCt, Math.max(minCt, ((height - mouseY) * (maxCt - minCt) / (height - dragHandle.height)) + minCt))

View File

@ -32,310 +32,164 @@ import QtQuick 2.5
import QtQuick.Controls 2.2
import Nymea 1.0
import QtQuick.Layouts 1.2
import QtQuick.Controls.Material 2.2
import "../utils"
ColumnLayout {
id: dial
Item {
id: root
property Thing thing: null
property alias device: dial.thing
property StateType stateType: null
property string stateName: ""
property StateType stateType: thing ? thing.thingClass.stateTypes.findByName(stateName) : null
property bool showValueLabel: true
property int steps: 10
property color color: Style.accentColor
property int maxAngle: 235
property int precision: 1
// value : max = angle : maxAngle
function valueToAngle(value) {
return (value - from) * maxAngle / (to - from)
}
function angleToValue(angle) {
return (to - from) * angle / maxAngle + from
readonly property State progressState: thing ? thing.states.getState(stateType.id) : null
readonly property State powerState: thing ? thing.stateByName("power") : null
property int startAngle: 135
property int maxAngle: 270
readonly property int steps: canvas.roundToPrecision(root.stateType.maxValue - root.stateType.minValue) / root.precision + 1
readonly property double stepSize: (root.stateType.maxValue - root.stateType.minValue) / steps
readonly property double anglePerStep: maxAngle / steps
ActionQueue {
id: actionQueue
thing: root.thing
stateType: root.stateType
onPendingValueChanged: canvas.requestPaint()
}
readonly property State deviceState: thing && stateType ? thing.states.getState(stateType.id) : null
readonly property double from: dial.stateType ? dial.stateType.minValue : 0
readonly property double to: dial.stateType ? dial.stateType.maxValue : 100
readonly property double anglePerStep: maxAngle / dial.steps
readonly property double startAngle: -(dial.steps * dial.anglePerStep) / 2
readonly property StateType powerStateType: dial.thing.thingClass.stateTypes.findByName("power")
readonly property State powerState: powerStateType ? dial.thing.states.getState(powerStateType.id) : null
QtObject {
id: d
property int pendingActionId: -1
property real valueCache: 0
property bool valueCacheDirty: false
property bool busy: rotateMouseArea.pressed || pendingActionId != -1 || valueCacheDirty
property color onColor: dial.color
property color offColor: "#808080"
property color poweredColor: dial.powerStateType
? (dial.powerState.value === true ? onColor : offColor)
: onColor
function enqueueSetValue(value) {
if (d.pendingActionId == -1) {
executeAction(value);
return;
} else {
valueCache = value
valueCacheDirty = true;
}
ActionQueue {
id: powerActionQueue
thing: root.thing
stateName: "power"
}
function executeAction(value) {
var params = []
var param = {}
param["paramName"] = dial.stateType.name
param["value"] = value
params.push(param)
d.pendingActionId = dial.thing.executeAction(dial.stateType.name, params)
}
}
Connections {
target: engine.thingManager
onExecuteActionReply: {
if (d.pendingActionId == commandId) {
d.pendingActionId = -1
if (d.valueCacheDirty) {
d.executeAction(d.valueCache)
d.valueCacheDirty = false;
}
}
}
}
Connections {
target: dial.thing
onActionExecutionFinished: {
if (id == d.pendingActionId) {
d.pendingActionId = -1;
if (d.valueCacheDirty) {
d.executeAction(d.valueCache)
d.valueCacheDirty = false;
}
}
}
}
Component.onCompleted: rotationButton.rotation = dial.valueToAngle(dial.thingState.value)
Connections {
target: dial.thingState
target: root.progressState
onValueChanged: {
if (!d.busy) {
rotationButton.rotation = dial.valueToAngle(dial.thingState.value)
}
canvas.requestPaint()
}
}
Label {
id: topLabel
Layout.fillWidth: true
property var unit: dial.stateType ? dial.stateType.unit : Types.UnitNone
text: Types.toUiValue(rotateMouseArea.currentValue, unit) + Types.toUiUnit(unit)
font.pixelSize: app.largeFont * 1.5
horizontalAlignment: Text.AlignHCenter
visible: dial.showValueLabel && dial.stateType !== null
}
Item {
id: buttonContainer
Layout.fillWidth: true
Layout.fillHeight: true
Item {
id: innerDial
height: Math.min(parent.height, parent.width) * .9
width: height
anchors.centerIn: parent
rotation: dial.startAngle
Rectangle {
anchors.fill: rotationButton
radius: height / 2
border.color: Style.foregroundColor
border.width: 2
color: "transparent"
opacity: rotateMouseArea.pressed && !rotateMouseArea.grabbed ? .7 : 1
}
Item {
id: rotationButton
height: parent.height * .75
width: height
anchors.centerIn: parent
visible: dial.stateType !== null
Behavior on rotation {
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
enabled: !rotateMouseArea.pressed && !d.busy
}
Item {
id: handle
anchors.horizontalCenter: parent.horizontalCenter
height: parent.height * .35
width: height
// Rectangle { anchors.fill: parent; color: "red"; opacity: .3}
Rectangle {
height: parent.height * .5
width: innerDial.width * 0.02
radius: width / 2
anchors.top: parent.top
anchors.topMargin: height * .25
anchors.horizontalCenter: parent.horizontalCenter
color: d.poweredColor
Behavior on color { ColorAnimation { duration: 200 } }
}
}
}
Repeater {
id: indexLEDs
model: dial.steps + 1
Item {
height: parent.height
width: parent.width * .04
anchors.centerIn: parent
rotation: dial.anglePerStep * index
visible: dial.stateType !== null
Rectangle {
width: parent.width
Canvas {
id: canvas
anchors.centerIn: root
width: Math.min(root.width, root.height)
height: width
radius: width / 2
color: dial.deviceState && dial.angleToValue(parent.rotation) <= dial.deviceState.value ? d.poweredColor : d.offColor
Behavior on color { ColorAnimation { duration: 200 } }
function roundToPrecision(value) {
var tmp = Math.round(value / root.precision) * root.precision;
return tmp;
}
onPaint: {
var ctx = getContext("2d");
ctx.reset();
var center = { x: canvas.width / 2, y: canvas.height / 2 };
// Step lines
var currentValue = actionQueue.pendingValue || root.progressState.value
var currentStep;
if (root.progressState) {
currentStep = roundToPrecision(currentValue - root.stateType.minValue) / root.precision
}
print("* current step", currentStep, root.steps, currentValue)
for(var step = 0; step < steps; step += root.precision) {
var angle = step * anglePerStep + startAngle;
var innerRadius = canvas.width * 0.4
var outerRadius = canvas.width * 0.5
if (step === currentStep) {
ctx.strokeStyle = root.color
innerRadius = canvas.width * 0.38
ctx.lineWidth = 4;
} else {
ctx.strokeStyle = Style.tileOverlayColor;
ctx.lineWidth = 1;
}
ctx.beginPath();
// rotate
//convert to radians
var rad = angle * Math.PI/180;
var c = Math.cos(rad);
var s = Math.sin(rad);
var innerPointX = center.x + (innerRadius * c);
var innerPointY = center.y + (innerRadius * s);
var outerPointX = center.x + (outerRadius * c);
var outerPointY = center.x + (outerRadius * s);
ctx.moveTo(innerPointX, innerPointY);
ctx.lineTo(outerPointX, outerPointY);
ctx.stroke();
ctx.closePath();
}
}
}
Rectangle {
id: buttonBorder
height: innerDial.height * .8
width: height
anchors.centerIn: parent
radius: height / 2
border.color: Style.foregroundColor
opacity: .3
border.width: width * .025
color: "transparent"
}
Label {
anchors { left: innerDial.left; bottom: innerDial.bottom; bottomMargin: innerDial.height * .1 }
text: "MIN"
font.pixelSize: innerDial.height * .06
visible: dial.stateType !== null
}
Label {
anchors { right: innerDial.right; bottom: innerDial.bottom; bottomMargin: innerDial.height * .1 }
text: "MAX"
font.pixelSize: innerDial.height * .06
visible: dial.stateType !== null
}
ColorIcon {
anchors.centerIn: innerDial
height: innerDial.height * .2
width: height
name: "../images/system-shutdown.svg"
visible: dial.powerStateType !== null
color: d.poweredColor
Behavior on color { ColorAnimation { duration: 200 } }
}
MouseArea {
id: rotateMouseArea
anchors.fill: buttonBorder
onPressedChanged: PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackImpact)
// Rectangle { anchors.fill: parent; color: "blue"; opacity: .3}
property bool grabbed: false
onPressed: {
startX = mouseX
startY = mouseY
var mappedToHandle = mapToItem(handle, mouseX, mouseY);
if (mappedToHandle.x >= 0
&& mappedToHandle.x < handle.width
&& mappedToHandle.y >= 0
&& mappedToHandle.y < handle.height
) {
grabbed = true;
return;
}
}
onCanceled: grabbed = false;
anchors.fill: canvas
property bool dragging: false
property double lastAngle
property double angleDiff: 0
onPressed: {
angleDiff = 0
lastAngle = calculateAngle(mouseX, mouseY)
}
onReleased: {
grabbed = false;
if (dial.powerStateType && !dragging) {
var params = []
var param = {}
param["paramName"] = "power"
param["value"] = !dial.powerState.value
params.push(param)
dial.thing.executeAction("power", params)
}
dragging = false;
}
readonly property int decimals: dial.stateType && dial.stateType.type.toLowerCase() === "int" ? 0 : 1
property var currentValue: dial.deviceState ? dial.deviceState.value.toFixed(decimals) : 0
property date lastVibration: new Date()
property int startX
property int startY
onPositionChanged: {
if (Math.abs(mouseX - startX) > 10 || Math.abs(mouseY - startY) > 10) {
dragging = true;
}
if (!grabbed) {
return;
}
var angle = calculateAngle(mouseX, mouseY)
angle = (360 + angle - dial.startAngle) % 360;
if (angle > 360 - ((360 - dial.maxAngle) / 2)) {
angle = 0;
} else if (angle > dial.maxAngle) {
angle = dial.maxAngle
}
var newValue = Math.round(dial.angleToValue(angle) * 2) / 2;
rotationButton.rotation = angle;
newValue = newValue.toFixed(decimals)
if (newValue != currentValue) {
currentValue = newValue;
if (newValue <= dial.stateType.minValue || newValue >= dial.stateType.maxValue) {
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackImpact)
} else {
if (lastVibration.getTime() + 35 < new Date()) {
if (!dragging && root.powerState) {
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection)
powerActionQueue.sendValue(!root.powerState.value)
}
lastVibration = new Date()
dragging = false
}
d.enqueueSetValue(newValue);
onPositionChanged: {
var angle = calculateAngle(mouseX, mouseY)
var tmpDiff = angle - lastAngle
if (tmpDiff > 300) {
tmpDiff -= 360
}
if (tmpDiff < -300) {
tmpDiff += 360
}
lastAngle = angle;
angleDiff += tmpDiff
if (Math.abs(angleDiff) > 1) {
dragging = true
}
var valueDiff = angleDiff / root.anglePerStep * root.stepSize
valueDiff = canvas.roundToPrecision(valueDiff)
if (Math.abs(valueDiff) > 0) {
var currentValue = actionQueue.pendingValue || root.progressState.value
var newValue = currentValue + valueDiff
newValue = Math.min(root.stateType.maxValue, Math.max(root.stateType.minValue, newValue))
if (currentValue !== newValue) {
actionQueue.sendValue(newValue)
}
var steps = Math.round(valueDiff / root.stepSize)
angleDiff -= steps * root.anglePerStep
}
}
function calculateAngle(mouseX, mouseY) {
// transform coords to center of dial
mouseX -= innerDial.width / 2
mouseY -= innerDial.height / 2
mouseX -= canvas.width / 2
mouseY -= canvas.height / 2
var rad = Math.atan(mouseY / mouseX);
var angle = rad * 180 / Math.PI
@ -349,4 +203,3 @@ ColumnLayout {
}
}
}
}

View File

@ -46,7 +46,8 @@ Item {
property alias color: icon.color
property alias backgroundColor: background.color
property bool longpressEnabled: true
property bool longpressEnabled: false
property bool busy: false
property int size: Style.iconSize
@ -100,6 +101,16 @@ Item {
}
}
NumberAnimation {
target: busyCanvas
property: "rotation"
from: 360
to: 0
duration: 2000
loops: Animation.Infinite
running: root.busy
}
Rectangle {
id: background
anchors.fill: parent
@ -136,9 +147,8 @@ Item {
property bool inverted: false
readonly property int penWidth: 2
onProgressChanged: {
requestPaint()
}
onProgressChanged: requestPaint()
Connections {
target: buttonDelegate
onPressedChanged: {
@ -152,6 +162,10 @@ Item {
onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw longpress progress
if (canvas.progress > 0) {
ctx.save();
ctx.fillStyle = Qt.rgba(1, 0, 0, 1);
ctx.lineWidth = canvas.penWidth
ctx.strokeStyle = Style.accentColor
@ -167,6 +181,30 @@ Item {
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, ((canvas.width - canvas.penWidth) / 2), start, stop);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
}
}
Canvas {
id: busyCanvas
visible: root.busy
anchors.fill: parent
anchors.margins: -4
onPaint: {
var ctx = getContext("2d");
ctx.save();
ctx.lineWidth = 2;
ctx.strokeStyle = root.backgroundColor;
var radius = (width - ctx.lineWidth) / 2
var circumference = 2 * Math.PI * radius
var dashRatio = circumference / 6 / ctx.lineWidth
ctx.setLineDash([dashRatio, dashRatio])
ctx.beginPath();
ctx.arc(width / 2, height / 2, (width - 2) / 2, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
}
}

View File

@ -30,13 +30,13 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2
import QtQuick.Layouts 1.3
import Nymea 1.0
RowLayout {
Item {
id: root
spacing: 0
implicitHeight: size * 4
implicitWidth: size * 7
property Thing thing: null
readonly property State openState: thing.stateByName("state")
@ -48,17 +48,27 @@ RowLayout {
signal activated(string button);
RowLayout {
anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter }
spacing: 0
Item { Layout.fillWidth: true; Layout.fillHeight: true }
ProgressButton {
longpressEnabled: false
imageSource: root.invert ? "../images/down.svg" : "../images/up.svg"
mode: root.backgroundEnabled ? "normal" : "transparent"
backgroundColor: root.backgroundEnabled ? Style.green : "transparent"
size: root.size
color: root.openState && root.openState.value === "opening" ? Material.accent : Style.iconColor
busy: root.openState ? root.openState.value === "opening" : openBusyTimer.running
onClicked: {
engine.thingManager.executeAction(root.thing.id, root.thing.thingClass.actionTypes.findByName("open").id)
root.activated("open")
openBusyTimer.start()
closeBusyTimer.stop()
}
Timer {
id: openBusyTimer
interval: 5000
}
}
@ -66,13 +76,14 @@ RowLayout {
ProgressButton {
visible: root.canStop
longpressEnabled: false
mode: root.backgroundEnabled ? "normal" : "transparent"
backgroundColor: root.backgroundEnabled ? Style.yellow : "transparent"
size: root.size
imageSource: "../images/media-playback-stop.svg"
onClicked: {
engine.thingManager.executeAction(root.thing.id, root.thing.thingClass.actionTypes.findByName("stop").id)
root.activated("stop")
openBusyTimer.stop()
closeBusyTimer.stop()
}
}
@ -80,15 +91,22 @@ RowLayout {
ProgressButton {
imageSource: root.invert ? "../images/up.svg" : "../images/down.svg"
longpressEnabled: false
mode: root.backgroundEnabled ? "normal" : "transparent"
backgroundColor: root.backgroundEnabled ? Style.red : "transparent"
size: root.size
color: root.openState && root.openState.value === "closing" ? Material.accent : Style.iconColor
busy: root.openState ? root.openState.value === "closing" : closeBusyTimer.running
onClicked: {
engine.thingManager.executeAction(root.thing.id, root.thing.thingClass.actionTypes.findByName("close").id)
root.activated("close")
openBusyTimer.stop();
closeBusyTimer.start()
}
Timer {
id: closeBusyTimer
interval: 5000
}
}
Item { Layout.fillWidth: true; Layout.fillHeight: true }
}
}

View File

@ -1,6 +1,7 @@
import QtQuick 2.9
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.1
import QtGraphicalEffects 1.0
import "../components"
import Nymea 1.0
@ -42,66 +43,49 @@ Item {
: Math.min(Math.min(parent.width, 500), parent.height - shutterControlsContainer.minimumHeight)
Layout.preferredHeight: width
ColorIcon {
Rectangle {
id: background
anchors.centerIn: parent
size: Math.min(parent.height, parent.width) - Style.hugeMargins * 2
property string currentImage: {
if (root.isExtended) {
return NymeaUtils.pad(Math.round(root.percentageState.value / 10), 2) + "0"
width: Math.min(500, Math.min(parent.width, parent.height) - Style.hugeMargins * 2)
height: width
radius: width / 2
color: Style.tileBackgroundColor
}
if (root.intermediatePositionStateType) {
return root.stateState.value === "closed" ? "100"
: root.intermediatePositionState.value === false ? "000" : "050"
}
return "100"
}
name: "../images/garage/garage-" + currentImage + ".svg"
Item {
id: arrows
id: door
anchors.fill: background
Canvas {
id: canvas
anchors.centerIn: parent
width: Style.iconSize * 2
height: parent.height * .6
clip: true
visible: root.stateStateType && (root.stateState.value === "opening" || root.stateState.value === "closing")
property bool up: root.stateState && root.stateState.value === "opening"
// NumberAnimation doesn't reload to/from while it's running. If we switch from closing to opening or vice versa
// we need to somehow stop and start the animation
property bool animationHack: true
onAnimationHackChanged: {
if (!animationHack) hackTimer.start();
}
Timer { id: hackTimer; interval: 1; onTriggered: arrows.animationHack = true }
Connections { target: root.stateState; onValueChanged: arrows.animationHack = false }
NumberAnimation {
target: arrowColumn
property: "y"
duration: 500
easing.type: Easing.Linear
from: arrows.up ? Style.iconSize : -Style.iconSize
to: arrows.up ? -Style.iconSize : Style.iconSize
loops: Animation.Infinite
running: arrows.animationHack && root.stateState && (root.stateState.value === "opening" || root.stateState.value === "closing")
}
Column {
id: arrowColumn
width: parent.width
height: parent.height + Style.margins
anchors.verticalCenterOffset: root.percentageState
? -height * (1 - (root.percentageState.value / 100))
: -height / 2
onPaint: {
var ctx = getContext("2d");
ctx.reset();
Repeater {
model: arrows.height / Style.iconSize + 1
ColorIcon {
name: arrows.up ? "../images/up.svg" : "../images/down.svg"
width: parent.width
height: width
color: Style.accentColor
ctx.fillStyle = Style.tileForegroundColor
var segments = 10;
var segmentHeight = height / segments
var barHeight = segmentHeight - Style.smallMargins
for (var i = 0; i < segments; i++) {
ctx.fillRect(0, i * segmentHeight, width, barHeight)
}
}
}
}
OpacityMask {
anchors.fill: background
source: ShaderEffectSource {
sourceItem: door
hideSource: true
}
maskSource: background
}
}
@ -109,19 +93,27 @@ Item {
id: shutterControlsContainer
Layout.fillWidth: true
Layout.minimumWidth: minimumWidth
Layout.fillHeight: true
Layout.preferredHeight: Style.bigIconSize * 4
property int minimumWidth: Style.iconSize * 10
property int minimumHeight: Style.iconSize * 2.5
ProgressButton {
anchors.centerIn: parent
mode: "highlight"
visible: root.isImpulseBased
longpressEnabled: false
size: Style.bigIconSize
imageSource: "../images/closable-move.svg"
busy: busyTimer.running
onClicked: {
var actionTypeId = root.thing.thingClass.actionTypes.findByName("triggerImpulse").id
print("Triggering impulse", actionTypeId)
engine.thingManager.executeAction(root.thing.id, actionTypeId)
busyTimer.start();
}
Timer {
id: busyTimer
interval: 5000
}
}
@ -132,7 +124,6 @@ Item {
anchors.centerIn: parent
backgroundEnabled: true
size: Style.bigIconSize
spacing: (parent.width - Style.iconSize*2*children.length) / (children.length - 1)
visible: !root.isImpulseBased
}
}

View File

@ -90,9 +90,9 @@ Item {
if (targetTempStep === step) {
if (currentTempStep && currentTempStep < targetTempStep) {
ctx.strokeStyle = "red";
ctx.strokeStyle = app.interfaceToColor("heating");
} else if (currentTempStep && currentTempStep > targetTempStep) {
ctx.strokeStyle = "dodgerblue";
ctx.strokeStyle = app.interfaceToColor("cooling");
} else {
ctx.strokeStyle = Style.accentColor;
}
@ -100,16 +100,16 @@ Item {
ctx.lineWidth = 4;
} else if (currentTempStep && currentTempStep === step) {
if (currentTempStep < targetTempStep) {
ctx.strokeStyle = "red";
ctx.strokeStyle = app.interfaceToColor("heating");
} else {
ctx.strokeStyle = "dodgerblue";
ctx.strokeStyle = app.interfaceToColor("cooling");
}
ctx.lineWidth = 3;
} else if (currentTempStep && currentTempStep < step && step < targetTempStep) {
ctx.strokeStyle = "red";
ctx.strokeStyle = app.interfaceToColor("heating");
ctx.lineWidth = 2;
} else if (currentTempStep && currentTempStep > step && step > targetTempStep) {
ctx.strokeStyle = "dodgerblue";
ctx.strokeStyle = app.interfaceToColor("cooling");
ctx.lineWidth = 2;
} else {
ctx.strokeStyle = Style.tileOverlayColor;
@ -127,8 +127,8 @@ Item {
var outerPointX = center.x + (outerRadius * c);
var outerPointY = center.x + (outerRadius * s);
context.moveTo(innerPointX, innerPointY);
context.lineTo(outerPointX, outerPointY);
ctx.moveTo(innerPointX, innerPointY);
ctx.lineTo(outerPointX, outerPointY);
ctx.stroke();
ctx.closePath();
}
@ -166,16 +166,16 @@ Item {
ColorIcon {
width: Style.largeIconSize
height: width
anchors { bottom: canvas.bottom; horizontalCenter: canvas.horizontalCenter }
anchors { bottom: canvas.bottom; horizontalCenter: canvas.horizontalCenter; margins: Style.margins }
name: root.heatingOnState && root.heatingOnState.value === true
? "../images/thermostat/heating.svg"
: root.coolingOnState && root.coolingOnState.value === true
? "../images/thermostat/cooling.svg"
: ""
color: root.heatingOnState && root.heatingOnState.value === true
? "red"
? app.interfaceToColor("heating")
: root.coolingOnState && root.coolingOnState.value === true
? "dodgerblue"
? app.interfaceToColor("cooling")
: Style.iconColor
}

View File

@ -74,6 +74,7 @@ MainPageTile {
// Open interface specific things list
switch (iface.name) {
case "heating":
case "cooling":
case "sensor":
page = "SensorsDeviceListPage.qml"
break;
@ -172,6 +173,7 @@ MainPageTile {
case "extendedsmartmeterconsumer":
case "extendedsmartmeterproducer":
case "heating":
case "cooling":
case "thermostat":
return sensorComponent;
// return labelComponent;

View File

@ -143,6 +143,8 @@ ThingsListPageBase {
ShutterControls {
id: shutterControls
Layout.fillWidth: false
Layout.preferredWidth: Style.iconSize * 5
Layout.preferredHeight: Style.iconSize
height: parent.height
thing: itemDelegate.thing
invert: root.invertControls

View File

@ -108,6 +108,26 @@ ThingsListPageBase {
ThingStatusIcons {
thing: itemDelegate.thing
}
ProgressButton {
visible: itemDelegate.thing.thingClass.interfaces.indexOf("impulsegaragedoor") >= 0
imageSource: "../images/closable-move.svg"
onClicked: {
var actionTypeId = itemDelegate.thing.thingClass.actionTypes.findByName("triggerImpulse").id
engine.thingManager.executeAction(itemDelegate.thing.id, actionTypeId)
}
}
ShutterControls {
visible: itemDelegate.thing.thingClass.interfaces.indexOf("simpleclosable") >= 0
id: shutterControls
Layout.fillWidth: false
Layout.preferredWidth: Style.iconSize * 5
Layout.preferredHeight: Style.iconSize
height: parent.height
thing: itemDelegate.thing
invert: root.invertControls
enabled: itemDelegate.isEnabled
}
}
}
}

View File

@ -30,11 +30,12 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.2
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.1
import Nymea 1.0
import "../components"
import "../customviews"
import "../utils"
ThingPageBase {
id: root
@ -48,75 +49,71 @@ ThingPageBase {
anchors.fill: parent
columns: root.landscape ? 2 : 1
Item {
id: shutterImage
Layout.preferredWidth: root.landscape ?
Math.min(parent.width - shutterControlsContainer.minimumWidth, parent.height)
: Math.min(Math.min(500, parent.width), parent.height - shutterControlsContainer.minimumHeight)
Layout.preferredHeight: width
ColorIcon {
anchors.centerIn: parent
size: Math.min(parent.height, parent.width) - Style.hugeMargins * 2
name: root.isExtended ?
"../images/awning/awning-" + app.pad(Math.round(root.percentageState.value / 10) * 10, 3) + ".svg"
: "../images/awning/awning-100.svg"
}
}
Item {
id: shutterControlsContainer
Layout.fillWidth: true
CircleBackground {
id: background
Layout.fillHeight: true
Layout.margins: app.margins * 2
property int minimumWidth: Style.iconSize * 2.7 * 3
property int minimumHeight: Style.iconSize * 4.5
ColumnLayout {
anchors.centerIn: parent
width: parent.width
spacing: app.margins
Slider {
id: percentageSlider
Layout.fillWidth: true
from: 0
to: 100
stepSize: 1
visible: isExtended
Layout.margins: Style.hugeMargins
onColor: Style.yellow
on: true
iconSource: "weathericons/weather-clear-day"
Binding {
target: percentageSlider
property: "value"
value: root.percentageState.value
when: !percentageSlider.pressed
ActionQueue {
id: actionQueue
thing: root.thing
stateName: "percentage"
}
onPressedChanged: {
if (pressed) {
return
}
print("should move", value)
Item {
id: awning
anchors.fill: parent
var actionType = root.thing.thingClass.actionTypes.findByName("percentage");
var params = [];
var percentageParam = {}
percentageParam["paramTypeId"] = actionType.paramTypes.findByName("percentage").id;
percentageParam["value"] = value
params.push(percentageParam);
engine.thingManager.executeAction(root.thing.id, actionType.id, params);
Rectangle {
anchors.centerIn: parent
width: background.contentItem.width
height: background.contentItem.height
property real progress: root.percentageState ?
dragArea.pressed ? dragArea.draggedProgress : root.percentageState.value / 100
: .5
anchors.verticalCenterOffset: -height * (1 - progress)
color: Style.tileOverlayColor
}
}
OpacityMask {
anchors.fill: background
source: ShaderEffectSource {
sourceItem: awning
hideSource: true
}
maskSource: background
}
MouseArea {
id: dragArea
anchors.centerIn: parent
width: background.contentItem.width
height: background.contentItem.height
property real draggedProgress: mouseY / height
onMouseYChanged: print("mouseY", mouseY, draggedProgress)
onReleased: {
actionQueue.sendValue(draggedProgress * 100)
}
}
}
ShutterControls {
id: shutterControls
Layout.fillWidth: true
Layout.minimumWidth: implicitWidth
Layout.preferredHeight: implicitHeight
thing: root.thing
size: Style.bigIconSize
backgroundEnabled: true
invert: true
Layout.fillWidth: true
}
}
}
}
}

View File

@ -0,0 +1,59 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, GNU version 3. This project is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Material 2.1
import Nymea 1.0
import "../components"
import "../utils"
ThingPageBase {
id: root
readonly property State powerState: thing.stateByName("power")
ActionQueue {
id: actionQueue
thing: root.thing
stateName: "power"
}
CircleBackground {
anchors.fill: parent
anchors.margins: Style.hugeMargins
iconSource: "thermostat/cooling"
onColor: app.interfaceToColor("cooling")
on: actionQueue.pendingValue || root.powerState.value
onClicked: actionQueue.sendValue(!root.powerState.value)
}
}

View File

@ -0,0 +1,74 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, GNU version 3. This project is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import Nymea 1.0
import "../components"
import "../utils"
ThingPageBase {
id: root
readonly property State powerState: thing.stateByName("power")
readonly property State maxChargingCurrentState: thing.stateByName("maxChargingCurrent")
readonly property StateType maxChargingCurrentStateType: thing.thingClass.stateTypes.findByName("maxChargingCurrent")
ActionQueue {
id: actionQueue
thing: root.thing
stateName: "power"
}
CircleBackground {
id: background
anchors.fill: parent
anchors.margins: Style.hugeMargins
iconSource: "ev-charger"
onColor: app.interfaceToColor("evcharger")
on: (actionQueue.pendingValue || powerState.value) === true
onClicked: {
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection)
actionQueue.sendValue(!root.powerState.value)
}
}
Dial {
anchors.centerIn: parent
height: background.contentItem.height
width: background.contentItem.width
visible: root.maxChargingCurrentState
thing: root.thing
stateName: "maxChargingCurrent"
color: app.interfaceToColor("evcharger")
}
}

View File

@ -34,58 +34,26 @@ import QtQuick.Layouts 1.1
import QtQuick.Controls.Material 2.1
import Nymea 1.0
import "../components"
import "../utils"
ThingPageBase {
id: root
readonly property State powerState: thing.stateByName("power")
readonly property ActionType powerActionType: thing.thingClass.actionTypes.findByName("power");
GridLayout {
ActionQueue {
id: actionQueue
thing: root.thing
stateName: "power"
}
CircleBackground {
anchors.fill: parent
anchors.margins: app.margins
columns: app.landscape ? 2 : 1
rowSpacing: app.margins
columnSpacing: app.margins
Layout.alignment: Qt.AlignCenter
Item {
Layout.preferredWidth: Math.max(Style.iconSize * 6, parent.width / 5)
Layout.preferredHeight: width
Layout.topMargin: app.margins
Layout.bottomMargin: app.landscape ? app.margins : 0
Layout.alignment: Qt.AlignCenter
Layout.rowSpan: app.landscape ? 4 : 1
Layout.fillHeight: true
AbstractButton {
height: Math.min(parent.height, parent.width)
width: height
anchors.centerIn: parent
Rectangle {
anchors.fill: parent
color: "transparent"
border.color: root.powerState.value === true ? Style.accentColor : Style.iconColor
border.width: 4
radius: width / 2
}
ColorIcon {
id: bulbIcon
anchors.fill: parent
anchors.margins: app.margins * 1.5
name: "../images/thermostat/heating.svg"
color: root.powerState.value === true ? Style.accentColor : Style.iconColor
}
onClicked: {
var params = []
var param = {}
param["paramTypeId"] = root.powerActionType.paramTypes.get(0).id;
param["value"] = !root.powerState.value;
params.push(param)
engine.thingManager.executeAction(root.thing.id, root.powerActionType.id, params);
}
}
}
anchors.margins: Style.hugeMargins
iconSource: "thermostat/heating"
onColor: app.interfaceToColor("heating")
on: actionQueue.pendingValue || root.powerState.value
onClicked: actionQueue.sendValue(!root.powerState.value)
}
}

View File

@ -34,6 +34,7 @@ import QtQuick.Layouts 1.1
import QtQuick.Controls.Material 2.1
import Nymea 1.0
import "../components"
import "../utils"
ThingPageBase {
id: root
@ -149,54 +150,36 @@ ThingPageBase {
filterValue: root.thing.id
}
ActionQueue {
id: actionQueue
thing: root.thing
stateName: "power"
}
GridLayout {
id: mainGrid
anchors.fill: parent
anchors.margins: app.margins
columns: app.landscape ? 2 : 1
Item {
Layout.preferredWidth: app.landscape ? parent.width * .4 : parent.width
Layout.preferredHeight: app.landscape ? parent.height : parent.height *.4
AbstractButton {
height: Math.min(Math.min(parent.height, parent.width), Style.iconSize * 5)
width: height
anchors.centerIn: parent
Rectangle {
anchors.fill: parent
color: "transparent"
border.color: root.powerState.value === true ? Style.accentColor : Style.iconColoor
border.width: 4
radius: width / 2
CircleBackground {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: Style.hugeMargins; Layout.rightMargin: Style.hugeMargins; Layout.topMargin: Style.hugeMargins
iconSource: "irrigation"
onColor: app.interfaceToColor("irrigation")
on: actionQueue.pendingValue || root.powerState.value
onClicked: actionQueue.sendValue(!root.powerState.value)
}
ColorIcon {
id: irrigationIcon
anchors.fill: parent
anchors.margins: app.margins * 1.5
name: "../images/irrigation.svg"
color: root.powerState.value === true ? Style.accentColor : Style.iconColor
}
onClicked: {
var params = []
var param = {}
param["paramTypeId"] = root.powerActionType.paramTypes.get(0).id;
param["value"] = !root.powerState.value;
params.push(param)
engine.thingManager.executeAction(root.thing.id, root.powerStateType.id, params);
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection)
}
}
}
ColumnLayout {
Layout.preferredWidth: app.landscape ? parent.width * .6 : parent.width
Layout.preferredHeight: app.landscape ? parent.height : parent.height * .6
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: Style.margins
Item { Layout.fillWidth: true; Layout.fillHeight: true }
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins
horizontalAlignment: Text.AlignHCenter
text: root.isOn ? qsTr("Watering since")
: history.lastWatering ? qsTr("Last watering")
@ -205,7 +188,7 @@ ThingPageBase {
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins
horizontalAlignment: Text.AlignHCenter
text: history.lastWatering ? Qt.formatDateTime(history.lastWatering) : ""
font.pixelSize: app.largeFont
@ -213,7 +196,7 @@ ThingPageBase {
}
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins; Layout.bottomMargin: Style.margins
horizontalAlignment: Text.AlignHCenter
font.pixelSize: app.smallFont
text: {
@ -252,7 +235,7 @@ ThingPageBase {
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins; Layout.topMargin: Style.margins
horizontalAlignment: Text.AlignHCenter
text: tagsProxy.count > 0 ?
//: Irrigation will be turned of at, e.g. 09:00
@ -299,7 +282,7 @@ ThingPageBase {
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins
horizontalAlignment: Text.AlignHCenter
visible: tagsProxy.count > 0
font.pixelSize: app.largeFont
@ -308,7 +291,7 @@ ThingPageBase {
}
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins; Layout.bottomMargin: Style.margins
horizontalAlignment: Text.AlignHCenter
visible: tagsProxy.count > 0
font.pixelSize: app.smallFont

View File

@ -112,7 +112,7 @@ ThingPageBase {
}
ColumnLayout {
spacing: Style.margins
spacing: Style.hugeMargins
StackLayout {
Layout.fillWidth: true
@ -162,7 +162,7 @@ ThingPageBase {
Rectangle {
id: brightnessCircle
anchors.centerIn: parent
width: Math.min(parent.width, parent.height)
width: Math.min(400, Math.min(parent.width, parent.height))
height: width
radius: width / 2
color: Style.tileBackgroundColor
@ -223,54 +223,24 @@ ThingPageBase {
property Thing thing: root.thing
readonly property State powerState: thing ? thing.stateByName("power") : null
property color borderColor: "#ffd649"
ActionQueue {
id: actionQueue
thing: powerController.thing
stateType: thing.thingClass.stateTypes.findByName("power")
}
Rectangle {
id: background
anchors.centerIn: parent
width: Math.min(parent.width, parent.height)
height: width
color: Style.tileBackgroundColor
radius: width / 2
ColorIcon {
anchors.centerIn: parent
size: Style.hugeIconSize
name: (actionQueue.pendingValue || powerState.value) === true ? "light-on" : "light-off"
// color: (actionQueue.pendingValue || powerState.value) === true ? Style.accentColor : Style.iconColor
}
MouseArea {
CircleBackground {
anchors.fill: parent
anchors.margins: Style.bigMargins
onColor: "#ffd649"
iconSource: (actionQueue.pendingValue || powerState.value) === true ? "light-on" : "light-off"
on: (actionQueue.pendingValue || powerState.value) === true ? 1 : 0
onClicked: {
actionQueue.sendValue(!powerState.value)
}
}
}
RadialGradient {
id: gradient
anchors.fill: background
visible: false
gradient: Gradient{
GradientStop { position: .45; color: "transparent" }
GradientStop { position: .5; color: Qt.rgba(borderColor.r, borderColor.g, borderColor.b, 1) }
}
}
OpacityMask {
opacity: (actionQueue.pendingValue || powerState.value) === true ? 1 : 0
anchors.fill: gradient
source: gradient
maskSource: background
Behavior on opacity { NumberAnimation { duration: Style.animationDuration } }
}
}
}
}

View File

@ -31,61 +31,31 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Material 2.1
import QtGraphicalEffects 1.0
import Nymea 1.0
import "../components"
import "../utils"
ThingPageBase {
id: root
readonly property State powerState: thing.stateByName("power")
readonly property var powerActionType: thing.thingClass.actionTypes.findByName("power");
GridLayout {
anchors.fill: parent
anchors.margins: app.margins
columns: app.landscape ? 2 : 1
rowSpacing: app.margins
columnSpacing: app.margins
Layout.alignment: Qt.AlignCenter
Item {
Layout.preferredWidth: Math.max(Style.iconSize * 6, parent.width / 5)
Layout.preferredHeight: width
Layout.topMargin: app.margins
Layout.bottomMargin: app.landscape ? app.margins : 0
Layout.alignment: Qt.AlignCenter
Layout.rowSpan: app.landscape ? 4 : 1
Layout.fillHeight: true
AbstractButton {
height: Math.min(parent.height, parent.width)
width: height
anchors.centerIn: parent
Rectangle {
anchors.fill: parent
color: "transparent"
border.color: root.powerState.value === true ? Style.accentColor : Style.iconColor
border.width: 4
radius: width / 2
ActionQueue {
id: actionQueue
thing: root.thing
stateName: "power"
}
ColorIcon {
id: bulbIcon
CircleBackground {
anchors.fill: parent
anchors.margins: app.margins * 1.5
name: "../images/powersocket.svg"
color: root.powerState.value === true ? Style.accentColor : Style.iconColor
}
anchors.margins: Style.hugeMargins
iconSource: "../images/powersocket.svg"
onColor: app.interfaceToColor("powersocket")
on: (actionQueue.pendingValue || powerState.value) === true
onClicked: {
var params = []
var param = {}
param["paramTypeId"] = root.powerActionType.paramTypes.get(0).id;
param["value"] = !root.powerState.value;
params.push(param)
engine.thingManager.executeAction(root.thing.id, root.powerActionType.id, params);
}
}
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection)
actionQueue.sendValue(!root.powerState.value)
}
}
}

View File

@ -36,6 +36,7 @@ import QtGraphicalEffects 1.0
import Nymea 1.0
import "../components"
import "../customviews"
import "../utils"
ThingPageBase {
id: root
@ -52,259 +53,391 @@ ThingPageBase {
readonly property bool moving: movingState ? movingState.value === true : false
readonly property int percentage: percentageState ? percentageState.value : 50
readonly property int angle: angleState ? angleState.value : 0
onMovingChanged: if (!moving) angleMovable.visible = false
// onMovingChanged: if (!moving) angleMovable.visible = false
GridLayout {
anchors.fill: parent
columns: root.isVenetian ?
root.landscape ? 3 : 2
: root.landscape ? 2 : 1
// columns: root.isVenetian ?
// root.landscape ? 3 : 2
// : root.landscape ? 2 : 1
columns: root.landscape ? 2 : 1
CircleBackground {
id: background
Layout.fillHeight: true
Layout.fillWidth: true
Layout.margins: Style.hugeMargins
Item {
id: window
id: blind
anchors.fill: parent
Layout.preferredWidth: root.landscape ?
Math.min(parent.width *.4, parent.height)
: Math.min(Math.min(parent.width, 500), (parent.height - shutterControlsContainer.minimumHeight)) / (root.isVenetian ? 2 : 1)
// Layout.preferredWidth: root.landscape ?
// Math.min(parent.width - shutterControlsContainer.minimumWidth, parent.height) - app.margins
// : Math.min(Math.min(parent.width, parent.height - shutterControlsContainer.minimumHeight), 500)
Layout.preferredHeight: root.landscape ?
width
: width * (root.isVenetian ? 2 : 1)
Layout.alignment: root.landscape ? Qt.AlignVCenter : Qt.AlignHCenter
clip: true
Rectangle {
anchors.centerIn: parent
height: parent.height
width: 2
color: Style.accentColor
visible: root.angleState
}
ClosablesControlLarge {
anchors { left: parent.left; top: parent.top; bottom: parent.bottom; }
width: height
Canvas {
id: canvas
anchors.centerIn: parent
anchors.horizontalCenterOffset: root.angleState ? -parent.width / 4 - Style.smallMargins: 0
width: background.contentItem.width / (root.angleState ? 2 : 1)
height: background.contentItem.height
property real progress: root.percentageState ?
percentageDragArea.pressed ? percentageDragArea.draggedProgress : root.percentageState.value / 100
: .5
anchors.verticalCenterOffset: -height * (1 - progress)
onPaint: {
var ctx = getContext("2d");
ctx.reset();
ctx.fillStyle = Style.tileForegroundColor
var segments = 10;
var segmentHeight = height / segments
var barHeight = segmentHeight - Style.smallMargins
for (var i = 0; i < segments; i++) {
ctx.fillRect(0, i * segmentHeight + (segmentHeight - barHeight) / 2, width, barHeight)
}
}
}
ActionQueue {
id: percentageActionQueue
thing: root.thing
ClosableArrowAnimation {
id: arrowAnimation
anchors.centerIn: parent
anchors.horizontalCenterOffset: isVenetian ? -width: 0
onStateChanged: {
if (state != "") {
animationTimer.start();
}
}
Timer {
id: animationTimer
running: false
interval: 5000
repeat: false
onTriggered: parent.state = ""
}
}
}
}
Item {
id: angleControls
Layout.preferredWidth: root.landscape ? window.width / 2 : window.width
Layout.preferredHeight: window.height
visible: root.isVenetian
Item {
anchors.fill: parent
Item {
anchors { fill: parent; topMargin: parent.height * .09; bottomMargin: parent.height * 0.09; leftMargin: app.margins * 2; rightMargin: app.margins * 2 }
Repeater {
model: 10
Item {
width: parent.height * .1
height: width
y: parent.height / 10 * index
Rectangle {
anchors.centerIn: parent
width: parent.width
height: width / 4
rotation: root.angle
color: "#808080"
}
}
}
Item {
id: angleMovable
anchors.fill: parent
property int angle: 0
visible: false
Repeater {
model: 10
Item {
width: parent.height * .1
height: width
y: parent.height / 10 * index
Rectangle {
anchors.centerIn: parent
width: parent.width
height: width / 4
rotation: angleMovable.angle
color: Style.foregroundColor
opacity: 0.1
}
}
}
}
Item {
anchors { top: parent.top; bottom: parent.bottom; right: parent.right; rightMargin: app.margins / 2 }
width: parent.width * .5
Rectangle {
id: angleSlider
anchors.fill: parent
color: Qt.rgba(Style.foregroundColor.r, Style.foregroundColor.g, Style.foregroundColor.b, 0.1)
visible: false
ColorIcon {
anchors { horizontalCenter: parent.horizontalCenter; top: parent.top; topMargin: app.margins }
height: Style.iconSize
width: Style.iconSize
name: "../images/up.svg"
}
ColorIcon {
anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom; bottomMargin: app.margins }
height: Style.iconSize
width: Style.iconSize
name: "../images/down.svg"
}
Rectangle {
width: parent.width
height: 2
color: angleMouseArea.containsMouse ? Style.accentColor : "transparent"
y: angleMouseArea.mouseY
onYChanged: sliderMask.update()
}
}
Rectangle {
id: mask
anchors.fill: parent
radius: Style.cornerRadius
color: "blue"
visible: false
}
OpacityMask {
id: sliderMask
anchors.fill: parent
source: angleSlider
maskSource: mask
stateName: "percentage"
}
MouseArea {
id: angleMouseArea
anchors.fill: parent
// angle : totalAngle = mouseY : height
property int totalAngle: root.angleState ? root.angleStateType.maxValue - root.angleStateType.minValue : 0
property int angle: root.angleState ? totalAngle * mouseY / height + root.angleStateType.minValue : 0
hoverEnabled: true
property int startY: 0
onPressed: {
startY = mouseY
angleMovable.visible = true
id: percentageDragArea
anchors.centerIn: parent
anchors.horizontalCenterOffset: root.angleState ? -parent.width / 4 - Style.smallMargins: 0
width: background.contentItem.width / (root.angleState ? 2 : 1)
height: background.contentItem.height
property real draggedProgress: Math.max(0, Math.min(1, mouseY / height))
onReleased: percentageActionQueue.sendValue(mouseY / height * 100)
}
onMouseYChanged: if (pressed) angleMovable.angle = angle
Canvas {
id: angleCanvas
anchors.centerIn: parent
visible: root.angleState
anchors.horizontalCenterOffset: parent.width / 4
width: background.contentItem.width / (root.angleState ? 2 : 1)
height: background.contentItem.height
property real angle: root.angleState ?
angleDragArea.pressed ? angleDragArea.draggedAngle : root.angleState.value
: 0
onAngleChanged: requestPaint()
property real pendingAngle: angle
onPaint: {
var ctx = getContext("2d");
ctx.reset();
ctx.fillStyle = Style.tileForegroundColor
var segments = 10;
var segmentHeight = height / segments
var barHeight = Style.smallMargins
var barWidth = width / 4
ctx.beginPath();
for (var i = 0; i < segments; i++) {
ctx.save()
ctx.translate(barWidth / 2 + Style.smallMargins, i * segmentHeight + (segmentHeight - barHeight) / 2)
ctx.rotate(angleCanvas.angle * Math.PI / 180)
ctx.fillRect(-barWidth / 2, -barHeight / 2, width / 4, barHeight)
ctx.restore()
}
ctx.closePath()
ctx.strokeStyle = Style.accentColor
ctx.lineWidth = 2
ctx.save()
ctx.beginPath();
ctx.translate(barWidth / 2 + Style.smallMargins, (height - barHeight) / 2)
ctx.rotate(angleCanvas.pendingAngle * Math.PI / 180)
ctx.moveTo(-barWidth / 2, 0)
ctx.lineTo(width, 0)
ctx.stroke();
ctx.closePath();
ctx.restore()
ctx.strokeStyle = Style.tileForegroundColor
ctx.save()
ctx.beginPath();
ctx.translate(barWidth / 2 + Style.smallMargins, (height - barHeight) / 2)
ctx.rotate(angleCanvas.angle * Math.PI / 180)
ctx.moveTo(-barWidth / 2, 0)
ctx.lineTo(width, 0)
ctx.stroke();
ctx.closePath();
ctx.restore()
}
}
ActionQueue {
id: angleActionQueue
thing: root.thing
stateName: "angle"
}
MouseArea {
id: angleDragArea
visible: root.angleState
anchors.fill: angleCanvas
property real draggedAngle: root.angleState ? Math.min(root.angleStateType.maxValue,
Math.max(root.angleStateType.minValue,
mouseY / height * (root.angleStateType.maxValue - root.angleStateType.minValue) + root.angleStateType.minValue))
: 0
onReleased: {
print("released at", angle)
var targetAngle = 0
if (Math.abs(mouseY - startY) < 5) {
print("clicked")
// clicked without drag
if (mouseY < width) {
print("top area")
// clicked in top area
if (root.angle > 5) {
targetAngle = 0;
} else {
targetAngle = root.angleStateType.minValue
}
} else if (mouseY > height - width){
print("bottom area")
//clicked in bottom area
if (root.angle < -5) {
targetAngle = 0;
} else {
targetAngle = root.angleStateType.maxValue
}
} else {
targetAngle = angle
}
} else {
targetAngle = angle
}
angleMovable.angle = targetAngle
var actionType = root.thing.thingClass.actionTypes.findByName("angle");
var params = [];
var percentageParam = {}
percentageParam["paramTypeId"] = actionType.paramTypes.findByName("angle").id;
percentageParam["value"] = targetAngle
params.push(percentageParam);
engine.thingManager.executeAction(root.thing.id, actionType.id, params);
}
}
}
}
print("sending angle", draggedAngle)
angleCanvas.pendingAngle = draggedAngle
angleActionQueue.sendValue(draggedAngle)
}
}
}
OpacityMask {
anchors.fill: parent
source: ShaderEffectSource {
sourceItem: blind
sourceRect: background.contentItem.childrenRect
hideSource: true
}
maskSource: background
}
}
// Item {
// id: window
// Layout.preferredWidth: root.landscape ?
// Math.min(parent.width *.4, parent.height)
// : Math.min(Math.min(parent.width, 500), (parent.height - shutterControlsContainer.minimumHeight)) / (root.isVenetian ? 2 : 1)
//// Layout.preferredWidth: root.landscape ?
//// Math.min(parent.width - shutterControlsContainer.minimumWidth, parent.height) - app.margins
//// : Math.min(Math.min(parent.width, parent.height - shutterControlsContainer.minimumHeight), 500)
// Layout.preferredHeight: root.landscape ?
// width
// : width * (root.isVenetian ? 2 : 1)
// Layout.alignment: root.landscape ? Qt.AlignVCenter : Qt.AlignHCenter
// clip: true
// ClosablesControlLarge {
// anchors { left: parent.left; top: parent.top; bottom: parent.bottom; }
// width: height
// thing: root.thing
// ClosableArrowAnimation {
// id: arrowAnimation
// anchors.centerIn: parent
// anchors.horizontalCenterOffset: isVenetian ? -width: 0
// onStateChanged: {
// if (state != "") {
// animationTimer.start();
// }
// }
// Timer {
// id: animationTimer
// running: false
// interval: 5000
// repeat: false
// onTriggered: parent.state = ""
// }
// }
// }
// }
// Item {
// id: angleControls
// Layout.preferredWidth: root.landscape ? window.width / 2 : window.width
// Layout.preferredHeight: window.height
// visible: root.isVenetian
// Item {
// anchors.fill: parent
// Item {
// anchors { fill: parent; topMargin: parent.height * .09; bottomMargin: parent.height * 0.09; leftMargin: app.margins * 2; rightMargin: app.margins * 2 }
// Repeater {
// model: 10
// Item {
// width: parent.height * .1
// height: width
// y: parent.height / 10 * index
// Rectangle {
// anchors.centerIn: parent
// width: parent.width
// height: width / 4
// rotation: root.angle
// color: "#808080"
// }
// }
// }
// Item {
// id: angleMovable
// anchors.fill: parent
// property int angle: 0
// visible: false
// Repeater {
// model: 10
// Item {
// width: parent.height * .1
// height: width
// y: parent.height / 10 * index
// Rectangle {
// anchors.centerIn: parent
// width: parent.width
// height: width / 4
// rotation: angleMovable.angle
// color: Style.foregroundColor
// opacity: 0.1
// }
// }
// }
// }
// Item {
// anchors { top: parent.top; bottom: parent.bottom; right: parent.right; rightMargin: app.margins / 2 }
// width: parent.width * .5
// Rectangle {
// id: angleSlider
// anchors.fill: parent
// color: Qt.rgba(Style.foregroundColor.r, Style.foregroundColor.g, Style.foregroundColor.b, 0.1)
// visible: false
// ColorIcon {
// anchors { horizontalCenter: parent.horizontalCenter; top: parent.top; topMargin: app.margins }
// height: Style.iconSize
// width: Style.iconSize
// name: "../images/up.svg"
// }
// ColorIcon {
// anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom; bottomMargin: app.margins }
// height: Style.iconSize
// width: Style.iconSize
// name: "../images/down.svg"
// }
// Rectangle {
// width: parent.width
// height: 2
// color: angleMouseArea.containsMouse ? Style.accentColor : "transparent"
// y: angleMouseArea.mouseY
// onYChanged: sliderMask.update()
// }
// }
// Rectangle {
// id: mask
// anchors.fill: parent
// radius: Style.cornerRadius
// color: "blue"
// visible: false
// }
// OpacityMask {
// id: sliderMask
// anchors.fill: parent
// source: angleSlider
// maskSource: mask
// }
// MouseArea {
// id: angleMouseArea
// anchors.fill: parent
// // angle : totalAngle = mouseY : height
// property int totalAngle: root.angleState ? root.angleStateType.maxValue - root.angleStateType.minValue : 0
// property int angle: root.angleState ? totalAngle * mouseY / height + root.angleStateType.minValue : 0
// hoverEnabled: true
// property int startY: 0
// onPressed: {
// startY = mouseY
// angleMovable.visible = true
// }
// onMouseYChanged: if (pressed) angleMovable.angle = angle
// onReleased: {
// print("released at", angle)
// var targetAngle = 0
// if (Math.abs(mouseY - startY) < 5) {
// print("clicked")
// // clicked without drag
// if (mouseY < width) {
// print("top area")
// // clicked in top area
// if (root.angle > 5) {
// targetAngle = 0;
// } else {
// targetAngle = root.angleStateType.minValue
// }
// } else if (mouseY > height - width){
// print("bottom area")
// //clicked in bottom area
// if (root.angle < -5) {
// targetAngle = 0;
// } else {
// targetAngle = root.angleStateType.maxValue
// }
// } else {
// targetAngle = angle
// }
// } else {
// targetAngle = angle
// }
// angleMovable.angle = targetAngle
// var actionType = root.thing.thingClass.actionTypes.findByName("angle");
// var params = [];
// var percentageParam = {}
// percentageParam["paramTypeId"] = actionType.paramTypes.findByName("angle").id;
// percentageParam["value"] = targetAngle
// params.push(percentageParam);
// engine.thingManager.executeAction(root.thing.id, actionType.id, params);
// }
// }
// }
// }
// }
// }
Item {
id: shutterControlsContainer
Layout.columnSpan: root.isVenetian && !root.landscape ? 2 : 1
Layout.fillWidth: true
Layout.maximumWidth: 500
// Layout.preferredWidth: root.landscape ? Math.max(parent.width / 2, shutterControls.implicitWidth) : parent.width
Layout.margins: app.margins * 2
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
property int minimumHeight: Style.iconSize * 2.5
property int minimumWidth: Style.iconSize * 2.5 * 3
ShutterControls {
id: shutterControls
thing: root.thing
width: parent.width
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Layout.fillWidth: true
size: Style.bigIconSize
backgroundEnabled: true
spacing: (width - Style.iconSize*2*children.length) / (children.length - 1)
property int count: children.length
onActivated: {
if (button == "open") {
arrowAnimation.state = "opening"
} else if (button == "close") {
arrowAnimation.state = "closing"
} else {
arrowAnimation.state = ""
}
}
}
thing: root.thing
}
}
}

View File

@ -35,6 +35,7 @@ import QtQuick.Layouts 1.1
import Nymea 1.0
import "../components"
import "../customviews"
import "../utils"
ThingPageBase {
id: root
@ -58,48 +59,32 @@ ThingPageBase {
anchors.margins: app.margins
columns: app.landscape ? 2 : 1
ThermostatController {
CircleBackground {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: Style.bigMargins
ThermostatController {
anchors.centerIn: parent
height: Math.min(400, Math.min(parent.height, parent.width))
width: height
thing: root.thing
}
Rectangle {
Layout.preferredWidth: app.landscape ? parent.width / 2 : parent.width
Layout.preferredHeight: 50
visible: root.boostStateType
border.color: boostMouseArea.pressed || root.boostStateType && root.boostState.value === true ? Style.accentColor : Style.foregroundColor
border.width: 1
radius: height / 2
color: root.boostStateType && root.boostState.value === true ? Style.accentColor : "transparent"
Row {
anchors.centerIn: parent
spacing: app.margins / 2
ColorIcon {
height: Style.iconSize
width: Style.iconSize
name: "../images/sensors/temperature.svg"
color: root.boostStateType && root.boostState.value === true ? "red" : Style.iconColor
}
Label {
text: qsTr("Boost")
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: boostMouseArea
anchors.fill: parent
onPressedChanged: PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackImpact)
onClicked: {
var params = []
var param = {}
param["paramTypeId"] = root.boostStateType.id
param["value"] = !root.boostState.value
params.push(param)
engine.thingManager.executeAction(root.thing.id, root.boostStateType.id, params);
}
ProgressButton {
Layout.alignment: Qt.AlignHCenter
Layout.margins: Style.bigMargins
size: Style.largeIconSize
imageSource: "thermostat/heating"
backgroundColor: app.interfaceToColor("heating")
visible: root.boostState
busy: actionQueue.pendingValue ? actionQueue.pendingValue : (root.boostState && root.boostState.value === true)
onClicked: actionQueue.sendValue(!root.boostState.value)
ActionQueue {
id: actionQueue
thing: root.thing
stateName: "boost"
}
}
}

View File

@ -31,52 +31,34 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Material 2.1
import Nymea 1.0
import "../components"
import "../utils"
ThingPageBase {
id: root
readonly property State powerState: thing.stateByName("power")
readonly property ActionType powerActionType: thing.thingClass.actionTypes.findByName("power");
readonly property State flowRateState: thing.stateByName("flowRate")
readonly property StateType flowRateStateType: thing.thingClass.stateTypes.findByName("flowRate")
GridLayout {
anchors.fill: parent
anchors.margins: app.margins
columns: app.landscape ? 2 : 1
rowSpacing: app.margins
columnSpacing: app.margins
Layout.alignment: Qt.AlignCenter
Item {
Layout.preferredWidth: Math.max(Style.iconSize * 6, parent.width / 5)
Layout.preferredHeight: width
Layout.topMargin: app.margins
Layout.bottomMargin: app.landscape ? app.margins : 0
Layout.alignment: Qt.AlignCenter
Layout.rowSpan: app.landscape ? 4 : 1
Layout.fillHeight: true
AbstractButton {
height: Math.min(parent.height, parent.width)
width: height
anchors.centerIn: parent
Rectangle {
anchors.fill: parent
color: "transparent"
border.color: root.powerState.value === true ? Style.accentColor : Style.iconColor
border.width: 4
radius: width / 2
ActionQueue {
id: actionQueue
thing: root.thing
stateName: "power"
}
ColorIcon {
id: bulbIcon
CircleBackground {
id: background
anchors.fill: parent
anchors.margins: app.margins * 1.5
name: "../images/ventilation.svg"
color: root.powerState.value === true ? Style.accentColor : Style.iconColor
anchors.margins: Style.hugeMargins
iconSource: "ventilation"
onColor: app.interfaceToColor("ventilation")
on: (actionQueue.pendingValue || powerState.value) === true
onClicked: {
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection)
actionQueue.sendValue(!root.powerState.value)
}
PropertyAnimation on rotation {
running: root.powerState.value === true
duration: 2000
@ -89,15 +71,15 @@ ThingPageBase {
}
}
}
onClicked: {
var params = []
var param = {}
param["paramTypeId"] = root.powerActionType.paramTypes.get(0).id;
param["value"] = !root.powerState.value;
params.push(param)
engine.thingManager.executeAction(root.thing.id, root.powerActionType.id, params);
}
}
}
Dial {
anchors.centerIn: parent
height: background.contentItem.height
width: background.contentItem.width
visible: root.flowRateState
thing: root.thing
stateName: "flowRate"
color: app.interfaceToColor("ventilation")
}
}

View File

@ -29,6 +29,8 @@ Item {
page = "WeatherDevicePage.qml";
} else if (interfaceList.indexOf("heating") >= 0) {
page = "HeatingDevicePage.qml";
} else if (interfaceList.indexOf("cooling") >= 0) {
page = "CoolingThingPage.qml";
} else if (interfaceList.indexOf("thermostat") >= 0) {
page = "ThermostatDevicePage.qml";
} else if (interfaceList.indexOf("sensor") >= 0) {
@ -47,6 +49,8 @@ Item {
page = "NotificationsDevicePage.qml";
} else if (interfaceList.indexOf("fingerprintreader") >= 0) {
page = "FingerprintReaderDevicePage.qml";
} else if (interfaceList.indexOf("evcharger") >= 0) {
page = "EvChargerThingPage.qml"
} else if (interfaceList.indexOf("smartmeter") >= 0) {
page = "SmartMeterDevicePage.qml"
} else if (interfaceList.indexOf("powersocket") >= 0) {
@ -120,7 +124,7 @@ Item {
"water": "/ui/images/sensors/water.svg",
"wind": "/ui/images/sensors/windspeed.svg",
"cloud": "/ui/images/weathericons/weather-clouds.svg",
"send": "/ui/images/send.svg"
"send": "/ui/images/send.svg",
}
function namedIcon(name) {
if (!namedIcons.hasOwnProperty(name)) {