Introduce overview for states and events of things
Signed-off-by: Martin Lukas <martin.lukas@chargebyte.com>
This commit is contained in:
parent
264ffb8107
commit
a3efbe110c
@ -318,5 +318,6 @@
|
|||||||
<file>ui/mainviews/dashboard/DashboardSensorDelegate.qml</file>
|
<file>ui/mainviews/dashboard/DashboardSensorDelegate.qml</file>
|
||||||
<file>ui/devicepages/ThingStatusPage.qml</file>
|
<file>ui/devicepages/ThingStatusPage.qml</file>
|
||||||
<file>ui/customviews/MultiStateChart.qml</file>
|
<file>ui/customviews/MultiStateChart.qml</file>
|
||||||
|
<file>ui/devicepages/DeviceDetailsPage.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -13,7 +13,7 @@ InfoPaneBase {
|
|||||||
readonly property bool setupFailure: root.thing.setupStatus == Thing.ThingSetupStatusFailed
|
readonly property bool setupFailure: root.thing.setupStatus == Thing.ThingSetupStatusFailed
|
||||||
readonly property State batteryState: root.thing.stateByName("batteryLevel")
|
readonly property State batteryState: root.thing.stateByName("batteryLevel")
|
||||||
readonly property State batteryCriticalState: root.thing.stateByName("batteryCritical")
|
readonly property State batteryCriticalState: root.thing.stateByName("batteryCritical")
|
||||||
readonly property State connectedState: root.thing.thingClass.interfaces.indexOf("connectable") >= 0 && root.thing.stateByName("connected")
|
readonly property bool connectedState: root.thing.thingClass.interfaces.indexOf("connectable") >= 0 && root.thing.stateByName("connected")
|
||||||
readonly property State signalStrengthState: root.thing.stateByName("signalStrength")
|
readonly property State signalStrengthState: root.thing.stateByName("signalStrength")
|
||||||
readonly property State updateStatusState: root.thing.stateByName("updateStatus")
|
readonly property State updateStatusState: root.thing.stateByName("updateStatus")
|
||||||
readonly property State childLockState: root.thing.stateByName("childLock")
|
readonly property State childLockState: root.thing.stateByName("childLock")
|
||||||
|
|||||||
238
nymea-app/ui/devicepages/DeviceDetailsPage.qml
Normal file
238
nymea-app/ui/devicepages/DeviceDetailsPage.qml
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Controls.Material 2.2
|
||||||
|
import Nymea 1.0
|
||||||
|
import "../components"
|
||||||
|
import "../delegates"
|
||||||
|
|
||||||
|
ThingPageBase {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: flickable
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
SwipeDelegateGroup {}
|
||||||
|
|
||||||
|
section.property: "type"
|
||||||
|
section.delegate: ListSectionHeader {
|
||||||
|
text: {
|
||||||
|
switch (parseInt(section)) {
|
||||||
|
case ThingModel.TypeStateType:
|
||||||
|
return qsTr("States")
|
||||||
|
case ThingModel.TypeEventType:
|
||||||
|
return qsTr("Events")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
model: ThingModel {
|
||||||
|
thing: root.thing
|
||||||
|
}
|
||||||
|
delegate: SwipeDelegate {
|
||||||
|
id: delegate
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
readonly property StateType stateType: model.type === ThingModel.TypeStateType ? root.thing.thingClass.stateTypes.getStateType(model.id) : null
|
||||||
|
readonly property EventType eventType: model.type === ThingModel.TypeEventType ? root.thing.thingClass.eventTypes.getEventType(model.id) : null
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
bottomPadding: 0
|
||||||
|
|
||||||
|
contentItem: Loader {
|
||||||
|
id: inlineLoader
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: Style.smallDelegateHeight
|
||||||
|
sourceComponent: {
|
||||||
|
switch (model.type) {
|
||||||
|
case ThingModel.TypeStateType:
|
||||||
|
return stateComponent;
|
||||||
|
case ThingModel.TypeEventType:
|
||||||
|
return eventComponent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: inlineLoader.item
|
||||||
|
when: model.type === ThingModel.TypeStateType
|
||||||
|
property: "stateType"
|
||||||
|
value: delegate.stateType
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: inlineLoader.item
|
||||||
|
when: model.type === ThingModel.TypeEventType
|
||||||
|
property: "eventType"
|
||||||
|
value: delegate.eventType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: stateComponent
|
||||||
|
RowLayout {
|
||||||
|
id: stateDelegate
|
||||||
|
property StateType stateType: null
|
||||||
|
readonly property State thingState: stateType ? root.thing.states.getState(stateType.id) : null
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: parent.width / 2
|
||||||
|
text: stateDelegate.stateType.displayName
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
Loader {
|
||||||
|
id: stateDelegateLoader
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
visible: text.length > 0 && stateDelegate.stateType.unit !== Types.UnitUnixTime
|
||||||
|
text: Types.toUiUnit(stateDelegate.stateType.unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: updateLoader()
|
||||||
|
onStateTypeChanged: updateLoader();
|
||||||
|
|
||||||
|
function updateLoader() {
|
||||||
|
if (stateDelegate.stateType == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceComp;
|
||||||
|
switch (stateDelegate.stateType.type.toLowerCase()) {
|
||||||
|
case "string":
|
||||||
|
sourceComp = "LabelDelegate.qml";
|
||||||
|
break;
|
||||||
|
case "stringlist":
|
||||||
|
sourceComp = "ListDelegate.qml";
|
||||||
|
break;
|
||||||
|
case "bool":
|
||||||
|
sourceComp = "LedDelegate.qml";
|
||||||
|
break;
|
||||||
|
case "int":
|
||||||
|
case "uint":
|
||||||
|
case "double":
|
||||||
|
if (stateDelegate.stateType.unit === Types.UnitUnixTime) {
|
||||||
|
sourceComp = "DateTimeDelegate.qml";
|
||||||
|
} else {
|
||||||
|
sourceComp = "NumberLabelDelegate.qml";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "color":
|
||||||
|
sourceComp = "ColorDelegate.qml";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!sourceComp) {
|
||||||
|
sourceComp = "LabelDelegate.qml";
|
||||||
|
print("GenericThingPage: unhandled entry", stateDelegate.stateType.displayName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var minValue = stateDelegate.stateType.minValue !== undefined
|
||||||
|
? stateDelegate.stateType.minValue
|
||||||
|
: stateDelegate.stateType.type.toLowerCase() === "uint"
|
||||||
|
? 0
|
||||||
|
: -2000000000; // As per QML spec
|
||||||
|
var maxValue = stateDelegate.stateType.maxValue !== undefined
|
||||||
|
? stateDelegate.stateType.maxValue
|
||||||
|
: 2000000000;
|
||||||
|
print("pushing delegate for", stateDelegate.stateType.name, "from:", minValue, "to:", maxValue, "possible:", stateDelegate.stateType.possibleValuesDisplayNames)
|
||||||
|
stateDelegateLoader.setSource("../delegates/statedelegates/" + sourceComp,
|
||||||
|
{
|
||||||
|
value: root.thing.states.getState(stateType.id).value,
|
||||||
|
possibleValues: stateDelegate.stateType.possibleValues,
|
||||||
|
possibleValuesDisplayNames: stateDelegate.stateType.possibleValuesDisplayNames,
|
||||||
|
from: minValue,
|
||||||
|
to: maxValue,
|
||||||
|
unit: stateDelegate.stateType.unit,
|
||||||
|
writable: false,
|
||||||
|
stateType: stateDelegate.stateType
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: stateDelegateLoader.item
|
||||||
|
property: "value"
|
||||||
|
value: stateDelegate.thingState.value
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: stateDelegateLoader.item.hasOwnProperty("from") ? stateDelegateLoader.item : null
|
||||||
|
property: "from"
|
||||||
|
value: stateDelegate.thingState.minValue
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: stateDelegateLoader.item.hasOwnProperty("to") ? stateDelegateLoader.item : null
|
||||||
|
property: "to"
|
||||||
|
value: stateDelegate.thingState.maxValue
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: stateDelegateLoader.item.hasOwnProperty("possibleValues") ? stateDelegateLoader.item : null
|
||||||
|
property: "possibleValues"
|
||||||
|
value: stateDelegate.thingState.possibleValues
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: stateDelegateLoader.item.hasOwnProperty("possibleValuesDisplayNames") ? stateDelegateLoader.item : null
|
||||||
|
property: "possibleValuesDisplayNames"
|
||||||
|
value: {
|
||||||
|
print("updating displayNames", stateDelegate.thingState.possibleValues)
|
||||||
|
var ret = []
|
||||||
|
for (var i = 0; i < stateDelegate.thingState.possibleValues.length; i++) {
|
||||||
|
var possibleValue = stateDelegate.thingState.possibleValues[i]
|
||||||
|
var idx = stateDelegate.stateType.possibleValues.indexOf(possibleValue)
|
||||||
|
print("value:", possibleValue, idx)
|
||||||
|
if (idx >= 0) {
|
||||||
|
ret.push(stateDelegate.stateType.possibleValuesDisplayNames[idx])
|
||||||
|
} else {
|
||||||
|
ret.push(possibleValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: stateDelegateLoader.item.hasOwnProperty("unit") ? stateDelegateLoader.item : null
|
||||||
|
property: "unit"
|
||||||
|
value: stateDelegate.stateType.unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: eventComponent
|
||||||
|
RowLayout {
|
||||||
|
id: eventComponentItem
|
||||||
|
property EventType eventType: null
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: eventComponentItem.eventType.displayName
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: flashlight
|
||||||
|
Layout.preferredHeight: Style.iconSize * .8
|
||||||
|
Layout.preferredWidth: height
|
||||||
|
color: "lightgray"
|
||||||
|
radius: width / 2
|
||||||
|
border.color: Style.foregroundColor
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
SequentialAnimation on color {
|
||||||
|
id: flashlightAnimation
|
||||||
|
running: false
|
||||||
|
ColorAnimation { to: "lightgreen"; duration: 100 }
|
||||||
|
ColorAnimation { to: "lightgray"; duration: 500 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: root.thing
|
||||||
|
onEventTriggered: {
|
||||||
|
if (eventTypeId === eventComponentItem.eventType.id) {
|
||||||
|
flashlightAnimation.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -73,6 +73,8 @@ SettingsPageBase {
|
|||||||
if (!root.thing.isChild || root.thingClass.createMethods.indexOf("CreateMethodAuto") < 0) {
|
if (!root.thing.isChild || root.thingClass.createMethods.indexOf("CreateMethodAuto") < 0) {
|
||||||
deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Reconfigure"), iconSource: "../images/configure.svg", functionName: "reconfigureThing"}))
|
deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Reconfigure"), iconSource: "../images/configure.svg", functionName: "reconfigureThing"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Details"), iconSource: "../images/info.svg", functionName: "thingDetails"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
function renameThing() {
|
function renameThing() {
|
||||||
@ -91,6 +93,10 @@ SettingsPageBase {
|
|||||||
configPage.aborted.connect(function() {pageStack.pop(root)})
|
configPage.aborted.connect(function() {pageStack.pop(root)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function thingDetails() {
|
||||||
|
var detailsPage = pageStack.push(Qt.resolvedUrl("qrc:/ui/devicepages/DeviceDetailsPage.qml"), {thing: root.thing})
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: menuEntryComponent
|
id: menuEntryComponent
|
||||||
IconMenuItem {
|
IconMenuItem {
|
||||||
|
|||||||
Reference in New Issue
Block a user