#!/usr/bin/env python3 # LOG4OM Web Statuts # Version: 8-JUN-2024 - Jay Moore/NQ4T # https://git.pickmy.org/nq4t/log4om-webstatus # https://nq4t.com/software/log4omudp/ # FreeBSD 3-Clause License (see LICENSE) # Please read comments for configuration. # Script must be configured for your setup. import time import xml.etree.ElementTree as ET import socket import select UDP_IP = "0.0.0.0" # Binds to "Any" For Broadcast Packets UDP_PORT = 2242 # Default Log4OM UDP Out Port UDP_C_IP = "0.0.0.0" # Set to IP running Log4OM UDP_C_PORT = 2241 # Default Log4OM Remote Port # Global Variable Definitions and Initializations tot = time.time() sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((UDP_IP, UDP_PORT)) check = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # These modes should ignore split mode indication ignored_modes = ["FT8", "FT4", "JT65", "JT9", "JS8", "MSK144"] # Script Functions def checkrig(): # Checks if it's radio off or Log4OM off check = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) check.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) check.bind((UDP_IP, UDP_C_PORT)) msg = """ C0FC027F-D09E-49F5-9CA6-33A11E05A053 Alive """ check.sendto(msg.encode(), (UDP_C_IP, UDP_C_PORT)) status = select.select([check], [], [], 2) # It's simple; we either get a response, or we don't. # It doesn't matter what it is. if status[0]: writehtml(f"Radio Off", False) else: writehtml(f"Log4OM Down", False) check.close() def writehtml(rs, t = "true"): # Takes data as argument from loop, writes HTML file. header = """\n\nFT-1000MP Status \n\n""" # if you use CSS then modify for your stylesheet URI css = """ """ # you may not need this div portion, it's part of nq4t.com's integration div = "
" # you will need the footer though footer = "\n
\n" # you can change it to "BEING A LID" if you want isonair = "ON THE AIR
\n" # HTML Output: Modify the file path. Also modify html.write to remove css or div if not needed. # isonair can be placed before or after 'rs' with open("/var/www/log/radio.html", "w") as html: if t == True: html.write(header + css + div + isonair + rs + footer) else: html.write(header + css + div + rs + footer) checkrig() # Run this once to ensure things are initalized. # Script Loop # Log4OM sends it's packets every five seconds. So we wait six to see if a packet comes in. # If it does, we parse the XML and send it as arguments to writehtml() # # If it doesn't.... # # Then we create the sleepy variable by subtracting tot from time.time(). If it's more than # a minute; we run checkrig(). If it's not, we go away. After a minute has passed, we check # again and reset the counter. # # This largely is done so we can daemon the script and have it pick back up when things change. # # You also probably shouldn't modify anything here unless you know what you're doing. while True: ready = select.select([sock], [], [], 6) if ready[0]: data, addr = sock.recvfrom(1024) root = ET.fromstring(data.decode("utf-8")) freq = int(root.find("Freq").text) # Frequency tx_freq = int(root.find("TXFreq").text) # TX Frequency mode = root.find("Mode").text # Operating Mode onair = (root.find("IsTransmitting").text == "true") # TX Status f = freq / 100 tf = tx_freq / 100 if mode in ignored_modes: sv = 0 else: sv = tf - f if sv > 0: writehtml(f"Frequency: {f}kHz {mode}
Tx Split · Up: {sv:.2f} kHz", onair) elif sv < 0: sv = sv * -1 # Invert negative numbers writehtml(f"Frequency: {f}kHz {mode}
Tx Split · Down: {sv:.2f} kHz", onair) else: writehtml(f"Frequency: {f}kHz {mode}", onair) time.sleep(.1) else: sleepy = time.time() - tot if sleepy > 60: # How often to check in Off/Down state checkrig() tot = time.time() # Reset tot.