diff --git a/front/src/config/i18n/de.json b/front/src/config/i18n/de.json index 4703487ea2..4a0aa80f9f 100644 --- a/front/src/config/i18n/de.json +++ b/front/src/config/i18n/de.json @@ -3282,6 +3282,12 @@ "label": "Wähle eine Szene aus", "notice": "Die Auslösebedingungen der aufgerufenen Szene werden ignoriert. Gladys gerät nicht in eine Endlosschleife, wenn eine Szene eine andere Szene aufruft oder umgekehrt: Jede Szene wird einmal ausgeführt." }, + "setSceneActive": { + "label": "Wähle eine Szene aus", + "disabledScene": "deaktiviert", + "enableNotice": "Die ausgewählte Szene wird aktiviert: Ihre Auslöser werden wieder überwacht. Eine Szene kann sich selbst aktivieren.", + "disableNotice": "Die ausgewählte Szene wird deaktiviert: Ihre Auslöser werden nicht mehr überwacht, bis sie wieder aktiviert wird. Eine Szene kann sich selbst deaktivieren, zum Beispiel um nur einmal ausgeführt zu werden, bis eine andere Szene sie wieder aktiviert." + }, "houseEmptyOrNot": { "houseIsEmptyDescription": "Wenn das Zuhause leer ist, wird die Szene fortgesetzt. Andernfalls wird die Szene gestoppt.", "houseIsNotEmptyDescription": "Wenn das Zuhause nicht leer ist, wird die Szene fortgesetzt. Andernfalls wird die Szene gestoppt.", @@ -3395,7 +3401,9 @@ "toggle": "Schalter umschalten" }, "scene": { - "start": "Szene starten" + "start": "Szene starten", + "enable": "Eine Szene aktivieren", + "disable": "Eine Szene deaktivieren" }, "service": { "start": "Dienst starten", diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index 3080417573..3eab41b298 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -3282,6 +3282,12 @@ "label": "Select a scene", "notice": "The trigger conditions of the called scene will be ignored. If a scene calls another scene, and vice versa, Gladys will not fall into an infinite loop: each scene will be executed once." }, + "setSceneActive": { + "label": "Select a scene", + "disabledScene": "disabled", + "enableNotice": "The selected scene will be enabled: its triggers will start listening again. A scene can enable itself.", + "disableNotice": "The selected scene will be disabled: its triggers will stop listening until it is enabled again. A scene can disable itself, for example to run only once until another scene re-enables it." + }, "houseEmptyOrNot": { "houseIsEmptyDescription": "If the house is empty, the scene will continue. Otherwise, the scene will stop.", "houseIsNotEmptyDescription": "If the house is not empty, the scene will continue. Otherwise, the scene will stop.", @@ -3395,7 +3401,9 @@ "toggle": "Toggle the Switches" }, "scene": { - "start": "Start scene" + "start": "Start scene", + "enable": "Enable a scene", + "disable": "Disable a scene" }, "service": { "start": "Start service", diff --git a/front/src/config/i18n/fr.json b/front/src/config/i18n/fr.json index 152327809a..e0d77a2658 100644 --- a/front/src/config/i18n/fr.json +++ b/front/src/config/i18n/fr.json @@ -3282,6 +3282,12 @@ "label": "Choisir une scène", "notice": "Les conditions de déclenchements de la scène appelée seront ignorées. Si une scène appelle une autre scène, et vice-versa, Gladys ne tombera pas dans une boucle infinie: chaque scène ne sera exécutée qu'une seule fois" }, + "setSceneActive": { + "label": "Choisir une scène", + "disabledScene": "désactivée", + "enableNotice": "La scène sélectionnée sera activée : ses déclencheurs seront de nouveau écoutés. Une scène peut s'activer elle-même.", + "disableNotice": "La scène sélectionnée sera désactivée : ses déclencheurs ne seront plus écoutés jusqu'à sa réactivation. Une scène peut se désactiver elle-même, par exemple pour ne s'exécuter qu'une fois jusqu'à ce qu'une autre scène la réactive." + }, "houseEmptyOrNot": { "houseIsEmptyDescription": "Si la maison est vide, cette scène continuera. Sinon, la scène s'arrêtera.", "houseIsNotEmptyDescription": "Si la maison n'est pas vide, cette scène continuera. Sinon, la scène s'arrêtera.", @@ -3395,7 +3401,9 @@ "toggle": "Inverser les prises" }, "scene": { - "start": "Démarrer la scène" + "start": "Démarrer la scène", + "enable": "Activer une scène", + "disable": "Désactiver une scène" }, "service": { "start": "Démarrer le service", diff --git a/front/src/routes/scene/edit-scene/ActionCard.jsx b/front/src/routes/scene/edit-scene/ActionCard.jsx index 9690a4411a..6e8163e255 100644 --- a/front/src/routes/scene/edit-scene/ActionCard.jsx +++ b/front/src/routes/scene/edit-scene/ActionCard.jsx @@ -18,6 +18,7 @@ import TurnOnOffLightParams from './actions/TurnOnOffLightParams'; import BlinkLightParams from './actions/BlinkLightParams'; import TurnOnOffSwitchParams from './actions/TurnOnOffSwitchParams'; import StartSceneParams from './actions/StartSceneParams'; +import EnableDisableSceneParams from './actions/EnableDisableSceneParams'; import UserPresence from './actions/UserPresence'; import HttpRequest from './actions/HttpRequest'; import CheckUserPresence from './actions/CheckUserPresence'; @@ -60,6 +61,8 @@ const ACTION_ICON = { [ACTIONS.USER.CHECK_PRESENCE]: 'fe fe-home', [ACTIONS.CONDITION.CHECK_TIME]: 'fe fe-watch', [ACTIONS.SCENE.START]: 'fe fe-fast-forward', + [ACTIONS.SCENE.ENABLE]: 'fe fe-play-circle', + [ACTIONS.SCENE.DISABLE]: 'fe fe-pause-circle', [ACTIONS.HOUSE.IS_EMPTY]: 'fe fe-home', [ACTIONS.HOUSE.IS_NOT_EMPTY]: 'fe fe-home', [ACTIONS.DEVICE.SET_VALUE]: 'fe fe-radio', @@ -97,6 +100,8 @@ const ACTION_COMPONENTS = { [ACTIONS.HTTP.REQUEST]: HttpRequest, [ACTIONS.CONDITION.CHECK_TIME]: CheckTime, [ACTIONS.SCENE.START]: StartSceneParams, + [ACTIONS.SCENE.ENABLE]: EnableDisableSceneParams, + [ACTIONS.SCENE.DISABLE]: EnableDisableSceneParams, [ACTIONS.HOUSE.IS_EMPTY]: HouseEmptyOrNotCondition, [ACTIONS.HOUSE.IS_NOT_EMPTY]: HouseEmptyOrNotCondition, [ACTIONS.DEVICE.SET_VALUE]: DeviceSetValue, diff --git a/front/src/routes/scene/edit-scene/actions/ChooseActionTypeCard.jsx b/front/src/routes/scene/edit-scene/actions/ChooseActionTypeCard.jsx index 72e21506cf..1b99258f90 100644 --- a/front/src/routes/scene/edit-scene/actions/ChooseActionTypeCard.jsx +++ b/front/src/routes/scene/edit-scene/actions/ChooseActionTypeCard.jsx @@ -26,6 +26,8 @@ const ACTION_LIST = [ ACTIONS.HTTP.REQUEST, ACTIONS.CONDITION.CHECK_TIME, ACTIONS.SCENE.START, + ACTIONS.SCENE.ENABLE, + ACTIONS.SCENE.DISABLE, ACTIONS.HOUSE.IS_EMPTY, ACTIONS.HOUSE.IS_NOT_EMPTY, ACTIONS.DEVICE.SET_VALUE, diff --git a/front/src/routes/scene/edit-scene/actions/EnableDisableSceneParams.jsx b/front/src/routes/scene/edit-scene/actions/EnableDisableSceneParams.jsx new file mode 100644 index 0000000000..9ecc920089 --- /dev/null +++ b/front/src/routes/scene/edit-scene/actions/EnableDisableSceneParams.jsx @@ -0,0 +1,80 @@ +import { Component } from 'preact'; +import { connect } from 'unistore/preact'; +import { Text, withText } from 'preact-i18n'; +import Select from 'react-select'; + +import { ACTIONS } from '../../../../../../server/utils/constants'; +import actions from '../../../../actions/scene'; + +class EnableDisableSceneParams extends Component { + handleChange = selectedOption => { + if (selectedOption) { + this.props.updateActionProperty(this.props.path, 'scene', selectedOption.value); + } else { + this.props.updateActionProperty(this.props.path, 'scene', null); + } + }; + + refreshSelectedOptions = nextProps => { + let selectedOption = null; + let scenes = this.state.scenes || []; + + // Contrary to the "start a scene" action, the current scene is part of the list: + // a scene disabling itself is the way to build a "run once, then disarm" scene. + if (scenes.length === 0 && nextProps.scenes) { + // Already disabled scenes are the most common target of an "enable a scene" action, + // so the current state of each scene is displayed next to its name. + scenes = nextProps.scenes.map(scene => ({ + value: scene.selector, + label: scene.active ? scene.name : `${scene.name} (${nextProps.disabledSceneLabel})` + })); + } + + if (nextProps.action.scene && scenes.length > 0) { + selectedOption = scenes.find(scene => scene.value === nextProps.action.scene) || null; + } + + this.setState({ selectedOption, scenes }); + }; + + constructor(props) { + super(props); + this.state = { + selectedOption: null + }; + } + + async componentDidMount() { + await this.props.getScenes(); + } + + componentWillReceiveProps(nextProps) { + this.refreshSelectedOptions(nextProps); + } + + render(props, { selectedOption, scenes }) { + const enabling = props.action.type === ACTIONS.SCENE.ENABLE; + return ( +
+
+ {enabling && } + {!enabling && } +
+ +