SideSail——Ubuntu 26.04(GNOME 50)侧边栏插件,支持设备信息剪贴板和米家设备简单控制

SideSail

SideSail 是一个 GNOME Shell 50/50.1 侧边栏扩展,提供系统信息、性能历史、天气、小组件、剪贴板历史,以及基于 python-miio/miiocli 的小米/米家本地智能家居控制。

Github: CairBin/SideSail

功能

  • 顶栏按钮打开/关闭侧边栏。
  • Info:系统、硬件、桌面环境信息。
  • System:CPU、内存、网络、磁盘、电池、温度、进程列表。
  • Weather:Open-Meteo 天气、天气动画、日出日落、日照进度。
  • Widgets:快捷系统状态、性能历史图表、剪贴板历史、智能家居控制。
  • 主题支持跟随系统、强制暗色、强制亮色。

安装

先进入扩展目录:

bash 复制代码
cd /home/owo/MyCode/rust/ubuntu_sidebar/sidebar

执行安装脚本:

bash 复制代码
./install.sh

脚本会复制扩展到:

text 复制代码
~/.local/share/gnome-shell/extensions/SideSail@top.cairbin

然后启用扩展:

bash 复制代码
gnome-extensions enable SideSail@top.cairbin

查看状态:

bash 复制代码
gnome-extensions info SideSail@top.cairbin

如果显示 Enabled: YesState: INACTIVE,通常需要重新加载 GNOME Shell:

  • Wayland:注销后重新登录。
  • X11:按 Alt + F2,输入 r,回车。

打开设置

可以用 GNOME 扩展设置打开:

bash 复制代码
gnome-extensions prefs SideSail@top.cairbin

设置里可以调整:

  • 侧边栏左右位置。
  • 顶栏按钮左右位置。
  • 主题模式。
  • 天气位置。
  • 剪贴板历史保留条数,默认 8 条。
  • 智能家居总开关、Python 命令和设备 JSON。

天气配置

默认经纬度都是 0 时,扩展会尝试用 IP 定位。更准确的方式是在设置里填写:

  • Location name:显示名称,例如 Hangzhou
  • Latitude:纬度。
  • Longitude:经度。

小组件配置

Widgets 分组里的 Clipboard history limit 用来控制剪贴板历史最多保留多少条。默认是 8

这个限制同时作用于两处:

  • 内存里只保留最近 N 条,旧内容会被丢弃。
  • Widgets 页的剪贴板历史也只显示最近 N 条。

如果剪贴板条目太多导致界面溢出,建议保持默认 8,或者设置成更小的值。

智能家居依赖

智能家居控制通过本地 Python helper 调用 python-miio,GNOME Shell 本身不会直接加载 Python 库。

为了避免切换到陌生网络时误触设备控制或暴露设备 token,智能家居命令默认是关闭的。你需要在 Widgets 页的智能家居卡片点击 启用,或在扩展设置的 Smart Home -> Enable smart home commands 打开后,SideSail 才会读取状态或执行 miiocli 命令。关闭后,按钮和滑杆都不会调用 helper。

你需要安装支持modern device(MIoT)的python-miio,请不要用pipx install python-miio,而是:

sh 复制代码
pipx install git+https://github.com/rytilahti/python-miio.git

更多信息请参考rytilahti/python-miio

除此之外,你需要手动修改click的版本,否则会导致miiocli的命令报错,详细参考python-miio issues#2048

对于使用pipx安装的你可以执行如下命令:

sh 复制代码
pipx runpip miio install click==8.0.0 --force-reinstall
pipx inject miio click==8.0.0 --force

设备 JSON 放在哪里

打开扩展设置:

bash 复制代码
gnome-extensions prefs SideSail@top.cairbin

Smart Home 分组里,把设备数组填到 Device JSON

如果你使用 pipx install python-miio 安装依赖,Python command 通常不用改;SideSail 会自动尝试使用 pipx 里的 Python。只有自动识别失败时,才需要手动填写:

text 复制代码
/home/owo/.local/share/pipx/venvs/python-miio/bin/python

genericmiot 最小示例

以下所有关于命令参数部分,请以python-miio和具体设备支持为准

json 复制代码
[
  {
    "name": "书桌台灯",
    "room": "书桌",
    "ip": "192.168.1.20",
    "token": "这里填写32位token",
    "kind": "genericmiot",
    "status": "status",
    "actions": [
      {"name": "status", "label": "状态", "command": "status"},
      {"name": "off", "label": "关闭", "command": "call 这里替换成你测试通过的动作"}
    ]
  }
]

配置后打开侧边栏的 Widgets 页,智能家居卡片会按 actions 显示按钮。

这段配置的含义:

字段 怎么填
name 显示在卡片上的设备名称,例如 书桌台灯
room 房间或分组名,可选,例如 书桌卧室
ip 设备在局域网里的 IP,例如 192.168.1.20
token 设备 token,通常是 32 位十六进制字符串
kind 使用自定义 miiocli genericmiot 命令时固定写 genericmiot
status 刷新状态时执行的命令片段,常用 "status"
actions 前端要显示的按钮数组
actions[].name 按钮内部 ID,只要同一设备内不重复即可
actions[].label 按钮上显示的文字,例如 开启关闭切换
actions[].command 点击按钮时执行的命令片段
controls 前端要显示的滑杆数组,适合亮度、色温、风速等连续数值
controls[].min 滑杆最小值
controls[].max 滑杆最大值
controls[].step 每次拖动变化的步进
controls[].unit 显示单位,例如 %K
controls[].command 松开滑杆时执行的命令片段,用 $value 表示当前值

command 只填写 miiocli genericmiot --ip <ip> --token <token> 后面的部分。例如你在终端能跑通:

bash 复制代码
miiocli genericmiot --ip 192.168.1.20 --token 0123456789abcdef0123456789abcdef status
miiocli genericmiot --ip 192.168.1.20 --token 0123456789abcdef0123456789abcdef call light:toggle

那么 JSON 里就写:

json 复制代码
{
  "status": "status",
  "actions": [
    {"name": "status", "label": "状态", "command": "status"},
    {"name": "toggle", "label": "切换", "command": "call light:toggle"}
  ]
}

如果命令里包含空格、引号或复杂参数,推荐把 command 写成数组,插件会按数组原样传给 miiocli

json 复制代码
{
  "actions": [
    {"name": "on", "label": "开启", "command": ["set", "light:on", "true"]},
    {"name": "off", "label": "关闭", "command": ["set", "light:on", "false"]}
  ]
}

亮度、色温这类需要拖动调节的设备,可以加 controls。例如亮度范围是 1-100,色温范围是 2700-6500K

json 复制代码
[
  {
    "name": "书桌台灯",
    "room": "书桌",
    "ip": "192.168.1.20",
    "token": "0123456789abcdef0123456789abcdef",
    "kind": "genericmiot",
    "status": "status",
    "actions": [
      {"name": "toggle", "label": "切换", "command": "call light:toggle"}
    ],
    "controls": [
      {
        "name": "brightness",
        "label": "亮度",
        "min": 1,
        "max": 100,
        "step": 1,
        "unit": "%",
        "value": 60,
        "command": ["set", "light:brightness", "$value"]
      },
      {
        "name": "color_temp",
        "label": "色温",
        "min": 2700,
        "max": 6500,
        "step": 100,
        "unit": "K",
        "value": 4000,
        "command": ["set", "light:color-temperature", "$value"]
      }
    ]
  }
]

$value 会在执行时替换成滑杆当前值。比如亮度拖到 42 时,上面的配置会执行:

bash 复制代码
miiocli genericmiot --ip 192.168.1.20 --token 0123456789abcdef0123456789abcdef set light:brightness 42

注意:不同型号台灯的属性名不一定相同,light:brightnesslight:color-temperature 只是示例。先在终端里用 miiocli genericmiot ... statusmiiocli genericmiot ... --help 查清你的设备支持什么属性,再把能跑通的命令写进 command

如果你的设备需要 raw_command set_properties,也可以把 $value 放在 JSON 参数字符串里。SideSail 会把字符串内部的 $value 替换成滑杆当前值:

json 复制代码
{
  "name": "color_temp",
  "label": "色温",
  "min": 2700,
  "max": 6500,
  "step": 100,
  "unit": "K",
  "value": 4000,
  "command": [
    "raw_command",
    "set_properties",
    "[{\"did\":\"你的设备did\",\"siid\":2,\"piid\":3,\"value\":$value}]"
  ]
}

上面会执行类似:

bash 复制代码
miiocli genericmiot --ip 192.168.1.20 --token 0123456789abcdef0123456789abcdef raw_command set_properties '[{"did":"你的设备did","siid":2,"piid":3,"value":4200}]'

这里的 did 不建议留空。可以用 xiaomi-cloud-token-extractor 输出里的 id/did,或者先运行 miiocli genericmiot ... status 看设备返回里是否包含 did。

填完后设置会自动保存。若侧边栏没有立刻更新,可以点智能家居卡片右上角的 刷新,或者禁用再启用扩展。

如何获取设备 IP 和 token

你至少需要两个信息:

  • ip:设备在局域网里的 IP 地址。
  • token:米家设备的本地控制 token,通常是 32 位十六进制字符串。

获取 IP 的常见方法:

  • 在路由器后台查看已连接设备。
  • 在米家 App 里查看设备网络信息。
  • 用局域网扫描工具查找设备地址。

获取 token 的常见方法:

  • 使用 python-miio/miiocli 的云账号能力列出设备。
  • 从米家 App 的本地数据库中导出。
  • 使用支持读取米家 token 的第三方工具。

如果你已经安装了 python-miio,可以先查看本机是否有 miiocli

bash 复制代码
miiocli --help

不同版本的 python-miio 命令可能略有差异,请以 miiocli --help 输出为准。一般思路是登录小米账号后列出设备,找到目标设备的 IP 和 token。

如果你使用 xiaomi-cloud-token-extractor,它通常会输出 idnamemactoken 等信息。插件真正需要的是 iptokenid/name/mac 可以保留下来用于识别设备。拿到 token 后还要去路由器后台、米家 App 网络信息页,或用局域网扫描工具找到这个设备当前的局域网 IP。

示例:提取结果里有这些信息:

text 复制代码
id: 123456789
name: 书桌台灯
mac: AA:BB:CC:DD:EE:FF
token: 0123456789abcdef0123456789abcdef

路由器里查到这个 MAC 对应的 IP 是 192.168.1.20,那么插件里的 JSON 写成:

json 复制代码
[
  {
    "id": "123456789",
    "name": "书桌台灯",
    "mac": "AA:BB:CC:DD:EE:FF",
    "ip": "192.168.1.20",
    "token": "0123456789abcdef0123456789abcdef",
    "kind": "miot",
    "siid": 2,
    "piid": 1
  }
]

如果 extractor 输出里本来就有 iplocalip 字段,就直接把它填到 ip

把提取结果转换成插件 JSON

仓库里提供了一个转换脚本 miioTokenToJson.py。新版脚本会按设备块解析,遇到新的 id/did 或空行/分隔线就结算上一台设备,避免把一台设备的 token 和另一台设备的 IP 配错。

xiaomi-cloud-token-extractor 的输出保存成文本文件,例如:

bash 复制代码
xiaomi-cloud-token-extractor > tokens.txt

然后转换:

bash 复制代码
python3 miioTokenToJson.py tokens.txt

也可以用管道:

bash 复制代码
xiaomi-cloud-token-extractor | python3 miioTokenToJson.py

如果你准备用小米云端控制,可以直接生成云端 JSON:

bash 复制代码
python3 miioTokenToJson.py --cloud --username '你的米家账号' --password '你的米家密码' --country cn tokens.txt

它会尽量识别这些字段:

text 复制代码
id / did
name
model
mac
ip / localip / local_ip
token
country

输出示例:

json 复制代码
[
  {
    "id": "123456789",
    "name": "书桌台灯",
    "mac": "AA:BB:CC:DD:EE:FF",
    "ip": "192.168.1.20",
    "token": "0123456789abcdef0123456789abcdef",
    "kind": "miot",
    "siid": 2,
    "piid": 1
  }
]

如果输出里没有 ip,需要你根据 mac 到路由器后台找到设备 IP,再手动补上 ip 字段。不要凭顺序手动把路由器里的 IP 批量贴给转换结果,最好逐台核对 name/mac/id,否则很容易设备错配。如果你准备用小米云端控制,则不需要 ip/token,改用下面的 kind: "cloud" 写法。

如何编写设备 JSON

Device JSON 必须是一个 JSON 数组。每个对象代表一个设备。

常用字段:

字段 必填 说明
name 显示名称
room 房间名
ip 本地模式必填 设备局域网 IP
token 本地模式必填 设备 token
kind 推荐 genericmiot,也支持 miotmiiocloud
status genericmiot 可选 状态命令片段,例如 "status"["status"]
actions genericmiot 常用 前端按钮列表,每个按钮可以绑定 command
commands genericmiot 常用 命令字典,按钮的 name 可以引用这里的命令;没写 actions 时会按命令名自动生成按钮
controls genericmiot 常用 滑杆控件列表,适合亮度、色温等数值调节
miiocli 手动指定 miiocli 路径,通常不用填
did 云端模式必填 小米云端设备 ID,也可以用 id
country 云端模式常用 小米服务器区域,例如 cndeus,默认 cn
cloud_username 云端模式必填 小米账号
cloud_password 云端模式必填 小米账号密码
icon 图标名,默认 power
siid MIOT 常用 MIOT service id,默认 2
piid MIOT 常用 MIOT property id,默认 1
status_method miot_getrawnone
set_method miot_setraw
value_type boolintfloatstring,默认 bool
on_value 写入开启状态时使用的值
off_value 写入关闭状态时使用的值
timeout 本地请求超时秒数,默认 5
retries 本地请求重试次数,默认 3
lazy_discover 是否延迟发现设备,默认 true

推荐:genericmiot 自定义按钮

现在更推荐使用 kind: "genericmiot"。插件不会猜你的设备模型,而是把 statusactions[].command 里的命令片段拼到下面这条命令后面执行:

bash 复制代码
miiocli genericmiot --ip <ip> --token <token> 你的命令片段

配置步骤:

  1. 先在终端里用完整 miiocli genericmiot --ip ... --token ... 命令测试。
  2. 能跑通后,只复制 --token <token> 后面的命令片段。
  3. 查询状态的片段写到 status
  4. 点击按钮要执行的片段写到 actions[].command

完整示例:

json 复制代码
[
  {
    "name": "书桌台灯",
    "room": "书桌",
    "ip": "192.168.1.20",
    "token": "0123456789abcdef0123456789abcdef",
    "kind": "genericmiot",
    "status": "status",
    "actions": [
      {"name": "refresh", "label": "状态", "command": "status"},
      {"name": "toggle", "label": "切换", "command": "call light:toggle"}
    ]
  }
]

如果命令里有复杂参数,也可以用数组,避免引号被拆错:

json 复制代码
[
  {
    "name": "书桌台灯",
    "room": "书桌",
    "ip": "192.168.1.20",
    "token": "0123456789abcdef0123456789abcdef",
    "kind": "genericmiot",
    "status": ["status"],
    "actions": [
      {"name": "on", "label": "开启", "command": ["set", "light:on", "true"]},
      {"name": "off", "label": "关闭", "command": ["set", "light:on", "false"]},
      {"name": "toggle", "label": "切换", "command": ["call", "light:toggle"]}
    ]
  }
]

也可以把命令集中写在 commands 里,按钮只引用 name

json 复制代码
[
  {
    "name": "书桌台灯",
    "room": "书桌",
    "ip": "192.168.1.20",
    "token": "0123456789abcdef0123456789abcdef",
    "kind": "genericmiot",
    "commands": {
      "status": "status",
      "on": ["set", "light:on", "true"],
      "off": ["set", "light:on", "false"]
    },
    "actions": [
      {"name": "on", "label": "开启"},
      {"name": "off", "label": "关闭"}
    ]
  }
]

status 没写时,卡片会显示"已配置",不会把设备标成失败。按钮命令没写或写错时,点击对应按钮才会报错。

MIOT 开关设备示例

很多灯、插座、开关类 MIOT 设备的电源属性是 siid: 2piid: 1

json 复制代码
[
  {
    "name": "卧室灯",
    "room": "卧室",
    "type": "灯",
    "ip": "192.168.1.31",
    "token": "0123456789abcdef0123456789abcdef",
    "kind": "miot",
    "siid": 2,
    "piid": 1,
    "icon": "power"
  }
]

如果设备的开关属性不是 2/1,需要查询该设备的 MIOT 说明或用调试工具确认正确的 siid/piid

如果局域网偶尔报 No response from device,可以先把超时加长:

json 复制代码
[
  {
    "name": "卧室灯",
    "room": "卧室",
    "ip": "192.168.1.31",
    "token": "0123456789abcdef0123456789abcdef",
    "kind": "miot",
    "siid": 2,
    "piid": 1,
    "timeout": 10,
    "retries": 5
  }
]

老 miIO raw 命令示例

有些旧设备不是 MIOT 属性模型,而是 get_propset_power 一类命令。可以这样写:

json 复制代码
[
  {
    "name": "空气净化器",
    "room": "客厅",
    "ip": "192.168.1.32",
    "token": "0123456789abcdef0123456789abcdef",
    "kind": "miio",
    "status_method": "raw",
    "status_command": "get_prop",
    "status_params": ["power"],
    "set_method": "raw",
    "set_command": "set_power",
    "set_params": ["$value"],
    "on_value": "on",
    "off_value": "off"
  }
]

"$value" 会在执行时替换成 on_valueoff_value

小米云端控制示例

如果本地控制报 Unable to discover device,说明电脑无法和设备完成局域网 miIO 握手。这时可以改用小米服务器控制。云端模式不依赖设备局域网 IP,也不依赖 token,但需要小米账号、密码、设备 did/id 和服务器区域。

json 复制代码
[
  {
    "name": "书桌台灯",
    "room": "书桌",
    "kind": "cloud",
    "did": "123456789",
    "country": "cn",
    "cloud_username": "你的米家账号",
    "cloud_password": "你的米家密码",
    "siid": 2,
    "piid": 1
  }
]

说明:

  • did 可以使用 xiaomi-cloud-token-extractor 输出里的 id
  • 国内米家账号通常用 country: "cn"
  • 欧洲区常见是 de,美国区常见是 us
  • 云端控制需要联网,响应会比局域网本地控制慢一些。
  • 账号密码会保存在本机 GSettings 配置里,不要提交到公开仓库。

多设备示例

json 复制代码
[
  {
    "name": "书桌台灯",
    "room": "书桌",
    "ip": "192.168.1.20",
    "token": "0123456789abcdef0123456789abcdef",
    "kind": "miot",
    "siid": 2,
    "piid": 1
  },
  {
    "name": "客厅净化器",
    "room": "客厅",
    "ip": "192.168.1.32",
    "token": "abcdef0123456789abcdef0123456789",
    "kind": "miio",
    "status_method": "raw",
    "status_command": "get_prop",
    "status_params": ["power"],
    "set_method": "raw",
    "set_command": "set_power",
    "set_params": ["$value"],
    "on_value": "on",
    "off_value": "off"
  }
]

手动测试设备

推荐先直接用 miiocli genericmiot 测试设备:

bash 复制代码
miiocli genericmiot --ip 192.168.1.20 --token 0123456789abcdef0123456789abcdef status

确认命令能跑通后,再运行 helper 测试同一份 JSON:

bash 复制代码
python3 miioHelper.py status '{"name":"书桌台灯","ip":"192.168.1.20","token":"0123456789abcdef0123456789abcdef","kind":"genericmiot","status":"status","actions":[{"name":"toggle","label":"切换","command":"call light:toggle"}]}'

执行按钮动作:

bash 复制代码
python3 miioHelper.py set '{"name":"书桌台灯","ip":"192.168.1.20","token":"0123456789abcdef0123456789abcdef","kind":"genericmiot","status":"status","actions":[{"name":"toggle","label":"切换","command":"call light:toggle"}]}' toggle

如果你仍然使用旧的 kind: "miot" 开关模式,也可以这样测试:

bash 复制代码
python3 miioHelper.py set '{"name":"书桌台灯","ip":"192.168.1.20","token":"0123456789abcdef0123456789abcdef","kind":"miot","siid":2,"piid":1}' true

如果返回 {"ok": true, ...},说明配置基本可用。

常见问题

Enabled: YesState: INACTIVE

重新登录,或在 X11 下 Alt + F2 输入 r 重启 GNOME Shell。

智能家居显示缺少 miio

Python command 指向已经安装 python-miio 的 Python,例如虚拟环境里的 Python。

设备一直失败:

检查 IP、token、设备和电脑是否在同一局域网、设备是否支持本地 miIO/MIOT 控制,以及 siid/piid 或 raw 命令是否正确。

局域网 No response from device

先确认电脑能 ping 到设备 IP:

bash 复制代码
ping -c 3 192.168.1.31

再用 miiocli 直接测试:

bash 复制代码
miiocli genericmiot --ip 192.168.1.31 --token 0123456789abcdef0123456789abcdef status

如果 ping 不通,通常是 IP 错了、设备离线、手机/电脑和设备不在同一个网络、访客 Wi-Fi 隔离、VLAN 隔离或路由器禁用了局域网互访。

如果 ping 能通但 miiocli 仍然无响应,常见原因是 token 不匹配、设备固件不开放本地 miIO、设备只允许云端控制,或者 UDP 54321 被网络策略挡住。可以先在 JSON 里加 "timeout": 10, "retries": 5,还不行就改用 kind: "cloud" 云端模式。

token 是否安全:

token 能控制你的设备,不要提交到公开仓库。建议只保存在本机 GSettings 配置里。

相关推荐
howard20051 小时前
3.4 Linux目录操作
linux·目录操作
Volunteer Technology1 小时前
Flink的DataStream分区操作
大数据·linux·flink
爱讲故事的2 小时前
操作系统第四讲:OS Interfaces and Syscalls(操作系统接口与系统调用)
linux·windows·ubuntu
「QT(C++)开发工程师」2 小时前
免费在线 Ubuntu/Linux 运行环境
linux·运维·ubuntu
hhhh明2 小时前
ubuntu22.04 桌面可视化(vncserver+novnc 方式)
linux·运维·服务器
Fcy6482 小时前
Linux下 进程间通信详解(一)管道、进程池与简单的Linux 进程间聊天室
linux·服务器·管道·进程间通信·进程池
‎ദ്ദിᵔ.˛.ᵔ₎2 小时前
Linux 权限
linux
拳里剑气2 小时前
Linux:权限
linux·学习方法
ole ' ola2 小时前
Linux DDR内存使用情况
linux·运维·服务器