mirror of
https://github.com/SpudGunMan/meshing-around.git
synced 2026-03-28 17:32:36 +01:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
410d32947c | ||
|
|
748652ac62 | ||
|
|
d715cb6b4d | ||
|
|
1895a365ae | ||
|
|
cc58a38165 | ||
|
|
a8ccb05d56 | ||
|
|
a90a533a30 | ||
|
|
57a4e5d68c | ||
|
|
7c99b684ad | ||
|
|
b957c89d70 | ||
|
|
9b986dd57a | ||
|
|
9e348332e5 | ||
|
|
0cfe759ef6 | ||
|
|
e95902ef98 | ||
|
|
c7df4d88d1 | ||
|
|
6d01c5a986 | ||
|
|
3f882dcfcd | ||
|
|
b146fd6f64 | ||
|
|
8709e5aed5 | ||
|
|
caf8a2708b | ||
|
|
9b4200c198 | ||
|
|
0a260b28b6 | ||
|
|
3f5c6f2e9a | ||
|
|
8a4f7a904a | ||
|
|
0bc3d392cf | ||
|
|
5eaef8b5b8 | ||
|
|
3a0007771d | ||
|
|
67ba2b1fb5 | ||
|
|
f2e7a9aa5c | ||
|
|
9d22270dde |
26
README.md
26
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,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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
42
mesh_bot.py
42
mesh_bot.py
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
34
pong_bot.py
34
pong_bot.py
@@ -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
|
||||
|
||||
@@ -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
46
update.sh
Normal 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
|
||||
Reference in New Issue
Block a user