Compare commits
2 Commits
feed1145f8
...
561c014c33
| Author | SHA1 | Date | |
|---|---|---|---|
| 561c014c33 | |||
| b4cd86ed78 |
@@ -1,8 +1,8 @@
|
|||||||
# LXST Client
|
# LXST Client
|
||||||
|
|
||||||
Single-file Python application for managing LXST Channel connections via Reticulum Network (RNS) and LXMF protocols. Supports CLI and Whisplay HAT GUI modes.
|
Single-file Python application for managing LXST Channel connections via Reticulum Network (RNS) and LXMF protocols using the Whisplay HAT.
|
||||||
|
|
||||||
## Running (CLI mode, default)
|
## Running
|
||||||
|
|
||||||
python client.py [-c CONFIGDIR] [-w WHITELIST]
|
python client.py [-c CONFIGDIR] [-w WHITELIST]
|
||||||
|
|
||||||
@@ -11,20 +11,6 @@ Single-file Python application for managing LXST Channel connections via Reticul
|
|||||||
-w, --whitelist : path to bridge_whitelist.json
|
-w, --whitelist : path to bridge_whitelist.json
|
||||||
-v, --verbose : enable debug logging
|
-v, --verbose : enable debug logging
|
||||||
--rnsconfig : alternative Reticulum config directory
|
--rnsconfig : alternative Reticulum config directory
|
||||||
--whisplay, -W : enable Whisplay HAT GUI mode
|
|
||||||
|
|
||||||
## Running (Whisplay GUI mode)
|
|
||||||
|
|
||||||
python client.py -W [-c CONFIGDIR] [-w WHITELIST]
|
|
||||||
|
|
||||||
## CLI Commands
|
|
||||||
|
|
||||||
Inside the interactive client:
|
|
||||||
d Discover channels from whitelist
|
|
||||||
l List discovered channels
|
|
||||||
<NUM> Connect to channel at index NUM (1-based)
|
|
||||||
h / ? Show help
|
|
||||||
q Quit
|
|
||||||
|
|
||||||
## Whisplay GUI Controls
|
## Whisplay GUI Controls
|
||||||
|
|
||||||
@@ -56,9 +42,8 @@ Single-file Python application for managing LXST Channel connections via Reticul
|
|||||||
1. Ensure ~/.lxst-client or /etc/lxst-client exists
|
1. Ensure ~/.lxst-client or /etc/lxst-client exists
|
||||||
2. Place bridge_whitelist.json in either directory (or pass with -w)
|
2. Place bridge_whitelist.json in either directory (or pass with -w)
|
||||||
3. First run creates identity automatically at configdir/identity
|
3. First run creates identity automatically at configdir/identity
|
||||||
4. In CLI mode: run `python client.py` then `d` to discover channels
|
4. In GUI mode: run `python client.py -W` — auto-discovers on startup
|
||||||
5. In GUI mode: run `python client.py -W` — auto-discovers on startup
|
5. Messages appear in configdir/lxmf/
|
||||||
6. Messages appear in configdir/lxmf/
|
|
||||||
|
|
||||||
## Known quirks
|
## Known quirks
|
||||||
|
|
||||||
@@ -67,4 +52,3 @@ Single-file Python application for managing LXST Channel connections via Reticul
|
|||||||
- Use -v for RNS debug output (path discovery is verbose here).
|
- Use -v for RNS debug output (path discovery is verbose here).
|
||||||
- Path discovery waits up to 10s for relays; increase timeout in client code if needed.
|
- Path discovery waits up to 10s for relays; increase timeout in client code if needed.
|
||||||
- Whisplay mode requires Pillow and the Whisplay runtime (whisplay.py / whisplay_client.py).
|
- Whisplay mode requires Pillow and the Whisplay runtime (whisplay.py / whisplay_client.py).
|
||||||
- Falls back to CLI gracefully if Whisplay hardware or dependencies are unavailable.
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class ChannelClient:
|
|||||||
STATE_IN_CALL = 4
|
STATE_IN_CALL = 4
|
||||||
|
|
||||||
def __init__(self, configdir=None, rnsconfigdir=None, whitelist_path=None,
|
def __init__(self, configdir=None, rnsconfigdir=None, whitelist_path=None,
|
||||||
verbosity=0, announce_interval=0, use_whisplay=False):
|
verbosity=0, announce_interval=0):
|
||||||
self.state = self.STATE_IDLE
|
self.state = self.STATE_IDLE
|
||||||
self.channels = []
|
self.channels = []
|
||||||
self.telephone = None
|
self.telephone = None
|
||||||
@@ -52,7 +52,6 @@ class ChannelClient:
|
|||||||
self.whisplay_interface = None
|
self.whisplay_interface = None
|
||||||
self._gesture_thread = None
|
self._gesture_thread = None
|
||||||
|
|
||||||
if use_whisplay:
|
|
||||||
self._init_whisplay()
|
self._init_whisplay()
|
||||||
|
|
||||||
if configdir is None:
|
if configdir is None:
|
||||||
@@ -555,10 +554,7 @@ class ChannelClient:
|
|||||||
signal.signal(signal.SIGTERM, self._sigterm_handler)
|
signal.signal(signal.SIGTERM, self._sigterm_handler)
|
||||||
self.should_run = True
|
self.should_run = True
|
||||||
|
|
||||||
if self.whisplay_interface.board:
|
|
||||||
self._run_gui()
|
self._run_gui()
|
||||||
else:
|
|
||||||
self._run_cli()
|
|
||||||
|
|
||||||
def _run_gui(self):
|
def _run_gui(self):
|
||||||
self._flash_screen()
|
self._flash_screen()
|
||||||
@@ -576,69 +572,6 @@ class ChannelClient:
|
|||||||
continue
|
continue
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
def _run_cli(self):
|
|
||||||
print(
|
|
||||||
f"\n{Terminal.BOLD}"
|
|
||||||
f"LXST Channel Client"
|
|
||||||
f"{Terminal.END}"
|
|
||||||
)
|
|
||||||
print(f" Identity: {RNS.prettyhexrep(self.identity.hash)}")
|
|
||||||
print(f" Whitelist: {self.whitelist_path}")
|
|
||||||
print()
|
|
||||||
|
|
||||||
self.discover(timeout=15)
|
|
||||||
self.show_channels()
|
|
||||||
self.show_help()
|
|
||||||
|
|
||||||
while self.should_run:
|
|
||||||
if self.state == self.STATE_IN_CALL:
|
|
||||||
try:
|
|
||||||
input(" In call, press Enter to hang up > ")
|
|
||||||
self.telephone.hangup()
|
|
||||||
self.state = self.STATE_READY
|
|
||||||
except (EOFError, KeyboardInterrupt):
|
|
||||||
break
|
|
||||||
continue
|
|
||||||
|
|
||||||
if self.state == self.STATE_CALLING:
|
|
||||||
time.sleep(0.5)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if self.state == self.STATE_DISCOVERING:
|
|
||||||
time.sleep(0.5)
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
inp = input("> ").strip().lower()
|
|
||||||
except (EOFError, KeyboardInterrupt):
|
|
||||||
print()
|
|
||||||
break
|
|
||||||
|
|
||||||
if not inp:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if inp == "q":
|
|
||||||
break
|
|
||||||
elif inp in ("h", "?"):
|
|
||||||
self.show_help()
|
|
||||||
elif inp == "d":
|
|
||||||
self.discover()
|
|
||||||
self.show_channels()
|
|
||||||
elif inp == "l":
|
|
||||||
self.show_channels()
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
idx = int(inp) - 1
|
|
||||||
if 0 <= idx < len(self.channels):
|
|
||||||
self.call_channel(idx)
|
|
||||||
else:
|
|
||||||
print(
|
|
||||||
f"Invalid index. "
|
|
||||||
f"Enter a number between 1 and {len(self.channels)}"
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
print(f"Unknown command: {inp}. Type 'h' for help.")
|
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
if self.telephone and self.state == self.STATE_IN_CALL:
|
if self.telephone and self.state == self.STATE_IN_CALL:
|
||||||
self.telephone.hangup()
|
self.telephone.hangup()
|
||||||
@@ -682,11 +615,6 @@ def main():
|
|||||||
help="seconds between LXMF destination announces (0 = announce once at startup, default)",
|
help="seconds between LXMF destination announces (0 = announce once at startup, default)",
|
||||||
type=int,
|
type=int,
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"--whisplay", "-W",
|
|
||||||
action="store_true",
|
|
||||||
help="enable Whisplay HAT GUI mode",
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
client = ChannelClient(
|
client = ChannelClient(
|
||||||
@@ -695,7 +623,6 @@ def main():
|
|||||||
whitelist_path=args.whitelist,
|
whitelist_path=args.whitelist,
|
||||||
verbosity=args.verbose,
|
verbosity=args.verbose,
|
||||||
announce_interval=args.announce_interval,
|
announce_interval=args.announce_interval,
|
||||||
use_whisplay=args.whisplay,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user