From 86a0a6b13d7fd7d48ffbedd1be42b1aa6e02be28 Mon Sep 17 00:00:00 2001 From: Vencislav Atanasov Date: Wed, 23 Oct 2024 19:13:42 +0300 Subject: [PATCH] Rework sensor value display, show humidity --- src/config.js | 9 +-- src/widgets/SensorReading/SensorReading.jsx | 61 +++++++++++-------- .../SensorReading/SensorReadingValue.jsx | 16 +++++ .../SensorReadingsWrapper.jsx | 15 ++++- 4 files changed, 67 insertions(+), 34 deletions(-) create mode 100644 src/widgets/SensorReading/SensorReadingValue.jsx diff --git a/src/config.js b/src/config.js index 3cf434c..7f08aba 100644 --- a/src/config.js +++ b/src/config.js @@ -5,16 +5,13 @@ export const oidc = { }; export const sensors = { - 'sensors/big-room/temperature': { - type: 'Temperature', + 'sensors/big-room': { label: 'Big room', }, - 'sensors/small-room/temperature': { - type: 'Temperature', + 'sensors/small-room': { label: 'Small room', }, - 'sensors/kitchen/temperature': { - type: 'Temperature', + 'sensors/kitchen': { label: 'Kitchen', }, }; diff --git a/src/widgets/SensorReading/SensorReading.jsx b/src/widgets/SensorReading/SensorReading.jsx index c8e380c..1eadd30 100644 --- a/src/widgets/SensorReading/SensorReading.jsx +++ b/src/widgets/SensorReading/SensorReading.jsx @@ -5,43 +5,51 @@ import { isValid } from 'date-fns'; import { useDateTimeFormatter } from '../../utils/useDateTimeFormatter.js'; import './SensorReading.css'; +import { useMemo } from 'react'; +import SensorReadingValue from './SensorReadingValue.jsx'; const units = { - Temperature: ['°C', 1], - Humidity: ['%', 1], + temperature: '°C', + humidity: '%', + battery: '%', }; const thresholds = [18, 24, 26, 32]; const SensorReading = ({ - type, label, - timestamp, - value, + values: rawValues, }) => { const { formatDefault, formatDistanceToNow, } = useDateTimeFormatter(); - if (!isValid(timestamp) || typeof value !== 'number') { - return null; - } + const values = useMemo(() => Object.fromEntries(Object.entries(rawValues).filter(([type, value]) => + Object.prototype.hasOwnProperty.call(units, type) && isValid(value.timestamp) && typeof value.value === 'number' + ).map(([type, value]) => [type, { + ...value, + dt: new Date(value.timestamp), + thermometerState: type === 'temperature' ? thresholds.filter(threshold => threshold < value).length : 0, + unit: units[type], + }]).map(([type, value]) => [type, { + ...value, + formattedTimestamp: formatDefault(value.dt) + ' (' + formatDistanceToNow(value.dt) + ')', + formattedValue: value.value.toFixed() + value.unit, + readingAge: Date.now() - value.dt, + }]).map(([type, value]) => [type, { + ...value, + isCurrent: value.readingAge <= 7_200_000, + isVisible: value.readingAge <= 86_400_000, + }]).filter(([, value]) => value.isVisible)), [formatDefault, formatDistanceToNow, rawValues]); - const lastUpdate = new Date(timestamp); - const formattedTimestamp = formatDefault(lastUpdate) + ' (' + formatDistanceToNow(lastUpdate) + ')'; - const unit = units[type]; - const formattedValue = value.toFixed(unit[1]) + unit[0]; - const readingAge = Date.now() - timestamp; - const isCurrent = readingAge <= 7_200_000; - const isVisible = readingAge <= 86_400_000; - // TODO: only for type === Temperature - const thermometerState = thresholds.filter(threshold => threshold < value).length; - - if (!isVisible) { + if (Object.keys(values).length < 1) { return null; } + const isCurrent = values?.temperature?.isCurrent || values?.humidity?.isCurrent; + const thermometerState = values?.temperature?.thermometerState; + return ( @@ -51,10 +59,12 @@ const SensorReading = ({ -
- {formattedValue} +
+ {values?.temperature && } + {values?.temperature && values?.humidity && ' '} + {values?.humidity && }
-
{label}
+
{label}
@@ -64,10 +74,11 @@ const SensorReading = ({ }; SensorReading.propTypes = { - type: PropTypes.string.isRequired, label: PropTypes.string.isRequired, - timestamp: PropTypes.number.isRequired, - value: PropTypes.number.isRequired, + values: PropTypes.objectOf(PropTypes.shape({ + timestamp: PropTypes.number.isRequired, + value: PropTypes.number.isRequired, + })).isRequired, }; export default SensorReading; diff --git a/src/widgets/SensorReading/SensorReadingValue.jsx b/src/widgets/SensorReading/SensorReadingValue.jsx new file mode 100644 index 0000000..1295b93 --- /dev/null +++ b/src/widgets/SensorReading/SensorReadingValue.jsx @@ -0,0 +1,16 @@ +import PropTypes from 'prop-types'; + +const SensorReadingValue = ({ + isCurrent, + formattedTimestamp, + formattedValue, +}) => ({formattedValue}); + +SensorReadingValue.propTypes = { + isCurrent: PropTypes.bool.isRequired, + formattedTimestamp: PropTypes.string.isRequired, + formattedValue: PropTypes.string.isRequired, +}; + +export default SensorReadingValue; diff --git a/src/widgets/SensorReadingsWrapper/SensorReadingsWrapper.jsx b/src/widgets/SensorReadingsWrapper/SensorReadingsWrapper.jsx index 6a7b6d1..decf5b7 100644 --- a/src/widgets/SensorReadingsWrapper/SensorReadingsWrapper.jsx +++ b/src/widgets/SensorReadingsWrapper/SensorReadingsWrapper.jsx @@ -6,18 +6,27 @@ import SensorReading from '../SensorReading/SensorReading.jsx'; import { useMqttStatus } from '../../hooks/useMqttStatus.js'; import LoadingIcon from '../icons/LoadingIcon.jsx'; import ErrorMessage from '../ErrorMessage.jsx'; +import { useMemo } from 'react'; const SensorReadingsWrapper = () => { const { t } = useTranslation(); const { - data: mqttStatus, + data: mqttStatus = {}, error, isLoading, } = useMqttStatus({ refreshInterval: 60_000, }); + const sensorReadings = useMemo(() => Object.entries(sensors).map(([topicPrefix, config]) => ({ + label: config.label, + values: Object.fromEntries(Object.entries(mqttStatus).filter(([topic]) => topic.startsWith(topicPrefix)).map(([topic, reading]) => [ + topic.substring(topicPrefix.length + 1), + reading, + ])), + })), [mqttStatus]); + return (<> @@ -35,8 +44,8 @@ const SensorReadingsWrapper = () => { } {mqttStatus && - {Object.entries(sensors).filter(([topic]) => Object.prototype.hasOwnProperty.call(mqttStatus, topic)).map(([topic, sensor]) => - + {sensorReadings.map(sensorReading => + )} } );