/* * 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"); } }