Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies = [
"argcomplete",
"aiohttp",
"attrs",
"bleak",
"cryptography",
"colorama",
"httpx",
Expand Down
17 changes: 12 additions & 5 deletions src/infuse_iot/tools/localhost.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@

import asyncio
import ctypes
import json
import pathlib
import threading
import time
from aiohttp import web
from aiohttp.web_request import BaseRequest
from aiohttp.web_runner import GracefulExit

from infuse_iot.util.console import Console
from infuse_iot.epacket import InfuseType, Interface
from infuse_iot.commands import InfuseCommand
from infuse_iot.socket_comms import LocalClient, default_multicast_address
Expand Down Expand Up @@ -41,10 +42,12 @@ async def handle_index(self, request):

return web.FileResponse(this_folder / "localhost" / "index.html")

async def websocket_handler(self, request):
async def websocket_handler(self, request: BaseRequest):
ws = web.WebSocketResponse()
await ws.prepare(request)

Console.log_info(f"Websocket client connected ({request.remote})")

try:
while True:
# Example data sent to the client
Expand Down Expand Up @@ -102,13 +105,14 @@ async def websocket_handler(self, request):
}
self._data_lock.release()

await ws.send_str(json.dumps(message))
await ws.send_json(message)
await asyncio.sleep(1)
except asyncio.CancelledError:
print("WebSocket connection closed")
except (asyncio.CancelledError, ConnectionResetError):
pass
finally:
await ws.close()

Console.log_info(f"Websocket client disconnected ({request.remote})")
return ws

def tdf_columns(self, tdf):
Expand Down Expand Up @@ -198,6 +202,7 @@ def recv_thread(self):
self._data_lock.release()

def run(self):
Console.init()
app = web.Application()
# Route for serving the HTML file
app.router.add_get("/", self.handle_index)
Expand All @@ -211,5 +216,7 @@ def run(self):
try:
web.run_app(app, host="localhost", port=8080)
except GracefulExit:
pass
finally:
self._thread_end.set()
rx_thread.join(1.0)
76 changes: 76 additions & 0 deletions src/infuse_iot/tools/native_bt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3

"""Native Bluetooth gateway tool"""

__author__ = "Jordan Yates"
__copyright__ = "Copyright 2024, Embeint Inc"

import asyncio

from bleak import BleakScanner
from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData

from infuse_iot.util.argparse import BtLeAddress
from infuse_iot.util.console import Console
from infuse_iot.epacket import CtypeBtAdvFrame, PacketReceived, HopReceived, Interface, Auth, Flags, InterfaceAddress
from infuse_iot.commands import InfuseCommand
from infuse_iot.socket_comms import LocalServer, default_multicast_address
from infuse_iot.database import DeviceDatabase

class SubCommand(InfuseCommand):
NAME = "native_bt"
HELP = "Native Bluetooth gateway"
DESCRIPTION = "Use the local Bluetooth adapater for Bluetooth interaction"

@classmethod
def add_parser(cls, parser):
pass

def __init__(self, args):
self.infuse_manu = 0x0DE4
self.infuse_service = '0000fc74-0000-1000-8000-00805f9b34fb'
self.database = DeviceDatabase()
Console.init()

def simple_callback(self, device: BLEDevice, data: AdvertisementData):
addr = InterfaceAddress.BluetoothLeAddr(0, BtLeAddress(device.address))
rssi = data.rssi
payload = data.manufacturer_data[self.infuse_manu]

hdr, decr = CtypeBtAdvFrame.decrypt(self.database, payload)

hop = HopReceived(
hdr.device_id,
Interface.BT_ADV,
addr,
(
Auth.DEVICE
if hdr.flags & Flags.ENCR_DEVICE
else Auth.NETWORK
),
hdr.key_metadata,
hdr.gps_time,
hdr.sequence,
rssi,
)

Console.log_rx(hdr.type, len(payload))
pkt = PacketReceived([hop], hdr.type, decr)
self.server.broadcast(pkt)

async def async_run(self):
self.server = LocalServer(default_multicast_address())

scanner = BleakScanner(
self.simple_callback, [self.infuse_service] , cb=dict(use_bdaddr=True)
)

while True:
Console.log_info("Starting scanner")
async with scanner:
# Run the scanner forever
await asyncio.Future()

def run(self):
asyncio.run(self.async_run())