Compare commits

...

30 Commits

Author SHA1 Message Date
SpudGunMan
410d32947c Update system.py 2025-07-21 20:13:07 -07:00
SpudGunMan
748652ac62 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
2025-07-21 20:10:59 -07:00
SpudGunMan
d715cb6b4d Update system.py 2025-07-21 04:33:04 -07:00
SpudGunMan
1895a365ae 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
2025-07-20 05:41:50 -07:00
SpudGunMan
cc58a38165 Update system.py 2025-07-18 21:24:55 -07:00
SpudGunMan
a8ccb05d56 Update system.py
bug of undefined for interface retry
2025-07-16 08:57:39 -07:00
SpudGunMan
a90a533a30 USGS Alerts
documented
2025-07-15 21:20:37 -07:00
SpudGunMan
57a4e5d68c Update README.md 2025-07-15 21:12:23 -07:00
SpudGunMan
7c99b684ad riverflow
never got documented well
2025-07-15 21:11:48 -07:00
SpudGunMan
b957c89d70 Update README.md 2025-07-15 20:32:51 -07:00
SpudGunMan
9b986dd57a Update locationdata.py
allow FIPS only
2025-07-15 20:30:43 -07:00
SpudGunMan
9e348332e5 SAME code back in iPAWS
the state only FIPS codes are too wide
2025-07-15 19:09:27 -07:00
SpudGunMan
0cfe759ef6 Update mesh_bot.py 2025-07-15 15:34:32 -07:00
SpudGunMan
e95902ef98 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>
2025-07-15 15:22:53 -07:00
SpudGunMan
c7df4d88d1 Update config.template
Co-Authored-By: Russell Schmidt <836646+rfschmid@users.noreply.github.com>
2025-07-15 14:28:25 -07:00
SpudGunMan
6d01c5a986 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>
2025-07-15 11:14:29 -07:00
SpudGunMan
3f882dcfcd 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>
2025-07-15 09:41:34 -07:00
SpudGunMan
b146fd6f64 Revert "enhance sysinfo"
This reverts commit 8709e5aed5.
2025-07-14 22:55:41 -07:00
SpudGunMan
8709e5aed5 enhance sysinfo
ChUtil/Node value
2025-07-14 22:13:49 -07:00
SpudGunMan
caf8a2708b Update log.py
fix time display
2025-07-14 22:04:22 -07:00
SpudGunMan
9b4200c198 Update config.template
adjustments for 2.7.2 firmware might change again
2025-07-14 21:54:30 -07:00
SpudGunMan
0a260b28b6 Update llm.py
last time?
2025-06-26 19:30:33 -07:00
SpudGunMan
3f5c6f2e9a Update llm.py 2025-06-26 19:03:13 -07:00
SpudGunMan
8a4f7a904a Update llm.py 2025-06-26 18:49:16 -07:00
SpudGunMan
0bc3d392cf fix Interface logic
a condition where TCP interfaces can fail leaving a none condition. this should resolve the errored interface better.
2025-06-25 07:30:46 -07:00
SpudGunMan
5eaef8b5b8 Update sysEnv.sh
enhance with git update check

Co-Authored-By: Johannes le Roux <dade@dade.co.za>
2025-06-25 07:25:04 -07:00
SpudGunMan
3a0007771d Update update.sh 2025-06-22 20:04:18 -07:00
SpudGunMan
67ba2b1fb5 Create update.sh 2025-06-22 19:59:31 -07:00
SpudGunMan
f2e7a9aa5c Update config.template 2025-06-18 12:15:44 -07:00
SpudGunMan
9d22270dde highfly_ignoreList
some nodes have bad altimeters
2025-06-18 12:14:14 -07:00
11 changed files with 238 additions and 134 deletions

View File

@@ -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,20 +214,21 @@ 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
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
enableDEalerts = False # Use DE Alert Broadcast Data see template for filters
myRegionalKeysDE = 110000000000,120510000000
```
#### NOAA EAS
@@ -243,6 +243,20 @@ ignoreEASenable = True # Ignore any headline that includes followig word list
ignoreEASwords = test,advisory
```
#### 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]
# 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
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.

View File

@@ -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
@@ -112,6 +115,8 @@ highFlyingAlert = True
highFlyingAlertAltitude = 2000
# Channel to send Alert when the high flying node is detected
highFlyingAlertChannel = 2
# list of nodes numbers to ignore high flying alert ex: 2813308004,4258675309
highFlyingIgnoreList =
[bbs]
enabled = True
@@ -144,7 +149,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
# USGS Hydrology unique identifiers, LID or USGS ID https://waterdata.usgs.gov
riverListDefault =
# NOAA EAS Alert Broadcast
@@ -159,16 +164,15 @@ 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
# 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
# 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
@@ -292,10 +296,10 @@ hangman = True
hamtest = True
[messagingSettings]
# 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
# delay in seconds for response to avoid message collision /throttling
responseDelay = 2.2
# 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
# Request Acknowledgement of message OTA

View File

@@ -1107,16 +1107,15 @@ def onReceive(packet, interface):
if rxType == 'TCPInterface':
rxHost = interface.__dict__.get('hostname', 'unknown')
if hostname1 in rxHost and interface1_type == 'tcp': rxNode = 1
elif multiple_interface and hostname2 in rxHost and interface2_type == 'tcp': rxNode = 2
elif multiple_interface and hostname3 in rxHost and interface3_type == 'tcp': rxNode = 3
elif multiple_interface and hostname4 in rxHost and interface4_type == 'tcp': rxNode = 4
elif multiple_interface and hostname5 in rxHost and interface5_type == 'tcp': rxNode = 5
elif multiple_interface and hostname6 in rxHost and interface6_type == 'tcp': rxNode = 6
elif multiple_interface and hostname7 in rxHost and interface7_type == 'tcp': rxNode = 7
elif multiple_interface and hostname8 in rxHost and interface8_type == 'tcp': rxNode = 8
elif multiple_interface and hostname9 in rxHost and interface9_type == 'tcp': rxNode = 9
if rxHost and hostname1 in rxHost and interface1_type == 'tcp': rxNode = 1
elif multiple_interface and rxHost and hostname2 in rxHost and interface2_type == 'tcp': rxNode = 2
elif multiple_interface and rxHost and hostname3 in rxHost and interface3_type == 'tcp': rxNode = 3
elif multiple_interface and rxHost and hostname4 in rxHost and interface4_type == 'tcp': rxNode = 4
elif multiple_interface and rxHost and hostname5 in rxHost and interface5_type == 'tcp': rxNode = 5
elif multiple_interface and rxHost and hostname6 in rxHost and interface6_type == 'tcp': rxNode = 6
elif multiple_interface and rxHost and hostname7 in rxHost and interface7_type == 'tcp': rxNode = 7
elif multiple_interface and rxHost and hostname8 in rxHost and interface8_type == 'tcp': rxNode = 8
elif multiple_interface and rxHost and hostname9 in rxHost and interface9_type == 'tcp': rxNode = 9
if rxType == 'BLEInterface':
if interface1_type == 'ble': rxNode = 1
elif multiple_interface and interface2_type == 'ble': rxNode = 2
@@ -1225,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
@@ -1272,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):
@@ -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:
@@ -1548,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

View File

@@ -146,6 +146,12 @@ def llm_query(input, nodeID=0, location_name=None):
googleResults = []
if not location_name:
location_name = "no location provided "
# remove askai: and ask: from the input
for trap in trap_list_llm:
if input.lower().startswith(trap):
input = input[len(trap):].strip()
break
# add the naughty list here to stop the function before we continue
# add a list of allowed nodes only to use the function

View File

@@ -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():
@@ -515,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"
@@ -532,32 +549,31 @@ 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):
# check if the alert is for the SAME location, if wanted keep alert
if (sameVal in mySAMEList) or (geocode_value in mySAMEList) or 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: Ignoring FEMA Alert: {headline} containing {word} at {areaDesc}")
logger.debug(f"System: Filtering FEMA Alert by WORD: {headline} containing {word} at {areaDesc}")
ignore_alert = True
break
if ignore_alert:
continue
if ignore_alert:
continue
# add to alerts list
# add to alert list
alerts.append({
'alertType': alertType,
'alertCode': alertCode,
@@ -567,10 +583,10 @@ def getIpawsAlert(lat=0, lon=0, shortAlerts = False):
'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}")
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:
for alertItem in alerts[:numWxAlerts]:
@@ -683,5 +699,3 @@ def get_volcano_usgs(lat=0, lon=0):
# return the alerts
alerts = abbreviate_noaa(alerts)
return alerts

View File

@@ -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
# 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"

View File

@@ -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
@@ -223,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
@@ -239,6 +239,7 @@ try:
highfly_enabled = config['sentry'].getboolean('highFlyingAlert', True) # default True
highfly_altitude = config['sentry'].getint('highFlyingAlertAltitude', 2000) # default 2000 meters
highfly_channel = config['sentry'].getint('highFlyingAlertChannel', 2) # default 2
highfly_ignoreList = config['sentry'].get('highFlyingIgnoreList', '').split(',') # default empty
# location
location_enabled = config['location'].getboolean('enabled', True)
@@ -258,12 +259,12 @@ 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
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

View File

@@ -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:
@@ -816,40 +817,9 @@ 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
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}")
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
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)
# Handle disconnection of the interface
logger.warning(f"System: Abrupt Disconnection of Interface detected")
interface.close()
# Telemetry Functions
telemetryData = {}
@@ -979,8 +949,8 @@ def consumeMetadata(packet, rxNode=0):
for key in keys:
positionMetadata[nodeID][key] = position_data.get(key, 0)
# if altitude is over 2000 send a log and message for high-flying nodes
if position_data.get('altitude', 0) > highfly_altitude and highfly_enabled:
# 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)
time.sleep(responseDelay)
@@ -1128,21 +1098,26 @@ 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}']
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()
@@ -1152,13 +1127,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
@@ -1248,3 +1225,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)

View File

@@ -217,15 +217,15 @@ def onReceive(packet, interface):
if rxType == 'TCPInterface':
rxHost = interface.__dict__.get('hostname', 'unknown')
if hostname1 in rxHost and interface1_type == 'tcp': rxNode = 1
elif multiple_interface and hostname2 in rxHost and interface2_type == 'tcp': rxNode = 2
elif multiple_interface and hostname3 in rxHost and interface3_type == 'tcp': rxNode = 3
elif multiple_interface and hostname4 in rxHost and interface4_type == 'tcp': rxNode = 4
elif multiple_interface and hostname5 in rxHost and interface5_type == 'tcp': rxNode = 5
elif multiple_interface and hostname6 in rxHost and interface6_type == 'tcp': rxNode = 6
elif multiple_interface and hostname7 in rxHost and interface7_type == 'tcp': rxNode = 7
elif multiple_interface and hostname8 in rxHost and interface8_type == 'tcp': rxNode = 8
elif multiple_interface and hostname9 in rxHost and interface9_type == 'tcp': rxNode = 9
if rxHost and hostname1 in rxHost and interface1_type == 'tcp': rxNode = 1
elif multiple_interface and rxHost and hostname2 in rxHost and interface2_type == 'tcp': rxNode = 2
elif multiple_interface and rxHost and hostname3 in rxHost and interface3_type == 'tcp': rxNode = 3
elif multiple_interface and rxHost and hostname4 in rxHost and interface4_type == 'tcp': rxNode = 4
elif multiple_interface and rxHost and hostname5 in rxHost and interface5_type == 'tcp': rxNode = 5
elif multiple_interface and rxHost and hostname6 in rxHost and interface6_type == 'tcp': rxNode = 6
elif multiple_interface and rxHost and hostname7 in rxHost and interface7_type == 'tcp': rxNode = 7
elif multiple_interface and rxHost and hostname8 in rxHost and interface8_type == 'tcp': rxNode = 8
elif multiple_interface and rxHost and hostname9 in rxHost and interface9_type == 'tcp': rxNode = 9
if rxType == 'BLEInterface':
if interface1_type == 'ble': rxNode = 1
@@ -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):
@@ -443,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

View File

@@ -24,4 +24,21 @@ else
fi
# print telemetry data rounded to 2 decimal places
printf "Disk:%s RAM:%.2f%% CPU:%.2f%% CPU-T:%.2f°C (%.2f°F)\n" "$free_space" "$ram_usage" "$cpu_usage" "$temp" "$tempf"
printf "Disk:%s RAM:%.2f%% CPU:%.2f%% CPU-T:%.2f°C (%.2f°F)\n" "$free_space" "$ram_usage" "$cpu_usage" "$temp" "$tempf"
# attempt check for updates
if command -v git &> /dev/null
then
if [ -d ../.git ]; then
# check for updates
git fetch --quiet
local_branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$local_branch" != "HEAD" ] && git show-ref --verify --quiet "refs/remotes/origin/$local_branch"; then
local_commit=$(git rev-parse "$local_branch")
remote_commit=$(git rev-parse "origin/$local_branch")
if [ "$local_commit" != "$remote_commit" ]; then
echo "Bot Update Available!"
fi
fi
fi
fi

46
update.sh Normal file
View File

@@ -0,0 +1,46 @@
#!/bin/bash
# MeshBot Update Script
# Usage: bash update.sh or ./update.sh after making it executable with chmod +x update.sh
# Check if the mesh_bot.service or pong_bot.service
if systemctl is-active --quiet mesh_bot.service; then
echo "Stopping mesh_bot.service..."
systemctl stop mesh_bot.service
fi
if systemctl is-active --quiet pong_bot.service; then
echo "Stopping pong_bot.service..."
systemctl stop pong_bot.service
fi
if systemctl is-active --quiet mesh_bot_reporting.service; then
echo "Stopping mesh_bot_reporting.service..."
systemctl stop mesh_bot_reporting.service
fi
if systemctl is-active --quiet mesh_bot_w3.service; then
echo "Stopping mesh_bot_w3.service..."
systemctl stop mesh_bot_w3.service
fi
# Update the local repository
echo "Updating local repository..."
#git fetch --all
#git reset --hard origin/main # Replace 'main' with your branch name if different
git pull origin main --rebase # Fetch and rebase to keep local changes if any
echo "Local repository updated."
# Install or update dependencies
echo "Installing or updating dependencies..."
pip install -r requirements.txt --upgrade
echo "Dependencies installed or updated."
# Restart the services
echo "Restarting services..."
systemctl start mesh_bot.service
systemctl start pong_bot.service
systemctl start mesh_bot_reporting.service
systemctl start mesh_bot_w3.service
echo "Services restarted."
# Print completion message
echo "Update completed successfully?"
exit 0
# End of script