µ±¸öÈËÖúÀíͨ¹ýÎïÁªÍø·ÃÎʸöÈËÊý¾ÝºÍÏÖʵÊÀ½çʱ£¬Ëü±äµÃ¸ü¼ÓÃÔÈË£¬ÈÃÈËÐijÛÉñÍù¡£Õâ¿ÉÄܳöÏÖеÄÓ¦Óó¡¾°£¬±È·½ËµÇëÇóÄãµÄÖúÊÖ´ò¿ªµÆ¹â£¬»òÕßÏòËüѯÎÊÄãµÄ˯ÃßÖÊÁ¿¡£ÎÒÃǽ«°ÑÄãµÄ
Api.ai ÖúÊÖÁ¬½Óµ½ Jawbone Up API£¬ÒÔ´Ë×÷ΪʾÀý¡£
ǰÆÚ×¼±¸
ΪÁ˸üºÃµØÕÆÎÕ±¾½Ì³ÌµÄÄÚÈÝ£¬ÄãÐèÒª×öÈçÏÂ×¼±¸£º
1.Á¬½Óµ½Ò»¸ö¼òµ¥ HTML ÍøÒ³Ó¦ÓÃµÄ Api.ai ´úÀí¡£Èç¹ûÄãÏëÁ˽â´Ë¹ý³Ì£¬Çë²ÎÔÄ´ËÎÄ¡£´ËÍ⣬ÄãÒ²¿ÉÒÔ´Ó±¾½Ì³ÌÏÂÔØ´úÂëʹÓá£
2.Ö¸¶¨Ò»¸ö entity Ϊ ¡°sleep¡± µÄ´úÀí¡£´´½¨·½·¨¿ÉÒÔ²éÔÄ´ËÎÄ¡£ËüÖÁÉÙÓ¦µ±Àí½âÖîÈç¡°how
much sleep did I have last night?¡± ºÍ ¡°how much REM
sleep did I get?¡±ÕâÑùµÄÖ¸Áî¡£Èç¹ûÄãÏëҪʹ֮ÊÊÓ¦×Ô¼ºµÄ IoT É豸£¬ÄãÐèÒª´´½¨×Ô¶¨ÒåµÄ
entity £¬ÓÃÓÚʶ±ðÄãµÄ IoT ¹¦ÄÜ¡£
3.ÐèÒªÄã¶Ô Node.js Óлù±¾µÄÁ˽ⲢÇÒÄܹ»ÔËÐÐ Node
·þÎñÆ÷¡£ÒòΪȱÉÙËüÃǵÄÖ§³Ö£¬±¾ÏµÍ³½«ÎÞ·¨ÔËÐС£
4.ÖªµÀÈçºÎʹÓÃJawbone UP API£¨»òÕßÄã´òËãʹÓÃµÄÆäËûAPI£©¡£Ö®Ç°ÎÒÃÇÒѾ½éÉÜÁËʹÓÃNode.jsÁ¬½Óµ½Jawbone
Up API µÄ·½·¨£¬±¾½Ì³Ì½«»áÒýÓøÃÎÄÕÂÖеÄһЩÄÚÈÝ¡£
5.»ùÓÚ HTTPS ÔËÐÐÄãµÄÕ¾µãµÄ SSL Ö¤Êé¡£Ëü¶ÔÓÚ Jawbone
Up API À´ËµÊDZØÐèµÄ¡£ÕýÈ籾ϵÁпªÊ¼Ê±ËùÌáµ½µÄÄÇÑù£¬»ùÓÚ HTTPS ʵÏÖ»áÏà¶ÔÈÝÒס£ÎÒÃÇÔÚJawbone
Up APIÎÄÕÂÖнéÉÜÁËÈçºÎÉèÖÃ×ÔÇ©ÃûÖ¤Ê飬ÓÐÐËȤ¿ÉÒÔ²éÔÄ£¬µ«Õâ²¢²»ÊÇ×î¼ò·½°¸¡£¸üÇáËɵÄʵÏÖ·½·¨ÊDZ¾ÏµÁеĵÚһƪÎÄÕÂÖÐÌáµ½µÄ
Let¡¯s Encrypt ¡£´ËÍ⣬Glitch.com »¹ÌṩÁËÒ»¸öĬÈÏΪ HTTPS µÄÔÐÍÉè¼Æ»·¾³¡£
´úÂë
±¾½Ì³ÌµÄËùÓдúÂëÌṩÃâ·ÑÏÂÔØºÍʹÓᣠGitHubµØÖ·£ºhttps://github.com/sitepoint-editors/Api-AI-Personal-Assistant-IoT-Demo¡£
ϵͳÈçºÎ¹¤×÷
ÄãµÄ Api.ai ÖúÊÖÁ¬½Óµ½Ò»¸ö¼òµ¥µÄ Web Ó¦ÓÃÖ®ºó£¬Ëüͨ¹ý HTML5 ÓïÒôʶ±ð API ½ÓÊÜÖ¸Áî¡£´Ëʱ£¬ÄãÐèÒªÌí¼ÓһЩй¦ÄÜ£¬ÓÃÓÚ¼àÌýÄãµÄ
Api.ai ´úÀíµÄÌØ¶¨ Action ¡£ÔÚ±¾ÀýÖУ¬ Action ÊÇ¡°sleepHours¡±¡£
ÿµ± JavaScript ¼ì²âµ½¸Ã Action ʱ£¬Ëü»á´¥·¢µ÷ÓÃÄãµÄ Node.js Ó¦ÓóÌÐò£¬ÒÔÏòJawbone
API ÇëÇóÊý¾Ý¡£Ò»µ© web Ó¦ÓÃÊÕµ½ÕâЩÊý¾Ý£¬Ëü»á½«Êý¾Ýת»»³ÉÓѺõÄÌáʾÓï²¢½«Æä¶Á³ö - ΪÄãµÄÖúÊÖÌṩȫеÄÖÇÄÜÌåÑ飡
ÏîÄ¿½á¹¹
ÎÒ½«Ó¦ÓóÌÐò´Ó³õʼµÄ HTML ½á¹¹µ÷ÕûΪʹÓà EJS ÊÓͼµÄÓ¦ÓóÌÐò£¬ÒÔ±ãÄãͨ¹ý
OAuth µÇ¼µ½ Jawbone Up API ºóÔÚ Web Ó¦ÓóÌÐòÖÐÇл»Ò³Ã档ʵ¼ÊÉÏ£¬ÄãÖ»ÓÐÒ»¸öÒ³Ãæ£¬µ«ÊÇÈç¹ûÐèÒªÆäËû
IoT É豸£¬Äã¿ÉÒÔͨ¹ý´Ë·½·¨Ìí¼Ó¸ü¶à¡£Õâ¸öΨһµÄ½çÃæÔÚ /views/index.ejs Öж¨Ò塣Ȼºó£¬ÄãµÄ
Node ·þÎñÆ÷ server.js ºÍÖ¤ÊéÎļþ½«Î»ÓÚ¸ùĿ¼ÖС£ÎªÁ˾¡¿ÉÄܼòµ¥ºÍÁé»î£¬ËùÓÐǰ¶Ë JavaScript
ºÍ CSS ´úÂë¶¼Ó¦µ±ÊÇÄÚÁªµÄ¡£Äã¿ÉÒÔËæÒ⽫ËüÃÇÒÆ¶¯µ½ CSS ºÍ JS ÎļþÖУ¬½«ËüÃÇϸ»¯²¢Ê¹Æä±äµÃ¸üºÃ¡£

ÔÚ JavaScript ÖÐΪ Api.ai Ìí¼Ó Action
ÕýÈçÄã´ÓÒÔǰµÄÎÄÕÂËùÁ˽⵽µÄÄÇÑù£¬µ± Api.ai ·µ»ØÒ»¸öÏìӦʱ£¬ËüÌṩÁËÒ»¸öÈçÏÂËùʾµÄ JSON
¶ÔÏó£º
{
"id": "6b42eb42-0ad2-4bab-b7ea-853773b90219",
"timestamp": "2016-02-12T01:25:09.173Z",
"result": {
"source": "agent",
"resolvedQuery": "how did I sleep
last night",
"speech": "I'll retrieve your
sleep stats for
you now, one moment!",
"action": "sleepHours",
"parameters": {
"sleep": "sleep"
},
"metadata": {
"intentId": "25d04dfc-c90c-4f55-a7bd-6681e83b45ec",
"inputContexts": [],
"outputContexts": [],
"contexts": ,
"intentName": "How many hours
of @sleep:sleep
did I get last
night?"
}
},
"status": {
"code": 200,
"errorType": "success"
}
} |
ÔÚÕâ¸ö JSON ¶ÔÏóÖÐÓÐÁ½¸ö±ÈÌØµÄÊý¾ÝÄãÐèҪʹÓã¬ËüÃÇÊÇ actionºÍ parameters.sleep¡£
"action":
"sleepHours",
"parameters": {
"sleep": "sleep"
}, |
action ¾ö¶¨ÁË´¥·¢Api.ai ÐÐΪµÄÃû³Æ¡£ÔÚ±¾Ê¾ÀýÖУ¬Ä㽫ÆäÃüÃûΪ¡°sleepHours¡±¡£parameters
°üº¬ÁËÄãµÄÓï¾äÖпÉÒԸıäϸ½ÚµÄ±äÁ¿¡£ÔÚ±¾ÀýÖУ¬ÄãµÄ parameters ¶¨ÒåÁË˯ÃßÀàÐÍ£º ¡°Ë¯Ãß¡±£¬¡°Éî˯Ãß¡±£¬¡°Ç³Ë¯Ãß¡±»ò¡°REM˯Ãß¡±£¨»ò¡°REM¡±£©¡£
×î³õ£¬ÔÚÔçÆÚµÄһƪ¹ØÓÚ Api.ai µÄÎÄÕÂÖУ¬prepareResponse()º¯Êý´Ó Api.ai
»ñÈ¡ JSON ÏìÓ¦£¬½«Æä·ÅÔÚÓÒÏ·½µÄµ÷ÊÔÎı¾×Ö¶ÎÖУ¬ËüÌáÈ¡³ö Api.ai µÄÎı¾ÏìÓ¦²¢ÏÔʾÔÚWebÓ¦ÓóÌÐòÖС£ÄãÍêÈ«ÒÀÀµ
Api.ai ´úÀíµÄ·´À¡£¬ÎÞÐè×Ô¼ºÌí¼ÓÈκι¦ÄÜ¡£
function
prepareResponse(val) {
var debugJSON = JSON.stringify(val,
undefined, 2),
spokenResponse = val.result.speech;
respond(spokenResponse);
debugRespond(debugJSON);
} |
ÔÚ action ´úÂëÆ¬¶ÎÖУ¬Èç¹û action °üº¬¡±sleepHours¡±£¬¾Íµ÷ÓÃÄã×Ô¼ºµÄrequestSleepData()º¯Êý¡£º¯ÊýÖеÄ˯Ãß²ÎÊý½«¾ö¶¨ÇëÇóʲôÀàÐ͵Ä˯ÃßÊý¾Ý¡£
function
prepareResponse(val) {
var debugJSON = JSON.stringify(val,
undefined, 2),
spokenResponse = val.result.speech;
if (val.result.action == "sleepHours")
{
requestSleepData(val.result.parameters.sleep);
} else {
respond(spokenResponse);
}
debugRespond(debugJSON);
} |
ÔÚrequestSleepData()º¯ÊýÖн«´Ó Node ·þÎñÆ÷ÇëÇóËùÓеÄ˯ÃßÊý¾Ý£¬È»ºóͨ¹ýÊý×édata.items[0].details£¨×òÍíµÄÊý¾Ý£©µÄµÚÒ»¸öÖµ½øÐÐ˯ÃßÀàÐ͵ÄÇø·Ö¡£
data.items[0].details.rem´ú±í REM ˯Ãߣ»data.items[0].details.sound´ú±íÉî¶È˯Ãߣ»data.items[0].details.light´ú±ídz˯Ãߣ»data.items[0].details.durationÔò´ú±í˯Ãß»ã×ÜÊý¾Ý¡£
function
requestSleepData(type) {
$.ajax({
type: "GET",
url: "/sleep_data/",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data) {
console.log("Sleep data!", data);
if (data.error) {
respond(data.error);
window.location.replace("/login/jawbone");
}
switch (type) {
case "REM sleep":
respond("You had " + toHours(data.items[0].
details.rem) + " of REM sleep.");
break;
case "deep sleep":
respond("You had " + toHours(data.items[0].
details.sound) + " of deep sleep.");
break;
case "light sleep":
respond("You had " + toHours(data.items[0].
details.light) + " of light sleep.");
break;
case "sleep":
respond("You had " + toHours(data.items[0].
details.duration) + " of sleep last night.
That includes " + toHours(data.items[0].details.rem)
+ " of REM sleep, " + toHours(data.items[0].details.sound)
+ " of deep sleep and " + toHours(data.items[0].details
.light) + " of light sleep.");
break;
}
},
error: function() {
respond(messageInternalError);
}
});
} |
toHours()º¯Êý½«Ê±¼ä¸ñʽת»»ÎªÆÕͨÓï¾ä£¬±ÈÈ磺1Сʱ53·Ö59Ãë¡£
function
toHours(secs) {
hours = Math.floor(secs / 3600),
minutes = Math.floor((secs - (hours * 3600))
/ 60),
seconds = secs - (hours * 3600) - (minutes *
60);
hourText = hours + (hours > 1 ? "
hours, " :
" hour, ");
minuteText = minutes + (minutes > 1 ? "
minutes " :
" minute ");
secondText = seconds + (seconds > 1 ? "
seconds" :
" second");
return hourText + minuteText + "and "
+ secondText;
} |
requestSleepData() º¯Êý×îÖÕ½«µ÷ÓÃresponse()º¯Êý£¬Ï¸ÐĵÄÄã»á·¢ÏÖresponse()º¯ÊýÔÚ֮ǰµÄ»ñÈ¡
Api.ai ÓïÒôÏìӦʱʹÓùý¡£Í¨¹ý¸´ÓÃÏÖÓеŦÄÜ£¬´ÓÓïÒô»ñµÃÏìÓ¦£¬ÈÃÄãµÄÖúÊÖÔÚ¼àÌýµ½ÐÅÏ¢Ö®ºó֪ͨ¸øÓû§¡£
²»ÒªÍüÁËÔÚǰ¶Ë JavaScript ´úÂëµÄ×îºó¼ÓÉÏ´íÎó´¦Àí¡£Èç¹û Jawbone ·µ»ØÊý¾Ý³öÏÖÎÊÌ⣨ͨ³£ÊÇÒòΪûÓеǼµ½·þÎñ£©£¬·þÎñÆ÷½«ÒÔ
JSON ×Ö¶Î{¡°error¡± : ¡°Your error message¡±}µÄÐÎʽÌáʾ´íÎó¡£ÖúÀíʶ±ðµ½´íÎóÌáʾʱ½«×Ô¶¯Ìø×ªµ½
OAuth µÇÂ¼Ò³Ãæ¡£
if
(data.error) {
respond(data.error);
window.location.replace("/login/jawbone");
}
|
Node.js ·þÎñÆ÷
ÄãµÄ Node ·þÎñÆ÷»ùÓÚʹÓÃNode.jsÁ¬½Óµ½Jawbone Up API Ò»ÎÄÖеķþÎñÆ÷¡£´ËÎĽ²ÊöÁËËùÓйØÓÚͨ¹ýOAuthÁ¬½Óµ½Jawbone
API²¢ÉèÖÃÒ»¸öHTTPS·þÎñÆ÷ÇÒÔËÐгɹ¦µÄ·½·¨£¬Èç¹û¶Ô´úÂëÓÐÈκÎÒÉ»ó£¬Çë²Î¿¼ÕâÆªÎÄÕ¡£Èç¹ûÄãÊÖͷûÓÐ
Jawbone Up £¬¿ÉÒÔʹÓÃÆäËû IoT É豸´úÌæ¡£ÕâÀïµÄ Jawbone Up Êý¾ÝÖ»ÊÇÒ»¸öÀý×Ó£¬Äã¿ÉÒÔ×Ô¶¨ÒåÏìÓ¦·½·¨ÒÔ»ñÈ¡²»Í¬µÄÏìÓ¦Êý¾Ý£¨Äã¿ÉÄÜÎÞÐèµ£ÐÄOAuth£©¡£
ÄãµÄ Jawbone Êý¾ÝÔÚ±¾ÀýÖÐÒÔ¼òµ¥µÄ JSON ¸ñʽչʾ£¬¶ø²»ÊÇÏñÒÔÍùÎÄÕÂÖÐÄÇÑù±»¸ñʽ»¯ÎªÒ»¸ö±íµ¥¡£±äÁ¿upºÍoptions±»¶¨ÒåΪȫ¾Ö±äÁ¿£¬ÒÔ±ã¶à¸öÇëÇó
API µ÷Óã¨ÆäËûSitePointÀý×ÓÖУ¬Ò»´ÎÇëÇóÒ»´ó¿éÊý¾Ý£©¡£
Óû§¿ÉÒÔ·ÃÎÊ/login/jawbone£¬Í¨¹ý OAuth µÇ¼ Jawbone API¡£È»¶ø£¬ÈçÉÏËùÊö£¬ÆäʵÄã²»ÐèÒª¿ÌÒâ¹Ø×¢´ËÊ£¬ÒòΪÄãµÄÖúÀí·¢ÏֵǼʧ°ÜÔò»á×Ô¶¯Öض¨Ïòµ½/login/jawbone¡£ÄúµÄÖúÀí»¹¿ÉÒÔÖØ¶¨ÏòËüÃÇ¡£
Èç¹ûÄãÏë¸Ã¹¦Äܸü¼ÓÍêÕû£¬»¹¿ÉÒÔÏòÄãµÄ Api.ai ´úÀíÌí¼ÓÒ»¸öÐ嵀 intent£¬ÓÃÓÚʶ±ð¡°log
me into my Jawbone Up data¡±¡£Node.js µÇ¼Á÷³ÌÈçÏ£º
app.get("/login/jawbone",
passport.authorize("jawbone", {
scope: ["basic_read","sleep_read"],
failureRedirect: "/"
})
); |
Ò»µ©Í¨¹ýpassport.use(¡°jawbone¡±, new JawboneStrategy())µÇ¼³É¹¦£¬½«·ÃÎÊȨÏÞ·ÖÅ䏸up±äÁ¿²¢½«Óû§×ªµ½/barry¡£Äã¿ÉÒÔ½«Óû§Öض¨Ïòµ½¸ùĿ¼£¨½«»áÔì³ÉËÀÑ»·£©Ö®ÍâµÄÈκη¾¶¡£ÎÒÑ¡ÔñÖØ¶¨Ïòµ½/barryÊÇÒòΪÎÒ½«ÎÒµÄÖúÊÖÃüÃûΪ
Barry£¬Ëü¾ßÓÐÒ»¶¨µÄ×ÔÎÒ±êʶÐÔ£¨Ò³ÃæÏÔʾÍêÈ«ÏàͬµÄË÷ÒýÊÓͼ£¬Í¨³£ºÜÄÑÇø·Ö£©¡£Èç¹ûÄúÔ¸Ò⣬ÄãÒ²¿ÉÒÔʹÓô˷½Ê½ÎªÒѾ³É¹¦µÇ¼µ½
Jawbone É豸µÄÓû§Ìṩ²»Í¬µÄÊÓͼ¡£µÇ¼ºó£¬Óû§¿ÉÒÔ·µ»Øµ½¸ùÒ³Ãæ£ºhttps://localhost:5000
£¬²¢Ê¹ÓÃUp º¯Êý¡£
·µ»Ø IoT Êý¾Ý
ÊÕµ½/sleep_dataµÄ GET ÇëÇóºó£¬¶Ô Jawbone Êý¾ÝµÄ¼ìË÷·Ç³£¼òµ¥¡£Ê×ÏÈÈ·ÈÏup±äÁ¿ÊÇ·ñ±»¶¨Ò壬Èç¹ûûÓÐÔòÖ¤Ã÷Óû§Î´µÇ¼£¬´ËʱÄãÐèÒª¸æÖª
web Ó¦ÓôӶøÖ´ÐÐÖØ¶¨Ïò²Ù×÷²¢ÇÒÌáʾÓû§µÇ¼¡£µ±Äãµ÷ÓÃup.sleeps.get()¶ø Jawbone
·µ»Ø´íÎó»òÕßjawboneData.items䶨Òåʱ£¬Äã¶¼ÐèÒªÖØ¸´´Ë²Ù×÷¡£
app.get("/sleep_data",
function(req, resp) {
if (up !== undefined) {
up.sleeps.get({}, function(err, body) {
if (err) {
console.log("Error receiving Jawbone UP
data");
resp.send({"error": "Your sleep
tracker isn't
talking to me.
Let's try logging in again."});
} else {
var jawboneData = JSON.parse(body).data;
if (jawboneData.items) {
resp.send(jawboneData);
} else {
console.log("Error: " + jawboneData);
resp.send({"error": "Your sleep
tracker isn't
talking to me. Let's try logging in again."});
}
}
});
} else {
console.log("Up is not ready, lets ask
to log in.");
resp.send({"error": "Your sleep
tracker isn't
talking to me. Let's try logging in again."});
}
}); |
ÕâÀïµÄ´íÎó¿ÉÄÜÊÇÓÉÆäËûÒòËØÒýÆðµÄ£¬µ«ÊÇΪÁ˽̸̳ü¼Ó¼òµ¥Ò×¶®£¬ÎÒÑ¡ÓÃÖØ¸´µÇ¼À´¾ÙÀý¡£ÔÚÕýʽ²úÆ·µÄÓ¦ÓóÌÐòÖУ¬ÄãÐèÒª²é¿´¸÷ÖÖÔÒò²¢µ÷Õû¶ÔÓ¦µÄÏìÓ¦¡£
Èç¹ûÒ»ÇÐ˳Àû£¬Ä㽫»áÊÕµ½ÓÐЧµÄÏìÓ¦ÐÅÏ¢£¬½«ÆäÓà JSON µÄ¸ñʽ·¢Ë͸ø web Ó¦ÓóÌÐò£¬ÒÔ±ã¸üºÃµÄ¶ÁÈ¡ºÍ½âÎö£º
if
(jawboneData.items) {
resp.send(jawboneData);
} |
Ëæ×Å Web Ó¦ÓóÌÐòºÍ Node.js ·þÎñÆ÷Ðͬ¹¤×÷£¬ÄãÓ¦¸ÃÄܹ»´Ó Jawbone Up É豸ÖмìË÷˯ÃßÊý¾ÝÁË¡£½ÓÏÂÀ´£¬ÎÒÃÇһͬ²âÊÔһϡ£
Action ²âÊÔ
ͨ¹ýÖ¸Áî node server.js ÔËÐÐÄãµÄ·þÎñÆ÷¡£ÔÚÕâ֮ǰÄãÐèÒªÖ´ÐÐ npm install
°²×° npm Ä£¿é£¬Í¬Ê±ÐèÒªÔËÐлùÓÚ HTTPS µÄ·þÎñÆ÷Ö¤Êé¡£
ÔÚä¯ÀÀÆ÷ÖÐÊäÈëÍøÖ· https://localhost:5000
µÄ·½Ê½·ÃÎÊÄãµÄ AI ÖúÊÖ£¨Èç¹ûÄãʹÓÃGlitch Ò»ÀàµÄ·þÎñ£¬ÔòÐèÓþßÌåµÄ Glitch URL
Ìæ»»£©¡£ÔÚ½çÃæÖÐѯÎÊÄãµÄ˯ÃßÐÅÏ¢£º

ϵͳ»áÌáʾÄãδµÇ¼£¬²¢ÇÒµ¯³ö Jawbone Up OAuth µÇ¼½çÃæ¡£ÄãÐèÒªÔڴ˽çÃæÖеǼ²¢Í¬ÒâÌṩÄãµÄÊý¾Ý·ÃÎÊȨÏÞ£¬È»ºóµ¥»÷¡°Í¬Ò⡱£º

´ËʱÄãÔÙ´ÎѯÎʽ«»áµÃµ½ÕýÈ·µÄ»Ø¸´¡£

Ä㻹¿ÉÒÔѯÎÊһЩ¸ü¼Óϸ½ÚµÄÎÊÌâÀ´½øÐвÎÊý²âÊÔ£¬±ÈÈç¡°How much
REM did I get?¡±¡£

×ܽá
ÕâÊǶԵ±Ç° ¶àÖÖ Api.ai ¹¦ÄܵÄÓÖÒ»ÖÖ̽Ë÷ºÍÌåÑé¡£Äã¿ÉÒÔÀ©Õ¹Õâ¸öÀý×Ó£¬Ìí¼ÓÉÏÈÕÆÚ·¶Î§£¨ÀýÈç¡°ÐÇÆÚ¶þÓжàÉÙ˯Ãߣ¿¡±£©£¬»òÕ߸üºÃµØ¸ñʽ»¯Ê±¼ä£¨ÔÚÆä¸öÏìÓ¦Öб¨¸öС
bug£©¡£Äã¿ÉÒÔ½øÐиöÐÔ»¯µÄÀ©Õ¹£¬Óøü¾«Çɵķ½Ê½À´ÃèÊöÏìÓ¦¡£
ÈçÄãËù¼û£¬Ê¹Óñ¾ÎÄÖеķ½·¨Äã¿ÉÒÔ½«ÈκΠNode.js ·þÎñ»òÕß web Ó¦ÓÃÁ¬½Óµ½ Node.js
·þÎñÆ÷£¬Îª Api.ai °ó¶¨Ò»¸ö intent£¬²¢Ö¸¶¨ËüÈçºÎ½øÐз´À¡¡£Äã¿ÉÒÔͨ¹ý IFTTT Á¬½Ó´óÁ¿µÄÎïÁªÍøÉ豸£¬Í¨¹ý
IFTTT Á¬½Ó LIFX ÖÇÄÜµÆ »òÕß Á¬½ÓÄã×Ô¼ºµÄ Nodebot¡£ÕâЩ³¢ÊÔ½ö½öÊÜÏÞÓÚÄãÓµÓеÄÉ豸£¡
|