🛰

GoogleAssistantを使ったスマートホームをガチガチに極めたい人用のサービスを作っている話

2020/12/24に公開

2017年10月 Google Home襲来 (背景)

※背景はいらん!というかたはスクロールして本題からお読みください

googlehome

2017年10月に日本で Google Home が発売されました。高い音声認識精度を持つスマートスピーカーが1万円程度の価格で、さらに IFTTT や Dialogflow を使って専用のコマンドやアプリを作って拡張可能ということで、発売当初は Qiita などで頻繁に記事があがっていました。 IFTTT と Dialogflow のどちらパターンも比較的簡単にカスタマイズできるため遊びでやる分には楽しいのですが、真面目に生活に取り込もうとすると細かな制約が導入の障壁となります。

例えば、 IFTTT ではトリガーはワイルドカード($)を使って比較的柔軟に書けて簡単にトリガーできますが、返答は定型文の1パターンしか指定できません。また、一問一答のため、 GoogleAssistant 側から質問について確認したり、深掘りしたりすることはできません。一方で、 Dialogflow は元々が自然言語理解プラットフォームのため、会話モデルについては IFTTT とは比較にならないレベルでかなりカスタマイズができるようになっていました。ですが、アプリとして Google のプラットフォームにデプロイする必要があり、 Google Assistant から利用するにはいちいち「〜〜〜につないで」とアプリを呼び出してから会話をはじめる必要があります。
(さらに、 IFTTT は2020年10月に有料サービスとなりました。また、IFTTTは2022年8月にGoogleHome上で汎用的なコマンドを実行するアプレットの提供終了し、スマートホームをベースとした連携のみとなりました。)

IFTTT Dialogflow
ifttt dialogflow

上記の通り、 IFTTT と Dialogflow には良い点と悪い点がありました。簡単にまとめると、呼び出しやすいが融通が効かない IFTTT と、呼び出しまでが1ステップ多いが、融通が効く Dialogflow という感じです。ですが、実はもう一種類連携の方法があります。それが、Actions on Google Smart Homeです。

Actions on Google Smart Home とは

Actions on Google

Actions on Google Smart Home は Google によるスマートホームのプラットフォームです。このプラットフォームの仕様に沿ってデバイスを実装することで、デバイスを GoogleAssistant と連携させることができるようになります。そして、この仕組みは Dialogflow のようにアプリを呼び出すことは必要なく、 Standby 状態でいきなり呼び出せます。さらに、必要に応じて GoogleAssistant 側から質問の確認をさせたり、実行前にPINコードを確認させることもできます。また、 GoogleHome シリーズを各部屋に配置しておけば、ユーザがどの部屋で話しかけたかによりデバイスを明示的に指定しなくても察してくれるようになります。例えば、リビングで「電気消して」と言った場合には、 GoogleAssistant はリビングに存在する電気を消灯する、といった具合です。 Dialogflow では GoogleHome の Id や部屋の情報などは取得できないため、この仕組みならではの機能です。

こんなに便利な Action on Google Smart Home ですが、 IFTTT や Dialogflow とは異なるポイントがあります。それは、Actions on Google Smart Home の仕組みは消費者向けではなく、デバイスの製造・販売側に向けた仕組みということです。 Google としてはスマートホームのプラットフォーム仕様を公開しており、メーカーはその仕様に沿って実装すれば自社の製品をスマートスピーカー対応とできるわけです。この仕組みが用いられているプロダクトとしては、家電そのものがスマートホーム Ready になっている製品である iRobot のルンバシリーズや Philips の Hue シリーズ、また、スマートホーム Ready ではない家電を Ready にするための Gateway の役割を担う製品として、 NatureRemo シリーズ、ラトックシステムのスマート家電コントローラーなどがあります。さらに、家電だけではなく、 HEMS にも GoogleAssistant 対応のものは存在して、 Panasonic の AiSEG2 がその一例です。(日本には ECHONET(lite) というスマートホームのための規格があったはずなのですが、今や最新家電が実装している共通プロトコルは Alexa, GoogleAssistant という状況です…。)

少し話が逸れましたが、デバイスの製造・販売側に向けた仕組みである Actions on Google Smart Home は消費者でもその仕様に則って環境を用意すれば自身のデバイスをプラットフォームにのせることは可能です。ただし、それには OAuth2 に対応した認証の仕組みを用意して、 Actoins on Google から届くリクエストを処理するWebサーバをたてて、仕様を読みながら中身を実装して、さらに自宅のデバイスを操作するためのサーバ(ゲートウェイ)も用意しなければなりません。この仕組みは素晴らしいですが、導入までのハードルがとても高いのです。

※(ここで、Home Assistantを使えばもっと楽できるんじゃない?と思った人は、本題を読み飛ばして最後のおまけを読んでみてください。)

本題

というわけでは長い長い前置きが終わったところで本題です。この記事は GoogleAssistant を使ったスマートホームをガチガチに極めたい人用のサービスを作っている話です。なお、具体的な対象者は「スマートスピーカー対応の家電を買い揃える人」ではなく「既存の家電やモノ・もしくは自分の作ったデバイスを何が何でも全て Google のプラットフォームに乗っけたい人」です。

作っているもの

端的に表現すると、「デバイス定義・アクションを Typescript で定義し、それを Gateway 役の RaspberryPi で稼働させると任意のデバイスが GoogleAssistant 連携となるもの」です。 Satellite-Kit という名前で開発を進めています。
なお、インフラは AWS で、 API Gateway, AWS IoT Core, DynamoDB, Cognito とサーバーレス構成になっています。AWSと家の通信は MQTTS(TCP:443) のため、 ngrok を使ったり、ポートを開放したりする必要はありません。

構成図

Action on Google Smart Home では、デバイス毎に状態(STATE)、状態の問い合わせ(QUERY)、状態の変更(EXECUTE)が定義されており、これら全てが音声(もしくはテキスト)から利用できるようになっています。例えば、オーディオ機器を定義する場合は、STATEに電源(ON,OFF)、入力(HDMI1, HDMI2, 光DIGITAL), 音量(数値)、EXECUTEに電源On/Off, 入力切り替え, 音量変更のような形で定義していきます。このように定義すると「今の音量は?(QUERY)」「今オーディオついてる?(QUERY)」や「音量を5にして(EXECUTE)」「入力を光DIGITALに切り替えて(EXECUTE)」のようなコマンドが利用になるといった具合です。この例をSatellite-Kitのコード定義に起こすと次のようになります。

class LivingAmp extends DeviceTypes.Settop {
  traits = {
    OnOff: new Traits.OnOff({
      Attributes: {
        CommandOnlyOnOff: new OnOff.Attribute.CommandOnlyOnOff(true)
      },
      States: {
        On: new OnOff.State.On(true)
      },
      Commands: {
        OnOff: new OnOffCommand({})
      }
    }),
    InputSelector: new Traits.InputSelector({
      Attributes: {
        AvailableInputs: new InputSelector.Attribute.AvailableInputs<AmpCommands>([
          {
            key: 'inputHdmiSelector',
            names: [
              {
                name_synonym: ['HDMIセレクタ'],
                lang: ja
              }
            ]
          },
          {
            key: 'inputChromecast',
            names: [
              {
                name_synonym: ['クロームキャスト', 'chromecast'],
                lang: ja
              }
            ]
          }
        OrderedInputs: new InputSelector.Attribute.OrderedInputs(false)
      },
      States: {
        CurrentInput: new InputSelector.State.CurrentInput('inputHdmiSelector')
      },
      Commands: {
        SetInput: new SetInputCommand({})
      }
    }),
    Volume: new Traits.Volume({
      Attributes: {
        VolumeMaxLevel: new Volume.Attribute.VolumeMaxLevel(50),
        VolumeCanMuteAndUnmute: new Volume.Attribute.VolumeCanMuteAndUnmute(true),
        CommandOnlyVolume: new Volume.Attribute.CommandOnlyVolume(false)
      },
      States: {
        CurrentVolume: new Volume.State.CurrentVolume(15),
        IsMuted: new Volume.State.IsMuted(false)
      },
      Commands: {
        SetVolume: new SetVolumeCommand({}),
        Mute: new MuteCommand({}),
        VolumeRelative: new VolumeRelativeCommand({})
      }
    })
  };
  willReportState = true;
}

オーディオ機器は比較的機能が多いため少し長い定義となってしまいますが、電球やスイッチなど機能が少ないデバイスはもう少しシンプルになります。なお、現在の設計では、 Attribute, State はサービス側で用意している定義をそのまま利用し、 Command(EXECUTE) はサービス側の定義を継承した上でデバイス操作のコードをユーザ側で実装する方式になっています。そのため、実際のコードでは MuteCommand クラスが別で存在し、その execute メソッドには、オーディオ機器に接続しMuteするコードが書かれています。

これの何が良いのか

世の中にはルンバや Hue 、NatureRemo がある中で、わざわざ面倒くさい TypeScript を書くモチベーションはどこから生まれるのでしょうか。理由は以下の三つです。

  • その1: 赤外線リモコン以外のインターフェースを持つ家電もスマートホーム対応にできる
  • その2: 自分でデバイスの特性(Trait)を組み立てられる
  • その3: より複雑な処理を定義できる

いいことその1: 赤外線リモコン以外のインターフェースを持つ家電もスマートホーム対応にできる

NatureRemo シリーズ、ラトックシステムのスマート家電コントローラーは赤外線リモコンと同じパターンで赤外線を発信することで、家電をコントロールするものです( NatureRemo シリーズの上位モデルは Bluetooth 対応の家電もコントロールできるようになっています)。そのため、赤外線リモコンのインターフェースを持たない家電はこういったデバイスを使ってもスマート家電化することはできません。

一方 Satellite-Kit では実装はユーザ側で行う必要があるので、自分で書きさえすればどんな家電のコントロールでも可能です。例えば、先ほど例に挙げたオーディオ機器は私の家で実際に利用している機器ですが、コントロールは telnet のコマンドで行っています。オーディオ機器なので赤外線リモコンでの操作も可能ですが、 telnet を使うと状態の取得もできるほか、赤外線リモコンの障壁となる遮蔽物を気にする必要もありません。

export const DeviceSettings = {
  connection: {
    ipAddress: 'XXX.XXX.XXX.XXX',
    port: 0000 
  },
  commands: {
    volumeDown: 'VD',
    volumeUp: 'VU',
    getVolume: '?V',
    powerOn: 'PO',
    powerOff: 'PF',
    getPower: '?Q',
    inputDvd: '04FN',
    inputSatCbl: '06FN',
    inputDvrBdr: '15FN',
    inputIpodUsb: '17FN',
    inputBd: '25FN',
    inputAdapter: '33FN',
    inputInternetRadio: '38FN',
    inputChange: 'FU',
    getInputStatus: '?F',
    inputHdmiSelector: '49FN', // alias of game
    inputChromecast: '05FN', // alias of tv
    inputWiiu: '06FN', // alias of satCbl
    inputAnalogAudio: '01FN', // alias of cd
    mute: 'MO',
    unmute: 'MF',
    muteStatus: '?M'
  }
};

※とあるオーディオ機器のTelnetコマンド

また、 M5Stack や Raspberry Pi などで作った自作デバイスに WebServer や MQTT で操作用のインターフェースを実装しておけば、 Satellite-Kit を使って GoogleAssistant から操作や状態の取得をすることが可能になります。
NatureRemo のような手軽さはないですが、なんでもかんでも Google の世界に持ち込めるのが Satellite-Kit の良いところです。

いいことその2: 自分でデバイスの特性(Trait)を組み立てられる

ここで新しい Trait という言葉が出てきましたので少し説明します。
Actions on Google Smart Home において、デバイスはデバイスタイプ(DeviceType)と特性(Trait)の2つの要素で成り立っており、1つのデバイスにつきデバイスタイプは1種、特性は複数種持つことができます。

DeviceType(デバイスタイプ)

DeviceType は、デバイスの種類です。1つのデバイスにつき、1種選択することができます。 DeviceType の例としては、エアコン、空気清浄機、バスタブ、ベッド、ブラインド、一酸化炭素検知器、クローゼット、コーヒーメーカー、食洗機、暖炉、門、照明、電子レンジなどです。メジャーな家電から、暖炉、門のような中々日本の家屋ではお目にかからないようなモノまで幅広く用意されています。

DeviceType にはそれぞれ推奨する特性(Trait)が定義されています。推奨のため必ずしもそれを選ぶ必要はなく推奨外のものを選択することもできますが、推奨された Trait を選んでおくと Google Assistant への要求(操作・質問)が理解されやすくなります。

なお、 DeviceType 自体は属性や状態を持ちません。 DeviceType によって変わるのは上記の推奨する Trait 定義です。

例で紹介しきれなかった分の情報は下記の公式ドキュメントを御覧ください。

Smart Home Device Types | Actions on Google Smart Home

Trait(特性)

Trait は、デバイスが持つ特性です。1つのデバイスにつき、複数種選択することができます。 Trait の例としては、オン・オフ、明るさ、チャンネル、色設定、出す(ディスペンサー)、電気容量、ファンのスピード、入力チャンネル、解錠・施錠、開閉、音量、などです。

各 Trait の詳細な情報は下記の公式ドキュメントを御覧ください。

Smart Home Device Traits | Actions on Google Smart Home


上記のように、 Actions on Google Smart Home では多数の DeviceType ,Trait が用意されており、 Satellite-Kit では、ユーザが持ち込むデバイスに応じて自由に DeviceType, Trait を選ぶことができます。既存のサービスの場合は、サービス側があらかじめ実装している DeviceType, Trait のみ利用可能であることが多いためここまで自由度は高くありません。

また、 Actions on Google Smart Home の世界に持ち込むモノを考えるときに、 DeviceType という言葉から連想すると家電や電子機器などが対象と思い込んでしまいますが、これらに囚われる必要はありません。例えば、家族をデバイスとして定義して On/Off Trait を持たせ、家族つけた活動量計から寝ているか否かを On/Off で表現すれば、あなたはオフィスにいながら、家族が起きているのか、寝ているのかを GoogleAssistant 経由で知ることができます(この場合、もちろん家族を操作することはできないので EXECUTE はありません。あくまでも State の更新・確認のみです)。また、 Amazon EC2 などのクラウドサービス上の仮想サーバをデバイスとして持ち込んで、起動、停止などを声で操作するのも面白いかもしれません。本来は仮想サーバ用途ではないですが、 Reboot Trait や NetworkControl Trait などを取り入れると、操作の幅が広がります。

※筆者の自宅にGrafana用EC2が鎮座する様子

いいことその3: より複雑な処理を定義できる

いいことその3、最後は「より複雑な処理を定義できる」です。

状態の変更(EXECUTE)には正しい合言葉を言わないと実行できなくするもの(PinCode)や、本当に実行するかの確認を求める機能(askNeeded)が用意されています。 Satellite-Kit ではこれらもサポートされているため、例えば PinCode を活用して、合言葉が日付によって毎日・毎時変わる Command を定義することも可能です。子供がゲームをやりすぎないように PinCode で意図的にコマンドを封じたり、ちょっとした算数の問題の答えがそのまま PinCode になっていたら面白いかもしれません。また、askNeed を活用して、夜間にオーディオのボリュームを一定以上にあげようとしたら、「本当にあげますか?」と確認させることもできます。また、電力系統毎の消費電力をリアルタイムで取得できる環境があれば、家電の電源を入れる前に消費電力をチェックし、ブレーカーがあがりそうなのであれば電源オンの EXECUTE を断るように実装するのも良いでしょう。

※PinCodeを使ったテスト

まとめ

というわけで、 GoogleAssistant を使ったスマートホームをガチガチに極めたい人(既存の家電やモノ・もしくは自分の作ったデバイスを何が何でも全て Google のプラットフォームに乗っけたい人)用のサービスを作っている話でした。Satellite-Kitはほぼ動く状態にはなっており、自宅では家族で活用しています。自画自賛ですが、デバイスの挙動をかなりカスタマイズできて快適に過ごせるようになるので、なんとかしてリリースまでこぎ着たいところです。

おまけ

え、Home Assistantでよくない?

はい、おっしゃるとおりです。ある程度できあがったところでこのプロダクトについて知りました。 GoogleAssistant とも連携が可能で、比較的自由に DeviceType, Trait が選べるようです。さらに、コントロールパネルや様々なデバイスとの連携も可能なようですね…すばらしい😇

その後(2023年)

(自分用に)まだ細々と開発を続けています。

一度サービスのリリース直前まで整えて連携アプリの申請まで進んだのですが、GoogleHomeの連携アプリをリリースするには物理的なデバイスを販売していることが条件としてあったため、その条件を満たせないことがわかりました…。

Discussion