Initial commit
This commit is contained in:
parent
804f94ae48
commit
030f325a08
33
README.md
33
README.md
@ -1,6 +1,35 @@
|
|||||||
# pistarlog2ws
|
# 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