Skip to content

Commit

Permalink
Add separate intent for setting the volume to a percentage (#81)
Browse files Browse the repository at this point in the history
* Add separate intent for setting the volume to a percentage

* Tests for volume percentage intent

* Extract common volume level validation to a function
  • Loading branch information
visadb committed Feb 26, 2021
1 parent 5ca7a6a commit 950afd1
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 48 deletions.
137 changes: 89 additions & 48 deletions connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,72 @@ app.intent('SkipPreviousIntent', {
}
);

// Handle volume level intent
// PUT to Spotify REST API
const setVolume = (volumePercent, req, res) => {
return request.put({
// Send new volume * 10 (convert to percentage)
url: "https://api.spotify.com/v1/me/player/volume?volume_percent=" + volumePercent,
// Send access token as bearer auth
auth: {
"bearer": req.getSession().details.user.accessToken
},
// Handle sending as JSON
json: true
}).catch((err) => {
if (err.statusCode === 403) res.say(req.__("Make sure your Spotify account is premium"));
if (err.statusCode === 404) {
res.say(req.__("I couldn't find any connect devices, check your Alexa app for information on connecting a device"));
res.card(connectDeviceCard(req));
}
});
};

const getAndValidateVolumePercentFromSlot = (req, res, isPercentIntent) => {
const slotName = isPercentIntent ? "VOLUMEPERCENT" : "VOLUMELEVEL"
// Check that the slot has a value
if (req.slot(slotName)) {
// Check if the slot is a number
if (!isNaN(req.slot(slotName))) {
var volumeValue = req.slot(slotName);
// Check that the volume is valid
if (volumeValue >= 0 && volumeValue <= (isPercentIntent ? 100 : 10)) {
return (isPercentIntent ? 1 : 10) * volumeValue;
}
else {
// If not valid volume
res.say(req.__(isPercentIntent
? "You can only set the volume percent between 0 and 100"
: "You can only set the volume between 0 and 10"));
// Keep session open
res.shouldEndSession(false);
return null;
}
}
else {
// Not a number
res.say(req.__(isPercentIntent
? "Try setting a volume percent between 0 and 100"
: "Try setting a volume between 0 and 10"))
.reprompt(req.__("What would you like to do?"));
// Keep session open
res.shouldEndSession(false);
return null;
}
}
else {
// No slot value
res.say(req.__("I couldn't work out the volume to use."))
.say(req.__(isPercentIntent
? "Try setting a volume percent between 0 and 100"
: "Try setting a volume between 0 and 10"))
.reprompt(req.__("What would you like to do?"));
// Keep session open
res.shouldEndSession(false);
return null;
}
};

// Handle 0-10 volume level intent
// Slot for new volume
app.intent('VolumeLevelIntent', {
"slots": {
Expand All @@ -207,54 +272,30 @@ app.intent('VolumeLevelIntent', {
function (req, res) {
// Check that request contains session
if (req.hasSession()) {
// Check that the slot has a value
if (req.slot("VOLUMELEVEL")) {
// Check if the slot is a number
if (!isNaN(req.slot("VOLUMELEVEL"))) {
var volumeLevel = req.slot("VOLUMELEVEL");
// Check that the volume is valid
if (volumeLevel >= 0 && volumeLevel <= 10) {
res.say(successSound);
// PUT to Spotify REST API
return request.put({
// Send new volume * 10 (convert to percentage)
url: "https://api.spotify.com/v1/me/player/volume?volume_percent=" + 10 * volumeLevel,
// Send access token as bearer auth
auth: {
"bearer": req.getSession().details.user.accessToken
},
// Handle sending as JSON
json: true
}).catch((err) => {
if (err.statusCode === 403) res.say(req.__("Make sure your Spotify account is premium"));
if (err.statusCode === 404) {
res.say(req.__("I couldn't find any connect devices, check your Alexa app for information on connecting a device"));
res.card(connectDeviceCard(req));
}
});
}
else {
// If not valid volume
res.say(req.__("You can only set the volume between 0 and 10"));
// Keep session open
res.shouldEndSession(false);
}
}
else {
// Not a number
res.say(req.__("Try setting a volume between 0 and 10"))
.reprompt(req.__("What would you like to do?"));
// Keep session open
res.shouldEndSession(false);
}
const volumePercent = getAndValidateVolumePercentFromSlot(req, res, false);
if (volumePercent !== null) {
return setVolume(volumePercent, req, res);
}
else {
// No slot value
res.say(req.__("I couldn't work out the volume to use."))
.say(req.__("Try setting a volume between 0 and 10"))
.reprompt(req.__("What would you like to do?"));
// Keep session open
res.shouldEndSession(false);
}
}
);

// Handle volume percent intent
// Slot for new volume
app.intent('VolumePercentIntent', {
"slots": {
"VOLUMEPERCENT": "AMAZON.NUMBER"
},
"utterances": [
"{set the|set|} volume {level|} {to|} {-|VOLUMEPERCENT} percent"
]
},
function (req, res) {
// Check that request contains session
if (req.hasSession()) {
const volumePercent = getAndValidateVolumePercentFromSlot(req, res, true);
if (volumePercent !== null) {
return setVolume(volumePercent, req, res);
}
}
}
Expand Down
82 changes: 82 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,88 @@ describe('VolumeLevelIntent', () => {
});
});

describe('VolumePercentIntent', () => {
test('should warn if no slot value', () => {
var req = generateRequest.intentRequest('VolumePercentIntent');
return getRequestSSML(req).then(res => {
expect(res).toContain("couldn't work out the volume to use");
});
});

test('should warn if not a number', () => {
var req = generateRequest.intentRequest('VolumePercentIntent', {
"VOLUMEPERCENT": {
"name": "VOLUMEPERCENT",
"value": "NaN"
}
});
return getRequestSSML(req).then(res => {
expect(res).toContain("Try setting a volume percent between 0 and 100");
});
});

test('should PUT correct volume to Spotify endpoint', () => {
var vol = Math.floor(Math.random() * 100);
var api = nock("https://api.spotify.com")
.put("/v1/me/player/volume")
.query({ "volume_percent": vol })
.reply(204);
var requested = pEvent(api, 'request')
.then(() => {
return api.isDone();
});
var req = generateRequest.intentRequest('VolumePercentIntent', {
"VOLUMEPERCENT": {
"name": "VOLUMEPERCENT",
"value": vol
}
});
connect.request(req);
return requested.then(res => {
expect(res).toBe(true);
});
});

test('should warn if volume outside of range', () => {
var slots = {
"VOLUMEPERCENT": {
"name": "VOLUMEPERCENT",
"value": 101
}
};
var req = generateRequest.intentRequest('VolumePercentIntent', slots);
return getRequestSSML(req).then(res => {
expect(res).toContain("You can only set the volume percent between 0 and 100");
});
});

test('should return nothing if no session', () => {
var req = generateRequest.intentRequestNoSession('VolumePercentIntent');
return connect.request(req).then(function (r) {
return r.response;
}).then(res => {
expect(res).not.toHaveProperty("outputSpeech");
});
});

test('should warn if not premium', () => {
var vol = Math.floor(Math.random() * 100);
nock("https://api.spotify.com")
.put("/v1/me/player/volume")
.query({ "volume_percent": vol })
.reply(403);
var req = generateRequest.intentRequest('VolumePercentIntent', {
"VOLUMEPERCENT": {
"name": "VOLUMEPERCENT",
"value": vol
}
});
return getRequestSSML(req).then(res => {
expect(res).toContain("Make sure your Spotify account is premium");
});
});
});

describe('GetDevicesIntent', () => {
test('should find no devices', () => {
nock("https://api.spotify.com")
Expand Down

0 comments on commit 950afd1

Please sign in to comment.