回调& Node&的异步问题LAMBDA

时间:2017-06-13 06:46:09

标签: node.js aws-lambda alexa alexa-skills-kit

我正在为Alexa Smart Home技能创建一个lambda函数来控制我的接收器。我正在使用亚马逊提供的样板代码示例代码,但我是节点的新手,我在等待我的回调设置响应方面遇到了很多麻烦。我一直在尝试操作,好像它们的总体结构在这里是正确的。

我可以调用它并且它执行POST请求就好了,但它总是返回null(Alexa不喜欢)。它没有在response.on('end'循环中记录任何内容。看起来它正好通过marantzAPI调用爆炸。我尝试在marantzAPI函数中使用回调(命令,回调),我最终得到了相同的结果我觉得有些东西我不会来这里。

这是lambda函数:

'use strict';

var http = require('http');
var qs = require('querystring');

/**
 * We're not discovering our devices, we're just hardcoding them.  Easy!
 */
const USER_DEVICES = [
    {
        applianceId: 'marantz-sr6010-shield',
        manufacturerName: 'Marantz nVidia',
        modelName: 'SR6010 Shield',
        version: '1.0',
        friendlyName: 'Shield',
        friendlyDescription: 'nVidia Shield via Marantz SR6010',
        isReachable: true,
        actions: ['turnOn', 'turnOff'],
    }
];

/**
 * Utility functions
 */

function log(title, msg) {
    console.log(`[${title}] ${msg}`);
}

/**
 * Generate a unique message ID
 *
 * https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
 * This isn't UUID V4 but it's good enough for what we're doing
 */
function generateMessageID() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

/**
 * Generate a response message
 *
 * @param {string} name - Directive name
 * @param {Object} payload - Any special payload required for the response
 * @returns {Object} Response object
 */
function generateResponse(name, payload) {
    return {
        header: {
            messageId: generateMessageID(),
            name: name,
            namespace: 'Alexa.ConnectedHome.Control',
            payloadVersion: '2',
        },
        payload: payload,
    };
}

/**
 * This is a lot easier when I'm just hard-coding my devices
 */
function getDevicesFromPartnerCloud() {
    return USER_DEVICES;
}


/**
 * The meat and potatoes, I'm butchering just the things I Need from the solid work done by Nathan Totten:
 * https://github.com/ntotten/marantz-avr/blob/master/lib/avreciever.js
 */

function marantzAPI(commands, apiCallback) {
    log('DEBUG', `MarantzAPI Invoked: ` + JSON.stringify(commands));
    var postData = {};

    // format commands for the Marantz POST (cmd0: cmd1: etc)
    // note: may need to send commands one at a time??
    for (var i=0; i<commands.length; i++) {
        postData['cmd' + i] = commands[i];
    }

    log('DEBUG', `MarantzAPI POST Data: ` + qs.stringify(postData));

    var serverError = function (e) {
        log('Error', e.message);
        apiCallback(generateResponse('UnexpectedInformationReceivedError', e.message));
    };    

    var httpCallback = function(response) {
        response.on('end', function () {
            log('DEBUG', `API Request Complete`);
            apiCallback(generateResponse('APIRequestComplete', postData));
        });        

        response.on('error', serverError); 
    };

    var apiRequest = http.request({
        hostname: process.env.receiverIp,
        path: '/MainZone/index.put.asp',
        port: process.env.receiverPort,
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(qs.stringify(postData))
        },
    }, httpCallback);  

    apiRequest.on('error', serverError);
    apiRequest.write(qs.stringify(postData));
    apiRequest.end();    
} 


/**
 * Main logic
 */

function handleDiscovery(request, callback) {
    log('DEBUG', `Discovery Request: ${JSON.stringify(request)}`);

    const userAccessToken = request.payload.accessToken.trim();

    const response = {
        header: {
            messageId: generateMessageID(),
            name: 'DiscoverAppliancesResponse',
            namespace: 'Alexa.ConnectedHome.Discovery',
            payloadVersion: '2',
        },
        payload: {
            discoveredAppliances: getDevicesFromPartnerCloud(userAccessToken),
        },
    };

    log('DEBUG', `Discovery Response: ${JSON.stringify(response)}`);

    callback(null, response);
}

function handleControl(request, callback) {
    log('DEBUG', `Control Request: ${JSON.stringify(request)}`);

    const userAccessToken = request.payload.accessToken.trim();
    const applianceId = request.payload.appliance.applianceId;

    let response;
    var commands = [];

    switch (request.header.name) {
        case 'TurnOnRequest':
            // turn on the device
            commands.push('PutZone_OnOff/ON');

            // set the input
            switch (applianceId) {
                case 'marantz-sr6010-shield':
                    commands.push('PutZone_InputFunction/MPLAY');
                    break;
            }

            // I guess?  Not even sure if it actually does all this.
            commands.push('aspMainZone_WebUpdateStatus/');

            marantzAPI(commands, function(response) {
                callback(null, response);
            });
            break;

        default: {
            log('ERROR', `No supported directive name: ${request.header.name}`);
            callback(null, generateResponse('UnsupportedOperationError', {}));
            return;
        }
    }

    // I think I need to remove these, because response is not set at execution time
    // log('DEBUG', `Control Confirmation: ${JSON.stringify(response)}`);
    // callback(null, response);
}

exports.handler = (request, context, callback) => {

    switch (request.header.namespace) {
        case 'Alexa.ConnectedHome.Discovery':
            handleDiscovery(request, callback);
            break;

        case 'Alexa.ConnectedHome.Control':
            handleControl(request, callback);
            break;

        default: {
            const errorMessage = `No supported namespace: ${request.header.namespace}`;
            log('ERROR', errorMessage);
            callback(new Error(errorMessage));
        }
    }
};

2 个答案:

答案 0 :(得分:0)

节点js环境默认情况下是异步环境,并且您的+-----------+-------------------------------------------+ | DATE_OUT | PERC | +-----------+-------------------------------------------+ | 01-JAN-17 | .5161290322580645161290322580645161290323 | | 01-FEB-17 | 1 | | 01-MAR-17 | .0322580645161290322580645161290322580645 | +-----------+-------------------------------------------+ 方法异步执行,因此在执行此函数时,控件不会停在那里并继续执行下一个函数并最终执行{{ 1}}虽然previouse函数尚未完成,导致空白响应。这是我建议您访问This SO thread以获得详细说明和可能的解决方案的最合适的答案。

答案 1 :(得分:0)

我已经开始工作了。这是我更新的marantzAPI函数。看起来像response.on('数据'对于HTTP请求的成功至关重要?不知道。

function marantzAPI(commands, apiCallback) {
    var postData = {};

    // format commands for the Marantz POST (cmd0: cmd1: etc)
    // note: may need to send commands one at a time??
    for (var i=0; i<commands.length; i++) {
        postData['cmd' + i] = commands[i];
    }

    log('DEBUG', `MarantzAPI Called w Data: ` + qs.stringify(postData));

    var serverError = function (e) {
        log('Error', e.message);
        apiCallback(false, e.message);
    };    

    var apiRequest = http.request({
        hostname: process.env.receiverIp,
        path: '/MainZone/index.put.asp',
        port: process.env.receiverPort,
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(qs.stringify(postData))
        },
    }, function(response) {

        response.setEncoding('utf8');
        response.on('data', function (chunk) {
            log('DEBUG', 'CHUNK RECEIVED');
        });

        response.on('end', function () {
            log('DEBUG', `API Request Complete`);
            apiCallback(true, '');
        });        

        response.on('error', serverError); 
    });

    apiRequest.on('error', serverError);
    apiRequest.write(qs.stringify(postData));
    apiRequest.end();    
}