2018年12月24日
Google smart homeで家電操作 - その4
○起動手順
今回、私が実際に変更した内容を記載します。
その前に今回のサンプルコードの起動手順は以下の通りとなっています。
1) Raspberry Piからサンプルアプリを起動
2) GoogleサイトでAction on consoleからアプリの起動。
3) https://your subdoamin.ngrok.ioにアクセスして使用するデバイスを選択。
4) iPhoneでGoogle Assistantを開き、Smart homeを選択してデバイスを追加。
5) Google Assistantで登録されたデバイスの部屋の割り当て、ルーティンの設定。
特に何かサンプルコードに不備があった場合など、何度も上記の手順が必要となり、かなり面倒です。
私の場合、どうしているかというと・・・
2) に関しては固定のsubdomainを使用することで、一度、起動しておけば、再度走らせる必要はなくなります。
3) に関しては、既にHomeKitで使用しているデバイスを登録するだけなので、サンプルアプリ側で起動時に登録するようにコードを変更します。
4)、5)に関しては、サンプルコードを変更して、4)で登録されたユーザー・デバイス情報をサンプルコードを再起動させても保持させるので、4)、5)の手順は二度目から必要なくなります。
ただし、デバイスリストの変更やデバイスIDの変更が必要になった場合は必要となります。
○変更内容
(1) デバイス情報・ユーザ情報を保持するように変更
Googleのサンプルコードは再起動をかけるとdatasotre.jsにあるAuth, Data(デバイス情報・ユーザー情報)の配列が全て初期化されます。
そうすると、4)、5)のデバイス登録作業が必要になるので、ここではその配列を一旦ファイルに書き込み、起動時に再読込するように変更しました。
smart-home-provider/cloud/datastore.jsを以下のように変更
ファイルの先頭に以下を挿入
以下の関数の最後にfs.writeFileを追加
以下の関数の最終行にfs.writeFileを追加
ファイルの最終行に以下のコードを追加
(2) デバイスを登録
サンプルアプリを起動し、Google AssistantをiPhoneで開くとGoogleのAssistant ServiceからSYNC Intentが発行され、Raspi側からはDeviceリストを渡します。この辺りはGoogleのSmart Homeのページに記載されています。
このタイミングでデバイスを登録するようにします。
ちなみに、GoogleのAssistant Service, HomeGraphから飛んでくるコマンドは、大きく分けて以下の3種類なので、それに対応して何かアクションを起こすように変更を加えていきます。
smart-home-provider/smart-home-app.jsに以下の箇所を変更。
ファイルの先頭に以下のように独自モジュールを追加します。
私の場合、既に家電がコントロールできるようになっているので、そこのI/Fだけを合わせるモジュールを作成し、追加しました。
SYNCコマンドに以下を追加。
QUERYでは、deviceの最新stateを返すように以下のコードを追加。
EXECが呼ばれたときに、ステータスのアップデートとコマンドを実行するように以下を変更。
また、参考までに私が独自で作成したモジュール smart-home-device.jsを置いておきます。
変更内容は以上ですが、内容を見ていただければなんとなくイメージがつかめるのではないかと思います。
○参考までに
このGoogleのサンプルコードですが、凄腕達が認証をGoogleに任せたり、Firebaseを使ったりと進化させているのを見つけました。
・解説ページ
・Githubのリポジトリ
私が調べた中で一番進んでいるのではないかと思いました。
今回、私が実際に変更した内容を記載します。
その前に今回のサンプルコードの起動手順は以下の通りとなっています。
1) Raspberry Piからサンプルアプリを起動
2) GoogleサイトでAction on consoleからアプリの起動。
3) https://your subdoamin.ngrok.ioにアクセスして使用するデバイスを選択。
4) iPhoneでGoogle Assistantを開き、Smart homeを選択してデバイスを追加。
5) Google Assistantで登録されたデバイスの部屋の割り当て、ルーティンの設定。
特に何かサンプルコードに不備があった場合など、何度も上記の手順が必要となり、かなり面倒です。
私の場合、どうしているかというと・・・
2) に関しては固定のsubdomainを使用することで、一度、起動しておけば、再度走らせる必要はなくなります。
3) に関しては、既にHomeKitで使用しているデバイスを登録するだけなので、サンプルアプリ側で起動時に登録するようにコードを変更します。
4)、5)に関しては、サンプルコードを変更して、4)で登録されたユーザー・デバイス情報をサンプルコードを再起動させても保持させるので、4)、5)の手順は二度目から必要なくなります。
ただし、デバイスリストの変更やデバイスIDの変更が必要になった場合は必要となります。
○変更内容
(1) デバイス情報・ユーザ情報を保持するように変更
Googleのサンプルコードは再起動をかけるとdatasotre.jsにあるAuth, Data(デバイス情報・ユーザー情報)の配列が全て初期化されます。
そうすると、4)、5)のデバイス登録作業が必要になるので、ここではその配列を一旦ファイルに書き込み、起動時に再読込するように変更しました。
smart-home-provider/cloud/datastore.jsを以下のように変更
ファイルの先頭に以下を挿入
const fs=require('fs');
const file_Data="Data.json";
const file_Auth="Auth.json";
以下の関数の最後にfs.writeFileを追加
Data.registerUser = function(uid, authToken) {
.....
.....
// ===
fs.writeFile(file_Auth, JSON.stringify(Auth));
// ===
};
以下の関数の最終行にfs.writeFileを追加
Data.registerDevice = function(uid, device) {
......
// ===
fs.writeFile(file_Data, JSON.stringify(Data));
// ===
};
ファイルの最終行に以下のコードを追加
// ===
try {
fs.statSync(file_Data);
console.log('file=', file_Data, ' exists');
let tempData=JSON.parse(fs.readFileSync(file_Data, 'utf8'));
for(let uid in tempData){
Data[uid] = {};
for(let devId in tempData[uid]){
tempData[uid][devId].id=devId;
Data.execDevice(uid,tempData[uid][devId]);
}
}
console.log('Data=',JSON.stringify(Data));
fs.statSync(file_Auth);
console.log('file=', file_Auth, ' exists');
let tempAuth=JSON.parse(fs.readFileSync(file_Auth, 'utf8'));
for(let uid in tempAuth){
Auth[uid]=tempAuth[uid];
}
console.log('Auth=',JSON.stringify(Auth));
} catch (error) {
if (error.code === 'ENOENT') {
console.log('file=', file_Data, ' does not exist');
} else {
console.log(error);
}
}
// ===
(2) デバイスを登録
サンプルアプリを起動し、Google AssistantをiPhoneで開くとGoogleのAssistant ServiceからSYNC Intentが発行され、Raspi側からはDeviceリストを渡します。この辺りはGoogleのSmart Homeのページに記載されています。
このタイミングでデバイスを登録するようにします。
ちなみに、GoogleのAssistant Service, HomeGraphから飛んでくるコマンドは、大きく分けて以下の3種類なので、それに対応して何かアクションを起こすように変更を加えていきます。
- SYNC: デバイス登録時
- QUERY: デイバスのステータスチェック
- EXEC: コマンド実行時
smart-home-provider/smart-home-app.jsに以下の箇所を変更。
ファイルの先頭に以下のように独自モジュールを追加します。
私の場合、既に家電がコントロールできるようになっているので、そこのI/Fだけを合わせるモジュールを作成し、追加しました。
const deviceProvider=require('./device/smart-home-device.js');
SYNCコマンドに以下を追加。
app.post('/smarthome', function(request, response) {
...
switch (intent) {
case 'action.devices.SYNC':
console.log('post /smarthome SYNC');
// ====
datastore.registerUser(uid, authToken);
var deviceList=deviceProvider.getDeviceList();
for(let j=0; j < deviceList.length; j++) {
datastore.registerDevice(uid,deviceList[j]);
}
// ===
...
}
}
QUERYでは、deviceの最新stateを返すように以下のコードを追加。
function query(data, response) {
console.log('query', JSON.stringify(data));
let deviceIds = getDeviceIds(data.devices);
// ===
{
let updateDevs=app.smartHomeQuery(data.uid, deviceIds);
// console.log('devices=', JSON.stringify(updateDevs));
for(let i in updateDevs){
deviceProvider.updateState(updateDevs[i]);
}
}
// ===
...
EXECが呼ばれたときに、ステータスのアップデートとコマンドを実行するように以下を変更。
function execDevice(uid, command, device) {
...
...
let deviceCommand = {
type: 'change',
name: null,
state: {},
};
// ===
deviceProvider.executeCommand(command,execDevice[curDevice.id]);
// ===
また、参考までに私が独自で作成したモジュール smart-home-device.jsを置いておきます。
変更内容は以上ですが、内容を見ていただければなんとなくイメージがつかめるのではないかと思います。
○参考までに
このGoogleのサンプルコードですが、凄腕達が認証をGoogleに任せたり、Firebaseを使ったりと進化させているのを見つけました。
・解説ページ
・Githubのリポジトリ
私が調べた中で一番進んでいるのではないかと思いました。
【Google smart homeの最新記事】
この記事へのコメント