can provision devices
This commit is contained in:
parent
0ae37f1370
commit
03f8ed20ca
18 changed files with 1467 additions and 17 deletions
|
|
@ -25,7 +25,7 @@ html, body, #root {
|
|||
}
|
||||
|
||||
/* styles.css or a CSS module */
|
||||
.MuiDialog-root {
|
||||
/* .MuiDialog-root {
|
||||
z-index: 1500 !important;
|
||||
}
|
||||
|
||||
|
|
@ -34,5 +34,5 @@ html, body, #root {
|
|||
}
|
||||
|
||||
.MuiMenu-root {
|
||||
z-index: 1400 !important; /* Ensure it's above the drawer */
|
||||
}
|
||||
z-index: 1400 !important;
|
||||
} */
|
||||
|
|
@ -120,6 +120,11 @@ export default function ChatInput(props: Props) {
|
|||
clearEntry();
|
||||
};
|
||||
|
||||
const getPlaceholder = () => {
|
||||
if (type === pond.NoteType.NOTE_TYPE_TEAM) return "Message team..."
|
||||
return "Type to chat..."
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid
|
||||
|
|
@ -139,6 +144,7 @@ export default function ChatInput(props: Props) {
|
|||
onChange={e => setMessage(e.target.value)}
|
||||
onKeyDown={onKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
placeholder={getPlaceholder()}
|
||||
/>
|
||||
<Box
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { pond } from "protobuf-ts/pond";
|
|||
import { red } from "@mui/material/colors";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import CancelSubmit from "common/CancelSubmit";
|
||||
import { getThemeType } from "theme/themeType";
|
||||
|
||||
interface Props {
|
||||
index: number;
|
||||
|
|
@ -35,12 +36,10 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
},
|
||||
oddGrid: {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
// padding: 4,
|
||||
padding: theme.spacing(1)
|
||||
},
|
||||
evenGrid: {
|
||||
// backgroundColor: theme.palette.background.default,
|
||||
// padding: 4
|
||||
backgroundColor: getThemeType() === "light" ? theme.palette.background.default : undefined,
|
||||
padding: theme.spacing(1)
|
||||
},
|
||||
userAvatar: {
|
||||
|
|
@ -51,8 +50,6 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
}
|
||||
}))
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Takes in a note and renders it
|
||||
* handles deleting of the note
|
||||
|
|
@ -132,7 +129,6 @@ export default function ChatMessage(props: Props) {
|
|||
<Dialog open={dialog} onClose={closeDialog}>
|
||||
<DialogTitle>Delete Message?</DialogTitle>
|
||||
<DialogContent>Are you sure you wish to delete this message</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<CancelSubmit
|
||||
onCancel={closeDialog}
|
||||
|
|
|
|||
403
src/device/ProvisionDevice.tsx
Normal file
403
src/device/ProvisionDevice.tsx
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
import {
|
||||
Button,
|
||||
CircularProgress,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Divider,
|
||||
Grid2 as Grid,
|
||||
IconButton,
|
||||
List,
|
||||
ListItem,
|
||||
TextField,
|
||||
Tooltip,
|
||||
Typography
|
||||
} from "@mui/material";
|
||||
import { FileCopy } from "@mui/icons-material";
|
||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||
import SearchSelect, { Option } from "common/SearchSelect";
|
||||
import SelectDevicePlatform from "device/SelectDevicePlatform";
|
||||
import { useBackpackAPI, useDeviceAPI, usePrevious, useSnackbar } from "hooks";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { Backpack, Device } from "models";
|
||||
import { backpackOptions, genericBackpacks } from "pbHelpers/Backpack";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
refreshCallback: Function;
|
||||
closeDialogCallback: Function;
|
||||
}
|
||||
|
||||
export default function ProvisionDevice(props: Props) {
|
||||
const deviceAPI = useDeviceAPI();
|
||||
const backpackAPI = useBackpackAPI();
|
||||
const { error, success, warning, info } = useSnackbar();
|
||||
const { isOpen, closeDialogCallback, refreshCallback } = props;
|
||||
const [provisioned, setProvisioned] = useState<boolean>(false);
|
||||
const [idHex, setIdHex] = useState<string>("");
|
||||
const [keyHex, setKeyHex] = useState<string>("");
|
||||
const [backpack, setBackpack] = useState<Backpack | undefined>(undefined);
|
||||
const [name, setName] = useState<string>("");
|
||||
const [description, setDescription] = useState<string>("");
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [backpacks, setBackpacks] = useState<Backpack[]>([]);
|
||||
const [command, setCommand] = useState<string>("");
|
||||
const [deviceLoading, setDeviceLoading] = useState<boolean>(false);
|
||||
const [platform, setPlatform] = useState<pond.DevicePlatform>(
|
||||
pond.DevicePlatform.DEVICE_PLATFORM_PHOTON
|
||||
);
|
||||
const [device, setDevice] = useState<Device | undefined>(undefined);
|
||||
const prevIsOpen = usePrevious(isOpen);
|
||||
const deviceID: number = parseInt(idHex, 16);
|
||||
|
||||
const setDefaultState = () => {
|
||||
setProvisioned(false);
|
||||
setIdHex("");
|
||||
setKeyHex("");
|
||||
setBackpack(undefined);
|
||||
setName("");
|
||||
setDescription("");
|
||||
setLoading(false);
|
||||
setBackpacks([]);
|
||||
setCommand("");
|
||||
setDeviceLoading(false);
|
||||
setDevice(undefined);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && !prevIsOpen) {
|
||||
setLoading(true);
|
||||
|
||||
backpackAPI
|
||||
.listBackpacks()
|
||||
.then((response: any) => {
|
||||
const rawBackpacks = response.data.backpacks;
|
||||
let updatedBackpacks: Backpack[] = genericBackpacks();
|
||||
if (rawBackpacks && rawBackpacks.length > 0) {
|
||||
rawBackpacks.forEach((backpack: any) => {
|
||||
updatedBackpacks.push(Backpack.any(backpack));
|
||||
});
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
setBackpacks(updatedBackpacks.sort((a, b) => (a.name() > b.name() ? 1 : -1)));
|
||||
setBackpack(undefined);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setDefaultState();
|
||||
error(err ? err : "Error occured while loading device profiles");
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
}
|
||||
}, [backpackAPI, isOpen, prevIsOpen, error]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && provisioned && !device && !deviceLoading) {
|
||||
setDeviceLoading(true);
|
||||
const deviceID = parseInt(idHex, 16);
|
||||
deviceAPI
|
||||
.get(deviceID)
|
||||
.then(response => {
|
||||
let rDevice = Device.any(response.data);
|
||||
setDevice(rDevice);
|
||||
setName(rDevice.name());
|
||||
setDescription(rDevice.settings.description);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
setDevice(
|
||||
Device.create(
|
||||
pond.Device.create({ settings: pond.DeviceSettings.create({ deviceId: deviceID }) })
|
||||
)
|
||||
);
|
||||
})
|
||||
.finally(() => setDeviceLoading(false));
|
||||
}
|
||||
}, [device, deviceAPI, deviceLoading, idHex, isOpen, provisioned]);
|
||||
|
||||
const close = () => {
|
||||
if (provisioned) {
|
||||
refreshCallback();
|
||||
}
|
||||
|
||||
closeDialogCallback();
|
||||
setDefaultState();
|
||||
};
|
||||
|
||||
const generateCommand = (idHex: any, keyHex: any): string => {
|
||||
if (!idHex || !keyHex) {
|
||||
return "";
|
||||
}
|
||||
return "set creds " + idHex + ":" + keyHex;
|
||||
};
|
||||
|
||||
const assembleBackpack = (): pond.BackpackSettings => {
|
||||
let assembledBackpack = backpack ? cloneDeep(backpack) : Backpack.create();
|
||||
let agnosticDevice = assembledBackpack.settings.device
|
||||
? cloneDeep(assembledBackpack.settings.device)
|
||||
: ({} as pond.DeviceSettings);
|
||||
agnosticDevice.deviceId = 0;
|
||||
agnosticDevice.platform = platform;
|
||||
agnosticDevice.product = assembledBackpack.settings.product;
|
||||
assembledBackpack.settings.device = agnosticDevice;
|
||||
|
||||
return assembledBackpack.settings;
|
||||
};
|
||||
|
||||
const submitProvision = () => {
|
||||
setProvisioned(false);
|
||||
deviceAPI
|
||||
.add("", "", assembleBackpack())
|
||||
.then((response: any) => {
|
||||
let updatedIdHex = response.data.idHex;
|
||||
let updatedKeyHex = response.data.keyHex;
|
||||
setIdHex(updatedIdHex);
|
||||
setKeyHex(updatedKeyHex);
|
||||
success("Successfully provisioned a new device (id: " + parseInt(updatedIdHex, 16) + ")");
|
||||
setProvisioned(true);
|
||||
setCommand(generateCommand(updatedIdHex, updatedKeyHex));
|
||||
})
|
||||
.catch((error: any) => {
|
||||
error.response.data.error
|
||||
? warning(error.response.data.error)
|
||||
: error("Error occured while provisioning a new device.");
|
||||
close();
|
||||
});
|
||||
};
|
||||
|
||||
const submitDeviceUpdate = () => {
|
||||
const deviceID = parseInt(idHex, 16);
|
||||
let settings = pond.DeviceSettings.create(device ? device.settings : { deviceId: deviceID });
|
||||
settings.name = name;
|
||||
settings.description = description;
|
||||
deviceAPI
|
||||
.update(deviceID, settings)
|
||||
.then(() => {
|
||||
success("Device " + deviceID.toString() + " was successfully updated");
|
||||
})
|
||||
.catch((error: any) => {
|
||||
error.response.data.error
|
||||
? warning(error.response.data.error)
|
||||
: error("Error occured while updating device " + deviceID.toString() + ".");
|
||||
close();
|
||||
})
|
||||
.finally(() => {
|
||||
setDevice(undefined);
|
||||
});
|
||||
};
|
||||
|
||||
const copyID = () => {
|
||||
navigator.clipboard.writeText(idHex);
|
||||
info("ID copied to clipboard");
|
||||
};
|
||||
|
||||
const copyKey = () => {
|
||||
navigator.clipboard.writeText(keyHex);
|
||||
info("Key copied to clipboard");
|
||||
};
|
||||
|
||||
const copyCommand = () => {
|
||||
navigator.clipboard.writeText(command + "\n");
|
||||
info("Command copied to clipboard");
|
||||
};
|
||||
|
||||
const changeBackpack = (option: Option | null) => {
|
||||
let selectedBackpack = undefined;
|
||||
if (option) {
|
||||
const value = Number(option.value);
|
||||
selectedBackpack = backpacks.find(backpack => {
|
||||
let idMatch: boolean = backpack.id() === value;
|
||||
return idMatch && (value === 0 ? backpack.name() === option.label : true);
|
||||
});
|
||||
}
|
||||
setBackpack(selectedBackpack);
|
||||
};
|
||||
|
||||
const content = () => {
|
||||
if (provisioned) {
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid size={{xs:12}} container justifyContent="center" alignItems="center">
|
||||
<Grid size={{xs: 11}}>
|
||||
<TextField
|
||||
id="id"
|
||||
name="ID"
|
||||
label="ID Hex"
|
||||
disabled
|
||||
value={idHex}
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
type="text"
|
||||
fullWidth
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{xs:1}}>
|
||||
<Tooltip title="Copy ID">
|
||||
<IconButton onClick={copyID}>
|
||||
<FileCopy />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid size={{xs:12}} container justifyContent="center" alignItems="center">
|
||||
<Grid size={{xs:11}}>
|
||||
<TextField
|
||||
id="key"
|
||||
name="Key"
|
||||
label="Key Hex"
|
||||
disabled
|
||||
value={keyHex}
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
type="text"
|
||||
fullWidth
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{xs:1}}>
|
||||
<Tooltip title="Copy Key">
|
||||
<IconButton onClick={copyKey}>
|
||||
<FileCopy />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid size={{xs:12}} container justifyContent="center" alignItems="center">
|
||||
<Grid size={{xs:11}}>
|
||||
<TextField
|
||||
id="command"
|
||||
name="command"
|
||||
label="Command"
|
||||
variant="outlined"
|
||||
value={command}
|
||||
disabled
|
||||
fullWidth
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{xs:1}}>
|
||||
<Tooltip title="Copy Command">
|
||||
<IconButton onClick={copyCommand}>
|
||||
<FileCopy />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{/* {window.NDEFReader && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
const ndef = new NDEFReader();
|
||||
ndef
|
||||
.write({
|
||||
records: [{ recordType: "text", data: command }]
|
||||
})
|
||||
.then(resp => {
|
||||
success("Comand sent");
|
||||
})
|
||||
.catch(error => {
|
||||
error("Failed to send command");
|
||||
});
|
||||
}}>
|
||||
Write to Device
|
||||
</Button>
|
||||
)} */}
|
||||
<Grid size={{xs:12}}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
{deviceLoading ? (
|
||||
<CircularProgress />
|
||||
) : (
|
||||
<Grid size={{xs:12}}>
|
||||
<Typography color="textPrimary" variant="body1" gutterBottom>
|
||||
Update device name/description
|
||||
</Typography>
|
||||
<TextField
|
||||
margin="normal"
|
||||
id="deviceName"
|
||||
label="Name"
|
||||
type="text"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={name}
|
||||
onChange={(event: any) => setName(event.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
margin="normal"
|
||||
id="deviceDescription"
|
||||
label="Description"
|
||||
type="text"
|
||||
multiline
|
||||
rows="4"
|
||||
// rowsMax="4"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={description}
|
||||
onChange={(event: any) => setDescription(event.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
const selectedBackpack = backpack
|
||||
? ({ label: backpack.name(), value: backpack.id() } as Option)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<List>
|
||||
<ListItem>
|
||||
<SelectDevicePlatform value={platform} onChange={platform => setPlatform(platform)} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<SearchSelect
|
||||
label="Select a profile to provision with"
|
||||
selected={selectedBackpack}
|
||||
options={backpackOptions(backpacks)}
|
||||
changeSelection={changeBackpack}
|
||||
loading={loading}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
const actions = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Button onClick={close} color="primary">
|
||||
Close
|
||||
</Button>
|
||||
{!provisioned ? (
|
||||
<Button onClick={submitProvision} color="primary" disabled={!backpack}>
|
||||
Provision
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={submitDeviceUpdate} color="primary" disabled={!backpack}>
|
||||
Update
|
||||
</Button>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ResponsiveDialog open={isOpen} onClose={close} aria-labelledby="form-dialog-title" fullWidth>
|
||||
<DialogTitle id="form-dialog-title">
|
||||
Provision Device
|
||||
{provisioned && (
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Device ID: {deviceID}
|
||||
</Typography>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogContent>{content()}</DialogContent>
|
||||
<DialogActions>{actions()}</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
);
|
||||
}
|
||||
31
src/device/SelectDevicePlatform.tsx
Normal file
31
src/device/SelectDevicePlatform.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { MenuItem, TextField } from "@mui/material";
|
||||
import { GetAllDevicePlatformDescribers } from "pbHelpers/DevicePlatform";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
|
||||
interface Props {
|
||||
value: pond.DevicePlatform;
|
||||
onChange: (platform: pond.DevicePlatform) => void;
|
||||
}
|
||||
|
||||
export default function SelectDevicePlatform(props: Props) {
|
||||
const { value, onChange } = props;
|
||||
|
||||
return (
|
||||
<TextField
|
||||
id="select-device-platform"
|
||||
select
|
||||
label="Device Platform"
|
||||
value={pond.DevicePlatform[value]}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={event =>
|
||||
onChange(pond.DevicePlatform[event.target.value as keyof typeof pond.DevicePlatform])
|
||||
}>
|
||||
{GetAllDevicePlatformDescribers().map(p => (
|
||||
<MenuItem key={pond.DevicePlatform[p.platform]} value={pond.DevicePlatform[p.platform]}>
|
||||
{p.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
export {
|
||||
// useAuth,
|
||||
// useBackpackAPI,
|
||||
useBackpackAPI,
|
||||
// useBilling,
|
||||
// useComponentAPI,
|
||||
// useComponentsWebsocket,
|
||||
// useComponentWebsocket,
|
||||
// useDeviceAPI,
|
||||
useDeviceAPI,
|
||||
// useDeviceWebsocket,
|
||||
// useFirmwareAPI,
|
||||
// useGitlab,
|
||||
|
|
|
|||
26
src/models/Backpack.ts
Normal file
26
src/models/Backpack.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { pond } from "protobuf-ts/pond";
|
||||
import { or } from "utils/types";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
export class Backpack {
|
||||
public settings: pond.BackpackSettings = pond.BackpackSettings.create();
|
||||
public static create(pb?: pond.Backpack): Backpack {
|
||||
let my = new Backpack();
|
||||
if (pb) {
|
||||
my.settings = pond.BackpackSettings.fromObject(cloneDeep(or(pb.settings, {})));
|
||||
}
|
||||
return my;
|
||||
}
|
||||
|
||||
public static any(data: any): Backpack {
|
||||
return Backpack.create(pond.Backpack.fromObject(cloneDeep(data)));
|
||||
}
|
||||
|
||||
public id(): number {
|
||||
return Number(this.settings.backpackId);
|
||||
}
|
||||
|
||||
public name(): string {
|
||||
return this.settings.name !== "" ? this.settings.name : "Device Profile " + this.id();
|
||||
}
|
||||
}
|
||||
64
src/models/Device.ts
Normal file
64
src/models/Device.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { pond } from "protobuf-ts/pond";
|
||||
import { or } from "utils/types";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { MarkerData } from "Maps/mapMarkers/Markers";
|
||||
|
||||
export class Device {
|
||||
public settings: pond.DeviceSettings = pond.DeviceSettings.create();
|
||||
public status: pond.DeviceStatus = pond.DeviceStatus.create();
|
||||
|
||||
public static create(pb?: pond.Device): Device {
|
||||
let my = new Device();
|
||||
if (pb) {
|
||||
my.settings = pond.DeviceSettings.fromObject(or(pb.settings, {}));
|
||||
my.status = pond.DeviceStatus.fromObject(or(pb.status, {}));
|
||||
}
|
||||
return my;
|
||||
}
|
||||
|
||||
public static any(data: any): Device {
|
||||
if (data.device) return Device.create(pond.Device.fromObject(cloneDeep(data.device)));
|
||||
return Device.create(pond.Device.fromObject(cloneDeep(data)));
|
||||
}
|
||||
|
||||
public static clone(other: Device): Device {
|
||||
let my = new Device();
|
||||
my.settings = pond.DeviceSettings.fromObject(cloneDeep(other.settings));
|
||||
my.status = pond.DeviceStatus.fromObject(cloneDeep(other.status));
|
||||
return my;
|
||||
}
|
||||
|
||||
public id(): number {
|
||||
return Number(this.settings.deviceId);
|
||||
}
|
||||
|
||||
public name(): string {
|
||||
return this.settings.name !== ""
|
||||
? this.settings.name
|
||||
: this.id() > 0
|
||||
? "Device " + this.id()
|
||||
: "";
|
||||
}
|
||||
|
||||
public location(): pond.Location {
|
||||
let loc = pond.Location.create();
|
||||
loc.longitude = this.settings.longitude;
|
||||
loc.latitude = this.settings.latitude;
|
||||
return loc;
|
||||
}
|
||||
|
||||
public getMarkerData(
|
||||
clickFunc?: (event: React.PointerEvent<HTMLElement>, index: number, isMobile: boolean) => void,
|
||||
updateFunc?: (location: pond.Location) => void
|
||||
): MarkerData {
|
||||
let m: MarkerData = {
|
||||
longitude: this.location()?.longitude ?? 0,
|
||||
latitude: this.location()?.latitude ?? 0,
|
||||
title: this.name(),
|
||||
colour: this.settings.theme?.color ?? "blue",
|
||||
clickFunc: clickFunc,
|
||||
updateFunc: updateFunc
|
||||
};
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
// export * from "./Backpack";
|
||||
export * from "./Backpack";
|
||||
// export * from "./Bin";
|
||||
// export * from "./BinYard";
|
||||
// export * from "./Component";
|
||||
// export * from "./Device";
|
||||
export * from "./Device";
|
||||
// export * from "./Firmware";
|
||||
// export * from "./Group";
|
||||
// export * from "./Interaction";
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import BottomNavigator from "./BottomNavigator";
|
|||
import TeamPage from "pages/Team";
|
||||
import Header from "app/Header";
|
||||
import Logout from "pages/Logout";
|
||||
import Devices from "pages/Devices";
|
||||
|
||||
interface Props {
|
||||
open: boolean,
|
||||
|
|
@ -70,6 +71,7 @@ export default function Router(props: Props) {
|
|||
<Route path="/teams" element={<Teams/>} />
|
||||
<Route path="/teams/:teamID" element={<TeamPage/>} />
|
||||
<Route path="/users" element={<Users/>} />
|
||||
<Route path="/devices" element={<Devices/>} />
|
||||
{/* <Route
|
||||
path="/teams/:teamID"
|
||||
element={<Team />}
|
||||
|
|
|
|||
42
src/pages/Devices.tsx
Normal file
42
src/pages/Devices.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { DeveloperBoard as ProvisionIcon } from "@mui/icons-material";
|
||||
import { IconButton, Theme, Tooltip } from "@mui/material";
|
||||
import { blue } from "@mui/material/colors";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import ProvisionDevice from "device/ProvisionDevice";
|
||||
import { useState } from "react";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
return ({
|
||||
provisionIcon: {
|
||||
color: blue["700"]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export default function Devices() {
|
||||
const classes = useStyles();
|
||||
const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false);
|
||||
|
||||
const openProvisionDialog = () => {
|
||||
setIsProvisionDialogOpen(true);
|
||||
};
|
||||
|
||||
const closeProvisionDialog = () => {
|
||||
setIsProvisionDialogOpen(false);
|
||||
};
|
||||
|
||||
return(
|
||||
<>
|
||||
<Tooltip title="Provision Device">
|
||||
<IconButton onClick={openProvisionDialog} aria-label="Provision Device">
|
||||
<ProvisionIcon className={classes.provisionIcon} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<ProvisionDevice
|
||||
isOpen={isProvisionDialogOpen}
|
||||
refreshCallback={/*load*/()=>{}}
|
||||
closeDialogCallback={closeProvisionDialog}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
25
src/pbHelpers/Backpack.ts
Normal file
25
src/pbHelpers/Backpack.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { Backpack } from "models";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { Option } from "common/SearchSelect";
|
||||
|
||||
export function backpackOptions(backpacks: Backpack[]): Option[] {
|
||||
return backpacks.map(b => {
|
||||
return { value: b.settings.backpackId, label: b.name() } as Option;
|
||||
});
|
||||
}
|
||||
|
||||
export function genericBackpacks(): Backpack[] {
|
||||
let basic = pond.Backpack.fromObject({
|
||||
settings: {
|
||||
backpackId: 0,
|
||||
name: "Basic",
|
||||
device: pond.DeviceSettings.fromObject({
|
||||
name: "Basic",
|
||||
pondCheckPeriodS: 60,
|
||||
upgradeChannel: pond.UpgradeChannel.UPGRADE_CHANNEL_STABLE,
|
||||
automaticallyUpgrade: false
|
||||
})
|
||||
}
|
||||
});
|
||||
return [Backpack.create(basic)];
|
||||
}
|
||||
116
src/pbHelpers/DevicePlatform.tsx
Normal file
116
src/pbHelpers/DevicePlatform.tsx
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import React from "react";
|
||||
import {
|
||||
DeviceUnknown,
|
||||
SignalCellular4Bar,
|
||||
SignalWifi4Bar,
|
||||
SignalCellularOff,
|
||||
SignalWifiOff
|
||||
} from "@mui/icons-material";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
|
||||
export interface DevicePlatformDescriber {
|
||||
platform: pond.DevicePlatform;
|
||||
label: string;
|
||||
description: string;
|
||||
icon: any;
|
||||
offlineIcon: any;
|
||||
platformName: string;
|
||||
}
|
||||
|
||||
const Default: DevicePlatformDescriber = {
|
||||
platform: pond.DevicePlatform.DEVICE_PLATFORM_INVALID,
|
||||
label: "Unknown",
|
||||
description: "Unknown communication platform",
|
||||
icon: <DeviceUnknown style={{ color: "#fff" }} />,
|
||||
offlineIcon: <DeviceUnknown style={{ color: "#fff" }} />,
|
||||
platformName: "unknown"
|
||||
};
|
||||
|
||||
const Photon: DevicePlatformDescriber = {
|
||||
platform: pond.DevicePlatform.DEVICE_PLATFORM_PHOTON,
|
||||
label: "Wi-Fi",
|
||||
description: "Communicates via Wi-Fi",
|
||||
icon: <SignalWifi4Bar style={{ color: "#fff" }} />,
|
||||
offlineIcon: <SignalWifiOff style={{ color: "#fff" }} />,
|
||||
platformName: "photon"
|
||||
};
|
||||
|
||||
const Electron: DevicePlatformDescriber = {
|
||||
platform: pond.DevicePlatform.DEVICE_PLATFORM_ELECTRON,
|
||||
label: "Cellular",
|
||||
description: "Communicates via cellular",
|
||||
icon: <SignalCellular4Bar style={{ color: "#fff" }} />,
|
||||
offlineIcon: <SignalCellularOff style={{ color: "#fff" }} />,
|
||||
platformName: "electron"
|
||||
};
|
||||
|
||||
const V2_Cellular: DevicePlatformDescriber = {
|
||||
platform: pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR,
|
||||
label: "V2 Cellular",
|
||||
description: "Communicates via cellular",
|
||||
icon: <SignalCellular4Bar style={{ color: "#fff" }} />,
|
||||
offlineIcon: <SignalCellularOff style={{ color: "#fff" }} />,
|
||||
platformName: "v2cellular"
|
||||
};
|
||||
|
||||
const V2_Wifi_S3: DevicePlatformDescriber = {
|
||||
platform: pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_S3,
|
||||
label: "V2 Wifi S3",
|
||||
description: "Communicates via cellular",
|
||||
icon: <SignalCellular4Bar style={{ color: "#fff" }} />,
|
||||
offlineIcon: <SignalCellularOff style={{ color: "#fff" }} />,
|
||||
platformName: "v2wifis3"
|
||||
};
|
||||
|
||||
const V2_Cell_Black: DevicePlatformDescriber = {
|
||||
platform: pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLACK,
|
||||
label: "V2 Cellular Black",
|
||||
description: "Communicates via cellular",
|
||||
icon: <SignalCellular4Bar style={{ color: "#fff" }} />,
|
||||
offlineIcon: <SignalCellularOff style={{ color: "#fff" }} />,
|
||||
platformName: "v2cellblack"
|
||||
};
|
||||
|
||||
const V2_Cell_Green: DevicePlatformDescriber = {
|
||||
platform: pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_GREEN,
|
||||
label: "V2 Cellular Green",
|
||||
description: "Communicates via cellular",
|
||||
icon: <SignalCellular4Bar style={{ color: "#fff" }} />,
|
||||
offlineIcon: <SignalCellularOff style={{ color: "#fff" }} />,
|
||||
platformName: "v2cellgreen"
|
||||
};
|
||||
|
||||
const map = new Map<pond.DevicePlatform, DevicePlatformDescriber>([
|
||||
[pond.DevicePlatform.DEVICE_PLATFORM_INVALID, Default],
|
||||
[pond.DevicePlatform.DEVICE_PLATFORM_PHOTON, Photon],
|
||||
[pond.DevicePlatform.DEVICE_PLATFORM_ELECTRON, Electron],
|
||||
[pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR, V2_Cellular],
|
||||
[pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_S3, V2_Wifi_S3],
|
||||
[pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLACK, V2_Cell_Black],
|
||||
[pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_GREEN, V2_Cell_Green]
|
||||
]);
|
||||
|
||||
export function getDevicePlatformDescriber(platform: pond.DevicePlatform): DevicePlatformDescriber {
|
||||
const describer = map.get(platform);
|
||||
return describer ? describer : Default;
|
||||
}
|
||||
|
||||
export function getDevicePlatformLabel(platform: pond.DevicePlatform): string {
|
||||
return getDevicePlatformDescriber(platform).label;
|
||||
}
|
||||
|
||||
export function getDevicePlatformDescription(platform: pond.DevicePlatform): string {
|
||||
return getDevicePlatformDescriber(platform).description;
|
||||
}
|
||||
|
||||
export function getDevicePlatformIcon(platform: pond.DevicePlatform): any {
|
||||
return getDevicePlatformDescriber(platform).icon;
|
||||
}
|
||||
|
||||
export function getDevicePlatformName(platform: pond.DevicePlatform): string {
|
||||
return getDevicePlatformDescriber(platform).platformName;
|
||||
}
|
||||
|
||||
export function GetAllDevicePlatformDescribers(): DevicePlatformDescriber[] {
|
||||
return [...map.values()].filter(p => p.platform !== pond.DevicePlatform.DEVICE_PLATFORM_INVALID);
|
||||
}
|
||||
|
|
@ -3,14 +3,14 @@
|
|||
// export { GitlabContext, useGitlab } from "./gitlab";
|
||||
export { HTTPContext, useHTTP } from "./http";
|
||||
export {
|
||||
// useBackpackAPI,
|
||||
useBackpackAPI,
|
||||
useBinAPI,
|
||||
// useBinYardAPI,
|
||||
useNoteAPI,
|
||||
// useComponentAPI,
|
||||
// useComponentsWebsocket,
|
||||
// useComponentWebsocket,
|
||||
// useDeviceAPI,
|
||||
useDeviceAPI,
|
||||
// useDeviceWebsocket,
|
||||
// useFieldAPI,
|
||||
// useFieldMarkerAPI,
|
||||
|
|
|
|||
59
src/providers/pond/backpackAPI.tsx
Normal file
59
src/providers/pond/backpackAPI.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { useHTTP } from "hooks";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { createContext, PropsWithChildren, useContext } from "react";
|
||||
import { pondURL } from "./pond";
|
||||
|
||||
export interface IBackpackAPI {
|
||||
addBackpack: (backpack: pond.Backpack) => Promise<any>;
|
||||
updateBackpack: (backpackID: number | Long, backpack: pond.Backpack) => Promise<any>;
|
||||
removeBackpack: (backpackID: number | Long) => Promise<any>;
|
||||
getBackpack: (backpackID: number | Long) => Promise<any>;
|
||||
listBackpacks: () => Promise<any>;
|
||||
}
|
||||
|
||||
export const BackpackAPIContext = createContext<IBackpackAPI>({} as IBackpackAPI);
|
||||
|
||||
export default function BackpackProvider(props: PropsWithChildren<any>) {
|
||||
const { children } = props;
|
||||
const { get, post, put, del } = useHTTP();
|
||||
|
||||
const addBackpack = (backpack: pond.Backpack) => {
|
||||
const url = pondURL("/backpacks");
|
||||
return post(url, backpack);
|
||||
};
|
||||
|
||||
const updateBackpack = (backpackID: number | Long, backpack: pond.Backpack) => {
|
||||
const url = pondURL("/backpacks/" + backpackID);
|
||||
return put(url, backpack);
|
||||
};
|
||||
|
||||
const removeBackpack = (backpackID: number | Long) => {
|
||||
const url = pondURL("/backpacks/" + backpackID);
|
||||
return del(url);
|
||||
};
|
||||
|
||||
const getBackpack = (backpackID: number | Long) => {
|
||||
const url = pondURL("/backpacks/" + backpackID);
|
||||
return get(url);
|
||||
};
|
||||
|
||||
const listBackpacks = () => {
|
||||
const url = pondURL("/backpacks");
|
||||
return get(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<BackpackAPIContext.Provider
|
||||
value={{
|
||||
addBackpack,
|
||||
updateBackpack,
|
||||
removeBackpack,
|
||||
getBackpack,
|
||||
listBackpacks
|
||||
}}>
|
||||
{children}
|
||||
</BackpackAPIContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useBackpackAPI = () => useContext(BackpackAPIContext);
|
||||
668
src/providers/pond/deviceAPI.tsx
Normal file
668
src/providers/pond/deviceAPI.tsx
Normal file
|
|
@ -0,0 +1,668 @@
|
|||
import { useHTTP, usePermissionAPI } from "hooks";
|
||||
// import { useWebsocket } from "websocket";
|
||||
import { /*Component,*/ deviceScope, User } from "models";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { createContext, PropsWithChildren, useContext } from "react";
|
||||
import { pondURL } from "./pond";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { useGlobalState } from "providers";
|
||||
import { dateRange } from "providers/http";
|
||||
import moment from "moment";
|
||||
|
||||
export interface IDeviceAPIContext {
|
||||
add: (name: string, description: string, backpack: pond.BackpackSettings) => Promise<any>;
|
||||
update: (id: number, settings: pond.DeviceSettings) => Promise<any>;
|
||||
remove: (id: number) => Promise<any>;
|
||||
get: (id: number | string, demo?: boolean, keys?: string[], types?: string[]) => Promise<any>;
|
||||
getPageData: (
|
||||
id: number | string,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => Promise<AxiosResponse<pond.GetDevicePageDataResponse>>;
|
||||
getMulti: (ids: number[] | string[]) => Promise<any>;
|
||||
getGeoJson: (id: number | string, demo?: boolean) => Promise<any>;
|
||||
getMultiGeoJson: (ids: number[] | string[]) => Promise<any>;
|
||||
list: (
|
||||
limit: number,
|
||||
offset: number,
|
||||
order?: "asc" | "desc",
|
||||
orderBy?: string,
|
||||
search?: string,
|
||||
settings?: string,
|
||||
status?: string,
|
||||
ids?: number[] | string[] | Long[],
|
||||
asRoot?: boolean,
|
||||
comprehensive?: boolean,
|
||||
withMeasurements?: boolean,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => Promise<AxiosResponse<pond.ListDevicesResponse>>;
|
||||
listForUser: (
|
||||
asRoot: boolean,
|
||||
user?: string,
|
||||
search?: string,
|
||||
hologram?: boolean,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => Promise<AxiosResponse<pond.ListDevicesResponse>>;
|
||||
claim: (id: number | Long, body: any) => Promise<any>;
|
||||
listHistory: (id: number, limit: number, offset: number) => Promise<any>;
|
||||
listCompleteHistory: (
|
||||
id: number,
|
||||
limit: number,
|
||||
offset: number,
|
||||
filters?: pond.ObjectType[]
|
||||
) => Promise<AxiosResponse<pond.ListCompleteHistoryResponse>>;
|
||||
updatePermissions: (id: number | string, users: User[]) => Promise<any>;
|
||||
updatePreferences: (
|
||||
id: number | string,
|
||||
preferences: pond.UserPreferences,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => Promise<any>;
|
||||
updateComponentPreferences: (
|
||||
id: number | string,
|
||||
component: string,
|
||||
preferences: pond.DeviceComponentPreferences,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => Promise<any>;
|
||||
listDeviceComponentPreferences: (
|
||||
id: number | string,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => Promise<AxiosResponse<pond.ListDeviceComponentPreferencesResponse>>;
|
||||
updateStatus: (
|
||||
id: number | string,
|
||||
status: pond.DeviceStatus,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => Promise<any>;
|
||||
sync: (id: number) => Promise<any>;
|
||||
clearPending: (id: number) => Promise<any>;
|
||||
pause: (id: number) => Promise<any>;
|
||||
bulkPause: (
|
||||
ids: number[],
|
||||
setPaused: boolean
|
||||
) => Promise<AxiosResponse<pond.BulkPauseDeviceResponse>>;
|
||||
bulkChangeDataCaps: (
|
||||
ids: number[],
|
||||
newCap: number
|
||||
) => Promise<AxiosResponse<pond.BulkChangeDataCapsResponse>>;
|
||||
resume: (id: number) => Promise<any>;
|
||||
getUpgradeStatus: (id: number, keys?: string[], types?: string[]) => Promise<any>;
|
||||
loadBackpack: (id: number, backpack: number) => Promise<any>;
|
||||
setTags: (id: number, tags: string[]) => Promise<any>;
|
||||
setWifi: (
|
||||
id: number,
|
||||
gateway: string,
|
||||
password: string,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => Promise<any>;
|
||||
tag: (id: number, tag: string) => Promise<any>;
|
||||
untag: (id: number, tag: string) => Promise<any>;
|
||||
getDatacap: (id: number) => Promise<any>;
|
||||
isOverLimit: (id: number) => Promise<any>;
|
||||
isPaused: (id: number) => Promise<any>;
|
||||
setDatacap: (id: number, newCap: number) => Promise<any>;
|
||||
// listJSONMeasurements: (
|
||||
// id: number,
|
||||
// components: Component[],
|
||||
// startDate: any,
|
||||
// endDate: any,
|
||||
// limit: number,
|
||||
// offset: number,
|
||||
// order: string,
|
||||
// orderBy: string
|
||||
// ) => Promise<AxiosResponse<pond.ListDeviceExportedMeasurementsResponse>>;
|
||||
listSimpleJSON: (device: number) => Promise<any>;
|
||||
resetQuackCount: (id: number) => Promise<any>;
|
||||
resetQuackCountTx: (id: number) => Promise<any>;
|
||||
resetQuackCountTx1000: (id: number) => Promise<any>;
|
||||
linearMutation: (
|
||||
id: number,
|
||||
mutation: pond.LinearMutation
|
||||
) => Promise<AxiosResponse<pond.DeviceLinearMutationResponse>>;
|
||||
getDataUsage: (id: number, start?: any) => Promise<any>;
|
||||
buyData: (
|
||||
id: number | string,
|
||||
MB: number,
|
||||
source?: string,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => Promise<any>;
|
||||
}
|
||||
|
||||
export const DeviceAPIContext = createContext<IDeviceAPIContext>({} as IDeviceAPIContext);
|
||||
|
||||
interface Props {}
|
||||
|
||||
export default function DeviceProvider(props: PropsWithChildren<Props>) {
|
||||
const { children } = props;
|
||||
const { get, post, put, del } = useHTTP();
|
||||
const permissionAPI = usePermissionAPI();
|
||||
const [{ as, team }] = useGlobalState();
|
||||
|
||||
const add = (name: string, description: string, backpack: pond.BackpackSettings) => {
|
||||
if (as) return post(pondURL("/devices?as=" + as), { name, description, backpack });
|
||||
return post(pondURL("/devices"), { name, description, backpack });
|
||||
};
|
||||
|
||||
const getDevice = (
|
||||
id: number | string,
|
||||
demo: boolean = false,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => {
|
||||
if (as && !(types && types.length > 0 && types[0] === "team"))
|
||||
return get(
|
||||
pondURL(
|
||||
"/devices/" +
|
||||
id +
|
||||
"?as=" +
|
||||
as +
|
||||
(keys ? "&keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : ""),
|
||||
demo
|
||||
)
|
||||
);
|
||||
const url = pondURL(
|
||||
"/devices/" +
|
||||
id +
|
||||
(keys ? "?keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : ""),
|
||||
demo
|
||||
);
|
||||
return get(url);
|
||||
};
|
||||
|
||||
const getDevicePageData = (
|
||||
id: number | string,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
): Promise<AxiosResponse<pond.GetDevicePageDataResponse>> => {
|
||||
if (as && !(types && types.length > 0 && types[0] === "team"))
|
||||
return get(
|
||||
pondURL(
|
||||
"/devicePageData/" +
|
||||
id +
|
||||
"?as=" +
|
||||
as +
|
||||
(keys ? "&keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : "")
|
||||
)
|
||||
);
|
||||
const url = pondURL(
|
||||
"/devicePageData/" +
|
||||
id +
|
||||
(keys ? "?keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : "")
|
||||
);
|
||||
return get<pond.GetDevicePageDataResponse>(url);
|
||||
};
|
||||
|
||||
const getDeviceGeoJSON = (id: number | string, demo: boolean = false) => {
|
||||
if (as) return get(pondURL("/devices/" + id + "/geojson?as=" + as, demo));
|
||||
const url = pondURL("/devices/" + id + "/geojson", demo);
|
||||
return get(url);
|
||||
};
|
||||
|
||||
const getMulti = (ids: number[] | string[]) => {
|
||||
let idString = "";
|
||||
ids.forEach((id: number | string, i: number) => {
|
||||
if (i === 0) {
|
||||
idString = idString + id;
|
||||
} else {
|
||||
idString = idString + "," + id;
|
||||
}
|
||||
});
|
||||
if (as) return get(pondURL("/multidevices?devices=" + idString + "&as=" + as));
|
||||
return get(pondURL("/multidevices?devices=" + idString));
|
||||
};
|
||||
|
||||
const getMultiDevicesGeoJSON = (ids: number[] | string[]) => {
|
||||
let idString = "";
|
||||
ids.forEach((id: number | string, i: number) => {
|
||||
if (i === 0) {
|
||||
idString = idString + id;
|
||||
} else {
|
||||
idString = idString + "," + id;
|
||||
}
|
||||
});
|
||||
if (as) return get(pondURL("/geojson/devices?devices=" + idString + "&as=" + as));
|
||||
return get(pondURL("/geojson/devices?devices=" + idString));
|
||||
};
|
||||
|
||||
const list = (
|
||||
limit: number,
|
||||
offset: number,
|
||||
order?: "asc" | "desc",
|
||||
orderBy?: string,
|
||||
search?: string,
|
||||
settings?: string,
|
||||
status?: string,
|
||||
ids?: number[] | string[] | Long[],
|
||||
asRoot?: boolean,
|
||||
comprehensive?: boolean,
|
||||
withMeasurements?: boolean,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => {
|
||||
const url = pondURL(
|
||||
"/devices" +
|
||||
"?limit=" +
|
||||
limit +
|
||||
"&offset=" +
|
||||
offset +
|
||||
("&order=" + (order ? order : "asc")) +
|
||||
("&by=" + (orderBy ? orderBy : "deviceId")) +
|
||||
(search ? "&search=" + search : "") +
|
||||
(settings ? "&settings=" + settings : "") +
|
||||
(status ? "&status=" + status : "") +
|
||||
(ids ? "&ids=" + ids.join(",") : "") +
|
||||
(asRoot ? "&asRoot=" + asRoot.toString() : "") +
|
||||
(as && !(types && types.length > 0 && types[0] === "team") ? "&as=" + as : "") +
|
||||
(comprehensive ? "&comprehensive=" + comprehensive.toString() : "") +
|
||||
(withMeasurements ? "&measurements=" + withMeasurements.toString() : "") +
|
||||
(keys ? "&keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : "")
|
||||
);
|
||||
return get<pond.ListDevicesResponse>(url);
|
||||
};
|
||||
|
||||
const listForUser = (
|
||||
asRoot: boolean,
|
||||
user?: string,
|
||||
search?: string,
|
||||
hologram?: boolean,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => {
|
||||
const url = pondURL(
|
||||
"/devices" +
|
||||
(user ? "?as=" + user : "?asRoot=" + asRoot.toString()) +
|
||||
(search ? "&search=" + search : "") +
|
||||
(hologram ? "&hologram=" + hologram.toString() : "") +
|
||||
(keys ? "&keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : "")
|
||||
);
|
||||
return get<pond.ListDevicesResponse>(url);
|
||||
};
|
||||
|
||||
const remove = (id: number) => {
|
||||
if (as) return del(pondURL("/devices/" + id + "?as=" + as));
|
||||
const url = pondURL("/devices/" + id);
|
||||
return del(url);
|
||||
};
|
||||
|
||||
const update = (id: number, settings: pond.DeviceSettings) => {
|
||||
if (as) return put(pondURL(`/devices/${id}/update?as=${as}`), settings);
|
||||
const url = pondURL("/devices/" + id + "/update");
|
||||
return put(url, settings);
|
||||
};
|
||||
|
||||
const claim = (id: number | Long, body: any) => {
|
||||
return put(pondURL("/devices/" + id), body);
|
||||
};
|
||||
|
||||
const listHistory = (id: number, limit: number, offset: number) => {
|
||||
return get(pondURL("/devices/" + id + "/history?limit=" + limit + "&offset=" + offset));
|
||||
};
|
||||
const listCompleteHistory = (
|
||||
id: number,
|
||||
limit: number,
|
||||
offset: number,
|
||||
filters?: pond.ObjectType[]
|
||||
) => {
|
||||
return get<pond.ListCompleteHistoryResponse>(
|
||||
pondURL(
|
||||
"/devices/" +
|
||||
id +
|
||||
"/completehistory?limit=" +
|
||||
limit +
|
||||
"&offset=" +
|
||||
offset +
|
||||
(filters ? "&sources=" + filters.toString() : "")
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const updateDevicePermissions = (id: number | string, users: User[]) => {
|
||||
return permissionAPI.updatePermissions(deviceScope(id.toString()), users);
|
||||
};
|
||||
|
||||
const updatePreferences = (
|
||||
id: number | string,
|
||||
preferences: pond.UserPreferences,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => {
|
||||
return put(
|
||||
pondURL(
|
||||
"/devices/" +
|
||||
id +
|
||||
"/preferences" +
|
||||
"?as=" +
|
||||
as +
|
||||
(keys ? "&keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : "")
|
||||
),
|
||||
preferences
|
||||
);
|
||||
};
|
||||
|
||||
const updateComponentPreferences = (
|
||||
id: number | string,
|
||||
component: string,
|
||||
preferences: pond.DeviceComponentPreferences,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => {
|
||||
return put(
|
||||
pondURL(
|
||||
"/devices/" +
|
||||
id +
|
||||
"/components/" +
|
||||
component +
|
||||
"/componentPreferences" +
|
||||
"?as=" +
|
||||
as +
|
||||
(keys ? "&keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : "")
|
||||
),
|
||||
preferences
|
||||
);
|
||||
};
|
||||
|
||||
const listDeviceComponentPreferences = (
|
||||
id: number | string,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => {
|
||||
return get<pond.ListDeviceComponentPreferencesResponse>(
|
||||
pondURL(
|
||||
"/devices/" +
|
||||
id +
|
||||
"/componentPreferences?as=" +
|
||||
as +
|
||||
(keys ? "&keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : "")
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const updateStatus = (
|
||||
id: number | string,
|
||||
status: pond.DeviceStatus,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => {
|
||||
return put(
|
||||
pondURL(
|
||||
"/devices/" +
|
||||
id +
|
||||
"/updateStatus" +
|
||||
"?as=" +
|
||||
as +
|
||||
(keys ? "&keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : "")
|
||||
),
|
||||
status
|
||||
);
|
||||
};
|
||||
|
||||
const sync = (id: number) => {
|
||||
return post(pondURL("/devices/" + id + "/sync"), {});
|
||||
};
|
||||
|
||||
const clearPending = (id: number) => {
|
||||
return post(pondURL("/devices/" + id + "/clearPending"), {});
|
||||
};
|
||||
|
||||
const pause = (id: number) => {
|
||||
return post(pondURL("/devices/" + id + "/pause"), {});
|
||||
};
|
||||
|
||||
const bulkPause = (ids: number[], setPaused: boolean) => {
|
||||
return post<pond.BulkPauseDeviceResponse>(
|
||||
pondURL(
|
||||
"/bulkPauseDevices?ids=" +
|
||||
ids.toString() +
|
||||
(setPaused ? "&pause=" + setPaused.toString() : "")
|
||||
),
|
||||
{}
|
||||
);
|
||||
};
|
||||
|
||||
const bulkChangeDataCaps = (ids: number[], newCap: number) => {
|
||||
return post<pond.BulkChangeDataCapsResponse>(
|
||||
pondURL("/bulkChangeDataCap?ids=" + ids.toString() + "&dataCap=" + newCap),
|
||||
{}
|
||||
);
|
||||
};
|
||||
|
||||
const resume = (id: number) => {
|
||||
return post(pondURL("/devices/" + id + "/resume"), {});
|
||||
};
|
||||
|
||||
const getUpgradeStatus = (id: number, keys?: string[], types?: string[]) => {
|
||||
let a = as ? "?as=" + as : "";
|
||||
if (keys && keys.length > 0 && types && types.length > 0) {
|
||||
a = as ? a + "&keys=" + keys + "&types=" + types : "?keys=" + keys + "&types=" + types;
|
||||
}
|
||||
let url = pondURL("/devices/" + id + "/firmware" + a);
|
||||
return get(url);
|
||||
};
|
||||
|
||||
const loadBackpack = (id: number, backpack: number) => {
|
||||
return put(pondURL("/devices/" + id + "/load/" + backpack), {});
|
||||
};
|
||||
|
||||
const setTags = (id: number, tags: string[]) => {
|
||||
return put(pondURL("/devices/" + id + "/tags"), { tags });
|
||||
};
|
||||
|
||||
const setWifi = (
|
||||
id: number,
|
||||
gateway: string,
|
||||
password: string,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => {
|
||||
let a = as ? "?as=" + as : "";
|
||||
if (keys && keys.length > 0 && types && types.length > 0) {
|
||||
a = as ? a + "&keys=" + keys + "&types=" + types : "?keys=" + keys + "&types=" + types;
|
||||
}
|
||||
return put(pondURL("/devices/" + id + "/wifi" + a), { gateway, password });
|
||||
};
|
||||
|
||||
const tag = (id: number, tag: string) => {
|
||||
return put(pondURL("/devices/" + id + "/tags/" + tag), {});
|
||||
};
|
||||
|
||||
const untag = (id: number, tag: string) => {
|
||||
return del(pondURL("/devices/" + id + "/tags/" + tag));
|
||||
};
|
||||
|
||||
const getDatacap = (id: number) => {
|
||||
return get(pondURL("/devices/" + id + "/datacap/"));
|
||||
};
|
||||
|
||||
const isOverLimit = (id: number) => {
|
||||
return get(pondURL("/devices/" + id + "/overlimit/"));
|
||||
};
|
||||
|
||||
const isPaused = (id: number) => {
|
||||
return get(pondURL("/devices/" + id + "/paused/"));
|
||||
};
|
||||
|
||||
const setDatacap = (id: number, newCap: number) => {
|
||||
return post(pondURL("/devices/" + id + "/datacap?limit=" + newCap));
|
||||
};
|
||||
|
||||
const getDataUsage = (id: number, start?: any) => {
|
||||
if (start) {
|
||||
start = "?start=" + moment(start).toISOString();
|
||||
} else {
|
||||
start = "";
|
||||
}
|
||||
return get(pondURL("/devices/" + id + "/usage/" + start));
|
||||
};
|
||||
|
||||
// const listJSONMeasurements = (
|
||||
// id: number,
|
||||
// components: Component[],
|
||||
// startDate: any,
|
||||
// endDate: any,
|
||||
// limit: number,
|
||||
// offset: number,
|
||||
// order: string,
|
||||
// orderBy: string
|
||||
// ) => {
|
||||
// let keyString = "";
|
||||
// components.forEach((comp, i) => {
|
||||
// if (i === 0) {
|
||||
// keyString = keyString + comp.key();
|
||||
// } else {
|
||||
// keyString = keyString + "," + comp.key();
|
||||
// }
|
||||
// });
|
||||
// if (as) {
|
||||
// return get<pond.ListDeviceExportedMeasurementsResponse>(
|
||||
// pondURL(
|
||||
// "/devices/" +
|
||||
// id +
|
||||
// "/measurements/exportComplex" +
|
||||
// dateRange(startDate, endDate) +
|
||||
// "&components=" +
|
||||
// keyString +
|
||||
// "&limit=" +
|
||||
// limit +
|
||||
// "&offset=" +
|
||||
// offset +
|
||||
// "&order=" +
|
||||
// order +
|
||||
// "&orderBy=" +
|
||||
// orderBy +
|
||||
// "&as=" +
|
||||
// as
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
// return get<pond.ListDeviceExportedMeasurementsResponse>(
|
||||
// pondURL(
|
||||
// "/devices/" +
|
||||
// id +
|
||||
// "/measurements/exportComplex" +
|
||||
// dateRange(startDate, endDate) +
|
||||
// "&components=" +
|
||||
// keyString +
|
||||
// "&limit=" +
|
||||
// limit +
|
||||
// "&offset=" +
|
||||
// offset +
|
||||
// "&order=" +
|
||||
// order +
|
||||
// "&orderBy=" +
|
||||
// orderBy
|
||||
// )
|
||||
// );
|
||||
// };
|
||||
|
||||
const listSimpleJSON = (device: number) => {
|
||||
return get(pondURL("/devices/" + device + "/measurements/exportSimple"));
|
||||
};
|
||||
|
||||
const resetQuackCount = (id: number) => {
|
||||
return post(pondURL("/devices/" + id + "/resetCounts"));
|
||||
};
|
||||
|
||||
const resetQuackCountTx = (id: number) => {
|
||||
return post(pondURL("/devices/" + id + "/resetCountTx"));
|
||||
};
|
||||
|
||||
const resetQuackCountTx1000 = (id: number) => {
|
||||
return post(pondURL("/devices/" + id + "/partialResetCountTx"));
|
||||
};
|
||||
|
||||
const linearMutation = (id: number, mutation: pond.LinearMutation) => {
|
||||
return post<pond.DeviceLinearMutationResponse>(
|
||||
pondURL("/devices/" + id + "/linearMutation"),
|
||||
mutation
|
||||
);
|
||||
};
|
||||
|
||||
const buyData = (
|
||||
id: number | string,
|
||||
MB: number,
|
||||
source?: string,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => {
|
||||
let url = "/devices/" + id + "/buyData?MB=" + MB;
|
||||
if (as === team.key()) url = url + "&team=" + team.key();
|
||||
if (source) url = url + "&source=" + source;
|
||||
if (keys) url = url + "&keys=" + keys;
|
||||
if (types) url = url + "&types=" + types;
|
||||
return put(pondURL(url));
|
||||
};
|
||||
|
||||
return (
|
||||
<DeviceAPIContext.Provider
|
||||
value={{
|
||||
add,
|
||||
update,
|
||||
remove,
|
||||
get: getDevice,
|
||||
getPageData: getDevicePageData,
|
||||
getMulti,
|
||||
getGeoJson: getDeviceGeoJSON,
|
||||
getMultiGeoJson: getMultiDevicesGeoJSON,
|
||||
list,
|
||||
claim,
|
||||
listHistory,
|
||||
listCompleteHistory,
|
||||
updatePermissions: updateDevicePermissions,
|
||||
updateStatus,
|
||||
updatePreferences,
|
||||
clearPending,
|
||||
sync,
|
||||
pause,
|
||||
bulkPause,
|
||||
bulkChangeDataCaps,
|
||||
resume,
|
||||
getUpgradeStatus,
|
||||
loadBackpack,
|
||||
setTags,
|
||||
setWifi,
|
||||
tag,
|
||||
untag,
|
||||
getDatacap,
|
||||
isOverLimit,
|
||||
isPaused,
|
||||
setDatacap,
|
||||
// listJSONMeasurements,
|
||||
listSimpleJSON,
|
||||
resetQuackCount,
|
||||
resetQuackCountTx,
|
||||
resetQuackCountTx1000,
|
||||
listForUser,
|
||||
linearMutation,
|
||||
getDataUsage,
|
||||
buyData,
|
||||
updateComponentPreferences,
|
||||
listDeviceComponentPreferences
|
||||
}}>
|
||||
{children}
|
||||
</DeviceAPIContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useDeviceAPI = () => useContext(DeviceAPIContext);
|
||||
|
||||
// export const useDeviceWebsocket = (
|
||||
// id: number | string,
|
||||
// emitter: (m: any) => void,
|
||||
// rate: number = 0
|
||||
// ) => useWebsocket("/devices/" + id, m => Device.any(JSON.parse(m.data)), emitter, rate);
|
||||
|
|
@ -7,6 +7,8 @@ import { Scope } from "models";
|
|||
import BinProvider, { useBinAPI } from "./binAPI";
|
||||
import GateProvider, { useGateAPI } from "./gateAPI";
|
||||
import NoteProvider, { useNoteAPI } from "./noteAPI";
|
||||
import DeviceProvider, { useDeviceAPI } from "./deviceAPI";
|
||||
import BackpackProvider, { useBackpackAPI } from "./backpackAPI";
|
||||
// import NoteProvider from "providers/noteAPI";
|
||||
|
||||
export const pondURL = (partial: string, demo: boolean = false): string => {
|
||||
|
|
@ -30,7 +32,11 @@ export default function PondProvider(props: PropsWithChildren<any>) {
|
|||
<BinProvider>
|
||||
<GateProvider>
|
||||
<NoteProvider>
|
||||
{children}
|
||||
<DeviceProvider>
|
||||
<BackpackProvider>
|
||||
{children}
|
||||
</BackpackProvider>
|
||||
</DeviceProvider>
|
||||
</NoteProvider>
|
||||
</GateProvider>
|
||||
</BinProvider>
|
||||
|
|
@ -41,6 +47,8 @@ export default function PondProvider(props: PropsWithChildren<any>) {
|
|||
}
|
||||
|
||||
export {
|
||||
useBackpackAPI,
|
||||
useDeviceAPI,
|
||||
useUserAPI,
|
||||
useTeamAPI,
|
||||
useImagekitAPI,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ function options(themeType: "light" | "dark"): ThemeOptions {
|
|||
const highlight = themeType === "light" ? "black" : "white";
|
||||
const bg = generateBackgroundShades(themeType)
|
||||
return {
|
||||
zIndex: {
|
||||
modal: 1300,
|
||||
popover: 1350
|
||||
},
|
||||
palette: {
|
||||
primary: Colours[getPrimaryColour() as keyof typeof Colours],
|
||||
secondary: Colours[getSecondaryColour() as keyof typeof Colours],
|
||||
|
|
@ -122,7 +126,7 @@ function options(themeType: "light" | "dark"): ThemeOptions {
|
|||
overflowX: "hidden"
|
||||
},
|
||||
root: {
|
||||
zIndex: 1500
|
||||
// zIndex: 1500
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue