Initial commit
This commit is contained in:
parent
804f94ae48
commit
030f325a08
33
README.md
33
README.md
@ -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
63
install.sh
Executable 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
77
pistarlog2ws.py
Normal 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())
|
Loading…
x
Reference in New Issue
Block a user