From 323d8f6d9f2b98cbebc9043d9bc47a49cd876f17 Mon Sep 17 00:00:00 2001 From: callemand Date: Wed, 20 Sep 2023 19:04:43 +0200 Subject: [PATCH] Humidity widget: User can define the humidity threshold (#1877) --- front/package-lock.json | 112 ++++++------------ front/package.json | 1 + .../room-humidity/EditRoomHumidityBox.jsx | 72 ++++++++++- .../boxs/room-humidity/RoomHumidity.jsx | 51 ++++++-- front/src/components/house/RoomSelector.jsx | 13 +- front/src/config/i18n/en.json | 3 +- front/src/config/i18n/fr.json | 3 +- front/src/style/index.css | 51 ++++++++ server/models/dashboard.js | 3 + server/utils/constants.js | 7 ++ 10 files changed, 226 insertions(+), 90 deletions(-) diff --git a/front/package-lock.json b/front/package-lock.json index 6174a5d769..2e3f424f90 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -37,6 +37,7 @@ "react-dnd-html5-backend": "^16.0.1", "react-dnd-touch-backend": "^16.0.1", "react-select": "^4.3.1", + "react-slider": "^2.0.6", "unistore": "^3.5.2", "useragent-parser-js": "^1.0.3", "uuid": "^3.4.0" @@ -12693,23 +12694,6 @@ "node": ">=4.0" } }, - "node_modules/eslint-plugin-react/node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", @@ -23910,13 +23894,13 @@ } }, "node_modules/prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "node_modules/proxy-addr": { @@ -24356,21 +24340,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/react-big-calendar/node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/react-big-calendar/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/react-clock": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/react-clock/-/react-clock-4.5.0.tgz", @@ -24491,9 +24460,9 @@ } }, "node_modules/react-is": { - "version": "16.11.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.11.0.tgz", - "integrity": "sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==" + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", @@ -24581,6 +24550,17 @@ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" }, + "node_modules/react-slider": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/react-slider/-/react-slider-2.0.6.tgz", + "integrity": "sha512-gJxG1HwmuMTJ+oWIRCmVWvgwotNCbByTwRkFZC6U4MBsHqJBmxwbYRJUmxy4Tke1ef8r9jfXjgkmY/uHOCEvbA==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18" + } + }, "node_modules/react-transition-group": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", @@ -41559,23 +41539,6 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, "resolve": { "version": "2.0.0-next.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", @@ -50077,13 +50040,13 @@ } }, "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "proxy-addr": { @@ -50433,21 +50396,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" } } }, @@ -50530,9 +50478,9 @@ } }, "react-is": { - "version": "16.11.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.11.0.tgz", - "integrity": "sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==" + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "react-lifecycles-compat": { "version": "3.0.4", @@ -50601,6 +50549,14 @@ } } }, + "react-slider": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/react-slider/-/react-slider-2.0.6.tgz", + "integrity": "sha512-gJxG1HwmuMTJ+oWIRCmVWvgwotNCbByTwRkFZC6U4MBsHqJBmxwbYRJUmxy4Tke1ef8r9jfXjgkmY/uHOCEvbA==", + "requires": { + "prop-types": "^15.8.1" + } + }, "react-transition-group": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", diff --git a/front/package.json b/front/package.json index 1697828014..5bce21c9cc 100644 --- a/front/package.json +++ b/front/package.json @@ -75,6 +75,7 @@ "react-dnd-html5-backend": "^16.0.1", "react-dnd-touch-backend": "^16.0.1", "react-select": "^4.3.1", + "react-slider": "^2.0.6", "unistore": "^3.5.2", "useragent-parser-js": "^1.0.3", "uuid": "^3.4.0" diff --git a/front/src/components/boxs/room-humidity/EditRoomHumidityBox.jsx b/front/src/components/boxs/room-humidity/EditRoomHumidityBox.jsx index 8cad86ece2..b22fdf1203 100644 --- a/front/src/components/boxs/room-humidity/EditRoomHumidityBox.jsx +++ b/front/src/components/boxs/room-humidity/EditRoomHumidityBox.jsx @@ -1,8 +1,11 @@ import { Component } from 'preact'; import { Text } from 'preact-i18n'; import BaseEditBox from '../baseEditBox'; +import ReactSlider from 'react-slider'; +import { DEFAULT_VALUE_HUMIDITY } from '../../../../../server/utils/constants'; import RoomSelector from '../../house/RoomSelector'; +import cx from 'classnames'; const updateBoxRoom = (updateBoxRoomFunc, x, y) => room => { updateBoxRoomFunc(x, y, room.selector); @@ -19,6 +22,35 @@ const EditRoomHumidityBox = ({ children, ...props }) => ( updateRoomSelection={updateBoxRoom(props.updateBoxRoom, props.x, props.y)} /> +
+ +
+ +
+
{state.valueNow}%
} + pearling + minDistance={10} + onAfterChange={props.updateBoxValue} + value={[props.humidityMin, props.humidityMax]} + disabled={!(props.box.humidity_use_custom_value || false)} + /> +
); @@ -28,8 +60,46 @@ class EditRoomHumidityBoxComponent extends Component { room: selector }); }; + + updateBoxUseCustomValue = e => { + this.props.updateBoxConfig(this.props.x, this.props.y, { + humidity_use_custom_value: e.target.checked + }); + }; + + updateBoxValue = values => { + this.props.updateBoxConfig(this.props.x, this.props.y, { + humidity_min: values[0], + humidity_max: values[1] + }); + }; + render(props, {}) { - return ; + let humidity_min = this.props.box.humidity_min; + let humidity_max = this.props.box.humidity_max; + + if (!this.props.box.humidity_use_custom_value) { + humidity_min = DEFAULT_VALUE_HUMIDITY.MINIMUM; + humidity_max = DEFAULT_VALUE_HUMIDITY.MAXIMUM; + } + + if (isNaN(humidity_min)) { + humidity_min = DEFAULT_VALUE_HUMIDITY.MINIMUM; + } + if (isNaN(humidity_max)) { + humidity_max = DEFAULT_VALUE_HUMIDITY.MAXIMUM; + } + + return ( + + ); } } diff --git a/front/src/components/boxs/room-humidity/RoomHumidity.jsx b/front/src/components/boxs/room-humidity/RoomHumidity.jsx index 660fb13026..3d6d174d48 100644 --- a/front/src/components/boxs/room-humidity/RoomHumidity.jsx +++ b/front/src/components/boxs/room-humidity/RoomHumidity.jsx @@ -5,25 +5,32 @@ import get from 'get-value'; import actions from '../../../actions/dashboard/boxes/humidityInRoom'; import { DASHBOARD_BOX_STATUS_KEY, DASHBOARD_BOX_DATA_KEY } from '../../../utils/consts'; -import { WEBSOCKET_MESSAGE_TYPES } from '../../../../../server/utils/constants'; +import { DEFAULT_VALUE_HUMIDITY, WEBSOCKET_MESSAGE_TYPES } from '../../../../../server/utils/constants'; const isNotNullOrUndefined = value => value !== undefined && value !== null; const RoomHumidityBox = ({ children, ...props }) => (
- {props.humidity > 45 && props.humidity < 60 && ( - + {isNotNullOrUndefined(props.humidity) && + props.humidity >= props.humidityMin && + props.humidity <= props.humidityMax && ( + + + + )} + {isNotNullOrUndefined(props.humidity) && props.humidity < props.humidityMin && ( + )} - {props.humidity <= 45 && ( - + {isNotNullOrUndefined(props.humidity) && props.humidity > props.humidityMax && ( + )} - {props.humidity >= 60 && ( - + {!isNotNullOrUndefined(props.humidity) && ( + )} @@ -74,8 +81,36 @@ class RoomHumidityBoxComponent extends Component { const boxStatus = get(props, `${DASHBOARD_BOX_STATUS_KEY}HumidityInRoom.${props.x}_${props.y}`); const humidity = get(boxData, 'room.humidity.humidity'); const unit = get(boxData, 'room.humidity.unit'); + + const humidity_use_custom_value = get(props, 'box.humidity_use_custom_value'); + let humidity_min = get(props, 'box.humidity_min'); + let humidity_max = get(props, 'box.humidity_max'); + + if (!humidity_use_custom_value) { + humidity_min = DEFAULT_VALUE_HUMIDITY.MINIMUM; + humidity_max = DEFAULT_VALUE_HUMIDITY.MAXIMUM; + } + + if (isNaN(humidity_min)) { + humidity_min = DEFAULT_VALUE_HUMIDITY.MINIMUM; + } + if (isNaN(humidity_max)) { + humidity_max = DEFAULT_VALUE_HUMIDITY.MAXIMUM; + } + const roomName = get(boxData, 'room.name'); - return ; + return ( + + ); } } diff --git a/front/src/components/house/RoomSelector.jsx b/front/src/components/house/RoomSelector.jsx index b2f35e538d..89da017f67 100644 --- a/front/src/components/house/RoomSelector.jsx +++ b/front/src/components/house/RoomSelector.jsx @@ -70,7 +70,18 @@ class RoomSelector extends Component { } render({}, { selectedRoom, houseOptions }) { - return ({ ...provided, zIndex: 100 }) + }} + /> + ); } } diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index 28b8647a97..1d62ccce83 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -257,7 +257,8 @@ }, "humidityInRoom": { "editRoomLabel": "Select the room you want to display here.", - "noHumidityRecorded": "No humidity recorded recently." + "noHumidityRecorded": "No humidity recorded recently.", + "thresholdsLabel": "Configure custom thresholds" }, "userPresence": { "description": "Display who's at home and who is not. You can change the user presence in scenes, and select here the users displayed.", diff --git a/front/src/config/i18n/fr.json b/front/src/config/i18n/fr.json index 38918180c7..3288aa9514 100644 --- a/front/src/config/i18n/fr.json +++ b/front/src/config/i18n/fr.json @@ -257,7 +257,8 @@ }, "humidityInRoom": { "editRoomLabel": "Sélectionnez la pièce que vous souhaitez afficher ici.", - "noHumidityRecorded": "Aucune mesure d'humidité enregistrée récemment." + "noHumidityRecorded": "Aucune mesure d'humidité enregistrée récemment.", + "thresholdsLabel": "Configurer des seuils personnalisés" }, "userPresence": { "description": "Cette box affiche qui est à la maison et qui ne l'est pas. Vous pouvez changer la présence d'un utilisateur dans les scènes, et sélectionner ici quels utilisateurs afficher.", diff --git a/front/src/style/index.css b/front/src/style/index.css index f5a3d1e499..def1688683 100644 --- a/front/src/style/index.css +++ b/front/src/style/index.css @@ -307,3 +307,54 @@ body { .react-clock__second-hand { transition: transform cubic-bezier(0.68, 0, 0.27, 1.55) 0.2s; } + +.opacity-60 { + opacity: 60%; +} + + +.humidity-slider { + width: 100%; + max-width: 500px; + height: 30px; +} + +.humidity-slider-thumb { + font-size: 0.9em; + text-align: center; + background-color: #fafafa; + #color: white; + cursor: pointer; + border: 1px solid gray; + border-radius: 10px; + box-sizing: border-box; +} + +.humidity-slider-thumb.active { + border: 1px solid #467fcf; +} + +.humidity-slider-track { + position: relative; + background: #f1c40f; +} + +.humidity-slider-track-1 { + background: #5eba00; +} + +.humidity-slider-track-2 { + background: #467fcf ; +} + +.humidity-slider .humidity-slider-track { + top: 10px; + height: 10px; +} + +.humidity-slider .humidity-slider-thumb { + top: 3px; + width: 41px; + height: 24px; + line-height: 22px; +} diff --git a/server/models/dashboard.js b/server/models/dashboard.js index bb092f774f..e32929342d 100644 --- a/server/models/dashboard.js +++ b/server/models/dashboard.js @@ -29,6 +29,9 @@ const boxesSchema = Joi.array().items( camera_latency: Joi.string(), camera_live_auto_start: Joi.boolean(), scenes: Joi.array().items(Joi.string()), + humidity_use_custom_value: Joi.boolean(), + humidity_min: Joi.number(), + humidity_max: Joi.number(), }), ), ); diff --git a/server/utils/constants.js b/server/utils/constants.js index 4de848f105..ab4b91e31d 100644 --- a/server/utils/constants.js +++ b/server/utils/constants.js @@ -944,6 +944,11 @@ const JOB_ERROR_TYPES = { UNKNOWN_ERROR: 'unknown-error', }; +const DEFAULT_VALUE_HUMIDITY = { + MINIMUM: 45, + MAXIMUM: 60, +}; + const createList = (obj) => { const list = []; Object.keys(obj).forEach((key) => { @@ -1039,3 +1044,5 @@ module.exports.JOB_STATUS = JOB_STATUS; module.exports.JOB_STATUS_LIST = JOB_STATUS_LIST; module.exports.JOB_ERROR_TYPES = JOB_ERROR_TYPES; module.exports.JOB_ERROR_TYPES_LIST = JOB_ERROR_TYPES_LIST; + +module.exports.DEFAULT_VALUE_HUMIDITY = DEFAULT_VALUE_HUMIDITY;