Skip to content

New plugin: ThinkPad - Fan & Thermal Control#870

Draft
Piero-93 wants to merge 15 commits into
noctalia-dev:mainfrom
Piero-93:feature/fan-state
Draft

New plugin: ThinkPad - Fan & Thermal Control#870
Piero-93 wants to merge 15 commits into
noctalia-dev:mainfrom
Piero-93:feature/fan-state

Conversation

@Piero-93
Copy link
Copy Markdown

Description

This PR adds the ThinkPad Fan Control (thinkpad-fan) plugin to the community registry.

It provides real-time hardware monitoring and control for Lenovo ThinkPad laptops by interfaces with /proc/acpi/ibm/fan and system thermal zones. The plugin has been fully updated to utilize Noctalia's native settings API and UX guidelines, integrating seamlessly with the system settings panel.

Features

  • Real-time Monitoring: Passive tracking of fan speed (RPM), current ACPI fan level, and CPU/System temperature.
  • Dynamic Coloring: Optional visual cues that change the bar capsule color based on fan status (e.g., when the fan is stopped or set to a custom override level).
  • Native Integration: matching Noctalia's UI design language
  • Compatible with every laptop with /proc/acpi/ibm/fan interface.

Plugin Structure

thinkpad-fan/
├── manifest.json      # Metadata with default settings declared for persistence
├── preview.png        # 16:9 preview image
├── README.md          # Documentation and configuration requirements
├── BarWidget.qml      # Main bar UI component
└── Settings.qml       # Native settings module

Test

Tested on 2 ThinkPad T14 Gen 1.

@github-actions

This comment was marked as resolved.

@spiros132 spiros132 marked this pull request as draft May 18, 2026 17:59
@Piero-93 Piero-93 marked this pull request as ready for review May 18, 2026 20:07
@spiros132
Copy link
Copy Markdown
Collaborator

This PR also contains your other PR about the battery-and-power-management plugin. Prefer to have one PR for one plugin.

@github-actions

This comment was marked as resolved.

@Piero-93
Copy link
Copy Markdown
Author

Hi, thanks, I'm sorry but this are my very first PRs and I'm not very skilled with git. I'll work on it!

@Piero-93 Piero-93 marked this pull request as draft May 22, 2026 09:56
@Piero-93 Piero-93 marked this pull request as ready for review May 22, 2026 10:06
@spiros132
Copy link
Copy Markdown
Collaborator

Hi, thanks, I'm sorry but this are my very first PRs and I'm not very skilled with git. I'll work on it!

No worries at all, if you need any help just ask! :)

Copy link
Copy Markdown
Collaborator

@spiros132 spiros132 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some feedback about the PR! :D

readonly property string screenName: screen ? (screen.name ?? "") : ""
readonly property real capsuleHeight: (typeof Style !== "undefined" && typeof Style.getCapsuleHeightForScreen === "function") ? Style.getCapsuleHeightForScreen(root.screenName) : 26
readonly property real barFontSize: (typeof Style !== "undefined" && typeof Style.getBarFontSizeForScreen === "function") ? Style.getBarFontSizeForScreen(root.screenName) : 10
readonly property string fixedFont: (typeof Settings !== "undefined" && Settings.data?.ui?.fontFixed) ? Settings.data.ui.fontFixed : "monospace"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Settings and Style should never be undefined. If they are then something has gone wrong. You don't need to check if they are undefined or not.

pluginApi?.manifest?.metadata?.defaultSettings?.allowPopupOpening ??
true

readonly property real contentWidth: layout.implicitWidth + ((typeof Style !== "undefined") ? Style.marginS * 2 : 8)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well, you don't need to check if Style is undefined or not.

readonly property real contentWidth: layout.implicitWidth + ((typeof Style !== "undefined") ? Style.marginS * 2 : 8)
readonly property real contentHeight: capsuleHeight
implicitWidth: contentWidth
implicitHeight: (typeof Style !== "undefined") ? Style.barHeight : 32
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here either

import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Io as QSIo
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why this needs to be imported as a separate name?

}

// Hardware fan monitoring (passive tracking bound to thinkfan)
QSIo.FileView {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just use the regular FileView no need to specify where the component comes from.

}

Timer { id: refreshTimer; interval: 300; repeat: false; onTriggered: { fanLoader.reload(); tempLoader.reload(); } }
Timer { interval: 2000; running: true; repeat: true; triggeredOnStart: true; onTriggered: { fanLoader.reload(); tempLoader.reload(); } }
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this timer really needed? I believe that the FileView component gets updated immediately when any change happens on the file.

anchors.centerIn: parent
width: root.contentWidth
height: root.contentHeight
radius: (typeof Style !== "undefined") ? Style.radiusL : 6
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As before, you don't need to check if Style is undefined or not.

: (root.isCustomActive
? ((typeof Color !== "undefined") ? Color.mPrimary : "#3355ff")
: (root.fanLevel === "0" ? "#cc241d" : ((typeof Style !== "undefined") ? Style.capsuleBorderColor : "#33ffffff")))
border.width: (typeof Style !== "undefined") ? Style.capsuleBorderWidth : 1
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to check if Style or any other singleton component exists on any of these.

I won't comment on each and every one to skip spam.

Comment thread thinkpad-fan/FanPopup.qml
pointSize: (typeof Style !== "undefined") ? Style.fontSizeS : 10
color: parent.isSelected
? ((typeof Color !== "undefined") ? Color.mOnPrimary : "#ffffff")
: ((typeof Color !== "undefined") ? Color.mOnSurface : "#ffffff")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As before, no need to check if these singleton are undefined or not.

Comment thread thinkpad-fan/README.md

Grant group write parameters over systemic thermal interfaces by applying the setup script:

cd ~/.config/noctalia/plugins/fan-speed/
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't the correct plugin name.

@spiros132 spiros132 marked this pull request as draft May 24, 2026 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants