Skip to content

Commit

Permalink
Fix GladysAssistant#727 : The user should be able to set the timezone…
Browse files Browse the repository at this point in the history
… used in scenes in the UI (GladysAssistant#856)

* Add timezone configuration in parameters in UI

* When the timezone is modified, the server reloads all scenes

* Translate configuration in UI

* Fix tests

* Add more tests to the scene.init function
  • Loading branch information
Pierre-Gilles authored Aug 25, 2020
1 parent b1c9318 commit 4312305
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 12 deletions.
3 changes: 3 additions & 0 deletions front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,9 @@
"containerStatus": "Status",
"downloadFinished": "Download finished.",
"restartingGladys": "Restarting Gladys...",
"configurationTitle": "Configuration",
"timezone": "Timezone",
"timezoneText": "The timezone is used in scheduled scene.",
"containerState": {
"created": "Created",
"restarting": "Restarting",
Expand Down
3 changes: 3 additions & 0 deletions front/src/config/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,9 @@
"containerStatus": "État",
"downloadFinished": "Téléchargement terminé",
"restartingGladys": "Redémarrage de Gladys ...",
"configurationTitle": "Configuration",
"timezone": "Fuseau horaire",
"timezoneText": "Le fuseau horaire est utilisé dans les scènes programmées.",
"containerState": {
"created": "Créé",
"restarting": "Redémarrage",
Expand Down
252 changes: 252 additions & 0 deletions front/src/config/timezones.js

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions front/src/routes/settings/settings-system/SettingsSystemPage.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Text } from 'preact-i18n';
import SettingsLayout from '../SettingsLayout';
import cx from 'classnames';
import Select from 'react-select';

const SystemPage = ({ children, ...props }) => (
<SettingsLayout>
Expand Down Expand Up @@ -168,6 +169,24 @@ const SystemPage = ({ children, ...props }) => (
</table>
</div>
</div>
<div class="card">
<h3 class="card-header">
<Text id="systemSettings.configurationTitle" />
</h3>
<div class="card-body">
<form>
<label>
<Text id="systemSettings.timezone" />
</label>
<p>
<small>
<Text id="systemSettings.timezoneText" />
</small>
</p>
<Select options={props.timezoneOptions} onChange={props.updateTimezone} value={props.selectedTimezone} />
</form>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card">
Expand Down
39 changes: 36 additions & 3 deletions front/src/routes/settings/settings-system/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,52 @@
import { Component } from 'preact';
import { connect } from 'unistore/preact';
import get from 'get-value';
import timezones from '../../../config/timezones';
import SettingsSystemPage from './SettingsSystemPage';
import actions from '../../../actions/system';
import { WEBSOCKET_MESSAGE_TYPES } from '../../../../../server/utils/constants';
import { WEBSOCKET_MESSAGE_TYPES, SYSTEM_VARIABLE_NAMES } from '../../../../../server/utils/constants';
import { RequestStatus } from '../../../utils/consts';

@connect(
'session,systemPing,systemInfos,systemDiskSpace,systemContainers,downloadUpgradeProgress,downloadUpgradeStatus',
'httpClient,session,systemPing,systemInfos,systemDiskSpace,systemContainers,downloadUpgradeProgress,downloadUpgradeStatus',
actions
)
class SettingsSystem extends Component {
updateTimezone = async option => {
this.setState({
savingTimezone: true,
selectedTimezone: option
});
try {
await this.props.httpClient.post(`/api/v1/variable/${SYSTEM_VARIABLE_NAMES.TIMEZONE}`, {
value: option.value
});
} catch (e) {
console.log(e);
}
};

getTimezone = async () => {
try {
const { value } = await this.props.httpClient.get(`/api/v1/variable/${SYSTEM_VARIABLE_NAMES.TIMEZONE}`);
const selectedTimezone = timezones.find(tz => tz.value === value);
if (selectedTimezone) {
this.setState({
selectedTimezone
});
}
} catch (e) {
console.log(e);
}
};

componentWillMount() {
this.props.ping();
this.props.getInfos();
this.props.getDiskSpace();
this.props.getContainers();
this.props.getUpgradeDownloadStatus();
this.getTimezone();
this.props.session.dispatcher.addListener(WEBSOCKET_MESSAGE_TYPES.UPGRADE.DOWNLOAD_PROGRESS, payload =>
this.props.newDownloadProgress(payload)
);
Expand All @@ -28,7 +58,7 @@ class SettingsSystem extends Component {
);
}

render(props, {}) {
render(props, { selectedTimezone }) {
const isDocker = get(props, 'systemInfos.is_docker');
const upgradeDownloadInProgress = props.downloadUpgradeStatus === RequestStatus.Getting;
const upgradeDownloadFinished = props.downloadUpgradeStatus === RequestStatus.Success;
Expand All @@ -41,6 +71,9 @@ class SettingsSystem extends Component {
upgradeDownloadInProgress={upgradeDownloadInProgress}
upgradeDownloadFinished={upgradeDownloadFinished}
upgradeAvailable={upgradeAvailable}
timezoneOptions={timezones}
updateTimezone={this.updateTimezone}
selectedTimezone={selectedTimezone}
/>
);
}
Expand Down
6 changes: 3 additions & 3 deletions server/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ function Gladys(params = {}) {
params.jwtSecret = params.jwtSecret || generateJwtSecret();
const config = getConfig();

const variable = new Variable();
const event = new Event();
const variable = new Variable(event);
const brain = new Brain();
const cache = new Cache();
const calendar = new Calendar();
const event = new Event();
const area = new Area();
const dashboard = new Dashboard();
const stateManager = new StateManager(event);
Expand All @@ -58,7 +58,7 @@ function Gladys(params = {}) {
const user = new User(session, stateManager, variable);
const location = new Location(user, event);
const device = new Device(event, message, stateManager, service, room, variable);
const scene = new Scene(stateManager, event, device, message);
const scene = new Scene(stateManager, event, device, message, variable);
const scheduler = new Scheduler(event);
const system = new System(db.sequelize, event, config);
const weather = new Weather(service, event, message, house);
Expand Down
8 changes: 7 additions & 1 deletion server/lib/scene/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,24 @@ const { update } = require('./scene.update');
const { EVENTS } = require('../../utils/constants');
const { eventFunctionWrapper } = require('../../utils/functionsWrapper');

const SceneManager = function SceneManager(stateManager, event, device, message) {
const DEFAULT_TIMEZONE = 'Europe/Paris';

const SceneManager = function SceneManager(stateManager, event, device, message, variable) {
this.stateManager = stateManager;
this.event = event;
this.device = device;
this.message = message;
this.variable = variable;
this.scenes = {};
this.timezone = DEFAULT_TIMEZONE;
// @ts-ignore
this.queue = queue({
autostart: true,
});
this.event.on(EVENTS.TRIGGERS.CHECK, eventFunctionWrapper(this.checkTrigger.bind(this)));
this.event.on(EVENTS.ACTION.TRIGGERED, eventFunctionWrapper(this.executeSingleAction.bind(this)));
// on timezone change, reload all scenes
this.event.on(EVENTS.SYSTEM.TIMEZONE_CHANGED, eventFunctionWrapper(this.init.bind(this)));
};

SceneManager.prototype.addScene = addScene;
Expand Down
1 change: 1 addition & 0 deletions server/lib/scene/scene.addScene.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function addScene(sceneRaw) {
trigger.key = uuid.v4();
if (trigger.type === EVENTS.TIME.CHANGED && trigger.scheduler_type !== 'interval') {
const rule = new schedule.RecurrenceRule();
rule.tz = this.timezone;
switch (trigger.scheduler_type) {
case 'every-month':
rule.date = trigger.day_of_the_month;
Expand Down
10 changes: 9 additions & 1 deletion server/lib/scene/scene.init.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
const db = require('../../models');

const { SYSTEM_VARIABLE_NAMES } = require('../../utils/constants');
const logger = require('../../utils/logger');
/**
* @description Load all scenes from the database to the trigger store.
* @returns {Promise} Resolve when success.
* @example
* scene.init();
*/
async function init() {
logger.debug('Scene.init');
// get timezone settings
const timezone = await this.variable.getValue(SYSTEM_VARIABLE_NAMES.TIMEZONE);
if (timezone) {
this.timezone = timezone;
}
// get all scenes
const scenes = await db.Scene.findAll();
const plainScenes = scenes.map((scene) => {
const plainScene = scene.get({ plain: true });
Expand Down
4 changes: 3 additions & 1 deletion server/lib/variable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ const { destroy } = require('./variable.destroy');
const { getValue } = require('./variable.getValue');
const { setValue } = require('./variable.setValue');

const Variable = function Variable() {};
const Variable = function Variable(event) {
this.event = event;
};

Variable.prototype.destroy = destroy;
Variable.prototype.setValue = setValue;
Expand Down
17 changes: 14 additions & 3 deletions server/lib/variable/variable.setValue.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const db = require('../../models');
const { EVENTS, SYSTEM_VARIABLE_NAMES } = require('../../utils/constants');

/**
* @description Set the value of a variable
Expand All @@ -19,18 +20,28 @@ async function setValue(key, value, serviceId = null, userId = null) {
},
});

let createdOrUpdatedVariable;

// if variable doesn't exist, we create it
if (variable === null) {
return db.Variable.create({
createdOrUpdatedVariable = await db.Variable.create({
value,
name: key,
service_id: serviceId,
user_id: userId,
});
} else {
// if it exists, we update it
createdOrUpdatedVariable = await variable.update({ value });
}

// if the variable updated is the timezone settings, we send an event for the system
// to reload all timezone related code
if (key === SYSTEM_VARIABLE_NAMES.TIMEZONE) {
this.event.emit(EVENTS.SYSTEM.TIMEZONE_CHANGED);
}

// if it exists, we update it
return variable.update({ value });
return createdOrUpdatedVariable;
}

module.exports = {
Expand Down
26 changes: 26 additions & 0 deletions server/test/lib/scene/scene.init.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { expect } = require('chai');
const EventEmitter = require('events');
const SceneManager = require('../../../lib/scene');

const event = new EventEmitter();

describe('SceneManager.init', () => {
it('should init scene with default timezone', async () => {
const variable = {
getValue: () => Promise.resolve(null),
setValue: () => Promise.resolve(null),
};
const sceneManager = new SceneManager({}, event, {}, {}, variable);
await sceneManager.init();
expect(sceneManager.timezone).to.equal('Europe/Paris');
});
it('should init scene with timezone from variable', async () => {
const variable = {
getValue: () => Promise.resolve('Europe/London'),
setValue: () => Promise.resolve(null),
};
const sceneManager = new SceneManager({}, event, {}, {}, variable);
await sceneManager.init();
expect(sceneManager.timezone).to.equal('Europe/London');
});
});
2 changes: 2 additions & 0 deletions server/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const SESSION_TOKEN_TYPES = {
const SYSTEM_VARIABLE_NAMES = {
DEVICE_STATE_HISTORY_IN_DAYS: 'DEVICE_STATE_HISTORY_IN_DAYS',
GLADYS_GATEWAY_BACKUP_KEY: 'GLADYS_GATEWAY_BACKUP_KEY',
TIMEZONE: 'TIMEZONE',
};

const EVENTS = {
Expand Down Expand Up @@ -109,6 +110,7 @@ const EVENTS = {
SYSTEM: {
DOWNLOAD_UPGRADE: 'system.download-upgrade',
CHECK_UPGRADE: 'system.check-upgrade',
TIMEZONE_CHANGED: 'system.timezone-changed',
},
WEBSOCKET: {
SEND: 'websocket.send',
Expand Down

0 comments on commit 4312305

Please sign in to comment.