mirror of
https://github.com/craigerl/aprsd.git
synced 2024-11-21 23:55:17 -05:00
Added the ability to use direwolf KISS socket
This patch adds APRS KISS connectivity. I have tested this with a running Direwolf install via either a serial KISS connection or the optional new TCPKISS connection, both to Direwolf. This adds the new required aioax25 python library for the underlying KISS and AX25 support. NOTE: For the TCPKISS connection, this patch requires a pull request patch the aioax25 library to include a TCP Based KISS TNC client to enable the TCPKISS client So you will need to pull down this PR https://github.com/sjlongland/aioax25/pull/7 To enable this, Edit your aprsd.yml file and enable one of the 2 KISS connections. Only one is supported at a time. kiss: serial: enabled: True device: /dev/ttyS1 baudrate: 9600 or kiss: tcp: enabled: True host: "ip address/hostname of direwolf" port: "direwolf configured kiss port" This patch alters the Message object classes to be able to send messages out via the aprslib socket connection to the APRS-IS network on the internet, or via the direwolf KISS TCP socket, depending on the origination of the initial message coming in. If an APRS message comes in via APRS-IS, then replies will go out APRS-IS. IF an APRS message comes in via direwolf, then replies will go out via direwolf KISS TCP socket. Both can work at the same time. TODO: I need some real APRS message packets to verify that the new thread is processing packets correctly through the plugins and able to send the resulting messages back out to direwolf. Have a hard coded callsign for now in the kissclient consumer call, just so I can see messages coming in from direwolf. I dont' have an APRS capable radio at the moment to send messages directly to direwolf. Might need to write a simple python socket server to send fake APRS messages to aprsd kiss, just for finishing up development.
This commit is contained in:
parent
a7d79a6e1b
commit
b53e2ba7fe
@ -90,6 +90,11 @@ class Aprsdis(aprslib.IS):
|
||||
self.thread_stop = True
|
||||
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):
|
||||
"""
|
||||
Generator for complete lines, received from the server
|
||||
|
138
aprsd/kissclient.py
Normal file
138
aprsd/kissclient.py
Normal file
@ -0,0 +1,138 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from aioax25 import frame as axframe
|
||||
from aioax25 import interface
|
||||
from aioax25 import kiss as kiss
|
||||
from aioax25.aprs import APRSInterface
|
||||
from aprsd import trace
|
||||
|
||||
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 "serial" in config["kiss"]:
|
||||
if config["kiss"]["serial"].get("enabled", False):
|
||||
return True
|
||||
|
||||
if "tcp" in config["kiss"]:
|
||||
if config["kiss"]["serial"].get("enabled", False):
|
||||
return True
|
||||
|
||||
@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"]["host"],
|
||||
self.config["kiss"]["port"],
|
||||
),
|
||||
)
|
||||
self.kissdev = kiss.TCPKISSDevice(
|
||||
self.config["kiss"]["host"],
|
||||
self.config["kiss"]["port"],
|
||||
loop=self.loop,
|
||||
)
|
||||
|
||||
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["ham"]["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=callsign, regex=True)
|
||||
|
||||
def send(self, msg):
|
||||
"""Send an APRS Message object."""
|
||||
payload = msg._filter_for_send()
|
||||
frame = axframe.AX25UnnumberedInformationFrame(
|
||||
msg.tocall,
|
||||
msg.fromcall.encode("UTF-8"),
|
||||
pid=0xF0,
|
||||
repeaters=b"WIDE2-1",
|
||||
payload=payload,
|
||||
)
|
||||
LOG.debug(frame)
|
||||
self.ax25int.transmit(frame)
|
||||
|
||||
|
||||
def get_client():
|
||||
cl = KISSClient()
|
||||
return cl.client
|
@ -37,7 +37,7 @@ import click_completion
|
||||
# local imports here
|
||||
import aprsd
|
||||
from aprsd import (
|
||||
client, flask, messaging, packets, plugin, stats, threads, trace, utils,
|
||||
client, flask, kissclient, messaging, packets, plugin, stats, threads, trace, utils,
|
||||
)
|
||||
|
||||
|
||||
@ -458,16 +458,22 @@ def server(
|
||||
trace.setup_tracing(["method", "api"])
|
||||
stats.APRSDStats(config)
|
||||
|
||||
try:
|
||||
cl = client.Client(config)
|
||||
cl.client
|
||||
except LoginError:
|
||||
sys.exit(-1)
|
||||
|
||||
# Create the initial PM singleton and Register plugins
|
||||
plugin_manager = plugin.PluginManager(config)
|
||||
plugin_manager.setup_plugins()
|
||||
|
||||
if config["aprs"].get("enabled", True):
|
||||
try:
|
||||
cl = client.Client(config)
|
||||
cl.client
|
||||
except LoginError:
|
||||
sys.exit(-1)
|
||||
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
|
||||
if flush:
|
||||
LOG.debug("Deleting saved MsgTrack.")
|
||||
@ -492,6 +498,14 @@ def server(
|
||||
):
|
||||
packets.WatchList(config=config)
|
||||
|
||||
if kissclient.KISSClient.kiss_enabled(config):
|
||||
kcl = kissclient.KISSClient(config=config)
|
||||
kcl.client
|
||||
|
||||
kissrx_thread = threads.KISSRXThread(msg_queues=msg_queues, config=config)
|
||||
kissrx_thread.start()
|
||||
|
||||
|
||||
messaging.MsgTrack().restart()
|
||||
|
||||
keepalive = threads.KeepAliveThread(config=config)
|
||||
|
@ -9,7 +9,7 @@ import re
|
||||
import threading
|
||||
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")
|
||||
@ -18,6 +18,9 @@ LOG = logging.getLogger("APRSD")
|
||||
# and it's ok, but don't send a usage string back
|
||||
NULL_MESSAGE = -1
|
||||
|
||||
MESSAGE_TRANSPORT_TCPKISS = "tcpkiss"
|
||||
MESSAGE_TRANSPORT_APRSIS = "aprsis"
|
||||
|
||||
|
||||
class MsgTrack:
|
||||
"""Class to keep track of outstanding text messages.
|
||||
@ -228,7 +231,15 @@ class Message(metaclass=abc.ABCMeta):
|
||||
last_send_time = 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.tocall = tocall
|
||||
if not msg_id:
|
||||
@ -236,11 +247,18 @@ class Message(metaclass=abc.ABCMeta):
|
||||
c.increment()
|
||||
msg_id = c.value
|
||||
self.id = msg_id
|
||||
self.transport = transport
|
||||
|
||||
@abc.abstractmethod
|
||||
def send(self):
|
||||
"""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):
|
||||
"""Send a raw message.
|
||||
@ -252,8 +270,8 @@ class RawMessage(Message):
|
||||
|
||||
message = None
|
||||
|
||||
def __init__(self, message):
|
||||
super().__init__(None, None, msg_id=None)
|
||||
def __init__(self, message, transport=MESSAGE_TRANSPORT_APRSIS):
|
||||
super().__init__(None, None, msg_id=None, transport=transport)
|
||||
self.message = message
|
||||
|
||||
def dict(self):
|
||||
@ -282,7 +300,7 @@ class RawMessage(Message):
|
||||
|
||||
def send_direct(self):
|
||||
"""Send a message without a separate thread."""
|
||||
cl = client.get_client()
|
||||
cl = self.get_transport()
|
||||
log_message(
|
||||
"Sending Message Direct",
|
||||
str(self).rstrip("\n"),
|
||||
@ -290,7 +308,7 @@ class RawMessage(Message):
|
||||
tocall=self.tocall,
|
||||
fromcall=self.fromcall,
|
||||
)
|
||||
cl.sendall(str(self))
|
||||
cl.send(self)
|
||||
stats.APRSDStats().msgs_sent_inc()
|
||||
|
||||
|
||||
@ -299,8 +317,16 @@ class TextMessage(Message):
|
||||
|
||||
message = None
|
||||
|
||||
def __init__(self, fromcall, tocall, message, msg_id=None, allow_delay=True):
|
||||
super().__init__(fromcall, tocall, msg_id)
|
||||
def __init__(
|
||||
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
|
||||
# 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.
|
||||
@ -354,7 +380,7 @@ class TextMessage(Message):
|
||||
|
||||
def send_direct(self):
|
||||
"""Send a message without a separate thread."""
|
||||
cl = client.get_client()
|
||||
cl = self.get_transport()
|
||||
log_message(
|
||||
"Sending Message Direct",
|
||||
str(self).rstrip("\n"),
|
||||
@ -362,7 +388,7 @@ class TextMessage(Message):
|
||||
tocall=self.tocall,
|
||||
fromcall=self.fromcall,
|
||||
)
|
||||
cl.sendall(str(self))
|
||||
cl.send(self)
|
||||
stats.APRSDStats().msgs_tx_inc()
|
||||
|
||||
|
||||
@ -382,7 +408,6 @@ class SendMessageThread(threads.APRSDThread):
|
||||
last send attempt is old enough.
|
||||
|
||||
"""
|
||||
cl = client.get_client()
|
||||
tracker = MsgTrack()
|
||||
# lets see if the message is still in the tracking queue
|
||||
msg = tracker.get(self.msg.id)
|
||||
@ -392,6 +417,7 @@ class SendMessageThread(threads.APRSDThread):
|
||||
LOG.info("Message Send Complete via Ack.")
|
||||
return False
|
||||
else:
|
||||
cl = msg.get_transport()
|
||||
send_now = False
|
||||
if msg.last_send_attempt == msg.retry_count:
|
||||
# we reached the send limit, don't send again
|
||||
@ -422,7 +448,7 @@ class SendMessageThread(threads.APRSDThread):
|
||||
retry_number=msg.last_send_attempt,
|
||||
msg_num=msg.id,
|
||||
)
|
||||
cl.sendall(str(msg))
|
||||
cl.send(msg)
|
||||
stats.APRSDStats().msgs_tx_inc()
|
||||
packets.PacketList().add(msg.dict())
|
||||
msg.last_send_time = datetime.datetime.now()
|
||||
@ -436,8 +462,8 @@ class SendMessageThread(threads.APRSDThread):
|
||||
class AckMessage(Message):
|
||||
"""Class for building Acks and sending them."""
|
||||
|
||||
def __init__(self, fromcall, tocall, msg_id):
|
||||
super().__init__(fromcall, tocall, msg_id=msg_id)
|
||||
def __init__(self, fromcall, tocall, msg_id, transport=MESSAGE_TRANSPORT_APRSIS):
|
||||
super().__init__(fromcall, tocall, msg_id=msg_id, transport=transport)
|
||||
|
||||
def dict(self):
|
||||
now = datetime.datetime.now()
|
||||
@ -470,7 +496,7 @@ class AckMessage(Message):
|
||||
|
||||
def send_direct(self):
|
||||
"""Send an ack message without a separate thread."""
|
||||
cl = client.get_client()
|
||||
cl = self.get_transport()
|
||||
log_message(
|
||||
"Sending ack",
|
||||
str(self).rstrip("\n"),
|
||||
@ -479,7 +505,7 @@ class AckMessage(Message):
|
||||
tocall=self.tocall,
|
||||
fromcall=self.fromcall,
|
||||
)
|
||||
cl.sendall(str(self))
|
||||
cl.send(self)
|
||||
|
||||
|
||||
class SendAckThread(threads.APRSDThread):
|
||||
@ -515,7 +541,7 @@ class SendAckThread(threads.APRSDThread):
|
||||
send_now = True
|
||||
|
||||
if send_now:
|
||||
cl = client.get_client()
|
||||
cl = self.ack.get_transport()
|
||||
log_message(
|
||||
"Sending ack",
|
||||
str(self.ack).rstrip("\n"),
|
||||
@ -524,7 +550,7 @@ class SendAckThread(threads.APRSDThread):
|
||||
tocall=self.ack.tocall,
|
||||
retry_number=self.ack.last_send_attempt,
|
||||
)
|
||||
cl.sendall(str(self.ack))
|
||||
cl.send(self.ack)
|
||||
stats.APRSDStats().ack_tx_inc()
|
||||
packets.PacketList().add(self.ack.dict())
|
||||
self.ack.last_send_attempt += 1
|
||||
|
175
aprsd/threads.py
175
aprsd/threads.py
@ -8,7 +8,7 @@ import tracemalloc
|
||||
|
||||
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")
|
||||
@ -314,3 +314,176 @@ class APRSDTXThread(APRSDThread):
|
||||
pass
|
||||
# Continue to loop
|
||||
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="APN382")
|
||||
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
|
||||
|
||||
@trace.trace
|
||||
def process_packet(self, interface, frame, match):
|
||||
"""Process a packet recieved from aprs-is server."""
|
||||
|
||||
LOG.debug("Got an APRS Frame '{}'".format(frame))
|
||||
|
||||
payload = str(frame.payload.decode())
|
||||
msg = "{}:{}".format(str(frame.header), payload)
|
||||
|
||||
packet = aprslib.parse(msg)
|
||||
LOG.debug(packet)
|
||||
|
||||
try:
|
||||
stats.APRSDStats().msgs_rx_inc()
|
||||
|
||||
msg = packet.get("message_text", None)
|
||||
msg_format = packet.get("format", None)
|
||||
msg_response = packet.get("response", None)
|
||||
if msg_format == "message" and msg:
|
||||
# we want to send the message through the
|
||||
# plugins
|
||||
self.process_message_packet(packet)
|
||||
return
|
||||
elif msg_response == "ack":
|
||||
self.process_ack_packet(packet)
|
||||
return
|
||||
|
||||
if msg_format == "mic-e":
|
||||
# process a mic-e packet
|
||||
self.process_mic_e_packet(packet)
|
||||
return
|
||||
|
||||
except (aprslib.ParseError, aprslib.UnknownFormat) as exp:
|
||||
LOG.exception("Failed to parse packet from aprs-is", exp)
|
||||
|
||||
@trace.trace
|
||||
def process_message_packet(self, packet):
|
||||
LOG.debug("Message packet rx")
|
||||
fromcall = packet["from"]
|
||||
message = packet.get("message_text", None)
|
||||
msg_id = packet.get("msgNo", "0")
|
||||
messaging.log_message(
|
||||
"Received Message",
|
||||
packet["raw"],
|
||||
message,
|
||||
fromcall=fromcall,
|
||||
msg_num=msg_id,
|
||||
)
|
||||
found_command = False
|
||||
# Get singleton of the PM
|
||||
pm = plugin.PluginManager()
|
||||
try:
|
||||
results = pm.run(fromcall=fromcall, message=message, ack=msg_id)
|
||||
for reply in results:
|
||||
found_command = True
|
||||
# A plugin can return a null message flag which signals
|
||||
# us that they processed the message correctly, but have
|
||||
# nothing to reply with, so we avoid replying with a usage string
|
||||
if reply is not messaging.NULL_MESSAGE:
|
||||
LOG.debug("Sending '{}'".format(reply))
|
||||
|
||||
msg = messaging.TextMessage(
|
||||
self.config["aprs"]["login"],
|
||||
fromcall,
|
||||
reply,
|
||||
transport=messaging.MESSAGE_TRANSPORT_TCPKISS,
|
||||
)
|
||||
self.msg_queues["tx"].put(msg)
|
||||
else:
|
||||
LOG.debug("Got NULL MESSAGE from plugin")
|
||||
|
||||
if not found_command:
|
||||
plugins = pm.get_plugins()
|
||||
names = [x.command_name for x in plugins]
|
||||
names.sort()
|
||||
|
||||
# reply = "Usage: {}".format(", ".join(names))
|
||||
reply = "Usage: weather, locate [call], time, fortune, ping"
|
||||
|
||||
msg = messaging.TextMessage(
|
||||
self.config["aprs"]["login"],
|
||||
fromcall,
|
||||
reply,
|
||||
transport=messaging.MESSAGE_TRANSPORT_TCPKISS,
|
||||
)
|
||||
self.msg_queues["tx"].put(msg)
|
||||
except Exception as ex:
|
||||
LOG.exception("Plugin failed!!!", ex)
|
||||
reply = "A Plugin failed! try again?"
|
||||
msg = messaging.TextMessage(
|
||||
self.config["aprs"]["login"],
|
||||
fromcall,
|
||||
reply,
|
||||
transport=messaging.MESSAGE_TRANSPORT_TCPKISS,
|
||||
)
|
||||
self.msg_queues["tx"].put(msg)
|
||||
|
||||
# let any threads do their thing, then ack
|
||||
# send an ack last
|
||||
ack = messaging.AckMessage(
|
||||
self.config["aprs"]["login"],
|
||||
fromcall,
|
||||
msg_id=msg_id,
|
||||
transport=messaging.MESSAGE_TRANSPORT_TCPKISS,
|
||||
)
|
||||
self.msg_queues["tx"].put(ack)
|
||||
LOG.debug("Packet processing complete")
|
||||
|
||||
def process_ack_packet(self, packet):
|
||||
ack_num = packet.get("msgNo")
|
||||
LOG.info("Got ack for message {}".format(ack_num))
|
||||
messaging.log_message(
|
||||
"ACK",
|
||||
packet["raw"],
|
||||
None,
|
||||
ack=ack_num,
|
||||
fromcall=packet["from"],
|
||||
)
|
||||
tracker = messaging.MsgTrack()
|
||||
tracker.remove(ack_num)
|
||||
stats.APRSDStats().ack_rx_inc()
|
||||
return
|
||||
|
||||
def process_mic_e_packet(self, packet):
|
||||
LOG.info("Mic-E Packet detected. Currenlty unsupported.")
|
||||
messaging.log_packet(packet)
|
||||
stats.APRSDStats().msgs_mice_inc()
|
||||
return
|
||||
|
@ -37,11 +37,24 @@ DEFAULT_DATE_FORMAT = "%m/%d/%Y %I:%M:%S %p"
|
||||
DEFAULT_CONFIG_DICT = {
|
||||
"ham": {"callsign": "NOCALL"},
|
||||
"aprs": {
|
||||
"login": "NOCALL",
|
||||
"enabled": True,
|
||||
"login": "CALLSIGN",
|
||||
"password": "00000",
|
||||
"host": "rotate.aprs2.net",
|
||||
"port": 14580,
|
||||
},
|
||||
"kiss": {
|
||||
"tcp": {
|
||||
"enabled": False,
|
||||
"host": "direwolf.ip.address",
|
||||
"port": "8001",
|
||||
},
|
||||
"serial": {
|
||||
"enabled": False,
|
||||
"device": "/dev/ttyS0",
|
||||
"baudrate": 9600,
|
||||
},
|
||||
},
|
||||
"aprsd": {
|
||||
"logfile": "/tmp/aprsd.log",
|
||||
"logformat": DEFAULT_LOG_FORMAT,
|
||||
@ -172,6 +185,9 @@ def add_config_comments(raw_yaml):
|
||||
# lets insert a comment
|
||||
raw_yaml = insert_str(
|
||||
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 # https://apps.magicbug.co.uk/passcode",
|
||||
end_idx,
|
||||
|
@ -1,3 +1,4 @@
|
||||
aioax25
|
||||
aprslib
|
||||
click
|
||||
click-completion
|
||||
|
@ -4,6 +4,8 @@
|
||||
#
|
||||
# pip-compile requirements.in
|
||||
#
|
||||
aioax25==0.0.9
|
||||
# via -r requirements.in
|
||||
aprslib==0.6.47
|
||||
# via -r requirements.in
|
||||
backoff==1.10.0
|
||||
@ -21,6 +23,8 @@ click==7.1.2
|
||||
# -r requirements.in
|
||||
# click-completion
|
||||
# flask
|
||||
contexter==0.1.4
|
||||
# via signalslot
|
||||
cryptography==3.4.7
|
||||
# via pyopenssl
|
||||
dnspython==2.1.0
|
||||
@ -72,6 +76,8 @@ pycparser==2.20
|
||||
# via cffi
|
||||
pyopenssl==20.0.1
|
||||
# via opencage
|
||||
pyserial==3.5
|
||||
# via aioax25
|
||||
python-dateutil==2.8.1
|
||||
# via pandas
|
||||
pytz==2021.1
|
||||
@ -88,6 +94,8 @@ requests==2.25.1
|
||||
# yfinance
|
||||
shellingham==1.4.0
|
||||
# via click-completion
|
||||
signalslot==0.1.2
|
||||
# via aioax25
|
||||
six==1.15.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
@ -96,12 +104,15 @@ six==1.15.0
|
||||
# opencage
|
||||
# pyopenssl
|
||||
# python-dateutil
|
||||
# signalslot
|
||||
thesmuggler==1.0.1
|
||||
# via -r requirements.in
|
||||
update-checker==0.18.0
|
||||
# via -r requirements.in
|
||||
urllib3==1.26.5
|
||||
# via requests
|
||||
weakrefmethod==1.0.3
|
||||
# via signalslot
|
||||
werkzeug==1.0.1
|
||||
# via flask
|
||||
yfinance==0.1.59
|
||||
|
Loading…
Reference in New Issue
Block a user