diff --git a/components/DataPoint.qml b/components/DataPoint.qml
new file mode 100644
index 000000000..a9d140372
--- /dev/null
+++ b/components/DataPoint.qml
@@ -0,0 +1,111 @@
+/*
+** Copyright (C) 2022 Victron Energy B.V.
+*/
+
+import QtQuick
+import Victron.VenusOS
+import Victron.Veutil
+
+QtObject {
+ id: root
+
+ // Valid 'source' values are:
+ // - dbus paths, e.g. 'com.victronenergy.blah/path/to/value'
+ // - as above, but with as full dbus uid, e.g. 'dbus/com.victronenergy.blah/path/to/value'
+ // - full mqtt uid, e.g. 'mqtt/blah/device-id/path/to/value'
+ // Note that dbus and mqtt uids should only be used when the application is using their
+ // respective backend connection types. If a mqtt uid is used on a dbus connection, for
+ // example, the DataPoint would not produce a valid value.
+ property string source
+ property var sourceObject
+
+ readonly property var value: sourceObject ? sourceObject.value : undefined
+ readonly property bool valid: value !== undefined
+
+ property bool hasMin
+ property bool hasMax
+ readonly property var min: hasMin && sourceObject ? sourceObject.min : undefined
+ readonly property var max: hasMax && sourceObject ? sourceObject.max : undefined
+ property bool invalidate: true
+
+ property Component _dbusComponent : Component {
+ VeQuickItem {
+ uid: root.source.startsWith("dbus/") ? root.source : "dbus/" + root.source
+ invalidate: root.invalidate
+
+ onInvalidateChanged: root.invalidate = invalidate
+ }
+ }
+
+ property Component _mqttComponent : Component {
+ VeQuickItem {
+ readonly property string mqttUid: root.source.startsWith("mqtt/") ? root.source : _uidConverter.mqttUid
+
+ readonly property SingleUidHelper _uidConverter: SingleUidHelper {
+ dbusUid: root.source.length === 0 || root.source.startsWith("mqtt/")
+ ? ""
+ : (root.source.startsWith("dbus/") ? root.source : "dbus/" + root.source)
+ }
+
+ uid: mqttUid
+ invalidate: root.invalidate
+
+ onInvalidateChanged: root.invalidate = invalidate
+ }
+ }
+
+ property Component _mockComponent : Component {
+ QtObject {
+ id: root
+
+ property string source: root.source
+ property var value: Global.mockDataSimulator ? Global.mockDataSimulator.mockDataValues[source] : undefined
+ property real min: 0
+ property real max: 100
+ property bool invalidate: root.invalidate
+
+ function setValue(v) {
+ value = v
+ }
+
+ onInvalidateChanged: root.invalidate = invalidate
+ }
+ }
+
+ function setValue(v) {
+ if (sourceObject) {
+ sourceObject.setValue(v)
+ } else {
+ console.warn("Set value() failed, no sourceObject for source", source)
+ }
+ }
+
+ function _reset() {
+ if (source.length === 0) {
+ return
+ }
+ if (sourceObject) {
+ sourceObject.destroy()
+ sourceObject = null
+ }
+ // TODO: maybe use incubateObject() in future if synchronous construction of sourceObject
+ // takes too long.
+ switch (BackendConnection.type) {
+ case BackendConnection.DBusSource:
+ sourceObject = _dbusComponent.createObject(root)
+ break
+ case BackendConnection.MqttSource:
+ sourceObject = _mqttComponent.createObject(root)
+ break
+ case BackendConnection.MockSource:
+ sourceObject = _mockComponent.createObject(root)
+ break
+ default:
+ console.warn("Unknown DataPoint source type:", BackendConnection.type)
+ break
+ }
+ }
+
+ onSourceChanged: _reset()
+ Component.onCompleted: _reset()
+}
diff --git a/components/datapoints/DataPoint.qml b/components/datapoints/DataPoint.qml
deleted file mode 100644
index 926a372f7..000000000
--- a/components/datapoints/DataPoint.qml
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
-** Copyright (C) 2022 Victron Energy B.V.
-*/
-
-import QtQuick
-import Victron.VenusOS
-
-QtObject {
- id: root
-
- // Valid 'source' values are:
- // - dbus paths, e.g. 'com.victronenergy.blah/path/to/value'
- // - as above, but with as full dbus uid, e.g. 'dbus/com.victronenergy.blah/path/to/value'
- // - full mqtt uid, e.g. 'mqtt/blah/device-id/path/to/value'
- // Note that dbus and mqtt uids should only be used when the application is using their
- // respective backend connection types. If a mqtt uid is used on a dbus connection, for
- // example, the DataPoint would not produce a valid value.
- property string source
-
- property var sourceObject
- readonly property int sourceType: BackendConnection.type
-
- readonly property var value: sourceObject ? sourceObject.value : undefined
- readonly property bool valid: value !== undefined
-
- property bool hasMin
- property bool hasMax
- readonly property var min: hasMin && sourceObject ? sourceObject.min : undefined
- readonly property var max: hasMax && sourceObject ? sourceObject.max : undefined
- property bool invalidate: true
-
- property var _dbusImpl
- property var _mqttImpl
- property var _mqttUidHelper
- property Component _mqttUidHelperComponent: Component {
- QtObject {
- readonly property string mqttUid: root.source.startsWith("mqtt/") ? root.source : _uidConverter.mqttUid
-
- readonly property SingleUidHelper _uidConverter: SingleUidHelper {
- dbusUid: root.source.length === 0 || root.source.startsWith("mqtt/")
- ? ""
- : (root.source.startsWith("dbus/") ? root.source : "dbus/" + root.source)
- }
- }
- }
-
- function setValue(v) {
- if (sourceObject) {
- sourceObject.setValue(v)
- } else {
- console.warn("Set value() failed, no sourceObject for source", source)
- }
- }
-
- function _dbusImplStatusChanged() {
- if (!_dbusImpl) {
- return
- }
- if (_dbusImpl.status === Component.Error) {
- console.warn("Unable to load DataPointDBusImpl.qml", _dbusImpl.errorString())
- } else if (_dbusImpl.status === Component.Ready) {
- _dbusImpl.statusChanged.disconnect(_dbusImplStatusChanged)
- _createDBusImpl()
- }
- }
-
- function _mqttImplStatusChanged() {
- if (!_mqttImpl) {
- return
- }
- if (_mqttImpl.status === Component.Error) {
- console.warn("Unable to load DataPointMqttImpl.qml", _mqttImpl.errorString())
- } else if (_mqttImpl.status === Component.Ready) {
- _mqttImpl.statusChanged.disconnect(_mqttImplStatusChanged)
- _createMqttImpl()
- }
- }
-
- function _createDBusImpl() {
- if (!_dbusImpl || _dbusImpl.status !== Component.Ready) {
- console.warn("Cannot create object from component", _dbusImpl ? _dbusImpl.url : "")
- return
- }
- if (sourceObject) {
- sourceObject.destroy()
- sourceObject = null
- }
- sourceObject = _dbusImpl.createObject(root,
- {
- uid: Qt.binding(function() { return root.source.startsWith("dbus/") ? root.source : "dbus/" + root.source }),
- invalidate: Qt.binding(function() { return root.invalidate })
- }
- )
- if (!sourceObject) {
- console.warn("Failed to create object from DataPointDBusImpl.qml", _dbusImpl.errorString())
- return
- }
- sourceObject.onInvalidateChanged.connect(function() { root.invalidate = sourceObject.invalidate })
- }
-
- function _createMqttImpl() {
- if (!_mqttImpl || _mqttImpl.status !== Component.Ready) {
- console.warn("Cannot create object from component", _mqttImpl ? _mqttImpl.url : "")
- return
- }
- if (sourceObject) {
- sourceObject.destroy()
- sourceObject = null
- }
- sourceObject = _mqttImpl.createObject(root,
- {
- uid: Qt.binding(function() { return _mqttUidHelper.mqttUid }),
- invalidate: Qt.binding(function() { return root.invalidate })
- }
- )
- if (!sourceObject) {
- console.warn("Failed to create object from DataPointMqttImpl.qml", _mqttImpl.errorString())
- return
- }
- sourceObject.onInvalidateChanged.connect(function() { root.invalidate = sourceObject.invalidate })
- }
-
- function _reset() {
- if (source.length === 0) {
- return
- }
- switch (sourceType) {
- case BackendConnection.DBusSource:
- if (!_dbusImpl) {
- _dbusImpl = Qt.createComponent(Qt.resolvedUrl("DataPointDBusImpl.qml"),
- Component.Asynchronous)
- }
- if (_dbusImpl.status === Component.Loading) {
- _dbusImpl.statusChanged.connect(_dbusImplStatusChanged)
- } else if (_dbusImpl.status === Component.Ready) {
- _createDBusImpl()
- }
- break
- case BackendConnection.MqttSource:
- if (!_mqttImpl) {
- _mqttUidHelper = _mqttUidHelperComponent.createObject(root)
- _mqttImpl = Qt.createComponent(Qt.resolvedUrl("DataPointMqttImpl.qml"),
- Component.Asynchronous)
- }
- if (_mqttImpl.status === Component.Loading) {
- _mqttImpl.statusChanged.connect(_mqttImplStatusChanged)
- } else if (_mqttImpl.status === Component.Ready) {
- _createMqttImpl()
- }
- break
- case BackendConnection.MockSource:
- const comp = Qt.createComponent(Qt.resolvedUrl("DataPointMockImpl.qml"))
- sourceObject = comp.createObject(root,
- {
- "source": root.source,
- "invalidate": Qt.binding(function() { return root.invalidate })
- }
- )
- sourceObject.onInvalidateChanged.connect(function() { root.invalidate = sourceObject.invalidate })
- break
- default:
- console.warn("Unknown DataPoint source type:", sourceType)
- break
- }
- }
-
- onSourceChanged: _reset()
- onSourceTypeChanged: _reset()
- Component.onCompleted: _reset()
- Component.onDestruction: {
- // As a precaution, if asynchronous component creation finishes after object destruction,
- // ensure the statusChanged() handlers are not called.
- if (_dbusImpl) {
- _dbusImpl.statusChanged.disconnect(_dbusImplStatusChanged)
- _dbusImpl = null
- }
- if (_mqttImpl) {
- _mqttImpl.statusChanged.disconnect(_mqttImplStatusChanged)
- _mqttImpl = null
- }
- }
-}
diff --git a/components/datapoints/DataPointDBusImpl.qml b/components/datapoints/DataPointDBusImpl.qml
deleted file mode 100644
index e93361726..000000000
--- a/components/datapoints/DataPointDBusImpl.qml
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
-** Copyright (C) 2022 Victron Energy B.V.
-*/
-
-import QtQuick
-import Victron.VenusOS
-import Victron.Veutil
-
-VeQuickItem {
- id: root
-}
diff --git a/components/datapoints/DataPointMockImpl.qml b/components/datapoints/DataPointMockImpl.qml
deleted file mode 100644
index 254ef84dd..000000000
--- a/components/datapoints/DataPointMockImpl.qml
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-** Copyright (C) 2022 Victron Energy B.V.
-*/
-
-import QtQml
-import Victron.VenusOS
-
-QtObject {
- id: root
-
- property string source
- property var value: Global.mockDataSimulator ? Global.mockDataSimulator.mockDataValues[source] : undefined
- property real min: 0
- property real max: 100
- property bool invalidate: true
-
- function setValue(v) {
- value = v
- }
-}
diff --git a/components/datapoints/DataPointMqttImpl.qml b/components/datapoints/DataPointMqttImpl.qml
deleted file mode 100644
index e93361726..000000000
--- a/components/datapoints/DataPointMqttImpl.qml
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
-** Copyright (C) 2022 Victron Energy B.V.
-*/
-
-import QtQuick
-import Victron.VenusOS
-import Victron.Veutil
-
-VeQuickItem {
- id: root
-}
diff --git a/qml.qrc b/qml.qrc
index b0553c661..912f0c102 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -14,6 +14,7 @@
components/CommonWords.qml
components/ControlCard.qml
components/ControlValue.qml
+ components/DataPoint.qml
components/Device.qml
components/ElectricalQuantityLabel.qml
components/EnvironmentGauge.qml
@@ -83,10 +84,6 @@
components/controls/SpinBox.qml
components/controls/Switch.qml
components/controls/TextField.qml
- components/datapoints/DataPoint.qml
- components/datapoints/DataPointDBusImpl.qml
- components/datapoints/DataPointMockImpl.qml
- components/datapoints/DataPointMqttImpl.qml
components/dialogs/DateSelectorDialog.qml
components/dialogs/DialogShadow.qml
components/dialogs/VrmInstanceSwapDialog.qml
diff --git a/src/main.cpp b/src/main.cpp
index 7f445f31b..bccef6d03 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -343,6 +343,8 @@ void registerQmlTypes()
"Victron.VenusOS", 2, 0, "QuantityTableSummary");
qmlRegisterType(QUrl(QStringLiteral("qrc:/components/QuantityTable.qml")),
"Victron.VenusOS", 2, 0, "QuantityTable");
+ qmlRegisterType(QUrl(QStringLiteral("qrc:/components/DataPoint.qml")),
+ "Victron.VenusOS", 2, 0, "DataPoint");
qmlRegisterType(QUrl(QStringLiteral("qrc:/components/ElectricalQuantityLabel.qml")),
"Victron.VenusOS", 2, 0, "ElectricalQuantityLabel");
qmlRegisterType(QUrl(QStringLiteral("qrc:/components/FixedWidthLabel.qml")),
@@ -402,10 +404,6 @@ void registerQmlTypes()
qmlRegisterType(QUrl(QStringLiteral("qrc:/components/WeatherDetails.qml")),
"Victron.VenusOS", 2, 0, "WeatherDetails");
- /* data points */
- qmlRegisterType(QUrl(QStringLiteral("qrc:/components/datapoints/DataPoint.qml")),
- "Victron.VenusOS", 2, 0, "DataPoint");
-
/* list items */
qmlRegisterType(QUrl(QStringLiteral("qrc:/components/listitems/ListLabel.qml")),
"Victron.VenusOS", 2, 0, "ListLabel");