Rework media views for new interfaces
This commit is contained in:
parent
8ce72bb9da
commit
10bf8ed00c
@ -189,7 +189,7 @@ public class NymeaAppControlService extends ControlsProviderService {
|
|||||||
actionTypeId = thing.actionByName("close").typeId;
|
actionTypeId = thing.actionByName("close").typeId;
|
||||||
}
|
}
|
||||||
param = "";
|
param = "";
|
||||||
} else if (thing.interfaces.contains("extendedvolumecontroller")) {
|
} else if (thing.interfaces.contains("volumecontroller") && thing.stateByName("volume") != null) {
|
||||||
actionTypeId = thing.stateByName("volume").typeId;
|
actionTypeId = thing.stateByName("volume").typeId;
|
||||||
FloatAction fAction = (FloatAction) action;
|
FloatAction fAction = (FloatAction) action;
|
||||||
param = String.valueOf(Math.round(fAction.getNewValue()));
|
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!?!
|
// FIXME: There doesn't seem to be a speaker DeviceType!?!
|
||||||
builder.setDeviceType(DeviceTypes.TYPE_TV);
|
builder.setDeviceType(DeviceTypes.TYPE_TV);
|
||||||
}
|
}
|
||||||
if (thing.interfaces.contains("extendedvolumecontroller")) {
|
if (thing.interfaces.contains("volumecontroller")) {
|
||||||
State volumeState = thing.stateByName("volume");
|
State volumeState = thing.stateByName("volume");
|
||||||
RangeTemplate rangeTemplate = new RangeTemplate(thing.id.toString(), 0, 100, Float.parseFloat(volumeState.value), 1, volumeState.displayName);
|
if (volumeState != null) {
|
||||||
builder.setControlTemplate(rangeTemplate);
|
RangeTemplate rangeTemplate = new RangeTemplate(thing.id.toString(), 0, 100, Float.parseFloat(volumeState.value), 1, volumeState.displayName);
|
||||||
|
builder.setControlTemplate(rangeTemplate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
builder.setDeviceType(DeviceTypes.TYPE_GENERIC_ON_OFF);
|
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)
|
int DeviceManager::executeBrowserItem(const QUuid &deviceId, const QString &itemId)
|
||||||
{
|
{
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
params.insert("deviceId", deviceId);
|
params.insert("thingId", deviceId);
|
||||||
params.insert("itemId", itemId);
|
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)
|
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)
|
int DeviceManager::executeBrowserItemAction(const QUuid &deviceId, const QString &itemId, const QUuid &actionTypeId, const QVariantList ¶ms)
|
||||||
{
|
{
|
||||||
QVariantMap data;
|
QVariantMap data;
|
||||||
data.insert("deviceId", deviceId);
|
data.insert("thingId", deviceId);
|
||||||
data.insert("itemId", itemId);
|
data.insert("itemId", itemId);
|
||||||
data.insert("actionTypeId", actionTypeId);
|
data.insert("actionTypeId", actionTypeId);
|
||||||
data.insert("params", params);
|
data.insert("params", params);
|
||||||
qDebug() << "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)
|
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") {
|
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") {
|
if (reply->nameSpace() == "JSONRPC" && reply->method() == "Hello") {
|
||||||
qWarning() << "Hello call failed. Trying again without locale";
|
qWarning() << "Hello call failed. Trying again without locale";
|
||||||
m_id = 0;
|
m_id = 0;
|
||||||
|
|||||||
@ -144,10 +144,10 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
|
|||||||
addActionType("mediacontroller", "pause", tr("Pause playback"), new ParamTypes());
|
addActionType("mediacontroller", "pause", tr("Pause playback"), new ParamTypes());
|
||||||
addActionType("mediacontroller", "skipBack", tr("Skip back"), new ParamTypes());
|
addActionType("mediacontroller", "skipBack", tr("Skip back"), new ParamTypes());
|
||||||
addActionType("mediacontroller", "skipNext", tr("Skip next"), new ParamTypes());
|
addActionType("mediacontroller", "skipNext", tr("Skip next"), new ParamTypes());
|
||||||
|
addActionType("mediacontroller", "fastForward", tr("Fast forward"), new ParamTypes());
|
||||||
addInterface("extendedmediacontroller", tr("Media controllers with seeking"), {"mediacontroller"});
|
addActionType("mediacontroller", "fastRewind", tr("Fast rewind"), new ParamTypes());
|
||||||
addActionType("extendedmediacontroller", "fastForward", tr("Fast forward"), new ParamTypes());
|
addStateType("mediacontroller", "shuffle", QVariant::Bool, true, tr("Shuffle"), tr("Shuffle changed"), tr("Set shuffle"));
|
||||||
addActionType("extendedmediacontroller", "fastRewind", tr("Fast rewind"), new ParamTypes());
|
addStateType("mediacontroller", "repeat", QVariant::Bool, true, tr("Repeat"), tr("Repeat changed"), tr("Set repeat"));
|
||||||
|
|
||||||
addInterface("navigationpad", tr("Navigation pad"));
|
addInterface("navigationpad", tr("Navigation pad"));
|
||||||
pts = createParamTypes("to", tr("To"), QVariant::String, QVariant(), {"up", "down", "left", "right", "enter", "back"});
|
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"});
|
addInterface("extendedsmartmeterproducer", tr("Smart meters"), {"smartmeterproducer"});
|
||||||
addStateType("extendedsmartmeterproducer", "currentPower", QVariant::Double, false, tr("Current power"), tr("Current power changed"));
|
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"});
|
addInterface("useraccesscontrol", tr("User access control systems"), {"accesscontrol"});
|
||||||
addStateType("useraccesscontrol", "users", QVariant::StringList, false, tr("Users"), tr("Users changed"));
|
addStateType("useraccesscontrol", "users", QVariant::StringList, false, tr("Users"), tr("Users changed"));
|
||||||
pts = createParamTypes("user", tr("User"), QVariant::String);
|
pts = createParamTypes("user", tr("User"), QVariant::String);
|
||||||
@ -268,10 +264,6 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
|
|||||||
addInterface("pressuresensor", tr("Pressure sensors"), {"sensor"});
|
addInterface("pressuresensor", tr("Pressure sensors"), {"sensor"});
|
||||||
addStateType("pressuresensor", "pressure", QVariant::Double, false, tr("Pressure"), tr("Pressure changed"));
|
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"));
|
addInterface("smartlock", tr("Smart locks"));
|
||||||
addStateType("smartlock", "state", QVariant::String, false, tr("State"), tr("State changed"));
|
addStateType("smartlock", "state", QVariant::String, false, tr("State"), tr("State changed"));
|
||||||
addActionType("smartlock", "unlatch", tr("Unlatch"), new ParamTypes());
|
addActionType("smartlock", "unlatch", tr("Unlatch"), new ParamTypes());
|
||||||
@ -285,6 +277,8 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
|
|||||||
addInterface("ventilation", tr("Ventilation"), {"power"});
|
addInterface("ventilation", tr("Ventilation"), {"power"});
|
||||||
|
|
||||||
addInterface("volumecontroller", tr("Speakers"));
|
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", "increaseVolume", tr("Increase volume"), new ParamTypes());
|
||||||
addActionType("volumecontroller", "decreaseVolume", tr("Decrease 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/connections/network-wired-disabled.svg</file>
|
||||||
<file>ui/images/nfc.svg</file>
|
<file>ui/images/nfc.svg</file>
|
||||||
<file>ui/images/smartphone.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>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -167,3 +167,9 @@ INSTALLS += target
|
|||||||
|
|
||||||
ANDROID_ABIS = armeabi-v7a arm64-v8a
|
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/GenericTypeLogView.qml</file>
|
||||||
<file>ui/customviews/CustomViewBase.qml</file>
|
<file>ui/customviews/CustomViewBase.qml</file>
|
||||||
<file>ui/customviews/WeatherView.qml</file>
|
<file>ui/customviews/WeatherView.qml</file>
|
||||||
<file>ui/customviews/MediaControllerView.qml</file>
|
|
||||||
<file>ui/customviews/NotificationsView.qml</file>
|
<file>ui/customviews/NotificationsView.qml</file>
|
||||||
<file>ui/customviews/ExtendedVolumeController.qml</file>
|
<file>ui/devicepages/MediaThingPage.qml</file>
|
||||||
<file>ui/devicepages/MediaDevicePage.qml</file>
|
|
||||||
<file>ui/devicepages/ButtonDevicePage.qml</file>
|
<file>ui/devicepages/ButtonDevicePage.qml</file>
|
||||||
<file>ui/devicepages/GenericDevicePage.qml</file>
|
<file>ui/devicepages/GenericDevicePage.qml</file>
|
||||||
<file>ui/devicepages/WeatherDevicePage.qml</file>
|
<file>ui/devicepages/WeatherDevicePage.qml</file>
|
||||||
@ -77,7 +75,6 @@
|
|||||||
<file>ui/devicelistpages/SensorsDeviceListPage.qml</file>
|
<file>ui/devicelistpages/SensorsDeviceListPage.qml</file>
|
||||||
<file>ui/devicelistpages/WeatherDeviceListPage.qml</file>
|
<file>ui/devicelistpages/WeatherDeviceListPage.qml</file>
|
||||||
<file>ui/devicelistpages/DeviceListPageBase.qml</file>
|
<file>ui/devicelistpages/DeviceListPageBase.qml</file>
|
||||||
<file>ui/magic/SelectActionPage.qml</file>
|
|
||||||
<file>ui/magic/DeviceRulesPage.qml</file>
|
<file>ui/magic/DeviceRulesPage.qml</file>
|
||||||
<file>ui/magic/EditRulePage.qml</file>
|
<file>ui/magic/EditRulePage.qml</file>
|
||||||
<file>ui/magic/SelectThingPage.qml</file>
|
<file>ui/magic/SelectThingPage.qml</file>
|
||||||
@ -224,5 +221,6 @@
|
|||||||
<file>ui/components/SetupStatusIcon.qml</file>
|
<file>ui/components/SetupStatusIcon.qml</file>
|
||||||
<file>ui/components/UpdateStatusIcon.qml</file>
|
<file>ui/components/UpdateStatusIcon.qml</file>
|
||||||
<file>ui/magic/WriteNfcTagPage.qml</file>
|
<file>ui/magic/WriteNfcTagPage.qml</file>
|
||||||
|
<file>ui/components/MediaPlayer.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -45,7 +45,9 @@ Page {
|
|||||||
|
|
||||||
header: FancyHeader {
|
header: FancyHeader {
|
||||||
id: mainHeader
|
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
|
leftButtonVisible: true
|
||||||
leftButtonImageSource: {
|
leftButtonImageSource: {
|
||||||
if (app.hasOwnProperty("headerIcon")) {
|
if (app.hasOwnProperty("headerIcon")) {
|
||||||
@ -288,13 +290,13 @@ Page {
|
|||||||
}
|
}
|
||||||
footer: Item {
|
footer: Item {
|
||||||
readonly property bool shown: tabsRepeater.count > 1 || mainHeader.menuOpen || d.configOverlay
|
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 }}
|
Behavior on implicitHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }}
|
||||||
|
|
||||||
TabBar {
|
TabBar {
|
||||||
id: tabBar
|
id: tabBar
|
||||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
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
|
Material.elevation: 3
|
||||||
position: TabBar.Footer
|
position: TabBar.Footer
|
||||||
|
|
||||||
|
|||||||
@ -59,7 +59,7 @@ ApplicationWindow {
|
|||||||
property int smallFont: 13
|
property int smallFont: 13
|
||||||
property int mediumFont: 16
|
property int mediumFont: 16
|
||||||
property int largeFont: 20
|
property int largeFont: 20
|
||||||
property int iconSize: 30
|
property int iconSize: 24
|
||||||
property int delegateHeight: 60
|
property int delegateHeight: 60
|
||||||
property color backgroundColor: Material.background
|
property color backgroundColor: Material.background
|
||||||
|
|
||||||
@ -259,7 +259,6 @@ ApplicationWindow {
|
|||||||
return Qt.resolvedUrl("images/sensors/windspeed.svg")
|
return Qt.resolvedUrl("images/sensors/windspeed.svg")
|
||||||
case "media":
|
case "media":
|
||||||
case "mediacontroller":
|
case "mediacontroller":
|
||||||
case "extendedmediacontroller":
|
|
||||||
case "mediaplayer":
|
case "mediaplayer":
|
||||||
return Qt.resolvedUrl("images/media.svg")
|
return Qt.resolvedUrl("images/media.svg")
|
||||||
case "powersocket":
|
case "powersocket":
|
||||||
@ -337,7 +336,6 @@ ApplicationWindow {
|
|||||||
case "extendednavigationpad":
|
case "extendednavigationpad":
|
||||||
return Qt.resolvedUrl("images/navigationpad.svg")
|
return Qt.resolvedUrl("images/navigationpad.svg")
|
||||||
case "volumecontroller":
|
case "volumecontroller":
|
||||||
case "extendedvolumecontroller":
|
|
||||||
return Qt.resolvedUrl("images/audio-speakers-symbolic.svg")
|
return Qt.resolvedUrl("images/audio-speakers-symbolic.svg")
|
||||||
case "shufflerepeat":
|
case "shufflerepeat":
|
||||||
return Qt.resolvedUrl("images/media-playlist-shuffle.svg")
|
return Qt.resolvedUrl("images/media-playlist-shuffle.svg")
|
||||||
|
|||||||
@ -150,8 +150,8 @@ ToolBar {
|
|||||||
id: menuRepeater
|
id: menuRepeater
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
height: app.iconSize * 3
|
height: 80
|
||||||
width: app.iconSize * 3
|
width: height
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
menuOpen = false
|
menuOpen = false
|
||||||
|
|||||||
@ -92,7 +92,7 @@ Item {
|
|||||||
ColorIcon {
|
ColorIcon {
|
||||||
id: colorIcon
|
id: colorIcon
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
height: app.iconSize * 1.3
|
height: app.iconSize * 1.5
|
||||||
width: height
|
width: height
|
||||||
ColorIcon {
|
ColorIcon {
|
||||||
id: fallbackIcon
|
id: fallbackIcon
|
||||||
|
|||||||
@ -43,4 +43,6 @@ MouseArea {
|
|||||||
preventStealing: true
|
preventStealing: true
|
||||||
onWheel: wheel.accepted = 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 StateType playerTypeStateType: thing ? thing.thingClass.stateTypes.findByName("playerType") : null
|
||||||
readonly property State playerTypeState: playerTypeStateType ? thing.states.getState(playerTypeStateType.id) : null
|
readonly property State playerTypeState: playerTypeStateType ? thing.states.getState(playerTypeStateType.id) : null
|
||||||
|
|
||||||
Pane {
|
readonly property int paintedWidth: fallbackImage.visible ? fallbackImage.width : artworkImage.paintedWidth
|
||||||
Material.elevation: 2
|
readonly property int paintedHeight: fallbackImage.visible ? fallbackImage.height : artworkImage.paintedHeight
|
||||||
anchors.centerIn: parent
|
|
||||||
height: fallback.visible ? Math.min(parent.height, parent.width) : artworkImage.paintedHeight - 1
|
Rectangle {
|
||||||
width: fallback.visible ? Math.min(parent.height, parent.width) : artworkImage.paintedWidth - 1
|
id: fallbackImage
|
||||||
padding: 0
|
anchors { left: parent.left; top: parent.top }
|
||||||
contentItem: Rectangle {
|
height: visible ? Math.min(parent.height, parent.width) : artworkImage.paintedHeight - 1
|
||||||
color: "black"
|
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
|
fillMode: Image.PreserveAspectFit
|
||||||
source: root.artworkState.value
|
source: root.artworkState.value
|
||||||
visible: source !== ""
|
visible: source !== ""
|
||||||
}
|
horizontalAlignment: Image.AlignLeft
|
||||||
|
verticalAlignment: Image.AlignTop
|
||||||
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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,11 +42,46 @@ Item {
|
|||||||
|
|
||||||
property Thing thing: null
|
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) {
|
if (internalPageStack.depth > 1) {
|
||||||
internalPageStack.pop();
|
pathModel.remove(pathModel.count - 1)
|
||||||
|
internalPageStack.pop(immediate ? StackView.Immediate : StackView.PopTransition);
|
||||||
} else {
|
} 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: {
|
onClicked: {
|
||||||
print("clicked:", model.id)
|
print("clicked:", model.id)
|
||||||
if (model.executable) {
|
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) {
|
} else if (model.browsable) {
|
||||||
|
pathModel.append({modelData: model.displayName})
|
||||||
internalPageStack.push(internalBrowserPage, {nodeId: model.id})
|
internalPageStack.push(internalBrowserPage, {nodeId: model.id})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,12 +36,15 @@ import Nymea 1.0
|
|||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: root
|
id: root
|
||||||
implicitHeight: iconSize + app.margins
|
implicitHeight: app.iconSize * (showExtendedControls ? 2 : 1) + app.margins
|
||||||
|
|
||||||
property Thing thing: null
|
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 playbackState: thing.stateByName("playbackStatus")
|
||||||
|
readonly property State shuffleState: thing.stateByName("shuffle")
|
||||||
|
readonly property State repeatState: thing.stateByName("repeat")
|
||||||
|
|
||||||
function executeAction(actionName, params) {
|
function executeAction(actionName, params) {
|
||||||
if (params === undefined) {
|
if (params === undefined) {
|
||||||
@ -51,12 +54,31 @@ RowLayout {
|
|||||||
engine.thingManager.executeAction(thing.id, actionTypeId, params)
|
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 }
|
Item { Layout.fillWidth: true }
|
||||||
ProgressButton {
|
ProgressButton {
|
||||||
Layout.preferredHeight: root.iconSize * .6
|
Layout.preferredHeight: app.iconSize * (root.showExtendedControls ? 1.5 : 1)
|
||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: height
|
||||||
imageSource: "../images/media-skip-backward.svg"
|
imageSource: "../images/media-skip-backward.svg"
|
||||||
longpressImageSource: "../images/media-seek-backward.svg"
|
longpressImageSource: "../images/media-seek-backward.svg"
|
||||||
|
longpressEnabled: root.thing.thingClass.actionTypes.findByName("fastRewind") !== null
|
||||||
enabled: root.playbackState && root.playbackState.value !== "Stopped"
|
enabled: root.playbackState && root.playbackState.value !== "Stopped"
|
||||||
opacity: enabled ? 1 : .5
|
opacity: enabled ? 1 : .5
|
||||||
|
|
||||||
@ -70,7 +92,7 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
ProgressButton {
|
ProgressButton {
|
||||||
Layout.preferredHeight: root.iconSize
|
Layout.preferredHeight: app.iconSize * (root.showExtendedControls ? 2 : 1)
|
||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: height
|
||||||
imageSource: root.playbackState && root.playbackState.value === "Playing" ? "../images/media-playback-pause.svg" : "../images/media-playback-start.svg"
|
imageSource: root.playbackState && root.playbackState.value === "Playing" ? "../images/media-playback-pause.svg" : "../images/media-playback-start.svg"
|
||||||
longpressImageSource: "../images/media-playback-stop.svg"
|
longpressImageSource: "../images/media-playback-stop.svg"
|
||||||
@ -90,10 +112,11 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
ProgressButton {
|
ProgressButton {
|
||||||
Layout.preferredHeight: root.iconSize * .6
|
Layout.preferredHeight: app.iconSize * (root.showExtendedControls ? 1.5 : 1)
|
||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: height
|
||||||
imageSource: "../images/media-skip-forward.svg"
|
imageSource: "../images/media-skip-forward.svg"
|
||||||
longpressImageSource: "../images/media-seek-forward.svg"
|
longpressImageSource: "../images/media-seek-forward.svg"
|
||||||
|
longpressEnabled: root.thing.thingClass.actionTypes.findByName("fastForward") !== null
|
||||||
enabled: root.playbackState && root.playbackState.value !== "Stopped"
|
enabled: root.playbackState && root.playbackState.value !== "Stopped"
|
||||||
opacity: enabled ? 1 : .5
|
opacity: enabled ? 1 : .5
|
||||||
repeat: true
|
repeat: true
|
||||||
@ -105,4 +128,24 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item { Layout.fillWidth: true }
|
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 backButtonVisible: backButton.visible
|
||||||
property alias menuButtonVisible: menuButton.visible
|
property alias menuButtonVisible: menuButton.visible
|
||||||
default property alias children: layout.data
|
default property alias children: layout.data
|
||||||
|
property alias elide: label.elide
|
||||||
|
|
||||||
signal backPressed();
|
signal backPressed();
|
||||||
signal menuPressed();
|
signal menuPressed();
|
||||||
|
|||||||
@ -33,10 +33,14 @@ import QtQuick.Layouts 1.3
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
implicitHeight: app.iconSize
|
||||||
|
implicitWidth: app.iconSize
|
||||||
|
|
||||||
property string imageSource
|
property string imageSource
|
||||||
property string longpressImageSource: imageSource
|
property string longpressImageSource: imageSource
|
||||||
property bool repeat: false
|
property bool repeat: false
|
||||||
|
property alias keyColor: icon.keyColor
|
||||||
|
property alias color: icon.color
|
||||||
|
|
||||||
property bool longpressEnabled: true
|
property bool longpressEnabled: true
|
||||||
|
|
||||||
@ -46,6 +50,7 @@ Item {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
id: buttonDelegate
|
id: buttonDelegate
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
property bool longpressed: false
|
property bool longpressed: false
|
||||||
|
|
||||||
@ -94,7 +99,7 @@ Item {
|
|||||||
anchors.margins: -app.margins / 2
|
anchors.margins: -app.margins / 2
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
color: app.foregroundColor
|
color: app.foregroundColor
|
||||||
opacity: buttonDelegate.pressed ? .08 : 0
|
opacity: buttonDelegate.pressed || buttonDelegate.containsMouse ? .08 : 0
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation { duration: 200 }
|
NumberAnimation { duration: 200 }
|
||||||
}
|
}
|
||||||
@ -144,6 +149,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ColorIcon {
|
ColorIcon {
|
||||||
|
id: icon
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
name: buttonDelegate.longpressed ? root.longpressImageSource : root.imageSource
|
name: buttonDelegate.longpressed ? root.longpressImageSource : root.imageSource
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,7 +93,9 @@ RowLayout {
|
|||||||
HeaderButton {
|
HeaderButton {
|
||||||
id: volumeButton
|
id: volumeButton
|
||||||
anchors.centerIn: parent
|
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: {
|
onClicked: {
|
||||||
print(volumeButton.x, volumeButton.y)
|
print(volumeButton.x, volumeButton.y)
|
||||||
print(Qt.point(volumeButton.x, volumeButton.y))
|
print(Qt.point(volumeButton.x, volumeButton.y))
|
||||||
@ -125,8 +127,20 @@ RowLayout {
|
|||||||
property int pendingVolumeValue: -1
|
property int pendingVolumeValue: -1
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
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 {
|
ThrottledSlider {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
visible: root.volumeState !== null
|
||||||
from: 0
|
from: 0
|
||||||
to: 100
|
to: 100
|
||||||
value: root.volumeState.value
|
value: root.volumeState.value
|
||||||
@ -135,6 +149,7 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HeaderButton {
|
HeaderButton {
|
||||||
|
visible: root.muteState !== null
|
||||||
imageSource: "../images/audio-speakers-muted-symbolic.svg"
|
imageSource: "../images/audio-speakers-muted-symbolic.svg"
|
||||||
color: root.muteState.value === true ? app.accentColor : keyColor
|
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}]);
|
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 QtQuick.Layouts 1.3
|
||||||
import Nymea 1.0
|
import Nymea 1.0
|
||||||
|
|
||||||
Row {
|
RowLayout {
|
||||||
id: root
|
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 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 var openState: device ? device.states.getState(deviceClass.stateTypes.findByName("state").id) : null
|
||||||
readonly property bool canStop: device && device.deviceClass.actionTypes.findByName("stop")
|
readonly property bool canStop: device && device.deviceClass.actionTypes.findByName("stop")
|
||||||
@ -46,54 +47,41 @@ Row {
|
|||||||
|
|
||||||
signal activated(string button);
|
signal activated(string button);
|
||||||
|
|
||||||
ItemDelegate {
|
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||||
width: app.iconSize * 2
|
|
||||||
height: width
|
|
||||||
|
|
||||||
ColorIcon {
|
ProgressButton {
|
||||||
anchors.fill: parent
|
longpressEnabled: false
|
||||||
anchors.margins: app.margins
|
imageSource: root.invert ? "../images/down.svg" : "../images/up.svg"
|
||||||
name: root.invert ? "../images/down.svg" : "../images/up.svg"
|
color: root.openState && root.openState.value === "opening" ? Material.accent : keyColor
|
||||||
color: root.openState && root.openState.value === "opening" ? Material.accent : keyColor
|
|
||||||
}
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
engine.deviceManager.executeAction(root.device.id, root.deviceClass.actionTypes.findByName("open").id)
|
engine.deviceManager.executeAction(root.device.id, root.deviceClass.actionTypes.findByName("open").id)
|
||||||
root.activated("open")
|
root.activated("open")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||||
|
|
||||||
ItemDelegate {
|
ProgressButton {
|
||||||
width: app.iconSize * 2
|
|
||||||
height: width
|
|
||||||
visible: root.canStop
|
visible: root.canStop
|
||||||
// color: Material.foreground
|
longpressEnabled: false
|
||||||
// radius: height / 2
|
imageSource: "../images/media-playback-stop.svg"
|
||||||
|
|
||||||
ColorIcon {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: app.margins
|
|
||||||
name: "../images/media-playback-stop.svg"
|
|
||||||
}
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
engine.deviceManager.executeAction(root.device.id, root.deviceClass.actionTypes.findByName("stop").id)
|
engine.deviceManager.executeAction(root.device.id, root.deviceClass.actionTypes.findByName("stop").id)
|
||||||
root.activated("stop")
|
root.activated("stop")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemDelegate {
|
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||||
width: app.iconSize * 2
|
|
||||||
height: width
|
|
||||||
|
|
||||||
ColorIcon {
|
ProgressButton {
|
||||||
anchors.fill: parent
|
imageSource: root.invert ? "../images/up.svg" : "../images/down.svg"
|
||||||
anchors.margins: app.margins
|
longpressEnabled: false
|
||||||
name: root.invert ? "../images/up.svg" : "../images/down.svg"
|
color: root.openState && root.openState.value === "closing" ? Material.accent : keyColor
|
||||||
color: root.openState && root.openState.value === "closing" ? Material.accent : keyColor
|
|
||||||
}
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
engine.deviceManager.executeAction(root.device.id, root.deviceClass.actionTypes.findByName("close").id)
|
engine.deviceManager.executeAction(root.device.id, root.deviceClass.actionTypes.findByName("close").id)
|
||||||
root.activated("close")
|
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 "extendedsmartmeterproducer":
|
||||||
case "heating":
|
case "heating":
|
||||||
return sensorComponent;
|
return sensorComponent;
|
||||||
// return labelComponent;
|
// return labelComponent;
|
||||||
|
|
||||||
case "light":
|
case "light":
|
||||||
case "garagedoor":
|
case "garagedoor":
|
||||||
@ -247,7 +247,6 @@ MainPageTile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MediaControls {
|
MediaControls {
|
||||||
iconSize: app.iconSize * 1.2
|
|
||||||
thing: inlineMediaControl.currentDevice
|
thing: inlineMediaControl.currentDevice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,8 +254,11 @@ MainPageTile {
|
|||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: buttonComponent
|
id: buttonComponent
|
||||||
ColumnLayout {
|
|
||||||
spacing: 0
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Item { Layout.fillWidth: true; Layout.fillHeight: true }
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: label
|
id: label
|
||||||
@ -281,22 +283,6 @@ MainPageTile {
|
|||||||
}
|
}
|
||||||
return count === 0 ? qsTr("All off") : qsTr("%1 on").arg(count)
|
return count === 0 ? qsTr("All off") : qsTr("%1 on").arg(count)
|
||||||
case "garagedoor":
|
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 "blind":
|
||||||
case "extendedblind":
|
case "extendedblind":
|
||||||
case "awning":
|
case "awning":
|
||||||
@ -311,307 +297,282 @@ MainPageTile {
|
|||||||
font.pixelSize: app.smallFont
|
font.pixelSize: app.smallFont
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
RowLayout {
|
|
||||||
// Layout.alignment: Qt.AlignRight
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: (parent.width - app.iconSize * 3) / 2
|
|
||||||
|
|
||||||
ItemDelegate {
|
ProgressButton {
|
||||||
Layout.preferredHeight: app.iconSize
|
longpressEnabled: false
|
||||||
Layout.preferredWidth: height
|
visible: imageSource.length > 0
|
||||||
|
imageSource: {
|
||||||
ColorIcon {
|
switch (iface.name) {
|
||||||
id: leftIcon
|
case "media":
|
||||||
width: app.iconSize
|
case "light":
|
||||||
height: width
|
case "irrigation":
|
||||||
color: app.accentColor
|
case "ventilation":
|
||||||
|
return ""
|
||||||
name: {
|
case "garagedoor":
|
||||||
switch (iface.name) {
|
var dev = devicesProxy.get(0)
|
||||||
case "media":
|
if (dev.thingClass.interfaces.indexOf("simplegaragedoor") >= 0
|
||||||
case "light":
|
|| dev.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
|
||||||
case "irrigation":
|
|| dev.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
|
||||||
case "ventilation":
|
|| dev.thingClass.interfaces.indexOf("garagegate") >= 0) {
|
||||||
return ""
|
return "../images/up.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/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)
|
|
||||||
}
|
}
|
||||||
|
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 {
|
onClicked: {
|
||||||
Layout.preferredHeight: app.iconSize
|
switch (iface.name) {
|
||||||
Layout.preferredWidth: height
|
case "light":
|
||||||
enabled: centerIcon.name.length > 0
|
case "media":
|
||||||
ColorIcon {
|
case "irrigation":
|
||||||
id: centerIcon
|
case "ventilation":
|
||||||
width: app.iconSize
|
break;
|
||||||
height: width
|
case "garagedoor":
|
||||||
color: app.accentColor
|
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: {
|
var actionType = thing.thingClass.actionTypes.findByName("open");
|
||||||
switch (iface.name) {
|
engine.deviceManager.executeAction(thing.id, actionType.id)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
case "shutter":
|
||||||
onClicked: {
|
case "extendedshutter":
|
||||||
switch (iface.name) {
|
case "blind":
|
||||||
case "light":
|
case "extendedblind":
|
||||||
case "powersocket":
|
case "awning":
|
||||||
case "irrigation":
|
case "extendedawning":
|
||||||
case "ventilation":
|
case "simpleclosable":
|
||||||
var allOff = true;
|
for (var i = 0; i < devicesProxy.count; i++) {
|
||||||
for (var i = 0; i < devicesProxy.count; i++) {
|
var device = devicesProxy.get(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 deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
||||||
var stateType = deviceClass.stateTypes.findByName("playbackStatus");
|
var actionType = deviceClass.actionTypes.findByName("open");
|
||||||
var state = device.states.getState(stateType.id)
|
engine.deviceManager.executeAction(device.id, actionType.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)
|
|
||||||
}
|
}
|
||||||
|
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 {
|
Component {
|
||||||
@ -728,7 +689,7 @@ MainPageTile {
|
|||||||
text: sensorsRoot.shownStateType
|
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)
|
? (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
|
Layout.fillWidth: true
|
||||||
visible: sensorsRoot.shownStateType && sensorsRoot.shownStateType.type.toLowerCase() !== "bool"
|
visible: sensorsRoot.shownStateType && sensorsRoot.shownStateType.type.toLowerCase() !== "bool"
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
|||||||
@ -208,16 +208,11 @@ MainPageTile {
|
|||||||
currentStateIndex = 0
|
currentStateIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemDelegate {
|
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||||
Layout.preferredWidth: app.iconSize
|
ProgressButton {
|
||||||
Layout.preferredHeight: width
|
|
||||||
Layout.leftMargin: app.margins / 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
padding: 0; topPadding: 0; bottomPadding: 0
|
|
||||||
visible: sensorsRoot.shownInterfaces.length > 1
|
visible: sensorsRoot.shownInterfaces.length > 1
|
||||||
contentItem: ColorIcon {
|
longpressEnabled: false
|
||||||
name: "../images/back.svg"
|
imageSource: "../images/back.svg"
|
||||||
}
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var newIndex = sensorsRoot.currentStateIndex - 1;
|
var newIndex = sensorsRoot.currentStateIndex - 1;
|
||||||
if (newIndex < 0) newIndex = sensorsRoot.shownInterfaces.length - 1
|
if (newIndex < 0) newIndex = sensorsRoot.shownInterfaces.length - 1
|
||||||
@ -236,7 +231,7 @@ MainPageTile {
|
|||||||
|
|
||||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: false
|
||||||
spacing: 0
|
spacing: 0
|
||||||
visible: sensorsRoot.currentStateType.type.toLowerCase() !== "bool"
|
visible: sensorsRoot.currentStateType.type.toLowerCase() !== "bool"
|
||||||
|
|
||||||
@ -260,151 +255,34 @@ MainPageTile {
|
|||||||
|
|
||||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||||
|
|
||||||
ItemDelegate {
|
ProgressButton {
|
||||||
Layout.preferredWidth: app.iconSize
|
|
||||||
Layout.preferredHeight: width
|
|
||||||
Layout.rightMargin: app.margins / 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
padding: 0; topPadding: 0; bottomPadding: 0
|
|
||||||
visible: sensorsRoot.shownInterfaces.length > 1
|
visible: sensorsRoot.shownInterfaces.length > 1
|
||||||
contentItem: ColorIcon {
|
longpressEnabled: false
|
||||||
name: "../images/next.svg"
|
imageSource: "../images/next.svg"
|
||||||
}
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var newIndex = sensorsRoot.currentStateIndex + 1;
|
var newIndex = sensorsRoot.currentStateIndex + 1;
|
||||||
if (newIndex >= sensorsRoot.shownInterfaces.length) newIndex = 0;
|
if (newIndex >= sensorsRoot.shownInterfaces.length) newIndex = 0;
|
||||||
sensorsRoot.currentStateIndex = newIndex;
|
sensorsRoot.currentStateIndex = newIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: closableComponent
|
id: closableComponent
|
||||||
RowLayout {
|
|
||||||
property var device: null
|
|
||||||
property var deviceClass: null
|
|
||||||
|
|
||||||
ItemDelegate {
|
ShutterControls {
|
||||||
Layout.preferredWidth: app.iconSize
|
|
||||||
Layout.preferredHeight: width
|
|
||||||
Layout.leftMargin: app.margins / 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
padding: 0; topPadding: 0; bottomPadding: 0
|
|
||||||
contentItem: ColorIcon {
|
|
||||||
name: "../images/up.svg"
|
|
||||||
color: app.accentColor
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
|
||||||
var actionType = deviceClass.actionTypes.findByName("open");
|
|
||||||
engine.deviceManager.executeAction(device.id, actionType.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Slider {
|
|
||||||
id: closableSlider
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
visible: deviceClass.interfaces.indexOf("extendedclosable") >= 0
|
|
||||||
readonly property var percentageStateType: deviceClass.stateTypes.findByName("percentage");
|
|
||||||
readonly property var percentateState: percentageStateType ? device.states.getState(percentageStateType.id) : null
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
value: percentateState ? percentateState.value : 0
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
visible: !closableSlider.visible
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemDelegate {
|
|
||||||
Layout.preferredWidth: app.iconSize
|
|
||||||
Layout.preferredHeight: width
|
|
||||||
Layout.rightMargin: app.margins / 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
padding: 0; topPadding: 0; bottomPadding: 0
|
|
||||||
contentItem: ColorIcon {
|
|
||||||
name: "../images/down.svg"
|
|
||||||
color: app.accentColor
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
|
|
||||||
var actionType = deviceClass.actionTypes.findByName("close");
|
|
||||||
engine.deviceManager.executeAction(device.id, actionType.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: mediaComponent
|
id: mediaComponent
|
||||||
RowLayout {
|
|
||||||
id: mediaRoot
|
|
||||||
|
|
||||||
|
MediaControls {
|
||||||
property Device device: null
|
property Device device: null
|
||||||
property DeviceClass deviceClass: null
|
thing: device
|
||||||
|
|
||||||
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 }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,11 +73,11 @@ Page {
|
|||||||
if (commandId === d.pendingBrowserItemId) {
|
if (commandId === d.pendingBrowserItemId) {
|
||||||
d.pendingBrowserItemId = -1;
|
d.pendingBrowserItemId = -1;
|
||||||
d.pendingItemId = ""
|
d.pendingItemId = ""
|
||||||
if (params.deviceError !== "DeviceErrorNoError") {
|
if (params.thinggError !== "ThingErrorNoError") {
|
||||||
if (params.displayMessage.length > 0) {
|
if (params.displayMessage.length > 0) {
|
||||||
header.showInfo(qsTr("Error: %1").arg(params.displayMessage), true)
|
header.showInfo(qsTr("Error: %1").arg(params.displayMessage), true)
|
||||||
} else {
|
} 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 StateType intermediatePositionStateType: thing.thingClass.stateTypes.findByName("intermediatePosition")
|
||||||
readonly property var intermediatePositionState: intermediatePositionStateType ? device.states.getState(intermediatePositionStateType.id) : null
|
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: {
|
Component.onCompleted: {
|
||||||
print("Creating garage page. Impulse based:", isImpulseBased, "stateful:", isStateful, "extended:", isExtended, "legacy:", intermediatePositionState !== null)
|
print("Creating garage page. Impulse based:", isImpulseBased, "stateful:", isStateful, "extended:", isExtended, "legacy:", intermediatePositionState !== null)
|
||||||
}
|
}
|
||||||
@ -140,19 +136,14 @@ DevicePageBase {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: app.margins * 2
|
Layout.margins: app.margins * 2
|
||||||
Layout.fillHeight: true
|
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
|
property int minimumHeight: app.iconSize * 2.5
|
||||||
|
|
||||||
ItemDelegate {
|
ProgressButton {
|
||||||
height: app.iconSize * 2
|
|
||||||
width: height
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: root.isImpulseBased
|
visible: root.isImpulseBased
|
||||||
ColorIcon {
|
longpressEnabled: false
|
||||||
anchors.fill: parent
|
imageSource: "../images/closable-move.svg"
|
||||||
name: "../images/closable-move.svg"
|
|
||||||
anchors.margins: app.margins
|
|
||||||
}
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var actionTypeId = root.thing.thingClass.actionTypes.findByName("triggerImpulse").id
|
var actionTypeId = root.thing.thingClass.actionTypes.findByName("triggerImpulse").id
|
||||||
print("Triggering impulse", actionTypeId)
|
print("Triggering impulse", actionTypeId)
|
||||||
@ -163,31 +154,10 @@ DevicePageBase {
|
|||||||
ShutterControls {
|
ShutterControls {
|
||||||
id: shutterControls
|
id: shutterControls
|
||||||
device: root.device
|
device: root.device
|
||||||
|
width: parent.width
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: (parent.width - app.iconSize*2*children.length) / (children.length - 1)
|
spacing: (parent.width - app.iconSize*2*children.length) / (children.length - 1)
|
||||||
visible: !root.isImpulseBased
|
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -475,7 +475,6 @@ MainViewBase {
|
|||||||
id: mediaControllerDelegate
|
id: mediaControllerDelegate
|
||||||
MediaControls {
|
MediaControls {
|
||||||
property var devices: null
|
property var devices: null
|
||||||
iconSize: app.iconSize
|
|
||||||
DevicesProxy {
|
DevicesProxy {
|
||||||
id: mediaControllers
|
id: mediaControllers
|
||||||
engine: _engine
|
engine: _engine
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
import QtQuick 2.8
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.1
|
||||||
import QtQuick.Controls.Material 2.1
|
import QtQuick.Controls.Material 2.1
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
@ -40,6 +40,8 @@ import "../delegates"
|
|||||||
MainViewBase {
|
MainViewBase {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
title: swipeView.currentItem ? swipeView.currentItem.thing.name : ""
|
||||||
|
|
||||||
ThingsProxy {
|
ThingsProxy {
|
||||||
id: mediaDevices
|
id: mediaDevices
|
||||||
engine: _engine
|
engine: _engine
|
||||||
@ -48,125 +50,20 @@ MainViewBase {
|
|||||||
|
|
||||||
SwipeView {
|
SwipeView {
|
||||||
id: swipeView
|
id: swipeView
|
||||||
anchors.fill: parent
|
anchors { left: parent.left; top: parent.top; right: parent.right; bottom: parent.bottom }
|
||||||
currentIndex: pageIndicator.currentIndex
|
currentIndex: pageIndicator.currentIndex
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: mediaDevices
|
model: mediaDevices
|
||||||
delegate: Item {
|
delegate: MediaPlayer {
|
||||||
id: playerDelegate
|
thing: mediaDevices.get(index)
|
||||||
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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PageIndicator {
|
PageIndicator {
|
||||||
id: pageIndicator
|
id: pageIndicator
|
||||||
count: swipeView.count
|
count: swipeView.count
|
||||||
|
visible: count > 1
|
||||||
currentIndex: swipeView.currentIndex
|
currentIndex: swipeView.currentIndex
|
||||||
interactive: true
|
interactive: true
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
@ -183,5 +80,4 @@ MainViewBase {
|
|||||||
buttonText: qsTr("Add things")
|
buttonText: qsTr("Add things")
|
||||||
onButtonClicked: pageStack.push(Qt.resolvedUrl("../thingconfiguration/NewThingPage.qml"))
|
onButtonClicked: pageStack.push(Qt.resolvedUrl("../thingconfiguration/NewThingPage.qml"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ Item {
|
|||||||
print("**** getting page for interfaces", interfaceList)
|
print("**** getting page for interfaces", interfaceList)
|
||||||
var page;
|
var page;
|
||||||
if (interfaceList.indexOf("media") >= 0) {
|
if (interfaceList.indexOf("media") >= 0) {
|
||||||
page = "MediaDevicePage.qml";
|
page = "MediaThingPage.qml";
|
||||||
} else if (interfaceList.indexOf("button") >= 0) {
|
} else if (interfaceList.indexOf("button") >= 0) {
|
||||||
page = "ButtonDevicePage.qml";
|
page = "ButtonDevicePage.qml";
|
||||||
} else if (interfaceList.indexOf("powerswitch") >= 0) {
|
} else if (interfaceList.indexOf("powerswitch") >= 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user