fnOS 风扇控制开发日志(一):为什么要自己写风扇控制

 

飞牛NAS 买回来第一天,我就被风扇噪音劝退了。默认策略要么全速轰鸣,要么低温时完全不转让人焦虑。官方没有提供灵活的风扇控制方案,社区的脚本又各有各的问题——于是我决定自己写一个。

痛点在哪

飞牛NAS(fnOS)基于 Linux,硬件控制走的是标准的 hwmon 子系统。理论上可以直接写 sysfs 文件来控制风扇,但实际使用中有几个现实问题:

  1. 噪音与散热的平衡难:默认的 BIOS 风扇策略太粗暴,低温全速或者高温才启动,没有中间地带
  2. 机型差异大:不同飞牛机型用的主板芯片不同(ITE、Nuvoton、Fintek),控制方式有细微差别
  3. 缺少可视化管理:命令行脚本虽然能用,但调曲线、看温度都不方便
  4. 安全风险:手动控制风扇如果程序崩了,风扇可能停转导致硬件过热

我需要的是一个安装即用、有 Web 界面、自带安全保护的风扇控制应用。

目标定义

经过需求分析,我确定了几个核心目标:

目标 说明
四种运行模式 默认(保守曲线)/ 自动(自定义曲线)/ 手动(固定转速)/ 全速(紧急散热)
自定义温控曲线 2-10 个节点,支持自动生成和手动微调
Web 管理界面 深色主题、实时监控、响应式布局
多设备兼容 不绑定特定芯片型号,通用 hwmon 探测
多层安全保护 程序崩溃、温度读取失败、PWM 写入异常都有兜底
FPK 打包 作为飞牛应用商店的标准应用分发

技术选型

为什么用 Python?

这个项目的核心是读写 sysfs 文件和运行一个轻量 HTTP 服务,不需要高性能计算。Python 标准库自带 http.serverjsonthreading,完全够用。最关键的是——零第三方依赖

NAS 环境特殊,用户不一定有 pip,也不想装一堆包。Python 3.11+ 在飞牛系统上是预装的,拿来就能用。

前端方案:纯 HTML + CSS + 原生 JavaScript,单文件。不需要 Node.js 构建工具链,不需要 npm install,打开就能用。

整体技术栈

后端:Python 3.11+(标准库 only)
前端:HTML + CSS + Vanilla JS(单文件)
硬件接口:Linux hwmon sysfs
打包:FPK(飞牛应用包格式)
CI:GitHub Actions
测试:unittest(120 个测试用例)

项目结构规划

fnOS-fan-control/
├── src/
│   ├── app/bin/           # Python 应用核心(5 个模块)
│   │   ├── main.py        # 入口(启动、信号处理、OOM 保护)
│   │   ├── hardware.py    # 硬件抽象层(hwmon 探测、PWM 读写)
│   │   ├── fan_controller.py  # 风扇控制核心(温控逻辑)
│   │   ├── config_manager.py  # 配置管理(校验、线程安全)
│   │   ├── web_server.py  # REST API 服务
│   │   └── static/index.html  # Web 管理界面
│   ├── cmd/               # FPK 生命周期脚本(9 个)
│   └── manifest           # FPK 包元数据
├── tests/                 # 120 个单元 + 集成测试
├── scripts/               # 构建和清理脚本
└── .github/workflows/     # CI 自动构建

模块划分的原则是单一职责:硬件层只管读写 sysfs,控制层只管温控逻辑,配置层只管数据校验,Web 层只管 HTTP 请求。模块之间通过明确的接口交互,任何一个模块出问题都不会拖垮整个系统。

开发计划

整个开发分为 6 个阶段:

  1. 基础框架 — 硬件探测、配置管理、单风扇控制
  2. Web 管理界面 — REST API、前端页面、实时监控
  3. 温控曲线编辑 — SVG 可视化、自动生成、手动微调
  4. FPK 打包 — 生命周期脚本、安装向导、权限配置
  5. 多设备适配 — 通用 hwmon 探测、多区域控制
  6. 测试与文档 — 单元测试、集成测试、完整文档

下一篇预告

下一篇会聊架构设计的核心决策:为什么选择「sysfs 就是抽象层」的设计哲学,如何用最少的代码实现多芯片兼容,以及多层安全机制是怎么设计的。


项目地址:fnOS-fan-control on GitHub(MIT 许可证)

本文遵守 Attribution-NonCommercial 4.0 International 许可协议。 Attribution-NonCommercial 4.0 International