diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index 9a185284f7..853c97c69c 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -1638,6 +1638,13 @@ "messageLabel": "Message", "variablesExplanation": "To inject a variable in the text, press '{{ '. To set a variable value, you need to use the 'Get device value' box before this one.", "messagePlaceholder": "My message" + }, + "playNotification": { + "description": "This action will make Gladys speak on the selected speaker.", + "needGladysPlus": "Requires Gladys Plus as Text-To-Speech APIs are paid.", + "deviceLabel": "Speaker", + "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." } }, "actions": { @@ -1695,6 +1702,9 @@ }, "mqtt": { "send": "Send MQTT Message" + }, + "music": { + "play-notification": "Talk on a speaker" } }, "variables": { @@ -2743,7 +2753,8 @@ "pause": "Music pause button", "previous": "Music previous button", "next": "Music next button", - "playback_state": "Music playback state" + "playback_state": "Music playback state", + "play_notification": "Broadcast a notification" }, "unknown": { "shortCategoryName": "Unknown", diff --git a/front/src/config/i18n/fr.json b/front/src/config/i18n/fr.json index 4cc234b364..8a4878b471 100644 --- a/front/src/config/i18n/fr.json +++ b/front/src/config/i18n/fr.json @@ -1640,6 +1640,13 @@ "messageLabel": "Message", "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.", "messagePlaceholder": "Mon message" + }, + "playNotification": { + "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", + "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." } }, "actions": { @@ -1697,6 +1704,9 @@ }, "mqtt": { "send": "Envoyer un message MQTT" + }, + "music": { + "play-notification": "Parler sur une enceinte" } }, "variables": { @@ -2745,7 +2755,8 @@ "pause": "Bouton pause musique", "previous": "Bouton précédent musique", "next": "Bouton suivant musique", - "playback_state": "Etat de la lecture musique" + "playback_state": "Etat de la lecture musique", + "play_notification": "Diffuser une notification" }, "unknown": { "shortCategoryName": "Inconnu", diff --git a/front/src/routes/scene/edit-scene/ActionCard.jsx b/front/src/routes/scene/edit-scene/ActionCard.jsx index a3e0305611..e8d9ca58e7 100644 --- a/front/src/routes/scene/edit-scene/ActionCard.jsx +++ b/front/src/routes/scene/edit-scene/ActionCard.jsx @@ -28,6 +28,7 @@ import SendMessageCameraParams from './actions/SendMessageCameraParams'; import CheckAlarmMode from './actions/CheckAlarmMode'; import SetAlarmMode from './actions/SetAlarmMode'; import SendMqttMessage from './actions/SendMqttMessage'; +import PlayNotification from './actions/PlayNotification'; const deleteActionFromColumn = (columnIndex, rowIndex, deleteAction) => () => { deleteAction(columnIndex, rowIndex); @@ -58,7 +59,8 @@ const ACTION_ICON = { [ACTIONS.ECOWATT.CONDITION]: 'fe fe-zap', [ACTIONS.ALARM.CHECK_ALARM_MODE]: 'fe fe-bell', [ACTIONS.ALARM.SET_ALARM_MODE]: 'fe fe-bell', - [ACTIONS.MQTT.SEND]: 'fe fe-message-square' + [ACTIONS.MQTT.SEND]: 'fe fe-message-square', + [ACTIONS.MUSIC.PLAY_NOTIFICATION]: 'fe fe-speaker' }; const ACTION_CARD_TYPE = 'ACTION_CARD_TYPE'; @@ -367,6 +369,17 @@ const ActionCard = ({ children, ...props }) => { triggersVariables={props.triggersVariables} /> )} + {props.action.type === ACTIONS.MUSIC.PLAY_NOTIFICATION && ( + + )} diff --git a/front/src/routes/scene/edit-scene/actions/ChooseActionTypeCard.jsx b/front/src/routes/scene/edit-scene/actions/ChooseActionTypeCard.jsx index 8a542024a9..17e8f5ed6e 100644 --- a/front/src/routes/scene/edit-scene/actions/ChooseActionTypeCard.jsx +++ b/front/src/routes/scene/edit-scene/actions/ChooseActionTypeCard.jsx @@ -30,7 +30,8 @@ const ACTION_LIST = [ ACTIONS.ECOWATT.CONDITION, ACTIONS.ALARM.CHECK_ALARM_MODE, ACTIONS.ALARM.SET_ALARM_MODE, - ACTIONS.MQTT.SEND + ACTIONS.MQTT.SEND, + ACTIONS.MUSIC.PLAY_NOTIFICATION ]; const TRANSLATIONS = ACTION_LIST.reduce((acc, action) => { diff --git a/front/src/routes/scene/edit-scene/actions/PlayNotification.jsx b/front/src/routes/scene/edit-scene/actions/PlayNotification.jsx new file mode 100644 index 0000000000..96cb7eff9c --- /dev/null +++ b/front/src/routes/scene/edit-scene/actions/PlayNotification.jsx @@ -0,0 +1,115 @@ +import Select from 'react-select'; +import { Component } from 'preact'; +import { connect } from 'unistore/preact'; +import { Text } from 'preact-i18n'; + +import { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } from '../../../../../../server/utils/constants'; + +import TextWithVariablesInjected from '../../../../components/scene/TextWithVariablesInjected'; + +class PlayNotification extends Component { + getOptions = async () => { + try { + const devices = await this.props.httpClient.get('/api/v1/device', { + device_feature_category: DEVICE_FEATURE_CATEGORIES.MUSIC, + device_feature_type: DEVICE_FEATURE_TYPES.MUSIC.PLAY_NOTIFICATION + }); + const devicesOptions = devices.map(device => ({ + value: device.selector, + label: device.name + })); + + await this.setState({ devicesOptions }); + this.refreshSelectedOptions(this.props); + return devicesOptions; + } catch (e) { + console.error(e); + } + }; + updateText = text => { + this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'text', text); + }; + handleDeviceChange = selectedOption => { + if (selectedOption && selectedOption.value) { + this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'device', selectedOption.value); + } else { + this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'device', null); + } + }; + + refreshSelectedOptions = nextProps => { + let selectedDeviceFeatureOption = ''; + if (nextProps.action.device && this.state.devicesOptions) { + const deviceFeatureOption = this.state.devicesOptions.find(option => option.value === nextProps.action.device); + + if (deviceFeatureOption) { + selectedDeviceFeatureOption = deviceFeatureOption; + } + } + this.setState({ selectedDeviceFeatureOption }); + }; + constructor(props) { + super(props); + this.props = props; + this.state = { + selectedDeviceFeatureOption: '' + }; + } + componentDidMount() { + this.getOptions(); + } + componentWillReceiveProps(nextProps) { + this.refreshSelectedOptions(nextProps); + } + render(props, { selectedDeviceFeatureOption, devicesOptions }) { + return ( +
+

+ +

+
+ +