mirror of
https://github.com/meshtastic/web.git
synced 2026-03-13 11:27:33 -04:00
WIP updates
This commit is contained in:
132
src/components/PageComponents/AppConfig/Map.tsx
Normal file
132
src/components/PageComponents/AppConfig/Map.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import type React from "react";
|
||||
|
||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
|
||||
import { Button } from "@app/components/Button.js";
|
||||
import { InfoWrapper } from "@app/components/form/InfoWrapper.js";
|
||||
import { Input } from "@app/components/form/Input.js";
|
||||
import { Toggle } from "@app/components/form/Toggle.js";
|
||||
import { IconButton } from "@app/components/IconButton.js";
|
||||
import { useAppStore } from "@app/core/stores/appStore.js";
|
||||
import { MapValidation } from "@app/validation/appConfig/map.js";
|
||||
import { Form } from "@components/form/Form";
|
||||
import { TrashIcon } from "@heroicons/react/24/outline";
|
||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||
|
||||
export const Map = (): JSX.Element => {
|
||||
const { rasterSources, setRasterSources } = useAppStore();
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
control,
|
||||
reset
|
||||
} = useForm<MapValidation>({
|
||||
defaultValues: {
|
||||
// wmsSources: wmsSources ?? [
|
||||
// {
|
||||
// url: "",
|
||||
// tileSize: 512,
|
||||
// type: "raster"
|
||||
// }
|
||||
// ]
|
||||
},
|
||||
resolver: classValidatorResolver(MapValidation)
|
||||
});
|
||||
|
||||
const { fields, append, remove, insert } = useFieldArray({
|
||||
control,
|
||||
name: "rasterSources"
|
||||
});
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
setRasterSources(data.rasterSources);
|
||||
});
|
||||
|
||||
// useEffect(() => {
|
||||
// reset(rasterSources);
|
||||
// }, [reset, rasterSources]);
|
||||
|
||||
return (
|
||||
<Form
|
||||
title="Map Config"
|
||||
breadcrumbs={["App Config", "Map"]}
|
||||
reset={() =>
|
||||
reset({
|
||||
rasterSources
|
||||
})
|
||||
}
|
||||
dirty={isDirty}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<InfoWrapper label="WMS Sources">
|
||||
<div className="flex flex-col gap-2">
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className="flex w-full gap-2">
|
||||
<Controller
|
||||
name={`rasterSources.${index}.enabled`}
|
||||
control={control}
|
||||
render={({ field: { value, ...rest } }) => (
|
||||
<Toggle checked={value} {...rest} />
|
||||
)}
|
||||
/>
|
||||
<Input
|
||||
placeholder="Name"
|
||||
error={
|
||||
errors.rasterSources
|
||||
? errors.rasterSources[index]?.title?.message
|
||||
: undefined
|
||||
}
|
||||
{...register(`rasterSources.${index}.title`)}
|
||||
/>
|
||||
<Input
|
||||
placeholder="Tile Size"
|
||||
type="number"
|
||||
error={
|
||||
errors.rasterSources
|
||||
? errors.rasterSources[index]?.tileSize?.message
|
||||
: undefined
|
||||
}
|
||||
{...register(`rasterSources.${index}.tileSize`, {
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
placeholder="URL"
|
||||
error={
|
||||
errors.rasterSources
|
||||
? errors.rasterSources[index]?.tiles?.message
|
||||
: undefined
|
||||
}
|
||||
{...register(`rasterSources.${index}.tiles`)}
|
||||
/>
|
||||
<IconButton
|
||||
className="shrink-0"
|
||||
icon={<TrashIcon className="w-4" />}
|
||||
onClick={() => {
|
||||
remove(index);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
append({
|
||||
enabled: true,
|
||||
title: "",
|
||||
tiles: [
|
||||
"https://img.nj.gov/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&transparent=true&width=256&height=256&layers=Natural2015"
|
||||
],
|
||||
tileSize: 512
|
||||
});
|
||||
}}
|
||||
>
|
||||
New Source
|
||||
</Button>
|
||||
</div>
|
||||
</InfoWrapper>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
@@ -14,7 +14,7 @@ import { useDevice } from "@core/providers/useDevice.js";
|
||||
import {
|
||||
ArrowPathIcon,
|
||||
EyeIcon,
|
||||
EyeSlashIcon,
|
||||
EyeSlashIcon
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||
@@ -34,39 +34,39 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
setValue,
|
||||
setValue
|
||||
} = useForm<ChannelSettingsValidation>({
|
||||
defaultValues: {
|
||||
enabled: [
|
||||
Protobuf.Channel_Role.SECONDARY,
|
||||
Protobuf.Channel_Role.PRIMARY,
|
||||
Protobuf.Channel_Role.PRIMARY
|
||||
].find((role) => role === channel?.role)
|
||||
? true
|
||||
: false,
|
||||
...channel?.settings,
|
||||
psk: fromByteArray(channel?.settings?.psk ?? new Uint8Array(0)),
|
||||
psk: fromByteArray(channel?.settings?.psk ?? new Uint8Array(0))
|
||||
},
|
||||
resolver: classValidatorResolver(ChannelSettingsValidation),
|
||||
resolver: classValidatorResolver(ChannelSettingsValidation)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
enabled: [
|
||||
Protobuf.Channel_Role.SECONDARY,
|
||||
Protobuf.Channel_Role.PRIMARY,
|
||||
Protobuf.Channel_Role.PRIMARY
|
||||
].find((role) => role === channel?.role)
|
||||
? true
|
||||
: false,
|
||||
...channel?.settings,
|
||||
psk: fromByteArray(channel?.settings?.psk ?? new Uint8Array(0)),
|
||||
psk: fromByteArray(channel?.settings?.psk ?? new Uint8Array(0))
|
||||
});
|
||||
}, [channel, reset]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setChannel(
|
||||
{
|
||||
connection.setChannel({
|
||||
channel: {
|
||||
role:
|
||||
channel?.role === Protobuf.Channel_Role.PRIMARY
|
||||
? Protobuf.Channel_Role.PRIMARY
|
||||
@@ -76,18 +76,18 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
||||
index: channel?.index,
|
||||
settings: {
|
||||
...data,
|
||||
psk: toByteArray(data.psk ?? ""),
|
||||
},
|
||||
psk: toByteArray(data.psk ?? "")
|
||||
}
|
||||
},
|
||||
(): Promise<void> => {
|
||||
callback: (): Promise<void> => {
|
||||
reset({ ...data });
|
||||
return Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Channel",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -102,18 +102,18 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
||||
? channel.settings.name
|
||||
: channel.role === Protobuf.Channel_Role.PRIMARY
|
||||
? "Primary"
|
||||
: `Channel: ${channel.index}`,
|
||||
: `Channel: ${channel.index}`
|
||||
]}
|
||||
reset={() =>
|
||||
reset({
|
||||
enabled: [
|
||||
Protobuf.Channel_Role.SECONDARY,
|
||||
Protobuf.Channel_Role.PRIMARY,
|
||||
Protobuf.Channel_Role.PRIMARY
|
||||
].find((role) => role === channel?.role)
|
||||
? true
|
||||
: false,
|
||||
...channel?.settings,
|
||||
psk: fromByteArray(channel?.settings?.psk ?? new Uint8Array(0)),
|
||||
psk: fromByteArray(channel?.settings?.psk ?? new Uint8Array(0))
|
||||
})
|
||||
}
|
||||
dirty={isDirty}
|
||||
@@ -153,8 +153,10 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
||||
action: () => {
|
||||
const key = new Uint8Array(keySize / 8);
|
||||
crypto.getRandomValues(key);
|
||||
setValue("psk", fromByteArray(key));
|
||||
},
|
||||
setValue("psk", fromByteArray(key), {
|
||||
shouldDirty: true
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<option value={128}>128 Bit</option>
|
||||
@@ -173,7 +175,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
||||
),
|
||||
action: () => {
|
||||
setPskHidden(!pskHidden);
|
||||
},
|
||||
}
|
||||
}}
|
||||
error={errors.psk?.message}
|
||||
{...register("psk")}
|
||||
|
||||
@@ -22,10 +22,10 @@ export const Bluetooth = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
control,
|
||||
reset,
|
||||
reset
|
||||
} = useForm<BluetoothValidation>({
|
||||
defaultValues: config.bluetooth,
|
||||
resolver: classValidatorResolver(BluetoothValidation),
|
||||
resolver: classValidatorResolver(BluetoothValidation)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -35,22 +35,22 @@ export const Bluetooth = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setConfig(
|
||||
{
|
||||
connection.setConfig({
|
||||
config: {
|
||||
payloadVariant: {
|
||||
oneofKind: "bluetooth",
|
||||
bluetooth: data,
|
||||
},
|
||||
bluetooth: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Bluetooth Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -59,7 +59,7 @@ export const Bluetooth = (): JSX.Element => {
|
||||
const pairingMode = useWatch({
|
||||
control,
|
||||
name: "mode",
|
||||
defaultValue: Protobuf.Config_BluetoothConfig_PairingMode.RANDOM_PIN,
|
||||
defaultValue: Protobuf.Config_BluetoothConfig_PairingMode.RANDOM_PIN
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -98,7 +98,7 @@ export const Bluetooth = (): JSX.Element => {
|
||||
description="Pin to use when pairing"
|
||||
type="number"
|
||||
{...register("fixedPin", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
</Form>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
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 { DeviceValidation } from "@app/validation/config/device.js";
|
||||
@@ -20,10 +21,10 @@ export const Device = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
control,
|
||||
reset,
|
||||
reset
|
||||
} = useForm<DeviceValidation>({
|
||||
defaultValues: config.device,
|
||||
resolver: classValidatorResolver(DeviceValidation),
|
||||
resolver: classValidatorResolver(DeviceValidation)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -33,22 +34,22 @@ export const Device = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setConfig(
|
||||
{
|
||||
connection.setConfig({
|
||||
config: {
|
||||
payloadVariant: {
|
||||
oneofKind: "device",
|
||||
device: data,
|
||||
},
|
||||
device: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Device Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -93,6 +94,20 @@ export const Device = (): JSX.Element => {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Input
|
||||
label="Button Pin"
|
||||
description="Button pin override"
|
||||
type="number"
|
||||
error={errors.buttonGpio?.message}
|
||||
{...register("buttonGpio", { valueAsNumber: true })}
|
||||
/>
|
||||
<Input
|
||||
label="Buzzer Pin"
|
||||
description="Buzzer pin override"
|
||||
type="number"
|
||||
error={errors.buzzerGpio?.message}
|
||||
{...register("buzzerGpio", { valueAsNumber: true })}
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,10 +21,10 @@ export const Display = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<Protobuf.Config_DisplayConfig>({
|
||||
defaultValues: config.display,
|
||||
resolver: classValidatorResolver(DisplayValidation),
|
||||
resolver: classValidatorResolver(DisplayValidation)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -34,22 +34,22 @@ export const Display = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setConfig(
|
||||
{
|
||||
connection.setConfig({
|
||||
config: {
|
||||
payloadVariant: {
|
||||
oneofKind: "display",
|
||||
display: data,
|
||||
},
|
||||
display: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Display Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -115,6 +115,13 @@ export const Display = (): JSX.Element => {
|
||||
>
|
||||
{renderOptions(Protobuf.Config_DisplayConfig_DisplayUnits)}
|
||||
</Select>
|
||||
<Select
|
||||
label="OLED Type"
|
||||
description="Type of OLED screen attached to the device"
|
||||
{...register("oled", { valueAsNumber: true })}
|
||||
>
|
||||
{renderOptions(Protobuf.Config_DisplayConfig_OledType)}
|
||||
</Select>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,16 +23,16 @@ export const LoRa = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
control,
|
||||
reset,
|
||||
reset
|
||||
} = useForm<LoRaValidation>({
|
||||
defaultValues: config.lora,
|
||||
resolver: classValidatorResolver(LoRaValidation),
|
||||
resolver: classValidatorResolver(LoRaValidation)
|
||||
});
|
||||
|
||||
const usePreset = useWatch({
|
||||
control,
|
||||
name: "usePreset",
|
||||
defaultValue: true,
|
||||
defaultValue: true
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -42,22 +42,22 @@ export const LoRa = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setConfig(
|
||||
{
|
||||
connection.setConfig({
|
||||
config: {
|
||||
payloadVariant: {
|
||||
oneofKind: "lora",
|
||||
lora: data,
|
||||
},
|
||||
lora: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved LoRa Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -99,7 +99,7 @@ export const LoRa = (): JSX.Element => {
|
||||
suffix="MHz"
|
||||
error={errors.bandwidth?.message}
|
||||
{...register("bandwidth", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
disabled={usePreset}
|
||||
/>
|
||||
@@ -110,7 +110,7 @@ export const LoRa = (): JSX.Element => {
|
||||
suffix="CPS"
|
||||
error={errors.spreadFactor?.message}
|
||||
{...register("spreadFactor", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
disabled={usePreset}
|
||||
/>
|
||||
@@ -120,7 +120,7 @@ export const LoRa = (): JSX.Element => {
|
||||
type="number"
|
||||
error={errors.codingRate?.message}
|
||||
{...register("codingRate", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
disabled={usePreset}
|
||||
/>
|
||||
|
||||
@@ -6,12 +6,14 @@ import { toast } from "react-hot-toast";
|
||||
|
||||
import { FormSection } from "@app/components/form/FormSection.js";
|
||||
import { Input } from "@app/components/form/Input.js";
|
||||
import { IPAddress } from "@app/components/form/IPAddress.js";
|
||||
import { Select } from "@app/components/form/Select.js";
|
||||
import { Toggle } from "@app/components/form/Toggle.js";
|
||||
import { renderOptions } from "@app/core/utils/selectEnumOptions.js";
|
||||
import { NetworkValidation } from "@app/validation/config/network.js";
|
||||
import { Form } from "@components/form/Form";
|
||||
import { useDevice } from "@core/providers/useDevice.js";
|
||||
import { ErrorMessage } from "@hookform/error-message";
|
||||
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
||||
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||
|
||||
@@ -22,22 +24,22 @@ export const Network = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
control,
|
||||
reset,
|
||||
reset
|
||||
} = useForm<NetworkValidation>({
|
||||
defaultValues: config.network,
|
||||
resolver: classValidatorResolver(NetworkValidation),
|
||||
resolver: classValidatorResolver(NetworkValidation)
|
||||
});
|
||||
|
||||
const wifiEnabled = useWatch({
|
||||
control,
|
||||
name: "wifiEnabled",
|
||||
defaultValue: false,
|
||||
defaultValue: false
|
||||
});
|
||||
|
||||
const ethEnabled = useWatch({
|
||||
control,
|
||||
name: "ethEnabled",
|
||||
defaultValue: false,
|
||||
defaultValue: false
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -45,24 +47,32 @@ export const Network = (): JSX.Element => {
|
||||
}, [reset, config.network]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
console.log(data);
|
||||
|
||||
if (connection) {
|
||||
const tmp = Protobuf.Config_NetworkConfig.create({
|
||||
ethEnabled: true,
|
||||
ethMode: Protobuf.Config_NetworkConfig_EthMode.DHCP
|
||||
});
|
||||
void toast.promise(
|
||||
connection.setConfig(
|
||||
{
|
||||
payloadVariant: {
|
||||
oneofKind: "network",
|
||||
network: data,
|
||||
connection
|
||||
.setConfig({
|
||||
config: {
|
||||
payloadVariant: {
|
||||
oneofKind: "network",
|
||||
network: tmp
|
||||
}
|
||||
},
|
||||
},
|
||||
async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
})
|
||||
.catch((e) => console.log(e)),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Network Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -76,6 +86,19 @@ export const Network = (): JSX.Element => {
|
||||
dirty={isDirty}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<ErrorMessage errors={errors} name="wifiEnabled" />
|
||||
<ErrorMessage errors={errors} name="wifiMode" />
|
||||
<ErrorMessage errors={errors} name="wifiSsid" />
|
||||
<ErrorMessage errors={errors} name="wifiPsk" />
|
||||
<ErrorMessage errors={errors} name="ntpServer" />
|
||||
<ErrorMessage errors={errors} name="ethEnabled" />
|
||||
<ErrorMessage errors={errors} name="ethMode" />
|
||||
<ErrorMessage errors={errors} name="ethConfig" />
|
||||
<ErrorMessage errors={errors} name="ip" />
|
||||
<ErrorMessage errors={errors} name="gateway" />
|
||||
<ErrorMessage errors={errors} name="subnet" />
|
||||
<ErrorMessage errors={errors} name="dns" />
|
||||
|
||||
<FormSection title="WiFi Config">
|
||||
<Controller
|
||||
name="wifiEnabled"
|
||||
@@ -83,26 +106,18 @@ export const Network = (): JSX.Element => {
|
||||
render={({ field: { value, ...rest } }) => (
|
||||
<Toggle
|
||||
label="WiFi Enabled"
|
||||
description="Enable or disbale the WiFi radio"
|
||||
description="Enable or disable the WiFi radio"
|
||||
checked={value}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Select
|
||||
label="WiFi Mode"
|
||||
description="How the WiFi radio should be used"
|
||||
disabled={!wifiEnabled}
|
||||
{...register("wifiMode", { valueAsNumber: true })}
|
||||
>
|
||||
{renderOptions(Protobuf.Config_NetworkConfig_WiFiMode)}
|
||||
</Select>
|
||||
<Input
|
||||
label="SSID"
|
||||
description="Network name"
|
||||
error={errors.wifiSsid?.message}
|
||||
disabled={!wifiEnabled}
|
||||
{...register("wifiSsid")}
|
||||
{...register("wifiSsid", { disabled: !wifiEnabled })}
|
||||
/>
|
||||
<Input
|
||||
label="PSK"
|
||||
@@ -110,7 +125,7 @@ export const Network = (): JSX.Element => {
|
||||
description="Network password"
|
||||
error={errors.wifiPsk?.message}
|
||||
disabled={!wifiEnabled}
|
||||
{...register("wifiPsk")}
|
||||
{...register("wifiPsk", { disabled: !wifiEnabled })}
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection title="Ethernet Config">
|
||||
@@ -130,35 +145,43 @@ export const Network = (): JSX.Element => {
|
||||
label="Ethernet Mode"
|
||||
description="Address assignment selection"
|
||||
disabled={!ethEnabled}
|
||||
{...register("ethMode", { valueAsNumber: true })}
|
||||
{...register("ethMode", {
|
||||
valueAsNumber: true,
|
||||
disabled: !ethEnabled
|
||||
})}
|
||||
>
|
||||
{renderOptions(Protobuf.Config_NetworkConfig_EthMode)}
|
||||
</Select>
|
||||
</FormSection>
|
||||
<FormSection title="IP Config">
|
||||
<IPAddress label="IP" description="IP Address" />
|
||||
<Input
|
||||
label="IP"
|
||||
type="number"
|
||||
description="IP Address"
|
||||
error={errors.ethConfig?.ip?.message}
|
||||
{...register("ethConfig.ip")}
|
||||
error={errors.ipv4Config?.ip?.message}
|
||||
{...register("ipv4Config.ip", { valueAsNumber: true })}
|
||||
/>
|
||||
<Input
|
||||
label="Gateway"
|
||||
type="number"
|
||||
description="Default Gateway"
|
||||
error={errors.ethConfig?.gateway?.message}
|
||||
{...register("ethConfig.gateway")}
|
||||
error={errors.ipv4Config?.gateway?.message}
|
||||
{...register("ipv4Config.gateway", { valueAsNumber: true })}
|
||||
/>
|
||||
<Input
|
||||
label="Subnet"
|
||||
type="number"
|
||||
description="Subnet Mask"
|
||||
error={errors.ethConfig?.subnet?.message}
|
||||
{...register("ethConfig.subnet")}
|
||||
error={errors.ipv4Config?.subnet?.message}
|
||||
{...register("ipv4Config.subnet", { valueAsNumber: true })}
|
||||
/>
|
||||
<Input
|
||||
label="DNS"
|
||||
type="number"
|
||||
description="DNS Server"
|
||||
error={errors.ethConfig?.dns?.message}
|
||||
{...register("ethConfig.dns")}
|
||||
error={errors.ipv4Config?.dns?.message}
|
||||
{...register("ipv4Config.dns", { valueAsNumber: true })}
|
||||
/>
|
||||
</FormSection>
|
||||
<Input
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useEffect } from "react";
|
||||
import { Controller, useForm, useWatch } from "react-hook-form";
|
||||
import { toast } from "react-hot-toast";
|
||||
|
||||
import { BitwiseSelect } from "@app/components/form/BitwiseSelect.js";
|
||||
import { FormSection } from "@app/components/form/FormSection.js";
|
||||
import { Input } from "@app/components/form/Input.js";
|
||||
import { Toggle } from "@app/components/form/Toggle.js";
|
||||
@@ -23,21 +24,21 @@ export const Position = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<PositionValidation>({
|
||||
defaultValues: {
|
||||
fixedAlt: myNode?.data.position?.altitude,
|
||||
fixedLat: (myNode?.data.position?.latitudeI ?? 0) / 1e7,
|
||||
fixedLng: (myNode?.data.position?.longitudeI ?? 0) / 1e7,
|
||||
...config.position,
|
||||
...config.position
|
||||
},
|
||||
resolver: classValidatorResolver(PositionValidation),
|
||||
resolver: classValidatorResolver(PositionValidation)
|
||||
});
|
||||
|
||||
const fixedPositionEnabled = useWatch({
|
||||
control,
|
||||
name: "fixedPosition",
|
||||
defaultValue: false,
|
||||
defaultValue: false
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -45,7 +46,7 @@ export const Position = (): JSX.Element => {
|
||||
fixedAlt: myNode?.data.position?.altitude,
|
||||
fixedLat: (myNode?.data.position?.latitudeI ?? 0) / 1e7,
|
||||
fixedLng: (myNode?.data.position?.longitudeI ?? 0) / 1e7,
|
||||
...config.position,
|
||||
...config.position
|
||||
});
|
||||
}, [reset, config.position, myNode?.data.position]);
|
||||
|
||||
@@ -59,49 +60,41 @@ export const Position = (): JSX.Element => {
|
||||
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.sendPacket(
|
||||
Protobuf.Position.toBinary(
|
||||
Protobuf.Position.create({
|
||||
altitude: fixedAlt,
|
||||
latitudeI: fixedLat * 1e7,
|
||||
longitudeI: fixedLng * 1e7,
|
||||
})
|
||||
),
|
||||
Protobuf.PortNum.POSITION_APP,
|
||||
undefined,
|
||||
true,
|
||||
undefined,
|
||||
true,
|
||||
false,
|
||||
async () => {
|
||||
connection.setPosition({
|
||||
position: Protobuf.Position.create({
|
||||
altitude: fixedAlt,
|
||||
latitudeI: fixedLat * 1e7,
|
||||
longitudeI: fixedLng * 1e7
|
||||
}),
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Channel",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
if (configHasChanged) {
|
||||
void toast.promise(
|
||||
connection.setConfig(
|
||||
{
|
||||
connection.setConfig({
|
||||
config: {
|
||||
payloadVariant: {
|
||||
oneofKind: "position",
|
||||
position: rest,
|
||||
},
|
||||
position: rest
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Position Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -201,94 +194,65 @@ export const Position = (): JSX.Element => {
|
||||
error={errors.gpsAttemptTime?.message}
|
||||
{...register("gpsAttemptTime", { valueAsNumber: true })}
|
||||
/>
|
||||
{/* <Controller
|
||||
<Controller
|
||||
name="positionFlags"
|
||||
control={control}
|
||||
render={({ field, fieldState }): JSX.Element => {
|
||||
const { value, onChange, ...rest } = field;
|
||||
const { error } = fieldState;
|
||||
const options = Object.entries(
|
||||
Protobuf.Config_PositionConfig_PositionFlags
|
||||
)
|
||||
.filter((value) => typeof value[1] !== "number")
|
||||
.filter(
|
||||
(value) =>
|
||||
parseInt(value[0]) !==
|
||||
Protobuf.Config_PositionConfig_PositionFlags.UNSET
|
||||
)
|
||||
.map((value) => {
|
||||
return {
|
||||
value: parseInt(value[0]),
|
||||
label: value[1].toString().replace("POS_", "").toLowerCase(),
|
||||
};
|
||||
});
|
||||
// const options = Object.entries(
|
||||
// Protobuf.Config_PositionConfig_PositionFlags
|
||||
// )
|
||||
// .filter((value) => typeof value[1] !== "number")
|
||||
// .filter(
|
||||
// (value) =>
|
||||
// parseInt(value[0]) !==
|
||||
// Protobuf.Config_PositionConfig_PositionFlags.UNSET
|
||||
// )
|
||||
// .map((value) => {
|
||||
// return {
|
||||
// value: parseInt(value[0]),
|
||||
// label: value[1].toString().replace("POS_", "").toLowerCase(),
|
||||
// };
|
||||
// });
|
||||
|
||||
const selected = bitwiseDecode(
|
||||
value,
|
||||
Protobuf.Config_PositionConfig_PositionFlags
|
||||
).map((flag) =>
|
||||
Protobuf.Config_PositionConfig_PositionFlags[flag]
|
||||
.replace("POS_", "")
|
||||
.toLowerCase()
|
||||
);
|
||||
// const selected = bitwiseDecode(
|
||||
// value,
|
||||
// Protobuf.Config_PositionConfig_PositionFlags
|
||||
// ).map((flag) =>
|
||||
// Protobuf.Config_PositionConfig_PositionFlags[flag]
|
||||
// .replace("POS_", "")
|
||||
// .toLowerCase()
|
||||
// );
|
||||
// onChange={(e: { value: number; label: string }[]): void =>
|
||||
// onChange(bitwiseEncode(e.map((v) => v.value)))
|
||||
// }
|
||||
return (
|
||||
<FormField
|
||||
<BitwiseSelect
|
||||
label="Position Flags"
|
||||
description="Description"
|
||||
isInvalid={!!errors.positionFlags?.message}
|
||||
validationMessage={errors.positionFlags?.message}
|
||||
>
|
||||
<SelectMenu
|
||||
isMultiSelect
|
||||
title="Select multiple names"
|
||||
options={options}
|
||||
selected={selected}
|
||||
// onSelect={(item) => {
|
||||
// const selected = [...selectedItemsState, item.value]
|
||||
// const selectedItems = selected
|
||||
// const selectedItemsLength = selectedItems.length
|
||||
// let selectedNames = ''
|
||||
// if (selectedItemsLength === 0) {
|
||||
// selectedNames = ''
|
||||
// } else if (selectedItemsLength === 1) {
|
||||
// selectedNames = selectedItems.toString()
|
||||
// } else if (selectedItemsLength > 1) {
|
||||
// selectedNames = selectedItemsLength.toString() + ' selected...'
|
||||
// }
|
||||
// setSelectedItems(selectedItems)
|
||||
// setSelectedItemNames(selectedNames)
|
||||
// }}
|
||||
// onDeselect={(item) => {
|
||||
// const deselectedItemIndex = selectedItemsState.indexOf(item.value)
|
||||
// const selectedItems = selectedItemsState.filter((_item, i) => i !== deselectedItemIndex)
|
||||
// const selectedItemsLength = selectedItems.length
|
||||
// let selectedNames = ''
|
||||
// if (selectedItemsLength === 0) {
|
||||
// selectedNames = ''
|
||||
// } else if (selectedItemsLength === 1) {
|
||||
// selectedNames = selectedItems.toString()
|
||||
// } else if (selectedItemsLength > 1) {
|
||||
// selectedNames = selectedItemsLength.toString() + ' selected...'
|
||||
// }
|
||||
|
||||
// setSelectedItems(selectedItems)
|
||||
// setSelectedItemNames(selectedNames)
|
||||
// }}
|
||||
>
|
||||
<Button>
|
||||
{selected.map(
|
||||
(item, index) =>
|
||||
`${item}${index !== selected.length - 1 ? ", " : ""}`
|
||||
)}
|
||||
</Button>
|
||||
</SelectMenu>
|
||||
</FormField>
|
||||
error={error?.message}
|
||||
selected={value}
|
||||
decodeEnun={Protobuf.Config_PositionConfig_PositionFlags}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/> */}
|
||||
/>
|
||||
<Input
|
||||
label="RX Pin"
|
||||
description="GPS Module RX pin override"
|
||||
type="number"
|
||||
error={errors.rxGpio?.message}
|
||||
{...register("rxGpio", { valueAsNumber: true })}
|
||||
/>
|
||||
<Input
|
||||
label="TX Pin"
|
||||
description="GPS Module TX pin override"
|
||||
type="number"
|
||||
error={errors.txGpio?.message}
|
||||
{...register("txGpio", { valueAsNumber: true })}
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,10 +19,10 @@ export const Power = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<PowerValidation>({
|
||||
defaultValues: config.power,
|
||||
resolver: classValidatorResolver(PowerValidation),
|
||||
resolver: classValidatorResolver(PowerValidation)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -32,22 +32,22 @@ export const Power = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setConfig(
|
||||
{
|
||||
connection.setConfig({
|
||||
config: {
|
||||
payloadVariant: {
|
||||
oneofKind: "power",
|
||||
power: data,
|
||||
},
|
||||
power: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Power Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,31 +25,34 @@ export const User = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<UserValidation>({
|
||||
defaultValues: myNode?.data.user,
|
||||
resolver: classValidatorResolver(UserValidation),
|
||||
resolver: classValidatorResolver(UserValidation)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
longName: myNode?.data.user?.longName,
|
||||
shortName: myNode?.data.user?.shortName,
|
||||
isLicensed: myNode?.data.user?.isLicensed,
|
||||
isLicensed: myNode?.data.user?.isLicensed
|
||||
});
|
||||
}, [reset, myNode]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection && myNode?.data.user) {
|
||||
void toast.promise(
|
||||
connection.setOwner({ ...myNode.data.user, ...data }, async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
connection.setOwner({
|
||||
owner: { ...myNode.data.user, ...data },
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved User, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -63,7 +66,7 @@ export const User = (): JSX.Element => {
|
||||
reset({
|
||||
longName: myNode?.data.user?.longName,
|
||||
shortName: myNode?.data.user?.shortName,
|
||||
isLicensed: myNode?.data.user?.isLicensed,
|
||||
isLicensed: myNode?.data.user?.isLicensed
|
||||
});
|
||||
}}
|
||||
dirty={isDirty}
|
||||
|
||||
@@ -29,7 +29,7 @@ export const BLE = (): JSX.Element => {
|
||||
setSelectedDevice(id);
|
||||
const connection = new IBLEConnection(id);
|
||||
await connection.connect({
|
||||
device: BLEDevice,
|
||||
device: BLEDevice
|
||||
});
|
||||
device.addConnection(connection);
|
||||
subscribeAll(device, connection);
|
||||
@@ -58,7 +58,7 @@ export const BLE = (): JSX.Element => {
|
||||
onClick={() => {
|
||||
void navigator.bluetooth
|
||||
.requestDevice({
|
||||
filters: [{ services: [Constants.serviceUUID] }],
|
||||
filters: [{ services: [Constants.serviceUUID] }]
|
||||
})
|
||||
.then((device) => {
|
||||
const exists = bleDevices.findIndex((d) => d.id === device.id);
|
||||
|
||||
@@ -21,14 +21,14 @@ export const HTTP = (): JSX.Element => {
|
||||
}>({
|
||||
defaultValues: {
|
||||
ip: "meshtastic.local",
|
||||
tls: location.protocol === "https:",
|
||||
},
|
||||
tls: location.protocol === "https:"
|
||||
}
|
||||
});
|
||||
|
||||
const TLSEnabled = useWatch({
|
||||
control,
|
||||
name: "tls",
|
||||
defaultValue: location.protocol === "https:",
|
||||
defaultValue: location.protocol === "https:"
|
||||
});
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
@@ -40,7 +40,7 @@ export const HTTP = (): JSX.Element => {
|
||||
void connection.connect({
|
||||
address: data.ip,
|
||||
fetchInterval: 2000,
|
||||
tls: data.tls,
|
||||
tls: data.tls
|
||||
});
|
||||
device.addConnection(connection);
|
||||
subscribeAll(device, connection);
|
||||
|
||||
@@ -38,7 +38,7 @@ export const Serial = (): JSX.Element => {
|
||||
.connect({
|
||||
port,
|
||||
baudRate: undefined,
|
||||
concurrentLogOutput: true,
|
||||
concurrentLogOutput: true
|
||||
})
|
||||
.catch((e: Error) => console.log(`Unable to Connect: ${e.message}`));
|
||||
device.addConnection(connection);
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { AllMessageTypes } from "@core/stores/deviceStore.js";
|
||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
EllipsisHorizontalCircleIcon,
|
||||
EllipsisHorizontalCircleIcon
|
||||
} from "@heroicons/react/24/outline";
|
||||
import type { Protobuf } from "@meshtastic/meshtasticjs";
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface MessageProps {
|
||||
export const Message = ({
|
||||
lastMsgSameUser,
|
||||
message,
|
||||
sender,
|
||||
sender
|
||||
}: MessageProps): JSX.Element => {
|
||||
const { setPeerInfoOpen, setActivePeer, connection } = useDevice();
|
||||
|
||||
@@ -62,7 +62,7 @@ export const Message = ({
|
||||
<span className="text-sm">
|
||||
{new Date(message.packet.rxTime).toLocaleTimeString(undefined, {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
minute: "2-digit"
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -20,21 +20,20 @@ export const MessageInput = ({ channel }: MessageInputProps): JSX.Element => {
|
||||
message: string;
|
||||
}>({
|
||||
defaultValues: {
|
||||
message: "",
|
||||
},
|
||||
message: ""
|
||||
}
|
||||
});
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
void connection?.sendText(
|
||||
data.message,
|
||||
undefined,
|
||||
true,
|
||||
channel.config.index as Types.ChannelNumber,
|
||||
(id) => {
|
||||
void connection?.sendText({
|
||||
text: data.message,
|
||||
wantAck: true,
|
||||
channel: channel.config.index as Types.ChannelNumber,
|
||||
callback: (id) => {
|
||||
ackMessage(channel.config.index, id);
|
||||
return Promise.resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||
enum LocationType {
|
||||
MGRS,
|
||||
LatLng,
|
||||
DecimalDegrees,
|
||||
DecimalDegrees
|
||||
}
|
||||
|
||||
export const NewLocationMessage = (): JSX.Element => {
|
||||
@@ -31,14 +31,15 @@ export const NewLocationMessage = (): JSX.Element => {
|
||||
<Input label="Coordinates" />
|
||||
<Button
|
||||
onClick={() => {
|
||||
void connection?.sendWaypoint(
|
||||
Protobuf.Waypoint.create({
|
||||
void connection?.sendWaypoint({
|
||||
waypoint: Protobuf.Waypoint.create({
|
||||
latitudeI: Math.floor(3.89103 * 1e7),
|
||||
longitudeI: Math.floor(105.87005 * 1e7),
|
||||
name: "TEST",
|
||||
description: "This is a description",
|
||||
})
|
||||
);
|
||||
description: "This is a description"
|
||||
}),
|
||||
destination: "broadcast"
|
||||
});
|
||||
}}
|
||||
>
|
||||
Send
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface WaypointMessageProps {
|
||||
}
|
||||
|
||||
export const WaypointMessage = ({
|
||||
waypointID,
|
||||
waypointID
|
||||
}: WaypointMessageProps): JSX.Element => {
|
||||
const { waypoints } = useDevice();
|
||||
const waypoint = waypoints.find((wp) => wp.id === waypointID);
|
||||
|
||||
@@ -21,16 +21,16 @@ export const CannedMessage = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<CannedMessageValidation>({
|
||||
defaultValues: moduleConfig.cannedMessage,
|
||||
resolver: classValidatorResolver(CannedMessageValidation),
|
||||
resolver: classValidatorResolver(CannedMessageValidation)
|
||||
});
|
||||
|
||||
const moduleEnabled = useWatch({
|
||||
control,
|
||||
name: "rotary1Enabled",
|
||||
defaultValue: false,
|
||||
defaultValue: false
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -40,22 +40,22 @@ export const CannedMessage = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setModuleConfig(
|
||||
{
|
||||
connection.setModuleConfig({
|
||||
moduleConfig: {
|
||||
payloadVariant: {
|
||||
oneofKind: "cannedMessage",
|
||||
cannedMessage: data,
|
||||
},
|
||||
cannedMessage: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Canned Message Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ export const ExternalNotification = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<ExternalNotificationValidation>({
|
||||
defaultValues: moduleConfig.externalNotification,
|
||||
resolver: classValidatorResolver(ExternalNotificationValidation),
|
||||
resolver: classValidatorResolver(ExternalNotificationValidation)
|
||||
});
|
||||
useEffect(() => {
|
||||
reset(moduleConfig.externalNotification);
|
||||
@@ -30,22 +30,22 @@ export const ExternalNotification = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setModuleConfig(
|
||||
{
|
||||
connection.setModuleConfig({
|
||||
moduleConfig: {
|
||||
payloadVariant: {
|
||||
oneofKind: "externalNotification",
|
||||
externalNotification: data,
|
||||
},
|
||||
externalNotification: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved External Notification Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ export const ExternalNotification = (): JSX.Element => {
|
||||
const moduleEnabled = useWatch({
|
||||
control,
|
||||
name: "enabled",
|
||||
defaultValue: false,
|
||||
defaultValue: false
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -84,7 +84,7 @@ export const ExternalNotification = (): JSX.Element => {
|
||||
suffix="ms"
|
||||
disabled={!moduleEnabled}
|
||||
{...register("outputMs", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
@@ -93,7 +93,7 @@ export const ExternalNotification = (): JSX.Element => {
|
||||
description="Max transmit power in dBm"
|
||||
disabled={!moduleEnabled}
|
||||
{...register("output", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Controller
|
||||
@@ -132,6 +132,18 @@ export const ExternalNotification = (): JSX.Element => {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="usePwm"
|
||||
control={control}
|
||||
render={({ field: { value, ...rest } }) => (
|
||||
<Toggle
|
||||
label="Use PWM"
|
||||
description="Description"
|
||||
checked={value}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,16 +18,16 @@ export const MQTT = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<MQTTValidation>({
|
||||
defaultValues: moduleConfig.mqtt,
|
||||
resolver: classValidatorResolver(MQTTValidation),
|
||||
resolver: classValidatorResolver(MQTTValidation)
|
||||
});
|
||||
|
||||
const moduleEnabled = useWatch({
|
||||
control,
|
||||
name: "enabled",
|
||||
defaultValue: false,
|
||||
defaultValue: false
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -37,22 +37,22 @@ export const MQTT = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setModuleConfig(
|
||||
{
|
||||
connection.setModuleConfig({
|
||||
moduleConfig: {
|
||||
payloadVariant: {
|
||||
oneofKind: "mqtt",
|
||||
mqtt: data,
|
||||
},
|
||||
mqtt: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved MQTT Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ export const RangeTest = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<RangeTestValidation>({
|
||||
defaultValues: moduleConfig.rangeTest,
|
||||
resolver: classValidatorResolver(RangeTestValidation),
|
||||
resolver: classValidatorResolver(RangeTestValidation)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -31,22 +31,22 @@ export const RangeTest = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setModuleConfig(
|
||||
{
|
||||
connection.setModuleConfig({
|
||||
moduleConfig: {
|
||||
payloadVariant: {
|
||||
oneofKind: "rangeTest",
|
||||
rangeTest: data,
|
||||
},
|
||||
rangeTest: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Range Test Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export const RangeTest = (): JSX.Element => {
|
||||
const moduleEnabled = useWatch({
|
||||
control,
|
||||
name: "enabled",
|
||||
defaultValue: false,
|
||||
defaultValue: false
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -85,7 +85,7 @@ export const RangeTest = (): JSX.Element => {
|
||||
disabled={!moduleEnabled}
|
||||
suffix="Seconds"
|
||||
{...register("sender", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Controller
|
||||
|
||||
@@ -18,10 +18,10 @@ export const Serial = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<SerialValidation>({
|
||||
defaultValues: moduleConfig.serial,
|
||||
resolver: classValidatorResolver(SerialValidation),
|
||||
resolver: classValidatorResolver(SerialValidation)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -31,22 +31,22 @@ export const Serial = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setModuleConfig(
|
||||
{
|
||||
connection.setModuleConfig({
|
||||
moduleConfig: {
|
||||
payloadVariant: {
|
||||
oneofKind: "serial",
|
||||
serial: data,
|
||||
},
|
||||
serial: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Serial Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export const Serial = (): JSX.Element => {
|
||||
const moduleEnabled = useWatch({
|
||||
control,
|
||||
name: "enabled",
|
||||
defaultValue: false,
|
||||
defaultValue: false
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -96,7 +96,7 @@ export const Serial = (): JSX.Element => {
|
||||
description="Max transmit power in dBm"
|
||||
disabled={!moduleEnabled}
|
||||
{...register("rxd", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
@@ -105,7 +105,7 @@ export const Serial = (): JSX.Element => {
|
||||
description="Max transmit power in dBm"
|
||||
disabled={!moduleEnabled}
|
||||
{...register("txd", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
@@ -114,7 +114,7 @@ export const Serial = (): JSX.Element => {
|
||||
description="Max transmit power in dBm"
|
||||
disabled={!moduleEnabled}
|
||||
{...register("baud", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
@@ -123,7 +123,7 @@ export const Serial = (): JSX.Element => {
|
||||
description="Max transmit power in dBm"
|
||||
disabled={!moduleEnabled}
|
||||
{...register("timeout", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
@@ -132,7 +132,7 @@ export const Serial = (): JSX.Element => {
|
||||
description="Max transmit power in dBm"
|
||||
disabled={!moduleEnabled}
|
||||
{...register("mode", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
</Form>
|
||||
|
||||
@@ -18,10 +18,10 @@ export const StoreForward = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<StoreForwardValidation>({
|
||||
defaultValues: moduleConfig.storeForward,
|
||||
resolver: classValidatorResolver(StoreForwardValidation),
|
||||
resolver: classValidatorResolver(StoreForwardValidation)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -31,22 +31,22 @@ export const StoreForward = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setModuleConfig(
|
||||
{
|
||||
connection.setModuleConfig({
|
||||
moduleConfig: {
|
||||
payloadVariant: {
|
||||
oneofKind: "storeForward",
|
||||
storeForward: data,
|
||||
},
|
||||
storeForward: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Store & Forward Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export const StoreForward = (): JSX.Element => {
|
||||
const moduleEnabled = useWatch({
|
||||
control,
|
||||
name: "enabled",
|
||||
defaultValue: false,
|
||||
defaultValue: false
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -97,7 +97,7 @@ export const StoreForward = (): JSX.Element => {
|
||||
suffix="Records"
|
||||
disabled={!moduleEnabled}
|
||||
{...register("records", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
@@ -106,7 +106,7 @@ export const StoreForward = (): JSX.Element => {
|
||||
description="Max transmit power in dBm"
|
||||
disabled={!moduleEnabled}
|
||||
{...register("historyReturnMax", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
@@ -115,7 +115,7 @@ export const StoreForward = (): JSX.Element => {
|
||||
description="Max transmit power in dBm"
|
||||
disabled={!moduleEnabled}
|
||||
{...register("historyReturnWindow", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
</Form>
|
||||
|
||||
@@ -18,10 +18,10 @@ export const Telemetry = (): JSX.Element => {
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
reset,
|
||||
control,
|
||||
control
|
||||
} = useForm<TelemetryValidation>({
|
||||
defaultValues: moduleConfig.telemetry,
|
||||
resolver: classValidatorResolver(TelemetryValidation),
|
||||
resolver: classValidatorResolver(TelemetryValidation)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -31,22 +31,22 @@ export const Telemetry = (): JSX.Element => {
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
if (connection) {
|
||||
void toast.promise(
|
||||
connection.setModuleConfig(
|
||||
{
|
||||
connection.setModuleConfig({
|
||||
moduleConfig: {
|
||||
payloadVariant: {
|
||||
oneofKind: "telemetry",
|
||||
telemetry: data,
|
||||
},
|
||||
telemetry: data
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
callback: async () => {
|
||||
reset({ ...data });
|
||||
await Promise.resolve();
|
||||
}
|
||||
),
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved Telemetry Config, Restarting Node",
|
||||
error: "No response received",
|
||||
error: "No response received"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -90,7 +90,7 @@ export const Telemetry = (): JSX.Element => {
|
||||
suffix="Seconds"
|
||||
type="number"
|
||||
{...register("environmentUpdateInterval", {
|
||||
valueAsNumber: true,
|
||||
valueAsNumber: true
|
||||
})}
|
||||
/>
|
||||
<Controller
|
||||
|
||||
Reference in New Issue
Block a user