Skip to content

Commit

Permalink
Scene: Add action to blink lights (#2007)
Browse files Browse the repository at this point in the history
Co-authored-by: Cyril Beslay <[email protected]>
  • Loading branch information
cicoub13 and Cyril Beslay authored Mar 1, 2024
1 parent ce947cf commit 4a80cd8
Show file tree
Hide file tree
Showing 12 changed files with 541 additions and 141 deletions.
17 changes: 16 additions & 1 deletion front/src/config/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -1684,6 +1684,20 @@
"label": "Wähle die Lichter aus, die umgeschaltet werden sollen",
"description": "Diese Aktion schaltet das Licht ein, wenn es ausgeschaltet ist bzw. schaltet es aus, wenn es eingeschaltet ist"
},
"blinkLights": {
"label": "Wählen Sie die Lichter aus, die Sie zum Blinken bringen möchten",
"description": "Stellen Sie die Dauer und die Geschwindigkeit des Blinkens ein",
"blinkingTime": {
"label": "Dauer (in Sekunden)",
"placeholder": "3"
},
"blinkingSpeed": {
"label": "Modus",
"slow": "Langsam",
"medium": "Mittel",
"fast": "Schnell"
}
},
"turnOnSwitches": {
"label": "Wähle die Schalter aus, die eingeschaltet werden sollen"
},
Expand Down Expand Up @@ -1825,7 +1839,8 @@
"light": {
"turn-on": "Licht einschalten",
"turn-off": "Licht ausschalten",
"toggle": "Lichter umschalten"
"toggle": "Lichter umschalten",
"blink": "Lichter blinken lassen"
},
"switch": {
"turn-on": "Schalter einschalten",
Expand Down
17 changes: 16 additions & 1 deletion front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,20 @@
"label": "Select the lights you want to toggle",
"description": "This action turn on the light if state is off or turn off the light if state is on"
},
"blinkLights": {
"label": "Select the lights you want to make blink",
"description": "Configure blinking duration and speed",
"blinkingTime": {
"label": "Duration (in seconds)",
"placeholder": "3"
},
"blinkingSpeed": {
"label": "Mode",
"slow": "Slow",
"medium": "Medium",
"fast": "Fast"
}
},
"turnOnSwitches": {
"label": "Select the switches you want to turn on"
},
Expand Down Expand Up @@ -1827,7 +1841,8 @@
"light": {
"turn-on": "Turn On the Lights",
"turn-off": "Turn Off the Lights",
"toggle": "Toggle the Lights"
"toggle": "Toggle the Lights",
"blink": "Make the Lights blink"
},
"switch": {
"turn-on": "Turn On the Switches",
Expand Down
19 changes: 17 additions & 2 deletions front/src/config/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,20 @@
"label": "Sélectionnez les lumières que vous souhaitez inverser",
"description": "Cette action allume la lumière si éteinte, sinon éteins la lumière"
},
"blinkLights": {
"label": "Sélectionnez les lumières que vous souhaitez faire clignoter",
"description": "Réglez la durée et la vitesse de clignotement",
"blinkingTime": {
"label": "Durée (en secondes)",
"placeholder": "3"
},
"blinkingSpeed": {
"label": "Mode",
"slow": "Lent",
"medium": "Normal",
"fast": "Rapide"
}
},
"turnOnSwitches": {
"label": "Sélectionnez les prises que vous souhaitez allumer"
},
Expand Down Expand Up @@ -1827,9 +1841,10 @@
},
"delay": "Attendre",
"light": {
"turn-on": "Allumer la lumière",
"turn-on": "Allumer les lumières",
"turn-off": "Eteindre les lumières",
"toggle": "Inverser les lumières"
"toggle": "Inverser les lumières",
"blink": "Faire clignoter les lumières"
},
"switch": {
"turn-on": "Allumer les prises",
Expand Down
13 changes: 12 additions & 1 deletion front/src/routes/scene/edit-scene/ActionCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import DeviceSetValue from './actions/DeviceSetValue';
import SendMessageParams from './actions/SendMessageParams';
import OnlyContinueIfParams from './actions/only-continue-if/OnlyContinueIfParams';
import TurnOnOffLightParams from './actions/TurnOnOffLightParams';
import BlinkLightParams from './actions/BlinkLightParams';
import TurnOnOffSwitchParams from './actions/TurnOnOffSwitchParams';
import StartSceneParams from './actions/StartSceneParams';
import UserPresence from './actions/UserPresence';
Expand All @@ -38,6 +39,7 @@ const ACTION_ICON = {
[ACTIONS.LIGHT.TURN_ON]: 'fe fe-toggle-right',
[ACTIONS.LIGHT.TURN_OFF]: 'fe fe-toggle-left',
[ACTIONS.LIGHT.TOGGLE]: 'fe fe-shuffle',
[ACTIONS.LIGHT.BLINK]: 'fe fe-star',
[ACTIONS.SWITCH.TURN_ON]: 'fe fe-toggle-right',
[ACTIONS.SWITCH.TURN_OFF]: 'fe fe-toggle-left',
[ACTIONS.SWITCH.TOGGLE]: 'fe fe-shuffle',
Expand Down Expand Up @@ -97,7 +99,8 @@ const ActionCard = ({ children, ...props }) => {
'col-lg-6':
props.action.type === ACTIONS.MESSAGE.SEND ||
props.action.type === ACTIONS.CALENDAR.IS_EVENT_RUNNING ||
props.action.type === ACTIONS.MQTT.SEND,
props.action.type === ACTIONS.MQTT.SEND ||
props.action.type === ACTIONS.LIGHT.BLINK,
'col-lg-4':
props.action.type !== ACTIONS.CONDITION.ONLY_CONTINUE_IF &&
props.action.type !== ACTIONS.MESSAGE.SEND &&
Expand Down Expand Up @@ -173,6 +176,14 @@ const ActionCard = ({ children, ...props }) => {
updateActionProperty={props.updateActionProperty}
/>
)}
{props.action.type === ACTIONS.LIGHT.BLINK && (
<BlinkLightParams
action={props.action}
columnIndex={props.columnIndex}
index={props.index}
updateActionProperty={props.updateActionProperty}
/>
)}
{props.action.type === ACTIONS.SWITCH.TURN_ON && (
<TurnOnOffSwitchParams
action={props.action}
Expand Down
133 changes: 133 additions & 0 deletions front/src/routes/scene/edit-scene/actions/BlinkLightParams.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { Component } from 'preact';
import { connect } from 'unistore/preact';
import { Text, Localizer } from 'preact-i18n';
import Select from 'react-select';

class BlinkLight extends Component {
getOptions = async () => {
try {
const devices = await this.props.httpClient.get('/api/v1/device', {
device_feature_category: 'light',
device_feature_type: 'binary'
});
const deviceOptions = devices.map(device => ({
value: device.selector,
label: device.name
}));
await this.setState({ deviceOptions });
this.refreshSelectedOptions(this.props);
return deviceOptions;
} catch (e) {
console.error(e);
}
};
handleChange = selectedOptions => {
if (selectedOptions) {
const lights = selectedOptions.map(selectedOption => selectedOption.value);
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'devices', lights);
} else {
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'devices', []);
}
};
handleChangeBlinkingTime = e => {
let newValue = Number.isInteger(parseInt(e.target.value, 10)) ? parseInt(e.target.value, 10) : 0;
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'blinking_time', newValue);
};
handleChangeBlinkingSpeed = e => {
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'blinking_speed', e.target.value);
};
refreshSelectedOptions = nextProps => {
const selectedOptions = [];
if (nextProps.action.devices && this.state.deviceOptions) {
nextProps.action.devices.forEach(light => {
const deviceOption = this.state.deviceOptions.find(deviceOption => deviceOption.value === light);
if (deviceOption) {
selectedOptions.push(deviceOption);
}
});
}
this.setState({ selectedOptions });
};
constructor(props) {
super(props);
this.state = {
deviceOptions: null,
selectedOptions: []
};
}
async componentDidMount() {
this.getOptions();
if (!this.props.action.blinking_speed) {
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'blinking_speed', 'slow');
}
}

componentWillReceiveProps(nextProps) {
this.refreshSelectedOptions(nextProps);
}

render(props, { selectedOptions, deviceOptions }) {
return (
<div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<div class="form-label">
<Text id="editScene.actionsCard.blinkLigths.label" />
</div>
<Select
defaultValue={[]}
isMulti
value={selectedOptions}
onChange={this.handleChange}
options={deviceOptions}
/>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<div class="form-label">
<Text id="editScene.actionsCard.blinkLights.blinkingTime.label" />
</div>
<Localizer>
<input
type="text"
class="form-control"
value={props.action.blinking_time}
onChange={this.handleChangeBlinkingTime}
placeholder={<Text id="editScene.actionsCard.blinkLights.blinkingTime.placeholder" />}
/>
</Localizer>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<div class="form-label">
<Text id="editScene.actionsCard.blinkLights.blinkingSpeed.label" />
</div>
<select
class="custom-select"
value={props.action.blinking_speed}
onChange={this.handleChangeBlinkingSpeed}
>
<option value="slow">
<Text id="editScene.actionsCard.blinkLights.blinkingSpeed.slow" />
</option>
<option value="medium">
<Text id="editScene.actionsCard.blinkLights.blinkingSpeed.medium" />
</option>
<option value="fast">
<Text id="editScene.actionsCard.blinkLights.blinkingSpeed.fast" />
</option>
</select>
</div>
</div>
</div>
</div>
);
}
}

export default connect('httpClient', {})(BlinkLight);
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const ACTION_LIST = [
ACTIONS.LIGHT.TURN_ON,
ACTIONS.LIGHT.TURN_OFF,
ACTIONS.LIGHT.TOGGLE,
ACTIONS.LIGHT.BLINK,
ACTIONS.SWITCH.TURN_ON,
ACTIONS.SWITCH.TURN_OFF,
ACTIONS.SWITCH.TOGGLE,
Expand Down
42 changes: 42 additions & 0 deletions server/lib/scene/scene.actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,48 @@ const actionsFunc = {
}
});
},
[ACTIONS.LIGHT.BLINK]: async (self, action, scope) => {
const blinkingSpeed = action.blinking_speed;
const blinkingTime = action.blinking_time * 1000 + 1;
let blinkingInterval;
switch (blinkingSpeed) {
case 'slow':
blinkingInterval = 1000;
break;
case 'medium':
blinkingInterval = 500;
break;
case 'fast':
blinkingInterval = 200;
break;
default:
blinkingInterval = 200;
break;
}
await Promise.map(action.devices, async (deviceSelector) => {
try {
const device = self.stateManager.get('device', deviceSelector);
const deviceFeature = getDeviceFeature(
device,
DEVICE_FEATURE_CATEGORIES.LIGHT,
DEVICE_FEATURE_TYPES.LIGHT.BINARY,
);
const oldValue = deviceFeature.last_value;
let newValue = 0;
/* eslint-disable no-await-in-loop */
// We want this loops to be sequential
for (let i = 0; i < blinkingTime; i += blinkingInterval) {
newValue = 1 - newValue;
await self.device.setValue(device, deviceFeature, newValue);
await Promise.delay(blinkingInterval);
}
/* eslint-enable no-await-in-loop */
await self.device.setValue(device, deviceFeature, oldValue);
} catch (e) {
logger.warn(e);
}
});
},
[ACTIONS.SWITCH.TURN_ON]: async (self, action, scope) => {
await Promise.map(action.devices, async (deviceSelector) => {
try {
Expand Down
2 changes: 2 additions & 0 deletions server/models/scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ const actionSchema = Joi.array().items(
alarm_mode: Joi.string().valid(...ALARM_MODES_LIST),
topic: Joi.string(),
message: Joi.string().allow(''),
blinking_time: Joi.number(),
blinking_speed: Joi.string().valid('slow', 'medium', 'fast'),
}),
),
);
Expand Down
Loading

0 comments on commit 4a80cd8

Please sign in to comment.