Skip to content

Commit

Permalink
Auto flush & Performance improvements (#525)
Browse files Browse the repository at this point in the history
  • Loading branch information
Zer0-bit authored Aug 1, 2023
2 parents 251629e + 1dafa14 commit fc7cfa5
Show file tree
Hide file tree
Showing 13 changed files with 340 additions and 165 deletions.
2 changes: 2 additions & 0 deletions lib/Common/proto/message_converters.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class OperationModeConverter : public NanoPb::Converter::EnumConverter<Operation
case OperationMode::FLUSH: return OperationModeDto::OperationModeDto_FLUSH;
case OperationMode::DESCALE: return OperationModeDto::OperationModeDto_DESCALE;
case OperationMode::STEAM: return OperationModeDto::OperationModeDto_STEAM;
case OperationMode::FLUSH_AUTO: return OperationModeDto::OperationModeDto_FLUSH_AUTO;
}
return OperationModeDto::OperationModeDto_BREW_AUTO;
};
Expand All @@ -37,6 +38,7 @@ class OperationModeConverter : public NanoPb::Converter::EnumConverter<Operation
case OperationModeDto::OperationModeDto_FLUSH: return OperationMode::FLUSH;
case OperationModeDto::OperationModeDto_DESCALE: return OperationMode::DESCALE;
case OperationModeDto::OperationModeDto_STEAM: return OperationMode::STEAM;
case OperationModeDto::OperationModeDto_FLUSH_AUTO: return OperationMode::FLUSH_AUTO;
}
return OperationMode::BREW_AUTO;
};
Expand Down
1 change: 1 addition & 0 deletions lib/Common/proto/messages.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ enum OperationModeDto {
FLUSH = 2;
DESCALE = 3;
STEAM = 4;
FLUSH_AUTO = 5;
};

message UpdateOperationModeDto {
Expand Down
1 change: 1 addition & 0 deletions lib/Common/system_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ enum class OperationMode {
FLUSH = 2,
DESCALE = 3,
STEAM = 4,
FLUSH_AUTO = 5,
};

struct SystemState {
Expand Down
25 changes: 24 additions & 1 deletion src/gaggiuino.ino
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ static void modeSelect(void) {
if (!systemState.startupInitFinished) return;

switch (systemState.operationMode) {
//REPLACE ALL THE BELOW WITH OPMODE_auto_profiling
case OperationMode::BREW_AUTO:
if (currentState.hotWaterSwitchState) hotWaterMode(currentState);
else if (currentState.steamSwitchState) steamCtrl(runningCfg, currentState, systemState);
Expand All @@ -286,6 +285,10 @@ static void modeSelect(void) {
backFlush(currentState);
brewActive ? setBoilerOff() : justDoCoffee(runningCfg, currentState, activeProfile.waterTemperature, false);
break;
case OperationMode::FLUSH_AUTO:
if (!currentState.steamSwitchState) steamTime = millis();
flushAuto();
break;
case OperationMode::STEAM:
steamCtrl(runningCfg, currentState, systemState);

Expand Down Expand Up @@ -609,3 +612,23 @@ static void doLed(void) {
led.setColor(runningCfg.led.color.R, runningCfg.led.color.G, runningCfg.led.color.B);
}
}

static void flushAuto() {
#if defined(LEGO_VALVE_RELAY) || defined(SINGLE_BOARD)
static uint32_t flushAutoTimer = 0;


if (millis() - flushAutoTimer > 5000) { //FLUSH_AUTO just started
flushAutoTimer = millis();
}

if (millis() - flushAutoTimer < 4000) {
currentState.brewSwitchState = true;
backFlush(currentState);
setBoilerOff();
} else {
flushAutoTimer = 0;
systemState.operationMode = OperationMode::BREW_AUTO;
}
#endif
}
2 changes: 2 additions & 0 deletions webserver/src/server/json/json_system_state_converters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace json {
case OperationMode::FLUSH: return "FLUSH";
case OperationMode::DESCALE: return "DESCALE";
case OperationMode::STEAM: return "STEAM";
case OperationMode::FLUSH_AUTO: return "FLUSH_AUTO";
default: return "BREW_AUTO";
}
}
Expand Down Expand Up @@ -66,6 +67,7 @@ namespace json {
if (modeValue == "FLUSH") return OperationMode::FLUSH;
if (modeValue == "DESCALE") return OperationMode::DESCALE;
if (modeValue == "STEAM") return OperationMode::STEAM;
if (modeValue == "FLUSH_AUTO") return OperationMode::FLUSH_AUTO;
return OperationMode::BREW_AUTO;
}

Expand Down
74 changes: 46 additions & 28 deletions webserver/web-interface/src/components/appbar/MainAppBar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react';
import React, { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import Tabs from '@mui/material/Tabs';
import Tab from '@mui/material/Tab';
Expand Down Expand Up @@ -160,20 +160,14 @@ function NavMenu({ activeItem, onChange }: NavMenuProps) {
function MainAppBar() {
const theme = useTheme();
const location = useLocation();
const [activeTab, setActiveTab] = useState<string>(location.pathname || '/');
const [shotDialogOpen, setShotDialogOpen] = useState<boolean>(false);
const isBiggerScreen = useMediaQuery(theme.breakpoints.up('sm'));
const { latestShotDatapoint } = useShotDataStore();
const { timeAlive } = useSystemStateStore().systemState;
const { latestNotification } = useNotificationStore();

useEffect(() => {
if (latestShotDatapoint.timeInShot > 0) setShotDialogOpen(true);
}, [latestShotDatapoint]);
const [activeTab, setActiveTab] = useState<string>(location.pathname || '/');
const [shotDialogOpen, setShotDialogOpen] = useState<boolean>(false);

const activeColor = useMemo(() => (theme.palette.mode === 'light'
const activeColor = theme.palette.mode === 'light'
? theme.palette.primary.contrastText
: theme.palette.primary.main), [theme]);
: theme.palette.primary.main;

return (
<AppBar sx={{ position: 'static' }} elevation={1}>
Expand Down Expand Up @@ -213,28 +207,52 @@ function MainAppBar() {
<TabMenu activeItem={activeTab} activeColor={activeColor} onChange={setActiveTab} />
)}
{!isBiggerScreen && <NavMenu activeItem={activeTab} onChange={setActiveTab} />}
<div
style={{
fontSize: '1rem',
width: '100px',
height: '40px',
borderRadius: '16px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid #ccc',
// Add any other styling you want for the container
}}
>
{formatTime({ time: timeAlive * 1000 })}
</div>
<TimeCounter />
</Stack>
</Toolbar>
<Box />
{shotDialogOpen && <ShotDialog open={shotDialogOpen} setOpen={setShotDialogOpen} />}
<SnackNotification notification={latestNotification} />
<ShotDialogObserver open={shotDialogOpen} setOpen={setShotDialogOpen} />
<AppNotification />
</AppBar>
);
}

function TimeCounter() {
const timeAlive = useSystemStateStore((state) => state.systemState.timeAlive);
return (
<div
style={{
fontSize: '1rem',
width: '100px',
height: '40px',
borderRadius: '16px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid #ccc',
}}
>
{formatTime({ time: timeAlive * 1000 })}
</div>
);
}

function ShotDialogObserver({ open, setOpen }: {open:boolean, setOpen: (value: boolean) => void}) {
const latestShotDatapoint = useShotDataStore((state) => state.latestShotDatapoint);

useEffect(() => {
if (latestShotDatapoint.timeInShot > 0) setOpen(true);
}, [latestShotDatapoint, setOpen]);

if (open) {
return <ShotDialog open={open} setOpen={setOpen} />;
}
return null;
}

function AppNotification() {
const latestNotification = useNotificationStore((state) => state.latestNotification);
return <SnackNotification notification={latestNotification} />;
}

export default MainAppBar;
19 changes: 19 additions & 0 deletions webserver/web-interface/src/components/gauges/PressureGauge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import { useTheme } from '@mui/material';
import useSensorStateStore from '../../state/SensorStateStore';
import GaugeChart from '../chart/GaugeChart';

export default function PressureGauge() {
const theme = useTheme();
const pressure = useSensorStateStore((state) => state.sensorState.pressure);

return (
<GaugeChart
value={pressure}
primaryColor={theme.palette.pressure.main}
title="Pressure"
unit="bar"
maxValue={14}
/>
);
}
19 changes: 19 additions & 0 deletions webserver/web-interface/src/components/gauges/TemperatureGauge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import { useTheme } from '@mui/material';
import useSensorStateStore from '../../state/SensorStateStore';
import GaugeChart from '../chart/GaugeChart';

export default function TemperatureGauge({ targetTemperature }: { targetTemperature: number}) {
const theme = useTheme();
const temperature = useSensorStateStore((state) => state.sensorState.waterTemperature);

return (
<GaugeChart
value={temperature}
primaryColor={theme.palette.temperature.main}
maxValue={targetTemperature}
flashAfterValue={120}
unit="°C"
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import GaugeLiquid from '../chart/GaugeLiquid';
import useSensorStateStore from '../../state/SensorStateStore';

export default function WaterLevelGauge() {
const waterLevel = useSensorStateStore((state) => state.sensorState.waterLevel);

return <GaugeLiquid value={waterLevel} />;
}
151 changes: 151 additions & 0 deletions webserver/web-interface/src/components/inputs/SwitchLedButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import React, { ReactNode, useCallback, useState } from 'react';
import {
Box, ButtonBase, alpha, useTheme,
} from '@mui/material';
import AspectRatioBox from '../layout/AspectRatioBox';

const LED_COLOR_ON = '#ef4e2b';
const GLOW_ON = alpha(LED_COLOR_ON, 0.5);
const LED_COLOR_AUTO = '#00b9ff';
const GLOW_AUTO = alpha(LED_COLOR_AUTO, 0.5);
const LED_COLOR_OFF = '#822714';
const SWITCH_BACKGROUND_ON = 'linear-gradient(to top, rgba(127, 127, 127, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%)';
const SWITCH_BACKGROUND_OFF = 'linear-gradient(to top, rgba(255, 255, 255, 0.1) 0%, rgba(80, 80, 80, 0.1) 100%)';
const SWITCH_SHADOW = '0px 1px 2px -1px rgba(0,0,0,0.2), 0px 3px 3px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)';

export enum SwitchLedState {
ON,
AUTO,
OFF
}

export interface SwitchLedButtonProps {
state: SwitchLedState;
icon?: ReactNode;
label?: ReactNode;
supportsAuto?: boolean;
longPressDuration?: number;
onChange?: (state: SwitchLedState) => void;
}

function stateToLedColor(state:SwitchLedState) {
if (state === SwitchLedState.ON) return LED_COLOR_ON;
if (state === SwitchLedState.AUTO) return LED_COLOR_AUTO;
return LED_COLOR_OFF;
}

function stateToGlowColor(state:SwitchLedState) {
if (state === SwitchLedState.ON) return GLOW_ON;
if (state === SwitchLedState.AUTO) return GLOW_AUTO;
return '';
}

export function SwitchLedButton({
state,
icon = undefined,
label = undefined,
supportsAuto = false,
longPressDuration = 1500,
onChange = undefined,
}: SwitchLedButtonProps) {
const theme = useTheme();
const background = (state === SwitchLedState.OFF) ? SWITCH_BACKGROUND_OFF : SWITCH_BACKGROUND_ON;

const [pressTimeoutId, setPressTimeoutId] = useState<NodeJS.Timeout | null>(null);
const [longPressActive, setLongPressActive] = useState(false);

const handleLongPress = useCallback(() => {
if (!supportsAuto) return;
if (state === SwitchLedState.AUTO) onChange?.(SwitchLedState.OFF);
else onChange?.(SwitchLedState.AUTO);
}, [state, onChange, supportsAuto]);

const handleShortPress = useCallback(() => {
if (state === SwitchLedState.OFF) onChange?.(SwitchLedState.ON);
else onChange?.(SwitchLedState.OFF);
}, [state, onChange]);

const handlePressStart = useCallback((event: React.MouseEvent | React.TouchEvent) => {
if (event.type === 'touchstart') {
event.preventDefault();
}

if (!supportsAuto) return;

const id = setTimeout(() => {
setLongPressActive(true);
}, longPressDuration);
setPressTimeoutId(id);
}, [longPressDuration, supportsAuto]);

const handlePressEnd = useCallback((event: React.MouseEvent | React.TouchEvent) => {
if (event.type === 'touchend') {
event.preventDefault();
}

if (pressTimeoutId) {
clearTimeout(pressTimeoutId);
}
if (longPressActive) {
handleLongPress();
} else {
handleShortPress();
}
setLongPressActive(false);
}, [pressTimeoutId, longPressActive, handleShortPress, handleLongPress]);

return (
<Box sx={{
p: { xs: 0.5, sm: 1 },
borderRadius: theme.spacing(1),
background: (theme.palette.mode === 'light') ? 'rgba(0, 0, 0, 0.4)' : 'rgba(25, 25, 25, 0.2)',
width: '100%',
maxWidth: '120px',
border: `1px solid ${alpha(theme.palette.divider, 0.05)}`,
boxShadow: SWITCH_SHADOW,
}}
>
<AspectRatioBox ratio={1 / 1.4}>
<ButtonBase
sx={{
borderRadius: theme.spacing(1),
boxShadow: SWITCH_SHADOW,
background,
border: `1px solid ${alpha(theme.palette.divider, 0.05)}`,
':hover': {
border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
},
}}
onMouseDown={handlePressStart}
onMouseUp={handlePressEnd}
onTouchStart={handlePressStart}
onTouchEnd={handlePressEnd}
>
<Box sx={{
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-around',
alignItems: 'center',
color: 'rgba(255, 255, 255, 0.8)',
py: { xs: 1, sm: 2 },
}}
>
<Box sx={{ fontSize: { xs: theme.typography.body2.fontSize, sm: theme.typography.body1.fontSize } }}>
{icon}
</Box>
<Box sx={{ overflowX: 'hidden', textOverflow: 'ellipsis', width: '100%' }}>{label}</Box>
<Box sx={{
width: '15px',
height: '10px',
backgroundColor: stateToLedColor(state),
boxShadow: state !== SwitchLedState.OFF ? `0 0 5px 3px ${stateToGlowColor(state)}` : '',
}}
/>
</Box>
</ButtonBase>
</AspectRatioBox>
</Box>
);
}
Loading

0 comments on commit fc7cfa5

Please sign in to comment.