Skip to content

Commit bde01da

Browse files
committed
Read battery charge from frontend
1 parent 879c1ee commit bde01da

8 files changed

Lines changed: 75 additions & 58 deletions

File tree

web/src/app/store.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import ambientReducer from '../features/sensors/ambient/ambientSlice';
33
import heatersReducer from '../features/sensors/heaters/heatersSlice';
44
import powerReducer from '../features/sensors/power/powerSlice';
55
import appReducer from '../features/app/appSlice';
6-
import wifiReducer from '../features/app/wifiSlice';
6+
import configReducer from '../features/app/configSlice';
77
import { createLogger } from 'redux-logger'
88

99
const logger = createLogger({});
@@ -14,7 +14,7 @@ export const store = configureStore({
1414
heaters: heatersReducer,
1515
power: powerReducer,
1616
app: appReducer,
17-
wifi: wifiReducer,
17+
config: configReducer,
1818
},
1919
// middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
2020
devTools: true,

web/src/features/app/SaveConfigModalButton.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useDispatch } from "react-redux"
22
import { ConfirmModal } from "../../ConfirmModal"
33
import Button from 'react-bootstrap/Button';
4-
import { saveConfigAsync } from "./wifiSlice";
4+
import { saveConfigAsync } from "./configSlice";
55

66
const defaultButtonText = 'Save Configuration'
77
const defaultModalMessage = 'Are you sure you want to save the configuration? The new configuration will be applied when restarting'

web/src/features/app/System.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useDispatch, useSelector } from 'react-redux';
55
import { appInfoSelector, appUptimeSelector, getAppInfoAsync, reconnectWiFiAsync, restartAsync } from './appSlice';
66
import { Number, formatPercentage, formatSize, formatTime } from '../Number';
77
import { useEffect } from 'react';
8-
import { saveConfigAsync } from './wifiSlice';
8+
import { saveConfigAsync } from './configSlice';
99
import { ConfirmModal } from '../../ConfirmModal';
1010
import { RestartModalButton } from './RestartModalButton';
1111

web/src/features/app/WiFi.js

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
1-
import { Fragment, useEffect, useState } from 'react';
1+
import { useEffect, useState } from 'react';
22
import Form from 'react-bootstrap/Form';
33
import Button from 'react-bootstrap/Button';
44
import ButtonGroup from 'react-bootstrap/ButtonGroup';
55
import InputGroup from 'react-bootstrap/InputGroup';
66
import { useDispatch, useSelector } from 'react-redux';
7-
import { getConfigAsync, removeStationConfigAsync, saveAccessPointConfigAsync, saveConfigAsync, saveStationConfigAsync, selectWiFiAccessPointConfig, selectWiFiConfig, selectWiFiStationsConfig } from './wifiSlice';
7+
import {
8+
getConfigAsync,
9+
removeStationConfigAsync,
10+
saveAccessPointConfigAsync,
11+
saveStationConfigAsync,
12+
selectWiFiAccessPointConfig,
13+
selectConfig,
14+
selectWiFiStationsConfig
15+
} from './configSlice';
816
import Spinner from 'react-bootstrap/Spinner';
917
import { FaRegEye, FaRegEyeSlash } from "react-icons/fa";
1018
import Container from 'react-bootstrap/Container';
1119
import Row from 'react-bootstrap/Row';
1220
import Col from 'react-bootstrap/Col';
1321
import { ConfirmModal } from '../../ConfirmModal';
14-
import { reconnectWiFiAsync, restartAsync } from './appSlice';
22+
import { reconnectWiFiAsync } from './appSlice';
1523
import { RestartModalButton } from './RestartModalButton';
1624
import { SaveConfigModalButton } from './SaveConfigModalButton';
1725

18-
const WiFiPasswordControl = ({label, ...props}) => {
26+
const WiFiPasswordControl = ({ label, ...props }) => {
1927
const [show, setShow] = useState(false);
2028
return <InputGroup>
21-
{ label && <InputGroup.Text>{label}</InputGroup.Text>}
29+
{label && <InputGroup.Text>{label}</InputGroup.Text>}
2230
<Form.Control type={show ? 'text' : 'password'} {...props} autoComplete='new-password' />
23-
<Button variant='outline-secondary' onClick={() => setShow(!show) }>
24-
{ show ? <FaRegEyeSlash /> : <FaRegEye />}
31+
<Button variant='outline-secondary' onClick={() => setShow(!show)}>
32+
{show ? <FaRegEyeSlash /> : <FaRegEye />}
2533
</Button>
2634
</InputGroup>
2735
}
@@ -40,7 +48,7 @@ const WiFiAccessPoint = () => {
4048
<Form.Group as={Row}>
4149
<Form.Label column sm={4}>Hostname/AccessPoint ESSID</Form.Label>
4250
<Col sm={8}>
43-
<Form.Control type='text' value={essid} onChange={e => setEssid(e.target.value)}/>
51+
<Form.Control type='text' value={essid} onChange={e => setEssid(e.target.value)} />
4452
<Form.Text muted>This will be used as he DHCP hostname sent to your server. It's also the access point ESSID that will be used if AstroPowerBox can't be connected to any WiFi station</Form.Text>
4553
</Col>
4654
</Form.Group>
@@ -54,12 +62,12 @@ const WiFiAccessPoint = () => {
5462
</Form.Group>
5563
<ButtonGroup className='float-end'>
5664
<Button disabled={!isChanged()} variant="secondary" onClick={resetState}>Reset</Button>
57-
<Button disabled={!isChanged()} variant="danger" onClick={() => dispatch(saveAccessPointConfigAsync({essid, psk}))}>Update</Button>
65+
<Button disabled={!isChanged()} variant="danger" onClick={() => dispatch(saveAccessPointConfigAsync({ essid, psk }))}>Update</Button>
5866
</ButtonGroup>
5967
</Form>
6068
}
6169

62-
const WifiStation = ({station, index}) => {
70+
const WifiStation = ({ station, index }) => {
6371
const [essid, setEssid] = useState(station.essid)
6472
const [psk, setPsk] = useState(station.psk)
6573
const dispatch = useDispatch();
@@ -69,49 +77,49 @@ const WifiStation = ({station, index}) => {
6977
setPsk(station.psk)
7078
}
7179
useEffect(resetState, [station]);
72-
return <Form.Group as={Row} className='mt-2'>
73-
<Form.Label column lg={1}>Station {index}</Form.Label>
74-
<Col lg={4}>
75-
<InputGroup>
76-
<InputGroup.Text>ESSID</InputGroup.Text>
77-
<Form.Control type='text' value={essid} onChange={e => setEssid(e.target.value)} />
78-
</InputGroup>
79-
</Col>
80-
<Col lg={5}>
81-
<WiFiPasswordControl label='Password' value={psk} onChange={e => setPsk(e.target.value)} />
82-
</Col>
83-
<Col lg={2} className='d-grid'>
84-
<ButtonGroup className='float-end' size='sm'>
85-
<Button disabled={!isChanged()} variant="secondary" onClick={resetState}>Reset</Button>
86-
<Button disabled={!isChanged()} variant="danger" onClick={() => dispatch(saveStationConfigAsync({index, essid, psk}))}>Update</Button>
87-
<Button disabled={!station.essid && !station.psk} variant="warning" onClick={() => dispatch(removeStationConfigAsync({index}))}>Remove</Button>
88-
</ButtonGroup>
89-
</Col>
90-
</Form.Group>
80+
return <Form.Group as={Row} className='mt-2'>
81+
<Form.Label column lg={1}>Station {index}</Form.Label>
82+
<Col lg={4}>
83+
<InputGroup>
84+
<InputGroup.Text>ESSID</InputGroup.Text>
85+
<Form.Control type='text' value={essid} onChange={e => setEssid(e.target.value)} />
86+
</InputGroup>
87+
</Col>
88+
<Col lg={5}>
89+
<WiFiPasswordControl label='Password' value={psk} onChange={e => setPsk(e.target.value)} />
90+
</Col>
91+
<Col lg={2} className='d-grid'>
92+
<ButtonGroup className='float-end' size='sm'>
93+
<Button disabled={!isChanged()} variant="secondary" onClick={resetState}>Reset</Button>
94+
<Button disabled={!isChanged()} variant="danger" onClick={() => dispatch(saveStationConfigAsync({ index, essid, psk }))}>Update</Button>
95+
<Button disabled={!station.essid && !station.psk} variant="warning" onClick={() => dispatch(removeStationConfigAsync({ index }))}>Remove</Button>
96+
</ButtonGroup>
97+
</Col>
98+
</Form.Group>
9199
}
92100

93101
const WiFiStations = () => {
94102
const stations = useSelector(selectWiFiStationsConfig);
95103
return <Form>
96-
{ stations.map((station, index) => <WifiStation station={station} index={index} key={index} /> )}
104+
{stations.map((station, index) => <WifiStation station={station} index={index} key={index} />)}
97105
</Form>
98-
}
106+
}
99107

100108
export const WiFi = () => {
101109
const dispatch = useDispatch();
102110
useEffect(() => { dispatch(getConfigAsync()) }, [dispatch])
103-
const wifiConfig = useSelector(selectWiFiConfig)
111+
const wifiConfig = useSelector(selectConfig)
104112

105-
if(!wifiConfig.ready) {
106-
return <Spinner />
113+
if (!wifiConfig.ready) {
114+
return <Spinner />
107115
}
108116
return <Container>
109117
<Row>
110-
<Col md={{ span:10, offset:1 }}>
118+
<Col md={{ span: 10, offset: 1 }}>
111119
<WiFiAccessPoint />
112120
</Col>
113121
</Row>
114-
<Row className='mt-5'>
122+
<Row className='mt-5'>
115123
<WiFiStations />
116124
</Row>
117125
<Row className='mt-5'>
@@ -121,8 +129,8 @@ export const WiFi = () => {
121129
<Row className='mt-2'>
122130
<Col sm={10}>Reconnecting wifi will allow to apply some changes without restarting.</Col>
123131
<Col sm={2} className='d-grid'>
124-
<ConfirmModal
125-
confirmButton='Reconnect'
132+
<ConfirmModal
133+
confirmButton='Reconnect'
126134
text='Reconnecting WiFi might cause connectivity loss. Do you want to continue?'
127135
onConfirm={() => dispatch(reconnectWiFiAsync())}
128136
RenderButton={(props) => <Button {...props} variant='warning'>Reconnect WiFi</Button>}
@@ -134,6 +142,6 @@ export const WiFi = () => {
134142
<Col sm={2} className='d-grid'><RestartModalButton /></Col>
135143
</Row>
136144
</Container>
137-
138-
145+
146+
139147
}
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
1+
import { createAsyncThunk, createSelector, createSlice } from '@reduxjs/toolkit';
22
import { fetchConfig, removeWiFiStationConfig, saveConfig, saveWiFiAccessPointConfig, saveWiFiStationConfig } from './api';
33

44
const initialState = {
@@ -11,40 +11,41 @@ const initialState = {
1111
};
1212

1313
export const getConfigAsync = createAsyncThunk(
14-
'wifi/getConfig',
14+
'config/getConfig',
1515
async () => await fetchConfig()
1616
);
1717

1818
export const saveAccessPointConfigAsync = createAsyncThunk(
19-
'wifi/saveAccessPoint',
19+
'config/saveAccessPoint',
2020
async payload => await saveWiFiAccessPointConfig(payload)
2121
);
2222

2323
export const saveStationConfigAsync = createAsyncThunk(
24-
'wifi/saveStation',
24+
'config/saveStation',
2525
async payload => await saveWiFiStationConfig(payload)
2626
);
2727

2828
export const removeStationConfigAsync = createAsyncThunk(
29-
'wifi/removeStation',
29+
'config/removeStation',
3030
async payload => await removeWiFiStationConfig(payload)
3131
);
3232

3333

3434
export const saveConfigAsync = createAsyncThunk(
35-
'wifi/saveConfig',
35+
'config/saveConfig',
3636
async () => await saveConfig()
3737
);
3838

3939

4040

41-
export const selectWiFiConfig = state => state.wifi;
42-
export const selectWiFiConfigReady = state => state.wifi.ready;
43-
export const selectWiFiAccessPointConfig = state => state.wifi.accessPoint;
44-
export const selectWiFiStationsConfig = state => state.wifi.stations;
41+
export const selectConfig = state => state.config;
42+
export const selectConfigReady = createSelector([selectConfig], config => config.ready)
43+
export const selectPowerSourceType = createSelector([selectConfig], config => config.powerSourceType)
44+
export const selectWiFiAccessPointConfig = createSelector([selectConfig], config => config.accessPoint)
45+
export const selectWiFiStationsConfig = createSelector([selectConfig], config => config.stations)
4546

4647
export const wifiSlice = createSlice({
47-
name: 'wifi',
48+
name: 'config',
4849
initialState,
4950
reducers: {
5051
setWiFiConfig: (state, action) => {

web/src/features/sensors/power/Power.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { selectPower, selectPowerHistory } from './powerSlice';
55
import { LineChart, CartesianGrid, XAxis, YAxis, Legend, Line, ResponsiveContainer } from 'recharts'
66
import Accordion from 'react-bootstrap/Accordion';
77
import { useId, useState } from 'react';
8+
import { selectPowerSourceType } from '../../app/configSlice';
89

910
const PowerChart= ({}) => {
1011
const id = useId();
@@ -31,7 +32,8 @@ const PowerChart= ({}) => {
3132

3233

3334
export const Power = () => {
34-
const { busVoltage, current, power } = useSelector(selectPower);
35+
const { busVoltage, current, power, charge } = useSelector(selectPower);
36+
const powerSourceType = useSelector(selectPowerSourceType)
3537
const [historyVisible, setHistoryVisible] = useState(false);
3638
return <>
3739
<Table>
@@ -48,6 +50,10 @@ export const Power = () => {
4850
<th scope="row">Power</th>
4951
<td><Number value={power} unit="W"/></td>
5052
</tr>
53+
{ powerSourceType !== 'AC' && <tr>
54+
<th scope="row">Battery charge</th>
55+
<td><Number value={charge} unit="%"/></td>
56+
</tr>}
5157
</tbody>
5258
</Table>
5359
<Accordion>

web/src/features/sensors/power/powerSlice.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const initialState = {
77
current: null,
88
power: null,
99
shuntVoltage: null,
10+
charge: null,
1011
history: [],
1112
};
1213

@@ -18,11 +19,12 @@ export const powerSlice = createSlice({
1819
initialState,
1920
reducers: {
2021
setPower: (state, action) => {
21-
const { busVoltage, current, power, shuntVoltage } = action.payload;
22+
const { busVoltage, current, power, shuntVoltage, charge } = action.payload;
2223
state.busVoltage = busVoltage;
2324
state.current = current;
2425
state.power = power;
2526
state.shuntVoltage = shuntVoltage;
27+
state.charge = charge;
2628
state.history = [...state.history, { timestamp: new Date().getTime(), busVoltage, current, power}]
2729
},
2830
},

web/src/setupProxy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const { createProxyMiddleware } = require('http-proxy-middleware');
22

33
module.exports = function(app) {
4-
const target = 'http://astropowerbox-prototype.lan'
4+
const target = 'http://astropowerbox-travel.lan'
55
console.log(`****** proxy setup: "/api" => ${target}`)
66
app.use(
77
'/api',

0 commit comments

Comments
 (0)