xiaomusic 定时任务配置指南:基于 Crontab 语法实现小爱音箱的自动播放、关机与语音播报
【免费下载链接】xiaomusic使用小爱音箱播放音乐,音乐使用 yt-dlp 下载。项目地址: https://gitcode.com/GitHub_Trending/xia/xiaomusic
本指南围绕 xiaomusic 项目的定时任务(crontab)配置功能展开,讲解如何用标准 crontab 表达式驱动小爱音箱在指定时刻自动播放歌曲、播放列表、执行关机、文字转语音(TTS)、刷新曲库、调整音量等操作。读完本文,你将掌握定时任务的完整配置格式、所有受支持任务类型的参数语义,以及工作日/休息日限定执行等进阶用法,可直接落地为可运行的配置文件。
定时任务是什么:功能定位与配置入口
在 xiaomusic 中,定时任务是一段 JSON 数组配置,每一条记录描述"在什么时间、对哪台设备、执行什么操作"。系统采用标准 crontab 表达式描述执行时机,由 APScheduler 的AsyncIOScheduler调度器驱动(见 crontab.py 中Crontab类的实现)。
该配置的核心应用场景包括:
- 工作日早晨定时播放叫醒歌曲,随后自动关机;
- 每晚固定时间播放指定歌单、播报语音提醒;
- 每天凌晨自动刷新音乐列表、重新初始化登录态以缓解风控/登录失效问题;
- 按法定工作日/休息日差异化调度(需结合项目内置的节假日数据)。
配置入口主要有两个:
- Web 设置页面:在
crontab_json文本框中粘贴 JSON 配置(setting.html 中即为该表单项); - 环境变量:
XIAOMUSIC_CRONTAB_JSON,见 config.py 中crontab_json: str = os.getenv("XIAOMUSIC_CRONTAB_JSON", "")的定义。
保存配置后,xiaomusic.update_config_from_setting()会调用self.crontab.reload_config(self)(见 xiaomusic.py),重新加载所有定时任务,无需重启进程即可生效。
配置格式:JSON 数组与核心字段
定时任务整体是一个 JSON 数组,数组中的每个对象代表一条任务。以官方文档 docs/issues/182.md 给出的完整示例为基础,最简任务形如:
[ { "expression": "0 8 * * 0-4", "name": "play", "did": "123456789", "arg1": "周杰伦晴天" } ]字段语义
| 字段 | 必填 | 含义 |
|---|---|---|
expression | 是 | 标准 crontab 表达式,描述任务的执行时机 |
name | 是 | 任务类型,目前仅支持下文列出的若干固定值 |
did | 视任务而定 | 小爱音箱的设备 ID,即设置页面中"音箱型号"后面的那串数字 |
arg1 | 视任务而定 | 任务参数,含义随name不同而不同 |
crontab 表达式说明
expression遵循标准 crontab 五段式格式:分 时 日 月 周。需要注意星期字段的取值约定:
星期一是 0,星期二是 1,星期日是 6。 取值范围为
0-6,也支持mon,tue,wed,thu,fri,sat,sun这类英文缩写写法;一周的第一天固定是 Monday。
例如0 8 * * 0-4表示"周一至周五每天 8 点";30 10 * * *表示"每天 10 点 30 分"。
加载与调度流程(源码视角)
从源码看,定时任务从配置到执行的调用链为(crontab.py):
reload_config()读取config.crontab_json,用json.loads解析为任务列表;- 对每条任务调用
add_job_cron(),按name动态拼接方法名add_job_{name}并通过getattr分发到对应的任务注册函数; - 每个注册函数内部定义
async def job(),再交给add_job()注册到AsyncIOScheduler; add_job()将 crontab 表达式解析为CronTrigger,并设置了三个关键调度参数:coalesce=True:若任务错过多次触发,只补执行一次(适合播放类任务);max_instances=30:同一任务最多允许 30 个并发实例,支持多设备并发;misfire_grace_time=60:任务延迟 60 秒内仍会执行。
此外,若配置开启了enable_auto_clean_temp,系统还会在每次加载时自动附加一个"每日凌晨 3 点清理临时文件"的任务。
支持的任务类型总览
当前支持以下任务类型(对应name取值):
| name | 功能 | 是否需要 did | arg1 含义 |
|---|---|---|---|
stop | 关机 | 是 | 无 |
play | 播放歌曲 | 是 | 歌曲名 |
play_music_list | 播放列表 | 是 | 播放目录名,可用\|追加目录内歌曲名 |
play_music_tmp_list | 播放自定义列表 | 是 | 临时歌单名称(可选) |
tts | 文字转语音 | 是 | 要播报的语音文字 |
refresh_music_list | 刷新播放列表 | 否 | 无 |
set_volume | 设置音量 | 是 | 音量值(0–100) |
set_play_type | 设置播放类型 | 是 | 0–4 的数字 |
set_pull_ask | 设置是否拉取对话记录 | 否 | enable/disable |
reinit | 重新初始化 | 否 | 无 |
set_play_type的arg1取值与播放类型的对应关系:
| arg1 | 播放类型 |
|---|---|
| 0 | 单曲循环 |
| 1 | 全部循环 |
| 2 | 随机播放 |
| 3 | 单曲播放 |
| 4 | 顺序播放 |
在 crontab.py 中,add_job_set_play_type会执行play_type = int(arg1)后调用xiaomusic.set_play_type(did, play_type, False);播放类型的枚举常量定义于 config.py(PLAY_TYPE_ONE/ALL/RND/SIN/SEQ)。
完整配置示例与逐条解读
官方文档给出的完整示例覆盖了全部任务类型,配置如下:
[ { "expression": "0 8 * * 0-4", "name": "play", "did": "123456789", "arg1": "周杰伦晴天" }, { "expression": "10 8 * * 0-4", "name": "stop", "did": "123456789" }, { "expression": "0 9 * * *", "name": "play", "did": "123456789", "arg1": "周杰伦晴天" }, { "expression": "0 10 * * *", "name": "play_music_list", "did": "123456789", "arg1": "周杰伦" }, { "expression": "30 10 * * *", "name": "play_music_list", "did": "123456789", "arg1": "周杰伦|晴天" }, { "expression": "0 7 * * *", "name": "tts", "did": "123456789", "arg1": "早上好!该起床了!" }, { "expression": "0 3 * * *", "name": "refresh_music_list" }, { "expression": "* * * * *", "name": "set_volume", "did": "123456789", "arg1": "25" }, { "expression": "* * * * *", "name": "set_play_type", "did": "123456789", "arg1": "2" }, { "expression": "0 6 * * *", "name": "set_pull_ask", "arg1": "enable" }, { "expression": "0 0 * * *", "name": "set_pull_ask", "arg1": "disable" }, { "expression": "0 1 * * *", "name": "reinit" }, { "expression": "33 20 * * *", "name": "play_music_tmp_list", "did": "978479727", "arg1": "临时列表1", "music_list": [ "1大青树下的小学", "7听听秋的声音", "11宝葫芦的秘密" ], "first": "11宝葫芦的秘密" } ]上述配置的逐条含义:
- 周一至周五每天 8 点播放歌曲"周杰伦晴天";
- 周一至周五每天 8 点 10 分执行关机指令;
- 每天 9 点播放歌曲"周杰伦晴天";
- 每天 10 点播放列表"周杰伦";
- 每天 10 点 30 分播放列表"周杰伦"里的"晴天"(
play_music_list的arg1用|分隔目录名与歌曲名); - 每天 7 点发出语音"早上好!该起床了!";
- 每天 3 点刷新播放列表,用于自动更新音乐目录下的歌曲到播放列表;
- 每分钟设置音量为 25;
- 每分钟设置为随机播放;
- 每天早上 6 点开启拉取对话记录;
- 每天晚上 12 点关闭拉取对话记录;
- 每天 1 点重新初始化;
- 晚上 8 点 33 分将 3 首歌曲组成名为"临时列表1"的临时歌单,并从"11宝葫芦的秘密"开始播放;其中
arg1与first均为可选字段。
各任务类型深入解析
play:定时播放歌曲
add_job_play内部调用xiaomusic.play(did, arg1)。从 xiaomusic.py 的实现看,play还支持用|分隔"搜索关键词"和"显示名称":parts = arg1.split("|"),第一个元素作为搜索关键词,第二个元素作为歌曲名称(缺省时名称即关键词)。
play_music_list:定时播放列表
add_job_play_music_list调用xiaomusic.play_music_list(did, arg1)。在 xiaomusic.py 中,arg1以|拆分为列表名与可选歌曲名,最终经do_play_music_list校验列表存在性后,交给设备播放器执行;若列表不存在,音箱会 TTS 播报"播放列表xx不存在"。
play_music_tmp_list:定时播放自定义临时歌单
这是把多首歌曲临时拼成一个歌单播放的任务,也是结构最特殊的一条(crontab.py 中add_job_play_music_tmp_list):
arg1:临时歌单名称,可选,缺省为crontab_tmp_list;music_list:歌曲名数组,必填;first:起始播放的歌曲名,可选。
其执行流程为:先通过music_library.play_list_update_music(name, music_list)以"覆盖"方式写入自定义歌单(歌单不存在则自动新建,见 music_library.py),再调用do_play_music_list(did, name, music_name)从指定歌曲开始播放。
tts:定时语音播报
add_job_tts调用xiaomusic.do_tts(did, arg1),将arg1的文字内容通过小爱音箱播报出来,适合做起床提醒、整点报时等场景。
refresh_music_list:定时刷新曲库
add_job_refresh_music_list调用xiaomusic.gen_music_list(),用于自动更新音乐目录下的歌曲到播放列表。从 music_library.py 看,刷新时会按配置的music_path、music_path_depth(目录深度)、exclude_dirs(排除目录)以及SUPPORT_MUSIC_TYPE(支持的音频扩展名)重新遍历音乐目录并重建列表。若设置了enable_file_watch目录监控,新增歌曲可被自动感知,而定时刷新则是一种不依赖监控的兜底方案。
set_volume:定时设置音量
add_job_set_volume调用xiaomusic.set_volume(did, arg1),arg1为 0–100 的整数音量值。示例中* * * * *即每分钟执行一次,可用于持续校正音量。
set_play_type:定时切换播放模式
add_job_set_play_type将arg1转为整数后调用xiaomusic.set_play_type(did, play_type, False),第三个参数False表示切换时不做 TTS 语音播报。对应关系见上文表格(0 单曲循环 / 1 全部循环 / 2 随机播放 / 3 单曲播放 / 4 顺序播放)。
set_pull_ask:定时开关对话记录拉取
add_job_set_pull_ask通过arg1为enable或disable直接切换config.enable_pull_ask布尔值(其余值一律视为关闭)。该开关控制是否向小爱音箱拉取对话记录,官方文档提示:每天定时关闭可缓解风控问题,因此常见做法是白天开启、夜间关闭(如示例中 6 点 enable、0 点 disable)。
reinit:定时重新初始化
add_job_reinit调用xiaomusic.reinit()。在 xiaomusic.py 中,reinit会重新初始化日志、调用auth_manager.init_all_data()重建登录认证数据,并重新生成音乐列表与播放列表。官方文档建议每天执行一次以缓解登录失效问题。
stop:定时关机
add_job_stop调用xiaomusic.stop(did, "notts"),"notts"参数表示关机前不做 TTS 播报。典型组合是"工作日 8 点播放叫醒歌曲 + 8 点 10 分关机",让音箱完成叫醒后自动进入待机。
进阶用法:按工作日/休息日限定执行
针对"工作日执行、休息日不执行"的需求,项目在 crontab.py 中实现了CustomCronTrigger自定义触发器,支持在expression末尾追加特殊注释标记:
- 末尾加
#workday:仅在法定工作日执行; - 末尾加
#offday:仅在法定休息日执行(含法定节假日与周末)。
示例:
[ { "expression": "0 8 * * * #workday", "name": "play", "did": "123456789", "arg1": "周杰伦晴天" }, { "expression": "0 10 * * * #offday", "name": "play", "did": "123456789", "arg1": "周杰伦晴天" } ]其实现原理是:CustomCronTrigger.get_next_fire_time()先用基础CronTrigger算出下一个候选触发时间,再根据标记调用 holiday.py 中的is_working_day()或is_off_day()校验当天是否满足条件,若不满足则递归寻找下一个候选时间。节假日判断依赖仓库holiday/目录下按年份组织的 JSON 数据(如 holiday/2025.json),数据加载逻辑见load_year_data():优先查表判定法定调休日,表中没有的日期则按周末(周六、周日)判定。因此该功能可直接覆盖春节、国庆等法定节假日以及调休上班日,比单纯用 crontab 的星期字段精确得多。
Crontab.add_job()中会检测expression是否包含workday/offday标记,自动选用CustomCronTrigger还是标准CronTrigger,用户无需手动指定。
配置校验与排障建议
- JSON 合法性:定时任务是整段 JSON 数组,任何语法错误都会导致
reload_config解析失败。建议先用 JSON 校验工具检查配置是否合法后再粘贴到设置页。 - 表达式合法性:非法 crontab 表达式会在注册时抛出
ValueError,日志中输出Invalid crontab expression ...,该任务会被跳过,不影响其他任务注册。 - name 拼写:
add_job_cron按add_job_{name}反射调用,name不在支持列表时日志会报object has no attribute 'add_job_xxx',任务不生效。 - did 归属:
did必须对应当前设备列表中的音箱 ID,即设置页面"音箱型号"后的那串数字,填写错误会导致任务找不到设备。 - 日志观察:每次加载成功都会打印
crontab reload_config ok及每条任务注册信息(crontab add_job_cron ok. did:... name:... arg1:... expression:...),可据此确认任务是否被正确解析。 - 运行时状态:
set_pull_ask、set_play_type等状态类任务会实时修改运行内存中的配置,配合 Web 设置页可以观察对应开关的实际状态。
小结
xiaomusic 的定时任务功能把标准 crontab 语法与音箱控制命令完整打通:一份 JSON 数组即可编排播放、关机、播报、刷新曲库、调整音量/播放模式、开关对话拉取、重新初始化等全部受支持操作,并可借助#workday/#offday标记叠加法定节假日判断,实现贴近真实生活的作息自动化。相关实现均可对照 crontab.py、holiday.py、xiaomusic.py 与 music_library.py 等源码深入研读。
【免费下载链接】xiaomusic使用小爱音箱播放音乐,音乐使用 yt-dlp 下载。项目地址: https://gitcode.com/GitHub_Trending/xia/xiaomusic
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考