v1.1.0: English UI + docs, bundled Bitcoin icon, canonical config page, gitea.a14r.be website URL, journal diagnostics

This commit is contained in:
ArnoldCordewiner
2026-09-05 20:07:12 +02:00
parent e3934f33b6
commit d3641469b0
8 changed files with 160 additions and 85 deletions
+57 -42
View File
@@ -1,77 +1,92 @@
# plasma-bitcoin-price # Bitcoin Price (Plasma 5 widget)
KDE Plasma 5 widget die de bitcoinprijs in EUR toont, opgehaald bij de KDE Plasma 5 widget that shows the bitcoin price in EUR, fetched from the
[CoinGecko API](https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=eur). [CoinGecko API](https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=eur).
## Weergave ## Display
| Situatie | Weergave | | Situation | Display |
|---|---| |---|---|
| Prijs gestegen t.o.v. vorige meting | groen | | Price up compared to the previous reading | green |
| Prijs gedaald t.o.v. vorige meting | rood | | Price down compared to the previous reading | red |
| Prijs gelijk gebleven (of eerste meting) | grijs | | Price unchanged (or first reading) | grey |
| API-fout (netwerkfout, 4xx/5xx, rate limit 429) | `--` | | API failure (network error, 4xx/5xx, rate limit 429) | `--` |
Formaat: `68.969 €` — duizendtallen volgens de systeemlocale, afgerond op hele euro's. Format: `68,969 €` (thousands separators follow the system locale, rounded to
whole euros) preceded by the bundled Bitcoin icon in the compact and full
representations.
## Configuratie ## Configuration
- **Vernieuwinterval (minuten)** — standaard 5, minimaal **5**, maximaal 1440. - **Refresh interval (minutes)** — default 5, minimum **5**, maximum 1440.
Het minimum van 5 minuten komt door de CoinGecko rate limit van 1 call per 5 minuten. The 5-minute floor exists because the CoinGecko API allows one request per
5 minutes. A hint text in the config dialog explains this.
Instelbaar via rechtsklik op de widget → *Configureer Bitcoin-prijs**Algemeen*. Configurable via right-click on the widget → *Configure Bitcoin Price…**General*.
## Installatie ## Installation
### Grafisch ### Graphical
Dubbelklik op `dist/bitcoin-price-1.0.0.plasmoid` in Dolphin (Plasma installeert Double-click `bitcoin-price-1.1.0.plasmoid` in Dolphin (Plasma installs it to
dan automatisch naar `~/.local/share/plasma/plasmoids/`). `~/.local/share/plasma/plasmoids/` automatically). The latest build is attached
to the [v1.1.0 release](../../releases/tag/v1.1.0).
### Terminal ### Terminal
```bash ```bash
kpackagetool5 --type Plasma/Applet --install dist/bitcoin-price-1.0.0.plasmoid kpackagetool5 --type Plasma/Applet --upgrade bitcoin-price-1.1.0.plasmoid
# widget toevoegen via Add Widgets; bij twijfel plasma herladen: # Then restart the shell if needed:
plasmashell --replace & plasmashell --replace &
``` ```
Verwijderen: Upgrading an already-installed widget requires `--upgrade` (`--install` fails
when the plugin id already exists).
Uninstall:
```bash ```bash
kpackagetool5 --type Plasma/Applet --remove nl.a14r.bitcoinprice kpackagetool5 --type Plasma/Applet --remove nl.a14r.bitcoinprice
``` ```
## Notities ## Notes
- De API-URL gebruikt `vs_currencies=eur`. In de oorspronkelijke opdracht stond - The API URL uses `vs_currencies=eur`. The original request referenced a
een `usd`-URL, maar de eis was "prijs t.o.v. de euro" met een `€`-teken; een `usd` URL, but the requirement was "price versus the euro" with a €-sign;
dollarwaarde onder een €-teken zou foutief zijn. showing a dollar amount under a € sign would be wrong.
- De trendvergelijking gebeurt tegen de laatst bekend geraakte prijs. Na een - The trend comparison is made against the last known price. After an API
API-fout blijft die referentie staan, zodat de kleur klopt zodra er weer failure that reference is kept, so the color is correct again as soon as
data binnenkomt. fresh data arrives.
- Zonder CoinGecko API-key kan de API ook tussendoor een 429 geven; de widget - Without a CoinGecko API key the API may occasionally return 429; the widget
toont dan `--` tot de eerstvolgende geslaagde poll. shows `--` until the next successful poll.
- Getest op Debian 12 / Plasma 5 (Qt 5.15). Plasma 6 werkt vermoedelijk ook, - Tested on Plasma 5.27 / Qt 5.15. Plasma 6 is expected to work but is untested.
maar is niet getest.
## Repository-layout ## Debugging
The widget logs to the plasmashell journal:
```bash
journalctl --user -u plasma-plasmashell.service | grep bitcoinprice
```
Expected lines: `started, refresh interval: N minutes`, `fetched EUR <price>`,
or `fetch failed, HTTP status: <code>`.
## Repository layout
``` ```
metadata.desktop metadata.desktop
contents/ contents/
├── config/main.xml # config-schema (refreshMinutes, min 5, max 1440) ├── config/main.xml # config schema (refreshMinutes, min 5, max 1440)
├── images/bitcoin.svg # bundled Bitcoin icon
└── ui/ └── ui/
├── main.qml # widget + fetch/vergelijk-logica ├── main.qml # widget + fetch/compare logic
├── config.qml # configuratievenster (categorieën) ├── config.qml # config dialog (categories)
└── configGeneral.qml # interval-instelling └── configGeneral.qml # interval setting
``` ```
Het kant-en-klare installatiebestand (`bitcoin-price-1.0.0.plasmoid`) staat als Repackage yourself:
bijlage bij de [v1.0.0-release](../../releases/tag/v1.0.0).
Zelf (her)packagen:
```bash ```bash
zip -r bitcoin-price.plasmoid metadata.desktop contents zip -r bitcoin-price.plasmoid metadata.desktop contents
``` ```
## Licentie ## License
MIT — zie [LICENSE](LICENSE). MIT — see [LICENSE](LICENSE).
Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="6.82666in" height="6.82666in" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 6.82666 6.82666"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_x0020_1">
<g id="_372402152">
<circle id="_372402800" fill="#FF9800" cx="3.41333" cy="3.41333" r="2.56"/>
<path id="_372402824" fill="#FFFFFE" d="M3.87171 3.18776c0.0803346,0 0.155882,-0.0312795 0.212665,-0.0880945 0.0568189,-0.0568465 0.0880945,-0.132335 0.0880945,-0.212665 0,-0.0803425 -0.0312756,-0.155886 -0.088122,-0.212701 -0.0567795,-0.0567913 -0.132303,-0.0880669 -0.212638,-0.0880669l-0.827106 0 0 0.601528 0.827106 0zm0.0249606 1.50341l0 0.225992 -0.451146 0 0 -0.225575 -0.200461 0 0 0.225575 -0.45115 0 0 -0.225575 -0.350843 0 0 -0.45115 0.150382 0 0 -1.6542 -0.150382 0 0 -0.45115 0.350843 0 0 -0.225575 0.45115 0 0 0.225575 0.200461 0 0 -0.225575 0.451146 0 0 0.225992c0.191488,0.00620866 0.37061,0.0837126 0.506701,0.219803 0.142051,0.142051 0.220244,0.330874 0.220244,0.531697 0,0.198319 -0.0762953,0.384929 -0.214953,0.526335 0.138697,0.141421 0.214953,0.328024 0.214953,0.526339 0,0.200823 -0.0782205,0.389646 -0.220217,0.531661 -0.136118,0.136122 -0.31524,0.213626 -0.506728,0.219835zm-0.852067 -0.450732l0.827106 0c0.0803346,0 0.155882,-0.0312835 0.212665,-0.0880906 0.0568189,-0.0568465 0.0880945,-0.132343 0.0880945,-0.212673 0,-0.0803346 -0.0312756,-0.155886 -0.088122,-0.212705 -0.0567795,-0.0567795 -0.132303,-0.088063 -0.212638,-0.088063l-0.827106 0 0 0.601531z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+1 -1
View File
@@ -3,7 +3,7 @@ import org.kde.plasma.configuration 2.0
ConfigModel { ConfigModel {
ConfigCategory { ConfigCategory {
name: i18n("Algemeen") name: i18n("General")
icon: "preferences-desktop" icon: "preferences-desktop"
source: "configGeneral.qml" source: "configGeneral.qml"
} }
+22 -3
View File
@@ -1,16 +1,35 @@
/*
* Config page for nl.a14r.bitcoinprice.
* Follows the canonical Plasma 5 config dialog pattern:
* Kirigami.FormLayout + cfg_ prefixed property aliases.
*/
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15 as QQC2
import org.kde.kirigami 2.5 as Kirigami import org.kde.kirigami 2.5 as Kirigami
Kirigami.FormLayout { Kirigami.FormLayout {
id: generalPage
// The cfg_ prefix is what Plasma uses to read/write plasmoid.configuration
property alias cfg_refreshMinutes: intervalSpin.value property alias cfg_refreshMinutes: intervalSpin.value
SpinBox { Item {
Kirigami.FormData.isSection: true
}
QQC2.SpinBox {
id: intervalSpin id: intervalSpin
Kirigami.FormData.label: i18n("Refresh interval (minutes):")
from: 5 from: 5
to: 1440 to: 1440
stepSize: 5 stepSize: 5
editable: true editable: true
Kirigami.FormData.label: i18n("Vernieuwinterval (minuten):") }
QQC2.Label {
text: i18n("CoinGecko allows one request per 5 minutes; lower values are not possible.")
font.italic: true
opacity: 0.7
} }
} }
+66 -35
View File
@@ -1,14 +1,14 @@
/* /*
* nl.a14r.bitcoinprice — KDE Plasma 5 widget * nl.a14r.bitcoinprice — KDE Plasma 5 widget
* *
* Toont de bitcoinprijs in EUR (CoinGecko API). * Shows the bitcoin price in EUR (CoinGecko API).
* - Groen : prijs gestegen t.o.v. vorige meting * - Green : price up compared to the previous reading
* - Rood : prijs gedaald t.o.v. vorige meting * - Red : price down compared to the previous reading
* - Grijs : gelijk gebleven (of eerste meting) * - Grey : unchanged (or first reading)
* - "--" : API-fout (netwerkfout, 4xx/5xx, rate limit 429) * - "--" : API failure (network error, 4xx/5xx, 429 rate limit)
* *
* Vernieuwinterval: configureerbaar, minimaal 5 minuten * Refresh interval: configurable, minimum 5 minutes
* (CoinGecko limiet: 1 call per 5 minuten). * (CoinGecko limit: 1 call per 5 minutes).
*/ */
import QtQuick 2.15 import QtQuick 2.15
@@ -22,42 +22,47 @@ Item {
readonly property int refreshMinutes: Math.max(5, plasmoid.configuration.refreshMinutes) 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" 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 // -1 = down, 0 = unchanged / no previous reading yet, 1 = up
property int trend: 0 property int trend: 0
property double lastPrice: NaN // laatste succesvol opgehaalde prijs property double lastPrice: NaN // last successfully fetched price
property string state: "init" // init | ok | error property string state: "init" // init | ok | error
property string lastUpdated: "" // HH:mm:ss van laatste succesvolle update property string lastUpdated: "" // HH:mm:ss of the last successful update
readonly property string displayText: { readonly property string displayText: {
if (state !== "ok" || isNaN(lastPrice)) { if (state !== "ok" || isNaN(lastPrice)) {
return "--"; return "--";
} }
return "\u20BF " + Number(lastPrice).toLocaleString(Qt.locale(), "f", 0) + " \u20AC"; return Number(lastPrice).toLocaleString(Qt.locale(), "f", 0) + " \u20AC";
} }
readonly property color priceColor: { readonly property color priceColor: {
if (state !== "ok" || isNaN(lastPrice)) { if (state !== "ok" || isNaN(lastPrice)) {
return PlasmaCore.Theme.disabledTextColor; // "--" bij fout/init return PlasmaCore.Theme.disabledTextColor; // "--" on error/init
} }
if (trend > 0) { if (trend > 0) {
return "#2ecc71"; // groen return "#2ecc71"; // green
} }
if (trend < 0) { if (trend < 0) {
return "#e74c3c"; // rood return "#e74c3c"; // red
} }
return "#9e9e9e"; // grijs return "#9e9e9e"; // grey
} }
readonly property string statusText: { readonly property string statusText: {
if (state === "error") { if (state === "error") {
return lastUpdated === "" return lastUpdated === ""
? i18n("API-fout") ? i18n("API error")
: i18n("API-fout — laatste geldige prijs: %1", lastUpdated); : i18n("API error — last known price: %1", lastUpdated);
} }
if (lastUpdated === "") { if (lastUpdated === "") {
return i18n("Bezig met ophalen\u2026"); return i18n("Fetching\u2026");
} }
return i18n("Laatst bijgewerkt: %1", lastUpdated); return i18n("Last updated: %1", lastUpdated);
}
Component.onCompleted: {
console.log("[bitcoinprice] started, refresh interval:",
refreshMinutes, "minutes");
} }
Plasmoid.backgroundHints: PlasmaCore.Types.StandardBackground Plasmoid.backgroundHints: PlasmaCore.Types.StandardBackground
@@ -70,22 +75,38 @@ Item {
Item { Item {
id: compact id: compact
Layout.minimumWidth: priceLabel.implicitWidth + PlasmaCore.Units.smallSpacing * 2 Layout.minimumWidth: row.implicitWidth + PlasmaCore.Units.smallSpacing * 2
Layout.minimumHeight: PlasmaCore.Units.iconSizes.smallMedium Layout.minimumHeight: PlasmaCore.Units.iconSizes.smallMedium
Text { Row {
id: priceLabel id: row
anchors.centerIn: parent anchors.centerIn: parent
text: root.displayText spacing: PlasmaCore.Units.smallSpacing
color: root.priceColor
textFormat: Text.PlainText Image {
elide: Text.ElideRight id: btcIcon
horizontalAlignment: Text.AlignHCenter source: "../images/bitcoin.svg"
verticalAlignment: Text.AlignVCenter sourceSize.width: PlasmaCore.Units.iconSizes.smallMedium
font.family: PlasmaCore.Theme.defaultFont.family sourceSize.height: PlasmaCore.Units.iconSizes.smallMedium
font.pixelSize: compact.height > PlasmaCore.Units.iconSizes.small anchors.verticalCenter: parent.verticalCenter
? Math.round(compact.height * 0.6) fillMode: Image.PreserveAspectFit
: PlasmaCore.Theme.defaultFont.pixelSize visible: root.state === "ok"
}
Text {
id: priceLabel
anchors.verticalCenter: parent.verticalCenter
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
}
} }
} }
} }
@@ -95,12 +116,21 @@ Item {
id: fullRep id: fullRep
implicitWidth: PlasmaCore.Units.gridUnit * 14 implicitWidth: PlasmaCore.Units.gridUnit * 14
implicitHeight: PlasmaCore.Units.gridUnit * 7 implicitHeight: PlasmaCore.Units.gridUnit * 9
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent
spacing: PlasmaCore.Units.smallSpacing spacing: PlasmaCore.Units.smallSpacing
Image {
anchors.horizontalCenter: parent.horizontalCenter
source: "../images/bitcoin.svg"
sourceSize.width: PlasmaCore.Units.iconSizes.large
sourceSize.height: PlasmaCore.Units.iconSizes.large
fillMode: Image.PreserveAspectFit
visible: root.state === "ok"
}
Text { Text {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: root.displayText text: root.displayText
@@ -141,14 +171,15 @@ Item {
try { try {
var price = JSON.parse(xhr.responseText).bitcoin.eur; var price = JSON.parse(xhr.responseText).bitcoin.eur;
if (typeof price === "number" && !isNaN(price)) { if (typeof price === "number" && !isNaN(price)) {
console.log("[bitcoinprice] fetched EUR", price);
root.applyPrice(price); root.applyPrice(price);
return; return;
} }
} catch (e) { } catch (e) {
// ongeldige JSON → val door naar de foutafhandeling // invalid JSON → fall through to error handling
} }
} }
// API-fout (netwerk, 4xx/5xx, rate limit 429, onverwachte payload) console.log("[bitcoinprice] fetch failed, HTTP status:", xhr.status);
root.state = "error"; root.state = "error";
}; };
xhr.send(); xhr.send();
Binary file not shown.
+2 -4
View File
@@ -1,8 +1,6 @@
[Desktop Entry] [Desktop Entry]
Name=Bitcoin Price Name=Bitcoin Price
Name[nl]=Bitcoin-prijs
Comment=Shows the bitcoin price in EUR, color-coded by trend Comment=Shows the bitcoin price in EUR, color-coded by trend
Comment[nl]=Toont de bitcoinprijs in EUR, kleurcodering op trend
Type=Service Type=Service
Icon=bitcoin Icon=bitcoin
@@ -12,8 +10,8 @@ X-KDE-ServiceTypes=Plasma/Applet
X-KDE-PluginInfo-Author=Arnold Cordewiner X-KDE-PluginInfo-Author=Arnold Cordewiner
X-KDE-PluginInfo-Email=hermes@a14r.be X-KDE-PluginInfo-Email=hermes@a14r.be
X-KDE-PluginInfo-Name=nl.a14r.bitcoinprice X-KDE-PluginInfo-Name=nl.a14r.bitcoinprice
X-KDE-PluginInfo-Version=1.0.0 X-KDE-PluginInfo-Version=1.1.0
X-KDE-PluginInfo-Website=http://192.168.110.5:40004/hermes/plasma-bitcoin-price X-KDE-PluginInfo-Website=https://gitea.a14r.be/hermes/plasma-bitcoin-price
X-KDE-PluginInfo-Category=Utilities X-KDE-PluginInfo-Category=Utilities
X-KDE-PluginInfo-License=MIT X-KDE-PluginInfo-License=MIT
X-KDE-PluginInfo-EnabledByDefault=true X-KDE-PluginInfo-EnabledByDefault=true