improve groups view

This commit is contained in:
Michael Zanetti 2019-11-30 13:18:08 +01:00
parent 37397e2c65
commit 14c7a3de1e
13 changed files with 543 additions and 444 deletions

View File

@ -137,6 +137,7 @@ void Engine::onConnectedChanged()
qDebug() << "Engine: connected changed:" << m_jsonRpcClient->connected();
m_deviceManager->clear();
m_ruleManager->clear();
m_tagsManager->clear();
if (m_jsonRpcClient->connected()) {
qDebug() << "Engine: inital setup required:" << m_jsonRpcClient->initialSetupRequired() << "auth required:" << m_jsonRpcClient->authenticationRequired();
if (!m_jsonRpcClient->initialSetupRequired() && !m_jsonRpcClient->authenticationRequired()) {

View File

@ -23,6 +23,11 @@ void TagsManager::init()
m_jsonClient->sendCommand("Tags.GetTags", this, "getTagsReply");
}
void TagsManager::clear()
{
m_tags->clear();
}
bool TagsManager::busy() const
{
return m_busy;
@ -123,7 +128,6 @@ void TagsManager::getTagsReply(const QVariantMap &params)
{
QList<Tag*> tags;
foreach (const QVariant &tagVariant, params.value("params").toMap().value("tags").toList()) {
qDebug() << "aDDING TAG";
Tag *tag = unpackTag(tagVariant.toMap());
if (tag) {
tags.append(tag);

View File

@ -17,6 +17,7 @@ public:
QString nameSpace() const override;
void init();
void clear();
bool busy() const;
Tags* tags() const;

View File

@ -120,6 +120,13 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
tr("Playback status"),
tr("Playback status changed"),
tr("Set playback status"));
addInterface("mediacontroller", tr("Media controllers"));
addActionType("mediacontroller", "play", tr("Start playback"), new ParamTypes());
addActionType("mediacontroller", "stop", tr("Stop playback"), new ParamTypes());
addActionType("mediacontroller", "pause", tr("Pause playback"), new ParamTypes());
addActionType("mediacontroller", "skipBack", tr("Skip back"), new ParamTypes());
addActionType("mediacontroller", "skipNext", tr("Skip next"), new ParamTypes());
}
int Interfaces::rowCount(const QModelIndex &parent) const
@ -149,6 +156,9 @@ QHash<int, QByteArray> Interfaces::roleNames() const
Interface *Interfaces::get(int index) const
{
if (index < 0 || index >= m_list.count()) {
return nullptr;
}
return m_list.at(index);
}

View File

@ -199,5 +199,6 @@
<file>ui/magic/NewScenePage.qml</file>
<file>ui/mainviews/GroupsView.qml</file>
<file>ui/grouping/GroupPage.qml</file>
<file>ui/delegates/ThingTile.qml</file>
</qresource>
</RCC>

View File

@ -299,7 +299,7 @@ Page {
GroupsView {
id: groupsView
property string title: qsTr("My groups" + count);
property string title: qsTr("My groups");
width: swipeView.width
height: swipeView.height
}

View File

@ -0,0 +1,376 @@
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"
MainPageTile {
id: root
text: device.name.toUpperCase()
iconName: app.interfacesToIcon(deviceClass.interfaces)
iconColor: app.accentColor
batteryCritical: batteryCriticalState && batteryCriticalState.value === true
disconnected: connectedState && connectedState.value === false
backgroundImage: artworkState && artworkState.value.length > 0 ? artworkState.value : ""
property Device device: null
readonly property DeviceClass deviceClass: device ? engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
readonly property State connectedState: deviceClass.interfaces.indexOf("connectable") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("connected").id) : null
readonly property State batteryCriticalState: deviceClass.interfaces.indexOf("battery") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("batteryCritical").id) : null
readonly property State artworkState: deviceClass.interfaces.indexOf("mediametadataprovider") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("artwork").id) : null
contentItem: Loader {
id: loader
anchors.fill: parent
sourceComponent: {
if (root.deviceClass.interfaces.indexOf("closable") >= 0) {
return closableComponent;
}
if (root.deviceClass.interfaces.indexOf("power") >= 0) {
return lightsComponent;
}
if (root.deviceClass.interfaces.indexOf("sensor") >= 0) {
return sensorsComponent;
}
if (root.deviceClass.interfaces.indexOf("weather") >= 0) {
return sensorsComponent;
}
if (root.deviceClass.interfaces.indexOf("smartmeter") >= 0) {
return sensorsComponent;
}
if (root.deviceClass.interfaces.indexOf("mediacontroller") >= 0) {
return mediaComponent;
}
}
Binding { target: loader.item ? loader.item : null; property: "deviceClass"; value: root.deviceClass }
Binding { target: loader.item ? loader.item : null; property: "device"; value: root.device }
}
Component {
id: lightsComponent
RowLayout {
property var device: null
property var deviceClass: null
readonly property var powerStateType: deviceClass.stateTypes.findByName("power");
readonly property var powerState: device.states.getState(powerStateType.id)
readonly property var brightnessStateType: deviceClass.stateTypes.findByName("brightness");
readonly property var brightnessState: brightnessStateType ? device.states.getState(brightnessStateType.id) : null
ThrottledSlider {
Layout.fillWidth: true
Layout.leftMargin: app.margins / 2
Layout.alignment: Qt.AlignVCenter
opacity: deviceClass.interfaces.indexOf("dimmablelight") >= 0 ? 1 : 0
enabled: opacity > 0
from: 0
to: 100
value: brightnessState ? brightnessState.value : 0
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: deviceClass.interfaces.indexOf("light") >= 0
? (powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg")
: app.interfacesToIcon(deviceClass.interfaces)
color: powerState.value === true ? app.accentColor : keyColor
}
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"] = !powerState.value;
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("smartmeterconsumer") >= 0) {
tmp.push({iface: "smartmeterconsumer", state: "totalEnergyConsumed"});
}
if (deviceClass.interfaces.indexOf("smartmeterproducer") >= 0) {
tmp.push({iface: "smartmeterproducer", state: "totalEnergyProduced"});
}
if (deviceClass.interfaces.indexOf("daylightsensor") >= 0) {
tmp.push({iface: "daylightsensor", state: "daylight"});
}
if (deviceClass.interfaces.indexOf("presencesensor") >= 0) {
tmp.push({iface: "presencesensor", state: "isPresent"});
}
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;
}
}
Item { Layout.fillHeight: true; Layout.fillWidth: true }
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)
}
Item { Layout.fillHeight: true; Layout.fillWidth: true }
ColumnLayout {
Layout.fillWidth: true
spacing: 0
visible: sensorsRoot.currentStateType.type.toLowerCase() !== "bool"
Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
text: sensorsRoot.currentStateType.unitString
font.pixelSize: app.smallFont
}
Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
text: sensorsRoot.currentState.value// + " " + sensorsRoot.currentStateType.unitString
elide: Text.ElideRight
}
}
Led {
state: sensorsRoot.currentState.value === true ? "on" : "off"
visible: sensorsRoot.currentStateType.type.toLowerCase() === "bool"
}
Item { Layout.fillHeight: true; Layout.fillWidth: true }
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: percentageStateType ? device.states.getState(percentageStateType.id) : null
from: 0
to: 100
value: percentateState ? percentateState.value : 0
}
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);
}
}
}
}
Component {
id: mediaComponent
RowLayout {
id: mediaRoot
property Device device: null
property DeviceClass deviceClass: null
readonly property State playbackState: device.states.getState(deviceClass.stateTypes.findByName("playbackStatus").id)
function executeAction(actionName, params) {
var actionTypeId = deviceClass.actionTypes.findByName(actionName).id;
engine.deviceManager.executeAction(device.id, actionTypeId, params)
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: app.iconSize * .9
Layout.preferredWidth: height
imageSource: "../images/media-skip-backward.svg"
longpressImageSource: "../images/media-seek-backward.svg"
repeat: true
onClicked: {
mediaRoot.executeAction("skipBack")
}
onLongpressed: {
mediaRoot.executeAction("fastRewind")
}
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: app.iconSize * 1.3
Layout.preferredWidth: height
imageSource: mediaRoot.playbackState.value === "Playing" ? "../images/media-playback-pause.svg" : "../images/media-playback-start.svg"
longpressImageSource: "../images/media-playback-stop.svg"
longpressEnabled: mediaRoot.playbackState.value !== "Stopped"
onClicked: {
if (mediaRoot.playbackState.value === "Playing") {
mediaRoot.executeAction("pause")
} else {
mediaRoot.executeAction("play")
}
}
onLongpressed: {
mediaRoot.executeAction("stop")
}
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: app.iconSize * .9
Layout.preferredWidth: height
imageSource: "../images/media-skip-forward.svg"
longpressImageSource: "../images/media-seek-forward.svg"
repeat: true
onClicked: {
mediaRoot.executeAction("skipNext")
}
onLongpressed: {
mediaRoot.executeAction("fastForward")
}
}
Item { Layout.fillWidth: true }
}
}
}

View File

@ -93,7 +93,6 @@ Page {
}
}
function addToGroup() {
// engine.tagsManager.tagDevice(root.device.id, "group", "My group 1")
var dialog = addToGroupDialog.createObject(root)
dialog.open();
}
@ -122,6 +121,9 @@ Page {
headerIcon: "../images/view-grid-symbolic.svg"
RowLayout {
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
spacing: app.margins
TextField {
id: newGroupdTextField
Layout.fillWidth: true
@ -141,6 +143,8 @@ Page {
ListView {
Layout.fillWidth: true
height: 200
clip: true
ScrollIndicator.vertical: ScrollIndicator {}
model: TagListModel {
id: groupTags

View File

@ -4,6 +4,7 @@ import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.1
import Nymea 1.0
import "../components"
import "../delegates"
Page {
id: root
@ -27,11 +28,28 @@ Page {
showStates: true
}
ListView {
GridView {
id: gridView
anchors.fill: parent
anchors.margins: app.margins / 2
model: devicesInGroup
delegate: Label {
text: model.name
readonly property int minTileWidth: 180
readonly property int minTileHeight: 240
readonly property int tilesPerRow: root.width / minTileWidth
cellWidth: gridView.width / tilesPerRow
cellHeight: cellWidth
delegate: ThingTile {
width: gridView.cellWidth
height: gridView.cellHeight
device: devicesInGroup.get(index)
onClicked: pageStack.push(Qt.resolvedUrl("../devicepages/" + app.interfaceListToDevicePage(deviceClass.interfaces)), {device: device})
}
}

View File

@ -5,11 +5,15 @@ import QtQuick.Layouts 1.2
import Nymea 1.0
import "../components"
Item {
MouseArea {
id: root
property alias count: interfacesGridView.count
property alias model: interfacesGridView.model
// Prevent scroll events to swipe left/right in case they fall through the grid
preventStealing: true
onWheel: wheel.accepted = true
GridView {
id: interfacesGridView
anchors.fill: parent

View File

@ -4,13 +4,18 @@ import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.2
import Nymea 1.0
import "../components"
import "../delegates"
Item {
MouseArea {
id: root
property bool editMode: false
readonly property int count: tagsProxy.count
// Prevent scroll events to swipe left/right in case they fall through the grid
preventStealing: true
onWheel: wheel.accepted = true
TagsProxyModel {
id: tagsProxy
tags: engine.tagsManager.tags
@ -29,57 +34,16 @@ Item {
cellHeight: cellWidth
model: tagsProxy
delegate: MainPageTile {
delegate: ThingTile {
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 Device device: engine.deviceManager.devices.getDevice(deviceId)
readonly property DeviceClass deviceClass: device ? engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
readonly property State connectedState: deviceClass.interfaces.indexOf("connectable") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("connected").id) : null
readonly property State batteryCriticalState: deviceClass.interfaces.indexOf("battery") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("batteryCritical").id) : null
device: engine.deviceManager.devices.getDevice(deviceId)
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("power") >= 0) {
return lightsComponent;
}
if (delegateRoot.deviceClass.interfaces.indexOf("sensor") >= 0) {
return sensorsComponent;
}
if (delegateRoot.deviceClass.interfaces.indexOf("weather") >= 0) {
return sensorsComponent;
}
if (delegateRoot.deviceClass.interfaces.indexOf("smartmeter") >= 0) {
return sensorsComponent;
}
if (delegateRoot.deviceClass.interfaces.indexOf("mediacontroller") >= 0) {
return mediaComponent;
}
}
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
@ -174,330 +138,4 @@ Item {
}
}
}
Component {
id: lightsComponent
RowLayout {
property var device: null
property var deviceClass: null
readonly property var powerStateType: deviceClass.stateTypes.findByName("power");
readonly property var powerState: device.states.getState(powerStateType.id)
readonly property var brightnessStateType: deviceClass.stateTypes.findByName("brightness");
readonly property var brightnessState: brightnessStateType ? device.states.getState(brightnessStateType.id) : null
ThrottledSlider {
Layout.fillWidth: true
Layout.leftMargin: app.margins / 2
Layout.alignment: Qt.AlignVCenter
opacity: deviceClass.interfaces.indexOf("dimmablelight") >= 0 ? 1 : 0
enabled: opacity > 0
from: 0
to: 100
value: brightnessState ? brightnessState.value : 0
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: deviceClass.interfaces.indexOf("light") >= 0
? (powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg")
: app.interfacesToIcon(deviceClass.interfaces)
color: powerState.value === true ? app.accentColor : keyColor
}
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"] = !powerState.value;
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("smartmeterconsumer") >= 0) {
tmp.push({iface: "smartmeterconsumer", state: "totalEnergyConsumed"});
}
if (deviceClass.interfaces.indexOf("smartmeterproducer") >= 0) {
tmp.push({iface: "smartmeterproducer", state: "totalEnergyProduced"});
}
if (deviceClass.interfaces.indexOf("daylightsensor") >= 0) {
tmp.push({iface: "daylightsensor", state: "daylight"});
}
if (deviceClass.interfaces.indexOf("presencesensor") >= 0) {
tmp.push({iface: "presencesensor", state: "isPresent"});
}
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;
}
}
Item { Layout.fillHeight: true; Layout.fillWidth: true }
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)
}
Item { Layout.fillHeight: true; Layout.fillWidth: true }
ColumnLayout {
Layout.fillWidth: true
spacing: 0
visible: sensorsRoot.currentStateType.type.toLowerCase() !== "bool"
Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
text: sensorsRoot.currentStateType.unitString
font.pixelSize: app.smallFont
}
Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
text: sensorsRoot.currentState.value// + " " + sensorsRoot.currentStateType.unitString
elide: Text.ElideRight
}
}
Led {
state: sensorsRoot.currentState.value === true ? "on" : "off"
visible: sensorsRoot.currentStateType.type.toLowerCase() === "bool"
}
Item { Layout.fillHeight: true; Layout.fillWidth: true }
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);
}
}
}
}
Component {
id: mediaComponent
RowLayout {
id: mediaRoot
property Device device: null
property DeviceClass deviceClass: null
readonly property State playbackState: device.states.getState(deviceClass.stateTypes.findByName("playbackStatus").id)
function executeAction(actionName, params) {
var actionTypeId = deviceClass.actionTypes.findByName(actionName).id;
engine.deviceManager.executeAction(device.id, actionTypeId, params)
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: app.iconSize * .9
Layout.preferredWidth: height
imageSource: "../images/media-skip-backward.svg"
longpressImageSource: "../images/media-seek-backward.svg"
repeat: true
onClicked: {
mediaRoot.executeAction("skipBack")
}
onLongpressed: {
mediaRoot.executeAction("fastRewind")
}
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: app.iconSize * 1.3
Layout.preferredWidth: height
imageSource: mediaRoot.playbackState.value === "Playing" ? "../images/media-playback-pause.svg" : "../images/media-playback-start.svg"
longpressImageSource: "../images/media-playback-stop.svg"
longpressEnabled: mediaRoot.playbackState.value !== "Stopped"
onClicked: {
if (mediaRoot.playbackState.value === "Playing") {
mediaRoot.executeAction("pause")
} else {
mediaRoot.executeAction("play")
}
}
onLongpressed: {
mediaRoot.executeAction("stop")
}
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: app.iconSize * .9
Layout.preferredWidth: height
imageSource: "../images/media-skip-forward.svg"
longpressImageSource: "../images/media-seek-forward.svg"
repeat: true
onClicked: {
mediaRoot.executeAction("skipNext")
}
onLongpressed: {
mediaRoot.executeAction("fastForward")
}
}
Item { Layout.fillWidth: true }
}
}
}

View File

@ -5,8 +5,10 @@ import Nymea 1.0
import QtQuick.Controls.Material 2.2
import "../components"
Item {
MouseArea {
id: root
preventStealing: true
onWheel: wheel.accepted = true
readonly property int count: groupsGridView.count
@ -15,6 +17,7 @@ Item {
anchors.fill: parent
anchors.margins: app.margins / 2
readonly property int minTileWidth: 180
readonly property int minTileHeight: 180
readonly property int tilesPerRow: root.width / minTileWidth
@ -48,7 +51,7 @@ Item {
InterfacesProxy {
id: controlsInGroup
shownInterfaces: ["light", "simpleclosable"]
shownInterfaces: ["light", "simpleclosable", "mediacontroller"]
devicesProxyFilter: devicesInGroup
showStates: true
showActions: true
@ -61,7 +64,10 @@ Item {
}
contentItem: ItemDelegate {
padding: 0
leftPadding: 0
topPadding: 0
rightPadding: 0
bottomPadding: 0
onClicked: {
pageStack.push(Qt.resolvedUrl("../grouping/GroupPage.qml"), {groupTag: model.tagId})
@ -74,6 +80,7 @@ Item {
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
Label {
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
anchors { leftMargin: app.margins; rightMargin: app.margins }
text: model.tagId.substring(6)
elide: Text.ElideRight
@ -82,22 +89,35 @@ Item {
Item {
Layout.fillHeight: true
Layout.fillWidth: true
ColorIcon {
anchors.centerIn: parent
height: app.iconSize * 2
width: height
visible: controlsInGroup.count == 0
color: app.accentColor
name: "../images/view-grid-symbolic.svg"
}
ColumnLayout {
anchors.fill: parent
Repeater {
model: controlsInGroup
model: Math.min(controlsInGroup.count, parent.height / 50)
delegate: Loader {
id: controlLoader
Layout.fillWidth: true
Layout.leftMargin: app.margins / 2
Layout.rightMargin: app.margins / 2
property string interfaceName: controlsInGroup.get(index).name
sourceComponent: {
switch (model.name) {
switch (interfaceName) {
case "simpleclosable":
return closableDelegate
case "light":
return lightDelegate
case "mediacontroller":
return mediaControllerDelegate
}
}
Binding {
@ -111,74 +131,77 @@ Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: app.iconSize * 1.2
Layout.alignment: Qt.AlignRight
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, 0.05)
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: app.iconSize * 1.2
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, 0.05)
RowLayout {
anchors.fill: parent
RowLayout {
anchors.fill: parent
Repeater {
model: sensorsInGroup
delegate: Row {
ColorIcon {
height: app.iconSize * .8
width: height
name: app.interfaceToIcon(model.name)
color: app.interfaceToColor(model.name)
Repeater {
model: sensorsInGroup
delegate: Row {
height: parent.height
ColorIcon {
height: app.iconSize * .8
width: height
name: app.interfaceToIcon(model.name)
color: app.interfaceToColor(model.name)
}
DevicesProxy {
id: innerProxy
engine: _engine
parentProxy: devicesInGroup
shownInterfaces: [model.name]
}
Led {
visible: ["presencesensor"].indexOf(model.name) >= 0
state: {
var stateName = null
switch (model.name) {
case "presencesensor":
stateName = "isPresent"
break;
}
DevicesProxy {
id: innerProxy
engine: _engine
parentProxy: devicesInGroup
shownInterfaces: [model.name]
if (!stateName) {
return "off";
}
var ret = false;
for (var i = 0; i < innerProxy.count; i++) {
ret |= innerProxy.get(i).states.getState(innerProxy.get(i).deviceClass.stateTypes.findByName(stateName).id).value
}
return ret ? "on" : "off";
}
}
Label {
height: parent.height
verticalAlignment: Text.AlignVCenter
text: {
var stateName = null;
switch (model.name) {
case "temperaturesensor":
stateName = "temperature";
break;
case "lightsensor":
stateName = "lightIntensity"
break;
}
if (!stateName) {
return "";
}
Led {
visible: ["presencesensor"].indexOf(model.name) >= 0
state: {
var stateName = null
switch (model.name) {
case "presencesensor":
stateName = "isPresent"
break;
}
if (!stateName) {
return "off";
}
var ret = false;
for (var i = 0; i < innerProxy.count; i++) {
ret |= innerProxy.get(i).states.getState(innerProxy.get(i).deviceClass.stateTypes.findByName(stateName).id).value
}
return ret ? "on" : "off";
}
}
Label {
text: {
var stateName = null;
switch (model.name) {
case "temperaturesensor":
stateName = "temperature";
break;
case "lightsensor":
stateName = "lightIntensity"
break;
}
if (!stateName) {
return "";
}
var ret = 0
for (var i = 0; i < innerProxy.count; i++) {
ret += innerProxy.get(i).states.getState(innerProxy.get(i).deviceClass.stateTypes.findByName(stateName).id).value
}
return (ret / innerProxy.count).toFixed(1)
}
var ret = 0
for (var i = 0; i < innerProxy.count; i++) {
ret += innerProxy.get(i).states.getState(innerProxy.get(i).deviceClass.stateTypes.findByName(stateName).id).value
}
return (ret / innerProxy.count).toFixed(1)
}
}
}
@ -351,4 +374,19 @@ Item {
}
}
}
Component {
id: mediaControllerDelegate
MediaControls {
property var devices: null
DevicesProxy {
id: mediaControllers
engine: _engine
parentProxy: devices
shownInterfaces: ["mediacontroller"]
}
device: mediaControllers.parentProxy ? mediaControllers.get(0) : null
}
}
}

View File

@ -5,11 +5,15 @@ import Nymea 1.0
import QtQuick.Controls.Material 2.2
import "../components"
Item {
MouseArea {
id: root
readonly property int count: interfacesGridView.count
// Prevent scroll events to swipe left/right in case they fall through the grid
preventStealing: true
onWheel: wheel.accepted = true
GridView {
id: interfacesGridView
anchors.fill: parent