YDiary

メモ的な

Google Homeで普通のシーリングライトを制御する

はじめに

先日,Google Home Miniを購入しました.

Google関連のサービス・製品*1を多く利用しているので,それらと連携出来たら便利そうだと感じたのと,どうやらAPIを通して機能を拡張できそうだと分かったのが購入の決め手です.

さて,せっかくGoogle Homeを購入したので,音楽を再生したり,ニュースを聞いたりする他にシーリングライトなどの家電を制御したりしたくなってきます.

Google Homeで家電を制御する

Google Homeには,元々家電などを制御する機能が搭載されていますが,これを利用することができるのはPHILIPS Hueなどの一部のスマート家電のみです.

残念なことに,我が家で使用しているシーリングライトはHueではなく,一般的な,リモコンで操作するタイプのLEDシーリングライトです.そのため,そのままではGoogle Homeから制御することができません.

Google Homeの機能拡張

そこで目を付けたのが,APIを通した機能の拡張です.調べたところ,Google Homeの中身*2Google Assistantであり,Dialogflow上でアプリを作成してActions on Google経由で呼び出すことによって機能を拡張することができるようです.

dialogflow.com

しかし,この方法で作成したアプリは起動のために毎回「テスト用アプリに接続」というようなコマンドを言わないと使用することができない*3ようです.部屋の明かりを点けるために毎回そうしたコマンドを言うのは面倒ですし,何より直感的ではありません.

そこで次に調べたのが,IFTTTを使用する方法です.IFTTTを使用して連携する場合には,どういう訳か*4「テスト用アプリに接続」というようなコマンドを言わずに,直接制御用のコマンドを認識することが可能なようです.シーリングライトの制御という目的を考えれば,アプリにはそれほど高度な機能は求められず,せいぜいWebhookが使えれば十分なので,今回はIFTTTを使用することにしました.

ifttt.com

シーリングライトの制御

次に問題になるのが,Webhookを受け取った後に,どうやってシーリングライトを制御するかという点です.先述の通りHueなどのスマート家電ではないので,制御するためにはリモコンを使用するほかなさそうです.そこで,リモコンの信号を解析し,その通りに真似してしゃべることで制御することができるのではないかと考えました.ちょうど秋月通販で買い物をする予定があったので,ついでに赤外線受信器赤外線LEDなどを買いそろえました.

さて,リモコン信号の解析ですが,手元にArduinoもどき*5があったので,それを使用することにします.調べたところ,ArduinoのライブラリにIRremoteというものがあり,リモコン信号の送受信をサポートしていたのでこれを使用することとしました.

github.com

こんな感じのスケッチで,受信したリモコンの信号を出力させます.

IR Receive.c gist.github.com

回路図なんかは,ググればいくらでも出てくると思うので,そちらを参照してください. 一つ注意が必要な点として,我が家で使ってるシーリングライト*6はリモコンの信号が長いようで,IRremoteライブラリそのままでは全ての信号を受信しきれなかったため, IRremoteInt.h ファイルの43行目にある #define RAWBUF の値を500ぐらいの大きな値に変更しています.

そして,受信した信号を元に次のようなスケッチを作成しました.このコードは,デジタル入力の2ピンと4ピンを監視し, HIGH になった場合にはそれぞれリモコンの全灯と消灯の信号を送信しています.

IR Send.c gist.github.com

Webhookを受信して,Arduinoに伝えるのはRaspberry Piを使用しました*7C#で簡易HTTPサーバを書き,アクセスに応じてGPIOを制御してます.

ひとまず,モノとしてはこんな感じになりました.

最後に,IFTTTでこんな感じのアプレットを作成して完成です.

f:id:YDKK:20171114153044p:plain

出来上がったもの

実際に動かしている様子は次の通りです.

システム全体の構成図はこんな感じ.Google HomeとWebサーバより右側が我が家の中です. f:id:YDKK:20171114154230p:plain

終わりに

とりあえず家にあるもので作ったので,ラズパイとかArduinoのあたりが煩雑になってる気がする.既製品のスマートリモコンの類を利用したり,ESP-WROOM-02みたいなWi-Fiマイコンを使ったりするともっとスマートに仕上がりそう.

Amazon.co.jp: スマートリモコン

akizukidenshi.com

最初のうちは,つい今までの習慣で自然とリモコンに手が伸びてしまいますが,すぐに慣れました.両手がふさがってても明かりを点けられたり,消し忘れた際に遠くから喋るだけで明かりを消したりできるので最高です. 今後は,何か良さそうなものがあれば他のものも制御してみたいです.
Splatoon2向けの何かを作ったりもしてみようと思ったのですが,ログイン周りの処理が変わってからは iksm_session を自動生成できないようなので保留してます.

*1:カレンダとかGmailとかAndroidとか

*2:音声アシスタント

*3:あまりよく調べてないので,間違ってたら教えてください

*4:OAuth連携時に「音声コマンドの上書き」的な権限があったのが関係してそう.一般に開放されているのであればぜひ使ってみたい

*5:中華通販で300円ぐらいで買ったArduino UNO互換機.USB端子がMicro Bで便利

*6:SHARP製のシーリングライトで,CRMC-A018SDEZというリモコンを使用するもの

*7:諸々の都合で前にもう一段Webサーバがあります

Vpassのパズル認証を突破する

SMBCから

★━━【三井住友カード】━★

【重要なお知らせ】Vpassログイン時の「パズル認証」導入について

★━━━━ 2017年8月7日 ━★

などと書かれたメールが届きました.
どうやら今後Vpassにログインする際にパズル認証が必要になるらしいです.
メール本文には人の手によるログイン操作であることを認証するものでございますとあります.
個人的にはDr.Walletで請求額取得できなくなりそうで嫌だなぁという感じですが,果たしてパズル認証に機械的なログイン試行を防ぐ効果はあるのでしょうか.

f:id:YDKK:20170812035142p:plain

実際にログインページで使われてるパズル認証を見てみると,よく見かけるもので,調べてみるとCapy社のパズルキャプチャのようです.
このパズル認証は既にだいぶ前に突破されています*1が,今回Vpassのログイン画面で使用されているものはピースの輪郭が白で塗りつぶされており,この手法が使えないように対策してあるようです.

しかし,見ての通りピースをはめる部分の色が単色で,しかも固定という何を考えているのかよく分からない*2仕様になっています.こんなパズルでは突破手法を考えるまでもなく,単純な画像処理だけで簡単にピースをはめる部分の座標が求まります.

せっかくなので画像処理の練習がてらにコードを書いてみました.C#で適当に書いたところ,80行程度でクリップボードから張り付けた画像に対してピースをはめる部分を求めるという処理が書けました.コードはGistに置いておく*3ので気になったら見てみてください.

f:id:YDKK:20170812041518g:plain

さて,これでVpassのログイン画面に使われているパズル認証がいかに意味のない*4ものであるかが分かったと思います.また,こうしたタイプの認証は視覚障害者などに対するアクセシビリティを損なうという指摘も多数あります.

個人的には,こんな意味のないパズル認証を導入するよりも,reCAPTCHAを使うなり二段階認証を使うなりした方が,よっぽど機械的なログイン試行に対して有効で,ユーザビリティを損なわないと思います.そうしたうえで,安全なAPIを通してDr.Walletをはじめとする家計簿アプリなどが情報を取得できるような未来が来たら良いなーと思ってます.うーん,現状を鑑みるとなかなか難しそうですね.

*1:https://news.gehirn.jp/security/513/

*2:見やすさを考慮してる?

*3:https://gist.github.com/YDKK/1a3199934ca1d5f6f5c8b2bc923cf915

*4:今回は背景色を使っただけですが,仮にこれが対策されたところでエッジ検出するなりいくらでも方法はあると思います

Slackを黒くする

SlackのWindowsアプリが白くて目が疲れるので,黒くするお話.
SlackのWindowsアプリはElectron製で,jsで書かれているので簡単に中身を弄れる.
下記の内容の利用は自己責任でお願いします.

app.asarを展開

今回はBoW上で作業した.
まず,
Ubuntuに最新のNode.jsを難なくインストールする - Qiita
を参考にBoW上にnodejsをインストール. npmを消す前に apt-get install node-gyp でnode-gypもインストール.

そして,asarを展開(バージョンは環境に合わせて読み替えること).

npm install -g asar
cd %LOCALAPPDATA%/slack/app-2.3.3/resources
asar e app.asar app.extract

ロード時にcssを適用

Webviewが読まれるタイミングで,好きなcssをinjectionする.
アプリ版も,中身のhtmlはweb版とほとんど変わらないので,web版と同じuserstyleを適用できる.
Slackでは,Webviewのpreloadで\src\static\ssb-interop.jsを読みに行ってるので,このファイルの末尾に次のように追記する.

onload = function(){
  $.ajax({
    url: "https://cdn.rawgit.com/laCour/slack-night-mode/master/css/raw/black.css",
    success: function(css) {
      $("<style></style>").appendTo("head").html(css);
    }
  });
}

app.asarをパッキング

編集したファイルをパッキングして,app.asarを上書きする.

asar p app.extract app.asar

サイドバーの色も揃える

このままでは,サイドバーだけ元の色のままなので,Preferencesからサイドバーの色も調節する.
f:id:YDKK:20161204184509p:plain
#363636,#2A2A2A,#222222,#FFFFFF,#4A5664,#FFFFFF,#94E864,#bf360c

完成

これで,黒くて目に優しいSlackの出来上がり.
f:id:YDKK:20161204185006p:plain

※Slackのバージョンアップの度に同じ操作を適用する必要あり.
公式で黒テーマ提供してくれれば良いのだが….

参考

blog.hinaloe.net

blog.lacour.me

github.com

GP7 ドアミラーカバー交換

疲れからか、不幸にも電信柱に追突してさせてしまったドアミラー君
f:id:YDKK:20160818162050j:plain ディーラーでカバー上下とレンズの割れてしまったランプを注文
f:id:YDKK:20160818162452j:plain f:id:YDKK:20160818162617j:plain お客様感謝デイが近かったのでお願いして10%引きに
自分で交換すると言ったので技術料は無し

つらい

無線LANルータ(WN-AC1167DGR)を再起動させるやつ

使ってる無線LANルータ(WN-AC1167DGR)が,定期的に再起動しないとスループットがどんどん落ちる(200Mbps→20Mbpsみたいな感じ)ので外から再起動させられるように書いた.

無線LANルータ(WN-AC1167DGR)を再起動させるやつ

Html Agility Packと,管理ページにログインするためのIDとPassを設定に追加する必要あり.
あとurlのホストを各自の管理ページに向ける.
最初だけ手動で起動してIDとPassを入力すれば,あとはタスクスケジューラとかに登録して好きな間隔で呼べばおk.
関係ないけどHtml Agility Pack,なんでデフォルトだとformがエレメントになるんだろう.

Smali基本文法

Smali基本语法 - lee0oo0 - 博客园
が分かりやすかったのでそのまま訳しただけ

.field private isFlag:z  変数定義
.method  メソッド
.parameter  メソッドパラメータ
.prologue  メソッドの始まり
.line 12  ソースコードだと12行目
invoke-super  親クラスのメソッド呼び出し
const/high16 v0, 0x7fo3  v0に0x7fo3を割り当て
invoke-direct  メソッド呼び出し
return-void  voidを返す
.end method  メソッドの終わり
new-instance  インスタンスの生成
iput-object  オブジェクトの割り当て
iget-object  オブジェクトの取得
invoke-static  スタティックメソッドの呼び出し

条件分岐命令:

"if-eq vA, vB, :cond_**"  vAがvBと等しければ:cond_**に飛ぶ  
"if-ne vA, vB, :cond_**"  vAがvBと等しくなければ:cond_**に飛ぶ  
"if-lt vA, vB, :cond_**"  vAがvBより小さければ:cond_**に飛ぶ  
"if-ge vA, vB, :cond_**"  vAがvB以上ならば:cond_**に飛ぶ  
"if-gt vA, vB, :cond_**"  vAがvBより大きければ:cond_**に飛ぶ  
"if-le vA, vB, :cond_**"  vAがvB以下ならば:cond_**に飛ぶ  
"if-eqz vA, :cond_**"  vAが0ならば:cond_**に飛ぶ  
"if-nez vA, :cond_**"  vAが0でないならば:cond_**に飛ぶ  
"if-ltz vA, :cond_**"  vAが0より小さければ:cond_**に飛ぶ  
"if-gez vA, :cond_**"  vAが0以上ならば:cond_**に飛ぶ  
"if-gtz vA, :cond_**"  vAが0より大きければ:cond_**に飛ぶ  
"if-lez vA, :cond_**"  vAが0以下ならば:cond_**に飛ぶ  

========================================

javaでのif文:

private boolean ifSense(){
  boolean tempFlag = ((3-2)==1)? true : false;
  if (tempFlag) {
    return true;
  }else{
    return false;
  }
}

Smaliでのif文:

.method private ifSense()Z
  .locals 2

  .prologue
  .line 22
  const/4 v0, 0x1 //v0に1を入れる

  .line 24
  .local v0, tempFlag:Z
  if-eqz v0, :cond_0 //v0が0に等しいか判断する,等しければcond_0に飛ぶ

  .line 25
  const/4 v1, 0x1 //v0が0と等しくない場合

  .line 27
  :goto_0
  return v1

  :cond_0
  const/4 v1, 0x0 //v0が0と等しい場合

  goto :goto_0
.end method

###メモ:if文の条件を満たす場合は,そのまま下に行きreturnされる.満たさない場合は:cond_0に飛んだ後に,goto命令でgoto_0に飛び,returnされる.

========================================

javaでのfor文:

private void forSense(){
  listStr = new ArrayList<String>(COUNT);
  for (int i = 0; i < COUNT; i++) {
    listStr.add("现在轮到我上场乐");
  }
}

Smaliでのfor文:

.line 40
  const/4 v0, 0x0

  .local v0, i:I
  :goto_0
  if-lt v0, v3, :cond_0 //if-ltでv0とv3を比較,v0<v3ならcond_0に飛ぶ

  .line 43
  return-void

  .line 41
  :cond_0 //ラベル
  iget-object v1, p0, Lcom/example/smalidemo/MainActivity;->listStr:Ljava/util/List; //オブジェクトの取得

  const-string v2, "\u73b0\u5728\u8f6e\u5230\u6211\u4e0a\u573a\u4e50"

  invoke-interface {v1, v2}, Ljava/util/List;->add(Ljava/lang/Object;)Z //Listはインターフェースなので,インターフェースメソッドaddを実行する

  .line 40
  add-int/lit8 v0, v0, 0x1 //第二オペランドのv0レジスタに0x1を加え,第一オペランドのv0レジスタに代入する

  goto :goto_0 //goto_0に飛ぶ

###メモ:v0<v3を判断する部分に,ラベルgoto_0をセットする.条件を満たせばcond_0に飛び,その後またgoto_0に戻り条件を判断する.

CM 690 II Plus rev2にGV-N970WF3OC-4GDは入る

Divisionのオープンベータとか遊んでると,さすがに今まで使ってたGTX580では厳しそうな感じがしたので,思い切ってグラボを新調しました.
購入したのはGTX970のこちら.

どうもこのグラボ,三連ファンのおかげかとても長いことで有名らしく,ミドルタワーケースだと入ったり入らなかったりするみたいです.
今使ってるケースはCM 690 II Plus rev2というミドルタワーケースで,グラボ取り付けスペースは304mm.
それに対してGV-N970WF3OC-4GDの長さは…

Amazon商品説明:312mm
GIGABYTE公式サイト:297mm

とサイトによって微妙に違う.普通なら公式サイトのスペック表記を信じるべきですが,ネット上の書き込みを調べたところ「入る」という書き込みもあれば「無理」という書き込みも…
まぁ,入らなかったらその時はその時だ,と考えつつポチる.
結果はタイトルの通りでちゃんと入りました.
写真
こんな感じでちょうどピッタリサイズ
やはり公式サイトの297mmが正解のようです.

さて,せっかくの新しいグラボなのでとりあえずベンチを取りますよね.
写真
結果はこんな感じで,GTX580と比べて2倍以上スコアが伸びています!
これで最新のゲームでもそこそこの設定なら快適に遊べそうです.

また,現在GTX9xxシリーズを購入するとDivisionが無料で付いてくるキャンペーンをNDIVIAがやってるのでDivisionも入手出来てちょうど良かったです. www.nvidia.co.jp