(
type="number"
min="1"
max="100"
- disabled={props.savingBatteryLevelUnderWarning}
- value={props.batteryLevelUnderWarning}
- onChange={props.updateBatteryLevelUnderWarning}
+ disabled={props.savingBatteryLevelUnderWarning || !props.batteryLevelUnderWarningEnabled}
+ value={props.batteryLevelUnderWarningThreshold}
+ onChange={props.updateBatteryLevelUnderWarningThreshold}
/>
-
-
-
- {props.systemInfos && props.systemInfos.new_release_available === true && (
-
- )}
-
- {props.systemInfos && props.systemInfos.new_release_available === false && (
-
-
-
-
-
-
- |
-
-
-
-
- |
-
-
-
-
- )}
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
- |
-
-
- |
-
-
- |
-
-
-
- {props.systemContainers &&
- props.systemContainers.map(container => (
-
- {container.name} |
- {container.created_at_formatted} |
-
-
-
-
- |
-
- ))}
-
-
-
-
+
+
+
+
diff --git a/front/src/routes/settings/settings-system/SettingsSystemTimeExpiryState.jsx b/front/src/routes/settings/settings-system/SettingsSystemTimeExpiryState.jsx
new file mode 100644
index 0000000000..ff755910cf
--- /dev/null
+++ b/front/src/routes/settings/settings-system/SettingsSystemTimeExpiryState.jsx
@@ -0,0 +1,82 @@
+import { connect } from 'unistore/preact';
+import { Component } from 'preact';
+import { Text } from 'preact-i18n';
+import { SYSTEM_VARIABLE_NAMES } from '../../../../../server/utils/constants';
+
+class SettingsSystemTimeExpiryState extends Component {
+ getNumberOfHoursBeforeStateIsOutdated = async () => {
+ try {
+ const { value } = await this.props.httpClient.get(
+ `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_STATE_NUMBER_OF_HOURS_BEFORE_STATE_IS_OUTDATED}`
+ );
+ this.setState({
+ numberOfHoursBeforeStateIsOutdated: value
+ });
+ } catch (e) {
+ console.error(e);
+ // if variable doesn't exist, value is 48
+ this.setState({
+ numberOfHoursBeforeStateIsOutdated: 48
+ });
+ }
+ };
+
+ updateNumberOfHoursBeforeStateIsOutdated = async e => {
+ await this.setState({
+ numberOfHoursBeforeStateIsOutdated: e.target.value,
+ savingNumberOfHourseBeforeStateIsOutdated: true
+ });
+ try {
+ await this.props.httpClient.post(
+ `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_STATE_NUMBER_OF_HOURS_BEFORE_STATE_IS_OUTDATED}`,
+ {
+ value: e.target.value
+ }
+ );
+ } catch (e) {
+ console.error(e);
+ }
+ await this.setState({
+ savingNumberOfHourseBeforeStateIsOutdated: false
+ });
+ };
+
+ componentDidMount() {
+ this.getNumberOfHoursBeforeStateIsOutdated();
+ }
+
+ render({}, { numberOfHoursBeforeStateIsOutdated, savingNumberOfHourseBeforeStateIsOutdated }) {
+ return (
+
+ );
+ }
+}
+
+export default connect('httpClient', null)(SettingsSystemTimeExpiryState);
diff --git a/front/src/routes/settings/settings-system/SettingsSystemTimezone.jsx b/front/src/routes/settings/settings-system/SettingsSystemTimezone.jsx
new file mode 100644
index 0000000000..692c551657
--- /dev/null
+++ b/front/src/routes/settings/settings-system/SettingsSystemTimezone.jsx
@@ -0,0 +1,61 @@
+import { connect } from 'unistore/preact';
+import { Component } from 'preact';
+import { Text } from 'preact-i18n';
+import Select from 'react-select';
+import timezones from '../../../config/timezones';
+import { SYSTEM_VARIABLE_NAMES } from '../../../../../server/utils/constants';
+
+class SettingsSystemTimezone extends Component {
+ 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.error(e);
+ }
+ };
+
+ 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.error(e);
+ }
+ };
+
+ componentDidMount() {
+ this.getTimezone();
+ }
+
+ render({}, { selectedTimezone }) {
+ return (
+
+ );
+ }
+}
+
+export default connect('httpClient', null)(SettingsSystemTimezone);
diff --git a/front/src/routes/settings/settings-system/index.js b/front/src/routes/settings/settings-system/index.js
index 0a427f8d13..548841be36 100644
--- a/front/src/routes/settings/settings-system/index.js
+++ b/front/src/routes/settings/settings-system/index.js
@@ -1,236 +1,14 @@
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 { SYSTEM_VARIABLE_NAMES } from '../../../../../server/utils/constants';
-import { RequestStatus } from '../../../utils/consts';
-import debounce from 'debounce';
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.error(e);
- }
- };
-
- vacuumDatabase = async e => {
- e.preventDefault();
- this.setState({
- vacuumStarted: true
- });
- try {
- await this.props.httpClient.post('/api/v1/system/vacuum');
- } catch (e) {
- console.error(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.error(e);
- }
- };
-
- getBatteryLevelUnderWarning = async () => {
- try {
- const { value: batteryLevelUnderWarningThreshold } = await this.props.httpClient.get(
- `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_BATTERY_LEVEL_WARNING_THRESHOLD}`
- );
-
- const { value: batteryLevelUnderWarningEnabled } = await this.props.httpClient.get(
- `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_BATTERY_LEVEL_WARNING_ENABLED}`
- );
-
- console.log(batteryLevelUnderWarningThreshold, batteryLevelUnderWarningEnabled);
-
- this.setState({
- batteryLevelUnderWarningThreshold,
- batteryLevelUnderWarningEnabled: batteryLevelUnderWarningEnabled === '1'
- });
- } catch (e) {
- console.error(e);
- }
- };
-
- updateBatteryLevelUnderWarningThreshold = async e => {
- await this.setState({
- batteryLevelUnderWarningThreshold: e.target.value,
- savingBatteryLevelUnderWarning: true
- });
- try {
- await this.props.httpClient.post(
- `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_BATTERY_LEVEL_WARNING_THRESHOLD}`,
- {
- value: e.target.value
- }
- );
- } catch (e) {
- console.error(e);
- }
- await this.setState({
- savingBatteryLevelUnderWarning: false
- });
- };
-
- debouncedUpdateBatteryLevelUnderWarningThreshold = debounce(this.updateBatteryLevelUnderWarningThreshold, 200);
-
- updateBatteryLevelUnderWarningEnabled = async () => {
- console.log(this.state.batteryLevelUnderWarningEnabled);
- const value = !this.state.batteryLevelUnderWarningEnabled;
- console.log(value);
- await this.setState({
- batteryLevelUnderWarningEnabled: value,
- savingBatteryLevelUnderWarning: true
- });
- try {
- await this.props.httpClient.post(
- `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_BATTERY_LEVEL_WARNING_ENABLED}`,
- {
- value
- }
- );
- } catch (e) {
- console.error(e);
- }
- await this.setState({
- savingBatteryLevelUnderWarning: false
- });
- };
-
- updateDeviceStateHistory = async e => {
- await this.setState({
- deviceStateHistoryInDays: e.target.value,
- savingDeviceStateHistory: true
- });
- try {
- await this.props.httpClient.post(`/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_STATE_HISTORY_IN_DAYS}`, {
- value: e.target.value
- });
- } catch (e) {
- console.error(e);
- }
- await this.setState({
- savingDeviceStateHistory: false
- });
- };
-
- updateDeviceAggregateStateHistory = async e => {
- await this.setState({
- deviceAggregateStateHistoryInDays: e.target.value,
- savingDeviceStateHistory: true
- });
- try {
- await this.props.httpClient.post(
- `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_AGGREGATE_STATE_HISTORY_IN_DAYS}`,
- {
- value: e.target.value
- }
- );
- } catch (e) {
- console.error(e);
- }
- await this.setState({
- savingDeviceStateHistory: false
- });
- };
-
- updateNumberOfHoursBeforeStateIsOutdated = async e => {
- await this.setState({
- numberOfHoursBeforeStateIsOutdated: e.target.value,
- savingNumberOfHourseBeforeStateIsOutdated: true
- });
- try {
- await this.props.httpClient.post(
- `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_STATE_NUMBER_OF_HOURS_BEFORE_STATE_IS_OUTDATED}`,
- {
- value: e.target.value
- }
- );
- } catch (e) {
- console.error(e);
- }
- await this.setState({
- savingNumberOfHourseBeforeStateIsOutdated: false
- });
- };
-
- getDeviceStateHistoryPreference = async () => {
- try {
- const { value } = await this.props.httpClient.get(
- `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_STATE_HISTORY_IN_DAYS}`
- );
- this.setState({
- deviceStateHistoryInDays: value
- });
- } catch (e) {
- console.error(e);
- }
- };
-
- getDeviceAggregateStateHistoryPreference = async () => {
- try {
- const { value } = await this.props.httpClient.get(
- `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_AGGREGATE_STATE_HISTORY_IN_DAYS}`
- );
- this.setState({
- deviceAggregateStateHistoryInDays: value
- });
- } catch (e) {
- console.error(e);
- const status = get(e, 'response.status');
- if (status === 404) {
- // Default value is -1
- this.setState({
- deviceAggregateStateHistoryInDays: '-1'
- });
- }
- }
- };
-
- getNumberOfHoursBeforeStateIsOutdated = async () => {
- try {
- const { value } = await this.props.httpClient.get(
- `/api/v1/variable/${SYSTEM_VARIABLE_NAMES.DEVICE_STATE_NUMBER_OF_HOURS_BEFORE_STATE_IS_OUTDATED}`
- );
- this.setState({
- numberOfHoursBeforeStateIsOutdated: value
- });
- } catch (e) {
- console.error(e);
- // if variable doesn't exist, value is 48
- this.setState({
- numberOfHoursBeforeStateIsOutdated: 48
- });
- }
- };
-
componentDidMount() {
this.props.getInfos();
this.props.getDiskSpace();
this.props.getContainers();
- this.getTimezone();
- this.getDeviceStateHistoryPreference();
- this.getDeviceAggregateStateHistoryPreference();
- this.getNumberOfHoursBeforeStateIsOutdated();
- this.getBatteryLevelUnderWarning();
+
// we start the ping a little bit after to give it some time to breathe
this.refreshPingIntervalId = setInterval(() => {
this.props.ping();
@@ -241,62 +19,9 @@ class SettingsSystem extends Component {
clearInterval(this.refreshPingIntervalId);
}
- constructor(props) {
- super(props);
- this.state = {
- vacuumStarted: false
- };
- }
-
- render(
- props,
- {
- selectedTimezone,
- deviceStateHistoryInDays,
- deviceAggregateStateHistoryInDays,
- vacuumStarted,
- numberOfHoursBeforeStateIsOutdated,
- savingNumberOfHourseBeforeStateIsOutdated,
- batteryLevelUnderWarningThreshold,
- batteryLevelUnderWarningEnabled,
- savingBatteryLevelUnderWarning
- }
- ) {
- const isDocker = get(props, 'systemInfos.is_docker');
- const upgradeDownloadInProgress = props.downloadUpgradeStatus === RequestStatus.Getting;
- const upgradeDownloadFinished = props.downloadUpgradeStatus === RequestStatus.Success;
- const upgradeAvailable =
- !upgradeDownloadInProgress && !upgradeDownloadFinished && get(props, 'systemInfos.new_release_available');
- return (
-
- );
+ render(props, {}) {
+ return
;
}
}
-export default connect(
- 'httpClient,session,systemPing,systemInfos,systemDiskSpace,systemContainers,downloadUpgradeProgress,downloadUpgradeStatus',
- actions
-)(SettingsSystem);
+export default connect('httpClient,session,systemPing,systemDiskSpace,systemInfos', actions)(SettingsSystem);
diff --git a/server/config/scheduler-jobs.js b/server/config/scheduler-jobs.js
index 3f9110b426..12fa9bb0b0 100644
--- a/server/config/scheduler-jobs.js
+++ b/server/config/scheduler-jobs.js
@@ -23,7 +23,7 @@ const jobs = [
},
{
name: 'hourly-device-check-batteries',
- rule: '0 0 * * * *', // every hour
+ rule: '0 0 9 * * 6', // At 09:00 AM, only on Saturday
event: EVENTS.DEVICE.CHECK_BATTERIES,
},
];
From cd0b72f468c8da2038ab99b4dfccc32150472446 Mon Sep 17 00:00:00 2001
From: callemand
Date: Fri, 17 Nov 2023 16:06:19 +0100
Subject: [PATCH 4/5] Fix unit test
---
.../seeders/20190227081700-device-feature.js | 17 ++++++
.../lib/device/device.checkBatteries.test.js | 59 +++++++++++++++++++
server/test/lib/user/user.getByRole.test.js | 18 ++++++
3 files changed, 94 insertions(+)
create mode 100644 server/test/lib/device/device.checkBatteries.test.js
create mode 100644 server/test/lib/user/user.getByRole.test.js
diff --git a/server/seeders/20190227081700-device-feature.js b/server/seeders/20190227081700-device-feature.js
index 066638af0c..fa9bfac98b 100644
--- a/server/seeders/20190227081700-device-feature.js
+++ b/server/seeders/20190227081700-device-feature.js
@@ -40,6 +40,23 @@ module.exports = {
created_at: '2019-02-12 07:49:07.556 +00:00',
updated_at: '2019-02-12 07:49:07.556 +00:00',
},
+ {
+ id: '3e1e3c30-18c6-4311-8ac3-7ebd2cea10d2',
+ name: 'Test device battery',
+ selector: 'test-device-feature-battery',
+ external_id: 'hue:battery:1',
+ category: 'battery',
+ type: 'integer',
+ read_only: true,
+ has_feedback: false,
+ min: 0,
+ max: 100,
+ last_value: 20,
+ last_value_changed: '2019-02-12 07:49:07.556 +00:00',
+ device_id: '7f85c2f8-86cc-4600-84db-6c074dadb4e8',
+ created_at: '2019-02-12 07:49:07.556 +00:00',
+ updated_at: '2019-02-12 07:49:07.556 +00:00',
+ },
{
id: 'bb1af3b9-f87d-4d9c-b5be-958cd9d28900',
name: 'Test temperature sensor celsius',
diff --git a/server/test/lib/device/device.checkBatteries.test.js b/server/test/lib/device/device.checkBatteries.test.js
new file mode 100644
index 0000000000..184c57e21c
--- /dev/null
+++ b/server/test/lib/device/device.checkBatteries.test.js
@@ -0,0 +1,59 @@
+const EventEmitter = require('events');
+const { assert, stub } = require('sinon');
+
+const Device = require('../../../lib/device');
+
+const StateManager = require('../../../lib/state');
+const Job = require('../../../lib/job');
+const { SYSTEM_VARIABLE_NAMES } = require('../../../utils/constants');
+
+const event = new EventEmitter();
+const job = new Job(event);
+
+const user = {
+ getByRole: stub().returns([{ selector: 'admin', language: 'fr' }]),
+};
+
+const messageManager = {
+ sendToUser: stub().returns(null),
+};
+
+describe('Device check batteries', () => {
+ it('should do nothing if is not enabled', async () => {
+ const stateManager = new StateManager(event);
+ const service = {
+ getService: () => null,
+ };
+ const variables = {
+ getValue: () => false,
+ };
+ const device = new Device(event, messageManager, stateManager, service, {}, variables, job, {}, user);
+
+ await device.checkBatteries();
+
+ assert.notCalled(user.getByRole);
+ });
+ it('should send a message if battery is low', async () => {
+ const stateManager = new StateManager(event);
+ const service = {
+ getService: () => null,
+ };
+ const variables = {
+ getValue: (key) => {
+ if (key === SYSTEM_VARIABLE_NAMES.DEVICE_BATTERY_LEVEL_WARNING_ENABLED) {
+ return true;
+ }
+ return 30;
+ },
+ };
+ const device = new Device(event, messageManager, stateManager, service, {}, variables, job, {}, user);
+
+ await device.checkBatteries();
+
+ assert.calledWith(
+ messageManager.sendToUser,
+ 'admin',
+ 'Avertissement !!! Le niveau de la batterie de Test device est inférieur à 30 % (actuel : 20 %)',
+ );
+ });
+});
diff --git a/server/test/lib/user/user.getByRole.test.js b/server/test/lib/user/user.getByRole.test.js
new file mode 100644
index 0000000000..25b22781f3
--- /dev/null
+++ b/server/test/lib/user/user.getByRole.test.js
@@ -0,0 +1,18 @@
+const { expect } = require('chai');
+
+const User = require('../../../lib/user');
+
+describe('user.getByRole', () => {
+ const user = new User();
+ it('should return list of user', async () => {
+ const userFound = await user.getByRole('admin');
+ expect(userFound[0]).to.deep.equal({
+ id: '0cd30aef-9c4e-4a23-88e3-3547971296e5',
+ firstname: 'John',
+ lastname: 'Doe',
+ selector: 'john',
+ email: 'demo@demo.com',
+ language: 'en',
+ });
+ });
+});
From f3da5e156a695ce6a0f700d3e5991c9b72594b7f Mon Sep 17 00:00:00 2001
From: callemand
Date: Fri, 17 Nov 2023 16:22:43 +0100
Subject: [PATCH 5/5] Fix test missing line
---
.../lib/device/device.checkBatteries.test.js | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/server/test/lib/device/device.checkBatteries.test.js b/server/test/lib/device/device.checkBatteries.test.js
index 184c57e21c..261da736de 100644
--- a/server/test/lib/device/device.checkBatteries.test.js
+++ b/server/test/lib/device/device.checkBatteries.test.js
@@ -32,6 +32,26 @@ describe('Device check batteries', () => {
await device.checkBatteries();
assert.notCalled(user.getByRole);
+ assert.notCalled(messageManager.sendToUser);
+ });
+ it('should do nothing if the threshold is not set', async () => {
+ const stateManager = new StateManager(event);
+ const service = {
+ getService: () => null,
+ };
+ const variables = {
+ getValue: (key) => {
+ if (key === SYSTEM_VARIABLE_NAMES.DEVICE_BATTERY_LEVEL_WARNING_ENABLED) {
+ return true;
+ }
+ return undefined;
+ },
+ };
+ const device = new Device(event, messageManager, stateManager, service, {}, variables, job, {}, user);
+
+ await device.checkBatteries();
+
+ assert.notCalled(messageManager.sendToUser);
});
it('should send a message if battery is low', async () => {
const stateManager = new StateManager(event);