Skip to content

Commit

Permalink
Add alarm set mode action
Browse files Browse the repository at this point in the history
  • Loading branch information
Pierre-Gilles committed Oct 26, 2023
1 parent 8f5c259 commit 4eca352
Show file tree
Hide file tree
Showing 10 changed files with 323 additions and 13 deletions.
8 changes: 7 additions & 1 deletion front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1531,6 +1531,11 @@
"description": "The scene will continue if the alarm is in the selected mode.",
"houseLabel": "House",
"alarmModeLabel": "Alarm Mode"
},
"alarmSetMode": {
"description": "This action will set the selected house to the selected alarm mode.",
"houseLabel": "House",
"alarmModeLabel": "Alarm Mode"
}
},
"actions": {
Expand Down Expand Up @@ -1583,7 +1588,8 @@
"condition": "Condition on Ecowatt (France)"
},
"alarm": {
"check-alarm-mode": "If the alarm is in mode"
"check-alarm-mode": "If the alarm is in mode",
"set-alarm-mode": "Set alarm mode to"
}
},
"variables": {
Expand Down
8 changes: 7 additions & 1 deletion front/src/config/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,11 @@
"description": "La scène continuera si l'alarme est dans le mode sélectionné.",
"houseLabel": "Maison",
"alarmModeLabel": "Mode de l'alarme"
},
"alarmSetMode": {
"description": "Cette action passera la maison sélectionnée dans le mode d'alarme sélectionné.",
"houseLabel": "Maison",
"alarmModeLabel": "Mode de l'alarme"
}
},
"actions": {
Expand Down Expand Up @@ -1585,7 +1590,8 @@
"condition": "Condition sur Ecowatt ( France )"
},
"alarm": {
"check-alarm-mode": "Si l'alarme est en mode"
"check-alarm-mode": "Si l'alarme est en mode",
"set-alarm-mode": "Passer l'alarme en mode"
}
},
"variables": {
Expand Down
12 changes: 11 additions & 1 deletion front/src/routes/scene/edit-scene/ActionCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import CalendarIsEventRunning from './actions/CalendarIsEventRunning';
import EcowattCondition from './actions/EcowattCondition';
import SendMessageCameraParams from './actions/SendMessageCameraParams';
import CheckAlarmMode from './actions/CheckAlarmMode';
import SetAlarmMode from './actions/SetAlarmMode';

const deleteActionFromColumn = (columnIndex, rowIndex, deleteAction) => () => {
deleteAction(columnIndex, rowIndex);
Expand Down Expand Up @@ -54,7 +55,8 @@ const ACTION_ICON = {
[ACTIONS.DEVICE.SET_VALUE]: 'fe fe-radio',
[ACTIONS.CALENDAR.IS_EVENT_RUNNING]: 'fe fe-calendar',
[ACTIONS.ECOWATT.CONDITION]: 'fe fe-zap',
[ACTIONS.ALARM.CHECK_ALARM_MODE]: 'fe fe-bell'
[ACTIONS.ALARM.CHECK_ALARM_MODE]: 'fe fe-bell',
[ACTIONS.ALARM.SET_ALARM_MODE]: 'fe fe-bell'
};

const ACTION_CARD_TYPE = 'ACTION_CARD_TYPE';
Expand Down Expand Up @@ -342,6 +344,14 @@ const ActionCard = ({ children, ...props }) => {
updateActionProperty={props.updateActionProperty}
/>
)}
{props.action.type === ACTIONS.ALARM.SET_ALARM_MODE && (
<SetAlarmMode
action={props.action}
columnIndex={props.columnIndex}
index={props.index}
updateActionProperty={props.updateActionProperty}
/>
)}
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ const ACTION_LIST = [
ACTIONS.DEVICE.SET_VALUE,
ACTIONS.CALENDAR.IS_EVENT_RUNNING,
ACTIONS.ECOWATT.CONDITION,
ACTIONS.ALARM.CHECK_ALARM_MODE
ACTIONS.ALARM.CHECK_ALARM_MODE,
ACTIONS.ALARM.SET_ALARM_MODE
];

const TRANSLATIONS = ACTION_LIST.reduce((acc, action) => {
Expand Down
123 changes: 123 additions & 0 deletions front/src/routes/scene/edit-scene/actions/SetAlarmMode.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import Select from 'react-select';
import { Component } from 'preact';
import { connect } from 'unistore/preact';
import { Text } from 'preact-i18n';
import withIntlAsProp from '../../../../utils/withIntlAsProp';
import get from 'get-value';

import { ALARM_MODES_LIST } from '../../../../../../server/utils/constants';

const capitalizeFirstLetter = string => {
return string.charAt(0).toUpperCase() + string.slice(1);
};

class SetAlarmMode extends Component {
getOptions = async () => {
try {
const houses = await this.props.httpClient.get('/api/v1/house');
const houseOptions = [];
houses.forEach(house => {
houseOptions.push({
label: house.name,
value: house.selector
});
});
await this.setState({ houseOptions });
this.refreshSelectedOptions(this.props);
} catch (e) {
console.error(e);
}
};
handleHouseChange = selectedOption => {
if (selectedOption && selectedOption.value) {
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'house', selectedOption.value);
} else {
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'house', null);
}
};
handleAlarmModeChange = selectedOption => {
if (selectedOption && selectedOption.value) {
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'alarm_mode', selectedOption.value);
} else {
this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'alarm_mode', null);
}
};
refreshSelectedOptions = nextProps => {
let selectedHouseOption = '';
if (nextProps.action.house && this.state.houseOptions) {
const houseOption = this.state.houseOptions.find(option => option.value === nextProps.action.house);

if (houseOption) {
selectedHouseOption = houseOption;
}
}
let selectedAlarmModeOption = '';
if (nextProps.action.alarm_mode && this.state.alarmModesOptions) {
const alarmModeOption = this.state.alarmModesOptions.find(option => option.value === nextProps.action.alarm_mode);

if (alarmModeOption) {
selectedAlarmModeOption = alarmModeOption;
}
}
this.setState({ selectedHouseOption, selectedAlarmModeOption });
};
constructor(props) {
super(props);
this.props = props;
const alarmModesOptions = ALARM_MODES_LIST.map(alarmMode => {
return {
value: alarmMode,
label: capitalizeFirstLetter(get(props.intl.dictionary, `alarmModes.${alarmMode}`, { default: alarmMode }))
};
});
this.state = {
alarmModesOptions,
selectedHouseOption: ''
};
}
componentDidMount() {
this.getOptions();
}
componentWillReceiveProps(nextProps) {
this.refreshSelectedOptions(nextProps);
}
render(props, { alarmModesOptions, houseOptions, selectedHouseOption, selectedAlarmModeOption }) {
return (
<div>
<p>
<Text id="editScene.actionsCard.alarmSetMode.description" />
</p>
<div class="form-group">
<label class="form-label">
<Text id="editScene.actionsCard.alarmSetMode.houseLabel" />
<span class="form-required">
<Text id="global.requiredField" />
</span>
</label>
<Select
options={houseOptions}
class="scene-check-alarm-mode-choose-house"
value={selectedHouseOption}
onChange={this.handleHouseChange}
/>
</div>
<div class="form-group">
<label class="form-label">
<Text id="editScene.actionsCard.alarmSetMode.alarmModeLabel" />
<span class="form-required">
<Text id="global.requiredField" />
</span>
</label>
<Select
options={alarmModesOptions}
class="scene-check-alarm-mode-choose-alarm-mode"
value={selectedAlarmModeOption}
onChange={this.handleAlarmModeChange}
/>
</div>
</div>
);
}
}

export default withIntlAsProp(connect('httpClient', {})(SetAlarmMode));
25 changes: 18 additions & 7 deletions server/lib/house/house.arm.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ const { NotFoundError, ConflictError } = require('../../utils/coreErrors');
/**
* @public
* @description Arm house Alarm.
* @param {object} selector - Selector of the house.
* @param {string} selector - Selector of the house.
* @param {boolean} disableWaitTime - Should not wait to arm.
* @returns {Promise} Resolve with house object.
* @example
* const mainHouse = await gladys.house.arm('main-house');
*/
async function arm(selector) {
async function arm(selector, disableWaitTime = false) {
const house = await db.House.findOne({
where: {
selector,
Expand All @@ -38,8 +39,10 @@ async function arm(selector) {
type: EVENTS.ALARM.ARMING,
house: selector,
});
// Wait the delay before arming
const currentTimeout = setTimeout(async () => {

const waitTimeInMs = disableWaitTime ? 0 : house.alarm_delay_before_arming * 1000;

const armHouse = async () => {
// Update database
await house.update({ alarm_mode: ALARM_MODES.ARMED });
// Lock all tablets in this house
Expand All @@ -56,10 +59,18 @@ async function arm(selector) {
house: selector,
},
});
}, house.alarm_delay_before_arming * 1000);
};

// store the timeout so we can cancel it if needed
this.armingHouseTimeout.set(selector, currentTimeout);
// if the wait time is 0, just arm now
if (waitTimeInMs === 0) {
await armHouse();
} else {
// Wait the delay before arming
const currentTimeout = setTimeout(armHouse, waitTimeInMs);

// store the timeout so we can cancel it if needed
this.armingHouseTimeout.set(selector, currentTimeout);
}
}

module.exports = {
Expand Down
16 changes: 15 additions & 1 deletion server/lib/scene/scene.actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const get = require('get-value');
const dayjs = require('dayjs');
const utc = require('dayjs/plugin/utc');
const timezone = require('dayjs/plugin/timezone');
const { ACTIONS, DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } = require('../../utils/constants');
const { ACTIONS, DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES, ALARM_MODES } = require('../../utils/constants');
const { getDeviceFeature } = require('../../utils/device');
const { AbortScene } = require('../../utils/coreErrors');
const { compare } = require('../../utils/compare');
Expand Down Expand Up @@ -443,6 +443,20 @@ const actionsFunc = {
throw new AbortScene(`House "${house.name}" is not in mode ${action.alarm_mode}`);
}
},
[ACTIONS.ALARM.SET_ALARM_MODE]: async (self, action) => {
if (action.alarm_mode === ALARM_MODES.ARMED) {
await self.house.arm(action.house, true);
}
if (action.alarm_mode === ALARM_MODES.DISARMED) {
await self.house.disarm(action.house);
}
if (action.alarm_mode === ALARM_MODES.PARTIALLY_ARMED) {
await self.house.partialArm(action.house);
}
if (action.alarm_mode === ALARM_MODES.PANIC) {
await self.house.panic(action.house);
}
},
};

module.exports = {
Expand Down
38 changes: 37 additions & 1 deletion server/test/lib/house/house.arm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('house.arm', () => {
const house = new House(event, {}, session);
beforeEach(async () => {
await house.update('test-house', {
alarm_delay_before_arming: 0,
alarm_delay_before_arming: 0.001,
});
sinon.reset();
});
Expand Down Expand Up @@ -64,6 +64,42 @@ describe('house.arm', () => {
},
]);
});
it('should arm a house immediately', async () => {
await house.arm('test-house', true);
assert.callCount(event.emit, 4);
expect(event.emit.firstCall.args).to.deep.equal([
EVENTS.WEBSOCKET.SEND_ALL,
{
type: WEBSOCKET_MESSAGE_TYPES.ALARM.ARMING,
payload: {
house: 'test-house',
},
},
]);
expect(event.emit.secondCall.args).to.deep.equal([
EVENTS.TRIGGERS.CHECK,
{
type: EVENTS.ALARM.ARMING,
house: 'test-house',
},
]);
expect(event.emit.thirdCall.args).to.deep.equal([
EVENTS.TRIGGERS.CHECK,
{
type: EVENTS.ALARM.ARM,
house: 'test-house',
},
]);
expect(event.emit.args[3]).to.deep.equal([
EVENTS.WEBSOCKET.SEND_ALL,
{
type: WEBSOCKET_MESSAGE_TYPES.ALARM.ARMED,
payload: {
house: 'test-house',
},
},
]);
});
it('should return house not found', async () => {
const promise = house.arm('house-not-found');
return assertChai.isRejected(promise, 'House not found');
Expand Down
Loading

0 comments on commit 4eca352

Please sign in to comment.