SublimeText in Windows で TabNine

評判がいいのでTabNineを入れてみた。 Rubyの Semantic Completion を有効にするところでハマったのでメモ

  1. TabNineのインストール Package Manager から普通にインストール

  2. rubyのLanguage Serverをインストール

> gem install solargraph
  1. TabNineのコンフィグディレクトリを検索 Sublimeのエディタ画面で、TabNine::config_dirと入力するとディレクトリが補完される。 このディレクトリにTabNineSample.tomlファイルがあるので、同じフォルダのTabNine.tomlにコピーする。

  2. TabNine.toml ファイルを修正 Rubyの場合下記のようにgem実行ファイルがbatになるので、フルパスでbatファイルを指定してやるようにすると動いた。

[language.ruby]
command = "C:\\Ruby25-x64\\bin\\solargraph.bat"
args = ["stdio"]
install = [["gem", "install", "solargraph"]]
  1. Semantic Completion 有効化 エディタ画面で TabNine::sem と入力。 これで動くはず。

海外に置いたAppleTVで日本のNetflixを見る - Raspberry Piで作るVPN切り替え機

やりたいこと

  • USの家に置いてあるAppleTVで、日本のIPからしか使えないサービスを使いたい。
  • 常時日本経由のインターネットだと速度面、レイテンシ面で不利なので任意に切り替えられるようにしたい。

実装の方針

  1. RaspberryPiで日本の実家においてあるサーバへVPNを張り、日本側の出口とする。
  2. RaspberryPiのwlan0を家庭内LAN、eth0を切り替え用LANにして、eth0の先にAppleTVをぶら下げる。
  3. RaspberryPiにて
    • eth0DHCPサーバ立てて、IP配るようにする
    • eth0wlan0の転送設定を入れる
    • デフォルトゲートウェイ(DGW)をJP側とUS側の間で切り替えることで、出口を制御する
    • DGWの切り替えはコマンド入力だと家族が使えないので、Homebridgeを使ってiPhoneから制御できるようにする

実際の構築

VPNを張る

SoftEther VPN Serverを使った拠点間VPN。これはまぁ色んな所に参考情報が記載されているので簡単に。

  • 実家側はNIC2枚刺しのCentOSSoftEther VPN ServerVPNサーバを用意
    • 仮想HUBを作成し、2枚めのethにブリッジするようにする
    • これでこの仮想HUBへアクセスすると、実家のLANにL2接続できるようになる
  • USのRaspberryPiにSoftEther VPN Bridgeをインストール

    • 仮想HUBを作成して実家の仮想ハブにカスケード接続を設定する。
    • tapバイスにローカルブリッジするように設定する。(ここではtap_jikkaとした)
  • この時点でうまくローカルブリッジが動いていれば、数百msec遅延のping、すなわち日本経由でのpingが返ってくるはず。

$ ping -I tap_jikka 8.8.8.8

ラズパイのネットワークの設定

IPアドレスを固定

/etc/dhcpcd.conf で、下記行を追加して、eth0wlan0IPアドレスを固定する。

interface eth0
static ip_address=10.0.0.1/8

interface wlan0
static ip_address=192.168.50.111/24

udhcpd設定

DHCPサーバをインストールしてセットアップ。

  1. udhcpdをインストール。
  2. /etc/udhcpd.conf をデフォルトの設定を参考にしながら編集。
start           10.0.0.2        #default: 192.168.0.20
end             10.0.0.254      #default: 192.168.0.254
option  subnet  255.0.0.0
interface       eth0            #default: eth0
opt     dns     8.8.8.8
opt     router  10.0.0.1
option  domain  local
option  lease   864000          # 10 days of seconds
  1. 自動起動とかを設定
# systemctl enable udhcpd
# systemctl restart udhcpd

ルーティングやNATの設定

/etc/rc.localexit 0より前に下記記載を追加する。 これにより起動時にこれらのコマンドが実行され、再起動後も有効になる。

# パケット転送を許可
echo 1 > /proc/sys/net/ipv4/ip_forward

# 実家サーバへのアクセスは、常にUS側のゲートウェイを通るようにスタティックルートを設定
jikka_ip=$(ping <実家サーバドメイン> | head -n1 | awk 'BEGIN{FS="\\(|\\)"}{print $2}')
route add -host ${jikka_ip} gw 192.168.50.1 wlan0

# iptablesで転送の許可と、10.0.0.0/8 ネットワークへのIPマスカレード設定
iptables -I FORWARD -j ACCEPT
iptables -t nat -I POSTROUTING -s 10.0.0.0/8 -j MASQUERADE

ここまでで、eth0に繋がっている装置からインターネットに出ていこうとすると、10.0.0.1を経由して、192.168.50.1のルータ経由での通信となる。 

ここでデフォルトゲートウェイを下記コマンドで変更すると、日本のルータ192.168.0.1からインターネットに出ていくようになる。

# route add default gw 192.168.0.1 tap_jikka
# route del default gw 192.168.50.1 wlan0

Homebridgeで切り替えできるようにする

HomebridgeはApple HomeKitの装置を模倣するオープンソースプロダクト。 これを使って電球と同じような感じでiPhoneから切り替えられるようにする。

Homebridgeは導入済みとする。

  1. cmdswitch2プラグインを導入する。
# npm -g install homebridge-cmdswitch2
  1. config.json を編集する。
  2. on_cmdには日本ルータをデフォルトゲートウェイにする設定を入れる。
  3. off_cmdにはUSルータをデフォルトゲートウェイにする設定を入れる。また、VPNサーバへのスタティックルートが落ちる事があるので、ここで改めて追加するようにする。
  4. state_cmdには、日本ルータを向いているときに終了コード0を返すようなコマンドを設定する。
    {
      "platform": "cmdSwitch2",
      "name": "JP-US switch",
      "switches": [
        {
          "name": "JP net",
          "on_cmd": "sudo sh -c 'route add default gw 192.168.0.1 tap_jikka; route del default gw 192.168.50.1 wlan0; true'",
          "off_cmd": "sudo sh -c 'route add default gw 192.168.50.1 wlan0; route del default gw 192.168.0.1 tap_jikka; true'",
          "state_cmd": "ip route | grep default | grep jikka",
          "polling": true,
          "interval": 5
        }
      ]
    }

これでHomebridgeをrestartするとこんな感じにiPhoneに表示されるようになり、切り替えられるようになる。

Apple TVリモートを使えるようにする

この構成だとApple TVは10.0.0.0/8のネットワークにあり、iPhone192.168.0.50/24のネットワークにあるので、Apple TVからのBonjourマルチキャストDNS配信がiPhoneから見えないことになる。iOSにはAppleTVのリモコンとして動く機能があるが、この機能はBonjourに依存しているため、このままでは使えないことになる。

Bonjourの実装のひとつであるAvahi-daemonがRaspbianにはプリインストールされており、これのreflector機能を使って、プロキシとして機能して他のネットワークに伝播させることができる。

設定は、/etc/avahi/avachi-daemon.confenable-refrectoryesにして有効にすればOK。

enable-reflector=yes

ただしこれだけでは10.0.0.0/8への経路がiOSからは分からないので、家のルータにLAN側のスタティックルートを設定してやる必要がある。うちのASUSのルータだとこんな感じ。

複数インタフェースのサーバでHomebridgeがうまく動かないときの対処

TL;DR

config.json の一番上の階層に、mdnsプロパティを作って、HomebridgeにつなげるiOSバイスが居るネットワークに繋がっているインターフェースに割りあたっているIPアドレスを指定してやる。

{
  "bridge": {
    "name": "Homebridge",
    "username": "CC:22:33:44:55:66",
    "port": 51826,
    "pin": "031-45-155"
  },
  "mdns": {
    "interface": "192.168.50.123"   ←ココ
  },
  ....
}

詳細

RaspberryPiで、VPNサーバとHomebridgeをセットアップしようとしたところ、eth0とwlan0を両方使うと、wlan0側のネットワークからHomebridgeが見えなくなる現象が発生した。

avahi-browse -a でデバイスのアドバタイズがどうなっているのかを検索すると、Homebridgeのデバイスはeth0にしか流れていない。

どうやらデフォルトだと、先頭(?)のネットワークにしかmDNSのアドバタイズをしてくれないようで、eth0側にしか情報が流れていないらしい。

解決法

ソースを掘ると、配信はmulticast-dnsモジュールでやっている事がわかる。

multicast-dnsのコードを見ると、UDPのリスンはココでやっているので、interfaceオプションにインターフェースを示すIPを与えればいいことがわかる。

ここにオプションを渡すための変更が、このコミットで行われていたので、それに従って、config.jsonに設定を入れることで、うまく接続できるようになった。

Amazon Echo Wall Clock買ってみた

今年9月のイベントで発表されたAmazon Echo Wall Clockが発売されたのでポチってみたやつが届いた。

f:id:tomo-ono:20181224081631j:plain

付属品はペラいマニュアルと単3電池4本。電池はAmazon Basicのものだった。

デザインはシンプルで、文字盤がカバーで覆われておらず、むき出しになっているタイプの時計。

電池をセットして、マニュアルにかかれている通り、Alexaに「Setup Echo Wall Clock」というと、裏面の青いボタンを押すように言われる。

言われるがままに青いボタンを押すと文字盤中央のLEDが光り、ペアリング状態になる。しばらくするとAlexaがペアリングが完了したことを知らせてくれる。

この時計の機能は、普通の掛け時計に加えて下記の2つの機能しかない。

  • 時刻を自動で合わせる
  • タイマーが動いている場合、残り時間をLEDで示す

つまり時計だけではAlexaの操作もできないし、スピーカーにもならないということになる。

Alexaを使って5分タイマーを設定すると、下記のように0時から1時までの間のLEDが光り、残り時間が5分間であることを示してくれる。

残り時間が1minを切ると下記のように一周分を使ってカウントダウンしてくれる。

機能としては非常に少ないが、現在Alexaの一番の利用用途がタイマー機能なので、十分ペイすると考えている。 また、サマータイムがあるアメリカでは、時刻を自動で合わせてくれる時計は非常にありがたい。電波時計も試してみたが、家のあたりは電波が良くないようでうまく動かなかったので、これは嬉しい。

単3電池を4本も使うのはちょっと気になるところではある。使用頻度にもよると思うが、あまりに電池の持ちが悪かったりするとちょっと悲しい。。。 #初代ゲームボーイを彷彿させる。。。

また、日本に持っていけばそのまま動きそうなので、日本での発売もそれほど遠くないのではないだろうか?

Whole Foods Marketで生牡蠣Get

Wholefoods Marketの生鮮食品売り場カウンターで、生牡蠣が殻付きで1個1ドルで売られているのを発見した。

生牡蠣は好物なので生で食べれるか聞いてみたところ大丈夫らしい。

4個ほど買ってみた。殻は剥くか聞かれたので剥いてほしいと頼んだところ、お店で出てくるような氷満載のバットに並べて渡された。

f:id:tomo-ono:20181124184639j:plain

Wholefoods Market内にはバーコーナーも設けられているので「そこのバーで食べていってもいいよ」と至れり尽くせり。

家まで持ち帰って、ポン酢をかけて白ワインと頂く。身はしっかりしているし、以前近所のシーフード屋で食べたときのような消毒液の匂いもしない。非常に良いものを見つけた。

夏はバーベキューで焼いても良さそうだ。

なお、今の所食中毒の症状は出ていない。。。

D&Dやコピペでファイルアップロードできるtextareaをサクッと作る

GithubやQiitaなんかで使えるような、使い勝手の良い画像やファイル添付のTextareaをサクッと作った

使ったライブラリは Inline Attachement。 今回はレガシーWebAppに使ったので、JQueryで実装。

設定はドキュメントに掲載されているが、実はここに書かれている以上に拡張性があるので、コード自体を読むことをおすすめする。これによりやりたかったことは全て実現できた。

やりたかったこと

どんなファイルでも添付できるようにしたい

デフォルトだと画像ファイルに制限されているが、どんなファイルでも受け付けるようにしたかった。 コードを読んで、allowedTypes: ['*'] とすれば良いことがわかったのでそのようにした。

アップロードされたファイルの名前が、サーバサイドでわかるようにしたい

デフォルトだと、サーバサイドから見えるファイル名は、image-タイムスタンプ.拡張子となり、もとのファイル名がわからない。 ドキュメントに記載は無いが、remoteFilenameというプロパティに関数を入れてやると、引数にファイルオブジェクトを渡してくれるので、ファイルオブジェクトの内容を使ってファイル名を作成して返してやることで、サーバサイド側ではそのファイル名で見える。 例えば下記のようにすると、もともとのD&Dされたファイル名がサーバサイドに送付される。

    remoteFilename: (file)=>{return file.name}

ファイルの種類によって、埋め込まれるコードを変えたい

Markdown形式で入力するテキストエリアだったので、ファイルの種類によって埋め込まれるコードを修正したかった。

![image.png](url to image)   ←画像ならこっち
[document.docx](url to file)  ←ドキュメントならこっち

埋め込まれるコードは、urlTextプロパティで静的に設定できるが、このプロパティに関数を与えることで好きなデータに編集できる。 ファイルの種類はサーバサイドで判定したMIMEJSONに入れて返し、これを読んで画像ファイルか普通のファイルかどうかの判別がつくようにしてある。 また、リンク文字列ももともとのファイル名にしたかったので、同じくサーバサイドからオリジナルのファイル名を返して埋め込むようにしている。

    urlText: (filename,result)=>{
      if(result.mime.match(/image/)) {
        return `![${result.orig_file_name}](${filename})`
      } else {
        return `[${result.orig_file_name}](${filename})`
      }
    }

マウスオーバしたときにドロップできる感じを出す

ファイルをドラッグしてきたときに、textareaに点線で枠線を表示することで、ドロップできますよー感を出した。

textarea.dragover {
    border: dashed 6px #ccc;
    padding: 12px 6px 0px;
}
  $('textarea').on('dragenter dragover', function() {
    $(this).addClass('dragover')
  })
  $('textarea').on('drop dragleave dragend', function() {
    $(this).removeClass('dragover')
  })

最終型

JSはこんな感じ。

$(function(){
  // ファイル貼り付け
  $('textarea').inlineattachment({
    uploadUrl: 'upload_attachment.php',
    allowedTypes: ['*'],
    remoteFilename: (file)=>{return file.name},
    urlText: (filename,result)=>{
      if(result.mime.match(/image/)) {
        return `![${result.orig_file_name}](${filename})`
      } else {
        return `[${result.orig_file_name}](${filename})`
      }
    }
  });
  $('textarea').on('dragenter dragover', function() {
    $(this).addClass('dragover')
  })
  $('textarea').on('drop dragleave dragend', function() {
    $(this).removeClass('dragover')
  })
});

CSSはこんなん。

textarea.dragover {
    border: dashed 6px #ccc;
    padding: 12px 6px 0px;
}

PHPはこんなもんで。

$response = array();
$upload_directory = "/data/";
if (isset($_FILES['file'])) {
    $file = $_FILES['file'];
 
    // MIMEタイプの取得
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime_type = $finfo->file($file['tmp_name']);
 
    // ファイルのID(=MD5ハッシュ)を計算
    $file_md5 = md5_file($file['tmp_name']);
 
    // ファイルの移動
    move_uploaded_file($file['tmp_name'], $upload_directory . "/" . $file_md5);
 
    $response['filename'] = "file_get.php?id=".$file_md5;
    $response["mime"] = $mime_type;
    $response["orig_file_name"] = $file["name"];
} else {
    $response['error'] = 'Error while uploading file';
}
echo json_encode($response);

BIABをAnovaでやってみた

個人でのビール自家醸造のやり方にBIAB(Brewing in A Bag)というのがある。これはビール醸造の最初の手順である麦芽の糖化(マッシング)にて、袋に麦芽を入れてそれをティーバックの要領で煮出すというやり方になる。使う鍋の量が少ないので、利便性が高く人気がある。

マッシング中は1時間以上に渡って温度を一定に保つ必要があるので、通常は定期的に温度を確認して、必要に応じて火にかけたりする必要がある。

要するにある程度の時間、水温を一定に保てばよいということなので、みんな大好き低温調理器Anovaが使えるんじゃないか?と思い、実際にやってみた。

Anovaは水を吸いこんでヒータで温度を上昇させ、水を吐くという仕組みになっている。Anovaに麦芽が吸い込まれるのはあまりよろしくなさそうなので、麦芽の入った袋の外側にAnovaを取り付ける。

今回は5galを仕込むので、その1.5倍の7.5galの水で煮出すことにした。

まずは鍋に水を張り、バッグとAnovaを取り付けてスイッチ・オン。マッシング温度は65℃とした。水の容量が大きいためAnovaだけの熱量だとパワー不足のため、最初は電熱コンロも併用する。

f:id:tomo-ono:20180908162707j:plain

65℃になった時点で麦芽を投入。最高水位線を超えてしまうことが懸念だったが、特に問題なかった。

f:id:tomo-ono:20180908163305j:plain

初めは非常に順調だったが、30分ほど経ったときに水量不足の警告音を出してAnovaが停止するようになってしまった。もちろん水量不足になるほど、鍋内の水は減っていない。誤検知かと思い、何度かスイッチをオンにしてもすぐに止まってしまう。

試行錯誤の上、Anovaの水を吸い込む口の部分にバッグと麦芽がへばりついて、水がうまく吸えなくなったので、水量不足という表示をしているのではないかと思いあたった。そこで、Anovaの水を吸い込む口のあたりにトングを突っ込んでバッグをブロックしてやったところ、うまく動作するようになってくれた。

次回は水を吸い込む口のところに、何らかのつっかえ棒などを取り付けて、バッグがへばりつくようなことが無いように工夫してみるつもり。

とにかくマッシングの工程は、多少エラーがでてまごついた部分はあったが、ほぼ全自動で完了させることが出来た。 比重も1052でほぼ狙い通りまで行けた。