From 26086edaf3b1529b6b640ebbc6b434794f05744f Mon Sep 17 00:00:00 2001 From: Joerg Schultze-Lutter Date: Mon, 24 Jun 2024 19:13:36 +0200 Subject: [PATCH 1/9] Config file enhancement --- src/aed.py | 1 + src/aed_definitions.py | 48 +++++++++++++++++++++++++++++++++++++++++- src/utils.py | 4 ++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/aed.py b/src/aed.py index c09cd9c..937c761 100644 --- a/src/aed.py +++ b/src/aed.py @@ -146,6 +146,7 @@ def mycallback(raw_aprs_packet): aed_latitude, aed_longitude, range_detection, + aed_aprs_extension, ) = get_program_config_from_file(config_filename=aed_configfile) if not success: sys.exit(0) diff --git a/src/aed_definitions.py b/src/aed_definitions.py index eccfce9..2bb9bd9 100644 --- a/src/aed_definitions.py +++ b/src/aed_definitions.py @@ -53,6 +53,7 @@ AED_MICE_SPECIAL = "SPECIAL" AED_MICE_PRIORITY = "PRIORITY" AED_MICE_EMERGENCY = "EMERGENCY" +AED_MICE_TESTALARM = "TESTALARM" # only used for APRS 1.2 extended messages # this is the command line parser vs. message_type_mapping AED_APRS_MAPPING = { @@ -66,8 +67,53 @@ AED_MICE_EMERGENCY: APRS_MICE_EMERGENCY, } +# APRS 1.2 Emergency extension for the msg body, see +# http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt +# only used when enabled in the program's config file +APRS_EXTENDED_OFF_DUTY = "!OFF-DUTY!" +APRS_EXTENDED_EN_ROUTE = "!ENROUTE!" +APRS_EXTENDED_IN_SERVICE = "!INSERVICE!" +APRS_EXTENDED_RETURNING = "!RETURNING!" +APRS_EXTENDED_COMMITTED = "!COMMITTED!" +APRS_EXTENDED_SPECIAL = "!SPECIAL!" +APRS_EXTENDED_PRIORITY = "!PRIORITY!" +APRS_EXTENDED_EMERGENCY = "!EMERGENCY!" +APRS_EXTENDED_TESTALARM = "!TESTALARM!" + +# APRS 1.2 Emergency extension for TOCALL, see +# http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt +# only used when enabled in the program's config file +APRS_EXTENDED_TOCALL_ALARM = "ALARM" +APRS_EXTENDED_TOCALL_ALERT = "ALERT" +APRS_EXTENDED_TOCALL_WARNING = "WARNING" +APRS_EXTENDED_TOCALL_WXALARM = "WXALARM" +APRS_EXTENDED_TOCALL_EM = "EM" + +# APRS 1.2 extension mapping - message body +AED_APRS_EXTENDED_MAPPING = { + AED_MICE_OFF_DUTY: APRS_EXTENDED_OFF_DUTY, + AED_MICE_EN_ROUTE: APRS_EXTENDED_EN_ROUTE, + AED_MICE_IN_SERVICE: APRS_EXTENDED_IN_SERVICE, + AED_MICE_RETURNING: APRS_EXTENDED_RETURNING, + AED_MICE_COMMITTED: APRS_EXTENDED_COMMITTED, + AED_MICE_SPECIAL: APRS_EXTENDED_SPECIAL, + AED_MICE_PRIORITY: APRS_EXTENDED_PRIORITY, + AED_MICE_EMERGENCY: APRS_EXTENDED_EMERGENCY, + AED_MICE_TESTALARM: APRS_EXTENDED_TESTALARM, +} + +# APRS 1.2 extension mapping - TOCALL +APRS_EXTENDED_TOCALL_LIST = [ + APRS_EXTENDED_TOCALL_ALARM, + APRS_EXTENDED_TOCALL_ALERT, + APRS_EXTENDED_TOCALL_WARNING, + APRS_EXTENDED_TOCALL_WXALARM, + APRS_EXTENDED_TOCALL_EM, +] + + # Max number of APRS TTL entries for the -#ExpiringDict dictionary +# ExpiringDict dictionary APRS_TTL_MAX_MESSAGES = 1000 if __name__ == "__main__": diff --git a/src/utils.py b/src/utils.py index 435958b..dbc5b6e 100644 --- a/src/utils.py +++ b/src/utils.py @@ -135,6 +135,9 @@ def get_program_config_from_file(config_filename: str = "aed.cfg"): except ValueError: aed_range_limit = None + aed_aprs_extension = config.get("aed_config", "aed_aprs_extension").upper() + aed_aprs_extension = True if aed_aprs_extension in ("YES", "TRUE") else False + success = True except Exception as ex: logger.info( @@ -150,6 +153,7 @@ def get_program_config_from_file(config_filename: str = "aed.cfg"): aed_lat, aed_lon, aed_range_limit, + aed_aprs_extension, ) From 667fde373a159d88f5aa48749cf4f932367890cd Mon Sep 17 00:00:00 2001 From: Joerg Schultze-Lutter Date: Mon, 24 Jun 2024 22:08:02 +0200 Subject: [PATCH 2/9] APRS 1.2 enhancements --- src/aed.cfg.TEMPLATE | 18 ++++- src/aed.py | 175 +++++++++++++++++++++++++++---------------- src/utils.py | 28 ++++++- 3 files changed, 153 insertions(+), 68 deletions(-) diff --git a/src/aed.cfg.TEMPLATE b/src/aed.cfg.TEMPLATE index 56f4a30..84cac2e 100644 --- a/src/aed.cfg.TEMPLATE +++ b/src/aed.cfg.TEMPLATE @@ -22,4 +22,20 @@ aed_active_categories = PRIORITY,EMERGENCY # # Value's unit of measure: km # -aed_range_limit = NONE \ No newline at end of file +aed_range_limit = NONE + +# Enable / Disable APRS 1.2 Emergency extensions +# If set to TRUE or YES, this will emable the APRS 1.2 +# extensions (http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt) +# Note that by enabling this setting, the list of to-be-processed +# messages will laregly grow as the program will not only have to +# digest Mic-E position reports but also regular messages as well +# +aed_aprs_extension = FALSE + +# TOCALL categories which will trigger a message +# only used if aed_aprs_extension is enabled +# valid values are ALARM, ALERT, WARNING, +# WXALARM and EM +# +aed_tocall_categories = ALARM \ No newline at end of file diff --git a/src/aed.py b/src/aed.py index 937c761..a502ad6 100644 --- a/src/aed.py +++ b/src/aed.py @@ -41,13 +41,41 @@ logger = logging.getLogger(__name__) -def mycallback(raw_aprs_packet): +def mycallback(raw_aprs_packet: dict): # Unfortunately, APRS-IS does not offer a filter # for Mic-E messages. Therefore, we have to filter # these messages ourselves if "format" in raw_aprs_packet: fmt = raw_aprs_packet["format"] - if fmt == "mic-e": + + # This will be our indicator which will tell us if we had a match + aed_mice_category = None + + # APRS 1.2 extension branch (t/pm filter) + # Emergency code via comment field + if aed_aprs_extension and fmt in ( + "compressed", + "uncompressed", + "object", + "mic-e", + ): + if "comment" in raw_aprs_packet: + comment = raw_aprs_packet["comment"] + for key, value in AED_APRS_EXTENDED_MAPPING.items(): + if key in aed_extended_categories: + if comment.startswith(value): + aed_mice_category = key + break + + # APRS 1.2 extension, emergency code via TOCALL + if aed_aprs_extension and not aed_mice_category: + if "to" in raw_aprs_packet: + tocall = raw_aprs_packet["to"] + if tocall in aed_tocall_categories: + aed_mice_category = tocall + + # regular branch for position reports only (t/p filter) + if fmt == "mic-e" and not aed_mice_category: if "mtype" in raw_aprs_packet: # check if the mtype is part of the list of # categories that we are to monitor @@ -62,69 +90,72 @@ def mycallback(raw_aprs_packet): aed_mice_category = key break - # get the remaining values from the message - mice_lat = mice_lon = mice_from = None - mice_speed = mice_course = None - if "latitude" in raw_aprs_packet: - mice_lat = round(raw_aprs_packet["latitude"], 6) - if "longitude" in raw_aprs_packet: - mice_lon = round(raw_aprs_packet["longitude"], 6) - if "from" in raw_aprs_packet: - mice_from = raw_aprs_packet["from"] - if "speed" in raw_aprs_packet: - mice_speed = round(raw_aprs_packet["speed"], 1) - if "course" in raw_aprs_packet: - mice_course = round(raw_aprs_packet["course"], 0) - - # now let's check if we need to send the message or - # if it is still stored in our cache - send_the_message = set_message_cache_entry( - message_cache=message_cache, - callsign=mice_from, - latitude=mice_lat, - longitude=mice_lon, - speed=mice_speed, - course=mice_course, - category=aed_mice_category, - ) - - # and generate the Apprise message(s), dependent on how many - # config files the user has specified - # - # this is the "full message" branch which includes images - # - if aed_messenger_configfile and send_the_message: - logger.debug(msg="Sending 'short' Apprise message") - generate_apprise_message( - apprise_config_file=aed_messenger_configfile, - callsign=mice_from, - latitude_aprs=mice_lat, - longitude_aprs=mice_lon, - latitude_aed=aed_latitude, - longitude_aed=aed_longitude, - course=mice_course, - speed=mice_speed, - category=aed_mice_category, - abbreviated_message_format=False, - ) - - # and this is the branch where we only send an abbreviated message to the user - if aed_sms_messenger_configfile and send_the_message: - logger.debug(msg="Sending 'short' Apprise message") - generate_apprise_message( - apprise_config_file=aed_sms_messenger_configfile, - callsign=mice_from, - latitude_aprs=mice_lat, - longitude_aprs=mice_lon, - latitude_aed=aed_latitude, - longitude_aed=aed_longitude, - course=mice_course, - speed=mice_speed, - category=aed_mice_category, - abbreviated_message_format=True, - ) - - logger.debug(raw_aprs_packet) + # now check if we need to send something to the user + # if the category is set, then the answer is 'yes' :-) + if aed_mice_category: + # get the remaining values from the message + mice_lat = mice_lon = mice_from = None + mice_speed = mice_course = None + if "latitude" in raw_aprs_packet: + mice_lat = round(raw_aprs_packet["latitude"], 6) + if "longitude" in raw_aprs_packet: + mice_lon = round(raw_aprs_packet["longitude"], 6) + if "from" in raw_aprs_packet: + mice_from = raw_aprs_packet["from"] + if "speed" in raw_aprs_packet: + mice_speed = round(raw_aprs_packet["speed"], 1) + if "course" in raw_aprs_packet: + mice_course = round(raw_aprs_packet["course"], 0) + + # now let's check if we need to send the message or + # if it is still stored in our cache + send_the_message = set_message_cache_entry( + message_cache=message_cache, + callsign=mice_from, + latitude=mice_lat, + longitude=mice_lon, + speed=mice_speed, + course=mice_course, + category=aed_mice_category, + ) + + # and generate the Apprise message(s), dependent on how many + # config files the user has specified + # + # this is the "full message" branch which includes images + # + if aed_messenger_configfile and send_the_message: + logger.debug(msg="Sending 'short' Apprise message") + generate_apprise_message( + apprise_config_file=aed_messenger_configfile, + callsign=mice_from, + latitude_aprs=mice_lat, + longitude_aprs=mice_lon, + latitude_aed=aed_latitude, + longitude_aed=aed_longitude, + course=mice_course, + speed=mice_speed, + category=aed_mice_category, + abbreviated_message_format=False, + ) + + # and this is the branch where we only send an abbreviated message to the user + if aed_sms_messenger_configfile and send_the_message: + logger.debug(msg="Sending 'short' Apprise message") + generate_apprise_message( + apprise_config_file=aed_sms_messenger_configfile, + callsign=mice_from, + latitude_aprs=mice_lat, + longitude_aprs=mice_lon, + latitude_aed=aed_latitude, + longitude_aed=aed_longitude, + course=mice_course, + speed=mice_speed, + category=aed_mice_category, + abbreviated_message_format=True, + ) + + logger.debug(raw_aprs_packet) ### main loop @@ -147,15 +178,23 @@ def mycallback(raw_aprs_packet): aed_longitude, range_detection, aed_aprs_extension, + aed_tocall_categories, ) = get_program_config_from_file(config_filename=aed_configfile) if not success: sys.exit(0) # Determine the categories that we need to investigate aed_categories = [] + aed_extended_categories = [] for aed_mice_message_type in aed_mice_message_types: if aed_mice_message_type in AED_APRS_MAPPING: aed_categories.append(AED_APRS_MAPPING[aed_mice_message_type]) + # Not do the same for the APRS 1.2 extension if enabled + if aed_aprs_extension: + if aed_mice_message_type in AED_APRS_EXTENDED_MAPPING: + aed_extended_categories.append( + AED_APRS_EXTENDED_MAPPING[aed_mice_message_type] + ) # Check if we are to generate test messages if aed_generate_test_message: @@ -212,6 +251,12 @@ def mycallback(raw_aprs_packet): max_len=APRS_TTL_MAX_MESSAGES, max_age_seconds=aed_time_to_live * 60 ) + # set the APRS-IS filter, dependent on whether the user has enabled or disabled + # the APRS 1.2 extensions (http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt) + # APRS 1.2 = position reports,messages, and objects + # default settings = position reports only + aprsis_filter = "t/pmo" if aed_aprs_extension else "t/p" + # amend the APRS-IS filter in case we have received lat/lon/range if aed_latitude and aed_longitude and range_detection: aprsis_filter += f" r/{aed_latitude}/{aed_longitude}/{range_detection}" diff --git a/src/utils.py b/src/utils.py index dbc5b6e..d33a0b9 100644 --- a/src/utils.py +++ b/src/utils.py @@ -35,6 +35,8 @@ AED_MICE_EN_ROUTE, AED_MICE_IN_SERVICE, AED_MICE_OFF_DUTY, + AED_MICE_TESTALARM, + APRS_EXTENDED_TOCALL_LIST, ) from expiringdict import ExpiringDict @@ -121,8 +123,9 @@ def get_program_config_from_file(config_filename: str = "aed.cfg"): AED_MICE_SPECIAL, AED_MICE_PRIORITY, AED_MICE_EMERGENCY, + AED_MICE_TESTALARM, # APRS 1.2 extension only ]: - logger.info(msg=f"Config file error; received category '{ac}'") + logger.info(msg=f"Config file error; received unknown category '{ac}'") raise ValueError("Error in config file") # Get the range limit @@ -138,13 +141,33 @@ def get_program_config_from_file(config_filename: str = "aed.cfg"): aed_aprs_extension = config.get("aed_config", "aed_aprs_extension").upper() aed_aprs_extension = True if aed_aprs_extension in ("YES", "TRUE") else False + # predefine the list of TOCALL categories; this list can be empty + aed_tocall_categories = [] + + if aed_aprs_extension: + # get the list of TOCALL categories that we are to examine + aed_acs = config.get("aed_config", "aed_tocall_categories") + aed_tocall_categories_raw = [ + s.strip().upper() for s in aed_acs.split(",") if aed_acs != "" + ] + # remove any potential dupes + aed_tocall_categories = list(set(aed_tocall_categories_raw)) + # Note: this list can also be empty + for ac in aed_tocall_categories: + if ac not in APRS_EXTENDED_TOCALL_LIST: + logger.info( + msg=f"Config file error; received unknown TOCALL category '{ac}'" + ) + raise ValueError("Error in config file") + success = True except Exception as ex: logger.info( msg="Error in configuration file; Check if your config format is correct." ) aed_lat = aed_lon = aed_range_limit = None - aed_active_categories = None + aed_active_categories = aed_tocall_categories = None + success = False return ( @@ -154,6 +177,7 @@ def get_program_config_from_file(config_filename: str = "aed.cfg"): aed_lon, aed_range_limit, aed_aprs_extension, + aed_tocall_categories, ) From 39a3f5ff96a8b0e59f2701b7e6b4715e5511c21e Mon Sep 17 00:00:00 2001 From: Joerg Schultze-Lutter <76180229+joergschultzelutter@users.noreply.github.com> Date: Tue, 25 Jun 2024 09:52:50 +0200 Subject: [PATCH 3/9] Update README.md --- README.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 52722ea..74da432 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![CodeQL](https://github.com/joergschultzelutter/aprs-emergency-detector/actions/workflows/codeql.yml/badge.svg)](https://github.com/joergschultzelutter/aprs-emergency-detector/actions/workflows/codeql.yml) -This program establishes a read-only connection to [APRS-IS](https://www.aprs-is.net/) and is going to listen to position reports in [Mic-E](http://www.aprs.org/aprs12/mic-e-examples.txt) format. Whenever a Mic-E position report's [message type](https://jgromes.github.io/RadioLib/group__mic__e__message__types.html) matches the program's user configuration, the program is going to generate [Apprise](https://github.com/caronc/apprise/)-based notifications to the messenger accounts provided by the user. Full-length messages as well as APRS-length-compliant short messages can be generated. +This program establishes a read-only connection to [APRS-IS](https://www.aprs-is.net/) and is going to listen to position reports in [Mic-E](http://www.aprs.org/aprs12/mic-e-examples.txt) format. Whenever a Mic-E position report's [message type](https://jgromes.github.io/RadioLib/group__mic__e__message__types.html) matches the program's user configuration, the program is going to generate [Apprise](https://github.com/caronc/apprise/)-based notifications to the messenger accounts provided by the user. Full-length messages as well as APRS-length-compliant short messages can be generated. Optional usage of the [APRS 1.2 Emergency Code extensions](http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt) for messages and APRS objects is supported, too. ## Examples This is what you will get if you generate a test message via ```--generate-test-message```. All coordinates are fixed, meaning that the result will always look like this. The test message assumes that we have received an ```EMERGENCY``` Mic-E position beacon. All examples have been taken from the Telegram messenger. @@ -49,9 +49,29 @@ This message format contains the absolute minimum of date but will still permit # Value's unit of measure: km # aed_range_limit = NONE + + # Enable / Disable APRS 1.2 Emergency extensions + # If set to TRUE or YES, this will emable the APRS 1.2 + # extensions (http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt) + # Note that by enabling this setting, the list of to-be-processed + # messages will laregly grow as the program will not only have to + # digest Mic-E position reports but also regular messages as well + # + aed_aprs_extension = FALSE + + # TOCALL categories which will trigger a message + # only used if aed_aprs_extension is enabled + # valid values are ALARM, ALERT, WARNING, + # WXALARM and EM + # Specify 1..n categories from that list. Separate by comma. + # + aed_tocall_categories = ALARM + - Set the ```aed_my_position```value to your current fixed location. If we get a Mic-E message match, ```aed``` will calculate the distance from your coordinates to the message's coordinates. - change the ```aed_active_categories```value and specify the Mic-E categories that you intend to monitor. Separate categories by comma. -- OPTIONAL: set the ```aed_range_limit``` to an integer value (unit of mesure: km; see https://www.aprs-is.net/javAPRSFilter.aspx) if you intend to limit the lookup process to a range relative to your lat/lon coordinates. Keep the standard value NONE if you do not want to apply range limits. +- OPTIONAL: set the ```aed_range_limit``` variable to an integer value (unit of mesure: km; see https://www.aprs-is.net/javAPRSFilter.aspx) if you intend to limit the lookup process to a range relative to your lat/lon coordinates. Keep the standard value NONE if you do not want to apply range limits. +- OPTIONAL: set the ```aed_aprs_extension``` variable to TRUE or YES if you want to enable the [APRS 1.2 Emergency Code extensions](http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt). Note that this setting will tell the program to receive standard APRS messages and APRS object reports, thus increasing the number of messages to be processed. +- OPTIONAL: set the ```aed_tocall_categories``` to 0..n entries from the list of given values (can also be empty). Note that this setting is only used if ```aed_aprs_extension``` is enabled. - Save the file - Copy the [apprise_demo_template.yml](https://github.com/joergschultzelutter/aprs-emergency-detector/blob/master/src/apprise_demo_template.yml) file and rename it to a file name of your choice. Each message target (full message and abbreviated message) requires its own config file; so if you intend to send full messages and abbreviated messages, you need to create two config files. I suggest using proper messenger config file names, e.g. ```full_msg.yml``` and ```sms_msg.yml``` but this choice is up to you. - Edit each config file and add the desired [Apprise messenger configuration](https://github.com/caronc/apprise/). Then save the file. From d1de68c82a861f73428ba23d7cfe5c876897c4d6 Mon Sep 17 00:00:00 2001 From: Joerg Schultze-Lutter <76180229+joergschultzelutter@users.noreply.github.com> Date: Tue, 25 Jun 2024 09:57:35 +0200 Subject: [PATCH 4/9] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 74da432..cab7637 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ This message format contains literally everything that is needed to identify the ![Demo](img/test_message_full.jpg) -### 'Short' message format -This message format contains the absolute minimum of date but will still permit you to locate the user's position. All messages are APRS-compliant in length, meaning that you can use Apprise in order to forward this message to another ham radio user and/or SMS phone user. +### 'Short'/'SMS' message format +This message format contains the absolute minimum of date but will still permit you to locate the user's position. All messages are APRS-compliant in length, meaning that you can use [Apprise](https://github.com/caronc/apprise/) for forwarding this message to another ham radio user and/or SMS phone user. ![Demo](img/test_message_short.jpg) @@ -73,12 +73,12 @@ This message format contains the absolute minimum of date but will still permit - OPTIONAL: set the ```aed_aprs_extension``` variable to TRUE or YES if you want to enable the [APRS 1.2 Emergency Code extensions](http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt). Note that this setting will tell the program to receive standard APRS messages and APRS object reports, thus increasing the number of messages to be processed. - OPTIONAL: set the ```aed_tocall_categories``` to 0..n entries from the list of given values (can also be empty). Note that this setting is only used if ```aed_aprs_extension``` is enabled. - Save the file -- Copy the [apprise_demo_template.yml](https://github.com/joergschultzelutter/aprs-emergency-detector/blob/master/src/apprise_demo_template.yml) file and rename it to a file name of your choice. Each message target (full message and abbreviated message) requires its own config file; so if you intend to send full messages and abbreviated messages, you need to create two config files. I suggest using proper messenger config file names, e.g. ```full_msg.yml``` and ```sms_msg.yml``` but this choice is up to you. +- Copy the [apprise_demo_template.yml](https://github.com/joergschultzelutter/aprs-emergency-detector/blob/master/src/apprise_demo_template.yml) file and rename it to a file name of your choice. Each message target (full message and abbreviated message) requires its own config file; so if you intend to send full messages and abbreviated messages, you need to create two config files. I suggest using proper messenger config file names, e.g. ```full_msg.yml``` and ```sms_msg.yml``` but this choice is up to you. In order to run the program, you need to specify at least one Apprise config file. - Edit each config file and add the desired [Apprise messenger configuration](https://github.com/caronc/apprise/). Then save the file. ## Command line parameters - python aed.py --configfile + python aed.py --configfile Default name: aed.cfg --messenger-config-file [Apprise full-message config file] --sms-messenger-config-file [Apprise abbreviated-message config file] --generate-test-message @@ -92,4 +92,4 @@ This message format contains the absolute minimum of date but will still permit ## Known issues and constraints - As mentioned earlier, 'short' messages are generated in an APRS compliant format and therefore limited in length to 67 characters. Due to that constraint, 'short' messages will limit its position information to Maidenhead grid info and is going to omit any distance-related information that is in imperial units (sorry, but I am a metric system guy 😄) -- Unfortunately, APRS-IS does not offer filter settings which allow the program to filter on e.g. Mic-E messages and/or message types. The program does use a filter based on position reports - but everything else is filtered on the fly, meaning that unless you limit the range (see ```aed_range_limit```), the program has to digest a lot of messages, thus resulting in potential CPU load spikes. +- Unfortunately, APRS-IS does not offer filter settings which allow the program to filter on e.g. Mic-E messages and/or message types. The program does use a filter based on position reports - but everything else is filtered on the fly, meaning that unless you limit the range (see ```aed_range_limit```), the program has to digest a lot of messages, thus resulting in potential CPU load spikes - especially when enabling the APRS 1.2 extensions. From 761f7aaa8d3198912a8c4e78840fe6bbc9f9f415 Mon Sep 17 00:00:00 2001 From: Joerg Schultze-Lutter <76180229+joergschultzelutter@users.noreply.github.com> Date: Tue, 25 Jun 2024 09:58:23 +0200 Subject: [PATCH 5/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cab7637..b0b954e 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ This message format contains the absolute minimum of date but will still permit aed_range_limit = NONE # Enable / Disable APRS 1.2 Emergency extensions - # If set to TRUE or YES, this will emable the APRS 1.2 + # If set to TRUE or YES, this will enable the APRS 1.2 # extensions (http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt) # Note that by enabling this setting, the list of to-be-processed # messages will laregly grow as the program will not only have to From fc1c777b7c40af28cb577669d535e677941c39d8 Mon Sep 17 00:00:00 2001 From: Joerg Schultze-Lutter <76180229+joergschultzelutter@users.noreply.github.com> Date: Tue, 25 Jun 2024 09:58:46 +0200 Subject: [PATCH 6/9] Update aed.cfg.TEMPLATE --- src/aed.cfg.TEMPLATE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aed.cfg.TEMPLATE b/src/aed.cfg.TEMPLATE index 84cac2e..e51147d 100644 --- a/src/aed.cfg.TEMPLATE +++ b/src/aed.cfg.TEMPLATE @@ -25,7 +25,7 @@ aed_active_categories = PRIORITY,EMERGENCY aed_range_limit = NONE # Enable / Disable APRS 1.2 Emergency extensions -# If set to TRUE or YES, this will emable the APRS 1.2 +# If set to TRUE or YES, this will enable the APRS 1.2 # extensions (http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt) # Note that by enabling this setting, the list of to-be-processed # messages will laregly grow as the program will not only have to @@ -38,4 +38,4 @@ aed_aprs_extension = FALSE # valid values are ALARM, ALERT, WARNING, # WXALARM and EM # -aed_tocall_categories = ALARM \ No newline at end of file +aed_tocall_categories = ALARM From eecf97d72f7b63e1ce4e0f4d42e17c08be862eb3 Mon Sep 17 00:00:00 2001 From: Joerg Schultze-Lutter <76180229+joergschultzelutter@users.noreply.github.com> Date: Tue, 25 Jun 2024 10:29:24 +0200 Subject: [PATCH 7/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b0b954e..0f21da4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![CodeQL](https://github.com/joergschultzelutter/aprs-emergency-detector/actions/workflows/codeql.yml/badge.svg)](https://github.com/joergschultzelutter/aprs-emergency-detector/actions/workflows/codeql.yml) -This program establishes a read-only connection to [APRS-IS](https://www.aprs-is.net/) and is going to listen to position reports in [Mic-E](http://www.aprs.org/aprs12/mic-e-examples.txt) format. Whenever a Mic-E position report's [message type](https://jgromes.github.io/RadioLib/group__mic__e__message__types.html) matches the program's user configuration, the program is going to generate [Apprise](https://github.com/caronc/apprise/)-based notifications to the messenger accounts provided by the user. Full-length messages as well as APRS-length-compliant short messages can be generated. Optional usage of the [APRS 1.2 Emergency Code extensions](http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt) for messages and APRS objects is supported, too. +This program establishes a read-only connection to [APRS-IS](https://www.aprs-is.net/) and is going to listen to position reports in [Mic-E](http://www.aprs.org/aprs12/mic-e-examples.txt) format. Whenever a Mic-E position report's [message type](https://jgromes.github.io/RadioLib/group__mic__e__message__types.html) matches the program's user configuration, the program is going to generate [Apprise](https://github.com/caronc/apprise/)-based notifications to the messenger accounts provided by the user. Full-length messages as well as APRS-length-compliant short messages can be generated. Optional usage of the [APRS 1.2 Emergency Code extensions](http://wa8lmf.net/bruninga/aprs/EmergencyCode.txt) for regular APRS messages and APRS objects is supported, too. ## Examples This is what you will get if you generate a test message via ```--generate-test-message```. All coordinates are fixed, meaning that the result will always look like this. The test message assumes that we have received an ```EMERGENCY``` Mic-E position beacon. All examples have been taken from the Telegram messenger. From d1147b739dfd6af68f372d834bc9f0983e0b3729 Mon Sep 17 00:00:00 2001 From: Joerg Schultze-Lutter <76180229+joergschultzelutter@users.noreply.github.com> Date: Tue, 25 Jun 2024 10:37:09 +0200 Subject: [PATCH 8/9] Update aed.cfg.TEMPLATE --- src/aed.cfg.TEMPLATE | 1 + 1 file changed, 1 insertion(+) diff --git a/src/aed.cfg.TEMPLATE b/src/aed.cfg.TEMPLATE index e51147d..7aabd5e 100644 --- a/src/aed.cfg.TEMPLATE +++ b/src/aed.cfg.TEMPLATE @@ -37,5 +37,6 @@ aed_aprs_extension = FALSE # only used if aed_aprs_extension is enabled # valid values are ALARM, ALERT, WARNING, # WXALARM and EM +# Specify 1..n categories from that list. Separate by comma. # aed_tocall_categories = ALARM From 035e04199b982c27dc238e9b8af562ca6972e437 Mon Sep 17 00:00:00 2001 From: Joerg Schultze-Lutter Date: Tue, 25 Jun 2024 22:01:37 +0200 Subject: [PATCH 9/9] Tested the APRS 1.2 extension integration --- README.md | 23 ++++++++++++++--------- src/aed.cfg.TEMPLATE | 6 +++++- src/aed.py | 2 +- src/utils.py | 6 +++--- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 0f21da4..a519cf9 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ This message format contains the absolute minimum of date but will still permit # Details: https://www.aprs-is.net/javAPRSFilter.aspx # # Value's unit of measure: km + # Example: aed_range_limit = 100 # aed_range_limit = NONE @@ -78,18 +79,22 @@ This message format contains the absolute minimum of date but will still permit ## Command line parameters - python aed.py --configfile Default name: aed.cfg - --messenger-config-file [Apprise full-message config file] - --sms-messenger-config-file [Apprise abbreviated-message config file] - --generate-test-message - --ttl [time-to-live for expiring dictionary] - -- ```configfile``` is the program's configuration file (containing your lat/lon coordinates et al) -- ```messenger-config-file``` and ```sms-messenger-config-file``` represent the [Apprise messenger configuration]([Apprise](https://github.com/caronc/apprise/) files. Although both settings are listed as optional, you need to specify at least one of these two messenger configuration files - or the program will exit. + usage: aed.py [-h] [--configfile CONFIGFILE] [--messenger-config-file MESSENGER_CONFIG_FILE] [--sms-messenger-config-file SMS_MESSENGER_CONFIG_FILE] [--generate-test-message] [--ttl TIME_TO_LIVE] + + optional arguments: + -h, --help show this help message and exit + --configfile CONFIGFILE Program config file name. Default name: aed.cfg + --messenger-config-file MESSENGER_CONFIG_FILE Config file name for regular messenger full-content messages + --sms-messenger-config-file SMS_MESSENGER_CONFIG_FILE Config file name for sms-like messengers, using an abbreviated notification message + --generate-test-message Generates a generic test message (whereas this config is enabled) and exits the program. + --ttl TIME_TO_LIVE Message 'time to live' setting in minutes. Default value is 240m mins = 4h + +- ```configfile``` is the program's configuration file (containing your lat/lon coordinates et al). Default name is ```aed.cfg``` +- ```messenger-config-file``` and ```sms-messenger-config-file``` represent the [Apprise messenger configuration]([Apprise](https://github.com/caronc/apprise/) files. Although both settings are listed as optional, you need to specify __at least one__ of these two messenger configuration files - or the program will exit. - ```generate-test-message``` is a boolean switch. If specified, the program will not connect to APRS-IS but is simply going to generate a single test message which is then broadcasted by the two Apprise messenger files (whereas specified). Once broadcasted, the program will self-terminate. - ```ttl``` specifies the time-to-live for the message buffer (unit of measure: minutes). If we receive a matching position report from a call sign and lat/lon/course/speed/category have NOT changed, then we will ignore that position report and NOT broadcast an Apprise message to the user ## Known issues and constraints - As mentioned earlier, 'short' messages are generated in an APRS compliant format and therefore limited in length to 67 characters. Due to that constraint, 'short' messages will limit its position information to Maidenhead grid info and is going to omit any distance-related information that is in imperial units (sorry, but I am a metric system guy 😄) -- Unfortunately, APRS-IS does not offer filter settings which allow the program to filter on e.g. Mic-E messages and/or message types. The program does use a filter based on position reports - but everything else is filtered on the fly, meaning that unless you limit the range (see ```aed_range_limit```), the program has to digest a lot of messages, thus resulting in potential CPU load spikes - especially when enabling the APRS 1.2 extensions. +- Unfortunately, APRS-IS does not offer filter settings which allow the program to filter on e.g. Mic-E messages and/or message types containing just these emergency messages. The program does use a filter based on position reports - but everything else is filtered on the fly, meaning that unless you limit the range (see ```aed_range_limit```), the program has to digest a lot of messages, thus resulting in potential CPU load spikes - especially if you enable the APRS 1.2 extensions. diff --git a/src/aed.cfg.TEMPLATE b/src/aed.cfg.TEMPLATE index 7aabd5e..7c9d600 100644 --- a/src/aed.cfg.TEMPLATE +++ b/src/aed.cfg.TEMPLATE @@ -37,6 +37,10 @@ aed_aprs_extension = FALSE # only used if aed_aprs_extension is enabled # valid values are ALARM, ALERT, WARNING, # WXALARM and EM -# Specify 1..n categories from that list. Separate by comma. +# +# Specify 0..n categories from that list. Separate by comma. + +# Optional entry; can stay empty, e.g. +# aed_tocall_categories = # aed_tocall_categories = ALARM diff --git a/src/aed.py b/src/aed.py index a502ad6..f22bc77 100644 --- a/src/aed.py +++ b/src/aed.py @@ -62,7 +62,7 @@ def mycallback(raw_aprs_packet: dict): if "comment" in raw_aprs_packet: comment = raw_aprs_packet["comment"] for key, value in AED_APRS_EXTENDED_MAPPING.items(): - if key in aed_extended_categories: + if value in aed_extended_categories: if comment.startswith(value): aed_mice_category = key break diff --git a/src/utils.py b/src/utils.py index d33a0b9..ec62254 100644 --- a/src/utils.py +++ b/src/utils.py @@ -297,7 +297,7 @@ def get_command_line_params(): "--configfile", default="aed.cfg", type=argparse.FileType("r"), - help="Program config file name", + help="Program config file name. Default name: aed.cfg", ) parser.add_argument( @@ -311,14 +311,14 @@ def get_command_line_params(): "--sms-messenger-config-file", default=None, type=str, - help="Config file name for sms-like messengers", + help="Config file name for sms-like messengers, using an abbreviated notification message", ) parser.add_argument( "--generate-test-message", dest="generate_test_message", action="store_true", - help="Generates a generic test message (whereas this config is enabled) and exits the program", + help="Generates a generic test message (whereas this config is enabled) and exits the program.", ) parser.add_argument(