I have implement Smart Home Actions for Set-top Box Documentation.
Here I have trying to add action.devices.traits.Volume but getting below error
this is my cloud function code
'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();
});
Can you please suggest me any sample code for
action.devices.types.SETTOP
I checked many blocks, but I am getting same error

After reviewing your cloud function code, I found that the issue may be related to how you handle the updateDevice function for the action.devices.traits.Volume. In your updateDevice function, you correctly handle the action.devices.commands.OnOff command, but it seems there's a potential problem with how you handle the action.devices.commands.setVolume command.
In this code block, you are updating the state for the "Volume" trait, but it might not be structured correctly in the JSON response, causing the error.
Here's how you can modify the updateDevice function: