From 3a0ee78b86f35d3f5d2bbd3c090535e2b69d9271 Mon Sep 17 00:00:00 2001 From: nq4t Date: Wed, 5 Apr 2023 23:42:21 +0000 Subject: [PATCH] Second Version. Major rewrite --- LICENSE | 28 ++++++++++++++ README.md | 51 +++++++++++++++++++++--- log4om.py | 114 +++++++++++++++++++++++++++++++----------------------- 3 files changed, 138 insertions(+), 55 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..95d2fee --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2023, Jay Moore + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index a78ae99..cfa115c 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,51 @@ the following in a very basic way: - Your current VFO frequency and opearating mode. - Your TX offset/split if it exists +- If your radio is off or Log4OM is not loaded -If no data is received from Log4OM, because you turned your rig off or shutdown Log4OM; it will indicate your radio is -off and wait for data to come back. Support is planned to show if rig is in transmit. +This now will determine if the radio is off or if Log4OM is not loaded by trying to request the Alive command over remote +control. It will assue Log4OM is active if a response is received. In an effort to make the thing a bit less chatty, it +backs down to 60 second checks when not receiving data automatically. -The "Radio Off" message happens after 15 seconds of no data. This is done by checking the elapsed time since the timestamo -was last updated; which happens every time a message comes in. +Code could likely use more optimization. ChatGPT has been used to some degree but currently has issues giving complete +output. -The script contains examples of how to add additional portions of HTML. This was done for nq4t.com. They are commented out by -default. You will need to modify this as you require. +## History + +``` +02-FEB-2023: Initial Version. Shows status and basic offline message. +04-APR-2023: Second Version. Now shows more percise offline message. Removes threads. +``` + +## License + +``` +BSD 3-Clause License + +Copyright (c) 2023, Jay Moore + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` diff --git a/log4om.py b/log4om.py index 6d45ba8..9dbf13e 100644 --- a/log4om.py +++ b/log4om.py @@ -1,65 +1,81 @@ -from threading import * +# LOG4OM Web Statuts +# Version: 5-APR-2023 - Jay Moore/NQ4T +# https://git.pickmy.org/nq4t/log4om-webstatus +# https://nq4t.com/software/log4omudp/ +# FreeBSD 3-Clause License (see LICENSE) + import time import xml.etree.ElementTree as ET import socket +import select UDP_IP = "0.0.0.0" UDP_PORT = 2242 # Default Log4OM UDP Out Port +UDP_C_IP = "192.168.1.70" # Set to IP running Log4OM +UDP_C_PORT = 2241 + +tot = 1 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((UDP_IP, UDP_PORT)) -tot = time.time() +check = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -def monitor(): - global tot - while True: - tc = time.time() - tot # time since last timestamp - if tc < 15: #15 seconds - time.sleep(6) - else: - writehtml(f"Radio Off or Log4OM Not Running", False) #writehtml() requires two arguements and you can't be on air if radio is off - time.sleep(30) # Slow checks when radio off/no UDP - -def log4om(): - global tot - while True: - data, addr = sock.recvfrom(1024) - root = ET.fromstring(data.decode("utf-8")) - freq = int(root.find("Freq").text) - tx_freq = int(root.find("TXFreq").text) - mode = root.find("Mode").text - onair = (root.find("IsTransmitting").text == "true") - f = freq / 100 - tf = tx_freq / 100 - sv = tf - f # Determines split value - 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) - tot = time.time() #timestamp - time.sleep(.1) +def checkrig(): + 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) + if status[0]: + writehtml(f"Radio Off", False) + else: + writehtml(f"Log4OM Down", False) + check.close() def writehtml(rs, t = "true"): - header = "\n\nFT-1000MP Status\n\n\n" # Basic Header - #css = "\n\n" # This adds my CSS files. - #div = "
" # This is additional formatting - footer = "\n
\n" # footer - isonair = "ON THE AIR
\n" # on-air html + header = """\n\nFT-1000MP Status + \n\n""" + # if you use CSS then modify for your stylesheet URI + css = """ + + """ + div = "
" + footer = "\n
\n" + isonair = "ON THE AIR
\n" with open("/var/www/log/radio.html", "w") as html: # Modifiy file location as needed. if t == True: # Checks for on-air - #html.write(header + css + div + isonair + rs + footer) # This will add additional variables (for css, etc.) to the HTML file. - html.write (header + isonair + rs + footer) # No formatting version - if t == False: # Checks for on-air - #html.write(header + css + div + rs + footer) # Same as above without isonair, use if additional html is used - html.write(header + rs + footer) # No formatting version - -T = Thread(target=monitor,daemon=True) # Daemon the monitor thread -L = Thread(target=log4om) + html.write(header + css + div + isonair + rs + footer) # If not using CSS, remove it. + else: + html.write(header + css + div + rs + footer) -L.start() # Start the UDP thread -time.sleep(5) # Wait 5 seconds in case Log4OM is already running and outputting messages -T.start() # Start monitor thread +while True: + ready = select.select([sock], [], [], 6) # Just give it an extra second + if ready[0]: + data, addr = sock.recvfrom(1024) + root = ET.fromstring(data.decode("utf-8")) + freq = int(root.find("Freq").text) + tx_freq = int(root.find("TXFreq").text) + mode = root.find("Mode").text + onair = (root.find("IsTransmitting").text == "true") + f = freq / 100 + tf = tx_freq / 100 + sv = tf - f # Determines split value + 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()