Skip to content

Commit

Permalink
Add volume setting to play notification
Browse files Browse the repository at this point in the history
  • Loading branch information
bertrandda committed Dec 3, 2024
1 parent 6a1779d commit 5298664
Show file tree
Hide file tree
Showing 13 changed files with 61 additions and 15 deletions.
1 change: 1 addition & 0 deletions front/src/config/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -2021,6 +2021,7 @@
"description": "Diese Aktion lässt Gladys auf dem ausgewählten Lautsprecher sprechen.",
"needGladysPlus": "Erfordert Gladys Plus, da die Text-to-Speech-APIs kostenpflichtig sind.",
"deviceLabel": "Lautsprecher",
"volumeLabel": "Volumen",
"textLabel": "Nachricht zum Sprechen auf dem Lautsprecher",
"variablesExplanation": "Um eine Variable einzufügen, gib \"{{\" ein. Hinweis: Du musst vor diesem Block eine Variable in einer \"Letzten Zustand abrufen\"-Aktion definiert haben."
}
Expand Down
1 change: 1 addition & 0 deletions front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2021,6 +2021,7 @@
"description": "This action will make Gladys speak on the selected speaker.",
"needGladysPlus": "Requires Gladys Plus as Text-To-Speech APIs are paid.",
"deviceLabel": "Speaker",
"volumeLabel": "Volume",
"textLabel": "Message to speak on the speaker",
"variablesExplanation": "To inject a variable, type '{{ '. Note: You must have defined a variable beforehand in a 'Retrieve Last State' action placed before this message block."
}
Expand Down
1 change: 1 addition & 0 deletions front/src/config/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2021,6 +2021,7 @@
"description": "Cette action fera parler Gladys sur l'enceinte sélectionnée.",
"needGladysPlus": "Nécessite Gladys Plus car les API de \"Text To Speech\" sont payantes.",
"deviceLabel": "Enceinte",
"volumeLabel": "Volume",
"textLabel": "Message à dire sur l'enceinte",
"variablesExplanation": "Pour injecter une variable, tapez '{{ '. Attention, vous devez avoir défini une variable auparavant dans une action 'Récupérer le dernier état' placé avant ce bloc message."
}
Expand Down
21 changes: 21 additions & 0 deletions front/src/routes/scene/edit-scene/actions/PlayNotification.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ class PlayNotification extends Component {
console.error(e);
}
};
updateVolume = e => {
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'volume', e.target.value);
};
updateText = text => {
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'text', text);
};
Expand Down Expand Up @@ -84,6 +87,24 @@ class PlayNotification extends Component {
onChange={this.handleDeviceChange}
/>
</div>
<div class="form-group">
<label class="form-label">
<Text id="editScene.actionsCard.playNotification.volumeLabel" />
<span class="form-required">
<Text id="global.requiredField" />
</span>
</label>
<input type="text" class="form-control" value={props.action.volume} disabled />
<input
type="range"
value={props.action.volume}
onChange={this.updateVolume}
class="form-control custom-range"
step="1"
min={0}
max={100}
/>
</div>
<div class="form-group">
<label class="form-label">
<Text id="editScene.actionsCard.playNotification.textLabel" />{' '}
Expand Down
5 changes: 3 additions & 2 deletions server/lib/device/device.setValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ const { NotFoundError } = require('../../utils/coreErrors');
* @param {object} device - The device to control.
* @param {object} deviceFeature - The deviceFeature to control.
* @param {string|number} value - The new state to set.
* @param {object} options - Optional configs.
* @example
* device.setValue(device, deviceFeature);
*/
async function setValue(device, deviceFeature, value) {
async function setValue(device, deviceFeature, value, options = {}) {
const service = this.serviceManager.getService(device.service.name);
if (service === null) {
throw new NotFoundError(`Service ${device.service.name} was not found.`);
}
if (typeof get(service, 'device.setValue') !== 'function') {
throw new NotFoundError(`Function device.setValue in service ${device.service.name} does not exist.`);
}
await service.device.setValue(device, deviceFeature, value);
await service.device.setValue(device, deviceFeature, value, options);
// If device has feedback, the feedback will be sent and saved
// If value is a string, no need to save it
// @ts-ignore
Expand Down
2 changes: 1 addition & 1 deletion server/lib/scene/scene.actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ const actionsFunc = {
// Get TTS URL
const { url } = await self.gateway.getTTSApiUrl({ text: messageWithVariables });
// Play TTS Notification on device
await self.device.setValue(device, deviceFeature, url);
await self.device.setValue(device, deviceFeature, url, { volume: action.volume });
},
[ACTIONS.SMS.SEND]: async (self, action, scope) => {
const freeMobileService = self.service.getService('free-mobile');
Expand Down
4 changes: 4 additions & 0 deletions server/models/scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ const actionSchema = Joi.array().items(
message: Joi.string().allow(''),
blinking_time: Joi.number(),
blinking_speed: Joi.string().valid('slow', 'medium', 'fast'),
volume: Joi.number()
.integer()
.max(100)
.min(0),
}),
),
);
Expand Down
7 changes: 4 additions & 3 deletions server/services/airplay/lib/airplay.setValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ const logger = require('../../../utils/logger');
* @param {object} device - Updated Gladys device.
* @param {object} deviceFeature - Updated Gladys device feature.
* @param {string|number} value - The new device feature value.
* @param {object} options - Optional configs.
* @example
* setValue(device, deviceFeature, 0);
* setValue(device, deviceFeature, 0, 30);
*/
async function setValue(device, deviceFeature, value) {
async function setValue(device, deviceFeature, value, options) {
const deviceName = device.external_id.split(':')[1];
const ipAddress = this.deviceIpAddresses.get(deviceName);
if (!ipAddress) {
Expand All @@ -19,7 +20,7 @@ async function setValue(device, deviceFeature, value) {
if (deviceFeature.type === DEVICE_FEATURE_TYPES.MUSIC.PLAY_NOTIFICATION) {
const client = new this.Airtunes();
const airplayDevice = client.add(ipAddress, {
volume: 70,
volume: options.volume || 70,
});
let decodeProcess;

Expand Down
12 changes: 11 additions & 1 deletion server/services/google-cast/lib/google_cast.setValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ const logger = require('../../../utils/logger');
* @param {object} device - Updated Gladys device.
* @param {object} deviceFeature - Updated Gladys device feature.
* @param {string|number} value - The new device feature value.
* @param {object} options - Optional configs.
* @example
* setValue(device, deviceFeature, 0);
*/
async function setValue(device, deviceFeature, value) {
async function setValue(device, deviceFeature, value, options) {
const deviceName = device.external_id.split(':')[1];
const ipAddress = this.deviceIpAddresses.get(deviceName);
if (!ipAddress) {
Expand All @@ -22,6 +23,15 @@ async function setValue(device, deviceFeature, value) {
client.connect(ipAddress, () => {
logger.debug('Google Cast Connected, launching app ...');

if (options.volume) {
client.setVolume({ level: options.volume / 100 }, (err, newvol) => {
if (err) {
logger.debug('there was an error setting the volume');

Check warning on line 29 in server/services/google-cast/lib/google_cast.setValue.js

View check run for this annotation

Codecov / codecov/patch

server/services/google-cast/lib/google_cast.setValue.js#L29

Added line #L29 was not covered by tests
}
logger.debug('volume changed to %s', newvol);
});
}

client.launch(DefaultMediaReceiver, (err, player) => {
const media = {
// Here you can plug an URL to any mp4, webm, mp3 or jpg file with the proper contentType.
Expand Down
5 changes: 3 additions & 2 deletions server/services/sonos/lib/sonos.setValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ const { DEVICE_FEATURE_TYPES } = require('../../../utils/constants');
* @param {object} device - Updated Gladys device.
* @param {object} deviceFeature - Updated Gladys device feature.
* @param {string|number} value - The new device feature value.
* @param {object} options - Optional configs.
* @example
* setValue(device, deviceFeature, 0);
*/
async function setValue(device, deviceFeature, value) {
async function setValue(device, deviceFeature, value, options) {
const deviceUuid = device.external_id.split(':')[1];
const sonosDevice = this.manager.Devices.find((d) => d.uuid === deviceUuid);
if (deviceFeature.type === DEVICE_FEATURE_TYPES.MUSIC.PLAY) {
Expand All @@ -30,7 +31,7 @@ async function setValue(device, deviceFeature, value) {
await sonosDevice.PlayNotification({
trackUri: value,
onlyWhenPlaying: false,
volume: 45, // Set the volume for the notification (and revert back afterwards)
volume: options.volume || 45, // Set the volume for the notification (and revert back afterwards)
timeout: 20, // If the events don't work (to see when it stops playing) or if you turned on a stream,
// it will revert back after this amount of seconds.
delayMs: 700, // Pause between commands in ms, (when sonos fails to play sort notification sounds).
Expand Down
4 changes: 2 additions & 2 deletions server/test/services/airplay/lib/airplay.setValue.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ describe('AirplayHandler.setValue', () => {
airplayHandler.scanTimeout = 1;
const devices = await airplayHandler.scan();
const device = devices[0];
await airplayHandler.setValue(device, device.features[0], 'http://play-url.com');
await airplayHandler.setValue(device, device.features[0], 'http://play-url.com', { volume: 30 });
sinon.assert.calledOnce(pipe);
});
it('should return device not found', async () => {
airplayHandler.scanTimeout = 1;
const device = {
external_id: 'airplay:toto',
};
const promise = airplayHandler.setValue(device, {}, 'http://play-url.com');
const promise = airplayHandler.setValue(device, {}, 'http://play-url.com', { volume: 30 });
await assert.isRejected(promise, 'Device not found on network');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ class GoogleCastClient {
cb({ message: 'this is an error' });
}

// eslint-disable-next-line class-methods-use-this
setVolume(volume, cb) {
cb(null, 30);
}

// eslint-disable-next-line class-methods-use-this
close() {}
}
Expand Down Expand Up @@ -80,14 +85,14 @@ describe('GoogleCastHandler.setValue', () => {
googleCastHandler.scanTimeout = 1;
const devices = await googleCastHandler.scan();
const device = devices[0];
await googleCastHandler.setValue(device, device.features[0], 'http://play-url.com');
await googleCastHandler.setValue(device, device.features[0], 'http://play-url.com', { volume: 30 });
});
it('should return device not found', async () => {
googleCastHandler.scanTimeout = 1;
const device = {
external_id: 'google_cast:toto',
};
const promise = googleCastHandler.setValue(device, {}, 'http://play-url.com');
const promise = googleCastHandler.setValue(device, {}, 'http://play-url.com', { volume: 30 });
await assert.isRejected(promise, 'Device not found on network');
});
});
4 changes: 2 additions & 2 deletions server/test/services/sonos/lib/sonos.setValue.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,12 @@ describe('SonosHandler.setValue', () => {
read_only: false,
has_feedback: false,
};
await sonosHandler.setValue(device, deviceFeature, 'http://test.com');
await sonosHandler.setValue(device, deviceFeature, 'http://test.com', { volume: 30 });
assert.calledWith(devicePlayNotification, {
onlyWhenPlaying: false,
timeout: 20,
trackUri: 'http://test.com',
volume: 45,
volume: 30,
delayMs: 700,
});
});
Expand Down

0 comments on commit 5298664

Please sign in to comment.