我已经实现了
Smart Home Actions
Set-top Box
文档。
这里我尝试添加
action.devices.traits.Volume
但出现以下错误
这是我的云函数代码
'use strict';
const functions = require('firebase-functions');
const {smarthome} = require('actions-on-google');
const {google} = require('googleapis');
const util = require('util');
const admin = require('firebase-admin');
// Initialize Firebase
admin.initializeApp();
const firebaseRef = admin.database().ref('/');
// Initialize Homegraph
const auth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/homegraph'],
});
const homegraph = google.homegraph({
version: 'v1',
auth: auth,
});
// Hardcoded user ID
const USER_ID = '123';
exports.login = functions.https.onRequest((request, response) => {
if (request.method === 'GET') {
functions.logger.log('Requesting login page');
response.send(`
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<h1>Account Linking</h1>
<form action="/login" method="post">
<input type="hidden"
name="responseurl" value="${request.query.responseurl}" />
<button type="submit" style="background-color: #4CAF50; /* Green */ border: none; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px;">
Clik to link your device
</button>
</form>
</body>
</html>
`);
} else if (request.method === 'POST') {
// Here, you should validate the user account.
// In this sample, we do not do that.
const responseurl = decodeURIComponent(request.body.responseurl);
functions.logger.log(`Redirect to ${responseurl}`);
return response.redirect(responseurl);
} else {
// Unsupported method
response.send(405, 'Method Not Allowed');
}
});
exports.fakeauth = functions.https.onRequest((request, response) => {
const responseurl = util.format('%s?code=%s&state=%s',
decodeURIComponent(request.query.redirect_uri), 'xxxxxx',
request.query.state);
functions.logger.log(`Set redirect as ${responseurl}`);
return response.redirect(
`/login?responseurl=${encodeURIComponent(responseurl)}`);
});
exports.faketoken = functions.https.onRequest((request, response) => {
const grantType = request.query.grant_type ?
request.query.grant_type : request.body.grant_type;
const secondsInDay = 86400; // 60 * 60 * 24
const HTTP_STATUS_OK = 200;
functions.logger.log(`Grant type ${grantType}`);
let obj;
if (grantType === 'authorization_code') {
obj = {
token_type: 'bearer',
access_token: '123access',
refresh_token: '123refresh',
expires_in: secondsInDay,
};
} else if (grantType === 'refresh_token') {
obj = {
token_type: 'bearer',
access_token: '123access',
expires_in: secondsInDay,
};
}
response.status(HTTP_STATUS_OK)
.json(obj);
});
const app = smarthome();
const boxDetails = [{
id: 'washer',
type: 'action.devices.types.SETTOP',
traits: [
'action.devices.traits.OnOff',
'action.devices.traits.Volume',
],
name: {
defaultNames: ['Test TV'],
name: 'Test TV',
nicknames: ['Test TV'],
},
deviceInfo: {
manufacturer: 'Test',
model: 'Test',
hwVersion: '1.0',
swVersion: '1.0.1',
},
willReportState: true,
attributes: {
pausable: true,
},
}]
app.onSync((body) => {
return {
requestId: body.requestId,
payload: {
agentUserId: USER_ID,
devices: boxDetails,
},
};
});
const queryFirebase = async (deviceId) => {
const snapshot = await firebaseRef.child(deviceId).once('value');
const snapshotVal = snapshot.val();
return {
on: snapshotVal.OnOff.on,
volumeLevel: 20
};
};
const queryDevice = async (deviceId) => {
const data = await queryFirebase(deviceId);
return {
on: data.on,
currentVolume : 20,
};
};
app.onQuery(async (body) => {
const {requestId} = body;
const payload = {
devices: {},
};
const queryPromises = [];
const intent = body.inputs[0];
for (const device of intent.payload.devices) {
const deviceId = device.id;
queryPromises.push(queryDevice(deviceId)
.then((data) => {
payload.devices[deviceId] = data;
},
));
}
await Promise.all(queryPromises);
return {
requestId: requestId,
payload: payload,
};
});
const updateDevice = async (execution, deviceId) => {
functions.logger.info(`deviceId ${deviceId}`);
const {params, command} = execution;
let state; let ref;
switch (command) {
case 'action.devices.commands.OnOff':
state = {on: params.on};
ref = firebaseRef.child(deviceId).child('OnOff');
break;
case 'action.devices.commands.setVolume':
state = {currentVolume: params.volumeLevel};
ref = firebaseRef.child(deviceId).child('Volume');
break;
}
return ref.update(state)
.then(() => state);
};
app.onExecute(async (body) => {
const {requestId} = body;
const result = {
ids: [],
status: 'SUCCESS',
states: {
online: true,
},
};
const executePromises = [];
const intent = body.inputs[0];
for (const command of intent.payload.commands) {
for (const device of command.devices) {
for (const execution of command.execution) {
executePromises.push(
updateDevice(execution, device.id)
.then((data) => {
result.ids.push(device.id);
Object.assign(result.states, data);
})
.catch(() => functions.logger.error('EXECUTE', device.id)),
);
}
}
}
await Promise.all(executePromises);
return {
requestId: requestId,
payload: {
commands: [result],
},
};
});
app.onDisconnect((body, headers) => {
functions.logger.log('User account unlinked from Google Assistant');
return {};
});
exports.smarthome = functions.https.onRequest(app);
exports.requestsync = functions.https.onRequest(async (request, response) => {
response.set('Access-Control-Allow-Origin', '*');
functions.logger.info(`Request SYNC for user ${USER_ID}`);
try {
const res = await homegraph.devices.requestSync({
requestBody: {
agentUserId: USER_ID,
},
});
functions.logger.info('Request sync response:', res.status, res.data);
response.json(res.data);
} catch (err) {
functions.logger.error(err);
response.status(500).send(`Error requesting sync: ${err}`);
}
});
exports.reportstate = functions.database.ref('{deviceId}').onWrite(
async (change, context) => {
functions.logger.info('Firebase write event triggered Report State');
const snapshot = change.after.val();
const requestBody = {
requestId: 'ff36a3cc',
agentUserId: USER_ID,
payload: {
devices: {
states: {
[context.params.deviceId]: {
on: snapshot.OnOff.on,
volumeLevel : 40,
},
},
},
},
};
const res = await homegraph.devices.reportStateAndNotification({
requestBody,
});
functions.logger.info('Report state response:', res.status, res.data);
});
exports.updatestate = functions.https.onRequest((request, response) => {
firebaseRef.child('set-to-box').update({
OnOff: {
on: request.body.on,
},
Volume:{
volumeLevel :40,
}
});
return response.status(200).end();
});
您能给我推荐一些示例代码吗
action.devices.types.SETTOP
我检查了很多块,但我得到了同样的错误
查看您的云函数代码后,我发现该问题可能与您如何处理action.devices.traits.Volume的updateDevice函数有关。在 updateDevice 函数中,您正确处理了 action.devices.commands.OnOff 命令,但处理 action.devices.commands.setVolume 命令的方式似乎存在潜在问题。
在此代码块中,您正在更新“Volume”特征的状态,但它在 JSON 响应中的结构可能不正确,从而导致错误。
case 'action.devices.commands.setVolume':
state = {currentVolume: params.volumeLevel};
ref = firebaseRef.child(deviceId).child('Volume');
break;
以下是修改 updateDevice 函数的方法:
const updateDevice = async (execution, deviceId) => {
functions.logger.info(`deviceId ${deviceId}`);
const { params, command } = execution;
let state;
let ref;
switch (command) {
case 'action.devices.commands.OnOff':
state = { on: params.on };
ref = firebaseRef.child(deviceId).child('OnOff');
break;
case 'action.devices.commands.setVolume':
state = { currentVolume: params.volumeLevel };
ref = firebaseRef.child(deviceId).child('Volume');
break;
}
// Include the device in the response
const device = {
id: deviceId,
states: state,
};
return ref
.update(state)
.then(() => device);
};