From 037ef5aa09e149b3b7753c97e1c8ed84aa78039e Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Thu, 2 Jul 2026 11:49:39 +0200 Subject: [PATCH 1/3] Update remote html to add support for enum items and groups --- remote.html | 142 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 136 insertions(+), 6 deletions(-) diff --git a/remote.html b/remote.html index 4765978..78f5a36 100644 --- a/remote.html +++ b/remote.html @@ -185,6 +185,25 @@ .refresh-btn:hover { background-color: #45a049; } + .filter-container { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 10px; + } + .filter-label { + color: #ccc; + font-size: 0.9em; + } + .filter-select { + flex: 1; + padding: 8px; + border-radius: 5px; + border: 1px solid #555; + background-color: #4a4c56; + color: white; + font-size: 0.9em; + } @@ -198,10 +217,22 @@
+
+ Group: + +
+
+ Group: + +
@@ -310,14 +341,24 @@ function renderItems() { const itemList = document.getElementById('itemList'); + const groupFilter = document.getElementById('itemGroupFilter').value; itemList.innerHTML = ''; - if (Object.keys(items).length === 0) { + // Update group filter options + updateItemGroupFilter(); + + // Filter items by group + let filteredItems = Object.values(items); + if (groupFilter) { + filteredItems = filteredItems.filter(item => item.GroupName === groupFilter); + } + + if (filteredItems.length === 0) { itemList.innerHTML = '
No items found
'; return; } - Object.values(items).forEach(item => { + filteredItems.forEach(item => { const itemCard = document.createElement('div'); itemCard.className = 'item-card'; @@ -348,12 +389,26 @@ case 2: // ITEM_VALUE_NO_VALUE controlHtml = '
No control
'; break; + case 3: // ITEM_VALUE_ENUM + const valueNames = item.ValueNames || []; + let optionsHtml = ''; + valueNames.forEach((name, index) => { + optionsHtml += ``; + }); + controlHtml = ` + + `; + break; } + const groupInfo = item.GroupName ? ` | Group: ${item.GroupName}` : ''; itemCard.innerHTML = `
${item.Name}
-
ItemId: ${item.ItemId} | Type: ${getTypeName(type)}
+
ItemId: ${item.ItemId} | Type: ${getTypeName(type)}${groupInfo}
${controlHtml} `; @@ -362,6 +417,28 @@ }); } + function updateItemGroupFilter() { + const select = document.getElementById('itemGroupFilter'); + const currentValue = select.value; + const groups = new Set(); + + Object.values(items).forEach(item => { + if (item.GroupName) { + groups.add(item.GroupName); + } + }); + + select.innerHTML = ''; + Array.from(groups).sort().forEach(group => { + const option = document.createElement('option'); + option.value = group; + option.textContent = group; + select.appendChild(option); + }); + + select.value = currentValue; + } + function toggleItem(itemId, newValue) { if (!socket || socket.readyState !== WebSocket.OPEN) { alert('Not connected to server'); @@ -401,11 +478,30 @@ socket.send(JSON.stringify(message)); } + function setEnumItem(itemId, value) { + if (!socket || socket.readyState !== WebSocket.OPEN) { + alert('Not connected to server'); + return; + } + + const message = { + MessageType: 'ItemUpdate', + Data: [{ + ItemId: itemId, + Value: parseInt(value) + }], + FullList: false + }; + + socket.send(JSON.stringify(message)); + } + function getTypeName(type) { switch (type) { case 0: return 'BOOL'; case 1: return 'UINT'; case 2: return 'NO_VALUE'; + case 3: return 'ENUM'; default: return 'UNKNOWN'; } } @@ -425,14 +521,24 @@ function renderSensors() { const sensorList = document.getElementById('sensorList'); + const groupFilter = document.getElementById('sensorGroupFilter').value; sensorList.innerHTML = ''; - if (Object.keys(sensors).length === 0) { + // Update group filter options + updateSensorGroupFilter(); + + // Filter sensors by group + let filteredSensors = Object.values(sensors); + if (groupFilter) { + filteredSensors = filteredSensors.filter(sensor => sensor.GroupName === groupFilter); + } + + if (filteredSensors.length === 0) { sensorList.innerHTML = '
No sensors found
'; return; } - Object.values(sensors).forEach(sensor => { + filteredSensors.forEach(sensor => { const sensorCard = document.createElement('div'); sensorCard.className = 'sensor-card'; @@ -456,11 +562,12 @@ // Truncate to 2 decimal places const fieldValue = Number(sensor.Field).toFixed(2); + const groupInfo = sensor.GroupName ? ` | Group: ${sensor.GroupName}` : ''; sensorCard.innerHTML = `
${sensor.Name}
-
Type: ${typeName} | ID: ${sensor.Id}
+
Type: ${typeName} | ID: ${sensor.Id}${groupInfo}
@@ -473,11 +580,34 @@ }); } + function updateSensorGroupFilter() { + const select = document.getElementById('sensorGroupFilter'); + const currentValue = select.value; + const groups = new Set(); + + Object.values(sensors).forEach(sensor => { + if (sensor.GroupName) { + groups.add(sensor.GroupName); + } + }); + + select.innerHTML = ''; + Array.from(groups).sort().forEach(group => { + const option = document.createElement('option'); + option.value = group; + option.textContent = group; + select.appendChild(option); + }); + + select.value = currentValue; + } + function getTypeName(type) { switch (type) { case 0: return 'BOOL'; case 1: return 'UINT'; case 2: return 'NO_VALUE'; + case 3: return 'ENUM'; default: return 'UNKNOWN'; } } From e1195c6f5f5cf64eec0d39c7b684153353cef0f8 Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Thu, 2 Jul 2026 11:50:06 +0200 Subject: [PATCH 2/3] update app name in agents.md --- AGENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 81908fc..999bdb1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,7 @@ -# SHinterface - Smart Home Interface +# smartvos - Smart Home System ## Overview -SHinterface is a Qt6-based smart home control application that interfaces with microcontrollers and various sensors to manage home automation devices. It supports both primary (master) and secondary (client) modes, allowing for distributed control across multiple devices. +smartvos is a Qt6-based smart home control application that interfaces with microcontrollers and various sensors to manage home automation devices. It supports both primary (master) and secondary (client) modes, allowing for distributed control across multiple devices. ## Architecture From ffe8b0095de4ca4c4f3a0fa064aa254a03b1f7d0 Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Thu, 2 Jul 2026 11:50:19 +0200 Subject: [PATCH 3/3] Add support for soil moisture sensors --- src/sensors/mqttsensorsource.cpp | 8 ++++++++ src/sensors/sensor.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/sensors/mqttsensorsource.cpp b/src/sensors/mqttsensorsource.cpp index 9dab556..6d25e8f 100644 --- a/src/sensors/mqttsensorsource.cpp +++ b/src/sensors/mqttsensorsource.cpp @@ -191,6 +191,14 @@ void MqttSensorSource::onMessageReceived(const QMqttMessage& message) sensor.field = obj["voltage"].toDouble(0); stateChanged(sensor, SENSOR_UPDATE_BACKEND); } + + if(obj.contains("soil_moisture")) + { + sensor.name = baseName + " Soil Moisture"; + sensor.type = Sensor::TYPE_SOIL_MOISTURE; + sensor.field = obj["soil_moisture"].toDouble(0); + stateChanged(sensor, SENSOR_UPDATE_BACKEND); + } } } diff --git a/src/sensors/sensor.h b/src/sensors/sensor.h index 86d79db..0ed9fee 100644 --- a/src/sensors/sensor.h +++ b/src/sensors/sensor.h @@ -25,6 +25,7 @@ public: TYPE_ENERGY_USE, TYPE_POWER, TYPE_VOLTAGE, + TYPE_SOIL_MOISTURE, TYPE_LOWBATTERY = 128, TYPE_SHUTDOWN_IMMINENT = 251, TYPE_OCUPANCY, @@ -143,6 +144,7 @@ public: case TYPE_TEMPERATURE: return "°C"; case TYPE_HUMIDITY: + case TYPE_SOIL_MOISTURE: return "%"; case TYPE_PRESSURE: return "hPa";