Bitcoin Price widget v1.0.0: Plasma 5 applet (EUR via CoinGecko, trend-kleuren, config interval min 5 min)

This commit is contained in:
ArnoldCordewiner
2026-09-05 19:26:24 +02:00
commit e3934f33b6
7 changed files with 327 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Arnold Cordewiner
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+77
View File
@@ -0,0 +1,77 @@
# plasma-bitcoin-price
KDE Plasma 5 widget die de bitcoinprijs in EUR toont, opgehaald bij de
[CoinGecko API](https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=eur).
## Weergave
| Situatie | Weergave |
|---|---|
| Prijs gestegen t.o.v. vorige meting | groen |
| Prijs gedaald t.o.v. vorige meting | rood |
| Prijs gelijk gebleven (of eerste meting) | grijs |
| API-fout (netwerkfout, 4xx/5xx, rate limit 429) | `--` |
Formaat: `₿ 68.969 €` — duizendtallen volgens de systeemlocale, afgerond op hele euro's.
## Configuratie
- **Vernieuwinterval (minuten)** — standaard 5, minimaal **5**, maximaal 1440.
Het minimum van 5 minuten komt door de CoinGecko rate limit van 1 call per 5 minuten.
Instelbaar via rechtsklik op de widget → *Configureer Bitcoin-prijs**Algemeen*.
## Installatie
### Grafisch
Dubbelklik op `dist/bitcoin-price-1.0.0.plasmoid` in Dolphin (Plasma installeert
dan automatisch naar `~/.local/share/plasma/plasmoids/`).
### Terminal
```bash
kpackagetool5 --type Plasma/Applet --install dist/bitcoin-price-1.0.0.plasmoid
# widget toevoegen via Add Widgets; bij twijfel plasma herladen:
plasmashell --replace &
```
Verwijderen:
```bash
kpackagetool5 --type Plasma/Applet --remove nl.a14r.bitcoinprice
```
## Notities
- De API-URL gebruikt `vs_currencies=eur`. In de oorspronkelijke opdracht stond
een `usd`-URL, maar de eis was "prijs t.o.v. de euro" met een `€`-teken; een
dollarwaarde onder een €-teken zou foutief zijn.
- De trendvergelijking gebeurt tegen de laatst bekend geraakte prijs. Na een
API-fout blijft die referentie staan, zodat de kleur klopt zodra er weer
data binnenkomt.
- Zonder CoinGecko API-key kan de API ook tussendoor een 429 geven; de widget
toont dan `--` tot de eerstvolgende geslaagde poll.
- Getest op Debian 12 / Plasma 5 (Qt 5.15). Plasma 6 werkt vermoedelijk ook,
maar is niet getest.
## Repository-layout
```
metadata.desktop
contents/
├── config/main.xml # config-schema (refreshMinutes, min 5, max 1440)
└── ui/
├── main.qml # widget + fetch/vergelijk-logica
├── config.qml # configuratievenster (categorieën)
└── configGeneral.qml # interval-instelling
```
Het kant-en-klare installatiebestand (`bitcoin-price-1.0.0.plasmoid`) staat als
bijlage bij de [v1.0.0-release](../../releases/tag/v1.0.0).
Zelf (her)packagen:
```bash
zip -r bitcoin-price.plasmoid metadata.desktop contents
```
## Licentie
MIT — zie [LICENSE](LICENSE).
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
<kcfgfile name=""/>
<group name="General">
<entry name="refreshMinutes" type="Int">
<label>Refresh interval in minutes (minimum 5 because of the CoinGecko rate limit)</label>
<default>5</default>
<min>5</min>
<max>1440</max>
</entry>
</group>
</kcfg>
+10
View File
@@ -0,0 +1,10 @@
import QtQuick 2.15
import org.kde.plasma.configuration 2.0
ConfigModel {
ConfigCategory {
name: i18n("Algemeen")
icon: "preferences-desktop"
source: "configGeneral.qml"
}
}
+16
View File
@@ -0,0 +1,16 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import org.kde.kirigami 2.5 as Kirigami
Kirigami.FormLayout {
property alias cfg_refreshMinutes: intervalSpin.value
SpinBox {
id: intervalSpin
from: 5
to: 1440
stepSize: 5
editable: true
Kirigami.FormData.label: i18n("Vernieuwinterval (minuten):")
}
}
+169
View File
@@ -0,0 +1,169 @@
/*
* 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");
}
}
+19
View File
@@ -0,0 +1,19 @@
[Desktop Entry]
Name=Bitcoin Price
Name[nl]=Bitcoin-prijs
Comment=Shows the bitcoin price in EUR, color-coded by trend
Comment[nl]=Toont de bitcoinprijs in EUR, kleurcodering op trend
Type=Service
Icon=bitcoin
X-Plasma-API=declarativeappletscript
X-Plasma-MainScript=ui/main.qml
X-KDE-ServiceTypes=Plasma/Applet
X-KDE-PluginInfo-Author=Arnold Cordewiner
X-KDE-PluginInfo-Email=hermes@a14r.be
X-KDE-PluginInfo-Name=nl.a14r.bitcoinprice
X-KDE-PluginInfo-Version=1.0.0
X-KDE-PluginInfo-Website=http://192.168.110.5:40004/hermes/plasma-bitcoin-price
X-KDE-PluginInfo-Category=Utilities
X-KDE-PluginInfo-License=MIT
X-KDE-PluginInfo-EnabledByDefault=true