Fix destination node selection for Zigbee bindings

This commit is contained in:
Michael Zanetti 2022-09-16 00:41:39 +02:00
parent d6fe4d9249
commit e95724b375
4 changed files with 151 additions and 68 deletions

View File

@ -49,12 +49,12 @@ StatsBase {
} }
var upcomingTimestamp = root.calculateTimestamp(d.config.startTime(), d.config.sampleRate, d.config.count) var upcomingTimestamp = root.calculateTimestamp(d.config.startTime(), d.config.sampleRate, d.config.count)
print("refreshing config start", d.config.startTime(), "upcoming:", upcomingTimestamp, "fetchPending", d.fetchPending) // print("refreshing config start", d.config.startTime(), "upcoming:", upcomingTimestamp, "fetchPending", d.fetchPending)
for (var i = 0; i < d.config.count; i++) { for (var i = 0; i < d.config.count; i++) {
var timestamp = root.calculateTimestamp(d.config.startTime(), d.config.sampleRate, d.startOffset + i + 1) var timestamp = root.calculateTimestamp(d.config.startTime(), d.config.sampleRate, d.startOffset + i + 1)
var previousTimestamp = root.calculateTimestamp(timestamp, d.config.sampleRate, -1) var previousTimestamp = root.calculateTimestamp(timestamp, d.config.sampleRate, -1)
var entry = powerBalanceLogs.find(timestamp) var entry = powerBalanceLogs.find(timestamp)
print("timestamp:", timestamp, "found", (entry ? entry.timestamp : "")) // print("timestamp:", timestamp, "found", (entry ? entry.timestamp : ""))
var previousEntry = powerBalanceLogs.find(previousTimestamp); var previousEntry = powerBalanceLogs.find(previousTimestamp);
if (entry && (previousEntry || !d.loading)) { if (entry && (previousEntry || !d.loading)) {
// print("found entry:", entry.timestamp, previousEntry) // print("found entry:", entry.timestamp, previousEntry)

View File

@ -91,7 +91,8 @@ SettingsPageBase {
NymeaItemDelegate { NymeaItemDelegate {
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Network topology") text: qsTr("Network map")
visible: engine.jsonRpcClient.ensureServerVersion("6.2")
onClicked: { onClicked: {
pageStack.push(Qt.resolvedUrl("ZigbeeNetworkTopologyPage.qml"), {zigbeeManager: root.zigbeeManager, network: root.network}) pageStack.push(Qt.resolvedUrl("ZigbeeNetworkTopologyPage.qml"), {zigbeeManager: root.zigbeeManager, network: root.network})
} }

View File

@ -8,7 +8,7 @@ Page {
id: root id: root
header: NymeaHeader { header: NymeaHeader {
text: qsTr("ZigBee network topology") text: qsTr("ZigBee network map")
backButtonVisible: true backButtonVisible: true
onBackPressed: pageStack.pop() onBackPressed: pageStack.pop()
HeaderButton { HeaderButton {

View File

@ -41,6 +41,7 @@ SettingsPageBase {
property ZigbeeManager zigbeeManager: null property ZigbeeManager zigbeeManager: null
property ZigbeeNetwork network: null property ZigbeeNetwork network: null
property ZigbeeNode node: null property ZigbeeNode node: null
readonly property ZigbeeNode coordinatorNode: root.network.nodes.getNodeByNetworkAddress(0)
header: NymeaHeader { header: NymeaHeader {
text: qsTr("ZigBee node info") text: qsTr("ZigBee node info")
@ -220,69 +221,92 @@ SettingsPageBase {
} }
SettingsPageSectionHeader { ColumnLayout {
text: qsTr("Bindings") visible: engine.jsonRpcClient.ensureServerVersion("6.2")
}
property ZigbeeNode coordinatorNode: root.network.nodes.getNodeByNetworkAddress(0) SettingsPageSectionHeader {
Repeater { text: qsTr("Bindings")
model: root.node.bindings }
delegate: NymeaSwipeDelegate {
id: bindingDelegate
Layout.fillWidth: true
property ZigbeeNodeBinding binding: root.node.bindings[index]
ThingsProxy {
id: destinationThings
engine: _engine
paramsFilter: {"ieeeAddress": bindingDelegate.binding.destinationAddress}
}
canDelete: true Repeater {
progressive: false model: root.node.bindings
text: { delegate: NymeaSwipeDelegate {
if (destinationThings.count > 0) { id: bindingDelegate
return destinationThings.get(0).name Layout.fillWidth: true
property ZigbeeNodeBinding binding: root.node.bindings[index]
property ZigbeeNode destinationNode: root.network.nodes.getNode(binding.destinationAddress)
property ZigbeeNodeEndpoint endpoint: root.node.getEndpoint(binding.sourceEndpointId);
property ZigbeeCluster inputCluster: endpoint ? endpoint.getInputCluster(binding.clusterId) : null
property ZigbeeCluster outputCluster: endpoint ? endpoint.getOutputCluster(binding.clusterId) : null
property ZigbeeCluster usedCluster: inputCluster ? inputCluster : outputCluster
property Thing destinationThing: destinationThings.count > 0 ? destinationThings.get(0) : null
ThingsProxy {
id: destinationThings
engine: _engine
paramsFilter: {"ieeeAddress": bindingDelegate.binding.destinationAddress}
} }
if (binding.destinationAddress != "") {
if (binding.destinationAddress == coordinatorNode.ieeeAddress) { iconName: destinationNode && destinationNode == root.coordinatorNode
return Configuration.systemName ? "qrc:/styles/%1/logo.svg".arg(styleController.currentStyle)
: destinationThing
? app.interfacesToIcon(destinationThing.thingClass.interfaces)
: "/ui/images/zigbee.svg"
canDelete: true
progressive: false
text: {
if (binding.destinationAddress == "") {
return qsTr("Group: 0x%1").arg(NymeaUtils.pad(binding.groupAddress.toString(16), 4))
} }
return binding.destinationAddress var ret = ""
if (destinationNode) {
if (destinationNode == root.coordinatorNode) {
ret += Configuration.systemName
} else {
ret += destinationNode.model
}
} else {
ret += binding.destinationAddress
}
if (destinationThings.count == 1) {
ret += " (" + destinationThings.get(0).name + ")"
} else if (destinationThings.count > 1) {
ret += " (" + destinationThing.count + " things)"
}
return ret
}
subText: {
var ret = usedCluster.clusterName();
if (binding.destinationAddress != "") {
ret += " (" + binding.sourceEndpointId + " -> " + binding.destinationEndpointId + ")"
}
return ret;
}
onDeleteClicked: {
if (!node.rxOnWhenIdle) {
d.wakeupDialog = wakeupDialogComponent.createObject(root)
d.wakeupDialog.open()
}
d.pendingCommandId = zigbeeManager.removeBinding(network.networkUuid, binding)
} }
return qsTr("Group: 0x%1").arg(NymeaUtils.pad(binding.groupAddress.toString(16), 4))
} }
property ZigbeeNodeEndpoint endpoint: root.node.getEndpoint(binding.sourceEndpointId); }
property ZigbeeCluster inputCluster: endpoint ? endpoint.getInputCluster(binding.clusterId) : null
property ZigbeeCluster outputCluster: endpoint ? endpoint.getOutputCluster(binding.clusterId) : null Button {
property ZigbeeCluster usedCluster: inputCluster ? inputCluster : outputCluster Layout.fillWidth: true
subText: { Layout.leftMargin: Style.margins
var ret = usedCluster.clusterName(); Layout.rightMargin: Style.margins
if (binding.destinationAddress != "") { text: qsTr("Add binding")
ret += " (" + binding.sourceEndpointId + " -> " + binding.destinationEndpointId + ")" onClicked: {
} var dialog = addBindingComponent.createObject(root)
return ret; dialog.open()
}
onDeleteClicked: {
if (!node.rxOnWhenIdle) {
d.wakeupDialog = wakeupDialogComponent.createObject(root)
d.wakeupDialog.open()
}
d.pendingCommandId = zigbeeManager.removeBinding(network.networkUuid, binding)
} }
} }
} }
Button {
Layout.fillWidth: true
Layout.leftMargin: Style.margins
Layout.rightMargin: Style.margins
text: qsTr("Add binding")
onClicked: {
var dialog = addBindingComponent.createObject(root)
dialog.open()
}
}
@ -382,27 +406,85 @@ SettingsPageBase {
ComboBox { ComboBox {
id: destinationNodeComboBox id: destinationNodeComboBox
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: Style.delegateHeight
model: network.nodes model: network.nodes
displayText: currentDestinationNodeThings.count > 0
? currentDestinationNodeThings.get(0).name
: currentNode == root.coordinatorNode
? Configuration.systemName
: currentNode.model
property ZigbeeNode currentNode: network.nodes.get(currentIndex) property ZigbeeNode currentNode: network.nodes.get(currentIndex)
property Thing currentNodeThing: currentNode && currentDestinationNodeThings.count > 0 ? currentDestinationNodeThings.get(0) : null
ThingsProxy { ThingsProxy {
id: currentDestinationNodeThings id: currentDestinationNodeThings
engine: _engine engine: _engine
paramsFilter: {"ieeeAddress": destinationNodeDelegate.node.ieeeAddress} paramsFilter: destinationNodeComboBox.currentNode ? {"ieeeAddress": destinationNodeComboBox.currentNode.ieeeAddress} : {}
} }
delegate: ItemDelegate {
contentItem: RowLayout {
id: destinationNodeContentItem
width: parent.width - destinationNodeComboBox.indicator.width - Style.smallMargins
height: Style.delegateHeight
spacing: Style.smallMargins
ColorIcon {
Layout.leftMargin: Style.smallMargins
size: Style.iconSize
name: destinationNodeComboBox.currentNode == root.coordinatorNode
? "qrc:/styles/%1/logo.svg".arg(styleController.currentStyle)
: destinationNodeComboBox.currentNodeThing
? app.interfacesToIcon(destinationNodeComboBox.currentNodeThing.thingClass.interfaces)
: "/ui/images/zigbee.svg"
color: Style.accentColor
}
ColumnLayout {
Label {
Layout.fillWidth: true
text: destinationNodeComboBox.currentNode == root.coordinatorNode
? Configuration.systemName
: destinationNodeComboBox.currentNode.model + " - " + destinationNodeComboBox.currentNode.manufacturer
elide: Text.ElideRight
}
Label {
Layout.fillWidth: true
text: destinationNodeComboBox.currentNode == root.coordinatorNode
? qsTr("Coordinator")
: currentDestinationNodeThings.count == 1
? destinationNodeComboBox.currentNodeThing.name
: currentDestinationNodeThings.count > 1
? qsTr("%1 things").arg(currentDestinationNodeThings.count)
: qsTr("Unrecognized device")
font: Style.smallFont
elide: Text.ElideRight
}
}
}
// displayText: currentDestinationNodeThings.count > 0
// ? currentDestinationNodeThings.get(0).name
// : currentNode == root.coordinatorNode
// ? Configuration.systemName
// : currentNode.model
// ThingsProxy {
// id: currentDestinationNodeThings
// engine: _engine
// paramsFilter: {"ieeeAddress": destinationNodeComboBox.currentNode.ieeeAddress}
// }
delegate: NymeaItemDelegate {
id: destinationNodeDelegate id: destinationNodeDelegate
property ZigbeeNode node: network.nodes.get(index) property ZigbeeNode node: network.nodes.get(index)
property Thing nodeThing: destinationNodeThings.count > 0 ? destinationNodeThings.get(0) : null
iconName: node == coordinatorNode
? "qrc:/styles/%1/logo.svg".arg(styleController.currentStyle)
: nodeThing ? app.interfacesToIcon(nodeThing.thingClass.interfaces) : "/ui/images/zigbee.svg"
width: parent.width width: parent.width
text: destinationNodeThings.count > 0 text: node == root.coordinatorNode
? destinationNodeThings.get(0).name ? Configuration.systemName
: node == root.coordinatorNode : node.model + " - " + node.manufacturer
? Configuration.systemName subText: node == root.coordinatorNode
: node.model ? qsTr("Coordinator")
: destinationNodeThings.count == 1
? nodeThing.name
: nodeThings.count > 1
? qsTr("%1 things").arg(nodeThings.count)
: qsTr("Unrecognized device")
progressive: false
ThingsProxy { ThingsProxy {
id: destinationNodeThings id: destinationNodeThings
engine: _engine engine: _engine