Skip to content

Commit

Permalink
feat: add outdoor temperature setting (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
ReidyT authored Dec 4, 2024
1 parent 1a9d17d commit 57a1fe7
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 66 deletions.
5 changes: 5 additions & 0 deletions src/config/simulation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export const SIMULATION_INDOOR_TEMPERATURE_CELCIUS = {
MIN: 5,
MAX: 35,
};
export const SIMULATION_OUTDOOR_TEMPERATURE_CELCIUS = {
DEFAULT: 15,
MIN: -10,
MAX: 35,
};
export const SIMULATION_PRICE_KWH = 0.22;

export const SIMULATION_DEFAULT_WALL_COMPONENT_INSULATION: Pick<
Expand Down
37 changes: 33 additions & 4 deletions src/context/SimulationContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {

import {
SIMULATION_INDOOR_TEMPERATURE_CELCIUS,
SIMULATION_OUTDOOR_TEMPERATURE_CELCIUS,
SIMULATION_PRICE_KWH,
} from '@/config/simulation';
import { useHeatLoss } from '@/hooks/useHeatLoss';
Expand All @@ -32,6 +33,8 @@ import {
} from '../models/TemperatureIterator';
import { useHouseComponents } from './HouseComponentsContext';

type OutdoorTemperature = { override: boolean; value: number };

type SimulationContextType = {
status: SimulationStatus;
heatLosses: HeatLossPerComponent;
Expand All @@ -40,7 +43,9 @@ type SimulationContextType = {
setPricekWh: (newPrice: number) => void;
indoorTemperature: number;
updateIndoorTemperature: (newTemperature: number) => void;
outdoorTemperature: number;
outdoorTemperature: OutdoorTemperature;
updateOutdoorTemperature: (props: OutdoorTemperature) => void;

progression: SimulationProgression;
period: SlidingWindow['period'];
duration: FormattedTime;
Expand Down Expand Up @@ -78,10 +83,15 @@ export const SimulationProvider = ({
unit: TimeUnit.Hours,
});

const [indoorTemperature, setOutdoorTemperature] = useState(
const [indoorTemperature, setIndoorTemperature] = useState(
SIMULATION_INDOOR_TEMPERATURE_CELCIUS.DEFAULT,
);

const [outdoorTemperature, setOutdoorTemperature] = useState({
override: false,
value: SIMULATION_OUTDOOR_TEMPERATURE_CELCIUS.DEFAULT,
});

const [pricekWh, setPricekWh] = useState(SIMULATION_PRICE_KWH);

const { houseComponentsConfigurator } = useHouseComponents();
Expand Down Expand Up @@ -131,6 +141,7 @@ export const SimulationProvider = ({

const intervalId = setInterval(() => {
if (temperatureIterator.current?.hasMore()) {
// TODO: should update this to use the overrided temperature in the calculation
setCurrentWindow(temperatureIterator.current.getNext());
} else {
clearInterval(intervalId);
Expand All @@ -139,11 +150,27 @@ export const SimulationProvider = ({
}, simulationFrameMS);
}, [simulationFrameMS, simulationStatus]);

const updateOutdoorTemperature = ({
override,
value,
}: {
override: boolean;
value: number;
}): void => {
setOutdoorTemperature({ override, value });
};

const contextValue = useMemo(
() => ({
indoorTemperature,
updateIndoorTemperature: setOutdoorTemperature,
outdoorTemperature: currentWindow.mean,
updateIndoorTemperature: setIndoorTemperature,
outdoorTemperature: {
override: outdoorTemperature.override,
value: outdoorTemperature.override
? outdoorTemperature.value
: currentWindow.mean,
},
updateOutdoorTemperature,
period: currentWindow.period,
progression,
duration: simulationDuration,
Expand All @@ -163,6 +190,8 @@ export const SimulationProvider = ({
currentWindow.period,
heatLosses,
indoorTemperature,
outdoorTemperature.override,
outdoorTemperature.value,
pricekWh,
progression,
simulationDuration,
Expand Down
2 changes: 1 addition & 1 deletion src/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"TITLE": "Temperatures",
"INDOOR_TEMPERATURE_LABEL": "Indoor Temperature",
"OUTDOOR_TEMPERATURE_LABEL": "Outdoor Temperature",
"OUTDOOR_TEMPERATURE_OVERRIDE_LABEL": "Outdoor Temperature"
"OUTDOOR_TEMPERATURE_OVERRIDE_LABEL": "Override Temperature"
}
},
"INSULATIONS": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import AccordionSummary from '@mui/material/AccordionSummary';

import { ElectricityCostControl } from './ElectricityCostControl';
import { HouseControl } from './HouseControl';
import { TemperatureControl } from './TemperatureControl';
import { TemperatureControl } from './TemperatureControl/TemperatureControl';

export const SimulationControlPanel = (): JSX.Element => {
const { t } = useTranslation('SIMULATION_CONTROL_PANEL');
Expand Down
55 changes: 0 additions & 55 deletions src/modules/scenes/SimulationControlPanel/TemperatureControl.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Slider, Stack, Typography } from '@mui/material';

type Props = {
dataTestId: string;
label: string;
value: number;
disabled?: boolean;
minTemperature: number;
maxTemperature: number;
onChange: (newValue: number) => void;
};

export const SliderTemperature = ({
dataTestId,
label,
value,
disabled = false,
minTemperature,
maxTemperature,
onChange,
}: Props): JSX.Element => {
const formatTemperature = (temperature: number): string =>
`${temperature} °C`;

return (
<Stack>
<Typography variant="caption">{label}</Typography>

<Stack direction="row" alignItems="flex-start" spacing={2}>
<Stack flexGrow={1}>
<Slider
value={value}
onChange={(_, v) => {
if (typeof v === 'number') {
onChange(v);
}
}}
valueLabelDisplay="auto"
min={minTemperature}
max={maxTemperature}
role="slider"
aria-label={dataTestId}
data-testid={`${dataTestId}-slider`}
disabled={disabled}
/>
<Stack justifyContent="space-between" direction="row">
<Typography>{formatTemperature(minTemperature)}</Typography>
<Typography>{formatTemperature(maxTemperature)}</Typography>
</Stack>
</Stack>
{/* padding use to align with the slider */}
<Typography
data-testid={`${dataTestId}-label`}
variant="caption"
pt={0.5}
>
{formatTemperature(value)}
</Typography>
</Stack>
</Stack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useTranslation } from 'react-i18next';

import { Checkbox, FormControlLabel, Stack } from '@mui/material';

import {
SIMULATION_INDOOR_TEMPERATURE_CELCIUS,
SIMULATION_OUTDOOR_TEMPERATURE_CELCIUS,
} from '@/config/simulation';
import { useSimulation } from '@/context/SimulationContext';

import { SliderTemperature } from './SliderTemperature';

export const TemperatureControl = (): JSX.Element => {
const { t } = useTranslation('SIMULATION_CONTROL_PANEL', {
keyPrefix: 'TEMPERATURES_CONTROL_PANEL',
});

const {
indoorTemperature,
updateIndoorTemperature,
outdoorTemperature,
updateOutdoorTemperature,
} = useSimulation();

const { MIN: minIndoorTemperature, MAX: maxIndoorTemperature } =
SIMULATION_INDOOR_TEMPERATURE_CELCIUS;

const { MIN: minOutdoorTemperature, MAX: maxOutdoorTemperature } =
SIMULATION_OUTDOOR_TEMPERATURE_CELCIUS;

return (
<Stack spacing={3}>
<SliderTemperature
dataTestId="indoor-temperature"
label={t('INDOOR_TEMPERATURE_LABEL')}
value={indoorTemperature}
minTemperature={minIndoorTemperature}
maxTemperature={maxIndoorTemperature}
onChange={updateIndoorTemperature}
/>

<Stack>
<SliderTemperature
dataTestId="outdoor-temperature"
label={t('OUTDOOR_TEMPERATURE_LABEL')}
value={outdoorTemperature.value}
minTemperature={minOutdoorTemperature}
maxTemperature={maxOutdoorTemperature}
onChange={(v) =>
updateOutdoorTemperature({
override: outdoorTemperature.override,
value: v,
})
}
disabled={!outdoorTemperature.override}
/>
<FormControlLabel
control={
<Checkbox
checked={outdoorTemperature.override}
onChange={(_, checked) =>
updateOutdoorTemperature({
override: checked,
value: outdoorTemperature.value,
})
}
/>
}
label={t('OUTDOOR_TEMPERATURE_OVERRIDE_LABEL')}
/>
</Stack>
</Stack>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const SimulationInformations = (): JSX.Element => {
<ThermometerSun />
<Typography>{tInformations('CURRENT_PERIOD.OUTDOOR')}</Typography>
<Typography data-testid="simulation-info-outdoor-temperature">
{outdoorTemperature} °C
{outdoorTemperature.value} °C
</Typography>
</Stack>

Expand Down
37 changes: 33 additions & 4 deletions tests/player/HousePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export class HousePage {

readonly indoorTemperatureInfo: Locator;

readonly outdoorTemperatureInfo: Locator;

constructor(page: Page) {
this.page = page;
this.electricityCost = this.page.getByRole('spinbutton', {
Expand All @@ -20,6 +22,9 @@ export class HousePage {
this.indoorTemperatureInfo = this.page.getByTestId(
'simulation-info-indoor-temperature',
);
this.outdoorTemperatureInfo = this.page.getByTestId(
'simulation-info-outdoor-temperature',
);
}

async goto(): Promise<void> {
Expand Down Expand Up @@ -62,17 +67,41 @@ export class HousePage {
await expect(this.electricityCost).toHaveValue(newElectricityCost);
}

async setIndoorTemperature(percentage: number): Promise<string> {
const slider = this.page.getByTestId('indoor-temperature-slider');
const sliderLabel = this.page.getByTestId('indoor-temperature-label');
private async setTemperature(
id: string,
percentage: number,
shouldChanged: boolean = true,
): Promise<string> {
const slider = this.page.getByTestId(`${id}-temperature-slider`);
const sliderLabel = this.page.getByTestId(`${id}-temperature-label`);
const currValue = (await sliderLabel.textContent()) ?? '';

await changeSlider(this.page, slider, percentage);
await expect(sliderLabel).not.toHaveText(currValue);

if (shouldChanged) {
await expect(sliderLabel).not.toHaveText(currValue);
} else {
await expect(sliderLabel).toHaveText(currValue);
}

return (await sliderLabel.textContent()) ?? '';
}

async setIndoorTemperature(percentage: number): Promise<string> {
return this.setTemperature('indoor', percentage);
}

async setOutdoorTemperature(
percentage: number,
{ shouldHaveChanged }: { shouldHaveChanged: boolean },
): Promise<string> {
return this.setTemperature('outdoor', percentage, shouldHaveChanged);
}

async setOverrideOutdoorTemperature(checked: boolean): Promise<void> {
this.page.getByLabel('Override Temperature').setChecked(checked);
}

async checkErrorIsVisible(
label: string,
type: 'Required' | 'Min' | 'Max',
Expand Down
Loading

0 comments on commit 57a1fe7

Please sign in to comment.