打造属于你自己的Mac机器人助手



自从看了钢铁侠系列之后,就对各种自动化、监控、预警的想法乐此不疲。如果你使用iPhone那么你手机上有Siri,可以执行一些你想让它执行的操作(比如呼叫某个联系人等等)。但我一直都是对着电脑的时间比对着手机更长,所以总喜欢电脑也可以更智能一点。其实我这里所期待的"智能",指的是一种“伪自动化”,它大体包含了两个特征:自动处理+及时提醒。



Mac OS X源于开源操作系统:Darwin,(它是Unix-like操作系统)。由于天生的hack文化,使得mac os x的可编程性非常好,很多对于系统的监控、操作都对外开放了shell 命令。这让我的想法并非遥不可及!

这几天没事的时候,写了一系列的automatic-services开源在了github上(service还不够多,也没有完全构建好)。目前已经构建完成或正在构建的列表如下:



可以编写的服务还有很多,比如你可以参照那份人体的作息时间表。让机器人自动提醒你,当前时段建议你做些什么。

目前,这些服务已经在我的mbp上运行良好(其中大部分都是开机自启动的deamon 程序)。

# 实现

分析

首先,我们来分析一下,都有哪些可能的服务。其实服务以其生命周期来划分,可以简单得分为:即时运行的简短服务(如报时);在系统开机状态下,常驻系统的deamon服务(内存、CPU、电池电量监控/报警灯)。

服务的大部分模式都是基于经典的"请求/应答"模式,此处也不例外。因为这里没有牵扯到mac os x系统编程,所以无法享有系统级别的一些好处(比如信号机制、系统事件等),这样也让你去实现一套自定义的协议变得困难(因为你必不可少需要利用消息机制、事件机制)。而去写一个while true done;是非常不靠谱的,特别是以deamon在后台运行(这大大占用CPU,造成CPU不停空转),CPU的温度直线飙升。

基于以上的原因,最后还是选择了已有的标准协议(http)及其服务处理程序。从构建http server的简易性角度考虑,node.js无疑是最好的选择,并且它的特性也非常适合这样的需求(单线程、基于事件)。所以最终选择采用node.js在本机运行一个http server来当做服务的容器。

如何启动服务

因为这些服务都宿主于本机启动的http server内部,要启动它只能从本地向其发出请求。比如你想打开goAgent,这时大家可能首先会想到的是打开浏览器,然后输入:

http://localhost:9876/proxy_on

然后,敲一下回车键,即可请求一个服务。没错,这是方式之一(这也为很多厌恶命令行的人们提供了一个远离它的途径)。
但如果只能这样,才能向http server发出请求,那局限性也太大了。这影响我们想在开机启动的时候,直接启动以deamon形式运行的那些服务(因为上面的这种请求方式必须要以浏览器为支撑,而且需要人工操作)。很幸运的是,shell自带的curl命令,支持从命令行形式直接向任何可访问的http server发起请求。这样,我们可以写一个脚本,以如下这样的方式来发起请求,触发deamon服务,随开机自运行。

curl http://localhost:9876/sayHello

sleep 10
curl http://localhost:9876/proxy_on

sleep 10
curl http://localhost:9876/weather

sleep 10
curl http://localhost:9876/memoryMonitor?opt=d          #opt:d run as deamon

可以看到后面是可以携带参数的,第一个参数opt(option)值为d(deamon)表示以deamon方式运行。所以,如果你有这样的需求,希望一个服务既可以以即时请求方式运行又可以以deamon方式运行,你可以在你的服务内部做判断:

if [[ $# -eq 0 ]]; then
    speak "battery checking!"
    monitor
    exit 0
fi

while getopts ":d" optname
do
    case "$optname" in
    "d")
        echo "monitor_deamon"
        monitor_deamon
    ;;
    *)
        echo "others"
    ;;
    esac
done
而所谓的deamon模式,其实是一个while-true程序,以run-sleep-run-sleep这样的模式不停得循环执行而已:
function monitor_deamon (){
    while [[ true ]]; do

        monitor

        if [[ $CONNECTED != $CONNECTED_TMP ]]; then
            if [[ $CONNECTED = 'No' ]]; then
                speak "AC Power disconnected!"
            else
                speak "AC Power connected! Battery is Charging now!"
            fi

            CONNECTED_TMP=$CONNECTED
        fi

        sleep 60
    done
}
但不管什么请求,在http server内部都是单独开启一个子进程去执行的,因此它不会对任何其他的服务产生影响,也不会对其他任何请求造成阻塞:
var exec         = require("child_process").exec;
var shellCmdList = require("./shellCmd").getShellCmdList();
var url          = require('url');
var queryString  = require("querystring");
function battery (request, response){
    var paramObj=queryString.parse(url.parse(request.url).query);
    var cmd=shellCmdList["battery"];

    if (typeof paramObj !="undefined" 
        && 
        typeof paramObj.opt != "undefined" 
        && 
        paramObj.opt=="d") {
        cmd+=" -d";
    };

    exec(cmd, function(error, stdout, stderr){
        logExecInfo("battery", error, stdout, stderr);
    })

    response.writeHead(200, {"Content-Type": "text/plain"});
    response.end();
}
同时,因为大部分的服务其“实时性”都不需要太强,所以上面的run一次之后,至少会sleep 60秒,而run自身大概一次只需要1秒左右。因此cpu的占用率几乎为零,可以忽略不计(即时你开几十个这样的deamon service,以现在至少i5以上的处理器能力,都可以忽略它们)。 上面说到我这里的“伪自动化”至少需要两个特性。其中,自动处理,就交给这些service了,那即时提醒很明显需要依靠“语音提醒”。这也是mac 系统的一大特性,它不需要任何第三方程序的依赖,本身就是可“说话的”,而且支持基本所有的语言,以及可自动选择语音库。你只需要打开terminal,输入如下命令:
say -v alex 'Hello, Kobe bryant'

即可让其说话(其中的-v 表示可以选择特定的一个语言库,这里是alex发音,你可以选择中文)!

因此在使用该服务的之前,你首选需要开启系统的该功能。详见,我的另一篇文章:《Python脚本实现Mac开机自动语音播报天气》。并且,你同时还应该知道如何使得一个脚本程序开机自动运行,在这篇文章中也有说明。

只要你能想得到,并且你也愿意去实现(首先你得会shell或其他任何一种mac支持的脚本语言),你可以去尝试你想实现的自动化服务。

移动端控制

如果你的电脑跟手机都连接着同一个Wi-Fi,那么你同样可以通过手机的浏览器来完成对你电脑上的httpserver(虽然你的httpserver监听的是你本机[localhost/127.0.0.1],但在局域网里,你的电脑仍然有一个在局域网内的IP地址来唯一标示你的电脑,所以效果是一样的)。只需打开系统设置-网络设置,看看你Wi-fi那一栏上显示的IP是什么,然后在手机浏览器中,将上面的localhost修改为该IP即可,但端口还是必须要的。

当然,如果你会iPhone开发,能够为其编写一个专属的客户端,那就再好不过了!

远程控制猜想

这里在一台机器上,运行httpserver及client。那有没有可能像正常的访问网站的方式一样使得可以从远端操控电脑?我之前写过一篇文章,XXX是借以邮件协议来实现对电脑的远程操控(当然这里也依赖http协议,但它只是起到一个传输介质的作用)。那现在我们有httpserver了,一切都简单了,你只要对外暴露你当前的主机ip即可(当然这样你就必须对这些service进行授权访问了)。
另外,如果你觉得让你本机暴露在互联网上不现实。那么可以借助一个代理、中介的模式来实现。这个很容易,现在很多的app engine平台,你可以申请一个作为你的代理服务器。这样你跟你想操控的机子的http server都只需要直接跟这台代理服务器交互就可以了。代理服务器可以短暂缓存你发来的服务指令,http server间隔一段时间去轮询一下代理服务器,获取收到的命令。

是Mac让生活更美好

其实,你会发现一切重复的动作,都应该被程序来取代,然后交给计算机执行。至少unix系的操作系统是支持这样做的,但因为会编程的人只是少数,而mac os x一向以高水准的用户体验来标榜自己,所以它确实为很多不会编程,又非常想减轻一些重复劳动的普通用户提供了一些自动化的工具。

apple script


一个只适用于mac 操作系统的脚本语言,上图右框

Automator的工具


上图左框,里面可以将各种重复的流程化的操作串联起来,让系统来充当“机器人”去为你执行那些无聊而重复的事情(比如重命名一个文件夹里所有的文件名称,去一个网站上按一些规则抓取特定的图片…)。

mac 提供的语音听写

我尝试过,但识别率不是非常理想,除非你英语非常标准。

关于vino

vino是科比布莱恩特的新昵称,寓意是陈年美酒,愈久弥香!

Enjoy & Have fun!

<div  style="padding-top:20px">         

版权声明:本文为博主原创文章,未经博主允许不得转载。

</div>