From cc8d834e5cea51c392db20b124b825363d6324f7 Mon Sep 17 00:00:00 2001 From: Walter Boring Date: Sun, 18 Jan 2026 21:21:09 -0500 Subject: [PATCH] Remove the login callsign in aprs_network It's been confusing for a while that when we configured aprsd, we had to enter the callsign in the [DEFAULT] section and the [aprs_network] section. This patch removes the login from the aprs_network section. aprsd will now use the main callsign in the [DEFAULT] section as the callsign to login to the aprsis network. --- aprsd/client/drivers/aprsis.py | 11 ++++---- aprsd/cmds/dev.py | 33 +++++++++++++--------- aprsd/cmds/listen.py | 5 ++-- aprsd/cmds/send_message.py | 16 ++++------- aprsd/conf/client.py | 9 +----- docs/source/configure.rst | 5 ++-- docs/source/server.rst | 1 - tests/client/drivers/test_aprsis_driver.py | 30 +++++++++++++------- tests/client/test_registry.py | 4 +-- tests/cmds/test_send_message.py | 3 +- tests/plugins/test_notify.py | 1 - tests/test_plugin.py | 2 -- 12 files changed, 60 insertions(+), 60 deletions(-) diff --git a/aprsd/client/drivers/aprsis.py b/aprsd/client/drivers/aprsis.py index 7c3c20d..a62f072 100644 --- a/aprsd/client/drivers/aprsis.py +++ b/aprsd/client/drivers/aprsis.py @@ -50,11 +50,12 @@ class APRSISDriver: @staticmethod def is_configured(): if APRSISDriver.is_enabled(): - # Ensure that the config vars are correctly set - if not CONF.aprs_network.login: - LOG.error('Config aprs_network.login not set.') + # Ensure that the config vars are correctly set. + # The callsign in [DEFAULT] is used as the APRS-IS login. + if not CONF.callsign or CONF.callsign == 'NOCALL': + LOG.error('Config callsign (in [DEFAULT]) not set or is NOCALL.') raise exception.MissingConfigOptionException( - 'aprs_network.login is not set.', + 'callsign (in [DEFAULT]) is not set or is NOCALL.', ) if not CONF.aprs_network.password: LOG.error('Config aprs_network.password not set.') @@ -89,7 +90,7 @@ class APRSISDriver: def setup_connection(self): if self.connected: return - user = CONF.aprs_network.login + user = CONF.callsign password = CONF.aprs_network.password host = CONF.aprs_network.host port = CONF.aprs_network.port diff --git a/aprsd/cmds/dev.py b/aprsd/cmds/dev.py index 09aae59..eeac74c 100644 --- a/aprsd/cmds/dev.py +++ b/aprsd/cmds/dev.py @@ -10,7 +10,7 @@ import click from oslo_config import cfg import aprsd -from aprsd import cli_helper, conf, packets, plugin, utils +from aprsd import cli_helper, packets, plugin, utils # local imports here from aprsd.main import cli @@ -79,12 +79,13 @@ def test_plugin( CONF.log_opt_values(LOG, logging.DEBUG) if not aprs_login: - if CONF.aprs_network.login == conf.client.DEFAULT_LOGIN: - click.echo('Must set --aprs_login or APRS_LOGIN') + if CONF.callsign == 'NOCALL': + click.echo( + 'Must set --aprs_login or APRS_LOGIN, or set callsign in config ([DEFAULT])' + ) ctx.exit(-1) return - else: - fromcall = CONF.aprs_network.login + fromcall = CONF.callsign else: fromcall = aprs_login @@ -129,6 +130,9 @@ def test_plugin( LOG.info(f"P'{plugin_path}' F'{fromcall}' C'{message}'") for _ in range(number): + # PluginManager.run() executes all plugins in parallel + # Results may be in a different order than plugin registration + # NULL_MESSAGE results are already filtered out replies = pm.run(packet) # Plugin might have threads, so lets stop them so we can exit. # obj.stop_threads() @@ -149,12 +153,15 @@ def test_plugin( elif isinstance(reply, packets.Packet): # We have a message based object. LOG.info(reply) - elif reply is not packets.NULL_MESSAGE: - LOG.info( - packets.MessagePacket( - from_call=CONF.callsign, - to_call=fromcall, - message_text=reply, - ), - ) + else: + # Note: NULL_MESSAGE results are already filtered out + # in PluginManager.run(), but keeping this check for safety + if reply is not packets.NULL_MESSAGE: + LOG.info( + packets.MessagePacket( + from_call=CONF.callsign, + to_call=fromcall, + message_text=reply, + ), + ) pm.stop() diff --git a/aprsd/cmds/listen.py b/aprsd/cmds/listen.py index 8b2e03b..7e4a9c5 100644 --- a/aprsd/cmds/listen.py +++ b/aprsd/cmds/listen.py @@ -81,6 +81,8 @@ class APRSDListenProcessThread(rx.APRSDFilterThread): if self.plugin_manager: # Don't do anything with the reply. # This is the listen only command. + # PluginManager.run() executes all plugins in parallel + # Results may be in a different order than plugin registration self.plugin_manager.run(packet) @@ -258,9 +260,6 @@ def listen( ctx.fail('Must set --aprs-password or APRS_PASSWORD') ctx.exit() - # CONF.aprs_network.login = aprs_login - # config["aprs"]["password"] = aprs_password - LOG.info(f'Python version: {sys.version}') LOG.info(f'APRSD Listen Started version: {aprsd.__version__}') utils.package.log_installed_extensions_and_plugins() diff --git a/aprsd/cmds/send_message.py b/aprsd/cmds/send_message.py index c1296c2..51ab90d 100644 --- a/aprsd/cmds/send_message.py +++ b/aprsd/cmds/send_message.py @@ -9,12 +9,7 @@ from oslo_config import cfg import aprsd import aprsd.packets # noqa : F401 -from aprsd import ( - cli_helper, - conf, # noqa : F401 - packets, - utils, -) +from aprsd import cli_helper, packets, utils from aprsd.client.client import APRSDClient from aprsd.main import cli from aprsd.packets import collector @@ -75,12 +70,13 @@ def send_message( quiet = ctx.obj['quiet'] if not aprs_login: - if CONF.aprs_network.login == conf.client.DEFAULT_LOGIN: - click.echo('Must set --aprs_login or APRS_LOGIN') + if CONF.callsign == 'NOCALL': + click.echo( + 'Must set --aprs_login or APRS_LOGIN, or set callsign in config ([DEFAULT])' + ) ctx.exit(-1) return - else: - aprs_login = CONF.aprs_network.login + aprs_login = CONF.callsign if not aprs_password: if not CONF.aprs_network.password: diff --git a/aprsd/conf/client.py b/aprsd/conf/client.py index d98de52..ac0e025 100644 --- a/aprsd/conf/client.py +++ b/aprsd/conf/client.py @@ -4,8 +4,6 @@ The options for log setup from oslo_config import cfg -DEFAULT_LOGIN = 'NOCALL' - aprs_group = cfg.OptGroup( name='aprs_network', title='APRS-IS Network settings', @@ -32,15 +30,10 @@ aprs_opts = [ help='Set enabled to False if there is no internet connectivity.' 'This is useful for a direwolf KISS aprs connection only.', ), - cfg.StrOpt( - 'login', - default=DEFAULT_LOGIN, - help='APRS Username', - ), cfg.StrOpt( 'password', secret=True, - help='APRS Password ' + help='APRS Password for the callsign in [DEFAULT]. ' 'Get the passcode for your callsign here: ' 'https://apps.magicbug.co.uk/passcode', ), diff --git a/docs/source/configure.rst b/docs/source/configure.rst index 4dec077..658f1d8 100644 --- a/docs/source/configure.rst +++ b/docs/source/configure.rst @@ -157,10 +157,9 @@ Sample config file # useful for a direwolf KISS aprs connection only. (boolean value) #enabled = true - # APRS Username (string value) - #login = NOCALL + # The callsign in [DEFAULT] is used as the APRS-IS login. - # APRS Password Get the passcode for your callsign here: + # APRS Password for the callsign in [DEFAULT]. Get the passcode here: # https://apps.magicbug.co.uk/passcode (string value) #password = diff --git a/docs/source/server.rst b/docs/source/server.rst index e9659d0..1f42486 100644 --- a/docs/source/server.rst +++ b/docs/source/server.rst @@ -126,7 +126,6 @@ on creating your own plugins. 2025-12-10 14:30:05.259 | MainThread | DEBUG | aprs_registry.service_website = None | oslo_config.cfg:log_opt_values:2824 2025-12-10 14:30:05.259 | MainThread | DEBUG | aprs_network.enabled = True | oslo_config.cfg:log_opt_values:2824 2025-12-10 14:30:05.260 | MainThread | DEBUG | aprs_network.host = 155.138.131.1 | oslo_config.cfg:log_opt_values:2824 - 2025-12-10 14:30:05.260 | MainThread | DEBUG | aprs_network.login = WB4BOR-1 | oslo_config.cfg:log_opt_values:2824 2025-12-10 14:30:05.260 | MainThread | DEBUG | aprs_network.password = **** | oslo_config.cfg:log_opt_values:2824 2025-12-10 14:30:05.260 | MainThread | DEBUG | aprs_network.port = 14580 | oslo_config.cfg:log_opt_values:2824 2025-12-10 14:30:05.260 | MainThread | DEBUG | kiss_serial.baudrate = 9600 | oslo_config.cfg:log_opt_values:2824 diff --git a/tests/client/drivers/test_aprsis_driver.py b/tests/client/drivers/test_aprsis_driver.py index 42a5910..d024d4d 100644 --- a/tests/client/drivers/test_aprsis_driver.py +++ b/tests/client/drivers/test_aprsis_driver.py @@ -18,9 +18,9 @@ class TestAPRSISDriver(unittest.TestCase): self.conf_patcher = mock.patch('aprsd.client.drivers.aprsis.CONF') self.mock_conf = self.conf_patcher.start() - # Configure APRS-IS settings + # Configure APRS-IS settings. callsign in [DEFAULT] is used as APRS-IS login. self.mock_conf.aprs_network.enabled = True - self.mock_conf.aprs_network.login = 'TEST' + self.mock_conf.callsign = 'TEST' self.mock_conf.aprs_network.password = '12345' self.mock_conf.aprs_network.host = 'rotate.aprs.net' self.mock_conf.aprs_network.port = 14580 @@ -97,16 +97,26 @@ class TestAPRSISDriver(unittest.TestCase): def test_is_configured_true(self): """Test is_configured returns True when properly configured.""" with mock.patch.object(APRSISDriver, 'is_enabled', return_value=True): - self.mock_conf.aprs_network.login = 'TEST' + self.mock_conf.callsign = 'TEST' self.mock_conf.aprs_network.password = '12345' self.mock_conf.aprs_network.host = 'rotate.aprs.net' self.assertTrue(APRSISDriver.is_configured()) - def test_is_configured_no_login(self): - """Test is_configured raises exception when login not set.""" + def test_is_configured_no_callsign(self): + """Test is_configured raises exception when callsign not set or NOCALL.""" with mock.patch.object(APRSISDriver, 'is_enabled', return_value=True): - self.mock_conf.aprs_network.login = None + self.mock_conf.callsign = None + + with self.assertRaises(exception.MissingConfigOptionException): + APRSISDriver.is_configured() + + def test_is_configured_callsign_nocall(self): + """Test is_configured raises exception when callsign is NOCALL.""" + with mock.patch.object(APRSISDriver, 'is_enabled', return_value=True): + self.mock_conf.callsign = 'NOCALL' + self.mock_conf.aprs_network.password = '12345' + self.mock_conf.aprs_network.host = 'rotate.aprs.net' with self.assertRaises(exception.MissingConfigOptionException): APRSISDriver.is_configured() @@ -114,7 +124,7 @@ class TestAPRSISDriver(unittest.TestCase): def test_is_configured_no_password(self): """Test is_configured raises exception when password not set.""" with mock.patch.object(APRSISDriver, 'is_enabled', return_value=True): - self.mock_conf.aprs_network.login = 'TEST' + self.mock_conf.callsign = 'TEST' self.mock_conf.aprs_network.password = None with self.assertRaises(exception.MissingConfigOptionException): @@ -123,7 +133,7 @@ class TestAPRSISDriver(unittest.TestCase): def test_is_configured_no_host(self): """Test is_configured raises exception when host not set.""" with mock.patch.object(APRSISDriver, 'is_enabled', return_value=True): - self.mock_conf.aprs_network.login = 'TEST' + self.mock_conf.callsign = 'TEST' self.mock_conf.aprs_network.password = '12345' self.mock_conf.aprs_network.host = None @@ -197,9 +207,9 @@ class TestAPRSISDriver(unittest.TestCase): self.driver.setup_connection() - # Check client created with correct parameters + # Check client created with correct parameters (callsign is APRS-IS login) self.mock_aprslib.assert_called_once_with( - self.mock_conf.aprs_network.login, + self.mock_conf.callsign, passwd=self.mock_conf.aprs_network.password, host=self.mock_conf.aprs_network.host, port=self.mock_conf.aprs_network.port, diff --git a/tests/client/test_registry.py b/tests/client/test_registry.py index 3b6450c..1efbd0e 100644 --- a/tests/client/test_registry.py +++ b/tests/client/test_registry.py @@ -26,11 +26,11 @@ class TestDriverRegistry(unittest.TestCase): mock_instance.is_enabled.return_value = False mock_instance.is_configured.return_value = False - # Mock CONF to prevent password check + # Mock CONF to prevent password/callsign check self.conf_patcher = mock.patch('aprsd.client.drivers.aprsis.CONF') mock_conf = self.conf_patcher.start() mock_conf.aprs_network.password = 'dummy' - mock_conf.aprs_network.login = 'dummy' + mock_conf.callsign = 'dummy' # Patch the register method to skip Protocol check for MockClientDriver self._original_register = self.registry.register diff --git a/tests/cmds/test_send_message.py b/tests/cmds/test_send_message.py index 8d1818b..749591f 100644 --- a/tests/cmds/test_send_message.py +++ b/tests/cmds/test_send_message.py @@ -5,7 +5,6 @@ from unittest import mock from click.testing import CliRunner from oslo_config import cfg -from aprsd import conf # noqa : F401 from aprsd.cmds import send_message # noqa from aprsd.main import cli @@ -21,7 +20,7 @@ class TestSendMessageCommand(unittest.TestCase): CONF.trace_enabled = False CONF.watch_list.packet_keep_count = 1 if login: - CONF.aprs_network.login = login + CONF.callsign = login if password: CONF.aprs_network.password = password diff --git a/tests/plugins/test_notify.py b/tests/plugins/test_notify.py index d15fc38..e5323fb 100644 --- a/tests/plugins/test_notify.py +++ b/tests/plugins/test_notify.py @@ -65,7 +65,6 @@ class TestWatchListPlugin(test_plugin.TestPlugin): watchlist_callsigns=DEFAULT_WATCHLIST_CALLSIGNS, ): CONF.callsign = self.fromcall - CONF.aprs_network.login = self.fromcall CONF.aprs_fi.apiKey = 'something' # Add mock password CONF.aprs_network.password = '12345' diff --git a/tests/test_plugin.py b/tests/test_plugin.py index a4be3e1..428bb75 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -38,7 +38,6 @@ class TestPluginManager(unittest.TestCase): def config_and_init(self): CONF.callsign = self.fromcall - CONF.aprs_network.login = fake.FAKE_TO_CALLSIGN CONF.aprs_fi.apiKey = 'something' CONF.enabled_plugins = 'aprsd.plugins.ping.PingPlugin' CONF.enable_save = False @@ -115,7 +114,6 @@ class TestPlugin(unittest.TestCase): def config_and_init(self): CONF.callsign = self.fromcall - CONF.aprs_network.login = fake.FAKE_TO_CALLSIGN CONF.aprs_fi.apiKey = 'something' CONF.enabled_plugins = 'aprsd.plugins.ping.PingPlugin' CONF.enable_save = False