21 changed files with 802 additions and 397 deletions
File diff suppressed because it is too large
@ -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> |
|||
); |
|||
}; |
|||
@ -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> |
|||
); |
|||
}; |
|||
@ -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> |
|||
); |
|||
}; |
|||
@ -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> |
|||
); |
|||
}; |
|||
Loading…
Reference in new issue