added the firmware page

This commit is contained in:
csawatzky 2025-04-15 15:30:23 -06:00
parent 428e45f148
commit 31fae79fd5
10 changed files with 1909 additions and 1552 deletions

View file

@ -0,0 +1,125 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
MenuItem,
TextField
} from "@mui/material";
import { useFirmwareAPI, usePrevious, useSnackbar } from "hooks";
import { Firmware } from "models";
import { pond } from "protobuf-ts/pond";
import { useEffect, useState } from "react";
interface Props {
firmware: Firmware;
isOpen: boolean;
closeCallback: Function;
refreshCallback: Function;
}
export default function UpdateFirmwareChannel(props: Props) {
const firmwareAPI = useFirmwareAPI();
const { isOpen, closeCallback, refreshCallback } = props;
const prevFirmware = usePrevious(props.firmware);
const { success, error } = useSnackbar();
const [firmware, setFirmware] = useState<Firmware>(Firmware.create());
useEffect(() => {
if (prevFirmware !== props.firmware) {
setFirmware(Firmware.clone(props.firmware));
}
}, [firmware, prevFirmware, props.firmware]);
const closeDialog = () => {
closeCallback();
};
const changeChannel = (event: any) => {
let updatedFirmware = firmware;
updatedFirmware.settings.channel = event.target.value;
setFirmware(updatedFirmware);
};
const update = () => {
firmwareAPI
.updateChannel(
firmware.settings.platform,
firmware.settings.version,
firmware.settings.channel
)
.then((response: any) => {
success("Firmware channel updated");
closeDialog();
refreshCallback();
})
.catch((err: any) => {
error(err);
});
};
if (firmware === null || firmware === undefined) {
return null;
} else {
return (
<Dialog fullWidth open={isOpen} onClose={closeDialog} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Upload Firmware</DialogTitle>
<DialogContent>
<TextField
disabled
id="platform"
name="platform"
select
label="Platform"
margin="normal"
fullWidth
variant="outlined"
value={firmware.settings.platform}>
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_PHOTON}>Photon (Wifi)</MenuItem>
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_ELECTRON}>
Electron (Cellular)
</MenuItem>
</TextField>
<TextField
disabled
id="version"
name="version"
autoFocus
label="Version"
margin="normal"
type="text"
fullWidth
variant="outlined"
value={firmware.settings.version}
/>
<TextField
id="channel"
name="channel"
select
required
label="Channel"
margin="normal"
fullWidth
variant="outlined"
value={firmware.settings.channel}
onChange={changeChannel}>
<MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_RECOVERY}>Recovery</MenuItem>
<MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_DEVELOPMENT}>Development</MenuItem>
<MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_ALPHA}>Alpha</MenuItem>
<MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_BETA}>Beta</MenuItem>
<MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_STABLE}>Stable</MenuItem>
</TextField>
</DialogContent>
<DialogActions>
<Button onClick={closeDialog} color="primary">
Cancel
</Button>
<Button onClick={update} color="primary">
Update
</Button>
</DialogActions>
</Dialog>
);
}
}

View file

@ -0,0 +1,304 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
FormControlLabel,
IconButton,
InputBase,
MenuItem,
Paper,
Switch,
TextField,
Theme
} from "@mui/material";
import { createStyles } from "@mui/styles";
import withStyles, { WithStyles } from "@mui/styles/withStyles";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import { pond } from "protobuf-ts/pond";
import { FirmwareAPIContext } from "providers/pond/firmwareAPI";
import React from "react";
import semver from "semver";
import { or } from "utils/types";
const styles = (theme: Theme) => {
return createStyles({
root: {
padding: "2px 4px",
display: "flex",
alignItems: "center"
},
input: {
marginLeft: 8,
flex: 1
},
iconButton: {
padding: 10
}
});
};
interface Props extends WithStyles<typeof styles> {
isOpen: boolean;
closeDialogCallback: () => void;
refreshCallback: () => void;
}
interface State {
dirty: boolean;
version: string;
platform: pond.DevicePlatform;
channel: pond.UpgradeChannel;
file?: Blob;
breaksStorage: boolean;
}
class UploadFirmware extends React.Component<Props, State> {
static contextType = FirmwareAPIContext;
declare context: React.ContextType<typeof FirmwareAPIContext>;
selector: any;
constructor(props: Props) {
super(props);
this.state = {
dirty: false,
version: "",
platform: pond.DevicePlatform.DEVICE_PLATFORM_PHOTON,
channel: pond.UpgradeChannel.UPGRADE_CHANNEL_DEVELOPMENT,
breaksStorage: false
};
}
isValid() {
const { version, platform, channel, file } = this.state;
var isPlatformValid = [
pond.DevicePlatform.DEVICE_PLATFORM_PHOTON,
pond.DevicePlatform.DEVICE_PLATFORM_ELECTRON,
pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR,
pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_S3,
pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLACK,
pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_GREEN,
pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLUE,
pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_BLUE
].includes(platform);
var isChannelValid = [
pond.UpgradeChannel.UPGRADE_CHANNEL_ALPHA,
pond.UpgradeChannel.UPGRADE_CHANNEL_BETA,
pond.UpgradeChannel.UPGRADE_CHANNEL_STABLE,
pond.UpgradeChannel.UPGRADE_CHANNEL_DEVELOPMENT
].includes(channel);
var isVersionValid = semver.valid(version) !== null;
var isFileValid = file !== undefined;
return isPlatformValid && isChannelValid && isVersionValid && isFileValid;
}
componentDidMount() {
this.selector = document.createElement("input");
this.selector.setAttribute("type", "file");
var self = this;
this.selector.addEventListener("change", function() {
if (self.selector.files && self.selector.files.length > 0) {
self.selectFile(self.selector.files[0]);
}
});
}
closeDialog = () => {
this.props.closeDialogCallback();
};
openFileSelector = () => {
this.selector.click();
};
prefill = (filename: string) => {
filename = filename.toLowerCase();
var state: State = {} as State;
if (filename.includes("photon") || filename.includes("wifi")) {
state.platform = pond.DevicePlatform.DEVICE_PLATFORM_PHOTON;
}
if (filename.includes("electron") || filename.includes("cell")) {
state.platform = pond.DevicePlatform.DEVICE_PLATFORM_ELECTRON;
}
if (filename.includes("alpha")) {
state.channel = pond.UpgradeChannel.UPGRADE_CHANNEL_ALPHA;
}
if (filename.includes("beta")) {
state.channel = pond.UpgradeChannel.UPGRADE_CHANNEL_BETA;
}
if (filename.includes("stable")) {
state.channel = pond.UpgradeChannel.UPGRADE_CHANNEL_STABLE;
}
if (filename.includes("dev")) {
state.channel = pond.UpgradeChannel.UPGRADE_CHANNEL_DEVELOPMENT;
}
var version = filename.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)*/g);
if (version && version.length > 0) {
state.version = version[0];
}
if (filename.includes("break")) {
state.breaksStorage = true;
}
this.setState(state);
};
selectFile = (file: any) => {
const { dirty } = this.state;
if (!dirty) {
this.prefill(file.name);
}
this.setState({ file: file });
};
uploadFirmware = () => {
const { refreshCallback } = this.props;
const { version, channel, platform, file, breaksStorage } = this.state;
const firmwareAPI = this.context;
if (file) {
firmwareAPI
.uploadFirmware(version, channel, platform, file, breaksStorage)
.then(() => {
this.closeDialog();
refreshCallback();
})
.catch((err: any) => {
console.error(err ? err.message : "Error occurred while uploading firmware");
});
} else {
console.error("Cannot upload firmware, file not found");
}
};
render() {
const { classes, isOpen } = this.props;
const { version, channel, platform, file } = this.state;
return (
<Dialog
fullWidth
open={isOpen}
onClose={this.closeDialog}
aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Upload Firmware</DialogTitle>
<DialogContent>
<Paper className={classes.root} elevation={0}>
<IconButton
onClick={this.openFileSelector}
className={classes.iconButton}
aria-label="Select">
<CloudUploadIcon />
</IconButton>
<InputBase
disabled
className={classes.input}
placeholder="Select a binary"
value={or(file as any, { name: "" }).name}
/>
</Paper>
<TextField
id="platform"
name="platform"
select
required
label="Platform"
margin="normal"
fullWidth
variant="outlined"
value={platform}
onChange={event =>
this.setState({
platform: parseInt(event.target.value) as pond.DevicePlatform,
dirty: true
})
}>
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_PHOTON}>Photon (Wifi)</MenuItem>
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_ELECTRON}>
Electron (Cellular)
</MenuItem>
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR}>V2 Cellular</MenuItem>
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_S3}>V2 Wifi S3</MenuItem>
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLACK}>
V2 Cellular Black
</MenuItem>
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_GREEN}>
V2 Cellular Green
</MenuItem>
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLUE}>
V2 Cellular Blue
</MenuItem>
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_BLUE}>
V2 Wifi Blue
</MenuItem>
</TextField>
<TextField
id="channel"
name="channel"
select
required
label="Channel"
margin="normal"
fullWidth
variant="outlined"
value={channel}
onChange={event => {
this.setState({
channel: parseInt(event.target.value) as pond.UpgradeChannel,
dirty: true
});
}}>
<MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_RECOVERY}>Recovery</MenuItem>
<MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_DEVELOPMENT}>Development</MenuItem>
<MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_ALPHA}>Alpha</MenuItem>
<MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_BETA}>Beta</MenuItem>
<MenuItem value={pond.UpgradeChannel.UPGRADE_CHANNEL_STABLE}>Stable</MenuItem>
</TextField>
<TextField
id="version"
name="version"
autoFocus
required
label="Version"
margin="normal"
type="text"
fullWidth
variant="outlined"
value={version}
onChange={event => {
this.setState({
version: event.target.value,
dirty: true
});
}}
/>
<FormControlLabel
control={
<Switch
name="breaksStorage"
checked={this.state.breaksStorage}
onChange={event => {
this.setState({
breaksStorage: event.target.checked,
dirty: true
});
}}
color="primary"
/>
}
label="Breaks Storage"
/>
</DialogContent>
<DialogActions>
<Button onClick={this.closeDialog} color="primary">
Cancel
</Button>
<Button onClick={this.uploadFirmware} color="primary" disabled={!this.isValid()}>
Upload
</Button>
</DialogActions>
</Dialog>
);
}
}
export default withStyles(styles)(UploadFirmware);