なになれ

IT系のことを記録していきます

AngularとFirebaseでWebアプリを作った

AngularとFirebaseでWebアプリを作ったので、その記録です。

作ったものがこちらです。
友達同士で本を共有できるみんなの本棚というコンセプトで、本を登録し、持っている本を友達同士で共有できるサービスです。
bookshelf-share.hi1280.com

この記事では、このサービスを作るにあたって活用している技術について紹介します。

Webアプリのクライアント部分をカバーしているAngularですが、
さらにFirebaseを活用することでバックエンドのアプリなしでWebアプリを作成できます。

Angular

画面を作るためのベースのフレームワークとして利用しました。
Angular

Angular Material

Angularにマテリアルデザインコンポーネントを提供するライブラリです。
Angular Material

Paginator
ページ数をつけるコンポーネントを活用しました。
こんな感じのコンポーネントを簡単に配置できます。 f:id:hi1280:20180310181123p:plain pageEventでページ数を変えた時の処理を記述できます。

paginator.html

<mat-paginator [length]="length"
              [pageSize]="pageSize"
              [pageSizeOptions]="pageSizeOptions"
              (page)="pageEvent = $event">
</mat-paginator>

AngularFire

AngularからFirebaseを利用するためのライブラリです。
GitHub - angular/angularfire2: The official Angular library for Firebase.

Firebase

バックエンドの機能を備えたサービスです。
認証、データベース、ホスティングの機能などがあります。
Firebase

認証

Firebaseのデータベース機能を使うために認証が実用上必須になります。

Googleログインによる認証
認証をGoogleログインに任せる形で実装できます。

app.component.ts

export class AppComponent {
  user: firebase.User;
  constructor(private afAuth: AngularFireAuth, private router: Router) {
    afAuth.authState.subscribe(user => this.user = user);
  }

  login() {
    this.afAuth.authState.subscribe((user) => {
      if (user && user.uid) {
        this.router.navigate(['main']);
      } else {
        this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
      }
    });
  }

  logout() {
    this.router.navigate(['home']);
    this.afAuth.auth.signOut();
  }
}

Googleだけでなく、FacebookTwitterGitHubのアカウントを利用することもできます。

データベース

クライアントから直接データベースを操作することができます。

Realtime Database
本の一覧を取得するのにクエリを活用して以下のようなコードを書きました。
データ作成の降順でソートしつつ、ページングに対応しています。

books.component.ts

private getBooks(pageSize: number, page: number) {
  this.db.list('books', ref => ref.orderByKey()).snapshotChanges().subscribe((books) => {
    const book = books[pageSize * page];
    // orderByKeyはkeyで昇順にする。昇順によるソートしか対応していない。
    // limitToFirstはデータの取得するに制限をかける。
    // startAtは取得するデータ開始位置を表す。ここではページング対応のために利用している。
    this.booksRef = this.db.list('books', ref => ref.orderByKey().limitToFirst(this.pageSize).startAt(book.key));
    // keyを使うためのコード
    this.books = this.booksRef.snapshotChanges().map(changes => {
      return changes.map(c => ({ key: c.payload.key, ...c.payload.val() }));
    // 降順にする
    }).map((changes) => changes.reverse());
    this.length = books.length;
  });
}

ホスティング

Angularで作成したWebアプリをFirebaseでホスティングできます。

Angularのアプリをビルドします。

$ ng build --prod

FirebaseのCLIツールをインストールします。

$ npm install -g firebase-tools

Firebaseのプロジェクトとして初期設定をします。

$ firebase init

以下のように質問に答えます。

? What do you want to use as your public directory?
dist

? Configure as a single-page app (rewrite all urls to /index.html)?
y

? File dist/index.html already exists. Overwrite?
N

デプロイします。

$ firebase deploy

注意点

Realtime Databaseはクエリ操作が特殊な感じで、機能も充実していないので使う時には注意が必要です。
少なくとも複雑な検索はできません。

MEAN and Cosmos DBをVisual Studio Team ServicesでApp Service on Linuxにデプロイする

AzureのApp ServiceにはWindows版とLinux版があります。
OSSを使用するアプリケーションではLinux上で動作させる方がシンプルな形になりそうです。
Linux版のApp ServiceであるApp Service on Linuxを試してみます。

サンプルアプリの準備

MEAN構成のサンプルアプリとして以下を使用します。
GitHub - hi1280/angular-cosmosdb: Cosmos DB, Express.js, Angular, and Node.js app

前準備

Azure CLIをインストールします。
Azure CLI 2.0 のインストール | Microsoft Docs

Angular CLIをインストールします。

npm install -g @angular/cli

VSTSのプロジェクトを作成します。
Create a VSTS account with a Microsoft account or a work/organization account | Microsoft Docs

Cosmos DBの作成

Cosmos DBを以下のコマンドで作成します。
コマンドのオプションは環境に応じて適宜変更する必要があります。

az login
# リソースグループを作成する
az group create -n my-heroes-db-group -l "Japan East"

# Cosmos DBをMongoDBのAPIで作成する
az cosmosdb create -n my-cosmos-heroes-example -g my-heroes-db-group --kind MongoDB

サンプルアプリとCosmos DBの連携

Cosmos DBの接続文字列を確認します。

az cosmosdb list-connection-strings -n my-cosmos-heroes-example -g my-heroes-db-group --query "connectionStrings[0].connectionString"

example-environment.jsを接続文字列の内容に沿って修正します。
example-environment.jsにはサンプルアプリからCosmos DB(MongoDB)に接続するためのパラメータが定義されています。
example-environment.jsのファイル名をenvironment.jsに変更します。

ローカルでのアプリ起動
# Angular CLIのビルド実行
npm run build

# Node.jsの実行
npm start

http://localhost:3000をブラウザで開きます。

App Service on Linuxの作成

# リソースグループを作成する
az group create --name my-heroes-group --location "Japan East"

# App Serviceプランを作成する
az appservice plan create --name my-app-heroes-plan --resource-group my-heroes-group --sku B1 --is-linux

# App Service on Linuxを作成する
az webapp create --resource-group my-heroes-group --plan my-app-heroes-plan --name my-app-heroes-example --runtime "NODE|8.1"

Visual Studio Team Services(VSTS)との連携とデプロイ

サンプルアプリのコードをVSTSリポジトリにPushします。
Git index to content for VSTS & TFS | Microsoft Docs

ビルド定義

ビルドタスクは以下の通りに定義します。

Get Sources

  • This accountを選択します
  • 該当のリポジトリとブランチを選択します

npmパッケージをインストール

  • Add Taskをクリックし、npmを追加します
  • installコマンドを選択します

Angular CLIのビルド実行

  • Add Taskをクリックし、npmを追加します
  • customコマンドを選択します
  • Command and argumentsrun buildと入力します

サーバのnpmパッケージをインストール

  • Add Taskをクリックし、npmを追加します
  • installコマンドを選択します
  • Working folder with package.jsondistと入力します

成果物を公開する

  • Add Taskをクリックし、Publish Build Artifactsを追加します
  • Path to Publishdistと入力します
  • Artifact Namedistと入力します
  • Artifact publish locationVisual Studio Team Services/TFSを選択します

f:id:hi1280:20180217232228g:plain

リリース定義

リリースタスクは以下の通りに定義します

Artifact

  • ビルドのArtifactの最新を選択します

Azure App Serviceにデプロイする

  • Run on agentの+マークをクリックし、Azure App Service Deployを追加します
  • Azure subscriptionを選択します
  • App typeLinux Web Appを選択します
  • App Service nameを選択します
  • Package or folder$(System.DefaultWorkingDirectory)/my-app-heroes-example-build/distと入力します
  • Runtime StackでNode.jsを選択します

f:id:hi1280:20180218002023g:plain

感想

App ServiceのWindows版とほぼ変わらずに構成することができました。
Linux版ではそのままNode.jsが動作しているのでVSTSの設定もシンプルになっています。
Windows版ではIIS上でNode.jsのアプリケーションを動かすようになっていて、IISの設定が必要といった勝手が違う部分があります。
今後はLinux版を使っていくことになりそうです。

Amazon Echoとラズパイでスマートホーム化

Amazon Echoとラズパイでスマートホームを実現します。
Amazon Echoに対して音声で呼びかけて、Alexaスキルからラズパイを通して家電を操作します。

構成図

f:id:hi1280:20180129215335p:plain

AWS IoTの準備

AWS IoTを使って、Alexaスキルとラズパイとを連携します。
バイスの設定を案内通りに進めることで、該当のデバイスAWS IoTに接続することができます。
AWS IoTに接続するのに必要な証明書が得られます。
f:id:hi1280:20180129225533p:plain

今回はNode.jsを使用しました。
Node.jsでAWS IoTを使用したメッセージングの簡単な例はこちらが参考になります。
aws-iot-device-sdk-js/README.md at master · aws/aws-iot-device-sdk-js · GitHub

ラズパイでのAWS IoTプログラムの実装

AWS IoTのdeviceオブジェクトに証明書の情報を設定します。

AWS IoTからのメッセージを受け取って、赤外線を送信するコード

subscribeメソッドで受け取るメッセージを指定するのと、メッセージを受け取った後に実施する処理をmessageイベントで定義します。

AWS IoTの動作確認

テスト > 発行 からメッセージを発行することができます。
f:id:hi1280:20180130000103p:plain

ラズパイでの家電の操作方法は過去記事を参照してください。
hi1280.hatenablog.com

Alexaスキルの実装

Alexaスキルの種類はカスタムスキルとして実装しました。
カスタムスキルは名前を指定して、スキルを呼び出します。
例えば、こんな呼びかけになります。
「アレクサ、リモコンで照明をつけて」

「リモコン」の部分がスキルの名前になります。

AWS IoTのメッセージを送信するAlexaスキルのコード

AWS IoTのdeviceオブジェクトのpublishメソッドを実行することで、メッセージを送信します。

Alexaスキルの作成方法は過去記事を参照してください。
hi1280.hatenablog.com

まとめ

ラズパイでの赤外線による家電操作とAlexaスキルを組み合わせることでスマートホームっぽくなりました。
今回はスキルをカスタムスキルとして実装しましたが、スキルの名前をいちいち呼ぶのは面倒です。
スマートホームスキルとして実装すると名前を呼びかける必要がなくなるようです。
ただし、スマートホームスキルに対応しているデバイスがまだ少ないようなので、まだこれからという状況かと思います。
スマートホームスキルを複数の言語で開発する | ASK