托美国驻华大使馆的福,北京的空气质量在几年前成为大家都知道的事情。虽然中国政府一直想让这个数据源停止更新,但美国国务院很快上了一个 stateair.net 的网站,不仅仅提供北京地区的空气质量数据,还有成都、广州、上海和沈阳这几个拥有美国领事馆的城市。
这些数据可以帮助我确认出门是否要带防护,也能让我在每一个房间都放置一台空气净化器,但室内的空气质量是不能依靠美国大使馆的……这需要自己投资购买一些空气质量检测仪。经过多年考察、拖沓和纠结,最后我选择了镭豆(点击前往亚马逊中国购买,链接有返利),英文名叫做 Laser Egg。
几年前就知道它,当时有几个住在中国的外国人在推荐,而且镭豆本身也是一个住在国内的外国人做的。这台设备除了贵一些之外都不错:使用了激光检测元器件,支持 Wi-Fi 数据上传,自带 LCD 显示屏,外观设计可爱。当然,用了一阵子之后,我发现了更有趣的地方,容我慢慢分享这些人生经验。
空气质量
AQI(或者说空气质量水平)简单来看就是一个数字,大家还知道超过五百就是“爆表”。但这个简单的数字其实还有更多变量,不仅仅是表示 PM 2.5 的水平,还有其他好几个污染源能影响它:PM 10,二氧化氮,二氧化硫,一氧化碳和臭氧。要计算空气质量水平,需要把这些污染物的浓度按照各自的算法都计算出来,然后取其中最大值。
在北京,主要污染源不一定总是 PM 2.5,但这个数据是最受关注的。所以镭豆也偷工减料,仅仅检测可吸入颗粒物 PM 2.5 和 PM 10 的浓度,这一点也需要注意。
室外的空气质量我就拜托美国大使馆了,室内的空气质量我就交给镭豆,所以我先后买了四个镭豆放在不同的房间。接下来,你就可以开始调试空气净化器了。当然,也许还需要封堵漏风的窗户。
能不能给力一点?
镭豆自带一个叫做“呼吸之间”的智能手机应用,可以方便地察看五分钟更新一次的室内 AQI 和来自美国大使馆(或领事馆)每小时更新一次的室外 AQI。这个应用怎么说呢,我感觉有点缺少设计。先不说未针对高分辨率屏幕优化的界面,也没考虑到拥有四个或者更多镭豆设备的情况啊。
不能一眼看完所有数据的应用都是垃圾,不能当数据面板来用的话我要它做甚呐?所以我那阵子一直在思考一个问题:能不能给力一点?
当然可以。
一点微小的准备工作
只要用 Charles.app 稍微分析一下下,就能拿到呼吸之间背后的数据源。而且这个数据源没有加密,用浏览器就能直接访问到(真不知道是好事还是坏事……)。既然这样,自己动手吧。
根据我的猜测,每一台镭豆都有一个独立设备 ID。设置完毕之后,可以在“呼吸之间”的详情页面看到一个二维码和一个字符串。这个字符串在镭豆眼中叫 TimeID,可以通过一个 API 调用换算为更加方便的独立设备 ID,具体做法是访问这个链接:
http://api-ios.origins-china.cn:8080/topdata/getTopByTimeId?timeId=YOUR_TIME_ID_HERE
你会看到一个 JSON 数组,找到 "id":XXXXX
字段就行,这个五位数就是所对应的独立设备 ID。
然后就可以通过访问这个链接来获取数据了:
http://api-ios.origins-china.cn:8080/topdata/getTopDetail?id=YOUR_LASER_EGG_ID_HERE
这个 JSON 数组稍显复杂,但你很快就能找到 "pm2_5":XXX
和 "pm10":XXX
这两个字段对不对?这两个数值分别代表着 PM 2.5 和 PM 10 的浓度。
准备工作做完,接下来就是更加有趣的事情啦。
与空气净化器联动
为了对抗空气污染,我家客厅部署了一台学名叫 FFU 的设备。这本是用在无尘车间的工业设备,但因为造价便宜深受消费者欢迎。除了体积和噪音巨大之外,一切都还好。作为贝尔金 WeMo 智能家居系统的爱好者,我当然不会忘记加一个 Wi-Fi 电源,这样我就可以在远程启动或者关闭它了。除此之外,我还在 WeMo 的应用程序里设置了午夜十二点至早上七点期间自动开机功能。家人睡觉的时候,客厅的空气净化器努力工作,怎么看都觉得不错呢。
唯一的问题在于,白天我会忘记开机。如果可以做到“当空气质量差到一定水平就打开空气净化器”的话,那就完美啦。可惜的是,IFTTT 虽然支持 WeMo 但并不支持镭豆。
没关系,自己做就是了。
第一步,先在内网找一个服务器。没有的话,买一个树莓派就行。
第二步,在网上找一个可以控制 WeMo 开关的 Bash 脚本,比如 这个。当然,你需要知道你的 WoMo 开关的 IP 地址,去路由器或者 WeMo 应用里看看吧。如果可以,最好是给它分配一个固定的 IP 地址,这样会稳健一些。
第三步,写一个 Bash 脚本来获取客厅的 AQI 数值。如果大于某个阈值,就让 WeMo 启动,反之则关掉它。你可以用这段代码:
#!/bin/sh
#
# Laser Egg AQI Script
# Original author: i@gongm.in
#
# Usage: aqi <Laser Egg ID>
ID=$1
AlertLevel=21 #AQI=70
if [ "$1" = "" ]; then
echo "Usage: `basename $0` <Laser Egg ID>"
else
CMD="http://api-ios.origins-china.cn:8080/topdata/getTopDetail?id=$ID"
PM25=$(curl -s $CMD | jq -r ".pm2_5")
fi
echo "Laser Egg $ID PM 2.5 reads: [$PM25]."
if ((PM25 > AlertLevel)); then
echo "Alert!"
bash ./wemo.sh YOUR_WEMO_IP_ADDRESS_HERE ON
else
echo "OK"
bash ./wemo.sh YOUR_WEMO_IP_ADDRESS_HERE OFF
fi
因为水平有限,我不知道如何用 grep 和 sed 来解析 JSON,不得不借助于 jq 组件。所以在使用之前,你还需要安装一下 jq,用一下这个指令:sudo apt-get install jq
即可。
此外,这里用到的阈值是 PM 2.5 的浓度而不是 AQI。这也是因为水平有限,无法在 Bash 脚本里实现复杂的算法。为此本人深表歉意,附上一份对照图表,供快速调整阈值用。
大致 AQI 水平 | PM 2.5 浓度 | 实际 AQI 数值 |
10 | 3 | 13 |
20 | 5 | 21 |
30 | 7 | 29 |
40 | 10 | 42 |
50 | 12 | 50 |
60 | 17 | 61 |
70 | 21 | 70 |
80 | 26 | 80 |
90 | 31 | 91 |
100 | 35 | 99 |
最后一步,添加一个 Cron 任务即可,我选择在早七点至晚十一点之间每五分钟检测一次,所以 Cron 脚本是:
Cron */5 7-23 * * * bash ~/bin/bash/wemo/aqi.sh YOUR_LASER_EGG_ID_HERE
如果你的脚本名字叫 aqi.sh 而且放在 ~/bin/bash/wemo/
目录下的话……
这样一来,白天再也不用担心自己会关闭设备了。不过,这个脚本也有几个问题:如果空气很差的话,可能会有反复开关的问题,毕竟阈值只有一个;你可能会看到大量错误信息,因为 WeMo 设备在开启状态时 wemo.sh 脚本再次开启的话会报错。
前面那个问题可以简单粗暴地通过注释掉 echo "OK"
下面那一行来解决,觉得吵可以手工关闭嘛。当然,也可以设置另一个更低的、专门用于关闭空气净化器的阈值。后面那个问题可以简单粗暴地通过无视来解决,呵呵。
能不能再给力一点?
不用陈旧的官方应用
前面提到过“呼吸之间”这个应用陈旧不堪的问题:高分辨率屏幕未优化,看着就不爽;设备超过三个就需要滚动屏幕,想想就不爽。不过,既然他们的 API 一不鉴权二不加密,我这个文科生都能拿到数据的话……
没关系,自己做就是了。
这非常简单,既然能读取到 PM2.5 浓度,那么直接把这些数值写到一个 .js 文件不就可以了?再简单写一个 HTML 文件显示一下数据,简直就是完美。
当然,你需要给树莓派装一个 web 服务器,apache2 就可以,然后就可以通过访问树莓派获取最新 AQI 数值啦。如果你愿意,可以在路由器上设置端口转发,这样在室外也能看这个网页。
这里还涉及到一个数据转换问题。大家熟知的爆表指的是 AQI 而不是 PM 2.5 浓度,而镭豆 API 给的数据是后者,在认知上有一些不一致。所以需要转换一下,用更加直观的 AQI 显示出来。
虽然我可以在 AirNow.gov 找到算法,但我实在是懒得自己去写一个。于是就通过浏览器的开发者工具稍微那么一看 HTML 源码,直接将计算 PM2.5 浓度 AQI 的 Javascript 函数抓了出来,代码如下:
function Linear(AQIhigh, AQIlow, Conchigh, Conclow, Concentration) {
var linear;
var Conc=parseFloat(Concentration);
var a;
a=((Conc-Conclow)/(Conchigh-Conclow))*(AQIhigh-AQIlow)+AQIlow;
linear=Math.round(a);
return linear;
}
function getAQI (intPM25){
var Conc=parseFloat(intPM25);
var c;
var AQI;
c=(Math.floor(10*Conc))/10;
if (c>=0 && c<12.1) {
AQI=Linear(50,0,12,0,c);
} else if (c>=12.1 && c<35.5) {
AQI=Linear(100,51,35.4,12.1,c);
} else if (c>=35.5 && c<55.5) {
AQI=Linear(150,101,55.4,35.5,c);
} else if (c>=55.5 && c<150.5) {
AQI=Linear(200,151,150.4,55.5,c);
} else if (c>=150.5 && c<250.5) {
AQI=Linear(300,201,250.4,150.5,c);
} else if (c>=250.5 && c<350.5) {
AQI=Linear(400,301,350.4,250.5,c);
} else if (c>=350.5 && c<500.5) {
AQI=Linear(500,401,500.4,350.5,c);
} else {
AQI="555"; //爆表输出 555
}
return AQI;
}
请注意,以上代码版权属于美国政府,本人对某个函数的变量名称做了一点点微不足道的修改,请酌情使用。
那么接下来写两个 bash 脚本吧,一个负责将所有镭豆数据读出来,另一个负责控制并将结果输出到一个 .js 文件里。代码如下。
读取镭豆数据并输出 Javascript 格式数据的 Bash 脚本 aqijs.sh:
#!/bin/sh
ID=$1
PM25=$(curl -s "http://api-ios.origins-china.cn:8080/topdata/getTopDetail?id=$ID" | jq -r ".pm2_5")
echo "var AQI$ID = $PM25;"
输出结果的 Bash 脚本 aqis.sh :
#!/bin/sh
bash ./aqijs.sh YOUR_LASER_EGG_1_ID_HERE
bash ./aqijs.sh YOUR_LASER_EGG_2_ID_HERE
bash ./aqijs.sh YOUR_LASER_EGG_3_ID_HERE
bash ./aqijs.sh YOUR_LASER_EGG_4_ID_HERE
最后,还需要新建一个用于 Cron 的 Bash 脚本,名字随意:
cd ~/bin/bash/wemo/
bash ./aqis.sh > aqi.js
sudo cp aqi.js /var/www/html/
定时运行前述脚本即可,我选择每三分钟运行一次,而网页我选择每三十秒自动刷新一次。最终的效果如下:
能不能再给力一点?
漂亮的控制面板
这个网页很好用,而且是按照我自己的需求定制的,虽然很丑吧……不过,跟呼吸之间比起来,这个网页还缺少一个历史数据功能。按照自己动手的原则,现在应该开始安装 MySQL 了吧?然后找一个渲染数据的 Javascript 框架,华丽丽的 HTML Dashboard 就出现了吧。
你当然可以这么做,但还有更简单的办法对不对?
这次需要用到第三方服务 Initial State,这是一个为 IoT 设计的服务。拿到数据之后,可以用简单明快的图标展示出来,简直不要太方便。
先注册一下,然后就可以拿到各种 Key,以及 API 地址。你需要做的事情也很简单,在前面的 Bash 脚本(aqijs.sh)里加上一句提交给 Initial State 就可以啦,脚本如下:
curl --request POST "https://groker.initialstate.com/api/events?accessKey=YOUR_ACCESS_KEY_HERE&bucketKey=YOUR_BUCKET_KEY_HERE&AQI$ID=$PM25"
但是 Initial State 对免费账户有点限制,每个月只能提交两万五千次,按照每月三十一天来计算,其实只能每五分钟提交一次而已。如果不想花钱的话,可以优化一下脚本降低 API 调用频次。
总之,现在可以看到这样的界面啦。
能不能再给力一点?
YOUR_IDEAS_HERE
大鱼老师太给力了
My idea…
我在树莓派上跑了一个 Python,每天自动抓 pm2.5 的浓度,然后发到 twitter 上,我去试试把镭豆的数值也加进去…
大概的过程就是,找了一个 Python 上提供 pm2.5 数据的库,一个发 twitter 的库,简单十几行代码就搞定了
btw.文科生握手…
这个对大多数人来说门槛有点高啊
1、本地没有领馆,数据对比挠墙。
2、镭豆好贵。
3、树莓派盒子很赞,
4、屌丝家用的小米插座……囧
感觉弄的过於复杂了…有直接可以输出 AQI 并外带继电器控制开关的设备, 利用物联网网站也不需要代码就可以达到输出成带历史趋势图的网页或自动更新微信之类的服务…
还应该再串个电容,来降低噪音;
再按照不同的污染级别,自动切换串哪个电容,实现自动的电机强度控制。
装一个homebridge,然后写一个插件,直接在iOS系统里面就能看到数值啦
js 都给了的话 bash 的算法也很容易写出:
https://gist.github.com/anonymous/de5335892fcf011fb389a23d0782962f
不过不得不声明这个线性插值是捣糨糊……