More work on the lights pages
This commit is contained in:
parent
2c0075ec6b
commit
554575698b
@ -171,6 +171,7 @@ void ConfiguredHostsModel::saveToDisk()
|
|||||||
QSettings settings;
|
QSettings settings;
|
||||||
settings.beginGroup("ConfiguredHosts");
|
settings.beginGroup("ConfiguredHosts");
|
||||||
settings.remove("");
|
settings.remove("");
|
||||||
|
settings.setValue("currentIndex", m_currentIndex);
|
||||||
for (int i = 0; i < m_list.count(); i++) {
|
for (int i = 0; i < m_list.count(); i++) {
|
||||||
settings.beginGroup(QString::number(i));
|
settings.beginGroup(QString::number(i));
|
||||||
settings.setValue("uuid", m_list.at(i)->uuid());
|
settings.setValue("uuid", m_list.at(i)->uuid());
|
||||||
|
|||||||
@ -58,7 +58,7 @@
|
|||||||
<file>ui/devicepages/GarageThingPage.qml</file>
|
<file>ui/devicepages/GarageThingPage.qml</file>
|
||||||
<file>ui/devicepages/AwningThingPage.qml</file>
|
<file>ui/devicepages/AwningThingPage.qml</file>
|
||||||
<file>ui/devicepages/NotificationsDevicePage.qml</file>
|
<file>ui/devicepages/NotificationsDevicePage.qml</file>
|
||||||
<file>ui/devicepages/LightDevicePage.qml</file>
|
<file>ui/devicepages/LightThingPage.qml</file>
|
||||||
<file>ui/devicepages/FingerprintReaderDevicePage.qml</file>
|
<file>ui/devicepages/FingerprintReaderDevicePage.qml</file>
|
||||||
<file>ui/devicepages/DeviceLogPage.qml</file>
|
<file>ui/devicepages/DeviceLogPage.qml</file>
|
||||||
<file>ui/devicelistpages/GenericThingsListPage.qml</file>
|
<file>ui/devicelistpages/GenericThingsListPage.qml</file>
|
||||||
|
|||||||
@ -8,10 +8,25 @@ Item {
|
|||||||
|
|
||||||
property Thing thing: null
|
property Thing thing: null
|
||||||
|
|
||||||
|
readonly property State colorState: thing ? thing.stateByName("color") : null
|
||||||
|
readonly property State powerState: thing ? thing.stateByName("power") : null
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: colorState
|
||||||
|
onValueChanged: {
|
||||||
|
if (actionQueue.pendingValue === null) {
|
||||||
|
actionQueue.useStoredPoint = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ActionQueue {
|
ActionQueue {
|
||||||
id: actionQueue
|
id: actionQueue
|
||||||
thing: root.thing
|
thing: root.thing
|
||||||
stateType: thing.thingClass.stateTypes.findByName("color")
|
stateType: thing.thingClass.stateTypes.findByName("color")
|
||||||
|
|
||||||
|
property bool useStoredPoint: false
|
||||||
|
property point storedPoint: Qt.point(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
ConicalGradient {
|
ConicalGradient {
|
||||||
@ -30,6 +45,29 @@ Item {
|
|||||||
GradientStop { position: 0.833; color: Qt.rgba(1, 0, 1, 1) }
|
GradientStop { position: 0.833; color: Qt.rgba(1, 0, 1, 1) }
|
||||||
GradientStop { position: 1.000; color: Qt.rgba(1, 0, 0, 1) }
|
GradientStop { position: 1.000; color: Qt.rgba(1, 0, 0, 1) }
|
||||||
}
|
}
|
||||||
|
onWidthChanged: dragHandle.updatePoint()
|
||||||
|
onHeightChanged: dragHandle.updatePoint()
|
||||||
|
|
||||||
|
RadialGradient {
|
||||||
|
anchors.fill: gradient
|
||||||
|
gradient: Gradient{
|
||||||
|
GradientStop { position: 0.05; color: Qt.rgba(1, 1, 1, 1) }
|
||||||
|
GradientStop { position: 0.10; color: Qt.rgba(1, 1, 1, .9) }
|
||||||
|
GradientStop { position: 0.20; color: Qt.rgba(1, 1, 1, .7) }
|
||||||
|
GradientStop { position: 0.30; color: Qt.rgba(1, 1, 1, .5) }
|
||||||
|
GradientStop { position: 0.40; color: Qt.rgba(1, 1, 1, .3) }
|
||||||
|
GradientStop { position: 0.50; color: "transparent" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Desaturate {
|
||||||
|
id: colorizer
|
||||||
|
anchors.fill: gradient
|
||||||
|
source: gradient
|
||||||
|
desaturation: root.powerState.value === true ? 0 : 1
|
||||||
|
Behavior on desaturation { NumberAnimation { duration: Style.animationDuration } }
|
||||||
|
visible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@ -39,19 +77,100 @@ Item {
|
|||||||
}
|
}
|
||||||
OpacityMask {
|
OpacityMask {
|
||||||
anchors.fill: gradient
|
anchors.fill: gradient
|
||||||
source: gradient
|
source: colorizer
|
||||||
maskSource: mask
|
maskSource: mask
|
||||||
}
|
}
|
||||||
|
|
||||||
RadialGradient {
|
|
||||||
anchors.fill: gradient
|
Rectangle {
|
||||||
gradient: Gradient{
|
id: dragHandle
|
||||||
GradientStop { position: 0.05; color: Qt.rgba(1, 1, 1, 1) }
|
width: 20
|
||||||
GradientStop { position: 0.10; color: Qt.rgba(1, 1, 1, .9) }
|
height: 20
|
||||||
GradientStop { position: 0.20; color: Qt.rgba(1, 1, 1, .7) }
|
radius: height / 2
|
||||||
GradientStop { position: 0.30; color: Qt.rgba(1, 1, 1, .5) }
|
color: Style.backgroundColor
|
||||||
GradientStop { position: 0.40; color: Qt.rgba(1, 1, 1, .3) }
|
border.color: Style.foregroundColor
|
||||||
GradientStop { position: 0.50; color: "transparent" }
|
border.width: 2
|
||||||
|
|
||||||
|
x: point.x + gradient.width / 2 + gradient.x - width / 2
|
||||||
|
y: point.y + gradient.height / 2 + gradient.y - height / 2
|
||||||
|
|
||||||
|
property color shownColor: root.colorState ? actionQueue.pendingValue || root.colorState.value : "white`"
|
||||||
|
onShownColorChanged: updatePoint()
|
||||||
|
// Component.onCompleted: updatePoint()
|
||||||
|
|
||||||
|
property point point: Qt.point(0,0);
|
||||||
|
function updatePoint() {
|
||||||
|
|
||||||
|
if (actionQueue.useStoredPoint) {
|
||||||
|
point = actionQueue.storedPoint
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
print("current color:", shownColor.r, shownColor.g, shownColor.b)
|
||||||
|
|
||||||
|
var whitePart = Math.min(Math.min(shownColor.r, shownColor.g), shownColor.b)
|
||||||
|
|
||||||
|
var stopIndex = 0
|
||||||
|
var progressInStop = 0
|
||||||
|
if (shownColor.r === 1) {
|
||||||
|
if (shownColor.g > shownColor.b) {
|
||||||
|
stopIndex = 0
|
||||||
|
progressInStop = shownColor.g - whitePart
|
||||||
|
} else {
|
||||||
|
stopIndex = 5
|
||||||
|
progressInStop = 1 - shownColor.b + whitePart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shownColor.g === 1) {
|
||||||
|
if (shownColor.r > shownColor.b) {
|
||||||
|
stopIndex = 1
|
||||||
|
progressInStop = 1 - shownColor.r + whitePart
|
||||||
|
} else {
|
||||||
|
stopIndex = 2
|
||||||
|
progressInStop = shownColor.b - whitePart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shownColor.b === 1) {
|
||||||
|
if (shownColor.r > shownColor.g) {
|
||||||
|
stopIndex = 4
|
||||||
|
progressInStop = shownColor.r - whitePart
|
||||||
|
} else {
|
||||||
|
stopIndex = 3
|
||||||
|
progressInStop = 1-shownColor.g + whitePart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stopBefore = g.stops[stopIndex]
|
||||||
|
var stopAfter = g.stops[stopIndex+1]
|
||||||
|
|
||||||
|
print("stopIndex", stopIndex)
|
||||||
|
print("stopBefore:", stopBefore.color.r, stopBefore.color.g, stopBefore.color.b)
|
||||||
|
print("stopAfter:", stopAfter.color.r, stopAfter.color.g, stopAfter.color.b)
|
||||||
|
print("progressInStop", progressInStop)
|
||||||
|
|
||||||
|
|
||||||
|
print("beforePosition", stopBefore.position)
|
||||||
|
|
||||||
|
var positionInGradient = stopBefore.position + (stopAfter.position - stopBefore.position) * progressInStop
|
||||||
|
|
||||||
|
print("positionInGradient", positionInGradient)
|
||||||
|
|
||||||
|
var degrees = 360 * positionInGradient;
|
||||||
|
degrees -= 90;
|
||||||
|
|
||||||
|
var radian = degrees * 0.0174532925
|
||||||
|
|
||||||
|
var radius = gradient.height * 0.9 / 2 * (1-whitePart)
|
||||||
|
|
||||||
|
|
||||||
|
var x = radius * Math.cos(radian)
|
||||||
|
var y = radius * Math.sin(radian)
|
||||||
|
|
||||||
|
print("degrees", degrees)
|
||||||
|
print("radius", radius)
|
||||||
|
|
||||||
|
print("Setting point to", x, y)
|
||||||
|
point = Qt.point(x, y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,6 +217,13 @@ Item {
|
|||||||
1)
|
1)
|
||||||
|
|
||||||
actionQueue.sendValue(color);
|
actionQueue.sendValue(color);
|
||||||
|
|
||||||
|
// Store the coordinates (limited to the circle) as the above calculation is lossy so we can't precicely
|
||||||
|
// calcuate the position from the color but we don't want the drag handle jumping while dragging.
|
||||||
|
var rad = (angle - 90) / 180 * Math.PI
|
||||||
|
var radius = Math.min(distanceFromCenter, width * 0.9 / 2)
|
||||||
|
actionQueue.storedPoint = Qt.point(radius * Math.cos(rad), radius * Math.sin(rad))
|
||||||
|
actionQueue.useStoredPoint = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateAngle(mouseX, mouseY) {
|
function calculateAngle(mouseX, mouseY) {
|
||||||
|
|||||||
@ -10,9 +10,10 @@ Item {
|
|||||||
|
|
||||||
property Thing thing: null
|
property Thing thing: null
|
||||||
|
|
||||||
property int orientation: Qt.Horizontal
|
property int orientation: Qt.Vertical
|
||||||
|
|
||||||
readonly property StateType colorTemperatureStateType: root.thing.thingClass.stateTypes.findByName("colorTemperature")
|
readonly property StateType colorTemperatureStateType: root.thing.thingClass.stateTypes.findByName("colorTemperature")
|
||||||
|
readonly property State powerState: root.thing.stateByName("power")
|
||||||
|
|
||||||
property int value: thing.stateByName("colorTemperature").value
|
property int value: thing.stateByName("colorTemperature").value
|
||||||
|
|
||||||
@ -23,10 +24,12 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: background
|
||||||
width: Math.min(parent.width, parent.height)
|
width: Math.min(parent.width, parent.height)
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
height: width
|
height: width
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
|
visible: false
|
||||||
gradient: Gradient {
|
gradient: Gradient {
|
||||||
GradientStop { position: 0.0; color: "#dfffff" }
|
GradientStop { position: 0.0; color: "#dfffff" }
|
||||||
GradientStop { position: 0.5; color: "#ffffea" }
|
GradientStop { position: 0.5; color: "#ffffea" }
|
||||||
@ -34,48 +37,44 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rectangle {
|
Desaturate {
|
||||||
// id: clipRect
|
anchors.fill: background
|
||||||
// anchors.fill: parent
|
source: background
|
||||||
// radius: Style.cornerRadius
|
desaturation: root.powerState.value === true ? 0 : 1
|
||||||
// }
|
Behavior on desaturation { NumberAnimation { duration: Style.animationDuration } }
|
||||||
|
}
|
||||||
|
|
||||||
// LinearGradient {
|
Rectangle {
|
||||||
// anchors.fill: parent
|
id: dragHandle
|
||||||
// start: root.orientation == Qt.Horizontal ? Qt.point(0, 0) : Qt.point(0, height)
|
property double valuePercentage: ((actionQueue.pendingValue || root.value) - root.colorTemperatureStateType.minValue) / (root.colorTemperatureStateType.maxValue - root.colorTemperatureStateType.minValue)
|
||||||
// end: root.orientation == Qt.Horizontal ? Qt.point(width, 0) : Qt.point(0, 0)
|
width: 20
|
||||||
// source: clipRect
|
height: 20
|
||||||
// gradient: Gradient {
|
radius: height / 2
|
||||||
// GradientStop { position: 0.0; color: "#dfffff" }
|
color: Style.backgroundColor
|
||||||
// GradientStop { position: 0.5; color: "#ffffea" }
|
border.color: Style.foregroundColor
|
||||||
// GradientStop { position: 1.0; color: "#ffd649" }
|
border.width: 2
|
||||||
// }
|
x: (parent.width - width) / 2
|
||||||
// }
|
y: parent.height * valuePercentage - (height / 2)
|
||||||
|
|
||||||
// Rectangle {
|
}
|
||||||
// id: dragHandle
|
|
||||||
// property double valuePercentage: ((actionQueue.pendingValue || root.value) - root.colorTemperatureStateType.minValue) / (root.colorTemperatureStateType.maxValue - root.colorTemperatureStateType.minValue)
|
|
||||||
// x: orientation == Qt.Horizontal ? valuePercentage * (root.width - dragHandle.width) : 0
|
|
||||||
// y: root.orientation === Qt.Vertical ? root.height - dragHandle.height - (valuePercentage * (root.height - dragHandle.height)) : 0
|
|
||||||
// height: root.orientation == Qt.Horizontal ? parent.height : 8
|
|
||||||
// width: root.orientation == Qt.Horizontal ? 8 : parent.width
|
|
||||||
// radius: 4
|
|
||||||
// color: Qt.tint(Style.backgroundColor, Qt.rgba(Style.foregroundColor.r, Style.foregroundColor.g, Style.foregroundColor.b, 0.5))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MouseArea {
|
MouseArea {
|
||||||
// anchors.fill: parent
|
anchors.fill: parent
|
||||||
// onPositionChanged: {
|
onPositionChanged: {
|
||||||
// var minCt = root.colorTemperatureStateType.minValue;
|
var minCt = root.colorTemperatureStateType.minValue;
|
||||||
// var maxCt = root.colorTemperatureStateType.maxValue
|
var maxCt = root.colorTemperatureStateType.maxValue
|
||||||
// var ct;
|
var ct;
|
||||||
// if (root.orientation == Qt.Horizontal) {
|
// if (root.orientation == Qt.Horizontal) {
|
||||||
// ct = Math.min(maxCt, Math.max(minCt, (mouseX * (maxCt - minCt) / (width - dragHandle.width)) + minCt))
|
// ct = Math.min(maxCt, Math.max(minCt, (mouseX * (maxCt - minCt) / (width - dragHandle.width)) + minCt))
|
||||||
// } else {
|
// } else {
|
||||||
|
// ct : y = max : height
|
||||||
|
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))
|
// ct = Math.min(maxCt, Math.max(minCt, ((height - mouseY) * (maxCt - minCt) / (height - dragHandle.height)) + minCt))
|
||||||
// }
|
// }
|
||||||
// actionQueue.sendValue(ct);
|
actionQueue.sendValue(ct);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -209,7 +209,7 @@ MainPageTile {
|
|||||||
case "light":
|
case "light":
|
||||||
var group = engine.thingManager.createGroup(Interfaces.findByName("colorlight"), thingsProxy);
|
var group = engine.thingManager.createGroup(Interfaces.findByName("colorlight"), thingsProxy);
|
||||||
print("opening lights page for group", group)
|
print("opening lights page for group", group)
|
||||||
pageStack.push("../devicepages/LightDevicePage.qml", {thing: group})
|
pageStack.push("../devicepages/LightThingPage.qml", {thing: group})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,7 +107,7 @@ ThingsListPageBase {
|
|||||||
property bool colorInverted: tileColored && NymeaUtils.isDark(Style.foregroundColor) === NymeaUtils.isDark(colorState.value)
|
property bool colorInverted: tileColored && NymeaUtils.isDark(Style.foregroundColor) === NymeaUtils.isDark(colorState.value)
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (isEnabled && (colorState || colorTemperatureState)) {
|
if (isEnabled /*&& (colorState || colorTemperatureState)*/) {
|
||||||
root.enterPage(index)
|
root.enterPage(index)
|
||||||
} else {
|
} else {
|
||||||
itemDelegate.wobble()
|
itemDelegate.wobble()
|
||||||
|
|||||||
@ -1,193 +0,0 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
||||||
*
|
|
||||||
* 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.9
|
|
||||||
import QtQuick.Controls 2.1
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtQuick.Controls.Material 2.1
|
|
||||||
import Nymea 1.0
|
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
import "../components"
|
|
||||||
|
|
||||||
ThingPageBase {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
readonly property State powerState: thing.stateByName("power")
|
|
||||||
|
|
||||||
readonly property State brightnessState: thing.stateByName("brightness")
|
|
||||||
readonly property ActionType brightnessActionType: thingClass.actionTypes.findByName("brightness");
|
|
||||||
|
|
||||||
readonly property State colorState: thing.stateByName("color")
|
|
||||||
|
|
||||||
readonly property StateType ctStateType: thingClass.stateTypes.findByName("colorTemperature")
|
|
||||||
readonly property State ctState: thing.stateByName("colorTemperature")
|
|
||||||
readonly property ActionType ctActionType: thingClass.actionTypes.findByName("colorTemperature")
|
|
||||||
|
|
||||||
readonly property int statesCount: (powerState !== null ? 1 : 0) +
|
|
||||||
(brightnessState !== null ? 1 : 0) +
|
|
||||||
(ctState !== null ? 1 : 0) +
|
|
||||||
(colorState !== null ? 1 : 0)
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Style.bigMargins
|
|
||||||
columns: app.landscape ? root.statesCount : 1
|
|
||||||
rowSpacing: Style.bigMargins
|
|
||||||
columnSpacing: Style.bigMargins
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: !app.landscape
|
|
||||||
columnSpacing: app.margins
|
|
||||||
rowSpacing: app.margins
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
visible: root.ctStateType !== null
|
|
||||||
columns: app.landscape ? 1 : 4
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: ListModel {
|
|
||||||
ListElement { name: "activate"; ct: "0"; bri: 100; color: "#00c5ff" }
|
|
||||||
ListElement { name: "concentrate"; ct: "23"; bri: 100; color: "#3dddff" }
|
|
||||||
ListElement { name: "reading"; ct: "57"; bri: 100; color: "#f4de00" }
|
|
||||||
ListElement { name: "relax"; ct: "95" ; bri: 55; color: "#ffaf2a"}
|
|
||||||
}
|
|
||||||
delegate: ProgressButton {
|
|
||||||
Layout.preferredHeight: Style.hugeIconSize
|
|
||||||
Layout.preferredWidth: Style.hugeIconSize
|
|
||||||
imageSource: "../images/lighting/" + model.name + ".svg"
|
|
||||||
longpressEnabled: false
|
|
||||||
// mode: "normal"
|
|
||||||
// backgroundColor: model.color
|
|
||||||
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
// Translate from % to absolute value in min/max
|
|
||||||
// % : 100 = abs : (max - min)
|
|
||||||
print("min,max", root.ctStateType, root.ctStateType.minValue, root.ctStateType.maxValue)
|
|
||||||
var absoluteCtValue = (model.ct * (root.ctStateType.maxValue - root.ctStateType.minValue) / 100) + root.ctStateType.minValue
|
|
||||||
var params = [];
|
|
||||||
var param1 = {};
|
|
||||||
param1["paramName"] = root.ctActionType.paramTypes.get(0).name;
|
|
||||||
param1["value"] = absoluteCtValue;
|
|
||||||
params.push(param1)
|
|
||||||
root.thing.executeAction(root.ctActionType.name, params)
|
|
||||||
params = [];
|
|
||||||
param1 = {};
|
|
||||||
param1["paramName"] = root.brightnessActionType.paramTypes.get(0).name;
|
|
||||||
param1["value"] = model.bri;
|
|
||||||
params.push(param1)
|
|
||||||
root.thing.executeAction(root.brightnessActionType.name, params)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StackLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.maximumHeight: width
|
|
||||||
currentIndex: selectionTabs.currentIndex
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: modeModel
|
|
||||||
delegate: Loader {
|
|
||||||
sourceComponent: model.comp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: colorPickerComponent
|
|
||||||
ColorPicker {
|
|
||||||
anchors.fill: parent
|
|
||||||
thing: root.thing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: colorTemperatureComponent
|
|
||||||
ColorTemperaturePicker {
|
|
||||||
anchors.fill: parent
|
|
||||||
thing: root.thing
|
|
||||||
orientation: app.landscape ? Qt.Vertical : Qt.Horizontal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListModel {
|
|
||||||
id: modeModel
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (root.thing.thingClass.stateTypes.findByName("color") !== null) {
|
|
||||||
append({modelData: qsTr("Color"), comp: colorPickerComponent})
|
|
||||||
}
|
|
||||||
if (root.thing.thingClass.stateTypes.findByName("colorTemperature") !== null) {
|
|
||||||
append({modelData: qsTr("Temperature"), comp: colorTemperatureComponent})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectionTabs {
|
|
||||||
id: selectionTabs
|
|
||||||
Layout.fillWidth: true
|
|
||||||
model: modeModel
|
|
||||||
visible: modeModel.count > 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
id: basicItems
|
|
||||||
Layout.fillWidth: !app.landscape
|
|
||||||
Layout.fillHeight: app.landscape
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
columnSpacing: app.margins
|
|
||||||
rowSpacing: app.margins
|
|
||||||
columns: (app.landscape && (root.colorState !== null && root.ctState !== null))
|
|
||||||
|| (!app.landscape && (root.colorState === null && root.ctState === null)) ? 1 : 2
|
|
||||||
|
|
||||||
ProgressButton {
|
|
||||||
imageSource: root.powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg"
|
|
||||||
mode: "normal"
|
|
||||||
size: Style.bigIconSize
|
|
||||||
longpressEnabled: false
|
|
||||||
onClicked: {
|
|
||||||
root.thing.executeAction("power", [{paramName: "power", value: !root.powerState.value}])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BrightnessSlider {
|
|
||||||
Layout.fillWidth: orientation == Qt.Horizontal
|
|
||||||
Layout.fillHeight: orientation == Qt.Vertical
|
|
||||||
thing: root.thing
|
|
||||||
orientation: basicItems.columns === 1 ? Qt.Vertical : Qt.Horizontal
|
|
||||||
visible: root.thing.stateByName("brightness") !== null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
338
nymea-app/ui/devicepages/LightThingPage.qml
Normal file
338
nymea-app/ui/devicepages/LightThingPage.qml
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* 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.9
|
||||||
|
import QtQuick.Controls 2.1
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Controls.Material 2.1
|
||||||
|
import Nymea 1.0
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import "../components"
|
||||||
|
import "../utils"
|
||||||
|
|
||||||
|
ThingPageBase {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property State powerState: thing.stateByName("power")
|
||||||
|
|
||||||
|
readonly property State brightnessState: thing.stateByName("brightness")
|
||||||
|
readonly property ActionType brightnessActionType: thingClass.actionTypes.findByName("brightness");
|
||||||
|
|
||||||
|
readonly property State colorState: thing.stateByName("color")
|
||||||
|
|
||||||
|
readonly property StateType ctStateType: thingClass.stateTypes.findByName("colorTemperature")
|
||||||
|
readonly property State ctState: thing.stateByName("colorTemperature")
|
||||||
|
readonly property ActionType ctActionType: thingClass.actionTypes.findByName("colorTemperature")
|
||||||
|
|
||||||
|
readonly property int statesCount: (powerState !== null ? 1 : 0) +
|
||||||
|
(brightnessState !== null ? 1 : 0) +
|
||||||
|
(ctState !== null ? 1 : 0) +
|
||||||
|
(colorState !== null ? 1 : 0)
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.bigMargins
|
||||||
|
columns: app.landscape ? root.statesCount : 1
|
||||||
|
rowSpacing: Style.bigMargins
|
||||||
|
columnSpacing: Style.bigMargins
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: !app.landscape
|
||||||
|
columnSpacing: app.margins
|
||||||
|
rowSpacing: app.margins
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
visible: root.ctStateType !== null
|
||||||
|
columns: app.landscape ? 1 : 4
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ListModel {
|
||||||
|
ListElement { name: "activate"; ct: "0"; bri: 100; color: "#00c5ff" }
|
||||||
|
ListElement { name: "concentrate"; ct: "23"; bri: 100; color: "#3dddff" }
|
||||||
|
ListElement { name: "reading"; ct: "57"; bri: 100; color: "#f4de00" }
|
||||||
|
ListElement { name: "relax"; ct: "95" ; bri: 55; color: "#ffaf2a"}
|
||||||
|
}
|
||||||
|
delegate: ProgressButton {
|
||||||
|
Layout.preferredHeight: Style.hugeIconSize
|
||||||
|
Layout.preferredWidth: Style.hugeIconSize
|
||||||
|
imageSource: "../images/lighting/" + model.name + ".svg"
|
||||||
|
longpressEnabled: false
|
||||||
|
// mode: "normal"
|
||||||
|
// backgroundColor: model.color
|
||||||
|
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
// Translate from % to absolute value in min/max
|
||||||
|
// % : 100 = abs : (max - min)
|
||||||
|
print("min,max", root.ctStateType, root.ctStateType.minValue, root.ctStateType.maxValue)
|
||||||
|
var absoluteCtValue = (model.ct * (root.ctStateType.maxValue - root.ctStateType.minValue) / 100) + root.ctStateType.minValue
|
||||||
|
var params = [];
|
||||||
|
var param1 = {};
|
||||||
|
param1["paramName"] = root.ctActionType.paramTypes.get(0).name;
|
||||||
|
param1["value"] = absoluteCtValue;
|
||||||
|
params.push(param1)
|
||||||
|
root.thing.executeAction(root.ctActionType.name, params)
|
||||||
|
params = [];
|
||||||
|
param1 = {};
|
||||||
|
param1["paramName"] = root.brightnessActionType.paramTypes.get(0).name;
|
||||||
|
param1["value"] = model.bri;
|
||||||
|
params.push(param1)
|
||||||
|
root.thing.executeAction(root.brightnessActionType.name, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: Style.margins
|
||||||
|
|
||||||
|
StackLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.maximumHeight: width
|
||||||
|
currentIndex: selectionTabs.currentIndex
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: modeModel
|
||||||
|
delegate: Loader {
|
||||||
|
sourceComponent: model.comp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: colorPickerComponent
|
||||||
|
ColorPicker {
|
||||||
|
anchors.fill: parent
|
||||||
|
thing: root.thing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: colorTemperatureComponent
|
||||||
|
ColorTemperaturePicker {
|
||||||
|
anchors.fill: parent
|
||||||
|
thing: root.thing
|
||||||
|
orientation: app.landscape ? Qt.Vertical : Qt.Horizontal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: brightnessComponent
|
||||||
|
Item {
|
||||||
|
id: brightnessController
|
||||||
|
property Thing thing: root.thing
|
||||||
|
readonly property State brightnessState: thing ? thing.stateByName("brightness") : null
|
||||||
|
readonly property State colorState: thing ? thing.stateByName("color") : null
|
||||||
|
readonly property State powerState: thing ? thing.stateByName("power") : null
|
||||||
|
|
||||||
|
ActionQueue {
|
||||||
|
id: actionQueue
|
||||||
|
thing: brightnessController.thing
|
||||||
|
stateType: thing.thingClass.stateTypes.findByName("brightness")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: brightnessCircle
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Math.min(parent.width, parent.height)
|
||||||
|
height: width
|
||||||
|
radius: width / 2
|
||||||
|
color: Style.tileBackgroundColor
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
anchors.fill: brightnessCircle
|
||||||
|
property Item source: ShaderEffectSource {
|
||||||
|
id: shaderSource
|
||||||
|
anchors.fill: parent
|
||||||
|
sourceItem: brightnessCircle
|
||||||
|
hideSource: true
|
||||||
|
}
|
||||||
|
property color inColor: Style.tileBackgroundColor
|
||||||
|
property color outColor: powerState.value === true
|
||||||
|
? colorState ? colorState.value : "#ffd649"
|
||||||
|
: Style.tileOverlayColor
|
||||||
|
Behavior on outColor { ColorAnimation { duration: Style.animationDuration } }
|
||||||
|
|
||||||
|
property real threshold: 0.1
|
||||||
|
property real brightness: 1 - (actionQueue.pendingValue || brightnessState.value) / 100
|
||||||
|
|
||||||
|
fragmentShader: "
|
||||||
|
varying highp vec2 qt_TexCoord0;
|
||||||
|
uniform sampler2D source;
|
||||||
|
uniform highp vec4 outColor;
|
||||||
|
uniform highp vec4 inColor;
|
||||||
|
uniform lowp float threshold;
|
||||||
|
uniform lowp float qt_Opacity;
|
||||||
|
uniform lowp float brightness;
|
||||||
|
void main() {
|
||||||
|
bool isOn = qt_TexCoord0.y > brightness;
|
||||||
|
lowp vec4 sourceColor = texture2D(source, qt_TexCoord0);
|
||||||
|
if (isOn) {
|
||||||
|
gl_FragColor = mix(vec4(outColor.rgb, 1.0) * sourceColor.a, sourceColor, step(threshold, distance(sourceColor.rgb / sourceColor.a, inColor.rgb))) * qt_Opacity;
|
||||||
|
} else {
|
||||||
|
gl_FragColor = sourceColor;
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: brightnessCircle
|
||||||
|
onMouseYChanged: {
|
||||||
|
var progress = 1 - mouseY / height
|
||||||
|
actionQueue.sendValue(progress * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: powerComponent
|
||||||
|
Item {
|
||||||
|
id: powerController
|
||||||
|
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 {
|
||||||
|
anchors.fill: parent
|
||||||
|
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 } }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: modeModel
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (root.colorState) {
|
||||||
|
append({modelData: qsTr("Color"), comp: colorPickerComponent})
|
||||||
|
}
|
||||||
|
if (root.ctState) {
|
||||||
|
append({modelData: qsTr("Temperature"), comp: colorTemperatureComponent})
|
||||||
|
}
|
||||||
|
if (root.brightnessState && !root.ctState && !root.colorState) {
|
||||||
|
append({modelData: qsTr("Brightness"), comp: brightnessComponent})
|
||||||
|
}
|
||||||
|
if (!root.colorState && !root.ctState && !root.brightnessState) {
|
||||||
|
append({modelData: qsTr("Power"), comp: powerComponent})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectionTabs {
|
||||||
|
id: selectionTabs
|
||||||
|
Layout.fillWidth: true
|
||||||
|
model: modeModel
|
||||||
|
visible: modeModel.count > 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: basicItems
|
||||||
|
Layout.fillWidth: !app.landscape
|
||||||
|
Layout.fillHeight: app.landscape
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
columnSpacing: app.margins
|
||||||
|
rowSpacing: app.margins
|
||||||
|
columns: app.landscape ? 1 : 2
|
||||||
|
visible: powerButton.visible || brightnessSlider.visible
|
||||||
|
|
||||||
|
ProgressButton {
|
||||||
|
id: powerButton
|
||||||
|
imageSource: root.powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg"
|
||||||
|
mode: "normal"
|
||||||
|
size: Style.bigIconSize
|
||||||
|
longpressEnabled: false
|
||||||
|
visible: root.brightnessState || root.ctState || root.colorState
|
||||||
|
onClicked: {
|
||||||
|
root.thing.executeAction("power", [{paramName: "power", value: !root.powerState.value}])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BrightnessSlider {
|
||||||
|
id: brightnessSlider
|
||||||
|
Layout.fillWidth: orientation == Qt.Horizontal
|
||||||
|
Layout.fillHeight: orientation == Qt.Vertical
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
thing: root.thing
|
||||||
|
orientation: basicItems.columns === 1 ? Qt.Vertical : Qt.Horizontal
|
||||||
|
visible: root.brightnessState && (root.ctState || root.colorState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -38,7 +38,7 @@ Item {
|
|||||||
} else if (interfaceList.indexOf("garagedoor") >= 0 ) {
|
} else if (interfaceList.indexOf("garagedoor") >= 0 ) {
|
||||||
page = "GarageThingPage.qml";
|
page = "GarageThingPage.qml";
|
||||||
} else if (interfaceList.indexOf("light") >= 0) {
|
} else if (interfaceList.indexOf("light") >= 0) {
|
||||||
page = "LightDevicePage.qml";
|
page = "LightThingPage.qml";
|
||||||
} else if (interfaceList.indexOf("shutter") >= 0 || interfaceList.indexOf("blind") >= 0) {
|
} else if (interfaceList.indexOf("shutter") >= 0 || interfaceList.indexOf("blind") >= 0) {
|
||||||
page = "ShutterDevicePage.qml";
|
page = "ShutterDevicePage.qml";
|
||||||
} else if (interfaceList.indexOf("awning") >= 0) {
|
} else if (interfaceList.indexOf("awning") >= 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user