Compare commits

...

56 Commits

Author SHA1 Message Date
SpudGunMan
7fa5928537 Update README.md 2025-01-02 20:27:50 -08:00
SpudGunMan
f12198b140 enhance 2025-01-02 20:23:04 -08:00
Kelly
0d44ffb635 Merge pull request #101 from joshbowyer/patch-1
Update install.sh enhance stability
2025-01-02 20:00:22 -08:00
joshbowyer
c11ebf1443 Update install.sh
changed if statements to handle user input better
2025-01-02 21:55:09 -06:00
SpudGunMan
b94a5ebd8d POSIX 2025-01-02 19:26:30 -08:00
SpudGunMan
3392d2d5a8 Update install.sh 2025-01-01 11:48:57 -08:00
SpudGunMan
1df3a7aaa2 enhance 2025-01-01 11:35:49 -08:00
SpudGunMan
9a11214208 fix alerting 2024-12-28 09:28:08 -08:00
SpudGunMan
0a4f101370 Update install.sh 2024-12-27 17:22:58 -08:00
SpudGunMan
5f3c32dc00 Update install.sh 2024-12-27 16:50:24 -08:00
SpudGunMan
74cb135c6c Update install.sh
enhance embedded
2024-12-27 16:26:00 -08:00
SpudGunMan
a20e520501 Update install.sh 2024-12-27 14:03:20 -08:00
SpudGunMan
23e0e4c6a0 Update install.sh 2024-12-27 14:03:03 -08:00
SpudGunMan
10918546d6 Update install.sh 2024-12-27 14:01:19 -08:00
SpudGunMan
cf16cc6606 Update install.sh 2024-12-27 13:58:31 -08:00
SpudGunMan
3b73b665d6 Update install.sh 2024-12-27 13:57:02 -08:00
SpudGunMan
993fd760af Update install.sh 2024-12-27 13:55:41 -08:00
SpudGunMan
a029334576 Update install.sh 2024-12-27 13:48:47 -08:00
SpudGunMan
eb8143f298 Update install.sh 2024-12-27 13:36:15 -08:00
SpudGunMan
c756b447ac Update install.sh 2024-12-27 10:28:01 -08:00
SpudGunMan
cef05e061c Update install.sh 2024-12-27 10:04:58 -08:00
SpudGunMan
c85d517b91 Update install.sh 2024-12-27 10:03:13 -08:00
SpudGunMan
170d1a6a45 Update install.sh 2024-12-27 09:52:01 -08:00
SpudGunMan
8d2313cfb1 Update install.sh 2024-12-27 09:49:54 -08:00
SpudGunMan
ed8636f5a5 Update config.template 2024-12-26 16:28:52 -08:00
SpudGunMan
b95d94f06f alertChange 2024-12-26 09:29:30 -08:00
SpudGunMan
f7cdf446bf Update system.py 2024-12-24 18:44:44 -08:00
SpudGunMan
28e8e2705a fix Keyerror 2024-12-24 18:43:11 -08:00
SpudGunMan
9bc6f6f661 Update system.py 2024-12-24 12:21:58 -08:00
SpudGunMan
2630310210 Update system.py 2024-12-24 11:29:54 -08:00
SpudGunMan
3fae42305c sysEnv enhance 2024-12-23 18:56:05 -08:00
SpudGunMan
9cc8dd7143 Update runShell.sh 2024-12-23 16:36:33 -08:00
SpudGunMan
7ffa9d5309 Update mesh_bot.py 2024-12-23 13:17:06 -08:00
SpudGunMan
30d2b996c0 Update filemon.py 2024-12-23 13:16:11 -08:00
SpudGunMan
49c098ef0b Update filemon.py 2024-12-23 13:15:53 -08:00
SpudGunMan
afa41c6ecd Update runShell.sh 2024-12-23 13:12:06 -08:00
SpudGunMan
8861179cb2 Update runShell.sh 2024-12-23 13:11:47 -08:00
SpudGunMan
f32ceb0383 Update runShell.sh 2024-12-23 13:11:09 -08:00
SpudGunMan
9a380964aa Update runShell.sh 2024-12-23 13:09:46 -08:00
SpudGunMan
180a8261ca enhance 2024-12-23 12:55:29 -08:00
SpudGunMan
0536657c8e Update config.template 2024-12-23 12:52:17 -08:00
Kelly
c5a2330dd1 Merge pull request #98 from SpudGunMan/lab
external bash script access
2024-12-23 12:24:54 -08:00
SpudGunMan
dc0b5be387 Update README.md 2024-12-23 12:22:23 -08:00
SpudGunMan
a1f43a5e94 Update runShell.sh 2024-12-23 12:19:40 -08:00
SpudGunMan
b05a817769 Update runShell.sh 2024-12-23 12:18:26 -08:00
SpudGunMan
f7187fdf27 Update runShell.sh 2024-12-23 12:16:05 -08:00
SpudGunMan
cca51d68dd Update locationdata.py 2024-12-23 12:10:03 -08:00
SpudGunMan
21804cc975 scriptingEnhancment 2024-12-23 12:08:28 -08:00
SpudGunMan
7a9ee27336 Update filemon.py 2024-12-23 02:55:57 -08:00
SpudGunMan
0c637226b2 Update config.template 2024-12-22 20:31:13 -08:00
SpudGunMan
555b14ddc0 enhance🐝 2024-12-22 02:55:10 -08:00
SpudGunMan
656c23c631 Update system.py 2024-12-22 01:16:09 -08:00
SpudGunMan
bb591257c9 Update README.md 2024-12-22 00:13:49 -08:00
SpudGunMan
364a5c5c67 🐝
the b can be for the movie or a bible or any other fun idea. be kind.
2024-12-22 00:11:35 -08:00
SpudGunMan
8cb05d38db Update system.py 2024-12-20 21:26:16 -08:00
SpudGunMan
f9fe13f322 Update system.py 2024-12-20 21:22:21 -08:00
12 changed files with 313 additions and 175 deletions

View File

@@ -80,7 +80,7 @@ The code is under active development, so make sure to pull the latest changes re
#### Docker Installation
If you prefer to use Docker, follow these steps:
1. Ensure your serial port is properly shared and the GPU is configured if using LLM in docker with [NVIDIA](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/docker-specialized.html).
1. Ensure your serial port is properly shared.
2. Build the Docker image:
```sh
cd meshing-around
@@ -139,7 +139,7 @@ ignoreDefaultChannel = False # ignoreDefaultChannel, the bot will ignore the def
```
### Location Settings
The weather forecasting defaults to NOAA, for locations outside the USA, you can set `UseMeteoWxAPI` to `True`, to use a global weather API. The `lat` and `lon` are default values when a node has no location data. It is also the default (or value when none found for user) for Sentry, all NOAA, repeater lookup, etc.
The weather forecasting defaults to NOAA, for locations outside the USA, you can set `UseMeteoWxAPI` to `True`, to use a global weather API. The `lat` and `lon` are default values when a node has no location data, as well as the default for all NOAA, repeater lookup. It is also the center of radius for Sentry.
```ini
[location]
@@ -151,7 +151,7 @@ riverListDefault = # NOAA Hydrology data, unique identifiers, LID or USGS ID
```
### Module Settings
Modules can be enabled or disabled as needed.
Modules can be enabled or disabled as needed. They are essentally larger functions of code which you may not want on your mesh or in memory space.
```ini
[bbs]
@@ -184,7 +184,7 @@ sentryIgnoreList = # list of ignored nodes numbers ex: 2813308004,4258675309
```
### E-Mail / SMS Settings
To enable connectivity with SMTP/IMAP.
To enable connectivity with SMTP allows messages from meshtastic into SMTP. The term SMS here is for connection via [carrier email](https://avtech.com/articles/138/list-of-email-to-sms-addresses/)
```ini
[smtp]
@@ -203,10 +203,8 @@ Traps the following ("emergency", "911", "112", "999", "police", "fire", "ambula
```ini
[emergencyHandler]
# enable or disable the emergency response handler
enabled = True
# channel to send a message to when the emergency handler is triggered
alert_channel = 2
enabled = True # enable or disable the emergency response handler
alert_channel = 2 # channel to send a message to when the emergency handler is triggered
alert_interface = 1
```
@@ -263,6 +261,7 @@ llmEnableHistory = True # enable history for the LLM model to use in responses a
llmContext_fromGoogle = True # enable context from google search results helps with responses accuracy
googleSearchResults = 3 # number of google search results to include in the context more results = more compute time
```
Note for LLM in docker with [NVIDIA](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/docker-specialized.html). Needed for the container with ollama running.
### Radio Monitoring
A module allowing a Hamlib compatible radio to connect to the bot. When functioning, it will message the configured channel with a message of in use. **Requires hamlib/rigctld to be running as a service.**
@@ -289,6 +288,7 @@ broadcastCh = 2,4
enable_read_news = False
news_file_path = news.txt
news_random_line = False # only return a single random line from the news file
enable_runShellCmd = False # enables running of bash commands runShell.sh demo for sysinfo
```
#### Offline EAS
@@ -316,6 +316,8 @@ rtl_fm -f 162425000 -s 22050 | multimon-ng -t raw -a EAS /dev/stdin | python eas
#### Newspaper on mesh
a newspaper could be built by external scripts. could use Ollama to compile text via news web pages and write news.txt
you can also enable the line by line (hint just search for the commented lines with a 🐝) to return a string from the [bee movie](https://courses.cs.washington.edu/courses/cse163/20wi/files/lectures/L04/bee-movie.txt) for example adding it alongside news.txt as bee.txt
### Scheduler
In the config.ini enable the module
```ini
@@ -334,7 +336,7 @@ schedule.every().wednesday.at("19:00").do(lambda: send_message("Net Starting Now
```
#### BBS Link
The scheduler also handles the BBL Link Brodcast message, this would be an esxample of a mesh-admin channel on 8 being used to pass BBS post traffic between two bots as the initator, one direction pull.
The scheduler also handles the BBS Link Brodcast message, this would be an esxample of a mesh-admin channel on 8 being used to pass BBS post traffic between two bots as the initator, one direction pull.
```python
# Send bbslink looking for peers every other day at 10:00 using send_message function to channel 8 on device 1
schedule.every(2).days.at("10:00").do(lambda: send_message("bbslink MeshBot looking for peers", 8, 0, 1))
@@ -345,7 +347,9 @@ bbslink_whitelist = # list of whitelisted nodes numbers ex: 2813308004,425867530
```
### MQTT Notes
There is no direct support for MQTT in the code, however, reports from Discord are that using [meshtasticd](https://meshtastic.org/docs/hardware/devices/linux-native-hardware/) with no radio and attaching the bot to the software node, which is MQTT-linked, allows routing.~~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~~ methods have been mentioned as allowing MQTT routing for the project. Tested working fully Firmware:2.5.15.79da236 with [mosquitto](https://meshtastic.org/docs/software/integrations/mqtt/mosquitto/).
There is no direct support for MQTT in the code, however, reports from Discord are that using [meshtasticd](https://meshtastic.org/docs/hardware/devices/linux-native-hardware/) with no radio and attaching the bot to the software node, which is MQTT-linked, allows routing. Tested working fully Firmware:2.5.15.79da236 with [mosquitto](https://meshtastic.org/docs/software/integrations/mqtt/mosquitto/).
~~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
@@ -353,28 +357,28 @@ There is no direct support for MQTT in the code, however, reports from Discord a
| Command | Description | ✅ Works Off-Grid |
|---------|-------------|-
| `ping`, `ack` | Return data for signal. Example: `ping 15 #DrivingI5` (activates auto-ping every 20 seconds for count 15) | ✅ |
| `test` | Returns like ping but also can be used to test the limits of data buffers `test 4` sends data to the maxBuffer limit (default 220) | ✅ |
| `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 | ✅ |
| `motd` | Displays the message of the day or sets it. Example: `motd $New Message Of the day` | ✅ |
| `lheard` | Returns the last 5 heard nodes with SNR. Can also use `sitrep` | ✅ |
| `history` | Returns the last commands run by user(s) | ✅ |
| `sysinfo` | Returns the bot node telemetry info | ✅ |
| `cmd` | Returns the list of commands (the help message) | ✅ |
### Radio Propagation & Weather Forcasting
| Command | Description | |
|---------|-------------|-------------------
| `sun` and `moon` | Return info on rise and set local time | ✅ |
| `solar` | Gives an idea of the x-ray flux | |
| `ea` and `ealert` | Return FEMA iPAWS/EAS alerts in USA or UK. Headline or expanded details for USA | |
| `hfcond` | Returns a table of HF solar conditions | |
| `tide` | Returns the local tides (NOAA data source) |
| `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) |
| `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 | |
| `ea` and `ealert` | Return FEMA iPAWS/EAS alerts in USA or UK. Headline or expanded details for USA | |
| `riverflow` | Return information from NOAA for river flow info. Example: `riverflow modules/settings.py`| |
### Bulletin Board & Mail
| Command | Description | |
@@ -395,22 +399,22 @@ There is no direct support for MQTT in the code, however, reports from Discord a
### Data Lookup
| Command | Description | |
|---------|-------------|-
| `wiki:` | Searches Wikipedia and returns the first few sentences of the first result if a match. Example: `wiki: lora radio` |
| `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` |
### Games (via DM)
| Command | Description | |
|---------|-------------|-
| `blackjack` | Plays Blackjack (Casino 21) | ✅ |
| `dopewars` | Plays the classic drug trader game | ✅ |
| `golfsim` | Plays a 9-hole Golf Simulator | ✅ |
| `joke` | Tells a joke | ✅ |
| `lemonstand` | Plays the classic Lemonade Stand finance game | ✅ |
| `dopewars` | Plays the classic drug trader game | ✅ |
| `blackjack` | Plays Blackjack (Casino 21) | ✅ |
| `videopoker` | Plays basic 5-card hold Video Poker | ✅ |
| `mastermind` | Plays the classic code-breaking game | ✅ |
| `golfsim` | Plays a 9-hole Golf Simulator | ✅ |
| `videopoker` | Plays basic 5-card hold Video Poker | ✅ |
# Recognition
@@ -436,6 +440,8 @@ I used ideas and snippets from other responder bots and want to call them out!
- **[https://github.com/A-c0rN](A-c0rN)**: Assistance with iPAWS and EAS
- **Mike O'Connell/skrrt**: For [eas_alert_parser](etc/eas_alert_parser.py) enhanced by **sheer.cold**
- **PiDiBi**: For looking at test functions and other suggestions like wxc, CPU use, and alerting ideas.
- **WH6GXZ nurse dude**: For bashing on installer
- **Josh**: For more bashing on installer!
- **Cisien, bitflip, **Woof**, **propstg**, **Josh** and Hailo1999**: For testing and feature ideas on Discord and GitHub.
- **Meshtastic Discord Community**: For tossing out ideas and testing code.
@@ -443,7 +449,7 @@ I used ideas and snippets from other responder bots and want to call them out!
- **Node Backup Management**: [Node Slurper](https://github.com/SpudGunMan/node-slurper)
### Requirements
Python 3.8? or later is needed (dev on latest). The following can be installed with `pip install -r requirements.txt` or using the [install.sh](install.sh) script for venv and automation:
Python 3.8? or later is needed (docker on 3.13). The following can be installed with `pip install -r requirements.txt` or using the [install.sh](install.sh) script for venv and automation:
```sh
pip install meshtastic

View File

@@ -9,7 +9,7 @@ type = serial
port = /dev/ttyACM0
# port = /dev/ttyUSB0
# port = COM1
# hostname = 192.168.0.1
# hostname = localhost
# mac = 00:11:22:33:44:55
# Additional interface for dual radio support
@@ -188,6 +188,8 @@ enable_read_news = False
news_file_path = news.txt
# only return a single random line from the news file
news_random_line = False
# enable the use of exernal shell commands
enable_runShellCmd = False
[smtp]
# enable or disable the SMTP module
@@ -235,7 +237,7 @@ splitDelay = 0.0
MESSAGE_CHUNK_SIZE = 160
# Request Acknowledgement of message OTA
wantAck = False
# Max lilmit Buffer for radio testing
# Max limit buffer for radio testing. 233 is hard limit 2.5+ firmware
maxBuffer = 220

View File

@@ -11,23 +11,37 @@ printf "\nThis script will try and install the Meshing Around Bot and its depend
printf "Installer works best in raspian/debian/ubuntu, if there is a problem, try running the installer again.\n"
printf "\nChecking for dependencies...\n"
# check if running on embedded
printf "\nAre You installing into an embedded system? (y/n)"
read embedded
if [ $embedded == "y" ]; then
printf "\nDetected embedded skipping dependency installation\n"
if [ $program_path != "/opt/meshing-around" ]; then
printf "\nIt is suggested to project path to /opt/meshing-around\n"
printf "Do you want to move the project to /opt/meshing-around? (y/n)"
read move
if [ $move == "y" ]; then
sudo mv $program_path /opt/meshing-around
cd /opt/meshing-around
printf "\nProject moved to /opt/meshing-around. re-run the installer\n"
exit 0
fi
# check if we are in /opt/meshing-around
if [ $program_path != "/opt/meshing-around" ]; then
printf "\nIt is suggested to project path to /opt/meshing-around\n"
printf "Do you want to move the project to /opt/meshing-around? (y/n)"
read move
if [[ $(echo "$move" | grep -iq "^y") ]]; then
sudo mv $program_path /opt/meshing-around
cd /opt/meshing-around
printf "\nProject moved to /opt/meshing-around. re-run the installer\n"
exit 0
fi
fi
# check write access to program path
if [[ ! -w ${program_path} ]]; then
printf "\nInstall path not writable, try running the installer with sudo\n"
exit 1
fi
# if hostname = femtofox, then we are on embedded
if [[ $(hostname) == "femtofox" ]]; then
printf "\nDetected femtofox embedded system\n"
embedded="y"
else
# check if running on embedded
printf "\nAre You installing into an embedded system like a luckfox? (y/n)"
read embedded
fi
if [[ $(echo "${embedded}" | grep -i "^y") ]]; then
printf "\nDetected embedded skipping dependency installation\n"
else
# Check and install dependencies
if ! command -v python3 &> /dev/null
@@ -67,7 +81,7 @@ cp etc/mesh_bot.tmp etc/mesh_bot.service
cp etc/mesh_bot_reporting.tmp etc/mesh_bot_reporting.service
# generate config file, check if it exists
if [ -f config.ini ]; then
if [[ -f config.ini ]]; then
printf "\nConfig file already exists, moving to backup config.old\n"
mv config.ini config.old
fi
@@ -76,13 +90,13 @@ cp config.template config.ini
printf "\nConfig files generated!\n"
# check if running on embedded
if [ $embedded == "y" ]; then
if [[ $(echo "${embedded}" | grep -iq "^y") ]]; then
printf "\nDetected embedded skipping venv\n"
else
printf "\nDo you want to install the bot in a python virtual environment? (y/n)"
printf "\nRecomended install is in a python virtual environment, do you want to use venv? (y/n)"
read venv
if [ $venv == "y" ]; then
if [[ $(echo "${venv}" | grep -i "^y") ]]; then
# set virtual environment
if ! python3 -m venv --help &> /dev/null; then
printf "Python3/venv error, please install python3-venv with your OS\n"
@@ -91,7 +105,7 @@ else
echo "The Following could be messy, or take some time on slower devices."
echo "Creating virtual environment..."
#check if python3 has venv module
if [ -f venv/bin/activate ]; then
if [[ -f venv/bin/activate ]]; then
printf "\nFound virtual environment for python\n"
python3 -m venv venv
source venv/bin/activate
@@ -103,7 +117,7 @@ else
python3 -m venv venv
# double check for python3-venv
if [ -f venv/bin/activate ]; then
if [[ -f venv/bin/activate ]]; then
printf "\nFound virtual environment for python\n"
source venv/bin/activate
else
@@ -119,15 +133,15 @@ else
replace="s|python3 pong_bot.py|/usr/bin/bash launch.sh pong|g"
sed -i "$replace" etc/pong_bot.service
# install dependencies
# install dependencies to venv
pip install -U -r requirements.txt
fi
else
printf "\nSkipping virtual environment...\n"
# install dependencies
# install dependencies to system
printf "Are you on Raspberry Pi(debian/ubuntu)?\nshould we add --break-system-packages to the pip install command? (y/n)"
read rpi
if [ $rpi == "y" ]; then
if [[ $(echo "${rpi}" | grep -i "^y") ]]; then
pip install -U -r requirements.txt --break-system-packages
else
pip install -U -r requirements.txt
@@ -137,6 +151,7 @@ fi
printf "\n\n"
echo "Which bot do you want to install as a service? Pong Mesh or None? (pong/mesh/n)"
echo "Pong bot is a simple bot for network testing, Mesh bot is a more complex bot more suited for meshing around"
read bot
# set the correct path in the service file
@@ -145,7 +160,32 @@ sed -i $replace etc/pong_bot.service
sed -i $replace etc/mesh_bot.service
sed -i $replace etc/mesh_bot_reporting.service
# set the correct user in the service file?
whoami=$(whoami)
#ask if we should add a user for the bot
if [[ $(echo "${embedded}" | grep -i "^y") ]]; then
printf "\nDo you want to add a local user (meshbot) no login, for the bot? (y/n)"
read meshbotservice
else
meshbotservice="n"
fi
if [[ $(echo "${meshbotservice}" | grep -i "^y") ]] || [[ $(echo "${embedded}" | grep -i "^y") ]]; then
sudo useradd -M meshbot
sudo usermod -L meshbot
whoami="meshbot"
echo "Added user meshbot with no home directory"
sudo usermod -a -G dialout $whoami
sudo usermod -a -G tty $whoami
sudo usermod -a -G bluetooth $whoami
echo "Added meshbot to dialout, tty, and bluetooth groups"
sudo chown -R $whoami:$whoami $program_path/logs
sudo chown -R $whoami:$whoami $program_path/data
echo "Permissions set for meshbot on logs and data directories"
else
whoami=$(whoami)
fi
# set the correct user in the service file
replace="s|User=pi|User=$whoami|g"
sed -i $replace etc/pong_bot.service
sed -i $replace etc/mesh_bot.service
@@ -154,64 +194,60 @@ replace="s|Group=pi|Group=$whoami|g"
sed -i $replace etc/pong_bot.service
sed -i $replace etc/mesh_bot.service
sed -i $replace etc/mesh_bot_reporting.service
sudo systemctl daemon-reload
printf "\n service files updated\n"
if [ $bot == "pong" ]; then
echo "useradd -M meshbot"
echo "usermod -L meshbot"
echo "Added user meshbot with no home directory"
if [[ $(echo "${bot}" | grep -i "^p") ]]; then
# install service for pong bot
sudo cp etc/pong_bot.service /etc/systemd/system/
sudo systemctl enable pong_bot.service
sudo systemctl daemon-reload
echo "to start pong bot service: systemctl start pong_bot"
fi
if [ $bot == "mesh" ]; then
echo "useradd -M meshbot"
echo "usermod -L meshbot"
echo "Added user meshbot with no home directory"
if [[ $(echo "${bot}" | grep -i "^m") ]]; then
# install service for mesh bot
sudo cp etc/mesh_bot.service /etc/systemd/system/
sudo systemctl enable mesh_bot.service
sudo systemctl daemon-reload
echo "to start mesh bot service: systemctl start mesh_bot"
fi
# check if running on embedded
if [ $embedded == "n" ]; then
# check if running on embedded for final steps
if [[ $(echo "${embedded}" | grep -i "^n") ]]; then
# ask if emoji font should be installed for linux
printf "\nDo you want to install the emoji font for debian/ubuntu linux? (y/n)"
read emoji
if [ $emoji == "y" ]; then
if [[ $(echo "${emoji}" | grep -i "^y") ]]; then
sudo apt-get install -y fonts-noto-color-emoji
echo "Emoji font installed!, reboot to load the font"
fi
printf "\nOptionally if you want to install the multi gig LLM Ollama compnents we will execute the following commands\n"
printf "\ncurl -fsSL https://ollama.com/install.sh | sh\n"
printf "ollama pull gemma2:2b\n"
printf "Total download is multi GB, recomend pi5/8GB or better for this\n"
# ask if the user wants to install the LLM Ollama components
printf "\nDo you want to install the LLM Ollama components? (y/n)"
read ollama
if [ $ollama == "y" ]; then
if [[ $(echo "${ollama}" | grep -i "^y") ]]; then
curl -fsSL https://ollama.com/install.sh | sh
# ask if want to install gemma2:2b
printf "\n Ollama install done now we can install the Gemma2:2b components, multi GB download\n"
printf "\n Ollama install done now we can install the Gemma2:2b components\n"
echo "Do you want to install the Gemma2:2b components? (y/n)"
read gemma
if [ $gemma == "y" ]; then
if [[ $(echo "${gemma}" | grep -i "^y") ]]; then
ollama pull gemma2:2b
fi
fi
if [ $venv == "y" ]; then
printf "\nFor running in virtual, launch bot with './launch.sh mesh' in path $program_path\n"
if [[ $(echo "${venv}" | grep -i "^y") ]]; then
printf "\nFor running on venv, virtual launch bot with './launch.sh mesh' in path $program_path\n"
fi
printf "\nGood time to reboot? (y/n)"
read reboot
if [ $reboot == "y" ]; then
if [[ $(echo "${reboot}" | grep -i "^y") ]]; then
sudo reboot
fi
else
@@ -220,15 +256,25 @@ else
replace="s|type = serial|type = tcp|g"
sed -i "$replace" config.ini
# replace "# hostname = 192.168.0.1" with "hostname = localhost" in config.ini
replace="s|# hostname = 192.168.0.1|hostname = localhost|g"
replace="s|# hostname = localhost|hostname = localhost|g"
sed -i "$replace" config.ini
printf "\nConfig file updated for embedded\n"
# Set up the meshing around service
#sudo cp /opt/meshing-around/meshing-around.service /etc/systemd/system/meshing-around.service
#sudo systemctl enable meshing-around.service
printf "To install the meshing around service and keep notes, copy and paste the following commands:\n\n"
printf "sudo cp /opt/meshing-around/meshing-around.service /etc/systemd/system/meshing-around.service\n"
printf "sudo systemctl daemon-reload\n"
printf "sudo systemctl enable meshing-around.service\n"
printf "sudo systemctl start meshing-around.service\n"
printf "sudo systemctl status meshing-around.service\n\n"
printf "To see logs and stop the service:\n"
printf "sudo journalctl -u meshing-around.service\n"
printf "sudo systemctl stop meshing-around.service\n"
printf "sudo systemctl disable meshing-around.service\n"
fi
printf "\nInstallation complete!\n"
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

View File

@@ -5,7 +5,7 @@
import asyncio
import time # for sleep, get some when you can :)
import random
from pubsub import pub # pip install pubsub
from pubsub import pub # pip install pubsub, use launch.sh for venv
from modules.log import *
from modules.system import *
@@ -87,6 +87,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
"wxc": lambda: handle_wxc(message_from_id, deviceID, 'wxc'),
"📍": lambda: handle_whoami(message_from_id, deviceID, hop, snr, rssi, pkiStatus),
"🔔": lambda: handle_alertBell(message_from_id, deviceID, message),
"🐝": lambda: read_file("bee.txt", True),
# any value from system.py:trap_list_emergency will trigger the emergency function
"112": lambda: handle_emergency(message_from_id, deviceID, message),
"911": lambda: handle_emergency(message_from_id, deviceID, message),
@@ -768,7 +769,11 @@ def sysinfo(message, message_from_id, deviceID):
if "?" in message:
return "sysinfo command returns system information."
else:
return get_sysinfo(message_from_id, deviceID)
if enable_runShellCmd and file_monitor_enabled:
shellData = call_external_script(None, "sysEnv.sh").rstrip()
return get_sysinfo(message_from_id, deviceID) + "\n" + shellData
else:
return get_sysinfo(message_from_id, deviceID)
def handle_lheard(message, nodeid, deviceID, isDM):
if "?" in message and isDM:
@@ -1274,8 +1279,12 @@ async def start_rx():
logger.debug(f"System: Radio Detection Enabled using rigctld at {rigControlServerAddress} brodcasting to channels: {sigWatchBroadcastCh} for {get_freq_common_name(get_hamlib('f'))}")
if file_monitor_enabled:
logger.debug(f"System: File Monitor Enabled for {file_monitor_file_path}, broadcasting to channels: {file_monitor_broadcastCh}")
if read_news_enabled:
logger.debug(f"System: File Monitor News Reader Enabled for {news_file_path}")
if enable_runShellCmd:
logger.debug(f"System: Shell Command monitor enabled")
if read_news_enabled:
logger.debug(f"System: File Monitor News Reader Enabled for {news_file_path}")
if bee_enabled:
logger.debug(f"System: File Monitor Bee Monitor Enabled for bee.txt")
if wxAlertBroadcastEnabled:
logger.debug(f"System: Weather Alert Broadcast Enabled on channels {wxAlertBroadcastChannel}")
if emergencyAlertBrodcastEnabled:

View File

@@ -9,7 +9,12 @@ import os
trap_list_filemon = ("readnews",)
def read_file(file_monitor_file_path, random_line_only=False):
try:
if not os.path.exists(file_monitor_file_path):
logger.warning(f"FileMon: File not found: {file_monitor_file_path}")
if file_monitor_file_path == "bee.txt":
return "🐝buzz 💐buzz buzz🍯"
if random_line_only:
# read a random line from the file
with open(file_monitor_file_path, 'r') as f:
@@ -56,4 +61,24 @@ async def watch_file():
content = content.replace('\n', ' ').replace('\r', '').strip()
if content:
return content
await asyncio.sleep(1) # Check every
await asyncio.sleep(1) # Check every
def call_external_script(message, script="runShell.sh"):
try:
# Debugging: Print the current working directory and resolved script path
current_working_directory = os.getcwd()
script_path = os.path.join(current_working_directory, script)
if not os.path.exists(script_path):
# try the raw script name
script_path = script
if not os.path.exists(script_path):
logger.warning(f"FileMon: Script not found: {script_path}")
return "sorry I can't do that"
output = os.popen(f"bash {script_path} {message}").read()
return output
except Exception as e:
logger.warning(f"FileMon: Error calling external script: {e}")
return None

View File

@@ -542,9 +542,9 @@ 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:
# # 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:

View File

@@ -130,6 +130,7 @@ try:
whoami_enabled = config['general'].getboolean('whoami', True)
dad_jokes_enabled = config['general'].getboolean('DadJokes', False)
dad_jokes_emojiJokes = config['general'].getboolean('DadJokesEmoji', False)
bee_enabled = config['general'].getboolean('bee', False) # 🐝 off by default undocumented
solar_conditions_enabled = config['general'].getboolean('spaceWeather', True)
wikipedia_enabled = config['general'].getboolean('wikipedia', False)
llm_enabled = config['general'].getboolean('ollama', False) # https://ollama.com
@@ -221,6 +222,7 @@ try:
read_news_enabled = config['fileMon'].getboolean('enable_read_news', False) # default disabled
news_file_path = config['fileMon'].get('news_file_path', 'news.txt') # default news.txt
news_random_line_only = config['fileMon'].getboolean('news_random_line', False) # default False
enable_runShellCmd = config['fileMon'].getboolean('enable_runShellCmd', False) # default False
# games
game_hop_limit = config['messagingSettings'].getint('game_hop_limit', 5) # default 3 hops

View File

@@ -12,6 +12,7 @@ import io # for suppressing output on watchdog
from modules.log import *
# Global Variables
debugMetadata = False # packet debug for non text messages
trap_list = ("cmd","cmd?") # default trap list
help_message = "Bot CMD?:"
asyncLoop = asyncio.new_event_loop()
@@ -75,7 +76,7 @@ if location_enabled:
trap_list = trap_list + trap_list_location # items tide, whereami, wxc, wx
help_message = help_message + ", whereami, wx, wxc, rlist"
if enableGBalerts:
from modules.locationdata_eu import * # from the spudgunman/meshing-around repo
from modules.globalalert import * # from the spudgunman/meshing-around repo
trap_list = trap_list + trap_list_location_eu
#help_message = help_message + ", ukalert, ukwx, ukflood"
@@ -195,6 +196,9 @@ if file_monitor_enabled or read_news_enabled:
if read_news_enabled:
trap_list = trap_list + trap_list_filemon # items readnews
help_message = help_message + ", readnews"
# Bee Configuration uses file monitor module
if bee_enabled:
trap_list = trap_list + ("🐝",)
# clean up the help message
help_message = help_message.split(", ")
@@ -666,6 +670,9 @@ def handleMultiPing(nodeID=0, deviceID=1):
def handleAlertBroadcast(deviceID=1):
alertUk = NO_ALERTS
alertFema = NO_ALERTS
wxAlert = NO_ALERTS
# only allow API call every 20 minutes
# the watchdog will call this function 3 times, seeing possible throttling on the API
clock = datetime.now()
@@ -675,13 +682,15 @@ def handleAlertBroadcast(deviceID=1):
return False
# check for alerts
alertWx = alertBrodcastNOAA()
alertFema = getIpawsAlert(latitudeValue,longitudeValue, shortAlerts=True)
if wxAlertBroadcastEnabled:
alertWx = alertBrodcastNOAA()
if enableGBalerts:
alertUk = get_govUK_alerts()
else:
alertUk = NO_ALERTS
if emergencyAlertBrodcastEnabled:
if enableGBalerts:
alertUk = get_govUK_alerts()
else:
# default USA alerts
alertFema = getIpawsAlert(latitudeValue,longitudeValue, shortAlerts=True)
# format alert
if alertWx:
@@ -693,7 +702,7 @@ def handleAlertBroadcast(deviceID=1):
ukAlert = alertUk
if emergencyAlertBrodcastEnabled:
if NO_ALERTS not in femaAlert:
if NO_ALERTS not in femaAlert and ERROR_FETCHING_DATA not in femaAlert:
if isinstance(emergencyAlertBroadcastCh, list):
for channel in emergencyAlertBroadcastCh:
send_message(femaAlert, int(channel), 0, deviceID)
@@ -855,90 +864,92 @@ def displayNodeTelemetry(nodeID=0, rxNode=0, userRequested=False):
positionMetadata = {}
def consumeMetadata(packet, rxNode=0):
# keep records of recent telemetry data
debugMetadata = False
packet_type = ''
if packet.get('decoded'):
packet_type = packet['decoded']['portnum']
nodeID = packet['from']
try:
# keep records of recent telemetry data
packet_type = ''
if packet.get('decoded'):
packet_type = packet['decoded']['portnum']
nodeID = packet['from']
# TELEMETRY packets
if packet_type == 'TELEMETRY_APP':
#if debugMetadata: print(f"DEBUG TELEMETRY_APP: {packet}\n\n")
# get the telemetry data
telemetry_packet = packet['decoded']['telemetry']
if telemetry_packet.get('deviceMetrics'):
deviceMetrics = telemetry_packet['deviceMetrics']
if telemetry_packet.get('localStats'):
localStats = telemetry_packet['localStats']
# Check if 'numPacketsTx' and 'numPacketsRx' exist and are not zero
if localStats.get('numPacketsTx') is not None and localStats.get('numPacketsRx') is not None and localStats['numPacketsTx'] != 0:
# Assign the values to the telemetry dictionary
keys = [
'numPacketsTx', 'numPacketsRx', 'numOnlineNodes',
'numOfflineNodes', 'numPacketsTxErr', 'numPacketsRxErr', 'numTotalNodes']
# TELEMETRY packets
if packet_type == 'TELEMETRY_APP':
if debugMetadata: print(f"DEBUG TELEMETRY_APP: {packet}\n\n")
# get the telemetry data
telemetry_packet = packet['decoded']['telemetry']
if telemetry_packet.get('deviceMetrics'):
deviceMetrics = telemetry_packet['deviceMetrics']
if telemetry_packet.get('localStats'):
localStats = telemetry_packet['localStats']
# Check if 'numPacketsTx' and 'numPacketsRx' exist and are not zero
if localStats.get('numPacketsTx') is not None and localStats.get('numPacketsRx') is not None and localStats['numPacketsTx'] != 0:
# Assign the values to the telemetry dictionary
keys = [
'numPacketsTx', 'numPacketsRx', 'numOnlineNodes',
'numOfflineNodes', 'numPacketsTxErr', 'numPacketsRxErr', 'numTotalNodes']
for key in keys:
if localStats.get(key) is not None:
telemetryData[rxNode][key] = localStats.get(key)
# POSITION_APP packets
if packet_type == 'POSITION_APP':
if debugMetadata: print(f"DEBUG POSITION_APP: {packet}\n\n")
# get the position data
keys = ['altitude', 'groundSpeed', 'precisionBits']
position_data = packet['decoded']['position']
try:
if nodeID not in positionMetadata:
positionMetadata[nodeID] = {}
for key in keys:
if localStats.get(key) is not None:
telemetryData[rxNode][key] = localStats.get(key)
# POSITION_APP packets
if packet_type == 'POSITION_APP':
if debugMetadata: print(f"DEBUG POSITION_APP: {packet}\n\n")
# get the position data
keys = ['altitude', 'groundSpeed', 'precisionBits']
position_data = packet['decoded']['position']
try:
if nodeID not in positionMetadata:
positionMetadata[nodeID] = {}
for key in keys:
positionMetadata[nodeID][key] = position_data.get(key, 0)
# Keep the positionMetadata dictionary at 5 records
if len(positionMetadata) > 20:
# Remove the oldest entry
oldest_nodeID = next(iter(positionMetadata))
del positionMetadata[oldest_nodeID]
except Exception as e:
logger.debug(f"System: POSITION_APP decode error: {e} packet {packet}")
positionMetadata[nodeID][key] = position_data.get(key, 0)
# Keep the positionMetadata dictionary at 5 records
if len(positionMetadata) > 20:
# Remove the oldest entry
oldest_nodeID = next(iter(positionMetadata))
del positionMetadata[oldest_nodeID]
except Exception as e:
logger.debug(f"System: POSITION_APP decode error: {e} packet {packet}")
# WAYPOINT_APP packets
if packet_type == 'WAYPOINT_APP':
if debugMetadata: print(f"DEBUG WAYPOINT_APP: {packet['decoded']['waypoint']}\n\n")
# get the waypoint data
waypoint_data = packet['decoded']['waypoint']
keys = ['latitude', 'longitude',]
# WAYPOINT_APP packets
if packet_type == 'WAYPOINT_APP':
if debugMetadata: print(f"DEBUG WAYPOINT_APP: {packet['decoded']['waypoint']}\n\n")
# get the waypoint data
waypoint_data = packet['decoded']
# NEIGHBORINFO_APP
if packet_type == 'NEIGHBORINFO_APP':
if debugMetadata: print(f"DEBUG NEIGHBORINFO_APP: {packet}\n\n")
# get the neighbor info data
neighbor_data = packet['decoded']['neighborInfo']
# TRACEROUTE_APP
if packet_type == 'TRACEROUTE_APP':
if debugMetadata: print(f"DEBUG TRACEROUTE_APP: {packet}\n\n")
# get the traceroute data
traceroute_data = packet['decoded']['traceroute']
# NEIGHBORINFO_APP
if packet_type == 'NEIGHBORINFO_APP':
if debugMetadata: print(f"DEBUG NEIGHBORINFO_APP: {packet}\n\n")
# get the neighbor info data
neighbor_data = packet['decoded']
# TRACEROUTE_APP
if packet_type == 'TRACEROUTE_APP':
if debugMetadata: print(f"DEBUG TRACEROUTE_APP: {packet}\n\n")
# get the traceroute data
traceroute_data = packet['decoded']
# DETECTION_SENSOR_APP
if packet_type == 'DETECTION_SENSOR_APP':
if debugMetadata: print(f"DEBUG DETECTION_SENSOR_APP: {packet}\n\n")
# get the detection sensor data
detection_data = packet['decoded']['detectionSensor']
# DETECTION_SENSOR_APP
if packet_type == 'DETECTION_SENSOR_APP':
if debugMetadata: print(f"DEBUG DETECTION_SENSOR_APP: {packet}\n\n")
# get the detection sensor data
detection_data = packet['decoded']
# PAXCOUNTER_APP
if packet_type == 'PAXCOUNTER_APP':
if debugMetadata: print(f"DEBUG PAXCOUNTER_APP: {packet}\n\n")
# get the paxcounter data
paxcounter_data = packet['decoded']['paxcounter']
# PAXCOUNTER_APP
if packet_type == 'PAXCOUNTER_APP':
if debugMetadata: print(f"DEBUG PAXCOUNTER_APP: {packet}\n\n")
# get the paxcounter data
paxcounter_data = packet['decoded']
# REMOTE_HARDWARE_APP
if packet_type == 'REMOTE_HARDWARE_APP':
if debugMetadata: print(f"DEBUG REMOTE_HARDWARE_APP: {packet}\n\n")
# get the remote hardware data
remote_hardware_data = packet['decoded']['remoteHardware']
# REMOTE_HARDWARE_APP
if packet_type == 'REMOTE_HARDWARE_APP':
if debugMetadata: print(f"DEBUG REMOTE_HARDWARE_APP: {packet}\n\n")
# get the remote hardware data
remote_hardware_data = packet['decoded']
except KeyError as e:
logger.critical(f"System: Error consuming metadata: {e} Device:{rxNode}")
logger.debug(f"System: Error Packet = {packet}")
def get_sysinfo(nodeID=0, deviceID=1):
# Get the system telemetry data for return on the sysinfo command
@@ -1162,7 +1173,7 @@ async def watchdog():
# Alert Broadcast
if wxAlertBroadcastEnabled or emergencyAlertBrodcastEnabled:
# weather alerts
handleAlertBroadcast(1)
handleAlertBroadcast(2)
# Telemetry data
int2Data = displayNodeTelemetry(0, 2)

View File

@@ -4,7 +4,7 @@
import asyncio
import time # for sleep, get some when you can :)
from pubsub import pub # pip install pubsub
from pubsub import pub # pip install pubsub, use launch.sh for venv
from modules.log import *
from modules.system import *

7
runShell.sh Normal file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
# meshing-around demo script for shell scripting
# runShell.sh
cd "$(dirname "$0")"
program_path=$(pwd)
printf "Running meshing-around demo script for shell scripting\n"

30
sysEnv.sh Normal file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
# meshing-around shell script for sysinfo
# runShell.sh
cd "$(dirname "$0")"
program_path=$(pwd)
# get basic telemetry data. Free space, CPU, RAM, and temperature for a raspberry pi
free_space=$(df -h | grep ' /$' | awk '{print $4}')
cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
ram_usage=$(free | grep Mem | awk '{print $3/$2 * 100.0}')
ram_free=$(echo "scale=2; 100 - $ram_usage" | bc)
# if command vcgencmd is found, part of raspberrypi tools, use it to get temperature
if command -v vcgencmd &> /dev/null
then
# get temperature
temp=$(vcgencmd measure_temp | sed "s/temp=//" | sed "s/'C//")
# temp in fahrenheit
tempf=$(echo "scale=2; $temp * 9 / 5 + 32" | bc)
else
# get temperature from thermal zone
temp=$(paste <(cat /sys/class/thermal/thermal_zone*/type) <(cat /sys/class/thermal/thermal_zone*/temp) | grep "temp" | awk '{print $2/1000}' | awk '{s+=$1} END {print s/NR}')
tempf=$(echo "scale=2; $temp * 9 / 5 + 32" | bc)
fi
# print telemetry data
printf "Disk:%s" "$free_space"
printf " RAM:%.2f%%" "$ram_usage"
printf " CPU:%.1f%%" "$cpu_usage"
printf " CPU-T:%.1f°C (%.1f°F)" "$temp" "$tempf"