0

Ive got a working Google API.ai project that I speak to, ask it to turn on a component, it posts the json to a webhook URL and my nodejs parses the json to carry out a python script on a raspberry. The problem is that apparently just calling the welcome intent fires the json post and toggles python script to turn on the component. The strange thing i that the state-of-component required by the if-statement to determine which python script to call is empty at the welcome intent state.

This is my node.js:

'use strict';
require('dotenv').config();

const PythonShell = require('python-shell');
const fs = require('fs');
const express = require('express');
const bodyParser= require('body-parser');
const path = require('path')
const app = express();

process.env.DEBUG = 'actions-on-google:*';
let Assistant = require('actions-on-google').ApiAiAssistant;
app.use(bodyParser.json({type: 'application/json'}));

const GENERATE_ANSWER_ACTION = 'generate_answer';
const EXECUTE_HOME_COMMAND = 'execute_home_command';

const switches = [];

var readableStream = fs.createReadStream('saveState.json');
var data = ''

readableStream.on('data', function(chunk) {
    data+=chunk;
});

readableStream.on('end', function() {
  var parsed = JSON.parse(data);
  for (var i=0;i<parsed.switches.length;i++){
    switches.push(new Switch(parsed.switches[i]))
  }
});

function Switch(switchValues){
  this.id = switchValues.id || "sw"
  this.state = switchValues.state || "off"
  this.name = switchValues.name || "switch"
  this.toggle = function(){
    if(this.state === "on"){
      this.setState("off")
    } 
    else{
      this.setState("on");
    }
  }
  this.setState = function(state){
    var str = state === "on" ? onString(this.id[2]) : offString(this.id[2]);
    PythonShell.run(str, function (err) {
      if (!process.env.DEV){
        if (err) throw err;
      } 
    });
    this.state = state
  }
  this.setState(this.state);
}    

function onString(number){
  return './public/python/sw' + number + '_on.py'
}
function offString(number){
  return './public/python/sw' + number + '_off.py'
}

function getSwitch(string){
  return switches.filter(function(element){
    return element.id === string;
  })[0]
}

function saveState (){
  var formattedState = {
    switches: switches
  }
  fs.writeFile('./saveState.json', JSON.stringify(formattedState) )
}

app.use(bodyParser.urlencoded({ extended: true }))
app.use(express.static(__dirname + '/public'));

app.get('/', function(req, res){
  res.sendFile('index');
})

// Switch Routes for API
app.get('/api/switches', function(req, res){
  res.send(switches);
})

app.get('/api/switches/:id', function(req, res){
  var found = getSwitch(req.params.id);
  res.json(found);
})

app.post('/api/switches/:id', function(req, res){
   console.log('headers: ' + JSON.stringify(req.headers));
   console.log('body: ' + JSON.stringify(req.body));
   const assistant = new Assistant({request: req, response: res});
   let soc = assistant.getArgument('state-of-component')

   function generateAnswer(assistant) {
      console.log('genera answer');
      assistant.ask('I\'m thinking of a number from 0 and 100. What\'s your first guess?');
   }

   function executeHomeCommand(assistant) {
    console.log('revisear guess');
    console.log(soc);
    if (soc === "on") {
       console.log('SUCCESS soc=ON');
    } else {
        console.log('FAILUER soc=OFF');
    }
    }
//   //MAP ACTIONS to functions
      let actionMap = new Map();
      actionMap.set(GENERATE_ANSWER_ACTION, generateAnswer);
      actionMap.set(EXECUTE_HOME_COMMAND, executeHomeCommand);
      assistant.handleRequest(actionMap);

  if (req.query.password === process.env.PASS){
    var foundSwitch = getSwitch(req.params.id);

    if (soc === "on") {
        foundSwitch.setState("on");
        console.log('SWITCHING ON');
    } else {
        foundSwitch.setState("off");
        console.log('SWITCHING OFF');
    }

    saveState();
    console.log("postSwitch "+JSON.stringify(foundSwitch));
    res.json(foundSwitch);
  }
  else {
    console.log("invalid password")
    res.send("try again")
  }

})

app.listen(process.env.PORT, function(){
 console.log('Listening on port ' + process.env.PORT);
})

I thought it was my app.js code making the post on the welcome intent but Im not so sure, because on the welcome intent, the state-of-component is empty, no value, so soc is != "on", so the if statement is working properly. The problem is that the:

if (soc === "on") {
        foundSwitch.setState("on");
        console.log('SWITCHING ON');

code is being called on the welcome intent as well and it shouldnt.

Prisoner
  • 49,922
  • 7
  • 53
  • 105
marciokoko
  • 4,988
  • 8
  • 51
  • 91
  • What is the action value you have set for your welcome intent? If you can post screen shots from your API.AI welcome intent, this might help as well. – Prisoner May 11 '17 at 18:57
  • It has input.welcome as action value. I have no idea where I got that from actually. Ive been through so many examples. But its not called in code. – marciokoko May 11 '17 at 23:36

1 Answers1

2

Well, the real problem is that you seem to be doing processing for all intents instead of specific things based on specific intents. With API.AI, you have two ways to handle this:

  1. You can always turn webhook resolution off for a particular Intent, and still leave it on for other Intents. In this case, you could leave it off for your welcome intent (which would send a greeting prompt and so forth), but turn it on for other command processing.

  2. Once your webhook is called, the actions-on-google library determines the specific function to call for the Action that it was called for. This is handled via the assistant.handleRequest(actionMap); line.

In your code, you have additional processing happening after the call to assistant.handleRequest(actionMap);. So this additional code is being processed for every call. It sounds like this isn't what you want.

You should move this code into specific Action processing code that is registered with the actionMap. (I don't know what your actions are, but I assume placing the code just in the executeHomeCommand() function would make the most sense, assuming everything else is hooked up correctly.)

Prisoner
  • 49,922
  • 7
  • 53
  • 105
  • Got it! That was indeed it. I overlooked that because Im not using a proper editor. I guess I need to get TextWrangler connected to my rpi. Thx! – marciokoko May 11 '17 at 22:46