This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
powersync-app/nymea-app/ui/utils/NymeaUtils.qml
2025-12-11 16:40:13 +01:00

358 lines
14 KiB
QML

// 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 <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
pragma Singleton
import QtQuick
import Nymea
import QtCharts
Item {
id: root
function pad(num, size, base) {
if (base === undefined) {
base = 10
}
var trimmedNum = Math.floor(num)
var decimals = num - trimmedNum
var trimmedStr = "" + trimmedNum.toString(base)
var str = "000000000" + trimmedStr
str = str.substr(str.length - Math.max(size, trimmedStr.length));
if (decimals !== 0) {
str += "." + decimals.toString(base);
}
return str;
}
function interfaceListToDevicePage(interfaceList) {
print("**** getting page for interfaces", interfaceList)
var page;
if (interfaceList.indexOf("media") >= 0) {
page = "MediaThingPage.qml";
} else if (interfaceList.indexOf("button") >= 0) {
page = "ButtonThingPage.qml";
} else if (interfaceList.indexOf("powerswitch") >= 0) {
page = "ButtonThingPage.qml";
} else if (interfaceList.indexOf("weather") >= 0) {
page = "WeatherDevicePage.qml";
} else if (interfaceList.indexOf("heating") >= 0) {
page = "HeatingDevicePage.qml";
} else if (interfaceList.indexOf("cooling") >= 0) {
page = "CoolingThingPage.qml";
} else if (interfaceList.indexOf("thermostat") >= 0) {
page = "ThermostatDevicePage.qml";
} else if (interfaceList.indexOf("vibrationsensor") >= 0) {
page = "InputTriggerDevicePage.qml";
} else if (interfaceList.indexOf("sensor") >= 0) {
page = "SensorDevicePage.qml";
} else if (interfaceList.indexOf("inputtrigger") >= 0) {
page = "InputTriggerDevicePage.qml";
} else if (interfaceList.indexOf("garagedoor") >= 0 ) {
page = "GarageThingPage.qml";
} else if (interfaceList.indexOf("light") >= 0) {
page = "LightThingPage.qml";
} else if (interfaceList.indexOf("shutter") >= 0 || interfaceList.indexOf("blind") >= 0) {
page = "ShutterDevicePage.qml";
} else if (interfaceList.indexOf("awning") >= 0) {
page = "AwningThingPage.qml";
} else if (interfaceList.indexOf("notifications") >= 0) {
page = "NotificationsThingPage.qml";
} else if (interfaceList.indexOf("fingerprintreader") >= 0) {
page = "FingerprintReaderDevicePage.qml";
} else if (interfaceList.indexOf("evcharger") >= 0) {
page = "EvChargerThingPage.qml"
} else if (interfaceList.indexOf("smartmeter") >= 0) {
page = "SmartMeterDevicePage.qml"
} else if (interfaceList.indexOf("powersocket") >= 0) {
page = "PowersocketDevicePage.qml";
} else if (interfaceList.indexOf("doorbell") >= 0) {
page = "DoorbellDevicePage.qml";
} else if (interfaceList.indexOf("irrigation") >= 0) {
page = "IrrigationDevicePage.qml";
} else if (interfaceList.indexOf("ventilation") >= 0) {
page = "VentilationThingPage.qml";
} else if (interfaceList.indexOf("barcodescanner") >= 0) {
page = "BarcodeScannerThingPage.qml";
} else if (interfaceList.indexOf("cleaningrobot") >= 0) {
page = "CleaningRobotThingPage.qml";
} else {
page = "GenericThingPage.qml";
}
print("Selecting page", page, "for interface list:", interfaceList)
return page;
}
function isDark(color) {
var r, g, b;
if (color.constructor.name === "Object") {
r = color.r * 255;
g = color.g * 255;
b = color.b * 255;
} else if (color.constructor.name === "String") {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
r = parseInt(result[1], 16)
g = parseInt(result[2], 16)
b = parseInt(result[3], 16)
}
return ((r * 299 + g * 587 + b * 114) / 1000) < 128
}
property var namedIcons: {
"dashboard": "qrc:/icons/dashboard.svg",
"group": "qrc:/icons/groups.svg",
"folder": "qrc:/icons/folder.svg",
"star": "qrc:/icons/starred.svg",
"heart": "qrc:/icons/like.svg",
"wrench": "qrc:/icons/configure.svg",
"light": "qrc:/icons/light-on.svg",
"sensor": "qrc:/icons/sensors.svg",
"media": "qrc:/icons/media.svg",
"powersocket": "qrc:/icons/powersocket.svg",
"power": "qrc:/icons/system-shutdown.svg",
"weather": "qrc:/icons/weather-app-symbolic.svg",
"attention": "qrc:/icons/attention.svg",
"shutter": "qrc:/icons/shutter/shutter-040.svg",
"garage": "qrc:/icons/garage/garage-100.svg",
"awning": "qrc:/icons/awning/awning-100.svg",
"uncategorized": "qrc:/icons/select-none.svg",
"closable": "qrc:/icons/closable-move.svg",
"smartmeter": "qrc:/icons/smartmeter.svg",
"heating": "qrc:/icons/thermostat/heating.svg",
"cooling": "qrc:/icons/thermostat/cooling.svg",
"meter": "qrc:/icons/dial.svg",
"ev-charger": "qrc:/icons/ev-charger.svg",
"battery": "qrc:/icons/battery/battery-100.svg",
"message": "qrc:/icons/notification.svg",
"irrigation": "qrc:/icons/irrigation.svg",
"ventilation": "qrc:/icons/ventilation.svg",
"lock": "qrc:/icons/smartlock.svg",
"qrcode": "qrc:/icons/qrcode.svg",
"cleaningrobot": "qrc:/icons/cleaning-robot.svg",
"plant": "qrc:/icons/sensors/conductivity.svg",
"water": "qrc:/icons/sensors/water.svg",
"wind": "qrc:/icons/sensors/windspeed.svg",
"cloud": "qrc:/icons/weathericons/weather-clouds.svg",
"send": "qrc:/icons/send.svg",
}
function namedIcon(name) {
if (!namedIcons.hasOwnProperty(name)) {
console.error("No such named icon:", name)
return
}
return namedIcons[name]
}
property ListModel scopesModelPre8dot4: ListModel {
ListElement {
text: qsTr("Admin")
description: qsTr("Full access to the system.")
scope: UserInfo.PermissionScopeAdmin
}
ListElement {
text: qsTr("Control things")
description: qsTr("Execute actions and use things and services.")
scope: UserInfo.PermissionScopeControlThings
}
ListElement {
text: qsTr("Configure things")
description: qsTr("Add new things and change settings.")
scope: UserInfo.PermissionScopeConfigureThings
}
ListElement {
text: qsTr("Execute magic")
description: qsTr("Execute rules, scenes and scripts.")
scope: UserInfo.PermissionScopeExecuteRules
}
ListElement {
text: qsTr("Configure magic")
description: qsTr("Create new rules and scripts in the system.")
scope: UserInfo.PermissionScopeConfigureRules
}
}
property ListModel scopesModel: ListModel {
ListElement {
text: qsTr("Admin")
description: qsTr("Full access to the system.")
scope: UserInfo.PermissionScopeAdmin
}
ListElement {
text: qsTr("Control things")
description: qsTr("Execute actions and use things and services.")
scope: UserInfo.PermissionScopeControlThings
}
ListElement {
text: qsTr("Configure things")
description: qsTr("Add new things and change settings.")
scope: UserInfo.PermissionScopeConfigureThings
}
ListElement {
text: qsTr("Access all things")
description: qsTr("Allow to see and use all things of the system.")
scope: UserInfo.PermissionScopeAccessAllThings
}
ListElement {
text: qsTr("Execute magic")
description: qsTr("Execute rules, scenes and scripts.")
scope: UserInfo.PermissionScopeExecuteRules
}
ListElement {
text: qsTr("Configure magic")
description: qsTr("Create new rules and scripts in the system.")
scope: UserInfo.PermissionScopeConfigureRules
}
}
function getPermissionScopeAdjustments(scope, enabled, currentScopes) {
var adjustedScopes = currentScopes;
console.warn("Adjust permissions", scope, "->", enabled, currentScopes)
if (enabled) {
// Scope has been enabled
switch (scope) {
case UserInfo.PermissionScopeAdmin:
adjustedScopes = UserInfo.PermissionScopeAdmin
break;
case UserInfo.PermissionScopeControlThings:
break;
case UserInfo.PermissionScopeConfigureThings:
adjustedScopes |= UserInfo.PermissionScopeControlThings
adjustedScopes |= UserInfo.PermissionScopeAccessAllThings
break;
case UserInfo.PermissionScopeAccessAllThings:
adjustedScopes |= UserInfo.PermissionScopeControlThings
break;
case UserInfo.PermissionScopeExecuteRules:
adjustedScopes |= UserInfo.PermissionScopeAccessAllThings
break;
case UserInfo.PermissionScopeConfigureRules:
adjustedScopes |= UserInfo.PermissionScopeExecuteRules
adjustedScopes |= UserInfo.PermissionScopeAccessAllThings
break;
}
} else {
// Scope has been disabled
switch (scope) {
case UserInfo.PermissionScopeAdmin:
// Set the default permission for non admin
adjustedScopes = UserInfo.PermissionScopeAccessAllThings | UserInfo.PermissionScopeControlThings | UserInfo.PermissionScopeExecuteRules
break;
case UserInfo.PermissionScopeControlThings:
adjustedScopes &= ~UserInfo.PermissionScopeConfigureThings
break;
case UserInfo.PermissionScopeConfigureThings:
// Note: PermissionScopeConfigureThings is 3 and unsets therefore also the abbility to control things.
adjustedScopes |= UserInfo.PermissionScopeControlThings
break;
case UserInfo.PermissionScopeAccessAllThings:
adjustedScopes &= ~UserInfo.PermissionScopeConfigureThings
adjustedScopes &= ~UserInfo.PermissionScopeExecuteRules
adjustedScopes &= ~UserInfo.PermissionScopeConfigureRules
// Make sure we still can controll those things we added
adjustedScopes |= UserInfo.PermissionScopeControlThings
break;
case UserInfo.PermissionScopeExecuteRules:
adjustedScopes &= ~UserInfo.PermissionScopeConfigureRules
break;
case UserInfo.PermissionScopeConfigureRules:
// Note: PermissionScopeConfigureRules constand unsets therefore also the abbility to execute rules (screnes).
adjustedScopes |= UserInfo.PermissionScopeExecuteRules
break;
}
}
return adjustedScopes
}
function hasPermissionScope(permissions, requestedScope) {
return (permissions & requestedScope) === requestedScope;
}
property bool inhibitChartsAnimation: PlatformHelper.deviceModel.startsWith("SM-G950") // Samsung S8 has a buggy GPU driver :(
property int chartsAnimationOptions: !inhibitChartsAnimation ? ChartView.SeriesAnimations : ChartView.NoAnimation
function generateColor(baseColor, index, alpha) {
var stepSize = 30
var baseHSV = rgb2hsv(baseColor.r, baseColor.g, baseColor.b)
var currentHue = baseHSV[0]
var handledColors = [currentHue]
for (var i = 0; i < index; i++) {
while (handledColors.indexOf(currentHue) >= 0) {
currentHue = (currentHue + 60) % 360
if (handledColors.indexOf(currentHue) >= 0) {
currentHue += /*60 + */stepSize
stepSize = Math.max(1, stepSize / 2)
}
}
handledColors.push(currentHue)
}
return Qt.hsva(currentHue / 360, baseHSV[1], baseHSV[2], alpha || 1);
}
function rgb2hsv(r,g,b) {
var v=Math.max(r,g,b), c=v-Math.min(r,g,b);
var h= c && ((v===r) ? (g-b)/c : ((v===g) ? 2+(b-r)/c : 4+(r-g)/c));
return [60*(h<0?h+6:h), v&&c/v, v];
}
readonly property var sensorInterfaceStateMap: {
"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"
}
}