421 lines
17 KiB
QML
421 lines
17 KiB
QML
import QtQuick 2.9
|
|
import QtQuick.Controls 2.2
|
|
import QtQuick.Layouts 1.3
|
|
import QtQuick.Controls.Material 2.2
|
|
import Nymea 1.0
|
|
import "../components"
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property bool editMode: false
|
|
readonly property int count: tagsProxy.count
|
|
|
|
TagsProxyModel {
|
|
id: tagsProxy
|
|
tags: engine.tagsManager.tags
|
|
filterTagId: "favorites"
|
|
}
|
|
|
|
GridView {
|
|
id: gridView
|
|
anchors.fill: parent
|
|
anchors.margins: app.margins / 2
|
|
readonly property int minTileWidth: 180
|
|
readonly property int minTileHeight: 240
|
|
readonly property int tilesPerRow: root.width / minTileWidth
|
|
|
|
cellWidth: gridView.width / tilesPerRow
|
|
cellHeight: cellWidth
|
|
|
|
model: tagsProxy
|
|
delegate: MainPageTile {
|
|
id: delegateRoot
|
|
width: gridView.cellWidth
|
|
height: gridView.cellHeight
|
|
text: device.name.toUpperCase()
|
|
iconName: app.interfacesToIcon(deviceClass.interfaces)
|
|
iconColor: app.accentColor
|
|
visible: !fakeDragItem.visible || fakeDragItem.deviceId !== device.id
|
|
batteryCritical: batteryCriticalState && batteryCriticalState.value === true
|
|
disconnected: connectedState && connectedState.value === false
|
|
|
|
property var modelIndex: index
|
|
|
|
property string deviceId: model.deviceId
|
|
property string ruleId: model.ruleId
|
|
readonly property var device: engine.deviceManager.devices.getDevice(deviceId)
|
|
readonly property var deviceClass: device ? engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
|
|
readonly property var connectedState: deviceClass.interfaces.indexOf("connectable") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("connected").id) : null
|
|
readonly property var batteryCriticalState: deviceClass.interfaces.indexOf("battery") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("batteryCritical").id) : null
|
|
|
|
onClicked: pageStack.push(Qt.resolvedUrl("../devicepages/" + app.interfaceListToDevicePage(deviceClass.interfaces)), {device: device})
|
|
|
|
onPressAndHold: root.editMode = true
|
|
|
|
contentItem: Loader {
|
|
id: loader
|
|
anchors.fill: parent
|
|
sourceComponent: {
|
|
if (delegateRoot.deviceClass.interfaces.indexOf("closable") >= 0) {
|
|
return closableComponent;
|
|
}
|
|
if (delegateRoot.deviceClass.interfaces.indexOf("light") >= 0) {
|
|
return lightsComponent;
|
|
}
|
|
if (delegateRoot.deviceClass.interfaces.indexOf("sensor") >= 0) {
|
|
return sensorsComponent;
|
|
}
|
|
if (delegateRoot.deviceClass.interfaces.indexOf("weather") >= 0) {
|
|
return sensorsComponent;
|
|
}
|
|
}
|
|
Binding { target: loader.item ? loader.item : null; property: "deviceClass"; value: delegateRoot.deviceClass }
|
|
Binding { target: loader.item ? loader.item : null; property: "device"; value: delegateRoot.device }
|
|
}
|
|
|
|
SequentialAnimation {
|
|
loops: Animation.Infinite
|
|
running: root.editMode
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { from: 0; to: 3; target: delegateRoot; duration: 75; property: "rotation" }
|
|
NumberAnimation { from: 3; to: -3; target: delegateRoot; duration: 150; property: "rotation" }
|
|
NumberAnimation { from: -3; to: 0; target: delegateRoot; duration: 75; property: "rotation" }
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: dragArea
|
|
anchors.fill: parent
|
|
enabled: root.editMode
|
|
propagateComposedEvents: true
|
|
property var dragOffset: ({})
|
|
property var draggedItem: null
|
|
|
|
onPressed: {
|
|
var item = gridView.itemAt(mouseX, mouseY)
|
|
draggedItem = item;
|
|
dragOffset = mapToItem(item, mouseX, mouseY)
|
|
fakeDragItem.x = mouseX - dragOffset.x;
|
|
fakeDragItem.y = mouseY - dragOffset.y;
|
|
fakeDragItem.text = item.text
|
|
fakeDragItem.iconName = item.iconName
|
|
fakeDragItem.iconColor = item.iconColor;
|
|
fakeDragItem.deviceId = item.device.id
|
|
fakeDragItem.batteryCritical = item.batteryCritical
|
|
fakeDragItem.disconnected = item.disconnected
|
|
drag.target = fakeDragItem
|
|
}
|
|
onReleased: {
|
|
drag.target = null
|
|
draggedItem = null
|
|
}
|
|
|
|
onClicked: {
|
|
root.editMode = false
|
|
}
|
|
}
|
|
|
|
MainPageTile {
|
|
id: fakeDragItem
|
|
width: gridView.cellWidth
|
|
height: gridView.cellHeight
|
|
Drag.active: dragArea.drag.active
|
|
visible: Drag.active
|
|
property var deviceId
|
|
}
|
|
|
|
DropArea {
|
|
id: dropArea
|
|
anchors.fill: gridView
|
|
|
|
property int from: -1
|
|
property int to: -1
|
|
|
|
onEntered: {
|
|
var index = gridView.indexAt(drag.x + dragArea.dragOffset.x, drag.y + dragArea.dragOffset.y);
|
|
from = index;
|
|
to = index;
|
|
}
|
|
|
|
onPositionChanged: {
|
|
var index = gridView.indexAt(drag.x + dragArea.dragOffset.x, drag.y + dragArea.dragOffset.y);
|
|
if (to !== index && from !== index && index >= 0 && index <= tagsProxy.count) {
|
|
to = index;
|
|
print("should move", from, "to", to)
|
|
for (var i = 0; i < tagsProxy.count; i++) {
|
|
if (i < Math.min(from, to) || i > Math.max(from, to)) {
|
|
// outside the range... don't touch
|
|
continue;
|
|
}
|
|
var newIdx;
|
|
if (i == from) {
|
|
newIdx = to;
|
|
} else {
|
|
if (from < to) {
|
|
// item is moved down the list
|
|
newIdx = i - 1;
|
|
} else {
|
|
newIdx = i + 1;
|
|
}
|
|
}
|
|
|
|
var tag = tagsProxy.get(i);
|
|
engine.tagsManager.tagDevice(tag.deviceId, tag.tagId, newIdx);
|
|
}
|
|
from = index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: lightsComponent
|
|
RowLayout {
|
|
property var device: null
|
|
property var deviceClass: null
|
|
|
|
ItemDelegate {
|
|
Layout.preferredWidth: app.iconSize
|
|
Layout.preferredHeight: app.iconSize
|
|
Layout.leftMargin: app.margins / 2
|
|
Layout.alignment: Qt.AlignVCenter
|
|
padding: 0; topPadding: 0; bottomPadding: 0
|
|
contentItem: ColorIcon {
|
|
name: "../images/light-off.svg"
|
|
color: app.accentColor
|
|
}
|
|
onClicked: {
|
|
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
|
var actionType = deviceClass.actionTypes.findByName("power");
|
|
var params = [];
|
|
var powerParam = {}
|
|
powerParam["paramTypeId"] = actionType.paramTypes.get(0).id;
|
|
powerParam["value"] = false;
|
|
params.push(powerParam)
|
|
engine.deviceManager.executeAction(device.id, actionType.id, params);
|
|
}
|
|
}
|
|
|
|
ThrottledSlider {
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignVCenter
|
|
visible: deviceClass.interfaces.indexOf("dimmablelight") >= 0
|
|
readonly property var brightnessStateType: deviceClass.stateTypes.findByName("brightness");
|
|
readonly property var brightnessState: device.states.getState(brightnessStateType.id)
|
|
from: 0
|
|
to: 100
|
|
value: brightnessState.value
|
|
onMoved: {
|
|
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
|
var actionType = deviceClass.actionTypes.findByName("brightness");
|
|
var params = [];
|
|
var powerParam = {}
|
|
powerParam["paramTypeId"] = actionType.paramTypes.get(0).id;
|
|
powerParam["value"] = value;
|
|
params.push(powerParam)
|
|
engine.deviceManager.executeAction(device.id, actionType.id, params);
|
|
}
|
|
}
|
|
|
|
ItemDelegate {
|
|
Layout.preferredWidth: app.iconSize
|
|
Layout.preferredHeight: width
|
|
Layout.rightMargin: app.margins / 2
|
|
Layout.alignment: Qt.AlignVCenter
|
|
padding: 0; topPadding: 0; bottomPadding: 0
|
|
contentItem: ColorIcon {
|
|
name: "../images/light-on.svg"
|
|
color: app.accentColor
|
|
}
|
|
onClicked: {
|
|
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
|
var actionType = deviceClass.actionTypes.findByName("power");
|
|
var params = [];
|
|
var powerParam = {}
|
|
powerParam["paramTypeId"] = actionType.paramTypes.get(0).id;
|
|
powerParam["value"] = true;
|
|
params.push(powerParam)
|
|
engine.deviceManager.executeAction(device.id, actionType.id, params);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: sensorsComponent
|
|
RowLayout {
|
|
id: sensorsRoot
|
|
property var device: null
|
|
property var deviceClass: null
|
|
spacing: 0
|
|
|
|
property var shownInterfaces: []
|
|
property int currentStateIndex: -1
|
|
property var currentStateType: deviceClass ? deviceClass.stateTypes.findByName(shownInterfaces[currentStateIndex].state) : null
|
|
property var currentState: currentStateType ? device.states.getState(currentStateType.id) : null
|
|
|
|
onDeviceClassChanged: {
|
|
if (deviceClass == null) {
|
|
return;
|
|
}
|
|
|
|
var tmp = []
|
|
if (deviceClass.interfaces.indexOf("temperaturesensor") >= 0) {
|
|
tmp.push({iface: "temperaturesensor", state: "temperature"});
|
|
}
|
|
if (deviceClass.interfaces.indexOf("humiditysensor") >= 0) {
|
|
tmp.push({iface: "humiditysensor", state: "humidity"});
|
|
}
|
|
if (deviceClass.interfaces.indexOf("moisturesensor") >= 0) {
|
|
tmp.push({iface: "moisturesensor", state: "moisture"});
|
|
}
|
|
if (deviceClass.interfaces.indexOf("pressuresensor") >= 0) {
|
|
tmp.push({iface: "pressuresensor", state: "pressure"});
|
|
}
|
|
if (deviceClass.interfaces.indexOf("lightsensor") >= 0) {
|
|
tmp.push({iface: "lightsensor", state: "lightIntensity"});
|
|
}
|
|
if (deviceClass.interfaces.indexOf("conductivitysensor") >= 0) {
|
|
tmp.push({iface: "conductivitysensor", state: "conductivity"});
|
|
}
|
|
if (deviceClass.interfaces.indexOf("noisesensor") >= 0) {
|
|
tmp.push({iface: "noisesensor", state: "noise"});
|
|
}
|
|
if (deviceClass.interfaces.indexOf("co2sensor") >= 0) {
|
|
tmp.push({iface: "co2sensor", state: "co2"});
|
|
}
|
|
if (deviceClass.interfaces.indexOf("weather") >= 0) {
|
|
tmp.push({iface: "temperaturesensor", state: "temperature"});
|
|
tmp.push({iface: "humiditysensor", state: "humidity"});
|
|
tmp.push({iface: "pressuresensor", state: "pressure"});
|
|
}
|
|
|
|
shownInterfaces = tmp
|
|
currentStateIndex = 0
|
|
}
|
|
|
|
ItemDelegate {
|
|
Layout.preferredWidth: app.iconSize
|
|
Layout.preferredHeight: width
|
|
Layout.leftMargin: app.margins / 2
|
|
Layout.alignment: Qt.AlignVCenter
|
|
padding: 0; topPadding: 0; bottomPadding: 0
|
|
visible: sensorsRoot.shownInterfaces.length > 1
|
|
contentItem: ColorIcon {
|
|
name: "../images/back.svg"
|
|
}
|
|
onClicked: {
|
|
var newIndex = sensorsRoot.currentStateIndex - 1;
|
|
if (newIndex < 0) newIndex = sensorsRoot.shownInterfaces.length - 1
|
|
sensorsRoot.currentStateIndex = newIndex;
|
|
}
|
|
}
|
|
|
|
ColorIcon {
|
|
Layout.preferredWidth: app.iconSize
|
|
Layout.preferredHeight: width
|
|
Layout.alignment: Qt.AlignVCenter
|
|
color: app.interfaceToColor(sensorsRoot.shownInterfaces[sensorsRoot.currentStateIndex].iface)
|
|
name: app.interfaceToIcon(sensorsRoot.shownInterfaces[sensorsRoot.currentStateIndex].iface)
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 0
|
|
|
|
Label {
|
|
Layout.fillWidth: true
|
|
horizontalAlignment: Text.AlignRight
|
|
text: sensorsRoot.currentStateType.unitString
|
|
font.pixelSize: app.smallFont
|
|
color: app.accentColor
|
|
}
|
|
Label {
|
|
Layout.fillWidth: true
|
|
horizontalAlignment: Text.AlignRight
|
|
text: sensorsRoot.currentState.value// + " " + sensorsRoot.currentStateType.unitString
|
|
color: app.accentColor
|
|
}
|
|
}
|
|
|
|
ItemDelegate {
|
|
Layout.preferredWidth: app.iconSize
|
|
Layout.preferredHeight: width
|
|
Layout.rightMargin: app.margins / 2
|
|
Layout.alignment: Qt.AlignVCenter
|
|
padding: 0; topPadding: 0; bottomPadding: 0
|
|
visible: sensorsRoot.shownInterfaces.length > 1
|
|
contentItem: ColorIcon {
|
|
name: "../images/next.svg"
|
|
}
|
|
onClicked: {
|
|
var newIndex = sensorsRoot.currentStateIndex + 1;
|
|
if (newIndex >= sensorsRoot.shownInterfaces.length) newIndex = 0;
|
|
sensorsRoot.currentStateIndex = newIndex;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: closableComponent
|
|
RowLayout {
|
|
property var device: null
|
|
property var deviceClass: null
|
|
|
|
ItemDelegate {
|
|
Layout.preferredWidth: app.iconSize
|
|
Layout.preferredHeight: width
|
|
Layout.leftMargin: app.margins / 2
|
|
Layout.alignment: Qt.AlignVCenter
|
|
padding: 0; topPadding: 0; bottomPadding: 0
|
|
contentItem: ColorIcon {
|
|
name: "../images/up.svg"
|
|
color: app.accentColor
|
|
}
|
|
onClicked: {
|
|
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
|
var actionType = deviceClass.actionTypes.findByName("open");
|
|
engine.deviceManager.executeAction(device.id, actionType.id);
|
|
}
|
|
}
|
|
|
|
Slider {
|
|
id: closableSlider
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignVCenter
|
|
visible: deviceClass.interfaces.indexOf("extendedclosable") >= 0
|
|
readonly property var percentageStateType: deviceClass.stateTypes.findByName("percentage");
|
|
readonly property var percentateState: device.states.getState(percentageStateType.id)
|
|
from: 0
|
|
to: 100
|
|
value: percentateState.value
|
|
}
|
|
Item {
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignVCenter
|
|
visible: !closableSlider.visible
|
|
}
|
|
|
|
ItemDelegate {
|
|
Layout.preferredWidth: app.iconSize
|
|
Layout.preferredHeight: width
|
|
Layout.rightMargin: app.margins / 2
|
|
Layout.alignment: Qt.AlignVCenter
|
|
padding: 0; topPadding: 0; bottomPadding: 0
|
|
contentItem: ColorIcon {
|
|
name: "../images/down.svg"
|
|
color: app.accentColor
|
|
}
|
|
onClicked: {
|
|
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
|
var actionType = deviceClass.actionTypes.findByName("close");
|
|
engine.deviceManager.executeAction(device.id, actionType.id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|