diff --git a/thinkpad-led/README.md b/thinkpad-led/README.md new file mode 100644 index 0000000..a4e2013 --- /dev/null +++ b/thinkpad-led/README.md @@ -0,0 +1,27 @@ +# ThinkPad Logo LED + +A simple Noctalia bar widget to toggle the ThinkPad lid logo LED on and off. + +## Plugin + +| Property | Value | +| --- | --- | +| **ID** | `zeti1223/thinkpad-led` | +| **Widget** | `led` | +| **Version** | `1.1.0` | +| **Author** | zeti1223 | +| **License** | MIT | + +## Usage + +Add the `led` widget to your Noctalia bar layout. Clicking the widget toggles the ThinkPad logo LED state. + +* **Left Click**: Toggle LED (ON / OFF). + +## Requirements + +* `sh` + +## Notes + +* **Hardware & Permissions**: This plugin writes directly to `/sys/class/leds/tpacpi::lid_logo_dot/brightness`. Ensure your user account has write permissions to this path (for example, via a `udev` rule), otherwise command execution will fail. diff --git a/thinkpad-led/plugin.toml b/thinkpad-led/plugin.toml new file mode 100644 index 0000000..d6d7edc --- /dev/null +++ b/thinkpad-led/plugin.toml @@ -0,0 +1,14 @@ +id = "zeti1223/thinkpad-led" +name = "ThinkPad Logo LED" +version = "1.1.0" +plugin_api = 3 +author = "zeti1223" +license = "MIT" +dependencies = ["sh"] +tags = ["bar", "hardware"] +icon = "bulb" +description = "Turn the ThinkPad lid logo LED on or off." + +[[widget]] +id = "led" +entry = "widget.luau" \ No newline at end of file diff --git a/thinkpad-led/thumbnail.webp b/thinkpad-led/thumbnail.webp new file mode 100644 index 0000000..265cb97 Binary files /dev/null and b/thinkpad-led/thumbnail.webp differ diff --git a/thinkpad-led/translations/en.json b/thinkpad-led/translations/en.json new file mode 100644 index 0000000..69a88e3 --- /dev/null +++ b/thinkpad-led/translations/en.json @@ -0,0 +1 @@ +{} diff --git a/thinkpad-led/widget.luau b/thinkpad-led/widget.luau new file mode 100644 index 0000000..d5911bc --- /dev/null +++ b/thinkpad-led/widget.luau @@ -0,0 +1,101 @@ +--!nonstrict +-- ThinkPad lid logo LED — bar widget +-- A red dot in the bar that directly controls the /sys/class/leds/tpacpi::lid_logo_dot LED. +-- Left click : toggle on / off + +local LED_PATH = "/sys/class/leds/tpacpi::lid_logo_dot/brightness" + +-- Tri-state: true = on, false = off, nil = unknown (path missing, unreadable, etc). +local ledOn = nil +local pending = false -- true while a toggle write is in flight +local writeFailed = false -- true if the last write attempt did not succeed + +-- Reads the LED brightness straight from sysfs instead of trusting any cached +-- guess. Returns nil when the file can't be read (missing device, no +-- permissions, etc), which the UI renders as an "unavailable" state. +local function readLedState() + local contents = noctalia.readFile(LED_PATH) + if not contents then + return nil + end + local value = tonumber((contents:gsub("%s+", ""))) + if not value then + return nil + end + return value > 0 +end + +local function statusColor() + if ledOn then + return "error" + else + return "outline" + end +end + +local function render() + barWidget.render(ui.box({ + width = 10, + height = 10, + radius = 5, + fill = statusColor(), + border = "outline", + borderWidth = 1, + })) + + if ledOn == nil then + barWidget.setTooltip("ThinkPad LED: unavailable (check permissions on " .. LED_PATH .. ")") + elseif pending then + barWidget.setTooltip("ThinkPad LED: updating…") + elseif writeFailed then + barWidget.setTooltip("ThinkPad LED: last toggle failed (permission denied?)") + elseif ledOn then + barWidget.setTooltip("ThinkPad LED: ON (Left click: turn off)") + else + barWidget.setTooltip("ThinkPad LED: OFF (Left click: turn on)") + end +end + +function update() + if not pending then + ledOn = readLedState() + end + render() +end + +function onClick() + if pending then + return + end + + -- Toggle off the actual hardware state, not a guessed one. + local current = readLedState() + if current == nil then + ledOn = nil + render() + return + end + + local target = not current + local val = target and "255" or "0" + + pending = true + writeFailed = false + render() + + local started = noctalia.runAsync(`sh -c "echo {val} > {LED_PATH}"`, function(result) + pending = false + writeFailed = result.exitCode ~= 0 + -- Trust the hardware over the write result: read the brightness back + -- rather than assuming the echo did what we asked. + ledOn = readLedState() + render() + end) + + if not started then + pending = false + writeFailed = true + ledOn = readLedState() + render() + end +end