status card for gas readings

This commit is contained in:
Carter 2025-05-21 13:49:00 -06:00
parent abde5c6499
commit 6dc181b24c
5 changed files with 251 additions and 4 deletions

4
package-lock.json generated
View file

@ -42,7 +42,7 @@
"mui-tel-input": "^7.0.0",
"notistack": "^3.0.1",
"openweathermap-ts": "^1.2.10",
"protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#master",
"protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging",
"react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1",
"react-color": "^2.19.3",
@ -10864,7 +10864,7 @@
},
"node_modules/protobuf-ts": {
"version": "1.0.0",
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#f8704561c094b208029340cdf2ce0c510ef1eb12",
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#cead0be8708fa2c6679c77ed38f51239341c4ed4",
"dependencies": {
"protobufjs": "^6.8.8"
}

View file

@ -52,7 +52,7 @@
"mui-tel-input": "^7.0.0",
"notistack": "^3.0.1",
"openweathermap-ts": "^1.2.10",
"protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#master",
"protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging",
"react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1",
"react-color": "^2.19.3",

162
src/common/StatusGas.tsx Normal file
View file

@ -0,0 +1,162 @@
import { Box, Grid2, Theme, Tooltip, Typography } from "@mui/material";
import { blue, deepOrange, green, grey, orange, teal } from "@mui/material/colors";
import { makeStyles } from "@mui/styles";
import { Device } from "models";
import PulseBox from "./PulseBox";
import { or } from "utils";
import { useEffect, useState } from "react";
interface Props {
device: Device
// noDust?: boolean;
gasType: "co2" | "co" | "no2" | "o2" | "all"
}
const useStyles = makeStyles((theme: Theme) => {
const lightMode = theme.palette?.mode === "light";
return ({
box: {
display: "inline-block",
borderRadius: "6px",
backgroundColor: lightMode ? "rgb(210, 210, 210)" : "rgb(50, 50, 50)",
padding: theme.spacing(0.75),
marginRight: "auto",
margin: theme.spacing(1),
width: theme.spacing(14),
height: theme.spacing(6),
},
carouselContainer: {
position: 'relative',
height: '40px',
overflow: 'hidden',
textAlign: "center",
},
carouselItem: {
opacity: 0,
transform: 'translateX(100%)',
transition: 'opacity 0.5s ease, transform 0.5s ease',
position: 'absolute',
width: '100%',
overflow: "hidden",
// height: '100%',
},
active: {
opacity: 1,
transform: 'translateX(0)',
},
inactive: {
transform: 'translateX(-100%)',
},
})
})
export default function StatusGas(props: Props) {
const { device, gasType } = props;
const classes = useStyles()
// console.log(noDust)
// const colors = or(device.status.sen5x?.overlays.map(overlay => overlay.color), []);
// const messages = or(device.status.sen5x?.overlays.map(overlay => overlay.message), []);
// const [color, setColor] = useState(colors.length > 0 ? colors[0] : "");
// const [, setColorIndex] = useState(0)
// useEffect(() => {
// const interval = setInterval(() => {
// setColorIndex(prevIndex => {
// const newIndex = prevIndex >= colors.length - 1 ? 0 : prevIndex + 1;
// setColor(colors[newIndex]); // Use the new index here
// return newIndex;
// });
// }, 7500);
// return () => clearInterval(interval);
// }, []);
// const [currentIndex, setCurrentIndex] = useState(0);
// Auto-cycle through measurements every 5 seconds
// useEffect(() => {
// if (noDust) {
// setCurrentIndex(0)
// return
// }
// const interval = setInterval(() => {
// if (!noDust) setCurrentIndex((prevIndex) => (prevIndex + 1) % 2);
// setCurrentIndex((prevIndex) => (prevIndex + 1) % 2);
// }, 5000);
// return () => clearInterval(interval); // Cleanup on unmount
// }, [noDust]);
// const tooltip = () => {
// if (messages.length === 0) return null
// if (messages.length < 2) return (
// messages[0]
// )
// return (
// <Grid2 container direction={"column"}>
// <Grid2 container direction="row">
// <Typography fontWeight={"bold"} variant="caption">
// Overlay Messages
// </Typography>
// </Grid2>
// {messages.map((message, index) => {
// return (
// <Grid2 key={"tooltip-overly-message-" + message} container direction="row" alignItems={"center"}>
// <Box
// sx={{
// width: 10, // Size of the square
// height: 10,
// backgroundColor: colors[index], // Passed color prop
// borderRadius: 1, // Optional: slight rounding, remove for sharp square
// border: "1px solid black",
// }}
// />
// <Typography variant="caption" sx={{ marginLeft: 1 }}>
// {message}
// </Typography>
// </Grid2>
// )
// })}
// </Grid2>
// )
// }
const gasDisplay = () => {
if (gasType !== "all") {
return(
// <Tooltip title={tooltip()}>
<PulseBox color="" className={classes.box} >
<Grid2 container direction="column" >
<Grid2 container direction="column">
<Grid2>
{gasType !== "o2" ?
<Typography variant="body2" style={{ color: teal[500]}}>
Gas: {device.status[gasType]?.ppm.toFixed(1)}ppm
</Typography>
:
<Typography variant="body2" style={{ color: blue[500]}}>
Gas: {(device.status[gasType]?.ppm!/10000).toFixed(1)}%
</Typography>
}
</Grid2>
<Grid2>
<Typography variant="body2" style={{ color: deepOrange[800]}}>
Volt: {device.status[gasType]?.millivolts.toFixed(1)}mV
</Typography>
</Grid2>
</Grid2>
</Grid2>
</PulseBox>
// </Tooltip>
)
}
}
return (
<>
{gasDisplay()}
</>
)
}

View file

@ -49,7 +49,7 @@ const useStyles = makeStyles((theme: Theme) => {
})
})
export default function StatusPlenum(props: Props) {
export default function StatusSen5x(props: Props) {
const { device, noDust } = props;
const classes = useStyles()
// console.log(noDust)

View file

@ -25,6 +25,7 @@ import moment from "moment";
import StatusPlenum from "common/StatusPlenum";
import StatusSen5x from "common/StatusSen5x";
import StatusDust from "common/StatusDust";
import StatusGas from "common/StatusGas";
const useStyles = makeStyles((theme: Theme) => {
return ({
@ -101,6 +102,10 @@ export default function Devices() {
const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false);
const [hasPlenums, setHasPlenums] = useState(false)
const [hasSen5x, setHasSen5x] = useState(false)
const [hasCo, setHasCo] = useState(false)
const [hasCo2, setHasCo2] = useState(false)
const [hasNo2, setHasNo2] = useState(false)
const [hasO2, setHasO2] = useState(false)
const [groupsLoading, setGroupsLoading] = useState(false)
const [groups, setGroups] = useState<Group[]>([]);
@ -264,6 +269,10 @@ export default function Devices() {
resp.data.devices.forEach(device => {
if (device.status?.plenum?.temperature) setHasPlenums(true)
if (device.status?.sen5x?.temperature) setHasSen5x(true)
if (device.status?.o2) setHasO2(true)
if (device.status?.co) setHasCo(true)
if (device.status?.co2) setHasCo2(true)
if (device.status?.no2) setHasNo2(true)
newDevices.push(Device.create(device))
})
setDevices(newDevices)
@ -311,6 +320,10 @@ export default function Devices() {
return '/' + newSegments.join('/');
}
useEffect(() => {
console.log(devices)
}, [devices])
const toDevice = (device: Device) => {
let url = getGroup() ? insertGroupContext(getGroup().id().toString(), device.id().toString()) : ""
if (url.length < 1) url = device.id().toString()
@ -440,6 +453,78 @@ export default function Devices() {
}
})
}
if (hasCo) {
columns.push({
title: "CO",
render: (device: Device) => {
if (device.status.co) {
return (
<Box sx={{ marginLeft: 1 }}>
<StatusGas device={device} gasType={"co"}/>
</Box>
)
} else {
return (
<></>
)
}
}
})
}
if (hasCo2) {
columns.push({
title: "CO2",
render: (device: Device) => {
if (device.status.co2) {
return (
<Box sx={{ marginLeft: 1 }}>
<StatusGas device={device} gasType={"co2"}/>
</Box>
)
} else {
return (
<></>
)
}
}
})
}
if (hasNo2) {
columns.push({
title: "NO2",
render: (device: Device) => {
if (device.status.no2) {
return (
<Box sx={{ marginLeft: 1 }}>
<StatusGas device={device} gasType={"no2"}/>
</Box>
)
} else {
return (
<></>
)
}
}
})
}
if (hasCo2) {
columns.push({
title: "O2",
render: (device: Device) => {
if (device.status.o2) {
return (
<Box sx={{ marginLeft: 1 }}>
<StatusGas device={device} gasType={"o2"}/>
</Box>
)
} else {
return (
<></>
)
}
}
})
}
return columns
}