initial commit of C++ back end and react front end

This commit is contained in:
rjwats@gmail.com
2018-02-26 00:11:31 +00:00
commit 63a639eb22
87 changed files with 14975 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
import React, { Component } from 'react';
import Tabs, { Tab } from 'material-ui/Tabs';
import MenuAppBar from '../components/MenuAppBar';
import APSettings from './APSettings';
import APStatus from './APStatus';
class APConfiguration extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: "apStatus",
selectedNetwork: null
};
this.selectNetwork = this.selectNetwork.bind(this);
this.deselectNetwork = this.deselectNetwork.bind(this);
}
selectNetwork(network) {
this.setState({ selectedTab: "wifiSettings", selectedNetwork:network });
}
deselectNetwork(network) {
this.setState({ selectedNetwork:null });
}
handleTabChange = (event, selectedTab) => {
this.setState({ selectedTab });
};
render() {
const { selectedTab } = this.state;
return (
<MenuAppBar sectionTitle="AP Configuration">
<Tabs value={selectedTab} onChange={this.handleTabChange} indicatorColor="primary" textColor="primary" fullWidth centered scrollable>
<Tab value="apStatus" label="AP Status" />
<Tab value="apSettings" label="AP Settings" />
</Tabs>
{selectedTab === "apStatus" && <APStatus fullDetails={true} />}
{selectedTab === "apSettings" && <APSettings />}
</MenuAppBar>
)
}
}
export default APConfiguration;

View File

@@ -0,0 +1,70 @@
import React, { Component } from 'react';
import { AP_SETTINGS_ENDPOINT } from '../constants/Endpoints';
import SectionContent from '../components/SectionContent';
import SnackbarNotification from '../components/SnackbarNotification';
import APSettingsForm from '../forms/APSettingsForm';
import { simpleGet } from '../helpers/SimpleGet';
import { simplePost } from '../helpers/SimplePost';
class APSettings extends Component {
constructor(props) {
super(props);
this.state = {
apSettings:null,
apSettingsFetched: false,
errorMessage:null
};
this.setState = this.setState.bind(this);
this.loadAPSettings = this.loadAPSettings.bind(this);
this.saveAPSettings = this.saveAPSettings.bind(this);
}
componentDidMount() {
this.loadAPSettings();
}
loadAPSettings() {
simpleGet(
AP_SETTINGS_ENDPOINT,
this.setState,
this.raiseNotification,
"apSettings",
"apSettingsFetched"
);
}
saveAPSettings(e) {
simplePost(
AP_SETTINGS_ENDPOINT,
this.state,
this.setState,
this.raiseNotification,
"apSettings",
"apSettingsFetched"
);
}
wifiSettingValueChange = name => event => {
const { apSettings } = this.state;
apSettings[name] = event.target.value;
this.setState({apSettings});
};
render() {
const { apSettingsFetched, apSettings, errorMessage } = this.state;
return (
<SectionContent title="AP Settings">
<SnackbarNotification notificationRef={(raiseNotification)=>this.raiseNotification = raiseNotification} />
<APSettingsForm apSettingsFetched={apSettingsFetched} apSettings={apSettings} errorMessage={errorMessage}
onSubmit={this.saveAPSettings} onReset={this.loadAPSettings} handleValueChange={this.wifiSettingValueChange} />
</SectionContent>
)
}
}
export default APSettings;

View File

@@ -0,0 +1,165 @@
import React, { Component } from 'react';
import { withStyles } from 'material-ui/styles';
import Button from 'material-ui/Button';
import { LinearProgress } from 'material-ui/Progress';
import Typography from 'material-ui/Typography';
import List, { ListItem, ListItemText } from 'material-ui/List';
import Avatar from 'material-ui/Avatar';
import Divider from 'material-ui/Divider';
import SettingsInputAntennaIcon from 'material-ui-icons/SettingsInputAntenna';
import DeviceHubIcon from 'material-ui-icons/DeviceHub';
import ComputerIcon from 'material-ui-icons/Computer';
import SnackbarNotification from '../components/SnackbarNotification'
import SectionContent from '../components/SectionContent'
import * as Highlight from '../constants/Highlight';
import { AP_STATUS_ENDPOINT } from '../constants/Endpoints';
import { simpleGet } from '../helpers/SimpleGet';
const styles = theme => ({
["apStatus_" + Highlight.SUCCESS]: {
backgroundColor: theme.palette.highlight_success
},
["apStatus_" + Highlight.IDLE]: {
backgroundColor: theme.palette.highlight_idle
},
fetching: {
margin: theme.spacing.unit * 4,
textAlign: "center"
},
button: {
marginRight: theme.spacing.unit * 2,
marginTop: theme.spacing.unit * 2,
}
});
class APStatus extends Component {
constructor(props) {
super(props);
this.state = {
status:null,
fetched: false,
errorMessage:null
};
this.setState = this.setState.bind(this);
this.loadAPStatus = this.loadAPStatus.bind(this);
this.raiseNotification=this.raiseNotification.bind(this);
}
componentDidMount() {
this.loadAPStatus();
}
loadAPStatus() {
simpleGet(
AP_STATUS_ENDPOINT,
this.setState,
this.raiseNotification
);
}
raiseNotification(errorMessage) {
this.snackbarNotification(errorMessage);
}
apStatusHighlight(status){
return status.active ? Highlight.SUCCESS : Highlight.IDLE;
}
apStatus(status){
return status.active ? "Active" : "Inactive";
}
// active, ip_address, mac_address, station_num
renderAPStatus(status, fullDetails, classes){
const listItems = [];
listItems.push(
<ListItem key="ap_status">
<Avatar className={classes["apStatus_" + this.apStatusHighlight(status)]}>
<SettingsInputAntennaIcon />
</Avatar>
<ListItemText primary="Status" secondary={this.apStatus(status)} />
</ListItem>
);
listItems.push(<Divider key="ap_status_divider" inset component="li" />);
listItems.push(
<ListItem key="ip_address">
<Avatar>IP</Avatar>
<ListItemText primary="IP Address" secondary={status.ip_address} />
</ListItem>
);
listItems.push(<Divider key="ip_address_divider" inset component="li" />);
listItems.push(
<ListItem key="mac_address">
<Avatar>
<DeviceHubIcon />
</Avatar>
<ListItemText primary="MAC Address" secondary={status.mac_address} />
</ListItem>
);
listItems.push(<Divider key="mac_address_divider" inset component="li" />);
listItems.push(
<ListItem key="station_num">
<Avatar>
<ComputerIcon />
</Avatar>
<ListItemText primary="AP Clients" secondary={status.station_num} />
</ListItem>
);
listItems.push(<Divider key="mac_address_divider" inset component="li" />);
return (
<div>
<List>
{listItems}
</List>
<Button variant="raised" color="secondary" className={classes.button} onClick={this.loadAPStatus}>
Refresh
</Button>
</div>
);
}
render() {
const { status, fetched, errorMessage } = this.state;
const { classes, fullDetails } = this.props;
return (
<SectionContent title="AP Status">
<SnackbarNotification notificationRef={(snackbarNotification)=>this.snackbarNotification = snackbarNotification} />
{
!fetched ?
<div>
<LinearProgress className={classes.fetching}/>
<Typography variant="display1" className={classes.fetching}>
Loading...
</Typography>
</div>
:
status ? this.renderAPStatus(status, fullDetails, classes)
:
<div>
<Typography variant="display1" className={classes.fetching}>
{errorMessage}
</Typography>
<Button variant="raised" color="secondary" className={classes.button} onClick={this.loadAPStatus}>
Refresh
</Button>
</div>
}
</SectionContent>
)
}
}
export default withStyles(styles)(APStatus);

View File

@@ -0,0 +1,37 @@
import React, { Component } from 'react';
import MenuAppBar from '../components/MenuAppBar';
import NTPSettings from './NTPSettings';
import NTPStatus from './NTPStatus';
import Tabs, { Tab } from 'material-ui/Tabs';
class NTPConfiguration extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: "ntpStatus"
};
}
handleTabChange = (event, selectedTab) => {
this.setState({ selectedTab });
};
render() {
const { selectedTab } = this.state;
return (
<MenuAppBar sectionTitle="NTP Configuration">
<Tabs value={selectedTab} onChange={this.handleTabChange} indicatorColor="primary" textColor="primary" fullWidth centered scrollable>
<Tab value="ntpStatus" label="NTP Status" />
<Tab value="ntpSettings" label="NTP Settings" />
</Tabs>
{selectedTab === "ntpStatus" && <NTPStatus />}
{selectedTab === "ntpSettings" && <NTPSettings />}
</MenuAppBar>
)
}
}
export default NTPConfiguration

View File

@@ -0,0 +1,70 @@
import React, { Component } from 'react';
import { NTP_SETTINGS_ENDPOINT } from '../constants/Endpoints';
import SectionContent from '../components/SectionContent';
import SnackbarNotification from '../components/SnackbarNotification';
import NTPSettingsForm from '../forms/NTPSettingsForm';
import { simpleGet } from '../helpers/SimpleGet';
import { simplePost } from '../helpers/SimplePost';
class NTPSettings extends Component {
constructor(props) {
super(props);
this.state = {
ntpSettings:{},
ntpSettingsFetched: false,
errorMessage:null
};
this.setState = this.setState.bind(this);
this.loadNTPSettings = this.loadNTPSettings.bind(this);
this.saveNTPSettings = this.saveNTPSettings.bind(this);
}
componentDidMount() {
this.loadNTPSettings();
}
loadNTPSettings() {
simpleGet(
NTP_SETTINGS_ENDPOINT,
this.setState,
this.raiseNotification,
"ntpSettings",
"ntpSettingsFetched"
);
}
saveNTPSettings(e) {
simplePost(
NTP_SETTINGS_ENDPOINT,
this.state,
this.setState,
this.raiseNotification,
"ntpSettings",
"ntpSettingsFetched"
);
}
ntpSettingValueChange = name => event => {
const { ntpSettings } = this.state;
ntpSettings[name] = event.target.value;
this.setState({ntpSettings});
};
render() {
const { ntpSettingsFetched, ntpSettings, errorMessage } = this.state;
return (
<SectionContent title="NTP Settings">
<SnackbarNotification notificationRef={(raiseNotification)=>this.raiseNotification = raiseNotification} />
<NTPSettingsForm ntpSettingsFetched={ntpSettingsFetched} ntpSettings={ntpSettings} errorMessage={errorMessage}
onSubmit={this.saveNTPSettings} onReset={this.loadNTPSettings} handleValueChange={this.ntpSettingValueChange} />
</SectionContent>
)
}
}
export default NTPSettings;

View File

@@ -0,0 +1,189 @@
import React, { Component } from 'react';
import { withStyles } from 'material-ui/styles';
import Button from 'material-ui/Button';
import { LinearProgress } from 'material-ui/Progress';
import Typography from 'material-ui/Typography';
import List, { ListItem, ListItemText } from 'material-ui/List';
import Avatar from 'material-ui/Avatar';
import Divider from 'material-ui/Divider';
import SwapVerticalCircleIcon from 'material-ui-icons/SwapVerticalCircle';
import AccessTimeIcon from 'material-ui-icons/AccessTime';
import DNSIcon from 'material-ui-icons/Dns';
import TimerIcon from 'material-ui-icons/Timer';
import UpdateIcon from 'material-ui-icons/Update';
import AvTimerIcon from 'material-ui-icons/AvTimer';
import { isSynchronized, ntpStatusHighlight, ntpStatus } from '../constants/NTPStatus';
import * as Highlight from '../constants/Highlight';
import { unixTimeToTimeAndDate } from '../constants/TimeFormat';
import { NTP_STATUS_ENDPOINT } from '../constants/Endpoints';
import SnackbarNotification from '../components/SnackbarNotification';
import SectionContent from '../components/SectionContent';
import { simpleGet } from '../helpers/SimpleGet';
import moment from 'moment';
const styles = theme => ({
["ntpStatus_" + Highlight.SUCCESS]: {
backgroundColor: theme.palette.highlight_success
},
["ntpStatus_" + Highlight.ERROR]: {
backgroundColor: theme.palette.highlight_error
},
["ntpStatus_" + Highlight.WARN]: {
backgroundColor: theme.palette.highlight_warn
},
fetching: {
margin: theme.spacing.unit * 4,
textAlign: "center"
},
button: {
marginRight: theme.spacing.unit * 2,
marginTop: theme.spacing.unit * 2,
}
});
class NTPStatus extends Component {
constructor(props) {
super(props);
this.state = {
status:null,
fetched: false,
errorMessage:null
};
this.setState = this.setState.bind(this);
this.loadNTPStatus = this.loadNTPStatus.bind(this);
this.raiseNotification=this.raiseNotification.bind(this);
}
componentDidMount() {
this.loadNTPStatus();
}
loadNTPStatus() {
simpleGet(
NTP_STATUS_ENDPOINT,
this.setState,
this.raiseNotification
);
}
raiseNotification(errorMessage) {
this.snackbarNotification(errorMessage);
}
renderNTPStatus(status, fullDetails, classes){
const listItems = [];
listItems.push(
<ListItem key="ntp_status">
<Avatar className={classes["ntpStatus_" + ntpStatusHighlight(status)]}>
<UpdateIcon />
</Avatar>
<ListItemText primary="Status" secondary={ntpStatus(status)} />
</ListItem>
);
listItems.push(<Divider key="ntp_status_divider" inset component="li" />);
if (isSynchronized(status)) {
listItems.push(
<ListItem key="time_now">
<Avatar>
<AccessTimeIcon />
</Avatar>
<ListItemText primary="Time Now" secondary={unixTimeToTimeAndDate(status.now)} />
</ListItem>
);
listItems.push(<Divider key="time_now_divider" inset component="li" />);
}
listItems.push(
<ListItem key="last_sync">
<Avatar>
<SwapVerticalCircleIcon />
</Avatar>
<ListItemText primary="Last Sync" secondary={status.last_sync > 0 ? unixTimeToTimeAndDate(status.last_sync) : "never" } />
</ListItem>
);
listItems.push(<Divider key="last_sync_divider" inset component="li" />);
listItems.push(
<ListItem key="ntp_server">
<Avatar>
<DNSIcon />
</Avatar>
<ListItemText primary="NTP Server" secondary={status.server} />
</ListItem>
);
listItems.push(<Divider key="ntp_server_divider" inset component="li" />);
listItems.push(
<ListItem key="sync_interval">
<Avatar>
<TimerIcon />
</Avatar>
<ListItemText primary="Sync Interval" secondary={moment.duration(status.interval, 'seconds').humanize()} />
</ListItem>
);
listItems.push(<Divider key="sync_interval_divider" inset component="li" />);
listItems.push(
<ListItem key="uptime">
<Avatar>
<AvTimerIcon />
</Avatar>
<ListItemText primary="Uptime" secondary={moment.duration(status.uptime, 'seconds').humanize()} />
</ListItem>
);
return (
<div>
<List>
{listItems}
</List>
<Button variant="raised" color="secondary" className={classes.button} onClick={this.loadNTPStatus}>
Refresh
</Button>
</div>
);
}
render() {
const { status, fetched, errorMessage } = this.state;
const { classes, fullDetails } = this.props;
return (
<SectionContent title="NTP Status">
<SnackbarNotification notificationRef={(snackbarNotification)=>this.snackbarNotification = snackbarNotification} />
{
!fetched ?
<div>
<LinearProgress className={classes.fetching}/>
<Typography variant="display1" className={classes.fetching}>
Loading...
</Typography>
</div>
:
status ? this.renderNTPStatus(status, fullDetails, classes)
:
<div>
<Typography variant="display1" className={classes.fetching}>
{errorMessage}
</Typography>
<Button variant="raised" color="secondary" className={classes.button} onClick={this.loadNTPStatus}>
Refresh
</Button>
</div>
}
</SectionContent>
)
}
}
export default withStyles(styles)(NTPStatus);

View File

@@ -0,0 +1,15 @@
import React, { Component } from 'react';
import MenuAppBar from '../components/MenuAppBar';
import OTASettings from './OTASettings';
class OTAConfiguration extends Component {
render() {
return (
<MenuAppBar sectionTitle="OTA Configuration">
<OTASettings />
</MenuAppBar>
)
}
}
export default OTAConfiguration

View File

@@ -0,0 +1,77 @@
import React, { Component } from 'react';
import { OTA_SETTINGS_ENDPOINT } from '../constants/Endpoints';
import SectionContent from '../components/SectionContent';
import SnackbarNotification from '../components/SnackbarNotification';
import OTASettingsForm from '../forms/OTASettingsForm';
import { simpleGet } from '../helpers/SimpleGet';
import { simplePost } from '../helpers/SimplePost';
class OTASettings extends Component {
constructor(props) {
super(props);
this.state = {
otaSettings:null,
otaSettingsFetched: false,
errorMessage:null
};
this.setState = this.setState.bind(this);
this.loadOTASettings = this.loadOTASettings.bind(this);
this.saveOTASettings = this.saveOTASettings.bind(this);
}
componentDidMount() {
this.loadOTASettings();
}
loadOTASettings() {
simpleGet(
OTA_SETTINGS_ENDPOINT,
this.setState,
this.raiseNotification,
"otaSettings",
"otaSettingsFetched"
);
}
saveOTASettings(e) {
simplePost(
OTA_SETTINGS_ENDPOINT,
this.state,
this.setState,
this.raiseNotification,
"otaSettings",
"otaSettingsFetched"
);
}
otaSettingValueChange = name => event => {
const { otaSettings } = this.state;
otaSettings[name] = event.target.value;
this.setState({otaSettings});
};
otaSettingCheckboxChange = name => event => {
const { otaSettings } = this.state;
otaSettings[name] = event.target.checked;
this.setState({otaSettings});
}
render() {
const { otaSettingsFetched, otaSettings, errorMessage } = this.state;
return (
<SectionContent title="OTA Settings">
<SnackbarNotification notificationRef={(raiseNotification)=>this.raiseNotification = raiseNotification} />
<OTASettingsForm otaSettingsFetched={otaSettingsFetched} otaSettings={otaSettings} errorMessage={errorMessage}
onSubmit={this.saveOTASettings} onReset={this.loadOTASettings} handleValueChange={this.otaSettingValueChange}
handleCheckboxChange={this.otaSettingCheckboxChange} />
</SectionContent>
)
}
}
export default OTASettings;

View File

@@ -0,0 +1,53 @@
import React, { Component } from 'react';
import Tabs, { Tab } from 'material-ui/Tabs';
import MenuAppBar from '../components/MenuAppBar';
import WiFiNetworkScanner from './WiFiNetworkScanner';
import WiFiSettings from './WiFiSettings';
import WiFiStatus from './WiFiStatus';
class WiFiConfiguration extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: "wifiStatus",
selectedNetwork: null
};
this.selectNetwork = this.selectNetwork.bind(this);
this.deselectNetwork = this.deselectNetwork.bind(this);
}
// TODO - slightly inapproperate use of callback ref possibly.
selectNetwork(network) {
this.setState({ selectedTab: "wifiSettings", selectedNetwork:network });
}
// deselects the network after the settings component mounts.
deselectNetwork(network) {
this.setState({ selectedNetwork:null });
}
handleTabChange = (event, selectedTab) => {
this.setState({ selectedTab });
};
render() {
const { selectedTab } = this.state;
return (
<MenuAppBar sectionTitle="WiFi Configuration">
<Tabs value={selectedTab} onChange={this.handleTabChange} indicatorColor="primary" textColor="primary" fullWidth centered scrollable>
<Tab value="wifiStatus" label="WiFi Status" />
<Tab value="networkScanner" label="Network Scanner" />
<Tab value="wifiSettings" label="WiFi Settings" />
</Tabs>
{selectedTab === "wifiStatus" && <WiFiStatus fullDetails={true} />}
{selectedTab === "networkScanner" && <WiFiNetworkScanner selectNetwork={this.selectNetwork} />}
{selectedTab === "wifiSettings" && <WiFiSettings deselectNetwork={this.deselectNetwork} selectedNetwork={this.state.selectedNetwork} />}
</MenuAppBar>
)
}
}
export default WiFiConfiguration;

View File

@@ -0,0 +1,118 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { SCAN_NETWORKS_ENDPOINT, LIST_NETWORKS_ENDPOINT } from '../constants/Endpoints';
import SectionContent from '../components/SectionContent';
import WiFiNetworkSelector from '../forms/WiFiNetworkSelector';
const NUM_POLLS = 10
const POLLING_FREQUENCY = 500
const RETRY_EXCEPTION_TYPE = "retry"
class WiFiNetworkScanner extends Component {
constructor(props) {
super(props);
this.pollCount = 0;
this.state = {
scanningForNetworks: true,
errorMessage:null,
networkList: null
};
this.pollNetworkList = this.pollNetworkList.bind(this);
this.requestNetworkScan = this.requestNetworkScan.bind(this);
}
componentDidMount() {
this.scanNetworks();
}
requestNetworkScan() {
const { scanningForNetworks } = this.state;
if (!scanningForNetworks) {
this.scanNetworks();
}
}
scanNetworks() {
this.pollCount = 0;
this.setState({scanningForNetworks:true, networkList: null, errorMessage:null});
fetch(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.setState({scanningForNetworks:false, networkList: null, 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,network2) {
if (network1.rssi < network2.rssi)
return 1;
if (network1.rssi > network2.rssi)
return -1;
return 0;
}
pollNetworkList() {
fetch(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 timley manner.");
}
}
throw Error("Device returned unexpected response code: " + response.status);
})
.then(json => {
json.networks.sort(this.compareNetworks)
this.setState({scanningForNetworks:false, networkList: json, errorMessage:null})
})
.catch(error => {
console.log(error.message);
if (error.name !== RETRY_EXCEPTION_TYPE) {
this.setState({scanningForNetworks:false, networkList: null, errorMessage:error.message});
}
});
}
render() {
const { scanningForNetworks, networkList, errorMessage } = this.state;
return (
<SectionContent title="Network Scanner">
<WiFiNetworkSelector scanningForNetworks={scanningForNetworks}
networkList={networkList}
errorMessage={errorMessage}
requestNetworkScan={this.requestNetworkScan}
selectNetwork={this.props.selectNetwork}
/>
</SectionContent>
)
}
}
WiFiNetworkScanner.propTypes = {
selectNetwork: PropTypes.func.isRequired
};
export default (WiFiNetworkScanner);

View File

@@ -0,0 +1,102 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { WIFI_SETTINGS_ENDPOINT } from '../constants/Endpoints';
import SectionContent from '../components/SectionContent';
import SnackbarNotification from '../components/SnackbarNotification';
import WiFiSettingsForm from '../forms/WiFiSettingsForm';
import { simpleGet } from '../helpers/SimpleGet';
import { simplePost } from '../helpers/SimplePost';
class WiFiSettings extends Component {
constructor(props) {
super(props);
this.state = {
wifiSettingsFetched: false,
wifiSettings:{},
selectedNetwork: null,
errorMessage:null
};
this.setState = this.setState.bind(this);
this.loadWiFiSettings = this.loadWiFiSettings.bind(this);
this.saveWiFiSettings = this.saveWiFiSettings.bind(this);
this.deselectNetwork = this.deselectNetwork.bind(this);
}
componentDidMount() {
const { selectedNetwork, deselectNetwork } = this.props;
if (selectedNetwork){
var wifiSettings = {
ssid:selectedNetwork.ssid,
password:"",
hostname:"esp8266-react",
static_ip_config:false,
}
this.setState({wifiSettingsFetched:true, wifiSettings, selectedNetwork, errorMessage:null});
deselectNetwork();
}else {
this.loadWiFiSettings();
}
}
loadWiFiSettings() {
this.deselectNetwork();
simpleGet(
WIFI_SETTINGS_ENDPOINT,
this.setState,
this.raiseNotification,
"wifiSettings",
"wifiSettingsFetched"
);
}
saveWiFiSettings(e) {
simplePost(
WIFI_SETTINGS_ENDPOINT,
this.state,
this.setState,
this.raiseNotification,
"wifiSettings",
"wifiSettingsFetched"
);
}
deselectNetwork(nextNetwork) {
this.setState({selectedNetwork:null});
}
wifiSettingValueChange = name => event => {
const { wifiSettings } = this.state;
wifiSettings[name] = event.target.value;
this.setState({wifiSettings});
};
wifiSettingCheckboxChange = name => event => {
const { wifiSettings } = this.state;
wifiSettings[name] = event.target.checked;
this.setState({wifiSettings});
}
render() {
const { wifiSettingsFetched, wifiSettings, errorMessage, selectedNetwork } = this.state;
return (
<SectionContent title="WiFi Settings">
<SnackbarNotification notificationRef={(raiseNotification)=>this.raiseNotification = raiseNotification} />
<WiFiSettingsForm wifiSettingsFetched={wifiSettingsFetched} wifiSettings={wifiSettings} errorMessage={errorMessage} selectedNetwork={selectedNetwork} deselectNetwork={this.deselectNetwork}
onSubmit={this.saveWiFiSettings} onReset={this.loadWiFiSettings} handleValueChange={this.wifiSettingValueChange} handleCheckboxChange={this.wifiSettingCheckboxChange} />
</SectionContent>
)
}
}
WiFiSettings.propTypes = {
deselectNetwork: PropTypes.func,
selectedNetwork: PropTypes.object
};
export default WiFiSettings;

View File

@@ -0,0 +1,188 @@
import React, { Component } from 'react';
import { withStyles } from 'material-ui/styles';
import Button from 'material-ui/Button';
import { LinearProgress } from 'material-ui/Progress';
import Typography from 'material-ui/Typography';
import SnackbarNotification from '../components/SnackbarNotification';
import SectionContent from '../components/SectionContent';
import { WIFI_STATUS_ENDPOINT } from '../constants/Endpoints';
import { isConnected, connectionStatus, connectionStatusHighlight } from '../constants/WiFiConnectionStatus';
import * as Highlight from '../constants/Highlight';
import { simpleGet } from '../helpers/SimpleGet';
import List, { ListItem, ListItemText } from 'material-ui/List';
import Avatar from 'material-ui/Avatar';
import Divider from 'material-ui/Divider';
import WifiIcon from 'material-ui-icons/Wifi';
import DNSIcon from 'material-ui-icons/Dns';
import SettingsInputComponentIcon from 'material-ui-icons/SettingsInputComponent';
import SettingsInputAntennaIcon from 'material-ui-icons/SettingsInputAntenna';
const styles = theme => ({
["wifiStatus_" + Highlight.IDLE]: {
backgroundColor: theme.palette.highlight_idle
},
["wifiStatus_" + Highlight.SUCCESS]: {
backgroundColor: theme.palette.highlight_success
},
["wifiStatus_" + Highlight.ERROR]: {
backgroundColor: theme.palette.highlight_error
},
["wifiStatus_" + Highlight.WARN]: {
backgroundColor: theme.palette.highlight_warn
},
fetching: {
margin: theme.spacing.unit * 4,
textAlign: "center"
},
button: {
marginRight: theme.spacing.unit * 2,
marginTop: theme.spacing.unit * 2,
}
});
class WiFiStatus extends Component {
constructor(props) {
super(props);
this.state = {
status:null,
fetched: false,
errorMessage:null
};
this.setState = this.setState.bind(this);
this.loadWiFiStatus = this.loadWiFiStatus.bind(this);
}
componentDidMount() {
this.loadWiFiStatus();
}
dnsServers(status) {
if (!status.dns_ip_1){
return "none";
}
return status.dns_ip_1 + (status.dns_ip_2 ? ','+status.dns_ip_2 : '');
}
loadWiFiStatus() {
simpleGet(
WIFI_STATUS_ENDPOINT,
this.setState,
this.raiseNotification
);
}
renderWiFiStatus(status, fullDetails, classes) {
const listItems = [];
listItems.push(
<ListItem key="connection_status">
<Avatar className={classes["wifiStatus_" + connectionStatusHighlight(status)]}>
<WifiIcon />
</Avatar>
<ListItemText primary="Connection Status" secondary={connectionStatus(status)} />
</ListItem>
);
if (fullDetails && isConnected(status)) {
listItems.push(<Divider key="connection_status_divider" inset component="li" />);
listItems.push(
<ListItem key="ssid">
<Avatar>
<SettingsInputAntennaIcon />
</Avatar>
<ListItemText primary="SSID" secondary={status.ssid} />
</ListItem>
);
listItems.push(<Divider key="ssid_divider" inset component="li" />);
listItems.push(
<ListItem key="ip_address">
<Avatar>IP</Avatar>
<ListItemText primary="IP Address" secondary={status.local_ip} />
</ListItem>
);
listItems.push(<Divider key="ip_address_divider" inset component="li" />);
listItems.push(
<ListItem key="subnet_mask">
<Avatar>#</Avatar>
<ListItemText primary="Subnet Mask" secondary={status.subnet_mask} />
</ListItem>
);
listItems.push(<Divider key="subnet_mask_divider" inset component="li" />);
listItems.push(
<ListItem key="gateway_ip">
<Avatar>
<SettingsInputComponentIcon />
</Avatar>
<ListItemText primary="Gateway IP" secondary={status.gateway_ip ? status.gateway_ip : "none"} />
</ListItem>
);
listItems.push(<Divider key="gateway_ip_divider" inset component="li" />);
listItems.push(
<ListItem key="dns_server_ip">
<Avatar>
<DNSIcon />
</Avatar>
<ListItemText primary="DNS Server IP" secondary={this.dnsServers(status)} />
</ListItem>
);
}
return (
<div>
<List>
{listItems}
</List>
<Button variant="raised" color="secondary" className={classes.button} onClick={this.loadWiFiStatus}>
Refresh
</Button>
</div>
);
}
render() {
const { status, fetched, errorMessage } = this.state;
const { classes, fullDetails } = this.props;
return (
<SectionContent title="WiFi Status">
<SnackbarNotification notificationRef={(raiseNotification)=>this.raiseNotification = raiseNotification} />
{
!fetched ?
<div>
<LinearProgress className={classes.fetching}/>
<Typography variant="display1" className={classes.fetching}>
Loading...
</Typography>
</div>
:
status ? this.renderWiFiStatus(status, fullDetails, classes)
:
<div>
<Typography variant="display1" className={classes.fetching}>
{errorMessage}
</Typography>
<Button variant="raised" color="secondary" className={classes.button} onClick={this.loadWiFiStatus}>
Refresh
</Button>
</div>
}
</SectionContent>
)
}
}
export default withStyles(styles)(WiFiStatus);