Initial commit

This commit is contained in:
Treyfane Dingo 2025-04-07 17:17:12 +02:00
parent 804f94ae48
commit 030f325a08
3 changed files with 173 additions and 4 deletions

View File

@ -1,6 +1,35 @@
# pistarlog2ws
pistarlog2ws is a small Python script that provides a websocket output to a Pi-Star to be used by L.A.S.S.I.E.
**pistarlog2ws** is a small Python script that provides a websocket output of the radio log of a Pi-Star to be used by L.A.S.S.I.E..
Installation:
## Installation
### Prerequisites
You need [Pi-Star](https://www.pistar.uk/) installed and configured to the frequency of your repeater output on a Raspberry Pi with MMDVM HAT. You also need to enable SSH in the configuration of the Pi-Star.
*Pi-Star's disk is read-only by default, so you have to enable read-write with ```rpi-rw```.*
Clone the repository into the pi-star home and run the install script:
```shell
rpi-rw
git clone https://git.furcom.org/dingo/pistarlog2ws
cd pistarlog2ws
./install.sh
```
This should install and enable the websocket service and make the necessary adjustments to the firewall.
## Test
You can test the installation from another machine in the network using ```nc``` or ```wscat```.
```shell
nc -zv <IP-ADDRESS OF PI-STAR> 8765
```
```shell
wscat -c ws://<IP-ADDRESS OF PI-STAR>:8765 --no-color
```

63
install.sh Executable file
View File

@ -0,0 +1,63 @@
#!/bin/bash
# Define paths
SCRIPT_DIR="$HOME/bin"
SCRIPT_PATH="$SCRIPT_DIR/pistarlog2ws.py"
SERVICE_PATH="/etc/systemd/system/pistarlog2ws.service"
# Ensure required packages are installed
echo "Installing dependencies..."
sudo apt update
sudo apt install -y python3 python3-pip python3-aiofiles python3-websockets python3-tz
# Create the script directory if it does not exist
mkdir -p "$SCRIPT_DIR"
# Copy the provided pistarlog2ws.py script
if [[ -f "pistarlog2ws.py" ]]; then
echo "Copying pistarlog2ws.py to $SCRIPT_PATH..."
cp pistarlog2ws.py "$SCRIPT_PATH"
else
echo "Error: pistarlog2ws.py not found in the current directory!"
exit 1
fi
# Create the systemd service file
echo "Creating systemd service..."
sudo tee "$SERVICE_PATH" > /dev/null << EOF
[Unit]
Description=Log to WebSocket Server
After=network.target
[Service]
ExecStart=/usr/bin/python3 $SCRIPT_PATH
Restart=always
User=$USER
WorkingDirectory=$SCRIPT_DIR
StandardOutput=append:/var/log/pistarlog2ws.log
StandardError=append:/var/log/pistarlog2ws.log
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
# Open netfilter port
echo "Opening port (Netfilter)..."
sudo tee /root/ipv4.fw > /dev/null << EOF
iptables -I INPUT 1 -p tcp --dport 8765 -j ACCEPT
EOF
sudo pistar-firewall
# Reload systemd to recognize new service
echo "Enabling and starting the service..."
sudo systemctl daemon-reload
sudo systemctl enable pistarlog2ws.service
sudo systemctl start pistarlog2ws.service
echo "Installation complete. Use the following commands to manage the service:"
echo " sudo systemctl start pistarlog2ws.service # Start the service"
echo " sudo systemctl stop pistarlog2ws.service # Stop the service"
echo " sudo systemctl restart pistarlog2ws.service # Restart the service"
echo " sudo systemctl status pistarlog2ws.service # Check the status"

77
pistarlog2ws.py Normal file
View File

@ -0,0 +1,77 @@
"""Imports"""
import asyncio
import os
import traceback
from datetime import datetime
import websockets
import aiofiles
LOG_DIR = "/var/log/pi-star"
LOG_PREFIX = "MMDVM-"
LOG_EXT = ".log"
KEYWORDS = ["DMR"] # Add more keywords here
def get_logfile_path():
"""Return the correct logfile path based on UTC."""
date_str = datetime.utcnow().strftime("%Y-%m-%d")
return os.path.join(LOG_DIR, f"{LOG_PREFIX}{date_str}{LOG_EXT}")
async def tail_log(websocket, path):
"""Read the latest log file and stream lines with specified keywords."""
last_checked_file = get_logfile_path()
print(f"Client connected: {websocket.remote_address}")
# Wait for the file to exist
while not os.path.exists(last_checked_file):
print(f"Waiting for log file: {last_checked_file}")
await asyncio.sleep(2)
last_checked_file = get_logfile_path()
file = await aiofiles.open(last_checked_file, "r")
await file.seek(0, 2) # Move to the end of the file
try:
while True:
current_logfile = get_logfile_path()
if current_logfile != last_checked_file:
print(f"Switching to new log file: {current_logfile}")
await file.close() # Explicitly close previous file
last_checked_file = current_logfile
# Wait until the new log file is ready
while not os.path.exists(last_checked_file):
print(f"Waiting for new log file: {last_checked_file}")
await asyncio.sleep(2)
file = await aiofiles.open(last_checked_file, "r")
await file.seek(0, 2) # Go to end of new file
line = await file.readline()
if line:
print(f"READ LINE: {line.strip()}")
if any(keyword in line for keyword in KEYWORDS):
print(f"SENDING: {line.strip()}")
await websocket.send(line.strip())
else:
await asyncio.sleep(0.5) # Prevent busy-waiting
except websockets.exceptions.ConnectionClosed:
print(f"Client {websocket.remote_address} disconnected.")
except Exception as error:
print(f"Unexpected error: {error}")
traceback.print_exc()
finally:
await file.close() # Ensure file closure
async def main():
"""Start the WebSocket server."""
print("Starting WebSocket server on ws://0.0.0.0:8765")
async with websockets.serve(tail_log, "0.0.0.0", 8765):
await asyncio.Future() # Run forever
if __name__ == "__main__":
asyncio.run(main())