前回の「Nest MiniでLocal Home SDKを使ってお家のIoTロボットを操作する 第4回」では、コードラボの「Smart Home Local Execution」で使用していた仮想デバイス(仮想スマートウォッシャー)を、ピッコロボIoTに置き換えるように実装しました。

ピッコロボIoTについては、「Nest MiniでLocal Home SDKを使ってお家のIoTロボットを操作する 第1回」をご覧ください。

今回は、今までは「洗濯機」として扱っていたピッコロボIoTですが、適切なデバイス名で呼ぶように実装していきます。

GoogleHomeデバイスで利用できる家電デバイス

以前にも少し触れましたが、GoogleHomeデバイスでは、家電をアプリ呼び出しなしに操作できる機能があります。
Smart Homeのリファレンスに「Smart Home Device Types」というページがあり、ここの一覧のDeviceが、操作可能な家電になります。
また、各家電の操作コマンドは、Recommended Traitsに書かれているものが対象になります。

今まで「洗濯機」として扱ってきたのは、「Washer」というDeviceで、「Modes」「OnOff」「RunCycle」「StartStop」「Toggles」の5つのRecommended Traitsがあります。

「Switch」というDeviceで、「OnOff」というRecommended Traitsがあるので、これに変えて、ピッコロボIoTを「スイッチを入れて」で動かして、「スイッチを切って」で止めるようにしてみようと思います。

クラウドフルフィルメントの修正

「smarthome-local/app-start/functions」の「index.js」を以下のように変更します。
コメントアウトしたところは、説明上残しているだけで、除去してもらって構わないです。

「washer」から「switch」に変更し、状態がOnOffだけになったので、不要なものを除去しています。
また、デバイス名も「Piccorobo IoT」に変更してみました。

app.onSync((body) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '123',
      devices: [{
        /* washer から switch に変更 */
        id: 'switch',
        type: 'action.devices.types.SWITCH',
        traits: [
          'action.devices.traits.OnOff',
        ],
        name: {
          /* Piccorobo IoTに変更 */
          defaultNames: ['Piccorobo IoT'],
          name: 'PiccoroboIoT',
          nicknames: ['PiccoroboIoT'],
        },
        ...
      }],
    },
  };
});

const queryFirebase = async (deviceId) => {
  const snapshot = await firebaseRef.child(deviceId).once('value');
  const snapshotVal = snapshot.val();
  return {
    on: snapshotVal.OnOff.on,
    /* 不要なのでコメントアウト */
    //isPaused: snapshotVal.StartStop.isPaused,
    //isRunning: snapshotVal.StartStop.isRunning,
  };
}

const queryDevice = async (deviceId) => {
  const data = await queryFirebase(deviceId);
  return {
    on: data.on,
    /* 不要なのでコメントアウト */
    /*
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{
      currentCycle: 'rinse',
      nextCycle: 'spin',
      lang: 'en',
    }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
    */
  };
}

...

const updateDevice = async (execution,deviceId) => {
  const {params,command} = execution;
  let state, ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    /* 不要なのでコメントアウト */
    /*
    case 'action.devices.commands.StartStop':
      state = {isRunning: params.start};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.PauseUnpause':
      state = {isPaused: params.pause};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    */
  }

  return ref.update(state)
    .then(() => state);
};

...

exports.reportstate = functions.database.ref('{deviceId}').onWrite(async (change, context) => {
  ...
  const requestBody = {
    ...
    payload: {
      devices: {
        states: {
          /* Report the current state of our washer */
          [context.params.deviceId]: {
            on: snapshot.OnOff.on,
            /* 不要なのでコメントアウト */
            //isPaused: snapshot.StartStop.isPaused,
            //isRunning: snapshot.StartStop.isRunning,
          },
  ...
});

...

exports.updateState = functions.https.onRequest((request, response) => {
  /* washer から switch に変更 */
  firebaseRef.child('switch').update({
    OnOff: {
      on: request.body.on,
    },
    /* 不要なのでコメントアウト */
    /*
    StartStop: {
      isPaused: request.body.isPaused,
      isRunning: request.body.isRunning,
    }
    */
  });

  return response.status(200).end();
});

下記コマンドを実行して、functionsディレクトリに移動し、デプロイします。

cd functions
firebase deploy --only functions

ローカル実行の実装を修正する

「smarthome-local/app-start/local」の「index.ts」を以下のように変更します。
コメントアウトしたところは、説明上残しているだけで、除去してもらって構わないです。

「washer」から「switch」に変更し、状態がOnOffだけになったので、不要なものを除去しています。

  identifyHandler(request: IntentFlow.IdentifyRequest):
      Promise<IntentFlow.IdentifyResponse> {
      ...
      const response: IntentFlow.IdentifyResponse = {
        intent: Intents.IDENTIFY,
        requestId: request.requestId,
        payload: {
          device: {
            id: 'switch', // washer から switch に変更
            verificationId: localDeviceId.toString(),
          }
        }
      };
      ...
  }
  ...
  getDataForCommand(command: string, params: IWasherParams): unknown {
    switch (command) {
      case 'action.devices.commands.OnOff':
        return {
          on: params.on ? true : false
        };
      /* 不要なのでコメントアウト */
      /*
      case 'action.devices.commands.StartStop':
        return {
          isRunning: params.start ? true : false
        };
      case 'action.devices.commands.PauseUnpause':
        return {
          isPaused: params.pause ? true : false
        };
      */
      default:
        console.error('Unknown command', command);
        return {};
    }
  }

下記コマンドを実行して、localディレクトリに移動し、コンパイルとデプロイをします。

cd local
npm run build
firebase deploy --only hosting

ピッコロボIoTの実装を修正する

「LocalHomeSDK_PiccoroboIoT」の「httpsrv.h」を以下のように変更します。
コメントアウトしたところは、説明上残しているだけで、除去してもらって構わないです。

状態がOnOffだけになったので、不要なものを除去しています。

...
class Status {
    public:
        bool on;
        /* 不要なのでコメントアウト */
        // bool isRunning;
        // bool isPaused;
};
...
void LocalHomeServer::begin() {
    ...
    server->on("/", HTTP_POST,  [](){
        ...
        if(doc.containsKey("on")) {
            status.on = doc["on"];
        }
        /* 不要なのでコメントアウト */
        // if(doc.containsKey("isRunning")) {
        //     status.isRunning = doc["isRunning"];
        // }
        // if(doc.containsKey("isPaused")) {
        //     status.isPaused = doc["isPaused"];
        // }
        Serial.printf("on:%d\n", status.on);
        ...
    });
    server->begin();
    Serial.printf("Http Server at port %d\n", LOCAL_HOME_SERVER_PORT);

    status.on = false;
    /* 不要なのでコメントアウト */
    // status.isRunning = false;
    // status.isPaused = false;
}
...
void LocalHomeServer::reportState() {
    StaticJsonDocument<200> doc;
    doc["on"] = status.on;
    /* 不要なのでコメントアウト */
    // doc["isRunning"] = status.isRunning;
    // doc["isPaused"] = status.isPaused;
    ...
}

「LocalHomeSDK_PiccoroboIoT」を以下のように変更します。
「SoundSensor_PiccoroboIoT」の「motion.h」をコピーしてきて、インクルードするようにします。
「motion.h」については、「Nest MiniでLocal Home SDKを使ってお家のIoTロボットを操作する 第1回」を参照ください。

スイッチONの状態で動作するようにモーションを設定し、スイッチOFFでは初期姿勢(正面を向いている状態)のモーションを一度だけ呼んで動作を停止させています。

...
#include "motion.h"
...
void setup() {
    ...
    
    // 各サーボを有効化
    servoEnable(1, 1);
    servoEnable(2, 1);
    servoEnable(3, 1);
    servoEnable(4, 1);
}

void loop() {
    ...
    
    detectMotion();
    selectMotion();
}

void detectMotion() {
    int motionNum = M_NUM0;
    // スイッチONの状態で動作するモーションを設定する
    if(status.on){
        motionNum = M_NUM1;
    }
    
    // モーション番号を設定する
    setMotionNumber(motionNum);
}

void selectMotion(){
    // 設定していたモーション番号を取得し、その番号毎にモーションを実行する
    switch(getMotionNumber()){              
        case M_NUM0:
            // 初期姿勢(正面を向いている状態)のモーションを一度だけ実行する
            playMotionOnce(motion0, 1);
            break;
        case M_NUM1:
            // モーションを繰り返し実行する
            playMotion(motion3, 5);
            break;
    }
}

Googleアシスタントへのリンクを変更する

Googleアシスタントへのリンクを変更する必要があります。
スマートフォンでGoogleアシスタントの設定を開きます。
設定の開き方や以降の手順についての詳細は、「Nest MiniでLocal Home SDKを使ってお家のIoTロボットを操作する 第2回」を参照ください。

Googleアシスタントの設定画面を開いたら、「アシスタント > スマートホーム」を選択し、「デバイス」を選択した状態で、右下にあるプラス(+)アイコンを選択します。
デバイスの追加画面で、「リンクされたサービス」の中に「[test]」から始まるものがあると思います。(以前追加したものです。)
これをタップします。

「アカウントをリンク解除」が表示されるので、それをタップします。
一度リンクを解除して、この後、「[test]」から始まるものを再度追加しなおします。

部屋の割り当てで、以前は洗濯機のアイコンだったのが、スイッチのアイコンに変わっていると思います。
タップして、部屋を割り当てたら、完了ボタンを押下してください。

音声コマンドで動かす

Google Homeデバイス(Nest Mini)の電源を入れなおしてください。
ピッコロボIoTは、サーボを動かすので、電池を接続しておく必要があります。

Google Homeデバイス(Nest Mini)へ下記のように話し、音声コマンドを介してコマンドをピッコロボIoTに送信します。

  • スイッチを入れて
  • スイッチを切って

ピッコロボIoTが「スイッチを入れて」で動きだし、「スイッチを切って」で停止しますね!
なお、今回のソースコードはGithubのvstoneofficial/LocalHomeSDK_PiccoroboIoT_Switchに上げています。

Local Home SDKを使ってIoTロボットを操作するシリーズは以上になります。

ピッコロボIoTのV-duinoを使うと、ピッコロボIoTのページの「V-duinoを組み込んだ作例を公開」にあるようなロボット(下図)も作れます。
これらはソースファイルだけでなく、CADデータも公開されています。

V-duinoでIoT家電を作って、それをNest Miniから操作する、ということも出来そうですね!
Local Home SDKを使えば、IoT家電側のサーバは不要なので(Firebaseは使いますが)、手軽なのも良いですね。
また何か作ったら、ブログに上げていこうと思います。