WIP config overhaul

This commit is contained in:
Sacha Weatherstone
2023-01-29 16:18:00 +10:00
parent 3b0db3ef38
commit 6340809ab3
15 changed files with 315 additions and 362 deletions
@@ -15,7 +15,7 @@ import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
export const Bluetooth = (): JSX.Element => {
const { config, connection, setConfig } = useDevice();
const { config, setWorkingConfig } = useDevice();
const {
register,
@@ -24,6 +24,7 @@ export const Bluetooth = (): JSX.Element => {
control,
reset
} = useForm<BluetoothValidation>({
mode: "onChange",
defaultValues: config.bluetooth,
resolver: classValidatorResolver(BluetoothValidation)
});
@@ -33,34 +34,14 @@ export const Bluetooth = (): JSX.Element => {
}, [reset, config.bluetooth]);
const onSubmit = handleSubmit((data) => {
if (connection) {
void toast.promise(
connection
.setConfig(
new Protobuf.Config({
payloadVariant: {
case: "bluetooth",
value: data
}
})
)
.then(() =>
setConfig(
new Protobuf.Config({
payloadVariant: {
case: "bluetooth",
value: data
}
})
)
),
{
loading: "Saving...",
success: "Saved Bluetooth Config, Restarting Node",
error: "No response received"
setWorkingConfig(
new Protobuf.Config({
payloadVariant: {
case: "bluetooth",
value: data
}
);
}
})
);
});
const pairingMode = useWatch({
@@ -70,9 +51,7 @@ export const Bluetooth = (): JSX.Element => {
});
return (
<Form
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Controller
name="enabled"
control={control}
+10 -31
View File
@@ -15,7 +15,7 @@ import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
export const Device = (): JSX.Element => {
const { config, connection, setConfig } = useDevice();
const { config, setWorkingConfig } = useDevice();
const {
register,
handleSubmit,
@@ -23,6 +23,7 @@ export const Device = (): JSX.Element => {
control,
reset
} = useForm<DeviceValidation>({
mode: "onChange",
defaultValues: config.device,
resolver: classValidatorResolver(DeviceValidation)
});
@@ -32,40 +33,18 @@ export const Device = (): JSX.Element => {
}, [reset, config.device]);
const onSubmit = handleSubmit((data) => {
if (connection) {
void toast.promise(
connection
.setConfig(
new Protobuf.Config({
payloadVariant: {
case: "device",
value: data
}
})
)
.then(() =>
setConfig(
new Protobuf.Config({
payloadVariant: {
case: "device",
value: data
}
})
)
),
{
loading: "Saving...",
success: "Saved Device Config, Restarting Node",
error: "No response received"
setWorkingConfig(
new Protobuf.Config({
payloadVariant: {
case: "device",
value: data
}
);
}
})
);
});
return (
<Form
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Select
label="Role"
description="What role the device performs on the mesh"
@@ -7,7 +7,7 @@ import { toast } from "react-hot-toast";
import { Input } from "@app/components/form/Input.js";
import { Select } from "@app/components/form/Select.js";
import { Toggle } from "@app/components/form/Toggle.js";
import type { DisplayValidation } from "@app/validation/config/display.js";
import { DisplayValidation } from "@app/validation/config/display.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/providers/useDevice.js";
import { renderOptions } from "@core/utils/selectEnumOptions.js";
@@ -15,7 +15,7 @@ import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
export const Display = (): JSX.Element => {
const { config, connection, setConfig } = useDevice();
const { config, setWorkingConfig } = useDevice();
const {
register,
handleSubmit,
@@ -23,43 +23,24 @@ export const Display = (): JSX.Element => {
reset,
control
} = useForm<DisplayValidation>({
defaultValues: config.display
// resolver: classValidatorResolver(DisplayValidation)
mode: "onChange",
defaultValues: config.display,
resolver: classValidatorResolver(DisplayValidation)
});
// useEffect(() => {
// reset(config.display);
// }, [reset, config.display]);
useEffect(() => {
reset(config.display);
}, [reset, config.display]);
const onSubmit = handleSubmit((data) => {
// if (connection) {
// void toast.promise(
// connection
// .setConfig(
// new Protobuf.Config({
// payloadVariant: {
// case: "display",
// value: data
// }
// })
// )
// .then(() =>
// setConfig(
// new Protobuf.Config({
// payloadVariant: {
// case: "display",
// value: data
// }
// })
// )
// ),
// {
// loading: "Saving...",
// success: "Saved Display Config, Restarting Node",
// error: "No response received"
// }
// );
// }
setWorkingConfig(
new Protobuf.Config({
payloadVariant: {
case: "display",
value: data
}
})
);
});
return (
+10 -31
View File
@@ -16,7 +16,7 @@ import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
export const LoRa = (): JSX.Element => {
const { config, connection, setConfig } = useDevice();
const { config, setWorkingConfig } = useDevice();
const {
register,
@@ -25,6 +25,7 @@ export const LoRa = (): JSX.Element => {
control,
reset
} = useForm<LoRaValidation>({
mode: "onChange",
defaultValues: config.lora,
resolver: classValidatorResolver(LoRaValidation)
});
@@ -40,40 +41,18 @@ export const LoRa = (): JSX.Element => {
}, [reset, config.lora]);
const onSubmit = handleSubmit((data) => {
if (connection) {
void toast.promise(
connection
.setConfig(
new Protobuf.Config({
payloadVariant: {
case: "lora",
value: data
}
})
)
.then(() =>
setConfig(
new Protobuf.Config({
payloadVariant: {
case: "lora",
value: data
}
})
)
),
{
loading: "Saving...",
success: "Saved LoRa Config, Restarting Node",
error: "No response received"
setWorkingConfig(
new Protobuf.Config({
payloadVariant: {
case: "lora",
value: data
}
);
}
})
);
});
return (
<Form
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<FormSection title="Modem Settings">
<Controller
name="usePreset"
@@ -18,7 +18,7 @@ import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
export const Network = (): JSX.Element => {
const { config, connection, setConfig } = useDevice();
const { config, setWorkingConfig } = useDevice();
const {
register,
handleSubmit,
@@ -26,6 +26,7 @@ export const Network = (): JSX.Element => {
control,
reset
} = useForm<NetworkValidation>({
mode: "onChange",
defaultValues: config.network,
resolver: classValidatorResolver(NetworkValidation)
});
@@ -53,40 +54,18 @@ export const Network = (): JSX.Element => {
}, [reset, config.network]);
const onSubmit = handleSubmit((data) => {
if (connection) {
void toast.promise(
connection
.setConfig(
new Protobuf.Config({
payloadVariant: {
case: "network",
value: new Protobuf.Config_NetworkConfig(data)
}
})
)
.then(() =>
setConfig(
new Protobuf.Config({
payloadVariant: {
case: "network",
value: data
}
})
)
),
{
loading: "Saving...",
success: "Saved Network Config, Restarting Node",
error: "No response received"
setWorkingConfig(
new Protobuf.Config({
payloadVariant: {
case: "network",
value: data
}
);
}
})
);
});
return (
<Form
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<ErrorMessage errors={errors} name="wifiEnabled" />
<ErrorMessage errors={errors} name="wifiMode" />
<ErrorMessage errors={errors} name="wifiSsid" />
@@ -190,6 +169,12 @@ export const Network = (): JSX.Element => {
error={errors.ntpServer?.message}
{...register("ntpServer")}
/>
<Input
label="Rsyslog Server"
description="Rsyslog server for external logging"
error={errors.rsyslogServer?.message}
{...register("rsyslogServer")}
/>
</Form>
);
};
@@ -15,7 +15,7 @@ import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
export const Position = (): JSX.Element => {
const { config, connection, nodes, hardware, setConfig } = useDevice();
const { config, nodes, hardware, setWorkingConfig } = useDevice();
const myNode = nodes.find((n) => n.data.num === hardware.myNodeNum);
@@ -26,6 +26,7 @@ export const Position = (): JSX.Element => {
reset,
control
} = useForm<PositionValidation>({
mode: "onChange",
defaultValues: {
fixedAlt: myNode?.data.position?.altitude,
fixedLat: (myNode?.data.position?.latitudeI ?? 0) / 1e7,
@@ -58,58 +59,65 @@ export const Position = (): JSX.Element => {
new Protobuf.Config_PositionConfig(rest)
);
if (connection) {
void toast.promise(
connection
.setPosition(
new Protobuf.Position({
altitude: fixedAlt,
latitudeI: fixedLat * 1e7,
longitudeI: fixedLng * 1e7
})
)
.then(() => reset({ ...data })),
{
loading: "Saving...",
success: "Saved Position Config, Restarting Node",
error: "No response received"
setWorkingConfig(
new Protobuf.Config({
payloadVariant: {
case: "position",
value: rest
}
);
if (configHasChanged) {
void toast.promise(
connection
.setConfig(
new Protobuf.Config({
payloadVariant: {
case: "position",
value: rest
}
})
)
.then(() =>
setConfig(
new Protobuf.Config({
payloadVariant: {
case: "position",
value: rest
}
})
)
),
{
loading: "Saving...",
success: "Saved Position Config, Restarting Node",
error: "No response received"
}
);
}
}
})
);
// if (connection) {
// void toast.promise(
// connection
// .setPosition(
// new Protobuf.Position({
// altitude: fixedAlt,
// latitudeI: fixedLat * 1e7,
// longitudeI: fixedLng * 1e7
// })
// )
// .then(() => reset({ ...data })),
// {
// loading: "Saving...",
// success: "Saved Position Config, Restarting Node",
// error: "No response received"
// }
// );
// if (configHasChanged) {
// void toast.promise(
// connection
// .setConfig(
// new Protobuf.Config({
// payloadVariant: {
// case: "position",
// value: rest
// }
// })
// )
// .then(() =>
// setConfig(
// new Protobuf.Config({
// payloadVariant: {
// case: "position",
// value: rest
// }
// })
// )
// ),
// {
// loading: "Saving...",
// success: "Saved Position Config, Restarting Node",
// error: "No response received"
// }
// );
// }
// }
});
return (
<Form
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Controller
name="gpsEnabled"
control={control}
+10 -31
View File
@@ -14,7 +14,7 @@ import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
export const Power = (): JSX.Element => {
const { config, connection, setConfig } = useDevice();
const { config, setWorkingConfig } = useDevice();
const {
register,
handleSubmit,
@@ -22,6 +22,7 @@ export const Power = (): JSX.Element => {
reset,
control
} = useForm<PowerValidation>({
mode: "onChange",
defaultValues: config.power,
resolver: classValidatorResolver(PowerValidation)
});
@@ -31,40 +32,18 @@ export const Power = (): JSX.Element => {
}, [reset, config.power]);
const onSubmit = handleSubmit((data) => {
if (connection) {
void toast.promise(
connection
.setConfig(
new Protobuf.Config({
payloadVariant: {
case: "power",
value: data
}
})
)
.then(() =>
setConfig(
new Protobuf.Config({
payloadVariant: {
case: "power",
value: data
}
})
)
),
{
loading: "Saving...",
success: "Saved Power Config, Restarting Node",
error: "No response received"
setWorkingConfig(
new Protobuf.Config({
payloadVariant: {
case: "power",
value: data
}
);
}
})
);
});
return (
<Form
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Input
label="Shutdown on battery delay"
description="Automatically shutdown node after this long when on battery, 0 for indefinite"
+1
View File
@@ -12,6 +12,7 @@ import { CommandLineIcon } from "@heroicons/react/24/outline";
import { Types } from "@meshtastic/meshtasticjs";
import { Input } from "./form/Input.js";
import { Button } from "./form/Button.js";
export const Sidebar = (): JSX.Element => {
const { removeDevice } = useDeviceStore();
+2 -1
View File
@@ -2,7 +2,7 @@ import type React from "react";
import type { HTMLProps } from "react";
export interface FormProps extends HTMLProps<HTMLFormElement> {
onSubmit: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;
onSubmit?: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;
}
export const Form = ({
@@ -14,6 +14,7 @@ export const Form = ({
<form
className="mr-2 w-full rounded-md bg-backgroundSecondary px-2"
onSubmit={onSubmit}
onChange={onSubmit}
{...props}
>
<div className="flex flex-col gap-3 p-4">{children}</div>
+46 -12
View File
@@ -52,16 +52,6 @@ export interface processPacketParams {
time: number;
}
export type WorkingConfig = [
Protobuf.Config_DeviceConfig?,
Protobuf.Config_PositionConfig?,
Protobuf.Config_PowerConfig?,
Protobuf.Config_NetworkConfig?,
Protobuf.Config_DisplayConfig?,
Protobuf.Config_LoRaConfig?,
Protobuf.Config_BluetoothConfig?
];
export type DialogVariant = "import" | "QR" | "shutdown" | "reboot";
export interface Device {
@@ -71,6 +61,8 @@ export interface Device {
channels: Channel[];
config: Protobuf.LocalConfig;
moduleConfig: Protobuf.LocalModuleConfig;
workingConfig: Protobuf.Config[];
workingModuleConfig: Protobuf.ModuleConfig[];
hardware: Protobuf.MyNodeInfo;
nodes: Node[];
connection?: Types.ConnectionType;
@@ -82,7 +74,6 @@ export interface Device {
currentMetrics: Protobuf.DeviceMetrics;
pendingSettingsChanges: boolean;
messageDraft: string;
workingConfig: WorkingConfig;
dialog: {
import: boolean;
QR: boolean;
@@ -94,6 +85,8 @@ export interface Device {
setStatus: (status: Types.DeviceStatusEnum) => void;
setConfig: (config: Protobuf.Config) => void;
setModuleConfig: (config: Protobuf.ModuleConfig) => void;
setWorkingConfig: (config: Protobuf.Config) => void;
setWorkingModuleConfig: (config: Protobuf.ModuleConfig) => void;
setHardware: (hardware: Protobuf.MyNodeInfo) => void;
setMetrics: (metrics: Types.PacketMetadata<Protobuf.Telemetry>) => void;
setActivePage: (page: Page) => void;
@@ -145,6 +138,8 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
channels: [],
config: new Protobuf.LocalConfig(),
moduleConfig: new Protobuf.LocalModuleConfig(),
workingConfig: [],
workingModuleConfig: [],
hardware: new Protobuf.MyNodeInfo(),
nodes: [],
connection: undefined,
@@ -162,7 +157,6 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
},
pendingSettingsChanges: false,
messageDraft: "",
workingConfig: [],
setReady: (ready: boolean) => {
set(
@@ -260,6 +254,46 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
})
);
},
setWorkingConfig: (config: Protobuf.Config) => {
set(
produce<DeviceState>((draft) => {
const device = draft.devices.get(id);
if (!device) {
return;
}
const workingConfigIndex = device?.workingConfig.findIndex(
(wc) => wc.payloadVariant.case === config.payloadVariant.case
);
if (workingConfigIndex !== -1) {
device.workingConfig[workingConfigIndex] = config;
} else {
device?.workingConfig.push(config);
}
})
);
},
setWorkingModuleConfig: (moduleConfig: Protobuf.ModuleConfig) => {
set(
produce<DeviceState>((draft) => {
const device = draft.devices.get(id);
if (!device) {
return;
}
const workingModuleConfigIndex =
device?.workingModuleConfig.findIndex(
(wmc) =>
wmc.payloadVariant.case ===
moduleConfig.payloadVariant.case
);
if (workingModuleConfigIndex !== -1) {
device.workingModuleConfig[workingModuleConfigIndex] =
moduleConfig;
} else {
device?.workingModuleConfig.push(moduleConfig);
}
})
);
},
setHardware: (hardware: Protobuf.MyNodeInfo) => {
set(
produce<DeviceState>((draft) => {
+15 -3
View File
@@ -16,7 +16,7 @@ import { Button } from "@app/components/form/Button.js";
import { CheckIcon } from "@primer/octicons-react";
export const DeviceConfig = (): JSX.Element => {
const { hardware } = useDevice();
const { hardware, workingConfig, connection } = useDevice();
const configSections = [
{
@@ -71,7 +71,17 @@ export const DeviceConfig = (): JSX.Element => {
))}
</ol>
<div className="ml-auto">
<Button iconBefore={<CheckIcon className="w-4" />}>Save</Button>
<Button
onClick={async () => {
workingConfig.map(async (config) => {
await connection?.setConfig(config);
});
await connection?.commitEditSettings();
}}
iconBefore={<CheckIcon className="w-4" />}
>
Apply & Reboot
</Button>
</div>
</div>
@@ -88,7 +98,9 @@ export const DeviceConfig = (): JSX.Element => {
}`}
>
{Config.label}
<span className="ml-auto bg-accent rounded-full px-3 text-textPrimary">3</span>
<span className="ml-auto rounded-full bg-accent px-3 text-textPrimary">
3
</span>
</div>
)}
</Tab>
+3
View File
@@ -48,6 +48,9 @@ export class LoRaValidation
@IsBoolean()
overrideDutyCycle: boolean;
@IsBoolean()
sx126xRxBoostedGain: boolean;
@IsArray()
ignoreIncoming: number[];
}
+2
View File
@@ -30,6 +30,8 @@ export class NetworkValidation
addressMode: Protobuf.Config_NetworkConfig_AddressMode;
ipv4Config: NetworkValidation_IpV4Config;
rsyslogServer: string;
}
export class NetworkValidation_IpV4Config