mirror of
https://github.com/liamcottle/reticulum-meshchat.git
synced 2026-04-28 00:20:48 +00:00
1562 lines
74 KiB
Vue
1562 lines
74 KiB
Vue
<template>
|
|
<div class="flex flex-col flex-1 overflow-hidden min-w-full sm:min-w-[500px] dark:bg-zinc-950">
|
|
<div class="overflow-y-auto p-2 space-y-2">
|
|
|
|
<!-- community interfaces -->
|
|
<div v-if="!isEditingInterface && config != null && config.show_suggested_community_interfaces"
|
|
class="bg-white rounded shadow divide-y divide-gray-200 dark:bg-zinc-900">
|
|
<div class="flex p-2">
|
|
<div class="my-auto mr-auto">
|
|
<div class="font-bold dark:text-white">Community Interfaces</div>
|
|
<div class="text-sm dark:text-gray-100">These TCP interfaces serve as a quick way to test Reticulum. We
|
|
suggest running your own as these may not always be available.
|
|
</div>
|
|
</div>
|
|
<div class="my-auto ml-2">
|
|
<button @click="updateConfig({'show_suggested_community_interfaces': false})" type="button"
|
|
class="text-gray-700 bg-gray-100 hover:bg-gray-200 p-2 rounded-full dark:bg-zinc-600 dark:text-white dark:hover:bg-zinc-700 dark:focus-visible:outline-zinc-500">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
|
|
<path
|
|
d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94 6.28 5.22Z"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="divide-y divide-gray-200 dark:text-white">
|
|
|
|
<div class="flex px-2 py-1">
|
|
<div class="my-auto mr-auto">
|
|
<div>RNS Testnet Amsterdam</div>
|
|
<div class="text-xs">amsterdam.connect.reticulum.network:4965</div>
|
|
</div>
|
|
<div class="ml-2 my-auto">
|
|
<button
|
|
@click="newInterfaceName='RNS Testnet Amsterdam';newInterfaceType='TCPClientInterface';newInterfaceTargetHost='amsterdam.connect.reticulum.network';newInterfaceTargetPort='4965'"
|
|
type="button"
|
|
class="inline-flex items-center gap-x-1 rounded-md bg-gray-500 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-gray-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500">
|
|
<span>Use Interface</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex px-2 py-1">
|
|
<div class="my-auto mr-auto">
|
|
<div>RNS Testnet BetweenTheBorders</div>
|
|
<div class="text-xs">reticulum.betweentheborders.com:4242</div>
|
|
</div>
|
|
<div class="ml-2 my-auto">
|
|
<button
|
|
@click="newInterfaceName='RNS Testnet BetweenTheBorders';newInterfaceType='TCPClientInterface';newInterfaceTargetHost='reticulum.betweentheborders.com';newInterfaceTargetPort='4242'"
|
|
type="button"
|
|
class="inline-flex items-center gap-x-1 rounded-md bg-gray-500 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-gray-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500">
|
|
<span>Use Interface</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- add interface form -->
|
|
<div class="bg-white rounded shadow divide-y divide-gray-200 dark:bg-zinc-900">
|
|
<div class="p-2 font-bold dark:text-white">
|
|
<span v-if="isEditingInterface">Edit Interface</span>
|
|
<span v-else>Add Interface</span>
|
|
</div>
|
|
<div class="p-2 space-y-3">
|
|
<!-- iGeneric interface settings -->
|
|
<!-- interface name -->
|
|
<div>
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Name</label>
|
|
<input type="text" :disabled="isEditingInterface" placeholder="New Interface Name"
|
|
v-model="newInterfaceName"
|
|
class="border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
:class="[ isEditingInterface ? 'cursor-not-allowed bg-gray-200' : 'bg-gray-50' ]">
|
|
<div class="text-xs text-gray-600 dark:text-zinc-300">Interface names must be unique.</div>
|
|
</div>
|
|
|
|
<!-- interface type -->
|
|
<div class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Type</label>
|
|
<select v-model="newInterfaceType"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
<option disabled selected>--</option>
|
|
<option value="AutoInterface">Auto Interface</option>
|
|
<option disabled selected>RNodes</option>
|
|
<option value="RNodeInterface">RNode Interface</option>
|
|
<option value="RNodeMultiInterface">RNode Multi Interface</option>
|
|
<option disabled selected>IP Networks</option>
|
|
<option value="TCPClientInterface">TCP Client Interface</option>
|
|
<option value="TCPServerInterface">TCP Server Interface</option>
|
|
<option value="UDPInterface">UDP Interface</option>
|
|
<option value="I2PInterface">I2P Interface</option>
|
|
<option disabled selected>Hardware</option>
|
|
<option value="SerialInterface">Serial Interface</option>
|
|
<option value="KISSInterface">KISS Interface</option>
|
|
<option hidden value="AX25KISSInterface">AX.25 KISS Interface</option>
|
|
<option disabled selected>Other</option>
|
|
<option value="PipeInterface">Pipe Interface</option>
|
|
</select>
|
|
<div class="text-xs text-gray-600 dark:text-zinc-300">
|
|
Need help? <a class="text-blue-500 underline" href="https://reticulum.network/manual/interfaces.html"
|
|
target="_blank">Reticulum Docs: Configuring Interfaces</a>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div v-if="newInterfaceType === 'AutoInterface'" class="mb-4">
|
|
|
|
<div
|
|
@click="toggleAllSettings"
|
|
class="flex items-center justify-between cursor-pointer p-2 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
|
>
|
|
<span
|
|
class="font-semibold text-gray-900 dark:text-gray-100">Additional AutoInterface Settings</span>
|
|
<span class="w-5 h-5 text-gray-600 dark:text-gray-300 transform transition-transform duration-200 "
|
|
:class="{ 'rotate-180': showAllSettings }"
|
|
>
|
|
▲
|
|
</span>
|
|
</div>
|
|
|
|
<div
|
|
v-show="showAllSettings"
|
|
class="mt-3 space-y-4 p-4 border border-gray-200 rounded-lg bg-white dark:bg-zinc-900 dark:border-zinc-700"
|
|
>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400">ⓘ These settings are optional</p>
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Group ID</label>
|
|
<input
|
|
type="text"
|
|
v-model="newInterfaceGroupID"
|
|
placeholder="reticulum"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div class="flex-1">
|
|
<label class="text-sm dark:text-zinc-100">Multicast Address Type</label>
|
|
<select
|
|
v-model="newInterfaceMulticastAddressType"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600"
|
|
>
|
|
<option value="permanent">
|
|
Permanent
|
|
</option>
|
|
<option value="temporary">
|
|
Temporary
|
|
</option>
|
|
</select>
|
|
</div>
|
|
|
|
|
|
<div class="flex items-center space-x-4 mt-4">
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Network devices</label>
|
|
<input
|
|
type="text"
|
|
v-model="newInterfaceDevices"
|
|
placeholder="wlan0,eth1"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Ignored Devices</label>
|
|
<input
|
|
type="text"
|
|
v-model="newInterfaceIgnoredDevices"
|
|
placeholder="tun0,eth0"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center space-x-4 mt-4">
|
|
<div class="flex-1">
|
|
<label class="text-sm dark:text-zinc-100">Discovery Scope</label>
|
|
<select
|
|
v-model="newInterfaceDiscoveryScope"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600"
|
|
>
|
|
<option value="global">
|
|
Global
|
|
</option>
|
|
<option value="admin">
|
|
Admin
|
|
</option>
|
|
<option value="organisation">
|
|
Organisation
|
|
</option>
|
|
<option value="site">
|
|
Site
|
|
</option>
|
|
<option value="link">
|
|
Link
|
|
</option>
|
|
|
|
</select>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Discovery Port</label>
|
|
<input
|
|
type="number"
|
|
v-model="newInterfaceDiscoveryPort"
|
|
placeholder="48555"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Data Port</label>
|
|
<input
|
|
type="number"
|
|
v-model="newInterfaceDataPort"
|
|
placeholder="49555"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- TCPClientInterface -->
|
|
<!-- interface target host -->
|
|
<div v-if="newInterfaceType === 'TCPClientInterface'" class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Target Host</label>
|
|
<input type="text" placeholder="example.com" v-model="newInterfaceTargetHost"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
</div>
|
|
|
|
<!-- interface target port -->
|
|
<div v-if="newInterfaceType === 'TCPClientInterface'" class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Target Port</label>
|
|
<input type="text" placeholder="1234" v-model="newInterfaceTargetPort"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
</div>
|
|
|
|
<div v-if="newInterfaceType === 'TCPClientInterface'" class="mb-4">
|
|
|
|
<div
|
|
@click="toggleAllSettings"
|
|
class="flex items-center justify-between cursor-pointer p-2 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
|
>
|
|
<span
|
|
class="font-semibold text-gray-900 dark:text-gray-100">Additional TCP Client Interface settings</span>
|
|
<span class="w-5 h-5 text-gray-600 dark:text-gray-300 transform transition-transform duration-200 "
|
|
:class="{ 'rotate-180': showAllSettings }"
|
|
>
|
|
▲
|
|
</span>
|
|
</div>
|
|
|
|
<div
|
|
v-show="showAllSettings"
|
|
class="mt-3 space-y-4 p-4 border border-gray-200 rounded-lg bg-white dark:bg-zinc-900 dark:border-zinc-700"
|
|
>
|
|
<div class="flex items-start">
|
|
<div class="flex flex-col">
|
|
<label for="kiss-framing" class="text-sm font-medium text-gray-900 dark:text-zinc-100">
|
|
Enable KISS Framing
|
|
</label>
|
|
<span class="text-sm text-gray-500 dark:text-zinc-300">
|
|
ⓘ Enabled when connecting to software that uses KISS framing such as packet radio sound modems.
|
|
For KISS connections through serial hardware select "KISS Interface" as the interface type.
|
|
</span>
|
|
</div>
|
|
<input
|
|
id="kiss-framing"
|
|
type="checkbox"
|
|
v-model="newInterfaceKISSFramingEnabled"
|
|
class="ml-auto h-5 w-5 rounded border-gray-300 text-blue-600 focus:ring focus:ring-blue-500 dark:border-zinc-600 dark:bg-zinc-800 dark:focus:ring-blue-600"
|
|
/>
|
|
</div>
|
|
<div class="flex items-start">
|
|
<div class="flex flex-col">
|
|
<label for="i2p-tunneled" class="text-sm font-medium text-gray-900 dark:text-zinc-100">
|
|
Enable I2P tunneling
|
|
</label>
|
|
<span class="text-sm text-gray-500 dark:text-zinc-300">
|
|
ⓘ Enables tunnelling through an I2P Connection using the TCPClientInterface
|
|
</span>
|
|
</div>
|
|
<input
|
|
id="i2p-tunneled"
|
|
type="checkbox"
|
|
v-model="newInterfaceI2PTunnelingEnabled"
|
|
class="ml-auto h-5 w-5 rounded border-gray-300 text-blue-600 focus:ring focus:ring-blue-500 dark:border-zinc-600 dark:bg-zinc-800 dark:focus:ring-blue-600"
|
|
/>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- TCPServerInterface -->
|
|
<!-- interface listen ip -->
|
|
<div v-if="newInterfaceType === 'TCPServerInterface' || newInterfaceType === 'UDPInterface'" class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Listen IP</label>
|
|
<input type="text" placeholder="0.0.0.0" v-model="newInterfaceListenIp"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
</div>
|
|
|
|
<!-- interface listen port -->
|
|
<div v-if="newInterfaceType === 'TCPServerInterface' || newInterfaceType === 'UDPInterface'" class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Listen Port</label>
|
|
<input type="text" placeholder="1234" v-model="newInterfaceListenPort"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
</div>
|
|
|
|
|
|
<div v-if="newInterfaceType === 'TCPServerInterface' || newInterfaceType === 'UDPInterface'" class="mb-4">
|
|
|
|
<div
|
|
@click="toggleAllSettings"
|
|
class="flex items-center justify-between cursor-pointer p-2 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
|
>
|
|
<span
|
|
class="font-semibold text-gray-900 dark:text-gray-100">Additional Server Interface settings</span>
|
|
<span class="w-5 h-5 text-gray-600 dark:text-gray-300 transform transition-transform duration-200 "
|
|
:class="{ 'rotate-180': showAllSettings }"
|
|
>
|
|
▲
|
|
</span>
|
|
</div>
|
|
|
|
<div
|
|
v-show="showAllSettings"
|
|
class="mt-3 space-y-4 p-4 border border-gray-200 rounded-lg bg-white dark:bg-zinc-900 dark:border-zinc-700">
|
|
|
|
<div class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Network device</label>
|
|
<span class="text-sm text-gray-500 dark:text-zinc-300">
|
|
ⓘ Binds the interface to a specific network interface
|
|
</span>
|
|
<input type="text" placeholder="eth0" v-model="newInterfaceNetworkDevice"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
</div>
|
|
<div class="flex items-start">
|
|
<div class="flex flex-col">
|
|
<label for="prefer-ipv6" class="text-sm font-medium text-gray-900 dark:text-zinc-100">
|
|
Prefer IPv6
|
|
</label>
|
|
<span class="text-sm text-gray-500 dark:text-zinc-300">
|
|
ⓘ Binds the TCP Server Interface to an IPv6 address
|
|
</span>
|
|
</div>
|
|
<input
|
|
id="prefer-ipv6"
|
|
type="checkbox"
|
|
value="1"
|
|
v-model="newInterfacePreferIPV6"
|
|
class="ml-auto h-5 w-5 rounded border-gray-300 text-blue-600 focus:ring focus:ring-blue-500 dark:border-zinc-600 dark:bg-zinc-800 dark:focus:ring-blue-600"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- UDPInterface -->
|
|
<!-- interface forward ip -->
|
|
<div v-if="newInterfaceType === 'UDPInterface'" class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Forward IP</label>
|
|
<input type="text" placeholder="255.255.255.255" v-model="newInterfaceForwardIp"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
</div>
|
|
|
|
<!-- interface listen port -->
|
|
<div v-if="newInterfaceType === 'UDPInterface'" class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Forward Port</label>
|
|
<input type="text" placeholder="1234" v-model="newInterfaceForwardPort"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
</div>
|
|
|
|
<!-- I2PInterface -->
|
|
<div v-if="newInterfaceType === 'I2PInterface'" class="mb-4">
|
|
<span class="text-sm text-gray-500 dark:text-zinc-300">
|
|
ⓘ To use the I2P interface, you must have an I2P router running on your system. When the I2P Interface is added for the first time Reticulum will generate a new I2P address for the interface and begin listening for inbound traffic.
|
|
</span>
|
|
|
|
<div class="mt-4">
|
|
<div
|
|
@click="toggleI2PPeerAccordion"
|
|
class="flex items-center justify-between cursor-pointer p-2 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
|
>
|
|
<span class="font-semibold text-gray-900 dark:text-gray-100">Manage Peers</span>
|
|
<span
|
|
class="w-5 h-5 text-gray-600 dark:text-gray-300 transform transition-transform duration-200"
|
|
:class="{ 'rotate-180': I2PSettings.showPeerAccordion }"
|
|
>
|
|
▲
|
|
</span>
|
|
</div>
|
|
|
|
<div
|
|
v-show="I2PSettings.showPeerAccordion"
|
|
class="mt-3 space-y-4 p-4 border border-gray-200 rounded-lg bg-white dark:bg-zinc-900 dark:border-zinc-700"
|
|
>
|
|
<div v-for="(peer, index) in I2PSettings.newInterfacePeers" :key="index"
|
|
class="flex items-center space-x-2">
|
|
<input
|
|
type="text"
|
|
v-model="I2PSettings.newInterfacePeers[index]"
|
|
placeholder="Enter peer address (ex: 5urvjicpzi7q3ybztsef4i5ow2aq4soktfj7zedz53s47r54jnqq.b32.i2p)"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600"
|
|
/>
|
|
<button
|
|
@click="removeI2PPeer(index)"
|
|
type="button"
|
|
class="bg-red-500 hover:bg-red-400 text-white text-sm p-2 rounded-lg"
|
|
>
|
|
Remove
|
|
</button>
|
|
</div>
|
|
<button
|
|
@click="addI2PPeer('')"
|
|
type="button"
|
|
class="bg-green-500 hover:bg-green-400 text-white text-sm px-4 py-2 rounded-lg"
|
|
>
|
|
Add Peer
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- RNode interface -->
|
|
<!-- interface port -->
|
|
<div v-if="newInterfaceType === 'RNodeInterface'" class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Port</label>
|
|
<select v-model="newInterfacePort"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
<option v-for="comport of comports" :value="comport.device">{{ comport.device }} (Product:
|
|
{{ comport.product ?? '?' }}, Serial: {{ comport.serial ?? '?' }})
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<div v-if="newInterfaceType === 'RNodeInterface'" class="mb-2 flex flex-wrap items-start gap-4">
|
|
<!-- interface Frequency -->
|
|
<div class="flex-1">
|
|
<div class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Frequency</label>
|
|
<div class="flex items-center gap-2">
|
|
<div class="flex flex-col">
|
|
<input
|
|
type="number"
|
|
v-model.number="RNodeGHzValue"
|
|
min="0"
|
|
placeholder="GHz"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600"
|
|
/>
|
|
<label class="text-xs text-gray-500 dark:text-zinc-400 mt-1 text-center">GHz</label>
|
|
</div>
|
|
<div class="flex flex-col">
|
|
<input
|
|
type="number"
|
|
v-model.number="RNodeMHzValue"
|
|
min="0"
|
|
placeholder="MHz"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600"
|
|
/>
|
|
<label class="text-xs text-gray-500 dark:text-zinc-400 mt-1 text-center">MHz</label>
|
|
</div>
|
|
<div class="flex flex-col">
|
|
<input
|
|
type="number"
|
|
v-model.number="RNodekHzValue"
|
|
min="0"
|
|
placeholder="kHz"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600"
|
|
/>
|
|
<label class="text-xs text-gray-500 dark:text-zinc-400 mt-1 text-center">kHz</label>
|
|
</div>
|
|
</div>
|
|
<div class="text-sm text-gray-600 dark:text-zinc-100 mt-2 font-bold">
|
|
Selected Frequency: {{ formattedFrequency }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- interface Bandwidth -->
|
|
<div class="flex-1">
|
|
<div class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Bandwidth</label>
|
|
<select
|
|
v-model="newInterfaceBandwidth"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600"
|
|
>
|
|
<option v-for="bandwidth in RNodeInterfaceDefaults.bandwidths" :value="bandwidth">
|
|
{{ bandwidth / 1000 }} KHz
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- interface txpower -->
|
|
<div v-if="newInterfaceType === 'RNodeInterface'" class="mb-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Transmit Power (dBm)</label>
|
|
<input v-model="newInterfaceTxpower" type="number"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
</div>
|
|
|
|
<div v-if="newInterfaceType === 'RNodeInterface'" class="mb-2 flex flex-wrap items-start gap-4">
|
|
<!-- interface spreading factor -->
|
|
<div class="flex-1">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Spreading Factor</label>
|
|
<select v-model="newInterfaceSpreadingFactor"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
<option v-for="spreadingfactor in RNodeInterfaceDefaults.spreadingfactors" :value="spreadingfactor">
|
|
{{ spreadingfactor }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- interface coding rate -->
|
|
<div class="flex-1">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Coding Rate</label>
|
|
<select v-model="newInterfaceCodingRate"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
<option v-for="codingrate in RNodeInterfaceDefaults.codingrates" :value="codingrate">{{
|
|
codingrate
|
|
}}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<!-- RNode LoRa parameters-->
|
|
<div v-if="newInterfaceType === 'RNodeInterface'" class="mb-4">
|
|
|
|
<div
|
|
@click="toggleRNodeLoRaParameters"
|
|
class="flex items-center justify-between cursor-pointer p-2 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
|
>
|
|
<span
|
|
class="font-semibold text-gray-900 dark:text-gray-100">Show on-air RNode bitrate & link budget</span>
|
|
<span class="w-5 h-5 text-gray-600 dark:text-gray-300 transform transition-transform duration-200 "
|
|
:class="{ 'rotate-180': showRNodeLoRaParameters }"
|
|
>
|
|
▲
|
|
</span>
|
|
</div>
|
|
|
|
<div
|
|
v-show="showRNodeLoRaParameters"
|
|
class="mt-3 space-y-6 p-6 border border-gray-300 rounded-lg bg-gray-50 dark:bg-zinc-900 dark:border-zinc-700"
|
|
>
|
|
<div class="space-y-2">
|
|
<div class="flex items-start space-x-2">
|
|
<div class="w-1/4">
|
|
<label class="block text-sm font-medium text-gray-800 dark:text-white">
|
|
Antenna Gain (dBi)
|
|
</label>
|
|
<input
|
|
type="number"
|
|
v-model.number="RNodeInterfaceLoRaParameters.antennaGain"
|
|
placeholder="Enter gain"
|
|
class="w-1/2 bg-gray-100 border border-gray-300 rounded-lg py-1 px-2 text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-zinc-800 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
<p class="text-xs text-gray-600 dark:text-gray-400 mt-1">ⓘ A stub or PCB antenna might have around 1
|
|
dBi of
|
|
gain, where a directional Yagi might have 5 dBi of gain.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white text-center">
|
|
On-Air Calculations
|
|
</h3>
|
|
<div class="grid grid-cols-3 gap-4 text-center mt-2">
|
|
<div class="bg-gray-100 p-4 rounded-lg shadow dark:bg-zinc-800">
|
|
<div class="text-sm font-medium text-gray-700 dark:text-gray-300">Sensitivity</div>
|
|
<div class="text-xl font-bold text-gray-900 dark:text-white">
|
|
{{ RNodeInterfaceLoRaParameters.sensitivity }}
|
|
</div>
|
|
</div>
|
|
<div class="bg-gray-100 p-4 rounded-lg shadow dark:bg-zinc-800">
|
|
<div class="text-sm font-medium text-gray-700 dark:text-gray-300">Data Rate</div>
|
|
<div class="text-xl font-bold text-gray-900 dark:text-white">
|
|
{{ RNodeInterfaceLoRaParameters.dataRate }}
|
|
</div>
|
|
</div>
|
|
<div class="bg-gray-100 p-4 rounded-lg shadow dark:bg-zinc-800">
|
|
<div class="text-sm font-medium text-gray-700 dark:text-gray-300">Link Budget</div>
|
|
<div class="text-xl font-bold text-gray-900 dark:text-white">
|
|
{{ RNodeInterfaceLoRaParameters.linkBudget }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<div v-if="newInterfaceType === 'RNodeInterface'" class="mb-4">
|
|
|
|
<div
|
|
@click="toggleAllSettings"
|
|
class="flex items-center justify-between cursor-pointer p-2 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
|
>
|
|
<span
|
|
class="font-semibold text-gray-900 dark:text-gray-100">Additional RNode Interface Settings</span>
|
|
<span class="w-5 h-5 text-gray-600 dark:text-gray-300 transform transition-transform duration-200 "
|
|
:class="{ 'rotate-180': showAllSettings }"
|
|
>
|
|
▲
|
|
</span>
|
|
</div>
|
|
|
|
<div
|
|
v-show="showAllSettings"
|
|
class="mt-3 space-y-4 p-4 border border-gray-200 rounded-lg bg-white dark:bg-zinc-900 dark:border-zinc-700">
|
|
|
|
<div class="flex items-center space-x-4">
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Callsign</label>
|
|
<input
|
|
type="text"
|
|
v-model="newInterfaceCallsign"
|
|
placeholder="Enter callsign"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Callsign ID Interval</label>
|
|
<input
|
|
type="number"
|
|
v-model="newInterfaceIDInterval"
|
|
placeholder="Enter interval (seconds)"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center space-x-4">
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Airtime Limit
|
|
(Short)</label>
|
|
<input
|
|
type="number"
|
|
v-model="newInterfaceAirtimeLimitShort"
|
|
placeholder="Enter short airtime limit (seconds)"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Airtime Limit (Long)</label>
|
|
<input
|
|
type="number"
|
|
v-model="newInterfaceAirtimeLimitLong"
|
|
placeholder="Enter long airtime limit (seconds)"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RNodeMultiInterface -->
|
|
<div v-if="newInterfaceType === 'RNodeMultiInterface'" class="mb-2">
|
|
<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">ⓘ The RNode Multi Interface is used for custom
|
|
devices
|
|
with multiple LoRa transceivers such as the openCom XL. </p>
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Port</label>
|
|
<select v-model="newInterfacePort"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
<option v-for="comport of comports" :value="comport.device">{{ comport.device }} (Product:
|
|
{{ comport.product ?? '?' }}, Serial: {{ comport.serial ?? '?' }})
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<div v-if="newInterfaceType === 'RNodeMultiInterface'" class="mb-4">
|
|
<div
|
|
@click="toggleRNodeSubInterfaces"
|
|
class="flex items-center justify-between cursor-pointer p-2 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-zinc-800 dark:hover:bg-zinc-700">
|
|
<span class="font-semibold text-gray-900 dark:text-gray-100">Manage RNode Subinterfaces</span>
|
|
<span
|
|
class="w-5 h-5 text-gray-600 dark:text-gray-300 transform transition-transform duration-200"
|
|
:class="{ 'rotate-180': showRNodeSubInterfaces }">
|
|
▲
|
|
</span>
|
|
</div>
|
|
<div
|
|
v-show="showRNodeSubInterfaces"
|
|
class="mt-3 space-y-4 p-4 border border-gray-200 rounded-lg bg-white dark:bg-zinc-900 dark:border-zinc-700">
|
|
<div
|
|
v-for="(sub, idx) in RNodeMultiInterface.subInterfaces"
|
|
:key="idx"
|
|
class="space-y-2 border-b border-gray-200 dark:border-zinc-700 pb-4 mb-4">
|
|
<input
|
|
v-model="sub.name"
|
|
type="text"
|
|
placeholder="Subinterface Name"
|
|
class="w-full bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
<div class="flex gap-2">
|
|
<div class="flex-1">
|
|
<label class="text-sm dark:text-zinc-100">Frequency (Hz)</label>
|
|
<input
|
|
v-model.number="sub.frequency"
|
|
type="number"
|
|
class="w-full bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-sm dark:text-zinc-100">Bandwidth</label>
|
|
<select
|
|
v-model="sub.bandwidth"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600"
|
|
>
|
|
<option v-for="bandwidth in RNodeInterfaceDefaults.bandwidths" :value="bandwidth">
|
|
{{ bandwidth / 1000 }} KHz
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<div class="flex-1">
|
|
<label class="text-sm dark:text-zinc-100">Spreading Factor</label>
|
|
<select
|
|
v-model.number="sub.spreadingfactor"
|
|
class="w-full bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
<option
|
|
v-for="sf in RNodeInterfaceDefaults.spreadingfactors"
|
|
:key="sf"
|
|
:value="sf">
|
|
{{ sf }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-sm dark:text-zinc-100">Coding Rate</label>
|
|
<select
|
|
v-model.number="sub.codingrate"
|
|
class="w-full bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
<option
|
|
v-for="cr in RNodeInterfaceDefaults.codingrates"
|
|
:key="cr"
|
|
:value="cr">
|
|
{{ cr }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-2 items-center">
|
|
<div class="flex-1">
|
|
<label class="text-sm dark:text-zinc-100">TX Power (dBm)</label>
|
|
<input
|
|
v-model.number="sub.txpower"
|
|
type="number"
|
|
class="w-full bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="text-sm dark:text-zinc-100">Virtual Port</label>
|
|
<input
|
|
v-model.number="sub.vport"
|
|
type="number"
|
|
class="w-full bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
</div>
|
|
</div>
|
|
<button
|
|
@click="removeSubInterface(idx)"
|
|
type="button"
|
|
class="bg-red-500 hover:bg-red-400 text-white text-sm p-2 rounded-lg">
|
|
Remove Subinterface
|
|
</button>
|
|
</div>
|
|
<button
|
|
@click="addSubInterface"
|
|
type="button"
|
|
class="bg-green-500 hover:bg-green-400 text-white text-sm px-4 py-2 rounded-lg">
|
|
Add Subinterface
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Serial, KISS, and AX25Kiss -->
|
|
<div v-if="['SerialInterface', 'KISSInterface', 'AX25KISSInterface'].includes(newInterfaceType)" class="mb-4">
|
|
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Port</label>
|
|
<select v-model="newInterfacePort"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
<option v-for="comport of comports" :value="comport.device">{{ comport.device }} (Product:
|
|
{{ comport.product ?? '?' }}, Serial: {{ comport.serial ?? '?' }})
|
|
</option>
|
|
</select>
|
|
|
|
<div class="mb-2">
|
|
<label class="block mt-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Serial connection baud
|
|
rate (bps)</label>
|
|
<input v-model="newInterfaceSpeed" placeholder="115200" type="number"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
</div>
|
|
|
|
<div class="mt-2">
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Databits</label>
|
|
<input v-model="newInterfaceDatabits" type="number" placeholder="8"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
</div>
|
|
|
|
<div class="mt-2">
|
|
<label class="block mt-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Parity </label>
|
|
<input v-model="newInterfaceParity" type="number" placeholder=""
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
</div>
|
|
|
|
<div class="mt-2">
|
|
<label class="block mt-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Stopbits</label>
|
|
<input v-model="newInterfaceStopbits" type="number" placeholder="1"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white">
|
|
</div>
|
|
|
|
</div>
|
|
<!-- KISS and AX.25 KISS-->
|
|
<div v-if="['KISSInterface', 'AX25KISSInterface'].includes(newInterfaceType)" class="mb-4">
|
|
|
|
<div class="flex items-center">
|
|
<input
|
|
id="use-ax25"
|
|
type="checkbox"
|
|
:checked="newInterfaceType === 'AX25KISSInterface'"
|
|
@click="useKISSAX25"
|
|
class="h-5 w-5 rounded border-gray-300 text-blue-600 focus:ring focus:ring-blue-500 dark:border-zinc-600 dark:bg-zinc-800 dark:focus:ring-blue-600"
|
|
/>
|
|
<label for="use-ax25" class="ml-2 text-sm font-medium text-gray-900 dark:text-zinc-100">
|
|
Use AX.25
|
|
</label>
|
|
</div>
|
|
<span class="text-sm text-gray-500 dark:text-zinc-300 mb-3">
|
|
ⓘ Enables AX.25 Framing
|
|
</span>
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">
|
|
Preamble (milliseconds)
|
|
</label>
|
|
<input
|
|
v-model="this.newInterfacePreamble"
|
|
type="number"
|
|
placeholder="150"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">
|
|
TX Tail (milliseconds)
|
|
</label>
|
|
<input
|
|
v-model="this.newInterfaceTXTail"
|
|
type="number"
|
|
placeholder="10"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">
|
|
CDMA Persistence (milliseconds)
|
|
</label>
|
|
<input
|
|
v-model="this.newInterfacePersistence"
|
|
type="number"
|
|
placeholder="200"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">
|
|
CDMA Slot Time (milliseconds)
|
|
</label>
|
|
<input
|
|
v-model="this.newInterfaceSlotTime"
|
|
type="number"
|
|
placeholder="20"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center space-x-4 mt-4">
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">SSID</label>
|
|
<input
|
|
type="text"
|
|
value="0"
|
|
v-model="newInterfaceSSID"
|
|
placeholder="Enter SSID"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Callsign</label>
|
|
<input
|
|
type="text"
|
|
v-model="newInterfaceCallsign"
|
|
placeholder="Enter callsign"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div class="flex-1">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Callsign ID Interval</label>
|
|
<input
|
|
type="number"
|
|
v-model="newInterfaceIDInterval"
|
|
placeholder="Enter interval (seconds)"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pipe Interface-->
|
|
<div v-if="newInterfaceType === 'PipeInterface'" class="mb-2">
|
|
<span class="text-sm text-gray-500 dark:text-zinc-300 mb-3">
|
|
ⓘ Using this interface, Reticulum can use any program as an interface via stdin and stdout. This can be used
|
|
to easily create virtual interfaces, or to interface with custom hardware or other systems.
|
|
</span>
|
|
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Command</label>
|
|
<input type="text" placeholder="netcat -l 5757" v-model="newInterfaceCommand"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
|
|
<label class="block mt-2 text-sm font-medium text-gray-900 dark:text-zinc-100">Respawn Delay
|
|
(seconds)</label>
|
|
<input type="number" placeholder="5" v-model="newInterfaceRespawnDelay"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
|
|
</div>
|
|
|
|
<!-- Shared interface setings -->
|
|
<div v-if="newInterfaceType"
|
|
@click="toggleSharedInterfaceSettings"
|
|
class="flex items-center justify-between cursor-pointer p-2 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
|
>
|
|
<span class="font-semibold text-gray-900 dark:text-gray-100">Shared Interface Settings</span>
|
|
<span class="w-5 h-5 text-gray-600 dark:text-gray-300 transform transition-transform duration-200 "
|
|
:class="{ 'rotate-180': showSharedInterfaceSettings }"
|
|
>
|
|
▲
|
|
</span>
|
|
</div>
|
|
|
|
<div
|
|
v-show="showSharedInterfaceSettings"
|
|
class="mt-3 space-y-4 p-4 border border-gray-200 rounded-lg bg-white dark:bg-zinc-900 dark:border-zinc-700"
|
|
>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400">ⓘ These settings are optional.</p>
|
|
<div v-show="transportEnabled" class="mb-4">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Interface Mode</label>
|
|
<select
|
|
v-model="sharedInterfaceSettings.mode"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
>
|
|
<option value="full">Full</option>
|
|
<option value="gateway">Gateway</option>
|
|
<option value="access_point">Access Point</option>
|
|
<option value="roaming">Roaming</option>
|
|
<option value="boundary">Boundary</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Inferred Interface
|
|
Bitrate</label>
|
|
<input
|
|
v-model="sharedInterfaceSettings.bitrate"
|
|
type="number"
|
|
placeholder="Enter inferred bitrate"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-zinc-100 mb-2">IFAC Settings</h3>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">ⓘ IFAC (Interface Access Code) settings are used
|
|
for creating
|
|
private
|
|
networks and can be configured on the interface level. </p>
|
|
<div class="grid grid-cols-3 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Network Name</label>
|
|
<input
|
|
v-model="sharedInterfaceSettings.network_name"
|
|
type="text"
|
|
placeholder="Enter network name"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Passphrase</label>
|
|
<input
|
|
v-model="sharedInterfaceSettings.passphrase"
|
|
type="text"
|
|
placeholder="Enter passphrase"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">IFAC Size</label>
|
|
<input
|
|
v-model="sharedInterfaceSettings.ifac_size"
|
|
type="number"
|
|
min="8"
|
|
max="512"
|
|
placeholder="Enter size (8-512)"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-zinc-900 dark:border-zinc-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="p-2">
|
|
|
|
<!-- add button -->
|
|
<button @click="addInterface" type="button"
|
|
class="bg-green-500 hover:bg-green-400 focus-visible:outline-green-500 my-auto inline-flex items-center gap-x-1 rounded-md p-2 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 dark:text-white dark:focus:ring-blue-600 dark:focus:border-blue-600">
|
|
<span v-if="isEditingInterface">Save Interface</span>
|
|
<span v-else>Add Interface</span>
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import Utils from "../../js/Utils";
|
|
import DialogUtils from "../../js/DialogUtils";
|
|
|
|
export default {
|
|
name: 'AddInterfacePage',
|
|
data() {
|
|
return {
|
|
|
|
isEditingInterface: false,
|
|
showAllSettings: false, // more interface settings, used for TCPInterface and RNodeInterface
|
|
showSharedInterfaceSettings: false, // shared interface settings like IFAC, bitrate
|
|
showRNodeLoRaParameters: false, // accordion for displaying RNode physical LoRa parameters
|
|
showRNodeSubInterfaces: false, // accordion for adding multiple RNode interfaces
|
|
|
|
appInfo: null,
|
|
transportEnabled: false,
|
|
|
|
config: null,
|
|
|
|
comports: [],
|
|
|
|
newInterfaceName: null,
|
|
newInterfaceType: null,
|
|
|
|
newInterfaceGroupID: null,
|
|
newInterfaceMulticastAddressType: null,
|
|
newInterfaceDevices: null,
|
|
newInterfaceIgnoredDevices: null,
|
|
newInterfaceDiscoveryScope: null,
|
|
newInterfaceDiscoveryPort: null,
|
|
newInterfaceDataPort: null,
|
|
|
|
newInterfaceTargetHost: null,
|
|
newInterfaceTargetPort: null,
|
|
|
|
newInterfaceListenIp: null,
|
|
newInterfaceListenPort: null,
|
|
newInterfaceNetworkDevice: null,
|
|
newInterfacePreferIPV6: null,
|
|
newInterfaceKISSFramingEnabled: null,
|
|
newInterfaceI2PTunnelingEnabled: null,
|
|
|
|
sharedInterfaceSettings: {
|
|
"mode": null,
|
|
"network_name": null,
|
|
"passphrase": null,
|
|
"ifac_size": null,
|
|
},
|
|
|
|
newInterfaceForwardIp: null,
|
|
newInterfaceForwardPort: null,
|
|
|
|
I2PSettings: {
|
|
showPeerAccordion: false,
|
|
newInterfacePeers: [],
|
|
},
|
|
|
|
RNodeMultiInterface: {
|
|
port: null,
|
|
subInterfaces: [],
|
|
},
|
|
|
|
newInterfacePort: null,
|
|
RNodeGHzValue: 0,
|
|
RNodeMHzValue: 0,
|
|
RNodekHzValue: 0,
|
|
newInterfaceFrequency: null,
|
|
newInterfaceBandwidth: null,
|
|
newInterfaceTxpower: null,
|
|
newInterfaceSpreadingFactor: null,
|
|
newInterfaceCodingRate: null,
|
|
|
|
// Serial, KISS, and AX25KISS options
|
|
newInterfaceSpeed: null,
|
|
newInterfaceDatabits: null,
|
|
newInterfaceParity: null,
|
|
newInterfaceStopbits: null,
|
|
|
|
// KISS and AX25KISS
|
|
newInterfacePreamble: null,
|
|
newInterfaceTXTail: null,
|
|
newInterfacePersistence: null,
|
|
newInterfaceSlotTime: null,
|
|
|
|
// RNode and KISS
|
|
newInterfaceCallsign: null,
|
|
newInterfaceIDInterval: null,
|
|
newInterfaceFlowControl: null,
|
|
newInterfaceAirtimeLimitLong: null,
|
|
newInterfaceAirtimeLimitShort: null,
|
|
|
|
// Pipe interface
|
|
newInterfaceCommand: null,
|
|
newInterfaceRespawnDelay: null,
|
|
|
|
RNodeInterfaceDefaults: {
|
|
// bandwidth in hz
|
|
bandwidths: [
|
|
7800, // 7.8 kHz
|
|
10400, // 10.4 kHz
|
|
15600, // 15.6 kHz
|
|
20800, // 20.8 kHz
|
|
31250, // 31.25 kHz
|
|
41700, // 41.7 kHz
|
|
62500, // 62.5 kHz
|
|
125000, // 125 kHz
|
|
250000, // 250 kHz
|
|
500000, // 500 kHz
|
|
1625000, // 1625 kHz (for 2.4 GHz SX1280)
|
|
],
|
|
codingrates: [
|
|
5, // 4:5
|
|
6, // 4:6
|
|
7, // 4:7
|
|
8, // 4:8
|
|
],
|
|
spreadingfactors: [
|
|
5,
|
|
6,
|
|
7,
|
|
8,
|
|
9,
|
|
10,
|
|
11,
|
|
12,
|
|
],
|
|
},
|
|
RNodeInterfaceLoRaParameters: {
|
|
antennaGain: 0,
|
|
noiseFloor: 5,
|
|
sensitivity: null,
|
|
dataRate: null,
|
|
linkBudget: null,
|
|
},
|
|
};
|
|
},
|
|
computed: {
|
|
formattedFrequency() {
|
|
const totalHz = this.calculateFrequencyInHz();
|
|
if (totalHz >= 1e9) {
|
|
return `${(totalHz / 1e9).toFixed(3)} GHz`;
|
|
} else if (totalHz >= 1e6) {
|
|
return `${(totalHz / 1e6).toFixed(3)} MHz`;
|
|
} else if (totalHz >= 1e3) {
|
|
return `${(totalHz / 1e3).toFixed(3)} kHz`;
|
|
}
|
|
return `${totalHz} Hz`;
|
|
},
|
|
},
|
|
watch: {
|
|
newInterfaceBandwidth: "updateRNodeCalculations",
|
|
newInterfaceSpreadingFactor: "updateRNodeCalculations",
|
|
newInterfaceCodingRate: "updateRNodeCalculations",
|
|
newInterfaceTxpower: "updateRNodeCalculations",
|
|
'RNodeInterfaceLoRaParameters.antennaGain': "updateRNodeCalculations",
|
|
},
|
|
mounted() {
|
|
this.getAppInfo();
|
|
this.getConfig();
|
|
this.loadComports();
|
|
|
|
// check if we are editing an interface
|
|
const interfaceName = this.$route.query.interface_name;
|
|
if (interfaceName != null) {
|
|
this.isEditingInterface = true;
|
|
this.loadInterfaceToEdit(interfaceName);
|
|
}
|
|
|
|
},
|
|
methods: {
|
|
async getConfig() {
|
|
try {
|
|
const response = await window.axios.get(`/api/v1/config`);
|
|
this.config = response.data.config;
|
|
} catch (e) {
|
|
// do nothing if failed to load config
|
|
console.log(e);
|
|
}
|
|
},
|
|
async updateConfig(config) {
|
|
try {
|
|
const response = await window.axios.patch("/api/v1/config", config);
|
|
this.config = response.data.config;
|
|
} catch (e) {
|
|
alert("Failed to save config!");
|
|
console.log(e);
|
|
}
|
|
},
|
|
async loadComports() {
|
|
try {
|
|
const response = await window.axios.get(`/api/v1/comports`);
|
|
this.comports = response.data.comports;
|
|
} catch (e) {
|
|
// do nothing if failed to load interfaces
|
|
}
|
|
},
|
|
async loadInterfaceToEdit(interfaceName) {
|
|
try {
|
|
|
|
// fetch interfaces
|
|
const response = await window.axios.get(`/api/v1/reticulum/interfaces`);
|
|
const interfaces = response.data.interfaces;
|
|
|
|
// find interface, else show error and redirect to interfaces
|
|
const iface = interfaces[interfaceName];
|
|
if (!iface) {
|
|
DialogUtils.alert("The selected interface for editing could not be found.");
|
|
this.$router.push({
|
|
"name": "interfaces",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// set form values
|
|
this.newInterfaceName = interfaceName;
|
|
this.newInterfaceType = iface.type;
|
|
|
|
// AutoInterface additional settings
|
|
this.newInterfaceGroupID = iface.group_id;
|
|
this.newInterfaceMulticastAddressType = iface.multicast_address_type;
|
|
this.newInterfaceDevices = iface.devices;
|
|
this.newInterfaceIgnoredDevices = iface.ignored_devices;
|
|
this.newInterfaceDiscoveryScope = iface.discovery_scope;
|
|
this.newInterfaceDiscoveryPort = iface.discovery_port;
|
|
this.newInterfaceDataPort = iface.data_port;
|
|
|
|
// tcp client interface
|
|
this.newInterfaceTargetHost = iface.target_host;
|
|
this.newInterfaceTargetPort = iface.target_port;
|
|
|
|
if (iface.kiss_framing) {
|
|
this.newInterfaceKISSFramingEnabled = true;
|
|
}
|
|
if (iface.i2p_tunneled) {
|
|
this.newInterfaceI2PTunnelingEnabled = true;
|
|
}
|
|
if (iface.prefer_ipv6) {
|
|
this.newInterfacePreferIPV6 = true;
|
|
}
|
|
|
|
|
|
// tcp server interface & udp interface
|
|
this.newInterfaceNetworkDevice = iface.device;
|
|
this.newInterfaceListenIp = iface.listen_ip;
|
|
this.newInterfaceListenPort = iface.listen_port;
|
|
|
|
// I2P Interface
|
|
if (iface.peers) {
|
|
const peerstoAdd = iface.peers.split(',');
|
|
for (const address of peerstoAdd) {
|
|
this.addI2PPeer(address);
|
|
}
|
|
}
|
|
|
|
|
|
// udp interface
|
|
this.newInterfaceForwardIp = iface.forward_ip;
|
|
this.newInterfaceForwardPort = iface.forward_port;
|
|
|
|
// Port (For RNode, Serial, and KISS)
|
|
this.newInterfacePort = iface.port;
|
|
|
|
// RNode Interface
|
|
this.newInterfaceFrequency = iface.frequency;
|
|
this.RNodeGHzValue = Math.floor(iface.frequency / 1e9);
|
|
this.RNodeMHzValue = Math.floor((iface.frequency % 1e9) / 1e6);
|
|
this.RNodekHzValue = Math.floor((iface.frequency % 1e6) / 1e3);
|
|
this.newInterfaceBandwidth = iface.bandwidth;
|
|
this.newInterfaceTxpower = iface.txpower;
|
|
this.newInterfaceSpreadingFactor = iface.spreadingfactor;
|
|
this.newInterfaceCodingRate = iface.codingrate;
|
|
|
|
// RNode Multi Interface
|
|
this.RNodeMultiInterface.subInterfaces = iface.sub_interfaces;
|
|
|
|
// Serial, KISS, and AX25KISS
|
|
this.newInterfaceSpeed = iface.speed;
|
|
this.newInterfaceDatabits = iface.databits;
|
|
this.newInterfaceParity = iface.parity;
|
|
this.newInterfaceStopbits = iface.stopbits;
|
|
|
|
this.newInterfacePreamble = iface.preamble;
|
|
this.newInterfaceTXTail = iface.txtail;
|
|
this.newInterfacePersistence = iface.persistence;
|
|
this.newInterfaceSlotTime = iface.slottime;
|
|
|
|
this.newInterfaceCallsign = iface.callsign;
|
|
this.newInterfaceIDInterval = iface.id_interval;
|
|
this.newInterfaceSSID = iface.ssid;
|
|
|
|
// Airtime limit
|
|
this.newInterfaceAirtimeLimitLong = iface.airtime_limit_long;
|
|
this.newInterfaceAirtimeLimitShort = iface.airtime_limit_short;
|
|
|
|
// Pipe Interface
|
|
this.newInterfaceCommand = iface.command;
|
|
this.newInterfaceRespawnDelay = iface.respawn_delay;
|
|
|
|
// Shared interface settings
|
|
this.sharedInterfaceSettings.mode = iface.mode;
|
|
this.sharedInterfaceSettings.bitrate = iface.bitrate;
|
|
this.sharedInterfaceSettings.network_name = iface.network_name;
|
|
this.sharedInterfaceSettings.passphrase = iface.passphrase;
|
|
this.sharedInterfaceSettings.ifac_size = iface.ifac_size;
|
|
|
|
} catch (e) {
|
|
// do nothing if failed to load interfaces
|
|
}
|
|
},
|
|
async addInterface() {
|
|
try {
|
|
let subInterfacesData = null;
|
|
if (this.newInterfaceType === 'RNodeMultiInterface') {
|
|
subInterfacesData = this.RNodeMultiInterface.subInterfaces.map(s => ({
|
|
name: s.name,
|
|
frequency: s.frequency,
|
|
bandwidth: s.bandwidth,
|
|
txpower: s.txpower,
|
|
spreadingfactor: s.spreadingfactor,
|
|
codingrate: s.codingrate,
|
|
vport: s.vport
|
|
}));
|
|
}
|
|
// add interface
|
|
const response = await window.axios.post(`/api/v1/reticulum/interfaces/add`, {
|
|
allow_overwriting_interface: this.isEditingInterface,
|
|
// required values
|
|
name: this.newInterfaceName,
|
|
type: this.newInterfaceType,
|
|
|
|
// AutoInterface
|
|
group_id: this.newInterfaceGroupID,
|
|
multicast_address_type: this.newInterfaceMulticastAddressType,
|
|
devices: this.newInterfaceDevices,
|
|
ignored_devices: this.newInterfaceIgnoredDevices,
|
|
discovery_scope: this.newInterfaceDiscoveryScope,
|
|
discovery_port: this.newInterfaceDiscoveryPort,
|
|
data_port: this.newInterfaceDataPort,
|
|
|
|
// tcp client interface
|
|
target_host: this.newInterfaceTargetHost,
|
|
target_port: this.newInterfaceTargetPort,
|
|
|
|
// TCP Client & Server interface
|
|
kiss_framing: this.newInterfaceKISSFramingEnabled,
|
|
i2p_tunneled: this.newInterfaceI2PTunnelingEnabled,
|
|
|
|
// tcp server interface & udp interface
|
|
listen_ip: this.newInterfaceListenIp,
|
|
listen_port: this.newInterfaceListenPort,
|
|
device: this.newInterfaceNetworkDevice,
|
|
prefer_ipv6: this.newInterfacePreferIPV6,
|
|
|
|
|
|
// udp interface
|
|
forward_ip: this.newInterfaceForwardIp,
|
|
forward_port: this.newInterfaceForwardPort,
|
|
// I2P Interface
|
|
peers: this.I2PSettings.newInterfacePeers.join(','),
|
|
// rnode interface
|
|
port: this.newInterfacePort,
|
|
frequency: this.calculateFrequencyInHz(),
|
|
bandwidth: this.newInterfaceBandwidth,
|
|
txpower: this.newInterfaceTxpower,
|
|
spreadingfactor: this.newInterfaceSpreadingFactor,
|
|
codingrate: this.newInterfaceCodingRate,
|
|
|
|
// RNode Multi Interface
|
|
sub_interfaces: subInterfacesData,
|
|
|
|
// Seiral, KISS, and AX25KISS
|
|
speed: this.newInterfaceSpeed,
|
|
databits: this.newInterfaceDatabits,
|
|
parity: this.newInterfaceParity,
|
|
stopbits: this.newInterfaceStopbits,
|
|
|
|
// KISS and AX25KISS
|
|
preamble: this.newInterfacePreamble,
|
|
txtail: this.newInterfaceTXTail,
|
|
persistence: this.newInterfacePersistence,
|
|
slottime: this.newInterfaceSlotTime,
|
|
|
|
callsign: this.newInterfaceCallsign,
|
|
id_interval: this.newInterfaceIDInterval,
|
|
ssid: this.newInterfaceSSID,
|
|
|
|
// Pipe interface
|
|
command: this.newInterfaceCommand,
|
|
respawn_delay: this.newInterfaceRespawnDelay,
|
|
|
|
// Airtime limit
|
|
airtime_limit_long: this.newInterfaceAirtimeLimitLong,
|
|
airtime_limit_short: this.newInterfaceAirtimeLimitShort,
|
|
|
|
// Shared interface settings
|
|
mode: this.sharedInterfaceSettings.mode,
|
|
bitrate: this.sharedInterfaceSettings.bitrate,
|
|
network_name: this.sharedInterfaceSettings.network_name,
|
|
passphrase: this.sharedInterfaceSettings.passphrase,
|
|
ifac_size: this.sharedInterfaceSettings.ifac_size,
|
|
|
|
|
|
});
|
|
|
|
// show success message
|
|
if (response.data.message) {
|
|
DialogUtils.alert(response.data.message);
|
|
}
|
|
|
|
// go to interfaces page
|
|
this.$router.push({
|
|
name: "interfaces",
|
|
});
|
|
|
|
} catch (e) {
|
|
const message = e.response?.data?.message ?? "failed to add interface";
|
|
DialogUtils.alert(message);
|
|
console.log(e);
|
|
}
|
|
|
|
},
|
|
formatFrequency(hz) {
|
|
return Utils.formatFrequency(hz);
|
|
},
|
|
calculateFrequencyInHz() {
|
|
const ghzToHz = this.RNodeGHzValue * 1e9;
|
|
const mhzToHz = this.RNodeMHzValue * 1e6;
|
|
const khzToHz = this.RNodekHzValue * 1e3;
|
|
return ghzToHz + mhzToHz + khzToHz;
|
|
},
|
|
updateRNodeCalculations() {
|
|
this.calculateRNodeParameters(
|
|
this.newInterfaceBandwidth,
|
|
this.newInterfaceSpreadingFactor,
|
|
this.newInterfaceCodingRate,
|
|
this.RNodeInterfaceLoRaParameters.noiseFloor,
|
|
this.RNodeInterfaceLoRaParameters.antennaGain,
|
|
this.newInterfaceTxpower
|
|
);
|
|
},
|
|
calculateRNodeParameters(bandwidth, spreadingFactor, codingRate, noiseFloor, antennaGain, transmitPower) {
|
|
// https://unsigned.io/understanding-lora-parameters/
|
|
// "SX1272/3/6/7/8 LoRa Modem Design Guide" https://www.openhacks.com/uploadsproductos/loradesignguide_std.pdf
|
|
// 4:5 - 4:8
|
|
const crn = {
|
|
5: 1,
|
|
6: 2,
|
|
7: 3,
|
|
8: 4,
|
|
};
|
|
|
|
codingRate = crn[codingRate];
|
|
|
|
const sfn = {
|
|
5: -2.5,
|
|
6: -5,
|
|
7: -7.5,
|
|
8: -10,
|
|
9: -12.5,
|
|
10: -15,
|
|
11: -17.5,
|
|
12: -20
|
|
};
|
|
|
|
let dataRate = spreadingFactor * ((4 / (4 + codingRate)) / (Math.pow(2, spreadingFactor) / (bandwidth / 1000))) * 1000
|
|
|
|
let sensitivity = -174 + 10 * Math.log10(bandwidth) + noiseFloor + (sfn[spreadingFactor] || 0);
|
|
|
|
if (bandwidth === 203125 || bandwidth === 406250 || bandwidth > 500000) {
|
|
sensitivity = -165.6 + 10 * Math.log10(bandwidth) + noiseFloor + (sfn[spreadingFactor] || 0);
|
|
}
|
|
|
|
let linkBudget = (transmitPower - sensitivity) + antennaGain;
|
|
this.RNodeInterfaceLoRaParameters.dataRate = dataRate < 1000
|
|
? `${dataRate.toFixed(0)} bps`
|
|
: `${(dataRate / 1000).toFixed(2)} kbps`;
|
|
this.RNodeInterfaceLoRaParameters.linkBudget = `${linkBudget.toFixed(1)} dB`;
|
|
this.RNodeInterfaceLoRaParameters.sensitivity = `${sensitivity.toFixed(1)} dBm`;
|
|
},
|
|
|
|
|
|
toggleI2PPeerAccordion() {
|
|
this.I2PSettings.showPeerAccordion = !this.I2PSettings.showPeerAccordion;
|
|
},
|
|
addI2PPeer(address = "") {
|
|
this.I2PSettings.newInterfacePeers.push(address);
|
|
},
|
|
removeI2PPeer(index) {
|
|
this.I2PSettings.newInterfacePeers.splice(index, 1);
|
|
},
|
|
toggleAllSettings() {
|
|
this.showAllSettings = !this.showAllSettings;
|
|
},
|
|
toggleSharedInterfaceSettings() {
|
|
this.showSharedInterfaceSettings = !this.showSharedInterfaceSettings;
|
|
},
|
|
toggleRNodeLoRaParameters() {
|
|
this.showRNodeLoRaParameters = !this.showRNodeLoRaParameters;
|
|
},
|
|
toggleRNodeSubInterfaces() {
|
|
this.showRNodeSubInterfaces = !this.showRNodeSubInterfaces;
|
|
},
|
|
addSubInterface() {
|
|
this.RNodeMultiInterface.subInterfaces.push({
|
|
name: '',
|
|
frequency: null,
|
|
bandwidth: null,
|
|
txpower: null,
|
|
spreadingfactor: null,
|
|
codingrate: null,
|
|
vport: null
|
|
});
|
|
},
|
|
useKISSAX25() {
|
|
if (this.newInterfaceType === 'AX25KISSInterface') {
|
|
this.newInterfaceType = "KISSInterface";
|
|
} else {
|
|
this.newInterfaceType = 'AX25KISSInterface';
|
|
}
|
|
},
|
|
removeSubInterface(idx) {
|
|
this.RNodeMultiInterface.subInterfaces.splice(idx, 1);
|
|
},
|
|
async getAppInfo() {
|
|
try {
|
|
const response = await window.axios.get("/api/v1/app/info");
|
|
this.appInfo = response.data.app_info;
|
|
this.transportEnabled = this.appInfo.is_transport_enabled;
|
|
} catch (e) {
|
|
console.log(e);
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|