2020年7月31日金曜日

Wi-Fi Directを使って、棚卸パッケージを接続してみる

お客様から、棚卸パッケージってWi-Fi Directで接続できないの?というご質問をいただいたので、試してみました。結論としては、接続できました、なんですが、Bluetoothのときみたいに、手元以外の環境でもうまく接続できるか今後検証が必要そうです。

使ったもの

まず、Windows 10 PCがWi-Fi Directに対応しているかを確認してみます。コマンドラインでipconfig /all と打ってみて、下記のようにMicrosoft Wi-Fi Direct Virtual Adapterと表示があると、対応しているみたいです。


棚卸パッケージ

PC側、Android側、それぞれ手順書通り進めます。Androidアプリのセットアップ時に、PCとの接続方法に「Wifiで接続」を選択します。

Wi-Fi Directの設定

さて、いよいよWi-Fi Directの設定なんですが、2種類のパタンがあります。一つはPCのモバイルホットスポットをONにして、Androidから接続するパタンで、PCでテザリングするイメージになります。もう一つはAndroidのWi-Fi DirectをONにして、PCから接続するパタンです。こちらは1対1で接続する形になるので、よりBluetoothでの接続にイメージが近いのかなと思います。

PCのモバイルホットスポットをONにするパタン

PCのタスクトレイにあるネットワークアイコンをクリックすると、モバイルホットスポットというアイコンが右下の方にあると思います。こちらをONにしてみてください。下記の右端のアイコンとなります。

ONにしたら、そのアイコンを右クリックして、設定を開いてみてください。

ネットワーク名とネットワークパスワードを確認したら、Androidの方で普通のWi-Fiにつなぐのと同様に設定してみてください。設定できたら、AndroidのWi-Fiのネットワークの詳細からIPアドレスを確認してください。

AndroidのWi-Fi DirectをONにするパタン

まず、AndroidのWi-Fi Directの設定画面を開いてください。Androidのバージョンや機種によって異なると思うのですが、例としてMotorola moto G8 Plus (Android 9)の場合は、下記の順となります。あるいは、設定画面の上部にある検索窓で「direct」で検索してみてください。

設定 > ネットワークとインターネット > Wi-Fi > Wi-Fi設定 > 詳細設定 > Wi-Fi Direct


上記画像で、moto g(8) plus_9e20とある部分をクリックすると、グループの作成というダイアログが表示されるので、OKを押してください。

そうすると、こんな感じにWPA2パスワードが表示されると思います。

今度はPCでの手順になります。タスクトレイのネットワークアイコンをクリックすると、Android側で作成したグループIDが見つかると思います。Android上に表示されたWPA2パスワードを入力ください。


では、PCのコマンドラインでIPアドレスを確認してみましょう。ipconfigとすると、下記のように表示されると思います。このうち、デフォルトゲートウェイがAndroidのIPアドレスとなります。

PC上のExcelToolの設定

PC上のExcelToolの設定については、PCのモバイルホットスポットをONにする場合も、AndroidのWi-Fi DirectをONにする場合も同様です。それぞれの方法で確認したAndroidのIPアドレスを使います。

ExcelToolの設定画面を開いてみましょう。Androidのタブでリーダーを使用するにチェックして、Wifi接続にチェックして、IPアドレスにAndroidのIPアドレスを設定してください。OKボタンを押して設定画面を閉じると、接続アイコンの色が青に変わると思います。あとは普通に利用できると思います。




2020年7月27日月曜日

NTAG424 DNA で遊ぶ その4(キー設定編)

その3で設定までできました。もう1つ重要な設定が残っています。それが暗号化キーの変更です。暗号化キーは0~4まで5つあります。0番のキーはマスターキーと呼ばれていて、各種設定を行う際に使用されるキーです。一方 SDM の方は AN12196 では2番のキーを使うように設定しています。

これらのキーはすべて初期値はオール0の16バイトに設定されていますので、このまま使うと設定の変更やせっかく SDM を使って暗号化した URL パラメータを簡単に解読できてしまいます。そこでキーの変更を行うわけです。

というわけで AN12196 の Change Key のところ(6.16.1章)を見てみます。最初にキーを設定するには0番のキーで認証をしている必要がある旨記載されています。続いて Old Key(00000000000000000000000000000000)と新しいキー(C8EE97FD8B00185EDC7598D7FEBC818A)が記載されてます。そして次、「New Key = AES-Diversified New Key by UID」の記載が。だいばーしふぁいど?何それ?聞いたことないし。そういえばだいばーしふぁいどがどうしたとかさっき参照があったな。参照によると AN10922 を参照しろと。

えー、また何か文章見るのー?とブツブツ言いながら仕方なく見ていくと何か変換の方法のようだ。サンプルも載ってるから一個一個検証していけばいいか。何かすごい面倒なことになってきたけどここでやめるわけにもいかないしなー。変換に必要なものとして、UID、Application ID、System Identifier の3つが必要らしい。おー、UIDとか使うんだなるほどねー、で、アプリケーションID?、しすてむあいでんてぃふぁいあ?また何かわけのわからないものが出てきた。Application ID は DF Name のことか?などなど半日ぐらいいろいろ試したり調べたりするが全く何のことだかわからない。これはさすがに詰んだか。そうなのか。ここまでか。思えばよく頑張った。そう、頑張ったのだ。もうそれでいいではないか。

その時、サンプルの Application ID の説明のところが気になった。「3- byte DESFire AID」。...デスファイア?...そうか!この文章はデスファイア用に書かれているのか!NTAG424 DNA は NTAG の進化版ではなく、デスファイアの機能制限版というか若干機能を落としたものなのだ。ということはもしかして AN12196 で躓いただいばーしふぁいど、これはデスファイアの話であって、NTAG424 では不要なのではないか?要するにAN12196 の元となるデスファイアの資料があって、それを手直しして作るときに間違って残ってしまった記述なのではないか?

早速 NTAG424 の資料の方の Change Key の仕様を見てみる。「(NewKey XOR OldKey) || KeyVer || CRC32」。...そこにはだいばーしふぁいどなる記述は一切無い。まさしくビンゴ。ビンゴなのに何にも嬉しくない。何なんだこれは。一体誰と戦っているんだ。

気を取り直して先に進む。CRC32は普通にCRC32計算すればいいのかな? AN12196 のサンプルによれば F3847D627727ED3BC9C4CC050489B966 の CRC32 が 789DFADC らしい。計算サイトで計算してみると...合わない。違う。なにこれ。しかし待て、記載が間違っている可能性だってあるのだ。そう、記載が違うのだ。というわけで気にせず実装してみると、...やっぱり通らない。エラーを返してきやがる。するとCRCの計算方法が違うのか?調べてみるとCRCの計算方法にもいろいろあるらしい、というか計算は一緒だけど初期ベクトルみたいのが様々あるらしい。それらの計算結果を一度に表示してくれるサイトもある。ところが 789DFADC に一致するものが見当たらない。だめか。とうとう詰んだか。

は!そうだ。NTAG424 のコマンドはリトル・エンディアンで記載するのだ。ということは逆だ。789DFADC と思っていたのは DCFA9D78 なのだ。もう一度サイトの値を見直してみる。そうするとあった!あったぞ DCFA9D78 が。その計算は CRC32/JAMCRC というらしい。これで解決!

APDU の例を記載したかったですが、このコマンドは Full モードでのアクセスになるので設定編で行ったゴニョゴニョをやらなければならずここでは割愛いたします。

長かった戦いもこれで終わりを迎えます。いや、ほんとに長かった。

2020年7月13日月曜日

IFTTTを使ったMANICAモバイル Web連携

こちらの記事MANICAモバイルの貸出返却時にWebリクエストする方法が紹介されているのですが、最後の方にさらっと触れられているIFTTTを使った連携方法を実際に試してみたので、まとめてみます。

つかったもの

  • IFTTT
  • MANICAモバイル
  • Slack

IFTTT

最初にアカウントを作成してください。Google等のアカウントでも利用できます。

サインインすると、上部がこんな風に表示されると思います。


Createをクリックします。


Thisをクリックします。


Search servicesのところにWebhooksと入力します。



見つかったWebhooksのアイコンをクリックします。


Receive a web requestをクリックします。



とりあえず、貸出時のTriggerを作成したいので、Event Nameに「item_rented」と入力して、Create triggerをクリックします。あとで、同じ手順を繰り返して返却時用のTriggerとして、「item_returned」も作成します。


Thatをクリックします。


Search servicesのところにSlackと入力します。


見つかったSlackのアイコンをクリックします。


Post to channelをクリックします。初回登録だとSlack側の認証が必要だったと思います。

こんな感じの画面が表示されるので、それぞれ入力していきます。



Which channel?のところは、お使いの環境にしたがって適宜設定ください。
貸出時のMessageは下記のように設定しておきます。
{{Value1}}が{{Value2}}に貸出されました。
あとで登録する返却時のMessageは下記のように設定しておきます。
{{Value1}}が{{Value2}}から{{Value3}}に返却されました。
Titleは分かりやすく、MANICAモバイルとしておき、Title URLは現況一覧に飛べるように、下記を設定しておきます。
必要な項目が入力できたら、Create actionをクリックします。

Finishをクリックします。

同様の手順で返却時のレシピも作成しておきます。

いったんHOMEをクリックして、トップページに戻ります。

Webhooksをクリックします。


右上のほうにあるDocumentationをクリックします。



Make a POST or GET web request toのところをメモしておきます。{event}のところは作成したイベント名で置き換えます。今回は「item_rented」または「item_returned」になります。

MANICAモバイル

まずは、こちらの取扱説明書をもとにユーザ登録してみてください。タグ登録が50個までなら無償でお使いいただけます。

ログイン後、現況一覧が開かれていると思います。


右上にある店舗設定をクリックします。


下記のように設定しています。
貸出時Webリクエスト
URL https://maker.ifttt.com/trigger/item_rented/with/key/XXXXXXXXX-XX-XXXXX
GET/POST POST
BODY {"value1":"[製品名リスト]","value2":"[担当者名]"}

返却時Webリクエスト 
URL https://maker.ifttt.com/trigger/item_returned/with/key/XXXXXXXXX-XX-XXXXX
GET/POST POST
BODY {"value1":"[製品名リスト]","value2":"[担当者名]","value3":"[保管場所名]"}

BODYに設定した[]で囲った文字は実際の名称に置換されます。使用可能な文字列は下記の通りです。
[製品名リスト]
[担当者名]
[保管場所名]
[備考]
[店舗名]

IFTTTではvalue1~3までしか設定できませんが、たとえばSlackに投稿する場合は、一つのvalueに複数の名称を設定し、Messageの内容と調整してみてください。

さて、それでは実際に貸出、返却をしてみましょう。貸出をしてみると、Slackにはこんな感じで投稿されました。


続いて返却をしてみると、こんな感じで投稿されました。



2020年7月6日月曜日

棚卸パッケージをクラウド対応できなかった話

このブログはほとんど〇〇してみた、□□できましたっていう記事で占められていると思うのですが、今日はうまくいかなかった話。

以前にも棚卸パッケージをクラウド対応するっていう話がありました。その概要はGoogle Apps ScriptでAPIを作成して、Androidアプリを改造して通信先をそのAPIに向けるというものでした。今回はちょっとアプローチを変えて、Androidアプリはそのままで、この記事で試したように、WebとはBluetooth経由で通信しようと考えました。

使ったもの


やったこと

クラウド側にはGoogle Spreadsheetを使用しました。アドオンでSpreadsheetに機能を追加できるので、これを使用しました。アドオンでは、呼び出し元をgsで記述、UIをhtmlで作成して、動作をJavascriptで記述することができます。こんな感じです。

呼び出し元
function onInstall(e) {
  onOpen(e);
}
function onOpen(e) {
  var ui = SpreadsheetApp
    .getUi()
    .createAddonMenu()
    .addItem('リーダ', 'showSidebar')
    .addToUi();
}
function showSidebar() {
  var sidebarUi = HtmlService
    .createTemplateFromFile('Sidebar')
    .evaluate()
    .setTitle('MANICA');
  SpreadsheetApp
    .getUi()
    .showSidebar(sidebarUi);
}

UI
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
  </head>
  <body>
    <div>
      <form>
      <p>
      <select id="inputMode">
      <option value="fill">セルへ入力する</option>
      <option value="findTag">ICタグを探す</option>
      <option value="findCell" selected>セルを探す</option>
      </select>
      </p>
      </form>
      <button id="connect" onclick="readerLibrary.start();">接続</button>
    </div>
    <?!= HtmlService.createHtmlOutputFromFile('ReaderLibrary').getContent(); ?>
  </body>
</html>

Javascript(GAS上では拡張子はhtmlしか設定できないみたいです)
<script>
window.readerLibrary = {
    writer: undefined,
    encoder: undefined,
    start: async function () {
        const requestOptions = {};
        let port;
        try {
            port = await navigator.serial.requestPort(requestOptions);
            await port.open({ baudrate: 115200 });
        } catch (err) {
            console.log(err);
            return;
        }
        console.log('connected: ' + port);
        while (port.readable && port.writable) {
            console.log('readable and writable');
            this.encoder = new TextEncoder();
            this.writer = port.writable.getWriter();
            const decoder = new TextDecoder();
            const reader = port.readable.getReader();
            while (true) {
                console.log('while loop');
                let value, done;
                try {
                    ({ value, done } = await reader.read());
                } catch (err) {
                    console.log(err);
                    break;
                }
                if (done) {
                    break;
                }
                //this.handleChunk(decoder.decode(value));
               // this.dotNet.invokeMethod('OnReceived', value);
               console.log(value);
            }
            this.writer.releaseLock();
            reader.releaseLock();
            await port.close();
            console.log('disconnected');
        }
        console.log('while loop done');
    }
};
</script>

メニューから作ったアドオンを選択すると、こんな感じで表示されます。


あとは接続ボタンを押せばつながるはずと思ったのですが、残念ながら下記のエラーが表示されて、うまくいきませんでした。


userCodeAppPanel:9 [Violation] Feature policy violation: serial is not allowed in this document.

エラーメッセージを基に調べてみると、下記のIssueが見つかりました。(下記はgeolocationに関するIssueですが、今回のとエラーの原因は同じと思います)

feature policyでserialを許可する必要があるみたいなのですが、Spreadsheetのアドオンからですと、iframeを使った方法もresponse headerを使う方法もどちらも提供されていないようです。そのため、このやり方ではうまく行かず、まったく別の方法を考える必要があるようです。という訳で、うまくいかなかった話でした。

実は上記のあと、Office 365でもチャレンジしてみたのですが、チュートリアルでエラーが出てしまい、挫折しました。