From da50a89866282e2721d7593111eb673f20258505 Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Sun, 26 Apr 2026 18:08:10 +0200 Subject: [PATCH 1/2] Item: propagate override flag via item update requests --- src/items/item.cpp | 27 +++++++++++++++++++++++---- src/items/item.h | 8 ++++++-- src/ui/itemsettingsdialog.cpp | 5 ++++- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index 0ff8cd5..6b2eb5b 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -67,6 +67,8 @@ void ItemData::storeWithChanges(QJsonObject& json, const ItemFieldChanges& chang valueNamesArray.append(name); json["ValueNames"] = valueNamesArray; } + if(changes.override) + json["override"] = override_; } void ItemData::load(const QJsonObject &json, const bool preserve) @@ -107,6 +109,11 @@ ItemFieldChanges ItemData::loadWithChanges(const QJsonObject& json, const bool p valueNames_.push_back(valueNamesArray[i].toString()); changes.valueNames = true; } + if(json.contains("override")) + { + override_ = json["override"].toBool(false); + changes.override = true; + } itemId_ = static_cast(json["ItemId"].toDouble(0)); } return changes; @@ -142,6 +149,8 @@ bool ItemData::hasChanged(const ItemData& other, const ItemFieldChanges& changes return true; if(changes.valueNames && other.getValueNames() != getValueNames()) return true; + if(changes.override && other.getOverride() != getOverride()) + return true; return false; } @@ -197,6 +206,16 @@ QString ItemData::indexToValueName(int index) const return QString(); } +bool ItemData::getOverride() const +{ + return override_; +} + +void ItemData::setOverride(bool overrideVal) +{ + override_ = overrideVal; +} + //item Item::Item(uint32_t itemIdIn, QString name, uint8_t value, QObject *parent): QObject(parent), ItemData (itemIdIn, name, @@ -217,7 +236,6 @@ Item::~Item() void Item::store(QJsonObject &json) { ItemData::store(json); - json["override"] = override_; if(!actors_.empty()) { QJsonArray actorsArray; @@ -237,7 +255,6 @@ void Item::store(QJsonObject &json) void Item::load(const QJsonObject &json, const bool preserve) { ItemData::load(json, preserve); - override_ = json["override"].toBool(false); if(json.contains("Actors")) { const QJsonArray actorsArray(json["Actors"].toArray(QJsonArray())); @@ -306,6 +323,8 @@ void Item::requestUpdate(ItemUpdateRequest update) } if(update.changes.valueNames) valueNames_ = update.payload.getValueNames(); + if(update.changes.override) + override_ = update.payload.getOverride(); update.payload = *this; updated(update); } @@ -351,12 +370,12 @@ bool Item::removeActor(std::shared_ptr actor) void Item::setOverride(const bool in) { - override_ = in; + ItemData::setOverride(in); } bool Item::getOverride() { - return override_; + return ItemData::getOverride(); } void Item::removeAllActors() diff --git a/src/items/item.h b/src/items/item.h index 4a70fae..6ddf6ef 100644 --- a/src/items/item.h +++ b/src/items/item.h @@ -38,6 +38,7 @@ protected: item_value_type_t type_; QString groupName_; std::vector valueNames_; + bool override_ = false; public: ItemData(uint32_t itemIdIn = QRandomGenerator::global()->generate(), @@ -78,6 +79,8 @@ public: void storeWithChanges(QJsonObject& json, const ItemFieldChanges& changes); ItemFieldChanges loadWithChanges(const QJsonObject& json, const bool preserve = false); virtual QString getName() const; + bool getOverride() const; + void setOverride(bool overrideVal); virtual void store(QJsonObject& json); virtual void load(const QJsonObject& json, const bool preserve = false); }; @@ -87,7 +90,6 @@ class Item: public QObject, public ItemData Q_OBJECT private: std::vector< std::shared_ptr > actors_; - bool override_ = false; signals: void updated(ItemUpdateRequest update); @@ -135,6 +137,7 @@ struct ItemFieldChanges bool groupName :1; bool actors :1; bool valueNames :1; + bool override :1; ItemFieldChanges(bool defaultVal = false) { name = defaultVal; @@ -144,10 +147,11 @@ struct ItemFieldChanges groupName = defaultVal; actors = false; valueNames = defaultVal; + override = defaultVal; } inline bool isNone() const { - return !name && !value && !hidden && !type && !groupName && !actors && !valueNames; + return !name && !value && !hidden && !type && !groupName && !actors && !valueNames && !override; } }; diff --git a/src/ui/itemsettingsdialog.cpp b/src/ui/itemsettingsdialog.cpp index d517312..adc3b06 100644 --- a/src/ui/itemsettingsdialog.cpp +++ b/src/ui/itemsettingsdialog.cpp @@ -96,7 +96,10 @@ void ItemSettingsDialog::changeGroup() void ItemSettingsDialog::changeOverride() { - item_->setOverride(ui->checkBox_Override->isChecked()); + ItemUpdateRequest update = item_->createValueUpdateRequest(ITEM_UPDATE_USER); + update.payload.setOverride(ui->checkBox_Override->isChecked()); + update.changes.override = true; + item_->requestUpdate(update); } void ItemSettingsDialog::loadActorList() From 34f129967b84ea3bd8af99ad9831aa67e8724da4 Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Mon, 27 Apr 2026 00:27:56 +0200 Subject: [PATCH 2/2] Allow propagation of sensor updates from secondary to main --- src/mainobject.cpp | 1 + src/sensors/sensor.cpp | 25 +++++++++++++++++++++++++ src/service/service.h | 2 +- src/service/tcpclient.cpp | 10 ++++++++++ src/service/tcpclient.h | 1 + src/ui/sensorlistwidget.cpp | 2 +- tests/unit/sensors/test_sensor.cpp | 12 ++++++------ 7 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/mainobject.cpp b/src/mainobject.cpp index 103c8de..b70b891 100644 --- a/src/mainobject.cpp +++ b/src/mainobject.cpp @@ -165,6 +165,7 @@ SecondaryMainObject::SecondaryMainObject(QString host, int port, QObject *parent { connect(tcpClient, &TcpClient::gotSensor, &globalSensors, &SensorStore::sensorGotState); globalItems.registerItemSource(tcpClient); + connect(&globalSensors, &SensorStore::sensorChangedState, tcpClient, &TcpClient::sensorEvent); if(!tcpClient->launch(QHostAddress(host), port)) { diff --git a/src/sensors/sensor.cpp b/src/sensors/sensor.cpp index de07db4..8483127 100644 --- a/src/sensors/sensor.cpp +++ b/src/sensors/sensor.cpp @@ -89,6 +89,31 @@ void SensorStore::sensorGotState(const Sensor& sensor, sensor_update_type_t type needsUpdate = true; } } + else if(type == SENSOR_UPDATE_REMOTE) + { + if(sensors_[i].name != sensor.name || sensors_[i].hidden != sensor.hidden || sensors_[i].groupName != sensor.groupName) + { + sensors_[i].name = sensor.name; + sensors_[i].hidden = sensor.hidden; + sensors_[i].groupName = sensor.groupName; + for(Sensor& known : knownSensors_) + { + if(sensor.type == known.type && sensor.id == known.id) + { + known.name = sensor.name; + known.hidden = sensor.hidden; + known.groupName = sensor.groupName; + break; + } + } + needsUpdate = true; + } + if(sensors_[i].field != sensor.field) + { + needsUpdate = true; + sensors_[i].field = sensor.field; + } + } else if(sensors_[i].field != sensor.field) { needsUpdate = true; diff --git a/src/service/service.h b/src/service/service.h index 7b96e1b..fcd2201 100644 --- a/src/service/service.h +++ b/src/service/service.h @@ -24,7 +24,7 @@ signals: void sensorAdded(Sensor sensor, Sensor::sensor_backend_type_t backend, QJsonObject payload); public slots: - void sensorEvent(Sensor sensor, sensor_update_type_t type); + virtual void sensorEvent(Sensor sensor, sensor_update_type_t type); virtual void itemUpdated(ItemUpdateRequest update); virtual void refresh() override; virtual void addSensor(Sensor sensor, Sensor::sensor_backend_type_t backend, QJsonObject payload = {}); diff --git a/src/service/tcpclient.cpp b/src/service/tcpclient.cpp index 6e00e2d..ef2fc66 100644 --- a/src/service/tcpclient.cpp +++ b/src/service/tcpclient.cpp @@ -128,6 +128,16 @@ void TcpClient::itemUpdated(ItemUpdateRequest update) } } +void TcpClient::sensorEvent(Sensor sensor, sensor_update_type_t type) +{ + // Only forward user-initiated sensor updates to the server + // to prevent feedback loops with backend/remote updates + if(type == SENSOR_UPDATE_USER) + { + Service::sensorEvent(sensor, type); + } +} + TcpClient::~TcpClient() { delete socket; diff --git a/src/service/tcpclient.h b/src/service/tcpclient.h index b67f7b4..4e56e6b 100644 --- a/src/service/tcpclient.h +++ b/src/service/tcpclient.h @@ -18,6 +18,7 @@ class TcpClient : public Service public slots: virtual void itemUpdated(ItemUpdateRequest update) override; + virtual void sensorEvent(Sensor sensor, sensor_update_type_t type) override; public: TcpClient(QObject* parent = nullptr); diff --git a/src/ui/sensorlistwidget.cpp b/src/ui/sensorlistwidget.cpp index f9a24b5..b411768 100644 --- a/src/ui/sensorlistwidget.cpp +++ b/src/ui/sensorlistwidget.cpp @@ -37,7 +37,7 @@ void SensorListWidget::onDoubleClick(QTreeWidgetItem *item, int column) { if(item && item->type() == 1001) { - const Sensor& sensor = getSensorForIndex(currentIndex()); + const Sensor& sensor = static_cast(item)->getSensor(); SensorSettingsDialog diag(sensor, this); if(diag.exec()) { diff --git a/tests/unit/sensors/test_sensor.cpp b/tests/unit/sensors/test_sensor.cpp index e604f15..deb14e3 100644 --- a/tests/unit/sensors/test_sensor.cpp +++ b/tests/unit/sensors/test_sensor.cpp @@ -362,16 +362,16 @@ private slots: // Add initial sensor Sensor initialSensor(Sensor::TYPE_TEMPERATURE, 1, 20.0, "Initial Name", false); store.sensorGotState(initialSensor, SENSOR_UPDATE_BACKEND); - - // Send REMOTE update with new name and hidden state + + // Send REMOTE update with new name and hidden state (e.g., from secondary instance) Sensor remoteUpdate(Sensor::TYPE_TEMPERATURE, 1, 25.0, "Remote Name", true); store.sensorGotState(remoteUpdate, SENSOR_UPDATE_REMOTE); - - // Verify name and hidden were NOT updated + + // Verify name, hidden and field were updated (remote updates should sync user-configurable fields) std::vector* sensors = store.getSensors(); QVERIFY(sensors->size() == 1); - QVERIFY(sensors->at(0).name == "Initial Name"); - QVERIFY(sensors->at(0).hidden == false); + QVERIFY(sensors->at(0).name == "Remote Name"); + QVERIFY(sensors->at(0).hidden == true); QVERIFY(sensors->at(0).field == 25.0); }