diff --git a/package-lock.json b/package-lock.json index e369745..64aa39b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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#3f9a71a350651fb151d18514f990794f62a8e0cc", "dependencies": { "protobufjs": "^6.8.8" } diff --git a/package.json b/package.json index 8e3ebd3..5a32392 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/common/ButtonGroup.tsx b/src/common/ButtonGroup.tsx index f23bc0a..dfadd58 100644 --- a/src/common/ButtonGroup.tsx +++ b/src/common/ButtonGroup.tsx @@ -72,14 +72,14 @@ const useStyles = makeStyles((theme: Theme) => { marginLeft: theme.spacing(0.05), marginRight: theme.spacing(0.05), "&:hover": { - backgroundColor: getSecondaryColour(), //use the colour of the whitelabel - //backgroundColor: "gold", + //backgroundColor: getSecondaryColour(), //use the colour of the whitelabel + backgroundColor: "gold", //for now Dustin just wants it to be gold color: "black", } }, buttonToggled: { - backgroundColor: getSecondaryColour(), //use the colour of the whitelabel - //backgroundColor: "gold", + //backgroundColor: getSecondaryColour(), //use the colour of the whitelabel + backgroundColor: "gold", //for now Dustin just wants it to be gold color: "black", borderRadius: 24, fontWeight: "bold" @@ -148,9 +148,9 @@ export default function ButtonGroup(props: Props){ }}> {b.icon ? - b.icon + b.icon : - + {b.title} } diff --git a/src/common/ResponsiveTable.tsx b/src/common/ResponsiveTable.tsx index 3f637d3..628836c 100644 --- a/src/common/ResponsiveTable.tsx +++ b/src/common/ResponsiveTable.tsx @@ -1,4 +1,4 @@ -import { Box, Card, Checkbox, CircularProgress, Collapse, darken, Divider, Grid2, IconButton, InputAdornment, ListItemButton, ListItemIcon, ListItemText, Menu, MenuItem, Paper, SxProps, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TextField, Theme, Typography } from "@mui/material"; +import { Box, Button, Card, Checkbox, CircularProgress, Collapse, darken, Divider, Grid2, IconButton, InputAdornment, ListItemButton, ListItemIcon, ListItemText, Menu, MenuItem, Paper, SxProps, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TextField, Theme, Typography } from "@mui/material"; import { makeStyles } from "@mui/styles"; import { ArrowDownward, ArrowUpward, ChevronRight, Close, Search, ViewColumn } from "@mui/icons-material" import { useEffect, useState } from "react"; @@ -98,6 +98,8 @@ interface Props { setOrderBy?: React.Dispatch>; order?: string; setOrder?: React.Dispatch>; + endTitleElement?: string | JSX.Element | (() => string | JSX.Element); + loadMore?: () => void } export default function ResponsiveTable(props: Props) { @@ -130,13 +132,15 @@ export default function ResponsiveTable(props: Props) { orderBy, setOrderBy, order, - setOrder + setOrder, + loadMore } = props const classes = useStyles(); const columns = typeof(props.columns) === "function" ? props.columns() : props.columns const subtitle = typeof(props.subtitle) === "function" ? props.subtitle() : props.subtitle const title = typeof(props.title) === "function" ? props.title() : props.title // const order = props.order ? props.order : "asc" + const endTitleElement = typeof(props.endTitleElement) === "function" ? props.endTitleElement() : props.endTitleElement const isMobile = useMobile() @@ -371,6 +375,17 @@ export default function ResponsiveTable(props: Props) { ) } + {(loadMore && rows.length < total) && + + + + } ) @@ -387,8 +402,20 @@ export default function ResponsiveTable(props: Props) { const columnClick = (column: Column) => { const sortKey = column.sortKey ? column.sortKey : column.title.toLowerCase() // console.log(column.sortKey) - if (setOrderBy) setOrderBy(sortKey) - if (setOrder && order) setOrder(order === "asc" ? "desc" : "asc") + if (setOrderBy) { + if (orderBy === sortKey && setOrder && order) { + if (order === "desc") { + setOrder("asc") + } else { + setOrderBy("") + } + // setOrder(order === "asc" ? "desc" : "asc") + } else { + if (setOrder) setOrder("desc") + setOrderBy(sortKey) + } + } + // if (setOrder && order) setOrder(order === "asc" ? "desc" : "asc") } const showOrderIcon = (column: Column) => { @@ -422,6 +449,9 @@ export default function ResponsiveTable(props: Props) { {actions && actions} + + {endTitleElement} + {setSearchText && {searchBar()} diff --git a/src/common/StatusDust.tsx b/src/common/StatusDust.tsx new file mode 100644 index 0000000..2333318 --- /dev/null +++ b/src/common/StatusDust.tsx @@ -0,0 +1,149 @@ +import { Box, Grid2, Theme, Tooltip, Typography } from "@mui/material"; +import { green } 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 +} + +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(16), + height: theme.spacing(11), + }, + 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 StatusDust(props: Props) { + const { device } = props; + const classes = useStyles() + + 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 3 seconds + // useEffect(() => { + // const interval = setInterval(() => { + // setCurrentIndex((prevIndex) => (prevIndex + 1) % 2); + // }, 3000); // Adjust timing as needed (3000ms = 3s) + + // return () => clearInterval(interval); // Cleanup on unmount + // }, []); + + const tooltip = () => { + if (messages.length === 0) return null + if (messages.length < 2) return ( + messages[0] + ) + return ( + + + + Overlay Messages + + + {messages.map((message, index) => { + return ( + + + + {message} + + + ) + })} + + ) + } + + return ( + + + + + + + {"<1um:"} {device.status.sen5x?.dust1ug.toFixed(1)}ug/m3 + + + + + {"<2.5um:"} {device.status.sen5x?.dust2ug.toFixed(1)}ug/m3 + + + + + {"<4um: "}{device.status.sen5x?.dust4ug.toFixed(1)}ug/m3 + + + + + {"<10um: "}{device.status.sen5x?.dust10ug.toFixed(1)}ug/m3 + + + + + + + ) +} \ No newline at end of file diff --git a/src/common/StatusGas.tsx b/src/common/StatusGas.tsx new file mode 100644 index 0000000..7b40e60 --- /dev/null +++ b/src/common/StatusGas.tsx @@ -0,0 +1,198 @@ +import { Box, Grid2, Theme, Tooltip, Typography } from "@mui/material"; +import { blue, deepOrange, 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), + }, + boxTall: { + 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(8.5), + }, + 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) + let colors: string[] = [] + let messages: string[] = [] + if (gasType === "all") { + colors = or(device.status.o2?.overlays.map(overlay => overlay.color), []); + colors.push(...or(device.status.co2?.overlays.map(overlay => overlay.color), [])); + colors.push(...or(device.status.no2?.overlays.map(overlay => overlay.color), [])); + colors.push(...or(device.status.co?.overlays.map(overlay => overlay.color), [])); + messages = or(device.status.o2?.overlays.map(overlay => overlay.message), []); + messages.push(...or(device.status.co2?.overlays.map(overlay => overlay.message), [])); + messages.push(...or(device.status.no2?.overlays.map(overlay => overlay.message), [])); + messages.push(...or(device.status.co?.overlays.map(overlay => overlay.message), [])); + } else { + colors = or(device.status[gasType]?.overlays.map(overlay => overlay.color), []); + messages = or(device.status[gasType]?.overlays.map(overlay => overlay.message), []); + } + + const [color, setColor] = useState(colors.length > 0 ? colors[0] : ""); + const [, setColorIndex] = useState(0) + + const gasTypes: ("o2" | "no2" | "co2" | "co")[] = ["o2", "no2", "co2", "co"] + + 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 (gasType !== "all") { + setCurrentIndex(0) + return + } + const interval = setInterval(() => { + if (gasType === "all") setCurrentIndex((prevIndex) => (prevIndex + 1) % 4); + else setCurrentIndex((prevIndex) => (prevIndex + 1) % 4); + }, 6000); + + return () => clearInterval(interval); // Cleanup on unmount + }, [gasType]); + + const tooltip = () => { + if (messages.length === 0) return null + if (messages.length < 2) return ( + messages[0] + ) + return ( + + + + Overlay Messages + + + {messages.map((message, index) => { + return ( + + + + + + + {message} + + + + ) + })} + + ) + } + + const gasDisplay = () => { + const gas = gasType === "all" ? gasTypes[currentIndex] : gasType + // if (gasType !== "all") { + return ( + + + + { gasType === "all" && + + {gas.toUpperCase()} + + } + + + {gas !== "o2" ? + + Gas: {device.status[gas]?.ppm.toFixed(1)}ppm + + : + + Gas: {(device.status[gas]?.ppm!/10000).toFixed(1)}% + + } + + + + Volt: {device.status[gas]?.millivolts.toFixed(1)}mV + + + + + + + ) + + // } + } + + return ( + <> + {gasDisplay()} + + ) +} \ No newline at end of file diff --git a/src/common/StatusSen5x.tsx b/src/common/StatusSen5x.tsx index 912f128..288eb10 100644 --- a/src/common/StatusSen5x.tsx +++ b/src/common/StatusSen5x.tsx @@ -5,8 +5,10 @@ import { Device } from "models"; import PulseBox from "./PulseBox"; import { or } from "utils"; import { useEffect, useState } from "react"; + interface Props { device: Device + noDust?: boolean; } const useStyles = makeStyles((theme: Theme) => { @@ -19,8 +21,8 @@ const useStyles = makeStyles((theme: Theme) => { padding: theme.spacing(0.75), marginRight: "auto", margin: theme.spacing(1), - width: "72px", - height: "50px", + width: theme.spacing(16), + height: theme.spacing(11), }, carouselContainer: { position: 'relative', @@ -47,10 +49,10 @@ const useStyles = makeStyles((theme: Theme) => { }) }) -export default function StatusPlenum(props: Props) { - const { device } = props; +export default function StatusSen5x(props: Props) { + const { device, noDust } = 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), []); @@ -71,14 +73,18 @@ export default function StatusPlenum(props: Props) { const [currentIndex, setCurrentIndex] = useState(0); - // Auto-cycle through measurements every 3 seconds + // Auto-cycle through measurements every 5 seconds useEffect(() => { + if (noDust) { + setCurrentIndex(0) + return + } const interval = setInterval(() => { - setCurrentIndex((prevIndex) => (prevIndex + 1) % 4); - }, 3000); // Adjust timing as needed (3000ms = 3s) + if (!noDust) setCurrentIndex((prevIndex) => (prevIndex + 1) % 2); + }, 5000); return () => clearInterval(interval); // Cleanup on unmount - }, []); + }, [noDust]); const tooltip = () => { if (messages.length === 0) return null @@ -125,66 +131,52 @@ export default function StatusPlenum(props: Props) { > - {device.status.sen5x?.temperature.toFixed(1)}°C + Temp: {device.status.sen5x?.temperature.toFixed(1)}°C - {device.status.sen5x?.humidity.toFixed(1)}% + Humidity: {device.status.sen5x?.humidity.toFixed(1)}% + + + + + Voc: {device.status.sen5x?.voc.toFixed(1)}% + + + + + Nox: {device.status.sen5x?.nox.toFixed(1)}% - - {device.status.sen5x?.dust1ug.toFixed(1)}ug/m3 + {"<1um:"} {device.status.sen5x?.dust1ug.toFixed(1)}ug/m3 - {device.status.sen5x?.dust2ug.toFixed(1)}ug/m3 + {"<2.5um:"} {device.status.sen5x?.dust2ug.toFixed(1)}ug/m3 - - - - {device.status.sen5x?.dust4ug.toFixed(1)}ug/m3 + {"<4um: "}{device.status.sen5x?.dust4ug.toFixed(1)}ug/m3 - {device.status.sen5x?.dust10ug.toFixed(1)}ug/m3 + {"<10um: "}{device.status.sen5x?.dust10ug.toFixed(1)}ug/m3 - - - - - - V: {device.status.sen5x?.voc.toFixed(1)}% - - - - - N: {device.status.sen5x?.nox.toFixed(1)}% - - - + } diff --git a/src/component/ComponentActions.tsx b/src/component/ComponentActions.tsx index f633240..cd1f2c6 100644 --- a/src/component/ComponentActions.tsx +++ b/src/component/ComponentActions.tsx @@ -17,7 +17,7 @@ import { isController, isSource } from "pbHelpers/ComponentType"; import { DeviceAvailabilityMap, OffsetAvailabilityMap } from "pbHelpers/DeviceAvailability"; import { pond } from "protobuf-ts/pond"; import { useGlobalState } from "providers"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { or } from "utils/types"; import ComponentSettings from "./ComponentSettings"; import { green, teal } from "@mui/material/colors"; @@ -94,6 +94,7 @@ export default function ComponentActions(props: Props) { }; const openComponentSettingsDialog = (mode: string) => { + console.log("delete") setIsComponentSettingsOpen(true); setComponentSettingsMode(or(mode, "")); }; @@ -140,7 +141,7 @@ export default function ComponentActions(props: Props) { {canEdit && ( { openComponentSettingsDialog("remove") - closeMoreMenu() + setAnchorEl(null); }}> diff --git a/src/component/ComponentCard.tsx b/src/component/ComponentCard.tsx index 19482bc..a2231a6 100644 --- a/src/component/ComponentCard.tsx +++ b/src/component/ComponentCard.tsx @@ -254,15 +254,15 @@ export default function ComponentCard(props: Props) { size="small" onTouchStart={event => { event.stopPropagation(); - event.preventDefault(); + //event.preventDefault(); }} onMouseDown={event => { event.stopPropagation(); - event.preventDefault(); + //event.preventDefault(); }} onClick={event => { event.stopPropagation(); - event.preventDefault(); + //event.preventDefault(); }} disabled={!canEdit} onChange={(_, checked) => updateControllerState(checked)} diff --git a/src/device/DevicesSummary.tsx b/src/device/DevicesSummary.tsx index e09a5e6..827ffbb 100644 --- a/src/device/DevicesSummary.tsx +++ b/src/device/DevicesSummary.tsx @@ -4,7 +4,7 @@ import { Check, PermScanWifi, } from "@mui/icons-material"; -import { Card, Grid2, Skeleton, Theme, Typography, useTheme } from "@mui/material"; +import { Card, Grid2, Theme, Typography } from "@mui/material"; import { green } from "@mui/material/colors"; import { makeStyles } from "@mui/styles"; import classNames from "classnames"; @@ -41,7 +41,7 @@ interface Props { export default function DevicesSummary(props: Props) { const { setFieldContains, group } = props; const deviceAPI = useDeviceAPI() - const theme = useTheme() + // const theme = useTheme() const classes = useStyles() const [stats, setStats] = useState(pond.DeviceStatistics.create()) @@ -205,7 +205,7 @@ export default function DevicesSummary(props: Props) { } /> diff --git a/src/device/deviceSVG.tsx b/src/device/deviceSVG.tsx index d6399f7..e279268 100644 --- a/src/device/deviceSVG.tsx +++ b/src/device/deviceSVG.tsx @@ -5204,6 +5204,933 @@ export default function DeviceSVG(props: Props) { ); }; + const streamlineMonitorSVG = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* buttons for port selection */} + {/* I2C PORTS */} + { + openMenu(e, { + address: 0, //address here is not important for I2C, the restrictions are per component not per port + addressType: quack.AddressType.ADDRESS_TYPE_I2C, + label: "1", + autoDetectable: false + }); + }} + className={classes.testButton} + id="button" + cx="20.5" + cy="44" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* CONFIGURABLE PIN PORTS - Note input Ports 3 and 4 and all control ports are locked for the monitor */} + {/* PORT 1 */} + { + openMenu(e, { + address: 1, + addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + label: "1", + autoDetectable: true + }); + }} + className={classes.testButton} + id="button" + cx="20" + cy="80" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* PORT 2 */} + { + openMenu(e, { + address: 2, + addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + label: "2", + autoDetectable: true + }); + }} + className={classes.testButton} + id="button" + cx="20.5" + cy="116" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* PORT 3 */} + { + setLockOpen(true); + // openMenu(e, { + // address: 4, + // addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + // label: "3", + // autoDetectable: true + // }); + }} + className={classes.testButton} + id="button" + cx="21.5" + cy="153" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* Port 4 */} + { + setLockOpen(true); + // openMenu(e, { + // address: 8, + // addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + // label: "3", + // autoDetectable: true + // }); + }} + className={classes.testButton} + id="button" + cx="21.5" + cy="189" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* Controller ports - note these are all locked for monitor devices */} + {/* PORT C1 */} + { + setLockOpen(true); + // openMenu(e, { + // address: 2, + // addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + // label: "2", + // autoDetectable: true + // }); + }} + className={classes.testButton} + id="button" + cx="98" + cy="206" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* PORT C2 */} + { + setLockOpen(true); + // openMenu(e, { + // address: 4, + // addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + // label: "3", + // autoDetectable: true + // }); + }} + className={classes.testButton} + id="button" + cx="140" + cy="206" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* Port C3 */} + { + setLockOpen(true); + // openMenu(e, { + // address: 8, + // addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + // label: "3", + // autoDetectable: true + // }); + }} + className={classes.testButton} + id="button" + cx="182" + cy="206" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + + ); + }; + + const streamlineAutomateSVG = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* buttons for port selection */} + {/* I2C PORTS */} + { + openMenu(e, { + address: 0, //address here is not important for I2C, the restrictions are per component not per port + addressType: quack.AddressType.ADDRESS_TYPE_I2C, + label: "1", + autoDetectable: false + }); + }} + className={classes.testButton} + id="button" + cx="17" + cy="44" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* CONFIGURABLE PIN PORTS - Note input Ports 3 and 4 and all control ports are locked for the monitor */} + {/* PORT 1 */} + { + openMenu(e, { + address: 1, + addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + label: "1", + autoDetectable: true + }); + }} + className={classes.testButton} + id="button" + cx="17" + cy="80" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* PORT 2 */} + { + openMenu(e, { + address: 2, + addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + label: "2", + autoDetectable: true + }); + }} + className={classes.testButton} + id="button" + cx="17" + cy="116" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* PORT 3 */} + { + openMenu(e, { + address: 4, + addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + label: "3", + autoDetectable: true + }); + }} + className={classes.testButton} + id="button" + cx="17" + cy="152" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* Port 4 */} + { + openMenu(e, { + address: 8, + addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + label: "4", + autoDetectable: true + }); + }} + className={classes.testButton} + id="button" + cx="17" + cy="188" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* Controller ports - note these are all locked for monitor devices */} + {/* PORT C1 */} + { + openMenu(e, { + address: 2, + addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + label: "C1", + autoDetectable: true + }); + }} + className={classes.testButton} + id="button" + cx="95" + cy="207" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* PORT C2 */} + { + openMenu(e, { + address: 4, + addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + label: "C2", + autoDetectable: true + }); + }} + className={classes.testButton} + id="button" + cx="138" + cy="207" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + {/* Port C3 */} + { + openMenu(e, { + address: 8, + addressType: quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, + label: "C3", + autoDetectable: true + }); + }} + className={classes.testButton} + id="button" + cx="180" + cy="207" + rx={buttonSize.rxLarge} + ry={buttonSize.ryLarge} + /> + + ); + }; + const getProductSVG = () => { switch (device.settings.product) { case pond.DeviceProduct.DEVICE_PRODUCT_BINDAPT_PLUS_PRO: @@ -5232,6 +6159,10 @@ export default function DeviceSVG(props: Props) { return miVentSVG(); case pond.DeviceProduct.DEVICE_PRODUCT_MIVENT_V2: return miVentSVG(true); + case pond.DeviceProduct.DEVICE_PRODUCT_STREAMLINE_MONITOR: + return streamlineMonitorSVG(); + case pond.DeviceProduct.DEVICE_PRODUCT_STREAMLINE_AUTOMATE: + return streamlineAutomateSVG(); } }; diff --git a/src/firmware/UploadFirmware.tsx b/src/firmware/UploadFirmware.tsx index d985f7a..aa5aa41 100644 --- a/src/firmware/UploadFirmware.tsx +++ b/src/firmware/UploadFirmware.tsx @@ -79,7 +79,8 @@ class UploadFirmware extends React.Component { pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLACK, pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_GREEN, pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLUE, - pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_BLUE + pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_BLUE, + pond.DevicePlatform.DEVICE_PLATFORM_V2_ETHERNET_BLUE ].includes(platform); var isChannelValid = [ pond.UpgradeChannel.UPGRADE_CHANNEL_ALPHA, @@ -229,6 +230,9 @@ class UploadFirmware extends React.Component { V2 Wifi Blue + + + V2 Ethernet Blue { @@ -266,6 +269,7 @@ export default function GateDevice(props: Props) { return ( { @@ -335,9 +340,10 @@ export default function GateDevice(props: Props) { container direction="row" alignContent="center" + width="100%" //alignItems="center" > - + - {ambientSelector()} + {ambientSelector()} - {tempChainSelector()} + {tempChainSelector()} - {pressureChainSelector()} + {pressureChainSelector()} {pcaRed()} {pcaGreen()} diff --git a/src/models/Device.ts b/src/models/Device.ts index b99510e..b201407 100644 --- a/src/models/Device.ts +++ b/src/models/Device.ts @@ -14,6 +14,7 @@ interface FeatureVersionByPlatform { v2CellGreen: string; v2WifiBlue: string; v2CellBlue: string; + v2EthBlue: string; } const featureVersions: Map = new Map([ @@ -29,7 +30,8 @@ const featureVersions: Map = new Map([ v2CellBlack: "2.0.44", v2CellGreen: "2.0.44", v2WifiBlue: "2.0.44", - v2CellBlue: "2.0.44" + v2CellBlue: "2.0.44", + v2EthBlue: "2.0.44" } ] ]); @@ -118,6 +120,8 @@ export class Device { return this.status.firmwareVersion >= versions.v2WifiBlue; case pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLUE: return this.status.firmwareVersion >= versions.v2CellBlue; + case pond.DevicePlatform.DEVICE_PLATFORM_V2_ETHERNET_BLUE: + return this.status.firmwareVersion >= versions.v2EthBlue; default: return false; } diff --git a/src/pages/Device.tsx b/src/pages/Device.tsx index 911e9f0..eabaf80 100644 --- a/src/pages/Device.tsx +++ b/src/pages/Device.tsx @@ -14,7 +14,6 @@ import { pond, quack } from "protobuf-ts/pond"; import { cloneDeep } from "lodash"; import DeviceOverview from "device/DeviceOverview"; import { DeviceAvailabilityMap, FindAvailablePositions, OffsetAvailabilityMap } from "pbHelpers/DeviceAvailability"; -import AddComponentManualDialog from "component/AddComponentManualDialog"; import ComponentCard from "component/ComponentCard"; import { sameComponentID, sortComponents } from "pbHelpers/Component"; import { isController } from "pbHelpers/ComponentType"; diff --git a/src/pages/DeviceComponent.tsx b/src/pages/DeviceComponent.tsx index 424de58..c04a1b2 100644 --- a/src/pages/DeviceComponent.tsx +++ b/src/pages/DeviceComponent.tsx @@ -23,6 +23,7 @@ import { } from "hooks"; import InteractionChip from "interactions/InteractionChip"; import InteractionSettings from "interactions/InteractionSettings"; +import { cloneDeep } from "lodash"; // import MaterialTable from "material-table"; import { Component, Device, deviceScope, Group, Interaction } from "models"; import { convertedUnitMeasurement, MeasurementsFor, UnitMeasurement } from "models/UnitMeasurement"; @@ -450,7 +451,6 @@ export default function DeviceComponent() { // }); }) .catch((err: any) => { - // resolve({ // data: [], // page: 0, @@ -613,6 +613,32 @@ export default function DeviceComponent() { pageSize={pageSize} setPage={setPage} handleRowsPerPageChange={handleRowsPerPageChange} + loadMore={() => { + let currentRows = cloneDeep(rows) + let newOffset = rows.length + componentAPI.listUnitMeasurements( + deviceID, + componentID, + startDate, + endDate, + pageSize, + newOffset, + order, + undefined, + getContextKeys(), + getContextTypes(), + showErrors, + as) + .then(resp => { + let newRows = UnitMeasurement.convertMeasurements( + resp.data.measurements.map(um => UnitMeasurement.any(um, user)) + ) + setRows(currentRows.concat(newRows)) + }).catch(err => { + + }) + + }} /> ); diff --git a/src/pages/DeviceSupport.tsx b/src/pages/DeviceSupport.tsx index 20bf476..a6a19de 100644 --- a/src/pages/DeviceSupport.tsx +++ b/src/pages/DeviceSupport.tsx @@ -301,6 +301,9 @@ export default function DeviceSupport() { V2 Cellular Blue + + V2 Ethernet Blue +