diff --git a/libnymea-app/types/types.cpp b/libnymea-app/types/types.cpp
index d36c2bce..2964297a 100644
--- a/libnymea-app/types/types.cpp
+++ b/libnymea-app/types/types.cpp
@@ -182,6 +182,8 @@ QString Types::toUiUnit(Types::Unit unit) const
return "%";
case Types::UnitPartsPerMillion:
return "ppm";
+ case Types::UnitPartsPerBillion:
+ return "ppb";
case Types::UnitEuro:
return "€";
case Types::UnitDollar:
@@ -238,6 +240,8 @@ QString Types::toUiUnit(Types::Unit unit) const
return "l";
case Types::UnitFluidOunce:
return "fl oz";
+ case Types::UnitMicroGrammPerCubicalMeter:
+ return "µg/m³";
}
return "";
diff --git a/libnymea-app/types/types.h b/libnymea-app/types/types.h
index 60609ef9..99bc4447 100644
--- a/libnymea-app/types/types.h
+++ b/libnymea-app/types/types.h
@@ -95,6 +95,7 @@ public:
UnitEuroCentPerKiloWattHour,
UnitPercentage,
UnitPartsPerMillion,
+ UnitPartsPerBillion,
UnitEuro,
UnitDollar,
UnitHertz,
@@ -112,6 +113,7 @@ public:
UnitRpm,
UnitMilligramPerLiter,
UnitLiter,
+ UnitMicroGrammPerCubicalMeter,
// Those do not exist in nymea:core at this point, Adding them for easier conversion to imperial
UnitDegreeFahrenheit,
diff --git a/nymea-app/images.qrc b/nymea-app/images.qrc
index 6a5b4b5b..a5c0392c 100644
--- a/nymea-app/images.qrc
+++ b/nymea-app/images.qrc
@@ -289,5 +289,10 @@
ui/images/zigbee/zigbee-sibling.svg
ui/images/zigbee/zigbee-previous-child.svg
ui/images/arrow-down.svg
+ ui/images/sensors/voc.svg
+ ui/images/sensors/pm25.svg
+ ui/images/sensors/o3.svg
+ ui/images/sensors/pm10.svg
+ ui/images/sensors/no2.svg
diff --git a/nymea-app/main.cpp b/nymea-app/main.cpp
index 2e2da306..a0fce137 100644
--- a/nymea-app/main.cpp
+++ b/nymea-app/main.cpp
@@ -174,6 +174,7 @@ int main(int argc, char *argv[])
qmlRegisterSingletonType("Nymea", 1, 0, "PushNotifications", PushNotifications::pushNotificationsProvider);
qmlRegisterSingletonType(QUrl("qrc:///ui/utils/NymeaUtils.qml"), "Nymea", 1, 0, "NymeaUtils" );
+ qmlRegisterSingletonType(QUrl("qrc:///ui/utils/AirQualityIndex.qml"), "Nymea", 1, 0, "AirQualityIndex" );
qmlRegisterType("Nymea", 1, 0, "DashboardModel");
qmlRegisterUncreatableType("Nymea", 1, 0, "DashboardItem", "");
diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc
index 1b08b19d..dc9eeaa6 100644
--- a/nymea-app/resources.qrc
+++ b/nymea-app/resources.qrc
@@ -278,5 +278,6 @@
ui/system/zwave/ZWaveNetworkSettingsPage.qml
ui/components/ActivityIndicator.qml
ui/system/zigbee/ZigbeeNodePage.qml
+ ui/utils/AirQualityIndex.qml
diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml
index 68f64d4d..182bea8e 100644
--- a/nymea-app/ui/Nymea.qml
+++ b/nymea-app/ui/Nymea.qml
@@ -198,8 +198,12 @@ ApplicationWindow {
return qsTr("CO level")
case "co2sensor":
return qsTr("CO2 level")
+ case "no2sensor":
+ return qsTr("Nitrogen dioxide leve")
case "gassensor":
return qsTr("Flammable gas level")
+ case "vocsensor":
+ return qsTr("VOC level")
case "inputtrigger":
return qsTr("Incoming Events");
case "outputtrigger":
@@ -298,6 +302,16 @@ ApplicationWindow {
return Qt.resolvedUrl("images/sensors/co.svg")
case "co2sensor":
return Qt.resolvedUrl("images/sensors/co2.svg")
+ case "no2sensor":
+ return Qt.resolvedUrl("images/sensors/no2.svg")
+ case "o3sensor":
+ return Qt.resolvedUrl("images/sensors/o3.svg")
+ case "vocsensor":
+ return Qt.resolvedUrl("images/sensors/voc.svg")
+ case "pm10sensor":
+ return Qt.resolvedUrl("images/sensors/pm10.svg")
+ case "pm25sensor":
+ return Qt.resolvedUrl("images/sensors/pm25.svg")
case "gassensor":
return Qt.resolvedUrl("images/sensors/gas.svg")
case "daylightsensor":
diff --git a/nymea-app/ui/StyleBase.qml b/nymea-app/ui/StyleBase.qml
index f145ffdd..d73e83dc 100644
--- a/nymea-app/ui/StyleBase.qml
+++ b/nymea-app/ui/StyleBase.qml
@@ -84,6 +84,7 @@ Item {
property color blue: "#5c95cd"
property color purple: "#955ccd"
property color rose: "#cd5c95"
+ property color darkGreen: "#5ccd5e"
// Icon/graph colors for various interfaces
property var interfaceColors: {
@@ -96,6 +97,8 @@ Item {
"noisesensor": purple,
"cosensor": darkGray,
"co2sensor": turquoise,
+ "pm10sensor": lightGray,
+ "pm25sensor": gray,
"gassensor": orange,
"daylightsensor": yellow,
"presencesensor": darkBlue,
@@ -117,7 +120,10 @@ Item {
"orpsensor": yellow,
"powersocket": lime,
"evcharger": lime,
- "energystorage": lime
+ "energystorage": lime,
+ "vocsensor": green,
+ "o3sensor": blue,
+ "no2sensor": purple
}
property var stateColors: {
diff --git a/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml b/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml
index 7c3cd81d..b15c74df 100644
--- a/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml
+++ b/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml
@@ -94,6 +94,11 @@ ThingsListPageBase {
ListElement { interfaceName: "o2sensor"; stateName: "o2saturation" }
ListElement { interfaceName: "phsensor"; stateName: "ph" }
ListElement { interfaceName: "orpsensor"; stateName: "orp" }
+ ListElement { interfaceName: "vocsensor"; stateName: "voc" }
+ ListElement { interfaceName: "pm10sensor"; stateName: "pm10" }
+ ListElement { interfaceName: "pm25sensor"; stateName: "pm25" }
+ ListElement { interfaceName: "no2sensor"; stateName: "no2" }
+ ListElement { interfaceName: "o3sensor"; stateName: "o3" }
}
delegate: RowLayout {
diff --git a/nymea-app/ui/devicepages/SensorDevicePage.qml b/nymea-app/ui/devicepages/SensorDevicePage.qml
index 89e746c9..c02afa51 100644
--- a/nymea-app/ui/devicepages/SensorDevicePage.qml
+++ b/nymea-app/ui/devicepages/SensorDevicePage.qml
@@ -57,9 +57,13 @@ ThingPageBase {
"waterlevelsensor": "waterLevel",
"phsensor": "ph",
"o2sensor": "o2saturation",
+ "o3sensor": "o3",
"orpsensor": "orp",
- "airquality": "airQuality",
- "indoorairquality": "indoorAirQuality"
+ "vocsensor": "voc",
+ "cosensor": "co",
+ "pm10sensor": "pm10",
+ "pm25sensor": "pm25",
+ "no2sensor": "no2"
}
ListModel {
@@ -141,25 +145,29 @@ ThingPageBase {
if (["temperaturesensor"].indexOf(modelData) >= 0) {
return Types.toUiValue(-50, Types.UnitDegreeCelsius)
}
+ if (["pressuresensor"].indexOf(modelData) >= 0) {
+ return Types.toUiValue(900, Types.UnitMilliBar)
+ }
+
return state.minValue
}
property var maxValue: {
if (["temperaturesensor"].indexOf(modelData) >= 0) {
return Types.toUiValue(50, Types.UnitDegreeCelsius)
}
+ if (["pressuresensor"].indexOf(modelData) >= 0) {
+ return Types.toUiValue(1100, Types.UnitMilliBar)
+ }
return state.maxValue
}
sourceComponent: {
- var handledInterfaces = [
+ var progressInterfaces = [
"humiditysensor",
"o2sensor",
"temperaturesensor",
"moisturesensor",
- "co2sensor",
"conductivitysensor",
- "cosensor",
- "co2sensor",
"gassensor",
"lightsensor",
"orpsensor",
@@ -168,9 +176,21 @@ ThingPageBase {
"waterlevelsensor",
"windspeedsensor"
]
- if (handledInterfaces.indexOf(modelData) >= 0) {
+ if (progressInterfaces.indexOf(modelData) >= 0) {
return progressComponent
}
+ var scaleInterfaces = [
+ "vocsensor",
+ "cosensor",
+ "co2sensor",
+ "o3sensor",
+ "pm10sensor",
+ "pm25sensor",
+ "no2sensor"
+ ]
+ if (scaleInterfaces.indexOf(modelData) >= 0) {
+ return scaleComponent
+ }
}
}
}
@@ -344,11 +364,13 @@ ThingPageBase {
Label {
anchors.centerIn: parent
+ anchors.verticalCenter: -Style.smallMargins
width: parent.width * 0.6
text: Types.toUiValue(progressCanvas.state.value, progressCanvas.stateType.unit).toFixed(1) + " " + Types.toUiUnit(progressCanvas.stateType.unit)
- font.pixelSize: Math.min(Style.hugeFont.pixelSize, parent.height / 6)
+ font.pixelSize: Math.min(Style.largeFont.pixelSize, parent.height / 6)
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
+ maximumLineCount: 2
}
onPaint: {
@@ -383,8 +405,9 @@ ThingPageBase {
}
ColorIcon {
- anchors.centerIn: parent
- anchors.verticalCenterOffset: progressCanvas.height / 2 - height
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: Style.smallMargins
name: app.interfaceToIcon(progressCanvas.interfaceName)
size: Math.min(Style.bigIconSize, parent.height / 5)
color: app.interfaceToColor(progressCanvas.interfaceName)
@@ -392,5 +415,177 @@ ThingPageBase {
}
}
+ Component {
+ id: scaleComponent
+ Canvas {
+ id: scaleCanvas
+ property string interfaceName: parent.interfaceName
+ property StateType stateType: parent.stateType
+ property State state: parent.state
+ property var minValue: parent.minValue
+ property var maxValue: parent.maxValue
+
+ property int scaleWidth: width * .1
+
+ property var scale: {
+ switch (interfaceName) {
+ case "vocsensor":
+ return AirQualityIndex.iaqVoc
+ case "cosensor":
+ return AirQualityIndex.caqiCo
+ case "o3sensor":
+ return AirQualityIndex.caqiO3
+ case "pm10sensor":
+ return AirQualityIndex.caqiPm10
+ case "pm25sensor":
+ return AirQualityIndex.caqiPm25
+ case "no2sensor":
+ return AirQualityIndex.caqiNo2
+ }
+ return baseScale
+ }
+ property var baseScale: [
+ {
+ "value": maxValue,
+ "angle": 270,
+ "color": Style.tileOverlayColor
+ }
+ ]
+
+ property var currentIndex: {
+ for (var i = 0; i < scale.length; i++) {
+ if (state.value <= scale[i].value) {
+ return i;
+ }
+ }
+ log.warn("Value out of scale!")
+ return -1
+ }
+
+ property double angle: {
+ var baseAngle = 0
+ var baseValue = 0;
+ if (currentIndex > 0) {
+ baseAngle = scale[currentIndex-1].angle
+ baseValue = scale[currentIndex-1].value
+ }
+ var valueRange = scale[currentIndex].value - baseValue
+ var angleRange = scale[currentIndex].angle - baseAngle
+ var progress = (state.value - baseValue) / (scale[currentIndex].value - baseValue)
+ return baseAngle + angleRange * progress
+ }
+ Behavior on angle { NumberAnimation { duration: Style.slowAnimationDuration; easing.type: Easing.InOutQuad } }
+ onAngleChanged: requestPaint();
+
+ ColumnLayout {
+ anchors.centerIn: parent
+ anchors.verticalCenterOffset: -Style.smallMargins
+ width: parent.width * 0.6
+
+ Label {
+ Layout.fillWidth: true
+ text: scaleCanvas.scale[scaleCanvas.currentIndex].text
+ font.pixelSize: Math.min(Style.largeFont.pixelSize, scaleCanvas.height / 6)
+ wrapMode: Text.WordWrap
+// color: scaleCanvas.scale[scaleCanvas.currentIndex].color
+ horizontalAlignment: Text.AlignHCenter
+ maximumLineCount: 2
+ }
+ Label {
+ Layout.fillWidth: true
+ text: Types.toUiValue(scaleCanvas.state.value, scaleCanvas.stateType.unit).toFixed(1) + " " + Types.toUiUnit(scaleCanvas.stateType.unit)
+ font.pixelSize: Math.min(Style.smallFont.pixelSize, scaleCanvas.height / 12)
+ wrapMode: Text.WordWrap
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
+
+
+ onPaint: {
+ var ctx = getContext("2d");
+ ctx.reset();
+
+ ctx.beginPath()
+ ctx.fillStyle = Style.foregroundColor
+
+ ctx.translate(width / 2, height / 2)
+ ctx.rotate(135 * Math.PI / 180)
+
+ ctx.lineCap = "round"
+ ctx.lineWidth = scaleCanvas.scaleWidth
+
+ // paint first rounded
+ ctx.beginPath()
+ ctx.strokeStyle = scaleCanvas.scale[0].color
+ var startAngle = 0
+ var endAngle = scaleCanvas.scale[0].angle * Math.PI/180
+ ctx.arc(0, 0, width / 2 - ctx.lineWidth / 2, startAngle, endAngle)
+ ctx.stroke()
+ ctx.closePath()
+
+ // paint last rounded
+ ctx.beginPath()
+ ctx.strokeStyle = scaleCanvas.scale[scaleCanvas.scale.length - 1].color
+ startAngle = scaleCanvas.scale[scaleCanvas.scale.length - 2].angle * Math.PI/180
+ endAngle = scaleCanvas.scale[scaleCanvas.scale.length - 1].angle * Math.PI/180
+ ctx.arc(0, 0, width / 2 - ctx.lineWidth / 2, startAngle, endAngle)
+ ctx.stroke()
+ ctx.closePath()
+
+ // paint inner parts
+ ctx.lineCap = "butt"
+ for (var i = 1; i < scaleCanvas.scale.length - 1; i++) {
+ ctx.beginPath()
+ ctx.strokeStyle = scaleCanvas.scale[i].color
+ startAngle = scaleCanvas.scale[i - 1].angle * Math.PI/180
+ endAngle = scaleCanvas.scale[i].angle * Math.PI/180
+ ctx.arc(0, 0, width / 2 - ctx.lineWidth / 2, startAngle, endAngle)
+ ctx.stroke()
+ ctx.closePath()
+ }
+
+ var tipRadius = width / 2 - scaleCanvas.scaleWidth
+ var baseRadius = tipRadius - scaleCanvas.scaleWidth / 2
+
+ var tipX = tipRadius * Math.cos(scaleCanvas.angle * Math.PI/180)
+ var tipY = tipRadius * Math.sin(scaleCanvas.angle * Math.PI/180)
+
+ var base1X = baseRadius * Math.cos((scaleCanvas.angle - 5) * Math.PI/180)
+ var base1Y = baseRadius * Math.sin((scaleCanvas.angle - 5) * Math.PI/180)
+ var base2X = baseRadius * Math.cos((scaleCanvas.angle + 5) * Math.PI/180)
+ var base2Y = baseRadius * Math.sin((scaleCanvas.angle + 5) * Math.PI/180)
+
+
+ ctx.lineWidth = 1
+ ctx.fillStyle = Style.foregroundColor
+ ctx.beginPath()
+ ctx.moveTo(tipX, tipY)
+ ctx.lineTo(base1X, base1Y)
+ ctx.lineTo(base2X, base2Y)
+ ctx.moveTo(tipX, tipY)
+ ctx.stroke()
+ ctx.fill()
+ ctx.closePath()
+
+
+// ctx.beginPath()
+// ctx.strokeStyle = app.interfaceToColor(progressCanvas.interfaceName)
+// radEnd *= progressCanvas.progress
+// ctx.arc(0, 0, width / 2 - ctx.lineWidth / 2, radStart, radEnd)
+// ctx.stroke()
+// ctx.closePath()
+ }
+
+ ColorIcon {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: Style.smallMargins
+// anchors.verticalCenterOffset: scaleCanvas.height / 2 - height
+ name: app.interfaceToIcon(scaleCanvas.interfaceName)
+ size: Math.min(Style.bigIconSize, parent.height / 5)
+ color: app.interfaceToColor(scaleCanvas.interfaceName)
+ }
+ }
+ }
}
diff --git a/nymea-app/ui/images/sensors/co.svg b/nymea-app/ui/images/sensors/co.svg
index 874e90d7..44750253 100644
--- a/nymea-app/ui/images/sensors/co.svg
+++ b/nymea-app/ui/images/sensors/co.svg
@@ -2,20 +2,20 @@
diff --git a/nymea-app/ui/images/sensors/co2.svg b/nymea-app/ui/images/sensors/co2.svg
index 874e90d7..27a0e6d0 100644
--- a/nymea-app/ui/images/sensors/co2.svg
+++ b/nymea-app/ui/images/sensors/co2.svg
@@ -2,20 +2,20 @@
diff --git a/nymea-app/ui/images/sensors/no2.svg b/nymea-app/ui/images/sensors/no2.svg
new file mode 100644
index 00000000..494fe508
--- /dev/null
+++ b/nymea-app/ui/images/sensors/no2.svg
@@ -0,0 +1,161 @@
+
+
+
+
diff --git a/nymea-app/ui/images/sensors/o3.svg b/nymea-app/ui/images/sensors/o3.svg
new file mode 100644
index 00000000..28a55fe1
--- /dev/null
+++ b/nymea-app/ui/images/sensors/o3.svg
@@ -0,0 +1,161 @@
+
+
+
+
diff --git a/nymea-app/ui/images/sensors/pm10.svg b/nymea-app/ui/images/sensors/pm10.svg
new file mode 100644
index 00000000..bcfa4fea
--- /dev/null
+++ b/nymea-app/ui/images/sensors/pm10.svg
@@ -0,0 +1,161 @@
+
+
+
+
diff --git a/nymea-app/ui/images/sensors/pm25.svg b/nymea-app/ui/images/sensors/pm25.svg
new file mode 100644
index 00000000..6d637c86
--- /dev/null
+++ b/nymea-app/ui/images/sensors/pm25.svg
@@ -0,0 +1,161 @@
+
+
+
+
diff --git a/nymea-app/ui/images/sensors/voc.svg b/nymea-app/ui/images/sensors/voc.svg
new file mode 100644
index 00000000..408908d3
--- /dev/null
+++ b/nymea-app/ui/images/sensors/voc.svg
@@ -0,0 +1,161 @@
+
+
+
+
diff --git a/nymea-app/ui/utils/AirQualityIndex.qml b/nymea-app/ui/utils/AirQualityIndex.qml
new file mode 100644
index 00000000..3c3b7d75
--- /dev/null
+++ b/nymea-app/ui/utils/AirQualityIndex.qml
@@ -0,0 +1,244 @@
+pragma Singleton
+import QtQuick 2.9
+import Nymea 1.0
+
+Item {
+ id: root
+
+ property var caqiPm10: [
+ {
+ "value": 25,
+ "angle": 54,
+ "text": qsTr("Very low"),
+ "color": Style.blue
+ },
+ {
+ "value": 50,
+ "angle": 108,
+ "text": qsTr("Low"),
+ "color": Style.green
+ },
+ {
+ "value": 90,
+ "angle": 162,
+ "text": qsTr("Medium"),
+ "color": Style.yellow
+ },
+ {
+ "value": 180,
+ "angle": 216,
+ "text": qsTr("High"),
+ "color": Style.orange
+ },
+ {
+ "value": 500,
+ "angle": 270,
+ "text": qsTr("Very high"),
+ "color": Style.red
+ }
+ ]
+
+ property var caqiPm25: [
+ {
+ "value": 15,
+ "angle": 54,
+ "text": qsTr("Very low"),
+ "color": Style.blue
+ },
+ {
+ "value": 30,
+ "angle": 108,
+ "text": qsTr("Low"),
+ "color": Style.green
+ },
+ {
+ "value": 55,
+ "angle": 162,
+ "text": qsTr("Medium"),
+ "color": Style.yellow
+ },
+ {
+ "value": 110,
+ "angle": 216,
+ "text": qsTr("High"),
+ "color": Style.orange
+ },
+ {
+ "value": 500,
+ "angle": 270,
+ "text": qsTr("Very high"),
+ "color": Style.red
+ }
+ ]
+
+ property var caqiO3: [
+ {
+ "value": 60,
+ "angle": 54,
+ "text": qsTr("Very low"),
+ "color": Style.blue
+ },
+ {
+ "value": 120,
+ "angle": 108,
+ "text": qsTr("Low"),
+ "color": Style.green
+ },
+ {
+ "value": 180,
+ "angle": 162,
+ "text": qsTr("Medium"),
+ "color": Style.yellow
+ },
+ {
+ "value": 240,
+ "angle": 216,
+ "text": qsTr("High"),
+ "color": Style.orange
+ },
+ {
+ "value": 500,
+ "angle": 270,
+ "text": qsTr("Very high"),
+ "color": Style.red
+ }
+ ]
+
+ property var caqiNo2: [
+ {
+ "value": 50,
+ "angle": 54,
+ "text": qsTr("Very low"),
+ "color": Style.blue
+ },
+ {
+ "value": 100,
+ "angle": 108,
+ "text": qsTr("Low"),
+ "color": Style.green
+ },
+ {
+ "value": 200,
+ "angle": 162,
+ "text": qsTr("Medium"),
+ "color": Style.yellow
+ },
+ {
+ "value": 400,
+ "angle": 216,
+ "text": qsTr("High"),
+ "color": Style.orange
+ },
+ {
+ "value": 800,
+ "angle": 270,
+ "text": qsTr("Very high"),
+ "color": Style.red
+ }
+ ]
+
+ property var caqiCo: [
+ {
+ "value": 5,
+ "angle": 54,
+ "text": qsTr("Very low"),
+ "color": Style.blue
+ },
+ {
+ "value": 7.5,
+ "angle": 108,
+ "text": qsTr("Low"),
+ "color": Style.green
+ },
+ {
+ "value": 10,
+ "angle": 162,
+ "text": qsTr("Medium"),
+ "color": Style.yellow
+ },
+ {
+ "value": 20,
+ "angle": 216,
+ "text": qsTr("High"),
+ "color": Style.orange
+ },
+ {
+ "value": 50,
+ "angle": 270,
+ "text": qsTr("Very high"),
+ "color": Style.red
+ }
+ ]
+
+ property var iaqVoc: [
+ {
+ "value": 65,
+ "angle": 54,
+ "text": qsTr("Excellent"),
+ "color": Style.blue
+ },
+ {
+ "value": 220,
+ "angle": 108,
+ "text": qsTr("Good"),
+ "color": Style.green
+ },
+ {
+ "value": 660,
+ "angle": 162,
+ "text": qsTr("Moderate"),
+ "color": Style.yellow
+ },
+ {
+ "value": 2200,
+ "angle": 216,
+ "text": qsTr("Poor"),
+ "color": Style.orange
+ },
+ {
+ "value": 65535,
+ "angle": 270,
+ "text": qsTr("Unhealthy"),
+ "color": Style.red
+ }
+ ]
+
+ property var epaAqiO3: [
+ {
+ "value": 54,
+ "angle": 54,
+ "text": qsTr("Good"),
+ "color": Style.blue
+ },
+ {
+ "value": 70,
+ "angle": 108,
+ "text": qsTr("Moderate"),
+ "color": Style.blue
+ },
+ {
+ "value": 85,
+ "angle": 162,
+ "text": qsTr("Unhealthy for sensitive groups"),
+ "color": Style.yellow
+ },
+ {
+ "value": 105,
+ "angle": 216,
+ "text": qsTr("Unhealthy"),
+ "color": Style.orange
+ },
+ {
+ "value": 200,
+ "angle": 270,
+ "text": qsTr("Very unhealthy"),
+ "color": Style.red
+ },
+ {
+ "value": 200,
+ "angle": 270,
+ "text": qsTr("Hazardeous"),
+ "color": Style.purple
+ }
+ ]
+}