CIが通ったら自動で部屋をダンスフロアにする

CIが通ったら自動で部屋をダンスフロアにする

先日、ミラーボールを購入しました。

ライトが内蔵されており、ミラー部分が回りながらギラギラギラギラ光り、レーザーが出まくります。強烈なビジュアルです。

電源や点滅パターンの操作が可能なリモコンが付属しており、リモコンの赤外線信号をスマートリモコンに学習させることで、スマートフォン、Amazon Alexa などから操作できるようになります。

また、Web サービス同士の連動設定などを行えるサービス「IFTTT」や、センサー類と組み合わせることで、さらに活用の幅が広がります。

暮らしを便利・安心にする活用例 :

  • 外出先からミラーボールの電源を操作する
    • ミラーボールを回したまま家を出てしまった!」「ミラーボールを止めたか不安……という場面でも、外から電源を切ることができるのでとても安心です。
  • 侵入者を感知したらミラーボールを回して威嚇する
    • 人感センサーを利用し、反応があったらミラーボールを回します。
    • 常時ミラーボールを回しておいて部屋の窓が輝いている方が、空き巣対策として有効なのでは?という気もします。

CI が通ると踊りたくなる

僕は開発作業中、CI で回した自動テストが正常終了したらとてもゴキゲンな気分になるのですが、CI の無機質なモニター画面で成功表示を見ても、いまいち気分が盛り上がらないのが悩みでした……(◞‸◟)

今回はそんな悩みを解決するため、「自動テストが全部通ったら、自動で部屋の照明を消しつつミラーボールを回し、ゴキゲンな音楽が流れる」ようにしてみたいと思います ٩(ˊωˋ*)و

具体的な手段も含め、整理するとこのようになります。

  • アクションを起こすきっかけ
    • CI(GitHub Actions) で実行した自動テストが正常終了する
  • 自動で実行するアクション
    • スマートリモコンを使って照明を消す
    • スマートリモコンを使ってミラーボールを回す
    • Spotify に接続した Amazon Echo からゴキゲンな音楽を流す

Web API が公開されていないスマートリモコンでも、HTTP リクエストで操作を実行したい!

少し横道に逸れますが、こういったスマートリモコンの操作は Web API にリクエストを投げて実行できると便利です。スマートリモコン 「Nature Remo」 には公式の Web API が公開されていますが、僕が所持しているラトックシステムのスマートリモコン RS-WFIREX4 には、Web API が存在しません(2020年4月現在)。

参考: 同期の小林くんによるスマートリモコンの記事です(https://www.membersedge.co.jp/blog/nature-remo-cloud-api/)

ですが、「IFTTT」からの操作には対応しているため、IFTTT で「トリガーにWebhooks を設定」し、「アクションにスマートリモコンの操作を割り当てる」ことで、「IFTTT のエンドポイントにリクエストを投げるとリモコン信号が送られる」状態になり、簡易的に Web API として使うことができるようになります。

今回はこの方法でスマートリモコンの操作を行ってみます。

照明を消す

IFTTT を利用し、HTTP リクエストを受けとったら照明が消えるよう設定します。

IFTTT から新規 Applet を作成、”If” には「Webhooks」を指定し、任意のイベント名を設定します。今回は “turn_off_light” としました。

スマートリモコンと IFTTT の連携設定を行い、”Then” に照明のリモコンをオフにするアクションを設定します。

Webhooks トリガーを利用する操作に必要な「key」を、IFTTT のドキュメントページから取得します。key の文字列が漏れないよう十分注意して取り扱いましょう。第三者の手にわたると、IFTTT で設定しているアクションが自由に実行可能な状態になってしまいます。

設定が完了すると、以下の URL に GET か POST リクエストを送信することでアクションが実行される状態になります。

https://maker.ifttt.com/trigger/turn_off_light/with/key/[ifttt key]

ミラーボールを回す設定

こちらも IFTTT で設定します。If には「Webhooks」として任意のトリガーを設定し、イベント名を “turn_on_mirror_ball” としました。アクションに、ミラーボールリモコンの電源ボタンに対応するものを指定します。
操作の実行に必要な URL は以下のようになります。

https://maker.ifttt.com/trigger/turn_on_mirror_ball/with/key/[ifttt key]

Spotify で音楽再生

Spotify API は、楽曲情報の取得、ユーザーが持つプレイリストデータ取得、ユーザーのデバイスの楽曲の再生 / 停止などの制御を行うなど、多様なコントロールが可能な API です。
API の利用のために OAuth 2.0 のフローを利用して認証しますが、今回は認可サーバーとして、 Spotify 公式が公開している “web-api-auth-examples” を利用することにしました。

https://github.com/spotify/web-api-auth-examples

Spotify ユーザーのリソースにアクセスする必要があることなどを踏まえ、今回は OAuth 2.0 の Authorization Code フローを使って認証します。Spotify for Developers の Dashboard から、今回の操作に使う App を新規に登録し、CLIENT_ID、CLIENT_SECRET を控えておきます。

“web-api-auth-examples” で認可サーバーを立てる準備をします。

git clone https://github.com/spotify/web-api-auth-examples
cd web-api-auth-examples
npm install
cd authorization_code

authorization_code/app.js を編集し、先ほどの CLIENT_ID と CLIENT_SECRET を貼りつけます。

var client_id = 'xxxxx'; // Your client id
var client_secret = 'xxxxx'; // Your secret
var redirect_uri = 'http://localhost:8888/callback'; // Your redirect uri

また、目的の操作を行うために必要な権限を得るため、スコープを指定します。今回はユーザーのデバイスから再生させるため、”user-modify-playback-state” が必要です。

// your application requests authorization
var scope = 'user-modify-playback-state';

もう一度 Spotify for Developers のページに戻り、先ほど登録した App 設定画面から Redirect URIs を指定します。今回はローカルで実行した認可サーバーに合わせるため、`http://localhost:8888/callback` と設定しました。

“node app.js” でサーバーを起動し、ブラウザから “localhost:8888” へアクセスしてみましょう。

画面中のログインボタンから Spotify にログインすると、先程作成した App に対してユーザーのリソースにアクセスする権限を与える承認画面が表れます。承認ボタンを押すと、ACCESS_TOKEN が表示されるので、コピーしておきます。

(ここで取得できる ACCESS_TOKEN は有効時間が1時間しかないため、それ以上使い続ける場合は、同画面で取得できる REFRESH_TOKEN を用いて更新する必要があります。今回はお試しということで、REFRESH_TOKEN 更新の実装は割愛します。)

最後に、デバイスから再生させるエンドポイントへのリクエストを組み立てます。Authorization ヘッダに ACCESS_TOKEN を指定し、URL に再生したいデバイスのID(今回は Amazon Echo の Spotify Device ID)を指定します。body には楽曲の Spotify Track URI を指定します。

curl -i -X PUT \
  -H "Content-Type:application/json" \
  -H "Authorization:Bearer [ACCESS_TOKEN]" \
  -d \
    '{"uris": ["spotify:track:[再生したい Track URI]"]}' \
    'https://api.spotify.com/v1/me/player/play?device_id=[再生に使いたい device ID]'

GitHub Actions から動かす

今回は CI として GitHub Actions を利用し、その中から curl コマンドを実行して API にリクエストを送信し、照明・ミラーボール・Spotify の操作を行います。

操作のために、IFTTT の Key や、Spotify の Access Token などの秘匿すべきデータが必要ですが、それらは GitHub Actions の Workflow を定義する YAML ファイルには直接書かず、GitHub の Secret に記述して設定し、YAML ファイルを元に動くシェルから環境変数として読み出せるようにします。

Workflow 定義の YAML ファイルに記述を加えます。”if: success()” を利用することで、直前に動いた step の終了結果が成功だったときのみ実行されるようになります。

steps:
  # (中略)
  - name: Run test
    run: npm test
    # テストがここで実行される
  - name: Turn on mirror ball
    if: success()
    shell: bash
    env:
      IFTTT_KEY: ${{ secrets.IftttKey }}
      SPOTIFY_ACCESS_TOKEN: ${{ secrets.SpotifyAccessToken }}
      SPOTIFY_DEVICE_ID: ${{ secrets.SpotifyDeviceId }}
    run: |
      curl -X POST https://maker.ifttt.com/trigger/turn_off_light/with/key/$IFTTT_KEY
      curl -X POST https://maker.ifttt.com/trigger/turn_on_mirror_ball/with/key/$IFTTT_KEY
      curl -i -X PUT -H "Content-Type:application/json" -H "Authorization:Bearer $SPOTIFY_ACCESS_TOKEN" -d '{"uris": ["spotify:track:[再生したい Track URI]"]}' 'https://api.spotify.com/v1/me/player/play?device_id=$SPOTIFY_DEVICE_ID'

これで設定は完了です!

動かしてみよう!

master に push して Workflow を動かしてみると……?

🕺💃🕺💃🕺💃🕺💃

🕺💃🕺💃🕺💃🕺💃

🕺💃🕺💃🕺💃🕺💃

(ちなみに外から見ると……)

🕺💃🕺💃🕺💃🕺💃

🕺💃🕺💃🕺💃🕺💃

🕺💃🕺💃🕺💃🕺💃

踊り疲れたら、手動で諸々止めます。

おわりに

CI の実行結果をトリガーとして、家電や Spotify のコントロールを行うことができました。今回は音楽を流すだけにとどまりましたが、Spotify の Audio Analysis API を使えば、楽曲のテンポや拍のタイミングといった詳細な情報が取得できるため、ミラーボールの色が切り変わるタイミングを音楽に合わせて制御するなども可能です。

他にも、赤外線リモコンで操作できる機器や各種センサーの組み合わせによって、活用の幅はまさに無限大に広がります。皆さんにも、様々な機器を組み合わせて、日々の生活を便利に、豊かにしていただきたいと思います!

採用情報

メンバーズエッジで最高のチームで最高のプロダクトを作りませんか?

最高のプロダクトをつくる 最高のチームで働く

在宅でも、地方でも、首都圏でも。多様な働き方で最高のチームをつくり、お客様のプロダクトパートナーを目指します。アジャイル開発を通じ、開発現場の第一線で活躍し続けたいエンジニアを募集しています。