月別アーカイブ: 2014年8月

個人的に購入して便利だったBluetooth機器

概要

今時は複数の機器とペアリングできるBluetoothデバイスがあって便利だな、という話です。

キーボード

「ELECOM Bluetoothフルキーボード ゲーム用82キー 9台切替 ブラック TK-GMFBP043BK」

最大9台までペアリングが可能なキーボードです。単4電池2本で動きます。他の特徴を以下に挙げます。

  • USBによる有線接続もできる(mini B端子、ケーブル別売り)
  • Windows, Mac, Android向けの配列モードがある
  • 液晶エリアでバッテリー残量、モード、ペアリング状態等が確認できる

自分は持ち歩くことを前提にしていたので、この小さいタイプを購入しましたが、フルキー版もあります。自宅で使う分にはこちらの方がよさそうなので、近いうちに買おうかなあ、と思っています。

マウス

「ELECOM Bluetooth3.0マウス 5ボタン 9台切替 IR ブラック M-NV1BRBK」

同じくエレコム製の、9台までペアリング可能なマウスです。こちらも単4電池2本で動きます。

  • LED表示エリアで1~9までの数値が表示され、どの機器がペアリング対象か判別できる
  • ペアリング切り替えボタンはマウスの頭頂部にある。1→9の順にシーケンシャルにしか切り替えできない(これはしょうがない)
  • 光学式
  • 電源スイッチとペアリングボタンは裏面にある

キーボードと違って表示インジケーターや切り替えは若干制限がありますが、マウスというデバイスであることを考えると妥当なところです。こちらにはUSB接続の口もありません。

HID送信機

「iBUFFALO Bluetooth HID送信機 ブラック BSHSBT04BK」

上記2つを買うまではこれを使っていました。このデバイスは刺したマシンの入力デバイスを、Bluetooth HIDとして扱えるものです。たとえばノートPCに差してそのキーボードやトラックポインタをAndroid端末につなげることができます。

  • USB的にはMass Storageに見える
  • ストレージ内に専用アプリ(Windows/Mac用)があり、それを実行してデバイスを制御する
  • Windows/Mac/Androidのキーボードレイアウトに対応

なかなかよさそうなデバイスに見えるのですが、長く使っていると接触が悪くなるようで、うっかりちょっと触ったらUSB切断されるような事象が見られます。2つ持っているので、個体差でなく全体の傾向がそうなのではないかと怪しんでいます。あと、最初に購入した奴は初期不良でそもそもUSBデバイスとして認識できませんでした。

これをLinuxで使えるようにしたい、と常々思っているのですが、試しにUSBパケットをキャプチャしてみたところ、USB Mass Storage以外のパケットが流れていませんでした。パケットの中身を解析しないとダメそうです。

以下余談

個人的に、Bluetoothには10年ぐらい前から注目していました。それ以前はIrDAに注目していました。どちらも無接点で扱えるという点に魅力を感じています。

かつてPDAのPalmやZaurus(Linuxでない方)を持っていた頃、いわゆる母艦となるPCとのやりとりは可能な限りIrDAを使っていました。大きな理由として、PalmとPCをシリアルポート経由で接続するクレイドルの公式耐用回数が3000回と少なかったからです。1日3回使えば、1年3年持たない計算です(石川さんのご指摘があったので修正)。余談ですが、一般的なUSBのB端子(フラッシュメモリ等)は規格上1500回とさらに少ない値となっています。microUSBなら1万回です。

かつてIrDAはノートPに標準搭載されるぐらい一般的なものでした。自分がDebian開発者になって最初にパッケージしたのはirda-utilsでした(今はメンテナを別人に交代)。しかし代替となるような技術が出ないまま徐々に廃れてゆきました。

今の携帯電話についているような赤外線通信機能はIrDAのサブセットで、メールアドレスなどの交換にしか使わないことが前提の、軽量な実装です。

IrDAに代わってPDAに搭載されるようになったのがBluetooothです。Bluetoothは2.4GHzの周波数帯を使います。Bluetoothの出始めの時期は無線LANと割とかぶっていますが、周波数帯までかぶっています。

受難の時期

ゼロ年台前半は、毎年「今年はBluetooth元年だ」という記事が出るくらいに注目されつつも普及しませんでした。実際にBluetooth機器が安価に出回るようになったのは、iPhone、Android端末が売れるようになってからです。

デバイスドライバ、プロトコルスタックもなかなか整備されませんでした。Linuxでは3つぐらいの実装が乱立していた時期がありましたし、WindowsでもXPの時点での公式サポートは限定的なものでした。この時代、WindowsでBluetoothを使うのであれば東芝のBluetoothスタックが高機能でした。東芝はBluetoothにいち早く対応したメーカーの一つです。

その気になれば(+デバイス側が対応していれば)Bluetoothでテザリングもできます。無線LANより消費電力が低い点は魅力です。その分速度が犠牲になりますが。

ここには書きませんでしたが、Bluetoothヘッドフォンも愛用しています。うっかりスマホを置き忘れても、一定距離以上離れれば通信が切れるので、忘れ物対策にもなります。

Bluetoothは便利という話でした。

caffから受け取った署名を自分の鍵へ取り込む(gpg import)

ずいぶん間が開いてしまいましたが、GnuPG key signの作業をする(signing-party/caff)の続きです。前回は自分が相手に対して署名を行い、その結果を送るための手続きについて説明しました。今回はその逆、自分が受け取った相手からの署名を自分の鍵に追加する方法について説明します。

メールをファイルに保存

相手がcaffを使っている場合、pgp鍵のおそらくは各IDごとに1通のメールが届きます。

pub   4096R/9C0C1404 2009-09-11
                 指紋 = 9945 2B4D D28B EEAC 8AB5  C931 B066 62EC 9C0C 1404
uid                  NOKUBI Takatsugu <knok@daionet.gr.jp>
uid                  NOKUBI Takatsugu <knok@debian.org>
uid                  NOKUBI Takatsugu <knok@namazu.org>
uid                  NOKUBI Takatsugu <knok@fsij.org>
sub   4096R/07098680 2009-09-11
sub   2048R/861243E1 2012-08-23

私の鍵の場合だと、IDとしているメールアドレスは4つあるので、一人当たり4通メールが送られてきます。それらを1つづつファイルとして保存します。

メールの内容は、それぞれのIDに対応する、自身の公開鍵で暗号化されたpgp asciiメッセージです。

Subject: Your signed PGP key 0xB06662EC9C0C1404
Content-Transfer-Encoding: 7bit
Content-Type: multipart/encrypted;
 protocol="application/pgp-encrypted";
 boundary="----------=_1409005317-7907-1"
User-Agent: caff 0.0.0.638 - http://pgp-tools.alioth.debian.org/
To: knok@daionet.gr.jp
From: "Sender" <user@example.jp>
X-Mailer: MIME-tools 5.505 (Entity 5.505)
MIME-Version: 1.0
Message-Id: <20140825222158.8DAC3399@example.jp>
Date: Tue, 26 Aug 2014 07:21:58 +0900 (JST)

This is a multi-part message in MIME format...

------------=_1409005317-7907-1
Content-Type: application/pgp-encrypted; name="signedkey.msg"
Content-Disposition: attachment; filename="signedkey.msg"
Content-Transfer-Encoding: 7bit

Version: 1

------------=_1409005317-7907-1
Content-Type: application/octet-stream; name="msg.asc"
Content-Disposition: inline; filename="msg.asc"
Content-Transfer-Encoding: 7bit

-----BEGIN PGP MESSAGE-----
Version: GnuPG v1

hQIMA8hTZXMHCYaAAQ//XfTpsTvsdKaLNZ2TkYf/lDRtHo9DRjtSiFSYT4lWcFwF
9yxFzX8TL/L+7o2xZTZ7nXTOHAIMkgxi5KXnIDY5O8iNvrN9pqh3VmKDhB2GWHRD
yJGIBiQZ+bPavFWgiscWhy7FqhWfGi7XHBEbEIhfn9FBrziwgvLwkkC24ufUtnOM
/u39RDXrJKpi0GT0rMW+Ykg9vs6QNSdyDeBQRRS16KTRyljSGmRX/Q3y57WVw09v
(以下略)

メールの復号化

このメールをpgpコマンドで復号化します。

$ gpg -o msg mail.eml

次のユーザーの秘密鍵のロックを解除するには
パスフレーズがいります:“NOKUBI Takatsugu <knok@daionet.gr.jp>”
4096ビットRSA鍵, ID 07098680作成日付は2009-09-11 (主鍵ID 9C0C1404)
gpg: このセッションでgpg-agentは無効です
パスフレーズを入力:

gpg: 4096-ビットRSA鍵, ID xxxxxxx, 日付2009-06-19に暗号化されました
      “Sender <sender@example.jp>”
gpg: 4096-ビットRSA鍵, ID 07098680, 日付2009-09-11に暗号化されました
      “NOKUBI Takatsugu <knok@daionet.gr.jp>”

上記コマンド例では、入力されたファイルをmsgというファイル名で復号化しています。復号化した内容は、caffの標準テンプレートであれば以下のようになります。

MIME-Version: 1.0
X-Mailer: MIME-tools 5.505 (Entity 5.505)
Content-Type: multipart/mixed; boundary="----------=_1409005317-7907-0"

This is a multi-part message in MIME format...

------------=_1409005317-7907-0
Content-Type: text/plain; charset="utf-8"
Content-Disposition: inline
Content-Transfer-Encoding: binary

Hi,

please find attached the user id
        NOKUBI Takatsugu <knok@daionet.gr.jp>
of your key B06662EC9C0C1404 signed by me.

If you have multiple user ids, I sent the signature for each user id
separately to that user id's associated email address. You can import
the signatures by running each through `gpg --import`.

Note that I did not upload your key to any keyservers. If you want this
new signature to be available to others, please upload it yourself.
Note that I did not upload your key to any keyservers. If you want this
new signature to be available to others, please upload it yourself.
With GnuPG this can be done using
        gpg --keyserver subkeys.pgp.net --send-key B06662EC9C0C1404

If you have any questions, don't hesitate to ask.

Regards,
Sender

------------=_1409005317-7907-0
Content-Type: application/pgp-keys;
 name="0xB06662EC9C0C1404.1.signed-by-0x2E8162547E37CE41.asc"
Content-Disposition: attachment;
 filename="0xB06662EC9C0C1404.1.signed-by-0x2E8162547E37CE41.asc"
Content-Transfer-Encoding: 7bit
Content-Description: PGP Key 0xB06662EC9C0C1404, uid NOKUBI Takatsugu
 <knok@daionet.gr.jp> (1), signed by 0x2E8162547E37CE41

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1

mQINBEqq3iEBEAC2u1/t+pm5FSK//w9Qp/KxHW17rM+tOxqBNqVAZbFpOFu9Sa1i
U/C85ujEHKRIuLyESfzLg3gQmeojTritYPzG04s+bP+bbjujMkZZGhbrl4VjWA0L
DLO7uxwS/4scnBrlOONPaeRfP43T8a1hK2E0+8uIGhJIUt5fyW0o7Cs1MvL6T7UZ
YEwUW9lqciFhYEXr2zC6JYuofLytG1nUUFY4gcLWX/XSi1NUutL7Z5uJK3Ct8nlc
2AxFmPWkTYOcU6HpePQLlB7YGdldS8+41QkeDJm+w+ljqfI1vrVFT+SIHza8RiuW
(以下略)

署名の取り込み

この復号化されたファイルには、添付ファイルとして鍵への署名があります。本文中にも説明がありますが、gpg –importコマンドを実行することで署名を自分の鍵に取り込むことができます。

$  gpg --import msg
gpg: 鍵9C0C1404:“NOKUBI Takatsugu <knok@daionet.gr.jp>”新しい署名を1個
gpg:     処理数の合計: 1
gpg:       新しい署名: 1
gpg: 最小の「ある程度の信用」3、最小の「全面的信用」1、classic信用モデル
gpg: 深さ: 0  有効性:   3  署名: 127  信用: 0-, 0q, 0n, 0m, 0f, 3u
gpg: 深さ: 1  有効性: 127  署名:  33  信用: 126-, 0q, 0n, 0m, 1f, 0u
gpg: 次回の信用データベース検査は、2016-07-04です

これを、届いたメールの数だけ実施します。さすがに毎回passphreaseを入力するのはしんどいので、gpg-agent等を使うと楽になります。gpg-agentはバージョン2系からついてきます。1.4系にはありません。エージェントの使い方はssh-agentとよく似ているので、そちらに慣れている人であれば楽に使えるでしょう。

私は以下のようなワンライナーで一括処理をするようにしています。

for i in *.eml; do gpg --yes -o msg $i ; gpg --import msg; done

公開キーサーバー上の鍵の更新

最後に、公開キーサーバーへ新しい(電子署名の増えた)鍵を送信して更新します。これは普段から公開キーサーバーに鍵を登録している人だけがすればよい作業なので、運用によっては不要な作業です。

$ gpg --keyserver pgp.nic.ad.jp --send-keys 9C0C1404
gpg: 鍵9C0C1404をhkpサーバーpgp.nic.ad.jpへ送信
$ gpg --keyserver keyring.debian.org --send-keys 9C0C1404
gpg: 鍵9C0C1404をhkpサーバーkeyring.debian.orgへ送信

以上で作業は終わりです。

第116回東京エリアDebian勉強会参加、そしてJessieインストーラBeta1

先の8/23に、第116回東京エリアDebian勉強会に参加してきました。

今回のお題

東京エリアDebian勉強会は、参加に際してなんらかの「お題」が出ます。ここ最近は「もくもく会」と呼ばれる「みんなでその場で集まって、各自が抱えている課題に黙々と取り組む」スタイルが多く、お題の内容は「もくもく会での目標」となることがほぼ常態になっています。

私の目標は

といったあたりを考えていたのですが、当日の午前中にイレギュラーが発生したので、急きょそちらをもくもく会のターゲットにしました。

DELL Latitude E6520購入

日経トレンディネットでDELL Latitude E6520が約4万円で販売という記事をみかけ、急きょ開店直後のPCボンバーへ赴きました。あらかじめスペックはある程度把握していたのですが、店頭で現物も見せてもらえたので、以下の点を評価してその場で購入を決意しました。

  • 1080p フルHDディスプレイ
  • SandyBridge世代だけどCore i7-4200M
  • 今となっては貴重なIEEE1394端子搭載
  • メモリは最大8GB(もしかしたら16GBいけるかもしれない)
  • eSATA端子搭載
  • ビデオにNVIDIA GF119内蔵

特に1394はいまだDVビデオカメラを持っている身としては貴重です。今まで使っていた動画配信用のノートPCは液晶を壊してしまったので、これでdvswitch+icecast2でのOgg Theora/Vorbisによる自由ソフトウェアでの動画中継も可能です。

Debianのセットアップ

wheezy 7.6

まずは普通に現状の安定板、7.6のインストーラーを試しました。使用したメディアはUSBメモリです。

無線LANで躓く

E6520の無線LANデバイスはIntel Corporation Centrino Advanced-N 6205、iwlwifiドライバで動作します。このドライバはnon-free firmware blobが必要なのでhttp://cdimage.debian.org/cdimage/unofficial/non-free/firmware/から取得しておき、インストールメディアにudebファイルを置いておく必要があります。デバイスの認識自体はこれだけでいけました。

問題は接続勉強会の会場はWPA2-PSKな無線LANが来ていたのですが、SSIDとパスワードを入力してもうまくネットワークに接続できませんでした。きちんとログを見ていなかったので、原因はまだわかっていません。再度インストールにトライすれば確認できるだろう、ということでとりあえずこの問題はさておいて、ちょっと前にリリースされたJessie Installer Beta 1を使ってみることにしました。

Jessie Installer Beta 1

次の安定板になるJessieは来年初頭には出るだろうと思われます。まずはbeta 1インストーラーをためし、何か不具合がないかを調べようと試してみました。

無線LANが認識しない

いきなりこれです。有線(82579LM e1000e)は普通に認識できました。幸い会場には有線の口とケーブルもあったので、そちらを使ってインストールを続けました。

インストール完了後であれば、firmwre-iwlwifiをインストールすることで無線LANを使えるようになりますが、インストール時にうまく検出できていないのは問題です。

再起動できない

wheezy, jessieどちらにも共通していますが、再起動がうまくゆきません。ただしシャットダウンはうまく動きます。それぞれでインストールされるカーネルのバージョンは3.2.0, 3.14.0です。

世代的に枯れたハードウェアだと思うので、BIOSあたりが怪しいのではないかと思っています。バージョンを確認してみると、A00 (2011-05-11)と出ました。現在のBIOSはA19が最新のようなので、一度更新して確認したいところです。

今後の予定

幸い購入したばかりなので、今の段階であればいくらでもインストーラーのテストが可能な状態です。いろいろと試行錯誤をして、可能であればJessieのインストーラのバグなどをつぶしてゆきたいところです。

インストール状況が落ち着くまでは、USBメモリをルートメディアにしてしのごうと思います。今のところ16GBの2000円程度のメディアを使っていますが、速度的には思ったほど不満はありません。

twitter gemの更新に追従

概要

Rubyのtwitter gem Version 1.7.2を前提に書いていたコードを5.11.0向けに書き直したという話。

経緯

2011年ごろ、Twitterの自分のタイムラインを定期的に取得して保存するロガー的スクリプトをrubyとtwitter gemで書いて使っていました。

そのスクリプトは2013年のAPI変更で使えなくなってしまいました。この時には「過去のツイートを取得する」機能がTwitter側から用意されていたので、じゃあそれでいいやと思い、運用を止めました。

しかし、実際に取得できる情報は自分のツイートのみで、自分のタイムラインそのものではありませんでした。現状のTwitter検索は過去の内容を探すのも難しいので、「以前書いたスクリプトを直して再度自力でログを取ろう」と思い立ったので、そのために実施した修正についての記録をここに残しておきます。

修正点

クライアントインスタンスの取得と認証情報の設定

twitter 1.7.2では、Twitterモジュールのモジュール関数を呼び出して処理を行うことができたので、その方法を使って実装していました。5.11.0ではその手段が提供されなくなったので、REST APIを使う方法をとりました。

# 旧コード
require 'twitter'
Twitter.configure do |config|
config.consumer_key = 'xxxxxxxxx'
config.consumer_secret = 'zzzzzzzzzzzzzzzzzzzz'
config.oauth_token = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
config.oauth_token_secret = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
end
# 新コード
require 'twitter'
client = Twitter::REST::Client.new do |config|
config.consumer_key = 'xxxxxxxxx'
config.consumer_secret = 'zzzzzzzzzzzzzzzzzzzz'
config.access_token = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
config.access_token_secret = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
end

oauth_tokenとoauth_token_secretというアクセサは5.11.0でも使えますが、deprecated扱いなので警告が出ます。

タイムラインの取得

取得方法は単純で、今までTwitter.user_timeline()を読んでいたところをクラスメソッドuser_timelineに変えるだけです。

# 古い書き方
x = Twitter.user_timeline({:since_id => id)
# 新しい書き方
x = client.user_timeline({:since_id => id)

created_atの扱い

1.7.2ではツイートの作成時刻created_atは文字列で返されていたのでDateTime.parseで処理していましたが、5.11.0ではTime型を返すようになっています。

-        d = DateTime.parse(x.created_at)
+        d = DateTime.parse(x.created_at.to_s)

既存のコードをいじるのが面倒だったので、to_sした結果をDateTime.parseに渡しています。無駄ですがコードの変更は少なくてすみます。

結果

これらの修正で、自分のコードは無事動くようになりました。その後以下のような機能拡張を施して今にいたります。

  • 自分だけでなくフォロワーのツイートも取得する(user_timelineの代わりにhome_timelineを使う)
  • 各ツイートにscreen nameを付加する(x.user.screen_nameを参照)
  • 自分がつけたfavoriteも記録する(client.favorites()メソッドを使う)

UserStreamについて

今回、あえてUserStreamを使う方法は採用しませんでした。どうも同一ユーザーによるサードパーティアプリから接続できるUserStreamの数に制限があるようで、現状制限いっぱいいっぱいだからです。

しかし今回自作ロガーを再度稼働させたことで、UserStreamを貼りっぱなしのクライアントを立ち上げておく必要性は大きく下がったので、今後はUserStreamの利用も考えてみたいところです。

参考

関連書籍

GData.SpreadsheetsライブラリのListFeedに潜む罠

ほぼ1日ぐらいはまっていたのでメモ代わりに記録しておきます。

Googleの各種サービスはWeb APIを持っているだけでなく、.NET, Ruby, Python, Java等の各種言語向けSDKを用意しています。これを使ってGoogle Spreadsheetsを操作しようとして、ちょっとはまりました。

Spreadsheet APIのリファレンスには、spreadsheetを操作する2種類の方法を提供しています。ListFeed型(行単位処理)とCellFeed型(セル単位処理)です。

CellFeedは割と簡単で、指定した矩形範囲のセルを逐次処理させるのに向いている手段です。ただし、こちらにも若干の罠があって、デフォルトでは値の入っているセルのみしか操作できません。APIリファレンスのサンプルの書き方もそのようになっています。値の入っていないセルも捜査対象にするためには、CellQueryのReturnEmptyプロパティにReturnEmptyCells.yesを代入しておく必要があります。

using Google.GData;
using Google.GData.Client;
using Google.GData.Spreadsheets;

            CellQuery cq = new CellQuery(f.CellFeedLink); // f as WorksheetEntry
            cq.MaximumColumn = 5;
            cq.MaximumRow = 3;
            cq.ReturnEmpty = ReturnEmptyCells.yes; // 
            CellFeed cf = (serv.Query(cq));

上記のコードは、5×3のセルすべてにアクセスするためのコード例です。ReturnEmptyを指定しないと、値の入っていないセルはとばして結果を返してきます。

本題のListFeedの罠についてですが、まずAPIガイドのサンプルの一部を見てみます。

      AtomLink listFeedLink = worksheet.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);

      ListQuery listQuery = new ListQuery(listFeedLink.HRef.ToString());
      ListFeed listFeed = service.Query(listQuery);

      ListEntry row = new ListEntry();
      row.Elements.Add(new ListEntry.Custom() { LocalName = "firstname", Value = "Joe" });
      row.Elements.Add(new ListEntry.Custom() { LocalName = "lastname", Value = "Smith" });
      row.Elements.Add(new ListEntry.Custom() { LocalName = "age", Value = "26" });
      row.Elements.Add(new ListEntry.Custom() { LocalName = "height", Value = "176" });

      service.Insert(listFeed, row);

このコードをまっさらなシートに対して実行をすると、API側がHTTPステータス400を返すので、GDataRequestExceptionが発生します。

何が問題なのかというと、ListEntry.CustomのプロパティであるLocalNameは「1行目のカラムの値」との対応付けがなされている前提のAPIだからです。上記の例だと、1行目に”firstname, lastname, age, height”という値が入ったセルが必要だということです。このことについて気付いたのは、値の入ったシートに対してListFeedで読み出しをしてみたら、1行分だけ少ない結果が返ってきたからです。CellFeedはそういう制約がないため、1行目を含めて内容の取得、変更等ができます。

あとになってガイドを読み返したら、以下のような説明がありました。

list row
Row of cells in a worksheet, represented as a key-value pair, where each key is a column name, and each value is the cell value. The first row of a worksheet is always considered the header row when using the API, and therefore is the row that defines the keys represented in each row.

強調部分に明記されていました(強調はこちらでしたもの、原文に強調はなし)。ドキュメントはちゃんと読みましょう、というお話でした。