LoginSignup
7
2

More than 5 years have passed since last update.

firebase-adminでAlexaスキルからRealtime Databaseを更新する

Last updated at Posted at 2018-12-03

Realtime Database側

こんな感じにしておく。
スキルが起動するとOn。キャンセルするとOffにする。

Root
 -launch : on/off
 

実装

index.js
'use strict';

const Alexa = require('ask-sdk-core');
const admin = require('firebase-admin');
const serviceAccount = require('秘密鍵jsonのパス');

admin.initializeApp( {
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "データベースのURL" 
} );

const db = admin.database();
const ref = db.ref(); //ハマりポイント1

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  async handle(handlerInput) {
    await ref.update({
      "launch" : "on"
    },  (error)=>{
      if(error) {
        console.log("書き込み失敗");
        console.log(error);
      }else{
        console.log("書き込み成功");        
      }
    });

    const speechText = '起動したよ';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .getResponse();
  },
};

const CancelAndStopIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
        || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
  },
  async handle(handlerInput) {
    await ref.update({
      "launch" : "off"
    },  (error)=>{
      if(error) {
        console.log("書き込み失敗");
        console.log(error);
      }else{
        console.log("書き込み成功");        
      }
    });

    const speechText = 'さようなら!';

    return handlerInput.responseBuilder
      .speak(speechText)
      .getResponse();
  },
};

let skill; //ハマりポイント2
exports.handler = async (event, context) =>{
  console.log(skill);
  if(!skill){
    skill = Alexa.SkillBuilders.custom()
          .addRequestHandlers(
            LaunchRequestHandler,
            HelpIntentHandler,
            CancelAndStopIntentHandler,
            SessionEndedRequestHandler,
            LightRequestHandler)
          .addErrorHandlers(ErrorHandler)
          .create();

  }
  return skill.invoke(event); 
}

(ハマるとしたら多分私ぐらいだが)ハマりポイント解説

Realtime Databaseのルート指定方法

index.js
const ref = db.ref(); 

色々な方のソースを参考にさせていただきましたが、どれもルートの下にちゃんと構造作られてて、ルート直下の要素を更新するサンプルが見つけられませんでした。
ちゃんと公式サイトに載ってました。この書き方でルートを指定できました。

firebaseのリファレンス(ルート指定方法)

handlerの実装方法

ここの部分ですが・・・

index.js
let skill; //ハマりポイント2
exports.handler = async (event, context) =>{
  console.log(skill);
  if(!skill){
    skill = Alexa.SkillBuilders.custom()
          .addRequestHandlers(
            LaunchRequestHandler,
            HelpIntentHandler,
            CancelAndStopIntentHandler,
            SessionEndedRequestHandler,
            LightRequestHandler)
          .addErrorHandlers(ErrorHandler)
          .create();

  }
  return skill.invoke(event); 
}

こんな風にも書くこともできます。

index.js
const skillBuilder = Alexa.SkillBuilders.custom(); 

exports.handler = skillBuilder
   .addRequestHandlers(
     LaunchRequestHandler,
     HelpIntentHandler,
     CancelAndStopIntentHandler,
     SessionEndedRequestHandler,
     LightRequestHandler)
   .addErrorHandlers(ErrorHandler)
   .lambda();

一応公式でも「また、ASK SDK v2 for Node.jsで提供されるlambdaビルダー関数を使って、Skillのインスタンスを呼び出して応答を返すLambdaハンドラー関数を簡単に作成することもできます。」と説明されてます。
ASK SDK for Node.jsの公式ガイド

ですが、動きが違いました。

後者の書き方だとRealtime Databaseのセッションが残ってしまい(?)、Lambdaでタイムアウトが発生して、結果的に「スキルからの応答に問題があります」とエラーが返ってきてしまいました。
(Realtime Databaseへの更新はうまくいきます)

今のところ理由は定かではありませんが、とりあえず前者のcreate()でハンドラ作る方法を推奨します。

動きの違いが気になって試したこと

goOfflineでRealtime Databaseとの接続を切れるらしい

lambdaで試した

index.js
    await ref.update({
      "launch" : "on"
    },  (error)=>{
      if(error) {
        console.log("書き込み失敗");
        console.log(error);
      }else{
        console.log("書き込み成功");        
      }
    });

    db.goOffline(); //←これを追記


結果:タイムアウトは変わらず。ダメでした。

ローカルで試したけど・・・

local.js
'use strict';

//省略

const db = admin.database();
const ref = db.ref();

const dbupdate = async () => {
  await ref.update({"launch" : "on"});
  db.goOffline();
};

dbupdate();

結果:こいつもダメでした。

間違いなどあったら訂正しますので是非ご指摘ください。

追記

12/4 タイトルが日本語的にあれだったので直しました
12/6 ソース汚くて恥ずかしいのでスリム化しました
12/6 試したこと色々追記しました。田中みそさん、にしぞのさんのディスカッションも貴重。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2