mirror of
https://github.com/liamcottle/reticulum-meshchat.git
synced 2026-04-28 00:20:48 +00:00
Compare commits
5 commits
master
...
feature/bl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6100a3bdff | ||
|
|
3aaebfccba | ||
|
|
4970d5088f | ||
|
|
7c5235845a | ||
|
|
b1c3cc767c |
3 changed files with 75 additions and 1 deletions
41
meshchat.py
41
meshchat.py
|
|
@ -13,11 +13,13 @@ import RNS
|
||||||
import RNS.vendor.umsgpack as msgpack
|
import RNS.vendor.umsgpack as msgpack
|
||||||
import LXMF
|
import LXMF
|
||||||
from LXMF import LXMRouter
|
from LXMF import LXMRouter
|
||||||
|
from RNS.Interfaces.RNodeInterface import BLEConnection
|
||||||
from aiohttp import web, WSMessage, WSMsgType, WSCloseCode
|
from aiohttp import web, WSMessage, WSMsgType, WSCloseCode
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
|
from bleak import BleakScanner
|
||||||
from peewee import SqliteDatabase
|
from peewee import SqliteDatabase
|
||||||
from serial.tools import list_ports
|
from serial.tools import list_ports
|
||||||
|
|
||||||
|
|
@ -317,6 +319,45 @@ class ReticulumMeshChat:
|
||||||
"comports": comports,
|
"comports": comports,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# scan for rnodes available via ble
|
||||||
|
@routes.get("/api/v1/rnodes/ble-scan")
|
||||||
|
async def index(request):
|
||||||
|
|
||||||
|
# determine how long we should scan for
|
||||||
|
scan_duration_seconds = int(request.query.get("scan_duration_seconds", 3))
|
||||||
|
|
||||||
|
# discover ble devices
|
||||||
|
ble_scan_results = await BleakScanner.discover(
|
||||||
|
timeout=scan_duration_seconds,
|
||||||
|
return_adv=True,
|
||||||
|
cb=dict(use_bdaddr=True),
|
||||||
|
service_uuids=[
|
||||||
|
BLEConnection.UART_SERVICE_UUID,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# format scan results
|
||||||
|
rnodes = []
|
||||||
|
for ble_address in ble_scan_results:
|
||||||
|
|
||||||
|
# get device and advertisement data from scan result
|
||||||
|
device, advertisement_data = ble_scan_results[ble_address]
|
||||||
|
|
||||||
|
# skip this result if advertisement data is unavailable
|
||||||
|
if advertisement_data is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
rnodes.append({
|
||||||
|
"name": device.name,
|
||||||
|
"local_name": advertisement_data.local_name,
|
||||||
|
"ble_address": device.address,
|
||||||
|
"port": "ble://" + advertisement_data.local_name,
|
||||||
|
})
|
||||||
|
|
||||||
|
return web.json_response({
|
||||||
|
"rnodes": rnodes,
|
||||||
|
})
|
||||||
|
|
||||||
# fetch reticulum interfaces
|
# fetch reticulum interfaces
|
||||||
@routes.get("/api/v1/reticulum/interfaces")
|
@routes.get("/api/v1/reticulum/interfaces")
|
||||||
async def index(request):
|
async def index(request):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
aiohttp>=3.9.5
|
aiohttp>=3.9.5
|
||||||
|
bleak>=0.22.3
|
||||||
cx_freeze>=7.0.0
|
cx_freeze>=7.0.0
|
||||||
lxmf>=0.5.8
|
lxmf>=0.5.8
|
||||||
peewee>=3.17.3
|
peewee>=3.17.3
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,15 @@
|
||||||
<div v-if="newInterfaceType === 'RNodeInterface'" class="mb-2">
|
<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>
|
<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">
|
<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 disabled>Serial Devices ({{ comports.length }})</option>
|
||||||
<option v-for="comport of comports" :value="comport.device">{{ comport.device }} (Product: {{ comport.product ?? '?' }}, Serial: {{ comport.serial ?? '?' }})</option>
|
<option v-for="comport of comports" :value="comport.device">{{ comport.device }} (Product: {{ comport.product ?? '?' }}, Serial: {{ comport.serial ?? '?' }})</option>
|
||||||
|
<option disabled>Bluetooth Devices ({{ rnodes.length }})</option>
|
||||||
|
<option v-for="rnode of rnodes" :value="rnode.port">{{ rnode.port }} ({{ rnode.ble_address }})</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div class="text-xs text-gray-600">
|
||||||
|
<span v-if="isLoadingRnodes" class="text-gray-500">Discovering Bluetooth RNodes...</span>
|
||||||
|
<span v-else @click="loadRnodes" class="text-blue-500 underline cursor-pointer">Discover Bluetooth RNodes</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- interface frequency -->
|
<!-- interface frequency -->
|
||||||
|
|
@ -184,6 +191,9 @@ export default {
|
||||||
config: null,
|
config: null,
|
||||||
|
|
||||||
comports: [],
|
comports: [],
|
||||||
|
rnodes: [],
|
||||||
|
|
||||||
|
isLoadingRnodes: false,
|
||||||
|
|
||||||
newInterfaceName: null,
|
newInterfaceName: null,
|
||||||
newInterfaceType: null,
|
newInterfaceType: null,
|
||||||
|
|
@ -243,6 +253,7 @@ export default {
|
||||||
|
|
||||||
this.getConfig();
|
this.getConfig();
|
||||||
this.loadComports();
|
this.loadComports();
|
||||||
|
this.loadRnodes();
|
||||||
|
|
||||||
// check if we are editing an interface
|
// check if we are editing an interface
|
||||||
const interfaceName = this.$route.query.interface_name;
|
const interfaceName = this.$route.query.interface_name;
|
||||||
|
|
@ -276,9 +287,30 @@ export default {
|
||||||
const response = await window.axios.get(`/api/v1/comports`);
|
const response = await window.axios.get(`/api/v1/comports`);
|
||||||
this.comports = response.data.comports;
|
this.comports = response.data.comports;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// do nothing if failed to load interfaces
|
// do nothing if failed to load comports
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async loadRnodes() {
|
||||||
|
|
||||||
|
// do nothing if already loading
|
||||||
|
if(this.isLoadingRnodes){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// show loading
|
||||||
|
this.isLoadingRnodes = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await window.axios.get(`/api/v1/rnodes/ble-scan`);
|
||||||
|
this.rnodes = response.data.rnodes;
|
||||||
|
} catch(e) {
|
||||||
|
// do nothing if failed to load rnodes
|
||||||
|
} finally {
|
||||||
|
// no longer loading
|
||||||
|
this.isLoadingRnodes = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
async loadInterfaceToEdit(interfaceName) {
|
async loadInterfaceToEdit(interfaceName) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue