mirror of
				https://github.com/craigerl/aprsd.git
				synced 2025-11-03 21:20:23 -05:00 
			
		
		
		
	Merge pull request #50 from craigerl/tcpkiss
Added the ability to use direwolf KISS socket
This commit is contained in:
		
						commit
						d243e577f0
					
				@ -90,6 +90,11 @@ class Aprsdis(aprslib.IS):
 | 
				
			|||||||
        self.thread_stop = True
 | 
					        self.thread_stop = True
 | 
				
			||||||
        LOG.info("Shutdown Aprsdis client.")
 | 
					        LOG.info("Shutdown Aprsdis client.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def send(self, msg):
 | 
				
			||||||
 | 
					        """Send an APRS Message object."""
 | 
				
			||||||
 | 
					        line = str(msg)
 | 
				
			||||||
 | 
					        self.sendall(line)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _socket_readlines(self, blocking=False):
 | 
					    def _socket_readlines(self, blocking=False):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Generator for complete lines, received from the server
 | 
					        Generator for complete lines, received from the server
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ from flask_httpauth import HTTPBasicAuth
 | 
				
			|||||||
from werkzeug.security import check_password_hash, generate_password_hash
 | 
					from werkzeug.security import check_password_hash, generate_password_hash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import aprsd
 | 
					import aprsd
 | 
				
			||||||
from aprsd import messaging, packets, plugin, stats, utils
 | 
					from aprsd import kissclient, messaging, packets, plugin, stats, utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOG = logging.getLogger("APRSD")
 | 
					LOG = logging.getLogger("APRSD")
 | 
				
			||||||
@ -65,9 +65,38 @@ class APRSDFlask(flask_classful.FlaskView):
 | 
				
			|||||||
        plugins = pm.get_plugins()
 | 
					        plugins = pm.get_plugins()
 | 
				
			||||||
        plugin_count = len(plugins)
 | 
					        plugin_count = len(plugins)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.config["aprs"].get("enabled", True):
 | 
				
			||||||
 | 
					            transport = "aprs-is"
 | 
				
			||||||
 | 
					            aprs_connection = (
 | 
				
			||||||
 | 
					                "APRS-IS Server: <a href='http://status.aprs2.net' >"
 | 
				
			||||||
 | 
					                "{}</a>".format(stats["stats"]["aprs-is"]["server"])
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # We might be connected to a KISS socket?
 | 
				
			||||||
 | 
					            if kissclient.KISSClient.kiss_enabled(self.config):
 | 
				
			||||||
 | 
					                transport = kissclient.KISSClient.transport(self.config)
 | 
				
			||||||
 | 
					                if transport == kissclient.TRANSPORT_TCPKISS:
 | 
				
			||||||
 | 
					                    aprs_connection = (
 | 
				
			||||||
 | 
					                        "TCPKISS://{}:{}".format(
 | 
				
			||||||
 | 
					                            self.config["kiss"]["tcp"]["host"],
 | 
				
			||||||
 | 
					                            self.config["kiss"]["tcp"]["port"],
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                elif transport == kissclient.TRANSPORT_SERIALKISS:
 | 
				
			||||||
 | 
					                    aprs_connection = (
 | 
				
			||||||
 | 
					                        "SerialKISS://{}@{} baud".format(
 | 
				
			||||||
 | 
					                            self.config["kiss"]["serial"]["device"],
 | 
				
			||||||
 | 
					                            self.config["kiss"]["serial"]["baudrate"],
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stats["transport"] = transport
 | 
				
			||||||
 | 
					        stats["aprs_connection"] = aprs_connection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return flask.render_template(
 | 
					        return flask.render_template(
 | 
				
			||||||
            "index.html",
 | 
					            "index.html",
 | 
				
			||||||
            initial_stats=stats,
 | 
					            initial_stats=stats,
 | 
				
			||||||
 | 
					            aprs_connection=aprs_connection,
 | 
				
			||||||
            callsign=self.config["aprs"]["login"],
 | 
					            callsign=self.config["aprs"]["login"],
 | 
				
			||||||
            version=aprsd.__version__,
 | 
					            version=aprsd.__version__,
 | 
				
			||||||
            config_json=json.dumps(self.config),
 | 
					            config_json=json.dumps(self.config),
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										152
									
								
								aprsd/kissclient.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								aprsd/kissclient.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,152 @@
 | 
				
			|||||||
 | 
					import asyncio
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from aioax25 import interface
 | 
				
			||||||
 | 
					from aioax25 import kiss as kiss
 | 
				
			||||||
 | 
					from aioax25.aprs import APRSInterface
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from aprsd import trace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TRANSPORT_TCPKISS = "tcpkiss"
 | 
				
			||||||
 | 
					TRANSPORT_SERIALKISS = "serialkiss"
 | 
				
			||||||
 | 
					LOG = logging.getLogger("APRSD")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class KISSClient:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _instance = None
 | 
				
			||||||
 | 
					    config = None
 | 
				
			||||||
 | 
					    ax25client = None
 | 
				
			||||||
 | 
					    loop = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __new__(cls, *args, **kwargs):
 | 
				
			||||||
 | 
					        """Singleton for this class."""
 | 
				
			||||||
 | 
					        if cls._instance is None:
 | 
				
			||||||
 | 
					            cls._instance = super().__new__(cls)
 | 
				
			||||||
 | 
					            # initialize shit here
 | 
				
			||||||
 | 
					        return cls._instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, config=None):
 | 
				
			||||||
 | 
					        if config:
 | 
				
			||||||
 | 
					            self.config = config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def kiss_enabled(config):
 | 
				
			||||||
 | 
					        """Return if tcp or serial KISS is enabled."""
 | 
				
			||||||
 | 
					        if "kiss" not in config:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if "serial" in config["kiss"]:
 | 
				
			||||||
 | 
					            if config["kiss"]["serial"].get("enabled", False):
 | 
				
			||||||
 | 
					                return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if "tcp" in config["kiss"]:
 | 
				
			||||||
 | 
					            if config["kiss"]["tcp"].get("enabled", False):
 | 
				
			||||||
 | 
					                return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def transport(config):
 | 
				
			||||||
 | 
					        if "serial" in config["kiss"]:
 | 
				
			||||||
 | 
					            if config["kiss"]["serial"].get("enabled", False):
 | 
				
			||||||
 | 
					                return TRANSPORT_SERIALKISS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if "tcp" in config["kiss"]:
 | 
				
			||||||
 | 
					            if config["kiss"]["tcp"].get("enabled", False):
 | 
				
			||||||
 | 
					                return TRANSPORT_TCPKISS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def client(self):
 | 
				
			||||||
 | 
					        if not self.ax25client:
 | 
				
			||||||
 | 
					            self.ax25client = self.setup_connection()
 | 
				
			||||||
 | 
					        return self.ax25client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reset(self):
 | 
				
			||||||
 | 
					        """Call this to fore a rebuild/reconnect."""
 | 
				
			||||||
 | 
					        self.ax25client.stop()
 | 
				
			||||||
 | 
					        del self.ax25client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @trace.trace
 | 
				
			||||||
 | 
					    def setup_connection(self):
 | 
				
			||||||
 | 
					        ax25client = Aioax25Client(self.config)
 | 
				
			||||||
 | 
					        LOG.debug("Complete")
 | 
				
			||||||
 | 
					        return ax25client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Aioax25Client:
 | 
				
			||||||
 | 
					    def __init__(self, config):
 | 
				
			||||||
 | 
					        self.config = config
 | 
				
			||||||
 | 
					        self.setup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setup(self):
 | 
				
			||||||
 | 
					        # we can be TCP kiss or Serial kiss
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.loop = asyncio.get_event_loop()
 | 
				
			||||||
 | 
					        if "serial" in self.config["kiss"] and self.config["kiss"]["serial"].get(
 | 
				
			||||||
 | 
					            "enabled",
 | 
				
			||||||
 | 
					            False,
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
 | 
					            LOG.debug(
 | 
				
			||||||
 | 
					                "Setting up Serial KISS connection to {}".format(
 | 
				
			||||||
 | 
					                    self.config["kiss"]["serial"]["device"],
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            self.kissdev = kiss.SerialKISSDevice(
 | 
				
			||||||
 | 
					                device=self.config["kiss"]["serial"]["device"],
 | 
				
			||||||
 | 
					                baudrate=self.config["kiss"]["serial"].get("baudrate", 9600),
 | 
				
			||||||
 | 
					                loop=self.loop,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        elif "tcp" in self.config["kiss"] and self.config["kiss"]["tcp"].get(
 | 
				
			||||||
 | 
					            "enabled",
 | 
				
			||||||
 | 
					            False,
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
 | 
					            LOG.debug(
 | 
				
			||||||
 | 
					                "Setting up KISSTCP Connection to {}:{}".format(
 | 
				
			||||||
 | 
					                    self.config["kiss"]["tcp"]["host"],
 | 
				
			||||||
 | 
					                    self.config["kiss"]["tcp"]["port"],
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            self.kissdev = kiss.TCPKISSDevice(
 | 
				
			||||||
 | 
					                self.config["kiss"]["tcp"]["host"],
 | 
				
			||||||
 | 
					                self.config["kiss"]["tcp"]["port"],
 | 
				
			||||||
 | 
					                loop=self.loop,
 | 
				
			||||||
 | 
					                log=LOG,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.kissdev.open()
 | 
				
			||||||
 | 
					        self.kissport0 = self.kissdev[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.debug("Creating AX25Interface")
 | 
				
			||||||
 | 
					        self.ax25int = interface.AX25Interface(kissport=self.kissport0, loop=self.loop)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.debug("Creating APRSInterface")
 | 
				
			||||||
 | 
					        self.aprsint = APRSInterface(
 | 
				
			||||||
 | 
					            ax25int=self.ax25int,
 | 
				
			||||||
 | 
					            mycall=self.config["kiss"]["callsign"],
 | 
				
			||||||
 | 
					            log=LOG,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def stop(self):
 | 
				
			||||||
 | 
					        LOG.debug(self.kissdev)
 | 
				
			||||||
 | 
					        self.kissdev._close()
 | 
				
			||||||
 | 
					        self.loop.stop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def consumer(self, callback, callsign=None):
 | 
				
			||||||
 | 
					        if not callsign:
 | 
				
			||||||
 | 
					            callsign = self.config["ham"]["callsign"]
 | 
				
			||||||
 | 
					        self.aprsint.bind(callback=callback, callsign="WB4BOR", ssid=12, regex=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def send(self, msg):
 | 
				
			||||||
 | 
					        """Send an APRS Message object."""
 | 
				
			||||||
 | 
					        payload = f"{msg._filter_for_send()}"
 | 
				
			||||||
 | 
					        self.aprsint.send_message(
 | 
				
			||||||
 | 
					            addressee=msg.tocall,
 | 
				
			||||||
 | 
					            message=payload,
 | 
				
			||||||
 | 
					            path=["WIDE1-1", "WIDE2-1"],
 | 
				
			||||||
 | 
					            oneshot=True,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_client():
 | 
				
			||||||
 | 
					    cl = KISSClient()
 | 
				
			||||||
 | 
					    return cl.client
 | 
				
			||||||
@ -37,7 +37,8 @@ import click_completion
 | 
				
			|||||||
# local imports here
 | 
					# local imports here
 | 
				
			||||||
import aprsd
 | 
					import aprsd
 | 
				
			||||||
from aprsd import (
 | 
					from aprsd import (
 | 
				
			||||||
    client, flask, messaging, packets, plugin, stats, threads, trace, utils,
 | 
					    client, flask, kissclient, messaging, packets, plugin, stats, threads,
 | 
				
			||||||
 | 
					    trace, utils,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -458,16 +459,28 @@ def server(
 | 
				
			|||||||
        trace.setup_tracing(["method", "api"])
 | 
					        trace.setup_tracing(["method", "api"])
 | 
				
			||||||
    stats.APRSDStats(config)
 | 
					    stats.APRSDStats(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        cl = client.Client(config)
 | 
					 | 
				
			||||||
        cl.client
 | 
					 | 
				
			||||||
    except LoginError:
 | 
					 | 
				
			||||||
        sys.exit(-1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Create the initial PM singleton and Register plugins
 | 
					    # Create the initial PM singleton and Register plugins
 | 
				
			||||||
    plugin_manager = plugin.PluginManager(config)
 | 
					    plugin_manager = plugin.PluginManager(config)
 | 
				
			||||||
    plugin_manager.setup_plugins()
 | 
					    plugin_manager.setup_plugins()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if config["aprs"].get("enabled", True):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            cl = client.Client(config)
 | 
				
			||||||
 | 
					            cl.client
 | 
				
			||||||
 | 
					        except LoginError:
 | 
				
			||||||
 | 
					            sys.exit(-1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rx_thread = threads.APRSDRXThread(
 | 
				
			||||||
 | 
					            msg_queues=threads.msg_queues,
 | 
				
			||||||
 | 
					            config=config,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        rx_thread.start()
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        LOG.info(
 | 
				
			||||||
 | 
					            "APRS network connection Not Enabled in config.  This is"
 | 
				
			||||||
 | 
					            " for setups without internet connectivity.",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Now load the msgTrack from disk if any
 | 
					    # Now load the msgTrack from disk if any
 | 
				
			||||||
    if flush:
 | 
					    if flush:
 | 
				
			||||||
        LOG.debug("Deleting saved MsgTrack.")
 | 
					        LOG.debug("Deleting saved MsgTrack.")
 | 
				
			||||||
@ -478,19 +491,15 @@ def server(
 | 
				
			|||||||
        messaging.MsgTrack().load()
 | 
					        messaging.MsgTrack().load()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    packets.PacketList(config=config)
 | 
					    packets.PacketList(config=config)
 | 
				
			||||||
 | 
					    packets.WatchList(config=config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rx_thread = threads.APRSDRXThread(
 | 
					    if kissclient.KISSClient.kiss_enabled(config):
 | 
				
			||||||
        msg_queues=threads.msg_queues,
 | 
					        kcl = kissclient.KISSClient(config=config)
 | 
				
			||||||
        config=config,
 | 
					        # This initializes the client object.
 | 
				
			||||||
    )
 | 
					        kcl.client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rx_thread.start()
 | 
					        kissrx_thread = threads.KISSRXThread(msg_queues=threads.msg_queues, config=config)
 | 
				
			||||||
 | 
					        kissrx_thread.start()
 | 
				
			||||||
    if "watch_list" in config["aprsd"] and config["aprsd"]["watch_list"].get(
 | 
					 | 
				
			||||||
        "enabled",
 | 
					 | 
				
			||||||
        True,
 | 
					 | 
				
			||||||
    ):
 | 
					 | 
				
			||||||
        packets.WatchList(config=config)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    messaging.MsgTrack().restart()
 | 
					    messaging.MsgTrack().restart()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import re
 | 
				
			|||||||
import threading
 | 
					import threading
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from aprsd import client, packets, stats, threads, trace, utils
 | 
					from aprsd import client, kissclient, packets, stats, threads, trace, utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOG = logging.getLogger("APRSD")
 | 
					LOG = logging.getLogger("APRSD")
 | 
				
			||||||
@ -18,6 +18,10 @@ LOG = logging.getLogger("APRSD")
 | 
				
			|||||||
# and it's ok, but don't send a usage string back
 | 
					# and it's ok, but don't send a usage string back
 | 
				
			||||||
NULL_MESSAGE = -1
 | 
					NULL_MESSAGE = -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MESSAGE_TRANSPORT_TCPKISS = "tcpkiss"
 | 
				
			||||||
 | 
					MESSAGE_TRANSPORT_SERIALKISS = "serialkiss"
 | 
				
			||||||
 | 
					MESSAGE_TRANSPORT_APRSIS = "aprsis"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MsgTrack:
 | 
					class MsgTrack:
 | 
				
			||||||
    """Class to keep track of outstanding text messages.
 | 
					    """Class to keep track of outstanding text messages.
 | 
				
			||||||
@ -228,7 +232,15 @@ class Message(metaclass=abc.ABCMeta):
 | 
				
			|||||||
    last_send_time = 0
 | 
					    last_send_time = 0
 | 
				
			||||||
    last_send_attempt = 0
 | 
					    last_send_attempt = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, fromcall, tocall, msg_id=None):
 | 
					    transport = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        fromcall,
 | 
				
			||||||
 | 
					        tocall,
 | 
				
			||||||
 | 
					        msg_id=None,
 | 
				
			||||||
 | 
					        transport=MESSAGE_TRANSPORT_APRSIS,
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
        self.fromcall = fromcall
 | 
					        self.fromcall = fromcall
 | 
				
			||||||
        self.tocall = tocall
 | 
					        self.tocall = tocall
 | 
				
			||||||
        if not msg_id:
 | 
					        if not msg_id:
 | 
				
			||||||
@ -236,11 +248,18 @@ class Message(metaclass=abc.ABCMeta):
 | 
				
			|||||||
            c.increment()
 | 
					            c.increment()
 | 
				
			||||||
            msg_id = c.value
 | 
					            msg_id = c.value
 | 
				
			||||||
        self.id = msg_id
 | 
					        self.id = msg_id
 | 
				
			||||||
 | 
					        self.transport = transport
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @abc.abstractmethod
 | 
					    @abc.abstractmethod
 | 
				
			||||||
    def send(self):
 | 
					    def send(self):
 | 
				
			||||||
        """Child class must declare."""
 | 
					        """Child class must declare."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_transport(self):
 | 
				
			||||||
 | 
					        if self.transport == MESSAGE_TRANSPORT_APRSIS:
 | 
				
			||||||
 | 
					            return client.get_client()
 | 
				
			||||||
 | 
					        elif self.transport == MESSAGE_TRANSPORT_TCPKISS:
 | 
				
			||||||
 | 
					            return kissclient.get_client()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RawMessage(Message):
 | 
					class RawMessage(Message):
 | 
				
			||||||
    """Send a raw message.
 | 
					    """Send a raw message.
 | 
				
			||||||
@ -252,8 +271,8 @@ class RawMessage(Message):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    message = None
 | 
					    message = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, message):
 | 
					    def __init__(self, message, transport=MESSAGE_TRANSPORT_APRSIS):
 | 
				
			||||||
        super().__init__(None, None, msg_id=None)
 | 
					        super().__init__(None, None, msg_id=None, transport=transport)
 | 
				
			||||||
        self.message = message
 | 
					        self.message = message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dict(self):
 | 
					    def dict(self):
 | 
				
			||||||
@ -282,7 +301,7 @@ class RawMessage(Message):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def send_direct(self):
 | 
					    def send_direct(self):
 | 
				
			||||||
        """Send a message without a separate thread."""
 | 
					        """Send a message without a separate thread."""
 | 
				
			||||||
        cl = client.get_client()
 | 
					        cl = self.get_transport()
 | 
				
			||||||
        log_message(
 | 
					        log_message(
 | 
				
			||||||
            "Sending Message Direct",
 | 
					            "Sending Message Direct",
 | 
				
			||||||
            str(self).rstrip("\n"),
 | 
					            str(self).rstrip("\n"),
 | 
				
			||||||
@ -290,7 +309,7 @@ class RawMessage(Message):
 | 
				
			|||||||
            tocall=self.tocall,
 | 
					            tocall=self.tocall,
 | 
				
			||||||
            fromcall=self.fromcall,
 | 
					            fromcall=self.fromcall,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        cl.sendall(str(self))
 | 
					        cl.send(self)
 | 
				
			||||||
        stats.APRSDStats().msgs_sent_inc()
 | 
					        stats.APRSDStats().msgs_sent_inc()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -299,8 +318,16 @@ class TextMessage(Message):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    message = None
 | 
					    message = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, fromcall, tocall, message, msg_id=None, allow_delay=True):
 | 
					    def __init__(
 | 
				
			||||||
        super().__init__(fromcall, tocall, msg_id)
 | 
					        self,
 | 
				
			||||||
 | 
					        fromcall,
 | 
				
			||||||
 | 
					        tocall,
 | 
				
			||||||
 | 
					        message,
 | 
				
			||||||
 | 
					        msg_id=None,
 | 
				
			||||||
 | 
					        allow_delay=True,
 | 
				
			||||||
 | 
					        transport=MESSAGE_TRANSPORT_APRSIS,
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
 | 
					        super().__init__(fromcall, tocall, msg_id, transport=transport)
 | 
				
			||||||
        self.message = message
 | 
					        self.message = message
 | 
				
			||||||
        # do we try and save this message for later if we don't get
 | 
					        # do we try and save this message for later if we don't get
 | 
				
			||||||
        # an ack?  Some messages we don't want to do this ever.
 | 
					        # an ack?  Some messages we don't want to do this ever.
 | 
				
			||||||
@ -354,7 +381,7 @@ class TextMessage(Message):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def send_direct(self):
 | 
					    def send_direct(self):
 | 
				
			||||||
        """Send a message without a separate thread."""
 | 
					        """Send a message without a separate thread."""
 | 
				
			||||||
        cl = client.get_client()
 | 
					        cl = self.get_transport()
 | 
				
			||||||
        log_message(
 | 
					        log_message(
 | 
				
			||||||
            "Sending Message Direct",
 | 
					            "Sending Message Direct",
 | 
				
			||||||
            str(self).rstrip("\n"),
 | 
					            str(self).rstrip("\n"),
 | 
				
			||||||
@ -362,7 +389,7 @@ class TextMessage(Message):
 | 
				
			|||||||
            tocall=self.tocall,
 | 
					            tocall=self.tocall,
 | 
				
			||||||
            fromcall=self.fromcall,
 | 
					            fromcall=self.fromcall,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        cl.sendall(str(self))
 | 
					        cl.send(self)
 | 
				
			||||||
        stats.APRSDStats().msgs_tx_inc()
 | 
					        stats.APRSDStats().msgs_tx_inc()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -382,7 +409,6 @@ class SendMessageThread(threads.APRSDThread):
 | 
				
			|||||||
        last send attempt is old enough.
 | 
					        last send attempt is old enough.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        cl = client.get_client()
 | 
					 | 
				
			||||||
        tracker = MsgTrack()
 | 
					        tracker = MsgTrack()
 | 
				
			||||||
        # lets see if the message is still in the tracking queue
 | 
					        # lets see if the message is still in the tracking queue
 | 
				
			||||||
        msg = tracker.get(self.msg.id)
 | 
					        msg = tracker.get(self.msg.id)
 | 
				
			||||||
@ -392,6 +418,7 @@ class SendMessageThread(threads.APRSDThread):
 | 
				
			|||||||
            LOG.info("Message Send Complete via Ack.")
 | 
					            LOG.info("Message Send Complete via Ack.")
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
 | 
					            cl = msg.get_transport()
 | 
				
			||||||
            send_now = False
 | 
					            send_now = False
 | 
				
			||||||
            if msg.last_send_attempt == msg.retry_count:
 | 
					            if msg.last_send_attempt == msg.retry_count:
 | 
				
			||||||
                # we reached the send limit, don't send again
 | 
					                # we reached the send limit, don't send again
 | 
				
			||||||
@ -422,7 +449,7 @@ class SendMessageThread(threads.APRSDThread):
 | 
				
			|||||||
                    retry_number=msg.last_send_attempt,
 | 
					                    retry_number=msg.last_send_attempt,
 | 
				
			||||||
                    msg_num=msg.id,
 | 
					                    msg_num=msg.id,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                cl.sendall(str(msg))
 | 
					                cl.send(msg)
 | 
				
			||||||
                stats.APRSDStats().msgs_tx_inc()
 | 
					                stats.APRSDStats().msgs_tx_inc()
 | 
				
			||||||
                packets.PacketList().add(msg.dict())
 | 
					                packets.PacketList().add(msg.dict())
 | 
				
			||||||
                msg.last_send_time = datetime.datetime.now()
 | 
					                msg.last_send_time = datetime.datetime.now()
 | 
				
			||||||
@ -436,8 +463,8 @@ class SendMessageThread(threads.APRSDThread):
 | 
				
			|||||||
class AckMessage(Message):
 | 
					class AckMessage(Message):
 | 
				
			||||||
    """Class for building Acks and sending them."""
 | 
					    """Class for building Acks and sending them."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, fromcall, tocall, msg_id):
 | 
					    def __init__(self, fromcall, tocall, msg_id, transport=MESSAGE_TRANSPORT_APRSIS):
 | 
				
			||||||
        super().__init__(fromcall, tocall, msg_id=msg_id)
 | 
					        super().__init__(fromcall, tocall, msg_id=msg_id, transport=transport)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dict(self):
 | 
					    def dict(self):
 | 
				
			||||||
        now = datetime.datetime.now()
 | 
					        now = datetime.datetime.now()
 | 
				
			||||||
@ -463,6 +490,9 @@ class AckMessage(Message):
 | 
				
			|||||||
            self.id,
 | 
					            self.id,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _filter_for_send(self):
 | 
				
			||||||
 | 
					        return f"ack{self.id}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def send(self):
 | 
					    def send(self):
 | 
				
			||||||
        LOG.debug(f"Send ACK({self.tocall}:{self.id}) to radio.")
 | 
					        LOG.debug(f"Send ACK({self.tocall}:{self.id}) to radio.")
 | 
				
			||||||
        thread = SendAckThread(self)
 | 
					        thread = SendAckThread(self)
 | 
				
			||||||
@ -470,7 +500,7 @@ class AckMessage(Message):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def send_direct(self):
 | 
					    def send_direct(self):
 | 
				
			||||||
        """Send an ack message without a separate thread."""
 | 
					        """Send an ack message without a separate thread."""
 | 
				
			||||||
        cl = client.get_client()
 | 
					        cl = self.get_transport()
 | 
				
			||||||
        log_message(
 | 
					        log_message(
 | 
				
			||||||
            "Sending ack",
 | 
					            "Sending ack",
 | 
				
			||||||
            str(self).rstrip("\n"),
 | 
					            str(self).rstrip("\n"),
 | 
				
			||||||
@ -479,7 +509,7 @@ class AckMessage(Message):
 | 
				
			|||||||
            tocall=self.tocall,
 | 
					            tocall=self.tocall,
 | 
				
			||||||
            fromcall=self.fromcall,
 | 
					            fromcall=self.fromcall,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        cl.sendall(str(self))
 | 
					        cl.send(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SendAckThread(threads.APRSDThread):
 | 
					class SendAckThread(threads.APRSDThread):
 | 
				
			||||||
@ -515,7 +545,7 @@ class SendAckThread(threads.APRSDThread):
 | 
				
			|||||||
            send_now = True
 | 
					            send_now = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if send_now:
 | 
					        if send_now:
 | 
				
			||||||
            cl = client.get_client()
 | 
					            cl = self.ack.get_transport()
 | 
				
			||||||
            log_message(
 | 
					            log_message(
 | 
				
			||||||
                "Sending ack",
 | 
					                "Sending ack",
 | 
				
			||||||
                str(self.ack).rstrip("\n"),
 | 
					                str(self.ack).rstrip("\n"),
 | 
				
			||||||
@ -524,7 +554,7 @@ class SendAckThread(threads.APRSDThread):
 | 
				
			|||||||
                tocall=self.ack.tocall,
 | 
					                tocall=self.ack.tocall,
 | 
				
			||||||
                retry_number=self.ack.last_send_attempt,
 | 
					                retry_number=self.ack.last_send_attempt,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            cl.sendall(str(self.ack))
 | 
					            cl.send(self.ack)
 | 
				
			||||||
            stats.APRSDStats().ack_tx_inc()
 | 
					            stats.APRSDStats().ack_tx_inc()
 | 
				
			||||||
            packets.PacketList().add(self.ack.dict())
 | 
					            packets.PacketList().add(self.ack.dict())
 | 
				
			||||||
            self.ack.last_send_attempt += 1
 | 
					            self.ack.last_send_attempt += 1
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ import tracemalloc
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import aprslib
 | 
					import aprslib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from aprsd import client, messaging, packets, plugin, stats, utils
 | 
					from aprsd import client, kissclient, messaging, packets, plugin, stats, utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOG = logging.getLogger("APRSD")
 | 
					LOG = logging.getLogger("APRSD")
 | 
				
			||||||
@ -180,9 +180,10 @@ class APRSDRXThread(APRSDThread):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class APRSDProcessPacketThread(APRSDThread):
 | 
					class APRSDProcessPacketThread(APRSDThread):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, packet, config):
 | 
					    def __init__(self, packet, config, transport="aprsis"):
 | 
				
			||||||
        self.packet = packet
 | 
					        self.packet = packet
 | 
				
			||||||
        self.config = config
 | 
					        self.config = config
 | 
				
			||||||
 | 
					        self.transport = transport
 | 
				
			||||||
        name = self.packet["raw"][:10]
 | 
					        name = self.packet["raw"][:10]
 | 
				
			||||||
        super().__init__(f"RX_PACKET-{name}")
 | 
					        super().__init__(f"RX_PACKET-{name}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -237,6 +238,7 @@ class APRSDProcessPacketThread(APRSDThread):
 | 
				
			|||||||
                    self.config["aprs"]["login"],
 | 
					                    self.config["aprs"]["login"],
 | 
				
			||||||
                    fromcall,
 | 
					                    fromcall,
 | 
				
			||||||
                    msg_id=msg_id,
 | 
					                    msg_id=msg_id,
 | 
				
			||||||
 | 
					                    transport=self.transport,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                ack.send()
 | 
					                ack.send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -255,6 +257,7 @@ class APRSDProcessPacketThread(APRSDThread):
 | 
				
			|||||||
                                self.config["aprs"]["login"],
 | 
					                                self.config["aprs"]["login"],
 | 
				
			||||||
                                fromcall,
 | 
					                                fromcall,
 | 
				
			||||||
                                subreply,
 | 
					                                subreply,
 | 
				
			||||||
 | 
					                                transport=self.transport,
 | 
				
			||||||
                            )
 | 
					                            )
 | 
				
			||||||
                            msg.send()
 | 
					                            msg.send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -271,6 +274,7 @@ class APRSDProcessPacketThread(APRSDThread):
 | 
				
			|||||||
                                self.config["aprs"]["login"],
 | 
					                                self.config["aprs"]["login"],
 | 
				
			||||||
                                fromcall,
 | 
					                                fromcall,
 | 
				
			||||||
                                reply,
 | 
					                                reply,
 | 
				
			||||||
 | 
					                                transport=self.transport,
 | 
				
			||||||
                            )
 | 
					                            )
 | 
				
			||||||
                            msg.send()
 | 
					                            msg.send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -283,6 +287,7 @@ class APRSDProcessPacketThread(APRSDThread):
 | 
				
			|||||||
                        self.config["aprs"]["login"],
 | 
					                        self.config["aprs"]["login"],
 | 
				
			||||||
                        fromcall,
 | 
					                        fromcall,
 | 
				
			||||||
                        reply,
 | 
					                        reply,
 | 
				
			||||||
 | 
					                        transport=self.transport,
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                    msg.send()
 | 
					                    msg.send()
 | 
				
			||||||
            except Exception as ex:
 | 
					            except Exception as ex:
 | 
				
			||||||
@ -294,6 +299,7 @@ class APRSDProcessPacketThread(APRSDThread):
 | 
				
			|||||||
                        self.config["aprs"]["login"],
 | 
					                        self.config["aprs"]["login"],
 | 
				
			||||||
                        fromcall,
 | 
					                        fromcall,
 | 
				
			||||||
                        reply,
 | 
					                        reply,
 | 
				
			||||||
 | 
					                        transport=self.transport,
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                    msg.send()
 | 
					                    msg.send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -314,3 +320,66 @@ class APRSDTXThread(APRSDThread):
 | 
				
			|||||||
            pass
 | 
					            pass
 | 
				
			||||||
        # Continue to loop
 | 
					        # Continue to loop
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class KISSRXThread(APRSDThread):
 | 
				
			||||||
 | 
					    """Thread that connects to direwolf's TCPKISS interface.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    All Packets are processed and sent back out the direwolf
 | 
				
			||||||
 | 
					    interface instead of the aprs-is server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, msg_queues, config):
 | 
				
			||||||
 | 
					        super().__init__("KISSRX_MSG")
 | 
				
			||||||
 | 
					        self.msg_queues = msg_queues
 | 
				
			||||||
 | 
					        self.config = config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def stop(self):
 | 
				
			||||||
 | 
					        self.thread_stop = True
 | 
				
			||||||
 | 
					        kissclient.get_client().stop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def loop(self):
 | 
				
			||||||
 | 
					        kiss_client = kissclient.get_client()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # setup the consumer of messages and block until a messages
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            # This will register a packet consumer with aprslib
 | 
				
			||||||
 | 
					            # When new packets come in the consumer will process
 | 
				
			||||||
 | 
					            # the packet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Do a partial here because the consumer signature doesn't allow
 | 
				
			||||||
 | 
					            # For kwargs to be passed in to the consumer func we declare
 | 
				
			||||||
 | 
					            # and the aprslib developer didn't want to allow a PR to add
 | 
				
			||||||
 | 
					            # kwargs.  :(
 | 
				
			||||||
 | 
					            # https://github.com/rossengeorgiev/aprs-python/pull/56
 | 
				
			||||||
 | 
					            kiss_client.consumer(self.process_packet, callsign=self.config["kiss"]["callsign"])
 | 
				
			||||||
 | 
					            kiss_client.loop.run_forever()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        except aprslib.exceptions.ConnectionDrop:
 | 
				
			||||||
 | 
					            LOG.error("Connection dropped, reconnecting")
 | 
				
			||||||
 | 
					            time.sleep(5)
 | 
				
			||||||
 | 
					            # Force the deletion of the client object connected to aprs
 | 
				
			||||||
 | 
					            # This will cause a reconnect, next time client.get_client()
 | 
				
			||||||
 | 
					            # is called
 | 
				
			||||||
 | 
					            client.Client().reset()
 | 
				
			||||||
 | 
					        # Continue to loop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def process_packet(self, interface, frame):
 | 
				
			||||||
 | 
					        """Process a packet recieved from aprs-is server."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.debug(f"Got an APRS Frame '{frame}'")
 | 
				
			||||||
 | 
					        # try and nuke the * from the fromcall sign.
 | 
				
			||||||
 | 
					        frame.header._source._ch = False
 | 
				
			||||||
 | 
					        payload = str(frame.payload.decode())
 | 
				
			||||||
 | 
					        msg = f"{str(frame.header)}:{payload}"
 | 
				
			||||||
 | 
					        LOG.debug(f"Decoding {msg}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        packet = aprslib.parse(msg)
 | 
				
			||||||
 | 
					        LOG.debug(packet)
 | 
				
			||||||
 | 
					        thread = APRSDProcessPacketThread(
 | 
				
			||||||
 | 
					            packet=packet, config=self.config,
 | 
				
			||||||
 | 
					            transport=messaging.MESSAGE_TRANSPORT_TCPKISS,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        thread.start()
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
				
			|||||||
@ -37,11 +37,24 @@ DEFAULT_DATE_FORMAT = "%m/%d/%Y %I:%M:%S %p"
 | 
				
			|||||||
DEFAULT_CONFIG_DICT = {
 | 
					DEFAULT_CONFIG_DICT = {
 | 
				
			||||||
    "ham": {"callsign": "NOCALL"},
 | 
					    "ham": {"callsign": "NOCALL"},
 | 
				
			||||||
    "aprs": {
 | 
					    "aprs": {
 | 
				
			||||||
        "login": "NOCALL",
 | 
					        "enabled": True,
 | 
				
			||||||
 | 
					        "login": "CALLSIGN",
 | 
				
			||||||
        "password": "00000",
 | 
					        "password": "00000",
 | 
				
			||||||
        "host": "rotate.aprs2.net",
 | 
					        "host": "rotate.aprs2.net",
 | 
				
			||||||
        "port": 14580,
 | 
					        "port": 14580,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "kiss": {
 | 
				
			||||||
 | 
					        "tcp": {
 | 
				
			||||||
 | 
					            "enabled": False,
 | 
				
			||||||
 | 
					            "host": "direwolf.ip.address",
 | 
				
			||||||
 | 
					            "port": "8001",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "serial": {
 | 
				
			||||||
 | 
					            "enabled": False,
 | 
				
			||||||
 | 
					            "device": "/dev/ttyS0",
 | 
				
			||||||
 | 
					            "baudrate": 9600,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "aprsd": {
 | 
					    "aprsd": {
 | 
				
			||||||
        "logfile": "/tmp/aprsd.log",
 | 
					        "logfile": "/tmp/aprsd.log",
 | 
				
			||||||
        "logformat": DEFAULT_LOG_FORMAT,
 | 
					        "logformat": DEFAULT_LOG_FORMAT,
 | 
				
			||||||
@ -172,6 +185,9 @@ def add_config_comments(raw_yaml):
 | 
				
			|||||||
        # lets insert a comment
 | 
					        # lets insert a comment
 | 
				
			||||||
        raw_yaml = insert_str(
 | 
					        raw_yaml = insert_str(
 | 
				
			||||||
            raw_yaml,
 | 
					            raw_yaml,
 | 
				
			||||||
 | 
					            "\n    # Set enabled to False if there is no internet connectivity."
 | 
				
			||||||
 | 
					            "\n    # This is useful for a direwolf KISS aprs connection only. "
 | 
				
			||||||
 | 
					            "\n"
 | 
				
			||||||
            "\n    # Get the passcode for your callsign here: "
 | 
					            "\n    # Get the passcode for your callsign here: "
 | 
				
			||||||
            "\n    # https://apps.magicbug.co.uk/passcode",
 | 
					            "\n    # https://apps.magicbug.co.uk/passcode",
 | 
				
			||||||
            end_idx,
 | 
					            end_idx,
 | 
				
			||||||
 | 
				
			|||||||
@ -220,7 +220,7 @@ function updateQuadData(chart, label, first, second, third, fourth) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function update_stats( data ) {
 | 
					function update_stats( data ) {
 | 
				
			||||||
    $("#version").text( data["stats"]["aprsd"]["version"] );
 | 
					    $("#version").text( data["stats"]["aprsd"]["version"] );
 | 
				
			||||||
    $("#aprsis").html( "APRS-IS Server: <a href='http://status.aprs2.net' >" + data["stats"]["aprs-is"]["server"] + "</a>" );
 | 
					    $("#aprs_connection").html( data["aprs_connection"] );
 | 
				
			||||||
    $("#uptime").text( "uptime: " + data["stats"]["aprsd"]["uptime"] );
 | 
					    $("#uptime").text( "uptime: " + data["stats"]["aprsd"]["uptime"] );
 | 
				
			||||||
    const html_pretty = Prism.highlight(JSON.stringify(data, null, '\t'), Prism.languages.json, 'json');
 | 
					    const html_pretty = Prism.highlight(JSON.stringify(data, null, '\t'), Prism.languages.json, 'json');
 | 
				
			||||||
    $("#jsonstats").html(html_pretty);
 | 
					    $("#jsonstats").html(html_pretty);
 | 
				
			||||||
 | 
				
			|||||||
@ -58,7 +58,7 @@
 | 
				
			|||||||
            <div class='left floated ten wide column'>
 | 
					            <div class='left floated ten wide column'>
 | 
				
			||||||
                <span style='color: green'>{{ callsign }}</span>
 | 
					                <span style='color: green'>{{ callsign }}</span>
 | 
				
			||||||
                connected to
 | 
					                connected to
 | 
				
			||||||
                <span style='color: blue' id='aprsis'>NONE</span>
 | 
					                <span style='color: blue' id='aprs_connection'>{{ aprs_connection|safe }}</span>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <div class='right floated four wide column'>
 | 
					            <div class='right floated four wide column'>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					aioax25>=0.0.10
 | 
				
			||||||
aprslib
 | 
					aprslib
 | 
				
			||||||
click
 | 
					click
 | 
				
			||||||
click-completion
 | 
					click-completion
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,8 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
#    pip-compile requirements.in
 | 
					#    pip-compile requirements.in
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					aioax25==0.0.10
 | 
				
			||||||
 | 
					    # via -r requirements.in
 | 
				
			||||||
aprslib==0.6.47
 | 
					aprslib==0.6.47
 | 
				
			||||||
    # via -r requirements.in
 | 
					    # via -r requirements.in
 | 
				
			||||||
backoff==1.10.0
 | 
					backoff==1.10.0
 | 
				
			||||||
@ -21,6 +23,8 @@ click==7.1.2
 | 
				
			|||||||
    #   -r requirements.in
 | 
					    #   -r requirements.in
 | 
				
			||||||
    #   click-completion
 | 
					    #   click-completion
 | 
				
			||||||
    #   flask
 | 
					    #   flask
 | 
				
			||||||
 | 
					contexter==0.1.4
 | 
				
			||||||
 | 
					    # via signalslot
 | 
				
			||||||
cryptography==3.4.7
 | 
					cryptography==3.4.7
 | 
				
			||||||
    # via pyopenssl
 | 
					    # via pyopenssl
 | 
				
			||||||
dnspython==2.1.0
 | 
					dnspython==2.1.0
 | 
				
			||||||
@ -72,6 +76,8 @@ pycparser==2.20
 | 
				
			|||||||
    # via cffi
 | 
					    # via cffi
 | 
				
			||||||
pyopenssl==20.0.1
 | 
					pyopenssl==20.0.1
 | 
				
			||||||
    # via opencage
 | 
					    # via opencage
 | 
				
			||||||
 | 
					pyserial==3.5
 | 
				
			||||||
 | 
					    # via aioax25
 | 
				
			||||||
python-dateutil==2.8.1
 | 
					python-dateutil==2.8.1
 | 
				
			||||||
    # via pandas
 | 
					    # via pandas
 | 
				
			||||||
pytz==2021.1
 | 
					pytz==2021.1
 | 
				
			||||||
@ -88,6 +94,8 @@ requests==2.25.1
 | 
				
			|||||||
    #   yfinance
 | 
					    #   yfinance
 | 
				
			||||||
shellingham==1.4.0
 | 
					shellingham==1.4.0
 | 
				
			||||||
    # via click-completion
 | 
					    # via click-completion
 | 
				
			||||||
 | 
					signalslot==0.1.2
 | 
				
			||||||
 | 
					    # via aioax25
 | 
				
			||||||
six==1.15.0
 | 
					six==1.15.0
 | 
				
			||||||
    # via
 | 
					    # via
 | 
				
			||||||
    #   -r requirements.in
 | 
					    #   -r requirements.in
 | 
				
			||||||
@ -96,12 +104,15 @@ six==1.15.0
 | 
				
			|||||||
    #   opencage
 | 
					    #   opencage
 | 
				
			||||||
    #   pyopenssl
 | 
					    #   pyopenssl
 | 
				
			||||||
    #   python-dateutil
 | 
					    #   python-dateutil
 | 
				
			||||||
 | 
					    #   signalslot
 | 
				
			||||||
thesmuggler==1.0.1
 | 
					thesmuggler==1.0.1
 | 
				
			||||||
    # via -r requirements.in
 | 
					    # via -r requirements.in
 | 
				
			||||||
update-checker==0.18.0
 | 
					update-checker==0.18.0
 | 
				
			||||||
    # via -r requirements.in
 | 
					    # via -r requirements.in
 | 
				
			||||||
urllib3==1.26.5
 | 
					urllib3==1.26.5
 | 
				
			||||||
    # via requests
 | 
					    # via requests
 | 
				
			||||||
 | 
					weakrefmethod==1.0.3
 | 
				
			||||||
 | 
					    # via signalslot
 | 
				
			||||||
werkzeug==1.0.1
 | 
					werkzeug==1.0.1
 | 
				
			||||||
    # via flask
 | 
					    # via flask
 | 
				
			||||||
yfinance==0.1.59
 | 
					yfinance==0.1.59
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user