Zapic's Blog
联想ThinkBook 2021(或者更早的型号以及其他机型的任意型号) 空闲时自动关闭键盘灯

需要电脑已经安装Lenovo Vantage, 如果按下Fn+空格能够切换键盘灯则已经安装.
仅在ThinkBook 15 2021款测试过, 其他机型可能略有不同甚至不受支持.
较旧的机型使用其他方式控制键盘灯, 脚本并未做支持.
base_path各个系统可能不同, 可以尝试自行寻找有IdeaNotebookPlugin.dllContract_Keyboard.dll的目录.
timeout单位为秒.

将代码保存为Powershell脚本(.ps1)并设置开机自动启动即可, 无需管理员特权.
PowerShell脚本:

$base_path = "C:\ProgramData\Lenovo\ImController\Plugins\IdeaNotebookPlugin\x64"
$timeout = 15


Add-Type -Path ($base_path + "\IdeaNotebookPlugin.dll")
Add-Type -Path ($base_path + "\Contract_Keyboard.dll")

$assembly = [System.Reflection.Assembly]::LoadFrom($base_path + "\IdeaNotebookPlugin.dll")
$agent = $assembly.GetTypes()[0].GetMethod("GetInstance").Invoke($null,$null)
$list = New-Object -TypeName Lenovo.Modern.Contracts.Keyboard.SettingList
$request = New-Object -TypeName Lenovo.Modern.Contracts.Keyboard.KeyboardSettingsRequest
$request.List = $list
$item = New-Object -TypeName Lenovo.Modern.Contracts.Keyboard.Setting
$list.Items.Add($item)
$item.key = "KeyboardBacklightStatus"

Add-Type @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PInvoke.Win32 {

    public static class UserInput {

        [DllImport("user32.dll", SetLastError=false)]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

        [StructLayout(LayoutKind.Sequential)]
        private struct LASTINPUTINFO {
            public uint cbSize;
            public int dwTime;
        }

        public static DateTime LastInput {
            get {
                DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
                DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
                return lastInput;
            }
        }

        public static TimeSpan IdleTime {
            get {
                return DateTime.UtcNow.Subtract(LastInput);
            }
        }

        public static int LastInputTicks {
            get {
                LASTINPUTINFO lii = new LASTINPUTINFO();
                lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
                GetLastInputInfo(ref lii);
                return lii.dwTime;
            }
        }
    }
}
'@
function GetBacklightStatus {
    $level = $agent.GetBacklightStatus()
    return ($level.List.Items | where { $_.key -eq "KeyboardBacklightStatus" }).value;
}

$state = 1
$level = GetBacklightStatus
while (1) {
    Start-Sleep -Milliseconds 500
    $idle = [PInvoke.Win32.UserInput]::IdleTime.TotalSeconds
    
    if($idle -gt $timeout) {
        if ($state -eq 0) { continue }
        $state = 0
        $level = GetBacklightStatus
        $item.value = "off"
    } else {
        if ($state -eq 1) { continue }
        $state = 1
        if ((GetBacklightStatus) -ne "off") { continue }
        $item.value = $level
    }
    Write-Host "Dispatch"
    $agent.SetBacklightStatus($request)
}