Ringでリモコン

あのRingが届きました。


iPhoneアプリで好きなジェスチャーとアクションを対応付けして使います(もちろん設定後は、アプリがバックグラウンドに回っていてもRingの操作を受け付けてくれます)。
標準アクションだけでもいろいろな操作に対応していますが、任意のURLにGETリクエストを送ることができるため、新しいアクションを作るのも比較的簡単。そこで、ひとまず部屋にあるものをいろいろ操作して魔法気分を味わってみることにしました。


とりあえず、照明のON/OFF。


ArduinoEthernetシールドをつけ、付属スケッチのWebサーバをちょっと改造して、アクセスがくるたびに適当なピンの出力のHIGH/LOWが切り替わるようにします。そして、その出力をSSRに入れ、AC出力をトグルできるようにします。


そんでもって、適当なジェスチャー(電球を描いてるつもり)に対して、 ArduinoのwebサーバのURLを開くアクションを設定すれば、準備は完了。

早速、実際に照明をON/OFFしてみました。(ピコピコ鳴ってるのはArduinoに取り付けた圧電スピーカーの音で、アクセスがあった時に鳴らしてます。)


もひとつ、同じようにルンバも操作(起動)できるようにしてみました。ルンバのリモコン操作は以下の日記で書いたようにXBeeを使ってます。
ルンバをXBeeでリモコン操作 - Okiraku Programming

同じく、CLEANボタンを押すコマンドを送信する処理を書いたCGIのURLを開くよう設定して、

試してみると…

ジェスチャー一つで部屋を掃除できるようになりました。


さて、難点もいくつか。結構大きく、重さも感じる。そのかわりバッテリはジェスチャー1000回分持つそうなのでまだましか。それよりも、iPhoneとはBluetooth接続しているのですが、RingとiPhoneが2m以内の距離にある必要があります。これはちょっと狭い。。iPhoneを目の前の机に置いていても、腕を伸ばしただけで接続が切れるレベル。しかも再接続に失敗することが多い。特にMacなどBLEを喋れるデバイスが近くにあると高確率で再接続できないような気がします。 iPhoneアプリをホームボタンのダプルプッシュで強制終了して再起動すると概ね再接続できますが。。


というわけで、常用はちょっと困難ですが、なかなか魅力的なデバイスではあります。

U2F対応キーを買ってみた

Chrome 38 (Win/Mac/Linux) がUSBセキュリティキーに対応し、あわせてGoogleの2段階認証もこれに対応しました。従来のスマートフォンによる認証と比べてキーを挿す*1だけとお手軽なほか、似たような外見・URLのフィッシングページに誘導されてもブラウザが正規のページか自動的に判断するので騙されてキーを入力してしまうことがなくセキュアだということです。

Use Security Key for 2-Step Verification - Android - Google Account Help


利用できるのは、FIDOが策定しているU2F (Universal 2nd Factor)に対応したキー。


私はamazon.comで↓のを買いました。 たったの$6なり。ボタン等はついてないので挿しっぱなしにはできず、認証時に挿すという感じになりますが、とにかく安かったので。

http://www.amazon.com/gp/product/B00OGPO3ZS/ref=oh_aui_detailpage_o02_s00?ie=UTF8&psc=1


プラスチックのカードが封筒に入って届くので、切り取って、USB端子部分の厚み調整のために折り曲げて完成。


登録はGoogleの登録ページを開いてキーを挿すだけ。以降、認証するときはキーをさすと、自動的にログイン後のページに進んでくれるという感じです。


USBキーが使えない環境では、従来のスマートフォンを使った2段階認証を利用できます。

JavaScript API

FIDOでは、JavaScriptAPI*2も策定しています。

https://fidoalliance.org/specs/fido-u2f-javascript-api-v1.0-rd-20141008.pdf


Googleの認証ページではhigh-level APIを使っているようですが、プラグインと会話できるようにmanifest等が必要なのでセットアップが大変。

今のところ、Googleの2段階認証以外でお手軽に実験するには、リファレンス実装( GitHub )のExtension ( https://chrome.google.com/webstore/detail/fido-u2f-universal-2nd-fa/pfboblefjcgdjicmnffhdgionmgcdmne ) を入れ、デモサイト( https://u2fdemo.appspot.com/ )を訪れるのが簡単そう。

発行されたキーの内容や、認証の様子がビジュアルに見れます。Mac/Chrome 38でなんども動かしてると結構落ちるのがアレですが。

*1:またはボタンを押す

*2:直接キーと情報をやり取りするlow-level、low-levelなページとEventで会話して認証してもらうhigh-levelの2種類

MacのPreviewで画像ベースのPDFファイルサイズを小さくする

スキャナ等で取り込んだJPEGファイルをPDF変換する際、Previewで単純に形式をPDFにして保存すると、とんでもなくファイルサイズが大きくなって驚くことがあります(1MBのJPEGファイルが10MB超になるとか)。


システム標準でReduce File SizeというQuartzフィルタが用意されており、これを保存する際に選択しておくとファイルサイズを縮小してくれるのですが、こいつは非常に強力に圧縮をかけてくれるため、画質がひどいことになります(主に最大512×512ピクセルに縮小していることが原因かと)。


こんなときは、もとのJPEG程度のファイルサイズにしてくれるQuartzフィルタを用意してあげると良いです。

具体的には、 http://neocat.jp/Reduce%20File%20Size%20Slightly.qfilter をダウンロードして /Library/Filters/Reduce File Size Slightly.qfilter に保存します。そして、変換したい画像ファイルを「プレビュー」開いて、「ファイル」→「書き出す…」を選択、フォーマットをPDFにした後、Quartzフィルタから「Reduce File Size Slightly」を選んで保存すればOKです。


やってることはPDF内の画像をJPEG圧縮してるだけ。要はReduce File Size*1から画像縮小とかのよけいな処理を取り除いたものです。

*1:フィルタのファイルは /System/Library/Filters/Reduce File Size.qfilter にあります。

zshのcorrectallでドットファイルを無視

zshの機能に、コマンドやファイル名のスペルミスを修正してくれるCORRECTとかCORRECT_ALLという機能があります。

具体的には、correctを指定しておくとコマンドのスペルミスすると修正候補を提示してくれるので、n (修正せずそのまま実行)、y (修正して実行)、a (中止)、e (コマンドを再編集) のいずれかのキーを入力します。例えば、

% setopt correct
% sl
zsh: correct `sl' to `ls' [nyae]? y
... (lsの実行結果) ...

という感じ。


さらにcorrectallを使うと、

% ls /etc/paswd
zsh: correct to `/etc/paswd' to `/etc/passwd' [nyae]? y
/etc/passwd

といった具合に、引数のファイル名も修正してくれるようになります。


慣れると、neko.c を neok.c と打ち間違えた、などという時にyキー一発で正しく修正できるので、便利かもしれません。



しかし、correctallはコマンドの引数と似たファイル名があると、「もしかしてこのファイル?」と提案してきてしまうので、場合によっては鬱陶しいことになります。
特にドットファイル(隠しファイル)の場合、存在を意識していない分、突然修正候補が出てくると「??」となりやすいです。例えば.log というがあるディレクトリで、git logと打つと…

% git log
zsh: correct 'log' to '.log' [nyae]? n

という具合に、毎回 n を入力してあげなければなりません。


そういうときは、zsh-5.0.6から導入された CORRECT_IGNORE_FILE 環境変数に、修正候補として出してほしくないファイル名(ワイルドカード使用可能)を指定しておくと良いです。ちなみに以前からあるCORRECT_IGNOREはcorrectオプションの方に対応しており、コマンド名の修正候補から除外することができます。例えば、.zshrcに

CORRECT_IGNORE='_*'
CORRECT_IGNORE_FILE='.*'

と指定しておけば、コマンド名として_で始まる補完関数を修正候補として出ないようにしたり、ファイル名としてドットファイルを修正候補にしないようになります。


ちなみに、mvやcp, mkdirなど、存在しないファイルを指定するのが普通のコマンドに対しては.zshrcで

alias mv='nocorrect mv'
alias cp='nocorrect cp'
alias mkdir='nocorrect mkdir'

として最初からスペルチェックがかからないようにしておいた方が良いでしょう。

Gmailでプッシュ配信されるメールを取得

Gmailをはじめとする、IMAPのメールサーバ*1から、プッシュ配信されるメールを取得する方法を調べたのでメモ。新規メールを監視してリアルタイムに何らかの処理を実行するといったことが可能になります。

使うライブラリは

require 'net/imap'


まず接続と認証、それから監視したいメールボックスGmailなら"INBOX"や特定のラベル*2など)をselectしたのち、IDLEコマンドを発行してイベントが起こるのを待機します。新たなメールが追加されるとEXISTSイベントで新しいメールのIDが通知されるので、imap.idleの中でそれを検知し、idleを抜けます(そうしないとメール取得コマンドなどがデッドロックする)。また、しばらくイベントがないとサーバから切断されることがあるので、その場合は再接続します。

last_id = -1
while true
  begin
    unless $imap
      $imap = Net::$imap.new('$imap.gmail.com', 993, true)
      $imap.login(mail_user, mail_password)
      $imap.select(mail_label)
      puts "connected to $imap server"
    end

    $imap.idle do |resp|
      if resp.name == "EXISTS"
       last_id = resp.data
       $imap.idle_done
      else
        p resp
      end
    end
  rescue Net::$imap::Error => e
    if e.inspect.include? "connection closed"
      puts "connection closed: reconecting..."
      $imap = nil
    else
      raise
    end
  end
  
  next unless $imap

  fetch_mail last_id..-1 
end

この実装だとIDLEコマンド実行中以外に新規メールが届いたらイベントを取り逃すような気がするので、イベントハンドラを別途登録してその中で処理すべきかも。


メールの取得はfetch_mailという関数で。HTMLメールの場合、マルチパートになっているので、下記ではプレインテキストの部分をとるようにしています。part.subtype == "HTML"とすれば、HTML部分がとれます。BODYというattributeをfetchすると、そこにマルチパートの情報が入っているので、それを解析して"BODY[n]"を改めてfetchしています。QUOTED-PRINTABLEでエンコードされている場合はそのデコードも行っています。

def fetch_mail(range)
  mails = $imap.fetch(range, ["UID","BODY","FLAGS"])
  mails.each do |mail|
    uid = mail.attr["UID"]
    seen = mail.attr["FLAGS"].include? :Seen
    body = mail.attr["BODY[1]"]
    if mail.attr["BODY"].multipart?
      n = mail.attr["BODY"].parts.map.with_index{|part,idx|
        idx if part.media_type == "TEXT" and part.subtype == "PLAIN"
      }.select{|x| x}[0]
      if not n
        raise "NO PLAINTEXT PART"
      end
      body = $imap.uid_fetch(uid, "BODY[#{n+1}]")[0].attr["BODY[#{n+1}]"]
      if mail.attr["BODY"].parts[n].encoding == "QUOTED-PRINTABLE"
        body = body.unpack('M')[0]
      end
    end
  end

  process_body body
end


あとは、process_bodyに本文が渡されるので、適当な処理をすればOK。seenは既読かどうかのフラグです。ちなみに、BODY[n]をとると既読になります。そういえばタイトルを取り忘れましたが、fetchするattributeに"ENVELOPE"を追加し、mail.attr["ENVELOPE"].subjectをとってくれば良いでしょう。


ちなみにこれを書いた目的は、Ingressのポータル破壊通知メールが飛んできたときに、それをGmailの振り分けでラベルをつけた後アーカイブすることで新着メール通知が飛んでこないようにし、別途このスクリプトでポータルの位置と画像付きでリアルタイムにTwitterに通知する、というものです。まとまってたくさん来たりするから他のメール通知に混ざってほしくない、という。。

HTMLメールなので、nokogiriを使ってHTMLをパースして、画像ファイルやIntel MapのURLなどを抽出しています。

*1:ただしプッシュ配信に必要なIDLEに対応したIMAPサーバ

*2:自動振り分け機能でラベル付けすることを想定しています

JavaScriptでMacに歌ってもらう

最近のChrome, SafariJavaScriptには音声合成APIが入っています。
使い方は簡単で、JavaScriptコンソール等で

var msg = new SpeechSynthesisUtterance('こんにちは!');
msg.lang = "ja-JP"; // 言語指定
window.speechSynthesis.speak(msg);

のようにするだけで喋ってくれます。


音声エンジンを変えることも可能です。使える音声の一覧を

var voices = speechSynthesis.getVoices();

で取得すると、voicesに音声オブジェクトのリストが返ってくるので、この中のlangやnameを見て使いたいものを

msg.voice = voices[3];

などとSpeechSynthesisUtteranceオブジェクトのvoice属性に指定すればOKです。


MacだとOSの音声合成エンジンを呼び出すことができます。Mac1984年から脈々とMacInTalkという音声合成エンジンを受け継いできており、Mac OS Xになっても昔のVoiceがまだ利用できます。その中には、MacInTalk 3で収録された、歌を歌ってくれるという遊び心あふれるVoiceも未だに入っています。当時はこれを33MHzの68030とかで合成していたんですよね。そんなVoiceがJavaScriptで呼び出せるなんて、ちょっと感慨深いものがあります。というわけでやってみない手はありません。

さっそく動かしてみる

MacJavaScriptで歌わせるサンプル: http://jsfiddle.net/ApvvB/7/


上記のページで、Bells, Cellos, Pipe Organ, Good News, Bad Newsのボタンを押すと、それぞれのVoiceでラララーと歌ってくれます。
defaultは0番のボイスで読み上げ、stopは歌うのを停止させるボタンです。


rate,pitch,volumeのスライダーを動かすと、早さ、音程、音量を変えられます。が、rateは歌には効かないようでした。

Mac OS XSafari, Chromeで動作を確認できました。


ChromeはonLoadの中でvoicesを取得しようとすると、なぜか空のリストが返ってくるので、ボタンをクリックされたタイミングで取得するよう注意が必要でした。

おまけ

ちなみに、端末上で

say -v Bells 'have a nice day'

などとしても、歌わせることが可能です。

12言語polyglotで足し算

CodeIQのコーディングパズルで、同一のソースコードで複数の言語処理系で動作するプログラム(いわゆるpolyglot)として、標準入力からの2つの数を加算して出力するものを書くというお題"hybrid A+B problem"が出ていました。12言語(Bash, Brainfuck, C, CLIPS, INTERCAL, Perl, Perl 6, Python, Ruby, Pike, Prolog (swi), Whitespace)で動くものを書いてみましたので晒しておこうと思います。*1


まあ、マイナー言語で埋め込みが簡単なもの*2を多用していて、若干ずるい感じがしますが。。他の方の素晴らしい回答に埋め込んだら、きっと15言語とかにできるんじゃないかなということで晒す価値はあるかなと。


なお動作はideone.comで検証するということで、ideone.comのコンパイラインタープリタのバージョン向けでしか動かないものもあります*3。試したい時はコマンドでideone.comにソースを送信・実行 - Okiraku Programmingのツールなどを使うと良いと思います。


で、ソースはこちら。http://ideone.com/JmXx4l


行数を食っているINTERCAL*4とか適当に書いたせいで異様に文字数の多いBrainfuck*5が目立ちますが、一応少し説明をば。
まずあんまりエラーが出ないので楽に埋め込める系*6


まず、CLIPSについては()で括られたキーワードが出てくるまでスキップする模様、そして(exit)行が出てくるとそれ以降パースしないみたいなので、比較的簡単に埋め込めます。

INTERCALも、最初の「DO」とか「PLEASE」が出てくるまで何を書いてもOKで、文法エラーがあってもその行に実行が移らなければエラーになりませんので、これも簡単です。

Prologもswiであれば、最初に「:-」が出てくるまでスキップしてくれるよう。最後に:- とか書いておけば、それ以後でシンタックスエラーがあると警告が出るものの、mainの実行はしてくれますから割とどこでもかけます。gnuだとこうは行きませんが。

Brainfuckは[]<>+-,.意外は無視される上、最後に0をポイントした状態で [ ... ] と書いておけば、...部分はスキップされますから楽勝ですね。

それからWhitespaceも、各行に最初に3文字以上スペースを入れていれば、push ダミーの数値(スタックに値を積む)と見なされますから、適当に空白を入れておいて最後にコードを置きました。まあさっさと終了(改行3連続)させてしまっても良いんですが。


さて、以上を除いた残りのbash, C, pike, perl, perl6, python, rubyについてです。

まず1行目。

#define x /* ++[...] [# Brainf**k

Cとpikeについてはプリプロセッサコメントで以降しばらく無視してもらいます。それ以外の言語にとっては#で始まるのでコメント扱いです。

# :-set ... :-main. :- # prolog

これもコメント扱い。

0 and nil and q <<2;

pythonにとっては、0 and <有効な式>(q <<2はシフト演算)に見え、and以降は評価しないので、qという変数が未定義だとかエラーになりません。
bashはこれを"0"というコマンドだと思って実行してしまうため、エラーが出ます。が実行は継続されます。今回の問題ではstdoutへの出力さえ正しければ、stderrは気にしなくても良いことになっているので放置(ひどい)。また、<<2をヒアドキュメントの開始と見なし、次の2まで読み飛ばします。
perlは、q<をクォートの開始だと解釈します。そして、<>の対応のネストを追いながら、>が閉じられるまでを文字列とみなします。
一方perl6は同じくクォートを開始しますが、 "<<" という括弧だと思い、">>" を探します……というのが今の仕様なんですが、ideone.comの古いバージョンだと動きが違い、ネストを無視して">"を探します。
rubyにとっては0は真なので、0 and nil and ... まで評価し、...部分は実行されません。q <<2については、qというメソッドにヒアドキュメントで次の"2"の行が出てくるまでをテキストとして渡す、という意味としてパースされます。

''' #'

pythonにとってはマルチラインコメント(というか文字列)の開始。次の'''までスキップします。他の言語はコメントとか文字列としてスキップ中。

2

ここでrubybashがヒアドキュメント終了で復帰。

eval 'read a;read b;echo $[a+b];exit;#' if !1; #bash

evalでbashのコードを実行し、終了させて以降のパースを止めさせます。rubyはif !1 によってこの行は評価しません。

puts gets.to_i+gets.to_i;  #Ruby
__END__

rubyのコード実行、__END__によってパースを止めさせます。

>;say get()+get();' #Perl 6

最初の">"を見つけたperl6が復帰。コードを実行した後、シングルクォートにより以降は文字列だと思わせます。

#>;print <>+<>,"\n"; __END__ #Perl

2個目の">"を見つけたperlが復帰。コードを実行し、__END__によって以降脱落します。

ここで、しばらくCLIPSやINTERCALのコードが続きます。

   */

が出てきたところでCとpikeがコメントを抜けます。この2つはプリプロセッサで切り分けてます。

#if __PIKE__  
int main() {write((int)Stdio.stdin->gets()+(int)Stdio.stdin->gets()+"\n");} //pike
#else   
int main(){int a,b;scanf("%d%d",&a,&b);printf("%d\n",a+b);return 0;} //C
#endif   

最後の

//'#]''';print int(raw_input())+int(raw_input());  #Python #Whitespace

は、C・pikeはコメント、Perl6の文字列終了+以降コメント、Brainfuckの最後の]、Python文字列の終了と、Pythonのコード*7が出てきます。そしてそれ以降にWhitespaceが入ってます。


多分並べ替えたりすればもうちょっと綺麗というか短くなるような気もするんですが、まあ面倒だし動くから良いやという。イマイチ感。


なんかがっかりな気分になったでしょうから、shinhさんの素晴らしいコードと解説サイトをご覧になって口直しされると良いと思います。
http://d.hatena.ne.jp/shinichiro_h/20140421

*1:ちなみに、C、C++C++11、ObjectiveCは同じ言語クラスということで複数とカウントしないというルールでした。perlとperl6は全くといって良いほど互換性がないので別カウントです。

*2:CLIPS, INTERCAL, Prologあたり。Brainfuck, Whitespaceに、Cを入れたら使えるだろうpikeも入れたら約半分か…

*3:特に古めのperl6とか

*4:入力も出力も、1byteごとに前の文字とxorされたりビット列の前後がいれかわったりする狂気じみた仕様の言語なので、入出力処理がでかい…

*5:ヘッドをずらすようなループを書くのが面倒で桁数分コピペで繰り返すなど手抜き過ぎ

*6:ズルとも言う

*7:何でinput()じゃなくraw_input()にしたんだったかなあ…忘れた…