Updated channel editor + fixes

This commit is contained in:
Sacha Weatherstone
2021-11-16 23:15:41 +11:00
parent 053c015198
commit 1f8170130d
21 changed files with 802 additions and 397 deletions
+23 -10
View File
@@ -22,12 +22,13 @@ import {
setReady,
} from '@core/slices/meshtasticSlice';
import {
IBLEConnection,
IHTTPConnection,
ISerialConnection,
Protobuf,
SettingsManager,
Types,
} from '@meshtastic/meshtasticjs';
import { About } from '@pages/About';
import { Messages } from '@pages/Messages';
import { Nodes } from '@pages/Nodes/Index';
import { Settings } from '@pages/settings/Index';
@@ -55,15 +56,28 @@ const App = (): JSX.Element => {
'http://meshtastic.local';
React.useEffect(() => {
SettingsManager.debugMode = Protobuf.LogRecord_Level.TRACE;
const connectionMethod = localStorage.getItem('connectionMethod');
setConnection(new IHTTPConnection());
void connection.connect({
address: connectionURL,
tls: false,
receiveBatchRequests: false,
fetchInterval: 2000,
});
switch (connectionMethod) {
case 'serial':
setConnection(new ISerialConnection());
//show connection dialogue
break;
case 'bluetooth':
setConnection(new IBLEConnection());
//show connection dialogue
break;
default:
setConnection(new IHTTPConnection());
void connection.connect({
address: connectionURL,
tls: false,
receiveBatchRequests: false,
fetchInterval: 2000,
});
break;
}
SettingsManager.debugMode = Protobuf.LogRecord_Level.TRACE;
}, [hostOverrideEnabled, hostOverride, connectionURL]);
React.useEffect(() => {
@@ -168,7 +182,6 @@ const App = (): JSX.Element => {
{route.name === 'nodes' && <Nodes />}
{route.name === 'plugins' && <Plugins />}
{route.name === 'settings' && <Settings />}
{route.name === 'about' && <About />}
{route.name === false && <NotFound />}
</div>
</div>
+32 -38
View File
@@ -13,13 +13,17 @@ import { IconButton } from './generic/IconButton';
export interface ChannelProps {
channel: Protobuf.Channel;
hideEnabled?: boolean;
}
export const Channel = ({ channel }: ChannelProps): JSX.Element => {
export const Channel = ({
channel,
hideEnabled,
}: ChannelProps): JSX.Element => {
const [edit, setEdit] = React.useState(false);
const [loading, setLoading] = React.useState(false);
const { register, handleSubmit, formState } = useForm<{
const { register, handleSubmit } = useForm<{
enabled: boolean;
settings: {
name: string;
@@ -33,11 +37,12 @@ export const Channel = ({ channel }: ChannelProps): JSX.Element => {
};
}>({
defaultValues: {
enabled:
channel.role ===
(Protobuf.Channel_Role.PRIMARY || Protobuf.Channel_Role.SECONDARY)
? true
: false,
enabled: [
Protobuf.Channel_Role.SECONDARY,
Protobuf.Channel_Role.PRIMARY,
].find((role) => role === channel.role)
? true
: false,
settings: {
name: channel.settings?.name,
bandwidth: channel.settings?.bandwidth,
@@ -54,9 +59,12 @@ export const Channel = ({ channel }: ChannelProps): JSX.Element => {
const onSubmit = handleSubmit(async (data) => {
setLoading(true);
const adminChannel = Protobuf.Channel.create({
role: data.enabled
? Protobuf.Channel_Role.SECONDARY
: Protobuf.Channel_Role.DISABLED,
role:
channel.role === Protobuf.Channel_Role.PRIMARY
? Protobuf.Channel_Role.PRIMARY
: data.enabled
? Protobuf.Channel_Role.SECONDARY
: Protobuf.Channel_Role.DISABLED,
index: channel.index,
settings: {
...data.settings,
@@ -75,41 +83,24 @@ export const Channel = ({ channel }: ChannelProps): JSX.Element => {
{edit ? (
<>
{loading && <Loading />}
<div className="my-auto space-x-2">
<form>
<div className="flex space-x-2">
{/* @todo: change to disable & make primary buttons */}
<div className="flex my-auto">
{/* TODO: get gap working */}
<form className="gap-3">
{/* @todo: change to disable & make primary buttons */}
{!hideEnabled && (
<Checkbox
label="Enabled"
{...register('enabled', { valueAsNumber: true })}
/>
</div>
)}
<Input label="Name" {...register('settings.name')} />
<Input label="Pre-Shared Key" {...register('settings.psk')} />
<Input
label="Bandwidth"
type="number"
{...register('settings.bandwidth', { valueAsNumber: true })}
/>
<Input
label="Spread Factor"
type="number"
min={7}
max={12}
{...register('settings.spreadFactor', { valueAsNumber: true })}
/>
<Input
label="Coding Rate"
type="number"
{...register('settings.codingRate', { valueAsNumber: true })}
/>
<Input
label="Transmit Power"
type="number"
{...register('settings.txPower', { valueAsNumber: true })}
label="Pre-Shared Key"
type="password"
{...register('settings.psk')}
/>
<Checkbox
label="Upling Enabled"
label="Uplink Enabled"
{...register('settings.uplinkEnabled')}
/>
<Checkbox
@@ -132,7 +123,10 @@ export const Channel = ({ channel }: ChannelProps): JSX.Element => {
<div className="flex my-auto space-x-2">
<div
className={`h-3 my-auto w-3 rounded-full ${
channel.role === Protobuf.Channel_Role.SECONDARY
[
Protobuf.Channel_Role.SECONDARY,
Protobuf.Channel_Role.PRIMARY,
].find((role) => role === channel.role)
? 'bg-green-500'
: 'bg-gray-400'
}`}
+116
View File
@@ -0,0 +1,116 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import { Loading } from '@components/generic/Loading';
import { Protobuf } from '@meshtastic/meshtasticjs';
import { connection } from '../core/connection';
import { Card } from './generic/Card';
import { Checkbox } from './generic/form/Checkbox';
import { Input } from './generic/form/Input';
export interface LoraConfigProps {
channel: Protobuf.Channel;
}
export const LoraConfig = ({ channel }: LoraConfigProps): JSX.Element => {
const [loading, setLoading] = React.useState(false);
const { register, handleSubmit } = useForm<{
enabled: boolean;
settings: {
name: string;
bandwidth?: number;
codingRate?: number;
spreadFactor?: number;
downlinkEnabled?: boolean;
uplinkEnabled?: boolean;
txPower?: number;
psk?: string;
};
}>({
defaultValues: {
enabled:
channel.role ===
(Protobuf.Channel_Role.PRIMARY || Protobuf.Channel_Role.SECONDARY)
? true
: false,
settings: {
name: channel.settings?.name,
bandwidth: channel.settings?.bandwidth,
codingRate: channel.settings?.codingRate,
spreadFactor: channel.settings?.spreadFactor,
downlinkEnabled: channel.settings?.downlinkEnabled,
uplinkEnabled: channel.settings?.uplinkEnabled,
txPower: channel.settings?.txPower,
psk: new TextDecoder().decode(channel.settings?.psk),
},
},
});
const onSubmit = handleSubmit(async (data) => {
setLoading(true);
const adminChannel = Protobuf.Channel.create({
role: data.enabled
? Protobuf.Channel_Role.SECONDARY
: Protobuf.Channel_Role.DISABLED,
index: channel.index,
settings: {
...data.settings,
psk: new TextEncoder().encode(data.settings.psk),
},
});
await connection.setChannel(adminChannel, (): Promise<void> => {
setLoading(false);
return Promise.resolve();
});
});
return (
<Card>
{loading && <Loading />}
<div className="w-full max-w-3xl p-10 md:max-w-xl">
{/* TODO: get gap working */}
<form onSubmit={onSubmit}>
<Input
label="Bandwidth"
type="number"
suffix="MHz"
{...register('settings.bandwidth', { valueAsNumber: true })}
/>
<Input
label="Spread Factor"
type="number"
suffix="CPS"
min={7}
max={12}
{...register('settings.spreadFactor', {
valueAsNumber: true,
})}
/>
<Input
label="Coding Rate"
type="number"
{...register('settings.codingRate', { valueAsNumber: true })}
/>
<Input
label="Transmit Power"
type="number"
suffix="dBm"
{...register('settings.txPower', { valueAsNumber: true })}
/>
<Checkbox
label="Uplink Enabled"
{...register('settings.uplinkEnabled')}
/>
<Checkbox
label="Downlink Enabled"
{...register('settings.downlinkEnabled')}
/>
</form>
</div>
</Card>
);
};
+8 -1
View File
@@ -9,10 +9,12 @@ interface InputProps extends DefaultInputProps {
label?: string;
error?: string;
action?: JSX.Element;
prefix?: string;
suffix?: string;
}
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
function Input({ label, error, action, ...props }: InputProps, ref) {
function Input({ label, error, action, suffix, ...props }: InputProps, ref) {
return (
<div className="w-full">
{label && <Label label={label} error={error} />}
@@ -22,6 +24,11 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
className="w-full h-10 px-3 py-2 bg-transparent focus:outline-none"
{...props}
/>
{suffix && (
<span className="my-auto mr-3 text-sm font-medium text-gray-500 dark:text-gray-400">
{suffix}
</span>
)}
{action && <div className="flex mr-1">{action}</div>}
</InputWrapper>
</div>
+1 -1
View File
@@ -30,7 +30,7 @@ export const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
<select
ref={ref}
className={`w-full rounded-md bg-transparent focus:outline-none focus:border-primary ${
small ? 'p-1' : 'h-10 px-2'
small ? 'm-1' : 'h-10 mx-2'
}`}
disabled={
props.disabled
+1 -17
View File
@@ -1,12 +1,6 @@
import type React from 'react';
import {
FiGrid,
FiInfo,
FiMessageSquare,
FiPackage,
FiSettings,
} from 'react-icons/fi';
import { FiGrid, FiMessageSquare, FiPackage, FiSettings } from 'react-icons/fi';
import { Button } from '@components/generic/Button';
import { routes, useRoute } from '@core/router';
@@ -66,16 +60,6 @@ export const Navigation = ({
Settings
</Button>
</div>
<div onClick={onClick}>
<Button
icon={<FiInfo className="w-6 h-6" />}
className="w-full md:w-auto"
active={route.name === 'about'}
{...routes.about().link}
>
About
</Button>
</div>
</div>
);
};
+3
View File
@@ -8,6 +8,9 @@ type connectionType = IBLEConnection | IHTTPConnection | ISerialConnection;
export let connection: connectionType = new IHTTPConnection();
export const ble = new IBLEConnection();
export const serial = new ISerialConnection();
export const setConnection = (conn: connectionType): void => {
connection = conn;
};
-1
View File
@@ -5,5 +5,4 @@ export const { RouteProvider, useRoute, routes } = createRouter({
nodes: defineRoute('/nodes'),
plugins: defineRoute('/plugins'),
settings: defineRoute('/settings'),
about: defineRoute('/about'),
});
+15 -6
View File
@@ -21,7 +21,6 @@ interface MeshtasticState {
lastMeshInterraction: number;
ready: boolean;
myNodeInfo: Protobuf.MyNodeInfo;
myNode: Protobuf.NodeInfo;
users: Types.UserPacket[];
positionPackets: Types.PositionPacket[];
nodes: Protobuf.NodeInfo[];
@@ -38,13 +37,14 @@ const initialState: MeshtasticState = {
lastMeshInterraction: 0,
ready: false,
myNodeInfo: Protobuf.MyNodeInfo.create(),
myNode: Protobuf.NodeInfo.create(),
users: [],
positionPackets: [],
nodes: [],
channels: [],
preferences: Protobuf.RadioConfig_UserPreferences.create(),
messages: [],
//todo implement
// connectionMethod: localStorage.getItem('connectionMethod'),
hostOverrideEnabled:
localStorage.getItem('hostOverrideEnabled') === 'true' ?? false,
hostOverride: localStorage.getItem('hostOverride') ?? '',
@@ -83,12 +83,21 @@ export const meshtasticSlice = createSlice({
}
},
addPosition: (state, action: PayloadAction<Types.PositionPacket>) => {
state.positionPackets.push(action.payload);
if (
state.positionPackets.findIndex(
(position) => position.packet.from === action.payload.packet.from,
) !== -1
) {
state.positionPackets = state.positionPackets.map((position) => {
return position.packet.from === action.payload.packet.from
? action.payload
: position;
});
} else {
state.positionPackets.push(action.payload);
}
},
addNode: (state, action: PayloadAction<Protobuf.NodeInfo>) => {
if (action.payload.num === state.myNodeInfo.myNodeNum) {
state.myNode = action.payload;
}
if (
state.nodes.findIndex((node) => node.num === action.payload.num) !== -1
) {
-14
View File
@@ -1,14 +0,0 @@
import type React from 'react';
import { Card } from '@components/generic/Card';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
export const About = (): JSX.Element => {
return (
<PrimaryTemplate title="meshtastic-web" tagline="About">
<Card title="Project desc" description="...">
<p className="p-10">Content</p>
</Card>
</PrimaryTemplate>
);
};
+117
View File
@@ -0,0 +1,117 @@
import type React from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { FiMenu } from 'react-icons/fi';
import { FormFooter } from '@app/components/FormFooter';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Card } from '@components/generic/Card';
import { Checkbox } from '@components/generic/form/Checkbox';
import { Input } from '@components/generic/form/Input';
import { IconButton } from '@components/generic/IconButton';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
export interface ExternalNotificationProps {
navOpen?: boolean;
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>;
}
export const ExternalNotification = ({
navOpen,
setNavOpen,
}: ExternalNotificationProps): JSX.Element => {
const preferences = useAppSelector((state) => state.meshtastic.preferences);
const { register, handleSubmit, formState, reset, control } =
useForm<RadioConfig_UserPreferences>({
defaultValues: {
extNotificationPluginActive: preferences.extNotificationPluginActive,
extNotificationPluginAlertBell:
preferences.extNotificationPluginAlertBell,
extNotificationPluginAlertMessage:
preferences.extNotificationPluginAlertMessage,
extNotificationPluginEnabled: preferences.extNotificationPluginEnabled,
extNotificationPluginOutput: preferences.extNotificationPluginOutput,
extNotificationPluginOutputMs:
preferences.extNotificationPluginOutputMs,
},
});
const onSubmit = handleSubmit((data) => {
void connection.setPreferences(data);
});
const watchExternalNotificationPluginEnabled = useWatch({
control,
name: 'extNotificationPluginEnabled',
defaultValue: false,
});
return (
<PrimaryTemplate
title="External Notification"
tagline="Plugin"
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
setNavOpen && setNavOpen(!navOpen);
}}
/>
}
footer={
<FormFooter
dirty={formState.isDirty}
saveAction={onSubmit}
clearAction={reset}
/>
}
>
<div className="w-full space-y-4">
<Card>
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form onSubmit={onSubmit}>
<Checkbox
label="Plugin Enabled"
{...register('extNotificationPluginEnabled')}
/>
<Checkbox
label="Active"
disabled={!watchExternalNotificationPluginEnabled}
{...register('extNotificationPluginActive')}
/>
<Checkbox
label="Bell"
disabled={!watchExternalNotificationPluginEnabled}
{...register('extNotificationPluginAlertBell')}
/>
<Checkbox
label="Message"
disabled={!watchExternalNotificationPluginEnabled}
{...register('extNotificationPluginAlertMessage')}
/>
<Input
type="number"
label="Output"
disabled={!watchExternalNotificationPluginEnabled}
{...register('extNotificationPluginOutput', {
valueAsNumber: true,
})}
/>
<Input
type="number"
label="Output MS"
disabled={!watchExternalNotificationPluginEnabled}
{...register('extNotificationPluginOutputMs', {
valueAsNumber: true,
})}
/>
</form>
</div>
</Card>
</div>
</PrimaryTemplate>
);
};
+19 -2
View File
@@ -1,11 +1,13 @@
import type React from 'react';
import { FiFileText, FiRss } from 'react-icons/fi';
import { FiAlignLeft, FiBell, FiFileText, FiRss } from 'react-icons/fi';
import { PageLayout } from '@components/templates/PageLayout';
import { ExternalNotification } from './ExternalNotification';
import { Files } from './Files';
import { RangeTest } from './RangeTest';
import { Serial } from './Serial';
export const Plugins = (): JSX.Element => {
return (
@@ -22,8 +24,23 @@ export const Plugins = (): JSX.Element => {
description: 'HTTP only file browser',
icon: <FiFileText className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'External Notification',
description: 'External hardware alerts',
icon: <FiBell className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'Serial',
description: 'Send serial data over the mesh',
icon: <FiAlignLeft className="flex-shrink-0 w-6 h-6" />,
},
]}
panels={[
<RangeTest key={1} />,
<Files key={2} />,
<ExternalNotification key={3} />,
<Serial key={4} />,
]}
panels={[<RangeTest key={1} />, <Files key={2} />]}
/>
);
};
+1 -3
View File
@@ -1,7 +1,6 @@
import type React from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FiMenu } from 'react-icons/fi';
import { FormFooter } from '@app/components/FormFooter';
@@ -23,7 +22,6 @@ export const RangeTest = ({
navOpen,
setNavOpen,
}: RangeTestProps): JSX.Element => {
const { t } = useTranslation();
const preferences = useAppSelector((state) => state.meshtastic.preferences);
const { register, handleSubmit, formState, reset, control } =
@@ -66,7 +64,7 @@ export const RangeTest = ({
}
>
<div className="w-full space-y-4">
<Card title="Range Test" description="Settings">
<Card>
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form onSubmit={onSubmit}>
<Checkbox
+118
View File
@@ -0,0 +1,118 @@
import type React from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { FiMenu } from 'react-icons/fi';
import { FormFooter } from '@app/components/FormFooter';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Card } from '@components/generic/Card';
import { Checkbox } from '@components/generic/form/Checkbox';
import { Input } from '@components/generic/form/Input';
import { IconButton } from '@components/generic/IconButton';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
export interface SerialProps {
navOpen?: boolean;
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>;
}
export const Serial = ({ navOpen, setNavOpen }: SerialProps): JSX.Element => {
const preferences = useAppSelector((state) => state.meshtastic.preferences);
const { register, handleSubmit, formState, reset, control } =
useForm<RadioConfig_UserPreferences>({
defaultValues: {
serialpluginEnabled: preferences.serialpluginEnabled,
serialpluginEcho: preferences.serialpluginEcho,
serialpluginMode: preferences.serialpluginMode,
serialpluginRxd: preferences.serialpluginRxd,
serialpluginTimeout: preferences.serialpluginTimeout,
serialpluginTxd: preferences.serialpluginTxd,
},
});
const onSubmit = handleSubmit((data) => {
void connection.setPreferences(data);
});
const watchSerialPluginEnabled = useWatch({
control,
name: 'serialpluginEnabled',
defaultValue: false,
});
return (
<PrimaryTemplate
title="Serial"
tagline="Plugin"
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
setNavOpen && setNavOpen(!navOpen);
}}
/>
}
footer={
<FormFooter
dirty={formState.isDirty}
saveAction={onSubmit}
clearAction={reset}
/>
}
>
<div className="w-full space-y-4">
<Card>
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form onSubmit={onSubmit}>
<Checkbox
label="Plugin Enabled"
{...register('serialpluginEnabled')}
/>
<Checkbox
label="Echo"
disabled={!watchSerialPluginEnabled}
{...register('serialpluginEcho')}
/>
<Input
type="number"
label="RX"
disabled={!watchSerialPluginEnabled}
{...register('serialpluginRxd', {
valueAsNumber: true,
})}
/>
<Input
type="number"
label="TX"
disabled={!watchSerialPluginEnabled}
{...register('serialpluginTxd', {
valueAsNumber: true,
})}
/>
<Input
type="number"
label="Mode"
disabled={!watchSerialPluginEnabled}
{...register('serialpluginMode', {
valueAsNumber: true,
})}
/>
<Input
type="number"
label="Timeout"
disabled={!watchSerialPluginEnabled}
{...register('serialpluginTimeout', {
valueAsNumber: true,
})}
/>
</form>
</div>
</Card>
</div>
</PrimaryTemplate>
);
};
+8 -6
View File
@@ -1,9 +1,9 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { FiCode, FiMenu, FiSave } from 'react-icons/fi';
import JSONPretty from 'react-json-pretty';
import { LoraConfig } from '@app/components/LoraConfig';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Channel } from '@components/Channel';
@@ -22,10 +22,7 @@ export const Channels = ({
navOpen,
setNavOpen,
}: ChannelsProps): JSX.Element => {
const { t } = useTranslation();
const channels = useAppSelector((state) => state.meshtastic.channels).filter(
(channel) => channel.index !== 0,
);
const channels = useAppSelector((state) => state.meshtastic.channels);
const [debug, setDebug] = React.useState(false);
return (
@@ -61,11 +58,16 @@ export const Channels = ({
}
>
<div className="space-y-4">
{channels[0] && <LoraConfig channel={channels[0]} />}
<Card>
<Cover enabled={debug} content={<JSONPretty data={channels} />} />
<div className="w-full p-4 space-y-2 md:p-10">
{channels.map((channel) => (
<Channel key={channel.index} channel={channel} />
<Channel
key={channel.index}
channel={channel}
hideEnabled={channel.index === 0}
/>
))}
<div className="flex justify-between">
+37 -35
View File
@@ -1,12 +1,12 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FiCheck, FiMenu } from 'react-icons/fi';
import JSONPretty from 'react-json-pretty';
import { FormFooter } from '@app/components/FormFooter';
import { connection, setConnection } from '@app/core/connection';
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
import { ble, connection, serial, setConnection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Button } from '@components/generic/Button';
import { Card } from '@components/generic/Card';
import { Checkbox } from '@components/generic/form/Checkbox';
@@ -40,20 +40,18 @@ export const Connection = ({
navOpen,
setNavOpen,
}: ConnectionProps): JSX.Element => {
const dispatch = useAppDispatch();
const [selectedConnType, setSelectedConnType] = React.useState(connType.HTTP);
const [bleDevices, setBleDevices] = React.useState<BluetoothDevice[]>([]);
const [serialDevices, setSerialDevices] = React.useState<SerialPort[]>([]);
const [httpIpSource, setHttpIpSource] = React.useState<'local' | 'remote'>(
'local',
);
const { t } = useTranslation();
const hostOverrideEnabled = useAppSelector(
(state) => state.meshtastic.hostOverrideEnabled,
);
const hostOverride = useAppSelector((state) => state.meshtastic.hostOverride);
const { register, handleSubmit, formState, reset } = useForm<{
const { formState, reset } = useForm<{
method: connType;
}>({
defaultValues: {
@@ -83,30 +81,26 @@ export const Connection = ({
await connection.connect(params);
};
const updateBleDeviceList = async (): Promise<void> => {
const updateBleDeviceList = React.useCallback(async (): Promise<void> => {
const devices = await ble.getDevices();
setBleDevices(devices);
};
}, []);
const updateSerialDeviceList = async (): Promise<void> => {
const updateSerialDeviceList = React.useCallback(async (): Promise<void> => {
const devices = await serial.getPorts();
console.log(devices);
setSerialDevices(devices);
};
}, []);
// React.useEffect(() => {
// if (selectedConnType === connType.BLE) {
// void updateBleDeviceList();
// }
// if (selectedConnType === connType.SERIAL) {
// void updateSerialDeviceList();
// }
// }, [selectedConnType]);
const onSubmit = handleSubmit((data) => {
// void connection.setOwner(data);
});
React.useEffect(() => {
if (selectedConnType === connType.BLE) {
void updateBleDeviceList();
}
if (selectedConnType === connType.SERIAL) {
void updateSerialDeviceList();
}
}, [selectedConnType, updateBleDeviceList, updateSerialDeviceList]);
const connectionURL: string = hostOverrideEnabled
? hostOverride
@@ -115,9 +109,6 @@ export const Connection = ({
: (import.meta.env.VITE_PUBLIC_DEVICE_IP as string) ??
'http://meshtastic.local';
const ble = new IBLEConnection();
const serial = new ISerialConnection();
return (
<PrimaryTemplate
title="Connection"
@@ -133,14 +124,16 @@ export const Connection = ({
footer={
<FormFooter
dirty={formState.isDirty}
saveAction={onSubmit}
saveAction={(): void => {
return;
}}
clearAction={reset}
/>
}
>
<Card>
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form className="space-y-2" onSubmit={onSubmit}>
<form className="space-y-2">
<Select
label="Method"
optionsEnum={connType}
@@ -184,7 +177,7 @@ export const Connection = ({
</Button>
<Button
border
onClick={async () => {
onClick={async (): Promise<void> => {
await ble.getDevice();
}}
>
@@ -193,7 +186,7 @@ export const Connection = ({
</div>
<div className="space-y-2">
<div>Previously connected devices</div>
{bleDevices.map((device) => (
{bleDevices.map((device, index) => (
<div
onClick={async (): Promise<void> => {
console.log('clicked');
@@ -203,7 +196,7 @@ export const Connection = ({
});
}}
className="flex justify-between p-2 bg-gray-700 rounded-md"
key={device.id}
key={index}
>
<div className="my-auto">{device.name}</div>
<IconButton
@@ -224,13 +217,13 @@ export const Connection = ({
{selectedConnType === connType.SERIAL && (
<div>
<div className="flex space-x-2">
<Button border onClick={updateBleDeviceList}>
<Button border onClick={updateSerialDeviceList}>
Refresh List
</Button>
<Button
border
onClick={async () => {
await serial.getPort();
onClick={async (): Promise<void> => {
console.log(await serial.getPort());
}}
>
New Device
@@ -238,10 +231,10 @@ export const Connection = ({
</div>
<div className="space-y-2">
<div>Previously connected devices</div>
{serialDevices.map((device) => (
{serialDevices.map((device, index) => (
<div
className="flex justify-between p-2 bg-gray-700 rounded-md"
key={device.getInfo().usbProductId}
key={index}
>
<div className="my-auto">
{device.getInfo().usbProductId}
@@ -256,12 +249,21 @@ export const Connection = ({
}}
icon={<FiCheck />}
/>
<JSONPretty data={device.getInfo()} />
</div>
))}
</div>
</div>
)}
</form>
<Button
border
onClick={(): void => {
connection.disconnect();
}}
>
Disconnect
</Button>
</div>
</Card>
</PrimaryTemplate>
+2 -1
View File
@@ -82,8 +82,9 @@ export const Position = ({
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form className="space-y-2" onSubmit={onSubmit}>
<Input
label="Broadcast Interval (seconds)"
label="Broadcast Interval"
type="number"
suffix="Seconds"
{...register('positionBroadcastSecs', { valueAsNumber: true })}
/>
<Select
-2
View File
@@ -1,7 +1,6 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FiCode, FiMenu } from 'react-icons/fi';
import JSONPretty from 'react-json-pretty';
import { base16 } from 'rfc4648';
@@ -25,7 +24,6 @@ export interface UserProps {
}
export const User = ({ navOpen, setNavOpen }: UserProps): JSX.Element => {
const { t } = useTranslation();
const [debug, setDebug] = React.useState(false);
const [loading, setLoading] = React.useState(false);
const dispatch = useAppDispatch();