Skip to content

Commit

Permalink
Rework sensor value display, show humidity
Browse files Browse the repository at this point in the history
  • Loading branch information
user890104 committed Oct 23, 2024
1 parent 687b536 commit 86a0a6b
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 34 deletions.
9 changes: 3 additions & 6 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
};
Expand Down
61 changes: 36 additions & 25 deletions src/widgets/SensorReading/SensorReading.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<Col>
<Card bg="primary" text={isCurrent ? 'white' : 'secondary'}>
<Card.Body>
Expand All @@ -51,10 +59,12 @@ const SensorReading = ({
<i className={'fa-solid fa-5x fa-thermometer-' + thermometerState} />
</Col>
<Col xs={9} className="text-end">
<div className={'huge' + (isCurrent ? '' : ' text-decoration-line-through')}>
{formattedValue}
<div className="huge">
{values?.temperature && <SensorReadingValue {...values.temperature} />}
{values?.temperature && values?.humidity && ' '}
{values?.humidity && <SensorReadingValue {...values.humidity} />}
</div>
<div title={formattedTimestamp}>{label}</div>
<div>{label}</div>
</Col>
</Row>
</Container>
Expand All @@ -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;
16 changes: 16 additions & 0 deletions src/widgets/SensorReading/SensorReadingValue.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import PropTypes from 'prop-types';

const SensorReadingValue = ({
isCurrent,
formattedTimestamp,
formattedValue,
}) => (<span className={'huge' + (isCurrent ? '' : ' text-decoration-line-through')}
title={formattedTimestamp}>{formattedValue}</span>);

SensorReadingValue.propTypes = {
isCurrent: PropTypes.bool.isRequired,
formattedTimestamp: PropTypes.string.isRequired,
formattedValue: PropTypes.string.isRequired,
};

export default SensorReadingValue;
15 changes: 12 additions & 3 deletions src/widgets/SensorReadingsWrapper/SensorReadingsWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<>
<Row>
<Col>
Expand All @@ -35,8 +44,8 @@ const SensorReadingsWrapper = () => {
</Col>
</Row>}
{mqttStatus && <Row className="row-cols-1 row-cols-lg-3 g-3">
{Object.entries(sensors).filter(([topic]) => Object.prototype.hasOwnProperty.call(mqttStatus, topic)).map(([topic, sensor]) =>
<SensorReading key={topic} {...sensor} {...mqttStatus[topic]} />
{sensorReadings.map(sensorReading =>
<SensorReading key={sensorReading.label} {...sensorReading} />
)}
</Row>}
</>);
Expand Down

0 comments on commit 86a0a6b

Please sign in to comment.