Featured image of post 像使用盒子/LibreELEC一样使用PVE直装的Kodi,实现遥控器启停

像使用盒子/LibreELEC一样使用PVE直装的Kodi,实现遥控器启停

本文为《PVE安装Kodi》系列文章的一部分。

前言

在《PVE 直接安装最新版 Kodi》一文的评论中,有网友提出能不能像操控电视盒子/LibreELEC/CoreELCE一样用遥控器操控PVE中直装的Kodi,实现打开和关闭Kodi,这是个好想法,我也认为这样会大方便PVE中直装的Kodi的使用,所以学习了相关udev、udevadm、evtest、input、input remap相关知识,终于实现了这个非常便于使用的功能。

参考资料

udev, udevadm, evtest, input_remap_utilities, evsieve

思路

用遥控器操控PVE中直装的Kodi,实现打开和关闭Kodi,这个场景需要在无桌面环境下,胁持遥控器的电源键,让它的作用变更为打开/关闭Kodi,所以只是将电源键映射为其他键是不行的,而是需要胁持遥控器的电源键,让它变更为运行某个脚本,然后由脚本实现打开/关闭Kodi。

根据 input_remap_utilities 列出的几个按键映射工具,只有 evsieve 可以在无桌面环境下胁持按键并运行指定脚本,实现上述场景。

准备工作

以下相关命令均为root用户执行的。

如果需要新建文件,请直接在Linux环境中使用nano XXXX创建,请不要在Windows环境中创建后再上传。

所有的输入设备都由udev程序识别并保存在/dev/input下,通过输入ls -l /dev/input可以查看到许多eventX的设备。设备的编号会随着外设的插入或卸载以及开关机的变化而变化,也就是说电源键所在的输入设备的eventXX这个编号不是固定的,所以我们不能直接使用/dev/input/eventX来作为后续要胁持的输入设备。运行ls -l /dev/input/by-idudev会在这个目录下创建易于识别的、唯一的、不随外设的插入卸载或开关机而变化的软连接,无论X如何变化,它们都将指向正确的设备。

我的遥控器是自带接收器的2.4G无线遥控器,插上接收器,通过evtest程序先找出遥控器电源键在什么设备上。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 安装evtest
apt install evtest

# 运行evtest
evtest --grab

# evtest会列出所有可用的输入设备,可以看到我的遥控器有event5-event8共4个设备
# 你可以选择具体的编号来进行测试,找到每个设备分别能控制哪些按键
# 经过测试,我发现我遥控器的电源键在event8,也就是2.4G Composite Devic System Control这个设备
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0:      Power Button
/dev/input/event1:      AT Translated Set 2 keyboard
/dev/input/event2:      VirtualPS/2 VMware VMMouse
/dev/input/event3:      VirtualPS/2 VMware VMMouse
/dev/input/event4:      QEMU QEMU USB Tablet
/dev/input/event5:      2.4G Composite Devic
/dev/input/event6:      2.4G Composite Devic Mouse
/dev/input/event7:      2.4G Composite Devic Consumer Control
/dev/input/event8:      2.4G Composite Devic System Control
/dev/input/event9:      PC Speaker
Select the device event number [0-9]:8   # 手动输入要测试的设备编号,如果不清楚电源键在什么设备上,你可以一个一个试出来
Input device ID: bus 0x3 vendor 0x627 product 0x697d version 0x110
Input device name: "2.4G Composite Devic System Control"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 116 (KEY_POWER)
    Event code 142 (KEY_SLEEP)
    Event code 143 (KEY_WAKEUP)
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
Properties:
Testing ... (interrupt to exit)  # 按下电源键,evtest输出以下结果,没有反应表示设备择取不对
Event: time 1669209045.421999, type 4 (EV_MSC), code 4 (MSC_SCAN), value 10081
Event: time 1669209045.421999, type 1 (EV_KEY), code 116 (KEY_POWER), value 1
Event: time 1669209045.421999, -------------- SYN_REPORT ------------
Event: time 1669209045.422021, type 1 (EV_KEY), code 116 (KEY_POWER), value 0
Event: time 1669209045.422021, -------------- SYN_REPORT ------------

# Ctrl+C退出

通过测试,我知道了我遥控器的电源键的键码为116,键名为KEY_POWER,去掉KEY_,键值转换为小写power这个值在后面会用到。不出意外,一般遥控器的电源键的键值都是power

现在来找一下event8这个设备在/dev/input/by-id下的软连接叫什么,运行ls -l /dev/input/by-id/

1
2
3
4
5
6
7
total 0
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-0627_2.4G_Composite_Devic-event-if01 -> ../event7
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-0627_2.4G_Composite_Devic-event-kbd -> ../event5
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-0627_2.4G_Composite_Devic-if01-event-mouse -> ../event6
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-0627_2.4G_Composite_Devic-if01-mouse -> ../mouse3
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-QEMU_QEMU_USB_Tablet_28754-0000:00:01.2-1-event-mouse -> ../event4
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-QEMU_QEMU_USB_Tablet_28754-0000:00:01.2-1-mouse -> ../mouse2

这……udev没有识别出来……准确的讲,是因为udev的默认规则/usr/lib/udev/rules.d/60-persistent-input.rules无法区分2.4G Composite Devic Consumer Control2.4G Composite Devic System Control这两个设备,现象是每次关机再开机或每次卸载再插入接收器后,usb-0627_2.4G_Composite_Devic-event-if01这个设备一会儿指向2.4G Composite Devic Consumer Control,一会儿指向2.4G Composite Devic System Control,所以我需要修改一下udev的规则,让它能够准确识别出2.4G Composite Devic System Control。可能其他遥控器不存在此问题,需要你自行测试一下此问题存在不存在。

现在来让udev能准确识别2.4G Composite Devic System Control,运行udevadm info --attribute-walk --name=/dev/input/event8(其中event8是根据上面evtest测试后知道的),输出内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

looking at device '/devices/pci0000:00/0000:00:1e.0/0000:01:1b.0/usb2/2-1/2-1:1.1/0003:0627:697D.0003/input/input9/event8':
    KERNEL=="event8"
    SUBSYSTEM=="input"
    DRIVER==""
    ATTR{power/async}=="disabled"
    ATTR{power/control}=="auto"
    ATTR{power/runtime_active_kids}=="0"
    ATTR{power/runtime_active_time}=="0"
    ATTR{power/runtime_enabled}=="disabled"
    ATTR{power/runtime_status}=="unsupported"
    ATTR{power/runtime_suspended_time}=="0"
    ATTR{power/runtime_usage}=="0"

looking at parent device '/devices/pci0000:00/0000:00:1e.0/0000:01:1b.0/usb2/2-1/2-1:1.1/0003:0627:697D.0003/input/input9':
    KERNELS=="input9"
    SUBSYSTEMS=="input"
    DRIVERS==""
    ATTRS{capabilities/abs}=="0"
    ATTRS{capabilities/ev}=="13"
    ATTRS{capabilities/ff}=="0"
    ATTRS{capabilities/key}=="c000 10000000000000 0"
    ATTRS{capabilities/led}=="0"
    ATTRS{capabilities/msc}=="10"
    ATTRS{capabilities/rel}=="0"
    ATTRS{capabilities/snd}=="0"
    ATTRS{capabilities/sw}=="0"
    ATTRS{id/bustype}=="0003"
    ATTRS{id/product}=="697d"
    ATTRS{id/vendor}=="0627"
    ATTRS{id/version}=="0110"
    ATTRS{inhibited}=="0"
    ATTRS{name}=="2.4G Composite Devic System Control"
    ATTRS{phys}=="usb-0000:01:1b.0-1/input1"
    ATTRS{power/async}=="disabled"
    ATTRS{power/control}=="auto"
    ATTRS{power/runtime_active_kids}=="0"
    ATTRS{power/runtime_active_time}=="0"
    ATTRS{power/runtime_enabled}=="disabled"
    ATTRS{power/runtime_status}=="unsupported"
    ATTRS{power/runtime_suspended_time}=="0"
    ATTRS{power/runtime_usage}=="0"
    ATTRS{properties}=="0"
    ATTRS{uniq}==""

...下面其他信息此处略去

通过多次运行udevadm info --attribute-walk --name=/dev/input/eventX(X更换为不同的设备编号)可知,我的设备2.4G Composite Devic System Control可以通过ATTRS{name}这个属性即可以进行唯一识别。参考 udev 这个链接,新建/etc/udev/rules.d/01-system-control.rules,内容如下:

1
ATTRS{name}=="2.4G Composite Devic System Control", SYMLINK+="input/by-id/usb-0627_2.4G_Composite_Devic-System-Control"

这种方式是写死的,只针对这一个遥控器有效,复杂的规则咱也写不来…当然如果你想采用更智能的识别方式,可以参考 Writing udev rules编写udev规则

其中SYMLINK+="input/by-id/usb-0627_2.4G_Composite_Devic-System-Control"是指让udev/dev/input/by-id下自动创建usb-0627_2.4G_Composite_Devic-System-Control,其指向2.4G Composite Devic System Control这个设备。软连接名称你可以自行定义。

现在重启下或者重插一下接收器,终于可以识别出来了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ls -l /dev/input/by-id

## 生成了`usb-0627_2.4G_Composite_Devic-System-Control`这个软连接
total 0
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-0627_2.4G_Composite_Devic-event-if01 -> ../event7
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-0627_2.4G_Composite_Devic-event-kbd -> ../event5
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-0627_2.4G_Composite_Devic-System-Control -> ../event8
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-0627_2.4G_Composite_Devic-if01-event-mouse -> ../event6
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-0627_2.4G_Composite_Devic-if01-mouse -> ../mouse3
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-QEMU_QEMU_USB_Tablet_28754-0000:00:01.2-1-event-mouse -> ../event4
lrwxrwxrwx 1 root root 9 Nov 23 22:40 usb-QEMU_QEMU_USB_Tablet_28754-0000:00:01.2-1-mouse -> ../mouse2

设置按键胁持

以下相关命令均为root用户执行的。

如果需要新建文件,请直接在Linux环境中使用nano XXXX创建,请不要在Windows环境中创建后再上传。

  1. 下载我为PVE 7.X预编译好的 evsieve,其他版本不保证可用,如果下载不了,可以按照官方教程自己编译。我会随着Debian和evsieve版本的升级一直更新该文件,请及时在这个仓库查看更新情况:devome/files@github

    1
    2
    3
    4
    5
    
    wget https://raw.githubusercontent.com/devome/files/master/evsieve/evsieve -O /usr/local/bin/evsieve
    chmod +x /usr/local/bin/evsieve
    
    ## 安装evsieve的运行依赖
    apt install libevdev2
    
  2. 按照《PVE 直接安装最新版 Kodi》安装好Kodi,建议按非root用户设置好/etc/systemd/system/kodi.service,注意含有ExecStartExecStop这两行。如果你不需要Kodi开机自动启动,那么创建好这个文件就好了,不需要运行systemctl enable --now kodi.service

  3. 新建一个用来自动判断是启动Kodi还是关闭Kodi的脚本/usr/local/bin/kodi-power.sh,后续由evsieve来使用,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    #!/usr/bin/env bash
    
    export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    
    if [[ -z $(ps -ef | grep "kodi.bin" | grep -v "grep") ]]; then   ## 如果kodi还没有启动,那么就启动它
        systemctl start kodi.service
    else   ## 如果kodi已经启动了,那么就停止它
        systemctl stop kodi.service
    fi
    

    注:停止Kodi这一行,相比使用systemctl stop kodi.service,更推荐使用kodi-send,详见:kodi-send使用相关说明

    注意增加可执行权限:chmod +x /usr/local/bin/kodi-power.sh

  4. 根据 evsieve 的说明,在运行evsieve时,它所要胁持的设备必须已经插入了,但是我又希望evsieve能够开机自动运行,所以我需要在正式运行前先确保2.4G Composite Devic System Control这个设备已经插入。这项工作就交给脚本来判断吧,新建/usr/local/bin/evsieve.sh,内容如下(注意按照注释修改target_hijack_input_devicetarget_hijack_button):

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    #!/usr/bin/env bash
    
    export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    
    ## 要胁持的目标设备的绝对路径,请注意修改成自己的
    ## 不要直接写/dev/input/eventX,而要写/dev/input/by-id下的设备
    target_hijack_input_device="/dev/input/by-id/usb-0627_2.4G_Composite_Devic-System-Control"
    
    ## 要胁持的按键的键名,请注意修改成你获取到的,不出意外,一般遥控器的电源键的键值都是power
    target_hijack_button="power"
    
    ###### 以下无需修改 ######
    ## 先检测 target_hijack_input_device 是否已经存在
    while :; do
        if [[ -L "$target_hijack_input_device" ]]; then
            break
        else
            echo "The '$target_hijack_input_device' is not inserted, wait 2 seconds..."
            sleep 2
        fi
    done
    
    ## 执行电源键胁持,当按下指定键时,转换为运行脚本/usr/local/bin/kodi-power.sh,详细用法请见:https://github.com/KarsMulder/evsieve
    exec evsieve \
        --input "$target_hijack_input_device" grab persist=reopen \
        --hook key:$target_hijack_button exec-shell="/usr/local/bin/kodi-power.sh" \
        --block key:$target_hijack_button
    

    该脚本同样需要可执行权限:chmod +x /usr/local/bin/evsieve.sh

    注意:如果在进行 evtest 时,电源键所在的设备组在 Event type 1 下支持的 Event code 不仅仅只有 KEY_POWER KEY_SLEEP KEY_WAKEUP这三个,那么一般情况下这个设备组还有其他按键,这时不能完全屏蔽掉整个设备,需要将上述脚本中最后四行改成下面这样。这种情况一般出现在蓝牙遥控器上面。

    1
    2
    3
    4
    5
    
    exec evsieve \
        --input "$target_hijack_input_device" grab persist=reopen \
        --hook key:$target_hijack_button exec-shell="/usr/local/bin/kodi-power.sh" \
        --block key:$target_hijack_button \
        --output
    
  5. 新建/etc/systemd/system/evsieve.service,内容如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    [Unit]
    Description = Run evsieve
    Requires = systemd-udevd.service
    After = systemd-udevd.service
    Wants = systemd-udevd.service
    
    [Service]
    User = root
    Group = root
    ExecStart = /usr/local/bin/evsieve.sh
    ExecStop = /usr/bin/killall --user root --exact --wait evsieve
    TimeoutStartSec = infinity
    Restart = on-abort
    
    [Install]
    WantedBy = multi-user.target
    
  6. evsieve.service开机启动启动。这个必须得开机自动启动,要不然胁持不了。运行并确保evsieve.service已经运行好后,按一下遥控器的电源键来试一试效果吧。

    注意:因为Kodi程序比较大,无论是启动Kodi,还是关闭Kodi,系统都需要一定时间来处理,所以不要连续按电源键,这样只会让系统混乱。

    注意:如果evsieve.service没有正常启动,可能按下电源键会让PVE关机。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    ## 设置开机自动启动,并立即运行起来
    systemctl enable --now evsieve.service
    
    ## 再看看是不是evsieve.service是不是启动好了
    systemctl status evsieve.service
    
    ## 如果像这样就表示启动好啦
     evsieve.service - Run evsieve
         Loaded: loaded (/etc/systemd/system/evsieve.service; enabled; vendor preset: enabled)
         Active: active (running) since Thu 2022-11-24 18:11:27 CST; 36min ago
       Main PID: 1249 (evsieve)
          Tasks: 2 (limit: 72123)
         Memory: 1.8M
            CPU: 59ms
         CGroup: /system.slice/evsieve.service
                 └─1249 evsieve --input /dev/input/by-id/usb-0627_2.4G_Composite_Devic-System-Control grab persist=reopen --hook key:power exec-shell=/usr/local/bin/kodi-power.sh --block key:power
    11月 24 18:11:27 pve systemd[1]: Started Run evsieve.
    
  7. 有些遥控器的返回键不是真正的返回键,而是退出键,会直接退出到主菜单,而不是返回到上一层。如果不习惯这样的操作方式,那么我们可以仍然借助evsieve这个工具来实现按键的转换。详见 转换遥控器的退出键为返回键

总结

通过本教程,PVE直接安装Kodi也变得很好用了,就像使用电视盒子/LibreELEC/CoreELEC一样,实现遥控器启动/停止。

Built with Hugo
主题 StackJimmy 设计