I have built a Google Smart Home Action using the Local Fulfillment SDK as outlined in the following articles:
- https://developers.google.com/assistant/smarthome/develop/local
- https://github.com/actions-on-google/create-local-home-app
I am using UDP for device discovery and my Google Nest Hub can successfully scan and detect the virtual device running on my laptop as well as download the JS of the local app.
My Local Home SDK configuration is as follows - Local Home SDK Configuration.
When the Nest Hub executes the IDENTIFY intent of my app handler I am receiving the following error(s):
[smarthome.DeviceManager] Intent handler failed with error: Buffer is not defined
[smarthome.DeviceManager] Got a rejected promise Buffer is not defined
This appears to be a Node.JS error as opposed to something specific to the local SDK app itself. Below is the code of my local app.
/// <reference types="@google/local-home-sdk" />
import App = smarthome.App;
import Constants = smarthome.Constants;
import DataFlow = smarthome.DataFlow;
import Execute = smarthome.Execute;
import Intents = smarthome.Intents;
import IntentFlow = smarthome.IntentFlow;
const SERVER_PORT = 3388;
interface ILightParams {
on?: boolean,
brightness?: number
}
class LocalExecutionApp {
constructor(private readonly app: App) { }
identifyHandler(request: IntentFlow.IdentifyRequest):
Promise<IntentFlow.IdentifyResponse> {
console.log("IDENTIFY intent: " + JSON.stringify(request, null, 2));
const scanData = request.inputs[0].payload.device.udpScanData;
console.log("SCANDATA: " + JSON.stringify(scanData));
if (!scanData) {
const err = new IntentFlow.HandlerError(request.requestId, 'invalid_request', 'Invalid scan data');
return Promise.reject(err);
}
const localDeviceId = Buffer.from(scanData.data, 'hex');
console.log("ScanData Local Device: " + localDeviceId);
const response: IntentFlow.IdentifyResponse = {
intent: Intents.IDENTIFY,
requestId: request.requestId,
payload: {
device: {
// id: localDeviceId.toString(),
id: 'sample-device',
verificationId: localDeviceId.toString(),
}
}
};
console.log("IDENTIFY response: " + JSON.stringify(response, null, 2));
return Promise.resolve(response);
}
executeHandler(request: IntentFlow.ExecuteRequest):
Promise<IntentFlow.ExecuteResponse> {
console.log("EXECUTE intent: " + JSON.stringify(request, null, 2));
const command = request.inputs[0].payload.commands[0];
const execution = command.execution[0];
const response = new Execute.Response.Builder()
.setRequestId(request.requestId);
const promises: Promise<void>[] = command.devices.map((device) => {
console.log("Handling EXECUTE intent for device: " + JSON.stringify(device));
// Convert execution params to a string for the local device
const params = execution.params as ILightParams;
const payload = this.getDataForCommand(execution.command, params);
// Create a command to send over the local network
const radioCommand = new DataFlow.HttpRequestData();
radioCommand.requestId = request.requestId;
radioCommand.deviceId = device.id;
radioCommand.data = JSON.stringify(payload);
radioCommand.dataType = 'application/json';
radioCommand.port = SERVER_PORT;
radioCommand.method = Constants.HttpOperation.POST;
radioCommand.isSecure = false;
console.log("Sending HTTP request to the smart home device:", payload);
return this.app.getDeviceManager()
.send(radioCommand)
.then(() => {
const state = {online: true};
response.setSuccessState(device.id, Object.assign(state, params));
console.log(`Command successfully sent to ${device.id}`);
})
.catch((e: IntentFlow.HandlerError) => {
e.errorCode = e.errorCode || 'invalid_request';
response.setErrorState(device.id, e.errorCode);
console.error('An error occurred sending the command', e.errorCode);
});
});
return Promise.all(promises)
.then(() => {
return response.build();
})
.catch((e) => {
const err = new IntentFlow.HandlerError(request.requestId, 'invalid_request', e.message);
return Promise.reject(err);
});
}
/**
* Convert execution request into a local device command
*/
getDataForCommand(command: string, params: ILightParams): unknown {
switch (command) {
case 'action.devices.commands.OnOff':
return {
on: params.on ? true : false
};
default:
console.error('Unknown command', command);
return {};
}
}
}
const localHomeSdk = new App('1.0.0');
const localApp = new LocalExecutionApp(localHomeSdk);
localHomeSdk
.onIdentify(localApp.identifyHandler.bind(localApp))
.onExecute(localApp.executeHandler.bind(localApp))
.listen()
.then(() => console.log('Ready'))
.catch((e: Error) => console.error(e));
Any insight on why this error is occurring is greatly appreciated.
Cheers.