読者です 読者をやめる 読者になる 読者になる

ほげほげ(仮)

仮死状態

MESHを使って鍵の閉め忘れをなんとかする

MESHとは

MESH:遊び心を形にできる、アプリとつなげるブロック形状の電子タグ|ソニー

さまざまな機能を持ったブロック形状の "MESHタグ" を "MESHアプリ" でつなげることにより、 あなたの「あったらいいな」を実現できる それがMESHです。 難しいプログラミングや電子工作の知識は必要ありません。 IoT(モノ・コトのインターネット化)を活用した仕組みも 簡単に実現します。

とのことです。

去年くらいからあったっぽいのですが、ぼくは全然知らなくて、この前ソニーストア行った時に見て興味が湧いて実際に買ってみました。

iOSアプリを使って、アイコンを繋いでいくだけで様々なことが出来ます。

すごく簡単なので、子供がいらっしゃる方は子供と一緒にやる良いかもしれません。夏休みも残り少ないけど自由研究的なものに。

いろんな人がレシピを公開してるので、これを見るとどんなことが出来るのかイメージしやすいかもしれません。 MESHレシピ

MESHアプリ

iOSアプリで下のような感じでアイコンをつなぐだけで色々できます。下の例だと、動きタグが何か検知したらiPhoneの音が鳴ります。

f:id:STAR_ZERO:20160819141005p:plain:w200

詳しくは下のリンクを参考にしてください

MESHアプリ Creative DIY Toolkit|ソニー

鍵の閉め忘れをなんとかしたい

ぼくは鍵を閉めたのかが不安になることが多くて、一度家に戻るとかもよくやります。

今回はMESHの検証ついでにこの課題をなんとかしたいと思います。

鍵の開け閉めを検知する

MESH-100AC 動き(Move)タグ - 動きがきっかけになる新しい感覚 ワイヤレス加速度センサー | ソニー

今回は動きタグのみを使って、鍵の開閉を検知する仕組みを作ります。

これ想像以上に優秀で、振られたら、ひっくり返したら、振動したら、向きが変わったら、を検知できます。

今回はこの向きが変わったらをトリガーにします。

ドアの鍵にこんな感じにくっつけます。見た目がかなり残念な感じですが…

  • 開けた状態

f:id:STAR_ZERO:20160819135325j:plain:w200

  • 閉めた状態

f:id:STAR_ZERO:20160819135333j:plain:w200

鍵が閉められたのを検知

動きタグが表向きに変更されたら、鍵を閉めたと判断します。

キャンバスに動きタグを配置して、タップすると詳細が見れるので、そこで「向きが変わったら」の「表」を選択します。

f:id:STAR_ZERO:20160819141651p:plain:w200

鍵が開けられたのを検知

これも同じような感じで設定します。

「向きが変わったら」の「上」を選択します。

f:id:STAR_ZERO:20160819141905p:plain:w200

これで鍵の開け閉めは検知できるようになりました。

通知する

次は通知です。おそらく現状でMESHの状態(動きタグの例だと今は表を向いてるとか)が簡単に取れないと思うので、通知でログを残す方法で状態を確認できるようにします。

MESHでは、スピーカー鳴らしたり、iPhoneに通知をだしたりできますが、これではログとしてはあとから確認するには不十分です。

MESHは実はIFTTTと連携することができます。これを使ってSlackに投稿させればログとして残るし後から確認できます。

IFTTT設定

ここは省略します。詳しくは公式サイトの手順を参考にしてください。

IFTTT連携の初期設定方法を教えてください – MESHサポート | 遊び心を形にできる、アプリとつなげるブロック形状の電子タグ

IFTTT連携でレシピの作り方を教えてください – MESHサポート | 遊び心を形にできる、アプリとつなげるブロック形状の電子タグ

ぼくはSlack側の設定は下のような感じにしました。

f:id:STAR_ZERO:20160819143056p:plain:w200

Messageのところを{{Text}}にしておくと、MESHアプリからテキスト設定できます。

アプリ設定

アプリの右下にある連携タグからIFTTTを選択して、キャンバスへ配置します。

IFTTTの設定を下のようにイベントIDとテキストを設定します。

f:id:STAR_ZERO:20160819143304p:plain:w200

同じように開けられた時のIFTTTもキャンバスへ配置しておきます。

それを動きタグと繋ぎます。

最終的にキャンバスは次のようになります。上が鍵を閉めた時、下が鍵を開けた時になります。

f:id:STAR_ZERO:20160819143652p:plain:w200

確認

鍵の開け閉めをするとSlackに次のように通知されます。

f:id:STAR_ZERO:20160819144300p:plain:w200

これで、鍵の状態が確認できるようになりました。

完成

コーディングなしで簡単にここまで出来ました。工夫次第ではもっと色々できると思います。

MESHがくっつけられればだいたいの鍵に対応できると思います。オフィスとかで最終退出時間を自動で残すとかにも使えるかも。

まとめ

MESHは他にも色々な使い方が出来ると思うので、時間あるときにまた試そうと思います。またSDKもあるっぽいのでそれも見てみたいと思います。

MESHは単品でも買えるので、お試しで一つだけっていう感じで気軽に試せます(それなり値段はしますが…

暇な人はこれで遊んでみると良いと思います。

MESH Moveタグ

MESH Moveタグ

ワイヤレスファンクショナルタグ Button

ワイヤレスファンクショナルタグ Button

MESH Bundle 7

MESH Bundle 7

CQRSのメモ

設計

CQRSって知らなかったので調べました。かなり雑なメモです。

参考リンクとかではEvent Sourcingについても絡んできますが、ここでは触れてないです。

参考

ここにある記事を読みました。英語のとこはなんとくで読んだので怪しいですが。

ぼくのこのメモを読むより、下記の記事を読んだほうがいいです。

CQS(Command Query Separation)

CQRS(コマンドクエリ責務分離)に入る前に、CQS(コマンドクエリ分離原則)について。

CQSはコマンドとクエリを分けようってことです。

  • クエリ: 結果のみを返し、状態の変更は行わない
  • コマンド: 状態の変更のみを行い、結果は返さない

これをオブジェクトレベルで適用します。メソッドで分ける感じです。

ただ、Javaiteratorのようにnextで結果の取り出しと内部状態の変更をやっているものもあります。こういうイディオムは便利なので原則を破ることもあるようです。

CQRS(Command and Query Responsibility Segregation)

CQSではオブジェクトレベルで適用する感じでしたが、CQRSではもっと広い範囲に適用します。Event Sourcingも含むとアプリケーション全体になるかと思います。

CQRSではCQSと異なり、コマンドとクエリをメソッドで分けるのではなくオブジェクトごと分けます。

例えばこういうオブジェクトがあった場合、

CustomerService
void MakeCustomerPreferred(CustomerId)
Customer GetCustomer(CustomerId)
CustomerSet GetCustomersWithName(Name)
CustomerSet GetPreferredCustomers()
void ChangeCustomerLocale(CustomerId, NewLocale)
void CreateCustomer(Customer)
void EditCustomerDetails(CustomerDetails)

次のように分離します。

CustomerWriteService
void MakeCustomerPreferred(CustomerId)
void ChangeCustomerLocale(CustomerId, NewLocale)
void CreateCustomer(Customer)
void EditCustomerDetails(CustomerDetails)

CustomerReadService
Customer GetCustomer(CustomerId)
CustomerSet GetCustomersWithName(Name)
CustomerSet GetPreferredCustomers()

クエリ側

データを取得するメソッドを持つだけで、各画面にマッチするDTOを生成して返却します。

多くのドメインではクエリ最適化が困難、インピーダンスミスマッチ問題などがあります。

CQRSを適用すれば、クエリ側はドメインを使いません。新しい概念の「Thin Read Layer」でデータベースから直接読み込んでDTOに反映します。

コマンド側

読み込みをドメインから分離すれば、コマンドの処理だけに焦点があたります。

リポジトリではGetByIdを除くとクエリはわずかになります。

クエリを分離したドメインモデルで作業をすれば、概念的なオーバヘッドが減り、コスト削減につながります。

(ここらへん怪しい)CQRSを適用すると、クエリとコマンドで同一データモデルを使うのかという問題がある。ここから先はEvent Sourcingの話っぽい?(ココのP23の下の部分)

まとめ

CQSはすぐにでも適用できそうですが、CQRSになると簡単にはいかなさそうです。

Event Sourcingも絡んでくると、ぼくはお手上げ状態になりそうです。

ここの最後にも書いてあるのですが、CQRSについてはだいぶ慎重になったほうが良さそうです。

XCTestでthrowsをテストする

Swift

環境

テスト対象コード

テストの対象となるサンプルコードです。

エラータイプの定義です。

enum SampleError: ErrorType {
    case Invalid
    case Unknown
}

エラーをthrowするコードです。

class Sample {
    static func exec() throws {
        throw SampleError.Invalid
    }
}

テスト

throwされたかをテストする

エラーがthrowされた場合はテストはパスします。もしthrowされなかった場合はテストは失敗します。エラーのタイプなんでもいいです。

XCTAssertThrowsError(try Sample.exec())

throwされたエラーもテストする

Closure内でエラーが期待しているものかをXCTAssertEqualを使ってテストします。もし、違うタイプのエラーの場合はテストは失敗します。

XCTAssertThrowsError(try Sample.exec()) { error in
    XCTAssertEqual(error as? SampleError, SampleError.Invalid)
}

Firebase Cloud Messagingで通知をカスタマイズ(Android)

Android

Firebase Cloud Messaging (FCM)を試して、通知のカスタマイズについて調べてみました。

FCMとNotification

FirebaseにはFCMとは別にNotificationという似たようなのがあります。

この辺りの違いは下記の記事にまとまってます。

Firebaseによるプッシュ通知のハマりどころ - Qiita

ざっくりと言って、Web上のConsoleから通知を発行できるのがNotificationで、FCMはその基盤になっておりAPI経由で実行する感じかと思います。

概要

今回はNotificationは使いません。

というか、Notificationでは通知のカスタマイズが微妙に物足りません。ぼくは特に通知アイコンを変更できないのがツライです。

現状だとAPIを叩くしか方法はありません。

通知を受けた時の処理

通知をカスタマイズするには通知を受けた時に自分で通知を表示すればいいと思うのですが、ここの挙動がやっかいなので、軽く説明が必要になります。

ここのドキュメントに書いてる通り、FirebaseMessagingServiceを継承してonMessageReceivedをoverrideすれば通知受信時に処理が可能です。

ただし、onMessageReceivedが実行されるには、アプリの状態と通知がNotificationなのかDataなのかそれとも両方なのかで変わってきます。

onMessageReceivedは通知を受けたら必ず呼ばれるものではなく、条件で呼ばれないことがあります。あと自動で通知が表示されたりする場合もあります。

f:id:STAR_ZERO:20160529161944p:plain (https://firebase.google.com/docs/cloud-messaging/downstream から引用)

App state、Notification、Data、Both

さきほどのドキュメントの図が分かりづらいので、簡単に説明しておきます。

App stateはアプリ状態でForegroundBackgroundのいずれかです。

NotificationDataBothですが、APIの使い方で変わります。

言葉で説明しにくいのでAPIへ投げるJSONの例で簡単に。最低限の設定でやってます。

APIの仕様はここを見てください。

Notification
{
  "to" : "/topics/all",
  "notification": {
    "body": "メッセージ"
  }
}

この時、アプリの状態がForegroundの場合はonMessageReceivedが呼び出されますが、Backgroundの場合はSystem trayと記載されてる通り通知がすぐ表示されて、onMessageReceivedは呼び出されません。

Data
{
  "to" : "/topics/all",
  "data": {
    "hoge": "foo"
  }
}

この時は、アプリがForegroundBackgroundでもonMessageReceivedが実行されます。 dataに設定した値はonMessageReceived内で処理します。

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    Log.d(TAG, "Notification data: " + remoteMessage.getData().get("hoge"));
}
Both

これはJSONnotificationdataもある状態ですね。

{
  "to" : "/topics/all",
  "notification": {
    "body": "メッセージ"
  },
  "data": {
    "hoge": "foo"
  }
}

アプリがForegroundの場合はonMessageReceivedが実行されます。

Background時が少し複雑です。Notificationと同じように通知は表示されますが、onMessageReceivedが実行されません。では、dataに設定された値はどこで処理するのか?これは通知タップで起動されるActivityのintentに入ってくるのでそこで処理するようにします。

通知のカスタマイズ

少し前置きが長くなりましたが、通知のカスタマイズ方法です。

自分で通知を表示する

dataのみをJSONに含んでAPIを呼んであげれば良いです。

そうするとBackground状態でも、onMessageReceivedが呼ばれるので、NotificationCompat.Builderを使って自分で通知を表示することが可能になります。

APIで設定する

ここnotificationに設定できるキーがあります。

iconsoundを設定することである程度はカスタマイズ可能です。

{
  "to" : "/topics/all",
  "notification": {
    "title": "タイトル",
    "body": "メッセージ",
    "icon": "ic_notification"
  }
}

ただし、Foregroundの時でも通知を表示したい場合は、onMessageReceivedNotificationCompat.Builderの実装が必要になります。

まとめ

通知をちゃんとカスタマイズするには、FCMのAPI経由しかない状況です。あと、Notificationと違ってConsoleに履歴とか残らないのも問題です。(どこかにあるのかな?)

通知を発行するのがエンジニア以外だと、APIを直接投げるようなことは難しいでしょうし、そのあたりも考えないとダメですね。

Firebaseは便利は便利だと思いますが、通知だけやりたいって状況でFirebaseは微妙に合わないかもしれません。個人的はOneSignalのほうが通知に関しては簡単で便利かなと思います。

参考

RecyclerViewでDataBindingを使う

Android

RecyclerViewでDataBindingを使用する簡単なサンプルです。

Gradle

DataBindingの設定をしておきます。

android {
    // ...

    dataBinding {
        enabled = true
    }
}

データクラス

サンプル用に次のような簡単なデータクラスを用意します。

public class Item {

    public final String name;

    public Item(String name) {
        this.name = name;
    }
}

レイアウト

リスト1行のレイアウトです。

基本的には通常のDataBindingと同じです。TextViewに先ほど作ったデータクラスの値を表示します。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="item"
            type="com.example.Item" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{item.name}"/>
    </LinearLayout>
</layout>

Adapter

Adapterのコードです。

onCreateViewHolderでBindingクラスを取得し、それをViewHolderに渡して保持するようにしてます。

表示はonBindViewHolderでBindingクラスに対して変数をセットします。その後、executePendingBindingsを呼び出して即座に反映するようにしています。

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {

    private List<Item> items;

    public RecyclerAdapter(List<Item> items) {
        this.items = items;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // DataBinding
        ListItemBinding binding = ListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
        return new ViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Item item = items.get(position);

        // データセット
        holder.binding.setItem(item);

        // Viewへの反映を即座に行う
        holder.binding.executePendingBindings();
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {

        final ListItemBinding binding;

        public ViewHolder(ListItemBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
        }
    }
}

Activity or Fragment

ここまで実装したらあとはActivityなりFragmentなりで使用するだけです。

今回はActivityの例です。レイアウトファイルは省略で。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding =  DataBindingUtil.setContentView(this, R.layout.activity_main);

        List<Item> items = new ArrayList<>();
        items.add(new Item("Item1"));
        items.add(new Item("Item2"));
        items.add(new Item("Item3"));
        items.add(new Item("Item4"));
        items.add(new Item("Item5"));

        RecyclerAdapter adapter = new RecyclerAdapter(items);
        binding.recycler.setLayoutManager(new LinearLayoutManager(this));
        binding.recycler.setAdapter(adapter);
    }
}

ドキュメント

CircleCIでAndroidのビルドとテスト

Android

CircleCIでAndroidのビルドとテストをするためのcircle.ymlのメモです。

circle.yml

machine:
  java:
    version: oraclejdk8

dependencies:
  pre:
    - echo y | android update sdk --no-ui --all --filter "android-23,build-tools-23.0.3"

test:
  override:
    - ./gradlew app:assembleDebug
    - ./gradlew app:test
  post:
    - cp -r app/build/test-results/* $CIRCLE_TEST_REPORTS

解説

JDK8

retrolambdaを使ってる場合はJDK8が必要なので下記の記述が必要です

machine:
  java:
    version: oraclejdk8

SDKインストール/アップデート

必要なSDKのインストールをします。

dependencies:
  pre:
    - echo y | android update sdk --no-ui --all --filter "android-23,build-tools-23.0.3"

この例だとandroid-23,build-tools-23.0.3を指定していますが、他に何があるかは下記のコマンドでわかります。

$ android list sdk --all --extended

ビルド/テスト

ビルドとテストの実行です。単純にgradleのタスクを実行してるだけです。

test:
  override:
    - ./gradlew app:assembleDebug
    - ./gradlew app:test

テスト結果

テスト結果をCircleCIで表示するための設定です。

Androidでテストを実行した時はapp/build/test-resultsJUnitのテスト結果xmlファイルが出力されます。それを$CIRCLE_TEST_REPORTS以下にコピーするとCircleCI上でテスト結果が見れます。

  post:
    - cp -r app/build/test-results/* $CIRCLE_TEST_REPORTS

参考

AndroidにRetrolabmdaを導入する

Android

今更な感じもしますが、AndroidにRetrolabmdaを導入する手順です。

RetrolabmdaはJava5,6,7でJava8のラムダ式を使えるようにしたものです。

ちょっと導入設定で少しハマったので手順を残しておきます。

環境

JDK8インストール

Oracleのサイトからダウンロードしてインストールしましょう。

Java SE - Downloads | Oracle Technology Network | Oracle

Gradle

evant/gradle-retrolambdaというGradleプラグインを使います。

プロジェクト直下のbuild.gradle

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.0'
        classpath 'me.tatarka:gradle-retrolambda:3.3.0-beta4'
    }
}

これを書いてる時はREADMEのほうに以下のような記述があったので、その通りにします。ここは今後変更される可能性があると思うので適宜変更してください。

Note: If you are using the 2.0.0-alpha/beta android gradle plugins, you should use 3.3.0-beta4 since it better supports instant run.

モジュール内のbuild.gradle

apply plugin: 'com.android.application'
apply plugin: 'me.tatarka.retrolambda'

android {

    // ...

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

// ...

環境変数

次にJDK8のパスを設定する必要があります。これがないと次のようなエラーになります。

Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> When running gradle with java 5, 6 or 7, you must set the path to jdk8, either with property retrolambda.jdk or environment variable JAVA8_HOME

これを解決するにはMacだとAndroidStudioの環境変数がうまく渡せなかったりする問題があります。そのため、色々な解決策があります。ターミナルから起動したり、launchctl setenvを使ったり。

ここでは一番簡単なAndroidStudio側の設定で解決します。

AndroidStudioのメニューからFile -> Project Structure... -> SDK Location -> JDK locationにJDK8のパスを設定するだけです。

これで動くようになるかと思います。