esp8266-react-framework/interface/src/wifi/WiFiNetworkScanner.tsx

169 lines
5.0 KiB
TypeScript
Raw Normal View History

import React, { Component } from 'react';
import { withSnackbar, WithSnackbarProps } from 'notistack';
import { createStyles, WithStyles, Theme, withStyles, Typography, LinearProgress } from '@material-ui/core';
import PermScanWifiIcon from '@material-ui/icons/PermScanWifi';
import { FormActions, FormButton, SectionContent } from '../components';
import { redirectingAuthorizedFetch } from '../authentication';
import { SCAN_NETWORKS_ENDPOINT, LIST_NETWORKS_ENDPOINT } from '../api';
import WiFiNetworkSelector from './WiFiNetworkSelector';
import { WiFiNetworkList, WiFiNetwork } from './types';
const NUM_POLLS = 10
const POLLING_FREQUENCY = 500
const RETRY_EXCEPTION_TYPE = "retry"
interface WiFiNetworkScannerState {
scanningForNetworks: boolean;
errorMessage?: string;
networkList?: WiFiNetworkList;
}
const styles = (theme: Theme) => createStyles({
scanningSettings: {
margin: theme.spacing(0.5),
},
scanningSettingsDetails: {
margin: theme.spacing(4),
textAlign: "center"
},
scanningProgress: {
margin: theme.spacing(4),
textAlign: "center"
}
});
type WiFiNetworkScannerProps = WithSnackbarProps & WithStyles<typeof styles>;
class WiFiNetworkScanner extends Component<WiFiNetworkScannerProps, WiFiNetworkScannerState> {
pollCount: number = 0;
state: WiFiNetworkScannerState = {
scanningForNetworks: false,
};
componentDidMount() {
this.scanNetworks();
}
requestNetworkScan = () => {
const { scanningForNetworks } = this.state;
if (!scanningForNetworks) {
this.scanNetworks();
}
}
scanNetworks() {
this.pollCount = 0;
this.setState({ scanningForNetworks: true, networkList: undefined, errorMessage: undefined });
redirectingAuthorizedFetch(SCAN_NETWORKS_ENDPOINT).then(response => {
if (response.status === 202) {
this.schedulePollTimeout();
return;
}
throw Error("Scanning for networks returned unexpected response code: " + response.status);
}).catch(error => {
this.props.enqueueSnackbar("Problem scanning: " + error.message, {
variant: 'error',
});
this.setState({ scanningForNetworks: false, networkList: undefined, errorMessage: error.message });
});
}
schedulePollTimeout() {
setTimeout(this.pollNetworkList, POLLING_FREQUENCY);
}
retryError() {
return {
name: RETRY_EXCEPTION_TYPE,
message: "Network list not ready, will retry in " + POLLING_FREQUENCY + "ms."
};
}
compareNetworks(network1: WiFiNetwork, network2: WiFiNetwork) {
if (network1.rssi < network2.rssi)
return 1;
if (network1.rssi > network2.rssi)
return -1;
return 0;
}
pollNetworkList = () => {
redirectingAuthorizedFetch(LIST_NETWORKS_ENDPOINT)
.then(response => {
if (response.status === 200) {
return response.json();
}
if (response.status === 202) {
if (++this.pollCount < NUM_POLLS) {
this.schedulePollTimeout();
throw this.retryError();
} else {
throw Error("Device did not return network list in timely manner.");
}
}
throw Error("Device returned unexpected response code: " + response.status);
})
.then(json => {
json.networks.sort(this.compareNetworks)
this.setState({ scanningForNetworks: false, networkList: json, errorMessage: undefined })
})
.catch(error => {
if (error.name !== RETRY_EXCEPTION_TYPE) {
this.props.enqueueSnackbar("Problem scanning: " + error.message, {
variant: 'error',
});
this.setState({ scanningForNetworks: false, networkList: undefined, errorMessage: error.message });
}
});
}
renderNetworkScanner() {
const { classes } = this.props;
const { scanningForNetworks, networkList, errorMessage } = this.state;
if (scanningForNetworks || !networkList) {
return (
<div className={classes.scanningSettings}>
<LinearProgress className={classes.scanningSettingsDetails} />
<Typography variant="h6" className={classes.scanningProgress}>
Scanning...
</Typography>
</div>
);
}
if (errorMessage) {
return (
<div className={classes.scanningSettings}>
<Typography variant="h6" className={classes.scanningSettingsDetails}>
{errorMessage}
</Typography>
</div>
);
}
return (
<WiFiNetworkSelector networkList={networkList} />
);
}
render() {
const { scanningForNetworks } = this.state;
return (
<SectionContent title="Network Scanner">
{this.renderNetworkScanner()}
<FormActions>
<FormButton startIcon={<PermScanWifiIcon />} variant="contained" color="secondary" onClick={this.requestNetworkScan} disabled={scanningForNetworks}>
Scan again...
</FormButton>
</FormActions>
</SectionContent>
);
}
}
export default withSnackbar(withStyles(styles)(WiFiNetworkScanner));