2019年3月14日木曜日

【SESAME・SESAME mini】 GAS から Web API を利用する。(サンプル)

【作成日:2019/3/14】
【更新日:2020/12/20】Google Apps Scriptの新エディタに関する補足を追加。
【更新日:2020/6/16】SESAME3(Sesame OS2)のAPIに対応したバージョンへの案内を追加。

【追記】
※GASがV8対応する前に作ったものですが、動くので更新はしてません。
※SESAME3ではAPIが刷新されたので、この方法は使用できません。新しい方法はこちら


活用アイデアに続いて、具体的なコードの書き方。(活用アイデアはこちら
とりあえず、複数台同時操作とトグルに対応するサンプルを作ってみた。

【事前準備】

  1. セサミアプリでクラウド連携をオン。
  2. 公式の CANDY HOUSE Dashboard でAPIキーとデバイスIDを確認。

【スクリプトの新規作成】

  1. Googleドライブを開いて、新規→その他→Google Apps Script
  2. 適当に名前をつける。

【スクリプトプロパティを保存】


必要なプロパティの名前と値をセットする。


旧エディタの方法

  1. 「ファイル」→「プロジェクトのプロパティ」→「スクリプトのプロパティ」
  2. 画像のように、事前に取得したAPIキーやデバイスIDを記入して保存


    【各説明】
    APIKey:APIキー
    myKey:自前の認証キー ※
    玄関:セサミのデバイスID
    玄関カウント:0
    以下、複数台ある場合の例
    玄関2:セサミのデバイスID
    玄関2カウント:0
    倉庫:セサミのデバイスID
    倉庫カウント:0
※認証キーは第三者のアクセスをはじく簡易セキュリティ対策。任意の長い文字列を記入する。(エスケープが必要な記号は使用しないほうが運用しやすい) 
プロパティ名(左側)は、太字の部分は任意の名前でOK。
例:「玄関」「玄関カウント」→「SESAME1」「SESAME1カウント」

 

新旧を問わない方法

  1. エディタに以下の臨時コードを記入。(*****部分は差し替える)

    //ここから
function setProperties() { var property = PropertiesService.getScriptProperties(); var props = { "APIKey":"******************", // APIキー "myKey":"******************", // 自前の認証キー ※第三者のアクセスをはじく簡易セキュリティ。任意の長い文字列を記入 "玄関":"************", // セサミのデバイスID "玄関カウント":"0", //↓複数台ある場合はさらに //"玄関2":"************", // セサミ2のデバイスID //"玄関2カウント":"0", }; property.setProperties(props); } //ここまで
※認証キーには、エスケープが必要な記号は使用しないほうが運用しやすい。

※プロパティ名(左側)の太字の部分は任意の名前でOK。

例:
「玄関」「玄関カウント」→「SESAME1」「SESAME1カウント」
 
  • 保存。

  • メニューで「setProperties」を選択し、実行ボタンを押す。
  • 済んだらこのコードは消してOK

  • 【本コード(サンプル)を記入】


    //ここから
    var property = PropertiesService.getScriptProperties();
    var myKey = property.getProperty('myKey');
    var head = {'Authorization': property.getProperty('APIKey')};
    var endpoint = 'https://api.candyhouse.co/public/sesame/';

    function test() {
      main(myKey,"玄関","2"); //※プロパティ名を変更している場合は「玄関」の部分を書き換え
    }

    function doPost(e) {
      var p = JSON.parse(e.postData.contents);
      main(p.myKey,p.deviceName,p.command);
    }

    function doGet(e) {
      var p = e.parameter;
      main(p.myKey,p.deviceName,p.command);
    }

    //ロック状態取得 true/false
    function isLocked(deviceName) {
      var deviceID = property.getProperty(deviceName);
      var url = endpoint + deviceID;
      var options = {
        headers: head,
        method: 'GET'
      };
      var res = UrlFetchApp.fetch(url, options).getContentText();
      return JSON.parse(res).locked;
    }

    function main(key,deviceName,command) { //command:0=解錠,1=施錠,2=状態チェック型のトグル,3=回数型のトグル
      if(key != myKey)
        return;

      var devices = deviceName.split(',');
      var c = parseInt(command);
      if(c == 2)
        c = 1 - isLocked(devices[0]);//trueは1、falseは0とみなされる
      if(c == 3) {
        var target = devices[0] + 'カウント';
        var count = parseInt(property.getProperty(target)) + 1;
        c = count % 2;
        property.setProperty(target, count);
      }

      var control = ['unlock','lock'][c];
      devices.forEach(function(name) {
        var deviceID = property.getProperty(name);
        var url = endpoint + deviceID;
        var options = {
          headers: head,
          method: 'POST',
          contentType: 'application/json',
          payload: JSON.stringify({'command':control})
        };
        UrlFetchApp.fetch(url, options);
      });
    }

    //ここまで

    【補足説明】
    状態チェック型のトグルとは
    サーバーに現在のセサミの状態を問い合わせ、その逆の動作をさせる。ロック中ならアンロック、アンロック中ならロックという形。

    回数型のトグルとは
    実行ごとに動作が入れ替わる。(奇数回目ならロック、偶数回目ならアンロック)

    【回数型を設置した理由】
    手動の操作がサーバーに反映されるのが即時でないため、例えば出かける際、手動で開けて外に出てすぐ状態チェックすると、ロック中と認識され解錠処理してしまう可能性がある。こうした利用が見込まれる場合、回数型の方が適している場合がある。

    myKeyについて
    もちろんAPIキーを逐一送信する方法もあるけれど、個人的に端末にAPIキーを保存したくないので、代わりに独自の認証キーを使用している。

    【動作チェック】

    1. まず保存。 


    2. メニューで「test」を選択し、実行ボタンを押す。

      成功したら、SESAMEが動く。



    成功したら、

    【ウェブアプリ化】

    旧エディタの場合
    1. 「公開」→「ウェブアプリケーションとして導入」
    2. プロジェクトバージョンがNew(新規作成)になっていることを確認
    3. アクセスできるユーザーを「全員(匿名ユーザーを含む)」に。
    4. 更新を押して、「承認が必要です」と出た場合は流れに従って許可。
    5. 最後にURLをコピーしておく。
    新エディタの場合
    1. 「デプロイ」 → 「新しいデプロイ」
    2. 「種類の選択」で「ウェブアプリ」を選択
    3. アクセスできるユーザーを「全員」にして「デプロイ」実行
    4. 「承認が必要です」と出た場合は流れに従って許可。
    5. 最後にURLをコピーしておく。



    【運用】

    あとは、このURLに適切にリクエストを送れば実行される。
    使用例としては、
    • ラズパイやPCから
    • IFTTTのアクション(Webhooks)から
    • Androidのマクロアプリから(MacroDroid、Tasker系)
    • iOSのショートカット機能から
    など。

    POSTの Content-Type は、application/json 

    基本的にはPOSTメソッドで。ただ、MacroDroidからはGETしか送れない*ため、doGetとdoPost両方用意している。
    必要なパラメータは、myKey、deviceName、commandの3つ。

    -追記-
    * MacroDroidでも「シェルスクリプト」アクションでcurlを実行することでPOSTを使用できた。
    例:curl https://~~~ -X POST -H 'Content-Type: application/json' -d '{"myKey": "***","deviceName": "玄関","command": "1"}'


    • myKeyがスクリプトプロパティの値と一致しないと、はじかれる。

    • deviceNameは、プロパティ名として登録した名前。サンプルでは「玄関」
      複数台同時に動かしたい場合は、カンマ区切りで「玄関,玄関2,倉庫」のように。
    • commandは数字で、0~3のいずれか
      0=解錠
      1=施錠
      2=状態チェック型のトグル
      3=回数型のトグル
     
    (複数台のトグルコマンドのときは先頭のデバイスが判断基準になる)

    なお、エラー処理は省いているため、成功しても失敗しても特にわからない。セサミが実際に動けば成功。

    (注意・免責)リスクを管理し、あくまで自己責任で使用してください。

    【スクリプト更新時の注意点】

    スクリプトの編集をウェブアプリに反映させるには再デプロイ(「デプロイを管理」からの更新)が必要。

    7 件のコメント:

    1. https://api.candyhouse.co/public/sesame/sesame のリクエストに失敗しました(エラー: 400)。サーバー応答の一部: {"error": "BAD_PARAMS"}(応答の全文を見るには muteHttpExceptions オプションを使用してください)(行 28、ファイル「コード」)

      上記のようなエラーが発生します。
      セサミIDを足してもダメでした。
      解決方法はありますでしょうか。

      返信削除
      返信
      1. こんにちは。
        この情報だけで明確な原因はわかりかねるのですが、アプリ側でクラウド連携の許可はされていますか?
        「セサミの管理」から「クラウド」をONにします。

        削除
    2. こんにちは。
      sesame miniを使用しています。
      コードをPC上で実行すると
      解錠←→施錠はうまくいくのですが、
      ウェブアプリにしてMACRODROIDにて実行しようとしてもうまくいきません。
      《下記のような設定をしています》
       【アクション】
         [Webサイトを開く/HTTP GET]で
         https://script.google.com/~/exec
         □エンコード・パラメータURL
         ■HTTP GET (Webブラウザ不使用)
         □完了するまで次のアクションをブロック
         □「成功」を真偽変数に代入する
         □HTTPレスポンスを文字列変数に代入
       【ローカル変数】
        (整)command 値:2
        (文)deviceName 値:玄関
        (文)myKey 値:任意のパスワード

      御手隙の時間にでも教えていただけないでしょうか?

      返信削除
    3. こんにちは
      MacrodroidでのHTTP GETでの指定方法を教えていただけないでしょうか。
      《Webサイトを開く/HTTP GET》の設定で
      [エンコード・パラメータURL]をチェックして、
      https://script.google.com/~/exec?myKey=●&deviceName=▲&command=■
      であれば上手くいくのですが、
      [HTTP GET(Webブラウザ不使用)]のチェックだと反応がありません。

      返信削除
      返信
      1. こんにちは、返信が遅くなりすいません。
        「エンコード・パラメータURL」と「HTTP GET (Webブラウザ不使用)」、両方にチェックを入れた場合どうでしょうか?私はそのようにして問題なく動作しています。

        ちなみにURLも変数に入れていて、以下のようにしています。関係ないと思いますが、もしうまくいかないようでしたらこちらの方法も試してみてください。

        URL欄:[v=URL][v=Key][v=URL2]2

        変数
        URL 値:https://script.google.com/~/exec?myKey=
        Key 値:********
        URL2 値:&deviceName=SESAME&command=

        削除
    4. 回答ありがとうございます。
      変数を使用する方法を試してみました。
      やはり…
       ■エンコード・パラメータURL
       □HTTP GET(Webブラウザ不使用)
      では上手くいきますが、
       ■エンコード・パラメータURL
       ■HTTP GET(Webブラウザ不使用)
      では反応しません。
       □エンコード・パラメータURL
       ■HTTP GET(Webブラウザ不使用)
      でも反応しませんでした。
      エンコード・パラメータURLを使用する方法は、
      かなり反応が良いので残念です。
      おそらく、自分の環境に何か原因があるのだと思います。

      現在、TaskerとHTTP ShortcutでWeb APIにアクセスする方法で
      運用しています。この方法は割と安定しているので、しばらく
      この方法で運用したいと思います。
      ありがとうございました。

      返信削除
      返信
      1. んー、そうなんですね。こちらではうまくいくのでちょっとわかりませんね。

        そうですね、好きな経路で自由に利用できるのがAPIのいいところですし、それぞれの環境で最適な方法を使ってもらえればと思います。

        削除

    【SESAMEサイクル(SESAMEシリーズ)】 NFCタグをエミュレートするアプリをつくった (Android)

    セサミのNFCタグ機能を実際のタグなしで自由に発動させたくて、アプリを作った話。 ( ページ下部より APKダウンロードできます )  【まえがき】 うちでは玄関のセサミ3は前回記事で書いたGASで運用していてNFCタグ機能は使っていないのだけど、最近セサミサイクル(ママチャリ)...