diff --git a/simple-battery@suckatcoding.com/CHANGELOG.md b/simple-battery@suckatcoding.com/CHANGELOG.md new file mode 100644 index 000000000..085249e3c --- /dev/null +++ b/simple-battery@suckatcoding.com/CHANGELOG.md @@ -0,0 +1,2 @@ +# 1.0.0 +* Initial release \ No newline at end of file diff --git a/simple-battery@suckatcoding.com/README.md b/simple-battery@suckatcoding.com/README.md new file mode 100644 index 000000000..6a5a79d50 --- /dev/null +++ b/simple-battery@suckatcoding.com/README.md @@ -0,0 +1,56 @@ +# Simple Battery Desklet for Cinnamon + +A minimalist, highly customizable battery desklet for the Cinnamon desktop environment. It provides essential power metrics at a glance without cluttering your workspace. + +![Screenshot placeholder](screenshot.png) + +*(Note: Upload a screenshot of your desklet to your repo and replace this link!)* + +## ✨ Features + +* **Minimalist Design:** A sleek horizontal progress bar and compact text. +* **Real-Time Status:** Shows the exact battery percentage and time remaining (or time until fully charged). +* **Session Tracking:** Calculates how much battery percentage you have consumed (or charged) since you turned on the PC. +* **System Uptime:** Displays how long your current session has been running. +* **Customizable Appearance:** Turn the semi-transparent background on/off and adjust the opacity directly via Cinnamon's desklet settings. +* **Multi-Language Support (i18n):** English and German are included by default. Easily expandable to other languages. + +## 📦 Installation + +### Manual Installation via Git +1. Open your terminal. +2. Clone this repository directly into your Cinnamon desklets folder: + ```bash + git clone [https://github.com/suckatcoding-com/simple-battery-desklet.git](https://github.com/suckatcoding-com/simple-battery-desklet.git) ~/.local/share/cinnamon/desklets/simple-battery@local + ``` +3. Restart Cinnamon so it detects the new desklet. Press Alt + F2, type r, and press Enter. +4. Right-click your desktop, select Add Desklets, go to the Manage tab, and add "Simple Battery" to your desktop. + +## ⚙️ Configuration + +You can easily configure the desklet to match your desktop theme: + +1. Right-click the desklet on your desktop. +2. Select Configure (the gear icon). +3. Toggle the background visibility or adjust the background opacity using the slider. Changes are applied instantly. + +## 🌍 Translations (i18n) + +This desklet supports Gettext for translations. The base language of the code is English. + +Want to add your language? + +1. Create a new .po file in the main directory (e.g., fr.po for French) and translate the msgid strings. +2. Create the locale folder for your language: + ```bash + mkdir -p locale/fr/LC_MESSAGES + ``` +3. Compile the .po file into a .mo file using msgfmt: + ```bash + msgfmt po/fr.po -o locale/fr/LC_MESSAGES/simple-battery@suckatcoding.com.mo + ``` +4. Restart Cinnamon. +5. Feel free to open a Pull Request to share your translation with everyone! + +## 📜 License +This project is licensed under the Apache 2.0 License - see the LICENSE file for details. \ No newline at end of file diff --git a/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/desklet.js b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/desklet.js new file mode 100644 index 000000000..98f0bd61c --- /dev/null +++ b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/desklet.js @@ -0,0 +1,204 @@ +const Desklet = imports.ui.desklet; +const St = imports.gi.St; +const Mainloop = imports.mainloop; +const GLib = imports.gi.GLib; +const Gio = imports.gi.Gio; +const ByteArray = imports.byteArray; +const Settings = imports.ui.settings; +const Gettext = imports.gettext; + +const UUID = "simple-battery@suckatcoding.com"; +const DESKLET_DIR = GLib.get_user_data_dir() + "/cinnamon/desklets/" + UUID; +Gettext.bindtextdomain(UUID, DESKLET_DIR + "/locale"); + +function _(str) { + return Gettext.dgettext(UUID, str); +} + +function SimpleBatteryDesklet(metadata, desklet_id) { + this._init(metadata, desklet_id); +} + +SimpleBatteryDesklet.prototype = { + __proto__: Desklet.Desklet.prototype, + + _init: function(metadata, desklet_id) { + Desklet.Desklet.prototype._init.call(this, metadata, desklet_id); + + this.settings = new Settings.DeskletSettings(this, this.metadata["uuid"], desklet_id); + this.settings.bindProperty(Settings.BindingDirection.IN, "show-background", "show_background", this.on_setting_changed, null); + this.settings.bindProperty(Settings.BindingDirection.IN, "bg-opacity", "bg_opacity", this.on_setting_changed, null); + + this.setupUI(); + this.update(); + }, + + setupUI: function() { + this.container = new St.BoxLayout({vertical: true, style_class: "battery-desklet"}); + + this.barContainer = new St.BoxLayout({vertical: false}); + this.track = new St.BoxLayout({style_class: "battery-track"}); + this.fill = new St.BoxLayout({style_class: "battery-fill"}); + this.track.add_actor(this.fill); + this.pctLabel = new St.Label({style_class: "battery-pct-small", text: "--%"}); + this.barContainer.add_actor(this.track); + this.barContainer.add_actor(this.pctLabel); + + this.timeLabel = new St.Label({style_class: "battery-time", text: "..."}); + + this.divider = new St.BoxLayout({style_class: "battery-divider"}); + this.uptimeLabel = new St.Label({style_class: "battery-stats", text: "..."}); + this.usageLabel = new St.Label({style_class: "battery-stats", text: "..."}); + + this.container.add_actor(this.barContainer); + this.container.add_actor(this.timeLabel); + this.container.add_actor(this.divider); + this.container.add_actor(this.uptimeLabel); + this.container.add_actor(this.usageLabel); + + this.setContent(this.container); + this.on_setting_changed(); + }, + + on_setting_changed: function() { + if (this.show_background) { + let alpha = this.bg_opacity / 100; + this.container.set_style("background-color: rgba(0, 0, 0, " + alpha + ");"); + } else { + this.container.set_style("background-color: transparent;"); + } + }, + + update: function() { + let hasBatteryData = false; + try { + let procBat = Gio.Subprocess.new( + ['bash', '-c', 'upower -i $(upower -e | grep -i bat | head -n 1)'], + Gio.SubprocessFlags.STDOUT_PIPE + ); + + procBat.communicate_utf8_async(null, null, (proc, res) => { + try { + let [ok, outBat, stderr] = proc.communicate_utf8_finish(res); + if (ok) { + hasBatteryData = this.parseAndUpdate(outBat || ""); + } + } catch (e) { + this.timeLabel.set_text(_("Error reading data")); + } + this.scheduleNextUpdate(hasBatteryData); + }); + + } catch (e) { + this.timeLabel.set_text(_("Error reading data")); + this.scheduleNextUpdate(false); + } + }, + + scheduleNextUpdate: function(hasBatteryData) { + if (this.timeout) Mainloop.source_remove(this.timeout); + let nextUpdate = hasBatteryData ? 60 : 5; + this.timeout = Mainloop.timeout_add_seconds(nextUpdate, () => { + this.update(); + return false; + }); + }, + + parseAndUpdate: function(output) { + let matchPct = output.match(/percentage:\s+(\d+)%/); + let matchEmpty = output.match(/time to empty:\s+(.*)/); + let matchFull = output.match(/time to full:\s+(.*)/); + let matchState = output.match(/state:\s+(.*)/); + + let pctNum = 0; + let hasBatteryData = false; + + if (matchPct) { + pctNum = parseInt(matchPct[1]); + hasBatteryData = true; + this.pctLabel.set_text(matchPct[1] + "%"); + let fillWidth = Math.round(150 * (pctNum / 100)); + this.fill.set_style("width: " + fillWidth + "px;"); + + let state = matchState ? matchState[1] : ""; + if (state === "discharging" && matchEmpty) { + this.timeLabel.set_text(matchEmpty[1] + " " + _("remaining")); + } else if (state === "charging" && matchFull) { + this.timeLabel.set_text(matchFull[1] + " " + _("until full")); + } else if (state === "fully-charged") { + this.timeLabel.set_text(_("Fully charged")); + } else { + this.timeLabel.set_text(_("AC Power")); + } + } else { + this.pctLabel.set_text("--%"); + this.fill.set_style("width: 0px;"); + this.timeLabel.set_text(_("Connecting...")); + } + + if (hasBatteryData) { + let tmpPath = "/tmp/desklet_start_bat_" + UUID + ".txt"; + let tmpFile = Gio.File.new_for_path(tmpPath); + + tmpFile.load_contents_async(null, (file, res) => { + try { + let [success, contents] = file.load_contents_finish(res); + if (success) { + let contentStr = ByteArray.toString(contents).trim(); + let startPct = contentStr !== "" ? parseInt(contentStr) : pctNum; + this.updateUsageUI(startPct, pctNum); + } + } catch (e) { + try { + GLib.file_set_contents(tmpPath, pctNum.toString()); + } catch (err) {} + this.updateUsageUI(pctNum, pctNum); + } + }); + } else { + this.usageLabel.set_text(_("Usage:") + " --"); + } + + let uptimeFile = Gio.File.new_for_path("/proc/uptime"); + uptimeFile.load_contents_async(null, (file, res) => { + try { + let [success, contents] = file.load_contents_finish(res); + if (success) { + let outUptime = ByteArray.toString(contents); + let uptimeSeconds = parseFloat(outUptime.split(" ")[0]); + let hours = Math.floor(uptimeSeconds / 3600); + let minutes = Math.floor((uptimeSeconds % 3600) / 60); + + if (hours > 0) { + this.uptimeLabel.set_text(_("Uptime:") + " " + hours + _("h") + " " + minutes + _("min")); + } else { + this.uptimeLabel.set_text(_("Uptime:") + " " + minutes + _("min")); + } + } + } catch (e) {} + }); + + return hasBatteryData; + }, + + updateUsageUI: function(startPct, currentPct) { + let diff = startPct - currentPct; + if (diff > 0) { + this.usageLabel.set_text(_("Usage:") + " " + diff + "% " + _("since start")); + } else if (diff < 0) { + this.usageLabel.set_text(_("Charged:") + " " + Math.abs(diff) + "% " + _("since start")); + } else { + this.usageLabel.set_text(_("Usage:") + " 0% " + _("since start")); + } + }, + + on_desklet_removed: function() { + if (this.timeout) { + Mainloop.source_remove(this.timeout); + } + } +}; + +function main(metadata, desklet_id) { + return new SimpleBatteryDesklet(metadata, desklet_id); +} \ No newline at end of file diff --git a/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/icon.png b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/icon.png new file mode 100644 index 000000000..ed83e7f57 Binary files /dev/null and b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/icon.png differ diff --git a/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/metadata.json b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/metadata.json new file mode 100644 index 000000000..1bd155e96 --- /dev/null +++ b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/metadata.json @@ -0,0 +1,7 @@ +{ + "uuid": "simple-battery@suckatcoding.com", + "name": "Simple Battery", + "description": "Displays battery status, uptime, and usage in a sleek, minimalist progress bar.", + "prevent-decorations": true, + "version": "1.0.0" +} \ No newline at end of file diff --git a/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/po/de.po b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/po/de.po new file mode 100644 index 000000000..6dcd06685 --- /dev/null +++ b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/po/de.po @@ -0,0 +1,81 @@ +# SIMPLE BATTERY +# This file is put in the public domain. +# Nick 'Milchreis' Müller, 2026 +# +msgid "" +msgstr "" +"Project-Id-Version: simple-battery@suckatcoding.com 1.0.0\n" +"Report-Msgid-Bugs-To: https://github.com/linuxmint/cinnamon-spices-desklets/" +"issues\n" +"POT-Creation-Date: 2026-06-14 10:54+0200\n" +"PO-Revision-Date: 2026-06-14 11:00+0200\n" +"Last-Translator: Nick 'Milchreis' Müller\n" +"Language-Team: German\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. desklet.js:86 desklet.js:91 +msgid "Error reading data" +msgstr "Fehler beim Auslesen" + +#. desklet.js:115 +msgid "remaining" +msgstr "verbleibend" + +#. desklet.js:117 +msgid "until full" +msgstr "bis voll" + +#. desklet.js:119 +msgid "Fully charged" +msgstr "Vollständig geladen" + +#. desklet.js:121 +msgid "AC Power" +msgstr "Netzbetrieb" + +#. desklet.js:161 desklet.js:163 +msgid "Uptime:" +msgstr "Laufzeit:" + +#. desklet.js:161 +msgid "h" +msgstr "Std" + +#. desklet.js:161 desklet.js:163 +msgid "min" +msgstr "Min" + +#. desklet.js:174 desklet.js:178 +msgid "Usage:" +msgstr "Verbrauch:" + +#. desklet.js:174 desklet.js:176 desklet.js:178 +msgid "since start" +msgstr "seit Start" + +#. desklet.js:176 +msgid "Charged:" +msgstr "Geladen:" + +#. metadata.json->name +msgid "Simple Battery" +msgstr "Schlichter Akku" + +#. metadata.json->description +msgid "" +"Displays battery status, uptime, and usage in a sleek, minimalist progress " +"bar." +msgstr "" +"Zeigt den Akkustand, die Laufzeit und den Verbrauch in einem schlichten, " +"minimalistischen Fortschrittsbalken an." + +#. settings-schema.json->show-background->description +msgid "Show background" +msgstr "Hintergrund anzeigen" + +#. settings-schema.json->bg-opacity->description +msgid "Background opacity (%)" +msgstr "Hintergrund-Deckkraft (%)" \ No newline at end of file diff --git a/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/po/simple-battery@suckatcoding.com.pot b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/po/simple-battery@suckatcoding.com.pot new file mode 100644 index 000000000..e080810d7 --- /dev/null +++ b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/po/simple-battery@suckatcoding.com.pot @@ -0,0 +1,80 @@ +# SIMPLE BATTERY +# This file is put in the public domain. +# Nick 'Milchreis' Müller, 2026 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: simple-battery@suckatcoding.com 1.0.0\n" +"Report-Msgid-Bugs-To: https://github.com/linuxmint/cinnamon-spices-desklets/" +"issues\n" +"POT-Creation-Date: 2026-06-14 10:54+0200\n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. desklet.js:86 desklet.js:91 +msgid "Error reading data" +msgstr "" + +#. desklet.js:115 +msgid "remaining" +msgstr "" + +#. desklet.js:117 +msgid "until full" +msgstr "" + +#. desklet.js:119 +msgid "Fully charged" +msgstr "" + +#. desklet.js:121 +msgid "AC Power" +msgstr "" + +#. desklet.js:161 desklet.js:163 +msgid "Uptime:" +msgstr "" + +#. desklet.js:161 +msgid "h" +msgstr "" + +#. desklet.js:161 desklet.js:163 +msgid "min" +msgstr "" + +#. desklet.js:174 desklet.js:178 +msgid "Usage:" +msgstr "" + +#. desklet.js:174 desklet.js:176 desklet.js:178 +msgid "since start" +msgstr "" + +#. desklet.js:176 +msgid "Charged:" +msgstr "" + +#. metadata.json->name +msgid "Simple Battery" +msgstr "" + +#. metadata.json->description +msgid "" +"Displays battery status, uptime, and usage in a sleek, minimalist progress " +"bar." +msgstr "" + +#. settings-schema.json->show-background->description +msgid "Show background" +msgstr "" + +#. settings-schema.json->bg-opacity->description +msgid "Background opacity (%)" +msgstr "" diff --git a/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/settings-schema.json b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/settings-schema.json new file mode 100644 index 000000000..1e99f4cd7 --- /dev/null +++ b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/settings-schema.json @@ -0,0 +1,16 @@ +{ + "show-background": { + "type": "checkbox", + "default": true, + "description": "Show background" + }, + "bg-opacity": { + "type": "scale", + "default": 40, + "min": 0, + "max": 100, + "step": 5, + "description": "Background opacity (%)", + "dependency": "show-background" + } +} \ No newline at end of file diff --git a/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/stylesheet.css b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/stylesheet.css new file mode 100644 index 000000000..7739a560c --- /dev/null +++ b/simple-battery@suckatcoding.com/files/simple-battery@suckatcoding.com/stylesheet.css @@ -0,0 +1,83 @@ +.battery-desklet { + /* background-color wurde hier entfernt! */ + border-radius: 10px; + padding: 15px 20px; + color: #ffffff; + font-family: sans-serif; + min-width: 210px; +} + +.battery-track { + background-color: rgba(255, 255, 255, 0.2); + border-radius: 5px; + height: 12px; + width: 150px; +} + +.battery-fill { + background-color: #eeeeee; + border-radius: 5px; + height: 12px; +} + +.battery-pct-small { + font-size: 14px; + font-weight: bold; + margin-left: 12px; + text-shadow: 1px 1px 2px rgba(0,0,0,0.8); + min-width: 45px; +} + +.battery-time { + font-size: 13px; + margin-top: 10px; + color: #dddddd; + text-shadow: 1px 1px 2px rgba(0,0,0,0.8); +} + +.battery-desklet { + border-radius: 10px; + padding: 15px 20px; + color: #ffffff; + font-family: sans-serif; +} + +.battery-track { + background-color: rgba(255, 255, 255, 0.2); + border-radius: 5px; + height: 12px; + width: 150px; +} + +.battery-fill { + background-color: #eeeeee; + border-radius: 5px; + height: 12px; +} + +.battery-pct-small { + font-size: 14px; + font-weight: bold; + margin-left: 12px; + text-shadow: 1px 1px 2px rgba(0,0,0,0.8); +} + +.battery-time { + font-size: 13px; + margin-top: 10px; + color: #dddddd; + text-shadow: 1px 1px 2px rgba(0,0,0,0.8); +} + +.battery-divider { + height: 1px; + background-color: rgba(255, 255, 255, 0.2); + margin-top: 8px; + margin-bottom: 8px; +} + +.battery-stats { + font-size: 11px; + color: #bbbbbb; + text-shadow: 1px 1px 2px rgba(0,0,0,0.8); +} \ No newline at end of file diff --git a/simple-battery@suckatcoding.com/info.json b/simple-battery@suckatcoding.com/info.json new file mode 100644 index 000000000..166863c96 --- /dev/null +++ b/simple-battery@suckatcoding.com/info.json @@ -0,0 +1,4 @@ +{ + "author": "Nick 'Milchreis' Müller", + "license": "Apache 2.0" +} diff --git a/simple-battery@suckatcoding.com/screenshot.png b/simple-battery@suckatcoding.com/screenshot.png new file mode 100644 index 000000000..9ff1de69f Binary files /dev/null and b/simple-battery@suckatcoding.com/screenshot.png differ