Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #727 : The user should be able to set the timezone used in scenes in the UI #856

Merged
merged 6 commits into from
Aug 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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