Skip to content

Commit

Permalink
Copied work done by mdomox
Browse files Browse the repository at this point in the history
  • Loading branch information
inventor96 committed Sep 6, 2019
1 parent d767102 commit 202402f
Show file tree
Hide file tree
Showing 10 changed files with 292 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Community maintained fork of the
- [`override` (Override Mode)](#override-override-mode)
- [`xdrip-js` (xDrip-js)](#xdrip-js-xdrip-js)
- [`alexa` (Amazon Alexa)](#alexa-amazon-alexa)
- [`googlehome` (Google Home/DialogFLow)](#googlehome-google-homedialogflow)
- [`speech` (Speech)](#speech-speech)
- [`cors` (CORS)](#cors-cors)
- [Extended Settings](#extended-settings)
Expand Down Expand Up @@ -511,6 +512,9 @@ To learn more about the Nightscout API, visit https://YOUR-SITE.com/api-docs.htm
##### `alexa` (Amazon Alexa)
Integration with Amazon Alexa, [detailed setup instructions](docs/plugins/alexa-plugin.md)

##### `googlehome` (Google Home/DialogFLow)
Integration with Google Home (via DialogFlow), [detailed setup instructions](docs/plugins/googlehome-plugin.md)

##### `speech` (Speech)
Speech synthesis plugin. When enabled, speaks out the blood glucose values, IOB and alarms. Note you have to set the LANGUAGE setting on the server to get all translated alarms.

Expand Down
34 changes: 34 additions & 0 deletions docs/plugins/googlehome-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Nightscout Google Home/DialogFlow Plugin
========================================

## Overview

To add Google Home support for your Nightscout site, here's what you need to do:

1. Activate the `googlehome` plugin on your Nightscout site, so your site will respond correctly to Google's requests.
2. Create a custom DialogFlow agent that points at your site and defines certain questions you want to be able to ask. (You'll copy and paste a basic template for this, to keep things simple.)
3. Create desired integrations with DialogFlow


## Activate the Nightscout Google Home Plugin

1. Your Nightscout site needs to be new enough that it supports the `googlehome` plugin. .
2. Add `googlehome` to the list of plugins in your `ENABLE` setting. ([Environment variables](https://github.com/nightscout/cgm-remote-monitor#environment) are set in the configuration section for your monitor. Typically Azure, Heroku, etc.)

## Create Your DialogFlow Agent

### Signin to DialogFlow

- Sign in to DialogFlow with your Google account (https://console.dialogflow.com/api-client/#/login). If you don't already have one, signup with Google.

### Create a new custom DialogFlow agent

1. Select "Create new agent" in the main menu bar.
2. Input a custom name for your agent and click "CREATE".
3. Download the simple agent template : ( https://drive.google.com/drive/folders/18z2kQSEInvH4O_jfjB4Qh8z9508P9Oao?usp=sharing )
4. Select IMPORT FROM ZIP , in order to import the template.
5. SAVE
6. Go to "Fullfillment" menu and enter details about your webhook.
7. SAVE
8. Go to "Integration" menu and select your desired integration.
9. Follow instructions for each desired integration.
115 changes: 115 additions & 0 deletions lib/api/googlehome/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
'use strict';

var _ = require('lodash');
var moment = require('moment');

function configure (app, wares, ctx, env) {
var express = require('express');
var api = express.Router();
var entries = ctx.entries;
var translate = ctx.language.translate;

// invoke common middleware
api.use(wares.sendJSONStatus);
// text body types get handled as raw buffer stream
api.use(wares.bodyParser.raw());
// json body types get handled as parsed json
api.use(wares.bodyParser.json());

ctx.plugins.eachEnabledPlugin(function each(plugin) {
if (plugin.googleHome) {
if (plugin.googleHome.intentHandlers) {
console.log('Plugin ' + plugin.name + ' is Google Home enabled');
_.each(plugin.googleHome.intentHandlers, function (handler) {
if (handler) {
ctx.googleHome.configureIntentHandler(handler.intent, handler.intentHandler, handler.routableSlot, handler.slots);
}
});
}
} else {
console.log('Plugin ' + plugin.name + ' is not Google Home enabled');
}
});

ctx.googleHome.configureIntentHandler('CurrentMetric', function (result, next, sbx) {
entries.list({count: 1}, function(err, records) {
var response = '';
if (records && records.length > 0) {
var direction = '';
if (records[0].direction === 'FortyFiveDown') {
direction = ' and slightly dropping';
} else if (records[0].direction === 'FortyFiveUp') {
direction = ' and slightly rising';
} else if (records[0].direction === 'Flat') {
direction = ' and holding';
} else if (records[0].direction === 'SingleUp') {
direction = ' and rising';
} else if (records[0].direction === 'SingleDown') {
direction = ' and dropping';
} else if (records[0].direction === 'DoubleDown') {
direction = ' and rapidly dropping';
} else if (records[0].direction === 'DoubleUp') {
direction = ' and rapidly rising';
}
response = buildPreamble(result.parameters);
response += sbx.scaleMgdl(records[0].sgv) + direction + ' as of ' + moment(records[0].date).from(moment(sbx.time));
} else {
response = buildPreamble(result.parameters) + 'unknown';
}
next(response);
});
}, 'metric', ['bg', 'blood glucose', 'blood sugar', 'number']);

api.post('/googlehome', ctx.authorization.isPermitted('api:*:read'), function (req, res, next) {
console.log('Incoming request from Google Home');
onIntent(req.body, function (response) {
res.json(ctx.googleHome.buildResponse(response));
next();
});
});

function buildPreamble(parameters) {
var preamble = '';
if (parameters && parameters.givenName) {
preamble = parameters.givenName + '\'s current ';
} else {
preamble = 'Your current ';
}
if (parameters && parameters.readingType) {
preamble += parameters.readingType + ' is ';
} else {
preamble += 'blood glucose is ';
}
return preamble;
}

function onIntent(body, next) {
console.log('Received intent request');
console.log(JSON.stringify(body));
handleIntent(body, next);
}

// https://docs.api.ai/docs/webhook#section-format-of-request-to-the-service
function handleIntent(body, next) {
var displayName = body.queryResult.intent.displayName;
var metric = body.queryResult.parameters ? body.queryResult.parameters.metric : null;
var handler = ctx.googleHome.getIntentHandler(displayName, metric);
if (handler) {
var sbx = initializeSandbox();
handler(body.queryResult, next, sbx);
} else {
next('I\'m sorry I don\'t know what you\'re asking for');
}
}

function initializeSandbox() {
var sbx = require('../../sandbox')();
sbx.serverInit(env, ctx);
ctx.plugins.setProperties(sbx);
return sbx;
}

return api;
}

module.exports = configure;
4 changes: 4 additions & 0 deletions lib/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ function create (env, ctx) {
app.all('/alexa*', require('./alexa/')(app, wares, ctx, env));
}

if (ctx.googleHome) {
app.all('/googlehome*', require('./googlehome/')(app, wares, ctx, env));
}

return app;
}

Expand Down
14 changes: 14 additions & 0 deletions lib/plugins/basalprofile.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,20 @@ function init (ctx) {
}]
};

function googleHomeCurrentBasalhandler (result, next, sbx) {
var pwd = result.parameters && result.parameters.givenName ? result.parameters.givenName : null;
next(basalMessage(pwd, sbx));
}

basal.googleHome = {
intentHandlers: [{
intent: 'CurrentMetric'
, routableSlot:'metric'
, slots:['basal', 'current basal']
, intentHandler: googleHomeCurrentBasalhandler
}]
};

return basal;
}

Expand Down
19 changes: 19 additions & 0 deletions lib/plugins/cob.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,25 @@ function init (ctx) {
}]
};

function googleHomeCOBHandler(result, next, sbx) {
var preamble = result && result.parameters && result.parameters.givenName ? result.parameters.givenName + ' has' : 'You have';
var value = 'no';
if (sbx.properties.cob && sbx.properties.cob.cob !== 0) {
value = Math.round(sbx.properties.cob.cob);
}
var response = preamble + ' ' + value + ' carbohydrates on board';
next(response);
}

cob.googleHome = {
intentHandlers: [{
intent: 'CurrentMetric'
, routableSlot:'metric'
, slots:['cob', 'carbs on board', 'carbohydrates on board', 'carbohydrates']
, intentHandler: googleHomeCOBHandler
}]
};

return cob;

}
Expand Down
59 changes: 59 additions & 0 deletions lib/plugins/googlehome.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

function init(env, ctx) {

console.log('Configuring Google Home...');

function googleHome() {
return googleHome;
}

var intentHandlers = {};

googleHome.configureIntentHandler = function configureIntentHandler(intent, handler, routableSlot, slotValues) {
if (!intentHandlers[intent]) {
intentHandlers[intent] = {};
}
if (routableSlot && slotValues) {
for (var i = 0, len = slotValues.length; i < len; i++) {
if (!intentHandlers[intent][routableSlot]) {
intentHandlers[intent][routableSlot] = {};
}
if (!intentHandlers[intent][routableSlot][slotValues[i]]) {
intentHandlers[intent][routableSlot][slotValues[i]] = {};
}
intentHandlers[intent][routableSlot][slotValues[i]].handler = handler;
}
} else {
intentHandlers[intent].handler = handler;
}
};

googleHome.getIntentHandler = function getIntentHandler(intentName, metric) {
if (intentName && intentHandlers[intentName]) {
if (metric && intentHandlers[intentName]['metric'] &&
intentHandlers[intentName]['metric'][metric] &&
intentHandlers[intentName]['metric'][metric].handler) {
return intentHandlers[intentName]['metric'][metric].handler;
} else if (intentHandlers[intentName].handler) {
return intentHandlers[intentName].handler;
} else {
return null;
}
} else {
return null;
}

};

googleHome.buildResponse = function buildResponse(output) {
return {
fulfillmentText: output
// , fulfillmentMessages: [output]
, source: 'Nightscout'
};
};

return googleHome;
}

module.exports = init;
15 changes: 15 additions & 0 deletions lib/plugins/iob.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,21 @@ function init(ctx) {
}]
};

function googleHomeIOBIntentHandler(result, next, sbx) {
var preamble = result && result.parameters && result.parameters.givenName ? result.parameters.givenName + ' has ' : 'You have ';
var message = preamble + getIob(sbx) + ' insulin on board';
next(message);
}

iob.googleHome = {
intentHandlers: [{
intent: 'CurrentMetric'
, routableSlot: 'metric'
, slots: ['iob', 'insulin on board', 'insulin']
, intentHandler: googleHomeIOBIntentHandler
}]
};

return iob;

}
Expand Down
24 changes: 24 additions & 0 deletions lib/plugins/openaps.js
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,30 @@ function init (ctx) {
}]
};

function googleHomeForecastHandler(response, next, sbx) {
if (sbx.properties.openaps && sbx.properties.openaps.lastEventualBG) {
var response = 'The Open APS eventual BG is ' + sbx.properties.openaps.lastEventualBG;
next(response);
}
}

function googleHomeLastLoopHandler (response, next, sbx) {
var response = 'The last successful loop was ' + moment(sbx.properties.openaps.lastLoopMoment).from(moment(sbx.time));
next(response);
}

openaps.googleHome = {
intentHandlers: [{
intent: 'CurrentMetric'
, routableSlot: 'metric'
, slots: ['openaps', 'openaps forecast', 'forecast']
, intentHandler: googleHomeForecastHandler
}, {
intent: 'LastLoop'
, intentHandler: googleHomeLastLoopHandler
}]
};

function statusClass (prop, prefs, sbx) {
var level = statusLevel(prop, prefs, sbx);
return levels.toStatusClass(level);
Expand Down
4 changes: 4 additions & 0 deletions lib/server/bootevent.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ function boot (env, language) {
ctx.alexa = require('../plugins/alexa')(env, ctx);
}

if (env.settings.isEnabled('googlehome')) {
ctx.googleHome = require('../plugins/googlehome')(env, ctx);
}

next( );
}

Expand Down

1 comment on commit 202402f

@inventor96
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See nightscout#4035 for mdomox's original work.

Please sign in to comment.