Compare commits

...

9 Commits

Author SHA1 Message Date
SpudGunMan ff6292160f Update mesh_bot.py 2024-08-28 12:43:27 -07:00
SpudGunMan 52dcb7972f Update mesh_bot.py 2024-08-28 12:28:55 -07:00
SpudGunMan 10e2b0ee59 Update system.py 2024-08-27 20:41:35 -07:00
SpudGunMan 473eccbdea fix BLE 2024-08-27 20:31:00 -07:00
SpudGunMan f6b2e0a506 Update README.md 2024-08-27 19:27:07 -07:00
SpudGunMan 22e16db1f2 typos 2024-08-27 18:10:29 -07:00
SpudGunMan 2c71ca9b8a Update README.md 2024-08-27 18:07:11 -07:00
SpudGunMan 023189bca9 Update README.md 2024-08-27 17:19:18 -07:00
SpudGunMan 8447985b98 Update mesh_bot.py 2024-08-27 17:19:14 -07:00
6 changed files with 82 additions and 26 deletions
+20 -8
View File
@@ -6,7 +6,7 @@ Random Mesh Scripts for Network Testing and BBS Activities for Use with [Meshtas
## mesh_bot.sh
The feature-rich bot requires the internet for full functionality. These responder bots will trap keywords like ping and respond to a DM (direct message) with pong! The script will also monitor the group channels for keywords to trap. You can also `Ping @Data to Echo` as an example.
Along with network testing, this bot has a lot of other fun features, like simple mail messaging you can leave for another device, and when that device is seen, it can send the mail as a DM.
Along with network testing, this bot has a lot of other fun features, like simple mail messaging you can leave for another device, and when that device is seen, it can send the mail as a DM. Or a scheduler to send weather or a reminder weekly for the VHF net.
The bot is also capable of using dual radio/nodes, so you can monitor two networks at the same time and send messages to nodes using the same `bbspost @nodeNumber #message` or `bbspost @nodeShportName #message` function. There is a small message board to fit in the constraints of Meshtastic for posting bulletin messages with `bbspost $subject #message`.
@@ -33,7 +33,7 @@ Full list of commands for the bot.
- Other functions
- `whereami` returns the address of location of sender if known
- `tide` returns the local tides, NOAA data source
- `wx` and `wxc` returns local weather forecast, (wxc is metric value), NOAA or Open Meteo for weather forcasting.
- `wx` and `wxc` returns 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
- `joke` tells a joke
- `messages` Replay the last messages heard, like Store and Forward
@@ -57,7 +57,7 @@ Optionally:
- `launch.sh` will activate and launch the app in the venv if built.
### Configurations
Copy the [config.template](config.template) to `config.ini` and set the appropriate interface for your method (serial/ble/tcp). While BLE and TCP will work, they are not as reliable as serial connections. There is a watchdog to reconnect tcp if possible.
Copy the [config.template](config.template) to `config.ini` and set the appropriate interface for your method (serial/ble/tcp). While BLE and TCP will work, they are not as reliable as serial connections. There is a watchdog to reconnect tcp if possible. To get BLE mac `meshtastic --ble-scan` **NOTE** I have only tested with a single BLE device and the code is written to only have one interface be a BLE port
```
#config.ini
@@ -84,7 +84,7 @@ Setting the default channel is the channel that won't be spammed by the bot. It'
respond_by_dm_only = True
defaultChannel = 0
```
The weather forcasting defaults to NOAA but for outside the USA you can set UseMeteoWxAPI `True` to use a world weather API. The lat and lon are for defaults when a node has no location data to use.
The weather forecasting defaults to NOAA but for outside the USA you can set UseMeteoWxAPI `True` to use a world weather API. The lat and lon are for defaults when a node has no location data to use.
```
[location]
enabled = True
@@ -102,7 +102,7 @@ enabled = False
DadJokes = False
StoreForward = False
```
Sentry Bot detects anyone comeing close to the bot-node
Sentry Bot detects anyone coming close to the bot-node
```
# detect anyone close to the bot
SentryEnabled = True
@@ -131,8 +131,8 @@ A module allowing a Hamlib compatible radio to connect to the bot, when function
[radioMon]
enabled = False
rigControlServerAddress = localhost:4532
# channel to brodcast to can be 2,3
sigWatchBrodcastCh = 2
# channel to broadcast to can be 2,3
sigWatchBroadcastCh = 2
# minimum SNR as reported by radio via hamlib
signalDetectionThreshold = -10
# hold time for high SNR
@@ -142,17 +142,29 @@ signalCooldown = 5
signalCycleLimit = 5
```
Logging messages to disk or Syslog to disk uses the python native logging fuction. Take a look at the [/modules/log.py](/modules/log.py) you can set the file logger for syslog to INFO for example to not log DEBUG messages to file log, or modify the stdOut level.
Logging messages to disk or Syslog to disk uses the python native logging function. Take a look at the [/modules/log.py](/modules/log.py) you can set the file logger for syslog to INFO for example to not log DEBUG messages to file log, or modify the stdOut level.
```
[general]
# logging to file of the non Bot messages
LogMessagesToFile = True
# Logging of system messages to file
SyslogToFile = True
```
Example to log to disk only INFO and higher (ignore DEBUG)
```
*log.py
file_handler.setLevel(logging.INFO) # DEBUG used by default for system logs to disk example here shows INFO
```
The Scheduler is enabled in the [settings.py](modules/settings.py) by setting `scheduler_enabled = True` the actions and settings are via code only at this time. see [mesh_bot.py](mesh_bot.py) around line [425](https://github.com/SpudGunMan/meshing-around/blob/22983133ee4db3df34f66699f565e506de296197/mesh_bot.py#L425-L435) to edit schedule its most flexible to edit raw code right now. See https://schedule.readthedocs.io/en/stable/ for more.
```
# Send WX every Morning at 08:00 using handle_wxc function to channel 2 on device 1
#schedule.every().day.at("08:00").do(lambda: send_message(handle_wxc(0, 1, 'wx'), 2, 0, 1))
# Send a Net Starting Now Message Every Wednesday at 19:00 using send_message function to channel 2 on device 1
#schedule.every().wednesday.at("19:00").do(lambda: send_message("Net Starting Now", 2, 0, 1))
```
# requirements
Python 3.4 and likely higher is needed, developed on latest release.
+3 -3
View File
@@ -75,7 +75,7 @@ lon = -123.0
NOAAforecastDuration = 4
# number of weather alerts to display
NOAAalertCount = 2
# use Open-Meteo API for weather data not NOAA usefull for non US locations
# use Open-Meteo API for weather data not NOAA useful for non US locations
UseMeteoWxAPI = False
# Default to metric units rather than imperial
useMetric = False
@@ -93,8 +93,8 @@ repeater_channels =
# using Hamlib rig control will monitor and alert on channel use
enabled = False
rigControlServerAddress = localhost:4532
# brodcast to all nodes on the channel can alsp be = 2,3
sigWatchBrodcastCh = 2
# broadcast to all nodes on the channel can alsp be = 2,3
sigWatchBroadcastCh = 2
# minimum SNR as reported by radio via hamlib
signalDetectionThreshold = -10
# hold time for high SNR
+30 -4
View File
@@ -219,11 +219,19 @@ def onDisconnect(interface):
retry_int1 = True
elif interface2_enabled and hostname2 in rxHost and interface2_type == 'tcp':
retry_int2 = True
if rxType == 'BLEInterface':
logger.critical(f"System: Lost Connection to Device BLE")
if interface1_type == 'ble':
retry_int1 = True
elif interface2_enabled and interface2_type == 'ble':
retry_int2 = True
def onReceive(packet, interface):
# extract interface defailts from interface object
rxType = type(interface).__name__
rxNode = 0
#logger.debug(f"System: Packet Received on {rxType}")
# Debug print the interface object
#for item in interface.__dict__.items(): print (item)
@@ -241,6 +249,12 @@ def onReceive(packet, interface):
elif interface2_enabled and hostname2 in rxHost and interface2_type == 'tcp':
rxNode = 2
if rxType == 'BLEInterface':
if interface1_type == 'ble':
rxNode = 1
elif interface2_enabled and interface2_type == 'ble':
rxNode = 2
# Debug print the packet for debugging
#print(f"Packet Received\n {packet} \n END of packet \n")
message_from_id = 0
@@ -419,8 +433,8 @@ async def start_rx():
logger.debug(f"System: Respond by DM only")
if repeater_enabled and interface2_enabled:
logger.debug(f"System: Repeater Enabled for Channels: {repeater_channels}")
if radio_dectection_enabled:
logger.debug(f"System: Radio Detection Enabled using rigctld at {rigControlServerAddress} brodcasting to channels: {sigWatchBrodcastCh} for {get_freq_common_name(get_hamlib('f'))}")
if radio_detection_enabled:
logger.debug(f"System: Radio Detection Enabled using rigctld at {rigControlServerAddress} brodcasting to channels: {sigWatchBroadcastCh} for {get_freq_common_name(get_hamlib('f'))}")
if scheduler_enabled:
# Examples of using the scheduler, Times here are in 24hr format
# https://schedule.readthedocs.io/en/stable/
@@ -432,7 +446,19 @@ async def start_rx():
#schedule.every().day.at("08:00").do(lambda: send_message(handle_wxc(0, 1, 'wx'), 2, 0, 1))
# Send a Net Starting Now Message Every Wednesday at 19:00 using send_message function to channel 2 on device 1
#schedule.every().wednesday.at("19:00").do(lambda: send_message("Net Starting Now", 2, 0, 1)
#schedule.every().wednesday.at("19:00").do(lambda: send_message("Net Starting Now", 2, 0, 1))
# Send a Welcome Notice for group on the 15th and 25th of the month at 12:00 using send_message function to channel 2 on device 1
#schedule.every().day.at("12:00").do(lambda: send_message("Welcome to the group", 2, 0, 1)).day(15, 25)
# Send a joke every 6 hours using tell_joke function to channel 2 on device 1
#schedule.every(6).hours.do(lambda: send_message(tell_joke(), 2, 0, 1))
# Send the Welcome Message every other day at 08:00 using send_message function to channel 2 on device 1
#schedule.every(2).days.at("08:00").do(lambda: send_message(welcome_message, 2, 0, 1))
# Send the MOTD every day at 13:00 using send_message function to channel 2 on device 1
#schedule.every().day.at("13:00").do(lambda: send_message(MOTD, 2, 0, 1))
#
logger.debug("System: Starting the broadcast scheduler")
@@ -447,7 +473,7 @@ async def start_rx():
async def main():
meshRxTask = asyncio.create_task(start_rx())
watchdogTask = asyncio.create_task(watchdog())
if radio_dectection_enabled:
if radio_detection_enabled:
hamlibTask = asyncio.create_task(handleSignalWatcher())
await asyncio.wait([meshRxTask, watchdogTask, hamlibTask])
else:
+2 -2
View File
@@ -118,9 +118,9 @@ try:
repeater_enabled = config['repeater'].getboolean('enabled', False)
repeater_channels = config['repeater'].get('repeater_channels', '').split(',')
radio_dectection_enabled = config['radioMon'].getboolean('enabled', False)
radio_detection_enabled = config['radioMon'].getboolean('enabled', False)
rigControlServerAddress = config['radioMon'].get('rigControlServerAddress', 'localhost:4532') # default localhost:4532
sigWatchBrodcastCh = config['radioMon'].get('sigWatchBrodcastCh', '2').split(',') # default Channel 2
sigWatchBroadcastCh = config['radioMon'].get('sigWatchBroadcastCh', '2').split(',') # default Channel 2
signalDetectionThreshold = config['radioMon'].getint('signalDetectionThreshold', -10) # default -10 dBm
signalHoldTime = config['radioMon'].getint('signalHoldTime', 10) # default 10 seconds
signalCooldown = config['radioMon'].getint('signalCooldown', 5) # default 1 second
+12 -7
View File
@@ -81,8 +81,13 @@ if store_forward_enabled:
help_message = help_message + ", messages"
# Radio Monitor Configuration
if radio_dectection_enabled:
if radio_detection_enabled:
from modules.radio import * # from the spudgunman/meshing-around repo
# BLE dual interface prevention
if interface1_type == 'ble' and interface2_type == 'ble':
logger.critical(f"System: BLE Interface1 and Interface2 cannot both be BLE. Exiting")
exit()
# Interface1 Configuration
try:
@@ -523,7 +528,7 @@ async def BroadcastScheduler():
await asyncio.sleep(1)
async def handleSignalWatcher():
global lastHamLibAlert, antiSpam, sigWatchBrodcastCh
global lastHamLibAlert, antiSpam, sigWatchBroadcastCh
# monitor rigctld for signal strength and frequency
while True:
msg = await signalWatcher()
@@ -534,8 +539,8 @@ async def handleSignalWatcher():
if time.time() - lastHamLibAlert > 60:
lastHamLibAlert = time.time()
# if sigWatchBrodcastCh list contains multiple channels, broadcast to all
if type(sigWatchBrodcastCh) is list:
for ch in sigWatchBrodcastCh:
if type(sigWatchBroadcastCh) is list:
for ch in sigWatchBroadcastCh:
if antiSpam and ch != publicChannel:
send_message(msg, int(ch), 0, 1)
if interface2_enabled:
@@ -543,10 +548,10 @@ async def handleSignalWatcher():
else:
logger.error(f"System: antiSpam prevented Alert from Hamlib {msg}")
else:
if antiSpam and sigWatchBrodcastCh != publicChannel:
send_message(msg, int(sigWatchBrodcastCh), 0, 1)
if antiSpam and sigWatchBroadcastCh != publicChannel:
send_message(msg, int(sigWatchBroadcastCh), 0, 1)
if interface2_enabled:
send_message(msg, int(sigWatchBrodcastCh), 0, 2)
send_message(msg, int(sigWatchBroadcastCh), 0, 2)
else:
logger.error(f"System: antiSpam prevented Alert from Hamlib {msg}")
+15 -2
View File
@@ -104,6 +104,13 @@ def onDisconnect(interface):
elif interface2_enabled and hostname2 in rxHost and interface2_type == 'tcp':
retry_int2 = True
if rxType == 'BLEInterface':
logger.critical(f"System: Lost Connection to Device BLE")
if interface1_type == 'ble':
retry_int1 = True
elif interface2_enabled and interface2_type == 'ble':
retry_int2 = True
def onReceive(packet, interface):
# extract interface defailts from interface object
rxType = type(interface).__name__
@@ -125,6 +132,12 @@ def onReceive(packet, interface):
elif interface2_enabled and hostname2 in rxHost and interface2_type == 'tcp':
rxNode = 2
if rxType == 'BLEInterface':
if interface1_type == 'ble':
rxNode = 1
elif interface2_enabled and interface2_type == 'ble':
rxNode = 2
# Debug print the packet for debugging
#print(f"Packet Received\n {packet} \n END of packet \n")
message_from_id = 0
@@ -273,8 +286,8 @@ async def start_rx():
logger.debug(f"System: Respond by DM only")
if repeater_enabled and interface2_enabled:
logger.debug(f"System: Repeater Enabled for Channels: {repeater_channels}")
if radio_dectection_enabled:
logger.debug(f"System: Radio Detection Enabled using rigctld at {rigControlServerAddress} brodcasting to channels: {sigWatchBrodcastCh} for {get_freq_common_name(get_hamlib('f'))}")
if radio_detection_enabled:
logger.debug(f"System: Radio Detection Enabled using rigctld at {rigControlServerAddress} brodcasting to channels: {sigWatchBroadcastCh} for {get_freq_common_name(get_hamlib('f'))}")
# here we go loopty loo
while True: