Featured image of post 通过 Systemd 打造一个合盖也能通过 SSH 控制的 Linux 服务器笔记本

通过 Systemd 打造一个合盖也能通过 SSH 控制的 Linux 服务器笔记本

OLED屏笔记本必看

介绍

我觉得不止我一个拥有多台laptop作为主力机人会遇到这个问题,那就是桌面空间有限,不会每一台电脑都需要他打开盖子。

像我平常是用一个游戏本进行居家的娱乐、办公。一台轻薄Linux / Win双系统作为开发环境,因为在家的笔记本接了鼠标、键盘、显示器。也不方便将轻薄本将其替换。我的轻薄本是ASUS Zenbook S 14 UX5406SA_UX5406SA 目前有以下痛点:

  1. 盒盖后系统会进入休眠,导致所有例如ssh等服务关闭。
  2. 如果单纯忽略盒盖休眠,会导致屏幕未关闭,一个是不够节能,更主要的是害怕OLED烧屏。

想要实现以下功能。

  1. 未插电时 -> 盒盖 :系统默认休眠代理。
  2. 插电时 -> 盒盖 : 关闭屏幕但保持一切服务。
  3. 插电时 -> 开盖 :将屏幕亮度恢复到原来的亮度。

关闭系统默认的休眠代理

关闭桌面环境自动休眠

关闭Gnome自动休眠

按照图片操作在 Setting -> Power -> PowerSaving -> When Plugged in 关闭即可

KDE Xface等桌面环境,与之相似。

关闭Systemd盒盖代理

Systemd 的代理文件在此中

/etc/systemd/logind.conf

对于盒盖有关的Lid句柄如下:
HandleLidSwitch:合上笔记本盖
HandleLidSwitchDocked:链接外部显示器下合上盖子
HandleLidSwitchExternalPower: 当外接电源时合上盖子

他的值包括:

poweroff和halt 都是关机,实现方式不同
suspend 挂起/暂停 设备通电,内容保存在内存中
hybernate 休眠,设备断电(同关机状态),内容保存在硬盘中
hybrid-sleep 混合睡眠,设备通电,内容保存在硬盘和内存中
lock 锁屏
kexec 是从当前正在运行的内核直接引导到一个新内核
ignore 空事件 / 什么都不做

我们只需要进行设置

HandleLidSwitchExternalPower=ignore (默认为suspend)

即可,然后重启服务后重启

systemctl restart systemd-logind
reboot

就此,充电时的默认盒盖事件就被我们都关闭了。

添加自己的盒盖脚本

原理

我们检测三个状态,一个是盖子是否打开,一个是是否有充电,一个是我们要更改的亮度。我们分别按三个部分来说:

供电

供电的信息存储在 /sys/class/power_supply

对于我的电脑,分为 AC0(充电) 与 BAT0(电池), 其中 ./AC0/online 的信息就是电脑判断是否外接电源的文件。1为接电,0为电池带载。

也就是说 只要判断 /sys/class/power_supply/AC0/online 为 1 或 0 即可知道是否接电。

值得注意的是,不同的电脑可能AC也是不同,假如你的电脑有多个充电口,就分为 AC0 、 AC1 等… 也有可能只有一个AC。

盒盖状态

盒盖状态存储在 /proc/acpi/button/lid

对于我的电脑,相关信息存储在 ./LID0/state 当中。其中 open代表开盖,close代表盒盖。

同样的 LID0 与 AC0 情况类似,代表序号。

屏幕背光

屏幕亮度存储在 /sys/class/backlight 当中

不同显卡驱动也不一样,我使用的是intel核显,相关信息存储在 ./intel_backlight 文件当中。 在这当中有两个我们需要关注的文件,一个是 brightness 与 max_brightness 文件。

max_brightness当中为亮度上限,亮度切莫高于此。brightness存储的是当前的屏幕亮度,你可以读取这个值来获取屏幕亮度,同时可以更改这个值来调节屏幕背光。

编写脚本

我提供一个我使用的简单脚本,名字为 lid_watcher.sh 你们只需要根据需要进行更改就可以了。如果你的电脑也是 UX5406 , 应该也能直接用。如果是其他电脑,虽然我做了一些适配,但或许还是需要根据情况修改一下。

#!/bin/bash

# 记录初始亮度
BRIGHTNESS_FILE="/sys/class/backlight/intel_backlight/brightness"
MAX_BRIGHTNESS=$(cat $BRIGHTNESS_FILE)
LAST_STATE="open"

while true; do
	# 获取当前电源状态
	AC_ONLINE=$(cat /sys/class/power_supply/AC*/online)
	# 获取当前 lid 状态
	LID_STATE=$(cat /proc/acpi/button/lid/LID*/state | awk '{print $2}')

	if [ "$AC_ONLINE" -eq 1 ]; then
		if [ "$LID_STATE" == "closed" ]; then
			if [ "$LAST_STATE" == "open" ]; then
				# 盒盖后第一次检测到,保存当前亮度并设置为0
				MAX_BRIGHTNESS=$(cat $BRIGHTNESS_FILE)
				echo 0 | tee $BRIGHTNESS_FILE
				LAST_STATE="closed"
			fi
		else
			if [ "$LAST_STATE" == "closed" ]; then
				# 重新打开盖子,恢复亮度
				echo $MAX_BRIGHTNESS > $BRIGHTNESS_FILE
				LAST_STATE="open"
			fi
		fi
	fi
	
	# 每5秒检测一次
	sleep 5 
done

可以使用 sudo命令对该脚本进行测试,打开后尝试反复开关盖子进行测试,请确定该脚本能够正常使用后再进行之后的操作。

设定systemd服务

将脚本移动到 /usr/local/bin 来确保使用权限

sudo mv lid_watcher.sh /usr/local/bin/lid_watcher.sh
sudo chmod +x /usr/local/bin/lid_watcher.sh

创建一个新的 systemd 服务文件

sudo vim /etc/systemd/system/lid-watcher.service

文件内容如下

[Unit]
Description=Lid and Power Watcher Script
After=multi-user.target
StartLimitIntervalSec=0

[Service]
Type=simple
ExecStart=/usr/local/bin/lid_watcher.sh
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

启动并启用服务

# 重新加载 systemd 配置
sudo systemctl daemon-reexec
sudo systemctl daemon-reload

# 启动服务
sudo systemctl start lid-watcher.service

# 设置为开机自启动
sudo systemctl enable lid-watcher.service

最后检查日志之后

# 查看运行状态
sudo systemctl status lid-watcher.service

# 查看输出日志
journalctl -u lid-watcher.service -f

如果服务正常,我们的脚本就被正常挂在到 systemd 当中了。之后就能享受到 通电时也可盒盖保持屏幕关闭,电脑运行了!

Licensed under CC BY-NC-SA 4.0