diff --git a/nymea-app/configuredhostsmodel.cpp b/nymea-app/configuredhostsmodel.cpp
index 27b2f005..0a97f0c4 100644
--- a/nymea-app/configuredhostsmodel.cpp
+++ b/nymea-app/configuredhostsmodel.cpp
@@ -171,6 +171,7 @@ void ConfiguredHostsModel::saveToDisk()
QSettings settings;
settings.beginGroup("ConfiguredHosts");
settings.remove("");
+ settings.setValue("currentIndex", m_currentIndex);
for (int i = 0; i < m_list.count(); i++) {
settings.beginGroup(QString::number(i));
settings.setValue("uuid", m_list.at(i)->uuid());
diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc
index 6b5f9e70..b18e92d8 100644
--- a/nymea-app/resources.qrc
+++ b/nymea-app/resources.qrc
@@ -58,7 +58,7 @@
ui/devicepages/GarageThingPage.qml
ui/devicepages/AwningThingPage.qml
ui/devicepages/NotificationsDevicePage.qml
- ui/devicepages/LightDevicePage.qml
+ ui/devicepages/LightThingPage.qml
ui/devicepages/FingerprintReaderDevicePage.qml
ui/devicepages/DeviceLogPage.qml
ui/devicelistpages/GenericThingsListPage.qml
diff --git a/nymea-app/ui/components/ColorPicker.qml b/nymea-app/ui/components/ColorPicker.qml
index ebd4bc07..6d73a2ca 100644
--- a/nymea-app/ui/components/ColorPicker.qml
+++ b/nymea-app/ui/components/ColorPicker.qml
@@ -8,10 +8,25 @@ Item {
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 {
id: actionQueue
thing: root.thing
stateType: thing.thingClass.stateTypes.findByName("color")
+
+ property bool useStoredPoint: false
+ property point storedPoint: Qt.point(0, 0)
}
ConicalGradient {
@@ -30,6 +45,29 @@ Item {
GradientStop { position: 0.833; color: Qt.rgba(1, 0, 1, 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 {
@@ -39,19 +77,100 @@ Item {
}
OpacityMask {
anchors.fill: gradient
- source: gradient
+ source: colorizer
maskSource: mask
}
- 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" }
+
+ Rectangle {
+ id: dragHandle
+ width: 20
+ height: 20
+ radius: height / 2
+ color: Style.backgroundColor
+ border.color: Style.foregroundColor
+ 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)
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) {
diff --git a/nymea-app/ui/components/ColorTemperaturePicker.qml b/nymea-app/ui/components/ColorTemperaturePicker.qml
index a5dc60cb..a734ef39 100644
--- a/nymea-app/ui/components/ColorTemperaturePicker.qml
+++ b/nymea-app/ui/components/ColorTemperaturePicker.qml
@@ -10,9 +10,10 @@ Item {
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 State powerState: root.thing.stateByName("power")
property int value: thing.stateByName("colorTemperature").value
@@ -23,10 +24,12 @@ Item {
}
Rectangle {
+ id: background
width: Math.min(parent.width, parent.height)
anchors.centerIn: parent
height: width
radius: width / 2
+ visible: false
gradient: Gradient {
GradientStop { position: 0.0; color: "#dfffff" }
GradientStop { position: 0.5; color: "#ffffea" }
@@ -34,48 +37,44 @@ Item {
}
}
-// Rectangle {
-// id: clipRect
-// anchors.fill: parent
-// radius: Style.cornerRadius
-// }
+ Desaturate {
+ anchors.fill: background
+ source: background
+ desaturation: root.powerState.value === true ? 0 : 1
+ Behavior on desaturation { NumberAnimation { duration: Style.animationDuration } }
+ }
-// LinearGradient {
-// anchors.fill: parent
-// start: root.orientation == Qt.Horizontal ? Qt.point(0, 0) : Qt.point(0, height)
-// end: root.orientation == Qt.Horizontal ? Qt.point(width, 0) : Qt.point(0, 0)
-// source: clipRect
-// gradient: Gradient {
-// GradientStop { position: 0.0; color: "#dfffff" }
-// GradientStop { position: 0.5; color: "#ffffea" }
-// GradientStop { position: 1.0; color: "#ffd649" }
-// }
-// }
+ Rectangle {
+ id: dragHandle
+ property double valuePercentage: ((actionQueue.pendingValue || root.value) - root.colorTemperatureStateType.minValue) / (root.colorTemperatureStateType.maxValue - root.colorTemperatureStateType.minValue)
+ width: 20
+ height: 20
+ radius: height / 2
+ color: Style.backgroundColor
+ border.color: Style.foregroundColor
+ 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 {
-// anchors.fill: parent
-// onPositionChanged: {
-// var minCt = root.colorTemperatureStateType.minValue;
-// var maxCt = root.colorTemperatureStateType.maxValue
-// var ct;
+ MouseArea {
+ anchors.fill: parent
+ onPositionChanged: {
+ var minCt = root.colorTemperatureStateType.minValue;
+ var maxCt = root.colorTemperatureStateType.maxValue
+ var ct;
// if (root.orientation == Qt.Horizontal) {
// 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 = Math.min(maxCt, ct)
+ ct = Math.max(minCt, ct)
// ct = Math.min(maxCt, Math.max(minCt, ((height - mouseY) * (maxCt - minCt) / (height - dragHandle.height)) + minCt))
// }
-// actionQueue.sendValue(ct);
-// }
-// }
+ actionQueue.sendValue(ct);
+ }
+ }
}
diff --git a/nymea-app/ui/delegates/InterfaceTile.qml b/nymea-app/ui/delegates/InterfaceTile.qml
index 77392fa6..4f99725b 100644
--- a/nymea-app/ui/delegates/InterfaceTile.qml
+++ b/nymea-app/ui/delegates/InterfaceTile.qml
@@ -209,7 +209,7 @@ MainPageTile {
case "light":
var group = engine.thingManager.createGroup(Interfaces.findByName("colorlight"), thingsProxy);
print("opening lights page for group", group)
- pageStack.push("../devicepages/LightDevicePage.qml", {thing: group})
+ pageStack.push("../devicepages/LightThingPage.qml", {thing: group})
}
}
}
diff --git a/nymea-app/ui/devicelistpages/LightThingsListPage.qml b/nymea-app/ui/devicelistpages/LightThingsListPage.qml
index 884dbe39..f11ef46c 100644
--- a/nymea-app/ui/devicelistpages/LightThingsListPage.qml
+++ b/nymea-app/ui/devicelistpages/LightThingsListPage.qml
@@ -107,7 +107,7 @@ ThingsListPageBase {
property bool colorInverted: tileColored && NymeaUtils.isDark(Style.foregroundColor) === NymeaUtils.isDark(colorState.value)
onClicked: {
- if (isEnabled && (colorState || colorTemperatureState)) {
+ if (isEnabled /*&& (colorState || colorTemperatureState)*/) {
root.enterPage(index)
} else {
itemDelegate.wobble()
diff --git a/nymea-app/ui/devicepages/LightDevicePage.qml b/nymea-app/ui/devicepages/LightDevicePage.qml
deleted file mode 100644
index 4be900b2..00000000
--- a/nymea-app/ui/devicepages/LightDevicePage.qml
+++ /dev/null
@@ -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 .
-*
-* 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
- }
- }
- }
-}
diff --git a/nymea-app/ui/devicepages/LightThingPage.qml b/nymea-app/ui/devicepages/LightThingPage.qml
new file mode 100644
index 00000000..2892b23b
--- /dev/null
+++ b/nymea-app/ui/devicepages/LightThingPage.qml
@@ -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 .
+*
+* 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)
+ }
+ }
+ }
+}
diff --git a/nymea-app/ui/utils/NymeaUtils.qml b/nymea-app/ui/utils/NymeaUtils.qml
index 05870cad..b2450cf0 100644
--- a/nymea-app/ui/utils/NymeaUtils.qml
+++ b/nymea-app/ui/utils/NymeaUtils.qml
@@ -38,7 +38,7 @@ Item {
} else if (interfaceList.indexOf("garagedoor") >= 0 ) {
page = "GarageThingPage.qml";
} else if (interfaceList.indexOf("light") >= 0) {
- page = "LightDevicePage.qml";
+ page = "LightThingPage.qml";
} else if (interfaceList.indexOf("shutter") >= 0 || interfaceList.indexOf("blind") >= 0) {
page = "ShutterDevicePage.qml";
} else if (interfaceList.indexOf("awning") >= 0) {