Merge PR #466: Rework media views for new interfaces
This commit is contained in:
commit
4a80b7aea8
@ -189,7 +189,7 @@ public class NymeaAppControlService extends ControlsProviderService {
|
||||
actionTypeId = thing.actionByName("close").typeId;
|
||||
}
|
||||
param = "";
|
||||
} else if (thing.interfaces.contains("extendedvolumecontroller")) {
|
||||
} else if (thing.interfaces.contains("volumecontroller") && thing.stateByName("volume") != null) {
|
||||
actionTypeId = thing.stateByName("volume").typeId;
|
||||
FloatAction fAction = (FloatAction) action;
|
||||
param = String.valueOf(Math.round(fAction.getNewValue()));
|
||||
@ -271,10 +271,12 @@ public class NymeaAppControlService extends ControlsProviderService {
|
||||
// FIXME: There doesn't seem to be a speaker DeviceType!?!
|
||||
builder.setDeviceType(DeviceTypes.TYPE_TV);
|
||||
}
|
||||
if (thing.interfaces.contains("extendedvolumecontroller")) {
|
||||
if (thing.interfaces.contains("volumecontroller")) {
|
||||
State volumeState = thing.stateByName("volume");
|
||||
RangeTemplate rangeTemplate = new RangeTemplate(thing.id.toString(), 0, 100, Float.parseFloat(volumeState.value), 1, volumeState.displayName);
|
||||
builder.setControlTemplate(rangeTemplate);
|
||||
if (volumeState != null) {
|
||||
RangeTemplate rangeTemplate = new RangeTemplate(thing.id.toString(), 0, 100, Float.parseFloat(volumeState.value), 1, volumeState.displayName);
|
||||
builder.setControlTemplate(rangeTemplate);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.setDeviceType(DeviceTypes.TYPE_GENERIC_ON_OFF);
|
||||
|
||||
@ -712,9 +712,9 @@ void DeviceManager::browserItemResponse(int commandId, const QVariantMap ¶ms
|
||||
int DeviceManager::executeBrowserItem(const QUuid &deviceId, const QString &itemId)
|
||||
{
|
||||
QVariantMap params;
|
||||
params.insert("deviceId", deviceId);
|
||||
params.insert("thingId", deviceId);
|
||||
params.insert("itemId", itemId);
|
||||
return m_jsonClient->sendCommand("Actions.ExecuteBrowserItem", params, this, "executeBrowserItemResponse");
|
||||
return m_jsonClient->sendCommand("Integrations.ExecuteBrowserItem", params, this, "executeBrowserItemResponse");
|
||||
}
|
||||
|
||||
void DeviceManager::executeBrowserItemResponse(int commandId, const QVariantMap ¶ms)
|
||||
@ -726,12 +726,12 @@ void DeviceManager::executeBrowserItemResponse(int commandId, const QVariantMap
|
||||
int DeviceManager::executeBrowserItemAction(const QUuid &deviceId, const QString &itemId, const QUuid &actionTypeId, const QVariantList ¶ms)
|
||||
{
|
||||
QVariantMap data;
|
||||
data.insert("deviceId", deviceId);
|
||||
data.insert("thingId", deviceId);
|
||||
data.insert("itemId", itemId);
|
||||
data.insert("actionTypeId", actionTypeId);
|
||||
data.insert("params", params);
|
||||
qDebug() << "params:" << params;
|
||||
return m_jsonClient->sendCommand("Actions.ExecuteBrowserItemAction", data, this, "executeBrowserItemActionResponse");
|
||||
return m_jsonClient->sendCommand("Integrations.ExecuteBrowserItemAction", data, this, "executeBrowserItemActionResponse");
|
||||
}
|
||||
|
||||
int DeviceManager::connectIO(const QUuid &inputThingId, const QUuid &inputStateTypeId, const QUuid &outputThingId, const QUuid &outputStateTypeId, bool inverted)
|
||||
|
||||
@ -589,7 +589,8 @@ void JsonRpcClient::dataReceived(const QByteArray &data)
|
||||
}
|
||||
|
||||
if (dataMap.value("status").toString() == "error") {
|
||||
qWarning() << "An error happened in the JSONRPC layer!";
|
||||
qWarning() << "An error happened in the JSONRPC layer:" << dataMap.value("error").toString();
|
||||
qWarning() << "Request was:" << qUtf8Printable(QJsonDocument::fromVariant(reply->requestMap()).toJson());
|
||||
if (reply->nameSpace() == "JSONRPC" && reply->method() == "Hello") {
|
||||
qWarning() << "Hello call failed. Trying again without locale";
|
||||
m_id = 0;
|
||||
|
||||
@ -144,10 +144,10 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
|
||||
addActionType("mediacontroller", "pause", tr("Pause playback"), new ParamTypes());
|
||||
addActionType("mediacontroller", "skipBack", tr("Skip back"), new ParamTypes());
|
||||
addActionType("mediacontroller", "skipNext", tr("Skip next"), new ParamTypes());
|
||||
|
||||
addInterface("extendedmediacontroller", tr("Media controllers with seeking"), {"mediacontroller"});
|
||||
addActionType("extendedmediacontroller", "fastForward", tr("Fast forward"), new ParamTypes());
|
||||
addActionType("extendedmediacontroller", "fastRewind", tr("Fast rewind"), new ParamTypes());
|
||||
addActionType("mediacontroller", "fastForward", tr("Fast forward"), new ParamTypes());
|
||||
addActionType("mediacontroller", "fastRewind", tr("Fast rewind"), new ParamTypes());
|
||||
addStateType("mediacontroller", "shuffle", QVariant::Bool, true, tr("Shuffle"), tr("Shuffle changed"), tr("Set shuffle"));
|
||||
addStateType("mediacontroller", "repeat", QVariant::Bool, true, tr("Repeat"), tr("Repeat changed"), tr("Set repeat"));
|
||||
|
||||
addInterface("navigationpad", tr("Navigation pad"));
|
||||
pts = createParamTypes("to", tr("To"), QVariant::String, QVariant(), {"up", "down", "left", "right", "enter", "back"});
|
||||
@ -175,10 +175,6 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
|
||||
addInterface("extendedsmartmeterproducer", tr("Smart meters"), {"smartmeterproducer"});
|
||||
addStateType("extendedsmartmeterproducer", "currentPower", QVariant::Double, false, tr("Current power"), tr("Current power changed"));
|
||||
|
||||
addInterface("extendedvolumecontroller", tr("Volume control"), {"media"});
|
||||
addStateType("extendedvolumecontroller", "mute", QVariant::Bool, true, tr("Mute"), tr("Muted"), tr("Mute"));
|
||||
addStateType("extendedvolumecontroller", "volume", QVariant::Bool, true, tr("Volume"), tr("Volume changed"), tr("Set volume"), 0, 100);
|
||||
|
||||
addInterface("useraccesscontrol", tr("User access control systems"), {"accesscontrol"});
|
||||
addStateType("useraccesscontrol", "users", QVariant::StringList, false, tr("Users"), tr("Users changed"));
|
||||
pts = createParamTypes("user", tr("User"), QVariant::String);
|
||||
@ -268,10 +264,6 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
|
||||
addInterface("pressuresensor", tr("Pressure sensors"), {"sensor"});
|
||||
addStateType("pressuresensor", "pressure", QVariant::Double, false, tr("Pressure"), tr("Pressure changed"));
|
||||
|
||||
addInterface("shufflerepeat", tr("Shuffle and repeat controllers"));
|
||||
addStateType("shufflerepeat", "shuffle", QVariant::Bool, true, tr("Shuffle"), tr("Shuffle changed"), tr("Set shuffle"));
|
||||
addStateType("shufflerepeat", "repeat", QVariant::Bool, true, tr("Repeat"), tr("Repeat changed"), tr("Set repeat"));
|
||||
|
||||
addInterface("smartlock", tr("Smart locks"));
|
||||
addStateType("smartlock", "state", QVariant::String, false, tr("State"), tr("State changed"));
|
||||
addActionType("smartlock", "unlatch", tr("Unlatch"), new ParamTypes());
|
||||
@ -285,6 +277,8 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
|
||||
addInterface("ventilation", tr("Ventilation"), {"power"});
|
||||
|
||||
addInterface("volumecontroller", tr("Speakers"));
|
||||
addStateType("volumecontroller", "mute", QVariant::Bool, true, tr("Mute"), tr("Muted"), tr("Mute"));
|
||||
addStateType("volumecontroller", "volume", QVariant::Bool, true, tr("Volume"), tr("Volume changed"), tr("Set volume"), 0, 100);
|
||||
addActionType("volumecontroller", "increaseVolume", tr("Increase volume"), new ParamTypes());
|
||||
addActionType("volumecontroller", "decreaseVolume", tr("Decrease volume"), new ParamTypes());
|
||||
|
||||
|
||||
@ -238,5 +238,8 @@
|
||||
<file>ui/images/connections/network-wired-disabled.svg</file>
|
||||
<file>ui/images/nfc.svg</file>
|
||||
<file>ui/images/smartphone.svg</file>
|
||||
<file>ui/images/state-in.svg</file>
|
||||
<file>ui/images/state-out.svg</file>
|
||||
<file>ui/images/like.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -168,3 +168,9 @@ INSTALLS += target
|
||||
|
||||
ANDROID_ABIS = armeabi-v7a arm64-v8a
|
||||
|
||||
contains(ANDROID_TARGET_ARCH,) {
|
||||
ANDROID_ABIS = \
|
||||
armeabi-v7a \
|
||||
arm64-v8a
|
||||
}
|
||||
|
||||
|
||||
@ -49,10 +49,8 @@
|
||||
<file>ui/customviews/GenericTypeLogView.qml</file>
|
||||
<file>ui/customviews/CustomViewBase.qml</file>
|
||||
<file>ui/customviews/WeatherView.qml</file>
|
||||
<file>ui/customviews/MediaControllerView.qml</file>
|
||||
<file>ui/customviews/NotificationsView.qml</file>
|
||||
<file>ui/customviews/ExtendedVolumeController.qml</file>
|
||||
<file>ui/devicepages/MediaDevicePage.qml</file>
|
||||
<file>ui/devicepages/MediaThingPage.qml</file>
|
||||
<file>ui/devicepages/ButtonDevicePage.qml</file>
|
||||
<file>ui/devicepages/GenericDevicePage.qml</file>
|
||||
<file>ui/devicepages/WeatherDevicePage.qml</file>
|
||||
@ -77,7 +75,6 @@
|
||||
<file>ui/devicelistpages/SensorsDeviceListPage.qml</file>
|
||||
<file>ui/devicelistpages/WeatherDeviceListPage.qml</file>
|
||||
<file>ui/devicelistpages/DeviceListPageBase.qml</file>
|
||||
<file>ui/magic/SelectActionPage.qml</file>
|
||||
<file>ui/magic/DeviceRulesPage.qml</file>
|
||||
<file>ui/magic/EditRulePage.qml</file>
|
||||
<file>ui/magic/SelectThingPage.qml</file>
|
||||
@ -224,5 +221,6 @@
|
||||
<file>ui/components/SetupStatusIcon.qml</file>
|
||||
<file>ui/components/UpdateStatusIcon.qml</file>
|
||||
<file>ui/magic/WriteNfcTagPage.qml</file>
|
||||
<file>ui/components/MediaPlayer.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -45,7 +45,9 @@ Page {
|
||||
|
||||
header: FancyHeader {
|
||||
id: mainHeader
|
||||
title: d.configOverlay !== null ? qsTr("Configure main view") : filteredContentModel.data(swipeView.currentIndex, "displayName")
|
||||
title: d.configOverlay !== null ?
|
||||
qsTr("Configure main view")
|
||||
: swipeView.currentItem.item.title.length > 0 ? swipeView.currentItem.item.title : filteredContentModel.data(swipeView.currentIndex, "displayName")
|
||||
leftButtonVisible: true
|
||||
leftButtonImageSource: {
|
||||
if (app.hasOwnProperty("headerIcon")) {
|
||||
@ -288,13 +290,13 @@ Page {
|
||||
}
|
||||
footer: Item {
|
||||
readonly property bool shown: tabsRepeater.count > 1 || mainHeader.menuOpen || d.configOverlay
|
||||
implicitHeight: shown ? 70 + (app.landscape ? -20 : 0) : 0
|
||||
implicitHeight: shown ? 64 + (app.landscape ? -20 : 0) : 0
|
||||
Behavior on implicitHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }}
|
||||
|
||||
TabBar {
|
||||
id: tabBar
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
height: 70 + (app.landscape ? -20 : 0)
|
||||
height: 64 + (app.landscape ? -20 : 0)
|
||||
Material.elevation: 3
|
||||
position: TabBar.Footer
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ ApplicationWindow {
|
||||
property int smallFont: 13
|
||||
property int mediumFont: 16
|
||||
property int largeFont: 20
|
||||
property int iconSize: 30
|
||||
property int iconSize: 24
|
||||
property int delegateHeight: 60
|
||||
property color backgroundColor: Material.background
|
||||
|
||||
@ -259,7 +259,6 @@ ApplicationWindow {
|
||||
return Qt.resolvedUrl("images/sensors/windspeed.svg")
|
||||
case "media":
|
||||
case "mediacontroller":
|
||||
case "extendedmediacontroller":
|
||||
case "mediaplayer":
|
||||
return Qt.resolvedUrl("images/media.svg")
|
||||
case "powersocket":
|
||||
@ -337,7 +336,6 @@ ApplicationWindow {
|
||||
case "extendednavigationpad":
|
||||
return Qt.resolvedUrl("images/navigationpad.svg")
|
||||
case "volumecontroller":
|
||||
case "extendedvolumecontroller":
|
||||
return Qt.resolvedUrl("images/audio-speakers-symbolic.svg")
|
||||
case "shufflerepeat":
|
||||
return Qt.resolvedUrl("images/media-playlist-shuffle.svg")
|
||||
|
||||
@ -150,8 +150,8 @@ ToolBar {
|
||||
id: menuRepeater
|
||||
|
||||
MouseArea {
|
||||
height: app.iconSize * 3
|
||||
width: app.iconSize * 3
|
||||
height: 80
|
||||
width: height
|
||||
|
||||
onClicked: {
|
||||
menuOpen = false
|
||||
|
||||
@ -92,7 +92,7 @@ Item {
|
||||
ColorIcon {
|
||||
id: colorIcon
|
||||
anchors.centerIn: parent
|
||||
height: app.iconSize * 1.3
|
||||
height: app.iconSize * 1.5
|
||||
width: height
|
||||
ColorIcon {
|
||||
id: fallbackIcon
|
||||
|
||||
@ -43,4 +43,6 @@ MouseArea {
|
||||
preventStealing: true
|
||||
onWheel: wheel.accepted = true
|
||||
|
||||
property string title: ""
|
||||
|
||||
}
|
||||
|
||||
@ -44,14 +44,23 @@ Item {
|
||||
readonly property StateType playerTypeStateType: thing ? thing.thingClass.stateTypes.findByName("playerType") : null
|
||||
readonly property State playerTypeState: playerTypeStateType ? thing.states.getState(playerTypeStateType.id) : null
|
||||
|
||||
Pane {
|
||||
Material.elevation: 2
|
||||
anchors.centerIn: parent
|
||||
height: fallback.visible ? Math.min(parent.height, parent.width) : artworkImage.paintedHeight - 1
|
||||
width: fallback.visible ? Math.min(parent.height, parent.width) : artworkImage.paintedWidth - 1
|
||||
padding: 0
|
||||
contentItem: Rectangle {
|
||||
color: "black"
|
||||
readonly property int paintedWidth: fallbackImage.visible ? fallbackImage.width : artworkImage.paintedWidth
|
||||
readonly property int paintedHeight: fallbackImage.visible ? fallbackImage.height : artworkImage.paintedHeight
|
||||
|
||||
Rectangle {
|
||||
id: fallbackImage
|
||||
anchors { left: parent.left; top: parent.top }
|
||||
height: visible ? Math.min(parent.height, parent.width) : artworkImage.paintedHeight - 1
|
||||
width: visible ? Math.min(parent.height, parent.width) : artworkImage.paintedWidth - 1
|
||||
visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
|
||||
color: "black"
|
||||
|
||||
ColorIcon {
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(parent.height, parent.width) - app.margins * 2
|
||||
height: Math.min(parent.height, parent.width) - app.margins * 2
|
||||
name: root.playerTypeState.value === "video" ? "../images/stock_video.svg" : "../images/stock_music.svg"
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,17 +70,7 @@ Item {
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: root.artworkState.value
|
||||
visible: source !== ""
|
||||
}
|
||||
|
||||
ColorIcon {
|
||||
id: fallback
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(parent.height, parent.width) - app.margins * 2
|
||||
height: Math.min(parent.height, parent.width) - app.margins * 2
|
||||
|
||||
name: root.playerTypeState.value === "video" ? "../images/stock_video.svg" : "../images/stock_music.svg"
|
||||
visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
|
||||
// color: app.primaryColor
|
||||
color: "white"
|
||||
horizontalAlignment: Image.AlignLeft
|
||||
verticalAlignment: Image.AlignTop
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,11 +42,46 @@ Item {
|
||||
|
||||
property Thing thing: null
|
||||
|
||||
function backPressed() {
|
||||
signal exit();
|
||||
signal itemLaunched()
|
||||
|
||||
property ListModel path: ListModel {
|
||||
id: pathModel
|
||||
dynamicRoles: true
|
||||
Component.onCompleted: pathModel.append({modelData: root.thing.name})
|
||||
}
|
||||
|
||||
|
||||
function backPressed(immediate) {
|
||||
if (internalPageStack.depth > 1) {
|
||||
internalPageStack.pop();
|
||||
pathModel.remove(pathModel.count - 1)
|
||||
internalPageStack.pop(immediate ? StackView.Immediate : StackView.PopTransition);
|
||||
} else {
|
||||
swipeView.currentIndex--
|
||||
root.exit();
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
property int pendingItemExecutionId: -1
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: engine.thingManager
|
||||
onExecuteBrowserItemReply: {
|
||||
if (commandId == d.pendingItemExecutionId) {
|
||||
if (params.thingError === "ThingErrorNoError") {
|
||||
root.itemLaunched();
|
||||
} else {
|
||||
var errorDialog = Qt.createComponent(Qt.resolvedUrl("ErrorDialog.qml"));
|
||||
var text = qsTr("Sorry. An error happened launching the item. (Error code: %1)").arg(params.error);
|
||||
if (params.displayMessage.length > 0) {
|
||||
text = params.displayMessage;
|
||||
}
|
||||
var popup = errorDialog.createObject(app, {text: text})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,8 +113,9 @@ Item {
|
||||
onClicked: {
|
||||
print("clicked:", model.id)
|
||||
if (model.executable) {
|
||||
engine.thingManager.executeBrowserItem(root.thing.id, model.id)
|
||||
d.pendingItemExecutionId = engine.thingManager.executeBrowserItem(root.thing.id, model.id)
|
||||
} else if (model.browsable) {
|
||||
pathModel.append({modelData: model.displayName})
|
||||
internalPageStack.push(internalBrowserPage, {nodeId: model.id})
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,12 +36,15 @@ import Nymea 1.0
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
implicitHeight: iconSize + app.margins
|
||||
implicitHeight: app.iconSize * (showExtendedControls ? 2 : 1) + app.margins
|
||||
|
||||
property Thing thing: null
|
||||
property int iconSize: app.iconSize * 1.5
|
||||
|
||||
property bool showExtendedControls: false
|
||||
|
||||
readonly property State playbackState: thing.stateByName("playbackStatus")
|
||||
readonly property State shuffleState: thing.stateByName("shuffle")
|
||||
readonly property State repeatState: thing.stateByName("repeat")
|
||||
|
||||
function executeAction(actionName, params) {
|
||||
if (params === undefined) {
|
||||
@ -51,12 +54,31 @@ RowLayout {
|
||||
engine.thingManager.executeAction(thing.id, actionTypeId, params)
|
||||
}
|
||||
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: height
|
||||
imageSource: "../images/media-playlist-shuffle.svg"
|
||||
longpressEnabled: false
|
||||
enabled: root.shuffleState !== null
|
||||
opacity: enabled ? 1 : .5
|
||||
visible: root.showExtendedControls
|
||||
onClicked: {
|
||||
var params = []
|
||||
var param = {}
|
||||
param["paramTypeId"] = root.shuffleState.stateTypeId
|
||||
param["value"] = !root.shuffleState.value
|
||||
params.push(param)
|
||||
root.executeAction("shuffle", params)
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: root.iconSize * .6
|
||||
Layout.preferredHeight: app.iconSize * (root.showExtendedControls ? 1.5 : 1)
|
||||
Layout.preferredWidth: height
|
||||
imageSource: "../images/media-skip-backward.svg"
|
||||
longpressImageSource: "../images/media-seek-backward.svg"
|
||||
longpressEnabled: root.thing.thingClass.actionTypes.findByName("fastRewind") !== null
|
||||
enabled: root.playbackState && root.playbackState.value !== "Stopped"
|
||||
opacity: enabled ? 1 : .5
|
||||
|
||||
@ -70,7 +92,7 @@ RowLayout {
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: root.iconSize
|
||||
Layout.preferredHeight: app.iconSize * (root.showExtendedControls ? 2 : 1)
|
||||
Layout.preferredWidth: height
|
||||
imageSource: root.playbackState && root.playbackState.value === "Playing" ? "../images/media-playback-pause.svg" : "../images/media-playback-start.svg"
|
||||
longpressImageSource: "../images/media-playback-stop.svg"
|
||||
@ -90,10 +112,11 @@ RowLayout {
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: root.iconSize * .6
|
||||
Layout.preferredHeight: app.iconSize * (root.showExtendedControls ? 1.5 : 1)
|
||||
Layout.preferredWidth: height
|
||||
imageSource: "../images/media-skip-forward.svg"
|
||||
longpressImageSource: "../images/media-seek-forward.svg"
|
||||
longpressEnabled: root.thing.thingClass.actionTypes.findByName("fastForward") !== null
|
||||
enabled: root.playbackState && root.playbackState.value !== "Stopped"
|
||||
opacity: enabled ? 1 : .5
|
||||
repeat: true
|
||||
@ -105,4 +128,24 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: height
|
||||
imageSource: root.repeatState.value === "One" ? "../images/media-playlist-repeat-one.svg" : "../images/media-playlist-repeat.svg"
|
||||
color: root.repeatState.value === "None" ? keyColor : app.accentColor
|
||||
longpressEnabled: false
|
||||
enabled: root.repeatState !== null
|
||||
opacity: enabled ? 1 : .5
|
||||
visible: root.showExtendedControls
|
||||
property var allowedValues: ["None", "All", "One"]
|
||||
onClicked: {
|
||||
var params = []
|
||||
var param = {}
|
||||
param["paramTypeId"] = root.repeatState.stateTypeId;
|
||||
param["value"] = allowedValues[(allowedValues.indexOf(root.repeatState.value) + 1) % 3]
|
||||
params.push(param)
|
||||
root.executeAction("repeat", params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
468
nymea-app/ui/components/MediaPlayer.qml
Normal file
468
nymea-app/ui/components/MediaPlayer.qml
Normal file
@ -0,0 +1,468 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
import Nymea 1.0
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../delegates"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
height: swipeView.height
|
||||
width: swipeView.width
|
||||
property Thing thing: null
|
||||
|
||||
readonly property State playbackState: thing.stateByName("playbackStatus")
|
||||
readonly property State inputSourceState: thing.stateByName("inputSource")
|
||||
readonly property State playDurationState: thing.stateByName("playDuration")
|
||||
readonly property State playTimeState: thing.stateByName("playTime")
|
||||
|
||||
readonly property State titleState: thing.stateByName("title")
|
||||
readonly property State artistState: thing.stateByName("artist")
|
||||
readonly property State collectionState: thing.stateByName("collection")
|
||||
readonly property State artworkState: thing.stateByName("artwork")
|
||||
|
||||
readonly property bool hasVolumeControl: thing.thingClass.interfaces.indexOf("volumecontroller") >= 0
|
||||
readonly property State volumeState: thing.stateByName("volume")
|
||||
readonly property State muteState: thing.stateByName("mute")
|
||||
|
||||
readonly property State likeState: thing.stateByName("like")
|
||||
|
||||
readonly property bool hasNavigationPatd: thing.thingClass.interfaces.indexOf("navigationpad") >= 0
|
||||
|
||||
clip: true
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
property var browser: null
|
||||
property int pendingInputSourceSelectId: -1
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: engine.thingManager
|
||||
onExecuteActionReply: {
|
||||
if (commandId == d.pendingInputSourceSelectId) {
|
||||
if (params.deviceError !== "DeviceErrorNoError") {
|
||||
var errorDialog = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"));
|
||||
var text
|
||||
if (params.displayMessage.length > 0) {
|
||||
text = params.displayMessage
|
||||
}
|
||||
var popup = errorDialog.createObject(app, {text: text})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MediaArtworkImage {
|
||||
id: artworkImage
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||
height: parent.height
|
||||
thing: root.thing
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: gradientMask
|
||||
anchors.centerIn: parent
|
||||
height: Math.max(artworkImage.height, artworkImage.width)
|
||||
width: Math.max(artworkImage.height, artworkImage.width)
|
||||
rotation: app.landscape ? -90 : 0
|
||||
visible: contentStartPos < artworkEndPos
|
||||
|
||||
property double artworkEndPos: app.landscape ?
|
||||
artworkImage.paintedWidth / artworkImage.width
|
||||
: artworkImage.paintedHeight / artworkImage.height
|
||||
property double contentStartPos: app.landscape ?
|
||||
(artworkImage.width - content.width - app.margins * 2) / artworkImage.width
|
||||
: (artworkImage.height - content.height - app.margins * 2) / artworkImage.height
|
||||
property double gradientEnd: Math.min(artworkEndPos, contentStartPos + .2)
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: gradientMask.gradientEnd - .5; color: "transparent"}
|
||||
GradientStop { position: gradientMask.gradientEnd; color: app.backgroundColor }
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
left: parent.left;
|
||||
right: parent.right
|
||||
leftMargin: app.landscape ? root.width / 2 : app.margins
|
||||
rightMargin: app.margins
|
||||
bottomMargin: app.margins
|
||||
}
|
||||
|
||||
spacing: app.margins
|
||||
|
||||
RowLayout {
|
||||
ColumnLayout {
|
||||
Label {
|
||||
text: root.playbackState.value === "Stopped" ?
|
||||
qsTr("No playback")
|
||||
: root.titleState.value
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
|
||||
Label {
|
||||
text: root.artistState.value
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
visible: text.length > 0
|
||||
font.pixelSize: app.smallFont
|
||||
}
|
||||
|
||||
Label {
|
||||
text: root.collectionState.value
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
visible: text.length > 0
|
||||
font.pixelSize: app.smallFont
|
||||
}
|
||||
}
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
longpressEnabled: false
|
||||
imageSource: "../images/like.svg"
|
||||
visible: root.likeState !== null
|
||||
color: root.likeState && root.likeState.value === true ? app.accentColor : keyColor
|
||||
onClicked: {
|
||||
engine.thingManager.executeAction(root.thing.id, root.likeState.stateTypeId, [{ paramTypeId: root.likeState.stateTypeId, value: !root.likeState.value}])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: root.playTimeState !== null || root.playDurationState != null
|
||||
|
||||
function timeString(seconds) {
|
||||
var hours = Math.floor(seconds / 3600);
|
||||
seconds = seconds % 3600;
|
||||
var minutes = Math.floor(seconds / 60);
|
||||
seconds = seconds % 60;
|
||||
var ret = "";
|
||||
if (hours > 0) {
|
||||
ret += hours + ":";
|
||||
}
|
||||
ret += NymeaUtils.pad(minutes, 2) + ":";
|
||||
ret += NymeaUtils.pad(seconds, 2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Label {
|
||||
font.pixelSize: app.smallFont
|
||||
text: root.playTimeState ? parent.timeString(root.playTimeState.value) : "00:00"
|
||||
}
|
||||
|
||||
Slider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: root.playDurationState ? root.playDurationState.value : 0
|
||||
value: root.playTimeState ? root.playTimeState.value : 0
|
||||
property ActionType playTimeActionType: root.thing.thingClass.actionTypes.findByName("playTime")
|
||||
enabled: playTimeActionType !== null
|
||||
onPressedChanged: {
|
||||
if (!pressed) {
|
||||
engine.thingManager.executeAction(root.thing.id, playTimeActionType.id, [{paramTypeId: playTimeActionType.id, value: value}]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
font.pixelSize: app.smallFont
|
||||
text: root.playDurationState ? parent.timeString(root.playDurationState.value) : "00:00"
|
||||
}
|
||||
}
|
||||
|
||||
MediaControls {
|
||||
thing: root.thing
|
||||
showExtendedControls: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: app.margins
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
longpressEnabled: false
|
||||
visible: root.thing.thingClass.browsable
|
||||
imageSource: "../images/folder-symbolic.svg"
|
||||
onClicked: {
|
||||
if (!d.browser) {
|
||||
d.browser = browserPage.createObject(root, {x: 0, y: root.height})
|
||||
}
|
||||
d.browser.show();
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
longpressEnabled: false
|
||||
visible: root.inputSourceState !== null
|
||||
imageSource: "../images/state-in.svg"
|
||||
onClicked: {
|
||||
var popup = inputSourceSelectDialogComponent.createObject(root)
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: root.inputSourceState ? root.inputSourceState.value : ""
|
||||
font.pixelSize: app.smallFont
|
||||
}
|
||||
}
|
||||
ProgressButton {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
longpressEnabled: false
|
||||
visible: root.hasNavigationPatd
|
||||
imageSource: "../images/navigationpad.svg"
|
||||
onClicked: pageStack.push(navigationPadPage)
|
||||
}
|
||||
|
||||
ProgressButton {
|
||||
id: volumeButton
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
visible: root.hasVolumeControl
|
||||
imageSource: root.muteState && root.muteState.value === true ?
|
||||
"../images/audio-speakers-muted-symbolic.svg"
|
||||
: "../images/audio-speakers-symbolic.svg"
|
||||
onClicked: {
|
||||
print(volumeButton.x, volumeButton.y)
|
||||
print(Qt.point(volumeButton.x, volumeButton.y))
|
||||
print(volumeButton.mapToItem(root, volumeButton.x,0))
|
||||
var buttonPosition = root.mapFromItem(volumeButton, 0, 0)
|
||||
var sliderHeight = 200
|
||||
var props = {}
|
||||
props["x"] = buttonPosition.x - app.margins
|
||||
props["y"] = buttonPosition.y - sliderHeight
|
||||
props["height"] = sliderHeight
|
||||
var sliderPane = volumeSliderPaneComponent.createObject(root, props)
|
||||
sliderPane.open()
|
||||
}
|
||||
onLongpressed: {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: volumeSliderPaneComponent
|
||||
Dialog {
|
||||
id: volumeSliderDialog
|
||||
|
||||
leftPadding: 0
|
||||
topPadding: app.margins / 2
|
||||
rightPadding: 0
|
||||
bottomPadding: app.margins / 2
|
||||
modal: true
|
||||
|
||||
property int pendingVolumeValue: -1
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
ProgressButton {
|
||||
visible: root.volumeState === null
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
longpressEnabled: false
|
||||
imageSource: "../images/up.svg"
|
||||
onClicked: engine.thingManager.executeAction(root.thing.id, root.thing.thingClass.actionTypes.findByName("increaseVolume").id);
|
||||
}
|
||||
ProgressButton {
|
||||
visible: root.volumeState === null
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
longpressEnabled: false
|
||||
imageSource: "../images/down.svg"
|
||||
onClicked: engine.thingManager.executeAction(root.thing.id, root.thing.thingClass.actionTypes.findByName("decreaseVolume").id);
|
||||
}
|
||||
|
||||
ThrottledSlider {
|
||||
Layout.fillHeight: true
|
||||
visible: root.volumeState !== null
|
||||
from: 0
|
||||
to: 100
|
||||
value: root.volumeState.value
|
||||
orientation: Qt.Vertical
|
||||
onMoved: engine.thingManager.executeAction(root.thing.id, root.volumeState.stateTypeId, [{paramTypeId: root.volumeState.stateTypeId, value: value}])
|
||||
}
|
||||
|
||||
ProgressButton {
|
||||
visible: root.muteState !== null
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
imageSource: "../images/audio-speakers-muted-symbolic.svg"
|
||||
color: root.muteState.value === true ? app.accentColor : keyColor
|
||||
onClicked: engine.thingManager.executeAction(root.thing.id, root.muteState.stateTypeId, [{paramTypeId: root.muteState.stateTypeId, value: !root.muteState.value}]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: navigationPadPage
|
||||
Page {
|
||||
header: NymeaHeader { text: root.thing.name; onBackPressed: pageStack.pop() }
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
spacing: app.margins
|
||||
|
||||
NavigationPad { Layout.fillWidth: true; Layout.fillHeight: true; device: root.thing }
|
||||
MediaControls { Layout.fillWidth: true; thing: root.thing }
|
||||
ShuffleRepeatVolumeControl { Layout.fillWidth: true; Layout.fillHeight: false; Layout.preferredHeight: app.iconSize; thing: root.thing }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: browserPage
|
||||
Page {
|
||||
width: root.width
|
||||
height: root.height
|
||||
y: root.height
|
||||
|
||||
function show() { y = 0 }
|
||||
function hide() { y = root.height }
|
||||
|
||||
header: ToolBar {
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
HeaderButton {
|
||||
imageSource: "../images/down.svg"
|
||||
onClicked: d.browser.hide()
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: pathFlickable
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins / 2
|
||||
Layout.fillHeight: true
|
||||
contentX: Math.max(0, contentWidth - width)
|
||||
contentWidth: pathRow.width
|
||||
clip: true
|
||||
onContentWidthChanged: {
|
||||
print("contentWidth", contentWidth, "width", width, contentX)
|
||||
}
|
||||
|
||||
Row {
|
||||
id: pathRow
|
||||
Repeater {
|
||||
model: mediaBrowser.path
|
||||
// orientation: ListView.Horizontal
|
||||
Rectangle {
|
||||
height: pathFlickable.height
|
||||
width: Math.min(150, folderLabel.implicitWidth + app.margins)
|
||||
border.color: app.backgroundColor
|
||||
border.width: 1
|
||||
radius: 4
|
||||
color: Qt.lighter(app.backgroundColor)
|
||||
Label {
|
||||
id: folderLabel
|
||||
text: modelData
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
var backCount = mediaBrowser.path.count - index - 1;
|
||||
print("backCount:", backCount)
|
||||
for (var i = 0; i < backCount - 1; i++) {
|
||||
mediaBrowser.backPressed(true)
|
||||
}
|
||||
if (backCount > 0) {
|
||||
mediaBrowser.backPressed(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on y { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } }
|
||||
|
||||
MediaBrowser {
|
||||
id: mediaBrowser
|
||||
anchors.fill: parent
|
||||
thing: root.thing
|
||||
onExit: {
|
||||
d.browser.hide()
|
||||
}
|
||||
onItemLaunched: {
|
||||
d.browser.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: inputSourceSelectDialogComponent
|
||||
MeaDialog {
|
||||
id: inputSourceSelectDialog
|
||||
title: qsTr("Select input")
|
||||
standardButtons: Dialog.NoButton
|
||||
|
||||
ListView {
|
||||
id: inputSourceListView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredHeight: 200
|
||||
clip: true
|
||||
model: root.thing.thingClass.stateTypes.findByName("inputSource").allowedValues
|
||||
delegate: RadioDelegate {
|
||||
width: inputSourceListView.width
|
||||
text: modelData
|
||||
checked: root.inputSourceState.value === modelData
|
||||
onClicked: {
|
||||
d.pendingInputSourceSelectId = engine.thingManager.executeAction(root.thing.id, root.inputSourceState.stateTypeId, [{paramTypeId: root.inputSourceState.stateTypeId, value: modelData}])
|
||||
inputSourceSelectDialog.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -40,6 +40,7 @@ Item {
|
||||
property alias backButtonVisible: backButton.visible
|
||||
property alias menuButtonVisible: menuButton.visible
|
||||
default property alias children: layout.data
|
||||
property alias elide: label.elide
|
||||
|
||||
signal backPressed();
|
||||
signal menuPressed();
|
||||
|
||||
@ -33,10 +33,14 @@ import QtQuick.Layouts 1.3
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitHeight: app.iconSize
|
||||
implicitWidth: app.iconSize
|
||||
|
||||
property string imageSource
|
||||
property string longpressImageSource: imageSource
|
||||
property bool repeat: false
|
||||
property alias keyColor: icon.keyColor
|
||||
property alias color: icon.color
|
||||
|
||||
property bool longpressEnabled: true
|
||||
|
||||
@ -46,6 +50,7 @@ Item {
|
||||
MouseArea {
|
||||
id: buttonDelegate
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
property bool longpressed: false
|
||||
|
||||
@ -94,7 +99,7 @@ Item {
|
||||
anchors.margins: -app.margins / 2
|
||||
radius: width / 2
|
||||
color: app.foregroundColor
|
||||
opacity: buttonDelegate.pressed ? .08 : 0
|
||||
opacity: buttonDelegate.pressed || buttonDelegate.containsMouse ? .08 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 200 }
|
||||
}
|
||||
@ -144,6 +149,7 @@ Item {
|
||||
}
|
||||
|
||||
ColorIcon {
|
||||
id: icon
|
||||
anchors.fill: parent
|
||||
name: buttonDelegate.longpressed ? root.longpressImageSource : root.imageSource
|
||||
}
|
||||
|
||||
@ -93,7 +93,9 @@ RowLayout {
|
||||
HeaderButton {
|
||||
id: volumeButton
|
||||
anchors.centerIn: parent
|
||||
imageSource: "../images/audio-speakers-symbolic.svg"
|
||||
imageSource: root.muteState && root.muteState.value === true ?
|
||||
"../images/audio-speakers-muted-symbolic.svg"
|
||||
: "../images/audio-speakers-symbolic.svg"
|
||||
onClicked: {
|
||||
print(volumeButton.x, volumeButton.y)
|
||||
print(Qt.point(volumeButton.x, volumeButton.y))
|
||||
@ -125,8 +127,20 @@ RowLayout {
|
||||
property int pendingVolumeValue: -1
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
HeaderButton {
|
||||
visible: root.volumeState === null
|
||||
imageSource: "../images/up.svg"
|
||||
onClicked: engine.thingManager.executeAction(root.thing.id, root.thing.thingClass.actionTypes.findByName("increaseVolume").id);
|
||||
}
|
||||
HeaderButton {
|
||||
visible: root.volumeState === null
|
||||
imageSource: "../images/down.svg"
|
||||
onClicked: engine.thingManager.executeAction(root.thing.id, root.thing.thingClass.actionTypes.findByName("decreaseVolume").id);
|
||||
}
|
||||
|
||||
ThrottledSlider {
|
||||
Layout.fillHeight: true
|
||||
visible: root.volumeState !== null
|
||||
from: 0
|
||||
to: 100
|
||||
value: root.volumeState.value
|
||||
@ -135,6 +149,7 @@ RowLayout {
|
||||
}
|
||||
|
||||
HeaderButton {
|
||||
visible: root.muteState !== null
|
||||
imageSource: "../images/audio-speakers-muted-symbolic.svg"
|
||||
color: root.muteState.value === true ? app.accentColor : keyColor
|
||||
onClicked: engine.thingManager.executeAction(root.thing.id, root.muteState.stateTypeId, [{paramTypeId: root.muteState.stateTypeId, value: !root.muteState.value}]);
|
||||
|
||||
@ -34,10 +34,11 @@ import QtQuick.Controls.Material 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import Nymea 1.0
|
||||
|
||||
Row {
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
property Device device: null
|
||||
property Device thing: null
|
||||
property alias device: root.thing
|
||||
readonly property var deviceClass: device ? engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
|
||||
readonly property var openState: device ? device.states.getState(deviceClass.stateTypes.findByName("state").id) : null
|
||||
readonly property bool canStop: device && device.deviceClass.actionTypes.findByName("stop")
|
||||
@ -46,54 +47,41 @@ Row {
|
||||
|
||||
signal activated(string button);
|
||||
|
||||
ItemDelegate {
|
||||
width: app.iconSize * 2
|
||||
height: width
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
ColorIcon {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
name: root.invert ? "../images/down.svg" : "../images/up.svg"
|
||||
color: root.openState && root.openState.value === "opening" ? Material.accent : keyColor
|
||||
}
|
||||
ProgressButton {
|
||||
longpressEnabled: false
|
||||
imageSource: root.invert ? "../images/down.svg" : "../images/up.svg"
|
||||
color: root.openState && root.openState.value === "opening" ? Material.accent : keyColor
|
||||
onClicked: {
|
||||
engine.deviceManager.executeAction(root.device.id, root.deviceClass.actionTypes.findByName("open").id)
|
||||
root.activated("open")
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
ItemDelegate {
|
||||
width: app.iconSize * 2
|
||||
height: width
|
||||
ProgressButton {
|
||||
visible: root.canStop
|
||||
// color: Material.foreground
|
||||
// radius: height / 2
|
||||
|
||||
ColorIcon {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
name: "../images/media-playback-stop.svg"
|
||||
}
|
||||
longpressEnabled: false
|
||||
imageSource: "../images/media-playback-stop.svg"
|
||||
onClicked: {
|
||||
engine.deviceManager.executeAction(root.device.id, root.deviceClass.actionTypes.findByName("stop").id)
|
||||
root.activated("stop")
|
||||
}
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
width: app.iconSize * 2
|
||||
height: width
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
ColorIcon {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
name: root.invert ? "../images/up.svg" : "../images/down.svg"
|
||||
color: root.openState && root.openState.value === "closing" ? Material.accent : keyColor
|
||||
}
|
||||
ProgressButton {
|
||||
imageSource: root.invert ? "../images/up.svg" : "../images/down.svg"
|
||||
longpressEnabled: false
|
||||
color: root.openState && root.openState.value === "closing" ? Material.accent : keyColor
|
||||
onClicked: {
|
||||
engine.deviceManager.executeAction(root.device.id, root.deviceClass.actionTypes.findByName("close").id)
|
||||
root.activated("close")
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
}
|
||||
|
||||
@ -1,83 +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.5
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 2.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
CustomViewBase {
|
||||
id: root
|
||||
height: row.implicitHeight + app.margins * 2
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right; margins: app.margins }
|
||||
|
||||
AbstractButton {
|
||||
width: app.iconSize * 2
|
||||
height: width
|
||||
|
||||
property var muteState: root.device.states.getState(deviceClass.stateTypes.findByName("mute").id)
|
||||
property bool isMuted: muteState.value === true
|
||||
|
||||
ColorIcon {
|
||||
anchors.fill: parent
|
||||
name: "../images/audio-speakers-muted-symbolic.svg"
|
||||
color: parent.isMuted ? app.accentColor : keyColor
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
var paramList = []
|
||||
var muteParam = {}
|
||||
muteParam["paramTypeId"] = deviceClass.stateTypes.findByName("mute").id
|
||||
muteParam["value"] = !isMuted
|
||||
paramList.push(muteParam)
|
||||
engine.deviceManager.executeAction(root.device.id, deviceClass.actionTypes.findByName("mute").id, paramList)
|
||||
}
|
||||
}
|
||||
|
||||
ThrottledSlider {
|
||||
Layout.fillWidth: true
|
||||
value: root.device.stateValue(deviceClass.stateTypes.findByName("volume").id)
|
||||
from: 0
|
||||
to: 100
|
||||
onMoved: {
|
||||
var paramList = []
|
||||
var muteParam = {}
|
||||
muteParam["paramTypeId"] = deviceClass.stateTypes.findByName("volume").id
|
||||
muteParam["value"] = value
|
||||
paramList.push(muteParam)
|
||||
engine.deviceManager.executeAction(root.device.id, deviceClass.actionTypes.findByName("volume").id, paramList)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,100 +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.5
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 2.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
|
||||
CustomViewBase {
|
||||
id: root
|
||||
height: column.implicitHeight + app.margins * 2
|
||||
|
||||
function executeAction(actionName) {
|
||||
var actionTypeId = deviceClass.actionTypes.findByName(actionName).id;
|
||||
print("executing", device, device.id, actionTypeId, actionName, deviceClass.actionTypes)
|
||||
engine.deviceManager.executeAction(device.id, actionTypeId)
|
||||
}
|
||||
|
||||
property var playbackState: device.states.getState(deviceClass.stateTypes.findByName("playbackStatus").id)
|
||||
property var playbackStateValue: playbackState.value
|
||||
onPlaybackStateValueChanged: populateControls()
|
||||
Component.onCompleted: populateControls()
|
||||
|
||||
function populateControls() {
|
||||
print("generating controls")
|
||||
controlsModel.clear();
|
||||
controlsModel.append({image: "../images/media-skip-backward.svg", action: "skipBack"})
|
||||
controlsModel.append({image: "../images/media-seek-backward.svg", action: "rewind"})
|
||||
controlsModel.append({image: "../images/media-playback-stop.svg", action: "stop"})
|
||||
if (playbackState.value === "Paused" || playbackState.value === "Stopped") {
|
||||
controlsModel.append({image: "../images/media-playback-start.svg", action: "play"})
|
||||
}
|
||||
if (playbackState.value === "Playing") {
|
||||
controlsModel.append({image: "../images/media-playback-pause.svg", action: "pause"})
|
||||
}
|
||||
|
||||
controlsModel.append({image: "../images/media-seek-forward.svg", action: "fastForward"})
|
||||
controlsModel.append({image: "../images/media-skip-forward.svg", action: "skipNext"})
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
anchors { left: parent.left; right: parent.right }
|
||||
|
||||
Row {
|
||||
id: controlsRow
|
||||
Layout.fillWidth: true
|
||||
|
||||
property int iconSize: Math.max(app.iconSize * 2, column.width / controlsModel.count)
|
||||
|
||||
Repeater {
|
||||
model: ListModel {
|
||||
id: controlsModel
|
||||
}
|
||||
delegate: AbstractButton {
|
||||
|
||||
height: app.iconSize * 2
|
||||
width: controlsRow.iconSize
|
||||
ColorIcon {
|
||||
height: parent.height
|
||||
width: height
|
||||
name: model.image
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
onClicked: {
|
||||
executeAction(model.action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,7 +161,7 @@ MainPageTile {
|
||||
case "extendedsmartmeterproducer":
|
||||
case "heating":
|
||||
return sensorComponent;
|
||||
// return labelComponent;
|
||||
// return labelComponent;
|
||||
|
||||
case "light":
|
||||
case "garagedoor":
|
||||
@ -247,7 +247,6 @@ MainPageTile {
|
||||
}
|
||||
|
||||
MediaControls {
|
||||
iconSize: app.iconSize * 1.2
|
||||
thing: inlineMediaControl.currentDevice
|
||||
}
|
||||
}
|
||||
@ -255,8 +254,11 @@ MainPageTile {
|
||||
|
||||
Component {
|
||||
id: buttonComponent
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
Label {
|
||||
id: label
|
||||
@ -281,22 +283,6 @@ MainPageTile {
|
||||
}
|
||||
return count === 0 ? qsTr("All off") : qsTr("%1 on").arg(count)
|
||||
case "garagedoor":
|
||||
var statefulCount = 0;
|
||||
var count = 0;
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var thing = devicesProxy.get(i);
|
||||
if (thing.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0 || thing.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0 || thing.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
statefulCount++;
|
||||
var stateType = thing.thingClass.stateTypes.findByName("state");
|
||||
if (stateType && thing.states.getState(stateType.id).value !== "closed") {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (statefulCount > 0) {
|
||||
return count === 0 ? qsTr("All closed") : qsTr("%1 open").arg(count)
|
||||
}
|
||||
return "";
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
@ -311,307 +297,282 @@ MainPageTile {
|
||||
font.pixelSize: app.smallFont
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
RowLayout {
|
||||
// Layout.alignment: Qt.AlignRight
|
||||
Layout.fillWidth: true
|
||||
spacing: (parent.width - app.iconSize * 3) / 2
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: height
|
||||
|
||||
ColorIcon {
|
||||
id: leftIcon
|
||||
width: app.iconSize
|
||||
height: width
|
||||
color: app.accentColor
|
||||
|
||||
name: {
|
||||
switch (iface.name) {
|
||||
case "media":
|
||||
case "light":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
return ""
|
||||
case "garagedoor":
|
||||
var dev = devicesProxy.get(0)
|
||||
if (dev.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
return "../images/up.svg"
|
||||
}
|
||||
return ""
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
case "extendedawning":
|
||||
case "shutter":
|
||||
case "extendedshutter":
|
||||
return "../images/up.svg"
|
||||
default:
|
||||
console.warn("InterfaceTile", "inlineButtonControl image: Unhandled interface", iface.name)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
switch (iface.name) {
|
||||
case "light":
|
||||
case "media":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
break;
|
||||
case "garagedoor":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var thing = devicesProxy.get(i);
|
||||
if (thing.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
|
||||
var actionType = thing.thingClass.actionTypes.findByName("open");
|
||||
engine.deviceManager.executeAction(thing.id, actionType.id)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "shutter":
|
||||
case "extendedshutter":
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
case "extendedawning":
|
||||
case "simpleclosable":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("open");
|
||||
engine.deviceManager.executeAction(device.id, actionType.id)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn("InterfaceTile:", "inlineButtonControl clicked: Unhandled interface", iface.name)
|
||||
ProgressButton {
|
||||
longpressEnabled: false
|
||||
visible: imageSource.length > 0
|
||||
imageSource: {
|
||||
switch (iface.name) {
|
||||
case "media":
|
||||
case "light":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
return ""
|
||||
case "garagedoor":
|
||||
var dev = devicesProxy.get(0)
|
||||
if (dev.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
return "../images/up.svg"
|
||||
}
|
||||
return ""
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
case "extendedawning":
|
||||
case "shutter":
|
||||
case "extendedshutter":
|
||||
return "../images/up.svg"
|
||||
default:
|
||||
console.warn("InterfaceTile", "inlineButtonControl image: Unhandled interface", iface.name)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: height
|
||||
enabled: centerIcon.name.length > 0
|
||||
ColorIcon {
|
||||
id: centerIcon
|
||||
width: app.iconSize
|
||||
height: width
|
||||
color: app.accentColor
|
||||
onClicked: {
|
||||
switch (iface.name) {
|
||||
case "light":
|
||||
case "media":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
break;
|
||||
case "garagedoor":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var thing = devicesProxy.get(i);
|
||||
if (thing.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
|
||||
name: {
|
||||
switch (iface.name) {
|
||||
case "media":
|
||||
case "light":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
return ""
|
||||
case "garagedoor":
|
||||
var dev = devicesProxy.get(0)
|
||||
if (dev.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
return "../images/media-playback-stop.svg"
|
||||
}
|
||||
return ""
|
||||
case "blind":
|
||||
case "awning":
|
||||
case "shutter":
|
||||
case "extendedblind":
|
||||
case "extendedawning":
|
||||
case "extendedshutter":
|
||||
return "../images/media-playback-stop.svg"
|
||||
default:
|
||||
console.warn("InterfaceTile, inlineButtonControl image: Unhandled interface", iface.name)
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
switch (iface.name) {
|
||||
case "light":
|
||||
case "media":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
break;
|
||||
case "garagedoor":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var thing = devicesProxy.get(i);
|
||||
if (thing.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
|
||||
var actionType = thing.thingClass.actionTypes.findByName("stop");
|
||||
engine.thingManager.executeAction(thing.id, actionType.id)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "shutter":
|
||||
case "extendedshutter":
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
case "extendedawning":
|
||||
case "simpleclosable":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("stop");
|
||||
engine.deviceManager.executeAction(device.id, actionType.id)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn("InterfaceTile, inlineButtonControl clicked: Unhandled interface", iface.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: height
|
||||
|
||||
ColorIcon {
|
||||
id: icon
|
||||
width: app.iconSize
|
||||
height: width
|
||||
color: app.accentColor
|
||||
|
||||
name: {
|
||||
switch (iface.name) {
|
||||
case "media":
|
||||
var device = devicesProxy.get(0)
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("playbackStatus");
|
||||
var state = device.states.getState(stateType.id)
|
||||
return state.value === "Playing" ? "../images/media-playback-pause.svg" :
|
||||
state.value === "Paused" ? "../images/media-playback-start.svg" :
|
||||
""
|
||||
case "light":
|
||||
case "powersocket":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
return "../images/system-shutdown.svg"
|
||||
case "garagedoor":
|
||||
var dev = devicesProxy.get(0)
|
||||
if (dev.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
return "../images/down.svg"
|
||||
}
|
||||
if (dev.thingClass.interfaces.indexOf("impulsegaragedoor") >= 0) {
|
||||
return "../images/closable-move.svg"
|
||||
}
|
||||
return ""
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
case "extendedawning":
|
||||
case "shutter":
|
||||
case "extendedshutter":
|
||||
return "../images/down.svg"
|
||||
default:
|
||||
console.warn("InterfaceTile, inlineButtonControl image: Unhandled interface", iface.name)
|
||||
var actionType = thing.thingClass.actionTypes.findByName("open");
|
||||
engine.deviceManager.executeAction(thing.id, actionType.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
switch (iface.name) {
|
||||
case "light":
|
||||
case "powersocket":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
var allOff = true;
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
if (device.states.getState(device.deviceClass.stateTypes.findByName("power").id).value === true) {
|
||||
allOff = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("power");
|
||||
|
||||
var params = [];
|
||||
var param1 = {};
|
||||
param1["paramTypeId"] = actionType.paramTypes.get(0).id;
|
||||
param1["value"] = allOff ? true : false;
|
||||
params.push(param1)
|
||||
engine.deviceManager.executeAction(device.id, actionType.id, params)
|
||||
}
|
||||
break;
|
||||
case "media":
|
||||
var device = devicesProxy.get(0)
|
||||
break;
|
||||
case "shutter":
|
||||
case "extendedshutter":
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
case "extendedawning":
|
||||
case "simpleclosable":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("playbackStatus");
|
||||
var state = device.states.getState(stateType.id)
|
||||
|
||||
var actionName
|
||||
switch (state.value) {
|
||||
case "Playing":
|
||||
actionName = "pause";
|
||||
break;
|
||||
case "Paused":
|
||||
actionName = "play";
|
||||
break;
|
||||
}
|
||||
var actionTypeId = deviceClass.actionTypes.findByName(actionName).id;
|
||||
|
||||
print("executing", device, device.id, actionTypeId, actionName, deviceClass.actionTypes)
|
||||
|
||||
engine.deviceManager.executeAction(device.id, actionTypeId)
|
||||
case "garagedoor":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var thing = devicesProxy.get(i);
|
||||
if (thing.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
|
||||
var actionType = thing.thingClass.actionTypes.findByName("close");
|
||||
engine.deviceManager.executeAction(thing.id, actionType.id)
|
||||
}
|
||||
if (thing.thingClass.interfaces.indexOf("impulsegaragedoor") >= 0) {
|
||||
var actionType = thing.thingClass.actionTypes.findByName("triggerImpulse");
|
||||
engine.deviceManager.executeAction(thing.id, actionType.id)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "shutter":
|
||||
case "extendedshutter":
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
case "extendedawning":
|
||||
case "simpleclosable":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("close");
|
||||
engine.deviceManager.executeAction(device.id, actionType.id)
|
||||
}
|
||||
|
||||
default:
|
||||
console.warn("InterfaceTile, inlineButtonControl clicked: Unhandled interface", iface.name)
|
||||
var actionType = deviceClass.actionTypes.findByName("open");
|
||||
engine.deviceManager.executeAction(device.id, actionType.id)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn("InterfaceTile:", "inlineButtonControl clicked: Unhandled interface", iface.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
ProgressButton {
|
||||
longpressEnabled: false
|
||||
visible: imageSource.length > 0
|
||||
imageSource: {
|
||||
switch (iface.name) {
|
||||
case "media":
|
||||
case "light":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
return ""
|
||||
case "garagedoor":
|
||||
var dev = devicesProxy.get(0)
|
||||
if (dev.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
return "../images/media-playback-stop.svg"
|
||||
}
|
||||
return ""
|
||||
case "blind":
|
||||
case "awning":
|
||||
case "shutter":
|
||||
case "extendedblind":
|
||||
case "extendedawning":
|
||||
case "extendedshutter":
|
||||
return "../images/media-playback-stop.svg"
|
||||
default:
|
||||
console.warn("InterfaceTile, inlineButtonControl image: Unhandled interface", iface.name)
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
switch (iface.name) {
|
||||
case "light":
|
||||
case "media":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
break;
|
||||
case "garagedoor":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var thing = devicesProxy.get(i);
|
||||
if (thing.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
|
||||
var actionType = thing.thingClass.actionTypes.findByName("stop");
|
||||
engine.thingManager.executeAction(thing.id, actionType.id)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "shutter":
|
||||
case "extendedshutter":
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
case "extendedawning":
|
||||
case "simpleclosable":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("stop");
|
||||
engine.deviceManager.executeAction(device.id, actionType.id)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn("InterfaceTile, inlineButtonControl clicked: Unhandled interface", iface.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
|
||||
ProgressButton {
|
||||
longpressEnabled: false
|
||||
imageSource: {
|
||||
switch (iface.name) {
|
||||
case "media":
|
||||
var device = devicesProxy.get(0)
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("playbackStatus");
|
||||
var state = device.states.getState(stateType.id)
|
||||
return state.value === "Playing" ? "../images/media-playback-pause.svg" :
|
||||
state.value === "Paused" ? "../images/media-playback-start.svg" :
|
||||
""
|
||||
case "light":
|
||||
case "powersocket":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
return "../images/system-shutdown.svg"
|
||||
case "garagedoor":
|
||||
var dev = devicesProxy.get(0)
|
||||
if (dev.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| dev.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
return "../images/down.svg"
|
||||
}
|
||||
if (dev.thingClass.interfaces.indexOf("impulsegaragedoor") >= 0) {
|
||||
return "../images/closable-move.svg"
|
||||
}
|
||||
return ""
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
case "extendedawning":
|
||||
case "shutter":
|
||||
case "extendedshutter":
|
||||
return "../images/down.svg"
|
||||
default:
|
||||
console.warn("InterfaceTile, inlineButtonControl image: Unhandled interface", iface.name)
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
switch (iface.name) {
|
||||
case "light":
|
||||
case "powersocket":
|
||||
case "irrigation":
|
||||
case "ventilation":
|
||||
var allOff = true;
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
if (device.states.getState(device.deviceClass.stateTypes.findByName("power").id).value === true) {
|
||||
allOff = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("power");
|
||||
|
||||
var params = [];
|
||||
var param1 = {};
|
||||
param1["paramTypeId"] = actionType.paramTypes.get(0).id;
|
||||
param1["value"] = allOff ? true : false;
|
||||
params.push(param1)
|
||||
engine.deviceManager.executeAction(device.id, actionType.id, params)
|
||||
}
|
||||
break;
|
||||
case "media":
|
||||
var device = devicesProxy.get(0)
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var stateType = deviceClass.stateTypes.findByName("playbackStatus");
|
||||
var state = device.states.getState(stateType.id)
|
||||
|
||||
var actionName
|
||||
switch (state.value) {
|
||||
case "Playing":
|
||||
actionName = "pause";
|
||||
break;
|
||||
case "Paused":
|
||||
actionName = "play";
|
||||
break;
|
||||
}
|
||||
var actionTypeId = deviceClass.actionTypes.findByName(actionName).id;
|
||||
|
||||
print("executing", device, device.id, actionTypeId, actionName, deviceClass.actionTypes)
|
||||
|
||||
engine.deviceManager.executeAction(device.id, actionTypeId)
|
||||
case "garagedoor":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var thing = devicesProxy.get(i);
|
||||
if (thing.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||
|| thing.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||
|
||||
var actionType = thing.thingClass.actionTypes.findByName("close");
|
||||
engine.deviceManager.executeAction(thing.id, actionType.id)
|
||||
}
|
||||
if (thing.thingClass.interfaces.indexOf("impulsegaragedoor") >= 0) {
|
||||
var actionType = thing.thingClass.actionTypes.findByName("triggerImpulse");
|
||||
engine.deviceManager.executeAction(thing.id, actionType.id)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "shutter":
|
||||
case "extendedshutter":
|
||||
case "blind":
|
||||
case "extendedblind":
|
||||
case "awning":
|
||||
case "extendedawning":
|
||||
case "simpleclosable":
|
||||
for (var i = 0; i < devicesProxy.count; i++) {
|
||||
var device = devicesProxy.get(i);
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||
var actionType = deviceClass.actionTypes.findByName("close");
|
||||
engine.deviceManager.executeAction(device.id, actionType.id)
|
||||
}
|
||||
|
||||
default:
|
||||
console.warn("InterfaceTile, inlineButtonControl clicked: Unhandled interface", iface.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
@ -728,7 +689,7 @@ MainPageTile {
|
||||
text: sensorsRoot.shownStateType
|
||||
? (Math.round(Types.toUiValue(sensorsRoot.device.states.getState(sensorsRoot.shownStateType.id).value, sensorsRoot.shownStateType.unit) * 100) / 100) + " " + Types.toUiUnit(sensorsRoot.shownStateType.unit)
|
||||
: ""
|
||||
// font.pixelSize: app.smallFont
|
||||
// font.pixelSize: app.smallFont
|
||||
Layout.fillWidth: true
|
||||
visible: sensorsRoot.shownStateType && sensorsRoot.shownStateType.type.toLowerCase() !== "bool"
|
||||
elide: Text.ElideRight
|
||||
|
||||
@ -208,16 +208,11 @@ MainPageTile {
|
||||
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
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
ProgressButton {
|
||||
visible: sensorsRoot.shownInterfaces.length > 1
|
||||
contentItem: ColorIcon {
|
||||
name: "../images/back.svg"
|
||||
}
|
||||
longpressEnabled: false
|
||||
imageSource: "../images/back.svg"
|
||||
onClicked: {
|
||||
var newIndex = sensorsRoot.currentStateIndex - 1;
|
||||
if (newIndex < 0) newIndex = sensorsRoot.shownInterfaces.length - 1
|
||||
@ -236,7 +231,7 @@ MainPageTile {
|
||||
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillWidth: false
|
||||
spacing: 0
|
||||
visible: sensorsRoot.currentStateType.type.toLowerCase() !== "bool"
|
||||
|
||||
@ -260,151 +255,34 @@ MainPageTile {
|
||||
|
||||
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
|
||||
ProgressButton {
|
||||
visible: sensorsRoot.shownInterfaces.length > 1
|
||||
contentItem: ColorIcon {
|
||||
name: "../images/next.svg"
|
||||
}
|
||||
longpressEnabled: false
|
||||
imageSource: "../images/next.svg"
|
||||
onClicked: {
|
||||
var newIndex = sensorsRoot.currentStateIndex + 1;
|
||||
if (newIndex >= sensorsRoot.shownInterfaces.length) newIndex = 0;
|
||||
sensorsRoot.currentStateIndex = newIndex;
|
||||
}
|
||||
}
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
ShutterControls {
|
||||
|
||||
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
|
||||
|
||||
MediaControls {
|
||||
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 }
|
||||
thing: device
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,11 +73,11 @@ Page {
|
||||
if (commandId === d.pendingBrowserItemId) {
|
||||
d.pendingBrowserItemId = -1;
|
||||
d.pendingItemId = ""
|
||||
if (params.deviceError !== "DeviceErrorNoError") {
|
||||
if (params.thinggError !== "ThingErrorNoError") {
|
||||
if (params.displayMessage.length > 0) {
|
||||
header.showInfo(qsTr("Error: %1").arg(params.displayMessage), true)
|
||||
} else {
|
||||
header.showInfo(qsTr("Error: %1").arg(params.deviceError), true)
|
||||
header.showInfo(qsTr("Error: %1").arg(params.thingError), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,10 +58,6 @@ DevicePageBase {
|
||||
readonly property StateType intermediatePositionStateType: thing.thingClass.stateTypes.findByName("intermediatePosition")
|
||||
readonly property var intermediatePositionState: intermediatePositionStateType ? device.states.getState(intermediatePositionStateType.id) : null
|
||||
|
||||
// Some garages may also implement the light interface
|
||||
readonly property var lightStateType: thing.thingClass.stateTypes.findByName("power")
|
||||
readonly property var lightState: lightStateType ? thing.states.getState(lightStateType.id) : null
|
||||
|
||||
Component.onCompleted: {
|
||||
print("Creating garage page. Impulse based:", isImpulseBased, "stateful:", isStateful, "extended:", isExtended, "legacy:", intermediatePositionState !== null)
|
||||
}
|
||||
@ -140,19 +136,14 @@ DevicePageBase {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins * 2
|
||||
Layout.fillHeight: true
|
||||
property int minimumWidth: app.iconSize * 2.5 * (root.lightState ? 4 : 3)
|
||||
property int minimumWidth: app.iconSize * 2.5
|
||||
property int minimumHeight: app.iconSize * 2.5
|
||||
|
||||
ItemDelegate {
|
||||
height: app.iconSize * 2
|
||||
width: height
|
||||
ProgressButton {
|
||||
anchors.centerIn: parent
|
||||
visible: root.isImpulseBased
|
||||
ColorIcon {
|
||||
anchors.fill: parent
|
||||
name: "../images/closable-move.svg"
|
||||
anchors.margins: app.margins
|
||||
}
|
||||
longpressEnabled: false
|
||||
imageSource: "../images/closable-move.svg"
|
||||
onClicked: {
|
||||
var actionTypeId = root.thing.thingClass.actionTypes.findByName("triggerImpulse").id
|
||||
print("Triggering impulse", actionTypeId)
|
||||
@ -163,31 +154,10 @@ DevicePageBase {
|
||||
ShutterControls {
|
||||
id: shutterControls
|
||||
device: root.device
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
spacing: (parent.width - app.iconSize*2*children.length) / (children.length - 1)
|
||||
visible: !root.isImpulseBased
|
||||
|
||||
ItemDelegate {
|
||||
width: app.iconSize * 2
|
||||
height: width
|
||||
visible: root.lightStateType !== null
|
||||
|
||||
ColorIcon {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
name: "../images/light-" + (root.lightState && root.lightState.value === true ? "on" : "off") + ".svg"
|
||||
color: root.lightState && root.lightState.value === true ? Material.accent : keyColor
|
||||
}
|
||||
onClicked: {
|
||||
print("blabla", root.lightState, root.lightState.value, root.lightStateType.name, root.lightState.stateTypeId, root.lightStateType.id)
|
||||
var params = [];
|
||||
var param = {};
|
||||
param["paramTypeId"] = root.lightStateType.id;
|
||||
param["value"] = !root.lightState.value;
|
||||
params.push(param)
|
||||
engine.deviceManager.executeAction(root.device.id, root.lightStateType.id, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,305 +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.5
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtGraphicalEffects 1.0
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
import "../customviews"
|
||||
import "../delegates"
|
||||
|
||||
DevicePageBase {
|
||||
id: root
|
||||
popStackOnBackButton: false
|
||||
showBrowserButton: false
|
||||
|
||||
onBackPressed: {
|
||||
swipeView.currentItem.backPressed()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.deviceClass.browsable && playbackState.value === "Stopped") {
|
||||
swipeView.currentIndex = 1;
|
||||
}
|
||||
}
|
||||
|
||||
function stateValue(name) {
|
||||
var stateType = root.deviceClass.stateTypes.findByName(name);
|
||||
if (!stateType) return null
|
||||
return root.device.states.getState(stateType.id).value
|
||||
}
|
||||
|
||||
function executeAction(actionName, params) {
|
||||
var actionTypeId = deviceClass.actionTypes.findByName(actionName).id;
|
||||
print("executing", device, device.id, actionTypeId, actionName, deviceClass.actionTypes, params)
|
||||
engine.deviceManager.executeAction(device.id, actionTypeId, params)
|
||||
}
|
||||
|
||||
function executeBrowserItem(itemId) {
|
||||
d.pendingItemId = itemId
|
||||
d.pendingBrowserItemId = engine.deviceManager.executeBrowserItem(device.id, itemId);
|
||||
}
|
||||
function executeBrowserItemAction(itemId, actionTypeId, params) {
|
||||
print("params2:", JSON.stringify(params))
|
||||
d.pendingItemId = itemId
|
||||
d.pendingBrowserItemId = engine.deviceManager.executeBrowserItemAction(device.id, itemId, actionTypeId, params);
|
||||
}
|
||||
|
||||
function adjustVolume(volume) {
|
||||
d.pendingVolumeValue = volume;
|
||||
|
||||
if (d.pendingVolumeId !== -1) {
|
||||
// busy
|
||||
return;
|
||||
}
|
||||
|
||||
var params = []
|
||||
var volParam = {}
|
||||
volParam["paramTypeId"] = root.deviceClass.actionTypes.findByName("volume").id
|
||||
volParam["value"] = volume;
|
||||
params.push(volParam)
|
||||
var actionTypeId = deviceClass.actionTypes.findByName("volume").id;
|
||||
d.pendingVolumeId = engine.deviceManager.executeAction(device.id, actionTypeId, params);
|
||||
print("exec", d.pendingVolumeId)
|
||||
return;
|
||||
}
|
||||
|
||||
readonly property State playbackState: device.states.getState(deviceClass.stateTypes.findByName("playbackStatus").id)
|
||||
readonly property State volumeState: device.states.getState(deviceClass.stateTypes.findByName("volume").id)
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
property int pendingBrowserItemId: -1
|
||||
property string pendingItemId: ""
|
||||
|
||||
property int pendingVolumeId: -1
|
||||
property int pendingVolumeValue: -1
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: engine.deviceManager
|
||||
onExecuteBrowserItemReply: executionFinished(commandId, params)
|
||||
onExecuteBrowserItemActionReply: executionFinished(commandId, params)
|
||||
onExecuteActionReply: {
|
||||
print("actionfinished", commandId)
|
||||
if (commandId === d.pendingVolumeId) {
|
||||
d.pendingVolumeId = -1
|
||||
print("volume action finished")
|
||||
if (params.deviceError !== "DeviceErrorNoError") {
|
||||
print("Error setting volume", params.deviceError)
|
||||
d.pendingVolumeValue = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (d.pendingVolumeValue !== volumeState.value) {
|
||||
root.adjustVolume(d.pendingVolumeValue);
|
||||
} else {
|
||||
d.pendingVolumeValue = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function executionFinished(commandId, params) {
|
||||
print("Execute reply:", params, commandId, d.pendingBrowserItemId)
|
||||
if (commandId === d.pendingBrowserItemId) {
|
||||
d.pendingBrowserItemId = -1;
|
||||
d.pendingItemId = ""
|
||||
print("yep finished")
|
||||
if (params.deviceError === "DeviceErrorNoError") {
|
||||
swipeView.currentIndex = 0;
|
||||
} else {
|
||||
header.showInfo(qsTr("Error: %1").arg(params.deviceError), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
anchors.fill: parent
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.deviceClass.browsable) {
|
||||
browserComponent.createObject(swipeView)
|
||||
}
|
||||
|
||||
if (root.deviceClass.interfaces.indexOf("navigationpad") >= 0) {
|
||||
navigationComponent.createObject(swipeView)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
function backPressed() {
|
||||
pageStack.pop();
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: contentColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
columns: app.landscape ? 2 : 1
|
||||
columnSpacing: app.margins
|
||||
rowSpacing: app.margins
|
||||
|
||||
MediaArtworkImage {
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: parent.width / parent.columns
|
||||
thing: root.device
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: app.margins
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 2
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: app.largeFont
|
||||
font.bold: true
|
||||
text: root.stateValue("title")
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 2
|
||||
elide: Text.ElideRight
|
||||
text: root.stateValue("artist")
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 2
|
||||
elide: Text.ElideRight
|
||||
text: root.stateValue("collection")
|
||||
}
|
||||
|
||||
MediaControls {
|
||||
thing: root.device
|
||||
iconSize: app.iconSize * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: browserComponent
|
||||
|
||||
MediaBrowser {
|
||||
thing: root.device
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: navigationComponent
|
||||
Item {
|
||||
function backPressed() {
|
||||
swipeView.currentIndex--;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
|
||||
NavigationPad {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
device: root.device
|
||||
}
|
||||
|
||||
MediaControls {
|
||||
Layout.fillWidth: true
|
||||
thing: root.device
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
footer: Pane {
|
||||
Material.elevation: 1
|
||||
height: 52
|
||||
padding: 0
|
||||
contentItem: ColumnLayout {
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 2
|
||||
visible: swipeView.count > 1
|
||||
Rectangle {
|
||||
height: parent.height
|
||||
width: parent.width / swipeView.count
|
||||
color: app.accentColor
|
||||
x: swipeView.currentIndex * width
|
||||
Behavior on x { NumberAnimation { duration: 150 } }
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: swipeView.count > 1 && swipeView.currentIndex > 0 ? parent.width / 4 : 0
|
||||
Behavior on Layout.preferredWidth { NumberAnimation {} }
|
||||
HeaderButton {
|
||||
anchors.centerIn: parent
|
||||
imageSource: "../images/back.svg"
|
||||
opacity: swipeView.count > 1 && swipeView.currentIndex > 0 ? 1 : 0
|
||||
Behavior on opacity { NumberAnimation {} }
|
||||
onClicked: swipeView.currentIndex--
|
||||
}
|
||||
}
|
||||
ShuffleRepeatVolumeControl {
|
||||
Layout.fillWidth: true
|
||||
thing: root.device
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: swipeView.count > 1 && swipeView.currentIndex < swipeView.count - 1 ? parent.width / 4 : 0
|
||||
Behavior on Layout.preferredWidth { NumberAnimation {} }
|
||||
HeaderButton {
|
||||
anchors.centerIn: parent
|
||||
imageSource: "../images/next.svg"
|
||||
onClicked: swipeView.currentIndex++
|
||||
opacity: swipeView.count > 1 && swipeView.currentIndex < swipeView.count - 1 ? 1 : 0
|
||||
Behavior on opacity { NumberAnimation {} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
nymea-app/ui/devicepages/MediaThingPage.qml
Normal file
49
nymea-app/ui/devicepages/MediaThingPage.qml
Normal file
@ -0,0 +1,49 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, GNU version 3. This project is distributed in the hope that it
|
||||
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtGraphicalEffects 1.0
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
import "../customviews"
|
||||
import "../delegates"
|
||||
|
||||
DevicePageBase {
|
||||
id: root
|
||||
showBrowserButton: false
|
||||
|
||||
MediaPlayer {
|
||||
anchors.fill: parent
|
||||
thing: root.thing
|
||||
}
|
||||
}
|
||||
23
nymea-app/ui/images/like.svg
Normal file
23
nymea-app/ui/images/like.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg id="svg4874" width="96" height="96" version="1.1" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<metadata id="metadata4879">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1" transform="translate(67.857 -78.505)">
|
||||
<g id="g4845" transform="matrix(0 -1 -1 0 373.51 516.51)">
|
||||
<g id="g4778" transform="matrix(-.9996 0 0 1 575.94 -611)">
|
||||
<g id="g4780" transform="matrix(-1 0 0 1 576 611)">
|
||||
<rect id="rect4782" transform="scale(-1,1)" x="-438" y="345.36" width="96.038" height="96" style="color:#000000;fill:none"/>
|
||||
<path id="path4849" d="m430 411.93c0 11.675-9.3424 21.174-20.961 21.429-5.3915 0-9.5995-1.413-13.487-2.8571-16.066-6.7837-29.485-20-45.584-37.143 16.098-17.143 29.518-30.359 45.584-37.143 3.888-1.4441 8.096-2.8571 13.487-2.8571 11.618 0.25447 20.961 9.7538 20.961 21.429 0 7.9422-4.3266 14.87-10.748 18.571 6.4217 3.7014 10.748 10.629 10.748 18.571z" style="color:#000000;fill:#808080"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
75
nymea-app/ui/images/state-in.svg
Normal file
75
nymea-app/ui/images/state-in.svg
Normal file
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg4874"
|
||||
width="96"
|
||||
height="96"
|
||||
version="1.1"
|
||||
viewBox="0 0 96 96"
|
||||
sodipodi:docname="state-in.svg"
|
||||
inkscape:version="1.0.1 (1.0.1+r74)">
|
||||
<defs
|
||||
id="defs11" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1380"
|
||||
inkscape:window-height="873"
|
||||
id="namedview9"
|
||||
showgrid="false"
|
||||
inkscape:zoom="6.6458333"
|
||||
inkscape:cx="41.905956"
|
||||
inkscape:cy="48"
|
||||
inkscape:window-x="60"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4874" />
|
||||
<metadata
|
||||
id="metadata4879">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
transform="rotate(-90,-25.180998,53.323997)">
|
||||
<rect
|
||||
id="rect4782-2"
|
||||
transform="rotate(90)"
|
||||
x="78.504997"
|
||||
y="-28.143"
|
||||
width="96"
|
||||
height="96"
|
||||
style="color:#000000;fill:none" />
|
||||
<path
|
||||
id="path4643-2"
|
||||
d="m -39.869,98.504 -0.01134,0.002 c -5.0328,0.0582 -8.7136,-0.12019 -11.725,1.541 -1.5055,0.83062 -2.6968,2.2356 -3.3555,3.9902 -0.65866,1.7546 -0.89647,3.8364 -0.89647,6.4668 v 48.002 c 0,2.6304 0.23773,4.7122 0.89647,6.4668 0.65866,1.7546 1.85,3.1596 3.3555,3.9902 3.011,1.6613 6.6918,1.4848 11.725,1.543 H -39.869 0.154 0.16534 c 5.0328,-0.0582 8.7136,0.1183 11.725,-1.543 1.5055,-0.83066 2.6968,-2.2356 3.3555,-3.9902 0.65866,-1.7546 0.8965,-3.8364 0.8965,-6.4668 v -48.002 c 0,-2.6304 -0.23773,-4.7122 -0.8965,-6.4668 -0.65866,-1.7547 -1.85,-3.1596 -3.3555,-3.9902 -3.011,-1.6613 -6.6918,-1.4829 -11.725,-1.5411 l -0.01134,-0.002 -12.012,0.002 v 4 l 11.977,-0.002 c 5.0542,0.0586 8.3726,0.23547 9.8398,1.0449 0.73364,0.40479 1.1527,0.85493 1.543,1.8945 0.39024,1.0396 0.64059,2.691 0.64059,5.0606 v 48.002 c 0,2.3695 -0.2502,4.0209 -0.64059,5.0605 -0.39027,1.0396 -0.80935,1.4898 -1.543,1.8945 -1.4645,0.80806 -4.7782,0.98615 -9.8164,1.0449 h -39.977 -0.02268 c -5.0383,-0.059 -8.3519,-0.23697 -9.8164,-1.0449 -0.73364,-0.40475 -1.1508,-0.85489 -1.541,-1.8945 -0.39027,-1.0396 -0.6426,-2.691 -0.6426,-5.0605 v -48.002 c 0,-2.3696 0.25247,-4.0209 0.6426,-5.0606 0.39024,-1.0396 0.80734,-1.4897 1.541,-1.8945 1.4645,-0.80807 4.7782,-0.98616 9.8164,-1.0449 l 12,0.002 v -4 z"
|
||||
style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#808080;color-rendering:auto;image-rendering:auto;shape-rendering:auto" />
|
||||
<path
|
||||
id="path4237"
|
||||
d="m -21.858,82.505 v 50 h 4 v -50 z"
|
||||
style="fill:#808080" />
|
||||
<path
|
||||
id="path5588-9-2-96-04"
|
||||
d="m -7.8571,124.51 -24,0.008 c 1.6687,3.6501 3.5359,7.366 5.5987,11.149 2.0678,3.7477 4.2005,7.3628 6.4003,10.843 2.1557,-3.48 4.268,-7.0951 6.3358,-10.843 2.0639,-3.7854 3.9519,-7.5033 5.6652,-11.155 z"
|
||||
style="color:#000000;fill:#808080" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
84
nymea-app/ui/images/state-out.svg
Normal file
84
nymea-app/ui/images/state-out.svg
Normal file
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg4874"
|
||||
width="96"
|
||||
height="96"
|
||||
version="1.1"
|
||||
viewBox="0 0 96 96"
|
||||
sodipodi:docname="state-out.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<defs
|
||||
id="defs11" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2792"
|
||||
inkscape:window-height="1698"
|
||||
id="namedview9"
|
||||
showgrid="true"
|
||||
inkscape:zoom="2.4583333"
|
||||
inkscape:cx="-47.999999"
|
||||
inkscape:cy="47.999999"
|
||||
inkscape:window-x="88"
|
||||
inkscape:window-y="44"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4874">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1441" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4879">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<rect
|
||||
style="color:#000000;fill:none;stroke-width:1"
|
||||
height="83.999992"
|
||||
width="83.999992"
|
||||
y="6"
|
||||
x="-83.5"
|
||||
id="rect4782-2"
|
||||
transform="scale(-1,1)" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#808080;stroke-width:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
|
||||
d="m 66.00087,65.510496 -0.0017,0.0099 c -0.05093,4.4037 0.105166,7.6244 -1.348375,10.259374 -0.726792,1.317313 -1.95615,2.3597 -3.491425,2.936063 -1.535275,0.576327 -3.35685,0.784411 -5.658449,0.784411 H 13.499174 c -2.3016,0 -4.123175,-0.208014 -5.65845,-0.784411 C 6.305449,78.139505 5.076074,77.097083 4.349299,75.77977 2.895662,73.145146 3.050099,69.924446 2.999174,65.520396 v -0.0099 -35.020123 -0.0099 c 0.05093,-4.4037 -0.103512,-7.6244 1.350125,-10.259375 0.726828,-1.317312 1.95615,-2.3597 3.491425,-2.936062 1.535275,-0.576328 3.35685,-0.784438 5.65845,-0.784438 h 42.001697 c 2.301599,0 4.123174,0.208014 5.658449,0.784438 1.535363,0.576327 2.76465,1.61875 3.491425,2.936062 1.453637,2.634625 1.297537,5.855325 1.348462,10.259375 l 0.0018,0.0099 -0.0018,10.510499 h -3.499999 l 0.0017,-10.479874 c -0.05127,-4.422425 -0.206037,-7.326024 -0.914288,-8.609824 -0.354191,-0.641935 -0.748063,-1.008613 -1.657687,-1.350125 -0.90965,-0.34146 -2.354625,-0.560517 -4.428025,-0.560517 H 13.499161 c -2.073312,0 -3.518287,0.218925 -4.427937,0.560517 -0.90965,0.341486 -1.303575,0.708181 -1.657688,1.350125 -0.707052,1.281437 -0.862881,4.180924 -0.914287,8.589349 v 34.979873 0.01985 c 0.05163,4.408512 0.207349,7.307912 0.914287,8.589349 0.354157,0.641935 0.748029,1.00695 1.657688,1.348375 0.90965,0.341486 2.354625,0.562275 4.427937,0.562275 h 42.001797 c 2.0734,0 3.518287,-0.220911 4.428025,-0.562275 0.90965,-0.34146 1.303487,-0.706423 1.657687,-1.348375 0.707061,-1.281437 0.86289,-4.180925 0.914288,-8.589349 l -0.0017,-10.5 h 3.499999 z"
|
||||
id="path4643-2" />
|
||||
<g
|
||||
id="g1449"
|
||||
transform="translate(22.495617)">
|
||||
<path
|
||||
id="path4237"
|
||||
d="m 15.500006,49.750872 h 43.749997 v -3.5 H 15.500006 Z"
|
||||
style="fill:#808080;stroke-width:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path5588-9-2-96-04"
|
||||
d="m 52.254379,37.500085 0.007,20.999999 c 3.193837,-1.460113 6.445249,-3.093913 9.755374,-4.898863 3.279237,-1.809324 6.442449,-3.675437 9.487624,-5.600262 -3.045,-1.886237 -6.208212,-3.734499 -9.487624,-5.543824 -3.312225,-1.805913 -6.565387,-3.457913 -9.760624,-4.95705 z"
|
||||
style="color:#000000;fill:#808080;stroke-width:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@ -1,401 +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.5
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import Nymea 1.0
|
||||
import "../components"
|
||||
import "../paramdelegates"
|
||||
|
||||
Page {
|
||||
id: root
|
||||
|
||||
// input
|
||||
property string text
|
||||
|
||||
// output
|
||||
property var actions: []
|
||||
signal complete();
|
||||
|
||||
header: NymeaHeader {
|
||||
text: "Select action"
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: actionModel
|
||||
ListElement { interfaceName: "light"; text: qsTr("Switch lights..."); identifier: "switchLights" }
|
||||
ListElement { interfaceName: "mediacontroller"; text: qsTr("Control media playback..."); identifier: "controlMedia" }
|
||||
ListElement { interfaceName: "extendedvolumecontroller"; text: qsTr("Mute media playback..."); identifier: "muteMedia" }
|
||||
ListElement { interfaceName: "notifications"; text: qsTr("Notify me..."); identifier: "notify" }
|
||||
ListElement { interfaceName: ""; text: qsTr("Manually configure an action..."); identifier: "manualAction" }
|
||||
}
|
||||
|
||||
DevicesProxy {
|
||||
id: ifaceFilterModel
|
||||
engine: _engine
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
actualModel.clear()
|
||||
for (var i = 0; i < actionModel.count; i++) {
|
||||
ifaceFilterModel.shownInterfaces = [actionModel.get(i).interfaceName];
|
||||
if (actionModel.get(i).interfaceName === "" || ifaceFilterModel.count > 0) {
|
||||
actualModel.append(actionModel.get(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function actionSelected(identifier) {
|
||||
switch (identifier) {
|
||||
case "switchLights":
|
||||
pageStack.push(switchLightsCompoent)
|
||||
break;
|
||||
case "controlMedia":
|
||||
break;
|
||||
case "muteMedia":
|
||||
break;
|
||||
case "manualAction":
|
||||
pageStack.push(selectDeviceComponent)
|
||||
break;
|
||||
case "notify":
|
||||
pageStack.push(notificationActionComponent)
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: root.text
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
model: ListModel {
|
||||
id: actualModel
|
||||
}
|
||||
delegate: ItemDelegate {
|
||||
width: parent.width
|
||||
text: model.text
|
||||
onClicked: {
|
||||
root.actionSelected(model.identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: selectDeviceComponent
|
||||
Page {
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Select device")
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
ListView {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
model: engine.deviceManager.devices
|
||||
delegate: ItemDelegate {
|
||||
width: parent.width
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
text: model.name
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
onClicked: {
|
||||
var device = engine.deviceManager.devices.get(index)
|
||||
pageStack.push(selectDeviceActionComponent, {device: device})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: selectDeviceActionComponent
|
||||
Page {
|
||||
id: page
|
||||
property var device
|
||||
readonly property var deviceClass: engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
|
||||
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Select action")
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
ListView {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
model: page.deviceClass.actionTypes
|
||||
|
||||
delegate: ItemDelegate {
|
||||
width: parent.width
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
text: model.name
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
var actionType = page.deviceClass.actionTypes.get(index)
|
||||
if (page.deviceClass.actionTypes.get(index).paramTypes.count === 0) {
|
||||
// We're all set.
|
||||
var action = {}
|
||||
action["deviceId"] = page.device.id
|
||||
action["actionTypeId"] = actionType.id
|
||||
root.actions.push(action)
|
||||
root.complete();
|
||||
} else {
|
||||
// need to fill in params
|
||||
pageStack.push(selectDeviceActionParamComponent, {device: page.device, actionType: actionType})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: selectDeviceActionParamComponent
|
||||
Page {
|
||||
id: page
|
||||
property var device
|
||||
property var actionType
|
||||
header: NymeaHeader {
|
||||
text: qsTr("params")
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
Repeater {
|
||||
id: delegateRepeater
|
||||
model: page.actionType.paramTypes
|
||||
delegate: ParamDelegate {
|
||||
paramType: page.actionType.paramTypes.get(index)
|
||||
value: paramType.defaultValue
|
||||
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
Button {
|
||||
text: qsTr("OK")
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
onClicked: {
|
||||
var params = [];
|
||||
for (var i = 0; i < delegateRepeater.count; i++) {
|
||||
var paramDelegate = delegateRepeater.itemAt(i);
|
||||
var param = {}
|
||||
param["paramTypeId"] = paramDelegate.paramType.id
|
||||
param["value"] = paramDelegate.value
|
||||
params.push(param)
|
||||
}
|
||||
var action = {};
|
||||
action["deviceId"] = page.device.id
|
||||
action["actionTypeId"] = page.actionType.id
|
||||
action["ruleActionParams"] = params
|
||||
root.actions.push(action)
|
||||
root.complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: switchLightsCompoent
|
||||
Page {
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Switch lights")
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
SwitchDelegate {
|
||||
id: switchDelegate
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Set selected lights power to")
|
||||
position: 0
|
||||
}
|
||||
ThinDivider {}
|
||||
|
||||
Flickable {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
interactive: contentHeight > height
|
||||
clip: true
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
|
||||
Repeater {
|
||||
id: lightsRepeater
|
||||
|
||||
model: DevicesProxy {
|
||||
id: lightsModel
|
||||
engine: _engine
|
||||
shownInterfaces: ["light"]
|
||||
}
|
||||
delegate: CheckDelegate {
|
||||
width: parent.width
|
||||
text: model.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("OK")
|
||||
onClicked: {
|
||||
for (var i = 0; i < lightsRepeater.count; i++) {
|
||||
if (lightsRepeater.itemAt(i).checkState === Qt.Unchecked) {
|
||||
continue;
|
||||
}
|
||||
var device = lightsModel.get(i);
|
||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
|
||||
|
||||
var action = {}
|
||||
action["deviceId"] = device.id
|
||||
|
||||
var actionType = deviceClass.actionTypes.findByName("power")
|
||||
action["actionTypeId"] = actionType.id
|
||||
|
||||
var params = [];
|
||||
var paramType = actionType.paramTypes.getParamType("power");
|
||||
var param = {}
|
||||
param["paramTypeId"] = paramType.id
|
||||
param["value"] = switchDelegate.position === 1 ? true : false;
|
||||
params.push(param)
|
||||
|
||||
action["ruleActionParams"] = params
|
||||
root.actions.push(action)
|
||||
}
|
||||
root.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: notificationActionComponent
|
||||
Page {
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Send notification")
|
||||
onBackPressed: pageStack.pop()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: app.margins
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Notification text")
|
||||
Layout.topMargin: app.margins
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
}
|
||||
TextField {
|
||||
id: notificationTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: app.margins
|
||||
Layout.rightMargin: app.margins
|
||||
}
|
||||
ThinDivider {}
|
||||
Flickable {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
interactive: contentHeight > height
|
||||
clip: true
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
|
||||
Repeater {
|
||||
id: notificationsRepeater
|
||||
|
||||
model: DevicesProxy {
|
||||
id: notificationsModel
|
||||
engine: _engine
|
||||
shownInterfaces: ["notifications"]
|
||||
}
|
||||
delegate: CheckDelegate {
|
||||
width: parent.width
|
||||
text: model.name
|
||||
checked: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: app.margins
|
||||
text: qsTr("OK")
|
||||
onClicked: {
|
||||
var action = {}
|
||||
action["interface"] = "notifications";
|
||||
action["interfaceAction"] = "notify";
|
||||
action["ruleActionParams"] = [];
|
||||
var ruleActionParam = {};
|
||||
ruleActionParam["paramName"] = "title";
|
||||
ruleActionParam["value"] = notificationTextField.text
|
||||
action["ruleActionParams"].push(ruleActionParam)
|
||||
root.actions.push(action)
|
||||
root.complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -477,7 +477,6 @@ MainViewBase {
|
||||
id: mediaControllerDelegate
|
||||
MediaControls {
|
||||
property var devices: null
|
||||
iconSize: app.iconSize
|
||||
DevicesProxy {
|
||||
id: mediaControllers
|
||||
engine: _engine
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.2
|
||||
@ -40,6 +40,8 @@ import "../delegates"
|
||||
MainViewBase {
|
||||
id: root
|
||||
|
||||
title: swipeView.currentItem ? swipeView.currentItem.thing.name : ""
|
||||
|
||||
ThingsProxy {
|
||||
id: mediaDevices
|
||||
engine: _engine
|
||||
@ -48,125 +50,20 @@ MainViewBase {
|
||||
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
anchors.fill: parent
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right; bottom: parent.bottom }
|
||||
currentIndex: pageIndicator.currentIndex
|
||||
|
||||
Repeater {
|
||||
model: mediaDevices
|
||||
delegate: Item {
|
||||
id: playerDelegate
|
||||
height: swipeView.height
|
||||
width: swipeView.width
|
||||
property Thing thing: mediaDevices.get(index)
|
||||
property State titleState: thing.stateByName("title")
|
||||
property State artistState: thing.stateByName("artist")
|
||||
property State collectionState: thing.stateByName("collection")
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
columns: 1
|
||||
rowSpacing: app.margins
|
||||
|
||||
MediaArtworkImage {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
thing: playerDelegate.thing
|
||||
}
|
||||
ColumnLayout {
|
||||
spacing: app.margins
|
||||
Label {
|
||||
text: playerDelegate.titleState.value
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
Label {
|
||||
text: playerDelegate.artistState.value
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
Label {
|
||||
text: playerDelegate.collectionState.value
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
MediaControls {
|
||||
Layout.fillWidth: true
|
||||
thing: playerDelegate.thing
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.fillWidth: true
|
||||
visible: playerDelegate.thing.thingClass.browsable
|
||||
|
||||
HeaderButton {
|
||||
anchors.centerIn: parent
|
||||
imageSource: "../images/navigationpad.svg"
|
||||
onClicked: {
|
||||
pageStack.push(navigationPadPage)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: navigationPadPage
|
||||
Page {
|
||||
header: NymeaHeader { text: playerDelegate.thing.name; onBackPressed: pageStack.pop() }
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
spacing: app.margins
|
||||
|
||||
NavigationPad { Layout.fillWidth: true; Layout.fillHeight: true; device: playerDelegate.thing }
|
||||
MediaControls { Layout.fillWidth: true; thing: playerDelegate.thing }
|
||||
ShuffleRepeatVolumeControl { Layout.fillWidth: true; Layout.fillHeight: false; Layout.preferredHeight: app.iconSize; thing: playerDelegate.thing }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShuffleRepeatVolumeControl {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: false
|
||||
Layout.preferredHeight: app.iconSize
|
||||
thing: playerDelegate.thing
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.fillWidth: true
|
||||
visible: playerDelegate.thing.thingClass.interfaces.indexOf("navigationpad") >= 0
|
||||
|
||||
HeaderButton {
|
||||
anchors.centerIn: parent
|
||||
imageSource: "../images/folder-symbolic.svg"
|
||||
onClicked: {
|
||||
pageStack.push(browserPage)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: browserPage
|
||||
Page {
|
||||
header: NymeaHeader { text: playerDelegate.thing.name; onBackPressed: pageStack.pop() }
|
||||
MediaBrowser { anchors.fill: parent; thing: playerDelegate.thing }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: MediaPlayer {
|
||||
thing: mediaDevices.get(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
PageIndicator {
|
||||
id: pageIndicator
|
||||
count: swipeView.count
|
||||
visible: count > 1
|
||||
currentIndex: swipeView.currentIndex
|
||||
interactive: true
|
||||
anchors.bottom: parent.bottom
|
||||
@ -183,5 +80,4 @@ MainViewBase {
|
||||
buttonText: qsTr("Add things")
|
||||
onButtonClicked: pageStack.push(Qt.resolvedUrl("../thingconfiguration/NewThingPage.qml"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ Item {
|
||||
print("**** getting page for interfaces", interfaceList)
|
||||
var page;
|
||||
if (interfaceList.indexOf("media") >= 0) {
|
||||
page = "MediaDevicePage.qml";
|
||||
page = "MediaThingPage.qml";
|
||||
} else if (interfaceList.indexOf("button") >= 0) {
|
||||
page = "ButtonDevicePage.qml";
|
||||
} else if (interfaceList.indexOf("powerswitch") >= 0) {
|
||||
|
||||
Reference in New Issue
Block a user