自從看了鋼鐵俠系列之後,就對各種自動化、監控、預警的想法樂此不疲。如果你使用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/sayHellosleep 10curl http://localhost:9876/proxy_onsleep 10curl http://localhost:9876/weathersleep 10curl 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 0fiwhile getopts ":d" optnamedo case "$optname" in "d") echo "monitor_deamon" monitor_deamon ;; *) echo "others" ;; esacdone
而所謂的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平台,你可以申請一個作為你的Proxy 伺服器。這樣你跟你想操控的機子的http server都只需要直接跟這台Proxy 伺服器互動就可以了。Proxy 伺服器可以短暫緩衝你發來的服務指令,http server間隔一段時間去輪詢一下Proxy 伺服器,擷取收到的命令。
是Mac讓生活更美好
其實,你會發現一切重複的動作,都應該被程式來取代,然後交給電腦執行。至少unix系的作業系統是支援這樣做的,但因為會編程的人只是少數,而mac os x一向以高水準的使用者體驗來標榜自己,所以它確實為很多不會編程,又非常想減輕一些重複勞動的普通使用者提供了一些自動化的工具。
apple script
一個只適用於mac 作業系統的指令碼語言,右框
Automator的工具
左框,裡面可以將各種重複的流程化的操作串聯起來,讓系統來充當“機器人”去為你執行那些無聊而重複的事情(比如重新命名一個檔案夾裡所有的檔案名稱,去一個網站上按一些規則抓取特定的圖片...)。
mac 提供的聽寫
我嘗試過,但識別率不是非常理想,除非你英語非常標準。
關於vino
vino是科比布萊恩特的新暱稱,寓意是陳年美酒,愈久彌香!
Enjoy & Have fun!