From 097cae6e94d3889105cc9c8e33b90a696bb42a31 Mon Sep 17 00:00:00 2001 From: SudoRand Date: Sat, 12 Jul 2025 18:33:41 -0600 Subject: [PATCH 01/32] Allow chunker to consolidate lines when possible This allows the chunker to consolidate lines into significantly fewer messages in many cases without exceeding the max chunk size. Without this change, the chunker will either emit all lines in one message (if it fits in a single chunk) or else each line will be in a separate message. This often creates a long series of short messages, which doesn't transmit as quickly or display as compact. Instead, this consolidates as many lines as possible into each message, while being sure to stay within the chunk size limit. This should reduce the load on the mesh, and it's also more readable. --- modules/system.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/system.py b/modules/system.py index 32f2688..b1b3b33 100644 --- a/modules/system.py +++ b/modules/system.py @@ -540,6 +540,15 @@ def messageChunker(message): if current_chunk: message_list.append(current_chunk) + # Consolidate any adjacent messages that can fit in a single chunk. + idx = 0 + while idx < len(message_list) - 1: + if len(message_list[idx]) + len(message_list[idx+1]) < MESSAGE_CHUNK_SIZE: + message_list[idx] += '\n' + message_list[idx+1] + del message_list[idx+1] + else: + idx += 1 + # Ensure no chunk exceeds MESSAGE_CHUNK_SIZE final_message_list = [] for chunk in message_list: From 9b4200c198db57810523129408f8620e338bb8dc Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Mon, 14 Jul 2025 21:54:30 -0700 Subject: [PATCH 02/32] Update config.template adjustments for 2.7.2 firmware might change again --- config.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.template b/config.template index 6d68dc4..fd838c6 100644 --- a/config.template +++ b/config.template @@ -297,7 +297,7 @@ hamtest = True # delay in seconds for response to avoid message collision responseDelay = 1.2 # delay in seconds for splits in messages to avoid message collision -splitDelay = 0.0 +splitDelay = 2.5 # message chunk size for sending at high success rate, chunkr allows exceeding by 3 characters MESSAGE_CHUNK_SIZE = 160 # Request Acknowledgement of message OTA From caf8a2708b80fce40e6d833b3595a97fc4e3bd79 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Mon, 14 Jul 2025 22:04:22 -0700 Subject: [PATCH 03/32] Update log.py fix time display --- modules/log.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/modules/log.py b/modules/log.py index e8e44d0..f0ac37a 100644 --- a/modules/log.py +++ b/modules/log.py @@ -83,18 +83,14 @@ if log_messages_to_file: # Pretty Timestamp def getPrettyTime(seconds): - # convert unix time to minutes, hours, or days, or years for simple display - designator = "s" - if seconds > 0: - seconds = round(seconds / 60) - designator = "m" - if seconds > 60: - seconds = round(seconds / 60) - designator = "h" - if seconds > 24: - seconds = round(seconds / 24) - designator = "d" - if seconds > 365: - seconds = round(seconds / 365) - designator = "y" - return str(seconds) + designator \ No newline at end of file + # convert unix time to minutes, hours, days, or years for simple display + if seconds < 60: + return f"{int(seconds)}s" + elif seconds < 3600: + return f"{int(round(seconds / 60))}m" + elif seconds < 86400: + return f"{int(round(seconds / 3600))}h" + elif seconds < 31536000: + return f"{int(round(seconds / 86400))}d" + else: + return f"{int(round(seconds / 31536000))}y" \ No newline at end of file From 8709e5aed5a89ebc2a1dba3cd75fb9e2b64e5c3b Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Mon, 14 Jul 2025 22:13:49 -0700 Subject: [PATCH 04/32] enhance sysinfo ChUtil/Node value --- modules/system.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/system.py b/modules/system.py index 32f2688..83d4099 100644 --- a/modules/system.py +++ b/modules/system.py @@ -923,6 +923,11 @@ def displayNodeTelemetry(nodeID=0, rxNode=0, userRequested=False): # Number of nodes dataResponse += " totalNodes:" + str(numTotalNodes) + " Online:" + str(totalOnlineNodes) + # calculate the channel utilization per node + if totalOnlineNodes > 0: + chutilPerNode = round(chutil / totalOnlineNodes, 2) + dataResponse += " ChUtil/Node:" + str(chutilPerNode) + # Uptime uptimeSeconds = getPrettyTime(uptimeSeconds) dataResponse += " Uptime:" + str(uptimeSeconds) From b146fd6f64daac067f5902353391f4ec8ea6a79f Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Mon, 14 Jul 2025 22:55:41 -0700 Subject: [PATCH 05/32] Revert "enhance sysinfo" This reverts commit 8709e5aed5a89ebc2a1dba3cd75fb9e2b64e5c3b. --- modules/system.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/system.py b/modules/system.py index 83d4099..32f2688 100644 --- a/modules/system.py +++ b/modules/system.py @@ -923,11 +923,6 @@ def displayNodeTelemetry(nodeID=0, rxNode=0, userRequested=False): # Number of nodes dataResponse += " totalNodes:" + str(numTotalNodes) + " Online:" + str(totalOnlineNodes) - # calculate the channel utilization per node - if totalOnlineNodes > 0: - chutilPerNode = round(chutil / totalOnlineNodes, 2) - dataResponse += " ChUtil/Node:" + str(chutilPerNode) - # Uptime uptimeSeconds = getPrettyTime(uptimeSeconds) dataResponse += " Uptime:" + str(uptimeSeconds) From 3f882dcfcd73754d1eacc0d9e15c0bc667498c93 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 09:41:34 -0700 Subject: [PATCH 06/32] fix message.log fixing issue for log in https://github.com/SpudGunMan/meshing-around/pull/161 Co-Authored-By: SudoRand <25190078+sudorand@users.noreply.github.com> --- mesh_bot.py | 5 +++-- pong_bot.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mesh_bot.py b/mesh_bot.py index b14ccd8..38db5a0 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -1224,7 +1224,7 @@ def onReceive(packet, interface): isDM = True # check if the message contains a trap word, DMs are always responded to if (messageTrap(message_string) and not llm_enabled) or messageTrap(message_string.split()[0]): - # log the message to the message log + # log the message to stdout logger.info(f"Device:{rxNode} Channel: {channel_number} " + CustomFormatter.green + f"Received DM: " + CustomFormatter.white + f"{message_string} " + CustomFormatter.purple +\ "From: " + CustomFormatter.white + f"{get_name_from_number(message_from_id, 'long', rxNode)}") # respond with DM @@ -1271,7 +1271,8 @@ def onReceive(packet, interface): time.sleep(responseDelay) # log the message to the message log - msgLogger.info(f"Device:{rxNode} Channel:{channel_number} | {get_name_from_number(message_from_id, 'long', rxNode)} | " + message_string.replace('\n', '-nl-')) + if log_messages_to_file: + msgLogger.info(f"Device:{rxNode} Channel:{channel_number} | {get_name_from_number(message_from_id, 'long', rxNode)} | DM | " + message_string.replace('\n', '-nl-')) else: # message is on a channel if messageTrap(message_string): diff --git a/pong_bot.py b/pong_bot.py index 7eef81a..76f5b24 100755 --- a/pong_bot.py +++ b/pong_bot.py @@ -310,7 +310,7 @@ def onReceive(packet, interface): isDM = True # check if the message contains a trap word, DMs are always responded to if (messageTrap(message_string) and not llm_enabled) or messageTrap(message_string.split()[0]): - # log the message to the message log + # log the message to stdout logger.info(f"Device:{rxNode} Channel: {channel_number} " + CustomFormatter.green + f"Received DM: " + CustomFormatter.white + f"{message_string} " + CustomFormatter.purple +\ "From: " + CustomFormatter.white + f"{get_name_from_number(message_from_id, 'long', rxNode)}") # respond with DM @@ -321,7 +321,8 @@ def onReceive(packet, interface): time.sleep(responseDelay) # log the message to the message log - msgLogger.info(f"Device:{rxNode} Channel:{channel_number} | {get_name_from_number(message_from_id, 'long', rxNode)} | " + message_string.replace('\n', '-nl-')) + if log_messages_to_file: + msgLogger.info(f"Device:{rxNode} Channel:{channel_number} | {get_name_from_number(message_from_id, 'long', rxNode)} | DM | " + message_string.replace('\n', '-nl-')) else: # message is on a channel if messageTrap(message_string): From 6d01c5a9860e869886238881d8a4dcc20130a0ba Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 11:14:29 -0700 Subject: [PATCH 07/32] further adjustments for 2.7.2 https://github.com/SpudGunMan/meshing-around/pull/164#pullrequestreview-3021705851 and https: //github.com/SpudGunMan/meshing-around/issues/162 Co-Authored-By: SudoRand <25190078+sudorand@users.noreply.github.com> --- config.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.template b/config.template index fd838c6..194cbd0 100644 --- a/config.template +++ b/config.template @@ -295,7 +295,7 @@ hamtest = True [messagingSettings] # delay in seconds for response to avoid message collision -responseDelay = 1.2 +responseDelay = 2.2 # delay in seconds for splits in messages to avoid message collision splitDelay = 2.5 # message chunk size for sending at high success rate, chunkr allows exceeding by 3 characters From c7df4d88d1154014f36f0130efd0154850cd8d03 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 14:28:25 -0700 Subject: [PATCH 08/32] Update config.template Co-Authored-By: Russell Schmidt <836646+rfschmid@users.noreply.github.com> --- config.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.template b/config.template index 194cbd0..89a2fe7 100644 --- a/config.template +++ b/config.template @@ -294,9 +294,9 @@ hangman = True hamtest = True [messagingSettings] -# delay in seconds for response to avoid message collision +# delay in seconds for response to avoid message collision /throttling responseDelay = 2.2 -# delay in seconds for splits in messages to avoid message collision +# delay in seconds for splits in messages to avoid message collision /throttling splitDelay = 2.5 # message chunk size for sending at high success rate, chunkr allows exceeding by 3 characters MESSAGE_CHUNK_SIZE = 160 From e95902ef98a8410d70d63fa7255c546e75d07d9f Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 15:22:53 -0700 Subject: [PATCH 09/32] fix Excessive queries to FEMA issue raised https://github.com/SpudGunMan/meshing-around/issues/165 Co-Authored-By: DEVAFRS <180097515+devafrs@users.noreply.github.com> --- config.template | 9 ++--- modules/locationdata.py | 73 +++++++++++++++++++++-------------------- modules/settings.py | 3 +- 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/config.template b/config.template index 89a2fe7..496e2ca 100644 --- a/config.template +++ b/config.template @@ -161,16 +161,13 @@ enableExtraLocationWx = False # Goverment Alert Broadcast defaults to FEMA IPAWS eAlertBroadcastEnabled = False +# comma separated list of FIPS codes to trigger local alert. find your FIPS codes at https://en.wikipedia.org/wiki/Federal_Information_Processing_Standard_state_code +myFIPSList = 57,58,53 # Goverment Alert Broadcast Channels eAlertBroadcastCh = 2 - -# FEMA Alert Broadcast Settings -# Enable Ignore any headline that includes following word list +# Enable Ignore, headline that includes following word list ignoreFEMAenable = True ignoreFEMAwords = test,exercise -# comma separated list of codes (e.g., SAME,FIPS,ZIP) trigger local alert. -# find your SAME https://www.weather.gov/nwr/counties -mySAME = 053029,053073 # USGS Volcano alerts Enable USGS Volcano Alert Broadcast volcanoAlertBroadcastEnabled = False diff --git a/modules/locationdata.py b/modules/locationdata.py index 4802486..9da299f 100644 --- a/modules/locationdata.py +++ b/modules/locationdata.py @@ -472,8 +472,6 @@ def getIpawsAlert(lat=0, lon=0, shortAlerts = False): # set the API URL for IPAWS namespace = "urn:oasis:names:tc:emergency:cap:1.2" alert_url = "https://apps.fema.gov/IPAWSOPEN_EAS_SERVICE/rest/feed" - if ipawsPIN != "000000": - alert_url += "?pin=" + ipawsPIN # get the alerts from FEMA try: @@ -491,10 +489,25 @@ def getIpawsAlert(lat=0, lon=0, shortAlerts = False): # extract alerts from main feed for entry in alertxml.getElementsByTagName("entry"): link = entry.getElementsByTagName("link")[0].getAttribute("href") + + ## state FIPS + ## This logic is being added to reduce load on FEMA server. + stateFips = None + for cat in entry.getElementsByTagName("category"): + if cat.getAttribute("label") == "statefips": + stateFips = cat.getAttribute("term") + break + + if stateFips is None: + # no stateFIPS found β€” skip + continue + + # check if it matches your list + if stateFips not in myStateFIPSList: + #logger.debug(f"Skipping FEMA record link {link} with stateFIPS code of: {stateFips} because it doesn't match our StateFIPSList {myStateFIPSList}") + continue # skip to next entry + try: - #pin check - if ipawsPIN != "000000": - link += "?pin=" + ipawsPIN # get the linked alert data from FEMA linked_data = requests.get(link, timeout=urlTimeoutSeconds) if not linked_data.ok or not linked_data.text.strip(): @@ -533,44 +546,32 @@ def getIpawsAlert(lat=0, lon=0, shortAlerts = False): area_table = info.getElementsByTagName("area")[0] areaDesc = area_table.getElementsByTagName("areaDesc")[0].childNodes[0].nodeValue - geocode_table = area_table.getElementsByTagName("geocode")[0] - geocode_type = geocode_table.getElementsByTagName("valueName")[0].childNodes[0].nodeValue - geocode_value = geocode_table.getElementsByTagName("value")[0].childNodes[0].nodeValue - if geocode_type == "SAME": - sameVal = geocode_value except Exception as e: logger.debug(f"System: iPAWS Error extracting alert data: {link}") #print(f"DEBUG: {info.toprettyxml()}") continue - # check if the alert is for the current location, if wanted keep alert - if (sameVal in mySAME) or (geocode_value in mySAME): - # ignore the FEMA test alerts - if ignoreFEMAenable: - ignore_alert = False - for word in ignoreFEMAwords: - if word.lower() in headline.lower(): - logger.debug(f"System: Ignoring FEMA Alert: {headline} containing {word} at {areaDesc}") - ignore_alert = True - break + # ignore the FEMA test alerts + if ignoreFEMAenable: + ignore_alert = False + for word in ignoreFEMAwords: + if word.lower() in headline.lower(): + logger.debug(f"System: Ignoring FEMA Alert: {headline} containing {word} at {areaDesc}") + ignore_alert = True + break - if ignore_alert: - continue + if ignore_alert: + continue + + # add to alerts list + alerts.append({ + 'alertType': alertType, + 'alertCode': alertCode, + 'headline': headline, + 'areaDesc': areaDesc, + 'description': description + }) - # add to alerts list - alerts.append({ - 'alertType': alertType, - 'alertCode': alertCode, - 'headline': headline, - 'areaDesc': areaDesc, - 'geocode_type': geocode_type, - 'geocode_value': geocode_value, - 'description': description - }) - # else: - # # these are discarded some day but logged for debugging currently - # logger.debug(f"Debug iPAWS: Type:{alertType} Code:{alertCode} Desc:{areaDesc} GeoType:{geocode_type} GeoVal:{geocode_value}, Headline:{headline}") - # return the numWxAlerts of alerts if len(alerts) > 0: for alertItem in alerts[:numWxAlerts]: diff --git a/modules/settings.py b/modules/settings.py index e1c43f8..9e97e06 100644 --- a/modules/settings.py +++ b/modules/settings.py @@ -259,12 +259,11 @@ try: wxAlertsEnabled = config['location'].getboolean('NOAAalertsEnabled', True) # default True ignoreEASenable = config['location'].getboolean('ignoreEASenable', False) # default False ignoreEASwords = config['location'].get('ignoreEASwords', 'test,advisory').split(',') # default test,advisory - mySAME = config['location'].get('mySAME', '').split(',') # default empty myRegionalKeysDE = config['location'].get('myRegionalKeysDE', '110000000000').split(',') # default city Berlin forecastDuration = config['location'].getint('NOAAforecastDuration', 4) # NOAA forcast days numWxAlerts = config['location'].getint('NOAAalertCount', 2) # default 2 alerts enableExtraLocationWx = config['location'].getboolean('enableExtraLocationWx', False) # default False - ipawsPIN = config['location'].get('ipawsPIN', '000000') # default 000000 + myStateFIPSList = config['location'].get('myFIPSList', '').split(',') # default empty ignoreFEMAenable = config['location'].getboolean('ignoreFEMAenable', True) # default True ignoreFEMAwords = config['location'].get('ignoreFEMAwords', 'test,exercise').split(',') # default test,exercise wxAlertBroadcastChannel = config['location'].get('wxAlertBroadcastCh', '2').split(',') # default Channel 2 From 0cfe759ef6fe0bac52f43af2fe1c4735befb8328 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 15:34:32 -0700 Subject: [PATCH 10/32] Update mesh_bot.py --- mesh_bot.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mesh_bot.py b/mesh_bot.py index 38db5a0..2238f1f 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -1427,7 +1427,12 @@ async def start_rx(): if wxAlertBroadcastEnabled: logger.debug(f"System: Weather Alert Broadcast Enabled on channels {wxAlertBroadcastChannel}") if emergencyAlertBrodcastEnabled: - logger.debug(f"System: Emergency Alert Broadcast Enabled on channels {emergencyAlertBroadcastCh}") + logger.debug(f"System: Emergency Alert Broadcast Enabled on channels {emergencyAlertBroadcastCh} for FIPS codes {myStateFIPSList}") + # check if the FIPS codes are set + if myStateFIPSList == ['']: + logger.warning(f"System: No FIPS codes set for iPAWS Alerts") + + if emergency_responder_enabled: logger.debug(f"System: Emergency Responder Enabled on channels {emergency_responder_alert_channel} for interface {emergency_responder_alert_interface}") if volcanoAlertBroadcastEnabled: From 9e348332e558a0c3b013f8a5942e6d8d61516417 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 19:09:27 -0700 Subject: [PATCH 11/32] SAME code back in iPAWS the state only FIPS codes are too wide --- config.template | 2 ++ modules/locationdata.py | 51 ++++++++++++++++++++++++++--------------- modules/settings.py | 1 + 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/config.template b/config.template index 496e2ca..2191268 100644 --- a/config.template +++ b/config.template @@ -163,6 +163,8 @@ enableExtraLocationWx = False eAlertBroadcastEnabled = False # comma separated list of FIPS codes to trigger local alert. find your FIPS codes at https://en.wikipedia.org/wiki/Federal_Information_Processing_Standard_state_code myFIPSList = 57,58,53 +# find your SAME https://www.weather.gov/nwr/counties comma separated list of SAME code to further refine local alert. +mySAMEList = 053029,053073 # Goverment Alert Broadcast Channels eAlertBroadcastCh = 2 # Enable Ignore, headline that includes following word list diff --git a/modules/locationdata.py b/modules/locationdata.py index 9da299f..d7a86af 100644 --- a/modules/locationdata.py +++ b/modules/locationdata.py @@ -528,6 +528,10 @@ def getIpawsAlert(lat=0, lon=0, shortAlerts = False): continue for info in linked_xml.getElementsByTagName("info"): + # only get en-US language alerts (alternative is es-US) + language_nodes = info.getElementsByTagName("language") + if not any(node.firstChild and node.firstChild.nodeValue.strip() == "en-US" for node in language_nodes): + continue # skip if not en-US # extract values from XML sameVal = "NONE" geocode_value = "NONE" @@ -545,32 +549,43 @@ def getIpawsAlert(lat=0, lon=0, shortAlerts = False): area_table = info.getElementsByTagName("area")[0] areaDesc = area_table.getElementsByTagName("areaDesc")[0].childNodes[0].nodeValue + geocode_table = area_table.getElementsByTagName("geocode")[0] + geocode_type = geocode_table.getElementsByTagName("valueName")[0].childNodes[0].nodeValue + geocode_value = geocode_table.getElementsByTagName("value")[0].childNodes[0].nodeValue + if geocode_type == "SAME": + sameVal = geocode_value except Exception as e: logger.debug(f"System: iPAWS Error extracting alert data: {link}") #print(f"DEBUG: {info.toprettyxml()}") continue - # ignore the FEMA test alerts - if ignoreFEMAenable: - ignore_alert = False - for word in ignoreFEMAwords: - if word.lower() in headline.lower(): - logger.debug(f"System: Ignoring FEMA Alert: {headline} containing {word} at {areaDesc}") - ignore_alert = True - break - + # check if the alert is for the SAME location, if wanted keep alert + if (sameVal in mySAMEList) or (geocode_value in mySAMEList): + # ignore the FEMA test alerts + if ignoreFEMAenable: + ignore_alert = False + for word in ignoreFEMAwords: + if word.lower() in headline.lower(): + logger.debug(f"System: Filtering FEMA Alert by WORD: {headline} containing {word} at {areaDesc}") + ignore_alert = True + break if ignore_alert: continue - # add to alerts list - alerts.append({ - 'alertType': alertType, - 'alertCode': alertCode, - 'headline': headline, - 'areaDesc': areaDesc, - 'description': description - }) + # add to alert list + alerts.append({ + 'alertType': alertType, + 'alertCode': alertCode, + 'headline': headline, + 'areaDesc': areaDesc, + 'geocode_type': geocode_type, + 'geocode_value': geocode_value, + 'description': description + }) + else: + logger.debug(f"System: iPAWS Alert not in SAME List: {sameVal} or {geocode_value} for {headline} at {areaDesc}") + continue # return the numWxAlerts of alerts if len(alerts) > 0: @@ -684,5 +699,3 @@ def get_volcano_usgs(lat=0, lon=0): # return the alerts alerts = abbreviate_noaa(alerts) return alerts - - diff --git a/modules/settings.py b/modules/settings.py index 9e97e06..ea23be9 100644 --- a/modules/settings.py +++ b/modules/settings.py @@ -264,6 +264,7 @@ try: numWxAlerts = config['location'].getint('NOAAalertCount', 2) # default 2 alerts enableExtraLocationWx = config['location'].getboolean('enableExtraLocationWx', False) # default False myStateFIPSList = config['location'].get('myFIPSList', '').split(',') # default empty + mySAMEList = config['location'].get('mySAMEList', '').split(',') # default empty ignoreFEMAenable = config['location'].getboolean('ignoreFEMAenable', True) # default True ignoreFEMAwords = config['location'].get('ignoreFEMAwords', 'test,exercise').split(',') # default test,exercise wxAlertBroadcastChannel = config['location'].get('wxAlertBroadcastCh', '2').split(',') # default Channel 2 From 9b986dd57a8e79bc301eab7634b8ecddc29446a4 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 20:30:43 -0700 Subject: [PATCH 12/32] Update locationdata.py allow FIPS only --- modules/locationdata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/locationdata.py b/modules/locationdata.py index d7a86af..8f15f74 100644 --- a/modules/locationdata.py +++ b/modules/locationdata.py @@ -561,7 +561,7 @@ def getIpawsAlert(lat=0, lon=0, shortAlerts = False): continue # check if the alert is for the SAME location, if wanted keep alert - if (sameVal in mySAMEList) or (geocode_value in mySAMEList): + if (sameVal in mySAMEList) or (geocode_value in mySAMEList) or mySAMEList == ['']: # ignore the FEMA test alerts if ignoreFEMAenable: ignore_alert = False From b957c89d702fb69df2510d5b98f974054172c40e Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 20:32:51 -0700 Subject: [PATCH 13/32] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a19dcc6..19d342a 100644 --- a/README.md +++ b/README.md @@ -222,9 +222,10 @@ eAlertBroadcastEnabled = False # Goverment IPAWS/CAP Alert Broadcast eAlertBroadcastCh = 2,3 # Goverment Emergency IPAWS/CAP Alert Broadcast Channels ignoreFEMAenable = True # Ignore any headline that includes followig word list ignoreFEMAwords = test,exercise -# comma separated list of codes (e.g., SAME,FIPS,ZIP) trigger local alert. -# find your SAME https://www.weather.gov/nwr/counties -mySAME = 053029,053073 +# comma separated list of FIPS codes to trigger local alert. find your FIPS codes at https://en.wikipedia.org/wiki/Federal_Information_Processing_Standard_state_code +myFIPSList = 57,58,53 +# find your SAME https://www.weather.gov/nwr/counties comma separated list of SAME code to further refine local alert. +mySAMEList = 053029,053073 # To use other country services enable only a single optional serivce From 7c99b684ad0c37f0866237f7a17115d77606c442 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 21:11:48 -0700 Subject: [PATCH 14/32] riverflow never got documented well --- README.md | 10 ++++++++-- config.template | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 19d342a..f834990 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,6 @@ enabled = True lat = 48.50 lon = -123.0 UseMeteoWxAPI = True -riverListDefault = # NOAA Hydrology data, unique identifiers, LID or USGS ID ``` ### Module Settings @@ -215,7 +214,7 @@ alert_interface = 1 To Alert on Mesh with the EAS API you can set the channels and enable, checks every 20min. #### FEMA iPAWS/EAS and NINA -This uses USA: SAME, FIPS, ZIP code to locate the alerts in the feed. By default ignoring Test messages. +This uses USA: SAME, FIPS, to locate the alerts in the feed. By default ignoring Test messages. ```ini eAlertBroadcastEnabled = False # Goverment IPAWS/CAP Alert Broadcast @@ -244,6 +243,13 @@ ignoreEASenable = True # Ignore any headline that includes followig word list ignoreEASwords = test,advisory ``` +#### USGS River flow data +Using the USGS water data page locate a water flow device, for example Columbia River at Vancouver, WA - USGS-14144700 +```ini +# NOAA Hydrology unique identifiers, LID or USGS ID https://waterdata.usgs.gov +riverListDefault = 14144700 +``` + ### Repeater Settings A repeater function for two different nodes and cross-posting messages. The `repeater_channels` is a list of repeater channels that will be consumed and rebroadcast on the same number channel on the other device, node, or interface. Each node should have matching channel numbers. The channel names and PSK do not need to be the same on the nodes. Use this feature responsibly to avoid creating a feedback loop. diff --git a/config.template b/config.template index 2191268..e94f220 100644 --- a/config.template +++ b/config.template @@ -146,7 +146,7 @@ NOAAalertCount = 2 # use Open-Meteo API for weather data not NOAA useful for non US locations UseMeteoWxAPI = False -# NOAA Hydrology unique identifiers, LID or USGS ID +# NOAA Hydrology unique identifiers, LID or USGS ID https://waterdata.usgs.gov riverListDefault = # NOAA EAS Alert Broadcast From 57a4e5d68cde4587e06249dd6a5902946d04be92 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 21:12:23 -0700 Subject: [PATCH 15/32] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f834990..a4fe63f 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,7 @@ ignoreEASwords = test,advisory #### USGS River flow data Using the USGS water data page locate a water flow device, for example Columbia River at Vancouver, WA - USGS-14144700 ```ini +[location] # NOAA Hydrology unique identifiers, LID or USGS ID https://waterdata.usgs.gov riverListDefault = 14144700 ``` From a90a533a308d6616d6b14f2cad18f9a185a6480b Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 15 Jul 2025 21:20:37 -0700 Subject: [PATCH 16/32] USGS Alerts documented --- README.md | 12 +++++++++--- config.template | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a4fe63f..5ab0ba4 100644 --- a/README.md +++ b/README.md @@ -227,8 +227,8 @@ myFIPSList = 57,58,53 mySAMEList = 053029,053073 # To use other country services enable only a single optional serivce - enableDEalerts = False # Use DE Alert Broadcast Data see template for filters +myRegionalKeysDE = 110000000000,120510000000 ``` #### NOAA EAS @@ -243,12 +243,18 @@ ignoreEASenable = True # Ignore any headline that includes followig word list ignoreEASwords = test,advisory ``` -#### USGS River flow data +#### USGS River flow data and Volcano alerts Using the USGS water data page locate a water flow device, for example Columbia River at Vancouver, WA - USGS-14144700 + +Volcano Alerts use lat/long to determine ~1000km radius ```ini [location] -# NOAA Hydrology unique identifiers, LID or USGS ID https://waterdata.usgs.gov +# USGS Hydrology unique identifiers, LID or USGS ID https://waterdata.usgs.gov riverListDefault = 14144700 + +# USGS Volcano alerts Enable USGS Volcano Alert Broadcast +volcanoAlertBroadcastEnabled = False +volcanoAlertBroadcastCh = 2 ``` ### Repeater Settings diff --git a/config.template b/config.template index e94f220..7febac7 100644 --- a/config.template +++ b/config.template @@ -146,7 +146,7 @@ NOAAalertCount = 2 # use Open-Meteo API for weather data not NOAA useful for non US locations UseMeteoWxAPI = False -# NOAA Hydrology unique identifiers, LID or USGS ID https://waterdata.usgs.gov +# USGS Hydrology unique identifiers, LID or USGS ID https://waterdata.usgs.gov riverListDefault = # NOAA EAS Alert Broadcast From a8ccb05d56b8749e27a52ad2753a7f9281c40b71 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Wed, 16 Jul 2025 08:57:39 -0700 Subject: [PATCH 17/32] Update system.py bug of undefined for interface retry --- modules/system.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/system.py b/modules/system.py index 32f2688..1fcb47f 100644 --- a/modules/system.py +++ b/modules/system.py @@ -255,6 +255,7 @@ if ble_count > 1: logger.debug(f"System: Initializing Interfaces") interface1 = interface2 = interface3 = interface4 = interface5 = interface6 = interface7 = interface8 = interface9 = None retry_int1 = retry_int2 = retry_int3 = retry_int4 = retry_int5 = retry_int6 = retry_int7 = retry_int8 = retry_int9 = False +max_retry_count1 = max_retry_count2 = max_retry_count3 = max_retry_count4 = max_retry_count5 = max_retry_count6 = max_retry_count7 = max_retry_count8 = max_retry_count9 = 3 for i in range(1, 10): interface_type = globals().get(f'interface{i}_type') if not interface_type or interface_type == 'none' or globals().get(f'interface{i}_enabled') == False: @@ -817,6 +818,9 @@ def handleAlertBroadcast(deviceID=1): def onDisconnect(interface): global retry_int1, retry_int2, retry_int3, retry_int4, retry_int5, retry_int6, retry_int7, retry_int8, retry_int9 + if interface is None: + logger.critical("System: Lost Connection to Device None") + exit_handler() rxType = type(interface).__name__ if rxType in ['SerialInterface', 'TCPInterface', 'BLEInterface']: identifier = interface.__dict__.get('devPath', interface.__dict__.get('hostname', 'BLE')) @@ -1128,7 +1132,8 @@ async def handleFileWatcher(): pass async def retry_interface(nodeID): - global max_retry_count + global retry_int1, retry_int2, retry_int3, retry_int4, retry_int5, retry_int6, retry_int7, retry_int8, retry_int9 + global max_retry_count1, max_retry_count2, max_retry_count3, max_retry_count4, max_retry_count5, max_retry_count6, max_retry_count7, max_retry_count8, max_retry_count9 interface = globals()[f'interface{nodeID}'] retry_int = globals()[f'retry_int{nodeID}'] max_retry_count = globals()[f'max_retry_count{nodeID}'] From cc58a38165afcb1276db9a46fa52c2f43400e1d0 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Fri, 18 Jul 2025 21:24:55 -0700 Subject: [PATCH 18/32] Update system.py --- modules/system.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/modules/system.py b/modules/system.py index 1fcb47f..8f6e446 100644 --- a/modules/system.py +++ b/modules/system.py @@ -818,20 +818,13 @@ def handleAlertBroadcast(deviceID=1): def onDisconnect(interface): global retry_int1, retry_int2, retry_int3, retry_int4, retry_int5, retry_int6, retry_int7, retry_int8, retry_int9 - if interface is None: - logger.critical("System: Lost Connection to Device None") - exit_handler() rxType = type(interface).__name__ if rxType in ['SerialInterface', 'TCPInterface', 'BLEInterface']: - identifier = interface.__dict__.get('devPath', interface.__dict__.get('hostname', 'BLE')) - logger.critical(f"System: Lost Connection to Device {identifier}") + logger.critical(f"System: Lost Connection to Device {interface}") for i in range(1, 10): if globals().get(f'interface{i}_enabled'): - if (rxType == 'SerialInterface' and globals().get(f'port{i}') in identifier) or \ - (rxType == 'TCPInterface' and globals().get(f'hostname{i}') in identifier) or \ - (rxType == 'BLEInterface' and globals().get(f'interface{i}_type') == 'ble'): - globals()[f'retry_int{i}'] = True - break + globals()[f'retry_int{i}'] = True + break def exit_handler(): # Close the interface and save the BBS messages From 1895a365aef67fa52fdb5753a2776815cb32a2c8 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 20 Jul 2025 05:41:50 -0700 Subject: [PATCH 19/32] fix retry and failure correcting multiple issues with some bad code https://github.com/SpudGunMan/meshing-around/issues/137 https://github.com/SpudGunMan/meshing-around/issues/156 --- modules/settings.py | 3 +-- modules/system.py | 9 +++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/settings.py b/modules/settings.py index ea23be9..c3e2cb9 100644 --- a/modules/settings.py +++ b/modules/settings.py @@ -21,8 +21,7 @@ ping_enabled = True # ping feature to respond to pings, ack's etc. sitrep_enabled = True # sitrep feature to respond to sitreps lastHamLibAlert = 0 # last alert from hamlib lastFileAlert = 0 # last alert from file monitor -max_retry_count1 = 4 # max retry count for interface 1 -max_retry_count2 = 4 # max retry count for interface 2 +max_retry_count1 = max_retry_count2 = max_retry_count3 = max_retry_count4 = max_retry_count5 = max_retry_count6 = max_retry_count7 = max_retry_count8 = max_retry_count9 = 4 # default retry count for interfaces retry_int1 = False retry_int2 = False wiki_return_limit = 3 # limit the number of sentences returned off the first paragraph first hit diff --git a/modules/system.py b/modules/system.py index 8f6e446..12a6dbd 100644 --- a/modules/system.py +++ b/modules/system.py @@ -823,8 +823,13 @@ def onDisconnect(interface): logger.critical(f"System: Lost Connection to Device {interface}") for i in range(1, 10): if globals().get(f'interface{i}_enabled'): - globals()[f'retry_int{i}'] = True - break + if globals().get(f'max_retry_count{i}') > 0: + retry_flag = globals().get(f'retry_int{i}') + if not retry_flag: + globals()[f'retry_int{i}'] = True + else: + logger.critical(f"System: Interface{i} {globals()[f'interface{i}']} failed to reconnect after multiple attempts. Exiting") + exit_handler() def exit_handler(): # Close the interface and save the BBS messages From d715cb6b4d5d543293104deb00a17aa8bd215c63 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Mon, 21 Jul 2025 04:33:04 -0700 Subject: [PATCH 20/32] Update system.py --- modules/system.py | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/modules/system.py b/modules/system.py index 12a6dbd..9e154ca 100644 --- a/modules/system.py +++ b/modules/system.py @@ -831,28 +831,6 @@ def onDisconnect(interface): logger.critical(f"System: Interface{i} {globals()[f'interface{i}']} failed to reconnect after multiple attempts. Exiting") exit_handler() -def exit_handler(): - # Close the interface and save the BBS messages - logger.debug(f"System: Closing Autoresponder") - try: - logger.debug(f"System: Closing Interface1") - interface1.close() - if multiple_interface: - for i in range(2, 10): - if globals().get(f'interface{i}_enabled'): - logger.debug(f"System: Closing Interface{i}") - globals()[f'interface{i}'].close() - except Exception as e: - logger.error(f"System: closing: {e}") - if bbs_enabled: - save_bbsdb() - save_bbsdm() - logger.debug(f"System: BBS Messages Saved") - logger.debug(f"System: Exiting") - asyncLoop.stop() - asyncLoop.close() - exit (0) - # Telemetry Functions telemetryData = {} def initialize_telemetryData(): @@ -1251,3 +1229,24 @@ async def watchdog(): except Exception as e: logger.error(f"System: retrying interface{i}: {e}") +def exit_handler(): + # Close the interface and save the BBS messages + logger.debug(f"System: Closing Autoresponder") + try: + logger.debug(f"System: Closing Interface1") + interface1.close() + if multiple_interface: + for i in range(2, 10): + if globals().get(f'interface{i}_enabled'): + logger.debug(f"System: Closing Interface{i}") + globals()[f'interface{i}'].close() + except Exception as e: + logger.error(f"System: closing: {e}") + if bbs_enabled: + save_bbsdb() + save_bbsdm() + logger.debug(f"System: BBS Messages Saved") + logger.debug(f"System: Exiting") + asyncLoop.stop() + asyncLoop.close() + exit (0) From 748652ac621fe19adbe7112b2235f3ae5058be21 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Mon, 21 Jul 2025 20:10:59 -0700 Subject: [PATCH 21/32] onDisconnect correcting multiple issues adding config.ini feature for dont_retry_disconnect https://github.com/SpudGunMan/meshing-around/issues/137 https://github.com/SpudGunMan/meshing-around/issues/156 --- config.template | 3 +++ mesh_bot.py | 11 ++++++----- modules/settings.py | 1 + modules/system.py | 33 ++++++++++++++------------------- pong_bot.py | 11 ++++++----- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/config.template b/config.template index 7febac7..6bd5286 100644 --- a/config.template +++ b/config.template @@ -87,6 +87,9 @@ sysloglevel = DEBUG # Number of log files to keep in days, 0 to keep all log_backup_count = 32 +#Do not retry enabling interface if it fails, just exit to let OS restart the bot +dont_retry_disconnect = False + [emergencyHandler] # enable or disable the emergency response handler enabled = False diff --git a/mesh_bot.py b/mesh_bot.py index 2238f1f..63bef74 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -1553,10 +1553,11 @@ async def main(): await asyncio.sleep(0.01) -try: - if __name__ == "__main__": +if __name__ == "__main__": + try: asyncio.run(main()) -except KeyboardInterrupt: - exit_handler() - pass + except KeyboardInterrupt: + exit_handler() + except SystemExit: + pass # EOF diff --git a/modules/settings.py b/modules/settings.py index c3e2cb9..e03c666 100644 --- a/modules/settings.py +++ b/modules/settings.py @@ -222,6 +222,7 @@ try: llmModel = config['general'].get('ollamaModel', 'gemma2:2b') # default gemma2:2b ollamaHostName = config['general'].get('ollamaHostName', 'http://localhost:11434') # default localhost llmReplyToNonCommands = config['general'].getboolean('llmReplyToNonCommands', True) + dont_retry_disconnect = config['general'].getboolean('dont_retry_disconnect', False) # default False, retry on disconnect # emergency response emergency_responder_enabled = config['emergencyHandler'].getboolean('enabled', False) emergency_responder_alert_channel = config['emergencyHandler'].getint('alert_channel', 2) # default 2 diff --git a/modules/system.py b/modules/system.py index 9e154ca..28fab8f 100644 --- a/modules/system.py +++ b/modules/system.py @@ -818,18 +818,7 @@ def handleAlertBroadcast(deviceID=1): def onDisconnect(interface): global retry_int1, retry_int2, retry_int3, retry_int4, retry_int5, retry_int6, retry_int7, retry_int8, retry_int9 - rxType = type(interface).__name__ - if rxType in ['SerialInterface', 'TCPInterface', 'BLEInterface']: - logger.critical(f"System: Lost Connection to Device {interface}") - for i in range(1, 10): - if globals().get(f'interface{i}_enabled'): - if globals().get(f'max_retry_count{i}') > 0: - retry_flag = globals().get(f'retry_int{i}') - if not retry_flag: - globals()[f'retry_int{i}'] = True - else: - logger.critical(f"System: Interface{i} {globals()[f'interface{i}']} failed to reconnect after multiple attempts. Exiting") - exit_handler() + interface.close() # Telemetry Functions telemetryData = {} @@ -1112,18 +1101,22 @@ async def retry_interface(nodeID): global max_retry_count1, max_retry_count2, max_retry_count3, max_retry_count4, max_retry_count5, max_retry_count6, max_retry_count7, max_retry_count8, max_retry_count9 interface = globals()[f'interface{nodeID}'] retry_int = globals()[f'retry_int{nodeID}'] - max_retry_count = globals()[f'max_retry_count{nodeID}'] + + if dont_retry_disconnect: + logger.critical(f"System: dont_retry_disconnect is set, not retrying interface{nodeID}") + exit_handler() if interface is not None: - retry_int = True - max_retry_count -= 1 + globals()[f'retry_int{nodeID}'] = True + globals()[f'max_retry_count{nodeID}'] -= 1 + logger.debug(f"System: Retrying interface{nodeID} {globals()[f'max_retry_count{nodeID}']} attempts left") try: interface.close() + logger.debug(f"System: Retrying interface{nodeID} in 15 seconds") except Exception as e: logger.error(f"System: closing interface{nodeID}: {e}") - logger.debug(f"System: Retrying interface{nodeID} in 15 seconds") - if max_retry_count == 0: + if globals()[f'max_retry_count{nodeID}'] == 0: logger.critical(f"System: Max retry count reached for interface{nodeID}") exit_handler() @@ -1133,13 +1126,15 @@ async def retry_interface(nodeID): if retry_int: interface = None globals()[f'interface{nodeID}'] = None - logger.debug(f"System: Retrying Interface{nodeID}") interface_type = globals()[f'interface{nodeID}_type'] if interface_type == 'serial': + logger.debug(f"System: Retrying Interface{nodeID} Serial on port: {globals().get(f'port{nodeID}')}") globals()[f'interface{nodeID}'] = meshtastic.serial_interface.SerialInterface(globals().get(f'port{nodeID}')) elif interface_type == 'tcp': - globals()[f'interface{nodeID}'] = meshtastic.tcp_interface.TCPInterface(globals().get(f'host{nodeID}')) + logger.debug(f"System: Retrying Interface{nodeID} TCP on hostname: {globals().get(f'hostname{nodeID}')}") + globals()[f'interface{nodeID}'] = meshtastic.tcp_interface.TCPInterface(globals().get(f'hostname{nodeID}')) elif interface_type == 'ble': + logger.debug(f"System: Retrying Interface{nodeID} BLE on mac: {globals().get(f'mac{nodeID}')}") globals()[f'interface{nodeID}'] = meshtastic.ble_interface.BLEInterface(globals().get(f'mac{nodeID}')) logger.debug(f"System: Interface{nodeID} Opened!") globals()[f'retry_int{nodeID}'] = False diff --git a/pong_bot.py b/pong_bot.py index 76f5b24..a703a7f 100755 --- a/pong_bot.py +++ b/pong_bot.py @@ -444,10 +444,11 @@ async def main(): await asyncio.sleep(0.01) -try: - if __name__ == "__main__": +if __name__ == "__main__": + try: asyncio.run(main()) -except KeyboardInterrupt: - exit_handler() - pass + except KeyboardInterrupt: + exit_handler() + except SystemExit: + pass # EOF From 410d32947cf513f60d10b0190ec9598a7a1c9ccf Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Mon, 21 Jul 2025 20:13:07 -0700 Subject: [PATCH 22/32] Update system.py --- modules/system.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/system.py b/modules/system.py index 28fab8f..a5840e8 100644 --- a/modules/system.py +++ b/modules/system.py @@ -817,7 +817,8 @@ def handleAlertBroadcast(deviceID=1): return True def onDisconnect(interface): - global retry_int1, retry_int2, retry_int3, retry_int4, retry_int5, retry_int6, retry_int7, retry_int8, retry_int9 + # Handle disconnection of the interface + logger.warning(f"System: Abrupt Disconnection of Interface detected") interface.close() # Telemetry Functions From 45eefb24d86dcd2debc37c94562714c3010ea188 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 22 Jul 2025 07:02:08 -0700 Subject: [PATCH 23/32] enhance retry --- modules/system.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/system.py b/modules/system.py index a5840e8..45fcf4c 100644 --- a/modules/system.py +++ b/modules/system.py @@ -18,6 +18,7 @@ help_message = "Bot CMD?:" asyncLoop = asyncio.new_event_loop() games_enabled = False multiPingList = [{'message_from_id': 0, 'count': 0, 'type': '', 'deviceID': 0, 'channel_number': 0, 'startCount': 0}] +interface_retry_count = 3 # Ping Configuration if ping_enabled: @@ -255,7 +256,7 @@ if ble_count > 1: logger.debug(f"System: Initializing Interfaces") interface1 = interface2 = interface3 = interface4 = interface5 = interface6 = interface7 = interface8 = interface9 = None retry_int1 = retry_int2 = retry_int3 = retry_int4 = retry_int5 = retry_int6 = retry_int7 = retry_int8 = retry_int9 = False -max_retry_count1 = max_retry_count2 = max_retry_count3 = max_retry_count4 = max_retry_count5 = max_retry_count6 = max_retry_count7 = max_retry_count8 = max_retry_count9 = 3 +max_retry_count1 = max_retry_count2 = max_retry_count3 = max_retry_count4 = max_retry_count5 = max_retry_count6 = max_retry_count7 = max_retry_count8 = max_retry_count9 = interface_retry_count for i in range(1, 10): interface_type = globals().get(f'interface{i}_type') if not interface_type or interface_type == 'none' or globals().get(f'interface{i}_enabled') == False: @@ -1138,6 +1139,8 @@ async def retry_interface(nodeID): logger.debug(f"System: Retrying Interface{nodeID} BLE on mac: {globals().get(f'mac{nodeID}')}") globals()[f'interface{nodeID}'] = meshtastic.ble_interface.BLEInterface(globals().get(f'mac{nodeID}')) logger.debug(f"System: Interface{nodeID} Opened!") + # reset the retry_int and retry_count + globals()[f'max_retry_count{nodeID}'] = interface_retry_count globals()[f'retry_int{nodeID}'] = False except Exception as e: logger.error(f"System: Error Opening interface{nodeID} on: {e}") From 14798cb992486910548a72e897f08eb0bd515f61 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 22 Jul 2025 09:28:08 -0700 Subject: [PATCH 24/32] alertDe --- modules/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/system.py b/modules/system.py index 6860c5b..45e9d19 100644 --- a/modules/system.py +++ b/modules/system.py @@ -790,7 +790,7 @@ def handleAlertBroadcast(deviceID=1): send_message(ukAlert, emergencyAlertBroadcastCh, 0, deviceID) return True - if NO_ALERTS not in deAlert: + if NO_ALERTS not in alertDe: if isinstance(emergencyAlertBroadcastCh, list): for channel in emergencyAlertBroadcastCh: send_message(ukAlert, int(channel), 0, deviceID) From e1b47484f24f84c181fa96245c5bda8cebea60f5 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Wed, 30 Jul 2025 08:34:20 -0700 Subject: [PATCH 25/32] NOAA Coastal Marine Forcast data using older but handy products with new mwx --- README.md | 4 +++ config.template | 10 ++++++- install.sh | 4 +-- mesh_bot.py | 6 +++-- modules/locationdata.py | 59 +++++++++++++++++++++++++++++++++++++++++ modules/settings.py | 4 +++ modules/system.py | 6 +++++ 7 files changed, 88 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5ab0ba4..41e17d4 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,9 @@ enabled = True lat = 48.50 lon = -123.0 UseMeteoWxAPI = True + +pzzEnabled = False # NOAA Coastal Waters Forecasts Enable NOAA Coastal Waters Forecasts (PZZ) +pzzZoneID = 132 # My Forecast Zone ID, https://www.weather.gov/marine select location and then look at the 'Forecast-by-Zone Map' and select PZZ zone ``` ### Module Settings @@ -421,6 +424,7 @@ There is no direct support for MQTT in the code, however, reports from Discord a | `valert` | Returns USGS Volcano Data | | | `wx` and `wxc` | Return local weather forecast (wxc is metric value), NOAA or Open Meteo for weather forecasting | | | `wxa` and `wxalert` | Return NOAA alerts. Short title or expanded details | | +| `mwx` | Return the NOAA Coastal Marine Forcast data | | ### Bulletin Board & Mail | Command | Description | | diff --git a/config.template b/config.template index 6bd5286..9fefad7 100644 --- a/config.template +++ b/config.template @@ -108,7 +108,8 @@ SentryChannel = 2 # holdoff time multiplied by seconds(20) of the watchdog SentryHoldoff = 9 # list of ignored nodes numbers ex: 2813308004,4258675309 -sentryIgnoreList = +sentryIgnoreList = + # HighFlying Node alert highFlyingAlert = True # Altitude in meters to trigger the alert @@ -149,6 +150,13 @@ NOAAalertCount = 2 # use Open-Meteo API for weather data not NOAA useful for non US locations UseMeteoWxAPI = False +# NOAA Coastal Waters Forecasts Enable NOAA Coastal Waters Forecasts (PZZ) +pzzEnabled = False +# My Forecast Zone ID, https://www.weather.gov/marine select location and then look at the 'Forecast-by-Zone Map' and select PZZ zone +pzzZoneID = 132 +# number of data points to return, default is 3 +pzzForecastDays = 3 + # USGS Hydrology unique identifiers, LID or USGS ID https://waterdata.usgs.gov riverListDefault = diff --git a/install.sh b/install.sh index df4fbce..4c44c2f 100755 --- a/install.sh +++ b/install.sh @@ -356,5 +356,5 @@ exit 0 # after install shenannigans -# add 'bee = True' to config.ini General section. You will likley want to clean the txt up a bit -# wget https://courses.cs.washington.edu/courses/cse163/20wi/files/lectures/L04/bee-movie.txt -O bee.txt +# add 'bee = True' to config.ini General section. +# wget https://gist.github.com/MattIPv4/045239bc27b16b2bcf7a3a9a4648c08a -O bee.txt diff --git a/mesh_bot.py b/mesh_bot.py index 63bef74..52947fd 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -67,6 +67,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n "messages": lambda: handle_messages(message, deviceID, channel_number, msg_history, publicChannel, isDM), "moon": lambda: handle_moon(message_from_id, deviceID, channel_number), "motd": lambda: handle_motd(message, message_from_id, isDM), + "mwx": lambda: handle_mwx(message_from_id, deviceID, channel_number), "ping": lambda: handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, channel_number), "pinging": lambda: handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, channel_number), "pong": lambda: "πŸ“PING!!πŸ›œ", @@ -750,6 +751,9 @@ def handle_riverFlow(message, message_from_id, deviceID): msg = get_flood_noaa(location[0], location[1], userRiver) return msg +def handle_mwx(message_from_id, deviceID, cmd): + # NOAA Coastal and Marine Weather PZZ + return get_nws_marine(zone=pzzZoneID, days=pzzForecastDays) def handle_wxc(message_from_id, deviceID, cmd): location = get_node_location(message_from_id, deviceID) @@ -1431,8 +1435,6 @@ async def start_rx(): # check if the FIPS codes are set if myStateFIPSList == ['']: logger.warning(f"System: No FIPS codes set for iPAWS Alerts") - - if emergency_responder_enabled: logger.debug(f"System: Emergency Responder Enabled on channels {emergency_responder_alert_channel} for interface {emergency_responder_alert_interface}") if volcanoAlertBroadcastEnabled: diff --git a/modules/locationdata.py b/modules/locationdata.py index 8f15f74..b32ce68 100644 --- a/modules/locationdata.py +++ b/modules/locationdata.py @@ -699,3 +699,62 @@ def get_volcano_usgs(lat=0, lon=0): # return the alerts alerts = abbreviate_noaa(alerts) return alerts + +def get_nws_marine(zone, days=3): + # forcast from NWS coastal products + marine_pzz_url = "https://tgftp.nws.noaa.gov/data/forecasts/marine/coastal/pz/pzz" + str(zone) + ".txt" + try: + marine_pzz_data = requests.get(marine_pzz_url, timeout=urlTimeoutSeconds) + if not marine_pzz_data.ok: + logger.warning("Location:Error fetching NWS Marine PZ data") + return ERROR_FETCHING_DATA + except (requests.exceptions.RequestException): + logger.warning("Location:Error fetching NWS Marine PZ data") + return ERROR_FETCHING_DATA + + marine_pzz_data = marine_pzz_data.text + #validate data + todayDate = today.strftime("%Y%m%d") + if marine_pzz_data.startswith("Expires:"): + expires = marine_pzz_data.split(";;")[0].split(":")[1] + expires_date = expires[:8] + if expires_date < todayDate: + logger.debug("Location: NWS Marine PZ data expired") + return NO_DATA_NOGPS + else: + logger.debug("Location: NWS Marine PZ data not valid") + return NO_DATA_NOGPS + + # process the marine forecast data + marine_pzz_lines = marine_pzz_data.split("\n") + marine_pzz_report = "" + day_blocks = [] + current_block = "" + in_forecast = False + + for line in marine_pzz_lines: + if line.startswith(".") and "..." in line: + in_forecast = True + if current_block: + day_blocks.append(current_block.strip()) + current_block = "" + current_block += line.strip() + " " + elif in_forecast and line.strip() != "": + current_block += line.strip() + " " + if current_block: + day_blocks.append(current_block.strip()) + + # Only keep up to pzzDays blocks + for block in day_blocks[:days]: + marine_pzz_report += block + "\n" + + # remove last newline + if marine_pzz_report.endswith("\n"): + marine_pzz_report = marine_pzz_report[:-1] + + # abbreviate the report + marine_pzz_report = abbreviate_noaa(marine_pzz_report) + if marine_pzz_report == "": + return NO_DATA_NOGPS + return marine_pzz_report + diff --git a/modules/settings.py b/modules/settings.py index e03c666..32509aa 100644 --- a/modules/settings.py +++ b/modules/settings.py @@ -251,6 +251,10 @@ try: n2yoAPIKey = config['location'].get('n2yoAPIKey', '') # default empty satListConfig = config['location'].get('satList', '25544').split(',') # default 25544 ISS riverListDefault = config['location'].get('riverList', '').split(',') # default 12061500 Skagit River + pzzEnabled = config['location'].getboolean('pzzEnabled', False) # default False + pzzZoneID = config['location'].getint('pzzZoneID', 100) # default 100, PZZ132 for Seattle area + pzzForecastDays = config['location'].getint('pzzForecastDays', 3) # default 3 days + # location alerts emergencyAlertBrodcastEnabled = config['location'].getboolean('eAlertBroadcastEnabled', False) # default False wxAlertBroadcastEnabled = config['location'].getboolean('wxAlertBroadcastEnabled', False) # default False diff --git a/modules/system.py b/modules/system.py index 45e9d19..3e58ca7 100644 --- a/modules/system.py +++ b/modules/system.py @@ -98,6 +98,12 @@ if wxAlertBroadcastEnabled or emergencyAlertBrodcastEnabled or volcanoAlertBroad # limited subset, this should be done better but eh.. trap_list = trap_list + ("wx", "wxc", "wxa", "wxalert", "ea", "ealert", "valert") help_message = help_message + ", wxalert, ealert, valert" + +# NOAA Coastal Waters Forecasts PZZ +if pzzEnabled: + from modules.locationdata import * # from the spudgunman/meshing-around repo + trap_list = trap_list + ("mwx",) + help_message = help_message + ", mwx" # BBS Configuration if bbs_enabled: From 3fcd588d026e6e1b971d06255e0fdab3a58ce209 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Wed, 30 Jul 2025 10:05:04 -0700 Subject: [PATCH 26/32] bugs and docs Consolidated Tide with MWX fixed up readme and cleaned up rlist in help --- README.md | 168 +++++++++++++++++++++++----------------------- config.template | 2 +- modules/system.py | 20 ++++-- 3 files changed, 99 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 41e17d4..b3c9b95 100644 --- a/README.md +++ b/README.md @@ -72,23 +72,97 @@ Welcome to the Mesh Bot project! This feature-rich bot is designed to enhance yo ## Getting Started This project is developed on Linux (specifically a Raspberry Pi) but should work on any platform where the [Meshtastic protobuf API](https://meshtastic.org/docs/software/python/cli/) modules are supported, and with any compatible [Meshtastic](https://meshtastic.org/docs/getting-started/) hardware. For pico or low-powered devices, see projects for embedding, [buildroot](https://github.com/buildroot-meshtastic/buildroot-meshtastic), also see [femtofox](https://github.com/noon92/femtofox). πŸ₯” Please use responsibly and follow local rulings for such equipment. This project captures packets, logs them, and handles over the air communications which can include PII such as GPS locations. -### Installation - +### Quick Setup #### Clone the Repository If you dont have git you will need it `sudo apt-get install git` ```sh git clone https://github.com/spudgunman/meshing-around ``` -The code is under active development, so make sure to pull the latest changes regularly! - -#### Quick setup - **Automated Installation**: `install.sh` will automate optional venv and requirements installation. - **Launch Script**: `launch.sh` only used in a venv install, to launch the bot and the report generator. -#### Docker Installation +## Full list of commands for the bot + +### Networking +| Command | Description | βœ… Works Off-Grid | +|---------|-------------|- +| `ping`, `ack` | Return data for signal. Example: `ping 15 #DrivingI5` (activates auto-ping every 20 seconds for count 15) | βœ… | +| `cmd` | Returns the list of commands (the help message) | βœ… | +| `history` | Returns the last commands run by user(s) | βœ… | +| `lheard` | Returns the last 5 heard nodes with SNR. Can also use `sitrep` | βœ… | +| `motd` | Displays the message of the day or sets it. Example: `motd $New Message Of the day` | βœ… | +| `sysinfo` | Returns the bot node telemetry info | βœ… | +| `test` | used to test the limits of data transfer `test 4` sends data to the maxBuffer limit (default 220) | βœ… | +| `whereami` | Returns the address of the sender's location if known | +| `whoami` | Returns details of the node asking, also returned when position exchanged πŸ“ | βœ… | +| `whois` | Returns details known about node, more data with bbsadmin node | βœ… | + +### Radio Propagation & Weather Forcasting +| Command | Description | | +|---------|-------------|------------------- +| `ea` and `ealert` | Return FEMA iPAWS/EAS alerts in USA or DE Headline or expanded details for USA | | +| `hfcond` | Returns a table of HF solar conditions | | +| `rlist` | Returns a table of nearby repeaters from RepeaterBook | | +| `riverflow` | Return information from NOAA for river flow info. Example: `riverflow modules/settings.py`| | +| `solar` | Gives an idea of the x-ray flux | | +| `sun` and `moon` | Return info on rise and set local time | βœ… | +| `tide` | Returns the local tides (NOAA data source) | | +| `valert` | Returns USGS Volcano Data | | +| `wx` | Return local weather forecast, NOAA or Open Meteo (which also has `wxc` for metric and imperial) | | +| `wxa` and `wxalert` | Return NOAA alerts. Short title or expanded details | | +| `mwx` | Return the NOAA Coastal Marine Forcast data | | + +### Bulletin Board & Mail +| Command | Description | | +|---------|-------------|- +| `bbshelp` | Returns the following help message | βœ… | +| `bbslist` | Lists the messages by ID and subject | βœ… | +| `bbsread` | Reads a message. Example: `bbsread #1` | βœ… | +| `bbspost` | Posts a message to the public board or sends a DM(Mail) Examples: `bbspost $subject #message`, `bbspost @nodeNumber #message`, `bbspost @nodeShortName #message` | βœ… | +| `bbsdelete` | Deletes a message. Example: `bbsdelete #4` | βœ… | +| `bbsinfo` | Provides stats on BBS delivery and messages (sysop) | βœ… | +| `bbslink` | Links Bulletin Messages between BBS Systems | βœ… | +| `email:` | Sends email to address on file for the node or `email: bob@test.net # hello from mesh` | | +| `sms:` | Send sms-email to multiple address on file | | +| `setemail`| Sets the email for easy communciations | | +| `setsms` | Adds the SMS-Email for quick communications | | +| `clearsms` | Clears all SMS-Emails on file for node | | + +### Data Lookup +| Command | Description | | +|---------|-------------|- +| `askai` and `ask:` | Ask Ollama LLM AI for a response. Example: `askai what temp do I cook chicken` | βœ… | +| `messages` | Replays the last messages heard, like Store and Forward | βœ… | +| `readnews` | returns the contents of a file (news.txt, by default) via the chunker on air | βœ… | +| `satpass` | returns the pass info from API for defined NORAD ID in config or Example: `satpass 25544,33591`| | +| `wiki:` | Searches Wikipedia and returns the first few sentences of the first result if a match. Example: `wiki: lora radio` | + +### CheckList +| Command | Description | | +|---------|-------------|- +| `checkin` | Check in the node to the checklist database, you can add a note like `checkin ICO` or `checkin radio4` | βœ… | +| `checkout` | Checkout the node in the checklist database, checkout all from node | βœ… | +| `checklist` | Display the checklist database, with note | βœ… | + +### Games (via DM) +| Command | Description | | +|---------|-------------|- +| `blackjack` | Plays Blackjack (Casino 21) | βœ… | +| `dopewars` | Plays the classic drug trader game | βœ… | +| `golfsim` | Plays a 9-hole Golf Simulator | βœ… | +| `hamtest` | FCC/ARRL Quiz `hamtest general` or `hamtest extra` and `score` | βœ… | +| `hangman` | Plays the classic word guess game | βœ… | +| `joke` | Tells a joke | βœ… | +| `lemonstand` | Plays the classic Lemonade Stand finance game | βœ… | +| `mastermind` | Plays the classic code-breaking game | βœ… | +| `videopoker` | Plays basic 5-card hold Video Poker | βœ… | + +## Other Install Options + +### Docker Installation - handy for windows See further info on the [docker.md](script/docker/README.md) -#### Manual Install +### Manual Install Install the required dependencies using pip: ```sh pip install -r requirements.txt @@ -149,8 +223,10 @@ lat = 48.50 lon = -123.0 UseMeteoWxAPI = True -pzzEnabled = False # NOAA Coastal Waters Forecasts Enable NOAA Coastal Waters Forecasts (PZZ) +# NOAA Coastal Data Enable NOAA Coastal Waters Forecasts (PZZ) and tide cmd +pzzEnabled = False pzzZoneID = 132 # My Forecast Zone ID, https://www.weather.gov/marine select location and then look at the 'Forecast-by-Zone Map' and select PZZ zone +pzzForecastDays = 3 # number of data points to return, default is 3 ``` ### Module Settings @@ -395,82 +471,6 @@ There is no direct support for MQTT in the code, however, reports from Discord a ~~There also seems to be a quicker way to enable MQTT by having your bot node with the enabled [serial](https://meshtastic.org/docs/configuration/module/serial/) module with echo enabled and MQTT uplink and downlink. These two~~ -## Full list of commands for the bot - -### Networking -| Command | Description | βœ… Works Off-Grid | -|---------|-------------|- -| `ping`, `ack` | Return data for signal. Example: `ping 15 #DrivingI5` (activates auto-ping every 20 seconds for count 15) | βœ… | -| `cmd` | Returns the list of commands (the help message) | βœ… | -| `history` | Returns the last commands run by user(s) | βœ… | -| `lheard` | Returns the last 5 heard nodes with SNR. Can also use `sitrep` | βœ… | -| `motd` | Displays the message of the day or sets it. Example: `motd $New Message Of the day` | βœ… | -| `sysinfo` | Returns the bot node telemetry info | βœ… | -| `test` | used to test the limits of data transfer `test 4` sends data to the maxBuffer limit (default 220) | βœ… | -| `whereami` | Returns the address of the sender's location if known | -| `whoami` | Returns details of the node asking, also returned when position exchanged πŸ“ | βœ… | -| `whois` | Returns details known about node, more data with bbsadmin node | βœ… | - -### Radio Propagation & Weather Forcasting -| Command | Description | | -|---------|-------------|------------------- -| `ea` and `ealert` | Return FEMA iPAWS/EAS alerts in USA or DE Headline or expanded details for USA | | -| `hfcond` | Returns a table of HF solar conditions | | -| `rlist` | Returns a table of nearby repeaters from RepeaterBook | | -| `riverflow` | Return information from NOAA for river flow info. Example: `riverflow modules/settings.py`| | -| `solar` | Gives an idea of the x-ray flux | | -| `sun` and `moon` | Return info on rise and set local time | βœ… | -| `tide` | Returns the local tides (NOAA data source) | | -| `valert` | Returns USGS Volcano Data | | -| `wx` and `wxc` | Return local weather forecast (wxc is metric value), NOAA or Open Meteo for weather forecasting | | -| `wxa` and `wxalert` | Return NOAA alerts. Short title or expanded details | | -| `mwx` | Return the NOAA Coastal Marine Forcast data | | - -### Bulletin Board & Mail -| Command | Description | | -|---------|-------------|- -| `bbshelp` | Returns the following help message | βœ… | -| `bbslist` | Lists the messages by ID and subject | βœ… | -| `bbsread` | Reads a message. Example: `bbsread #1` | βœ… | -| `bbspost` | Posts a message to the public board or sends a DM(Mail) Examples: `bbspost $subject #message`, `bbspost @nodeNumber #message`, `bbspost @nodeShortName #message` | βœ… | -| `bbsdelete` | Deletes a message. Example: `bbsdelete #4` | βœ… | -| `bbsinfo` | Provides stats on BBS delivery and messages (sysop) | βœ… | -| `bbslink` | Links Bulletin Messages between BBS Systems | βœ… | -| `email:` | Sends email to address on file for the node or `email: bob@test.net # hello from mesh` | | -| `sms:` | Send sms-email to multiple address on file | | -| `setemail`| Sets the email for easy communciations | | -| `setsms` | Adds the SMS-Email for quick communications | | -| `clearsms` | Clears all SMS-Emails on file for node | | - -### Data Lookup -| Command | Description | | -|---------|-------------|- -| `askai` and `ask:` | Ask Ollama LLM AI for a response. Example: `askai what temp do I cook chicken` | βœ… | -| `messages` | Replays the last messages heard, like Store and Forward | βœ… | -| `readnews` | returns the contents of a file (news.txt, by default) via the chunker on air | βœ… | -| `satpass` | returns the pass info from API for defined NORAD ID in config or Example: `satpass 25544,33591`| | -| `wiki:` | Searches Wikipedia and returns the first few sentences of the first result if a match. Example: `wiki: lora radio` | - -### CheckList -| Command | Description | | -|---------|-------------|- -| `checkin` | Check in the node to the checklist database, you can add a note like `checkin ICO` or `checkin radio4` | βœ… | -| `checkout` | Checkout the node in the checklist database, checkout all from node | βœ… | -| `checklist` | Display the checklist database, with note | βœ… | - -### Games (via DM) -| Command | Description | | -|---------|-------------|- -| `blackjack` | Plays Blackjack (Casino 21) | βœ… | -| `dopewars` | Plays the classic drug trader game | βœ… | -| `golfsim` | Plays a 9-hole Golf Simulator | βœ… | -| `hamtest` | FCC/ARRL Quiz `hamtest general` or `hamtest extra` and `score` | βœ… | -| `hangman` | Plays the classic word guess game | βœ… | -| `joke` | Tells a joke | βœ… | -| `lemonstand` | Plays the classic Lemonade Stand finance game | βœ… | -| `mastermind` | Plays the classic code-breaking game | βœ… | -| `videopoker` | Plays basic 5-card hold Video Poker | βœ… | - # Recognition I used ideas and snippets from other responder bots and want to call them out! diff --git a/config.template b/config.template index 9fefad7..1b6dace 100644 --- a/config.template +++ b/config.template @@ -150,7 +150,7 @@ NOAAalertCount = 2 # use Open-Meteo API for weather data not NOAA useful for non US locations UseMeteoWxAPI = False -# NOAA Coastal Waters Forecasts Enable NOAA Coastal Waters Forecasts (PZZ) +# NOAA Coastal Data Enable NOAA Coastal Waters Forecasts (PZZ) and tide cmd pzzEnabled = False # My Forecast Zone ID, https://www.weather.gov/marine select location and then look at the 'Forecast-by-Zone Map' and select PZZ zone pzzZoneID = 132 diff --git a/modules/system.py b/modules/system.py index 3e58ca7..963d73b 100644 --- a/modules/system.py +++ b/modules/system.py @@ -71,11 +71,17 @@ if enableCmdHistory: trap_list = trap_list + ("history",) #help_message = help_message + ", history" +# repeater list Configuration +if repeater_enabled: + from modules.locationdata import * # from the spudgunman/meshing-around repo + trap_list = trap_list + ("rlist", ) + help_message = help_message + ", rlist" + # Location Configuration if location_enabled: from modules.locationdata import * # from the spudgunman/meshing-around repo - trap_list = trap_list + trap_list_location # items tide, whereami, wxc, wx - help_message = help_message + ", whereami, wx, wxc, rlist" + trap_list = trap_list + trap_list_location # items tide, whereami, wx + help_message = help_message + ", whereami, wx" if enableGBalerts and not enableDEalerts: from modules.globalalert import * # from the spudgunman/meshing-around repo logger.warning(f"System: GB Alerts not functional at this time need to find a source API") @@ -87,23 +93,25 @@ if location_enabled: # Open-Meteo Configuration for worldwide weather if use_meteo_wxApi: + trap_list = trap_list + ("wxc",) + help_message = help_message + ", wxc" from modules.wx_meteo import * # from the spudgunman/meshing-around repo else: # NOAA only features - help_message = help_message + ", wxa, tide" + help_message = help_message + ", wxa" # NOAA alerts needs location module if wxAlertBroadcastEnabled or emergencyAlertBrodcastEnabled or volcanoAlertBroadcastEnabled: from modules.locationdata import * # from the spudgunman/meshing-around repo # limited subset, this should be done better but eh.. - trap_list = trap_list + ("wx", "wxc", "wxa", "wxalert", "ea", "ealert", "valert") + trap_list = trap_list + ("wx", "wxa", "wxalert", "ea", "ealert", "valert") help_message = help_message + ", wxalert, ealert, valert" # NOAA Coastal Waters Forecasts PZZ if pzzEnabled: from modules.locationdata import * # from the spudgunman/meshing-around repo - trap_list = trap_list + ("mwx",) - help_message = help_message + ", mwx" + trap_list = trap_list + ("mwx", "tide",) + help_message = help_message + ", mwx, tide" # BBS Configuration if bbs_enabled: From f6e04a42a0cc245b108cce51378eefcb2d533f68 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Wed, 30 Jul 2025 10:11:34 -0700 Subject: [PATCH 27/32] Update system.py --- modules/system.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/modules/system.py b/modules/system.py index 963d73b..5fbe55b 100644 --- a/modules/system.py +++ b/modules/system.py @@ -70,12 +70,6 @@ else: if enableCmdHistory: trap_list = trap_list + ("history",) #help_message = help_message + ", history" - -# repeater list Configuration -if repeater_enabled: - from modules.locationdata import * # from the spudgunman/meshing-around repo - trap_list = trap_list + ("rlist", ) - help_message = help_message + ", rlist" # Location Configuration if location_enabled: From f3c6f77b237903b65da2c65f765055ef3854e0fd Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Wed, 30 Jul 2025 10:13:14 -0700 Subject: [PATCH 28/32] Update mesh_bot.py --- mesh_bot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesh_bot.py b/mesh_bot.py index 52947fd..c1e41ae 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -1402,6 +1402,8 @@ async def start_rx(): logger.debug("System: Location Telemetry Enabled using NOAA API") if dad_jokes_enabled: logger.debug("System: Dad Jokes Enabled!") + if pzzEnabled: + logger.debug("Coastal Forcast and Tide Enabled!") if games_enabled: logger.debug("System: Games Enabled!") if wikipedia_enabled: From 7395b96337582243d86ace29017a523e449d6bf6 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Wed, 30 Jul 2025 10:16:33 -0700 Subject: [PATCH 29/32] Update locationdata.py --- modules/locationdata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/locationdata.py b/modules/locationdata.py index b32ce68..ae5910c 100644 --- a/modules/locationdata.py +++ b/modules/locationdata.py @@ -9,7 +9,7 @@ import bs4 as bs # pip install beautifulsoup4 import xml.dom.minidom from modules.log import * -trap_list_location = ("whereami", "tide", "wx", "wxc", "wxa", "wxalert", "rlist", "ea", "ealert", "riverflow","valert") +trap_list_location = ("whereami", "wx", "wxa", "wxalert", "rlist", "ea", "ealert", "riverflow", "valert") def where_am_i(lat=0, lon=0, short=False, zip=False): whereIam = "" From ee1db5b7bece2e6c9312d568be42af30ff18c0cf Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sat, 2 Aug 2025 19:21:46 -0700 Subject: [PATCH 30/32] Update locationdata.py --- modules/locationdata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/locationdata.py b/modules/locationdata.py index ae5910c..c61805a 100644 --- a/modules/locationdata.py +++ b/modules/locationdata.py @@ -453,7 +453,7 @@ def getActiveWeatherAlertsDetailNOAA(lat=0, lon=0): alerts = alerts.split("\n***\n")[:numWxAlerts] if alerts == "" or alerts == ['']: - return ERROR_FETCHING_DATA + return NO_ALERTS # trim off last newline if alerts[-1] == "\n": From b5bd1008c29049016c609210eedf016d4e8ea666 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 3 Aug 2025 17:43:10 -0700 Subject: [PATCH 31/32] HowHigh? divideBy3 https://github.com/SpudGunMan/meshing-around/discussions/170 --- modules/system.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/system.py b/modules/system.py index 5fbe55b..afe5404 100644 --- a/modules/system.py +++ b/modules/system.py @@ -970,7 +970,8 @@ def consumeMetadata(packet, rxNode=0): # if altitude is over 2000 send a log and message for high-flying nodes and not in highfly_ignoreList if position_data.get('altitude', 0) > highfly_altitude and highfly_enabled and str(nodeID) not in highfly_ignoreList: logger.info(f"System: High Altitude {position_data['altitude']}m on Device: {rxNode} NodeID: {nodeID}") - send_message(f"High Altitude {position_data['altitude']}m on Device:{rxNode} Node:{get_name_from_number(nodeID,'short',rxNode)}", highfly_channel, 0, rxNode) + altFeet = round(position_data['altitude'] * 3.28084, 2) + send_message(f"High Altitude {altFeet}ft ({position_data['altitude']}m) on Device:{rxNode} Node:{get_name_from_number(nodeID,'short',rxNode)}", highfly_channel, 0, rxNode) time.sleep(responseDelay) # Keep the positionMetadata dictionary at a maximum size of 20 From 2fc928139472461df1818ac123980dc69048f3ea Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Mon, 4 Aug 2025 18:54:46 -0700 Subject: [PATCH 32/32] Update system.py --- modules/system.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/system.py b/modules/system.py index afe5404..598985c 100644 --- a/modules/system.py +++ b/modules/system.py @@ -74,8 +74,8 @@ if enableCmdHistory: # Location Configuration if location_enabled: from modules.locationdata import * # from the spudgunman/meshing-around repo - trap_list = trap_list + trap_list_location # items tide, whereami, wx - help_message = help_message + ", whereami, wx" + trap_list = trap_list + trap_list_location + ("tide",) + help_message = help_message + ", whereami, wx, tide" if enableGBalerts and not enableDEalerts: from modules.globalalert import * # from the spudgunman/meshing-around repo logger.warning(f"System: GB Alerts not functional at this time need to find a source API") @@ -104,8 +104,8 @@ if wxAlertBroadcastEnabled or emergencyAlertBrodcastEnabled or volcanoAlertBroad # NOAA Coastal Waters Forecasts PZZ if pzzEnabled: from modules.locationdata import * # from the spudgunman/meshing-around repo - trap_list = trap_list + ("mwx", "tide",) - help_message = help_message + ", mwx, tide" + trap_list = trap_list + ("mwx",) + help_message = help_message + ", mwx" # BBS Configuration if bbs_enabled: