diff --git a/ui/src/App.css b/ui/src/App.css index 8b908d5..1c69a9f 100644 --- a/ui/src/App.css +++ b/ui/src/App.css @@ -1,2 +1,9 @@ .App { +} + +.countdown-area { + width: 100%; + text-align: center; + font-weight: bold; + font-size: 2rem; } \ No newline at end of file diff --git a/ui/src/App.js b/ui/src/App.js index f27ad68..bb625c7 100644 --- a/ui/src/App.js +++ b/ui/src/App.js @@ -4,13 +4,13 @@ import { Container, Form } from 'react-bootstrap'; import { connect } from 'react-redux'; import NotificationsArea from './components/NotificationsArea.js'; -import APIAddressField from './components/APIAddressField'; -import PourTimeField from './components/PourTimeField'; -import SystemControls from './components/SystemControls'; -import SystemStatusArea from './components/SystemStatusArea'; +import APIAddressField from './components/APIAddressField.js'; +import PourTimeField from './components/PourTimeField.js'; +import SystemControls from './components/SystemControls.js'; +import SystemStatusArea from './components/SystemStatusArea.js'; +import CurrentOperationInfoArea from './components/CurrentOperationInfoArea.js'; function App({ isConnected }) { - // TODO: Add a fake countdown timer of timeLeft return (

Tea System UI

@@ -21,6 +21,7 @@ function App({ isConnected }) { {isConnected ? ( <> + ) : null} diff --git a/ui/src/Utils/time.js b/ui/src/Utils/time.js new file mode 100644 index 0000000..248738e --- /dev/null +++ b/ui/src/Utils/time.js @@ -0,0 +1,20 @@ +function toTimeStr(diff) { + const seconds = Math.floor(diff / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + + const secondsStr = (seconds % 60).toString().padStart(2, '0'); + const minutesStr = (minutes % 60).toString().padStart(2, '0'); + const hoursStr = hours.toString().padStart(2, '0'); + + return `${hoursStr}:${minutesStr}:${secondsStr}`; +} + +export function timeBetweenAsString({endTime=null, startTime=null}) { + if (null === startTime) startTime = new Date(); + if (null === endTime) endTime = new Date(); + + const diff = endTime - startTime; // in ms + if (diff < 0) return '-' + toTimeStr(-diff); + return toTimeStr(diff); +} \ No newline at end of file diff --git a/ui/src/api/CWaterPumpAPI.js b/ui/src/api/CWaterPumpAPI.js index 2942d15..d77098f 100644 --- a/ui/src/api/CWaterPumpAPI.js +++ b/ui/src/api/CWaterPumpAPI.js @@ -29,6 +29,7 @@ function preprocessResponse(response) { response.updated = Date.now(); // difference between current time on client and time on device response.timeDelta = response.updated - response.time; + // TODO: add field response.pump.estimatedEndTime return response; } diff --git a/ui/src/components/CurrentOperationInfoArea.js b/ui/src/components/CurrentOperationInfoArea.js new file mode 100644 index 0000000..b4ed46c --- /dev/null +++ b/ui/src/components/CurrentOperationInfoArea.js @@ -0,0 +1,22 @@ +import React from "react"; +import { connect } from "react-redux"; +import TimerArea from "./TimerArea"; + +export function CurrentOperationInfoAreaComponent({ + isRunning, estimatedEndTime +}) { + if (!isRunning) return null; + return ( +
+ +
+ ); +} + +export default connect( + state => ({ + isRunning: state.systemStatus.pump.running, + estimatedEndTime: state.systemStatus.pump.estimatedEndTime, + }), + [] +)(CurrentOperationInfoAreaComponent); \ No newline at end of file diff --git a/ui/src/components/SystemStatusArea.js b/ui/src/components/SystemStatusArea.js index 1279b0b..b95373f 100644 --- a/ui/src/components/SystemStatusArea.js +++ b/ui/src/components/SystemStatusArea.js @@ -1,25 +1,7 @@ import React from 'react'; import { Card } from 'react-bootstrap'; import { connect } from 'react-redux'; - -// time elapsed since last update -function TimeElapsedComponent({ updated }) { - const [diffString, setDiffString] = React.useState(''); - React.useEffect(() => { - const interval = setInterval(() => { - const now = new Date(); - const diff = now - updated; - const newDiffString = new Date(diff).toISOString().substr(11, 8); - setDiffString(newDiffString); - }, 1000); - - return () => clearInterval(interval); - }, [updated]); - - return ( - {diffString} - ); -} +import TimerArea from '../components/TimerArea'; function _systemStatus(status) { if (null === status) { @@ -30,7 +12,7 @@ function _systemStatus(status) { return ( <> Time since last update:{' '} - +
Pump Running: {pump.running ? "Yes" : "No"}
Time Left: {pump.timeLeft} ms diff --git a/ui/src/components/TimerArea.js b/ui/src/components/TimerArea.js new file mode 100644 index 0000000..2eb0a69 --- /dev/null +++ b/ui/src/components/TimerArea.js @@ -0,0 +1,22 @@ +import React from "react"; +import { timeBetweenAsString } from "../Utils/time"; + +export function TimerArea({ startTime=null, endTime=null, interval=450 }) { + const [countdown, setCountdown] = React.useState(''); + + React.useEffect(() => { + const tid = setInterval(() => { + setCountdown(timeBetweenAsString({ startTime, endTime })); + }, interval); + + return () => clearInterval(tid); + }, [startTime, endTime, interval]); + + return ( + + {countdown} + + ); +} + +export default TimerArea; \ No newline at end of file diff --git a/ui/src/store/slices/SystemStatus.js b/ui/src/store/slices/SystemStatus.js index 4910334..745cf4f 100644 --- a/ui/src/store/slices/SystemStatus.js +++ b/ui/src/store/slices/SystemStatus.js @@ -19,7 +19,7 @@ export const stopPump = createAsyncThunk( // slice for system status const bindStatus = (state, action) => { - return preprocessSystemStatus(action.payload); + return action.payload; }; export const SystemStatusSlice = createSlice({