// SPDX-License-Identifier: GPL-3.0-or-later /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH * Copyright (C) 2024 - 2025, chargebyte austria GmbH * * This file is part of nymea-app. * * nymea-app is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * nymea-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with nymea-app. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ import QtQuick import QtQuick.Controls import QtQuick.Layouts import Qt5Compat.GraphicalEffects import Nymea import NymeaApp.Utils import "../components" import "../customviews" ThingPageBase { id: root property var interfaceStateMap: { "temperaturesensor": "temperature", "humiditysensor": "humidity", "pressuresensor": "pressure", "moisturesensor": "moisture", "lightsensor": "lightIntensity", "conductivitysensor": "conductivity", "noisesensor": "noise", "cosensor": "co", "co2sensor": "co2", "gassensor": "gasLevel", "presencesensor": "isPresent", "daylightsensor": "daylight", "closablesensor": "closed", "watersensor": "waterDetected", "firesensor": "fireDetected", "waterlevelsensor": "waterLevel", "phsensor": "ph", "o2sensor": "o2saturation", "o3sensor": "o3", "orpsensor": "orp", "vocsensor": "voc", "cosensor": "co", "pm10sensor": "pm10", "pm25sensor": "pm25", "no2sensor": "no2" } ListModel { id: sensorsModel Component.onCompleted: { var supportedInterfaces = Object.keys(interfaceStateMap) for (var i = 0; i < supportedInterfaces.length; i++) { if (root.thing.thingClass.interfaces.indexOf(supportedInterfaces[i]) >= 0) { append({name: supportedInterfaces[i]}); } } } } Flickable { id: flickable anchors { fill: parent } topMargin: app.margins / 2 interactive: contentHeight > height contentHeight: contentGrid.implicitHeight ColumnLayout { id: contentGrid width: parent.width spacing: Style.margins Flow { id: flow Layout.fillWidth: true Layout.leftMargin: sensorsModel.count == 1 ? Style.hugeMargins : Style.bigMargins Layout.rightMargin: sensorsModel.count == 1 ? Style.hugeMargins : Style.bigMargins Layout.preferredHeight: flow.cellWidth * flow.totalRows property int columns: Math.min(flowRepeater.count, Math.floor(flickable.width / 150)) property int cellWidth: Math.min(400, width / columns) property int filledRows: flowRepeater.count / columns property int totalRows: Math.ceil(flowRepeater.count / columns) Repeater { id: flowRepeater model: sensorsModel delegate: SensorView { width: Math.floor(flow.width / itemsInRow) height: flow.cellWidth property int row: Math.floor(index / flow.columns) property int itemsInRow: row < flow.filledRows ? flow.columns : (flowRepeater.count % flow.columns) thing: root.thing interfaceName: modelData } } } GridLayout { columns: Math.ceil(width / 600) rowSpacing: 0 columnSpacing: 0 Repeater { model: sensorsModel delegate: Loader { id: loader Layout.fillWidth: true Layout.preferredHeight: item.implicitHeight property StateType stateType: root.thing.thingClass.stateTypes.findByName(interfaceStateMap[modelData]) property State state: root.thing.stateByName(interfaceStateMap[modelData]) property string interfaceName: modelData sourceComponent: engine.jsonRpcClient.ensureServerVersion("8.0") ? stateChartComponent : graphComponent } } } } Component { id: stateChartComponent StateChart { id: stateChart thing: root.thing stateType: parent.stateType color: app.interfaceToColor(interfaceName) iconSource: app.interfaceToIcon(interfaceName) Binding { target: stateChart property: "title" when: ["presencesensor", "daylightsensor", "closablesensor", "watersensor", "firesensor"].indexOf(interfaceName) >= 0 value: { switch (interfaceName) { case "presencesensor": return stateChart.valueState.value === true ? qsTr("Presence") : qsTr("Vacant") case "daylightsensor": return stateChart.valueState.value === true ? qsTr("Daytime") : qsTr("Nighttime") case "closablesensor": return stateChart.valueState.value === true ? qsTr("Closed") : qsTr("Open") case "watersensor": return stateChart.valueState.value === true ? qsTr("Wet") : qsTr("Dry") case "firesensor": return stateChart.valueState.value === true ? qsTr("Fire") : qsTr("No fire") } } } } } Component { id: graphComponent GenericTypeGraph { id: graph thing: root.thing color: app.interfaceToColor(interfaceName) iconSource: { if (graph.interfaceName == "closablesensor") { return graph.state.value === true ? "sensors/window-closed" : "sensors/window-open" } return app.interfaceToIcon(interfaceName) } implicitHeight: width * .6 property string interfaceName: parent.interfaceName stateType: parent.stateType property State state: parent.state Binding { target: graph property: "title" when: ["presencesensor", "daylightsensor", "closablesensor", "watersensor", "firesensor"].indexOf(graph.interfaceName) >= 0 value: { switch (graph.interfaceName) { case "presencesensor": return graph.state.value === true ? qsTr("Presence") : qsTr("Vacant") case "daylightsensor": return graph.state.value === true ? qsTr("Daytime") : qsTr("Nighttime") case "closablesensor": return graph.state.value === true ? qsTr("Closed") : qsTr("Open") case "watersensor": return graph.state.value === true ? qsTr("Wet") : qsTr("Dry") case "firesensor": return graph.state.value === true ? qsTr("Fire") : qsTr("No fire") } } } } } } }