Merge PR #398: Improve garage door views

This commit is contained in:
Jenkins nymea 2020-08-03 15:24:15 +02:00
commit dc83ff345f
18 changed files with 409 additions and 72 deletions

View File

@ -263,7 +263,7 @@ void DeviceManager::getVendorsResponse(const QVariantMap &params)
void DeviceManager::getSupportedDevicesResponse(const QVariantMap &params)
{
// qDebug() << "DeviceClass received:" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
qDebug() << "DeviceClass received:" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
if (params.value("params").toMap().keys().contains("deviceClasses")) {
QVariantList deviceClassList = params.value("params").toMap().value("deviceClasses").toList();
foreach (QVariant deviceClassVariant, deviceClassList) {

View File

@ -42,7 +42,7 @@
Engine::Engine(QObject *parent) :
QObject(parent),
m_jsonRpcClient(new JsonRpcClient(this)),
m_deviceManager(new DeviceManager(m_jsonRpcClient, this)),
m_thingManager(new DeviceManager(m_jsonRpcClient, this)),
m_ruleManager(new RuleManager(m_jsonRpcClient, this)),
m_scriptManager(new ScriptManager(m_jsonRpcClient, this)),
m_logManager(new LogManager(m_jsonRpcClient, this)),
@ -53,7 +53,7 @@ Engine::Engine(QObject *parent) :
connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, &Engine::onConnectedChanged);
connect(m_deviceManager, &DeviceManager::fetchingDataChanged, this, &Engine::onDeviceManagerFetchingChanged);
connect(m_thingManager, &DeviceManager::fetchingDataChanged, this, &Engine::onDeviceManagerFetchingChanged);
connect(AWSClient::instance(), &AWSClient::devicesFetched, this, [this]() {
if (m_jsonRpcClient->connected() && m_jsonRpcClient->cloudConnectionState() == JsonRpcClient::CloudConnectionStateConnected) {
@ -74,7 +74,12 @@ Engine::Engine(QObject *parent) :
DeviceManager *Engine::deviceManager() const
{
return m_deviceManager;
return m_thingManager;
}
DeviceManager *Engine::thingManager() const
{
return m_thingManager;
}
RuleManager *Engine::ruleManager() const
@ -131,20 +136,20 @@ void Engine::deployCertificate()
void Engine::onConnectedChanged()
{
qDebug() << "Engine: connected changed:" << m_jsonRpcClient->connected();
m_deviceManager->clear();
m_thingManager->clear();
m_ruleManager->clear();
m_tagsManager->clear();
if (m_jsonRpcClient->connected()) {
qDebug() << "Engine: inital setup required:" << m_jsonRpcClient->initialSetupRequired() << "auth required:" << m_jsonRpcClient->authenticationRequired();
if (!m_jsonRpcClient->initialSetupRequired() && !m_jsonRpcClient->authenticationRequired()) {
m_deviceManager->init();
m_thingManager->init();
}
}
}
void Engine::onDeviceManagerFetchingChanged()
{
if (!m_deviceManager->fetchingData()) {
if (!m_thingManager->fetchingData()) {
m_ruleManager->init();
m_scriptManager->init();
m_nymeaConfiguration->init();

View File

@ -50,6 +50,7 @@ class Engine : public QObject
{
Q_OBJECT
Q_PROPERTY(DeviceManager* deviceManager READ deviceManager CONSTANT)
Q_PROPERTY(DeviceManager* thingManager READ thingManager CONSTANT)
Q_PROPERTY(RuleManager* ruleManager READ ruleManager CONSTANT)
Q_PROPERTY(ScriptManager* scriptManager READ scriptManager CONSTANT)
Q_PROPERTY(TagsManager* tagsManager READ tagsManager CONSTANT)
@ -64,6 +65,7 @@ public:
QString connectedHost() const;
DeviceManager *deviceManager() const;
DeviceManager *thingManager() const;
RuleManager *ruleManager() const;
ScriptManager *scriptManager() const;
TagsManager *tagsManager() const;
@ -76,7 +78,7 @@ public:
private:
JsonRpcClient *m_jsonRpcClient;
DeviceManager *m_deviceManager;
DeviceManager *m_thingManager;
RuleManager *m_ruleManager;
ScriptManager *m_scriptManager;
LogManager *m_logManager;

View File

@ -68,6 +68,11 @@ QUuid Device::deviceClassId() const
return m_deviceClass->id();
}
QUuid Device::thingClassId() const
{
return m_deviceClass->id();
}
QUuid Device::parentDeviceId() const
{
return m_parentDeviceId;
@ -153,6 +158,11 @@ DeviceClass *Device::deviceClass() const
return m_deviceClass;
}
DeviceClass *Device::thingClass() const
{
return m_deviceClass;
}
bool Device::hasState(const QUuid &stateTypeId)
{
foreach (State *state, states()->states()) {

View File

@ -46,6 +46,7 @@ class Device : public QObject
Q_OBJECT
Q_PROPERTY(QUuid id READ id CONSTANT)
Q_PROPERTY(QUuid deviceClassId READ deviceClassId CONSTANT)
Q_PROPERTY(QUuid thingClassId READ thingClassId CONSTANT)
Q_PROPERTY(QUuid parentDeviceId READ parentDeviceId CONSTANT)
Q_PROPERTY(bool isChild READ isChild CONSTANT)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
@ -55,6 +56,7 @@ class Device : public QObject
Q_PROPERTY(Params *settings READ settings NOTIFY settingsChanged)
Q_PROPERTY(States *states READ states NOTIFY statesChanged)
Q_PROPERTY(DeviceClass *deviceClass READ deviceClass CONSTANT)
Q_PROPERTY(DeviceClass *thingClass READ thingClass CONSTANT)
public:
enum DeviceSetupStatus {
@ -74,6 +76,7 @@ public:
void setName(const QString &name);
QUuid deviceClassId() const;
QUuid thingClassId() const;
QUuid parentDeviceId() const;
bool isChild() const;
@ -91,6 +94,7 @@ public:
void setStates(States *states);
DeviceClass *deviceClass() const;
DeviceClass *thingClass() const;
Q_INVOKABLE bool hasState(const QUuid &stateTypeId);

View File

@ -129,8 +129,8 @@ QString DeviceClass::baseInterface() const
if (interface == "blind") {
return "blind";
}
if (interface == "garagegate") {
return "garagegate";
if (interface == "garagedoor") {
return "garagedoor";
}
if (interface == "inputtrigger") {
return "inputtrigger";

View File

@ -199,7 +199,20 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
pts = createParamTypes("user", tr("User"), QVariant::String);
addActionType("useraccesscontrol", "removeUser", tr("Remove user"), pts);
addInterface("garagegate", tr("Garage doors"), {"closable"});
addInterface("garagedoor", tr("Garage doors"));
addInterface("impulsegaragedoor", tr("Garage doors"), {"garagedoor"});
addActionType("impulsegaragedoor", "triggerImpulse", tr("Operate"), new ParamTypes());
addInterface("simplegaragedoor", tr("Garage doors"), {"garagedoor", "closable"});
addInterface("statefulgaragedoor", tr("Garage doors"), {"garagedoor", "closable"});
addStateType("statefulgaragedoor", "state", QVariant::String, false, tr("State"), tr("State changed"));
addInterface("extendedstatfulgaragedoor", tr("Garage doors"), {"statefulgaragedoor", "extendedclosable"});
// Deprecated garagegate
addInterface("garagegate", tr("Garage doors"), {"garagedoor", "closable"});
addStateType("garagegate", "state", QVariant::String, false, tr("State"), tr("State changed"));
addStateType("garagegate", "intermediatePosition", QVariant::Bool, false, tr("Intermediate position"), tr("Intermediate position changed"));

View File

@ -131,7 +131,7 @@
<file>ui/images/settings.svg</file>
<file>ui/images/share.svg</file>
<file>ui/images/slideshow.svg</file>
<file>ui/images/sort-listitem.svg</file>
<file>ui/images/closable-move.svg</file>
<file>ui/images/starred.svg</file>
<file>ui/images/state-interface.svg</file>
<file>ui/images/state.svg</file>

View File

@ -67,7 +67,7 @@
<file>ui/devicepages/InputTriggerDevicePage.qml</file>
<file>ui/devicepages/StateLogPage.qml</file>
<file>ui/devicepages/ShutterDevicePage.qml</file>
<file>ui/devicepages/GarageGateDevicePage.qml</file>
<file>ui/devicepages/GarageThingPage.qml</file>
<file>ui/devicepages/AwningDevicePage.qml</file>
<file>ui/devicepages/NotificationsDevicePage.qml</file>
<file>ui/devicepages/LightDevicePage.qml</file>
@ -75,7 +75,7 @@
<file>ui/devicepages/DeviceLogPage.qml</file>
<file>ui/devicelistpages/GenericDeviceListPage.qml</file>
<file>ui/devicelistpages/ClosablesDeviceListPage.qml</file>
<file>ui/devicelistpages/GarageDeviceListPage.qml</file>
<file>ui/devicelistpages/GarageThingListPage.qml</file>
<file>ui/devicelistpages/AwningDeviceListPage.qml</file>
<file>ui/devicelistpages/ShutterDeviceListPage.qml</file>
<file>ui/devicelistpages/BlindDeviceListPage.qml</file>

View File

@ -110,7 +110,7 @@ ApplicationWindow {
"light",
"weather",
"media",
"garagegate",
"garagedoor",
"awning",
"shutter",
"blind",
@ -172,7 +172,7 @@ ApplicationWindow {
case "awning":
case "extendedawning":
return qsTr("Awnings");
case "garagegate":
case "garagedoor":
return qsTr("Garage doors");
case "accesscontrol":
return qsTr("Access control");
@ -209,6 +209,7 @@ ApplicationWindow {
}
function interfacesToIcon(interfaces) {
print("finding icon for interfaces:", interfaces)
for (var i = 0; i < interfaces.length; i++) {
var icon = interfaceToIcon(interfaces[i]);
if (icon !== "") {
@ -219,6 +220,7 @@ ApplicationWindow {
}
function interfaceToIcon(name) {
print("finding icon for interface:", name)
switch (name) {
case "light":
case "colorlight":
@ -279,7 +281,7 @@ ApplicationWindow {
case "blind":
case "extendedblind":
return Qt.resolvedUrl("images/shutter/shutter-060.svg")
case "garagegate":
case "garagedoor":
return Qt.resolvedUrl("images/garage/garage-100.svg")
case "awning":
case "extendedawning":
@ -289,7 +291,7 @@ ApplicationWindow {
case "uncategorized":
return Qt.resolvedUrl("images/select-none.svg")
case "simpleclosable":
return Qt.resolvedUrl("images/sort-listitem.svg")
return Qt.resolvedUrl("images/closable-move.svg")
case "fingerprintreader":
return Qt.resolvedUrl("images/fingerprint.svg")
case "accesscontrol":
@ -451,8 +453,8 @@ ApplicationWindow {
page = "SensorDevicePage.qml";
} else if (interfaceList.indexOf("inputtrigger") >= 0) {
page = "InputTriggerDevicePage.qml";
} else if (interfaceList.indexOf("garagegate") >= 0 ) {
page = "GarageGateDevicePage.qml";
} else if (interfaceList.indexOf("garagedoor") >= 0 ) {
page = "GarageThingPage.qml";
} else if (interfaceList.indexOf("light") >= 0) {
page = "LightDevicePage.qml";
} else if (interfaceList.indexOf("shutter") >= 0 || interfaceList.indexOf("blind") >= 0) {

View File

@ -1,36 +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.9
ClosablesDeviceListPage {
title: qsTr("Garage gates")
iconBasename: "../images/shutter/shutter"
}

View File

@ -0,0 +1,287 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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"
DeviceListPageBase {
id: root
property string iconBasename: "../images/garage/garage"
header: NymeaHeader {
id: header
onBackPressed: pageStack.pop()
text: root.title
}
ListView {
anchors.fill: parent
model: devicesProxy
spacing: app.margins
delegate: Pane {
id: itemDelegate
width: parent.width
property bool inline: width > 500
property Device device: devicesProxy.getDevice(model.id)
property Device thing: device
property DeviceClass deviceClass: device.deviceClass
readonly property bool isImpulseBased: device.deviceClass.interfaces.indexOf("impulsegaragedoor") >= 0
readonly property bool isStateful: device.deviceClass.interfaces.indexOf("statefulgaragedoor") >= 0
|| device.deviceClass.interfaces.indexOf("garagegate") >= 0 // garagegate did not inherit garagedoor before 0.23
readonly property bool isExtended: device.deviceClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
property var connectedStateType: deviceClass.stateTypes.findByName("connected");
property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
property StateType movingStateType: deviceClass.stateTypes.findByName("moving");
property ActionType movingActionType: deviceClass.actionTypes.findByName("moving");
property State movingState: movingStateType ? device.states.getState(movingStateType.id) : null
Material.elevation: 1
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
contentItem: ItemDelegate {
id: contentItem
implicitHeight: contentLoader.item.implicitHeight
topPadding: 0
contentItem: Loader {
id: contentLoader
enabled: itemDelegate.connectedState === null || itemDelegate.connectedState.value === true
sourceComponent: isImpulseBased ? impulseGaragedoor
: isExtended ? extendedStatefulGaragedoor
: isStateful ? garagedoor
: simpleGaragedoor
Binding { target: contentLoader.item; property: "device"; value: itemDelegate.device }
}
onClicked: {
enterPage(index, false)
}
}
}
}
Component {
id: impulseGaragedoor
RowLayout {
id: contentItem
property Device device: null
spacing: app.margins
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
ColorIcon {
id: icon
anchors.fill: parent
name: root.iconBasename + "-100.svg"
}
}
Label {
Layout.fillWidth: true
text: contentItem.device.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
ItemDelegate {
Layout.preferredHeight: app.iconSize * 2
Layout.preferredWidth: height
ColorIcon {
anchors.centerIn: parent
height: app.iconSize
width: height
name: "../images/closable-move.svg"
}
onClicked: {
var actionTypeId = device.thingClass.actionTypes.findByName("triggerImpulse").id
print("Triggering impulse", actionTypeId)
engine.thingManager.executeAction(device.id, actionTypeId)
}
}
}
}
Component {
id: simpleGaragedoor
RowLayout {
id: contentItem
spacing: app.margins
property Device device: null
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
ColorIcon {
id: icon
anchors.fill: parent
color: keyColor
name: root.iconBasename + "-100.svg"
}
}
Label {
Layout.fillWidth: true
text: contentItem.device.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Item {
Layout.preferredWidth: shutterControls.implicitWidth
Layout.preferredHeight: app.iconSize * 2
ShutterControls {
id: shutterControls
height: parent.height
device: contentItem.device
}
}
}
}
Component {
id: garagedoor
RowLayout {
id: contentItem
spacing: app.margins
property Device device: null
property StateType stateStateType: device.deviceClass.stateTypes.findByName("state")
property State stateState: stateStateType ? device.states.getState(stateStateType.id) : null
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
ColorIcon {
id: icon
anchors.fill: parent
color: ["opening", "closing"].indexOf(contentItem.stateState.value) >= 0
? app.accentColor
: keyColor
name: root.iconBasename + "-100.svg"
}
}
Label {
Layout.fillWidth: true
text: contentItem.device.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Item {
Layout.preferredWidth: shutterControls.implicitWidth
Layout.preferredHeight: app.iconSize * 2
ShutterControls {
id: shutterControls
height: parent.height
device: contentItem.device
}
}
}
}
Component {
id: extendedStatefulGaragedoor
RowLayout {
id: contentItem
spacing: app.margins
property Device device: null
property StateType stateStateType: device.deviceClass.stateTypes.findByName("state")
property State stateState: stateStateType ? device.states.getState(stateStateType.id) : null
property StateType percentageStateType: device.deviceClass.stateTypes.findByName("percentage");
property ActionType percentageActionType: device.deviceClass.actionTypes.findByName("percentage");
property State percentageState: percentageStateType ? device.states.getState(percentageStateType.id) : null
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
ColorIcon {
id: icon
anchors.fill: parent
color: ["opening", "closing"].indexOf(contentItem.stateState.value) >= 0
? app.accentColor
: keyColor
name: contentItem.percentageStateType
? root.iconBasename + "-" + app.pad(Math.round(contentItem.percentageState.value / 10) * 10, 3) + ".svg"
: root.iconBasename + "-050.svg"
}
}
Label {
Layout.fillWidth: true
text: contentItem.device.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Item {
Layout.preferredWidth: shutterControls.implicitWidth
Layout.preferredHeight: app.iconSize * 2
ShutterControls {
id: shutterControls
height: parent.height
device: contentItem.device
}
}
}
}
}

View File

@ -39,6 +39,8 @@ Page {
property Device device: null
readonly property DeviceClass deviceClass: device.deviceClass
readonly property Device thing: device
property bool showLogsButton: true
property bool showDetailsButton: true
property bool showBrowserButton: true

View File

@ -40,10 +40,31 @@ DevicePageBase {
id: root
readonly property bool landscape: width > height
readonly property var openState: device.states.getState(deviceClass.stateTypes.findByName("state").id)
readonly property var intermediatePositionState: device.states.getState(deviceClass.stateTypes.findByName("intermediatePosition").id)
readonly property var lightStateType: deviceClass.stateTypes.findByName("power")
readonly property var lightState: lightStateType ? device.states.getState(lightStateType.id) : null
readonly property bool isImpulseBased: thing.thingClass.interfaces.indexOf("impulsegaragedoor") >= 0
readonly property bool isStateful: thing.thingClass.interfaces.indexOf("statefulgaragedoor") >= 0
readonly property bool isExtended: thing.thingClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
// Stateful garagedoor
readonly property StateType stateStateType: thing.thingClass.stateTypes.findByName("state")
readonly property State stateState: stateStateType ? thing.states.getState(stateStateType.id) : null
// Extended stateful garagedoor
readonly property StateType percentageStateType: thing.thingClass.stateTypes.findByName("percentage")
readonly property State percentageState: percentageStateType ? thing.states.getState(percentageStateType.id) : null
// Backward compatiblity with old garagegate interface
readonly property StateType intermediatePositionStateType: thing.thingClass.stateTypes.findByName("intermediatePosition")
readonly property var intermediatePositionState: intermediatePositionStateType ? device.states.getState(intermediatePositionStateType.id) : null
// Some garages may also implement the light interface
readonly property var lightStateType: thing.thingClass.stateTypes.findByName("power")
readonly property var lightState: lightStateType ? thing.states.getState(lightStateType.id) : null
Component.onCompleted: {
print("Creating garage page. Impulse based:", isImpulseBased, "stateful:", isStateful, "extended:", isExtended, "legacy:", intermediatePositionState !== null)
}
GridLayout {
anchors.fill: parent
@ -56,8 +77,16 @@ DevicePageBase {
: Math.min(Math.min(parent.width, 500), parent.height - shutterControlsContainer.minimumHeight)
Layout.preferredHeight: width
Layout.alignment: Qt.AlignHCenter
property string currentImage: root.openState.value === "closed" ? "100" :
root.openState.value === "open" && root.intermediatePositionState.value === false ? "000" : "050"
property string currentImage: {
if (root.isExtended) {
return app.pad(Math.round(root.percentageState.value / 10), 2) + "0"
}
if (root.intermediatePositionStateType) {
return root.stateState.value === "closed" ? "100"
: root.intermediatePositionState.value === false ? "000" : "050"
}
return "100"
}
name: "../images/garage/garage-" + currentImage + ".svg"
Item {
@ -66,8 +95,8 @@ DevicePageBase {
width: app.iconSize * 2
height: parent.height * .6
clip: true
visible: root.openState.value === "opening" || root.openState.value === "closing"
property bool up: root.openState.value === "opening"
visible: root.stateStateType && (root.stateState.value === "opening" || root.stateState.value === "closing")
property bool up: root.stateState && root.stateState.value === "opening"
// NumberAnimation doesn't reload to/from while it's running. If we switch from closing to opening or vice versa
// we need to somehow stop and start the animation
@ -76,7 +105,7 @@ DevicePageBase {
if (!animationHack) hackTimer.start();
}
Timer { id: hackTimer; interval: 1; onTriggered: arrows.animationHack = true }
Connections { target: root.openState; onValueChanged: arrows.animationHack = false }
Connections { target: root.stateState; onValueChanged: arrows.animationHack = false }
NumberAnimation {
target: arrowColumn
@ -86,7 +115,7 @@ DevicePageBase {
from: arrows.up ? app.iconSize : -app.iconSize
to: arrows.up ? -app.iconSize : app.iconSize
loops: Animation.Infinite
running: arrows.animationHack && (root.openState.value === "opening" || root.openState.value === "closing")
running: arrows.animationHack && root.stateState && (root.stateState.value === "opening" || root.stateState.value === "closing")
}
Column {
@ -114,11 +143,29 @@ DevicePageBase {
property int minimumWidth: app.iconSize * 2.5 * (root.lightState ? 4 : 3)
property int minimumHeight: app.iconSize * 2.5
ItemDelegate {
height: app.iconSize * 2
width: height
anchors.centerIn: parent
visible: root.isImpulseBased
ColorIcon {
anchors.fill: parent
name: "../images/closable-move.svg"
anchors.margins: app.margins
}
onClicked: {
var actionTypeId = root.thing.thingClass.actionTypes.findByName("triggerImpulse").id
print("Triggering impulse", actionTypeId)
engine.thingManager.executeAction(root.thing.id, actionTypeId)
}
}
ShutterControls {
id: shutterControls
device: root.device
anchors.centerIn: parent
spacing: (parent.width - app.iconSize*2*children.length) / (children.length - 1)
visible: !root.isImpulseBased
ItemDelegate {
width: app.iconSize * 2

View File

@ -36,19 +36,19 @@ import Nymea 1.0
Item {
id: root
readonly property string title: qsTr("Garage gates")
readonly property string title: qsTr("Garage doors")
readonly property string icon: Qt.resolvedUrl("qrc:/ui/images/shutter/shutter-050.svg")
DevicesProxy {
id: garagesFilterModel
engine: _engine
shownInterfaces: ["garagegate"]
shownInterfaces: ["garagedoors"]
}
EmptyViewPlaceholder {
anchors.centerIn: parent
width: parent.width - app.margins * 2
text: qsTr("There are no garage gates set up yet.")
text: qsTr("There are no garage doors set up yet.")
imageSource: "qrc:/ui/images/shutter/shutter-050.svg"
buttonText: qsTr("Set up now")
visible: garagesFilterModel.count === 0

View File

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -70,8 +70,9 @@ MainPageTile {
case "smartmeter":
page ="SmartMeterDeviceListPage.qml";
break;
case "garagegate":
page = "GarageDeviceListPage.qml";
case "garagegate": // Deprecated, might not inherit garagedoor in old versions
case "garagedoor":
page = "GarageThingListPage.qml";
break;
case "awning":
case "extendedAwning":

View File

@ -77,7 +77,7 @@ SettingsPageBase {
Layout.fillWidth: true
text: qsTr("Qt version:")
visible: engine.jsonRpcClient.ensureServerVersion("4.1")
subText: engine.jsonRpcClient.serverQtVersion + (engine.jsonRpcClient.serverQtVersion !== engine.jsonRpcClient.serverQtBuildVersion ? + " (" + qsTr("Built with %1").arg(engine.jsonRpcClient.serverQtBuildVersion) + ")" : "")
subText: engine.jsonRpcClient.serverQtVersion + (engine.jsonRpcClient.serverQtVersion !== engine.jsonRpcClient.serverQtBuildVersion ? " (" + qsTr("Built with %1").arg(engine.jsonRpcClient.serverQtBuildVersion) + ")" : "")
progressive: false
prominentSubText: false
}