Files
plasma-bitcoin-price/contents/ui/main.qml
T

170 lines
5.7 KiB
QML

/*
* nl.a14r.bitcoinprice — KDE Plasma 5 widget
*
* Toont de bitcoinprijs in EUR (CoinGecko API).
* - Groen : prijs gestegen t.o.v. vorige meting
* - Rood : prijs gedaald t.o.v. vorige meting
* - Grijs : gelijk gebleven (of eerste meting)
* - "--" : API-fout (netwerkfout, 4xx/5xx, rate limit 429)
*
* Vernieuwinterval: configureerbaar, minimaal 5 minuten
* (CoinGecko limiet: 1 call per 5 minuten).
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.plasmoid 2.0
Item {
id: root
readonly property int refreshMinutes: Math.max(5, plasmoid.configuration.refreshMinutes)
readonly property string apiUrl: "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=eur"
// -1 = gedaald, 0 = gelijk / nog geen vorige meting, 1 = gestegen
property int trend: 0
property double lastPrice: NaN // laatste succesvol opgehaalde prijs
property string state: "init" // init | ok | error
property string lastUpdated: "" // HH:mm:ss van laatste succesvolle update
readonly property string displayText: {
if (state !== "ok" || isNaN(lastPrice)) {
return "--";
}
return "\u20BF " + Number(lastPrice).toLocaleString(Qt.locale(), "f", 0) + " \u20AC";
}
readonly property color priceColor: {
if (state !== "ok" || isNaN(lastPrice)) {
return PlasmaCore.Theme.disabledTextColor; // "--" bij fout/init
}
if (trend > 0) {
return "#2ecc71"; // groen
}
if (trend < 0) {
return "#e74c3c"; // rood
}
return "#9e9e9e"; // grijs
}
readonly property string statusText: {
if (state === "error") {
return lastUpdated === ""
? i18n("API-fout")
: i18n("API-fout — laatste geldige prijs: %1", lastUpdated);
}
if (lastUpdated === "") {
return i18n("Bezig met ophalen\u2026");
}
return i18n("Laatst bijgewerkt: %1", lastUpdated);
}
Plasmoid.backgroundHints: PlasmaCore.Types.StandardBackground
Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation
Plasmoid.toolTipMainText: "Bitcoin / EUR"
Plasmoid.toolTipSubText: statusText
Plasmoid.compactRepresentation: Component {
Item {
id: compact
Layout.minimumWidth: priceLabel.implicitWidth + PlasmaCore.Units.smallSpacing * 2
Layout.minimumHeight: PlasmaCore.Units.iconSizes.smallMedium
Text {
id: priceLabel
anchors.centerIn: parent
text: root.displayText
color: root.priceColor
textFormat: Text.PlainText
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: PlasmaCore.Theme.defaultFont.family
font.pixelSize: compact.height > PlasmaCore.Units.iconSizes.small
? Math.round(compact.height * 0.6)
: PlasmaCore.Theme.defaultFont.pixelSize
}
}
}
Plasmoid.fullRepresentation: Component {
Item {
id: fullRep
implicitWidth: PlasmaCore.Units.gridUnit * 14
implicitHeight: PlasmaCore.Units.gridUnit * 7
Column {
anchors.centerIn: parent
spacing: PlasmaCore.Units.smallSpacing
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: root.displayText
color: root.priceColor
font.family: PlasmaCore.Theme.defaultFont.family
font.pixelSize: PlasmaCore.Units.gridUnit * 2
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: root.statusText
color: PlasmaCore.Theme.disabledTextColor
font.family: PlasmaCore.Theme.defaultFont.family
font.pixelSize: PlasmaCore.Theme.smallestFont.pixelSize
}
}
}
}
Timer {
id: refreshTimer
interval: root.refreshMinutes * 60 * 1000
running: true
repeat: true
triggeredOnStart: true
onTriggered: root.fetchPrice()
}
function fetchPrice() {
var xhr = new XMLHttpRequest();
xhr.open("GET", root.apiUrl);
xhr.setRequestHeader("Accept", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState !== XMLHttpRequest.DONE) {
return;
}
if (xhr.status === 200) {
try {
var price = JSON.parse(xhr.responseText).bitcoin.eur;
if (typeof price === "number" && !isNaN(price)) {
root.applyPrice(price);
return;
}
} catch (e) {
// ongeldige JSON → val door naar de foutafhandeling
}
}
// API-fout (netwerk, 4xx/5xx, rate limit 429, onverwachte payload)
root.state = "error";
};
xhr.send();
}
function applyPrice(price) {
if (isNaN(root.lastPrice) || price === root.lastPrice) {
root.trend = 0;
} else if (price > root.lastPrice) {
root.trend = 1;
} else {
root.trend = -1;
}
root.lastPrice = price;
root.state = "ok";
root.lastUpdated = Qt.formatDateTime(new Date(), "HH:mm:ss");
}
}