Riiiverの構成/環境

Riiiverは以下の構成物から成り立っています:

  • SDK – 皆さんの自作Appの中で動作するiOSおよびAndroid用のSDK
  • Service Proxy – Webサービス(REST APIなど)を利用したPieceを動作する際に利用するロジック機能。
  • 皆さんの Device と App – Riiiver対応しようとしている皆さんのデバイスとアプリ!
  • iiidea Store – 様々なユーザーが作ったiiideaをダウンロードできるストア機能。
  • iiidea/Piece – 様々なユーザーや開発者が作成したiiideaやPiece。
  • User認証機能 – ユーザーを認証するための機能群。
  • Piece Uploader – 作成したPieceをRiiiverに登録して、実行できるようにするツール。
  • Riiiver App – iOS/Androidのアプリ。iiideaの利用や、iiideaの作成ができる。

まず最初に最初の3つ、SDK、Service Proxy、皆さんのDeviceとAppの構成について見ていきましょう。

もし皆さんが既にIoTデバイスを持っている場合、きっと自前のデバイスとアプリを持っていると思います。おそらくは下図の様に「デバイス – アプリ – クラウド」の構成になっているでしょう。

みなさんのシステム

このような構成が既にある場合、皆さんのデバイスをRiiiver対応にしたいときは、Riiiver SDKを利用します。(iOS用とAndroid用を準備しています) SDKは皆さんの自前のAppの中にバンドルして利用します。 IoTデバイスはなく、アプリだけの場合もIoTデバイスが無いだけで同じ様な構成になるはずです。
また、皆さんがサービスプロバイダーの場合はクラウド側の機能を提供していることになります。↑の例のようなクライアントも存在することでしょう。

Riiiverと接続したシステム

SDKはiiideaを実際に動作させる実行環境として動作します。任意のiiideaを実行可能です。SDKを実行すると、T, S, Aの順に処理を開始し、皆さんのアプリから見るとiiideaの動作はブラックボックスとして扱うことができます。

SDKはiiideaに含まれるPieceを分析して順次実行していきますが、ここでPieceの実態について触れておきます。 Pieceの実態はJSON Schemaで記述されたテキストファイルです。

Pieceの実態

PieceについてJSON Schemaで以下のような内容が記述されています。(一部の重要な要素のみ列挙します)

  • input – 前のPieceから情報を受け付けるこのPieceの「入力」の型や範囲
  • output – このPieceが「出力」する型や範囲
  • executor – このPieceを実際に動作させる実態 = Piece Core

Pieceは前のPieceから情報を受け取ることができ、次のPieceに情報を渡すことができます。Piece間で受け渡す情報の解釈の方法がJSONでinput/outputとして記述されています。例えば、明日の降水確率を時計の針で示したい場合にはどの様にすれば実現できるでしょう?「明日の降水確率 = S Piece」「時計の秒針 = A Piece」とすると、以下の様に考えることができます。

降水確率は百分率、時計の秒針は0-59秒を示すことが出来る表示装置、そのままではうまく繋がりません。時計をより汎用的な情報を表示できるようにするには「百分率を表示することができる秒針」というPieceでまとめた方が良さそうです。
この例で分かる通り、あるコンテンツとコンテンツ(この例では、降水確率と時計の針)を接続するには情報の意味合いを揃える必要があります。RiiiverではPieceとPieceを正しく接続することを「Wiring (ワイヤリング)」と呼びます。
皆さんがPieceをRiiiverに提供しても、ワイヤリングできるPieceとできないPieceがあるということです。ここでワイヤリングのルールについて見てみましょう。

Wiring Rule

Piece AからPiece Bに接続しようとするとき、以下の内容について確認します:

  • Piece AのoutputとPiece Bのinputの型( type )が一致している

JSONの定義ファイルの中で、各Pieceはinput, outputについてtypeが定義されています。↓のような感じです。

{
  "title" : {
    "ja" : "Piece Aのサンプル",
    "en" : "Sample Piece A"
  },
  "version" : "0.0.1",
  "sdkVersion" : "0.0.1",
  "vendorId" : "CITIEN WATCH CO.,LTD",
  ...... 
  "output": {
    "type": "object",
    "properties": {
      "OutputOfThisPiece": {
        "type": "number",
        "minimum": 0,
        "maximum": 100
      }
    }
  },
  ......
}

outputのキーには”type”: “object”, “properties”:{ … } の様に2つの要素が格納されています。この部分はoutputがあるPieceでは共通して定義されている部分です。重要なのはoutputの型を定義している”properties”の中身です。Propertiesの中に実際の出力の型が定義されています。↑の例ですと、OutputOfThisPieceがこのPieceの出力値で、typeはnumber、取り得る値もminimum/maxmumというキーで設定されています。JSONの定義を読み取ることで、このPieceは「0-100の間の数値」を出力することが分かります。数値をinputできるPieceはこのPieceのoutputを受け付けることが出来る、つまりワイヤリング可能ということです。
number以外のtypeももちろんあります。

Riiiver で扱うtype

type名 type 説明
number 0, 1 ,198, 300.8などの数字
文字列 string 「こんにちは」などの文字列
論理値 boolean true, falseの論理値
列挙型文字列 enum 「晴れ, 曇, 雨」の様に
予め決められた文字列を定義
オブジェクト object 独自のフォーマットを定義可能な型

↑にRiiiverで扱うtypeを記述しました。ちょっと目を通してみて雰囲気を感じてみて下さい! もうなんとなくワイヤリングについては分かりますよね、number→numberはワイヤリングできますが、number→stringはワイヤリング出来ない、ということです。
通常のJSON schemaでは数値のtypeとしてinteger、numberなど複数種類が存在しますが、Riiiverでは単純化のため、数値はnumberのみを扱うことに注意してください。
では、次のワイヤリングルールも見てみましょう!

  • formatが一致している
    • Piece Aのoutputにformatがあり、かつ、Piece Bのinputにformatがない場合は接続する
    • Piece Aのoutputにformatがなく、かつ、Piece Bのinputにformatがある場合は接続しない

String Typeには文字列でも規程通りに定義する事で、特定の情報を表す様な利用方法が出来ます。以下の例を見てください。

データの意味 type 詳細指定 (format)
日時 string format: date-time 2019-06-13T20:20:39+09:00
日付 string format: date 2019-05-30
時刻 string format: time 14:35:57
Email Address string format: email someone@riiiver.com

上の例はどれも文字列の型なのですが、文字列の使い方をformatで定めています。Pieceのoutputとinputにformatの指定がある場合は先に記述したルールでワイヤリングします。もう一つ、ワイヤリングのルールを紹介させてください。

  • x-unitが一致している
    • outputにx-unitがあり、かつ、inputにx-unitがない場合は接続
    • outputにx-unitがなく、かつ、inputにx-unitがある場合は接続しない

string typeのformatと同じ様に数字を表すnumberにもx-unitという詳細な型を宣言する仕組みがあります。

数値で表現する対象 x-unit type
速度 mm/s number
角度 degree number
長さ/距離 m number
質量 kg number
歩数 step number

上の様に同じ数字でも、何を表すかは数字だけでは分かりませんが、x-unitを用いる事でその数値の意味がわかり、Pieceが受け付ける/出力する情報がより詳細に理解できます。x-unitが存在している場合のワイヤリングは先に記述したルールで行います。

他にも詳細なワイヤリングルールが存在します。興味がある方はここをご覧ください!
PieceでのJSON表現、ワイヤリングについてはこの辺りで十分のはずです。そろそろ元のSDKの動作の話に戻りましょう。

PieceとPiece Core

「Riiiver SDKはiiideaを実行を皆さんのアプリに代わって行う」、「Pieceの実体はJSON表現のテキストファイル」という説明をしましたが、ロジック部分となるプログラムの本体は一体どこにあって、実際どうやって動いているのでしょう?
各Pieceのロジック部分でiiidea実行中に対処のPiece順番になった時、実際に「実行」されている部分をPiece Coreと呼びます。皆さんのアプリ、Riiiver SDK、Piece、そしてPiece Coreの関係を↓に示します。

PieceとPiece Coreの関係

上の例では、皆さんのIoTデバイスがトリガーを放ち、Action Pieceとしての動作を表示する例を示しています。この場合、Piece Coreは皆さんのアプリ内で準備するもので、Piece CoreはiOSの場合はSwift, Androidの場合はjavaで記述したものになります。今までの説明の中でPieceはJSONで記述されているとありましたが、正確にここで定義し直すと、Pieceは以下の構成になっています。

  • Piece
    • Piece JSON – JSON Schemaで記述された定義ファイル
    • Piece Core – Pieceの実際のロジックを担う機能部分

一言でPieceというと上の図では黄色い破線で囲まれた領域を指し、中にPiece JSON、Piece Coreが含まれます。
実際にSDKがiiideaを動かす時、対象となるPiece Coreを実行するために、Piece JSONには対応するPiece Coreを定義するキーワードが準備されています。↓のサンプル[ここから実際のJSONサンプルをリンク]を見て下さい。

{
    "title": {
        "en": "Button Pressed Trigger",
        "ja": "ボタン押し"
    },
    "version": "1.0.0",
    "sdkVersion": "1.0.0",
    "deviceId": "Riiiver iOS",
    "vendorId": "CITIZEN",
    "description": {
        "en": "Description of Button Pressed Trigger",
        "ja": "ボタンが押されたらiiidea動作開始。"
    },
    "preferences": {
        "type": "object",
        "properties": {}
    },
    "blockType": "trigger",
    "executor": "ButtonPressTriggerPieceExecutor",
    "toolId": "Riiiver_Tutorial",
    "categoryIds": [
        "cat_3001",
        "cat_3012"
    ]
}

対象となるPiece Coreを定義している場所、見つかりましたか?そうです、”executor”がPiece Coreを指し示しています。↑のT Pieceの例ではButtonPressTriggerPieceExecutorが実際のPiece Coreだと定義しています。ButtonPressTriggerPieceExecutornは皆さんがiOS/Androidアプリのネイティブコードで記述したモジュールに当たります。


ここまではSDK上のPiece Coreの説明でしたが、みなさんがサービス・プロバイダである場合、S Pieceを以下の様に作ることができます。

皆さんのサービスをRiiiverに接続したシステム

↑の図は、みなさんがサービスをRESTで提供していると過程しています。図のS Pieceの場合、Piece Coreは皆さんのサービスを利用する小さなRESTクライアントに相当します。Piece JSONは今までの説明と同じく、iiidea内にJSONで記述されています。JSONが指すPiece CoreはRiiiverがAWSサーバ上に用意しているLambda上に配備されます。配備の方法、実装内容等は後ほど説明します。
ここで理解して頂きたいのは、「すべてのiiideaはスマホ上のSDKにて実行を管理される」ということです。みなさんがRiiiver上にS Pieceを提供した場合でも、必ずユーザーのスマホ上のSDKが処理を開始することで、皆さんのサービスが利用されます。S Pieceを作成する皆さんはSDK上の動きを気にすることありません。SDKはブラックボックスとして扱えますし、存在を意識する必要もありません。皆さんの準備したS Piece Core (node.jsで動くJavascriptなど)は必要に応じて実行され、決められた関数からスタートされます。(この関数から皆さんのREST APIを使う処理を書くだけです。ただし、あなたのPieceの入出力を定義するためにPiece JSONは必要です。)
では、実際に実装してみてPieceの動きを理解しましょう。

実際のCore Pieceの動き

皆さんが自前のアプリ持っていて、アプリやIoTデバイスうRiiiverに接続する場合と、皆さんはサービス・プロバイダで、皆さんのサービス(REST API等)をRiiiverにつなげる場合の二通りの説明をしたいと思います。

自前のサービスをRiiiverに接続する

例として、あなたが天気予報のコンテンツを持つプロバイダーだとします。そして、以下のようなInput/Outputを持つS Pieceを提供するとしましょう:

まず最初に考えるべきことは、ワイヤリングです。あなたのPieceは前のPieceからどんな情報を得て、どんな情報を出力するようにしますか?↑の例では、位置情報を入力として受け付け、出力として現在の気温を出しています。
これを実装・配備してみましょう。

Riiiver Proxy Server

図「 皆さんのサービスをRiiiverに接続したシステム 」にあるRiiiver Proxy Server上にAmazonさんのLambdaを用意しています。皆さんは我々の準備したLambda上にscriptを組むことで↑のPieceのPiece Coreを作成することができます。先の例でワイヤリングに必要なinput/outputの情報は定義しました。今度はPiece Coreを指定するJSONを記述してみましょう。
Riiiver Proxy Server上で動作する時に利用するPiece CoreはSDK内に存在する「RBCCommonWebServiceExecutor」というものを指定して下さい。皆さんの記述するLambda上のscriptをうまく実行してくれる仕組みがここには実装されています。このExecutorはJSONの中に定義されている「serviceProxy」キーで指定している文字列を皆さんがLambda上で実装しているscriptの中から実行すべき関数として探します。つまり、S Pieceの実行時にスマホ端末のSDK内のRBCCommonWebServiceExecutorが実行され、Exceutorは対応するLambda上のscriptの中から「serviceProxy」で指定した関数/パッケージを動的に実行します。
今考えている天気Pieceを最低限のPiece JSONとして完成させてみましょう。↓のような感じになります。ここのJSONファイルを参照して下さい。

{
    "title": {
        "ja": "指定された位置の気温(摂氏)を取得!",
        "en": "Get the temperature of the inputted position."
    },
    "version": "0.0.1",
    "sdkVersion": "0.0.1",
    "vendorId": "none",
    "deviceId": "none",
    "description": {
        "ja": "指定された位置(緯度、経度)の現在気温を取得します。",
        "en": "This piece gets the temperature of the target place."
    },
    "blockType": "service",
    "executor": "RBCCommonWebServiceExecutor",
    "preferences": {},
    "input": {
        "type": "object",
        "properties": {
            "latitude": {
                "type": "number",
                "x-title": {
                    "ja": "緯度",
                    "en": "latitude"
                },
                "x-description": {
                    "ja": "気温を知りたい場所の緯度",
                    "en": "The latitude of the target place where you would like to know the temperature."
                }
            },
            "longitude": {
                "type": "number",
                "x-title": {
                    "ja": "経度",
                    "en": "longitude"
                },
                "x-description": {
                    "ja": "気温を知りたい場所の経度",
                    "en": "The longitude of the target place where you would like to know the temperature."
                }
            }
        }
    },
    "output": {
        "type": "object",
        "properties": {
            "celsiusTemperature": {
                "x-title": {
                    "ja": "現在の気温(摂氏)",
                    "en": "Current Temperature (Celsius)"
                },
                "type": "number"
            }
        }
    },
    "serviceProxy": {
        "service": "getCurrentTemperatureFunction"
    },
    "categoryIds": [
        "cat_0006"
    ],
    "toolId": "serviceProxy"
}

Piece JSONに必要なキー項目と記述すべき内容は以下の通りです:

必須キー 説明
blockType “trigger”, “service”, “action”のどのtypeのPieceかを記述します。
block=pieceと読み替えて下さい。(blockはpieceの旧称です!)
categoryIds みなさんが作るPieceが属するカテゴリを指定します。
ここにRiiiverで用意したカテゴリリストがあります。
description 皆さんが作るPieceの説明です。
一般ユーザーが皆さんのPieceを利用する際に、何が出来るのかをここで確認します。
マルチリンガル対応です。↑のJSONの例のように言語別に指定することが出来ます。
deviceId このPieceを実行するデバイスのIDを記述します。
executor このPieceのPiece Coreを指定します。
Riiiver Proxy Serverを利用する場合、ここの値は「RBCCommonWebServiceExecutor 」となります。
sdkVersion 必要なSDKのVersionを記述します。
まずは1.0で問題ないはずです。
title 皆さんが作るPieceの名称です。
マルチリンガル対応です。タイトルの長さは24バイトまでです。(TODO)
toolId 工事中…
vendorId Riiiver SDKを利用したPieceを公開するにはVendor申請が必要です。
申請後に発行されるIDを入力して下さい。
version 皆さんが作るPieceのVersionです。アップデート時には新しい番号を振って下さい。

上記のJSONキーは必須項目です。必ず入力しましょう。一つでも抜けがあるとSDK内で実行時にエラーが返ります。上記には説明のないキーもいくつか載せています。じっくり確認してみて下さい。
中でもは「serviceProxy」キーはS Piece Coreを作る場合はとても大切です。先に説明したようにこのキーに記述している文字列をLambdaで実行する対象のパッケージ名と見立て動作します。また、「preference」キーも重要な役割りをします。(あとで詳細な説明をします)
↑のJSONサンプルでPieceの入出力やPiece Coreなどの定義が出来ました。

次は、皆さんのREST APIのクライアントとなる部分(=AWS上のLambdaで動作するNode.js)を実装しましょう。

AWS Lambdaの使い方

皆さんのREST APIのクライアントとなるS Pieceを作る場合、Piece CoreはAWS上のLambdaになります。(Riiiverでの利用方法の詳細な説明はここ(FixMe)にあります)
前のPieceからこれから作るPieceの順番に処理が移ったとき、以下の様に処理が走ります:

  1. 前のPieceの処理終了
  2. SDKは本Pieceの実行を試みる
  3. SDKは本Piece JSONを確認し、Piece Coreである「RBCCommonWebServiceExecutor」を実行する。
  4. RBCCommonWebServiceExecutor は 本Piece JSON内のserviceProxyキーで定義されたパッケージのLambda関数を実行する。

4の動作を実現するためにはLambda関数を準備し、Riiiver上に皆さんの作成したLambda関数うデプロイする必要があります。ここで実際に特定位置の現在の気温を返す機能をOpenWeatherMapを利用するRESTクライアントをLambda関数で作ってみましょう。OpenWeatherMapのREST APIで経度/緯度で指定した位置の現在の天気を入手するにはURIに以下を指定します:

https://api.openweathermap.org/data/2.5/weather?lat=35&lon=139&APPID=xxxxxxxxxxxxxxxxx

REST APIにわたす情報は以下の3つです:

  • lat:  緯度
  • lon: 経度
  • APPID: あなたのOpenWeahterMapのAPPID (API Key)

そうすることで以下のような情報が返ってきます:

{
    "coord": {
        "lon": -0.13,
        "lat": 51.51
    },
    "weather": [{
        "id": 501,
        "main": "Rain",
        "description": "moderate rain",
        "icon": "10d"
    }, {
        "id": 311,
        "main": "Drizzle",
        "description": "drizzle rain",
        "icon": "09d"
    }],
    "base": "stations",
    "main": {
        "temp": 287.22,
        "pressure": 1019,
        "humidity": 42,
        "temp_min": 283.15,
        "temp_max": 292.04
    },
    "visibility": 10000,
    "wind": {
        "speed": 5.7,
        "deg": 250
    },
    "clouds": {
        "all": 76
    },
    "dt": 1559132830,
    "sys": {
        "type": 1,
        "id": 1414,
        "message": 0.0081,
        "country": "GB",
        "sunrise": 1559101926,
        "sunset": 1559160240
    },
    "timezone": 3600,
    "id": 2643743,
    "name": "London",
    "cod": 200
}

↑実際は一行のテキストデータですが、見やすいように整形しています。得られたデータ内の”main”キー内に”temp”として気温が格納されています。単位はケルビン(K)の様です。

これらのやり取りを行うRESTクライアントをLambda上に実装してみましょう:

'use strict';
const requestPromise = require("request-promise");
exports.handler = async (event) => {
    console.log(`Request: ${JSON.stringify(event.properties)}`);
    const apiHost = "https://api.openweathermap.org";
    const apiPath = "/data/2.5/weather";
    const apikey = "98d281bbabc958615db63a1cxxxxxxxx";    // ←ここに皆さんのAPI Keyを入れる
    let response = {
        status: 200,
        body: {
            celsiusTemperature: 0.0
        }
    };

    const inputtedLatitude = event.properties.input.latitude;       // 前のPieceから受け取った緯度
    const inputtedLongitude = event.properties.input.longitude;     // 前のPieceから受け取った経度
    console.log("inputtedLatitude: " + inputtedLatitude + " inputtedLongitude: " + inputtedLongitude);

    const url = `${apiHost}${apiPath}?lat=${inputtedLatitude}&lon=${inputtedLongitude}&APPID=${apikey}`;
    console.log(url);
    
    try {
        const data = await requestPromise({
            method: "GET",
            uri: url
        });
        const json = JSON.parse(data);
        const tempK = json.main.temp;           // REST APIから得られたJSON内の温度を保存。単位はケルビン。
        response.body.celsiusTemperature = tempK - 273.15;  // 次のPieceに返す値は摂氏で数値(Number)
    } catch (error) {
        console.log("Error message: " + error.message);
        response.status = 400;
    } finally {
        console.log("Response: " + JSON.stringify(response));
        return response;
    }
};

↑のコード名はgetCurrentTemperature.jsしてLambda上に配備します。SDKはPiece JSONを確認しserviceProxy内のserviceキーの値を読み取り、その値と同じ.jsファイルを実行します。"service": "getCurrentTemperature"となっているので、getCurrentTemperature.js内のexports.handler =のhandlerが指し示す関数を↑の処理で言う4.で呼びだす仕組みになっています。(exports.handler部分の関数を呼び出すのはAWS Lambdaの仕組みです。)
ここで重要なのは↑のLambdaで実行するjavascriptのファイル名とPiece JSONのserviceProxyキー内のserviceキーの値を同じにすることです。そうすることでRiiiver内のたくさんのLambda関数の中から、あなたの関数が実行されます。皆さんが作るLambda関数/パッケージ名はRiiiver全体でユニークな必要があります。ファイル名はyourname_piecename.jsの様にしましょう。

REST APIで利用するHTTP周りの処理を簡単に行うために、 ↑のgetCurrentTemperature.js の例ではRequest-Promiseを利用しています。以下、動作について簡単に説明します:

  1. SDKからこのPieceが実行されPiece CoreであるLambda関数が呼ばます。(exports.handler = async (event)で指定している無名関数が実行される)
  2. URIの文字列を完成させるために、このPieceに入力されたデータを取得します。
    1. 入力データは引数evnet内に格納されており
      event.properties.input.xxxxxx

      の様に指定してアクセスすることが出来ます。

    2. このPieceはPiece JSONの20行目、33行目に定義している通りlatitude及び longitudeというキーで入力されますので以下の様に記述することでデータにアクセスできます:
      const inputtedLatitude = event.properties.input.latitude;
      const inputtedLongitude = event.properties.input.longitude;
  3. 入力された位置情報を利用してURIを完成させます。
  4. 作成したURIに対して実際にRequestを投げます。次のコードがそうです:
    const data = await requestPromise(
                                        {
                                            method: "GET",
                                            uri: `${apiHost}${apiPath}?lat=${inputtedLatitude}&lon=${inputtedLongitude}&APPID=${apikey}`
                                        }
                                     );

    Request-Promiseを利用することこの様に非常にシンプルな形でREST APIを実際に叩くことができます。
    await/async機能についてはここを参照して下さい。awaitを利用することでREST APIのレスポンスが返ってくるまで処理を待ち合わせてくれます。

  5. 得られたJSONデータ(現在の天気)の中から、気温になる部分を抽出します。const json = JSON.parse(data); const tempK = json.main.temp;
  6. 最後に次のPieceに出力するデータをJSONで返します。
    1. let response = {
          status: 200,
          body: {
              celsiusTemperature: 0.0
          }
      };

      上記のコードでLambda関数が返す戻り値を作っています。正常に値を次のPieceに返すにはstatusを200と設定し、bodyの値にPiece JSONで定義した型通りに結果を格納します。この例ではresponse.body.celsiusTemperature = tempK - 273.15;

      として得られた温度を摂氏に換算してからbodyに定義どおりのoutput値に出力されるようにしています。

どうでしょう?コード自体は簡単に読み解けたのではないでしょうか?続いて実際にRiiiver上のLambdaにデプロイする前にローカルでこのコードを実際に試してみましょう。

ローカル環境で作ったLambdaのコードを実行する

ローカル環境でコードを実際に実行してみます。AWS Serverless Application Model (AWS SAM)を利用することで、実際のLambdaの動作に近い環境上で実行することができます。ココを参考にしてみて下さい。ただし、Dockerのインストールが必要で、環境構築や環境の理解が必要になりますので、ここではコールLambdaが皆さんのコードを実行してくれる動作を再現するnode.jsのコードをローカルに書いて動かしてみましょう。

サーバ上で動作しているLambdaですが、ローカルでもある程度は実行できますし、デバッグにはもってこいです。ローカル環境で動作させるためにはLambda上に置くjavascript (↑の例で言うgetCurrentTemperature.js)の他に、

  • getCurrentTemperature.jsを呼び出してくれる呼び出し元(エントリポイント)
  • getCurrentTemperature.jsにわたす値

を準備する必要があります。これらをそれぞれl_dummy.jsとevent.jsonとして準備しましょう↓

  • l_dummy.js – Lambdaの動きをエミュレートして我々が準備したgetCurrentTemperature.jsをコールしてくれるもの。
  • event.json – 前のPieceからの情報を定義するファイル

各ファイルの役割は↓の様になります。

まず前のPieceから入力されるdataをevent.jsonとして作ります。この例では、前のPieceからは位置情報が渡されるので、以下のような情報が入っているとして入力データを準備しましょう:

{
    "latitude": 35.7285953,
    "longitude": 139.5309323
}

 

Lambdaの動きの代わりをするl_dummy.jsは↓の様なものです:

'use strict';
const target = require('./getCurrentTemperature.js');
var event = { "properties": {"input": {} } }; 
const input_event = require('./event.json');
event.properties.input = input_event;
console.log("event: " + event);

target.handler(event);

これで必要なファイルは整いました。実際にローカル環境にてnodeを実行してみましょう。その前にnodeをPC/Macにインストール必要があります。ココからお使いのOSに合ったnodeを入手してインストールして下さい。

実行するには↑で準備した下記3つのファイルを同じフォルダに置いて:

  • getCurrentTemperature.js
  • l_dummy.js
  • event.json

ターミナル(Windowsの場合はコマンドプロンプト)を開き、↑のフォルダ内まで移動して、次のようなコマンドでnodeを動かします → $ node l_dummy.js

うまく動作すると↓のような実行結果が出るはずです。

$ node l_dummy.js
event: [object Object]
Request: {"input":{"latitude":35.7285953,"longitude":139.5309323}}
inputtedLatitude: 35.7285953 inputtedLongitude: 139.5309323
https://api.openweathermap.org/data/2.5/weather?lat=35.7285953&lon=139.5309323&A
PPID=your_app_key_here
Response: {"status":200,"body":{"celsiusTemperature":19.470000000000027}}

前のPieceから入力された緯度経度情報をOpenweathermap.orgのAPIに入力してその地域の温度を入手し、次のPieceに温度を渡しています。ここで重要なのは、次のPieceへ渡る情報はgetCurrentTemperture.js内でLambdaから呼ばれる関数への戻り値であるresponseとなる値の内、”body”:タグで括られているJSONデータ{"celsiusTemperature":19.470000000000027}}であると言うことです。LambdaはこのJSONをそっくりそのままSDKの方に戻します。SDKは次のPieceにこのJSONデータを渡します。

ローカル環境で十分にデバッグして動作を確認しましょう。十分に確認が取れたら、次は実際にPieceをRiiiver上にアップロードしてみましょう!

Riiiver開発者登録する

作成したPieceをアップロードするにはRiiiverの開発者登録が必要になります。Lambdaを利用したService Pieceの作成・登録は無料で行えますので、安心して登録してください。登録はここ(工事中)からできます!

サインアップができましたら、登録したメールアドレスに確認メールが届きます。メール内のURLをクリックしてサインアップを完了してください。サインアップ完了後、↓に示す様なRiiiverの開発者管理サイト(=PieceBuilderと呼びます)にログインできる様になります。

PieceBuilderから、PieceのアップロードなどのPieceの管理をすることができます。
Pieceをアップロードするためには「piece一覧」メニューをクリックしてください。すると、次の様な画面が表示されます。

(鋭意執筆中です!)

自前のアプリ/IoTデバイスをRiiiverに接続する

import ERSDK

/// ボタン押しのトリガーブロック。
@objc(ButtonPressTriggerBlockExecutor)
class ButtonPressTriggerBlockExecutor: NSObject {
    weak var delegate: ERTriggerBlockDelegate?
    
    // トリガーを発火させる
    @objc private func startT() {
        Logger.debug(string: "[T] Trigger fire!!!")
        self.delegate?.didTrigger(outputJson: nil)
    }
}

extension ButtonPressTriggerBlockExecutor: ERTriggerBlockExecutor {
    func createTrigger(blockJsonString: String, preferenceJson: Dictionary<String, Any>, applet: ERApplet) -> Bool {
        Logger.debug(string: "[T] Trigger created.")
        // 必要な処理をここで記述。
        return true
    }
    
    func enable(delegate: ERTriggerBlockDelegate) -> Bool {
        Logger.debug(string: "[T] Trigger enabled (ACTIVATE).")
        self.delegate = delegate
        // 必要な処理をここで記述。
        return true
    }
    
    func disable() {
        Logger.debug(string: "[T] Trigger disabled (DEACTIVATE).")
        // 必要な処理をここで記述。
        delegate = nil
    }
}

 

さあ、順番に見ていきましょう。iOSのSwiftで説明をします。Piece Coreを作るにはSDKが必要です。コードの最初はSDKのインポートです。Riiiver SDK = ERSDKと読み替えてください。(ERはRiiiverを立ち上げる時のコンセプト「-er」から来ています。コンセプトについてはここ[FixMe]で説明してますので是非ご覧になって下さい!)

初めてのRiiiver対応アプリ

せっかくなので、このままの流れでコードを読みながら理解するのではなく、実際に単純なアプリを作って、実行してSDKの使い方、iiideaが実行される仕組み、Pieceの作り方を理解してしまいましょう!(一旦、先程までのコードの説明は保留です。後でちゃんと出て来ます!)
次の様なシンプルなアプリを作って、SDKの動きを確認します。(IoTデバイスは登場しません。アプリだけのシンプルな構成です!)

このアプリを作ってみましょう。アプリのUIの要素と動作、Riiiver上での役割は以下の様なものです。

UI Piece種別 動作
Button T (トリガー) アプリ上のボタン。押されたらiiideaの動作を開始する。
Text Field S (サービス) アプリ上のテキストフィールド。入力されている文字列を次のPieceに出力する。
Label A (アクション) アプリ上のラベル。入力された文字列をラベルとして出力する。

構成を図で書くとこんな↓感じです。

T, S, AそれぞれのPiece Coreは全てこれから作るアプリの中に準備します。これらのPieceから作られたiiideaを動かす、とってもシンプルなアプリを作製してみましょう。

(SDKは只今準備中です。リリース以降に続きを投稿いたします!)